Обнаружение утечек памяти в Visual C ++ (Windows)

Я работаю над большим C ++ проектом под Visual Studio 2010 и думаю, что внутри есть утечки памяти. Я попробовал подход с включением crtdbg.h, но это не очень помогает, так как я не вижу, где произошла утечка. Определение new имеет 2 ловушки: во-первых, это нужно сделать в каждом файле cpp, который на самом деле не является опцией, а во-вторых, это разрывается, например, с. Увеличение. Использование new (nothrow) или всего, что использует бустеры «has_new_operator.h», ломает это. [РЕДАКТИРОВАТЬ: он не может скомпилироваться, так как переопределенное «новое» не имеет перегрузок для чего-то вроде «nothrow» или «магия наддува»] (если только не определено «новое» после всех заголовков наддува, включая заголовки, ссылающиеся на надстройку)

Последнее, но не менее важное: у меня есть синглтоны. Они реализованы с использованием подклассов шаблона singleton и статической переменной функции. Одним из них является контейнер конфигурации, в котором регистрируются настройки (пары строк и целых, которые затем сохраняются в картах). Поскольку дамп утечки памяти вызывается до освобождения экземпляра синглтона, я получаю огромное количество утечек для всех этих строк и сам синглтон.

Любой способ показать только реальные утечки или сделать его дампом после освобождения статического объекта?

Какие бесплатные инструменты могут справиться с этим делом?

0

Решение

Я использовал Visual Leak Detector с довольно положительными результатами. Он небольшой и аккуратный и может быть встроен в ваш проект (при условии, что у вас запущена конфигурация Debug) за считанные секунды:

https://vld.codeplex.com/

Если настройка выполнена правильно (что можно сделать с помощью установщика), вам нужно только

#include <vld.h>

в один из ваших .cpp файлов для каждого модуля — вот и все, заголовок будет делать ссылки для вас. Вам не нужно помещать это везде. Внутренне инструмент использует CrtDbg, поэтому для его работы необходимо запустить отладочную сборку.

Он дает вам отладчик или вывод текста после каждого запуска (если он настроен с использованием файла конфигурации), даже если он не запускается через отладчик. Это не самый мощный инструмент, но обычно он стоит несколько монет;)

РЕДАКТИРОВАТЬ: Существует возможность включить VLD также в не отладочных конфигурациях, определив VLD_FORCE_ENABLE перед включением заголовка. Но результаты могут быть закалены тогда.

РЕДАКТИРОВАТЬЯ попробовал новую установку VLD. Обратите внимание, что для компиляторов VS2013 должна использоваться версия v2.4rc2 (или любая другая версия v2.3). Версия v2.3 работает только до компиляторов VS2010.

После установки я создал новый проект и настроил мои каталоги include и library для включения соответствующих папок VLD. После этого я использовал следующий код для проверки отчетов о синглетонах в memleak (обратите внимание, что этот код не имеет смысла, он просто доказывает свою точку зрения):

#include <iostream>
#include <string>
#include <sstream>
#include <map>

// Uncomment this, if you want VLD to work in non-debug configurations
//#define VLD_FORCE_ENABLE

#include <vld.h>

class FooSingleton {
private:
std::map<std::string, std::string*>
_map;

FooSingleton() {
}

public:
static FooSingleton* getInstance(void) {
/* THIS WOULD CAUSE LEAKS TO BE DETECTED
SINCE THE DESTRUCTOR WILL NEVER BE CALLEd
AND THE MAP IS NOT CLEARED.
*/
// FooSingleton* instance = new FooSingleton;
// return instance;

static FooSingleton instance;
return &instance;
}

void addString(const std::string& val) {
_map.insert(std::make_pair(val, new std::string(val)));
}

~FooSingleton(void) {
auto it = _map.begin();
auto ite = _map.end();

for(; it != ite; ++it) {
delete it->second;
}
}
};

int main(int argc, char** argv) {
FooSingleton* fs = FooSingleton::getInstance();
for(int i = 0; i < 100; ++i) {
std::stringstream ss;
ss << i << "nth string.";
fs->addString(ss.str());
}

return 0;
}

С этим кодом VLD не сообщает о каких-либо утечках, потому что статическая авто-переменная в getInstance() будет уничтожен при выходе, а элементы на карте будут удалены. Тем не менее, это должно быть сделано, даже если это одноэлементный, иначе будут сообщены утечки. Но в этом случае:

Visual Leak Detector Version 2.3 installed.
Aggregating duplicate leaks.
Outputting the report to the debugger and to D:\dev\projects\tmp\memleak\memleak\memory_leak_report.txt
No memory leaks detected. Visual Leak Detector is now exiting.

Если код в getInstance() изменяется на закомментированную версию, затем синглтон никогда не очищается и сообщается о следующих утечках (среди прочих):

