412 lines
8.9 KiB
C++
412 lines
8.9 KiB
C++
//Anything above this #include will be ignored by the compiler
|
|
#include "../qcommon/exe_headers.h"
|
|
|
|
#pragma warning( disable : 4786)
|
|
#include "client.h"
|
|
|
|
#include <windows.h>
|
|
#include "..\smartheap\smrtheap.h"
|
|
#if !defined(__Q_SHARED_H)
|
|
#include "../game/q_shared.h"
|
|
#endif
|
|
#if !defined(_QCOMMON_H_)
|
|
#include "../qcommon/qcommon.h"
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <map>
|
|
|
|
using namespace std;
|
|
|
|
#if MEM_DEBUG
|
|
#include "..\smartheap\heapagnt.h"
|
|
|
|
static const int maxStack=2048;
|
|
static int TotalMem;
|
|
static int TotalBlocks;
|
|
static int nStack;
|
|
static char StackNames[maxStack][256];
|
|
static int StackSize[maxStack];
|
|
static int StackCount[maxStack];
|
|
static int StackCache[48];
|
|
static int StackCacheAt=0;
|
|
static int CheckpointSize[1000];
|
|
static int CheckpointCount[1000];
|
|
|
|
#define _FASTRPT_
|
|
|
|
cvar_t *mem_leakfile;
|
|
cvar_t *mem_leakreport;
|
|
|
|
MEM_BOOL MEM_CALLBACK MyMemReporter2(MEM_ERROR_INFO *info)
|
|
{
|
|
static char buffer[10000];
|
|
if (!info->objectCreationInfo)
|
|
return 1;
|
|
info=info->objectCreationInfo;
|
|
int idx=info->checkpoint;
|
|
if (idx<0||idx>=1000)
|
|
{
|
|
idx=0;
|
|
}
|
|
CheckpointCount[idx]++;
|
|
CheckpointSize[idx]+=info->argSize;
|
|
//return 1;
|
|
dbgMemFormatCall(info,buffer,9999);
|
|
if (strstr(buffer,"ntdll"))
|
|
return 1;
|
|
if (strstr(buffer,"CLBCATQ"))
|
|
return 1;
|
|
int i;
|
|
TotalBlocks++;
|
|
if (TotalBlocks%1000==0)
|
|
{
|
|
char mess[1000];
|
|
sprintf(mess,"%d blocks processed\n",TotalBlocks);
|
|
OutputDebugString(mess);
|
|
}
|
|
for (i=strlen(buffer);i>0;i--)
|
|
{
|
|
if (buffer[i]=='\n')
|
|
break;
|
|
}
|
|
if (!i)
|
|
return 1;
|
|
buffer[i]=0;
|
|
char *buf=buffer;
|
|
while (*buf)
|
|
{
|
|
if (*buf=='\n')
|
|
{
|
|
buf++;
|
|
break;
|
|
}
|
|
buf++;
|
|
}
|
|
char *start=0;
|
|
while (*buf)
|
|
{
|
|
while (*buf==' ')
|
|
buf++;
|
|
start=buf;
|
|
while (*buf!=0&&*buf!='\n')
|
|
buf++;
|
|
if (*start)
|
|
{
|
|
if (*buf)
|
|
{
|
|
*buf=0;
|
|
buf++;
|
|
}
|
|
if (strlen(start)>255)
|
|
start[255]=0;
|
|
if (strstr(start,"std::"))
|
|
{
|
|
// start=0;
|
|
continue;
|
|
}
|
|
if (strstr(start,"Malloc"))
|
|
{
|
|
start=0;
|
|
continue;
|
|
}
|
|
if (strstr(start,"FS_LoadFile"))
|
|
{
|
|
start=0;
|
|
continue;
|
|
}
|
|
if (strstr(start,"CopyString"))
|
|
{
|
|
start=0;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!start||!*start)
|
|
{
|
|
start="UNKNOWN";
|
|
}
|
|
|
|
for (i=0;i<48;i++)
|
|
{
|
|
if (StackCache[i]<0||StackCache[i]>=nStack)
|
|
continue;
|
|
if (!strcmpi(start,StackNames[StackCache[i]]))
|
|
break;
|
|
}
|
|
if (i<48)
|
|
{
|
|
StackSize[StackCache[i]]+=info->argSize;
|
|
StackCount[StackCache[i]]++;
|
|
}
|
|
else
|
|
{
|
|
for (i=0;i<nStack;i++)
|
|
{
|
|
if (!strcmpi(start,StackNames[i]))
|
|
break;
|
|
}
|
|
if (i<nStack)
|
|
{
|
|
StackSize[i]+=info->argSize;
|
|
StackCount[i]++;
|
|
StackCache[StackCacheAt]=i;
|
|
StackCacheAt++;
|
|
if (StackCacheAt>=48)
|
|
StackCacheAt=0;
|
|
}
|
|
else if (i<maxStack)
|
|
{
|
|
strcpy(StackNames[i],start);
|
|
StackSize[i]=info->argSize;
|
|
StackCount[i]=1;
|
|
nStack++;
|
|
}
|
|
else if (nStack<maxStack)
|
|
{
|
|
nStack++;
|
|
strcpy(StackNames[maxStack-1],"*****OTHER*****");
|
|
StackSize[maxStack-1]=info->argSize;
|
|
StackCount[maxStack-1]=1;
|
|
}
|
|
else
|
|
{
|
|
StackSize[maxStack-1]+=info->argSize;
|
|
StackCount[maxStack-1]++;
|
|
}
|
|
}
|
|
TotalMem+=info->argSize;
|
|
return 1;
|
|
}
|
|
|
|
void SH_Checking_f(void);
|
|
#endif
|
|
|
|
class Leakage
|
|
{
|
|
MEM_POOL MyPool;
|
|
|
|
public:
|
|
Leakage()
|
|
{
|
|
MyPool = MemInitDefaultPool();
|
|
// MemPoolSetSmallBlockSize(MyPool, 16);
|
|
MemPoolSetSmallBlockAllocator(MyPool,MEM_SMALL_BLOCK_SH3);
|
|
#if MEM_DEBUG
|
|
dbgMemSetGuardSize(2);
|
|
#endif
|
|
EnableChecking(100000);
|
|
}
|
|
|
|
void LeakReport(void)
|
|
{
|
|
#if MEM_DEBUG
|
|
|
|
int i;
|
|
char mess[1000];
|
|
int blocks=dbgMemTotalCount();
|
|
int mem=dbgMemTotalSize()/1024;
|
|
sprintf(mess,"Final Memory Summary %d blocks %d K\n",blocks,mem);
|
|
OutputDebugString(mess);
|
|
for (i=0;i<1000;i++)
|
|
{
|
|
CheckpointSize[i]=0;
|
|
CheckpointCount[i]=0;
|
|
}
|
|
|
|
TotalMem=0;
|
|
TotalBlocks=0;
|
|
nStack=0;
|
|
MemSetErrorHandler(MyMemReporter2);
|
|
dbgMemReportLeakage(NULL,1,1000);
|
|
MemSetErrorHandler(MemDefaultErrorHandler);
|
|
if (TotalBlocks)
|
|
{
|
|
// Sort by size.
|
|
Sleep(100);
|
|
OutputDebugString("**************************************\n");
|
|
OutputDebugString("**********Memory Leak Report**********\n");
|
|
OutputDebugString("*************** By Size **************\n");
|
|
OutputDebugString("**************************************\n");
|
|
sprintf(mess,"Actual leakage %d blocks %d K\n",TotalBlocks,TotalMem/1024);
|
|
OutputDebugString(mess);
|
|
multimap<int,pair<int,char *> > sortit;
|
|
for (i=0;i<nStack;i++)
|
|
sortit.insert(pair<int,pair<int,char *> >(-StackSize[i],pair<int,char *>(StackCount[i],StackNames[i])));
|
|
multimap<int,pair<int,char *> >::iterator j;
|
|
Sleep(5);
|
|
for (j=sortit.begin();j!=sortit.end();j++)
|
|
{
|
|
sprintf(mess,"%5d KB %6d cnt %s\n",-(*j).first/1024,(*j).second.first,(*j).second.second);
|
|
// if (!(-(*j).first/1024))
|
|
// break;
|
|
Sleep(5);
|
|
OutputDebugString(mess);
|
|
}
|
|
|
|
// Sort by count.
|
|
Sleep(100);
|
|
OutputDebugString("**************************************\n");
|
|
OutputDebugString("**********Memory Leak Report**********\n");
|
|
OutputDebugString("************** By Count **************\n");
|
|
OutputDebugString("**************************************\n");
|
|
sprintf(mess,"Actual leakage %d blocks %d K\n",TotalBlocks,TotalMem/1024);
|
|
OutputDebugString(mess);
|
|
sortit.clear();
|
|
for (i=0;i<nStack;i++)
|
|
sortit.insert(pair<int,pair<int,char *> >(-StackCount[i],pair<int,char *>(StackSize[i],StackNames[i])));
|
|
Sleep(5);
|
|
for (j=sortit.begin();j!=sortit.end();j++)
|
|
{
|
|
sprintf(mess,"%5d KB %6d cnt %s\n",(*j).second.first/1024,-(*j).first,(*j).second.second);
|
|
// if (!(-(*j).first/1024))
|
|
// break;
|
|
Sleep(5);
|
|
OutputDebugString(mess);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutputDebugString("No Memory Leaks\n");
|
|
}
|
|
|
|
// Sort by size.
|
|
Sleep(5);
|
|
OutputDebugString("***************************************\n");
|
|
OutputDebugString("By Tag, sort: size ********************\n");
|
|
OutputDebugString("size(K) count name \n");
|
|
OutputDebugString("-----------------------\n");
|
|
Sleep(5);
|
|
multimap<int,int> sorted;
|
|
for (i=0;i<1000;i++)
|
|
{
|
|
if (CheckpointCount[i])
|
|
{
|
|
sorted.insert(pair<int,int>(-CheckpointSize[i],i));
|
|
}
|
|
}
|
|
multimap<int,int>::iterator k;
|
|
for (k=sorted.begin();k!=sorted.end();k++)
|
|
{
|
|
// sprintf(mess,"%8d %8d %s\n",CheckpointSize[(*k).second]/1024,CheckpointCount[(*k).second],(*k).second>=2?tagDefs[(*k).second-2]:"unknown");
|
|
sprintf(mess,"%8d %8d %s\n",CheckpointSize[(*k).second]/1024,CheckpointCount[(*k).second],"unknown");
|
|
Sleep(5);
|
|
OutputDebugString(mess);
|
|
}
|
|
|
|
// Sort by count.
|
|
Sleep(5);
|
|
OutputDebugString("***************************************\n");
|
|
OutputDebugString("By Tag, sort: count *******************\n");
|
|
OutputDebugString("size(K) count name \n");
|
|
OutputDebugString("-----------------------\n");
|
|
Sleep(5);
|
|
sorted.clear();
|
|
for (i=0;i<1000;i++)
|
|
{
|
|
if (CheckpointCount[i])
|
|
{
|
|
sorted.insert(pair<int,int>(-CheckpointCount[i],i));
|
|
}
|
|
}
|
|
for (k=sorted.begin();k!=sorted.end();k++)
|
|
{
|
|
// sprintf(mess,"%8d %8d %s\n",CheckpointSize[(*k).second]/1024,CheckpointCount[(*k).second],(*k).second>=2?tagDefs[(*k).second-2]:"unknown");
|
|
sprintf(mess,"%8d %8d %s\n",CheckpointSize[(*k).second]/1024,CheckpointCount[(*k).second],"unknown");
|
|
Sleep(5);
|
|
OutputDebugString(mess);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
~Leakage()
|
|
{
|
|
#if MEM_DEBUG
|
|
#if 0
|
|
if (mem_leakfile && mem_leakfile->integer)
|
|
{
|
|
dbgMemSetDefaultErrorOutput(DBGMEM_OUTPUT_FILE,"leakage.out");
|
|
dbgMemReportLeakage(NULL,1,1);
|
|
dbgMemSetDefaultErrorOutput(DBGMEM_OUTPUT_PROMPT,NULL);
|
|
}
|
|
#endif
|
|
if (mem_leakreport && mem_leakreport->integer)
|
|
{
|
|
LeakReport();
|
|
}
|
|
#endif
|
|
}
|
|
#if MEM_DEBUG
|
|
|
|
void EnableChecking(int x)
|
|
{
|
|
if (x)
|
|
{
|
|
dbgMemSetSafetyLevel(MEM_SAFETY_DEBUG);
|
|
dbgMemPoolSetCheckFrequency(MyPool, x);
|
|
dbgMemSetCheckFrequency(x);
|
|
dbgMemDeferFreeing(TRUE);
|
|
if (x>50000)
|
|
{
|
|
dbgMemSetDeferQueueLen(x+5000);
|
|
}
|
|
else
|
|
{
|
|
dbgMemSetDeferQueueLen(50000);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dbgMemSetSafetyLevel(MEM_SAFETY_SOME);
|
|
dbgMemDeferFreeing(FALSE);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
};
|
|
|
|
static Leakage TheLeakage;
|
|
|
|
#if MEM_DEBUG
|
|
|
|
void MEM_Checking_f(void)
|
|
{
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Com_Printf ("mem_checking <frequency>\n");
|
|
return;
|
|
}
|
|
|
|
if (atol(Cmd_Argv(1)) > 0 && atol(Cmd_Argv(1)) < 100)
|
|
{
|
|
Com_Printf ("mem_checking frequency is too low ( < 100 )\n");
|
|
return;
|
|
}
|
|
|
|
TheLeakage.EnableChecking(atol(Cmd_Argv(1)));
|
|
}
|
|
|
|
void MEM_Report_f(void)
|
|
{
|
|
TheLeakage.LeakReport();
|
|
}
|
|
|
|
/*
|
|
void myexit(void)
|
|
{
|
|
TheLeakage.LeakReport();
|
|
}
|
|
*/
|
|
|
|
void SH_Register(void)
|
|
{
|
|
Cmd_AddCommand ("mem_checking", MEM_Checking_f);
|
|
Cmd_AddCommand ("mem_report", MEM_Report_f);
|
|
|
|
mem_leakfile = Cvar_Get( "mem_leakfile", "1", 0 );
|
|
mem_leakreport = Cvar_Get( "mem_leakreport", "1", 0 );
|
|
// atexit(myexit);
|
|
}
|
|
|
|
#endif
|