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/// \cond
311typedef struct IMallocHashEntryType {
312 void *pData; // Key-Word
313 size_t nDataSize;
314 char cRemovedFlag;
315 struct IMallocHashEntryType *Next;
316 // Callstack for EIP
320 // Callstack for ESP
325/// \endcond
326
327
329
330static ULONG IMallocHashEntries = 0;
332static ULONG IMallocHashFreed = 0;
333static ULONG IMallocHashMaxUsed = 0; // maximal number of concurrent entries
335
338
339
340//static void AllocHashOut(FILE*);
342
343// AllocHashFunction
344static ULONG IMallocHashFunction(void *pData) {
347
349
350 _ASSERTE( (ulTemp >= 0) && (ulTemp < ALLOC_HASH_ENTRIES) );
351
352 return ulTemp;
353} // AllocHashFunction
354
355// IMallocHashInsert
356// pData: Key-Word (Pointer to address)
357// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
358// nDataSize: How many bytes
359void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize) {
362
367
370
372 if (pHashEntry->pData == 0) {
373 }
374 else {
379
380 while(pHashEntry->Next != NULL) {
381 pHashEntry = pHashEntry->Next;
382 }
383
385 pHashEntry = pHashEntry->Next;
386
387 }
388 pHashEntry->pData = pData; // Key-Word
389 pHashEntry->nDataSize = nDataSize;
390 pHashEntry->Next = NULL;
391 // Get EIP and save it in the record
392 pHashEntry->dwEIPOffset = Context.Eip;
393 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
394 // Could not read memory... remove everything...
395 memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
396 pHashEntry->dwEIPLen = 0;
397 pHashEntry->dwEIPOffset = 0;
398 }
399
400 // Get ESP and save it in the record
401 pHashEntry->dwESPOffset = Context.Ebp;
402 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
403 // Could not read memory... remove everything...
404 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
405 pHashEntry->dwESPLen = 0;
406 pHashEntry->dwESPOffset = 0;
407
408 // Check if I tried to read too much...
409 if (GetLastError() == ERROR_PARTIAL_COPY)
410 {
411 // ask how many I can read:
414 if (dwRet > 0)
415 {
416 // calculate the length
417 DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
418 if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
419 {
420 // try to read it again (with the shorter length)
421 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
422 {
423 // ok, now everything goes wrong... remove it...
424 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
425 pHashEntry->dwESPLen = 0;
426 pHashEntry->dwESPOffset = 0;
427 }
428 else
429 {
430 pHashEntry->dwESPOffset = Context.Ebp;
431 }
432 }
433 } // VirtualQuery was successfully
434 } // ERROR_PARTIAL_COPY
435 }
436}
437
438// IMallocHashFind
444
446 while(pHashEntry != NULL) {
447 if (pHashEntry->pData == pData) {
448 return pHashEntry;
449 }
450 pHashEntry = pHashEntry->Next;
451 }
452
454} // AllocHashFind
455
456// IMallocHashRemove
462
465 while(pHashEntry != NULL) {
466 if (pHashEntry->pData == pData) {
467#ifdef HASH_ENTRY_REMOVE_AT_FREE
470 if (pHashEntryLast == NULL) {
471 if (pHashEntry->Next == NULL) {
473 }
474 else {
475 *pHashEntry = *(pHashEntry->Next);
476 }
477 return TRUE;
478 }
479 else {
481 pHashEntryLast->Next = pHashEntry->Next;
483 return TRUE;
484 }
485#else
486 pHashEntry->cRemovedFlag++;
487 return TRUE;
488#endif
489 }
491 pHashEntry = pHashEntry->Next;
492 }
493
494 return FALSE;
495}
496
497
498
499#if API_VERSION_NUMBER >= 9
501#else
503#endif
504
507
511 return FALSE;
512 }
513 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
514 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
516 memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
518 if (dwSize != nSize)
519 return FALSE;
520 }
521
522 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
523 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
525 memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
527 if (dwSize != nSize)
528 return FALSE;
529 }
530
531 if (*lpNumberOfBytesRead == 0) // Der Speicher konnte nicht gefunden werden
532 return FALSE;
533
534 return TRUE;
535}
536// AllocHashOutLeaks
537// Returns the number of bytes, that are not freed (leaks)
541 ULONG ulCount = 0;
542 ULONG ulLeaksByte = 0;
543
544 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
546 if (pHashEntry->pData != 0) {
547 while(pHashEntry != NULL) {
548 if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
549 ulCount++;
551 _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);
552 else
553 _ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
554 CONTEXT c;
555 memset( &c, '\0', sizeof c );
556 c.Eip = pHashEntry->dwEIPOffset;
557 c.Ebp = pHashEntry->dwESPOffset;
559 if (pHashEntry->nDataSize > 0)
560 ulLeaksByte += pHashEntry->nDataSize;
561 else
562 ulLeaksByte++;
563
565 _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
566 }
567 pHashEntry = pHashEntry->Next;
568 }
569 }
570 }
572 _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
573 return ulLeaksByte;
574} // AllocHashOutLeaks
575#endif
576
577
578static void AllocHashInit(void) {
579
583 AllocHashFreed = 0;
586
589
590#ifdef WITH_IMALLOC_SPY
597
600#endif
601 return;
602} // AllocHashInit
603
604
605// AllocHashDeinit
606// Returns the number of bytes, that are not freed (leaks)
607static ULONG AllocHashDeinit(void) {
608 ULONG ulRet = 0;
610 AllocCheckFileOpen(bAppend); // open global log-file
611
613 {
614 _ftprintf(g_fFile, _T("<MEMREPORT "));
616 _ftprintf(g_fFile, _T(">\n"));
617 }
618 else
619 {
620 _ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));
622 _ftprintf(g_fFile, _T("\n"));
623 }
624
625#ifndef HASH_ENTRY_REMOVE_AT_FREE
626 // output the used memory
628 _ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));
630#endif
631
632 // output the Memoty leaks
634 _ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));
636
638 {
639 // output some statistics from the hash-table
640 _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
641 _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
642 _ftprintf(g_fFile, _T(" Inserts: %i\n"), AllocHashEntries);
643 _ftprintf(g_fFile, _T(" Freed: %i\n"), AllocHashFreed);
644 _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), AllocHashCollisions);
645 _ftprintf(g_fFile, _T("\n"));
646 _ftprintf(g_fFile, _T(" Max used: %i\n"), AllocHashMaxUsed);
647 _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), AllocHashMaxCollisions);
648 }
649
650 // Free Hash-Table
653
654 // Now, free my own memory
655 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
657 while(pHashEntry != NULL) {
659 pHashEntry = pHashEntry->Next;
661 // now free the dynamically allocated memory
663 }
664 } // while
665 } // for
666 // empty the hash-table
668
669#ifdef WITH_IMALLOC_SPY
670 // output the Memoty leaks
672 _ftprintf(g_fFile, _T("\n##### COM-Leaks: ###############################################\n"));
674
676 {
677 // output some statistics from the hash-table
678 _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
679 _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
680 _ftprintf(g_fFile, _T(" Inserts: %i\n"), IMallocHashEntries);
681 _ftprintf(g_fFile, _T(" Freed: %i\n"), IMallocHashFreed);
682 _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), IMallocHashCollisions);
683 _ftprintf(g_fFile, _T("\n"));
684 _ftprintf(g_fFile, _T(" Max used: %i\n"), IMallocHashMaxUsed);
685 _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), IMallocHashMaxCollisions);
686 }
687
688 // Free Hash-Table
689 //ULONG ulTemp;
691
692 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
694 while(pHashEntry != NULL) {
699 }
700 } // while
701 } // for
703#endif
704
705
707 _ftprintf(g_fFile, _T("</MEMREPORT>\n"));
708
709 return ulRet;
710} // AllocHashDeinit
711
712// AllocHashFunction
713// The has-function (very simple)
714static inline ULONG AllocHashFunction(long lRequestID) {
715 // I couldn't find any better and faster
717} // AllocHashFunction
718
719// AllocHashInsert
720// lRequestID: Key-Word (RequestID from AllocHook)
721// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
722// nDataSize: How many bytes
723static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {
726
727 // change statistical data
732
733 // generate hash-value
735
737 if (pHashEntry->lRequestID == 0) {
738 // Entry is empty...
739 }
740 else {
741 // Entry is not empy! make a list of entries for this hash value...
742 // change statistical data
743 // if this happens often, you should increase the hah size or change the heash-function;
744 // to fasten the allocation time
749
750 while(pHashEntry->Next != NULL) {
751 pHashEntry = pHashEntry->Next;
752 }
753
755 pHashEntry = pHashEntry->Next;
756
757 }
758 pHashEntry->lRequestID = lRequestID; // Key-Word
759 pHashEntry->nDataSize = nDataSize;
760 pHashEntry->Next = NULL;
761 // Get EIP and save it in the record
762 pHashEntry->dwEIPOffset = Context.Eip;
763 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
764 // Could not read memory... remove everything...
765 memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
766 pHashEntry->dwEIPLen = 0;
767 pHashEntry->dwEIPOffset = 0;
768 }
769
770 // Get ESP and save it in the record
771 pHashEntry->dwESPOffset = Context.Ebp;
772 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
773 // Could not read memory... remove everything...
774 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
775 pHashEntry->dwESPLen = 0;
776 pHashEntry->dwESPOffset = 0;
777
778 // Check if I tried to read too much...
779 if (GetLastError() == ERROR_PARTIAL_COPY)
780 {
781 // ask how many I can read:
784 if (dwRet > 0)
785 {
786 // calculate the length
787 DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
788 if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
789 {
790 // try to read it again (with the shorter length)
791 if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
792 {
793 // ok, now everything goes wrong... remove it...
794 memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
795 pHashEntry->dwESPLen = 0;
796 pHashEntry->dwESPOffset = 0;
797 }
798 else
799 {
800 pHashEntry->dwESPOffset = Context.Ebp;
801 }
802 }
803 } // VirtualQuery was successfully
804 } // ERROR_PARTIAL_COPY
805 }
806}
807
808// AllocHashFind
809// If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!
810// If the Key was found, a pointer to the entry is returned
814
815 // get the Hash-Value
817
818 // Just do some simple checks:
820
822 while(pHashEntry != NULL) {
823 if (pHashEntry->lRequestID == lRequestID) {
824 return pHashEntry;
825 }
826 pHashEntry = pHashEntry->Next;
827 }
828
829 // entry was not found!
831} // AllocHashFind
832
833// AllocHashRemove
834// Return: FALSE (0) : Key was found and removed/marked
835// TRUE (!=0): Key was not found
836static BOOL AllocHashRemove(long lRequestID) {
839
840 // get the Hash-Value
842
843 // Just do some simple checks:
845
848 while(pHashEntry != NULL) {
849 if (pHashEntry->lRequestID == lRequestID) {
850#ifdef HASH_ENTRY_REMOVE_AT_FREE
853 // release my memory
854 if (pHashEntryLast == NULL) {
855 // It is an entry in the table, so do not release this memory
856 if (pHashEntry->Next == NULL) {
857 // It was the last entry, so empty the table entry
859 }
860 else {
861 // There are some more entries, so shorten the list
862 *pHashEntry = *(pHashEntry->Next);
863 // TODO: Do I need to free the memory here !? I think I should do this...
864 }
865 return TRUE;
866 }
867 else {
868 // now, I am in an dynamic allocated entry
869 // it was a collision, so decrease the current collision count
871 pHashEntryLast->Next = pHashEntry->Next;
873 return TRUE;
874 }
875#else
876 // increase the Remove-Count and let the objet stay in memory
877 pHashEntry->cRemovedFlag++;
878 return TRUE;
879#endif
880 }
882 pHashEntry = pHashEntry->Next;
883 }
884
885 // if we are here, we could not find the RequestID
886 return FALSE;
887}
888
889// ReadProcMemoryFromHash
890// Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries
891#if API_VERSION_NUMBER >= 9
893#else
895#endif
896 // Try to find the RequestID
899
902 // Not found, so I cannot return any memory
904 return FALSE;
905 }
906 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
907 // Memory is located in ESP:
908 // Calculate the offset
909 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
911 memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
913 if (dwSize != nSize)
914 return FALSE;
915 }
916
917 if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
918 // Memory is located in EIP:
919 // Calculate the offset
920 DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
922 memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
924 if (dwSize != nSize)
925 return FALSE;
926 }
927
928 if (*lpNumberOfBytesRead == 0) // Memory could not be found
929 return FALSE;
930
931 return TRUE;
932}
933
934// AllocHashOutLeaks
935// Write all Memory (with callstack) which was not freed yet
936// Returns the number of bytes, that are not freed (leaks)
940 ULONG ulCount = 0;
941 ULONG ulLeaksByte = 0;
942
943 // Move throu every entry
944 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
946 if (pHashEntry->lRequestID != 0) {
947 while(pHashEntry != NULL) {
948 if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
949 ulCount++;
951 _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
952 else
953 _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
954 CONTEXT c;
955 memset( &c, '\0', sizeof c );
956 c.Eip = pHashEntry->dwEIPOffset;
957 c.Ebp = pHashEntry->dwESPOffset;
959 // Count the number of leaky bytes
960 if (pHashEntry->nDataSize > 0)
961 ulLeaksByte += pHashEntry->nDataSize;
962 else
963 ulLeaksByte++; // If memory was allocated with zero bytes, then just increase the counter 1
964
966 _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
967 }
968 pHashEntry = pHashEntry->Next;
969 }
970 }
971 }
973 _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
974 return ulLeaksByte;
975} // AllocHashOutLeaks
976
977// Write all used memory to a file
978void AllocHashOut(FILE *fFile) {
981
982 for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
984 if (pHashEntry->lRequestID != 0) {
985 while(pHashEntry != NULL) {
987 _ftprintf(fFile, _T("<MEMUSED requestID=\"%u\" size=\"%u\"\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
988 else
989 _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
990 pHashEntry = pHashEntry->Next;
991 }
992 }
993 }
994} // AllocHashOut
995
996/*******************************************************************************
997 * Ende der Hash-Tabelle
998 *******************************************************************************/
999
1000
1001// The follwoing is copied from dbgint.h:
1002// <CRT_INTERNALS>
1003/*
1004 * For diagnostic purpose, blocks are allocated with extra information and
1005 * stored in a doubly-linked list. This makes all blocks registered with
1006 * how big they are, when they were allocated, and what they are used for.
1007 */
1008
1009#define nNoMansLandSize 4
1010
1011/// \cond
1012typedef struct _CrtMemBlockHeader
1013{
1016 char * szFileName;
1017 int nLine;
1018#ifdef _WIN64
1019 /* These items are reversed on Win64 to eliminate gaps in the struct
1020 * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
1021 * maintained in the debug heap.
1022 */
1023 int nBlockUse;
1024 size_t nDataSize;
1025#else /* _WIN64 */
1026 size_t nDataSize;
1027 int nBlockUse;
1028#endif /* _WIN64 */
1029 long lRequest;
1030 unsigned char gap[nNoMansLandSize];
1031 /* followed by:
1032 * unsigned char data[nDataSize];
1033 * unsigned char anotherGap[nNoMansLandSize];
1034 */
1036/// \endcond
1037
1038#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
1039#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
1040
1041// </CRT_INTERNALS>
1042
1043
1044
1045
1046// Global data:
1047static BOOL g_bInitialized = FALSE;
1049
1050static DWORD g_dwShowCount = 0; // increase at every ShowStack-Call
1052
1053// Is used for syncronising call to MyAllocHook (to prevent reentrant calls)
1054static LONG g_lMallocCalled = 0;
1055
1057
1058// Deaktivate AllocHook, by increasing the Syncronisation-Counter
1059static void DeactivateMallocStackwalker(void) {
1061}
1062
1063
1064// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
1065// Special case for VC 5
1066#if _MSC_VER <= 1100
1067static int MyAllocHook(int nAllocType, void *pvData,
1068 size_t nSize, int nBlockUse, long lRequest,
1069 const char * szFileName, int nLine ) {
1070#else
1071static int MyAllocHook(int nAllocType, void *pvData,
1072 size_t nSize, int nBlockUse, long lRequest,
1073 const unsigned char * szFileName, int nLine ) {
1074#endif
1075 static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
1076 static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };
1077
1078#ifdef IGNORE_CRT_ALLOC
1079 if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations
1080 return TRUE;
1081#endif
1082 extern int _crtDbgFlag;
1084 {
1085 // Someone has disabled that the runtime should log this allocation
1086 // so we do not log this allocation
1087 if (pfnOldCrtAllocHook != NULL)
1089 return TRUE;
1090 }
1091
1092 // Prevent from reentrat calls
1093 if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called
1095 // call the previous alloc hook
1096 if (pfnOldCrtAllocHook != NULL)
1098 return TRUE;
1099 }
1100
1101 if (g_ulShowStackAtAlloc > 0) {
1102 AllocCheckFileOpen(); // Open logfile
1103 }
1104
1106 _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
1107
1108 if (nAllocType == _HOOK_FREE) { // freeing
1109 // Try to get the header information
1110 if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
1111 // get the ID
1113 // get a pointer to memory block header
1114 pHead = pHdr(pvData);
1115 nSize = pHead->nDataSize;
1116 lRequest = pHead->lRequest; // This is the ID!
1117
1118 if (pHead->nBlockUse == _IGNORE_BLOCK)
1119 {
1121 if (pfnOldCrtAllocHook != NULL)
1123 return TRUE;
1124 }
1125 }
1126 }
1127
1128 if (g_ulShowStackAtAlloc > 0) {
1129 _ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),
1131 if ( pvData != NULL )
1132 _ftprintf( g_fFile, _T(" at 0x%X"), pvData );
1133 _ftprintf(g_fFile, _T("\n"));
1134 }
1135
1136 if (nAllocType == _HOOK_FREE) { // freeing:
1137 if (lRequest != 0) { // RequestID was found
1138 BOOL bRet;
1139 // Try to find the RequestID in the Hash-Table, mark it that it was freed
1141 if(g_ulShowStackAtAlloc > 0) {
1142 if (bRet == FALSE) {
1143 // RequestID not found!
1144 _ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);
1145 }
1146 } // g_ulShowStackAtAlloc > 0
1147 }
1148 else {
1149 if(g_ulShowStackAtAlloc > 0) {
1150 // No valid RequestID found, display error
1151 _ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);
1152
1153 }
1154 }
1155 } // freeing
1156
1157 if (nAllocType == _HOOK_REALLOC) { // re-allocating
1158 // Try to get the header information
1159 if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
1160 BOOL bRet;
1162 // get the ID
1164 // get a pointer to memory block header
1165 pHead = pHdr(pvData);
1166 // Try to find the RequestID in the Hash-Table, mark it that it was freed
1167 lReallocRequest = pHead->lRequest;
1169 if (g_ulShowStackAtAlloc > 0) {
1170 if (bRet == FALSE) {
1171 // RequestID not found!
1172 _ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);
1173 }
1174 else {
1175 _ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);
1176 }
1177 } // g_ulShowStackAtAlloc > 0
1178 } // ValidHeapPointer
1179 } // re-allocating
1180
1181 if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
1183 // call the previous alloc hook
1184 if (pfnOldCrtAllocHook != NULL)
1186 return TRUE;
1187 }
1188
1189 // Get the context DIESES of this Thread
1192 GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) == 0) {
1193 // Something was wrong...
1194 _ftprintf(g_fFile, _T("###### Could not call 'DuplicateHandle' successfully\n"));
1196 // call the previous alloc hook
1197 if (pfnOldCrtAllocHook != NULL)
1199 return TRUE;
1200 }
1201
1202 CONTEXT c;
1203 memset( &c, '\0', sizeof c );
1204 c.ContextFlags = CONTEXT_FULL;
1205
1206 // init CONTEXT record so we know where to start the stackwalk
1207 if ( GetThreadContext( hThread, &c ) == 0) {
1208 if(g_ulShowStackAtAlloc > 1) {
1209 _ftprintf(g_fFile, _T("###### Could not call 'GetThreadContext' successfully\n"));
1210 }
1212 // call the previous alloc hook
1213 if (pfnOldCrtAllocHook != NULL)
1216 return TRUE; // could not get context
1217 }
1218
1219 if(g_ulShowStackAtAlloc > 1) {
1220 if(g_ulShowStackAtAlloc > 2) {
1221 // output the callstack
1223 }
1224 else {
1225 // Output only (re)allocs
1226 if (nAllocType != _HOOK_FREE) {
1228 }
1229 }
1230 } // g_ulShowStackAtAlloc > 1
1232
1233 // Only isert in the Hash-Table if it is not a "freeing"
1234 if (nAllocType != _HOOK_FREE) {
1235 if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
1237 }
1238
1240 // call the previous alloc hook
1241 if (pfnOldCrtAllocHook != NULL)
1243 return TRUE; // allow the memory operation to proceed
1244}
1245
1246
1247
1248
1249// ##########################################################################################
1250// ##########################################################################################
1251// ##########################################################################################
1252// ##########################################################################################
1253
1254#define gle (GetLastError())
1255#define lenof(a) (sizeof(a) / sizeof((a)[0]))
1256#define MAXNAMELEN 1024 // max name length for found symbols
1257#define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL )
1258#define TTBUFLEN 8096 // for a temp buffer (2^13)
1259
1260
1261
1262// SymCleanup()
1263typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
1264tSC pSC = NULL;
1265
1266// SymFunctionTableAccess()
1268tSFTA pSFTA = NULL;
1269
1270// SymGetLineFromAddr()
1274
1275// SymGetModuleBase()
1277tSGMB pSGMB = NULL;
1278
1279// SymGetModuleInfo()
1281tSGMI pSGMI = NULL;
1282
1283// SymGetOptions()
1284typedef DWORD (__stdcall *tSGO)( VOID );
1285tSGO pSGO = NULL;
1286
1287// SymGetSymFromAddr()
1291
1292// SymInitialize()
1294tSI pSI = NULL;
1295
1296// SymLoadModule()
1299tSLM pSLM = NULL;
1300
1301// SymSetOptions()
1302typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
1303tSSO pSSO = NULL;
1304
1305// StackWalk()
1312tSW pSW = NULL;
1313
1314// UnDecorateSymbolName()
1316 DWORD UndecoratedLength, DWORD Flags );
1317tUDSN pUDSN = NULL;
1318
1319
1320/// \cond
1321struct ModuleEntry
1322{
1323 std::string imageName;
1324 std::string moduleName;
1326 DWORD size;
1327};
1328typedef std::vector< ModuleEntry > ModuleList;
1329typedef ModuleList::iterator ModuleListIter;
1330/// \endcond
1331
1332// **************************************** ToolHelp32 ************************
1333#define MAX_MODULE_NAME32 255
1334#define TH32CS_SNAPMODULE 0x00000008
1335#pragma pack( push, 8 )
1336/// \cond
1337typedef struct tagMODULEENTRY32
1338{
1339 DWORD dwSize;
1340 DWORD th32ModuleID; // This module
1341 DWORD th32ProcessID; // owning process
1342 DWORD GlblcntUsage; // Global usage count on the module
1343 DWORD ProccntUsage; // Module usage count in th32ProcessID's context
1344 BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
1345 DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
1346 HMODULE hModule; // The hModule of this module in th32ProcessID's context
1347 char szModule[MAX_MODULE_NAME32 + 1];
1348 char szExePath[MAX_PATH];
1352/// \endcond
1353#pragma pack( pop )
1354
1355
1356
1358{
1359 // CreateToolhelp32Snapshot()
1361 // Module32First()
1363 // Module32Next()
1365
1366 // try both dlls...
1367 const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
1369 tCT32S pCT32S;
1370 tM32F pM32F;
1371 tM32N pM32N;
1372
1373 HANDLE hSnap;
1375 me.dwSize = sizeof(me);
1376 bool keepGoing;
1377 ModuleEntry e;
1378 int i;
1379
1380 for (i = 0; i<lenof(dllname); i++ )
1381 {
1383 if (hToolhelp == NULL)
1384 continue;
1385 pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
1386 pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
1387 pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
1388 if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )
1389 break; // found the functions!
1391 hToolhelp = NULL;
1392 }
1393
1394 if (hToolhelp == NULL)
1395 return false;
1396
1398 if (hSnap == (HANDLE) -1)
1399 return false;
1400
1401 keepGoing = !!pM32F( hSnap, &me );
1402 while (keepGoing)
1403 {
1404 e.imageName = me.szExePath;
1405 e.moduleName = me.szModule;
1406 e.baseAddress = (DWORD) me.modBaseAddr;
1407 e.size = me.modBaseSize;
1408 modules.push_back( e );
1409 keepGoing = !!pM32N( hSnap, &me );
1410 }
1411
1414
1415 return modules.size() != 0;
1416} // GetModuleListTH32
1417
1418
1419// **************************************** PSAPI ************************
1420/// \cond
1421typedef struct _MODULEINFO {
1426/// \endcond
1427
1429{
1430 // EnumProcessModules()
1432 // GetModuleFileNameEx()
1434 // GetModuleBaseName()
1436 // GetModuleInformation()
1438
1440 tEPM pEPM;
1441 tGMFNE pGMFNE;
1442 tGMBN pGMBN;
1443 tGMI pGMI;
1444
1445 DWORD i;
1446 ModuleEntry e;
1448 MODULEINFO mi;
1449 HMODULE *hMods = 0;
1450 char *tt = 0;
1451
1452 hPsapi = LoadLibrary( _T("psapi.dll") );
1453 if ( hPsapi == 0 )
1454 return false;
1455
1456 modules.clear();
1457
1458 pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
1459 pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
1460 pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
1461 pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
1462 if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )
1463 {
1464 // we couldn't find all functions
1466 return false;
1467 }
1468
1469 hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
1470 tt = (char*) malloc(sizeof(char) * TTBUFLEN);
1471
1472 if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
1473 {
1474 _ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
1475 goto cleanup;
1476 }
1477
1478 if ( cbNeeded > TTBUFLEN )
1479 {
1480 _ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
1481 goto cleanup;
1482 }
1483
1484 for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
1485 {
1486 // base address, size
1487 pGMI(hProcess, hMods[i], &mi, sizeof mi );
1488 e.baseAddress = (DWORD) mi.lpBaseOfDll;
1489 e.size = mi.SizeOfImage;
1490 // image file name
1491 tt[0] = 0;
1493 e.imageName = tt;
1494 // module name
1495 tt[0] = 0;
1497 e.moduleName = tt;
1498
1499 modules.push_back(e);
1500 }
1501
1502cleanup:
1503 if (hPsapi)
1505 free(tt);
1506 free(hMods);
1507
1508 return modules.size() != 0;
1509} // GetModuleListPSAPI
1510
1511
1513{
1514 // first try toolhelp32
1516 return true;
1517 // then try psapi
1519} // GetModuleList
1520
1522{
1523 char img[MAX_PATH], mod[MAX_PATH];
1524 img[sizeof(img) - 1] = 0;
1525 mod[sizeof(mod) - 1] = 0;
1526
1529 for (ModuleListIter it = modules.begin(); it != modules.end(); ++it)
1530 {
1531 // SymLoadModule() wants writeable strings
1532 strncpy(img, it->imageName.c_str(), sizeof(img) - 1);
1533 strncpy(mod, it->moduleName.c_str(), sizeof(mod) - 1);
1534
1535 pSLM( hProcess, 0, img, mod, it->baseAddress, it->size );
1536 }
1537} // EnumAndLoadModuleSymbols
1538
1539static int InitStackWalk(void)
1540{
1541 if (g_bInitialized != FALSE)
1542 return 0; // already initialized
1543
1544 // old: we load imagehlp.dll dynamically because the NT4-version does not
1545 // old: offer all the functions that are in the NT5 lib
1546 // 02-12-19: Now we only support dbghelp.dll!
1547 // To use it on NT you have to install the redistrubutable for DBGHELP.DLL
1548 g_hImagehlpDll = LoadLibrary( _T("dbghelp.dll") );
1549 if ( g_hImagehlpDll == NULL )
1550 {
1551 printf( "LoadLibrary( \"dbghelp.dll\" ): GetLastError = %lu\n", gle );
1553 return 1;
1554 }
1555
1556 pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );
1557 pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess" );
1558 pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr" );
1559 pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase" );
1560 pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo" );
1561 pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );
1562 pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr" );
1563 pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );
1564 pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );
1565 pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk" );
1566 pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );
1567 pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule" );
1568
1569 if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
1570 pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
1571 pSW == NULL || pUDSN == NULL || pSLM == NULL )
1572 {
1573 printf( "GetProcAddress(): some required function not found.\n" );
1576 return 1;
1577 }
1578
1581 return 0;
1582}
1583
1584static TCHAR s_szExceptionLogFileName[_MAX_PATH] = _T("\\exceptions.log"); // default
1587{
1588 LONG lRet;
1590 TCHAR lString[500];
1592 _T("*** Unhandled Exception!\n")
1593 _T(" ExpCode: 0x%8.8X\n")
1594 _T(" ExpFlags: %d\n")
1595 _T(" ExpAddress: 0x%8.8X\n")
1596 _T(" Please report!"),
1597 pExPtrs->ExceptionRecord->ExceptionCode,
1598 pExPtrs->ExceptionRecord->ExceptionFlags,
1599 pExPtrs->ExceptionRecord->ExceptionAddress);
1601 return lRet;
1602}
1603
1605 switch(dwExceptionCode) {
1606 case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");
1607 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");
1608 case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");
1609 case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");
1610 case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");
1611 case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");
1612 case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");
1613 case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");
1614 case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");
1615 case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");
1616 case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");
1617 case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");
1618 case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");
1619 case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");
1620 case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");
1621 case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");
1622 case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");
1623 case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");
1624 case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");
1625 case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");
1626 case DBG_CONTROL_C : return _T("DBG CONTROL C ");
1627 default:
1628 return _T("<unkown exception>");
1629 }
1630} // GetExpectionCodeText
1631
1632// Function is not multi-threading safe, because of static char!
1634 static TCHAR szTemp[100];
1635
1636 switch(pExceptionRecord->ExceptionCode) {
1638 if (pExceptionRecord->NumberParameters == 2) {
1639 switch(pExceptionRecord->ExceptionInformation[0]) {
1640 case 0: // read attempt
1641 _stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
1642 return szTemp;
1643 case 1: // write attempt
1644 _stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
1645 return szTemp;
1646 default:
1647 return _T("");
1648 }
1649 } // if (pExceptionRecord->NumberParameters == 2)
1650 return _T("");
1651 default:
1652 return _T("");
1653 } // switch(pExceptionRecord->ExceptionCode)
1654} // GetAdditionalExpectionCodeText
1655
1656std::string SimpleXMLEncode(PCSTR szText)
1657{
1658 std::string szRet;
1659
1660 for (size_t i=0; i<strlen(szText); i++)
1661 {
1662 switch(szText[i])
1663 {
1664 case '&':
1665 szRet.append("&amp;");
1666 break;
1667 case '<':
1668 szRet.append("&lt;");
1669 break;
1670 case '>':
1671 szRet.append("&gt;");
1672 break;
1673 case '"':
1674 szRet.append("&quot;");
1675 break;
1676 case '\'':
1677 szRet.append("&apos;");
1678 break;
1679 default:
1680 szRet += szText[i];
1681 }
1682 }
1683 return szRet;
1684}
1685
1686
1688{
1689 FILE *fFile = stdout; // default to stdout
1690
1691 if (pszLogFile != NULL) { // a filename is available
1692 // Open the logfile
1693 fFile = _tfopen(pszLogFile, _T("a"));
1694 if (fFile != NULL) { // Is the file too big?
1695 long size;
1696 fseek(fFile, 0, SEEK_END);
1697 size = ftell(fFile); // Get the size of the file
1698 if (size >= LOG_FILE_MAX_SIZE) {
1700 // It is too big...
1701 fclose(fFile);
1703 _tcscat(pszTemp, _T(".old"));
1704 _tremove(pszTemp); // Remove an old file, if exists
1705 _trename(pszLogFile, pszTemp); // rename the actual file
1706 fFile = _tfopen(pszLogFile, _T("w")); // open new file
1707 free(pszTemp);
1708 }
1709 }
1710 } // if (pszLogFile != NULL)
1711 if (fFile == NULL) {
1712 fFile = stdout;
1713 }
1714
1716
1717 fclose(fFile);
1718}
1719
1720
1721static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {
1723}
1724
1726 // normally, call ImageNtHeader() and use machine info from PE header
1728 HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside
1729 int frameNum; // counts walked frames
1730 DWORD offsetFromSymbol; // tells us how far from the symbol we were
1731 DWORD offsetFromLine; // tells us how far from the line we were
1732 DWORD symOptions; // symbol handler settings
1733
1734 static char Sym[IMGSYMLEN + MAXNAMELEN];
1735 IMAGEHLP_SYMBOL* const pSym = reinterpret_cast<IMAGEHLP_SYMBOL*>(&Sym);
1736 char undName[MAXNAMELEN]; // undecorated name
1737 char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans
1739 IMAGEHLP_LINE Line;
1741
1742 std::string symSearchPath;
1743
1744 static int bFirstTime = TRUE;
1745
1746 // If no logfile is present, outpur to "stdout"
1747 if (fLogFile == NULL) {
1748 fLogFile = stdout;
1749 }
1750
1751 STACKFRAME s; // in/out stackframe
1752 memset( &s, '\0', sizeof s );
1753
1754 if ( (g_bInitialized == FALSE) && (bFirstTime == TRUE) ) {
1755 InitStackWalk();
1756 }
1757
1758 if (g_bInitialized == FALSE)
1759 {
1760 // Could not init!!!!
1761 bFirstTime = FALSE;
1762 _ftprintf(fLogFile, _T("%lu: Stackwalker not initialized (or was not able to initialize)!\n"), g_dwShowCount);
1763 return;
1764 }
1765
1766// Critical section begin...
1768
1770
1771
1772 // NOTE: normally, the exe directory and the current directory should be taken
1773 // from the target process. The current dir would be gotten through injection
1774 // of a remote thread; the exe fir through either ToolHelp32 or PSAPI.
1775
1777 {
1778 _ftprintf(fLogFile, _T("%lu: "), g_dwShowCount);
1780 _ftprintf(fLogFile, _T("\n"));
1781 }
1782
1783
1784 if (bFirstTime) {
1785
1786 CHAR *tt, *p;
1787
1788 tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer
1789 if (!tt) goto cleanup; // not enough memory...
1790
1791 // build symbol search path from:
1792 symSearchPath = "";
1793 // current directory
1795 symSearchPath += tt + std::string( ";" );
1796 // dir with executable
1797 if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )
1798 {
1799 for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
1800 {
1801 // locate the rightmost path separator
1802 if ( *p == '\\' || *p == '/' || *p == ':' )
1803 break;
1804 }
1805 // if we found one, p is pointing at it; if not, tt only contains
1806 // an exe name (no path), and p points before its first byte
1807 if ( p != tt ) // path sep found?
1808 {
1809 if ( *p == ':' ) // we leave colons in place
1810 ++ p;
1811 *p = '\0'; // eliminate the exe name and last path sep
1812 symSearchPath += tt + std::string( ";" );
1813 }
1814 }
1815 // environment variable _NT_SYMBOL_PATH
1816 if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
1817 symSearchPath += tt + std::string( ";" );
1818 // environment variable _NT_ALTERNATE_SYMBOL_PATH
1819 if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
1820 symSearchPath += tt + std::string( ";" );
1821 // environment variable SYSTEMROOT
1822 if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )
1823 symSearchPath += tt + std::string( ";" );
1824
1825
1826
1827 if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
1828 symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );
1829
1830 // why oh why does SymInitialize() want a writeable string?
1831 strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
1832 tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator
1833
1834 // init symbol handler stuff (SymInitialize())
1835 if ( ! pSI( hProcess, tt, false ) )
1836 {
1838 _ftprintf(fLogFile, _T("%lu: SymInitialize(): GetLastError = %lu\n"), g_dwShowCount, gle );
1839 if (tt) free( tt );
1840 goto cleanup;
1841 }
1842
1843 // SymGetOptions()
1844 symOptions = pSGO();
1848 pSSO( symOptions ); // SymSetOptions()
1849
1850 // Enumerate modules and tell imagehlp.dll about them.
1851 // On NT, this is not necessary, but it won't hurt.
1853
1854 if (tt)
1855 free( tt );
1856 } // bFirstTime = TRUE
1857 bFirstTime = FALSE;
1858
1859 // init STACKFRAME for first call
1860 // Notes: AddrModeFlat is just an assumption. I hate VDM debugging.
1861 // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
1862 // and good riddance.
1863 s.AddrPC.Offset = c.Eip;
1864 s.AddrPC.Mode = AddrModeFlat;
1865 s.AddrFrame.Offset = c.Ebp;
1866 s.AddrFrame.Mode = AddrModeFlat;
1867
1868 memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
1869 pSym->SizeOfStruct = IMGSYMLEN;
1870 pSym->MaxNameLength = MAXNAMELEN;
1871
1872 memset( &Line, '\0', sizeof Line );
1873 Line.SizeOfStruct = sizeof Line;
1874
1875 memset( &Module, '\0', sizeof Module );
1876 Module.SizeOfStruct = sizeof Module;
1877
1878 for ( frameNum = 0; ; ++ frameNum )
1879 {
1880 // get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase())
1881 // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
1882 // assume that either you are done, or that the stack is so hosed that the next
1883 // deeper frame could not be found.
1884 // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
1886 break;
1887
1889
1891 _ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);
1892 if ( s.AddrPC.Offset == 0 )
1893 {
1894 // Special case: If we are here, we have no valid callstack entry!
1895 switch(g_CallstackOutputType)
1896 {
1897 case ACOutput_Simple:
1898 _ftprintf(fLogFile, _T("%lu: (-nosymbols- PC == 0)\n"), g_dwShowCount);
1899 break;
1900 case ACOutput_Advanced:
1901 _ftprintf(fLogFile, _T(" (-nosymbols- PC == 0)\n"));
1902 break;
1903 case ACOutput_XML:
1904 // TODO: ....
1905 _ftprintf(fLogFile, _T("<STACKENTRY decl=\"(-nosymbols- PC == 0)\"/>\n"));
1906 break;
1907 }
1908 }
1909 else
1910 {
1911 // we seem to have a valid PC
1912 undName[0] = 0;
1913 undFullName[0] = 0;
1914 offsetFromSymbol = 0;
1915 // show procedure info (SymGetSymFromAddr())
1916 if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
1917 {
1919 {
1920 if ( gle != 487 )
1921 _ftprintf(fLogFile, _T(" SymGetSymFromAddr(): GetLastError = %lu\n"), gle );
1922 else
1923 _ftprintf(fLogFile, _T("\n"));
1924 }
1925 }
1926 else
1927 {
1928 // UnDecorateSymbolName()
1932 {
1933 if (strlen(undName) > 0)
1934 fprintf(fLogFile, " %s %+ld bytes\n", undName, (long) offsetFromSymbol );
1935 else
1936 {
1937 fprintf(fLogFile, " Sig: %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );
1938 strcpy(undName, pSym->Name);
1939 }
1940 fprintf(fLogFile, "%lu: Decl: %s\n", g_dwShowCount, undFullName );
1941 }
1942 }
1943 //if (g_CallstackOutputType == ACOutput_XML)
1944 // fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
1945
1946 // show line number info, NT5.0-method (SymGetLineFromAddr())
1947 offsetFromLine = 0;
1948 if ( pSGLFA != NULL )
1949 { // yes, we have SymGetLineFromAddr()
1950 if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromLine, &Line ) )
1951 {
1952 if ( (gle != 487) && (frameNum > 0) ) // ignore error for first frame
1953 {
1955 {
1956 _ftprintf(fLogFile, _T("<STACKENTRY "));
1958 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
1959 _ftprintf(fLogFile, _T("srcfile=\"SymGetLineFromAddr(): GetLastError = %lu\" "), gle);
1960 }
1961 else
1962 _ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );
1963 }
1964 }
1965 else
1966 {
1967 switch(g_CallstackOutputType)
1968 {
1969 case ACOutput_Advanced:
1970 fprintf(fLogFile, "%lu: Line: %s(%lu) %+ld bytes\n", g_dwShowCount,
1971 Line.FileName, Line.LineNumber, offsetFromLine );
1972 break;
1973 case ACOutput_Simple:
1974 fprintf(fLogFile, "%lu: %s(%lu) %+ld bytes (%s)\n", g_dwShowCount,
1975 Line.FileName, Line.LineNumber, offsetFromLine, undName);
1976 break;
1977 case ACOutput_XML:
1978 _ftprintf(fLogFile, _T("<STACKENTRY "));
1980 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
1981 fprintf(fLogFile, "srcfile=\"%s\" line=\"%lu\" line_offset=\"%+ld\" ",
1982 SimpleXMLEncode(Line.FileName).c_str(), Line.LineNumber, offsetFromLine, undName);
1983 break;
1984 }
1985 }
1986 } // yes, we have SymGetLineFromAddr()
1987
1988 // show module info (SymGetModuleInfo())
1990 {
1991 if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )
1992 {
1994 _ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );
1995 }
1996 else
1997 { // got module info OK
1998 char ty[80];
1999 switch ( Module.SymType )
2000 {
2001 case SymNone:
2002 strcpy( ty, "-nosymbols-" );
2003 break;
2004 case SymCoff:
2005 strcpy( ty, "COFF" );
2006 break;
2007 case SymCv:
2008 strcpy( ty, "CV" );
2009 break;
2010 case SymPdb:
2011 strcpy( ty, "PDB" );
2012 break;
2013 case SymExport:
2014 strcpy( ty, "-exported-" );
2015 break;
2016 case SymDeferred:
2017 strcpy( ty, "-deferred-" );
2018 break;
2019 case SymSym:
2020 strcpy( ty, "SYM" );
2021 break;
2022 /* // TODO: #if API_VERSION_NUMBER >= 9 ?
2023 case SymDia:
2024 strcpy( ty, "DIA" );
2025 break;*/
2026 default:
2027 _snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
2028 break;
2029 }
2030
2032 {
2033 // now, check if the XML-Entry is written...
2034 if (bXMLTagWrote == FALSE)
2035 {
2036 _ftprintf(fLogFile, _T("<STACKENTRY "));
2038 fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
2039 _ftprintf(fLogFile, _T("srcfile=\"\" "));
2041 }
2042 }
2043
2045 {
2046 fprintf(fLogFile, "%lu: Mod: %s, base: %08lxh\n", g_dwShowCount,
2047 Module.ModuleName, Module.BaseOfImage );
2048 if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!
2049 _ftprintf(fLogFile, _T("%lu: Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);
2050 fprintf(fLogFile, "%lu: Sym: type: %s, file: %s\n", g_dwShowCount,
2051 ty, Module.LoadedImageName );
2052 }
2053 }
2054 else
2055 {
2056 // XML:
2057 if (bXMLTagWrote == TRUE)
2058 fprintf(fLogFile, "module=\"%s\" base=\"%08lx\" ", Module.ModuleName, Module.BaseOfImage);
2059 }
2060 } // got module info OK
2061 }
2063 _ftprintf(fLogFile, _T("/>\n")); // terminate the XML node
2064
2065 } // we seem to have a valid PC
2066
2067 // no return address means no deeper stackframe
2068 if ( s.AddrReturn.Offset == 0 )
2069 {
2070 // avoid misunderstandings in the printf() following the loop
2071 SetLastError( 0 );
2072 break;
2073 }
2074
2075 } // for ( frameNum )
2076
2077 if ( (g_CallstackOutputType != ACOutput_XML) && (gle != 0) )
2078 _ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );
2079
2080cleanup:
2081 if (fLogFile) {
2082 _ftprintf(fLogFile, _T("\n\n"));
2083 if (g_dwShowCount % 1000)
2085 }
2086
2088// Critical section end...
2089} // ShowStackRM
2090
2091} // StackWalker namespace
2092
2093//-------------------------------------------------------------------------------------------------
2094
2095//
2096// This function if NOT multi-threading capable
2097// It should only be called from the main-Function!
2098//
2100 if (g_bInitialized) {
2101 return 2; // already initialized!
2102 }
2103 if (ulShowStackAtAlloc <= 3)
2105 else
2107
2108 if (pszFileName != NULL)
2110 else
2112
2114
2115#ifdef _DEBUG
2116 AllocHashInit();
2117
2118#ifdef WITH_IMALLOC_SPY
2119 HRESULT hr;
2121 if (pMallocSpy != NULL)
2122 {
2124 if FAILED(hr)
2125 {
2126 _tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr);
2127 }
2128 }
2129#endif
2130
2131 // save the previous alloc hook
2133#endif
2134
2135 return InitStackWalk();
2136} // InitAllocCheckWN
2137
2139{
2141 if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
2142 {
2144 if (eOutput == ACOutput_XML)
2145 _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
2146 else
2147 _tcscat(s_szExceptionLogFileName, _T(".exp.log"));
2148
2149 if (eOutput == ACOutput_XML)
2150 _tcscat(szModName, _T(".mem.xml"));
2151 else
2152 _tcscat(szModName, _T(".mem.log"));
2153 }
2154 else
2155 {
2156 if (eOutput == ACOutput_XML)
2157 _tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default
2158 else
2159 _tcscpy(szModName, _T("\\mem-leaks.log")); // default
2160 }
2161
2163 {
2164 // set global exception handler (for handling all unhandled exceptions)
2167 }
2168
2170}
2171
2172//
2173// This function if NOT multi-threading capable
2174// It should only be called from the main-Function!
2175// Returns the number of bytes that are not freed (leaks)
2176//
2178 ULONG ulRet = 0;
2179 if (g_bInitialized) {
2180
2181#ifdef _DEBUG
2182 InterlockedIncrement(&g_lMallocCalled); // No deactivate MyAllocHook, because StackWalker will allocate some memory)
2183 ulRet = AllocHashDeinit(); // output the not freed memory
2184 // remove the hook and set the old one
2186
2187#ifdef WITH_IMALLOC_SPY
2189#endif
2190
2191#endif
2192
2193 EnterCriticalSection(&g_csFileOpenClose); // wait until a running stack dump was created
2195
2196 // de-init symbol handler etc. (SymCleanup())
2197 if (pSC != NULL)
2200
2202 if (g_pszAllocLogName != NULL) {
2205 }
2206 if (g_fFile != NULL) {
2207 fclose(g_fFile);
2208 g_fFile = NULL;
2209 }
2210
2213 }
2214
2216 {
2219 }
2220 return ulRet;
2221} // DeInitAllocCheck
2222
2223//
2224// #################################################################################
2225// #################################################################################
2226// Here the Stackwalk-Part begins.
2227// Some of the code is from an example from a book
2228// But I couldn't find the reference anymore... sorry...
2229// If someone knowns, please let me know...
2230// #################################################################################
2231// #################################################################################
2232//
2233// if you use C++ exception handling: install a translator function
2234// with set_se_translator(). In the context of that function (but *not*
2235// afterwards), you can either do your stack dump, or save the CONTEXT
2236// record as a local copy. Note that you must do the stack sump at the
2237// earliest opportunity, to avoid the interesting stackframes being gone
2238// by the time you do the dump.
2239//
2240// status:
2241// - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht
2242// - EXCEPTION_CONTINUE_EXECUTION:
2243// - EXCEPTION_EXECUTE_HANDLER:
2244//
2246{
2248 FILE *fFile = stdout; // default to stdout
2249
2250 if (pszLogFile != NULL) { // a filename is provided
2251 // Open the logfile
2252 fFile = _tfopen(pszLogFile, _T("a"));
2253 if (fFile != NULL) { // Is the file too big?
2254 long size;
2255 fseek(fFile, 0, SEEK_END);
2256 size = ftell(fFile); // Get the size of the file
2257 if (size >= LOG_FILE_MAX_SIZE) {
2259 // It is too big...
2260 fclose(fFile);
2262 _tcscat(pszTemp, _T(".old"));
2263 _tremove(pszTemp); // Remove an old file, if exists
2264 _trename(pszLogFile, pszTemp); // rename the actual file
2265 fFile = _tfopen(pszLogFile, _T("w")); // create a new file
2266 free(pszTemp);
2267 }
2268 }
2269 } // if (pszLogFile != NULL)
2270 if (fFile == NULL) {
2271 fFile = stdout;
2272 }
2273
2274 // Write infos about the exception
2276 {
2277 _ftprintf(fFile, _T("<EXCEPTION code=\"%8.8X\" addr=\"%8.8X\" "),
2278 ep->ExceptionRecord->ExceptionCode,
2279 ep->ExceptionRecord->ExceptionAddress);
2281 _ftprintf(fFile, _T("code_desc=\"%s\" more_desc=\"%s\">\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
2282 GetAdditionalExpectionCodeText(ep->ExceptionRecord));
2283 }
2284 else
2285 {
2286 _ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"),
2287 ep->ExceptionRecord->ExceptionCode,
2288 ep->ExceptionRecord->ExceptionAddress);
2289 _ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
2290 GetAdditionalExpectionCodeText(ep->ExceptionRecord));
2291 }
2292
2295 ShowStack( hThread, *(ep->ContextRecord), fFile);
2297
2299 _ftprintf(fFile, _T("</EXCEPTION>\n"));
2300
2301 fclose(fFile);
2302
2303 return status;
2304} // 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