|
@@ -5,6 +5,15 @@
|
|
|
#include <cstdio>
|
|
#include <cstdio>
|
|
|
#include <cstdarg>
|
|
#include <cstdarg>
|
|
|
|
|
|
|
|
|
|
+#ifdef WIN32
|
|
|
|
|
+#include <windows.h>
|
|
|
|
|
+#include <dbghelp.h>
|
|
|
|
|
+#pragma comment(lib,"dbghelp.lib")
|
|
|
|
|
+
|
|
|
|
|
+#define MAX_STACK_FRAMES 16
|
|
|
|
|
+bool __trackStackTrace = false;
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
struct MemoryAllocationRecord
|
|
struct MemoryAllocationRecord
|
|
|
{
|
|
{
|
|
|
unsigned long address; // address returned to the caller after allocation
|
|
unsigned long address; // address returned to the caller after allocation
|
|
@@ -13,6 +22,10 @@ struct MemoryAllocationRecord
|
|
|
int line; // source line of the allocation request
|
|
int line; // source line of the allocation request
|
|
|
MemoryAllocationRecord* next;
|
|
MemoryAllocationRecord* next;
|
|
|
MemoryAllocationRecord* prev;
|
|
MemoryAllocationRecord* prev;
|
|
|
|
|
+#ifdef WIN32
|
|
|
|
|
+ bool trackStackTrace;
|
|
|
|
|
+ unsigned int pc[MAX_STACK_FRAMES];
|
|
|
|
|
+#endif
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
MemoryAllocationRecord* __memoryAllocations = 0;
|
|
MemoryAllocationRecord* __memoryAllocations = 0;
|
|
@@ -99,6 +112,54 @@ void* debugAlloc(std::size_t size, const char* file, int line)
|
|
|
rec->next = __memoryAllocations;
|
|
rec->next = __memoryAllocations;
|
|
|
rec->prev = 0;
|
|
rec->prev = 0;
|
|
|
|
|
|
|
|
|
|
+ // Capture the stack frame (up to MAX_STACK_FRAMES) if we
|
|
|
|
|
+ // are running on Windows and the user has enabled it.
|
|
|
|
|
+#if defined(WIN32)
|
|
|
|
|
+ rec->trackStackTrace = __trackStackTrace;
|
|
|
|
|
+ if (rec->trackStackTrace)
|
|
|
|
|
+ {
|
|
|
|
|
+ static bool initialized = false;
|
|
|
|
|
+ if (!initialized)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!SymInitialize(GetCurrentProcess(), NULL, true))
|
|
|
|
|
+ gameplay::printError("Stack trace tracking will not work.");
|
|
|
|
|
+ initialized = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Get the current context (state of EBP, EIP, ESP registers).
|
|
|
|
|
+ static CONTEXT context;
|
|
|
|
|
+ RtlCaptureContext(&context);
|
|
|
|
|
+
|
|
|
|
|
+ static STACKFRAME64 stackFrame;
|
|
|
|
|
+ memset(&stackFrame, 0, sizeof(STACKFRAME64));
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize the stack frame based on the machine architecture.
|
|
|
|
|
+#ifdef _M_IX86
|
|
|
|
|
+ static const DWORD machineType = IMAGE_FILE_MACHINE_I386;
|
|
|
|
|
+ stackFrame.AddrPC.Offset = context.Eip;
|
|
|
|
|
+ stackFrame.AddrPC.Mode = AddrModeFlat;
|
|
|
|
|
+ stackFrame.AddrFrame.Offset = context.Ebp;
|
|
|
|
|
+ stackFrame.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
|
+ stackFrame.AddrStack.Offset = context.Esp;
|
|
|
|
|
+ stackFrame.AddrStack.Mode = AddrModeFlat;
|
|
|
|
|
+#else
|
|
|
|
|
+#error "Machine architecture not supported!"
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ // Walk up the stack and store the program counters.
|
|
|
|
|
+ memset(rec->pc, 0, sizeof(rec->pc));
|
|
|
|
|
+ for (int i = 0; i < MAX_STACK_FRAMES; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ rec->pc[i] = stackFrame.AddrPC.Offset;
|
|
|
|
|
+ if (!StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &stackFrame,
|
|
|
|
|
+ &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
|
|
|
|
|
+ {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
if (__memoryAllocations)
|
|
if (__memoryAllocations)
|
|
|
__memoryAllocations->prev = rec;
|
|
__memoryAllocations->prev = rec;
|
|
|
__memoryAllocations = rec;
|
|
__memoryAllocations = rec;
|
|
@@ -137,6 +198,70 @@ void debugFree(void* p)
|
|
|
free(mem);
|
|
free(mem);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#ifdef WIN32
|
|
|
|
|
+void printStackTrace(MemoryAllocationRecord* rec)
|
|
|
|
|
+{
|
|
|
|
|
+ const unsigned int bufferSize = 512;
|
|
|
|
|
+
|
|
|
|
|
+ // Resolve the program counter to the corresponding function names.
|
|
|
|
|
+ unsigned int pc;
|
|
|
|
|
+ for (int i = 0; i < MAX_STACK_FRAMES; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Check to see if we are at the end of the stack trace.
|
|
|
|
|
+ pc = rec->pc[i];
|
|
|
|
|
+ if (pc == 0)
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ // Get the function name.
|
|
|
|
|
+ unsigned char buffer[sizeof(IMAGEHLP_SYMBOL64) + bufferSize];
|
|
|
|
|
+ IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)buffer;
|
|
|
|
|
+ DWORD64 displacement;
|
|
|
|
|
+ memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + bufferSize);
|
|
|
|
|
+ symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
|
|
|
|
+ symbol->MaxNameLength = bufferSize;
|
|
|
|
|
+ if (!SymGetSymFromAddr64(GetCurrentProcess(), pc, &displacement, symbol))
|
|
|
|
|
+ {
|
|
|
|
|
+ gameplay::printError("[memory] STACK TRACE: <unknown location>");
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ symbol->Name[bufferSize - 1] = '\0';
|
|
|
|
|
+
|
|
|
|
|
+ // Check if we need to go further up the stack.
|
|
|
|
|
+ if (strncmp(symbol->Name, "operator new", 12) == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ // In operator new or new[], keep going...
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ // Get the file and line number.
|
|
|
|
|
+ if (pc != 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ IMAGEHLP_LINE64 line;
|
|
|
|
|
+ DWORD displacement;
|
|
|
|
|
+ memset(&line, 0, sizeof(line));
|
|
|
|
|
+ line.SizeOfStruct = sizeof(line);
|
|
|
|
|
+ if (!SymGetLineFromAddr64(GetCurrentProcess(), pc, &displacement, &line))
|
|
|
|
|
+ {
|
|
|
|
|
+ gameplay::printError("[memory] STACK TRACE: %s - <unknown file>:<unknown line number>", symbol->Name);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ const char* file = strrchr(line.FileName, '\\');
|
|
|
|
|
+ if(!file)
|
|
|
|
|
+ file = line.FileName;
|
|
|
|
|
+ else
|
|
|
|
|
+ file++;
|
|
|
|
|
+
|
|
|
|
|
+ gameplay::printError("[memory] STACK TRACE: %s - %s:%d", symbol->Name, file, line.LineNumber);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
extern void printMemoryLeaks()
|
|
extern void printMemoryLeaks()
|
|
|
{
|
|
{
|
|
|
// Dump general heap memory leaks
|
|
// Dump general heap memory leaks
|
|
@@ -150,10 +275,32 @@ extern void printMemoryLeaks()
|
|
|
MemoryAllocationRecord* rec = __memoryAllocations;
|
|
MemoryAllocationRecord* rec = __memoryAllocations;
|
|
|
while (rec)
|
|
while (rec)
|
|
|
{
|
|
{
|
|
|
- gameplay::printError("[memory] LEAK: HEAP allocation leak of size %d leak from line %d in file '%s'.", rec->size, rec->line, rec->file);
|
|
|
|
|
|
|
+#ifdef WIN32
|
|
|
|
|
+ if (rec->trackStackTrace)
|
|
|
|
|
+ {
|
|
|
|
|
+ gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d:", rec->address, rec->size);
|
|
|
|
|
+ printStackTrace(rec);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d from line %d in file '%s'.", rec->address, rec->size, rec->line, rec->file);
|
|
|
|
|
+#else
|
|
|
|
|
+ gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d from line %d in file '%s'.", rec->address, rec->size, rec->line, rec->file);
|
|
|
|
|
+#endif
|
|
|
rec = rec->next;
|
|
rec = rec->next;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#if defined(WIN32)
|
|
|
|
|
+void setTrackStackTrace(bool trackStackTrace)
|
|
|
|
|
+{
|
|
|
|
|
+ __trackStackTrace = trackStackTrace;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void toggleTrackStackTrace()
|
|
|
|
|
+{
|
|
|
|
|
+ __trackStackTrace = !__trackStackTrace;
|
|
|
|
|
+}
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
#endif
|
|
#endif
|