1 /*////////////////////////////////////////////////////////////////////////////
\r
3 * Memory_and_Exception_Trace
\r
5 * ///////////////////////////////////////////////////////////////////////////
\r
10 * Dumps memory leaks (unreleased allocations) for CRT-Allocs and COM-Allocs
\r
11 * Dumps the stack of an thread if an exepction occurs
\r
14 * - If the allocation-RequestID wrap, then allocations will get lost...
\r
17 * Jochen Kalmbach, Germany
\r
18 * (c) 2002-2005 (Freeware)
\r
19 * http://www.codeproject.com/tools/leakfinder.asp
\r
21 * License (The zlib/libpng License, http://www.opensource.org/licenses/zlib-license.php):
\r
23 * Copyright (c) 2005 Jochen Kalmbach
\r
25 * This software is provided 'as-is', without any express or implied warranty.
\r
26 * In no event will the authors be held liable for any damages arising from the
\r
27 * use of this software.
\r
29 * Permission is granted to anyone to use this software for any purpose, including
\r
30 * commercial applications, and to alter it and redistribute it freely, subject to
\r
31 * the following restrictions:
\r
33 * 1. The origin of this software must not be misrepresented; you must not claim
\r
34 * that you wrote the original software. If you use this software in a product,
\r
35 * an acknowledgment in the product documentation would be appreciated but is
\r
38 * 2. Altered source versions must be plainly marked as such, and must not be
\r
39 * misrepresented as being the original software.
\r
41 * 3. This notice may not be removed or altered from any source distribution.
\r
43 *//////////////////////////////////////////////////////////////////////////////
\r
45 //#include "stdafx.h" // should be uncommented for precompiled headers
\r
47 #include <windows.h>
\r
56 #include "Stackwalker.h"
\r
58 #pragma warning(push)
\r
59 #pragma warning(disable : 4100)
\r
60 #pragma warning(disable : 4996)
\r
61 #pragma warning(disable : 4189)
\r
62 #pragma warning(disable : 4245)
\r
63 #pragma warning(disable : 4701)
\r
65 // If the following is defined, only the used memories are stored in the hash-table.
\r
66 // If the memory is freed, it will be removed from the hash-table (to reduce memory)
\r
67 // Consequences: At DeInitAllocHook, only Leaks will be reported
\r
68 #define HASH_ENTRY_REMOVE_AT_FREE
\r
71 // 0 = Do not write any output during runtime-alloc-call
\r
72 // 1 = Write only the alloc action (malloc, realloc, free)
\r
73 // 2 = Write alloc action and callstack only for malloc/realloc
\r
74 // 3 = Write alloc action and callstack for all actions
\r
75 static ULONG g_ulShowStackAtAlloc = 0;
\r
77 // the form of the output file
\r
78 static eAllocCheckOutput g_CallstackOutputType = ACOutput_Simple;
\r
81 // Size of Hash-Table (this should be a prime number to avoid collisions)
\r
82 #define ALLOC_HASH_ENTRIES 1023
\r
85 // Size of Callstack-trace in bytes (0x500 => appr. 5-9 functions, depending on parameter count for each function)
\r
86 #define MAX_ESP_LEN_BUF 0x500
\r
89 // Normally we can ignore allocations from the Runtime-System
\r
90 #define IGNORE_CRT_ALLOC
\r
92 // MaxSize: 1 MByte (only for StackwalkFilter)
\r
93 #define LOG_FILE_MAX_SIZE 1024*1024
\r
95 // If the following is defined, then COM-Leaks will also be tracked
\r
96 #define WITH_IMALLOC_SPY
\r
99 // #############################################################################################
\r
100 #ifdef WITH_IMALLOC_SPY
\r
102 void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize);
\r
103 BOOL IMallocHashRemove(void *pData);
\r
105 // IMallocSpy-Interface
\r
106 class CMallocSpy : public IMallocSpy
\r
112 ~CMallocSpy(void) {
\r
114 // IUnknown methods
\r
115 STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppUnk) {
\r
117 if (IsEqualIID(riid, IID_IUnknown)) {
\r
118 *ppUnk = (IUnknown *) this;
\r
120 else if (IsEqualIID(riid, IID_IMallocSpy)) {
\r
121 *ppUnk = (IMalloc *) this;
\r
125 hr = E_NOINTERFACE;
\r
130 STDMETHOD_(ULONG, AddRef) (void) {
\r
131 return InterlockedIncrement(&m_cRef);
\r
133 STDMETHOD_(ULONG, Release) (void) {
\r
135 cRef = InterlockedDecrement(&m_cRef);
\r
142 // IMallocSpy methods
\r
143 STDMETHOD_(ULONG, PreAlloc) (ULONG cbRequest) {
\r
144 m_cbRequest = cbRequest;
\r
147 STDMETHOD_(void *, PostAlloc) (void *pActual) {
\r
149 if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
\r
150 GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {
\r
153 memset( &c, '\0', sizeof c );
\r
154 c.ContextFlags = CONTEXT_FULL;
\r
156 if ( GetThreadContext(hThread, &c) != 0) {
\r
168 IMallocHashInsert(pActual, c, m_cbRequest);
\r
170 CloseHandle(hThread);
\r
174 STDMETHOD_(void *, PreFree) (void *pRequest, BOOL fSpyed) {
\r
175 IMallocHashRemove(pRequest);
\r
178 STDMETHOD_(void, PostFree) (BOOL fSpyed) {
\r
181 STDMETHOD_(ULONG, PreRealloc) (void *pRequest, ULONG cbRequest,
\r
182 void **ppNewRequest, BOOL fSpyed) {
\r
183 IMallocHashRemove(pRequest);
\r
184 m_cbRequest = cbRequest;
\r
185 *ppNewRequest = pRequest; // Bug fixed. Thanx to Christoph Weber
\r
188 STDMETHOD_(void *, PostRealloc) (void *pActual, BOOL fSpyed) {
\r
190 if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
\r
191 GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {
\r
194 memset( &c, '\0', sizeof c );
\r
195 c.ContextFlags = CONTEXT_FULL;
\r
197 if ( GetThreadContext(hThread, &c) != 0) {
\r
209 IMallocHashInsert(pActual, c, m_cbRequest);
\r
211 CloseHandle(hThread);
\r
215 STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) {
\r
218 STDMETHOD_(ULONG, PostGetSize) (ULONG cbActual, BOOL fSpyed) {
\r
221 STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) {
\r
224 STDMETHOD_(BOOL, PostDidAlloc) (void *pRequest, BOOL fSpyed, BOOL fActual) {
\r
227 STDMETHOD_(void, PreHeapMinimize) (void) {
\r
230 STDMETHOD_(void, PostHeapMinimize) (void) {
\r
239 // #############################################################################################
\r
240 // Here I have included the API-Version 9 declarations, so it will also compile on systems, where the new PSDK is not installed
\r
241 // Normally we just need to include the "dbghelp.h" file
\r
242 #include <imagehlp.h>
\r
243 #if API_VERSION_NUMBER < 9
\r
246 (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
\r
248 DWORD64 qwBaseAddress,
\r
251 LPDWORD lpNumberOfBytesRead
\r
254 typedef struct _IMAGEHLP_LINE64 {
\r
255 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
\r
256 PVOID Key; // internal
\r
257 DWORD LineNumber; // line number in file
\r
258 PCHAR FileName; // full filename
\r
259 DWORD64 Address; // first instruction of line
\r
260 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
\r
263 typedef struct _IMAGEHLP_MODULE64 {
\r
264 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
\r
265 DWORD64 BaseOfImage; // base load address of module
\r
266 DWORD ImageSize; // virtual size of the loaded module
\r
267 DWORD TimeDateStamp; // date/time stamp from pe header
\r
268 DWORD CheckSum; // checksum from the pe header
\r
269 DWORD NumSyms; // number of symbols in the symbol table
\r
270 SYM_TYPE SymType; // type of symbols loaded
\r
271 CHAR ModuleName[32]; // module name
\r
272 CHAR ImageName[256]; // image name
\r
273 CHAR LoadedImageName[256]; // symbol file name
\r
274 } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
\r
276 typedef struct _IMAGEHLP_SYMBOL64 {
\r
277 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
\r
278 DWORD64 Address; // virtual address including dll base address
\r
279 DWORD Size; // estimated size of symbol, can be zero
\r
280 DWORD Flags; // info about the symbols, see the SYMF defines
\r
281 DWORD MaxNameLength; // maximum size of symbol name in 'Name'
\r
282 CHAR Name[1]; // symbol name (null terminated string)
\r
283 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
\r
285 typedef struct _tagADDRESS64 {
\r
289 } ADDRESS64, *LPADDRESS64;
\r
291 typedef struct _KDHELP64 {
\r
294 // address of kernel thread object, as provided in the
\r
295 // WAIT_STATE_CHANGE packet.
\r
300 // offset in thread object to pointer to the current callback frame
\r
301 // in kernel stack.
\r
303 DWORD ThCallbackStack;
\r
306 // offset in thread object to pointer to the current callback backing
\r
307 // store frame in kernel stack.
\r
309 DWORD ThCallbackBStore;
\r
312 // offsets to values in frame:
\r
314 // address of next callback frame
\r
315 DWORD NextCallback;
\r
317 // address of saved frame pointer (if applicable)
\r
318 DWORD FramePointer;
\r
322 // Address of the kernel function that calls out to user mode
\r
324 DWORD64 KiCallUserMode;
\r
327 // Address of the user mode dispatcher function
\r
329 DWORD64 KeUserCallbackDispatcher;
\r
332 // Lowest kernel mode address
\r
334 DWORD64 SystemRangeStart;
\r
336 DWORD64 Reserved[8];
\r
338 } KDHELP64, *PKDHELP64;
\r
341 typedef struct _tagSTACKFRAME64 {
\r
342 ADDRESS64 AddrPC; // program counter
\r
343 ADDRESS64 AddrReturn; // return address
\r
344 ADDRESS64 AddrFrame; // frame pointer
\r
345 ADDRESS64 AddrStack; // stack pointer
\r
346 ADDRESS64 AddrBStore; // backing store pointer
\r
347 PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
\r
348 DWORD64 Params[4]; // possible arguments to the function
\r
349 BOOL Far; // WOW far call
\r
350 BOOL Virtual; // is this a virtual frame?
\r
351 DWORD64 Reserved[3];
\r
353 } STACKFRAME64, *LPSTACKFRAME64;
\r
357 (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
\r
364 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(
\r
371 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
\r
377 // #############################################################################################
\r
381 // Forward definitions of functions:
\r
382 static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hProcess);
\r
383 static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile);
\r
385 //static void AllocHashOut(FILE*);
\r
386 static ULONG AllocHashOutLeaks(FILE*);
\r
391 static TCHAR *g_pszAllocLogName = NULL;
\r
392 static FILE *g_fFile = NULL;
\r
394 // AllocCheckFileOpen
\r
395 // Checks if the log-file is already opened
\r
396 // if not, try to open file (append or create if not exists)
\r
397 // if open failed, redirect output to stdout
\r
398 static void AllocCheckFileOpen(bool bAppend = true) {
\r
399 // is the File already open? If not open it...
\r
400 if (g_fFile == NULL)
\r
401 if (g_pszAllocLogName != NULL)
\r
403 if (bAppend == false)
\r
404 g_fFile = _tfopen(g_pszAllocLogName, _T("w"));
\r
406 g_fFile = _tfopen(g_pszAllocLogName, _T("a"));
\r
408 if (g_fFile == NULL)
\r
412 // Write Date/Time to specified file (will also work after 2038)
\r
413 static void WriteDateTime(FILE *fFile, BOOL asXMLAttrs = FALSE) {
\r
414 TCHAR pszTemp[11], pszTemp2[11];
\r
416 if (fFile != NULL) {
\r
417 _tstrdate( pszTemp );
\r
418 _tstrtime( pszTemp2 );
\r
419 if (asXMLAttrs == FALSE)
\r
420 _ftprintf(fFile, _T("%s %s"), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)
\r
422 _ftprintf(fFile, _T("date=\"%s\" time=\"%s\" "), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)
\r
427 /*******************************************************************************
\r
429 *******************************************************************************/
\r
430 // Memory for the EIP-Address (is used by the ShowStack-method)
\r
431 #define MAX_EIP_LEN_BUF 4
\r
433 #define ALLOC_ENTRY_NOT_FOUND 0xFFFFFFFF
\r
435 typedef struct AllocHashEntryType {
\r
436 long lRequestID; // RequestID from CRT (if 0, then this entry is empty)
\r
437 size_t nDataSize; // Size of the allocated memory
\r
438 char cRemovedFlag; // 0 => memory was not yet released
\r
439 struct AllocHashEntryType *Next;
\r
440 // Callstack for EIP
\r
443 char pcEIPAddr[MAX_EIP_LEN_BUF];
\r
444 // Callstack for ESP
\r
447 char pcESPAddr[MAX_ESP_LEN_BUF];
\r
448 } AllocHashEntryType;
\r
450 static AllocHashEntryType AllocHashTable[ALLOC_HASH_ENTRIES];
\r
451 static ULONG AllocHashEntries = 0;
\r
452 static ULONG AllocHashCollisions = 0;
\r
453 static ULONG AllocHashFreed = 0;
\r
454 static ULONG AllocHashMaxUsed = 0; // maximal number of concurrent entries
\r
455 static ULONG AllocHashCurrentCount = 0;
\r
457 static ULONG AllocHashMaxCollisions = 0;
\r
458 static ULONG AllocHashCurrentCollisions = 0;
\r
460 // ##########################################################################################
\r
461 #ifdef WITH_IMALLOC_SPY
\r
462 // eigene Tabelle für die IMallocs:
\r
463 typedef struct IMallocHashEntryType {
\r
464 void *pData; // Key-Word
\r
465 size_t nDataSize; // größe des Datenblocks (optional)
\r
466 char cRemovedFlag; // 0 => nicht wurde noch nicht freigegeben
\r
467 struct IMallocHashEntryType *Next;
\r
468 // Callstack für EIP
\r
471 char pcEIPAddr[MAX_EIP_LEN_BUF];
\r
472 // Callstack für ESP
\r
475 char pcESPAddr[MAX_ESP_LEN_BUF];
\r
476 } IMallocHashEntryType;
\r
478 static IMallocHashEntryType IMallocHashTable[ALLOC_HASH_ENTRIES];
\r
480 static ULONG IMallocHashEntries = 0;
\r
481 static ULONG IMallocHashCollisions = 0;
\r
482 static ULONG IMallocHashFreed = 0;
\r
483 static ULONG IMallocHashMaxUsed = 0; // maximal number of concurrent entries
\r
484 static ULONG IMallocHashCurrentCount = 0;
\r
486 static ULONG IMallocHashMaxCollisions = 0;
\r
487 static ULONG IMallocHashCurrentCollisions = 0;
\r
490 //static void AllocHashOut(FILE*);
\r
491 static ULONG IMallocHashOutLeaks(FILE*);
\r
493 // AllocHashFunction
\r
494 // Die eigentliche Hash-Funktion (hier ganz simpel)
\r
495 static ULONG IMallocHashFunction(void *pData) {
\r
497 DWORD dwPointer = (DWORD) pData;
\r
499 // relativ simpler Mechanismus für die Hash-Funktion,
\r
500 // mir ist nur nix besseres eingefallen...
\r
501 ulTemp = dwPointer % ALLOC_HASH_ENTRIES;
\r
503 _ASSERTE( (ulTemp >= 0) && (ulTemp < ALLOC_HASH_ENTRIES) );
\r
506 } // AllocHashFunction
\r
508 // IMallocHashInsert
\r
509 // pData: Key-Word (Pointer to address)
\r
510 // pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
\r
511 // nDataSize: How many bytes
\r
512 void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize) {
\r
514 IMallocHashEntryType *pHashEntry;
\r
516 // ermittle Statistische Werte
\r
517 IMallocHashEntries++;
\r
518 IMallocHashCurrentCount++;
\r
519 if (IMallocHashCurrentCount > IMallocHashMaxUsed)
\r
520 IMallocHashMaxUsed = IMallocHashCurrentCount;
\r
522 // ermittle den Hash-Wert
\r
523 HashIdx = IMallocHashFunction(pData);
\r
525 // Eintrag darf nicht größer als die Hash-Tabelle sein
\r
526 _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
\r
528 pHashEntry = &IMallocHashTable[HashIdx];
\r
529 if (pHashEntry->pData == 0) {
\r
530 // es ist noch kein Eintrag da
\r
533 //Statistische Daten:
\r
534 IMallocHashCollisions++;
\r
535 IMallocHashCurrentCollisions++;
\r
536 if (IMallocHashCurrentCollisions > IMallocHashMaxCollisions)
\r
537 IMallocHashMaxCollisions = IMallocHashCurrentCollisions;
\r
539 // Eintrag ist schon belegt, verkette die Einträge
\r
540 // wenn dies oft vorkommt, sollte man entweder die Tabelle vergrößern oder eine
\r
541 // andere Hash-Funktion wählen
\r
542 while(pHashEntry->Next != NULL) {
\r
543 pHashEntry = pHashEntry->Next;
\r
546 pHashEntry->Next = (IMallocHashEntryType*) _calloc_dbg(sizeof(IMallocHashEntryType), 1, _CRT_BLOCK, __FILE__, __LINE__);
\r
547 pHashEntry = pHashEntry->Next;
\r
550 pHashEntry->pData = pData; // Key-Word
\r
551 pHashEntry->nDataSize = nDataSize;
\r
552 pHashEntry->Next = NULL;
\r
553 // Get EIP and save it in the record
\r
554 pHashEntry->dwEIPOffset = Context.Eip;
\r
555 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
\r
556 // Could not read memory... remove everything...
\r
557 memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
\r
558 pHashEntry->dwEIPLen = 0;
\r
559 pHashEntry->dwEIPOffset = 0;
\r
562 // Get ESP and save it in the record
\r
563 pHashEntry->dwESPOffset = Context.Ebp;
\r
564 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
\r
565 // Could not read memory... remove everything...
\r
566 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
\r
567 pHashEntry->dwESPLen = 0;
\r
568 pHashEntry->dwESPOffset = 0;
\r
570 // Check if I tried to read too much...
\r
571 if (GetLastError() == ERROR_PARTIAL_COPY)
\r
573 // ask how many I can read:
\r
574 MEMORY_BASIC_INFORMATION MemBuffer;
\r
575 DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
\r
578 // calculate the length
\r
579 DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
\r
580 if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
\r
582 // try to read it again (with the shorter length)
\r
583 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
\r
585 // ok, now everything goes wrong... remove it...
\r
586 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
\r
587 pHashEntry->dwESPLen = 0;
\r
588 pHashEntry->dwESPOffset = 0;
\r
592 pHashEntry->dwESPOffset = Context.Ebp;
\r
595 } // VirtualQuery was successfully
\r
596 } // ERROR_PARTIAL_COPY
\r
601 // Wird ALLOC_ENTRY_NOT_FOUND zurückgegeben, so wurde der Key nicht
\r
602 // gefunden, ansonsten wird ein Zeiger auf den Hash-Eintrag zurückgegeben
\r
603 // ACHTUNG: In einem preemptiven Tasking-System kann hier nicht
\r
604 // garantiert werden, ob der Zeiger noch gültig ist, wenn er
\r
605 // zurückgegeben wird, da er von einem anderen Thread schon
\r
606 // freigegeben sein könnte.
\r
607 // Die synchronisation muß eine Ebene höher erfolgen
\r
608 static IMallocHashEntryType *IMallocHashFind(void *pData) {
\r
610 IMallocHashEntryType *pHashEntry;
\r
612 // ermittle den Hash-Wert
\r
613 HashIdx = IMallocHashFunction(pData);
\r
615 // Eintrag darf nicht größer als die Hash-Tabelle sein
\r
616 _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
\r
618 pHashEntry = &IMallocHashTable[HashIdx];
\r
619 while(pHashEntry != NULL) {
\r
620 if (pHashEntry->pData == pData) {
\r
623 pHashEntry = pHashEntry->Next;
\r
626 // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
\r
627 return (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
\r
630 // IMallocHashRemove
\r
631 // Return: FALSE (0) : Key wurde gefunden und entfernt/markiert
\r
632 // TRUE (!=0): Key wurde nicht gefunden!
\r
633 BOOL IMallocHashRemove(void *pData) {
\r
635 IMallocHashEntryType *pHashEntry, *pHashEntryLast;
\r
637 // ermittle den Hash-Wert
\r
638 HashIdx = IMallocHashFunction(pData);
\r
640 // Eintrag darf nicht größer als die Hash-Tabelle sein
\r
641 _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
\r
643 pHashEntryLast = NULL;
\r
644 pHashEntry = &IMallocHashTable[HashIdx];
\r
645 while(pHashEntry != NULL) {
\r
646 if (pHashEntry->pData == pData) {
\r
647 #ifdef HASH_ENTRY_REMOVE_AT_FREE
\r
648 IMallocHashFreed++;
\r
649 IMallocHashCurrentCount--;
\r
650 // gebe den Speicher frei
\r
651 if (pHashEntryLast == NULL) {
\r
652 // Es ist ein Eintrag direkt in der Tabelle
\r
653 if (pHashEntry->Next == NULL) {
\r
654 // Es ist der letze Eintrag lösche also die Tabelle
\r
655 memset(&IMallocHashTable[HashIdx], 0, sizeof(IMallocHashTable[HashIdx]));
\r
658 // Es sind noch Einträge verkettet, überschreibe einfach den nicht mehr gebrauchten...
\r
659 IMallocHashEntryType *pTmp = pHashEntry->Next;
\r
660 *pHashEntry = *(pHashEntry->Next);
\r
661 _free_dbg(pTmp, _CRT_BLOCK);
\r
666 // ich bin in einem dynamischen Bereich
\r
667 // dies war eine kollisions, zähle also wieder zurück:
\r
668 IMallocHashCurrentCollisions--;
\r
669 pHashEntryLast->Next = pHashEntry->Next;
\r
670 _free_dbg(pHashEntry, _CRT_BLOCK);
\r
674 // erhöhe nur den Removed counter und behalte das Object im Speicher
\r
675 pHashEntry->cRemovedFlag++;
\r
676 return TRUE; // erfolgreich
\r
679 pHashEntryLast = pHashEntry;
\r
680 pHashEntry = pHashEntry->Next;
\r
683 // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
\r
689 // Callback-Funtion for StackWalk für meine CallStack-Ausgabe aus der Hash-Tabelle
\r
690 static BOOL __stdcall ReadProcMemoryFromIMallocHash(HANDLE pData, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
\r
691 // Versuche die hRequestID zu finden
\r
692 IMallocHashEntryType *pHashEntry;
\r
693 *lpNumberOfBytesRead = 0;
\r
695 pHashEntry = IMallocHashFind((PVOID) pData);
\r
696 if (pHashEntry == (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
\r
697 // nicht gefunden, somit kann ich den Speicher nicht lesen
\r
698 *lpNumberOfBytesRead = 0;
\r
701 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
\r
702 // Speicher liegt im ESP:
\r
703 // Errechne den Offset
\r
704 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
\r
705 DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
\r
706 memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
\r
707 *lpNumberOfBytesRead = dwSize;
\r
708 if (dwSize != nSize)
\r
712 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
\r
713 // Speicher liegt im EIP:
\r
714 // Errechne den Offset
\r
715 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
\r
716 DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
\r
717 memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
\r
718 *lpNumberOfBytesRead = dwSize;
\r
719 if (dwSize != nSize)
\r
723 if (*lpNumberOfBytesRead == 0) // Der Speicher konnte nicht gefunden werden
\r
728 // AllocHashOutLeaks
\r
729 // Gibt allen Speicher aus, der noch nicht wieder freigegeben wurde
\r
730 // Returns the number of bytes, that are not freed (leaks)
\r
731 ULONG IMallocHashOutLeaks(FILE *fFile) {
\r
733 IMallocHashEntryType *pHashEntry;
\r
735 ULONG ulLeaksByte = 0;
\r
737 // Gehe jeden Eintrag durch und gebe ihn aus
\r
738 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
\r
739 pHashEntry = &IMallocHashTable[ulTemp];
\r
740 if (pHashEntry->pData != 0) {
\r
741 while(pHashEntry != NULL) {
\r
742 // gebe die Zeile aus
\r
743 if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
\r
745 if (g_CallstackOutputType == ACOutput_XML)
\r
746 _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);
\r
748 _ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
\r
750 memset( &c, '\0', sizeof c );
\r
751 c.Eip = pHashEntry->dwEIPOffset;
\r
752 c.Ebp = pHashEntry->dwESPOffset;
\r
753 ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromIMallocHash, (HANDLE) pHashEntry->pData);
\r
754 // Zähle zusammen wieviel Byte noch nicht freigegeben wurden
\r
755 if (pHashEntry->nDataSize > 0)
\r
756 ulLeaksByte += pHashEntry->nDataSize;
\r
758 ulLeaksByte++; // Wenn zwar Speicher allokiert wurde, dieser aber 0 Bytes lang war, so reserviere für diesen zumindest 1 Byte
\r
760 if (g_CallstackOutputType == ACOutput_XML)
\r
761 _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
\r
763 pHashEntry = pHashEntry->Next;
\r
767 if (g_CallstackOutputType != ACOutput_XML)
\r
768 _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
\r
769 return ulLeaksByte;
\r
770 } // AllocHashOutLeaks
\r
774 static void AllocHashInit(void) {
\r
776 memset(AllocHashTable, 0, sizeof(AllocHashTable));
\r
777 AllocHashEntries = 0;
\r
778 AllocHashCollisions = 0;
\r
779 AllocHashFreed = 0;
\r
780 AllocHashCurrentCount = 0;
\r
781 AllocHashMaxUsed = 0;
\r
783 AllocHashMaxCollisions = 0;
\r
784 AllocHashCurrentCollisions = 0;
\r
786 #ifdef WITH_IMALLOC_SPY
\r
787 memset(IMallocHashTable, 0, sizeof(IMallocHashTable));
\r
788 IMallocHashEntries = 0;
\r
789 IMallocHashCollisions = 0;
\r
790 IMallocHashFreed = 0;
\r
791 IMallocHashCurrentCount = 0;
\r
792 IMallocHashMaxUsed = 0;
\r
794 IMallocHashMaxCollisions = 0;
\r
795 IMallocHashCurrentCollisions = 0;
\r
802 // Returns the number of bytes, that are not freed (leaks)
\r
803 static ULONG AllocHashDeinit(void) {
\r
805 bool bAppend = g_CallstackOutputType != ACOutput_XML;
\r
806 AllocCheckFileOpen(false);//bAppend); // open global log-file
\r
808 if (g_CallstackOutputType == ACOutput_XML)
\r
810 _ftprintf(g_fFile, _T("<MEMREPORT "));
\r
811 WriteDateTime(g_fFile, TRUE);
\r
812 _ftprintf(g_fFile, _T(">\n"));
\r
816 _ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));
\r
817 WriteDateTime(g_fFile);
\r
818 _ftprintf(g_fFile, _T("\n"));
\r
821 #ifndef HASH_ENTRY_REMOVE_AT_FREE
\r
822 // output the used memory
\r
823 if (g_CallstackOutputType != ACOutput_XML)
\r
824 _ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));
\r
825 AllocHashOut(g_fFile);
\r
828 // output the Memoty leaks
\r
829 if (g_CallstackOutputType != ACOutput_XML)
\r
830 _ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));
\r
831 ulRet = AllocHashOutLeaks(g_fFile);
\r
833 if (g_CallstackOutputType == ACOutput_Advanced)
\r
835 // output some statistics from the hash-table
\r
836 _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
\r
837 _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
\r
838 _ftprintf(g_fFile, _T(" Inserts: %i\n"), AllocHashEntries);
\r
839 _ftprintf(g_fFile, _T(" Freed: %i\n"), AllocHashFreed);
\r
840 _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), AllocHashCollisions);
\r
841 _ftprintf(g_fFile, _T("\n"));
\r
842 _ftprintf(g_fFile, _T(" Max used: %i\n"), AllocHashMaxUsed);
\r
843 _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), AllocHashMaxCollisions);
\r
848 AllocHashEntryType *pHashEntry, *pHashEntryOld;
\r
850 // Now, free my own memory
\r
851 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
\r
852 pHashEntry = &AllocHashTable[ulTemp];
\r
853 while(pHashEntry != NULL) {
\r
854 pHashEntryOld = pHashEntry;
\r
855 pHashEntry = pHashEntry->Next;
\r
856 if (pHashEntryOld != &AllocHashTable[ulTemp]) {
\r
857 // now free the dynamically allocated memory
\r
858 free(pHashEntryOld);
\r
862 // empty the hash-table
\r
863 memset(AllocHashTable, 0, sizeof(AllocHashTable));
\r
865 #ifdef WITH_IMALLOC_SPY
\r
866 // output the Memoty leaks
\r
867 if (g_CallstackOutputType != ACOutput_XML)
\r
868 _ftprintf(g_fFile, _T("\n##### COM-Leaks: ###############################################\n"));
\r
869 ulRet = IMallocHashOutLeaks(g_fFile);
\r
871 if (g_CallstackOutputType == ACOutput_Advanced)
\r
873 // output some statistics from the hash-table
\r
874 _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
\r
875 _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
\r
876 _ftprintf(g_fFile, _T(" Inserts: %i\n"), IMallocHashEntries);
\r
877 _ftprintf(g_fFile, _T(" Freed: %i\n"), IMallocHashFreed);
\r
878 _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), IMallocHashCollisions);
\r
879 _ftprintf(g_fFile, _T("\n"));
\r
880 _ftprintf(g_fFile, _T(" Max used: %i\n"), IMallocHashMaxUsed);
\r
881 _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), IMallocHashMaxCollisions);
\r
886 IMallocHashEntryType *pIMHashEntry, *pIMHashEntryOld;
\r
888 // Gehe jeden Eintrag durch und gebe ihn frei
\r
889 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
\r
890 pIMHashEntry = &IMallocHashTable[ulTemp];
\r
891 while(pHashEntry != NULL) {
\r
892 pIMHashEntryOld = pIMHashEntry;
\r
893 pIMHashEntry = pIMHashEntry->Next;
\r
894 if (pIMHashEntryOld != &IMallocHashTable[ulTemp]) {
\r
895 // es ist dynamischer Speicher, gebe ihn also frei:
\r
896 _free_dbg(pIMHashEntryOld, _CRT_BLOCK);
\r
900 // Lösche die gesamte Hash-Tabelle
\r
901 memset(IMallocHashTable, 0, sizeof(IMallocHashTable));
\r
905 if (g_CallstackOutputType == ACOutput_XML)
\r
906 _ftprintf(g_fFile, _T("</MEMREPORT>\n"));
\r
909 } // AllocHashDeinit
\r
911 // AllocHashFunction
\r
912 // The has-function (very simple)
\r
913 static inline ULONG AllocHashFunction(long lRequestID) {
\r
914 // I couldn´t find any better and faster
\r
915 return lRequestID % ALLOC_HASH_ENTRIES;
\r
916 } // AllocHashFunction
\r
919 // lRequestID: Key-Word (RequestID from AllocHook)
\r
920 // pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
\r
921 // nDataSize: How many bytes
\r
922 static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {
\r
924 AllocHashEntryType *pHashEntry;
\r
926 // change statistical data
\r
927 AllocHashEntries++;
\r
928 AllocHashCurrentCount++;
\r
929 if (AllocHashCurrentCount > AllocHashMaxUsed)
\r
930 AllocHashMaxUsed = AllocHashCurrentCount;
\r
932 // generate hash-value
\r
933 HashIdx = AllocHashFunction(lRequestID);
\r
935 pHashEntry = &AllocHashTable[HashIdx];
\r
936 if (pHashEntry->lRequestID == 0) {
\r
937 // Entry is empty...
\r
940 // Entry is not empy! make a list of entries for this hash value...
\r
941 // change statistical data
\r
942 // if this happens often, you should increase the hah size or change the heash-function;
\r
943 // to fasten the allocation time
\r
944 AllocHashCollisions++;
\r
945 AllocHashCurrentCollisions++;
\r
946 if (AllocHashCurrentCollisions > AllocHashMaxCollisions)
\r
947 AllocHashMaxCollisions = AllocHashCurrentCollisions;
\r
949 while(pHashEntry->Next != NULL) {
\r
950 pHashEntry = pHashEntry->Next;
\r
953 pHashEntry->Next = (AllocHashEntryType*) calloc(sizeof(AllocHashEntryType), 1);
\r
954 pHashEntry = pHashEntry->Next;
\r
957 pHashEntry->lRequestID = lRequestID; // Key-Word
\r
958 pHashEntry->nDataSize = nDataSize;
\r
959 pHashEntry->Next = NULL;
\r
960 // Get EIP and save it in the record
\r
961 pHashEntry->dwEIPOffset = Context.Eip;
\r
962 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
\r
963 // Could not read memory... remove everything...
\r
964 memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
\r
965 pHashEntry->dwEIPLen = 0;
\r
966 pHashEntry->dwEIPOffset = 0;
\r
969 // Get ESP and save it in the record
\r
970 pHashEntry->dwESPOffset = Context.Ebp;
\r
971 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
\r
972 // Could not read memory... remove everything...
\r
973 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
\r
974 pHashEntry->dwESPLen = 0;
\r
975 pHashEntry->dwESPOffset = 0;
\r
977 // Check if I tried to read too much...
\r
978 if (GetLastError() == ERROR_PARTIAL_COPY)
\r
980 // ask how many I can read:
\r
981 MEMORY_BASIC_INFORMATION MemBuffer;
\r
982 DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
\r
985 // calculate the length
\r
986 DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
\r
987 if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
\r
989 // try to read it again (with the shorter length)
\r
990 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
\r
992 // ok, now everything goes wrong... remove it...
\r
993 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
\r
994 pHashEntry->dwESPLen = 0;
\r
995 pHashEntry->dwESPOffset = 0;
\r
999 pHashEntry->dwESPOffset = Context.Ebp;
\r
1002 } // VirtualQuery was successfully
\r
1003 } // ERROR_PARTIAL_COPY
\r
1008 // If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!
\r
1009 // If the Key was found, a pointer to the entry is returned
\r
1010 static AllocHashEntryType *AllocHashFind(long lRequestID) {
\r
1012 AllocHashEntryType *pHashEntry;
\r
1014 // get the Hash-Value
\r
1015 HashIdx = AllocHashFunction(lRequestID);
\r
1017 // Just do some simple checks:
\r
1018 _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
\r
1020 pHashEntry = &AllocHashTable[HashIdx];
\r
1021 while(pHashEntry != NULL) {
\r
1022 if (pHashEntry->lRequestID == lRequestID) {
\r
1023 return pHashEntry;
\r
1025 pHashEntry = pHashEntry->Next;
\r
1028 // entry was not found!
\r
1029 return (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
\r
1030 } // AllocHashFind
\r
1032 // AllocHashRemove
\r
1033 // Return: FALSE (0) : Key was found and removed/marked
\r
1034 // TRUE (!=0): Key was not found
\r
1035 static BOOL AllocHashRemove(long lRequestID) {
\r
1037 AllocHashEntryType *pHashEntry, *pHashEntryLast;
\r
1039 // get the Hash-Value
\r
1040 HashIdx = AllocHashFunction(lRequestID);
\r
1042 // Just do some simple checks:
\r
1043 _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
\r
1045 pHashEntryLast = NULL;
\r
1046 pHashEntry = &AllocHashTable[HashIdx];
\r
1047 while(pHashEntry != NULL) {
\r
1048 if (pHashEntry->lRequestID == lRequestID) {
\r
1049 #ifdef HASH_ENTRY_REMOVE_AT_FREE
\r
1051 AllocHashCurrentCount--;
\r
1052 // release my memory
\r
1053 if (pHashEntryLast == NULL) {
\r
1054 // It is an entry in the table, so do not release this memory
\r
1055 if (pHashEntry->Next == NULL) {
\r
1056 // It was the last entry, so empty the table entry
\r
1057 memset(&AllocHashTable[HashIdx], 0, sizeof(AllocHashTable[HashIdx]));
\r
1060 // There are some more entries, so shorten the list
\r
1061 AllocHashEntryType *pTmp = pHashEntry->Next;
\r
1062 *pHashEntry = *(pHashEntry->Next);
\r
1068 // now, I am in an dynamic allocated entry
\r
1069 // it was a collision, so decrease the current collision count
\r
1070 AllocHashCurrentCollisions--;
\r
1071 pHashEntryLast->Next = pHashEntry->Next;
\r
1076 // increase the Remove-Count and let the objet stay in memory
\r
1077 pHashEntry->cRemovedFlag++;
\r
1081 pHashEntryLast = pHashEntry;
\r
1082 pHashEntry = pHashEntry->Next;
\r
1085 // if we are here, we could not find the RequestID
\r
1089 // ReadProcMemoryFromHash
\r
1090 // Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries
\r
1091 static BOOL __stdcall ReadProcMemoryFromHash(HANDLE hRequestID, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
\r
1092 // Try to find the RequestID
\r
1093 AllocHashEntryType *pHashEntry;
\r
1094 *lpNumberOfBytesRead = 0;
\r
1096 pHashEntry = AllocHashFind((LONG) hRequestID);
\r
1097 if (pHashEntry == (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
\r
1098 // Not found, so I cannot return any memory
\r
1099 *lpNumberOfBytesRead = 0;
\r
1102 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
\r
1103 // Memory is located in ESP:
\r
1104 // Calculate the offset
\r
1105 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
\r
1106 DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
\r
1107 memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
\r
1108 *lpNumberOfBytesRead = dwSize;
\r
1109 if (dwSize != nSize)
\r
1113 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
\r
1114 // Memory is located in EIP:
\r
1115 // Calculate the offset
\r
1116 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
\r
1117 DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
\r
1118 memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
\r
1119 *lpNumberOfBytesRead = dwSize;
\r
1120 if (dwSize != nSize)
\r
1124 if (*lpNumberOfBytesRead == 0) // Memory could not be found
\r
1130 // AllocHashOutLeaks
\r
1131 // Write all Memory (with callstack) which was not freed yet
\r
1132 // Returns the number of bytes, that are not freed (leaks)
\r
1133 ULONG AllocHashOutLeaks(FILE *fFile) {
\r
1135 AllocHashEntryType *pHashEntry;
\r
1136 ULONG ulCount = 0;
\r
1137 ULONG ulLeaksByte = 0;
\r
1139 // Move throu every entry
\r
1140 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
\r
1141 pHashEntry = &AllocHashTable[ulTemp];
\r
1142 if (pHashEntry->lRequestID != 0) {
\r
1143 while(pHashEntry != NULL) {
\r
1144 if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
\r
1146 if (g_CallstackOutputType == ACOutput_XML)
\r
1147 _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
\r
1149 _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
\r
1151 memset( &c, '\0', sizeof c );
\r
1152 c.Eip = pHashEntry->dwEIPOffset;
\r
1153 c.Ebp = pHashEntry->dwESPOffset;
\r
1154 ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromHash, (HANDLE) pHashEntry->lRequestID);
\r
1155 // Count the number of leaky bytes
\r
1156 if (pHashEntry->nDataSize > 0)
\r
1157 ulLeaksByte += pHashEntry->nDataSize;
\r
1159 ulLeaksByte++; // If memory was allocated with zero bytes, then just increase the counter 1
\r
1161 if (g_CallstackOutputType == ACOutput_XML)
\r
1162 _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
\r
1164 pHashEntry = pHashEntry->Next;
\r
1168 if (g_CallstackOutputType != ACOutput_XML)
\r
1169 _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
\r
1170 return ulLeaksByte;
\r
1171 } // AllocHashOutLeaks
\r
1173 // Write all used memory to a file
\r
1174 void AllocHashOut(FILE *fFile) {
\r
1176 AllocHashEntryType *pHashEntry;
\r
1178 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
\r
1179 pHashEntry = &AllocHashTable[ulTemp];
\r
1180 if (pHashEntry->lRequestID != 0) {
\r
1181 while(pHashEntry != NULL) {
\r
1182 if (g_CallstackOutputType == ACOutput_XML)
\r
1183 _ftprintf(fFile, _T("<MEMUSED requestID=\"%u\" size=\"%u\"\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
\r
1185 _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
\r
1186 pHashEntry = pHashEntry->Next;
\r
1192 /*******************************************************************************
\r
1193 * Ende der Hash-Tabelle
\r
1194 *******************************************************************************/
\r
1197 // The follwoing is copied from dbgint.h:
\r
1198 // <CRT_INTERNALS>
\r
1200 * For diagnostic purpose, blocks are allocated with extra information and
\r
1201 * stored in a doubly-linked list. This makes all blocks registered with
\r
1202 * how big they are, when they were allocated, and what they are used for.
\r
1205 #define nNoMansLandSize 4
\r
1207 typedef struct _CrtMemBlockHeader
\r
1209 struct _CrtMemBlockHeader * pBlockHeaderNext;
\r
1210 struct _CrtMemBlockHeader * pBlockHeaderPrev;
\r
1211 char * szFileName;
\r
1214 /* These items are reversed on Win64 to eliminate gaps in the struct
\r
1215 * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
\r
1216 * maintained in the debug heap.
\r
1220 #else /* _WIN64 */
\r
1223 #endif /* _WIN64 */
\r
1225 unsigned char gap[nNoMansLandSize];
\r
1227 * unsigned char data[nDataSize];
\r
1228 * unsigned char anotherGap[nNoMansLandSize];
\r
1230 } _CrtMemBlockHeader;
\r
1231 #define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
\r
1232 #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
\r
1234 // </CRT_INTERNALS>
\r
1240 static BOOL g_bInitialized = FALSE;
\r
1241 static HINSTANCE g_hImagehlpDll = NULL;
\r
1243 static DWORD g_dwShowCount = 0; // increase at every ShowStack-Call
\r
1244 static CRITICAL_SECTION g_csFileOpenClose = {0};
\r
1246 // Is used for syncronising call to MyAllocHook (to prevent reentrant calls)
\r
1247 static LONG g_lMallocCalled = 0;
\r
1249 static _CRT_ALLOC_HOOK pfnOldCrtAllocHook = NULL;
\r
1251 // Deaktivate AllocHook, by increasing the Syncronisation-Counter
\r
1252 //static void DeactivateMallocStackwalker(void) {
\r
1253 // InterlockedIncrement(&g_lMallocCalled);
\r
1257 // MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
\r
1258 // Special case for VC 5
\r
1259 #if _MSC_VER <= 1100
\r
1260 static int MyAllocHook(int nAllocType, void *pvData,
\r
1261 size_t nSize, int nBlockUse, long lRequest,
\r
1262 const char * szFileName, int nLine ) {
\r
1264 static int MyAllocHook(int nAllocType, void *pvData,
\r
1265 size_t nSize, int nBlockUse, long lRequest,
\r
1266 const unsigned char * szFileName, int nLine ) {
\r
1268 static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
\r
1269 static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };
\r
1271 #ifdef IGNORE_CRT_ALLOC
\r
1272 if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations
\r
1275 extern int _crtDbgFlag;
\r
1276 if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )
\r
1278 // Someone has disabled that the runtime should log this allocation
\r
1279 // so we do not log this allocation
\r
1280 if (pfnOldCrtAllocHook != NULL)
\r
1281 pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
\r
1285 // Prevent from reentrat calls
\r
1286 if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called
\r
1287 InterlockedDecrement(&g_lMallocCalled);
\r
1288 // call the previous alloc hook
\r
1289 if (pfnOldCrtAllocHook != NULL)
\r
1290 pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
\r
1294 if (g_ulShowStackAtAlloc > 0) {
\r
1295 AllocCheckFileOpen(); // Open logfile
\r
1298 _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );
\r
1299 _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
\r
1301 if (nAllocType == _HOOK_FREE) { // freeing
\r
1302 // Try to get the header information
\r
1303 if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
\r
1305 _CrtMemBlockHeader *pHead;
\r
1306 // get a pointer to memory block header
\r
1307 pHead = pHdr(pvData);
\r
1308 nSize = pHead->nDataSize;
\r
1309 lRequest = pHead->lRequest; // This is the ID!
\r
1311 if (pHead->nBlockUse == _IGNORE_BLOCK)
\r
1313 InterlockedDecrement(&g_lMallocCalled);
\r
1314 if (pfnOldCrtAllocHook != NULL)
\r
1315 pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
\r
1321 if (g_ulShowStackAtAlloc > 0) {
\r
1322 _ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),
\r
1323 operation[nAllocType], nSize, blockType[_BLOCK_TYPE(nBlockUse)], lRequest );
\r
1324 if ( pvData != NULL )
\r
1325 _ftprintf( g_fFile, _T(" at 0x%X"), pvData );
\r
1326 _ftprintf(g_fFile, _T("\n"));
\r
1329 if (nAllocType == _HOOK_FREE) { // freeing:
\r
1330 if (lRequest != 0) { // RequestID was found
\r
1332 // Try to find the RequestID in the Hash-Table, mark it that it was freed
\r
1333 bRet = AllocHashRemove(lRequest);
\r
1334 if(g_ulShowStackAtAlloc > 0) {
\r
1335 if (bRet == FALSE) {
\r
1336 // RequestID not found!
\r
1337 _ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);
\r
1339 } // g_ulShowStackAtAlloc > 0
\r
1342 if(g_ulShowStackAtAlloc > 0) {
\r
1343 // No valid RequestID found, display error
\r
1344 _ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);
\r
1350 if (nAllocType == _HOOK_REALLOC) { // re-allocating
\r
1351 // Try to get the header information
\r
1352 if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
\r
1354 LONG lReallocRequest;
\r
1356 _CrtMemBlockHeader *pHead;
\r
1357 // get a pointer to memory block header
\r
1358 pHead = pHdr(pvData);
\r
1359 // Try to find the RequestID in the Hash-Table, mark it that it was freed
\r
1360 lReallocRequest = pHead->lRequest;
\r
1361 bRet = AllocHashRemove(lReallocRequest);
\r
1362 if (g_ulShowStackAtAlloc > 0) {
\r
1363 if (bRet == FALSE) {
\r
1364 // RequestID not found!
\r
1365 _ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);
\r
1368 _ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);
\r
1370 } // g_ulShowStackAtAlloc > 0
\r
1371 } // ValidHeapPointer
\r
1372 } // re-allocating
\r
1374 if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
\r
1375 InterlockedDecrement(&g_lMallocCalled);
\r
1376 // call the previous alloc hook
\r
1377 if (pfnOldCrtAllocHook != NULL)
\r
1378 pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
\r
1383 if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
\r
1384 GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) == 0) {
\r
1385 // Something was wrong...
\r
1386 _ftprintf(g_fFile, _T("###### Could not call 'DuplicateHandle' successfully\n"));
\r
1387 InterlockedDecrement(&g_lMallocCalled);
\r
1388 // call the previous alloc hook
\r
1389 if (pfnOldCrtAllocHook != NULL)
\r
1390 pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
\r
1395 memset( &c, '\0', sizeof c );
\r
1396 c.ContextFlags = CONTEXT_FULL;
\r
1398 // Get the context of this thread
\r
1400 // init CONTEXT record so we know where to start the stackwalk
\r
1401 if ( MyGetCurrentThreadContext( hThread, &c ) == 0) {
\r
1402 if(g_ulShowStackAtAlloc > 1) {
\r
1403 _ftprintf(g_fFile, _T("###### Could not call 'GetThreadContext' successfully\n"));
\r
1405 InterlockedDecrement(&g_lMallocCalled);
\r
1406 // call the previous alloc hook
\r
1407 if (pfnOldCrtAllocHook != NULL)
\r
1408 pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
\r
1409 CloseHandle(hThread);
\r
1410 return TRUE; // could not get context
\r
1422 if(g_ulShowStackAtAlloc > 1) {
\r
1423 if(g_ulShowStackAtAlloc > 2) {
\r
1424 // output the callstack
\r
1425 ShowStack( hThread, c, g_fFile);
\r
1428 // Output only (re)allocs
\r
1429 if (nAllocType != _HOOK_FREE) {
\r
1430 ShowStack( hThread, c, g_fFile);
\r
1433 } // g_ulShowStackAtAlloc > 1
\r
1434 CloseHandle( hThread );
\r
1436 // Only isert in the Hash-Table if it is not a "freeing"
\r
1437 if (nAllocType != _HOOK_FREE) {
\r
1438 if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
\r
1439 AllocHashInsert(lRequest, c, nSize);
\r
1442 InterlockedDecrement(&g_lMallocCalled);
\r
1443 // call the previous alloc hook
\r
1444 if (pfnOldCrtAllocHook != NULL)
\r
1445 pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
\r
1446 return TRUE; // allow the memory operation to proceed
\r
1452 // ##########################################################################################
\r
1453 // ##########################################################################################
\r
1454 // ##########################################################################################
\r
1455 // ##########################################################################################
\r
1457 #define gle (GetLastError())
\r
1458 #define lenof(a) (sizeof(a) / sizeof((a)[0]))
\r
1459 #define MAXNAMELEN 1024 // max name length for found symbols
\r
1460 #define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL64 )
\r
1461 #define TTBUFLEN 8096 // for a temp buffer (2^13)
\r
1466 typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
\r
1469 // SymFunctionTableAccess64()
\r
1470 typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
\r
1471 tSFTA pSFTA = NULL;
\r
1473 // SymGetLineFromAddr64()
\r
1474 typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
\r
1475 OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
\r
1476 tSGLFA pSGLFA = NULL;
\r
1478 // SymGetModuleBase64()
\r
1479 typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
\r
1480 tSGMB pSGMB = NULL;
\r
1482 // SymGetModuleInfo64()
\r
1483 typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo );
\r
1484 tSGMI pSGMI = NULL;
\r
1486 // SymGetOptions()
\r
1487 typedef DWORD (__stdcall *tSGO)( VOID );
\r
1490 // SymGetSymFromAddr64()
\r
1491 typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
\r
1492 OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
\r
1493 tSGSFA pSGSFA = NULL;
\r
1495 // SymInitialize()
\r
1496 typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
\r
1499 // SymLoadModule64()
\r
1500 typedef DWORD (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
\r
1501 IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
\r
1504 // SymSetOptions()
\r
1505 typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
\r
1509 typedef BOOL (__stdcall *tSW)(
\r
1510 DWORD MachineType,
\r
1513 LPSTACKFRAME64 StackFrame,
\r
1514 PVOID ContextRecord,
\r
1515 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
\r
1516 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
\r
1517 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
\r
1518 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
\r
1521 // UnDecorateSymbolName()
\r
1522 typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
\r
1523 DWORD UndecoratedLength, DWORD Flags );
\r
1524 tUDSN pUDSN = NULL;
\r
1528 struct ModuleEntry
\r
1530 std::string imageName;
\r
1531 std::string moduleName;
\r
1532 DWORD baseAddress;
\r
1535 typedef std::vector< ModuleEntry > ModuleList;
\r
1536 typedef ModuleList::iterator ModuleListIter;
\r
1538 // **************************************** ToolHelp32 ************************
\r
1539 #define MAX_MODULE_NAME32 255
\r
1540 #define TH32CS_SNAPMODULE 0x00000008
\r
1541 #pragma pack( push, 8 )
\r
1542 typedef struct tagMODULEENTRY32
\r
1545 DWORD th32ModuleID; // This module
\r
1546 DWORD th32ProcessID; // owning process
\r
1547 DWORD GlblcntUsage; // Global usage count on the module
\r
1548 DWORD ProccntUsage; // Module usage count in th32ProcessID's context
\r
1549 BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
\r
1550 DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
\r
1551 HMODULE hModule; // The hModule of this module in th32ProcessID's context
\r
1552 char szModule[MAX_MODULE_NAME32 + 1];
\r
1553 char szExePath[MAX_PATH];
\r
1555 typedef MODULEENTRY32 * PMODULEENTRY32;
\r
1556 typedef MODULEENTRY32 * LPMODULEENTRY32;
\r
1557 #pragma pack( pop )
\r
1561 static bool GetModuleListTH32(ModuleList& modules, DWORD pid, FILE *fLogFile)
\r
1563 // CreateToolhelp32Snapshot()
\r
1564 typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
\r
1565 // Module32First()
\r
1566 typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
\r
1568 typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
\r
1570 // try both dlls...
\r
1571 const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
\r
1572 HINSTANCE hToolhelp;
\r
1579 me.dwSize = sizeof(me);
\r
1584 for (i = 0; i<lenof(dllname); i++ )
\r
1586 hToolhelp = LoadLibrary( dllname[i] );
\r
1587 if (hToolhelp == NULL)
\r
1589 pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
\r
1590 pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
\r
1591 pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
\r
1592 if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )
\r
1593 break; // found the functions!
\r
1594 FreeLibrary(hToolhelp);
\r
1598 if (hToolhelp == NULL)
\r
1601 hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
\r
1602 if (hSnap == (HANDLE) -1)
\r
1605 keepGoing = !!pM32F( hSnap, &me );
\r
1608 e.imageName = me.szExePath;
\r
1609 e.moduleName = me.szModule;
\r
1610 e.baseAddress = (DWORD) me.modBaseAddr;
\r
1611 e.size = me.modBaseSize;
\r
1612 modules.push_back( e );
\r
1613 keepGoing = !!pM32N( hSnap, &me );
\r
1616 CloseHandle(hSnap);
\r
1617 FreeLibrary(hToolhelp);
\r
1619 return modules.size() != 0;
\r
1620 } // GetModuleListTH32
\r
1623 // **************************************** PSAPI ************************
\r
1624 typedef struct _MODULEINFO {
\r
1625 LPVOID lpBaseOfDll;
\r
1626 DWORD SizeOfImage;
\r
1627 LPVOID EntryPoint;
\r
1628 } MODULEINFO, *LPMODULEINFO;
\r
1630 static bool GetModuleListPSAPI(ModuleList &modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)
\r
1632 // EnumProcessModules()
\r
1633 typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
\r
1634 // GetModuleFileNameEx()
\r
1635 typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
\r
1636 // GetModuleBaseName()
\r
1637 typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
\r
1638 // GetModuleInformation()
\r
1639 typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );
\r
1651 HMODULE *hMods = 0;
\r
1654 hPsapi = LoadLibrary( _T("psapi.dll") );
\r
1655 if ( hPsapi == 0 )
\r
1660 pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
\r
1661 pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
\r
1662 pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
\r
1663 pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
\r
1664 if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )
\r
1666 // we couldn´t find all functions
\r
1667 FreeLibrary( hPsapi );
\r
1671 hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
\r
1672 tt = (char*) malloc(sizeof(char) * TTBUFLEN);
\r
1674 if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
\r
1676 _ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
\r
1680 if ( cbNeeded > TTBUFLEN )
\r
1682 _ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
\r
1686 for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
\r
1688 // base address, size
\r
1689 pGMI(hProcess, hMods[i], &mi, sizeof mi );
\r
1690 e.baseAddress = (DWORD) mi.lpBaseOfDll;
\r
1691 e.size = mi.SizeOfImage;
\r
1692 // image file name
\r
1694 pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
\r
1698 pGMBN(hProcess, hMods[i], tt, TTBUFLEN );
\r
1699 e.moduleName = tt;
\r
1701 modules.push_back(e);
\r
1706 FreeLibrary(hPsapi);
\r
1710 return modules.size() != 0;
\r
1711 } // GetModuleListPSAPI
\r
1714 static bool GetModuleList(ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)
\r
1716 // first try toolhelp32
\r
1717 if (GetModuleListTH32(modules, pid, fLogFile) )
\r
1720 return GetModuleListPSAPI(modules, pid, hProcess, fLogFile);
\r
1721 } // GetModuleList
\r
1724 static void EnumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid, FILE *fLogFile )
\r
1726 static ModuleList modules;
\r
1727 static ModuleListIter it;
\r
1730 // fill in module list
\r
1731 GetModuleList(modules, pid, hProcess, fLogFile);
\r
1733 for ( it = modules.begin(); it != modules.end(); ++ it )
\r
1735 // SymLoadModule() wants writeable strings
\r
1736 img = strdup(it->imageName.c_str());
\r
1737 mod = strdup(it->moduleName.c_str());
\r
1739 pSLM( hProcess, 0, img, mod, it->baseAddress, it->size );
\r
1745 } // EnumAndLoadModuleSymbols
\r
1747 static int InitStackWalk(void)
\r
1749 if (g_bInitialized != FALSE)
\r
1750 return 0; // already initialized
\r
1752 // 02-12-19: Now we only support dbghelp.dll!
\r
1753 // To use it on NT you have to install the redistrubutable for DBGHELP.DLL
\r
1754 g_hImagehlpDll = LoadLibrary( _T("dbghelp.dll") );
\r
1755 if ( g_hImagehlpDll == NULL )
\r
1757 printf( "LoadLibrary( \"dbghelp.dll\" ): GetLastError = %lu\n", gle );
\r
1758 g_bInitialized = FALSE;
\r
1762 // now we only support the newer dbghlp.dll with the "64"-functions (StackWalk64, a.s.o.)
\r
1763 // If your dbghlp.dll does not support this, please download the redistributable from MS
\r
1764 // Normally from: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=CD1FC4B2-0885-47F4-AF45-7FD5E14DB6C0
\r
1766 pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );
\r
1767 pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess64" );
\r
1768 pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr64" );
\r
1769 pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase64" );
\r
1770 pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo64" );
\r
1771 pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );
\r
1772 pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr64" );
\r
1773 pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );
\r
1774 pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );
\r
1775 pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk64" );
\r
1776 pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );
\r
1777 pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule64" );
\r
1779 if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
\r
1780 pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
\r
1781 pSW == NULL || pUDSN == NULL || pSLM == NULL )
\r
1783 printf( "GetProcAddress(): some required function not found.\n" );
\r
1784 FreeLibrary( g_hImagehlpDll );
\r
1785 g_bInitialized = FALSE;
\r
1789 g_bInitialized = TRUE;
\r
1790 InitializeCriticalSection(&g_csFileOpenClose);
\r
1794 // This function if NOT multi-threading capable
\r
1795 // It should only be called from the main-Function!
\r
1796 int InitAllocCheckWN(eAllocCheckOutput eOutput, LPCTSTR pszFileName, ULONG ulShowStackAtAlloc) {
\r
1797 if (g_bInitialized) {
\r
1798 return 2; // already initialized!
\r
1800 if (ulShowStackAtAlloc <= 3)
\r
1801 g_ulShowStackAtAlloc = ulShowStackAtAlloc;
\r
1803 g_ulShowStackAtAlloc = 0;
\r
1805 if (pszFileName != NULL)
\r
1806 g_pszAllocLogName = _tcsdup(pszFileName);
\r
1808 g_pszAllocLogName = NULL;
\r
1810 g_CallstackOutputType = eOutput;
\r
1815 #ifdef WITH_IMALLOC_SPY
\r
1817 // erzeuge mein malloc-Spy object
\r
1818 LPMALLOCSPY pMallocSpy = new CMallocSpy(); // wird später durch Release freigegeben
\r
1819 if (pMallocSpy != NULL)
\r
1821 // CoInitilize(); // ??? Ist dies notwendig ?
\r
1822 hr = CoRegisterMallocSpy(pMallocSpy);
\r
1825 _tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr);
\r
1830 // save the previous alloc hook
\r
1831 pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook);
\r
1834 return InitStackWalk();
\r
1835 } // InitAllocCheckWN
\r
1837 static TCHAR s_szExceptionLogFileName[_MAX_PATH] = _T("\\exceptions.log"); // default
\r
1838 static BOOL s_bUnhandledExeptionFilterSet = FALSE;
\r
1839 static LONG __stdcall CrashHandlerExceptionFilter(EXCEPTION_POINTERS* pExPtrs)
\r
1841 if (pExPtrs->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
\r
1843 static char MyStack[1024*128]; // be sure that we have enought space...
\r
1844 // it assumes that DS and SS are the same!!! (this is the case for Win32)
\r
1845 // change the stack only if the selectors are the same (this is the case for Win32)
\r
1846 //__asm push offset MyStack[1024*128];
\r
1848 __asm mov eax,offset MyStack[1024*128];
\r
1849 __asm mov esp,eax;
\r
1853 lRet = StackwalkFilter(pExPtrs, /*EXCEPTION_CONTINUE_SEARCH*/EXCEPTION_EXECUTE_HANDLER, s_szExceptionLogFileName);
\r
1854 TCHAR lString[500];
\r
1855 _stprintf(lString,
\r
1856 _T("*** Unhandled Exception!\n")
\r
1857 _T(" ExpCode: 0x%8.8X\n")
\r
1858 _T(" ExpFlags: %d\n")
\r
1859 _T(" ExpAddress: 0x%8.8X\n")
\r
1860 _T(" Please report!"),
\r
1861 pExPtrs->ExceptionRecord->ExceptionCode,
\r
1862 pExPtrs->ExceptionRecord->ExceptionFlags,
\r
1863 pExPtrs->ExceptionRecord->ExceptionAddress);
\r
1864 FatalAppExit(-1,lString);
\r
1868 int InitAllocCheck(eAllocCheckOutput eOutput, BOOL bSetUnhandledExeptionFilter, ULONG ulShowStackAtAlloc) // will create the filename by itself
\r
1870 TCHAR szModName[_MAX_PATH];
\r
1871 if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
\r
1873 _tcscpy(s_szExceptionLogFileName, szModName);
\r
1874 if (eOutput == ACOutput_XML)
\r
1875 _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
\r
1877 _tcscat(s_szExceptionLogFileName, _T(".exp.log"));
\r
1879 if (eOutput == ACOutput_XML)
\r
1880 _tcscat(szModName, _T(".mem.xml-leaks"));
\r
1882 _tcscat(szModName, _T(".mem.log"));
\r
1886 if (eOutput == ACOutput_XML)
\r
1887 _tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default
\r
1889 _tcscpy(szModName, _T("\\mem-leaks.log")); // default
\r
1892 if ((bSetUnhandledExeptionFilter != FALSE) && (s_bUnhandledExeptionFilterSet == FALSE) )
\r
1894 // set global exception handler (for handling all unhandled exceptions)
\r
1895 SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
\r
1896 s_bUnhandledExeptionFilterSet = TRUE;
\r
1899 return InitAllocCheckWN(eOutput, szModName, ulShowStackAtAlloc);
\r
1903 // This function if NOT multi-threading capable
\r
1904 // It should only be called from the main-Function!
\r
1905 // Returns the number of bytes that are not freed (leaks)
\r
1906 ULONG DeInitAllocCheck(void) {
\r
1908 if (g_bInitialized) {
\r
1911 InterlockedIncrement(&g_lMallocCalled); // No deactivate MyAllocHook, because StackWalker will allocate some memory)
\r
1912 ulRet = AllocHashDeinit(); // output the not freed memory
\r
1913 // remove the hook and set the old one
\r
1914 _CrtSetAllocHook(pfnOldCrtAllocHook);
\r
1916 #ifdef WITH_IMALLOC_SPY
\r
1917 CoRevokeMallocSpy();
\r
1922 EnterCriticalSection(&g_csFileOpenClose); // wait until a running stack dump was created
\r
1923 g_bInitialized = FALSE;
\r
1925 // de-init symbol handler etc. (SymCleanup())
\r
1927 pSC( GetCurrentProcess() );
\r
1928 FreeLibrary( g_hImagehlpDll );
\r
1930 LeaveCriticalSection(&g_csFileOpenClose);
\r
1931 if (g_pszAllocLogName != NULL) {
\r
1932 free(g_pszAllocLogName);
\r
1933 g_pszAllocLogName = NULL;
\r
1935 if (g_fFile != NULL) {
\r
1940 DeleteCriticalSection(&g_csFileOpenClose);
\r
1941 InterlockedDecrement(&g_lMallocCalled);
\r
1944 if (s_bUnhandledExeptionFilterSet != TRUE)
\r
1946 SetUnhandledExceptionFilter(NULL);
\r
1947 s_bUnhandledExeptionFilterSet = FALSE;
\r
1950 } // DeInitAllocCheck
\r
1954 void OnlyInstallUnhandeldExceptionFilter(eAllocCheckOutput eOutput)
\r
1956 if (s_bUnhandledExeptionFilterSet == FALSE)
\r
1958 TCHAR szModName[_MAX_PATH];
\r
1959 if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
\r
1961 _tcscpy(s_szExceptionLogFileName, szModName);
\r
1962 if (eOutput == ACOutput_XML)
\r
1963 _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
\r
1965 _tcscat(s_szExceptionLogFileName, _T(".exp.log"));
\r
1967 if (eOutput == ACOutput_XML)
\r
1968 _tcscat(szModName, _T(".mem.xml-leaks"));
\r
1970 _tcscat(szModName, _T(".mem.log"));
\r
1974 if (eOutput == ACOutput_XML)
\r
1975 _tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default
\r
1977 _tcscpy(szModName, _T("\\mem-leaks.log")); // default
\r
1979 // set it again; WARNING: this will override the setting for a possible AllocCheck-Setting
\r
1980 g_CallstackOutputType = eOutput;
\r
1982 // set global exception handler (for handling all unhandled exceptions)
\r
1983 SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
\r
1984 s_bUnhandledExeptionFilterSet = TRUE;
\r
1990 static TCHAR *GetExpectionCodeText(DWORD dwExceptionCode) {
\r
1991 switch(dwExceptionCode) {
\r
1992 case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");
\r
1993 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");
\r
1994 case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");
\r
1995 case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");
\r
1996 case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");
\r
1997 case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");
\r
1998 case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");
\r
1999 case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");
\r
2000 case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");
\r
2001 case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");
\r
2002 case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");
\r
2003 case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");
\r
2004 case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");
\r
2005 case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");
\r
2006 case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");
\r
2007 case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");
\r
2008 case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");
\r
2009 case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");
\r
2010 case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");
\r
2011 case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");
\r
2012 case DBG_CONTROL_C : return _T("DBG CONTROL C ");
\r
2014 return _T("<unkown exception>");
\r
2016 } // GetExpectionCodeText
\r
2018 // Function is not multi-threading safe, because of static char!
\r
2019 static TCHAR *GetAdditionalExpectionCodeText(PEXCEPTION_RECORD pExceptionRecord) {
\r
2020 static TCHAR szTemp[100];
\r
2022 switch(pExceptionRecord->ExceptionCode) {
\r
2023 case EXCEPTION_ACCESS_VIOLATION:
\r
2024 if (pExceptionRecord->NumberParameters == 2) {
\r
2025 switch(pExceptionRecord->ExceptionInformation[0]) {
\r
2026 case 0: // read attempt
\r
2027 _stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
\r
2029 case 1: // write attempt
\r
2030 _stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
\r
2035 } // if (pExceptionRecord->NumberParameters == 2)
\r
2039 } // switch(pExceptionRecord->ExceptionCode)
\r
2040 } // GetAdditionalExpectionCodeText
\r
2042 std::string SimpleXMLEncode(PCSTR szText)
\r
2044 std::string szRet;
\r
2046 for (size_t i=0; i<strlen(szText); i++)
\r
2051 szRet.append("&");
\r
2054 szRet.append("<");
\r
2057 szRet.append(">");
\r
2060 szRet.append(""");
\r
2063 szRet.append("'");
\r
2066 szRet += szText[i];
\r
2073 // #################################################################################
\r
2074 // #################################################################################
\r
2075 // Here the Stackwalk-Part begins.
\r
2076 // Some of the code is from an example from a book
\r
2077 // But I couldn´t find the reference anymore... sorry...
\r
2078 // If someone knowns, please let me know...
\r
2079 // #################################################################################
\r
2080 // #################################################################################
\r
2083 // if you use C++ exception handling: install a translator function
\r
2084 // with set_se_translator(). In the context of that function (but *not*
\r
2085 // afterwards), you can either do your stack dump, or save the CONTEXT
\r
2086 // record as a local copy. Note that you must do the stack sump at the
\r
2087 // earliest opportunity, to avoid the interesting stackframes being gone
\r
2088 // by the time you do the dump.
\r
2091 // - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht
\r
2092 // - EXCEPTION_CONTINUE_EXECUTION:
\r
2093 // - EXCEPTION_EXECUTE_HANDLER:
\r
2094 DWORD StackwalkFilter( EXCEPTION_POINTERS *ep, DWORD status, LPCTSTR pszLogFile)
\r
2097 FILE *fFile = stdout; // default to stdout
\r
2099 if (pszLogFile != NULL) { // a filename is provided
\r
2100 // Open the logfile
\r
2101 fFile = _tfopen(pszLogFile, _T("a"));
\r
2102 if (fFile != NULL) { // Is the file too big?
\r
2104 fseek(fFile, 0, SEEK_END);
\r
2105 size = ftell(fFile); // Get the size of the file
\r
2106 if (size >= LOG_FILE_MAX_SIZE) {
\r
2107 TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
\r
2108 // It is too big...
\r
2110 _tcscpy(pszTemp, pszLogFile);
\r
2111 _tcscat(pszTemp, _T(".old"));
\r
2112 _tremove(pszTemp); // Remove an old file, if exists
\r
2113 _trename(pszLogFile, pszTemp); // rename the actual file
\r
2114 fFile = _tfopen(pszLogFile, _T("w")); // create a new file
\r
2118 } // if (pszLogFile != NULL)
\r
2119 if (fFile == NULL) {
\r
2123 // Write infos about the exception
\r
2124 if (g_CallstackOutputType == ACOutput_XML)
\r
2126 _ftprintf(fFile, _T("<EXCEPTION code=\"%8.8X\" addr=\"%8.8X\" "),
\r
2127 ep->ExceptionRecord->ExceptionCode,
\r
2128 ep->ExceptionRecord->ExceptionAddress);
\r
2129 WriteDateTime(fFile, TRUE);
\r
2130 _ftprintf(fFile, _T("code_desc=\"%s\" more_desc=\"%s\">\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
\r
2131 GetAdditionalExpectionCodeText(ep->ExceptionRecord));
\r
2135 _ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"),
\r
2136 ep->ExceptionRecord->ExceptionCode,
\r
2137 ep->ExceptionRecord->ExceptionAddress);
\r
2138 _ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
\r
2139 GetAdditionalExpectionCodeText(ep->ExceptionRecord));
\r
2142 DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
\r
2143 GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS );
\r
2144 ShowStack( hThread, *(ep->ContextRecord), fFile);
\r
2145 CloseHandle( hThread );
\r
2147 if (g_CallstackOutputType == ACOutput_XML)
\r
2148 _ftprintf(fFile, _T("</EXCEPTION>\n"));
\r
2153 } // StackwalkFilter
\r
2155 void ShowStack( HANDLE hThread, CONTEXT& c, LPCTSTR pszLogFile)
\r
2157 FILE *fFile = stdout; // default to stdout
\r
2159 if (pszLogFile != NULL) { // a filename is available
\r
2160 // Open the logfile
\r
2161 fFile = _tfopen(pszLogFile, _T("a"));
\r
2162 if (fFile != NULL) { // Is the file too big?
\r
2164 fseek(fFile, 0, SEEK_END);
\r
2165 size = ftell(fFile); // Get the size of the file
\r
2166 if (size >= LOG_FILE_MAX_SIZE) {
\r
2167 TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
\r
2168 // It is too big...
\r
2170 _tcscpy(pszTemp, pszLogFile);
\r
2171 _tcscat(pszTemp, _T(".old"));
\r
2172 _tremove(pszTemp); // Remove an old file, if exists
\r
2173 _trename(pszLogFile, pszTemp); // rename the actual file
\r
2174 fFile = _tfopen(pszLogFile, _T("w")); // open new file
\r
2178 } // if (pszLogFile != NULL)
\r
2179 if (fFile == NULL) {
\r
2183 ShowStack( hThread, c, fFile);
\r
2189 static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {
\r
2190 ShowStackRM(hThread, c, fLogFile, NULL, GetCurrentProcess());
\r
2193 static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hSWProcess) {
\r
2194 // normally, call ImageNtHeader() and use machine info from PE header
\r
2195 // but we assume that it is an I386 image...
\r
2196 DWORD imageType = IMAGE_FILE_MACHINE_I386;
\r
2197 HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside but we only do the stackdump in our own process
\r
2198 int frameNum; // counts walked frames
\r
2199 DWORD64 offsetFromSymbol; // tells us how far from the symbol we were
\r
2200 DWORD offsetFromLine; // tells us how far from the line we were
\r
2201 DWORD symOptions; // symbol handler settings
\r
2203 static IMAGEHLP_SYMBOL64 *pSym = NULL;
\r
2204 char undName[MAXNAMELEN]; // undecorated name
\r
2205 char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans
\r
2206 IMAGEHLP_MODULE64 Module;
\r
2207 IMAGEHLP_LINE64 Line;
\r
2208 BOOL bXMLTagWrote;
\r
2210 std::string symSearchPath;
\r
2212 static bool bFirstTime = TRUE;
\r
2214 // If no logfile is present, outpur to "stdout"
\r
2215 if (fLogFile == NULL) {
\r
2216 fLogFile = stdout;
\r
2219 STACKFRAME64 s; // in/out stackframe
\r
2220 memset( &s, '\0', sizeof s );
\r
2222 if ( (g_bInitialized == FALSE) && (bFirstTime == TRUE) ) {
\r
2226 if (g_bInitialized == FALSE)
\r
2228 // Could not init!!!!
\r
2229 bFirstTime = FALSE;
\r
2230 _ftprintf(fLogFile, _T("%lu: Stackwalker not initialized (or was not able to initialize)!\n"), g_dwShowCount);
\r
2234 // Critical section begin...
\r
2235 EnterCriticalSection(&g_csFileOpenClose);
\r
2237 InterlockedIncrement((long*) &g_dwShowCount); // erhöhe counter
\r
2240 // NOTE: normally, the exe directory and the current directory should be taken
\r
2241 // from the target process. The current dir would be gotten through injection
\r
2242 // of a remote thread; the exe fir through either ToolHelp32 or PSAPI.
\r
2244 if (pSym == NULL) {
\r
2245 pSym = (IMAGEHLP_SYMBOL64 *) malloc( IMGSYMLEN + MAXNAMELEN );
\r
2246 if (!pSym) goto cleanup; // not enough memory...
\r
2249 if (g_CallstackOutputType != ACOutput_XML)
\r
2251 _ftprintf(fLogFile, _T("%lu: "), g_dwShowCount);
\r
2252 WriteDateTime(fLogFile);
\r
2253 _ftprintf(fLogFile, _T("\n"));
\r
2261 tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer
\r
2262 if (!tt) goto cleanup; // not enough memory...
\r
2264 // build symbol search path from:
\r
2265 symSearchPath = "";
\r
2266 // current directory
\r
2267 if ( GetCurrentDirectoryA( TTBUFLEN, tt ) )
\r
2268 symSearchPath += tt + std::string( ";" );
\r
2269 // dir with executable
\r
2270 if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )
\r
2272 for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
\r
2274 // locate the rightmost path separator
\r
2275 if ( *p == '\\' || *p == '/' || *p == ':' )
\r
2278 // if we found one, p is pointing at it; if not, tt only contains
\r
2279 // an exe name (no path), and p points before its first byte
\r
2280 if ( p != tt ) // path sep found?
\r
2282 if ( *p == ':' ) // we leave colons in place
\r
2284 *p = '\0'; // eliminate the exe name and last path sep
\r
2285 symSearchPath += tt + std::string( ";" );
\r
2288 // environment variable _NT_SYMBOL_PATH
\r
2289 if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
\r
2290 symSearchPath += tt + std::string( ";" );
\r
2291 // environment variable _NT_ALTERNATE_SYMBOL_PATH
\r
2292 if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
\r
2293 symSearchPath += tt + std::string( ";" );
\r
2294 // environment variable SYSTEMROOT
\r
2295 if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )
\r
2296 symSearchPath += tt + std::string( ";" );
\r
2300 if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
\r
2301 symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );
\r
2303 // why oh why does SymInitialize() want a writeable string?
\r
2304 strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
\r
2305 tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator
\r
2307 // init symbol handler stuff (SymInitialize())
\r
2308 if ( ! pSI( hProcess, tt, false ) )
\r
2310 if (g_CallstackOutputType != ACOutput_XML)
\r
2311 _ftprintf(fLogFile, _T("%lu: SymInitialize(): GetLastError = %lu\n"), g_dwShowCount, gle );
\r
2312 if (tt) free( tt );
\r
2316 // SymGetOptions()
\r
2317 symOptions = pSGO();
\r
2318 symOptions |= SYMOPT_LOAD_LINES;
\r
2319 symOptions &= ~SYMOPT_UNDNAME;
\r
2320 symOptions &= ~SYMOPT_DEFERRED_LOADS;
\r
2321 pSSO( symOptions ); // SymSetOptions()
\r
2323 // Enumerate modules and tell dbghlp.dll about them.
\r
2324 // On NT, this is not necessary, but it won't hurt.
\r
2325 EnumAndLoadModuleSymbols( hProcess, GetCurrentProcessId(), fLogFile );
\r
2329 } // bFirstTime = TRUE
\r
2330 bFirstTime = FALSE;
\r
2332 // init STACKFRAME for first call
\r
2333 // Notes: AddrModeFlat is just an assumption. I hate VDM debugging.
\r
2334 // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
\r
2335 // and good riddance.
\r
2336 s.AddrPC.Offset = c.Eip;
\r
2337 s.AddrPC.Mode = AddrModeFlat;
\r
2338 s.AddrFrame.Offset = c.Ebp;
\r
2339 s.AddrFrame.Mode = AddrModeFlat;
\r
2340 s.AddrStack.Offset = c.Ebp;
\r
2341 s.AddrStack.Mode = AddrModeFlat;
\r
2343 memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
\r
2344 pSym->SizeOfStruct = IMGSYMLEN;
\r
2345 pSym->MaxNameLength = MAXNAMELEN;
\r
2347 memset( &Line, '\0', sizeof Line );
\r
2348 Line.SizeOfStruct = sizeof Line;
\r
2350 memset( &Module, '\0', sizeof Module );
\r
2351 Module.SizeOfStruct = sizeof Module;
\r
2353 for ( frameNum = 0; ; ++ frameNum )
\r
2355 // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
\r
2356 // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
\r
2357 // assume that either you are done, or that the stack is so hosed that the next
\r
2358 // deeper frame could not be found.
\r
2359 // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
\r
2360 if ( ! pSW( imageType, hSWProcess, hThread, &s, NULL, ReadMemoryFunction, pSFTA, pSGMB, NULL ) )
\r
2363 bXMLTagWrote = FALSE;
\r
2365 if (g_CallstackOutputType == ACOutput_Advanced)
\r
2366 _ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);
\r
2367 if ( s.AddrPC.Offset == 0 )
\r
2369 // Special case: If we are here, we have no valid callstack entry!
\r
2370 switch(g_CallstackOutputType)
\r
2372 case ACOutput_Simple:
\r
2373 _ftprintf(fLogFile, _T("%lu: (-nosymbols- PC == 0)\n"), g_dwShowCount);
\r
2375 case ACOutput_Advanced:
\r
2376 _ftprintf(fLogFile, _T(" (-nosymbols- PC == 0)\n"));
\r
2378 case ACOutput_XML:
\r
2380 _ftprintf(fLogFile, _T("<STACKENTRY decl=\"(-nosymbols- PC == 0)\"/>\n"));
\r
2386 // we seem to have a valid PC
\r
2388 undFullName[0] = 0;
\r
2389 offsetFromSymbol = 0;
\r
2390 // show procedure info (SymGetSymFromAddr())
\r
2391 if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
\r
2393 if (g_CallstackOutputType == ACOutput_Advanced)
\r
2396 _ftprintf(fLogFile, _T(" SymGetSymFromAddr(): GetLastError = %lu\n"), gle );
\r
2398 _ftprintf(fLogFile, _T("\n"));
\r
2403 // UnDecorateSymbolName()
\r
2404 pUDSN( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY );
\r
2405 pUDSN( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );
\r
2406 if (g_CallstackOutputType == ACOutput_Advanced)
\r
2408 if (strlen(undName) > 0)
\r
2409 fprintf(fLogFile, " %s %+ld bytes\n", undName, (long) offsetFromSymbol );
\r
2412 fprintf(fLogFile, " Sig: %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );
\r
2413 strcpy(undName, pSym->Name);
\r
2415 fprintf(fLogFile, "%lu: Decl: %s\n", g_dwShowCount, undFullName );
\r
2418 //if (g_CallstackOutputType == ACOutput_XML)
\r
2419 // fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
\r
2421 // show line number info, NT5.0-method (SymGetLineFromAddr())
\r
2422 offsetFromLine = 0;
\r
2423 if ( pSGLFA != NULL )
\r
2424 { // yes, we have SymGetLineFromAddr()
\r
2425 if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromLine, &Line ) )
\r
2427 if ( (gle != 487) && (frameNum > 0) ) // ignore error for first frame
\r
2429 if (g_CallstackOutputType == ACOutput_XML)
\r
2431 _ftprintf(fLogFile, _T("<STACKENTRY "));
\r
2432 bXMLTagWrote = TRUE;
\r
2433 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
\r
2434 _ftprintf(fLogFile, _T("srcfile=\"SymGetLineFromAddr(): GetLastError = %lu\" "), gle);
\r
2437 _ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );
\r
2442 switch(g_CallstackOutputType)
\r
2444 case ACOutput_Advanced:
\r
2445 fprintf(fLogFile, "%lu: Line: %s(%lu) %+ld bytes\n", g_dwShowCount,
\r
2446 Line.FileName, Line.LineNumber, offsetFromLine );
\r
2448 case ACOutput_Simple:
\r
2449 fprintf(fLogFile, "%lu: %s(%lu) %+ld bytes (%s)\n", g_dwShowCount,
\r
2450 Line.FileName, Line.LineNumber, offsetFromLine, undName);
\r
2452 case ACOutput_XML:
\r
2453 _ftprintf(fLogFile, _T("<STACKENTRY "));
\r
2454 bXMLTagWrote = TRUE;
\r
2455 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
\r
2456 fprintf(fLogFile, "srcfile=\"%s\" line=\"%lu\" line_offset=\"%+ld\" ",
\r
2457 SimpleXMLEncode(Line.FileName).c_str(), Line.LineNumber, offsetFromLine, undName);
\r
2461 } // yes, we have SymGetLineFromAddr()
\r
2463 // show module info (SymGetModuleInfo())
\r
2464 if ( (g_CallstackOutputType == ACOutput_Advanced) || (g_CallstackOutputType == ACOutput_XML) )
\r
2466 if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )
\r
2468 if (g_CallstackOutputType == ACOutput_Advanced)
\r
2469 _ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );
\r
2472 { // got module info OK
\r
2474 switch ( Module.SymType )
\r
2477 strcpy( ty, "-nosymbols-" );
\r
2480 strcpy( ty, "COFF" );
\r
2483 strcpy( ty, "CV" );
\r
2486 strcpy( ty, "PDB" );
\r
2489 strcpy( ty, "-exported-" );
\r
2492 strcpy( ty, "-deferred-" );
\r
2495 strcpy( ty, "SYM" );
\r
2497 #if API_VERSION_NUMBER >= 9
\r
2499 strcpy( ty, "DIA" );
\r
2503 _snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
\r
2507 if (g_CallstackOutputType == ACOutput_XML)
\r
2509 // now, check if the XML-Entry is written...
\r
2510 if (bXMLTagWrote == FALSE)
\r
2512 _ftprintf(fLogFile, _T("<STACKENTRY "));
\r
2513 bXMLTagWrote = TRUE;
\r
2514 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
\r
2515 _ftprintf(fLogFile, _T("srcfile=\"\" "));
\r
2516 bXMLTagWrote = TRUE;
\r
2520 if (g_CallstackOutputType == ACOutput_Advanced)
\r
2522 fprintf(fLogFile, "%lu: Mod: %s, base: %08lxh\n", g_dwShowCount,
\r
2523 Module.ModuleName, Module.BaseOfImage );
\r
2524 if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!
\r
2525 _ftprintf(fLogFile, _T("%lu: Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);
\r
2526 fprintf(fLogFile, "%lu: Sym: type: %s, file: %s\n", g_dwShowCount,
\r
2527 ty, Module.LoadedImageName );
\r
2533 if (bXMLTagWrote == TRUE)
\r
2534 fprintf(fLogFile, "module=\"%s\" base=\"%08lx\" ", Module.ModuleName, Module.BaseOfImage);
\r
2536 } // got module info OK
\r
2538 if ( (g_CallstackOutputType == ACOutput_XML) && (bXMLTagWrote == TRUE) )
\r
2539 _ftprintf(fLogFile, _T("/>\n")); // terminate the XML node
\r
2541 } // we seem to have a valid PC
\r
2543 // no return address means no deeper stackframe
\r
2544 if ( s.AddrReturn.Offset == 0 )
\r
2546 // avoid misunderstandings in the printf() following the loop
\r
2547 SetLastError( 0 );
\r
2551 } // for ( frameNum )
\r
2553 if ( (g_CallstackOutputType != ACOutput_XML) && (gle != 0) )
\r
2554 _ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );
\r
2557 //if (pSym) free( pSym );
\r
2559 _ftprintf(fLogFile, _T("\n\n"));
\r
2560 if (g_dwShowCount % 1000)
\r
2564 LeaveCriticalSection(&g_csFileOpenClose);
\r
2567 // Critical section end...
\r
2570 #pragma warning(pop)
\r