Bug #78341 Memory leak in MySQLRecognitionBase at d = new Private();
Submitted: 5 Sep 2015 19:42 Modified: 5 Feb 2018 8:48
Reporter: Daniel Kelly (OCA) Email Updates:
Status: Won't fix Impact on me:
None 
Category:MySQL Workbench Severity:S1 (Critical)
Version:6.3.4 OS:Ubuntu (Linux Mint 17.2)
Assigned to: CPU Architecture:Any

[5 Sep 2015 19:42] Daniel Kelly
Description:
I am using the workbench bundled antlr3 mysql-parser in a project to parse queries and generate change logging and replication queries from inserts and updates. I am currently trying to iron out memory leaks and sloppy programming from the past months of development in my application but I have hit a speed bump in the process. I am not 100% sure the memory leak is directly from mysql-parser but it would seem that way although after closer inspection I cannot find anything wrong with your code ;)

Let me know what else I can provide.

Valgrind output:
==9813== 150 (80 direct, 70 indirect) bytes in 1 blocks are definitely lost in loss record 10 of 10
==9813==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9813==    by 0x856130: MySQLRecognitionBase::MySQLRecognitionBase(std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser-common.cpp:88)
==9813==    by 0x85C637: MySQLRecognizer::MySQLRecognizer(long, std::string const&, std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser.cpp:1177)
==9813==    by 0x453EB1: QueryDetails::QueryDetails(std::string, std::vector<std::string, std::allocator<std::string> >) (QueryDetails.cpp:33)
==9813==    by 0x453CAC: QueryDetails::QueryDetails(std::string) (QueryDetails.cpp:19)
==9813==    by 0x8AADCD: DatabaseHandlerTest_query_getquerydetails_Test::TestBody() (tDatabaseHandler.cpp:210)
==9813==    by 0x8CF423: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2078)
==9813==    by 0x8CADA7: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2114)
==9813==    by 0x8B3078: testing::Test::Run() (gtest.cc:2151)
==9813==    by 0x8B3853: testing::TestInfo::Run() (gtest.cc:2326)
==9813==    by 0x8B3EE3: testing::TestCase::Run() (gtest.cc:2444)
==9813==    by 0x8BA593: testing::internal::UnitTestImpl::RunAllTests() (gtest.cc:4315)

g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
valgrind-3.10.0.SVN
googletest v1.7.0

How to repeat:
Simply instantiate a recognizer object. I have tried defining as pointer and explicitly delete it as well but yielded the same results.

set<string> charsets = {"utf-8"};
// Build the recognizer to build our query walker.
MySQLRecognizer recognizer(50610, "", charsets);

Suggested fix:
Unsure, ?not experienced enough? to see an actual problem.
[5 Sep 2015 19:57] Daniel Kelly
Here's the full valgrind report

==11506== 
==11506== HEAP SUMMARY:
==11506==     in use at exit: 150 bytes in 3 blocks
==11506==   total heap usage: 1,579 allocs, 1,576 frees, 11,688,240 bytes allocated
==11506== 
==11506== 30 bytes in 1 blocks are indirectly lost in loss record 1 of 3
==11506==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11506==    by 0x5F3A208: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==11506==    by 0x499C82: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (basic_string.tcc:138)
==11506==    by 0x5F3BD47: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==11506==    by 0x454164: QueryDetails::QueryDetails(std::string, std::vector<std::string, std::allocator<std::string> >, bool) (QueryDetails.cpp:47)
==11506==    by 0x8A8F6D: DatabaseHandlerTest_query_getquerydetails_Test::TestBody() (tDatabaseHandler.cpp:172)
==11506==    by 0x8CD3B5: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2078)
==11506==    by 0x8C8D39: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2114)
==11506==    by 0x8B100A: testing::Test::Run() (gtest.cc:2151)
==11506==    by 0x8B17E5: testing::TestInfo::Run() (gtest.cc:2326)
==11506==    by 0x8B1E75: testing::TestCase::Run() (gtest.cc:2444)
==11506==    by 0x8B8525: testing::internal::UnitTestImpl::RunAllTests() (gtest.cc:4315)
==11506== 
==11506== 40 bytes in 1 blocks are indirectly lost in loss record 2 of 3
==11506==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11506==    by 0x45CCEF: __gnu_cxx::new_allocator<std::_Rb_tree_node<std::string> >::allocate(unsigned long, void const*) (new_allocator.h:104)
==11506==    by 0x45C9F1: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_get_node() (stl_tree.h:370)
==11506==    by 0x45BF02: std::_Rb_tree_node<std::string>* std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_create_node<std::string const&>(std::string const&) (stl_tree.h:403)
==11506==    by 0x858E8C: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_clone_node(std::_Rb_tree_node<std::string> const*) (stl_tree.h:429)
==11506==    by 0x858187: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_copy(std::_Rb_tree_node<std::string> const*, std::_Rb_tree_node<std::string>*) (stl_tree.h:1087)
==11506==    by 0x857795: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::operator=(std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> > const&) (stl_tree.h:997)
==11506==    by 0x85725A: std::set<std::string, std::less<std::string>, std::allocator<std::string> >::operator=(std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (stl_set.h:235)
==11506==    by 0x856144: MySQLRecognitionBase::MySQLRecognitionBase(std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser-common.cpp:89)
==11506==    by 0x85C60B: MySQLRecognizer::MySQLRecognizer(long, std::string const&, std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser.cpp:1177)
==11506==    by 0x454234: QueryDetails::QueryDetails(std::string, std::vector<std::string, std::allocator<std::string> >, bool) (QueryDetails.cpp:49)
==11506==    by 0x8A8F6D: DatabaseHandlerTest_query_getquerydetails_Test::TestBody() (tDatabaseHandler.cpp:172)
==11506== 
==11506== 150 (80 direct, 70 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
==11506==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11506==    by 0x856104: MySQLRecognitionBase::MySQLRecognitionBase(std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser-common.cpp:88)
==11506==    by 0x85C60B: MySQLRecognizer::MySQLRecognizer(long, std::string const&, std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser.cpp:1177)
==11506==    by 0x454234: QueryDetails::QueryDetails(std::string, std::vector<std::string, std::allocator<std::string> >, bool) (QueryDetails.cpp:49)
==11506==    by 0x8A8F6D: DatabaseHandlerTest_query_getquerydetails_Test::TestBody() (tDatabaseHandler.cpp:172)
==11506==    by 0x8CD3B5: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2078)
==11506==    by 0x8C8D39: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2114)
==11506==    by 0x8B100A: testing::Test::Run() (gtest.cc:2151)
==11506==    by 0x8B17E5: testing::TestInfo::Run() (gtest.cc:2326)
==11506==    by 0x8B1E75: testing::TestCase::Run() (gtest.cc:2444)
==11506==    by 0x8B8525: testing::internal::UnitTestImpl::RunAllTests() (gtest.cc:4315)
==11506==    by 0x8CE67B: bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (gtest.cc:2078)
==11506== 
==11506== LEAK SUMMARY:
==11506==    definitely lost: 80 bytes in 1 blocks
==11506==    indirectly lost: 70 bytes in 2 blocks
==11506==      possibly lost: 0 bytes in 0 blocks
==11506==    still reachable: 0 bytes in 0 blocks
==11506==         suppressed: 0 bytes in 0 blocks
[8 Sep 2015 10:38] Daniel Kelly
Here's another Valgrind report with all my code and googletest removed. Bare bones test to eliminate any other possibilities...

Also attached the main.cpp for replication.

