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