OWLNext    7.0
Borland's Object Windows Library for the modern age
Loading...
Searching...
No Matches
Stackwalker.cpp
Go to the documentation of this file.
1/*////////////////////////////////////////////////////////////////////////////
2 * Project:
3 * Memory_and_Exception_Trace
4 *
5 * ///////////////////////////////////////////////////////////////////////////
6 * File:
7 * Stackwalker.cpp
8 *
9 * Remarks:
10 * Dumps memory leaks (unreleased allocations) for CRT-Allocs and COM-Allocs
11 * Dumps the stack of an thread if an exepction occurs
12 *
13 * Known bugs:
14 * - If the allocation-RequestID wrap, then allocations will get lost...
15 *
16 * Author:
17 * Jochen Kalmbach, Germany
18 *
19 *//////////////////////////////////////////////////////////////////////////////
20
21#include <owl/pch.h>
22
23#if !(defined(BI_COMP_MSC) && (defined(__DEBUG) || defined(_DEBUG)))
24#error StackWalker only supports debug mode for VC++.
25#endif
26
27#include <windows.h>
28#include <string>
29#include <vector>
30#include <stdio.h>
31#include <stdlib.h>
32#include <time.h>
33#include <crtdbg.h>
34#include <tchar.h>
35#include <algorithm>
36
37#include "stackwalker.h"
38
39namespace {
40
41// If the following is defined, only the used memories are stored in the hash-table.
42// If the memory is freed, it will be removed from the hash-table (to reduce memory)
43// Consequences: At DeInitAllocHook, only Leaks will be reported
44#define HASH_ENTRY_REMOVE_AT_FREE
45
46
47// 0 = Do not write any output during runtime-alloc-call
48// 1 = Write only the alloc action (malloc, realloc, free)
49// 2 = Write alloc action and callstack only for malloc/realloc
50// 3 = Write alloc action and callstack for all actions
52
53// the form of the output file
55
56
57// Size of Hash-Table
58#define ALLOC_HASH_ENTRIES /*1024*/8192
59
60
61// Size of Callstack-trace in bytes (0x500 => appr. 5-9 functions, depending on parameter count for each function)
62#define MAX_ESP_LEN_BUF 0x500
63
64
65// Normally we can ignore allocations from the Runtime-System
66#define IGNORE_CRT_ALLOC
67
68// MaxSize: 128 KByte (only for StackwalkFilter)
69#define LOG_FILE_MAX_SIZE 1024*128
70
71// If the following is defined, then COM-Leaks will also be tracked
72#define WITH_IMALLOC_SPY
73
74// #############################################################################################
75#ifdef WITH_IMALLOC_SPY
76//forwards:
77void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize);
79
80// IMallocSpy-Interface
81/// \cond
82class CMallocSpy : public IMallocSpy
83{
84public:
85 CMallocSpy(void) {
86 m_cRef = 0;
87 m_cbRequest = 0;
88 }
89 ~CMallocSpy(void) {
90 }
91 // IUnknown methods
92 STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppUnk) {
93 HRESULT hr = S_OK;
95 *ppUnk = (IUnknown *) this;
96 }
97 else if (IsEqualIID(riid, IID_IMallocSpy)) {
98 *ppUnk = (IMalloc *) this;
99 }
100 else {
101 *ppUnk = NULL;
103 }
104 AddRef();
105 return hr;
106 }
107 STDMETHOD_(ULONG, AddRef) (void) {
109 }
110 STDMETHOD_(ULONG, Release) (void) {
111 LONG cRef;
113 if (cRef == 0)
114 {
115 delete this;
116 }
117 return cRef;
118 }
119 // IMallocSpy methods
122 return cbRequest;
123 }
124 STDMETHOD_(void *, PostAlloc) (void *pActual) {
127 GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {
128 // Ok
129 CONTEXT c;
130 memset( &c, '\0', sizeof c );
131 c.ContextFlags = CONTEXT_FULL;
132 if ( GetThreadContext( hThread, &c ) != 0) {
133 // Ok
135 }
137 }
138 return pActual;
139 }
140 STDMETHOD_(void *, PreFree) (void *pRequest, BOOL fSpyed) {
142 return pRequest;
143 }
144 STDMETHOD_(void, PostFree) (BOOL fSpyed) {
145 return;
146 }
148 void **ppNewRequest, BOOL fSpyed) {
151
152 *ppNewRequest = pRequest; // Bug fixed. Thanx to Christoph Weber
153
154 return cbRequest;
155 }
156 STDMETHOD_(void *, PostRealloc) (void *pActual, BOOL fSpyed) {
159 GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {
160 // Ok
161 CONTEXT c;
162 memset( &c, '\0', sizeof c );
163 c.ContextFlags = CONTEXT_FULL;
164 if ( GetThreadContext( hThread, &c ) != 0) {
165 // Ok
167 }
169 }
170 return pActual;
171 }
172 STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) {
173 return pRequest;
174 }
176 return cbActual;
177 }
178 STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) {
179 return pRequest;
180 }
182 return fActual;
183 }
185 return;
186 }
188 return;
189 }
190private:
191 LONG m_cRef;
193};
194/// \endcond
195#endif
196
197// #############################################################################################
198#ifdef _IMAGEHLP_
199#error "'imagehlp.h' should only included here, not before this point! Otherwise there are some problems!"
200#endif
201#pragma pack( push, before_imagehlp, 8 )
202#include <imagehlp.h>
203#pragma pack( pop, before_imagehlp )
204#if API_VERSION_NUMBER < 7 // ImageHelp-Version is older.... so define it by mayself
205// The following definition is only available with VC++ 6.0 or higher, so include it here
206extern "C" {
207//
208// source file line data structure
209//
210/// \cond
211typedef struct _IMAGEHLP_LINE
212{
213 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE)
214 DWORD Key; // internal
215 DWORD LineNumber; // line number in file
216 PCHAR FileName; // full filename
217 DWORD Address; // first instruction of line
219/// \endcond
220#define SYMOPT_LOAD_LINES 0x00000010
221} // extern "C"
222#endif
223
224
225
226// Forward definitions of functions:
228static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile);
229
230static void AllocHashOut(FILE*);
232
233
234
235// Globale Vars:
237static FILE *g_fFile = NULL;
238
239// AllocCheckFileOpen
240// Checks if the log-file is already opened
241// if not, try to open file (append or create if not exists)
242// if open failed, redirect output to stdout
243static void AllocCheckFileOpen(bool bAppend = true) {
244 // is the File already open? If not open it...
245 if (g_fFile == NULL)
246 if (g_pszAllocLogName != NULL)
247 {
248 if (bAppend == false)
250 else
252 }
253 if (g_fFile == NULL)
254 g_fFile = stdout;
255}
256
257// Write Date/Time to specified file (will also work after 2038)
258static void WriteDateTime(FILE *fFile, BOOL asXMLAttrs = FALSE) {
259 TCHAR pszTemp[11], pszTemp2[11];
260
261 if (fFile != NULL) {
264 if (asXMLAttrs == FALSE)
265 _ftprintf(fFile, _T("%s %s"), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)
266 else
267 _ftprintf(fFile, _T("date=\"%s\" time=\"%s\" "), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)
268 }
269} // WriteDateTime
270
271
272/*******************************************************************************
273 * Hash-Tabelle
274 *******************************************************************************/
275// Memory for the EIP-Address (is used by the ShowStack-method)
276#define MAX_EIP_LEN_BUF 4
277
278#define ALLOC_ENTRY_NOT_FOUND 0xFFFFFFFF
279
280/// \cond
281typedef struct AllocHashEntryType {
282 long lRequestID; // RequestID from CRT (if 0, then this entry is empty)
283 size_t nDataSize; // Size of the allocated memory
284 char cRemovedFlag; // 0 => memory was not yet released
285 struct AllocHashEntryType *Next;
286 // Callstack for EIP
290 // Callstack for ESP
295/// \endcond
296
297
299static ULONG AllocHashEntries = 0;
300static ULONG AllocHashCollisions = 0;
301static ULONG AllocHashFreed = 0;
302static ULONG AllocHashMaxUsed = 0; // maximal number of concurrent entries
304
307
308// ##########################################################################################
309#ifdef WITH_IMALLOC_SPY
310// eigene Tabelle für die IMallocs:
311/// \cond
312typedef struct IMallocHashEntryType {
313 void *pData; // Key-Word
314 size_t nDataSize; // größe des Datenblocks (optional)
315 char cRemovedFlag; // 0 => nicht wurde noch nicht freigegeben
316 struct IMallocHashEntryType *Next;
317 // Callstack für EIP
321 // Callstack für ESP
326/// \endcond
327
328
330
331static ULONG IMallocHashEntries = 0;
333static ULONG IMallocHashFreed = 0;
334static ULONG IMallocHashMaxUsed = 0; // maximal number of concurrent entries
336
339
340
341//static void AllocHashOut(FILE*);
343
344// AllocHashFunction
345// Die eigentliche Hash-Funktion (hier ganz simpel)
346static ULONG IMallocHashFunction(void *pData) {
349
350 // relativ simpler Mechanismus für die Hash-Funktion,
351 // mir ist nur nix besseres eingefallen...
353
354 _ASSERTE( (ulTemp >= 0) && (ulTemp < ALLOC_HASH_ENTRIES) );
355
356 return ulTemp;
357} // AllocHashFunction
358
359// IMallocHashInsert
360// pData: Key-Word (Pointer to address)
361// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
362// nDataSize: How many bytes
363void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize) {
366
367 // ermittle Statistische Werte
372
373 // ermittle den Hash-Wert
375
376 // Eintrag darf nicht größer als die Hash-Tabelle sein
378
380 if (pHashEntry->pData == 0) {
381 // es ist noch kein Eintrag da
382 }
383 else {
384 //Statistische Daten:
389
390 // Eintrag ist schon belegt, verkette die Einträge
391 // wenn dies oft vorkommt, sollte man entweder die Tabelle vergrößern oder eine
392 // andere Hash-Funktion wählen
393 while(pHashEntry->Next != NULL) {
394 pHashEntry = pHashEntry->Next;
395 }
396
398 pHashEntry = pHashEntry->Next;
399
400 }
401 pHashEntry->pData = pData; // Key-Word
402 pHashEntry->nDataSize = nDataSize;
403 pHashEntry->Next = NULL;
404 // Get EIP and save it in the record
405 pHashEntry->dwEIPOffset = Context.Eip;
406 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
407 // Could not read memory... remove everything...
408 memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
409 pHashEntry->dwEIPLen = 0;
410 pHashEntry->dwEIPOffset = 0;
411 }
412
413 // Get ESP and save it in the record
414 pHashEntry->dwESPOffset = Context.Ebp;
415 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
416 // Could not read memory... remove everything...
417 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
418 pHashEntry->dwESPLen = 0;
419 pHashEntry->dwESPOffset = 0;
420
421 // Check if I tried to read too much...
422 if (GetLastError() == ERROR_PARTIAL_COPY)
423 {
424 // ask how many I can read:
427 if (dwRet > 0)
428 {
429 // calculate the length
430 DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
431 if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
432 {
433 // try to read it again (with the shorter length)
434 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
435 {
436 // ok, now everything goes wrong... remove it...
437 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
438 pHashEntry->dwESPLen = 0;
439 pHashEntry->dwESPOffset = 0;
440 }
441 else
442 {
443 pHashEntry->dwESPOffset = Context.Ebp;
444 }
445 }
446 } // VirtualQuery was successfully
447 } // ERROR_PARTIAL_COPY
448 }
449}
450
451// IMallocHashFind
452// Wird ALLOC_ENTRY_NOT_FOUND zurückgegeben, so wurde der Key nicht
453// gefunden, ansonsten wird ein Zeiger auf den Hash-Eintrag zurückgegeben
454// ACHTUNG: In einem preemptiven Tasking-System kann hier nicht
455// garantiert werden, ob der Zeiger noch gültig ist, wenn er
456// zurückgegeben wird, da er von einem anderen Thread schon
457// freigegeben sein könnte.
458// Die synchronisation muß eine Ebene höher erfolgen
462
463 // ermittle den Hash-Wert
465
466 // Eintrag darf nicht größer als die Hash-Tabelle sein
468
470 while(pHashEntry != NULL) {
471 if (pHashEntry->pData == pData) {
472 return pHashEntry;
473 }
474 pHashEntry = pHashEntry->Next;
475 }
476
477 // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
479} // AllocHashFind
480
481// IMallocHashRemove
482// Return: FALSE (0) : Key wurde gefunden und entfernt/markiert
483// TRUE (!=0): Key wurde nicht gefunden!
487
488 // ermittle den Hash-Wert
490
491 // Eintrag darf nicht größer als die Hash-Tabelle sein
493
496 while(pHashEntry != NULL) {
497 if (pHashEntry->pData == pData) {
498#ifdef HASH_ENTRY_REMOVE_AT_FREE
501 // gebe den Speicher frei
502 if (pHashEntryLast == NULL) {
503 // Es ist ein Eintrag direkt in der Tabelle
504 if (pHashEntry->Next == NULL) {
505 // Es ist der letze Eintrag lösche also die Tabelle
507 }
508 else {
509 // Es sind noch Einträge verkettet, überschreibe einfach den nicht mehr gebrauchten...
510 *pHashEntry = *(pHashEntry->Next);
511 }
512 return TRUE;
513 }
514 else {
515 // ich bin in einem dynamischen Bereich
516 // dies war eine kollisions, zähle also wieder zurück:
518 pHashEntryLast->Next = pHashEntry->Next;
520 return TRUE;
521 }
522#else
523 // erhöhe nur den Removed counter und behalte das Object im Speicher
524 pHashEntry->cRemovedFlag++;
525 return TRUE; // erfolgreich
526#endif
527 }
529 pHashEntry = pHashEntry->Next;
530 }
531
532 // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
533 return FALSE;
534}
535
536
537
538// Callback-Funtion for StackWalk für meine CallStack-Ausgabe aus der Hash-Tabelle
539#if API_VERSION_NUMBER >= 9
541#else
543#endif
544 // Versuche die hRequestID zu finden
547
550 // nicht gefunden, somit kann ich den Speicher nicht lesen
552 return FALSE;
553 }
554 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
555 // Speicher liegt im ESP:
556 // Errechne den Offset
557 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
559 memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
561 if (dwSize != nSize)
562 return FALSE;
563 }
564
565 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
566 // Speicher liegt im EIP:
567 // Errechne den Offset
568 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
570 memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
572 if (dwSize != nSize)
573 return FALSE;
574 }
575
576 if (*lpNumberOfBytesRead == 0) // Der Speicher konnte nicht gefunden werden
577 return FALSE;
578
579 return TRUE;
580}
581// AllocHashOutLeaks
582// Gibt allen Speicher aus, der noch nicht wieder freigegeben wurde
583// Returns the number of bytes, that are not freed (leaks)
587 ULONG ulCount = 0;
588 ULONG ulLeaksByte = 0;
589
590 // Gehe jeden Eintrag durch und gebe ihn aus
591 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
593 if (pHashEntry->pData != 0) {
594 while(pHashEntry != NULL) {
595 // gebe die Zeile aus
596 if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
597 ulCount++;
599 _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);
600 else
601 _ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
602 CONTEXT c;
603 memset( &c, '\0', sizeof c );
604 c.Eip = pHashEntry->dwEIPOffset;
605 c.Ebp = pHashEntry->dwESPOffset;
607 // Zähle zusammen wieviel Byte noch nicht freigegeben wurden
608 if (pHashEntry->nDataSize > 0)
609 ulLeaksByte += pHashEntry->nDataSize;
610 else
611 ulLeaksByte++; // Wenn zwar Speicher allokiert wurde, dieser aber 0 Bytes lang war, so reserviere für diesen zumindest 1 Byte
612
614 _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
615 }
616 pHashEntry = pHashEntry->Next;
617 }
618 }
619 }
621 _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
622 return ulLeaksByte;
623} // AllocHashOutLeaks
624#endif
625
626
627static void AllocHashInit(void) {
628
632 AllocHashFreed = 0;
635
638
639#ifdef WITH_IMALLOC_SPY
646
649#endif
650 return;
651} // AllocHashInit
652
653
654// AllocHashDeinit
655// Returns the number of bytes, that are not freed (leaks)
656static ULONG AllocHashDeinit(void) {
657 ULONG ulRet = 0;
659 AllocCheckFileOpen(bAppend); // open global log-file
660
662 {
663 _ftprintf(g_fFile, _T("<MEMREPORT "));
665 _ftprintf(g_fFile, _T(">\n"));
666 }
667 else
668 {
669 _ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));
671 _ftprintf(g_fFile, _T("\n"));
672 }
673
674#ifndef HASH_ENTRY_REMOVE_AT_FREE
675 // output the used memory
677 _ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));
679#endif
680
681 // output the Memoty leaks
683 _ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));
685
687 {
688 // output some statistics from the hash-table
689 _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
690 _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
691 _ftprintf(g_fFile, _T(" Inserts: %i\n"), AllocHashEntries);
692 _ftprintf(g_fFile, _T(" Freed: %i\n"), AllocHashFreed);
693 _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), AllocHashCollisions);
694 _ftprintf(g_fFile, _T("\n"));
695 _ftprintf(g_fFile, _T(" Max used: %i\n"), AllocHashMaxUsed);
696 _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), AllocHashMaxCollisions);
697 }
698
699 // Free Hash-Table
702
703 // Now, free my own memory
704 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
706 while(pHashEntry != NULL) {
708 pHashEntry = pHashEntry->Next;
710 // now free the dynamically allocated memory
712 }
713 } // while
714 } // for
715 // empty the hash-table
717
718#ifdef WITH_IMALLOC_SPY
719 // output the Memoty leaks
721 _ftprintf(g_fFile, _T("\n##### COM-Leaks: ###############################################\n"));
723
725 {
726 // output some statistics from the hash-table
727 _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
728 _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
729 _ftprintf(g_fFile, _T(" Inserts: %i\n"), IMallocHashEntries);
730 _ftprintf(g_fFile, _T(" Freed: %i\n"), IMallocHashFreed);
731 _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), IMallocHashCollisions);
732 _ftprintf(g_fFile, _T("\n"));
733 _ftprintf(g_fFile, _T(" Max used: %i\n"), IMallocHashMaxUsed);
734 _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), IMallocHashMaxCollisions);
735 }
736
737 // Free Hash-Table
738 //ULONG ulTemp;
740
741 // Gehe jeden Eintrag durch und gebe ihn frei
742 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
744 while(pHashEntry != NULL) {
748 // es ist dynamischer Speicher, gebe ihn also frei:
750 }
751 } // while
752 } // for
753 // Lösche die gesamte Hash-Tabelle
755#endif
756
757
759 _ftprintf(g_fFile, _T("</MEMREPORT>\n"));
760
761 return ulRet;
762} // AllocHashDeinit
763
764// AllocHashFunction
765// The has-function (very simple)
766static inline ULONG AllocHashFunction(long lRequestID) {
767 // I couldn´t find any better and faster
769} // AllocHashFunction
770
771// AllocHashInsert
772// lRequestID: Key-Word (RequestID from AllocHook)
773// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
774// nDataSize: How many bytes
775static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {
778
779 // change statistical data
784
785 // generate hash-value
787
789 if (pHashEntry->lRequestID == 0) {
790 // Entry is empty...
791 }
792 else {
793 // Entry is not empy! make a list of entries for this hash value...
794 // change statistical data
795 // if this happens often, you should increase the hah size or change the heash-function;
796 // to fasten the allocation time
801
802 while(pHashEntry->Next != NULL) {
803 pHashEntry = pHashEntry->Next;
804 }
805
807 pHashEntry = pHashEntry->Next;
808
809 }
810 pHashEntry->lRequestID = lRequestID; // Key-Word
811 pHashEntry->nDataSize = nDataSize;
812 pHashEntry->Next = NULL;
813 // Get EIP and save it in the record
814 pHashEntry->dwEIPOffset = Context.Eip;
815 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
816 // Could not read memory... remove everything...
817 memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
818 pHashEntry->dwEIPLen = 0;
819 pHashEntry->dwEIPOffset = 0;
820 }
821
822 // Get ESP and save it in the record
823 pHashEntry->dwESPOffset = Context.Ebp;
824 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
825 // Could not read memory... remove everything...
826 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
827 pHashEntry->dwESPLen = 0;
828 pHashEntry->dwESPOffset = 0;
829
830 // Check if I tried to read too much...
831 if (GetLastError() == ERROR_PARTIAL_COPY)
832 {
833 // ask how many I can read:
836 if (dwRet > 0)
837 {
838 // calculate the length
839 DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
840 if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
841 {
842 // try to read it again (with the shorter length)
843 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
844 {
845 // ok, now everything goes wrong... remove it...
846 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
847 pHashEntry->dwESPLen = 0;
848 pHashEntry->dwESPOffset = 0;
849 }
850 else
851 {
852 pHashEntry->dwESPOffset = Context.Ebp;
853 }
854 }
855 } // VirtualQuery was successfully
856 } // ERROR_PARTIAL_COPY
857 }
858}
859
860// AllocHashFind
861// If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!
862// If the Key was found, a pointer to the entry is returned
866
867 // get the Hash-Value
869
870 // Just do some simple checks:
872
874 while(pHashEntry != NULL) {
875 if (pHashEntry->lRequestID == lRequestID) {
876 return pHashEntry;
877 }
878 pHashEntry = pHashEntry->Next;
879 }
880
881 // entry was not found!
883} // AllocHashFind
884
885// AllocHashRemove
886// Return: FALSE (0) : Key was found and removed/marked
887// TRUE (!=0): Key was not found
888static BOOL AllocHashRemove(long lRequestID) {
891
892 // get the Hash-Value
894
895 // Just do some simple checks:
897
900 while(pHashEntry != NULL) {
901 if (pHashEntry->lRequestID == lRequestID) {
902#ifdef HASH_ENTRY_REMOVE_AT_FREE
905 // release my memory
906 if (pHashEntryLast == NULL) {
907 // It is an entry in the table, so do not release this memory
908 if (pHashEntry->Next == NULL) {
909 // It was the last entry, so empty the table entry
911 }
912 else {
913 // There are some more entries, so shorten the list
914 *pHashEntry = *(pHashEntry->Next);
915 // TODO: Do I need to free the memory here !? I think I should do this...
916 }
917 return TRUE;
918 }
919 else {
920 // now, I am in an dynamic allocated entry
921 // it was a collision, so decrease the current collision count
923 pHashEntryLast->Next = pHashEntry->Next;
925 return TRUE;
926 }
927#else
928 // increase the Remove-Count and let the objet stay in memory
929 pHashEntry->cRemovedFlag++;
930 return TRUE;
931#endif
932 }
934 pHashEntry = pHashEntry->Next;
935 }
936
937 // if we are here, we could not find the RequestID
938 return FALSE;
939}
940
941// ReadProcMemoryFromHash
942// Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries
943#if API_VERSION_NUMBER >= 9
945#else
947#endif
948 // Try to find the RequestID
951
954 // Not found, so I cannot return any memory
956 return FALSE;
957 }
958 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
959 // Memory is located in ESP:
960 // Calculate the offset
961 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
963 memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
965 if (dwSize != nSize)
966 return FALSE;
967 }
968
969 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
970 // Memory is located in EIP:
971 // Calculate the offset
972 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
974 memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
976 if (dwSize != nSize)
977 return FALSE;
978 }
979
980 if (*lpNumberOfBytesRead == 0) // Memory could not be found
981 return FALSE;
982
983 return TRUE;
984}
985
986// AllocHashOutLeaks
987// Write all Memory (with callstack) which was not freed yet
988// Returns the number of bytes, that are not freed (leaks)
992 ULONG ulCount = 0;
993 ULONG ulLeaksByte = 0;
994
995 // Move throu every entry
996 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
998 if (pHashEntry->lRequestID != 0) {
999 while(pHashEntry != NULL) {
1000 if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
1001 ulCount++;
1003 _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
1004 else
1005 _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
1006 CONTEXT c;
1007 memset( &c, '\0', sizeof c );
1008 c.Eip = pHashEntry->dwEIPOffset;
1009 c.Ebp = pHashEntry->dwESPOffset;
1011 // Count the number of leaky bytes
1012 if (pHashEntry->nDataSize > 0)
1013 ulLeaksByte += pHashEntry->nDataSize;
1014 else
1015 ulLeaksByte++; // If memory was allocated with zero bytes, then just increase the counter 1
1016
1018 _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
1019 }
1020 pHashEntry = pHashEntry->Next;
1021 }
1022 }
1023 }
1025 _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
1026 return ulLeaksByte;
1027} // AllocHashOutLeaks
1028
1029// Write all used memory to a file
1030void AllocHashOut(FILE *fFile) {
1031 ULONG ulTemp;
1033
1034 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
1036 if (pHashEntry->lRequestID != 0) {
1037 while(pHashEntry != NULL) {
1039 _ftprintf(fFile, _T("<MEMUSED requestID=\"%u\" size=\"%u\"\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
1040 else
1041 _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
1042 pHashEntry = pHashEntry->Next;
1043 }
1044 }
1045 }
1046} // AllocHashOut
1047
1048/*******************************************************************************
1049 * Ende der Hash-Tabelle
1050 *******************************************************************************/
1051
1052
1053// The follwoing is copied from dbgint.h:
1054// <CRT_INTERNALS>
1055/*
1056 * For diagnostic purpose, blocks are allocated with extra information and
1057 * stored in a doubly-linked list. This makes all blocks registered with
1058 * how big they are, when they were allocated, and what they are used for.
1059 */
1060
1061#define nNoMansLandSize 4
1062
1063/// \cond
1064typedef struct _CrtMemBlockHeader
1065{
1068 char * szFileName;
1069 int nLine;
1070#ifdef _WIN64
1071 /* These items are reversed on Win64 to eliminate gaps in the struct
1072 * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
1073 * maintained in the debug heap.
1074 */
1075 int nBlockUse;
1076 size_t nDataSize;
1077#else /* _WIN64 */
1078 size_t nDataSize;
1079 int nBlockUse;
1080#endif /* _WIN64 */
1081 long lRequest;
1082 unsigned char gap[nNoMansLandSize];
1083 /* followed by:
1084 * unsigned char data[nDataSize];
1085 * unsigned char anotherGap[nNoMansLandSize];
1086 */
1088/// \endcond
1089
1090#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
1091#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
1092
1093// </CRT_INTERNALS>
1094
1095
1096
1097
1098// Global data:
1099static BOOL g_bInitialized = FALSE;
1101
1102static DWORD g_dwShowCount = 0; // increase at every ShowStack-Call
1104
1105// Is used for syncronising call to MyAllocHook (to prevent reentrant calls)
1106static LONG g_lMallocCalled = 0;
1107
1109
1110// Deaktivate AllocHook, by increasing the Syncronisation-Counter
1111static void DeactivateMallocStackwalker(void) {
1113}
1114
1115
1116// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
1117// Special case for VC 5
1118#if _MSC_VER <= 1100
1119static int MyAllocHook(int nAllocType, void *pvData,
1120 size_t nSize, int nBlockUse, long lRequest,
1121 const char * szFileName, int nLine ) {
1122#else
1123static int MyAllocHook(int nAllocType, void *pvData,
1124 size_t nSize, int nBlockUse, long lRequest,
1125 const unsigned char * szFileName, int nLine ) {
1126#endif
1127 static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
1128 static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };
1129
1130#ifdef IGNORE_CRT_ALLOC
1131 if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations
1132 return TRUE;
1133#endif
1134 extern int _crtDbgFlag;
1136 {
1137 // Someone has disabled that the runtime should log this allocation
1138 // so we do not log this allocation
1139 if (pfnOldCrtAllocHook != NULL)
1141 return TRUE;
1142 }
1143
1144 // Prevent from reentrat calls
1145 if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called
1147 // call the previous alloc hook
1148 if (pfnOldCrtAllocHook != NULL)
1150 return TRUE;
1151 }
1152
1153 if (g_ulShowStackAtAlloc > 0) {
1154 AllocCheckFileOpen(); // Open logfile
1155 }
1156
1158 _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
1159
1160 if (nAllocType == _HOOK_FREE) { // freeing
1161 // Try to get the header information
1162 if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
1163 // get the ID
1165 // get a pointer to memory block header
1166 pHead = pHdr(pvData);
1167 nSize = pHead->nDataSize;
1168 lRequest = pHead->lRequest; // This is the ID!
1169
1170 if (pHead->nBlockUse == _IGNORE_BLOCK)
1171 {
1173 if (pfnOldCrtAllocHook != NULL)
1175 return TRUE;
1176 }
1177 }
1178 }
1179
1180 if (g_ulShowStackAtAlloc > 0) {
1181 _ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),
1183 if ( pvData != NULL )
1184 _ftprintf( g_fFile, _T(" at 0x%X"), pvData );
1185 _ftprintf(g_fFile, _T("\n"));
1186 }
1187
1188 if (nAllocType == _HOOK_FREE) { // freeing:
1189 if (lRequest != 0) { // RequestID was found
1190 BOOL bRet;
1191 // Try to find the RequestID in the Hash-Table, mark it that it was freed
1193 if(g_ulShowStackAtAlloc > 0) {
1194 if (bRet == FALSE) {
1195 // RequestID not found!
1196 _ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);
1197 }
1198 } // g_ulShowStackAtAlloc > 0
1199 }
1200 else {
1201 if(g_ulShowStackAtAlloc > 0) {
1202 // No valid RequestID found, display error
1203 _ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);
1204
1205 }
1206 }
1207 } // freeing
1208
1209 if (nAllocType == _HOOK_REALLOC) { // re-allocating
1210 // Try to get the header information
1211 if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
1212 BOOL bRet;
1214 // get the ID
1216 // get a pointer to memory block header
1217 pHead = pHdr(pvData);
1218 // Try to find the RequestID in the Hash-Table, mark it that it was freed
1219 lReallocRequest = pHead->lRequest;
1221 if (g_ulShowStackAtAlloc > 0) {
1222 if (bRet == FALSE) {
1223 // RequestID not found!
1224 _ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);
1225 }
1226 else {
1227 _ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);
1228 }
1229 } // g_ulShowStackAtAlloc > 0
1230 } // ValidHeapPointer
1231 } // re-allocating
1232
1233 if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
1235 // call the previous alloc hook
1236 if (pfnOldCrtAllocHook != NULL)
1238 return TRUE;
1239 }
1240
1241 // Get the context DIESES of this Thread
1244 GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) == 0) {
1245 // Something was wrong...
1246 _ftprintf(g_fFile, _T("###### Could not call 'DuplicateHandle' successfully\n"));
1248 // call the previous alloc hook
1249 if (pfnOldCrtAllocHook != NULL)
1251 return TRUE;
1252 }
1253
1254 CONTEXT c;
1255 memset( &c, '\0', sizeof c );
1256 c.ContextFlags = CONTEXT_FULL;
1257
1258 // init CONTEXT record so we know where to start the stackwalk
1259 if ( GetThreadContext( hThread, &c ) == 0) {
1260 if(g_ulShowStackAtAlloc > 1) {
1261 _ftprintf(g_fFile, _T("###### Could not call 'GetThreadContext' successfully\n"));
1262 }
1264 // call the previous alloc hook
1265 if (pfnOldCrtAllocHook != NULL)
1268 return TRUE; // could not get context
1269 }
1270
1271 if(g_ulShowStackAtAlloc > 1) {
1272 if(g_ulShowStackAtAlloc > 2) {
1273 // output the callstack
1275 }
1276 else {
1277 // Output only (re)allocs
1278 if (nAllocType != _HOOK_FREE) {
1280 }
1281 }
1282 } // g_ulShowStackAtAlloc > 1
1284
1285 // Only isert in the Hash-Table if it is not a "freeing"
1286 if (nAllocType != _HOOK_FREE) {
1287 if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
1289 }
1290
1292 // call the previous alloc hook
1293 if (pfnOldCrtAllocHook != NULL)
1295 return TRUE; // allow the memory operation to proceed
1296}
1297
1298
1299
1300
1301// ##########################################################################################
1302// ##########################################################################################
1303// ##########################################################################################
1304// ##########################################################################################
1305
1306#define gle (GetLastError())
1307#define lenof(a) (sizeof(a) / sizeof((a)[0]))
1308#define MAXNAMELEN 1024 // max name length for found symbols
1309#define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL )
1310#define TTBUFLEN 8096 // for a temp buffer (2^13)
1311
1312
1313
1314// SymCleanup()
1315typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
1316tSC pSC = NULL;
1317
1318// SymFunctionTableAccess()
1320tSFTA pSFTA = NULL;
1321
1322// SymGetLineFromAddr()
1326
1327// SymGetModuleBase()
1329tSGMB pSGMB = NULL;
1330
1331// SymGetModuleInfo()
1333tSGMI pSGMI = NULL;
1334
1335// SymGetOptions()
1336typedef DWORD (__stdcall *tSGO)( VOID );
1337tSGO pSGO = NULL;
1338
1339// SymGetSymFromAddr()
1343
1344// SymInitialize()
1346tSI pSI = NULL;
1347
1348// SymLoadModule()
1351tSLM pSLM = NULL;
1352
1353// SymSetOptions()
1354typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
1355tSSO pSSO = NULL;
1356
1357// StackWalk()
1364tSW pSW = NULL;
1365
1366// UnDecorateSymbolName()
1368 DWORD UndecoratedLength, DWORD Flags );
1369tUDSN pUDSN = NULL;
1370
1371
1372/// \cond
1373struct ModuleEntry
1374{
1375 std::string imageName;
1376 std::string moduleName;
1378 DWORD size;
1379};
1380typedef std::vector< ModuleEntry > ModuleList;
1381typedef ModuleList::iterator ModuleListIter;
1382/// \endcond
1383
1384// **************************************** ToolHelp32 ************************
1385#define MAX_MODULE_NAME32 255
1386#define TH32CS_SNAPMODULE 0x00000008
1387#pragma pack( push, 8 )
1388/// \cond
1389typedef struct tagMODULEENTRY32
1390{
1391 DWORD dwSize;
1392 DWORD th32ModuleID; // This module
1393 DWORD th32ProcessID; // owning process
1394 DWORD GlblcntUsage; // Global usage count on the module
1395 DWORD ProccntUsage; // Module usage count in th32ProcessID's context
1396 BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
1397 DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
1398 HMODULE hModule; // The hModule of this module in th32ProcessID's context
1399 char szModule[MAX_MODULE_NAME32 + 1];
1400 char szExePath[MAX_PATH];
1404/// \endcond
1405#pragma pack( pop )
1406
1407
1408
1410{
1411 // CreateToolhelp32Snapshot()
1413 // Module32First()
1415 // Module32Next()
1417
1418 // try both dlls...
1419 const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
1421 tCT32S pCT32S;
1422 tM32F pM32F;
1423 tM32N pM32N;
1424
1425 HANDLE hSnap;
1427 me.dwSize = sizeof(me);
1428 bool keepGoing;
1429 ModuleEntry e;
1430 int i;
1431
1432 for (i = 0; i<lenof(dllname); i++ )
1433 {
1435 if (hToolhelp == NULL)
1436 continue;
1437 pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
1438 pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
1439 pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
1440 if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )
1441 break; // found the functions!
1443 hToolhelp = NULL;
1444 }
1445
1446 if (hToolhelp == NULL)
1447 return false;
1448
1450 if (hSnap == (HANDLE) -1)
1451 return false;
1452
1453 keepGoing = !!pM32F( hSnap, &me );
1454 while (keepGoing)
1455 {
1456 e.imageName = me.szExePath;
1457 e.moduleName = me.szModule;
1458 e.baseAddress = (DWORD) me.modBaseAddr;
1459 e.size = me.modBaseSize;
1460 modules.push_back( e );
1461 keepGoing = !!pM32N( hSnap, &me );
1462 }
1463
1466
1467 return modules.size() != 0;
1468} // GetModuleListTH32
1469
1470
1471// **************************************** PSAPI ************************
1472/// \cond
1473typedef struct _MODULEINFO {
1478/// \endcond
1479
1481{
1482 // EnumProcessModules()
1484 // GetModuleFileNameEx()
1486 // GetModuleBaseName()
1488 // GetModuleInformation()
1490
1492 tEPM pEPM;
1493 tGMFNE pGMFNE;
1494 tGMBN pGMBN;
1495 tGMI pGMI;
1496
1497 DWORD i;
1498 ModuleEntry e;
1500 MODULEINFO mi;
1501 HMODULE *hMods = 0;
1502 char *tt = 0;
1503
1504 hPsapi = LoadLibrary( _T("psapi.dll") );
1505 if ( hPsapi == 0 )
1506 return false;
1507
1508 modules.clear();
1509
1510 pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
1511 pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
1512 pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
1513 pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
1514 if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )
1515 {
1516 // we couldn´t find all functions
1518 return false;
1519 }
1520
1521 hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
1522 tt = (char*) malloc(sizeof(char) * TTBUFLEN);
1523
1524 if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
1525 {
1526 _ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
1527 goto cleanup;
1528 }
1529
1530 if ( cbNeeded > TTBUFLEN )
1531 {
1532 _ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
1533 goto cleanup;
1534 }
1535
1536 for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
1537 {
1538 // base address, size
1539 pGMI(hProcess, hMods[i], &mi, sizeof mi );
1540 e.baseAddress = (DWORD) mi.lpBaseOfDll;
1541 e.size = mi.SizeOfImage;
1542 // image file name
1543 tt[0] = 0;
1545 e.imageName = tt;
1546 // module name
1547 tt[0] = 0;
1549 e.moduleName = tt;
1550
1551 modules.push_back(e);
1552 }
1553
1554cleanup:
1555 if (hPsapi)
1557 free(tt);
1558 free(hMods);
1559
1560 return modules.size() != 0;
1561} // GetModuleListPSAPI
1562
1563
1565{
1566 // first try toolhelp32
1568 return true;
1569 // then try psapi
1571} // GetModuleList
1572
1574{
1575 char img[MAX_PATH], mod[MAX_PATH];
1576 img[sizeof(img) - 1] = 0;
1577 mod[sizeof(mod) - 1] = 0;
1578
1581 for (ModuleListIter it = modules.begin(); it != modules.end(); ++it)
1582 {
1583 // SymLoadModule() wants writeable strings
1584 strncpy(img, it->imageName.c_str(), sizeof(img) - 1);
1585 strncpy(mod, it->moduleName.c_str(), sizeof(mod) - 1);
1586
1587 pSLM( hProcess, 0, img, mod, it->baseAddress, it->size );
1588 }
1589} // EnumAndLoadModuleSymbols
1590
1591static int InitStackWalk(void)
1592{
1593 if (g_bInitialized != FALSE)
1594 return 0; // already initialized
1595
1596 // old: we load imagehlp.dll dynamically because the NT4-version does not
1597 // old: offer all the functions that are in the NT5 lib
1598 // 02-12-19: Now we only support dbghelp.dll!
1599 // To use it on NT you have to install the redistrubutable for DBGHELP.DLL
1600 g_hImagehlpDll = LoadLibrary( _T("dbghelp.dll") );
1601 if ( g_hImagehlpDll == NULL )
1602 {
1603 printf( "LoadLibrary( \"dbghelp.dll\" ): GetLastError = %lu\n", gle );
1605 return 1;
1606 }
1607
1608 pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );
1609 pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess" );
1610 pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr" );
1611 pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase" );
1612 pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo" );
1613 pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );
1614 pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr" );
1615 pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );
1616 pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );
1617 pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk" );
1618 pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );
1619 pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule" );
1620
1621 if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
1622 pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
1623 pSW == NULL || pUDSN == NULL || pSLM == NULL )
1624 {
1625 printf( "GetProcAddress(): some required function not found.\n" );
1628 return 1;
1629 }
1630
1633 return 0;
1634}
1635
1636static TCHAR s_szExceptionLogFileName[_MAX_PATH] = _T("\\exceptions.log"); // default
1639{
1640 LONG lRet;
1642 TCHAR lString[500];
1644 _T("*** Unhandled Exception!\n")
1645 _T(" ExpCode: 0x%8.8X\n")
1646 _T(" ExpFlags: %d\n")
1647 _T(" ExpAddress: 0x%8.8X\n")
1648 _T(" Please report!"),
1649 pExPtrs->ExceptionRecord->ExceptionCode,
1650 pExPtrs->ExceptionRecord->ExceptionFlags,
1651 pExPtrs->ExceptionRecord->ExceptionAddress);
1653 return lRet;
1654}
1655
1657 switch(dwExceptionCode) {
1658 case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");
1659 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");
1660 case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");
1661 case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");
1662 case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");
1663 case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");
1664 case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");
1665 case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");
1666 case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");
1667 case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");
1668 case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");
1669 case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");
1670 case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");
1671 case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");
1672 case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");
1673 case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");
1674 case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");
1675 case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");
1676 case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");
1677 case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");
1678 case DBG_CONTROL_C : return _T("DBG CONTROL C ");
1679 default:
1680 return _T("<unkown exception>");
1681 }
1682} // GetExpectionCodeText
1683
1684// Function is not multi-threading safe, because of static char!
1686 static TCHAR szTemp[100];
1687
1688 switch(pExceptionRecord->ExceptionCode) {
1690 if (pExceptionRecord->NumberParameters == 2) {
1691 switch(pExceptionRecord->ExceptionInformation[0]) {
1692 case 0: // read attempt
1693 _stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
1694 return szTemp;
1695 case 1: // write attempt
1696 _stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
1697 return szTemp;
1698 default:
1699 return _T("");
1700 }
1701 } // if (pExceptionRecord->NumberParameters == 2)
1702 return _T("");
1703 default:
1704 return _T("");
1705 } // switch(pExceptionRecord->ExceptionCode)
1706} // GetAdditionalExpectionCodeText
1707
1708std::string SimpleXMLEncode(PCSTR szText)
1709{
1710 std::string szRet;
1711
1712 for (size_t i=0; i<strlen(szText); i++)
1713 {
1714 switch(szText[i])
1715 {
1716 case '&':
1717 szRet.append("&amp;");
1718 break;
1719 case '<':
1720 szRet.append("&lt;");
1721 break;
1722 case '>':
1723 szRet.append("&gt;");
1724 break;
1725 case '"':
1726 szRet.append("&quot;");
1727 break;
1728 case '\'':
1729 szRet.append("&apos;");
1730 break;
1731 default:
1732 szRet += szText[i];
1733 }
1734 }
1735 return szRet;
1736}
1737
1738
1740{
1741 FILE *fFile = stdout; // default to stdout
1742
1743 if (pszLogFile != NULL) { // a filename is available
1744 // Open the logfile
1745 fFile = _tfopen(pszLogFile, _T("a"));
1746 if (fFile != NULL) { // Is the file too big?
1747 long size;
1748 fseek(fFile, 0, SEEK_END);
1749 size = ftell(fFile); // Get the size of the file
1750 if (size >= LOG_FILE_MAX_SIZE) {
1752 // It is too big...
1753 fclose(fFile);
1755 _tcscat(pszTemp, _T(".old"));
1756 _tremove(pszTemp); // Remove an old file, if exists
1757 _trename(pszLogFile, pszTemp); // rename the actual file
1758 fFile = _tfopen(pszLogFile, _T("w")); // open new file
1759 free(pszTemp);
1760 }
1761 }
1762 } // if (pszLogFile != NULL)
1763 if (fFile == NULL) {
1764 fFile = stdout;
1765 }
1766
1768
1769 fclose(fFile);
1770}
1771
1772
1773static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {
1775}
1776
1778 // normally, call ImageNtHeader() and use machine info from PE header
1780 HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside
1781 int frameNum; // counts walked frames
1782 DWORD offsetFromSymbol; // tells us how far from the symbol we were
1783 DWORD offsetFromLine; // tells us how far from the line we were
1784 DWORD symOptions; // symbol handler settings
1785
1786 static char Sym[IMGSYMLEN + MAXNAMELEN];
1787 IMAGEHLP_SYMBOL* const pSym = reinterpret_cast<IMAGEHLP_SYMBOL*>(&Sym);
1788 char undName[MAXNAMELEN]; // undecorated name
1789 char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans
1791 IMAGEHLP_LINE Line;
1793
1794 std::string symSearchPath;
1795
1796 static int bFirstTime = TRUE;
1797
1798 // If no logfile is present, outpur to "stdout"
1799 if (fLogFile == NULL) {
1800 fLogFile = stdout;
1801 }
1802
1803 STACKFRAME s; // in/out stackframe
1804 memset( &s, '\0', sizeof s );
1805
1806 if ( (g_bInitialized == FALSE) && (bFirstTime == TRUE) ) {
1807 InitStackWalk();
1808 }
1809
1810 if (g_bInitialized == FALSE)
1811 {
1812 // Could not init!!!!
1813 bFirstTime = FALSE;
1814 _ftprintf(fLogFile, _T("%lu: Stackwalker not initialized (or was not able to initialize)!\n"), g_dwShowCount);
1815 return;
1816 }
1817
1818// Critical section begin...
1820
1821 InterlockedIncrement((long*) &g_dwShowCount); // erhöhe counter
1822
1823
1824 // NOTE: normally, the exe directory and the current directory should be taken
1825 // from the target process. The current dir would be gotten through injection
1826 // of a remote thread; the exe fir through either ToolHelp32 or PSAPI.
1827
1829 {
1830 _ftprintf(fLogFile, _T("%lu: "), g_dwShowCount);
1832 _ftprintf(fLogFile, _T("\n"));
1833 }
1834
1835
1836 if (bFirstTime) {
1837
1838 CHAR *tt, *p;
1839
1840 tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer
1841 if (!tt) goto cleanup; // not enough memory...
1842
1843 // build symbol search path from:
1844 symSearchPath = "";
1845 // current directory
1847 symSearchPath += tt + std::string( ";" );
1848 // dir with executable
1849 if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )
1850 {
1851 for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
1852 {
1853 // locate the rightmost path separator
1854 if ( *p == '\\' || *p == '/' || *p == ':' )
1855 break;
1856 }
1857 // if we found one, p is pointing at it; if not, tt only contains
1858 // an exe name (no path), and p points before its first byte
1859 if ( p != tt ) // path sep found?
1860 {
1861 if ( *p == ':' ) // we leave colons in place
1862 ++ p;
1863 *p = '\0'; // eliminate the exe name and last path sep
1864 symSearchPath += tt + std::string( ";" );
1865 }
1866 }
1867 // environment variable _NT_SYMBOL_PATH
1868 if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
1869 symSearchPath += tt + std::string( ";" );
1870 // environment variable _NT_ALTERNATE_SYMBOL_PATH
1871 if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
1872 symSearchPath += tt + std::string( ";" );
1873 // environment variable SYSTEMROOT
1874 if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )
1875 symSearchPath += tt + std::string( ";" );
1876
1877
1878
1879 if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
1880 symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );
1881
1882 // why oh why does SymInitialize() want a writeable string?
1883 strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
1884 tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator
1885
1886 // init symbol handler stuff (SymInitialize())
1887 if ( ! pSI( hProcess, tt, false ) )
1888 {
1890 _ftprintf(fLogFile, _T("%lu: SymInitialize(): GetLastError = %lu\n"), g_dwShowCount, gle );
1891 if (tt) free( tt );
1892 goto cleanup;
1893 }
1894
1895 // SymGetOptions()
1896 symOptions = pSGO();
1900 pSSO( symOptions ); // SymSetOptions()
1901
1902 // Enumerate modules and tell imagehlp.dll about them.
1903 // On NT, this is not necessary, but it won't hurt.
1905
1906 if (tt)
1907 free( tt );
1908 } // bFirstTime = TRUE
1909 bFirstTime = FALSE;
1910
1911 // init STACKFRAME for first call
1912 // Notes: AddrModeFlat is just an assumption. I hate VDM debugging.
1913 // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
1914 // and good riddance.
1915 s.AddrPC.Offset = c.Eip;
1916 s.AddrPC.Mode = AddrModeFlat;
1917 s.AddrFrame.Offset = c.Ebp;
1918 s.AddrFrame.Mode = AddrModeFlat;
1919
1920 memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
1921 pSym->SizeOfStruct = IMGSYMLEN;
1922 pSym->MaxNameLength = MAXNAMELEN;
1923
1924 memset( &Line, '\0', sizeof Line );
1925 Line.SizeOfStruct = sizeof Line;
1926
1927 memset( &Module, '\0', sizeof Module );
1928 Module.SizeOfStruct = sizeof Module;
1929
1930 for ( frameNum = 0; ; ++ frameNum )
1931 {
1932 // get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase())
1933 // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
1934 // assume that either you are done, or that the stack is so hosed that the next
1935 // deeper frame could not be found.
1936 // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
1938 break;
1939
1941
1943 _ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);
1944 if ( s.AddrPC.Offset == 0 )
1945 {
1946 // Special case: If we are here, we have no valid callstack entry!
1947 switch(g_CallstackOutputType)
1948 {
1949 case ACOutput_Simple:
1950 _ftprintf(fLogFile, _T("%lu: (-nosymbols- PC == 0)\n"), g_dwShowCount);
1951 break;
1952 case ACOutput_Advanced:
1953 _ftprintf(fLogFile, _T(" (-nosymbols- PC == 0)\n"));
1954 break;
1955 case ACOutput_XML:
1956 // TODO: ....
1957 _ftprintf(fLogFile, _T("<STACKENTRY decl=\"(-nosymbols- PC == 0)\"/>\n"));
1958 break;
1959 }
1960 }
1961 else
1962 {
1963 // we seem to have a valid PC
1964 undName[0] = 0;
1965 undFullName[0] = 0;
1966 offsetFromSymbol = 0;
1967 // show procedure info (SymGetSymFromAddr())
1968 if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
1969 {
1971 {
1972 if ( gle != 487 )
1973 _ftprintf(fLogFile, _T(" SymGetSymFromAddr(): GetLastError = %lu\n"), gle );
1974 else
1975 _ftprintf(fLogFile, _T("\n"));
1976 }
1977 }
1978 else
1979 {
1980 // UnDecorateSymbolName()
1984 {
1985 if (strlen(undName) > 0)
1986 fprintf(fLogFile, " %s %+ld bytes\n", undName, (long) offsetFromSymbol );
1987 else
1988 {
1989 fprintf(fLogFile, " Sig: %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );
1990 strcpy(undName, pSym->Name);
1991 }
1992 fprintf(fLogFile, "%lu: Decl: %s\n", g_dwShowCount, undFullName );
1993 }
1994 }
1995 //if (g_CallstackOutputType == ACOutput_XML)
1996 // fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
1997
1998 // show line number info, NT5.0-method (SymGetLineFromAddr())
1999 offsetFromLine = 0;
2000 if ( pSGLFA != NULL )
2001 { // yes, we have SymGetLineFromAddr()
2002 if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromLine, &Line ) )
2003 {
2004 if ( (gle != 487) && (frameNum > 0) ) // ignore error for first frame
2005 {
2007 {
2008 _ftprintf(fLogFile, _T("<STACKENTRY "));
2010 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
2011 _ftprintf(fLogFile, _T("srcfile=\"SymGetLineFromAddr(): GetLastError = %lu\" "), gle);
2012 }
2013 else
2014 _ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );
2015 }
2016 }
2017 else
2018 {
2019 switch(g_CallstackOutputType)
2020 {
2021 case ACOutput_Advanced:
2022 fprintf(fLogFile, "%lu: Line: %s(%lu) %+ld bytes\n", g_dwShowCount,
2023 Line.FileName, Line.LineNumber, offsetFromLine );
2024 break;
2025 case ACOutput_Simple:
2026 fprintf(fLogFile, "%lu: %s(%lu) %+ld bytes (%s)\n", g_dwShowCount,
2027 Line.FileName, Line.LineNumber, offsetFromLine, undName);
2028 break;
2029 case ACOutput_XML:
2030 _ftprintf(fLogFile, _T("<STACKENTRY "));
2032 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
2033 fprintf(fLogFile, "srcfile=\"%s\" line=\"%lu\" line_offset=\"%+ld\" ",
2034 SimpleXMLEncode(Line.FileName).c_str(), Line.LineNumber, offsetFromLine, undName);
2035 break;
2036 }
2037 }
2038 } // yes, we have SymGetLineFromAddr()
2039
2040 // show module info (SymGetModuleInfo())
2042 {
2043 if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )
2044 {
2046 _ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );
2047 }
2048 else
2049 { // got module info OK
2050 char ty[80];
2051 switch ( Module.SymType )
2052 {
2053 case SymNone:
2054 strcpy( ty, "-nosymbols-" );
2055 break;
2056 case SymCoff:
2057 strcpy( ty, "COFF" );
2058 break;
2059 case SymCv:
2060 strcpy( ty, "CV" );
2061 break;
2062 case SymPdb:
2063 strcpy( ty, "PDB" );
2064 break;
2065 case SymExport:
2066 strcpy( ty, "-exported-" );
2067 break;
2068 case SymDeferred:
2069 strcpy( ty, "-deferred-" );
2070 break;
2071 case SymSym:
2072 strcpy( ty, "SYM" );
2073 break;
2074 /* // TODO: #if API_VERSION_NUMBER >= 9 ?
2075 case SymDia:
2076 strcpy( ty, "DIA" );
2077 break;*/
2078 default:
2079 _snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
2080 break;
2081 }
2082
2084 {
2085 // now, check if the XML-Entry is written...
2086 if (bXMLTagWrote == FALSE)
2087 {
2088 _ftprintf(fLogFile, _T("<STACKENTRY "));
2090 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
2091 _ftprintf(fLogFile, _T("srcfile=\"\" "));
2093 }
2094 }
2095
2097 {
2098 fprintf(fLogFile, "%lu: Mod: %s, base: %08lxh\n", g_dwShowCount,
2099 Module.ModuleName, Module.BaseOfImage );
2100 if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!
2101 _ftprintf(fLogFile, _T("%lu: Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);
2102 fprintf(fLogFile, "%lu: Sym: type: %s, file: %s\n", g_dwShowCount,
2103 ty, Module.LoadedImageName );
2104 }
2105 }
2106 else
2107 {
2108 // XML:
2109 if (bXMLTagWrote == TRUE)
2110 fprintf(fLogFile, "module=\"%s\" base=\"%08lx\" ", Module.ModuleName, Module.BaseOfImage);
2111 }
2112 } // got module info OK
2113 }
2115 _ftprintf(fLogFile, _T("/>\n")); // terminate the XML node
2116
2117 } // we seem to have a valid PC
2118
2119 // no return address means no deeper stackframe
2120 if ( s.AddrReturn.Offset == 0 )
2121 {
2122 // avoid misunderstandings in the printf() following the loop
2123 SetLastError( 0 );
2124 break;
2125 }
2126
2127 } // for ( frameNum )
2128
2129 if ( (g_CallstackOutputType != ACOutput_XML) && (gle != 0) )
2130 _ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );
2131
2132cleanup:
2133 if (fLogFile) {
2134 _ftprintf(fLogFile, _T("\n\n"));
2135 if (g_dwShowCount % 1000)
2137 }
2138
2140// Critical section end...
2141} // ShowStackRM
2142
2143} // StackWalker namespace
2144
2145//-------------------------------------------------------------------------------------------------
2146
2147//
2148// This function if NOT multi-threading capable
2149// It should only be called from the main-Function!
2150//
2152 if (g_bInitialized) {
2153 return 2; // already initialized!
2154 }
2155 if (ulShowStackAtAlloc <= 3)
2157 else
2159
2160 if (pszFileName != NULL)
2162 else
2164
2166
2167#ifdef _DEBUG
2168 AllocHashInit();
2169
2170#ifdef WITH_IMALLOC_SPY
2171 HRESULT hr;
2172 // erzeuge mein malloc-Spy object
2173 LPMALLOCSPY pMallocSpy = new CMallocSpy(); // wird später durch Release freigegeben
2174 if (pMallocSpy != NULL)
2175 {
2176 // CoInitilize(); // ??? Ist dies notwendig ?
2178 if FAILED(hr)
2179 {
2180 _tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr);
2181 }
2182 }
2183#endif
2184
2185 // save the previous alloc hook
2187#endif
2188
2189 return InitStackWalk();
2190} // InitAllocCheckWN
2191
2193{
2195 if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
2196 {
2198 if (eOutput == ACOutput_XML)
2199 _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
2200 else
2201 _tcscat(s_szExceptionLogFileName, _T(".exp.log"));
2202
2203 if (eOutput == ACOutput_XML)
2204 _tcscat(szModName, _T(".mem.xml"));
2205 else
2206 _tcscat(szModName, _T(".mem.log"));
2207 }
2208 else
2209 {
2210 if (eOutput == ACOutput_XML)
2211 _tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default
2212 else
2213 _tcscpy(szModName, _T("\\mem-leaks.log")); // default
2214 }
2215
2217 {
2218 // set global exception handler (for handling all unhandled exceptions)
2221 }
2222
2224}
2225
2226//
2227// This function if NOT multi-threading capable
2228// It should only be called from the main-Function!
2229// Returns the number of bytes that are not freed (leaks)
2230//
2232 ULONG ulRet = 0;
2233 if (g_bInitialized) {
2234
2235#ifdef _DEBUG
2236 InterlockedIncrement(&g_lMallocCalled); // No deactivate MyAllocHook, because StackWalker will allocate some memory)
2237 ulRet = AllocHashDeinit(); // output the not freed memory
2238 // remove the hook and set the old one
2240
2241#ifdef WITH_IMALLOC_SPY
2243#endif
2244
2245#endif
2246
2247 EnterCriticalSection(&g_csFileOpenClose); // wait until a running stack dump was created
2249
2250 // de-init symbol handler etc. (SymCleanup())
2251 if (pSC != NULL)
2254
2256 if (g_pszAllocLogName != NULL) {
2259 }
2260 if (g_fFile != NULL) {
2261 fclose(g_fFile);
2262 g_fFile = NULL;
2263 }
2264
2267 }
2268
2270 {
2273 }
2274 return ulRet;
2275} // DeInitAllocCheck
2276
2277//
2278// #################################################################################
2279// #################################################################################
2280// Here the Stackwalk-Part begins.
2281// Some of the code is from an example from a book
2282// But I couldn´t find the reference anymore... sorry...
2283// If someone knowns, please let me know...
2284// #################################################################################
2285// #################################################################################
2286//
2287// if you use C++ exception handling: install a translator function
2288// with set_se_translator(). In the context of that function (but *not*
2289// afterwards), you can either do your stack dump, or save the CONTEXT
2290// record as a local copy. Note that you must do the stack sump at the
2291// earliest opportunity, to avoid the interesting stackframes being gone
2292// by the time you do the dump.
2293//
2294// status:
2295// - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht
2296// - EXCEPTION_CONTINUE_EXECUTION:
2297// - EXCEPTION_EXECUTE_HANDLER:
2298//
2300{
2302 FILE *fFile = stdout; // default to stdout
2303
2304 if (pszLogFile != NULL) { // a filename is provided
2305 // Open the logfile
2306 fFile = _tfopen(pszLogFile, _T("a"));
2307 if (fFile != NULL) { // Is the file too big?
2308 long size;
2309 fseek(fFile, 0, SEEK_END);
2310 size = ftell(fFile); // Get the size of the file
2311 if (size >= LOG_FILE_MAX_SIZE) {
2313 // It is too big...
2314 fclose(fFile);
2316 _tcscat(pszTemp, _T(".old"));
2317 _tremove(pszTemp); // Remove an old file, if exists
2318 _trename(pszLogFile, pszTemp); // rename the actual file
2319 fFile = _tfopen(pszLogFile, _T("w")); // create a new file
2320 free(pszTemp);
2321 }
2322 }
2323 } // if (pszLogFile != NULL)
2324 if (fFile == NULL) {
2325 fFile = stdout;
2326 }
2327
2328 // Write infos about the exception
2330 {
2331 _ftprintf(fFile, _T("<EXCEPTION code=\"%8.8X\" addr=\"%8.8X\" "),
2332 ep->ExceptionRecord->ExceptionCode,
2333 ep->ExceptionRecord->ExceptionAddress);
2335 _ftprintf(fFile, _T("code_desc=\"%s\" more_desc=\"%s\">\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
2336 GetAdditionalExpectionCodeText(ep->ExceptionRecord));
2337 }
2338 else
2339 {
2340 _ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"),
2341 ep->ExceptionRecord->ExceptionCode,
2342 ep->ExceptionRecord->ExceptionAddress);
2343 _ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
2344 GetAdditionalExpectionCodeText(ep->ExceptionRecord));
2345 }
2346
2349 ShowStack( hThread, *(ep->ContextRecord), fFile);
2351
2353 _ftprintf(fFile, _T("</EXCEPTION>\n"));
2354
2355 fclose(fFile);
2356
2357 return status;
2358} // StackwalkFilter
#define gle
#define IMGSYMLEN
#define LOG_FILE_MAX_SIZE
#define nNoMansLandSize
#define MAX_EIP_LEN_BUF
int InitAllocCheckWN(eAllocCheckOutput eOutput, LPCTSTR pszFileName, ULONG ulShowStackAtAlloc)
#define ALLOC_HASH_ENTRIES
#define MAXNAMELEN
#define TH32CS_SNAPMODULE
#define lenof(a)
ULONG DeInitAllocCheck(void)
#define SYMOPT_LOAD_LINES
#define pHdr(pbData)
DWORD StackwalkFilter(EXCEPTION_POINTERS *ep, DWORD status, LPCTSTR pszLogFile)
#define ALLOC_ENTRY_NOT_FOUND
#define TTBUFLEN
int InitAllocCheck(eAllocCheckOutput eOutput, BOOL bSetUnhandledExeptionFilter, ULONG ulShowStackAtAlloc)
#define MAX_ESP_LEN_BUF
#define MAX_MODULE_NAME32
eAllocCheckOutput
Definition Stackwalker.h:40
@ ACOutput_XML
Definition Stackwalker.h:43
@ ACOutput_Advanced
Definition Stackwalker.h:42
@ ACOutput_Simple
Definition Stackwalker.h:41
char TCHAR
Definition cygwin.h:42
#define _tcscat
Definition cygwin.h:83
#define _tcscpy
Definition cygwin.h:79
#define _stprintf
Definition cygwin.h:88
#define _tfopen
Definition cygwin.h:69
#define _MAX_PATH
Definition cygwin.h:97
#define MAX_PATH
Definition cygwin.h:98
#define _T(x)
Definition cygwin.h:51
TModule * Module
Definition global.cpp:34
#define EXCEPTION_EXECUTE_HANDLER
Definition unixxcpt.cpp:58