MemoryLeakIndicator
Lade ...
Suche ...
Keine Treffer
MemoryLeakIndicator 0.0.2

Einleitung

Wenn es mal wieder Memory-Leaks gibt, einfach die DLL vom MemoryLeakIndicator zum Projekt linken.

In der Main bindet man MliForMain.h ein, in den Implementierungsdateien, in denen was mit new instantiiert wird, hinter allen anderen Includes nur die MilDebugNew.h.

Im jeweiligen Projekt muss die MilNewDelete.cpp mit kompiliert werden.

Motivation

Ich nutze mit Visual Studio unter Windows die crtdbg.h um Memory-Leaks angezeigt zu bekommen.

Siehe auch
https://docs.microsoft.com/de-de/visualstudio/debugger/finding-memory-leaks-using-the-crt-library?view=vs-2022

Leider werden der Dateiname und die Zeilennummer nur bei Benutzung von malloc, aber nicht bei Nutzung von new in der Ausgabe angezeigt. Bei Memory-Leaks verursacht durch ein new, werden nur die Adresse und die Daten angezeigt. Es ist zwar möglich, den Wertes aus den geschweiften Klammern der Variable _crtBreakAlloc zuzuweisen, in der Hoffnung, dass das Programm bei der Ausführung an der Stelle anhält, wo das nicht gelöschte Objekt instantiiert wird. Aber dieses ist leider nicht immer (z.B. bei DLLs) zuverlässig, da ggf. immer ein anderer Speicherort genommen wird.

Das Finden der verursachenden Anweisungen im Sourcecode gestaltet sich bisweilen schwierig. Gerne hätte hätte ich den Dateinamen und die Zeilennummer als Hinweis.

Implementierung

Wenn es mal wieder Memory-Leaks gibt, einfach die DLL vom MemoryLeakIndicator zum Projekt linken.

Zusätzlich muss im Projekt die Quellatei MilNewDelete.cpp (mit den überladenen Operator new und delete) mit kompiliert werden.

Das gilt auch für selbsterstellte DLLs, wenn dort Memory-Leaks erkannt werden sollen.

In der Main bindet man MliForMain.h ein, in den Implementierungsdateien, in denen was mit new instantiiert wird, hinter allen anderen Includes nur die MilDebugNew.h. Dieses umschließe ich mit einer Präprozessor-Anweisung wie z.B.:

#if defined _MSC_VER && defined _DEBUG_MEMORY_SPY
...
#endif
Zu beachten
Es ist nicht von Erfolg gekrönt, alle Sourcen von MemoryLeakIndicator in das eigene Projekt zu übernehmen, also die DLL nicht zu benutzen.

Beispiel

Im folgenden ein kleines Beispiel:

1 #if defined _MSC_VER && defined _DEBUG
2 // detect Memory-Leaks
3 #define _CRTDBG_MAP_ALLOC
4 #include <crtdbg.h>
5 #endif
6
7 #include <iostream>
8 #include <string>
9 #include <conio.h>
10 #if defined _MSC_VER && defined _DEBUG_MEMORY_SPY
11 #include <MliForMain.h>
12 #endif
13 //-----------------------------------------------------------------------------
14 class MyClass
15 {
16 private:
17 int * m_piT1;
18 public:
19 MyClass( int i )
20 {
21 m_piT1 = new int (i);
22 };
23 ~MyClass()
24 {
25 // Oh, m_piT1 forgot to delete
26 //delete m_piT1;
27 };
28 };
29 //-----------------------------------------------------------------------------
30 int main(int argc, char *argv[])
31 {
32 #if defined _MSC_VER && defined _DEBUG
33 // Displays memory leaks when there are any...
34 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
35 // Comment out next line with number from { } -> e.g..: "{173} normal block at 0x00D01C78, 10 bytes long."
36 //_crtBreakAlloc = 172;
37 // Is unfortunately not always (e.g. with DLLs) reliably, because if necessary other memory location.
38 #endif
39
40 #if defined _MSC_VER && defined _DEBUG_MEMORY_SPY
42 #endif
43
44 // Examples:
45 MyClass * t_pClass = new MyClass( 1 );
46 //delete t_pClass;
47
48 int * t_piLeak = new int( 2 );
49 //delete t_piLeak;
50
51 char * t_szLeak = new char[100];
52 //delete[] t_szLeak;
53
54 #if defined _MSC_VER && defined _DEBUG_MEMORY_SPY
55 // Display memory leaks
57 #endif
58 std::cout << "press any key . . .";
59 _getch();
60 return 0;
61 }
62 //-----------------------------------------------------------------------------
const TiOutput OUTPUT_TRACE
Definition MemoryLeakIndicator.h:186
void __mem_leak_indicator_export_dll finishMemoryMonitoring(MemSpy::TiOutput iOutput=MemSpy::OUTPUT_CONSOLE)
const TiOutput OUTPUT_CONSOLE
Definition MemoryLeakIndicator.h:173
__mem_leak_indicator_export_dll MemSpy::LeakMap * activateMemoryMonitoring(MemSpy::TbExceptionMode bExceptionMode=MemSpy::THROW)

