project64/Source/Common/MemTest.cpp

307 lines
7.9 KiB
C++

#include <crtdbg.h>
#include "stdafx.h"
#ifdef _DEBUG
#pragma warning(disable:4786) //Disable std library warning
#pragma warning(disable:4530) //Disable std library warning
#include <string>
#include <malloc.h>
#include "MemTest.h"
#ifdef MEM_LEAK_TEST
typedef struct {
char File[300];
int line;
int size;
int order;
} DEBUG_LOCATION;
typedef std::map<void *, DEBUG_LOCATION> MEMLIST;
typedef MEMLIST::iterator MEMLIST_ITER;
class CMemList {
MEMLIST MemList;
HANDLE hSemaphone;
DWORD ThreadID;
enum INIT { Initialized = 123, NotInitialized };
INIT State;
int order;
public:
CMemList();
~CMemList();
void * AddItem ( size_t size, LPCSTR filename, int line );
void * AddItem ( size_t size, LPCSTR filename, int line, void * MemoryPtr );
void * ReAllocItem ( void * ptr, size_t size, LPCSTR filename, int line );
void removeItem ( void * ptr, bool FreePointer );
};
#include <shellapi.h> //Needed for ShellExecute
#pragma comment(lib, "shell32.lib") //Needed for ShellExecute
#undef new
#undef malloc
#undef realloc
#undef free
#undef VirtualAlloc
#undef VirtualFree
#ifndef MB_SERVICE_NOTIFICATION
#define MB_SERVICE_NOTIFICATION 0x00200000L
#endif
CMemList *MemList ( void ) {
static CMemList m_MemList;
return &m_MemList;
}
CMemList::CMemList() {
MemList.clear();
hSemaphone = CreateSemaphore(NULL, 1,1, NULL);
State = Initialized;
order = 0;
}
CMemList::~CMemList() {
int ItemsLeft = MemList.size();
if (ItemsLeft > 0) {
char path_buffer[_MAX_PATH], drive[_MAX_DRIVE] ,dir[_MAX_DIR];
char fname[_MAX_FNAME],ext[_MAX_EXT], LogFileName[_MAX_PATH];
GetModuleFileName(NULL,path_buffer,sizeof(path_buffer));
_splitpath( path_buffer, drive, dir, fname, ext );
_makepath( LogFileName, drive, dir, fname, "leak.csv" );
HANDLE hLogFile;
do
{
hLogFile = CreateFile(LogFileName,GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,
CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hLogFile == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_SHARING_VIOLATION) {
std::string Msg = std::string(LogFileName) + "\nCan not be opened for writing please close app using this file\n\nTry Again ?";
int Result = MessageBox(NULL,Msg.c_str(),"Memory Leak",MB_YESNO|MB_ICONQUESTION|MB_SETFOREGROUND | MB_SERVICE_NOTIFICATION);
if (Result == IDNO) {
break;
}
}
}
} while (hLogFile == INVALID_HANDLE_VALUE);
if (hLogFile != INVALID_HANDLE_VALUE)
{
SetFilePointer(hLogFile,0,NULL,FILE_BEGIN);
DWORD dwWritten;
char Msg[800];
_snprintf(Msg,sizeof(Msg),"Order, Source File, Line Number, Mem Size\r\n");
WriteFile( hLogFile,Msg,strlen(Msg),&dwWritten,NULL );
for (MEMLIST_ITER item = MemList.begin(); item != MemList.end(); item++)
{
_snprintf(Msg,sizeof(Msg),"%d,%s, %d, %d\r\n",
(*item).second.order,
(*item).second.File,
(*item).second.line,
(*item).second.size);
WriteFile( hLogFile,Msg,strlen(Msg),&dwWritten,NULL );
}
CloseHandle(hLogFile);
}
std::string Msg = std::string(fname) + std::string(ext) + "\n\nMemory Leaks detected\n\nOpen the Log File ?";
int Result = MessageBox(NULL,Msg.c_str(),"Memory Leak",MB_YESNO|MB_ICONQUESTION|MB_SETFOREGROUND| MB_SERVICE_NOTIFICATION);
if (Result == IDYES) {
ShellExecute(NULL,"open",LogFileName,NULL,NULL,SW_SHOW);
}
}
CloseHandle(hSemaphone);
hSemaphone = NULL;
State = NotInitialized;
}
void * CMemList::AddItem ( size_t size, LPCSTR filename, int line)
{
void *res = malloc(size);
if (res == NULL) {
_asm int 3
}
return AddItem(size,filename,line,res);
}
void * CMemList::AddItem ( size_t size, LPCSTR filename, int line, void * MemoryPtr )
{
__try {
if (State == Initialized && hSemaphone != NULL) {
DWORD CurrentThread = GetCurrentThreadId();
DWORD Result = WaitForSingleObject(hSemaphone,CurrentThread != ThreadID ? 30000: 0);
if (Result != WAIT_TIMEOUT) {
ThreadID = CurrentThread;
DEBUG_LOCATION info;
strncpy(info.File,filename,sizeof(info.File));
info.line = line;
info.size = size;
info.order = order++;
MemList.insert(MEMLIST::value_type(MemoryPtr,info));
long dwSemCount = 0;
ThreadID = -1;
ReleaseSemaphore(hSemaphone,1,&dwSemCount);
}
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
_asm int 3
}
return MemoryPtr;
}
void * CMemList::ReAllocItem ( void * ptr, size_t size, LPCSTR filename, int line)
{
void *res = realloc(ptr, size);
if (res == NULL)
{
_asm int 3
}
if (ptr != res) {
__try {
if (State == Initialized && hSemaphone != NULL) {
DWORD CurrentThread = GetCurrentThreadId();
DWORD Result = WaitForSingleObject(hSemaphone,CurrentThread != ThreadID ? 30000 : 0);
if (Result != WAIT_TIMEOUT) {
ThreadID = CurrentThread;
//Add new pointer
DEBUG_LOCATION info;
strncpy(info.File,filename,sizeof(info.File));
info.line = line;
info.size = size;
info.order = order++;
MemList.insert(MEMLIST::value_type(res,info));
//remove old pointer
MEMLIST_ITER item = MemList.find(ptr);
if (item != MemList.end()) {
MemList.erase(ptr);
}
long dwSemCount = 0;
ThreadID = -1;
ReleaseSemaphore(hSemaphone,1,&dwSemCount);
}
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
_asm int 3
}
}
return res;
}
void CMemList::removeItem (void * ptr, bool FreePointer )
{
if (FreePointer)
{
free(ptr);
}
__try {
if (State == Initialized && hSemaphone != NULL) {
DWORD CurrentThread = GetCurrentThreadId();
DWORD Result = WaitForSingleObject(hSemaphone,CurrentThread != ThreadID ? 30000 : 0);
if (Result != WAIT_TIMEOUT) {
ThreadID = CurrentThread;
MEMLIST_ITER item = MemList.find(ptr);
if (item != MemList.end()) {
MemList.erase(ptr);
}
long dwSemCount = 0;
ThreadID = -1;
ReleaseSemaphore(hSemaphone,1,&dwSemCount);
}
}
}__except(EXCEPTION_EXECUTE_HANDLER ){
_asm int 3
}
}
void MemTest_AddLeak(char* Comment) {
MemList()->AddItem(1,Comment,-1);
}
void* MemTest_malloc (size_t size, char* filename, int line) {
return MemList()->AddItem(size,filename,line);
}
void* MemTest_realloc (void* ptr, size_t size, char* filename, int line) {
return MemList()->ReAllocItem(ptr, size,filename,line);
}
void MemTest_free(void* ptr)
{
MemList()->removeItem(ptr,true);
}
void* operator new (size_t size, char* filename, int line)
{
return MemList()->AddItem(size,filename,line);
}
void* operator new (size_t size)
{
return MemList()->AddItem(size,"Unknown",0);
}
void* operator new [] (size_t size, char* filename, int line)
{
return MemList()->AddItem(size,filename,line);
}
void* operator new [] (size_t size)
{
return MemList()->AddItem(size,"Unknown",0);
}
void operator delete ( void* ptr)
{
MemList()->removeItem(ptr,true);
}
void operator delete[](void* ptr)
{
delete ptr;
}
LPVOID MemTest_VirtualAlloc(
LPVOID lpAddress, // region to reserve or commit
SIZE_T dwSize, // size of region
DWORD flAllocationType, // type of allocation
DWORD flProtect, // type of access protection
LPCSTR filename,
int line)
{
LPVOID ptr = VirtualAlloc(lpAddress,dwSize,flAllocationType,flProtect);
if (ptr && lpAddress == NULL && (flAllocationType & MEM_RESERVE) != 0)
{
MemList()->AddItem(dwSize,filename,line,ptr);
}
return ptr;
}
BOOL MemTest_VirtualFree( LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType )
{
if ((dwFreeType & MEM_RELEASE) != 0)
{
MemList()->removeItem(lpAddress,false);
}
return VirtualFree(lpAddress,dwSize,dwFreeType);
}
#endif
#endif