]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/cpp/DataBoardTest/DataBoardTest/Stackwalker.cpp
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / cpp / DataBoardTest / DataBoardTest / Stackwalker.cpp
1 /*////////////////////////////////////////////////////////////////////////////
2  *  Project:
3  *    Memory_and_Exception_Trace
4  *
5  * ///////////////////////////////////////////////////////////////////////////
6  *      File:
7  *              Stackwalker.cpp
8  *
9  *      Remarks:
10  *    Dumps memory leaks (unreleased allocations) for CRT-Allocs and COM-Allocs
11  *    Dumps the stack of an thread if an exepction occurs
12  *
13  *  Known bugs:
14  *    - If the allocation-RequestID wrap, then allocations will get lost...
15  *
16  *      Author:
17  *              Jochen Kalmbach, Germany
18  *    (c) 2002-2005 (Freeware)
19  *    http://www.codeproject.com/tools/leakfinder.asp
20  * 
21  * License (The zlib/libpng License, http://www.opensource.org/licenses/zlib-license.php):
22  *
23  * Copyright (c) 2005 Jochen Kalmbach
24  *
25  * This software is provided 'as-is', without any express or implied warranty. 
26  * In no event will the authors be held liable for any damages arising from the 
27  * use of this software.
28  *
29  * Permission is granted to anyone to use this software for any purpose, including 
30  * commercial applications, and to alter it and redistribute it freely, subject to 
31  * the following restrictions:
32  * 
33  * 1. The origin of this software must not be misrepresented; you must not claim 
34  *    that you wrote the original software. If you use this software in a product, 
35  *    an acknowledgment in the product documentation would be appreciated but is 
36  *    not required.
37  *
38  * 2. Altered source versions must be plainly marked as such, and must not be 
39  *    misrepresented as being the original software.
40  *
41  * 3. This notice may not be removed or altered from any source distribution.
42  *
43  *//////////////////////////////////////////////////////////////////////////////
44
45 //#include "stdafx.h"  // should be uncommented for precompiled headers
46
47 #include <windows.h>
48 #include <string>
49 #include <vector>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <time.h>
53 #include <crtdbg.h>
54 #include <tchar.h>
55
56 #include "Stackwalker.h"
57
58 #pragma warning(push)
59 #pragma warning(disable : 4100)
60 #pragma warning(disable : 4996)
61 #pragma warning(disable : 4189)
62 #pragma warning(disable : 4245)
63 #pragma warning(disable : 4701)
64
65 // If the following is defined, only the used memories are stored in the hash-table. 
66 // If the memory is freed, it will be removed from the hash-table (to reduce memory)
67 // Consequences: At DeInitAllocHook, only Leaks will be reported
68 #define HASH_ENTRY_REMOVE_AT_FREE
69
70
71 // 0 = Do not write any output during runtime-alloc-call
72 // 1 = Write only the alloc action (malloc, realloc, free)
73 // 2 = Write alloc action and callstack only for malloc/realloc
74 // 3 = Write alloc action and callstack for all actions
75 static ULONG g_ulShowStackAtAlloc = 0;
76
77 // the form of the output file
78 static eAllocCheckOutput g_CallstackOutputType = ACOutput_Simple;
79
80
81 // Size of Hash-Table (this should be a prime number to avoid collisions)
82 #define ALLOC_HASH_ENTRIES 1023
83
84
85 // Size of Callstack-trace in bytes (0x500 => appr. 5-9 functions, depending on parameter count for each function)
86 #define MAX_ESP_LEN_BUF 0x500
87
88
89 // Normally we can ignore allocations from the Runtime-System
90 #define IGNORE_CRT_ALLOC
91
92 // MaxSize: 1 MByte (only for StackwalkFilter)
93 #define LOG_FILE_MAX_SIZE 1024*1024
94
95 // If the following is defined, then COM-Leaks will also be tracked
96 #define WITH_IMALLOC_SPY
97
98
99 // #############################################################################################
100 #ifdef WITH_IMALLOC_SPY
101 //forwards:
102 void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize);
103 BOOL IMallocHashRemove(void *pData);
104
105 // IMallocSpy-Interface
106 class CMallocSpy : public IMallocSpy
107 {
108 public:
109   CMallocSpy(void) {
110     m_cbRequest = 0;
111   }
112   ~CMallocSpy(void) {
113   }
114   // IUnknown methods
115   STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppUnk) {
116     HRESULT hr = S_OK;
117     if (IsEqualIID(riid, IID_IUnknown)) {
118         *ppUnk = (IUnknown *) this;
119     }
120     else if (IsEqualIID(riid, IID_IMallocSpy)) {
121         *ppUnk =  (IMalloc *) this;
122     }
123     else {
124         *ppUnk = NULL;
125         hr =  E_NOINTERFACE;
126     }
127     AddRef();
128     return hr;
129   }
130   STDMETHOD_(ULONG, AddRef) (void) {
131     return InterlockedIncrement(&m_cRef);
132   }
133   STDMETHOD_(ULONG, Release) (void) {
134     LONG cRef;
135     cRef = InterlockedDecrement(&m_cRef);
136     if (cRef == 0)
137     {
138       delete this;
139     }
140     return cRef;
141   }
142   // IMallocSpy methods
143   STDMETHOD_(ULONG, PreAlloc) (ULONG cbRequest) {
144     m_cbRequest = cbRequest;
145     return cbRequest;
146   }
147   STDMETHOD_(void *, PostAlloc) (void *pActual) {
148     HANDLE hThread;
149     if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
150       GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {
151       // Ok
152       CONTEXT c;
153       memset( &c, '\0', sizeof c );
154       c.ContextFlags = CONTEXT_FULL;
155 #if 0
156       if ( GetThreadContext(hThread, &c) != 0) {
157 #else
158       __asm
159       {
160         call x
161         x: pop eax
162         mov c.Eip, eax
163         mov c.Ebp, ebp
164       }
165       {
166 #endif
167         // Ok
168         IMallocHashInsert(pActual, c, m_cbRequest);
169       }
170       CloseHandle(hThread);
171     }
172     return pActual;
173   }
174   STDMETHOD_(void *, PreFree) (void *pRequest, BOOL fSpyed) {
175     IMallocHashRemove(pRequest);
176     return pRequest;
177   }
178   STDMETHOD_(void, PostFree) (BOOL fSpyed) {
179     return;
180   }
181   STDMETHOD_(ULONG, PreRealloc) (void *pRequest, ULONG cbRequest,
182     void **ppNewRequest, BOOL fSpyed) {
183     IMallocHashRemove(pRequest);
184     m_cbRequest = cbRequest;
185     *ppNewRequest = pRequest;  // Bug fixed. Thanx to Christoph Weber
186     return cbRequest;
187   }
188   STDMETHOD_(void *, PostRealloc) (void *pActual, BOOL fSpyed) {
189     HANDLE hThread;
190     if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
191       GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {
192       // Ok
193       CONTEXT c;
194       memset( &c, '\0', sizeof c );
195       c.ContextFlags = CONTEXT_FULL;
196 #if 0
197       if ( GetThreadContext(hThread, &c) != 0) {
198 #else
199       __asm
200       {
201         call x
202         x: pop eax
203         mov c.Eip, eax
204         mov c.Ebp, ebp
205       }
206       {
207 #endif
208         // Ok
209         IMallocHashInsert(pActual, c, m_cbRequest);
210       }
211       CloseHandle(hThread);
212     }
213     return pActual;
214   }
215   STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) {
216     return pRequest;
217   }
218   STDMETHOD_(ULONG, PostGetSize) (ULONG cbActual, BOOL fSpyed) {
219     return cbActual;
220   }
221   STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) {
222     return pRequest;
223   }
224   STDMETHOD_(BOOL, PostDidAlloc) (void *pRequest, BOOL fSpyed, BOOL fActual) {
225     return fActual;
226   }
227   STDMETHOD_(void, PreHeapMinimize) (void) {
228     return;
229   }
230   STDMETHOD_(void, PostHeapMinimize) (void) {
231     return;
232   }
233 private:
234   LONG    m_cRef;
235   ULONG m_cbRequest;
236 };
237 #endif
238
239 // #############################################################################################
240 // Here I have included the API-Version 9 declarations, so it will also compile on systems, where the new PSDK is not installed
241 // Normally we just need to include the "dbghelp.h" file
242 #include <imagehlp.h>
243 #if API_VERSION_NUMBER < 9
244 typedef
245 BOOL
246 (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
247     HANDLE      hProcess,
248     DWORD64     qwBaseAddress,
249     PVOID       lpBuffer,
250     DWORD       nSize,
251     LPDWORD     lpNumberOfBytesRead
252     );
253
254 typedef struct _IMAGEHLP_LINE64 {
255     DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_LINE64)
256     PVOID                       Key;                    // internal
257     DWORD                       LineNumber;             // line number in file
258     PCHAR                       FileName;               // full filename
259     DWORD64                     Address;                // first instruction of line
260 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
261
262
263 typedef struct _IMAGEHLP_MODULE64 {
264     DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)
265     DWORD64                     BaseOfImage;            // base load address of module
266     DWORD                       ImageSize;              // virtual size of the loaded module
267     DWORD                       TimeDateStamp;          // date/time stamp from pe header
268     DWORD                       CheckSum;               // checksum from the pe header
269     DWORD                       NumSyms;                // number of symbols in the symbol table
270     SYM_TYPE                    SymType;                // type of symbols loaded
271     CHAR                        ModuleName[32];         // module name
272     CHAR                        ImageName[256];         // image name
273     CHAR                        LoadedImageName[256];   // symbol file name
274 } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
275
276 typedef struct _IMAGEHLP_SYMBOL64 {
277     DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_SYMBOL64)
278     DWORD64                     Address;                // virtual address including dll base address
279     DWORD                       Size;                   // estimated size of symbol, can be zero
280     DWORD                       Flags;                  // info about the symbols, see the SYMF defines
281     DWORD                       MaxNameLength;          // maximum size of symbol name in 'Name'
282     CHAR                        Name[1];                // symbol name (null terminated string)
283 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
284
285 typedef struct _tagADDRESS64 {
286     DWORD64       Offset;
287     WORD          Segment;
288     ADDRESS_MODE  Mode;
289 } ADDRESS64, *LPADDRESS64;
290
291 typedef struct _KDHELP64 {
292
293     //
294     // address of kernel thread object, as provided in the
295     // WAIT_STATE_CHANGE packet.
296     //
297     DWORD64   Thread;
298
299     //
300     // offset in thread object to pointer to the current callback frame
301     // in kernel stack.
302     //
303     DWORD   ThCallbackStack;
304
305     //
306     // offset in thread object to pointer to the current callback backing
307     // store frame in kernel stack.
308     //
309     DWORD   ThCallbackBStore;
310
311     //
312     // offsets to values in frame:
313     //
314     // address of next callback frame
315     DWORD   NextCallback;
316
317     // address of saved frame pointer (if applicable)
318     DWORD   FramePointer;
319
320
321     //
322     // Address of the kernel function that calls out to user mode
323     //
324     DWORD64   KiCallUserMode;
325
326     //
327     // Address of the user mode dispatcher function
328     //
329     DWORD64   KeUserCallbackDispatcher;
330
331     //
332     // Lowest kernel mode address
333     //
334     DWORD64   SystemRangeStart;
335
336     DWORD64  Reserved[8];
337
338 } KDHELP64, *PKDHELP64;
339
340
341 typedef struct _tagSTACKFRAME64 {
342     ADDRESS64   AddrPC;               // program counter
343     ADDRESS64   AddrReturn;           // return address
344     ADDRESS64   AddrFrame;            // frame pointer
345     ADDRESS64   AddrStack;            // stack pointer
346     ADDRESS64   AddrBStore;           // backing store pointer
347     PVOID       FuncTableEntry;       // pointer to pdata/fpo or NULL
348     DWORD64     Params[4];            // possible arguments to the function
349     BOOL        Far;                  // WOW far call
350     BOOL        Virtual;              // is this a virtual frame?
351     DWORD64     Reserved[3];
352     KDHELP64    KdHelp;
353 } STACKFRAME64, *LPSTACKFRAME64;
354
355 typedef
356 PVOID
357 (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
358     HANDLE  hProcess,
359     DWORD64 AddrBase
360     );
361
362 typedef
363 DWORD64
364 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(
365     HANDLE  hProcess,
366     DWORD64 Address
367     );
368
369 typedef
370 DWORD64
371 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
372     HANDLE    hProcess,
373     HANDLE    hThread,
374     LPADDRESS64 lpaddr
375     );
376 #endif
377 // #############################################################################################
378
379
380
381 // Forward definitions of functions:
382 static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hProcess);
383 static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile);
384
385 //static void AllocHashOut(FILE*);
386 static ULONG AllocHashOutLeaks(FILE*);
387
388
389
390 // Globale Vars:
391 static TCHAR *g_pszAllocLogName = NULL;
392 static FILE *g_fFile = NULL;
393
394 // AllocCheckFileOpen
395 //  Checks if the log-file is already opened
396 //  if not, try to open file (append or create if not exists)
397 //  if open failed, redirect output to stdout
398 static void AllocCheckFileOpen(bool bAppend = true) {
399   // is the File already open? If not open it...
400   if (g_fFile == NULL)
401     if (g_pszAllocLogName != NULL)
402     {
403       if (bAppend == false)
404         g_fFile = _tfopen(g_pszAllocLogName, _T("w"));
405       else
406         g_fFile = _tfopen(g_pszAllocLogName, _T("a"));
407     }
408   if (g_fFile == NULL)
409     g_fFile = stdout;
410 }
411
412 // Write Date/Time to specified file (will also work after 2038)
413 static void WriteDateTime(FILE *fFile, BOOL asXMLAttrs = FALSE) {
414   TCHAR pszTemp[11], pszTemp2[11];
415
416   if (fFile != NULL) {
417     _tstrdate( pszTemp );
418     _tstrtime( pszTemp2 );
419     if (asXMLAttrs == FALSE)
420       _ftprintf(fFile,  _T("%s %s"), pszTemp, pszTemp2 );  // also ok after year 2038 (asctime is NOT ok)
421     else
422       _ftprintf(fFile,  _T("date=\"%s\" time=\"%s\" "), pszTemp, pszTemp2 );  // also ok after year 2038 (asctime is NOT ok)
423   }
424 }  // WriteDateTime
425
426
427 /*******************************************************************************
428  * Hash-Tabelle
429  *******************************************************************************/
430 // Memory for the EIP-Address (is used by the ShowStack-method)
431 #define MAX_EIP_LEN_BUF 4
432
433 #define ALLOC_ENTRY_NOT_FOUND 0xFFFFFFFF
434
435 typedef struct AllocHashEntryType {
436   long                       lRequestID;    // RequestID from CRT (if 0, then this entry is empty)
437   size_t                     nDataSize;     // Size of the allocated memory
438   char                       cRemovedFlag;  // 0 => memory was not yet released
439   struct AllocHashEntryType  *Next;
440   // Callstack for EIP
441   DWORD                      dwEIPOffset;
442   DWORD                      dwEIPLen;
443   char                       pcEIPAddr[MAX_EIP_LEN_BUF];
444   // Callstack for ESP
445   DWORD                      dwESPOffset;
446   DWORD                      dwESPLen;
447   char                       pcESPAddr[MAX_ESP_LEN_BUF];
448 } AllocHashEntryType;
449
450 static AllocHashEntryType AllocHashTable[ALLOC_HASH_ENTRIES];
451 static ULONG AllocHashEntries = 0;
452 static ULONG AllocHashCollisions = 0;
453 static ULONG AllocHashFreed = 0;
454 static ULONG AllocHashMaxUsed = 0; // maximal number of concurrent entries
455 static ULONG AllocHashCurrentCount = 0;
456
457 static ULONG AllocHashMaxCollisions = 0;
458 static ULONG AllocHashCurrentCollisions = 0;
459
460 // ##########################################################################################
461 #ifdef WITH_IMALLOC_SPY
462 // eigene Tabelle für die IMallocs:
463 typedef struct IMallocHashEntryType {
464   void                       *pData;    // Key-Word
465   size_t                     nDataSize;     // größe des Datenblocks (optional)
466   char                       cRemovedFlag;  // 0 => nicht wurde noch nicht freigegeben
467   struct IMallocHashEntryType  *Next;
468   // Callstack für EIP
469   DWORD                      dwEIPOffset;
470   DWORD                      dwEIPLen;
471   char                       pcEIPAddr[MAX_EIP_LEN_BUF];
472   // Callstack für ESP
473   DWORD                      dwESPOffset;
474   DWORD                      dwESPLen;
475   char                       pcESPAddr[MAX_ESP_LEN_BUF];
476 } IMallocHashEntryType;
477
478 static IMallocHashEntryType IMallocHashTable[ALLOC_HASH_ENTRIES];
479
480 static ULONG IMallocHashEntries = 0;
481 static ULONG IMallocHashCollisions = 0;
482 static ULONG IMallocHashFreed = 0;
483 static ULONG IMallocHashMaxUsed = 0; // maximal number of concurrent entries
484 static ULONG IMallocHashCurrentCount = 0;
485
486 static ULONG IMallocHashMaxCollisions = 0;
487 static ULONG IMallocHashCurrentCollisions = 0;
488
489
490 //static void AllocHashOut(FILE*);
491 static ULONG IMallocHashOutLeaks(FILE*);
492
493 // AllocHashFunction
494 //   Die eigentliche Hash-Funktion (hier ganz simpel)
495 static ULONG IMallocHashFunction(void *pData) {
496   ULONG ulTemp;
497   DWORD dwPointer = (DWORD) pData;
498
499   // relativ simpler Mechanismus für die Hash-Funktion,
500   // mir ist nur nix besseres eingefallen...
501   ulTemp = dwPointer % ALLOC_HASH_ENTRIES;
502
503   _ASSERTE( (ulTemp >= 0) && (ulTemp < ALLOC_HASH_ENTRIES) );
504
505   return ulTemp;
506 }  // AllocHashFunction
507
508 // IMallocHashInsert
509 //   pData: Key-Word (Pointer to address)
510 //   pContext:   Context-Record, for retrieving Callstack (EIP and EBP is only needed)
511 //   nDataSize:  How many bytes
512 void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize) {
513   ULONG HashIdx;
514   IMallocHashEntryType *pHashEntry;
515
516   // ermittle Statistische Werte
517   IMallocHashEntries++;
518   IMallocHashCurrentCount++;
519   if (IMallocHashCurrentCount > IMallocHashMaxUsed)
520     IMallocHashMaxUsed = IMallocHashCurrentCount;
521
522   // ermittle den Hash-Wert
523   HashIdx = IMallocHashFunction(pData);
524
525   // Eintrag darf nicht größer als die Hash-Tabelle sein
526   _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
527
528   pHashEntry = &IMallocHashTable[HashIdx];
529   if (pHashEntry->pData == 0) {
530     // es ist noch kein Eintrag da
531   }
532   else {
533     //Statistische Daten:
534     IMallocHashCollisions++;
535     IMallocHashCurrentCollisions++;
536     if (IMallocHashCurrentCollisions > IMallocHashMaxCollisions)
537       IMallocHashMaxCollisions = IMallocHashCurrentCollisions;
538
539     // Eintrag ist schon belegt, verkette die Einträge
540     // wenn dies oft vorkommt, sollte man entweder die Tabelle vergrößern oder eine
541     // andere Hash-Funktion wählen
542     while(pHashEntry->Next != NULL) {
543       pHashEntry = pHashEntry->Next;
544     }
545
546     pHashEntry->Next = (IMallocHashEntryType*) _calloc_dbg(sizeof(IMallocHashEntryType), 1, _CRT_BLOCK, __FILE__, __LINE__);
547     pHashEntry = pHashEntry->Next;
548
549   }
550   pHashEntry->pData = pData;  // Key-Word
551   pHashEntry->nDataSize = nDataSize;
552   pHashEntry->Next = NULL;
553   // Get EIP and save it in the record
554   pHashEntry->dwEIPOffset = Context.Eip;
555   if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
556     // Could not read memory... remove everything...
557     memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
558     pHashEntry->dwEIPLen = 0;
559     pHashEntry->dwEIPOffset = 0;
560   }
561
562   // Get ESP and save it in the record
563   pHashEntry->dwESPOffset = Context.Ebp;
564   if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
565     // Could not read memory... remove everything...
566     memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
567     pHashEntry->dwESPLen = 0;
568     pHashEntry->dwESPOffset = 0;
569
570     // Check if I tried to read too much...
571     if (GetLastError() == ERROR_PARTIAL_COPY)
572     {
573       // ask how many I can read:
574       MEMORY_BASIC_INFORMATION MemBuffer;
575       DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
576       if (dwRet > 0)
577       {
578         // calculate the length
579         DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
580         if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
581         {
582           // try to read it again (with the shorter length)
583           if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
584           {
585             // ok, now everything goes wrong... remove it...
586             memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
587             pHashEntry->dwESPLen = 0;
588             pHashEntry->dwESPOffset = 0;
589           }
590           else
591           {
592             pHashEntry->dwESPOffset = Context.Ebp;
593           }
594         }
595       } // VirtualQuery was successfully
596     }  // ERROR_PARTIAL_COPY
597   }
598 }
599
600 // IMallocHashFind
601 //   Wird ALLOC_ENTRY_NOT_FOUND zurückgegeben, so wurde der Key nicht 
602 //   gefunden, ansonsten wird ein Zeiger auf den Hash-Eintrag zurückgegeben
603 //   ACHTUNG: In einem preemptiven Tasking-System kann hier nicht 
604 //            garantiert werden, ob der Zeiger noch gültig ist, wenn er 
605 //            zurückgegeben wird, da er von einem anderen Thread schon
606 //            freigegeben sein könnte. 
607 //            Die synchronisation muß eine Ebene höher erfolgen
608 static IMallocHashEntryType *IMallocHashFind(void *pData) {
609   ULONG HashIdx;
610   IMallocHashEntryType *pHashEntry;
611
612   // ermittle den Hash-Wert
613   HashIdx = IMallocHashFunction(pData);
614
615   // Eintrag darf nicht größer als die Hash-Tabelle sein
616   _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
617
618   pHashEntry = &IMallocHashTable[HashIdx];
619   while(pHashEntry != NULL) {
620     if (pHashEntry->pData == pData) {
621       return pHashEntry;
622     }
623     pHashEntry = pHashEntry->Next;
624   }
625
626   // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
627   return (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
628 }  // AllocHashFind
629
630 // IMallocHashRemove
631 //   Return: FALSE (0) : Key wurde gefunden und entfernt/markiert
632 //           TRUE (!=0): Key wurde nicht gefunden!
633 BOOL IMallocHashRemove(void *pData) {
634   ULONG HashIdx;
635   IMallocHashEntryType *pHashEntry, *pHashEntryLast;
636
637   // ermittle den Hash-Wert
638   HashIdx = IMallocHashFunction(pData);
639
640   // Eintrag darf nicht größer als die Hash-Tabelle sein
641   _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
642
643   pHashEntryLast = NULL;
644   pHashEntry = &IMallocHashTable[HashIdx];
645   while(pHashEntry != NULL) {
646     if (pHashEntry->pData == pData) {
647 #ifdef HASH_ENTRY_REMOVE_AT_FREE
648       IMallocHashFreed++;
649       IMallocHashCurrentCount--;
650       // gebe den Speicher frei
651       if (pHashEntryLast == NULL) {
652         // Es ist ein Eintrag direkt in der Tabelle
653         if (pHashEntry->Next == NULL) {
654           // Es ist der letze Eintrag lösche also die Tabelle
655           memset(&IMallocHashTable[HashIdx], 0, sizeof(IMallocHashTable[HashIdx]));
656         }
657         else {
658           // Es sind noch Einträge verkettet, Ã¼berschreibe einfach den nicht mehr gebrauchten...
659           IMallocHashEntryType *pTmp = pHashEntry->Next;
660           *pHashEntry = *(pHashEntry->Next);
661           _free_dbg(pTmp, _CRT_BLOCK);
662         }
663         return TRUE;
664       }
665       else {
666         // ich bin in einem dynamischen Bereich
667         // dies war eine kollisions, zähle also wieder zurück:
668         IMallocHashCurrentCollisions--;
669         pHashEntryLast->Next = pHashEntry->Next;
670         _free_dbg(pHashEntry, _CRT_BLOCK);
671         return TRUE;
672       }
673 #else
674       // erhöhe nur den Removed counter und behalte das Object im Speicher
675       pHashEntry->cRemovedFlag++;
676       return TRUE;  // erfolgreich
677 #endif
678     }
679     pHashEntryLast = pHashEntry;
680     pHashEntry = pHashEntry->Next;
681   }
682
683   // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
684   return FALSE;
685 }
686
687
688
689 //   Callback-Funtion for StackWalk für meine CallStack-Ausgabe aus der Hash-Tabelle
690 static BOOL __stdcall ReadProcMemoryFromIMallocHash(HANDLE pData, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
691   // Versuche die hRequestID zu finden
692   IMallocHashEntryType *pHashEntry;
693   *lpNumberOfBytesRead = 0;
694
695   pHashEntry = IMallocHashFind((PVOID) pData);
696   if (pHashEntry == (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
697     // nicht gefunden, somit kann ich den Speicher nicht lesen
698     *lpNumberOfBytesRead = 0;
699     return FALSE;
700   }
701   if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
702     // Speicher liegt im ESP:
703     // Errechne den Offset
704     DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
705     DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
706     memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
707     *lpNumberOfBytesRead = dwSize;
708     if (dwSize != nSize)
709       return FALSE;
710   }
711
712   if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
713     // Speicher liegt im EIP:
714     // Errechne den Offset
715     DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
716     DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
717     memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
718     *lpNumberOfBytesRead = dwSize;
719     if (dwSize != nSize)
720       return FALSE;
721   }
722   
723   if (*lpNumberOfBytesRead == 0)  // Der Speicher konnte nicht gefunden werden
724     return FALSE;
725
726   return TRUE;
727 }
728 // AllocHashOutLeaks
729 // Gibt allen Speicher aus, der noch nicht wieder freigegeben wurde
730 //   Returns the number of bytes, that are not freed (leaks)
731 ULONG IMallocHashOutLeaks(FILE *fFile) {
732   ULONG ulTemp;
733   IMallocHashEntryType *pHashEntry;
734   ULONG ulCount = 0;
735   ULONG ulLeaksByte = 0;
736
737   // Gehe jeden Eintrag durch und gebe ihn aus
738   for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
739     pHashEntry = &IMallocHashTable[ulTemp];
740     if (pHashEntry->pData != 0) {
741       while(pHashEntry != NULL) {
742         // gebe die Zeile aus
743         if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
744           ulCount++;
745           if (g_CallstackOutputType == ACOutput_XML)
746             _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);
747           else
748             _ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
749           CONTEXT c;
750           memset( &c, '\0', sizeof c );
751           c.Eip = pHashEntry->dwEIPOffset;
752           c.Ebp = pHashEntry->dwESPOffset;
753           ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromIMallocHash, (HANDLE) pHashEntry->pData);
754           // Zähle zusammen wieviel Byte noch nicht freigegeben wurden
755           if (pHashEntry->nDataSize > 0)
756             ulLeaksByte += pHashEntry->nDataSize;
757           else
758             ulLeaksByte++;  // Wenn zwar Speicher allokiert wurde, dieser aber 0 Bytes lang war, so reserviere für diesen zumindest 1 Byte
759
760           if (g_CallstackOutputType == ACOutput_XML)
761             _ftprintf(fFile, _T("</LEAK>\n"));  // terminate the xml-node
762         }
763         pHashEntry = pHashEntry->Next;
764       }
765     }
766   }
767   if (g_CallstackOutputType != ACOutput_XML)
768     _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
769   return ulLeaksByte;
770 }  // AllocHashOutLeaks
771 #endif
772
773
774 static void AllocHashInit(void) {
775
776   memset(AllocHashTable, 0, sizeof(AllocHashTable));
777   AllocHashEntries = 0;
778   AllocHashCollisions = 0;
779   AllocHashFreed = 0;
780   AllocHashCurrentCount = 0;
781   AllocHashMaxUsed = 0;
782
783   AllocHashMaxCollisions = 0;
784   AllocHashCurrentCollisions = 0;
785
786 #ifdef WITH_IMALLOC_SPY
787   memset(IMallocHashTable, 0, sizeof(IMallocHashTable));
788   IMallocHashEntries = 0;
789   IMallocHashCollisions = 0;
790   IMallocHashFreed = 0;
791   IMallocHashCurrentCount = 0;
792   IMallocHashMaxUsed = 0;
793
794   IMallocHashMaxCollisions = 0;
795   IMallocHashCurrentCollisions = 0;
796 #endif
797   return;
798 }  // AllocHashInit
799
800
801 // AllocHashDeinit
802 // Returns the number of bytes, that are not freed (leaks)
803 static ULONG AllocHashDeinit(void) {
804   ULONG ulRet = 0;
805   bool bAppend = g_CallstackOutputType != ACOutput_XML;
806   AllocCheckFileOpen(false);//bAppend);  // open global log-file
807
808   if (g_CallstackOutputType == ACOutput_XML)
809   {
810     _ftprintf(g_fFile, _T("<MEMREPORT "));
811     WriteDateTime(g_fFile, TRUE);
812     _ftprintf(g_fFile, _T(">\n"));
813   }
814   else
815   {
816     _ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));
817     WriteDateTime(g_fFile);
818     _ftprintf(g_fFile, _T("\n"));
819   }
820
821 #ifndef HASH_ENTRY_REMOVE_AT_FREE
822   // output the used memory
823   if (g_CallstackOutputType != ACOutput_XML)
824     _ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));
825   AllocHashOut(g_fFile);
826 #endif
827
828   // output the Memoty leaks
829   if (g_CallstackOutputType != ACOutput_XML)
830     _ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));
831   ulRet = AllocHashOutLeaks(g_fFile);
832
833   if (g_CallstackOutputType == ACOutput_Advanced)
834   {
835     // output some statistics from the hash-table
836     _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
837     _ftprintf(g_fFile, _T("      Table-Size:     %i\n"), ALLOC_HASH_ENTRIES);
838     _ftprintf(g_fFile, _T("      Inserts:        %i\n"), AllocHashEntries);
839     _ftprintf(g_fFile, _T("      Freed:          %i\n"), AllocHashFreed);
840     _ftprintf(g_fFile, _T("      Sum Collisions: %i\n"), AllocHashCollisions);
841     _ftprintf(g_fFile, _T("\n"));
842     _ftprintf(g_fFile, _T("      Max used:       %i\n"), AllocHashMaxUsed);
843     _ftprintf(g_fFile, _T("      Max Collisions: %i\n"), AllocHashMaxCollisions);
844   }
845
846   // Free Hash-Table
847   ULONG ulTemp;
848   AllocHashEntryType *pHashEntry, *pHashEntryOld;
849
850   // Now, free my own memory
851   for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
852     pHashEntry = &AllocHashTable[ulTemp];
853     while(pHashEntry != NULL) {
854       pHashEntryOld = pHashEntry;
855       pHashEntry = pHashEntry->Next;
856       if (pHashEntryOld != &AllocHashTable[ulTemp]) {
857         // now free the dynamically allocated memory
858         free(pHashEntryOld);
859       }
860     }  // while
861   }  // for
862   // empty the hash-table
863   memset(AllocHashTable, 0, sizeof(AllocHashTable));
864
865 #ifdef WITH_IMALLOC_SPY
866   // output the Memoty leaks
867   if (g_CallstackOutputType != ACOutput_XML)
868     _ftprintf(g_fFile, _T("\n##### COM-Leaks: ###############################################\n"));
869   ulRet = IMallocHashOutLeaks(g_fFile);
870
871   if (g_CallstackOutputType == ACOutput_Advanced)
872   {
873     // output some statistics from the hash-table
874     _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
875     _ftprintf(g_fFile, _T("      Table-Size:     %i\n"), ALLOC_HASH_ENTRIES);
876     _ftprintf(g_fFile, _T("      Inserts:        %i\n"), IMallocHashEntries);
877     _ftprintf(g_fFile, _T("      Freed:          %i\n"), IMallocHashFreed);
878     _ftprintf(g_fFile, _T("      Sum Collisions: %i\n"), IMallocHashCollisions);
879     _ftprintf(g_fFile, _T("\n"));
880     _ftprintf(g_fFile, _T("      Max used:       %i\n"), IMallocHashMaxUsed);
881     _ftprintf(g_fFile, _T("      Max Collisions: %i\n"), IMallocHashMaxCollisions);
882   }
883
884   // Free Hash-Table
885   //ULONG ulTemp;
886   IMallocHashEntryType *pIMHashEntry, *pIMHashEntryOld;
887
888   // Gehe jeden Eintrag durch und gebe ihn frei
889   for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
890     pIMHashEntry = &IMallocHashTable[ulTemp];
891     while(pHashEntry != NULL) {
892       pIMHashEntryOld = pIMHashEntry;
893       pIMHashEntry = pIMHashEntry->Next;
894       if (pIMHashEntryOld != &IMallocHashTable[ulTemp]) {
895         // es ist dynamischer Speicher, gebe ihn also frei:
896         _free_dbg(pIMHashEntryOld, _CRT_BLOCK);
897       }
898     }  // while
899   }  // for
900   // Lösche die gesamte Hash-Tabelle
901   memset(IMallocHashTable, 0, sizeof(IMallocHashTable));
902 #endif
903
904
905   if (g_CallstackOutputType == ACOutput_XML)
906     _ftprintf(g_fFile, _T("</MEMREPORT>\n"));
907
908   return ulRet;
909 }  // AllocHashDeinit
910
911 // AllocHashFunction
912 // The has-function (very simple)
913 static inline ULONG AllocHashFunction(long lRequestID) {
914   // I couldn´t find any better and faster
915   return lRequestID % ALLOC_HASH_ENTRIES;
916 }  // AllocHashFunction
917
918 // AllocHashInsert
919 //   lRequestID: Key-Word (RequestID from AllocHook)
920 //   pContext:   Context-Record, for retrieving Callstack (EIP and EBP is only needed)
921 //   nDataSize:  How many bytes
922 static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {
923   ULONG HashIdx;
924   AllocHashEntryType *pHashEntry;
925
926   // change statistical data
927   AllocHashEntries++;
928   AllocHashCurrentCount++;
929   if (AllocHashCurrentCount > AllocHashMaxUsed)
930     AllocHashMaxUsed = AllocHashCurrentCount;
931
932   // generate hash-value
933   HashIdx = AllocHashFunction(lRequestID);
934
935   pHashEntry = &AllocHashTable[HashIdx];
936   if (pHashEntry->lRequestID == 0) {
937     // Entry is empty...
938   }
939   else {
940     // Entry is not empy! make a list of entries for this hash value...
941     // change statistical data
942     // if this happens often, you should increase the hah size or change the heash-function; 
943     // to fasten the allocation time
944     AllocHashCollisions++;
945     AllocHashCurrentCollisions++;
946     if (AllocHashCurrentCollisions > AllocHashMaxCollisions)
947       AllocHashMaxCollisions = AllocHashCurrentCollisions;
948
949     while(pHashEntry->Next != NULL) {
950       pHashEntry = pHashEntry->Next;
951     }
952
953     pHashEntry->Next = (AllocHashEntryType*) calloc(sizeof(AllocHashEntryType), 1);
954     pHashEntry = pHashEntry->Next;
955
956   }
957   pHashEntry->lRequestID = lRequestID;  // Key-Word
958   pHashEntry->nDataSize = nDataSize;
959   pHashEntry->Next = NULL;
960   // Get EIP and save it in the record
961   pHashEntry->dwEIPOffset = Context.Eip;
962   if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
963     // Could not read memory... remove everything...
964     memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
965     pHashEntry->dwEIPLen = 0;
966     pHashEntry->dwEIPOffset = 0;
967   }
968
969   // Get ESP and save it in the record
970   pHashEntry->dwESPOffset = Context.Ebp;
971   if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
972     // Could not read memory... remove everything...
973     memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
974     pHashEntry->dwESPLen = 0;
975     pHashEntry->dwESPOffset = 0;
976
977     // Check if I tried to read too much...
978     if (GetLastError() == ERROR_PARTIAL_COPY)
979     {
980       // ask how many I can read:
981       MEMORY_BASIC_INFORMATION MemBuffer;
982       DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
983       if (dwRet > 0)
984       {
985         // calculate the length
986         DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
987         if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
988         {
989           // try to read it again (with the shorter length)
990           if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
991           {
992             // ok, now everything goes wrong... remove it...
993             memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
994             pHashEntry->dwESPLen = 0;
995             pHashEntry->dwESPOffset = 0;
996           }
997           else
998           {
999             pHashEntry->dwESPOffset = Context.Ebp;
1000           }
1001         }
1002       } // VirtualQuery was successfully
1003     }  // ERROR_PARTIAL_COPY
1004   }
1005 }
1006
1007 // AllocHashFind
1008 //   If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!
1009 //   If the Key was found, a pointer to the entry is returned
1010 static AllocHashEntryType *AllocHashFind(long lRequestID) {
1011   ULONG HashIdx;
1012   AllocHashEntryType *pHashEntry;
1013
1014   // get the Hash-Value
1015   HashIdx = AllocHashFunction(lRequestID);
1016
1017   // Just do some simple checks:
1018   _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
1019
1020   pHashEntry = &AllocHashTable[HashIdx];
1021   while(pHashEntry != NULL) {
1022     if (pHashEntry->lRequestID == lRequestID) {
1023       return pHashEntry;
1024     }
1025     pHashEntry = pHashEntry->Next;
1026   }
1027
1028   // entry was not found!
1029   return (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
1030 }  // AllocHashFind
1031
1032 // AllocHashRemove
1033 //   Return: FALSE (0) : Key was found and removed/marked
1034 //           TRUE (!=0): Key was not found
1035 static BOOL AllocHashRemove(long lRequestID) {
1036   ULONG HashIdx;
1037   AllocHashEntryType *pHashEntry, *pHashEntryLast;
1038
1039   // get the Hash-Value
1040   HashIdx = AllocHashFunction(lRequestID);
1041
1042   // Just do some simple checks:
1043   _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
1044
1045   pHashEntryLast = NULL;
1046   pHashEntry = &AllocHashTable[HashIdx];
1047   while(pHashEntry != NULL) {
1048     if (pHashEntry->lRequestID == lRequestID) {
1049 #ifdef HASH_ENTRY_REMOVE_AT_FREE
1050       AllocHashFreed++;
1051       AllocHashCurrentCount--;
1052       // release my memory
1053       if (pHashEntryLast == NULL) {
1054         // It is an entry in the table, so do not release this memory
1055         if (pHashEntry->Next == NULL) {
1056           // It was the last entry, so empty the table entry
1057           memset(&AllocHashTable[HashIdx], 0, sizeof(AllocHashTable[HashIdx]));
1058         }
1059         else {
1060           // There are some more entries, so shorten the list
1061           AllocHashEntryType *pTmp = pHashEntry->Next;
1062           *pHashEntry = *(pHashEntry->Next);
1063           free(pTmp);
1064         }
1065         return TRUE;
1066       }
1067       else {
1068         // now, I am in an dynamic allocated entry
1069         // it was a collision, so decrease the current collision count
1070         AllocHashCurrentCollisions--;
1071         pHashEntryLast->Next = pHashEntry->Next;
1072         free(pHashEntry);
1073         return TRUE;
1074       }
1075 #else
1076       // increase the Remove-Count and let the objet stay in memory
1077       pHashEntry->cRemovedFlag++;
1078       return TRUE;
1079 #endif
1080     }
1081     pHashEntryLast = pHashEntry;
1082     pHashEntry = pHashEntry->Next;
1083   }
1084
1085   // if we are here, we could not find the RequestID
1086   return FALSE;
1087 }
1088
1089 // ReadProcMemoryFromHash
1090 //   Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries
1091 static BOOL __stdcall ReadProcMemoryFromHash(HANDLE hRequestID, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
1092   // Try to find the RequestID
1093   AllocHashEntryType *pHashEntry;
1094   *lpNumberOfBytesRead = 0;
1095
1096   pHashEntry = AllocHashFind((LONG) hRequestID);
1097   if (pHashEntry == (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
1098     // Not found, so I cannot return any memory
1099     *lpNumberOfBytesRead = 0;
1100     return FALSE;
1101   }
1102   if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
1103     // Memory is located in ESP:
1104     // Calculate the offset
1105     DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
1106     DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
1107     memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
1108     *lpNumberOfBytesRead = dwSize;
1109     if (dwSize != nSize)
1110       return FALSE;
1111   }
1112
1113   if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
1114     // Memory is located in EIP:
1115     // Calculate the offset
1116     DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
1117     DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
1118     memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
1119     *lpNumberOfBytesRead = dwSize;
1120     if (dwSize != nSize)
1121       return FALSE;
1122   }
1123   
1124   if (*lpNumberOfBytesRead == 0)  // Memory could not be found
1125     return FALSE;
1126
1127   return TRUE;
1128 }
1129
1130 // AllocHashOutLeaks
1131 // Write all Memory (with callstack) which was not freed yet
1132 //   Returns the number of bytes, that are not freed (leaks)
1133 ULONG AllocHashOutLeaks(FILE *fFile) {
1134   ULONG ulTemp;
1135   AllocHashEntryType *pHashEntry;
1136   ULONG ulCount = 0;
1137   ULONG ulLeaksByte = 0;
1138
1139   // Move throu every entry
1140   for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
1141     pHashEntry = &AllocHashTable[ulTemp];
1142     if (pHashEntry->lRequestID != 0) {
1143       while(pHashEntry != NULL) {
1144         if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
1145           ulCount++;
1146           if (g_CallstackOutputType == ACOutput_XML)
1147             _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
1148           else
1149             _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
1150           CONTEXT c;
1151           memset( &c, '\0', sizeof c );
1152           c.Eip = pHashEntry->dwEIPOffset;
1153           c.Ebp = pHashEntry->dwESPOffset;
1154           ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromHash, (HANDLE) pHashEntry->lRequestID);
1155           // Count the number of leaky bytes
1156           if (pHashEntry->nDataSize > 0)
1157             ulLeaksByte += pHashEntry->nDataSize;
1158           else
1159             ulLeaksByte++;  // If memory was allocated with zero bytes, then just increase the counter 1
1160
1161           if (g_CallstackOutputType == ACOutput_XML)
1162             _ftprintf(fFile, _T("</LEAK>\n"));  // terminate the xml-node
1163         }
1164         pHashEntry = pHashEntry->Next;
1165       }
1166     }
1167   }
1168   if (g_CallstackOutputType != ACOutput_XML)
1169     _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
1170   return ulLeaksByte;
1171 }  // AllocHashOutLeaks
1172
1173 // Write all used memory to a file
1174 void AllocHashOut(FILE *fFile) {
1175   ULONG ulTemp;
1176   AllocHashEntryType *pHashEntry;
1177
1178   for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
1179     pHashEntry = &AllocHashTable[ulTemp];
1180     if (pHashEntry->lRequestID != 0) {
1181       while(pHashEntry != NULL) {
1182         if (g_CallstackOutputType == ACOutput_XML)
1183           _ftprintf(fFile, _T("<MEMUSED requestID=\"%u\" size=\"%u\"\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
1184         else
1185           _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
1186         pHashEntry = pHashEntry->Next;
1187       }
1188     }
1189   }
1190 }  // AllocHashOut
1191
1192 /*******************************************************************************
1193  * Ende der Hash-Tabelle
1194  *******************************************************************************/
1195
1196
1197 // The follwoing is copied from dbgint.h:
1198 // <CRT_INTERNALS>
1199 /*
1200  * For diagnostic purpose, blocks are allocated with extra information and
1201  * stored in a doubly-linked list.  This makes all blocks registered with
1202  * how big they are, when they were allocated, and what they are used for.
1203  */
1204
1205 #define nNoMansLandSize 4
1206
1207 typedef struct _CrtMemBlockHeader
1208 {
1209         struct _CrtMemBlockHeader * pBlockHeaderNext;
1210         struct _CrtMemBlockHeader * pBlockHeaderPrev;
1211         char *                      szFileName;
1212         int                         nLine;
1213 #ifdef _WIN64
1214         /* These items are reversed on Win64 to eliminate gaps in the struct
1215          * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
1216          * maintained in the debug heap.
1217          */
1218         int                         nBlockUse;
1219         size_t                      nDataSize;
1220 #else  /* _WIN64 */
1221         size_t                      nDataSize;
1222         int                         nBlockUse;
1223 #endif  /* _WIN64 */
1224         long                        lRequest;
1225         unsigned char               gap[nNoMansLandSize];
1226         /* followed by:
1227          *  unsigned char           data[nDataSize];
1228          *  unsigned char           anotherGap[nNoMansLandSize];
1229          */
1230 } _CrtMemBlockHeader;
1231 #define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
1232 #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
1233  
1234 // </CRT_INTERNALS>
1235
1236
1237
1238
1239 // Global data:
1240 static BOOL g_bInitialized = FALSE;
1241 static HINSTANCE g_hImagehlpDll = NULL;
1242
1243 static DWORD g_dwShowCount = 0;  // increase at every ShowStack-Call
1244 static CRITICAL_SECTION g_csFileOpenClose = {0};
1245
1246 // Is used for syncronising call to MyAllocHook (to prevent reentrant calls)
1247 static LONG g_lMallocCalled = 0;
1248
1249 static _CRT_ALLOC_HOOK pfnOldCrtAllocHook = NULL;
1250
1251 // Deaktivate AllocHook, by increasing the Syncronisation-Counter
1252 //static void DeactivateMallocStackwalker(void) {
1253 //  InterlockedIncrement(&g_lMallocCalled);
1254 //}
1255
1256
1257 // MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
1258 // Special case for VC 5
1259 #if _MSC_VER <= 1100 
1260 static int MyAllocHook(int nAllocType, void *pvData, 
1261       size_t nSize, int nBlockUse, long lRequest, 
1262       const char * szFileName, int nLine ) {
1263 #else
1264 static int MyAllocHook(int nAllocType, void *pvData, 
1265       size_t nSize, int nBlockUse, long lRequest, 
1266       const unsigned char * szFileName, int nLine ) {
1267 #endif
1268   static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
1269   static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };
1270
1271 #ifdef IGNORE_CRT_ALLOC
1272   if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK)  // Ignore internal C runtime library allocations
1273     return TRUE;
1274 #endif
1275   extern int _crtDbgFlag;
1276   if  ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )
1277   {
1278     // Someone has disabled that the runtime should log this allocation
1279     // so we do not log this allocation
1280     if (pfnOldCrtAllocHook != NULL)
1281       pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
1282     return TRUE;
1283   }
1284
1285   // Prevent from reentrat calls
1286   if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called
1287     InterlockedDecrement(&g_lMallocCalled);
1288     // call the previous alloc hook
1289     if (pfnOldCrtAllocHook != NULL)
1290       pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
1291     return TRUE;
1292   }
1293
1294   if (g_ulShowStackAtAlloc > 0) {
1295     AllocCheckFileOpen();  // Open logfile
1296   }
1297
1298    _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );
1299    _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
1300
1301   if (nAllocType == _HOOK_FREE) { // freeing
1302     // Try to get the header information
1303     if (_CrtIsValidHeapPointer(pvData)) {  // it is a valid Heap-Pointer
1304       // get the ID
1305       _CrtMemBlockHeader *pHead;
1306       // get a pointer to memory block header
1307       pHead = pHdr(pvData);
1308       nSize = pHead->nDataSize;
1309       lRequest = pHead->lRequest; // This is the ID!
1310
1311       if (pHead->nBlockUse == _IGNORE_BLOCK)
1312       {
1313         InterlockedDecrement(&g_lMallocCalled);
1314         if (pfnOldCrtAllocHook != NULL)
1315           pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
1316         return TRUE;
1317       }
1318     }
1319   }
1320
1321   if (g_ulShowStackAtAlloc > 0) {
1322     _ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),
1323       operation[nAllocType], nSize, blockType[_BLOCK_TYPE(nBlockUse)], lRequest );
1324     if ( pvData != NULL )
1325       _ftprintf( g_fFile, _T(" at 0x%X"), pvData );
1326     _ftprintf(g_fFile, _T("\n"));
1327   }
1328
1329   if (nAllocType == _HOOK_FREE) { // freeing:
1330     if (lRequest != 0) {  // RequestID was found
1331       BOOL bRet;
1332       // Try to find the RequestID in the Hash-Table, mark it that it was freed
1333       bRet = AllocHashRemove(lRequest);
1334       if(g_ulShowStackAtAlloc > 0) {
1335         if (bRet == FALSE) {
1336           // RequestID not found!
1337           _ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);
1338         }
1339       } // g_ulShowStackAtAlloc > 0
1340     }
1341     else {
1342       if(g_ulShowStackAtAlloc > 0) {
1343       // No valid RequestID found, display error
1344       _ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);
1345  
1346       }
1347     }
1348   }  // freeing
1349
1350   if (nAllocType == _HOOK_REALLOC) { // re-allocating
1351     // Try to get the header information
1352     if (_CrtIsValidHeapPointer(pvData)) {  // it is a valid Heap-Pointer
1353       BOOL bRet;
1354       LONG lReallocRequest;
1355       // get the ID
1356       _CrtMemBlockHeader *pHead;
1357       // get a pointer to memory block header
1358       pHead = pHdr(pvData);
1359       // Try to find the RequestID in the Hash-Table, mark it that it was freed
1360       lReallocRequest = pHead->lRequest;
1361       bRet = AllocHashRemove(lReallocRequest);
1362       if (g_ulShowStackAtAlloc > 0) {
1363         if (bRet == FALSE) {
1364           // RequestID not found!
1365           _ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);
1366         }
1367         else {
1368           _ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);
1369         }
1370       }  // g_ulShowStackAtAlloc > 0
1371     }  // ValidHeapPointer
1372   }  // re-allocating
1373
1374   if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
1375     InterlockedDecrement(&g_lMallocCalled);
1376     // call the previous alloc hook
1377     if (pfnOldCrtAllocHook != NULL)
1378       pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
1379     return TRUE;
1380   }
1381
1382   HANDLE hThread;
1383   if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
1384     GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) == 0) {
1385       // Something was wrong...
1386       _ftprintf(g_fFile, _T("###### Could not call 'DuplicateHandle' successfully\n"));
1387       InterlockedDecrement(&g_lMallocCalled);
1388       // call the previous alloc hook
1389       if (pfnOldCrtAllocHook != NULL)
1390         pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
1391       return TRUE;
1392   }
1393
1394   CONTEXT c;
1395   memset( &c, '\0', sizeof c );
1396   c.ContextFlags = CONTEXT_FULL;
1397
1398   // Get the context of this thread
1399 #if 0
1400   // init CONTEXT record so we know where to start the stackwalk
1401   if ( MyGetCurrentThreadContext( hThread, &c )  == 0) {
1402     if(g_ulShowStackAtAlloc > 1) {
1403       _ftprintf(g_fFile, _T("###### Could not call 'GetThreadContext' successfully\n"));
1404     }
1405     InterlockedDecrement(&g_lMallocCalled);
1406     // call the previous alloc hook
1407     if (pfnOldCrtAllocHook != NULL)
1408       pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
1409     CloseHandle(hThread);
1410     return TRUE; // could not get context
1411   }
1412 #else
1413   __asm
1414   {
1415       call x
1416  x:   pop eax
1417       mov c.Eip, eax
1418       mov c.Ebp, ebp
1419   }
1420 #endif
1421
1422   if(g_ulShowStackAtAlloc > 1) {
1423     if(g_ulShowStackAtAlloc > 2) {
1424       // output the callstack
1425       ShowStack( hThread, c, g_fFile);
1426     }
1427     else {
1428       // Output only (re)allocs
1429       if (nAllocType != _HOOK_FREE) {
1430         ShowStack( hThread, c, g_fFile);
1431       }
1432     }
1433   }  // g_ulShowStackAtAlloc > 1
1434   CloseHandle( hThread );
1435
1436   // Only isert in the Hash-Table if it is not a "freeing"
1437   if (nAllocType != _HOOK_FREE) {
1438     if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
1439       AllocHashInsert(lRequest, c, nSize);
1440   }
1441
1442   InterlockedDecrement(&g_lMallocCalled);
1443   // call the previous alloc hook
1444   if (pfnOldCrtAllocHook != NULL)
1445     pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
1446   return TRUE; // allow the memory operation to proceed
1447 }
1448
1449
1450
1451
1452 // ##########################################################################################
1453 // ##########################################################################################
1454 // ##########################################################################################
1455 // ##########################################################################################
1456
1457 #define gle (GetLastError())
1458 #define lenof(a) (sizeof(a) / sizeof((a)[0]))
1459 #define MAXNAMELEN 1024 // max name length for found symbols
1460 #define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL64 )
1461 #define TTBUFLEN 8096 // for a temp buffer (2^13)
1462
1463
1464
1465 // SymCleanup()
1466 typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
1467 tSC pSC = NULL;
1468
1469 // SymFunctionTableAccess64()
1470 typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
1471 tSFTA pSFTA = NULL;
1472
1473 // SymGetLineFromAddr64()
1474 typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
1475   OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
1476 tSGLFA pSGLFA = NULL;
1477
1478 // SymGetModuleBase64()
1479 typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
1480 tSGMB pSGMB = NULL;
1481
1482 // SymGetModuleInfo64()
1483 typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo );
1484 tSGMI pSGMI = NULL;
1485
1486 // SymGetOptions()
1487 typedef DWORD (__stdcall *tSGO)( VOID );
1488 tSGO pSGO = NULL;
1489
1490 // SymGetSymFromAddr64()
1491 typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
1492   OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
1493 tSGSFA pSGSFA = NULL;
1494
1495 // SymInitialize()
1496 typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
1497 tSI pSI = NULL;
1498
1499 // SymLoadModule64()
1500 typedef DWORD (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
1501   IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
1502 tSLM pSLM = NULL;
1503
1504 // SymSetOptions()
1505 typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
1506 tSSO pSSO = NULL;
1507
1508 // StackWalk64()
1509 typedef BOOL (__stdcall *tSW)( 
1510   DWORD MachineType, 
1511   HANDLE hProcess,
1512   HANDLE hThread, 
1513   LPSTACKFRAME64 StackFrame, 
1514   PVOID ContextRecord,
1515   PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
1516   PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
1517   PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
1518   PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
1519 tSW pSW = NULL;
1520
1521 // UnDecorateSymbolName()
1522 typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
1523   DWORD UndecoratedLength, DWORD Flags );
1524 tUDSN pUDSN = NULL;
1525
1526
1527
1528 struct ModuleEntry
1529 {
1530   std::string imageName;
1531   std::string moduleName;
1532   DWORD baseAddress;
1533   DWORD size;
1534 };
1535 typedef std::vector< ModuleEntry > ModuleList;
1536 typedef ModuleList::iterator ModuleListIter;
1537
1538 // **************************************** ToolHelp32 ************************
1539 #define MAX_MODULE_NAME32 255
1540 #define TH32CS_SNAPMODULE   0x00000008
1541 #pragma pack( push, 8 )
1542 typedef struct tagMODULEENTRY32
1543 {
1544     DWORD   dwSize;
1545     DWORD   th32ModuleID;       // This module
1546     DWORD   th32ProcessID;      // owning process
1547     DWORD   GlblcntUsage;       // Global usage count on the module
1548     DWORD   ProccntUsage;       // Module usage count in th32ProcessID's context
1549     BYTE  * modBaseAddr;        // Base address of module in th32ProcessID's context
1550     DWORD   modBaseSize;        // Size in bytes of module starting at modBaseAddr
1551     HMODULE hModule;            // The hModule of this module in th32ProcessID's context
1552     char    szModule[MAX_MODULE_NAME32 + 1];
1553     char    szExePath[MAX_PATH];
1554 } MODULEENTRY32;
1555 typedef MODULEENTRY32 *  PMODULEENTRY32;
1556 typedef MODULEENTRY32 *  LPMODULEENTRY32;
1557 #pragma pack( pop )
1558
1559
1560
1561 static bool GetModuleListTH32(ModuleList& modules, DWORD pid, FILE *fLogFile)
1562 {
1563   // CreateToolhelp32Snapshot()
1564   typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
1565   // Module32First()
1566   typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
1567   // Module32Next()
1568   typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
1569
1570   // try both dlls...
1571   const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
1572   HINSTANCE hToolhelp;
1573   tCT32S pCT32S;
1574   tM32F pM32F;
1575   tM32N pM32N;
1576
1577   HANDLE hSnap;
1578   MODULEENTRY32 me;
1579   me.dwSize = sizeof(me);
1580   bool keepGoing;
1581   ModuleEntry e;
1582   int i;
1583
1584   for (i = 0; i<lenof(dllname); i++ )
1585   {
1586     hToolhelp = LoadLibrary( dllname[i] );
1587     if (hToolhelp == NULL)
1588       continue;
1589     pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
1590     pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
1591     pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
1592     if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )
1593       break; // found the functions!
1594     FreeLibrary(hToolhelp);
1595     hToolhelp = NULL;
1596   }
1597
1598   if (hToolhelp == NULL)
1599     return false;
1600
1601   hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
1602   if (hSnap == (HANDLE) -1)
1603     return false;
1604
1605   keepGoing = !!pM32F( hSnap, &me );
1606   while (keepGoing)
1607   {
1608     e.imageName = me.szExePath;
1609     e.moduleName = me.szModule;
1610     e.baseAddress = (DWORD) me.modBaseAddr;
1611     e.size = me.modBaseSize;
1612     modules.push_back( e );
1613     keepGoing = !!pM32N( hSnap, &me );
1614   }
1615
1616   CloseHandle(hSnap);
1617   FreeLibrary(hToolhelp);
1618
1619   return modules.size() != 0;
1620 }  // GetModuleListTH32
1621
1622
1623 // **************************************** PSAPI ************************
1624 typedef struct _MODULEINFO {
1625     LPVOID lpBaseOfDll;
1626     DWORD SizeOfImage;
1627     LPVOID EntryPoint;
1628 } MODULEINFO, *LPMODULEINFO;
1629
1630 static bool GetModuleListPSAPI(ModuleList &modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)
1631 {
1632   // EnumProcessModules()
1633   typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
1634   // GetModuleFileNameEx()
1635   typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
1636   // GetModuleBaseName()
1637   typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
1638   // GetModuleInformation()
1639   typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );
1640
1641   HINSTANCE hPsapi;
1642   tEPM pEPM;
1643   tGMFNE pGMFNE;
1644   tGMBN pGMBN;
1645   tGMI pGMI;
1646
1647   DWORD i;
1648   ModuleEntry e;
1649   DWORD cbNeeded;
1650   MODULEINFO mi;
1651   HMODULE *hMods = 0;
1652   char *tt = 0;
1653
1654   hPsapi = LoadLibrary( _T("psapi.dll") );
1655   if ( hPsapi == 0 )
1656     return false;
1657
1658   modules.clear();
1659
1660   pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
1661   pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
1662   pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
1663   pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
1664   if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )
1665   {
1666     // we couldn´t find all functions
1667     FreeLibrary( hPsapi );
1668     return false;
1669   }
1670
1671   hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
1672   tt = (char*) malloc(sizeof(char) * TTBUFLEN);
1673
1674   if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
1675   {
1676     _ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
1677     goto cleanup;
1678   }
1679
1680   if ( cbNeeded > TTBUFLEN )
1681   {
1682     _ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
1683     goto cleanup;
1684   }
1685
1686   for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
1687   {
1688     // base address, size
1689     pGMI(hProcess, hMods[i], &mi, sizeof mi );
1690     e.baseAddress = (DWORD) mi.lpBaseOfDll;
1691     e.size = mi.SizeOfImage;
1692     // image file name
1693     tt[0] = 0;
1694     pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
1695     e.imageName = tt;
1696     // module name
1697     tt[0] = 0;
1698     pGMBN(hProcess, hMods[i], tt, TTBUFLEN );
1699     e.moduleName = tt;
1700
1701     modules.push_back(e);
1702   }
1703
1704 cleanup:
1705   if (hPsapi)
1706     FreeLibrary(hPsapi);
1707   free(tt);
1708   free(hMods);
1709
1710   return modules.size() != 0;
1711 }  // GetModuleListPSAPI
1712
1713
1714 static bool GetModuleList(ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)
1715 {
1716   // first try toolhelp32
1717   if (GetModuleListTH32(modules, pid, fLogFile) )
1718     return true;
1719   // then try psapi
1720   return GetModuleListPSAPI(modules, pid, hProcess, fLogFile);
1721 }  // GetModuleList
1722
1723
1724 static void EnumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid, FILE *fLogFile )
1725 {
1726   static ModuleList modules;
1727   static ModuleListIter it;
1728   char *img, *mod;
1729
1730   // fill in module list
1731   GetModuleList(modules, pid, hProcess, fLogFile);
1732
1733   for ( it = modules.begin(); it != modules.end(); ++ it )
1734   {
1735     // SymLoadModule() wants writeable strings
1736     img = strdup(it->imageName.c_str());
1737     mod = strdup(it->moduleName.c_str());
1738
1739     pSLM( hProcess, 0, img, mod, it->baseAddress, it->size );
1740
1741     free(img);
1742     free(mod);
1743     std::string s;
1744   }
1745 }  // EnumAndLoadModuleSymbols
1746
1747 static int InitStackWalk(void)
1748 {
1749   if (g_bInitialized != FALSE)
1750     return 0;  // already initialized
1751
1752   // 02-12-19: Now we only support dbghelp.dll!
1753   //           To use it on NT you have to install the redistrubutable for DBGHELP.DLL
1754   g_hImagehlpDll = LoadLibrary( _T("dbghelp.dll") );
1755   if ( g_hImagehlpDll == NULL )
1756   {
1757     printf( "LoadLibrary( \"dbghelp.dll\" ): GetLastError = %lu\n", gle );
1758     g_bInitialized = FALSE;
1759     return 1;
1760   }
1761
1762   // now we only support the newer dbghlp.dll with the "64"-functions (StackWalk64, a.s.o.)
1763   // If your dbghlp.dll does not support this, please download the redistributable from MS
1764   // Normally from: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=CD1FC4B2-0885-47F4-AF45-7FD5E14DB6C0
1765
1766   pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );
1767   pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess64" );
1768   pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr64" );
1769   pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase64" );
1770   pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo64" );
1771   pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );
1772   pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr64" );
1773   pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );
1774   pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );
1775   pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk64" );
1776   pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );
1777   pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule64" );
1778
1779   if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
1780     pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
1781     pSW == NULL || pUDSN == NULL || pSLM == NULL )
1782   {
1783     printf( "GetProcAddress(): some required function not found.\n" );
1784     FreeLibrary( g_hImagehlpDll );
1785     g_bInitialized = FALSE;
1786     return 1;
1787   }
1788
1789   g_bInitialized = TRUE;
1790   InitializeCriticalSection(&g_csFileOpenClose);
1791   return 0;
1792 }
1793
1794 // This function if NOT multi-threading capable
1795 // It should only be called from the main-Function!
1796 int InitAllocCheckWN(eAllocCheckOutput eOutput, LPCTSTR pszFileName, ULONG ulShowStackAtAlloc) {
1797   if (g_bInitialized) {
1798     return 2;  // already initialized!
1799   }
1800   if (ulShowStackAtAlloc <= 3)
1801     g_ulShowStackAtAlloc = ulShowStackAtAlloc;
1802   else
1803     g_ulShowStackAtAlloc = 0;
1804
1805   if (pszFileName != NULL) 
1806     g_pszAllocLogName = _tcsdup(pszFileName);
1807   else
1808     g_pszAllocLogName = NULL;
1809
1810   g_CallstackOutputType = eOutput;
1811
1812 #ifdef _DEBUG
1813   AllocHashInit();
1814
1815 #ifdef WITH_IMALLOC_SPY
1816   HRESULT hr;
1817   // erzeuge mein malloc-Spy object
1818   LPMALLOCSPY pMallocSpy = new CMallocSpy(); // wird später durch Release freigegeben
1819   if (pMallocSpy != NULL)
1820   {
1821     // CoInitilize(); // ??? Ist dies notwendig ?
1822     hr = CoRegisterMallocSpy(pMallocSpy);
1823     if FAILED(hr)
1824     {
1825       _tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr);
1826     }
1827   }
1828 #endif
1829
1830   // save the previous alloc hook
1831   pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook);
1832 #endif
1833
1834   return InitStackWalk();
1835 }  // InitAllocCheckWN
1836
1837 static TCHAR s_szExceptionLogFileName[_MAX_PATH] = _T("\\exceptions.log");  // default
1838 static BOOL s_bUnhandledExeptionFilterSet = FALSE;
1839 static LONG __stdcall CrashHandlerExceptionFilter(EXCEPTION_POINTERS* pExPtrs)
1840 {
1841   if (pExPtrs->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
1842   {
1843     static char MyStack[1024*128];  // be sure that we have enought space...
1844     // it assumes that DS and SS are the same!!! (this is the case for Win32)
1845     // change the stack only if the selectors are the same (this is the case for Win32)
1846     //__asm push offset MyStack[1024*128];
1847     //__asm pop esp;
1848   __asm mov eax,offset MyStack[1024*128];
1849   __asm mov esp,eax;
1850   }
1851
1852    LONG lRet;
1853    lRet = StackwalkFilter(pExPtrs, /*EXCEPTION_CONTINUE_SEARCH*/EXCEPTION_EXECUTE_HANDLER, s_szExceptionLogFileName);
1854    TCHAR lString[500];
1855    _stprintf(lString,
1856       _T("*** Unhandled Exception!\n")
1857       _T("   ExpCode: 0x%8.8X\n")
1858       _T("   ExpFlags: %d\n")
1859       _T("   ExpAddress: 0x%8.8X\n")
1860       _T("   Please report!"),
1861       pExPtrs->ExceptionRecord->ExceptionCode,
1862       pExPtrs->ExceptionRecord->ExceptionFlags,
1863       pExPtrs->ExceptionRecord->ExceptionAddress);
1864    FatalAppExit(-1,lString);
1865    return lRet;
1866 }
1867
1868 int InitAllocCheck(eAllocCheckOutput eOutput, BOOL bSetUnhandledExeptionFilter, ULONG ulShowStackAtAlloc)  // will create the filename by itself
1869 {
1870   TCHAR szModName[_MAX_PATH];
1871   if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
1872   {
1873     _tcscpy(s_szExceptionLogFileName, szModName);
1874     if (eOutput == ACOutput_XML)
1875       _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
1876     else
1877       _tcscat(s_szExceptionLogFileName, _T(".exp.log"));
1878
1879     if (eOutput == ACOutput_XML)
1880       _tcscat(szModName, _T(".mem.xml-leaks"));
1881     else
1882       _tcscat(szModName, _T(".mem.log"));
1883   }
1884   else
1885   {
1886     if (eOutput == ACOutput_XML)
1887       _tcscpy(szModName, _T("\\mem-leaks.xml-leaks"));  // default
1888     else
1889       _tcscpy(szModName, _T("\\mem-leaks.log"));  // default
1890   }
1891
1892   if ((bSetUnhandledExeptionFilter != FALSE) && (s_bUnhandledExeptionFilterSet == FALSE) )
1893   {
1894     // set global exception handler (for handling all unhandled exceptions)
1895     SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
1896     s_bUnhandledExeptionFilterSet = TRUE;
1897   }
1898
1899   return InitAllocCheckWN(eOutput, szModName, ulShowStackAtAlloc);
1900 }
1901
1902
1903 // This function if NOT multi-threading capable
1904 // It should only be called from the main-Function!
1905 //   Returns the number of bytes that are not freed (leaks)
1906 ULONG DeInitAllocCheck(void) {
1907   ULONG ulRet = 0;
1908   if (g_bInitialized) {
1909
1910 #ifdef _DEBUG
1911     InterlockedIncrement(&g_lMallocCalled); // No deactivate MyAllocHook, because StackWalker will allocate some memory)
1912     ulRet = AllocHashDeinit();  // output the not freed memory
1913     // remove the hook and set the old one
1914     _CrtSetAllocHook(pfnOldCrtAllocHook);
1915
1916 #ifdef WITH_IMALLOC_SPY
1917     CoRevokeMallocSpy();
1918 #endif
1919
1920 #endif
1921
1922     EnterCriticalSection(&g_csFileOpenClose);  // wait until a running stack dump was created
1923     g_bInitialized = FALSE;
1924
1925     // de-init symbol handler etc. (SymCleanup())
1926     if (pSC != NULL)
1927       pSC( GetCurrentProcess() );
1928     FreeLibrary( g_hImagehlpDll );
1929
1930     LeaveCriticalSection(&g_csFileOpenClose);
1931     if (g_pszAllocLogName != NULL) {
1932       free(g_pszAllocLogName);
1933       g_pszAllocLogName = NULL;
1934     }
1935     if (g_fFile != NULL) {
1936        fclose(g_fFile);
1937        g_fFile = NULL;
1938     }
1939
1940     DeleteCriticalSection(&g_csFileOpenClose);
1941     InterlockedDecrement(&g_lMallocCalled);
1942   }
1943
1944   if (s_bUnhandledExeptionFilterSet != TRUE)
1945   {
1946     SetUnhandledExceptionFilter(NULL);
1947     s_bUnhandledExeptionFilterSet = FALSE;
1948   }
1949   return ulRet;
1950 }  // DeInitAllocCheck
1951
1952
1953
1954 void OnlyInstallUnhandeldExceptionFilter(eAllocCheckOutput eOutput)
1955 {
1956   if (s_bUnhandledExeptionFilterSet == FALSE)
1957   {
1958     TCHAR szModName[_MAX_PATH];
1959     if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
1960     {
1961       _tcscpy(s_szExceptionLogFileName, szModName);
1962       if (eOutput == ACOutput_XML)
1963         _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
1964       else
1965         _tcscat(s_szExceptionLogFileName, _T(".exp.log"));
1966
1967       if (eOutput == ACOutput_XML)
1968         _tcscat(szModName, _T(".mem.xml-leaks"));
1969       else
1970         _tcscat(szModName, _T(".mem.log"));
1971     }
1972     else
1973     {
1974       if (eOutput == ACOutput_XML)
1975         _tcscpy(szModName, _T("\\mem-leaks.xml-leaks"));  // default
1976       else
1977         _tcscpy(szModName, _T("\\mem-leaks.log"));  // default
1978     }
1979     // set it again; WARNING: this will override the setting for a possible AllocCheck-Setting
1980     g_CallstackOutputType = eOutput;
1981
1982     // set global exception handler (for handling all unhandled exceptions)
1983     SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
1984     s_bUnhandledExeptionFilterSet = TRUE;
1985   }
1986 }
1987
1988
1989
1990 static TCHAR *GetExpectionCodeText(DWORD dwExceptionCode) {
1991   switch(dwExceptionCode) {
1992   case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");
1993   case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");
1994   case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");
1995   case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");
1996   case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");
1997   case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");
1998   case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");
1999   case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");
2000   case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");
2001   case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");
2002   case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");
2003   case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");
2004   case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");
2005   case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");
2006   case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");
2007   case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");
2008   case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");
2009   case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");
2010   case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");
2011   case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");
2012   case DBG_CONTROL_C : return _T("DBG CONTROL C ");
2013   default:
2014     return _T("<unkown exception>");
2015   }
2016 }  // GetExpectionCodeText
2017
2018 // Function is not multi-threading safe, because of static char!
2019 static TCHAR *GetAdditionalExpectionCodeText(PEXCEPTION_RECORD pExceptionRecord) {
2020   static TCHAR szTemp[100];
2021
2022   switch(pExceptionRecord->ExceptionCode) {
2023   case EXCEPTION_ACCESS_VIOLATION:
2024     if (pExceptionRecord->NumberParameters == 2) {
2025       switch(pExceptionRecord->ExceptionInformation[0]) {
2026       case 0: // read attempt
2027         _stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
2028         return szTemp;
2029       case 1: // write attempt
2030         _stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
2031         return szTemp;
2032       default:
2033         return _T("");
2034       }
2035     }  // if (pExceptionRecord->NumberParameters == 2)
2036     return _T("");
2037   default:
2038     return _T("");
2039   }  // switch(pExceptionRecord->ExceptionCode)
2040 }  // GetAdditionalExpectionCodeText
2041
2042 std::string SimpleXMLEncode(PCSTR szText)
2043 {
2044   std::string szRet;
2045
2046   for (size_t i=0; i<strlen(szText); i++)
2047   {
2048     switch(szText[i])
2049     {
2050     case '&':
2051       szRet.append("&amp;");
2052       break;
2053     case '<':
2054       szRet.append("&lt;");
2055       break;
2056     case '>':
2057       szRet.append("&gt;");
2058       break;
2059     case '"':
2060       szRet.append("&quot;");
2061       break;
2062     case '\'':
2063       szRet.append("&apos;");
2064       break;
2065     default:
2066       szRet += szText[i];
2067     }
2068   }
2069   return szRet;
2070 }
2071
2072
2073 // #################################################################################
2074 // #################################################################################
2075 // Here the Stackwalk-Part begins.
2076 //   Some of the code is from an example from a book 
2077 //   But I couldn´t find the reference anymore... sorry...
2078 //   If someone knowns, please let me know...
2079 // #################################################################################
2080 // #################################################################################
2081
2082
2083 // if you use C++ exception handling: install a translator function
2084 // with set_se_translator(). In the context of that function (but *not*
2085 // afterwards), you can either do your stack dump, or save the CONTEXT
2086 // record as a local copy. Note that you must do the stack sump at the
2087 // earliest opportunity, to avoid the interesting stackframes being gone
2088 // by the time you do the dump.
2089
2090 // status: 
2091 // - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht
2092 // - EXCEPTION_CONTINUE_EXECUTION: 
2093 // - EXCEPTION_EXECUTE_HANDLER:
2094 DWORD StackwalkFilter( EXCEPTION_POINTERS *ep, DWORD status, LPCTSTR pszLogFile)
2095 {
2096   HANDLE hThread;
2097   FILE *fFile = stdout;  // default to stdout
2098
2099   if (pszLogFile != NULL) {  // a filename is provided
2100     // Open the logfile
2101     fFile = _tfopen(pszLogFile, _T("a"));
2102     if (fFile != NULL) {  // Is the file too big?
2103       long size;
2104       fseek(fFile, 0, SEEK_END);
2105       size = ftell(fFile);  // Get the size of the file
2106       if (size >= LOG_FILE_MAX_SIZE) {
2107         TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
2108         // It is too big...
2109         fclose(fFile);
2110         _tcscpy(pszTemp, pszLogFile);
2111         _tcscat(pszTemp, _T(".old"));
2112         _tremove(pszTemp);  // Remove an old file, if exists
2113         _trename(pszLogFile, pszTemp);  // rename the actual file
2114         fFile = _tfopen(pszLogFile, _T("w"));  // create a new file
2115         free(pszTemp);
2116       }
2117     }
2118   }  // if (pszLogFile != NULL) 
2119   if (fFile == NULL) {
2120     fFile = stdout;
2121   }
2122
2123   // Write infos about the exception
2124   if (g_CallstackOutputType == ACOutput_XML)
2125   {
2126     _ftprintf(fFile, _T("<EXCEPTION code=\"%8.8X\" addr=\"%8.8X\" "), 
2127       ep->ExceptionRecord->ExceptionCode,
2128       ep->ExceptionRecord->ExceptionAddress);
2129     WriteDateTime(fFile, TRUE);
2130     _ftprintf(fFile, _T("code_desc=\"%s\" more_desc=\"%s\">\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
2131       GetAdditionalExpectionCodeText(ep->ExceptionRecord));
2132   }
2133   else
2134   {
2135     _ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"), 
2136       ep->ExceptionRecord->ExceptionCode,
2137       ep->ExceptionRecord->ExceptionAddress);
2138     _ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
2139       GetAdditionalExpectionCodeText(ep->ExceptionRecord));
2140   }
2141
2142   DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
2143     GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS );
2144   ShowStack( hThread, *(ep->ContextRecord), fFile);
2145   CloseHandle( hThread );
2146
2147   if (g_CallstackOutputType == ACOutput_XML)
2148     _ftprintf(fFile, _T("</EXCEPTION>\n"));
2149
2150   fclose(fFile);
2151
2152   return status;
2153 }  // StackwalkFilter
2154
2155 void ShowStack( HANDLE hThread, CONTEXT& c, LPCTSTR pszLogFile)
2156 {
2157   FILE *fFile = stdout;  // default to stdout
2158
2159   if (pszLogFile != NULL) {  // a filename is available
2160     // Open the logfile
2161     fFile = _tfopen(pszLogFile, _T("a"));
2162     if (fFile != NULL) {  // Is the file too big?
2163       long size;
2164       fseek(fFile, 0, SEEK_END);
2165       size = ftell(fFile);  // Get the size of the file
2166       if (size >= LOG_FILE_MAX_SIZE) {
2167         TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
2168         // It is too big...
2169         fclose(fFile);
2170         _tcscpy(pszTemp, pszLogFile);
2171         _tcscat(pszTemp, _T(".old"));
2172         _tremove(pszTemp);  // Remove an old file, if exists
2173         _trename(pszLogFile, pszTemp);  // rename the actual file
2174         fFile = _tfopen(pszLogFile, _T("w"));  // open new file
2175         free(pszTemp);
2176       }
2177     }
2178   }  // if (pszLogFile != NULL) 
2179   if (fFile == NULL) {
2180     fFile = stdout;
2181   }
2182
2183   ShowStack( hThread, c, fFile);
2184
2185   fclose(fFile);
2186 }
2187
2188
2189 static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {
2190   ShowStackRM(hThread, c, fLogFile, NULL, GetCurrentProcess());
2191 }
2192
2193 static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hSWProcess) {
2194   // normally, call ImageNtHeader() and use machine info from PE header
2195   // but we assume that it is an I386 image...
2196   DWORD imageType = IMAGE_FILE_MACHINE_I386;
2197   HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside but we only do the stackdump in our own process
2198   int frameNum; // counts walked frames
2199   DWORD64 offsetFromSymbol; // tells us how far from the symbol we were
2200   DWORD offsetFromLine; // tells us how far from the line we were
2201   DWORD symOptions; // symbol handler settings
2202
2203   static IMAGEHLP_SYMBOL64 *pSym = NULL;
2204   char undName[MAXNAMELEN]; // undecorated name
2205   char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans
2206   IMAGEHLP_MODULE64 Module;
2207   IMAGEHLP_LINE64 Line;
2208   BOOL bXMLTagWrote;
2209
2210   std::string symSearchPath;
2211
2212   static bool bFirstTime = TRUE;
2213
2214   // If no logfile is present, outpur to "stdout"
2215   if (fLogFile == NULL) {
2216     fLogFile = stdout;
2217   }
2218
2219   STACKFRAME64 s; // in/out stackframe
2220   memset( &s, '\0', sizeof s );
2221
2222   if ( (g_bInitialized == FALSE) && (bFirstTime == TRUE) ) {
2223     InitStackWalk();
2224   }
2225
2226   if (g_bInitialized == FALSE)
2227   {
2228     // Could not init!!!!
2229     bFirstTime = FALSE;
2230     _ftprintf(fLogFile, _T("%lu: Stackwalker not initialized (or was not able to initialize)!\n"), g_dwShowCount);
2231     return;
2232   }
2233
2234 // Critical section begin...
2235   EnterCriticalSection(&g_csFileOpenClose);
2236
2237   InterlockedIncrement((long*) &g_dwShowCount);  // erhöhe counter
2238
2239
2240   // NOTE: normally, the exe directory and the current directory should be taken
2241   // from the target process. The current dir would be gotten through injection
2242   // of a remote thread; the exe fir through either ToolHelp32 or PSAPI.
2243
2244   if (pSym == NULL) {
2245     pSym = (IMAGEHLP_SYMBOL64 *) malloc( IMGSYMLEN + MAXNAMELEN );
2246     if (!pSym) goto cleanup;  // not enough memory...
2247   }
2248
2249   if (g_CallstackOutputType != ACOutput_XML)
2250   {
2251     _ftprintf(fLogFile, _T("%lu: "), g_dwShowCount);
2252     WriteDateTime(fLogFile);
2253     _ftprintf(fLogFile, _T("\n"));
2254   }
2255
2256
2257   if (bFirstTime) {
2258
2259     CHAR *tt, *p;
2260
2261     tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer
2262     if (!tt) goto cleanup;  // not enough memory...
2263
2264     // build symbol search path from:
2265     symSearchPath = "";
2266     // current directory
2267     if ( GetCurrentDirectoryA( TTBUFLEN, tt ) )
2268       symSearchPath += tt + std::string( ";" );
2269     // dir with executable
2270     if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )
2271     {
2272       for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
2273       {
2274         // locate the rightmost path separator
2275         if ( *p == '\\' || *p == '/' || *p == ':' )
2276           break;
2277       }
2278       // if we found one, p is pointing at it; if not, tt only contains
2279       // an exe name (no path), and p points before its first byte
2280       if ( p != tt ) // path sep found?
2281       {
2282         if ( *p == ':' ) // we leave colons in place
2283           ++ p;
2284         *p = '\0'; // eliminate the exe name and last path sep
2285         symSearchPath += tt + std::string( ";" );
2286       }
2287     }
2288     // environment variable _NT_SYMBOL_PATH
2289     if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
2290       symSearchPath += tt + std::string( ";" );
2291     // environment variable _NT_ALTERNATE_SYMBOL_PATH
2292     if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
2293       symSearchPath += tt + std::string( ";" );
2294     // environment variable SYSTEMROOT
2295     if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )
2296       symSearchPath += tt + std::string( ";" );
2297
2298
2299
2300     if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
2301       symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );
2302
2303     // why oh why does SymInitialize() want a writeable string?
2304     strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
2305     tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator
2306
2307     // init symbol handler stuff (SymInitialize())
2308     if ( ! pSI( hProcess, tt, false ) )
2309     {
2310       if (g_CallstackOutputType != ACOutput_XML)
2311         _ftprintf(fLogFile, _T("%lu: SymInitialize(): GetLastError = %lu\n"), g_dwShowCount, gle );
2312       if (tt) free( tt );
2313       goto cleanup;
2314     }
2315
2316     // SymGetOptions()
2317     symOptions = pSGO();
2318     symOptions |= SYMOPT_LOAD_LINES;
2319     symOptions &= ~SYMOPT_UNDNAME;
2320     symOptions &= ~SYMOPT_DEFERRED_LOADS;
2321     pSSO( symOptions ); // SymSetOptions()
2322
2323     // Enumerate modules and tell dbghlp.dll about them.
2324     // On NT, this is not necessary, but it won't hurt.
2325     EnumAndLoadModuleSymbols( hProcess, GetCurrentProcessId(), fLogFile );
2326
2327     if (tt) 
2328       free( tt );
2329   }  // bFirstTime = TRUE
2330   bFirstTime = FALSE;
2331
2332   // init STACKFRAME for first call
2333   // Notes: AddrModeFlat is just an assumption. I hate VDM debugging.
2334   // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
2335   // and good riddance.
2336   s.AddrPC.Offset = c.Eip;
2337   s.AddrPC.Mode = AddrModeFlat;
2338   s.AddrFrame.Offset = c.Ebp;
2339   s.AddrFrame.Mode = AddrModeFlat;
2340   s.AddrStack.Offset = c.Ebp;
2341   s.AddrStack.Mode = AddrModeFlat;
2342
2343   memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
2344   pSym->SizeOfStruct = IMGSYMLEN;
2345   pSym->MaxNameLength = MAXNAMELEN;
2346
2347   memset( &Line, '\0', sizeof Line );
2348   Line.SizeOfStruct = sizeof Line;
2349
2350   memset( &Module, '\0', sizeof Module );
2351   Module.SizeOfStruct = sizeof Module;
2352
2353   for ( frameNum = 0; ; ++ frameNum )
2354   {
2355     // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
2356     // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
2357     // assume that either you are done, or that the stack is so hosed that the next
2358     // deeper frame could not be found.
2359     // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
2360     if ( ! pSW( imageType, hSWProcess, hThread, &s, NULL, ReadMemoryFunction, pSFTA, pSGMB, NULL ) )
2361       break;
2362
2363     bXMLTagWrote = FALSE;
2364
2365     if (g_CallstackOutputType == ACOutput_Advanced)
2366       _ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);
2367     if ( s.AddrPC.Offset == 0 )
2368     {
2369       // Special case: If we are here, we have no valid callstack entry!
2370       switch(g_CallstackOutputType)
2371       {
2372       case ACOutput_Simple:
2373         _ftprintf(fLogFile, _T("%lu: (-nosymbols- PC == 0)\n"), g_dwShowCount);
2374         break;
2375       case ACOutput_Advanced:
2376         _ftprintf(fLogFile, _T("   (-nosymbols- PC == 0)\n"));
2377         break;
2378       case ACOutput_XML:
2379         // TODO: ....
2380         _ftprintf(fLogFile, _T("<STACKENTRY decl=\"(-nosymbols- PC == 0)\"/>\n"));
2381         break;
2382       }
2383     }
2384     else
2385     {
2386       // we seem to have a valid PC
2387       undName[0] = 0;
2388       undFullName[0] = 0;
2389       offsetFromSymbol = 0;
2390       // show procedure info (SymGetSymFromAddr())
2391       if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
2392       {
2393         if (g_CallstackOutputType == ACOutput_Advanced)
2394         {
2395           if ( gle != 487 )
2396             _ftprintf(fLogFile, _T("   SymGetSymFromAddr(): GetLastError = %lu\n"), gle );
2397           else
2398             _ftprintf(fLogFile, _T("\n"));
2399         }
2400       }
2401       else
2402       {
2403         // UnDecorateSymbolName()
2404         pUDSN( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY );
2405         pUDSN( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );
2406         if (g_CallstackOutputType == ACOutput_Advanced)
2407         {
2408           if (strlen(undName) > 0)
2409             fprintf(fLogFile, "     %s %+ld bytes\n", undName, (long) offsetFromSymbol );
2410           else
2411           {
2412             fprintf(fLogFile, "     Sig:  %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );
2413             strcpy(undName, pSym->Name);
2414           }
2415           fprintf(fLogFile, "%lu:     Decl: %s\n", g_dwShowCount, undFullName );
2416         }
2417       }
2418       //if (g_CallstackOutputType == ACOutput_XML)
2419       //  fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
2420
2421       // show line number info, NT5.0-method (SymGetLineFromAddr())
2422       offsetFromLine = 0;
2423       if ( pSGLFA != NULL )
2424       { // yes, we have SymGetLineFromAddr()
2425         if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromLine, &Line ) )
2426         {
2427           if ( (gle != 487) && (frameNum > 0) )  // ignore error for first frame
2428           {
2429             if (g_CallstackOutputType == ACOutput_XML)
2430             {
2431               _ftprintf(fLogFile, _T("<STACKENTRY "));
2432               bXMLTagWrote = TRUE;
2433               fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
2434               _ftprintf(fLogFile, _T("srcfile=\"SymGetLineFromAddr(): GetLastError = %lu\" "), gle);
2435             }
2436             else
2437               _ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );
2438           }
2439         }
2440         else
2441         {
2442           switch(g_CallstackOutputType)
2443           {
2444           case ACOutput_Advanced:
2445             fprintf(fLogFile, "%lu:     Line: %s(%lu) %+ld bytes\n", g_dwShowCount,
2446               Line.FileName, Line.LineNumber, offsetFromLine );
2447             break;
2448           case ACOutput_Simple:
2449             fprintf(fLogFile, "%lu: %s(%lu) %+ld bytes (%s)\n", g_dwShowCount,
2450               Line.FileName, Line.LineNumber, offsetFromLine, undName);
2451             break;
2452           case ACOutput_XML:
2453             _ftprintf(fLogFile, _T("<STACKENTRY "));
2454             bXMLTagWrote = TRUE;
2455             fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
2456             fprintf(fLogFile, "srcfile=\"%s\" line=\"%lu\" line_offset=\"%+ld\" ", 
2457               SimpleXMLEncode(Line.FileName).c_str(), Line.LineNumber, offsetFromLine, undName);
2458             break;
2459           }
2460         }
2461       } // yes, we have SymGetLineFromAddr()
2462
2463       // show module info (SymGetModuleInfo())
2464       if ( (g_CallstackOutputType == ACOutput_Advanced) || (g_CallstackOutputType == ACOutput_XML) )
2465       {
2466         if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )
2467         {
2468           if (g_CallstackOutputType == ACOutput_Advanced)
2469             _ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );
2470         }
2471         else
2472         { // got module info OK
2473           char ty[80];
2474           switch ( Module.SymType )
2475           {
2476           case SymNone:
2477             strcpy( ty, "-nosymbols-" );
2478             break;
2479           case SymCoff:
2480             strcpy( ty, "COFF" );
2481             break;
2482           case SymCv:
2483             strcpy( ty, "CV" );
2484             break;
2485           case SymPdb:
2486             strcpy( ty, "PDB" );
2487             break;
2488           case SymExport:
2489             strcpy( ty, "-exported-" );
2490             break;
2491           case SymDeferred:
2492             strcpy( ty, "-deferred-" );
2493             break;
2494           case SymSym:
2495             strcpy( ty, "SYM" );
2496             break;
2497 #if API_VERSION_NUMBER >= 9
2498           case SymDia:
2499             strcpy( ty, "DIA" );
2500             break;
2501 #endif
2502           default:
2503             _snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
2504             break;
2505           }
2506
2507           if (g_CallstackOutputType == ACOutput_XML)
2508           {
2509             // now, check if the XML-Entry is written...
2510             if (bXMLTagWrote == FALSE) 
2511             {
2512               _ftprintf(fLogFile, _T("<STACKENTRY "));
2513               bXMLTagWrote = TRUE;
2514               fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
2515               _ftprintf(fLogFile, _T("srcfile=\"\" "));
2516               bXMLTagWrote = TRUE;
2517             }
2518           }
2519
2520           if (g_CallstackOutputType == ACOutput_Advanced)
2521           {
2522             fprintf(fLogFile, "%lu:     Mod:  %s, base: %08lxh\n", g_dwShowCount,
2523               Module.ModuleName, Module.BaseOfImage );
2524             if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!
2525               _ftprintf(fLogFile, _T("%lu:     Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);
2526               fprintf(fLogFile, "%lu:     Sym:  type: %s, file: %s\n", g_dwShowCount,
2527                 ty, Module.LoadedImageName );
2528             }
2529           }
2530           else
2531           {
2532             // XML:
2533             if (bXMLTagWrote == TRUE)
2534               fprintf(fLogFile, "module=\"%s\" base=\"%08lx\" ", Module.ModuleName, Module.BaseOfImage);
2535           }
2536         } // got module info OK
2537       }
2538       if ( (g_CallstackOutputType == ACOutput_XML) && (bXMLTagWrote == TRUE) )
2539         _ftprintf(fLogFile, _T("/>\n"));  // terminate the XML node
2540
2541     } // we seem to have a valid PC
2542
2543     // no return address means no deeper stackframe
2544     if ( s.AddrReturn.Offset == 0 )
2545     {
2546       // avoid misunderstandings in the printf() following the loop
2547       SetLastError( 0 );
2548       break;
2549     }
2550
2551   } // for ( frameNum )
2552
2553   if ( (g_CallstackOutputType != ACOutput_XML) && (gle != 0) )
2554     _ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );
2555
2556 cleanup:
2557   //if (pSym) free( pSym );
2558   if (fLogFile) {
2559     _ftprintf(fLogFile, _T("\n\n"));
2560     if (g_dwShowCount % 1000)
2561       fflush(fLogFile);
2562   }
2563
2564   LeaveCriticalSection(&g_csFileOpenClose);
2565
2566
2567 // Critical section end...
2568 }  // ShowStackRM
2569
2570 #pragma warning(pop)