==9580== Memcheck, a memory error detector
==9580== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==9580== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==9580== Command: ./dist/Debug/GNU-Linux-x86/test_mysqlrecognitionbase
==9580== 
==9580== 
==9580== HEAP SUMMARY:
==9580==     in use at exit: 150 bytes in 3 blocks
==9580==   total heap usage: 14 allocs, 11 frees, 489 bytes allocated
==9580== 
==9580== 30 bytes in 1 blocks are indirectly lost in loss record 1 of 3
==9580==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9580==    by 0x5437208: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==9580==    by 0x808D62: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (basic_string.tcc:138)
==9580==    by 0x5438D47: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==9580==    by 0x7EAAC5: main (main.cpp:22)
==9580== 
==9580== 40 bytes in 1 blocks are indirectly lost in loss record 2 of 3
==9580==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9580==    by 0x7EB5F7: __gnu_cxx::new_allocator<std::_Rb_tree_node<std::string> >::allocate(unsigned long, void const*) (new_allocator.h:104)
==9580==    by 0x7EB5AB: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_get_node() (stl_tree.h:370)
==9580==    by 0x7EB472: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_create_node(std::string const&) (stl_tree.h:380)
==9580==    by 0x7EDFCE: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_clone_node(std::_Rb_tree_node<std::string> const*) (stl_tree.h:429)
==9580==    by 0x7ED72F: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_copy(std::_Rb_tree_node<std::string> const*, std::_Rb_tree_node<std::string>*) (stl_tree.h:1087)
==9580==    by 0x7ECD39: std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::operator=(std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> > const&) (stl_tree.h:997)
==9580==    by 0x7EC91A: std::set<std::string, std::less<std::string>, std::allocator<std::string> >::operator=(std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (stl_set.h:235)
==9580==    by 0x7EB823: MySQLRecognitionBase::MySQLRecognitionBase(std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser-common.cpp:89)
==9580==    by 0x7F16D9: MySQLRecognizer::MySQLRecognizer(long, std::string const&, std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser.cpp:1177)
==9580==    by 0x7EAB39: main (main.cpp:25)
==9580== 
==9580== 150 (80 direct, 70 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
==9580==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9580==    by 0x7EB7E3: MySQLRecognitionBase::MySQLRecognitionBase(std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser-common.cpp:88)
==9580==    by 0x7F16D9: MySQLRecognizer::MySQLRecognizer(long, std::string const&, std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&) (mysql-parser.cpp:1177)
==9580==    by 0x7EAB39: main (main.cpp:25)
==9580== 
==9580== LEAK SUMMARY:
==9580==    definitely lost: 80 bytes in 1 blocks
==9580==    indirectly lost: 70 bytes in 2 blocks
==9580==      possibly lost: 0 bytes in 0 blocks
==9580==    still reachable: 0 bytes in 0 blocks
==9580==         suppressed: 0 bytes in 0 blocks
==9580== 
==9580== For counts of detected and suppressed errors, rerun with: -v
==9580== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
[8 Sep 2015 10:38] Daniel Kelly
Main source for replication

Attachment: main.cpp (text/x-c++src), 505 bytes.

[8 Sep 2015 12:47] Daniel Kelly
Fixed it, I think...

Added destructor to the MySQLRecognitionBase to also delete d. I did this before but never removed the destructor body in the MySQLRecognitionBase header so I was still getting errors and immediately assumed that it was only deleted in the MySQLRecognizer for a reason.... I am still learning but I hope this actually resolves it.

// Need to delete d!!
MySQLRecognitionBase::~MySQLRecognitionBase()
{
	delete d;
}
[14 Sep 2015 13:43] Daniel Kelly
Added destructor for MySQLRecognitionBase

(*) I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it.

Contribution: mysql-parser-common.cpp (text/x-c++src), 12.71 KiB.

[14 Sep 2015 13:44] Daniel Kelly
Removed inline destructor body

(*) I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it.

Contribution: mysql-parser-common.h (text/x-chdr), 2.27 KiB.

[5 Feb 2018 8:48] Mike Lischke
Obsolete, now that we switched to ANTLR4 for the next version of MySQL Workbench.