Da der Funktion MemSpy::finishMemoryMonitoring(...) die Parameter MemSpy::OUTPUT_TRACE und MemSpy::OUTPUT_CONSOLE übergeben wurden, werden am Ende des Beispielprogramms die Memory-Leaks in der Konsole und im VisualStudio Ausgabefenster angezeigt.

Der Thread 15572 hat mit Code 0 (0x0) geendet.
Memory leaks detected! *
Memory leak in 1313691209200, file D:\Projekte\C++\Src\MemoryLeakIndicator\source\main.cpp, line 45
Memory leak in 1313691165808, file D:\Projekte\C++\Src\MemoryLeakIndicator\source\main.cpp, line 21
Memory leak in 1313691166000, file D:\Projekte\C++\Src\MemoryLeakIndicator\source\main.cpp, line 48
Memory leak in 1313691152224, file D:\Projekte\C++\Src\MemoryLeakIndicator\source\main.cpp, line 51
"MemoryLeakIndicatorDllTestd.exe" (Win32): "C:\Windows\System32\kernel.appcore.dll" geladen.
"MemoryLeakIndicatorDllTestd.exe" (Win32): "C:\Windows\System32\msvcrt.dll" geladen.
"MemoryLeakIndicatorDllTestd.exe" (Win32): "C:\Windows\System32\rpcrt4.dll" geladen.
Der Thread 10624 hat mit Code 0 (0x0) geendet.
Der Thread 17076 hat mit Code 0 (0x0) geendet.
Detected memory leaks!
Dumping objects ->
{172} normal block at 0x00000131DE182F60, 100 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
{171} normal block at 0x00000131DE186530, 4 bytes long.
Data: < > 02 00 00 00
{170} normal block at 0x00000131DE186470, 4 bytes long.
Data: < > 01 00 00 00
{169} normal block at 0x00000131DE190DF0, 8 bytes long.
Data: <pd 1 > 70 64 18 DE 31 01 00 00
Object dump complete.
Das Programm "[15928] MemoryLeakIndicatorDllTestd.exe" wurde mit Code 0 (0x0) beendet.
Zu beachten
Wenn man in modernen C++ Projekten Objekte erzeugt und ggf. weiterreicht, verwendet man möglichst keine einfachen Zeiger, sonder Smart-Pointer wie std::unique_ptr oder std::shared_ptr. Es sollte sich i.d.R. nach den Vorgaben von RAII gerichtet werden.
Siehe auch
https://docs.microsoft.com/de-de/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170
https://docs.microsoft.com/de-de/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=msvc-170

Einschränkungen

Mir gelang es nicht, den new (std::nothrow) zu überladen. Wenn es sich um eigenen Code handelt, muss dieser new operator entweder für diesen Zweck geändert werden (ohne std::nothrow), oder es wird MilDebugNew.h nicht eingebunden.
Ab und an, gerade in Multithread-Anwendungen, werden vom MemoryLeakIndicator unechte Memory-Leaks angezeigt. Diese sind erkennbar, wenn man die Anwendung im Debug-Modus unter Verwendung von crtdbg.h laufen lässt und in der Ausgabe im VisualStudio Ausgabefenster keine zusätzlich Ausgabe gibt wie z.B.:

Detected memory leaks!
Dumping objects →
{172} normal block at 0x00000131DE182F60, 100 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
...


Versuche mit MinGW und GCC sind bei mir auch fehlgeschlagen; geht also nur mit VisualStudio C++. Wenn jemand bereit ist, MemoryLeakIndicator an andere Compiler anzupassen, würde ich mich freuen.

Lizenz

MemoryLeakIndicator ist freie Software:

GNU Affero General Public License Nutzung
Sie können das Programm unter den Bedingungen der GNU Affero General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren, entweder gemäß Version 3 der Lizenz oder (nach Belieben) jeder späteren Version.
Details finden Sie in der GNU Affero General Public License: https://www.gnu.org/licenses/agpl-3.0.
Die Veröffentlichung dieses Programms erfolgt in der Hoffnung, daß es Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK.