My first public solution

Started by
24 comments, last by sasho648 11 years, 6 months ago
So this actually is my first solution I made tongue.png . It is c++ new\delete memory leaks finder. It does an memory check at the end of the program and if some object created with new wasn't deleted it post a message with the object line and source file name. It use an a tree for collecting runtime memory actions. Here is the .cpp and .h file. I will be happy for any opinion and advice about this solution.

MemoryHandler.cpp

[source]#include <iostream>
#include <windows.h>
#include <stdio.h>

#ifdef DEBUG
struct MemoryAllocTree {
char* File;
unsigned int Line;
MemoryAllocTree* next;
void* MemoryPointer;
} * _gpAllocTree(0) ;
MemoryAllocTree** ppCurAllocTree(&_gpAllocTree);
unsigned int _gCurLine;
char* _gCurFile;
#endif

void* operator new(size_t size)
{
void* pMem(malloc(size ? size : 1));
if(!pMem)
{
#ifdef WIN32_MEM_LOG
MessageBoxA(0, "No more memory to use. The app will exit now!", "Error!", MB_OK | MB_ICONERROR);
#endif
#ifndef WIN32_MEM_LOG
std::cout << "No more memory to use. The app will exit now!" << "Error!" << std::endl;
#endif
exit(1);
}
#ifdef DEBUG
(*ppCurAllocTree)=(MemoryAllocTree*)malloc(sizeof(MemoryAllocTree));
if(!(*ppCurAllocTree))
{
#ifdef WIN32_MEM_LOG
MessageBoxA(0, "No more memory to use for the debug version of MemoryHandler. Try compiling the app \
in release mode. It will exit now!", "Error!", MB_OK | MB_ICONERROR);
#endif
#ifndef WIN32_MEM_LOG
std::cout << "No more memory to use for the debug version of MemoryHandler. Try compiling the app \
in release mode. It will exit now!" << "Error!" << std::endl;
#endif
exit(1);
}
(*ppCurAllocTree)->Line=_gCurLine;
(*ppCurAllocTree)->File=_gCurFile;
(*ppCurAllocTree)->MemoryPointer=pMem;
(*ppCurAllocTree)->next=0;
ppCurAllocTree=&(*ppCurAllocTree)->next;
#endif
return pMem;
}

void* operator new[](size_t size)
{
return operator new(size);
}

void operator delete[](void* pMem)
{
operator delete(pMem);
}

void operator delete(void* pMem)
{
if(!pMem) return;
#ifdef DEBUG
MemoryAllocTree* sourc(_gpAllocTree);
while(sourc&&sourc->MemoryPointer!=pMem)
sourc=sourc->next;
if(!sourc)
{
#ifdef DEBUG
#ifdef WIN32_MEM_LOG
char str[80];
sprintf(str, "Invalid delete call at File: %s Line: %d", _gCurFile,
_gCurLine);
MessageBoxA(0, str, "Warning!", MB_OK | MB_ICONWARNING);
#endif
#ifndef WIN32_MEM_LOG
std::cout << "Invalid delete call at File: " << _gCurFile << " Line: " << _gCurLine << " Warning!" << std::endl;
#endif
#endif
return;
}
sourc->MemoryPointer=0;
#endif
free(pMem);
return;
}

#ifdef DEBUG
void EndOfProgramMemCheck() {
MemoryAllocTree* pCurAllocTree(_gpAllocTree);
while(pCurAllocTree) {
if(pCurAllocTree->MemoryPointer) {
#ifndef WIN32_MEM_LOG
std::cout << "Object at line: " << pCurAllocTree->Line << " in file: "
<< pCurAllocTree->File << " wasn't deleted!" << std::endl;
#endif
#ifdef WIN32_MEM_LOG
char str[80];
sprintf(str, "Object at line: %d in file: %s wasn't deleted!", pCurAllocTree->Line,
pCurAllocTree->File);
MessageBoxA(0, str, "Memory leaked!", MB_OK | MB_ICONWARNING);
#endif
}
MemoryAllocTree* cach(pCurAllocTree);
pCurAllocTree=pCurAllocTree->next;
free(cach);
}
}
#endif[/source]


MemoryHandler.h

[source]#pragma once

/*Sasho 648 Memory Handle functions and memory leaks finder
- define DEBUG to active it and add at the function EndOfProgramMemCheck()
before exit your app. Define a WIN32_MEM_LOG to active an Win32 Messages.
Type MEM_FUNC_CALL at the line begin where new\delete \[] function is called.
Example: MEM_FUNC_CALL char* ptr = new char[50];
*/

#ifdef DEBUG
extern unsigned int _gCurLine;
extern char* _gCurFile;
#endif

#ifdef DEBUG
#define MEM_FUNC_CALL _gCurLine=__LINE__; _gCurFile=(char*)__FILE__;
#endif

#ifndef DEBUG
#define MEM_FUNC_CALL
#endif

void* operator new(size_t size) ;
void* operator new[](size_t size) ;
void operator delete[](void* pMem) ;
void operator delete(void* pMem) ;

#ifdef DEBUG
void EndOfProgramMemCheck() ;
#endif
[/source]


You need to make compiling debug define (DEBUG) at the compiler options and for win32 message posting you need to define WIN32_MEM_LOG. Before each new\delete call you need to type MEM_FUNC_CALL.

Here is a simple code using my solution:

[source]class My {
public:
My() {std::cout<<"My::My()"<<std::endl;}
~My() {std::cout<<"My::~My()"<<std::endl;}
} ;

int main() {
MEM_FUNC_CALL My* ptr=new My[23];
MEM_FUNC_CALL delete[] ptr;
MEM_FUNC_CALL delete ptr;
size_t* My_Size_T=new size_t;
My_Size_T=(size_t*)0xbff7ffff;
delete My_Size_T;
#ifdef DEBUG
EndOfProgramMemCheck();
#endif
system("PAUSE");

return 0;
}[/source]


Here will print warning messages for invalid delete call (2) and a memory leak.
Advertisement
[font=trebuchet ms,helvetica,sans-serif]Funny how i just read about comments and the next program i see hasnt got anybiggrin.png[/font][font=trebuchet ms,helvetica,sans-serif] biggrin.png[/font][font=trebuchet ms,helvetica,sans-serif] i skimmed over i tbut i cant understand whats going on exactly(not because of the comments!) because im still going through the c++ documentation.[/font]
extern unsigned int _gCurLine;
extern char* _gCurFile;
#define MEM_FUNC_CALL _gCurLine=__LINE__; _gCurFile=(char*)__FILE__;[/quote]

skijl.png

For instance, try the following code:

#include "MemoryHandler.h"
#include <iostream>

class A { };

class B {
public:
B() { MEM_FUNC_CALL a = new A(); }
~B() { MEM_FUNC_CALL delete a; }

private:
B( const B& );
B& operator= ( const B& );

private:
A* a;
};

int main( int, char*[] ) {
B* b = 0;
if ( false )
MEM_FUNC_CALL b = new B;

EndOfProgramMemCheck();
}


You can also override new and new[] to include additional parameters and use a macro to hijack all calls to new and delete to call your overloaded version, without needing to remember to use MEM_FUNC_CALL. (Though, you do need to make sure that the macros are defined in each compilation unit.) Take a look at a demonstration of memory leak detection for Visual Studio on MSDN. It should give you some ideas.

Also:
Those aren't trees, those are linked lists :|

  • Identifiers starting with an underscore in the global scope are reserved for the implementation, so I wouldn't prefix globals with "_". Namespaces are your friend.
  • <stdio.h> is a C header. The C++ equivalent is <cstdio>. I'm actually not entirely sure why you include it.
  • You should be reporting errors to std::cerr, not std::cout.
  • Don't exit() when an allocation fails. Let the user determine if it's fatal. Throw a std::bad_alloc exception, like the normal new operator does.
  • Any planned future support for multithreading (it'll wreck havoc with your current global state)?
  • It would be useful if it didn't call any Windows specific functions so that it would be cross platform. Maybe output results/leaks to a log file?


Overall, it's a cool idea, and I think with some changes suggested by fastcall22 and me (and others who contribute), it could be a cool little project.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
[source]MessageBoxA(0, str, "Memory leaked!", MB_OK | MB_ICONWARNING);[/source]


You make this call multiple times. Don't. Just use 'MessageBox'. The reason that there's a macro defining the name is for character width (I think).

[quote name='sasho648' timestamp='1347299520' post='4978639'][source]MessageBoxA(0, str, "Memory leaked!", MB_OK | MB_ICONWARNING);[/source]


You make this call multiple times. Don't. Just use 'MessageBox'. The reason that there's a macro defining the name is for character width (I think).
[/quote]
MessageBoxA is technically the correct function to call for narrow width strings, which is what he has. If you use MessageBox you also have to, perhaps unnecessarily for cases like this, decorate the string to ensure that the string is actually compatible with the function that the macro expands to.

Not saying one or the other is better, just that the macro needs more support code, or unnecessary support code depending on your point of view, to function properly.
Thanks for the advices!
@fastcall22 I try adding the new operator additional parameters but it have ambigouos meaning. Here is what I mean:
[source lang="cpp"]void* operator new(size_t size, const char* szFile=__FILE__, unsigned int nLineNo=__LINE__) _GLIBCXX_THROW (std::bad_alloc)
__attribute__((__externally_visible__));[/source]

The error message is for the ambigouos function meaning (void* operator new(std::size_t) is the other). I tried using namespace to fix this but I think this is impossible.
Actually, your new operator is amibouos because of the default parameters. It now has the same signature as the regular new operator (the first parameter is filled by the compiler). Leave out the default parameters and you'll be fine. Then use macros (yeah, I know... bad time ;) ) to replace all occurences of new with your version:

void* operator new(size_t size, const char* szFile, unsigned int nLineNo);
#define MY_NEW new(__FILE__, __LINE__)
#define new MY_NEW
Yeah I do this and fix the problem minutes ago (forgoting to edit the post) but the problem now is that I can't find I way to do the same with the delete operator:

#define delete delete ( __FILE__ , __LINE__ ) //for my own delete function
//void operator delete(void* pMem, const char* szFile, unsigned int nLineNo)


This define doesn't work. I need the line and file for this function because it will output an error when you wrong call the function.
You need to get rid of the space between delete and (.

This topic is closed to new replies.

Advertisement