Bug #93593 ODBC memory leak
| Submitted: | 13 Dec 2018 14:36 | Modified: | 1 Jun 2020 3:58 |
|---|---|---|---|
| Reporter: | Bhargava Srinarasi | Email Updates: | |
| Status: | No Feedback | Impact on me: | |
| Category: | Connector / ODBC | Severity: | S2 (Serious) |
| Version: | 8.0.13 | OS: | Windows |
| Assigned to: | CPU Architecture: | Any | |
| Tags: | memory leak | ||
[1 May 2020 3:58]
MySQL Verification Team
Please try version 8.0.20. Thanks.
[14 May 2020 16:56]
Leigh Anderson
I still have this issue in 8.0.20. If I use 5.3.14 there is no leak.
[2 Jun 2020 1:00]
Bugs System
No feedback was provided for this bug for over a month, so it is being suspended automatically. If you are able to provide the information that was originally requested, please do so and change the status of the bug back to "Open".
[6 Jun 2020 10:08]
Leigh Anderson
I still have this issue with the 8.0.20 driver. If I use the 5.3.14 driver there is no leak.
[29 Mar 2022 12:08]
c t
still existing in mysql-connector-odbc-noinstall-8.0.28-win32
this bug is happening during load/unload of the driver dll.
when you use debugger in VS2017 you can see relation
between load/alloc and unload/NO free.
VS2017 code used for verification and analysis:
// NOTE: Use Multi-Byte Character Set
// tested with latest ODBC driver
// see output for unload/load
// 'memleak_test.exe' (Win32) : Unloaded 'C:\mysql-connector-odbc-noinstall-8.0.28-win32\lib\myodbc8w.dll'
// 'memleak_test.exe' (Win32) : Loaded 'C:\mysql-connector-odbc-noinstall-8.0.28-win32\lib\myodbc8w.dll'.Symbols loaded.
// REDUCED_OUTPUT can be used to prevent non error output of results of various odbc functions
#define REDUCED_OUTPUT 0
// ODBC_DATASOURCE is the ODBC connection used, configured to MySQL ODBC Unicode driver
#define ODBC_DATASOURCE "test_mycustomer"
#include <iostream>
#include <windows.h>
#include <sqltypes.h>
#include <sqlext.h>
#include <psapi.h>
#pragma comment(lib, "odbc32.lib")
class MemleakTest
{
public:
MemleakTest(const std::string &ds)
: odbcDataSource(ds)
{}
~MemleakTest()
{
disconnect();
}
bool connect()
{
const auto envAllocResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
#if !REDUCED_OUTPUT
std::cout << "envAllocResult: " << envAllocResult << std::endl;
#endif
if (SQL_SUCCEEDED(envAllocResult))
{
const auto setEnvAttrResult = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
#if !REDUCED_OUTPUT
std::cout << "setEnvAttrResult: " << setEnvAttrResult << std::endl;
#endif
if (SQL_SUCCEEDED(envAllocResult))
{
const auto hdbcAllocResult = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
#if !REDUCED_OUTPUT
std::cout << "hdbcAllocResult: " << hdbcAllocResult << std::endl;
#endif
if (SQL_SUCCEEDED(hdbcAllocResult))
{
const auto setConnAttrResult = SQLSetConnectAttr(hDbc, SQL_LOGIN_TIMEOUT, (void*)5, 0);
#if !REDUCED_OUTPUT
std::cout << "setConnAttrResult: " << setConnAttrResult << std::endl;
#endif
if (SQL_SUCCEEDED(setConnAttrResult))
{
// Connect to data source //
const auto connectResult = SQLConnect(hDbc, (SQLCHAR*)odbcDataSource.c_str(), SQL_NTS, (SQLCHAR*) "", SQL_NTS, (SQLCHAR*) "", SQL_NTS);
#if !REDUCED_OUTPUT
std::cout << "connectResult: " << connectResult << std::endl;
#endif
if (SQL_SUCCEEDED(connectResult))
{
#if !REDUCED_OUTPUT
std::cout << "CONNECTED" << std::endl;
#endif
return true;
}
else
{
std::cerr << "SQLConnect failed: " << connectResult << std::endl;
}
}
else
{
std::cerr << "SQLSetConnectAttr failed: " << setConnAttrResult << std::endl;
}
FreeHandle(SQL_HANDLE_DBC, hDbc);
}
else
{
std::cerr << "SQLAllocHandle hDbc failed: " << hdbcAllocResult << std::endl;
}
}
else
{
std::cerr << "SQLSetEnvAttr failed: " << setEnvAttrResult << std::endl;
}
FreeHandle(SQL_HANDLE_ENV, hEnv);
}
else
{
std::cerr << "SQLAllocHandle HENV failed: " << envAllocResult << std::endl;
}
return false;
}
void disconnect()
{
if (hDbc)
{
const auto disconnectResult = SQLDisconnect(hDbc);
#if !REDUCED_OUTPUT
std::cout << "disconnectResult: " << disconnectResult << std::endl;
#endif
if (!SQL_SUCCEEDED(disconnectResult))
{
std::cerr << "SQLDisconnect failed: " << disconnectResult << std::endl;
}
FreeHandle(SQL_HANDLE_DBC, hDbc);
}
if (hEnv)
{
FreeHandle(SQL_HANDLE_ENV, hEnv);
}
#if !REDUCED_OUTPUT
std::cout << "DISCONNECTED" << std::endl;
#endif
}
private:
std::string odbcDataSource;
SQLHANDLE hEnv = nullptr;
SQLHANDLE hDbc = nullptr;
bool FreeHandle(SQLSMALLINT type, SQLHANDLE &handle)
{
if (nullptr == handle)
{
std::cerr << "invalid handle given" << std::endl;
return false;
}
const auto freeHandleResult = SQLFreeHandle(type, handle);
if (!SQL_SUCCEEDED(freeHandleResult))
{
std::cerr << "SQLFreeHandle(type " << type << ", handle " << handle << ") failed: " << freeHandleResult << std::endl;
return false;
}
handle = nullptr;
return true;
}
};
void printMemory()
{
const HANDLE hProcess = GetCurrentProcess();
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
{
printf("\tWorkingSetSize: 0x%08X\n", pmc.WorkingSetSize);
}
CloseHandle(hProcess);
}
int main()
{
std::cout << "***START ";
printMemory();
for (uint32_t counter = 0; counter < 500; ++counter)
{
MemleakTest memleakTest(ODBC_DATASOURCE);
memleakTest.connect();
memleakTest.disconnect();
std::cout << "#" << counter;
printMemory();
Sleep(500); // dont let firewall/server think its DDoS attack
}
std::cout << "***END ";
printMemory();
return 0;
}
[30 Mar 2022 15:32]
c t
Add.: used system: Edition Windows 10 Enterprise Version 20H2 OS build 19042.1586 Experience Windows Feature Experience Pack 120.2212.4170.0
Portions of this website are copyright © 2001, 2002 The PHP Group
Page generated in 0.041 sec. using MySQL 8.4.7-u6-cloud
Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the
respective copyright holders.
It is not reviewed in advance
by Oracle and does not necessarily represent the opinion of
Oracle or any other party.
Description: I'm seeing a memory leak in SQLConnect() on Windows when I call SQLConnect() and SQLDisconnect() repeatedly. I can see that the memory that's allocated in SQLConnect() never gets released. A couple of observations that may be interesting keeping the C program below in mind. 1) After the first iteration, hdbc is the same on all iterations! 2) Instead of doing connect and disconnect in the same loop, if I put the handles in an array and disconnect/free them later, I see that there's much less leak. Also hdbc is different in each iteration. How to repeat: Here's a very simple C program which leaks around 150kB per iteration. int main() { for (int i = 0; i < 10; ++i) { SQLHENV henv; SQLHDBC hdbc; SQLRETURN retcode; // Allocate environment handle retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); // Set the ODBC version environment attribute if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); // Allocate connection handle if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); // Set login timeout to 5 seconds if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0); // Connect to data source retcode = SQLConnect(hdbc, (SQLCHAR*) "MySQL", SQL_NTS, (SQLCHAR*) "test", 0, (SQLCHAR*) "test", 0); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLDisconnect(hdbc); } SQLFreeHandle(SQL_HANDLE_DBC, hdbc); } } SQLFreeHandle(SQL_HANDLE_ENV, henv); } }