OWLNext    7.0
Borland's Object Windows Library for the modern age
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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