I am testing a class that takes multiple std::unique_ptr in initialisation. Raw pointers of mock objects are allocated with new, and then passed to an injected unique_ptr.
A test without any expectations passes fine. When I add an expectation, I get memory leaks.
The sources:
Tested class:
class TemperatureController : public IController
{
public:
TemperatureController(std::unique_ptr<IOutput> output,
std::unique_ptr<ISensor> sensor,
std::unique_ptr<IRegulator> regulator,
std::unique_ptr<ISetpoint> setpoint,
std::unique_ptr<IEnabler> enabler) :
output(std::move(output)),
sensor(std::move(sensor)),
regulator(std::move(regulator)),
setpoint(std::move(setpoint)),
enabler(std::move(enabler))
{ }
virtual ~TemperatureController(){}
virtual void setup() override;
virtual void controlLoop() override;
private:
std::unique_ptr<IOutput> output;
std::unique_ptr<ISensor> sensor;
std::unique_ptr<IRegulator> regulator;
std::unique_ptr<ISetpoint> setpoint;
std::unique_ptr<IEnabler> enabler;
};
void TemperatureController::controlLoop()
{
cout << "enabler address in sut = " << std::hex << *((int*)(enabler.get())) << endl;
cout << "regulator address in sut = 4" << std::hex << *((int*)(regulator.get())) << endl;
if(enabler->isEnabled())
{
regulator->controllOutput(*output,
*setpoint,
*sensor);
}
}
Test suite:
template <typename T>
auto injectMock(T* ptr)
{
return unique_ptr<T>(ptr);
}
struct TestTemperatureController : public Test
{
MockIEnabler* enabler = new MockIEnabler();
MockIOutput* output = new MockIOutput();
MockIRegulator* regulator = new MockIRegulator();
MockISensor* sensor = new MockISensor();
MockISetpoint* setpoint = new MockISetpoint();
TemperatureController sut{injectMock(output),
injectMock(sensor),
injectMock(regulator),
injectMock(setpoint),
injectMock(enabler)};
TestTemperatureController()
{
cout << "enabler address in test = "
<< std::hex << *((int*)(enabler)) << endl
<< "regulator address in test = "
<< std::hex << *((int*)(regulator)) << endl;
}
};
TEST_F(TestTemperatureController,
WhenEnabled_ShouldAct)
{
EXPECT_CALL(*enabler, isEnabled())
.WillOnce(Return(true));
//EXPECT_CALL(*regulator, controllOutput(_,_,_)) // commented out to make simpler logs
// .Times(AtLeast(1));
sut.controlLoop();
}
I ommited some noise code (includes, namespaces etc.).
When run with valgrind, I get the following output:
==10176== Memcheck, a memory error detector
==10176== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10176== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==10176== Command: ./unit_tests
==10176==
Running main() from gmock_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from TestTemperatureController
[ RUN ] TestTemperatureController.WhenEnabled_ShouldAct
enabler address in test = 54edc8
regulator address in test = 54eb90
enabler address in sut = 54edc8
regulator address in sut = 454eb90
GMOCK WARNING:
Uninteresting mock function call - returning directly.
Function call: controllOutput(#0x5cdf1b0 8-byte object <08-ED 54-00 00-00 00-00>, #0x5cdf430 8-byte object <C0-EA 54-00 00-00 00-00>, #0x5cdf390 8-byte object <D8-EA 54-00 00-00 00-00>)
NOTE: You can safely ignore the above warning unless this call should not happen. Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call. See https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#knowing-when-to-expect for details.
[ OK ] TestTemperatureController.WhenEnabled_ShouldAct (282 ms)
[----------] 1 test from TestTemperatureController (297 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (369 ms total)
[ PASSED ] 1 test.
/home/lukasz/workspace/arduino-thermostat/ut/tests/TestTemperatureController/TestTemperatureController.cpp:52: ERROR: this mock object (used in test TestTemperatureController.WhenEnabled_ShouldAct) should be deleted but never is. Its address is #0x5cdf110.
ERROR: 1 leaked mock object found at program exit.
==10176==
==10176== HEAP SUMMARY:
==10176== in use at exit: 74,863 bytes in 37 blocks
==10176== total heap usage: 231 allocs, 194 frees, 124,341 bytes allocated
==10176==
==10176== 403 (16 direct, 387 indirect) bytes in 1 blocks are definitely lost in loss record 35 of 37
==10176== at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10176== by 0x4DE485: __gnu_cxx::new_allocator<testing::internal::linked_ptr<testing::internal::ExpectationBase> >::allocate(unsigned long, void const*) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x4DE0F3: std::allocator_traits<std::allocator<testing::internal::linked_ptr<testing::internal::ExpectationBase> > >::allocate(std::allocator<testing::internal::linked_ptr<testing::internal::ExpectationBase> >&, unsigned long) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x4DDA47: std::_Vector_base<testing::internal::linked_ptr<testing::internal::ExpectationBase>, std::allocator<testing::internal::linked_ptr<testing::internal::ExpectationBase> > >::_M_allocate(unsigned long) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x4DD077: void std::vector<testing::internal::linked_ptr<testing::internal::ExpectationBase>, std::allocator<testing::internal::linked_ptr<testing::internal::ExpectationBase> > >::_M_emplace_back_aux<testing::internal::linked_ptr<testing::internal::ExpectationBase> const&>(testing::internal::linked_ptr<testing::internal::ExpectationBase> const&) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x4DC562: std::vector<testing::internal::linked_ptr<testing::internal::ExpectationBase>, std::allocator<testing::internal::linked_ptr<testing::internal::ExpectationBase> > >::push_back(testing::internal::linked_ptr<testing::internal::ExpectationBase> const&) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x4DB252: testing::internal::FunctionMockerBase<bool ()>::AddNewExpectation(char const*, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::tuple<> const&) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x4D9459: testing::internal::MockSpec<bool ()>::InternalExpectedAt(char const*, int, char const*, char const*) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x4D4862: TestTemperatureController_WhenEnabled_ShouldAct_Test::TestBody() (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x52DF4D: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x5281B6: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176== by 0x50C9A9: testing::Test::Run() (in /home/lukasz/workspace/build-arduino-thermostat-Desktop-Domyślna/unit_tests)
==10176==
==10176== LEAK SUMMARY:
==10176== definitely lost: 16 bytes in 1 blocks
==10176== indirectly lost: 387 bytes in 7 blocks
==10176== possibly lost: 0 bytes in 0 blocks
==10176== still reachable: 74,460 bytes in 29 blocks
==10176== suppressed: 0 bytes in 0 blocks
==10176== Reachable blocks (those to which a pointer was found) are not shown.
==10176== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==10176==
==10176== For counts of detected and suppressed errors, rerun with: -v
==10176== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
I'm not really good at googlemock implementation, but for me i seems like there are some expectations allocated, which are not released later. Or is it a false positive?
The question is: How do I deal with that? I want to inject mocks with unique_ptr's. I don't want to change the tested object's interface, of course.
Related
Is there a way to properly run qmltestrunner against a test, that uses a custom qml module, that is compiled with address sanitizer flags (e.g. --fsanitize=address), and to be able to detect all leaks in this qmlmodule?
The problem is that qmltestrunner is not linked against the address sanitizer, thus it complains regarding the linkage order when importing custom qmlmodule.
> qmltestrunner -import ../../../../build/qmlmodules/ -platform offscreen
==393140==ASan runtime does not come first in the initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
When I am trying to run a qmltestrunner explicitly preloading sanitizer it detects qt internal leaks, but doesn't detect leaks in my qmlmodule:
> LD_PRELOAD=/lib/x86_64-linux-gnu/libasan.so.6 qmltestrunner -import ../../../../build/MyLib/qmlmodules/ -platform offscreen
is 0x12345********* Start testing of qmltestrunner *********
Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.2.1 20210110), debian 11
PASS : qmltestrunner::TestIoauExample::initTestCase()
PASS : qmltestrunner::TestIoauExample::test_ioau_example()
PASS : qmltestrunner::TestIoauExample::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 2ms
********* Finished testing of qmltestrunner *********
=================================================================
==447337==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 500 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f3417a7 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
#1 0x7f3f06f6950a in ioau::IoauTester::HeightmapGenerator::HeightmapGenerator(QObject*) (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/libioautester-qmlplugin.so+0x1550a)
#2 0x7f3f06f62223 in void QQmlPrivate::createInto<ioau::IoauTester::HeightmapGenerator>(void*) (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/libioautester-qmlplugin.so+0xe223)
#3 0x7f3f0d4e2c89 in QQmlType::create(QObject**, void**, unsigned long) const (/lib/x86_64-linux-gnu/libQt5Qml.so.5+0x291c89)
#4 0x7f3f0d4c127c in QQmlComponent::QQmlComponent(QQmlEngine*, QObject*) (/lib/x86_64-linux-gnu/libQt5Qml.so.5+0x27027c)
Indirect leak of 168 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0dd51b53 in QScreen::QScreen(QPlatformScreen*) (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x165b53)
Indirect leak of 88 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0efb8d04 in QObject::QObject(QObject*) (/lib/x86_64-linux-gnu/libQt5Core.so.5+0x2e2d04)
Indirect leak of 40 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f076610a8 (/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/libqoffscreen.so+0x120a8)
Indirect leak of 24 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f07662167 (/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/libqoffscreen.so+0x13167)
Indirect leak of 16 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0ede4bb0 in QtSharedPointer::ExternalRefCountData::getAndRef(QObject const*) (/lib/x86_64-linux-gnu/libQt5Core.so.5+0x10ebb0)
Indirect leak of 16 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0dd01422 in QWindowSystemInterface::handleScreenAdded(QPlatformScreen*, bool) (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x115422)
#2 0x7773c9c2beee67ff (<unknown module>)
Indirect leak of 16 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0dd0cadb in QPlatformScreen::QPlatformScreen() (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x120adb)
SUMMARY: AddressSanitizer: 868 byte(s) leaked in 8 allocation(s).
When I disable Asan linkage order verification test qmltestrunner passes tests, but my leak is not detected:
> ASAN_OPTIONS=verify_asan_link_order=false qmltestrunner -import ../../../../build/MyLib/qmlmodules/ -platform offscreen
is 0x12345********* Start testing of qmltestrunner *********
Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.2.1 20210110), debian 11
PASS : qmltestrunner::TestIoauExample::initTestCase()
PASS : qmltestrunner::TestIoauExample::test_ioau_example()
PASS : qmltestrunner::TestIoauExample::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 2ms
********* Finished testing of qmltestrunner *********
qml source file with TestCase i am trying to run:
import QtQuick 2.0
import QtTest 1.2
import MyLibTester 0.1
Item {
TestCase {
name: "TestMyLibExample"
when: heightmapGenerator.ready
function test_mylib_example() {
verify(heightmapGenerator.ready)
}
}
resources: HeightmapGenerator {
id: heightmapGenerator
property bool loaded: false
property bool ready: false
Component.onCompleted: {
ready = true
loaded = true
}
}
}
a leak that I am trying to introduce and which is not detected when running qmltestrunner
--- a/mylib/qmlmodules/MyLibTester/src/HeightmapGenerator.cpp
+++ b/mylib/qmlmodules/MyLibTester/src/HeightmapGenerator.cpp
## -9,6 +9,8 ##
+#include "../tests/leak_tst.h"
namespace mylib
{
namespace MyLibTester
## -35,10 +37,13 ## static float cubicSpline(float x)
}
-
HeightmapGenerator::HeightmapGenerator(QObject* parent)
: QObject(parent)
{
+ auto test_leak = leak_test(500);
+ std::cout << "is "
+ << (test_leak == reinterpret_cast<bool*>(0x12345) ? "NOT" : "")
+ << "0x12345";
}
However, when I compile a simple app with the same leak introduced sanitizer is able to detect the leak:
//leak_tst.h
#ifndef leak_tst
#define leak_tst
bool* leak_test(int n)
{
return new bool[n];
}
#endif
///leak_tst.cpp
#include "leak_tst.h"
#include <iostream>
int main()
{
auto test_leak = leak_test(500);
std::cout << "is "
<< (test_leak == reinterpret_cast<bool*>(0x12345) ? "NOT" : "")
<< "0x12345";
return 0;
}
> leak_tst
is 0x12345
=================================================================
==419360==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 500 byte(s) in 1 object(s) allocated from:
#0 0x7fb68db4b7a7 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
#1 0x55b8687e3132 in main (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/tests/test-leak+0x1132)
#2 0x7fb68d734d09 in __libc_start_main ../csu/libc-start.c:308
SUMMARY: AddressSanitizer: 500 byte(s) leaked in 1 allocation(s).
I´m running a big code with lots of MYSQL database access that and I´m noticing a memory leak that consumes the whole server memory after one day running.
By isolating pieces I end up with the following test code:
#include <iostream>
#include <string>
#include <sstream>
#include <thread>
#include <chrono>
#include "mysql_connection.h"
#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
int main() {
try
{
sql::Driver *driver = NULL;
sql::Connection *connection = NULL;
driver = get_driver_instance();
connection = driver->connect("tcp://127.0.0.1:3306", "user", "pass");
connection->setSchema("myschema");
connection->setAutoCommit(true);
while (true)
{
std::string sql("INSERT INTO tablename ('field1', 'field2', 'field3') VALUES ('1', '2', '3')");
sql::Statement *stmt;
stmt = connection->createStatement();
stmt->execute(sql.c_str());
stmt->close();
std::cout << sql << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
catch (std::exception& ex)
{
std::cout << "Error occurred: " << std::endl;
std::cout << ex.what() << std::endl;
}
catch(...)
{
std::cout << "Unknown failure occurred." << std::endl;
}
}
As the main while runs, the process memory usage starts increasing like 1Mb per minute.
Using valgrid --leak-check=full I got the following result after hitting to exit from the main loop:
==11988== Process terminating with default action of signal 2 (SIGINT)
==11988== at 0x57F5D6D: recv (recv.c:29)
==11988== by 0x5B1B022: vio_read (in /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.1.0)
==11988== by 0x5B1B0A4: vio_read_buff (in /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.1.0)
==11988== by 0x5AFA702: ??? (in /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.1.0)
==11988== by 0x5AFA976: ??? (in /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.1.0)
==11988== by 0x5AFB6A3: my_net_read (in /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.1.0)
==11988== by 0x5AF151C: cli_safe_read (in /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.1.0)
==11988== by 0x5AF2A15: ??? (in /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.1.0)
==11988== by 0x5AF43A5: mysql_real_query (in /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.1.0)
==11988== by 0x4F15BEE: sql::mysql::NativeAPI::LibmysqlStaticProxy::real_query(st_mysql*, char const*, unsigned long) (in /usr/lib/libmysqlcppconn.so.7.1.1.3)
==11988== by 0x4F176F5: sql::mysql::NativeAPI::MySQL_NativeConnectionWrapper::query(sql::SQLString const&) (in /usr/lib/libmysqlcppconn.so.7.1.1.3)
==11988== by 0x4F10579: sql::mysql::MySQL_Statement::do_query(sql::SQLString const&) (in /usr/lib/libmysqlcppconn.so.7.1.1.3)
==11988==
==11988== HEAP SUMMARY:
==11988== in use at exit: 333,479 bytes in 1,869 blocks
==11988== total heap usage: 5,566 allocs, 3,697 frees, 1,100,674 bytes allocated
==11988==
==11988== 144,880 bytes in 1,811 blocks are definitely lost in loss record 42 of 42
==11988== at 0x4C2C12F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11988== by 0x4EC4334: sql::mysql::MySQL_Connection::createStatement() (in /usr/lib/libmysqlcppconn.so.7.1.1.3)
==11988== by 0x4011B7: main (test.cpp:32)
==11988==
==11988== LEAK SUMMARY:
==11988== definitely lost: 144,880 bytes in 1,811 blocks
==11988== indirectly lost: 0 bytes in 0 blocks
==11988== possibly lost: 0 bytes in 0 blocks
==11988== still reachable: 188,599 bytes in 58 blocks
==11988== suppressed: 0 bytes in 0 blocks
==11988== Reachable blocks (those to which a pointer was found) are not shown.
==11988== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==11988==
==11988== For counts of detected and suppressed errors, rerun with: -v
==11988== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
So, am I doing something wrong or is this a know MYSQL bug ? How to fix the code and/or mySQL ?
I´m running MySql 5.6.27-0ubuntu1 running on Ubuntu 15.10.
You're missing delete stmt. Better would be to use a std::unique_ptr<sql::Statement>.
(See e.g. https://dev.mysql.com/doc/connector-cpp/en/connector-cpp-examples-query.html)
Try
std::unique_ptr<sql::Statement> stmt(connection->CreateStatement());
I have a toy project which is a game engine. It uses SDL2 and C++11. In the code below I tried to make an objects which cleans memory after itself in destructor. But something goes wrong and some memory leaks. What am I doing wrong?
Example is minimal working code which triggers a leak. I suppose it works like this:
Instance of class Game upon costruction creates instances of SDLEngine and Graphics (in this order), both of which allocates some memory too. When game object is destroyed it calls destructors of Graphics and SDLEngine (in this order). If I add some printing in both of this destructors they are printed in the needed order. But valgrind thinks that memory allocated by SDL_Init() and SDL_CreateWindow() are leaked.
Edit: it is probably valgrind behaviour. I saw a similar question and similar warnings in the pretty basic SDL example: Why does valgrind say basic SDL program is leaking memory?
src/leak-test.cpp:
#include <SDL2/SDL.h>
#include <stdexcept>
class SDLEngine {
public:
SDLEngine() {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::runtime_error("SDL_Init"); // line 7
}
if (SDL_ShowCursor(SDL_DISABLE) < 0) {
throw std::runtime_error("SDL_ShowCursor");
}
}
~SDLEngine() {
SDL_Quit();
}
};
class Graphics {
public:
Graphics() :
sdlWindow{SDL_CreateWindow(
"LeakTest",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
320, 240,
0
)} // line 27
{
if (sdlWindow == nullptr) {
throw std::runtime_error("SDL_CreateWindow");
}
}
~Graphics() {
SDL_DestroyWindow(sdlWindow);
}
Graphics(const Graphics&)=delete;
Graphics& operator=(const Graphics&)=delete;
private:
SDL_Window *sdlWindow;
};
class Game {
public:
Game() :
sdlEngine_(),
graphics_() // line 46
{
SDL_Event event;
bool running{true};
while (running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
running = false;
break;
default:
break;
}
}
}
}
~Game() {}
private:
const SDLEngine sdlEngine_;
Graphics graphics_;
};
int main() {
Game game; // line 70
return 0;
}
Makefile:
CXX := g++
MKDIR := mkdir -p
CXXFLAGS += `pkg-config --cflags sdl2 SDL2_image`
CXXFLAGS += -Wall -Werror -Wextra -Weffc++ -pedantic -std=c++0x -g
LDFLAGS += `pkg-config --libs sdl2 SDL2_image`
PROG := bin/leak-test
OBJS := $(patsubst src/%.cpp,obj/%.o, $(wildcard src\/*.cpp))
# escaped to fool SO parser ^
.PHONY: all clean
all: build
build: $(PROG)
clean:
rm -rf $(PROG) $(OBJS)
$(PROG): obj/leak-test.o
$(PROG):
#$(MKDIR) $(dir $#)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $# $^
obj/%.o : src/%.cpp
#$(MKDIR) $(dir $#)
$(CXX) $(CXXFLAGS) -c -MD -o $# $<
Valgrind output:
host:cave-test » valgrind --leak-check=full ./bin/leak-test
==28815== Memcheck, a memory error detector
==28815== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==28815== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==28815== Command: ./bin/leak-test
==28815==
==28815==
==28815== HEAP SUMMARY:
==28815== in use at exit: 66,235 bytes in 506 blocks
==28815== total heap usage: 19,844 allocs, 19,338 frees, 44,931,400 bytes allocated
==28815==
==28815== 20 bytes in 2 blocks are definitely lost in loss record 7 of 101
==28815== at 0x4C274A0: malloc (vg_replace_malloc.c:291)
==28815== by 0x5BF8829: strdup (strdup.c:42)
==28815== by 0x7203666: ??? (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815== by 0x7204474: _XimSetICValueData (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815== by 0x71FFA69: _XimLocalCreateIC (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815== by 0x71E6044: XCreateIC (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815== by 0x5111CD2: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x51120F7: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x51055FF: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x510540F: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x507048E: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x400D6E: SDLEngine::SDLEngine() (leak-test.cpp:7)
==28815==
==28815== 20 bytes in 2 blocks are definitely lost in loss record 8 of 101
==28815== at 0x4C274A0: malloc (vg_replace_malloc.c:291)
==28815== by 0x5BF8829: strdup (strdup.c:42)
==28815== by 0x7203666: ??? (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815== by 0x7204474: _XimSetICValueData (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815== by 0x71FFA69: _XimLocalCreateIC (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815== by 0x71E6044: XCreateIC (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815== by 0x5111CD2: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x51120F7: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x51055FF: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x400F11: Graphics::Graphics() (leak-test.cpp:27)
==28815== by 0x401012: Game::Game() (leak-test.cpp:46)
==28815== by 0x400D31: main (leak-test.cpp:70)
==28815==
==28815== 104 bytes in 1 blocks are definitely lost in loss record 60 of 101
==28815== at 0x4C274A0: malloc (vg_replace_malloc.c:291)
==28815== by 0xD330A11: ??? (in /usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1.2.0)
==28815== by 0xD309600: ??? (in /usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1.2.0)
==28815== by 0xD305E7A: ??? (in /usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1.2.0)
==28815== by 0xD30660F: glXChooseVisual (in /usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1.2.0)
==28815== by 0x510ED0E: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x510EF40: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x5103B65: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x51056FB: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x510540F: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x507048E: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815== by 0x400D6E: SDLEngine::SDLEngine() (leak-test.cpp:7)
==28815==
==28815== LEAK SUMMARY:
==28815== definitely lost: 144 bytes in 5 blocks
==28815== indirectly lost: 0 bytes in 0 blocks
==28815== possibly lost: 0 bytes in 0 blocks
==28815== still reachable: 66,091 bytes in 501 blocks
==28815== suppressed: 0 bytes in 0 blocks
==28815== Reachable blocks (those to which a pointer was found) are not shown.
==28815== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==28815==
==28815== For counts of detected and suppressed errors, rerun with: -v
==28815== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 7 from 3)
It turns out this leak belongs to layer under SDL, in the X11 library. This bug is very old and known by SDL devs. See this bugzilla thread: https://bugzilla.libsdl.org/show_bug.cgi?id=2086
I close the question now.
When attempting to use getopt multiple times, the error I get in valgrind is Invalid read of size 1. The error only occurs when doing something like this:
ls -a -b
ls -a -b
Therefore I'm assuming the problem is with reusing the getopt functions.
Command.h
class Command {
protected:
// for use in getopt
int c = 0;
// name of command
char* name;
public:
Command(const char* nname) : name((char*)nname) { }
virtual ~Command() { };
virtual void process_args(int argc, char* argv[]) = 0;
virtual char* get_name() const {
return name;
}
};
ls.h is simply this wrapped in a class:
class ls : public Command {
public:
ls() : Command("ls") { }
~ls() { }
void process_args(int input_argc, char* input_argv[]) {
int verbose_flag = 0;
struct option long_options[] =
{
/* These options set a flag. */
{"verbose", no_argument, &verbose_flag, 1},
{"brief", no_argument, &verbose_flag, 0},
/* These options don't set a flag.
We distinguish them by their indices. */
{"add", no_argument, 0, 'a'},
{"append", no_argument, 0, 'b'},
{"delete", required_argument, 0, 'd'},
{"create", required_argument, 0, 'c'},
{"file", required_argument, 0, 'f'},
{0, 0, 0, 0}
};
while (1) {
// removed static and moved struct outside
// everything else is the same
}
}
};
main.cpp
std::vector<std::unique_ptr<Command>> commands;
commands.push_back(std::unique_ptr<Command>(new ls()));
commands.push_back(std::unique_ptr<Command>(new shibe()));
while (true) {
std::string input;
std::getline(std::cin, input);
if (input == "quit")
break;
std::istringstream iss(input);
std::vector<std::string> args;
std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter(args));
int input_argc = args.size();
char* input_argv[input_argc];
for (int i = 0; i < args.size(); i++) {
input_argv[i] = (char*)args[i].c_str();
}
for (int i = 0; i < commands.size(); i++) {
if (strcmp(input_argv[0], commands[i]->get_name()) == 0) {
commands[i]->process_args(input_argc, input_argv);
break;
}
}
}
Valgrind output is:
ls -a -b
--30624-- REDIR: 0x375e8812d0 (strlen) redirected to 0x480155c (_vgnU_ifunc_wrap per)
--30624-- REDIR: 0x375e87f810 (__GI_strchr) redirected to 0x4a07b30 (__GI_strchr )
option -a
option -b
ls -a -b
==30624== Invalid read of size 1
==30624== at 0x375E8CDFDC: _getopt_internal_r (in /lib64/libc-2.12.so)
==30624== by 0x375E8CF1EA: _getopt_internal (in /lib64/libc-2.12.so)
==30624== by 0x375E8CF2D2: getopt_long (in /lib64/libc-2.12.so)
==30624== by 0x401E1C: ls::process_args(int, char**) (ls.h:31)
==30624== by 0x4019CB: main (main.cpp:36)
==30624== Address 0x513e5da is 26 bytes inside a block of size 27 free'd
==30624== at 0x4A05FD6: operator delete(void*) (vg_replace_malloc.c:480)
==30624== by 0x4CCADFE: std::basic_string<char, std::char_traits<char>, std:: allocator<char> >::~basic_string() (basic_string.h:538)
==30624== by 0x403AA5: void std::_Destroy<std::string>(std::string*) (stl_con struct.h:93)
==30624== by 0x403855: void std::_Destroy_aux<false>::__destroy<std::string*> (std::string*, std::string*) (stl_construct.h:103)
==30624== by 0x403466: void std::_Destroy<std::string*>(std::string*, std::st ring*) (stl_construct.h:126)
==30624== by 0x402DE6: void std::_Destroy<std::string*, std::string>(std::str ing*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==30624== by 0x402878: std::vector<std::string, std::allocator<std::string> > ::~vector() (stl_vector.h:415)
==30624== by 0x401A03: main (main.cpp:26)
==30624==
--30624-- REDIR: 0x375e8846b0 (mempcpy) redirected to 0x4a09f80 (mempcpy)
non-option input_argv-elements: s b
quit
--30624-- REDIR: 0x375e87b6d0 (free) redirected to 0x4a06369 (free)
==30624==
==30624== HEAP SUMMARY:
==30624== in use at exit: 0 bytes in 0 blocks
==30624== total heap usage: 36 allocs, 36 frees, 916 bytes allocated
==30624==
==30624== All heap blocks were freed -- no leaks are possible
==30624==
==30624== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)
==30624==
Linux's manpage on getopt() makes it clear how to reset getopt() (which unfortuantely uses a number of global variables to communicate with the caller and maintain state):
The variable optind is the index of the next element to be processed in argv. The system initializes this value to 1. The caller can reset it to 1 to restart scanning of the same argv, or when scanning a new argument vector.
For a school project, we have to send big files across the network., we must use Poco::XML for our data.
After our files are send over the network, it appears that the memory does not free.
Here is an example for a file of ~9 Mb on the receiving part:
valgrind --leak-check=full --show-reachable=yes -v ourExecutable parms returns:
12,880,736 bytes in 37 blocks are definitely lost in loss record 101 of 101
at 0x4C2747E: operator new(unsigned long) (vg_replace_malloc.c:261)
by 0x5A3AC88: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.4.4/libstdc++.so.6.0.13)
by 0x5A3BC4A: std::string::_Rep::_M_clone(std::allocator<char> const&, unsigned long) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.4.4/libstdc++.so.6.0.13)
by 0x5A3C1BB: std::string::reserve(unsigned long) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.4.4/libstdc++.so.6.0.13)
by 0x5A3C68E: std::string::append(std::string const&) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.4.4/libstdc++.so.6.0.13)
by 0x5202359: Poco::XML::Element::innerText() const (in /home/tomwij/IGS/trunk/Project/external/lib/libPocoXML.so.8)
by 0x4145BF: NodeProtocol::getChildNodeStrValue(Poco::XML::Element*, std::string) (NodeProtocol.cpp:82)
by 0x41544F: NodeProtocol::deserialize(std::string const&) (NodeProtocol.cpp:200)
by 0x40B088: Node::handleClientPacket(PriorityElement*) (Node.cpp:760)
by 0x40A04C: Node::handlePackets() (Node.cpp:574)
by 0x4078EA: Node::run() (Node.cpp:162)
by 0x40772D: Node::activate() (Node.cpp:138)
LEAK SUMMARY:
definitely lost: 12,888,036 bytes in 190 blocks
indirectly lost: 644,979 bytes in 1,355 blocks
possibly lost: 10,089 bytes in 27 blocks
still reachable: 306,020 bytes in 43 blocks
suppressed: 0 bytes in 0 blocks
The function which is right before Poco is
const string NodeProtocol::getChildNodeStrValue(Element * elem, string child)
{
Element* tempNode = elem->getChildElement(child);
XMLString result(tempNode->innerText());
string ret = string(fromXMLString(result));
result.clear();
return ret;
}
which calls
XMLString Element::innerText() const
{
XMLString result;
Node* pChild = firstChild();
while (pChild)
{
result.append(pChild->innerText());
pChild = pChild->nextSibling();
}
return result;
}
(Note that XMLString is std::string)
Why is the append of STL string leaking memory?
If I just assign instead of using the copy constructors it gives the same problem.
EDIT:
I'm using the latest stable GNU GCC 4.4.4 on Gentoo x64 (linux-2.6.34-gentoo-r12).
More functions from the call stack (stripped irrelevant big chunks of code / if structures):
Command * NodeProtocol::deserialize(const string & msg)
{
DOMParser xmlParser;
// Get the root node.
AutoPtr<Document> doc = xmlParser.parseString(msg);
AutoPtr<Element> rootElement = doc->documentElement();
string root = fromXMLString(rootElement->nodeName());
string name = getChildNodeStrValue(rootElement, "name");
string data = getChildNodeStrValue(rootElement, "data");
return new PutCommand(name, data);
}
and
void Node::handleClientPacket(PriorityElement * prio)
{
Command * command = NodeProtocol::deserialize(prio->fPacket);
// CUT: Access some properties of command, let the command execute.
delete command;
}
and
void Node::handlePackets()
{
PriorityElement * prio = fQueue->top();
fQueue->pop();
if (prio->fSource == kCLIENT)
handleClientPacket(prio);
else if (prio->fSource == kNODE)
handleNodePacket(prio);
delete prio;
}
where fQueue is:
priority_queue< PriorityElement*, vector<PriorityElement*>, ComparisonFunction >
I would make this a comment, but apparently I don't have the rep. Have you remembered to make the destructor for Command virtual? If name or data are fields of PutCommand rather than Command and the Command destructor is not virtual, they may not be freed properly when you delete command in handleClientPacket.