---------- Block 11 at 0x008E5928: 52 bytes ----------
Leak Hash: 0x973608A9 Count: 100
Call Stack:
c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory (36): memleak.exe!std::_Allocate<std::_Tree_nod<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::alloca + 0x15 bytes
c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory (187): memleak.exe!std::allocator<std::_Tree_nod<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::alloca + 0xB bytes
c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (560): memleak.exe!std::_Tree_val<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,s + 0xD bytes
c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (588): memleak.exe!std::_Tree_val<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,s + 0x8 bytes
c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (756): memleak.exe!std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std:: + 0x17 bytes
d:\dev\projects\tmp\memleak\memleak\main.cpp (33): memleak.exe!FooSingleton::addString + 0xA9 bytes
d:\dev\projects\tmp\memleak\memleak\main.cpp (51): memleak.exe!main + 0x37 bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (555): memleak.exe!__tmainCRTStartup + 0x19 bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (371): memleak.exe!mainCRTStartup
0x76BF919F (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0xE bytes
0x7739A22B (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x84 bytes
0x7739A201 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x5A bytes
Data:
C0 53 8E 00    30 67 8E 00    C0 53 8E 00    98 58 8E 00     .S..0g.. .S...X..
30 6E 74 68    20 73 74 72    69 6E 67 2E    00 CD CD CD     0nth.str ing.....
0C 00 00 00    0F 00 00 00    CD CD CD CD    48 56 8E 00     ........ ....HV..
01 00 CD CD

Вы можете ясно видеть Count: 100 для этого блока кода, который является правильным.

Я также отредактировал мой vld.ini файл в каталоге установки, чтобы включить следующий набор:

AggregateDuplicates = yes
ReportTo = both

Это гарантирует, что a) все повторяющиеся утечки объединяются в один отчет с счетчиком утечек (как указано выше, в противном случае было бы 100 записей), а другой — чтобы файл отчета был выгружен в каталог приложения.

Так что для синглетов это работает нормально, если вы используете статический подход с автоматическими переменными, который используете, и выполняете очистку в деструкторе.

РЕДАКТИРОВАТЬКроме того, инструментарий может быть отключен в определенных частях кода. Если приведенный выше код будет изменен следующим образом:

void addString(const std::string& val) {
VLDDisable();
_map.insert(std::make_pair(val, new std::string(val)));
VLDEnable();
}

Утечки будут никогда быть профилированным и не отслеженным.

2

Другие решения

Вы можете получить источник утечки памяти из crtdebug, он не поможет вам с распределением буста, если вы не скомпилируете boost (или любую библиотеку) таким же образом, но в остальном он покажет вам файл и строку размещения.

Вот как вы правильно используете crtdebug.h:

в верхней части вашего stdafx.h (или любой файл PCH) добавьте следующие строки:

#ifdef DEBUG
//must define both _CRTDBG_MAP_ALLOC and _CRTDBG_MAP_ALLOC_NEW
#define _CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC_NEW

#include <stdlib.h>
#include <crtdbg.h>
//if you won't use this macro you'll get all new as called from crtdbg.h
#define DEBUG_NEW   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#define new DEBUG_NEW
#endif

Теперь в начале вашего main или же winmain или любую точку входа в вашу программу добавьте следующие строки:

//register memory leak check at end of execution:
//(if you use this you won't need to use _CrtDumpMemoryLeaks at the end of your main)
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
//set report mode:
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

Теперь вот небольшой тест, который я сделал:

После новой консольной программы от VS10 под названием «тест»:
Мой stdafx.h:

#pragma once

#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC_NEW
#include <stdlib.h>
#include <crtdbg.h>

#define DEBUG_NEW   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#define new DEBUG_NEW
#endif

#include "targetver.h"
#include <stdio.h>
#include <tchar.h>

и мой test.cpp это:

#include "stdafx.h"void CheckMemoryLeak()
{
char *ptr=new char[100];
int n=900;
sprintf(ptr,"%d",n);
}

int _tmain(int argc, _TCHAR* argv[])
{
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

CheckMemoryLeak();

return 0;
}

Выход:

'tests.exe': Loaded 'C:\Users\shr\Documents\Visual Studio 2010\Projects\tests\Debug\tests.exe', Symbols loaded.
'tests.exe': Loaded 'C:\Windows\SysWOW64\ntdll.dll', Cannot find or open the PDB file
'tests.exe': Loaded 'C:\Windows\SysWOW64\kernel32.dll', Cannot find or open the PDB file
'tests.exe': Loaded 'C:\Windows\SysWOW64\KernelBase.dll', Cannot find or open the PDB file
'tests.exe': Loaded 'C:\Windows\SysWOW64\msvcr100d.dll', Symbols loaded.
Detected memory leaks!
Dumping objects ->
c:\users\shr\documents\visual studio 2010\projects\tests\tests\tests.cpp(9) : {97} client block at 0x01003288, subtype 0, 100 bytes long.
Data: <900             > 39 30 30 00 CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
The program '[1600] tests.exe: Native' has exited with code 0 (0x0).
0