1 /**********************************************************************
\r
7 * 2005-07-27 v1 - First public release on http://www.codeproject.com/
\r
8 * http://www.codeproject.com/threads/stack_walker.asp
\r
9 * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack
\r
10 * (to simplify the usage)
\r
11 * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL
\r
12 * (should also be enough)
\r
13 * - Changed to compile correctly with the PSDK of VC7.0
\r
14 * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined:
\r
15 * it uses LPSTR instead of LPCSTR as first paremeter)
\r
16 * - Added declarations to support VC5/6 without using 'dbghelp.h'
\r
17 * - Added a 'pUserData' member to the ShowCallstack function and the
\r
18 * PReadProcessMemoryRoutine declaration (to pass some user-defined data,
\r
19 * which can be used in the readMemoryFunction-callback)
\r
20 * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default
\r
21 * - Added example for doing an exception-callstack-walking in main.cpp
\r
22 * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268)
\r
23 * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse!
\r
25 **********************************************************************/
\r
30 #pragma warning(push, 1)
\r
32 #pragma comment(lib, "version.lib") // for "VerQueryValue"
\r
34 #include "stack_walker.h"
\r
36 // If VC7 and later, then use the shipped 'dbghelp.h'-file
\r
37 #if _MSC_VER >= 1300
\r
38 #include <dbghelp.h>
\r
40 // inline the important dbghelp.h-declarations...
\r
53 typedef struct _IMAGEHLP_LINE64 {
\r
54 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
\r
55 PVOID Key; // internal
\r
56 DWORD LineNumber; // line number in file
\r
57 PCHAR FileName; // full filename
\r
58 DWORD64 Address; // first instruction of line
\r
59 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
\r
60 typedef struct _IMAGEHLP_MODULE64 {
\r
61 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
\r
62 DWORD64 BaseOfImage; // base load address of module
\r
63 DWORD ImageSize; // virtual size of the loaded module
\r
64 DWORD TimeDateStamp; // date/time stamp from pe header
\r
65 DWORD CheckSum; // checksum from the pe header
\r
66 DWORD NumSyms; // number of symbols in the symbol table
\r
67 SYM_TYPE SymType; // type of symbols loaded
\r
68 CHAR ModuleName[32]; // module name
\r
69 CHAR ImageName[256]; // image name
\r
70 CHAR LoadedImageName[256]; // symbol file name
\r
71 } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
\r
72 typedef struct _IMAGEHLP_SYMBOL64 {
\r
73 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
\r
74 DWORD64 Address; // virtual address including dll base address
\r
75 DWORD Size; // estimated size of symbol, can be zero
\r
76 DWORD Flags; // info about the symbols, see the SYMF defines
\r
77 DWORD MaxNameLength; // maximum size of symbol name in 'Name'
\r
78 CHAR Name[1]; // symbol name (null terminated string)
\r
79 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
\r
86 typedef struct _tagADDRESS64 {
\r
90 } ADDRESS64, *LPADDRESS64;
\r
91 typedef struct _KDHELP64 {
\r
93 DWORD ThCallbackStack;
\r
94 DWORD ThCallbackBStore;
\r
97 DWORD64 KiCallUserMode;
\r
98 DWORD64 KeUserCallbackDispatcher;
\r
99 DWORD64 SystemRangeStart;
\r
100 DWORD64 Reserved[8];
\r
101 } KDHELP64, *PKDHELP64;
\r
102 typedef struct _tagSTACKFRAME64 {
\r
103 ADDRESS64 AddrPC; // program counter
\r
104 ADDRESS64 AddrReturn; // return address
\r
105 ADDRESS64 AddrFrame; // frame pointer
\r
106 ADDRESS64 AddrStack; // stack pointer
\r
107 ADDRESS64 AddrBStore; // backing store pointer
\r
108 PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
\r
109 DWORD64 Params[4]; // possible arguments to the function
\r
110 BOOL Far; // WOW far call
\r
111 BOOL Virtual; // is this a virtual frame?
\r
112 DWORD64 Reserved[3];
\r
114 } STACKFRAME64, *LPSTACKFRAME64;
\r
117 (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
\r
119 DWORD64 qwBaseAddress,
\r
122 LPDWORD lpNumberOfBytesRead
\r
126 (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
\r
132 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(
\r
138 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
\r
143 #define SYMOPT_CASE_INSENSITIVE 0x00000001
\r
144 #define SYMOPT_UNDNAME 0x00000002
\r
145 #define SYMOPT_DEFERRED_LOADS 0x00000004
\r
146 #define SYMOPT_NO_CPP 0x00000008
\r
147 #define SYMOPT_LOAD_LINES 0x00000010
\r
148 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020
\r
149 #define SYMOPT_LOAD_ANYTHING 0x00000040
\r
150 #define SYMOPT_IGNORE_CVREC 0x00000080
\r
151 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
\r
152 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
\r
153 #define SYMOPT_EXACT_SYMBOLS 0x00000400
\r
154 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
\r
155 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
\r
156 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
\r
157 #define SYMOPT_PUBLICS_ONLY 0x00004000
\r
158 #define SYMOPT_NO_PUBLICS 0x00008000
\r
159 #define SYMOPT_AUTO_PUBLICS 0x00010000
\r
160 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000
\r
161 #define SYMOPT_SECURE 0x00040000
\r
162 #define SYMOPT_DEBUG 0x80000000
\r
163 #define UNDNAME_COMPLETE (0x0000) // Enable full undecoration
\r
164 #define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration;
\r
165 #endif // _MSC_VER < 1300
\r
167 // Some missing defines (for VC5/6):
\r
168 #ifndef INVALID_FILE_ATTRIBUTES
\r
169 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
\r
173 // secure-CRT_functions are only available starting with VC8
\r
174 #if _MSC_VER < 1400
\r
175 #define strcpy_s strcpy
\r
176 #define strcat_s(dst, len, src) strcat(dst, src)
\r
177 #define _snprintf_s _snprintf
\r
178 #define _tcscat_s _tcscat
\r
181 // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
\r
182 #define USED_CONTEXT_FLAGS CONTEXT_FULL
\r
185 class stack_walkerInternal
\r
188 stack_walkerInternal(stack_walker *parent, HANDLE hProcess)
\r
193 m_hProcess = hProcess;
\r
194 m_szSymPath = NULL;
\r
208 ~stack_walkerInternal()
\r
211 pSC(m_hProcess); // SymCleanup
\r
212 if (m_hDbhHelp != NULL)
\r
213 FreeLibrary(m_hDbhHelp);
\r
216 if(m_szSymPath != NULL)
\r
218 m_szSymPath = NULL;
\r
220 BOOL Init(LPCSTR szSymPath)
\r
222 if (m_parent == NULL)
\r
224 // Dynamically load the Entry-Points for dbghelp.dll:
\r
225 // First try to load the newsest one from
\r
226 TCHAR szTemp[4096];
\r
227 // But before wqe do this, we first check if the ".local" file exists
\r
228 if (GetModuleFileName(NULL, szTemp, 4096) > 0)
\r
230 _tcscat_s(szTemp, _T(".local"));
\r
231 if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)
\r
233 // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
\r
234 if (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)
\r
236 _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));
\r
237 // now check if the file exists:
\r
238 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
\r
240 m_hDbhHelp = LoadLibrary(szTemp);
\r
243 // Still not found? Then try to load the 64-Bit version:
\r
244 if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
\r
246 _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
\r
247 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
\r
249 m_hDbhHelp = LoadLibrary(szTemp);
\r
254 if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one
\r
255 m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") );
\r
256 if (m_hDbhHelp == NULL)
\r
258 pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" );
\r
259 pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" );
\r
261 pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" );
\r
262 pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" );
\r
263 pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" );
\r
265 pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" );
\r
266 pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" );
\r
267 pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" );
\r
268 pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
\r
269 //pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
\r
270 pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" );
\r
271 pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" );
\r
272 pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" );
\r
273 pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" );
\r
275 if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
\r
276 pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
\r
277 pSW == NULL || pUDSN == NULL || pSLM == NULL )
\r
279 FreeLibrary(m_hDbhHelp);
\r
286 if (szSymPath != NULL)
\r
287 m_szSymPath = _strdup(szSymPath);
\r
288 if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE)
\r
289 this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
\r
291 DWORD symOptions = this->pSGO(); // SymGetOptions
\r
292 symOptions |= SYMOPT_LOAD_LINES;
\r
293 symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
\r
294 //symOptions |= SYMOPT_NO_PROMPTS;
\r
296 symOptions = this->pSSO(symOptions);
\r
298 char buf[stack_walker::STACKWALK_MAX_NAMELEN] = {0};
\r
299 if (this->pSGSP != NULL)
\r
301 if (this->pSGSP(m_hProcess, buf, stack_walker::STACKWALK_MAX_NAMELEN) == FALSE)
\r
302 this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
\r
304 char szUserName[1024] = {0};
\r
305 DWORD dwSize = 1024;
\r
306 GetUserNameA(szUserName, &dwSize);
\r
307 this->m_parent->OnSymInit(buf, symOptions, szUserName);
\r
312 stack_walker *m_parent;
\r
314 HMODULE m_hDbhHelp;
\r
318 /*typedef struct IMAGEHLP_MODULE64_V3 {
\r
319 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
\r
320 DWORD64 BaseOfImage; // base load address of module
\r
321 DWORD ImageSize; // virtual size of the loaded module
\r
322 DWORD TimeDateStamp; // date/time stamp from pe header
\r
323 DWORD CheckSum; // checksum from the pe header
\r
324 DWORD NumSyms; // number of symbols in the symbol table
\r
325 SYM_TYPE SymType; // type of symbols loaded
\r
326 CHAR ModuleName[32]; // module name
\r
327 CHAR ImageName[256]; // image name
\r
328 // new elements: 07-Jun-2002
\r
329 CHAR LoadedImageName[256]; // symbol file name
\r
330 CHAR LoadedPdbName[256]; // pdb file name
\r
331 DWORD CVSig; // Signature of the CV record in the debug directories
\r
332 CHAR CVData[MAX_PATH * 3]; // Contents of the CV record
\r
333 DWORD PdbSig; // Signature of PDB
\r
334 GUID PdbSig70; // Signature of PDB (VC 7 and up)
\r
335 DWORD PdbAge; // DBI age of pdb
\r
336 BOOL PdbUnmatched; // loaded an unmatched pdb
\r
337 BOOL DbgUnmatched; // loaded an unmatched dbg
\r
338 BOOL LineNumbers; // we have line number information
\r
339 BOOL GlobalSymbols; // we have internal symbol information
\r
340 BOOL TypeInfo; // we have type information
\r
341 // new elements: 17-Dec-2003
\r
342 BOOL SourceIndexed; // pdb supports source server
\r
343 BOOL Publics; // contains public symbols
\r
346 typedef struct IMAGEHLP_MODULE64_V2 {
\r
347 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
\r
348 DWORD64 BaseOfImage; // base load address of module
\r
349 DWORD ImageSize; // virtual size of the loaded module
\r
350 DWORD TimeDateStamp; // date/time stamp from pe header
\r
351 DWORD CheckSum; // checksum from the pe header
\r
352 DWORD NumSyms; // number of symbols in the symbol table
\r
353 SYM_TYPE SymType; // type of symbols loaded
\r
354 CHAR ModuleName[32]; // module name
\r
355 CHAR ImageName[256]; // image name
\r
356 CHAR LoadedImageName[256]; // symbol file name
\r
361 typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
\r
364 // SymFunctionTableAccess64()
\r
365 typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
\r
368 // SymGetLineFromAddr64()
\r
369 typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
\r
370 OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
\r
373 // SymGetModuleBase64()
\r
374 typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
\r
377 // SymGetModuleInfo64()
\r
378 typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo );
\r
381 // // SymGetModuleInfo64()
\r
382 // typedef BOOL (__stdcall *tSGMI_V3)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo );
\r
383 // tSGMI_V3 pSGMI_V3;
\r
386 typedef DWORD (__stdcall *tSGO)( VOID );
\r
389 // SymGetSymFromAddr64()
\r
390 typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
\r
391 OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
\r
395 typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
\r
398 // SymLoadModule64()
\r
399 typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
\r
400 IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
\r
404 typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
\r
408 typedef BOOL (__stdcall *tSW)(
\r
409 DWORD MachineType,
\r
412 LPSTACKFRAME64 StackFrame,
\r
413 PVOID ContextRecord,
\r
414 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
\r
415 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
\r
416 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
\r
417 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
\r
420 // UnDecorateSymbolName()
\r
421 typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
\r
422 DWORD UndecoratedLength, DWORD Flags );
\r
425 typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
\r
430 // **************************************** ToolHelp32 ************************
\r
431 #define MAX_MODULE_NAME32 255
\r
432 #define TH32CS_SNAPMODULE 0x00000008
\r
433 #pragma pack( push, 8 )
\r
434 typedef struct tagMODULEENTRY32
\r
437 DWORD th32ModuleID; // This module
\r
438 DWORD th32ProcessID; // owning process
\r
439 DWORD GlblcntUsage; // Global usage count on the module
\r
440 DWORD ProccntUsage; // Module usage count in th32ProcessID's context
\r
441 BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
\r
442 DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
\r
443 HMODULE hModule; // The hModule of this module in th32ProcessID's context
\r
444 char szModule[MAX_MODULE_NAME32 + 1];
\r
445 char szExePath[MAX_PATH];
\r
447 typedef MODULEENTRY32 * PMODULEENTRY32;
\r
448 typedef MODULEENTRY32 * LPMODULEENTRY32;
\r
449 #pragma pack( pop )
\r
451 BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid)
\r
453 // CreateToolhelp32Snapshot()
\r
454 typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
\r
456 typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
\r
458 typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
\r
460 // try both dlls...
\r
461 const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
\r
462 HINSTANCE hToolhelp = NULL;
\r
463 tCT32S pCT32S = NULL;
\r
464 tM32F pM32F = NULL;
\r
465 tM32N pM32N = NULL;
\r
469 me.dwSize = sizeof(me);
\r
473 for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ )
\r
475 hToolhelp = LoadLibrary( dllname[i] );
\r
476 if (hToolhelp == NULL)
\r
478 pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
\r
479 pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
\r
480 pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
\r
481 if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) )
\r
482 break; // found the functions!
\r
483 FreeLibrary(hToolhelp);
\r
487 if (hToolhelp == NULL)
\r
490 hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
\r
491 if (hSnap == (HANDLE) -1)
\r
494 keepGoing = !!pM32F( hSnap, &me );
\r
498 this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize);
\r
500 keepGoing = !!pM32N( hSnap, &me );
\r
502 CloseHandle(hSnap);
\r
503 FreeLibrary(hToolhelp);
\r
507 } // GetModuleListTH32
\r
509 // **************************************** PSAPI ************************
\r
510 typedef struct _MODULEINFO {
\r
511 LPVOID lpBaseOfDll;
\r
514 } MODULEINFO, *LPMODULEINFO;
\r
516 BOOL GetModuleListPSAPI(HANDLE hProcess)
\r
518 // EnumProcessModules()
\r
519 typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
\r
520 // GetModuleFileNameEx()
\r
521 typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
\r
522 // GetModuleBaseName()
\r
523 typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
\r
524 // GetModuleInformation()
\r
525 typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );
\r
537 HMODULE *hMods = 0;
\r
540 const SIZE_T TTBUFLEN = 8096;
\r
543 hPsapi = LoadLibrary( _T("psapi.dll") );
\r
544 if (hPsapi == NULL)
\r
547 pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
\r
548 pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
\r
549 pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
\r
550 pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
\r
551 if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) )
\r
553 // we couldn´t find all functions
\r
554 FreeLibrary(hPsapi);
\r
558 hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
\r
559 tt = (char*) malloc(sizeof(char) * TTBUFLEN);
\r
560 tt2 = (char*) malloc(sizeof(char) * TTBUFLEN);
\r
561 if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) )
\r
564 if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
\r
566 //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
\r
570 if ( cbNeeded > TTBUFLEN )
\r
572 //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
\r
576 for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
\r
578 // base address, size
\r
579 pGMI(hProcess, hMods[i], &mi, sizeof mi );
\r
582 pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
\r
585 pGMBN(hProcess, hMods[i], tt2, TTBUFLEN );
\r
587 DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage);
\r
588 if (dwRes != ERROR_SUCCESS)
\r
589 this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
\r
594 if (hPsapi != NULL) FreeLibrary(hPsapi);
\r
595 if (tt2 != NULL) free(tt2);
\r
596 if (tt != NULL) free(tt);
\r
597 if (hMods != NULL) free(hMods);
\r
600 } // GetModuleListPSAPI
\r
602 DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
\r
604 CHAR *szImg = _strdup(img);
\r
605 CHAR *szMod = _strdup(mod);
\r
606 DWORD result = ERROR_SUCCESS;
\r
607 if ( (szImg == NULL) || (szMod == NULL) )
\r
608 result = ERROR_NOT_ENOUGH_MEMORY;
\r
611 if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
\r
612 result = GetLastError();
\r
614 ULONGLONG fileVersion = 0;
\r
615 if ( (m_parent != NULL) && (szImg != NULL) )
\r
617 // try to retrive the file-version:
\r
618 if ( (this->m_parent->m_options & stack_walker::RetrieveFileVersion) != 0)
\r
620 VS_FIXEDFILEINFO *fInfo = NULL;
\r
622 DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
\r
625 LPVOID vData = malloc(dwSize);
\r
628 if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
\r
631 TCHAR szSubBlock[] = _T("\\");
\r
632 if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0)
\r
636 fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
\r
644 // Retrive some additional-infos about the module
\r
645 IMAGEHLP_MODULE64_V2 Module;
\r
646 const char *szSymType = "-unknown-";
\r
647 if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
\r
649 switch(Module.SymType)
\r
652 szSymType = "-nosymbols-";
\r
655 szSymType = "COFF";
\r
664 szSymType = "-exported-";
\r
667 szSymType = "-deferred-";
\r
672 case 8: //SymVirtual:
\r
673 szSymType = "Virtual";
\r
680 this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion);
\r
682 if (szImg != NULL) free(szImg);
\r
683 if (szMod != NULL) free(szMod);
\r
687 BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId)
\r
689 // first try toolhelp32
\r
690 if (GetModuleListTH32(hProcess, dwProcessId))
\r
693 return GetModuleListPSAPI(hProcess);
\r
697 BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo)
\r
699 if(this->pSGMI == NULL)
\r
701 SetLastError(ERROR_DLL_INIT_FAILED);
\r
704 // First try to use the larger ModuleInfo-Structure
\r
705 // memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
\r
706 // pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
\r
707 // if (this->pSGMI_V3 != NULL)
\r
709 // if (this->pSGMI_V3(hProcess, baseAddr, pModuleInfo) != FALSE)
\r
711 // // check if the parameter was wrong (size is bad...)
\r
712 // if (GetLastError() != ERROR_INVALID_PARAMETER)
\r
715 // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)...
\r
716 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
\r
717 void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
\r
720 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
\r
723 memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
\r
724 if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2*) pData) != FALSE)
\r
726 // only copy as much memory as is reserved...
\r
727 memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
\r
728 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
\r
733 SetLastError(ERROR_DLL_INIT_FAILED);
\r
738 // #############################################################
\r
739 stack_walker::stack_walker(DWORD dwProcessId, HANDLE hProcess)
\r
741 this->m_options = OptionsAll;
\r
742 this->m_modulesLoaded = FALSE;
\r
743 this->m_hProcess = hProcess;
\r
744 this->m_sw = new stack_walkerInternal(this, this->m_hProcess);
\r
745 this->m_dwProcessId = dwProcessId;
\r
746 this->m_szSymPath = NULL;
\r
748 stack_walker::stack_walker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
\r
750 this->m_options = options;
\r
751 this->m_modulesLoaded = FALSE;
\r
752 this->m_hProcess = hProcess;
\r
753 this->m_sw = new stack_walkerInternal(this, this->m_hProcess);
\r
754 this->m_dwProcessId = dwProcessId;
\r
755 if (szSymPath != NULL)
\r
757 this->m_szSymPath = _strdup(szSymPath);
\r
758 this->m_options |= SymBuildPath;
\r
761 this->m_szSymPath = NULL;
\r
764 stack_walker::~stack_walker()
\r
766 if (m_szSymPath != NULL)
\r
768 m_szSymPath = NULL;
\r
769 if (this->m_sw != NULL)
\r
774 BOOL stack_walker::LoadModules()
\r
776 if (this->m_sw == NULL)
\r
778 SetLastError(ERROR_DLL_INIT_FAILED);
\r
781 if (m_modulesLoaded != FALSE)
\r
784 // Build the sym-path:
\r
785 char *szSymPath = NULL;
\r
786 if ( (this->m_options & SymBuildPath) != 0)
\r
788 const size_t nSymPathLen = 4096;
\r
789 szSymPath = (char*) malloc(nSymPathLen);
\r
790 if (szSymPath == NULL)
\r
792 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
\r
796 // Now first add the (optional) provided sympath:
\r
797 if (this->m_szSymPath != NULL)
\r
799 strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
\r
800 strcat_s(szSymPath, nSymPathLen, ";");
\r
803 strcat_s(szSymPath, nSymPathLen, ".;");
\r
805 const size_t nTempLen = 1024;
\r
806 char szTemp[nTempLen];
\r
807 // Now add the current directory:
\r
808 if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
\r
810 szTemp[nTempLen-1] = 0;
\r
811 strcat_s(szSymPath, nSymPathLen, szTemp);
\r
812 strcat_s(szSymPath, nSymPathLen, ";");
\r
815 // Now add the path for the main-module:
\r
816 if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
\r
818 szTemp[nTempLen-1] = 0;
\r
819 for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p)
\r
821 // locate the rightmost path separator
\r
822 if ( (*p == '\\') || (*p == '/') || (*p == ':') )
\r
827 } // for (search for path separator...)
\r
828 if (strlen(szTemp) > 0)
\r
830 strcat_s(szSymPath, nSymPathLen, szTemp);
\r
831 strcat_s(szSymPath, nSymPathLen, ";");
\r
834 if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
\r
836 szTemp[nTempLen-1] = 0;
\r
837 strcat_s(szSymPath, nSymPathLen, szTemp);
\r
838 strcat_s(szSymPath, nSymPathLen, ";");
\r
840 if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
\r
842 szTemp[nTempLen-1] = 0;
\r
843 strcat_s(szSymPath, nSymPathLen, szTemp);
\r
844 strcat_s(szSymPath, nSymPathLen, ";");
\r
846 if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
\r
848 szTemp[nTempLen-1] = 0;
\r
849 strcat_s(szSymPath, nSymPathLen, szTemp);
\r
850 strcat_s(szSymPath, nSymPathLen, ";");
\r
851 // also add the "system32"-directory:
\r
852 strcat_s(szTemp, nTempLen, "\\system32");
\r
853 strcat_s(szSymPath, nSymPathLen, szTemp);
\r
854 strcat_s(szSymPath, nSymPathLen, ";");
\r
857 if ( (this->m_options & SymBuildPath) != 0)
\r
859 if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
\r
861 szTemp[nTempLen-1] = 0;
\r
862 strcat_s(szSymPath, nSymPathLen, "SRV*");
\r
863 strcat_s(szSymPath, nSymPathLen, szTemp);
\r
864 strcat_s(szSymPath, nSymPathLen, "\\websymbols");
\r
865 strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
\r
868 strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
\r
872 // First Init the whole stuff...
\r
873 BOOL bRet = this->m_sw->Init(szSymPath);
\r
874 if (szSymPath != NULL) free(szSymPath); szSymPath = NULL;
\r
877 this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
\r
878 SetLastError(ERROR_DLL_INIT_FAILED);
\r
882 bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);
\r
884 m_modulesLoaded = TRUE;
\r
889 // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
\r
890 // This has to be done due to a problem with the "hProcess"-parameter in x64...
\r
891 // Because this class is in no case multi-threading-enabled (because of the limitations
\r
892 // of dbghelp.dll) it is "safe" to use a static-variable
\r
893 static stack_walker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;
\r
894 static LPVOID s_readMemoryFunction_UserData = NULL;
\r
896 BOOL stack_walker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData)
\r
899 CallstackEntry csEntry;
\r
900 IMAGEHLP_SYMBOL64 *pSym = NULL;
\r
901 stack_walkerInternal::IMAGEHLP_MODULE64_V2 Module;
\r
902 IMAGEHLP_LINE64 Line;
\r
905 if (m_modulesLoaded == FALSE)
\r
906 this->LoadModules(); // ignore the result...
\r
908 if (this->m_sw->m_hDbhHelp == NULL)
\r
910 SetLastError(ERROR_DLL_INIT_FAILED);
\r
914 s_readMemoryFunction = readMemoryFunction;
\r
915 s_readMemoryFunction_UserData = pUserData;
\r
917 if (context == NULL)
\r
919 // If no context is provided, capture the context
\r
920 if (hThread == GetCurrentThread())
\r
922 GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS);
\r
926 SuspendThread(hThread);
\r
927 memset(&c, 0, sizeof(CONTEXT));
\r
928 c.ContextFlags = USED_CONTEXT_FLAGS;
\r
929 if (GetThreadContext(hThread, &c) == FALSE)
\r
931 ResumeThread(hThread);
\r
939 // init STACKFRAME for first call
\r
940 STACKFRAME64 s; // in/out stackframe
\r
941 memset(&s, 0, sizeof(s));
\r
944 // normally, call ImageNtHeader() and use machine info from PE header
\r
945 imageType = IMAGE_FILE_MACHINE_I386;
\r
946 s.AddrPC.Offset = c.Eip;
\r
947 s.AddrPC.Mode = AddrModeFlat;
\r
948 s.AddrFrame.Offset = c.Ebp;
\r
949 s.AddrFrame.Mode = AddrModeFlat;
\r
950 s.AddrStack.Offset = c.Esp;
\r
951 s.AddrStack.Mode = AddrModeFlat;
\r
953 imageType = IMAGE_FILE_MACHINE_AMD64;
\r
954 s.AddrPC.Offset = c.Rip;
\r
955 s.AddrPC.Mode = AddrModeFlat;
\r
956 s.AddrFrame.Offset = c.Rsp;
\r
957 s.AddrFrame.Mode = AddrModeFlat;
\r
958 s.AddrStack.Offset = c.Rsp;
\r
959 s.AddrStack.Mode = AddrModeFlat;
\r
961 imageType = IMAGE_FILE_MACHINE_IA64;
\r
962 s.AddrPC.Offset = c.StIIP;
\r
963 s.AddrPC.Mode = AddrModeFlat;
\r
964 s.AddrFrame.Offset = c.IntSp;
\r
965 s.AddrFrame.Mode = AddrModeFlat;
\r
966 s.AddrBStore.Offset = c.RsBSP;
\r
967 s.AddrBStore.Mode = AddrModeFlat;
\r
968 s.AddrStack.Offset = c.IntSp;
\r
969 s.AddrStack.Mode = AddrModeFlat;
\r
971 #error "Platform not supported!"
\r
974 pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
\r
975 if (!pSym) goto cleanup; // not enough memory...
\r
976 memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
\r
977 pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
\r
978 pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
\r
980 memset(&Line, 0, sizeof(Line));
\r
981 Line.SizeOfStruct = sizeof(Line);
\r
983 memset(&Module, 0, sizeof(Module));
\r
984 Module.SizeOfStruct = sizeof(Module);
\r
986 for (frameNum = 0; ; ++frameNum )
\r
988 // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
\r
989 // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
\r
990 // assume that either you are done, or that the stack is so hosed that the next
\r
991 // deeper frame could not be found.
\r
992 // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
\r
993 if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) )
\r
995 this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset);
\r
999 csEntry.offset = s.AddrPC.Offset;
\r
1000 csEntry.name[0] = 0;
\r
1001 csEntry.undName[0] = 0;
\r
1002 csEntry.undFullName[0] = 0;
\r
1003 csEntry.offsetFromSmybol = 0;
\r
1004 csEntry.offsetFromLine = 0;
\r
1005 csEntry.lineFileName[0] = 0;
\r
1006 csEntry.lineNumber = 0;
\r
1007 csEntry.loadedImageName[0] = 0;
\r
1008 csEntry.moduleName[0] = 0;
\r
1009 if (s.AddrPC.Offset == s.AddrReturn.Offset)
\r
1011 this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
\r
1014 if (s.AddrPC.Offset != 0)
\r
1016 // we seem to have a valid PC
\r
1017 // show procedure info (SymGetSymFromAddr64())
\r
1018 if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE)
\r
1020 // TODO: Mache dies sicher...!
\r
1021 strcpy_s(csEntry.name, pSym->Name);
\r
1022 // UnDecorateSymbolName()
\r
1023 this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY );
\r
1024 this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE );
\r
1028 this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
\r
1031 // show line number info, NT5.0-method (SymGetLineFromAddr64())
\r
1032 if (this->m_sw->pSGLFA != NULL )
\r
1033 { // yes, we have SymGetLineFromAddr64()
\r
1034 if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE)
\r
1036 csEntry.lineNumber = Line.LineNumber;
\r
1037 // TODO: Mache dies sicher...!
\r
1038 strcpy_s(csEntry.lineFileName, Line.FileName);
\r
1042 this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
\r
1044 } // yes, we have SymGetLineFromAddr64()
\r
1046 // show module info (SymGetModuleInfo64())
\r
1047 if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE)
\r
1048 { // got module info OK
\r
1049 switch ( Module.SymType )
\r
1052 csEntry.symTypeString = "-nosymbols-";
\r
1055 csEntry.symTypeString = "COFF";
\r
1058 csEntry.symTypeString = "CV";
\r
1061 csEntry.symTypeString = "PDB";
\r
1064 csEntry.symTypeString = "-exported-";
\r
1067 csEntry.symTypeString = "-deferred-";
\r
1070 csEntry.symTypeString = "SYM";
\r
1072 #if API_VERSION_NUMBER >= 9
\r
1074 csEntry.symTypeString = "DIA";
\r
1077 case 8: //SymVirtual:
\r
1078 csEntry.symTypeString = "Virtual";
\r
1081 //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
\r
1082 csEntry.symTypeString = NULL;
\r
1086 // TODO: Mache dies sicher...!
\r
1087 strcpy_s(csEntry.moduleName, Module.ModuleName);
\r
1088 csEntry.baseOfImage = Module.BaseOfImage;
\r
1089 strcpy_s(csEntry.loadedImageName, Module.LoadedImageName);
\r
1090 } // got module info OK
\r
1093 this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
\r
1095 } // we seem to have a valid PC
\r
1097 CallstackEntryType et = nextEntry;
\r
1098 if (frameNum == 0)
\r
1100 this->OnCallstackEntry(et, csEntry);
\r
1102 if (s.AddrReturn.Offset == 0)
\r
1104 this->OnCallstackEntry(lastEntry, csEntry);
\r
1105 SetLastError(ERROR_SUCCESS);
\r
1108 } // for ( frameNum )
\r
1111 if (pSym) free( pSym );
\r
1113 if (context == NULL)
\r
1114 ResumeThread(hThread);
\r
1119 BOOL __stdcall stack_walker::myReadProcMem(
\r
1121 DWORD64 qwBaseAddress,
\r
1124 LPDWORD lpNumberOfBytesRead
\r
1127 if (s_readMemoryFunction == NULL)
\r
1130 BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st);
\r
1131 *lpNumberOfBytesRead = (DWORD) st;
\r
1132 //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet);
\r
1137 return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
\r
1141 void stack_walker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
\r
1143 CHAR buffer[STACKWALK_MAX_NAMELEN];
\r
1144 if (fileVersion == 0)
\r
1145 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName);
\r
1148 DWORD v4 = (DWORD) fileVersion & 0xFFFF;
\r
1149 DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF;
\r
1150 DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF;
\r
1151 DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF;
\r
1152 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
\r
1157 void stack_walker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
\r
1159 CHAR buffer[STACKWALK_MAX_NAMELEN];
\r
1160 if ( (eType != lastEntry) && (entry.offset != 0) )
\r
1162 if (entry.name[0] == 0)
\r
1163 strcpy_s(entry.name, "(function-name not available)");
\r
1164 if (entry.undName[0] != 0)
\r
1165 strcpy_s(entry.name, entry.undName);
\r
1166 if (entry.undFullName[0] != 0)
\r
1167 strcpy_s(entry.name, entry.undFullName);
\r
1168 if (entry.lineFileName[0] == 0)
\r
1170 strcpy_s(entry.lineFileName, "(filename not available)");
\r
1171 if (entry.moduleName[0] == 0)
\r
1172 strcpy_s(entry.moduleName, "(module-name not available)");
\r
1173 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name);
\r
1176 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name);
\r
1181 void stack_walker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
\r
1183 CHAR buffer[STACKWALK_MAX_NAMELEN];
\r
1184 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
\r
1188 void stack_walker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
\r
1190 CHAR buffer[STACKWALK_MAX_NAMELEN];
\r
1191 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
\r
1193 // Also display the OS-version
\r
1194 #if _MSC_VER <= 1200
\r
1195 OSVERSIONINFOA ver;
\r
1196 ZeroMemory(&ver, sizeof(OSVERSIONINFOA));
\r
1197 ver.dwOSVersionInfoSize = sizeof(ver);
\r
1198 if (GetVersionExA(&ver) != FALSE)
\r
1200 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n",
\r
1201 ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
\r
1202 ver.szCSDVersion);
\r
1206 OSVERSIONINFOEXA ver;
\r
1207 ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
\r
1208 ver.dwOSVersionInfoSize = sizeof(ver);
\r
1209 if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE)
\r
1211 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n",
\r
1212 ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
\r
1213 ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
\r
1219 void stack_walker::OnOutput(LPCSTR buffer)
\r
1221 OutputDebugStringA(buffer);
\r