StackWalker.cpp (53452B)
1 /********************************************************************** 2 * 3 * StackWalker.cpp 4 * https://github.com/JochenKalmbach/StackWalker 5 * 6 * Old location: http://stackwalker.codeplex.com/ 7 * 8 * 9 * History: 10 * 2005-07-27 v1 - First public release on http://www.codeproject.com/ 11 * http://www.codeproject.com/threads/StackWalker.asp 12 * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack 13 * (to simplify the usage) 14 * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL 15 * (should also be enough) 16 * - Changed to compile correctly with the PSDK of VC7.0 17 * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined: 18 * it uses LPSTR instead of LPCSTR as first parameter) 19 * - Added declarations to support VC5/6 without using 'dbghelp.h' 20 * - Added a 'pUserData' member to the ShowCallstack function and the 21 * PReadProcessMemoryRoutine declaration (to pass some user-defined data, 22 * which can be used in the readMemoryFunction-callback) 23 * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default 24 * - Added example for doing an exception-callstack-walking in main.cpp 25 * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268) 26 * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse! 27 * 2008-08-04 v6 - Fixed Bug: Missing LEAK-end-tag 28 * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx 29 * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" 30 * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx 31 * Fixed Bug: Compiling with "/Wall" 32 * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx 33 * Fixed Bug: Now checking SymUseSymSrv 34 * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx 35 * Fixed Bug: Support for recursive function calls 36 * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx 37 * Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32" 38 * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx 39 * Fixed Bug: SymDia is number 7, not 9! 40 * 2008-09-11 v7 For some (undocumented) reason, dbhelp.h is needing a packing of 8! 41 * Thanks to Teajay which reported the bug... 42 * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx 43 * 2008-11-27 v8 Debugging Tools for Windows are now stored in a different directory 44 * Thanks to Luiz Salamon which reported this "bug"... 45 * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx 46 * 2009-04-10 v9 License slightly corrected (<ORGANIZATION> replaced) 47 * 2009-11-01 v10 Moved to http://stackwalker.codeplex.com/ 48 * 2009-11-02 v11 Now try to use IMAGEHLP_MODULE64_V3 if available 49 * 2010-04-15 v12 Added support for VS2010 RTM 50 * 2010-05-25 v13 Now using secure MyStrcCpy. Thanks to luke.simon: 51 * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx 52 * 2013-01-07 v14 Runtime Check Error VS2010 Debug Builds fixed: 53 * http://stackwalker.codeplex.com/workitem/10511 54 * 55 * 56 * LICENSE (http://www.opensource.org/licenses/bsd-license.php) 57 * 58 * Copyright (c) 2005-2013, Jochen Kalmbach 59 * All rights reserved. 60 * 61 * Redistribution and use in source and binary forms, with or without modification, 62 * are permitted provided that the following conditions are met: 63 * 64 * Redistributions of source code must retain the above copyright notice, 65 * this list of conditions and the following disclaimer. 66 * Redistributions in binary form must reproduce the above copyright notice, 67 * this list of conditions and the following disclaimer in the documentation 68 * and/or other materials provided with the distribution. 69 * Neither the name of Jochen Kalmbach nor the names of its contributors may be 70 * used to endorse or promote products derived from this software without 71 * specific prior written permission. 72 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 73 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 74 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 75 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 76 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 77 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 78 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 79 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 80 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 81 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 82 * 83 **********************************************************************/ 84 85 #include "StackWalker.h" 86 87 #include <stdio.h> 88 #include <stdlib.h> 89 #include <tchar.h> 90 #include <windows.h> 91 #pragma comment(lib, "version.lib") // for "VerQueryValue" 92 #pragma warning(disable : 4826) 93 94 95 // If VC7 and later, then use the shipped 'dbghelp.h'-file 96 #pragma pack(push, 8) 97 #if _MSC_VER >= 1300 98 #include <dbghelp.h> 99 #else 100 // inline the important dbghelp.h-declarations... 101 typedef enum 102 { 103 SymNone = 0, 104 SymCoff, 105 SymCv, 106 SymPdb, 107 SymExport, 108 SymDeferred, 109 SymSym, 110 SymDia, 111 SymVirtual, 112 NumSymTypes 113 } SYM_TYPE; 114 typedef struct _IMAGEHLP_LINE64 115 { 116 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64) 117 PVOID Key; // internal 118 DWORD LineNumber; // line number in file 119 PCHAR FileName; // full filename 120 DWORD64 Address; // first instruction of line 121 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; 122 typedef struct _IMAGEHLP_MODULE64 123 { 124 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) 125 DWORD64 BaseOfImage; // base load address of module 126 DWORD ImageSize; // virtual size of the loaded module 127 DWORD TimeDateStamp; // date/time stamp from pe header 128 DWORD CheckSum; // checksum from the pe header 129 DWORD NumSyms; // number of symbols in the symbol table 130 SYM_TYPE SymType; // type of symbols loaded 131 CHAR ModuleName[32]; // module name 132 CHAR ImageName[256]; // image name 133 CHAR LoadedImageName[256]; // symbol file name 134 } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64; 135 typedef struct _IMAGEHLP_SYMBOL64 136 { 137 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64) 138 DWORD64 Address; // virtual address including dll base address 139 DWORD Size; // estimated size of symbol, can be zero 140 DWORD Flags; // info about the symbols, see the SYMF defines 141 DWORD MaxNameLength; // maximum size of symbol name in 'Name' 142 CHAR Name[1]; // symbol name (null terminated string) 143 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; 144 typedef enum 145 { 146 AddrMode1616, 147 AddrMode1632, 148 AddrModeReal, 149 AddrModeFlat 150 } ADDRESS_MODE; 151 typedef struct _tagADDRESS64 152 { 153 DWORD64 Offset; 154 WORD Segment; 155 ADDRESS_MODE Mode; 156 } ADDRESS64, *LPADDRESS64; 157 typedef struct _KDHELP64 158 { 159 DWORD64 Thread; 160 DWORD ThCallbackStack; 161 DWORD ThCallbackBStore; 162 DWORD NextCallback; 163 DWORD FramePointer; 164 DWORD64 KiCallUserMode; 165 DWORD64 KeUserCallbackDispatcher; 166 DWORD64 SystemRangeStart; 167 DWORD64 Reserved[8]; 168 } KDHELP64, *PKDHELP64; 169 typedef struct _tagSTACKFRAME64 170 { 171 ADDRESS64 AddrPC; // program counter 172 ADDRESS64 AddrReturn; // return address 173 ADDRESS64 AddrFrame; // frame pointer 174 ADDRESS64 AddrStack; // stack pointer 175 ADDRESS64 AddrBStore; // backing store pointer 176 PVOID FuncTableEntry; // pointer to pdata/fpo or NULL 177 DWORD64 Params[4]; // possible arguments to the function 178 BOOL Far; // WOW far call 179 BOOL Virtual; // is this a virtual frame? 180 DWORD64 Reserved[3]; 181 KDHELP64 KdHelp; 182 } STACKFRAME64, *LPSTACKFRAME64; 183 typedef BOOL(__stdcall* PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess, 184 DWORD64 qwBaseAddress, 185 PVOID lpBuffer, 186 DWORD nSize, 187 LPDWORD lpNumberOfBytesRead); 188 typedef PVOID(__stdcall* PFUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE hProcess, DWORD64 AddrBase); 189 typedef DWORD64(__stdcall* PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, DWORD64 Address); 190 typedef DWORD64(__stdcall* PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess, 191 HANDLE hThread, 192 LPADDRESS64 lpaddr); 193 194 // clang-format off 195 #define SYMOPT_CASE_INSENSITIVE 0x00000001 196 #define SYMOPT_UNDNAME 0x00000002 197 #define SYMOPT_DEFERRED_LOADS 0x00000004 198 #define SYMOPT_NO_CPP 0x00000008 199 #define SYMOPT_LOAD_LINES 0x00000010 200 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 201 #define SYMOPT_LOAD_ANYTHING 0x00000040 202 #define SYMOPT_IGNORE_CVREC 0x00000080 203 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 204 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 205 #define SYMOPT_EXACT_SYMBOLS 0x00000400 206 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 207 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 208 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 209 #define SYMOPT_PUBLICS_ONLY 0x00004000 210 #define SYMOPT_NO_PUBLICS 0x00008000 211 #define SYMOPT_AUTO_PUBLICS 0x00010000 212 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 213 #define SYMOPT_SECURE 0x00040000 214 #define SYMOPT_DEBUG 0x80000000 215 #define UNDNAME_COMPLETE (0x0000) // Enable full undecoration 216 #define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration; 217 // clang-format on 218 219 #endif // _MSC_VER < 1300 220 #pragma pack(pop) 221 222 // Some missing defines (for VC5/6): 223 #ifndef INVALID_FILE_ATTRIBUTES 224 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 225 #endif 226 227 // secure-CRT_functions are only available starting with VC8 228 #if _MSC_VER < 1400 229 #define strcpy_s(dst, len, src) strcpy(dst, src) 230 #define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src) 231 #define strcat_s(dst, len, src) strcat(dst, src) 232 #define _snprintf_s _snprintf 233 #define _tcscat_s _tcscat 234 #endif 235 236 static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc) 237 { 238 if (nMaxDestSize <= 0) 239 return; 240 strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE); 241 // INFO: _TRUNCATE will ensure that it is null-terminated; 242 // but with older compilers (<1400) it uses "strncpy" and this does not!) 243 szDest[nMaxDestSize - 1] = 0; 244 } // MyStrCpy 245 246 // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL') 247 #define USED_CONTEXT_FLAGS CONTEXT_FULL 248 249 HMODULE StackWalker::LoadDbgHelpLibrary() 250 { 251 HMODULE hModule = NULL; 252 253 // Dynamically load the Entry-Points for dbghelp.dll: 254 // First try to load the newest one from 255 TCHAR szTemp[4096]; 256 // But before we do this, we first check if the ".local" file exists 257 if (GetModuleFileName(NULL, szTemp, 4096) > 0) 258 { 259 _tcscat_s(szTemp, _T(".local")); 260 if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) 261 { 262 // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows" 263 // Ok, first try the new path according to the architecture: 264 #ifdef _M_IX86 265 if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) 266 { 267 _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll")); 268 // now check if the file exists: 269 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) 270 { 271 hModule = LoadLibrary(szTemp); 272 } 273 } 274 #elif _M_X64 275 if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) 276 { 277 _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll")); 278 // now check if the file exists: 279 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) 280 { 281 hModule = LoadLibrary(szTemp); 282 } 283 } 284 #elif _M_IA64 285 if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) 286 { 287 _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll")); 288 // now check if the file exists: 289 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) 290 { 291 hModule = LoadLibrary(szTemp); 292 } 293 } 294 #endif 295 // If still not found, try the old directories... 296 if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) 297 { 298 _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll")); 299 // now check if the file exists: 300 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) 301 { 302 hModule = LoadLibrary(szTemp); 303 } 304 } 305 #if defined _M_X64 || defined _M_IA64 306 // Still not found? Then try to load the (old) 64-Bit version: 307 if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) 308 { 309 _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); 310 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) 311 { 312 hModule = LoadLibrary(szTemp); 313 } 314 } 315 #endif 316 } 317 } 318 if (hModule == NULL) // if not already loaded, try to load a default-one 319 hModule = LoadLibrary(_T("dbghelp.dll")); 320 321 return hModule; 322 } 323 324 class StackWalkerInternal 325 { 326 public: 327 StackWalkerInternal(StackWalker* parent, HANDLE hProcess) 328 { 329 m_parent = parent; 330 m_hDbhHelp = NULL; 331 pSC = NULL; 332 m_hProcess = hProcess; 333 m_szSymPath = NULL; 334 pSFTA = NULL; 335 pSGLFA = NULL; 336 pSGMB = NULL; 337 pSGMI = NULL; 338 pSGO = NULL; 339 pSGSFA = NULL; 340 pSI = NULL; 341 pSLM = NULL; 342 pSSO = NULL; 343 pSW = NULL; 344 pUDSN = NULL; 345 pSGSP = NULL; 346 } 347 ~StackWalkerInternal() 348 { 349 if (pSC != NULL) 350 pSC(m_hProcess); // SymCleanup 351 if (m_hDbhHelp != NULL) 352 FreeLibrary(m_hDbhHelp); 353 m_hDbhHelp = NULL; 354 m_parent = NULL; 355 if (m_szSymPath != NULL) 356 free(m_szSymPath); 357 m_szSymPath = NULL; 358 } 359 BOOL Init(LPCSTR szSymPath) 360 { 361 if (m_parent == NULL) 362 return FALSE; 363 364 m_hDbhHelp = StackWalker::LoadDbgHelpLibrary(); 365 if (m_hDbhHelp == NULL) 366 return FALSE; 367 pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize"); 368 pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup"); 369 370 pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64"); 371 pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions"); 372 pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions"); 373 374 pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64"); 375 pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64"); 376 pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64"); 377 pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64"); 378 pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64"); 379 pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName"); 380 pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64"); 381 pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath"); 382 383 if (pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || pSGO == NULL || 384 pSGSFA == NULL || pSI == NULL || pSSO == NULL || pSW == NULL || pUDSN == NULL || 385 pSLM == NULL) 386 { 387 FreeLibrary(m_hDbhHelp); 388 m_hDbhHelp = NULL; 389 pSC = NULL; 390 return FALSE; 391 } 392 393 // SymInitialize 394 if (szSymPath != NULL) 395 m_szSymPath = _strdup(szSymPath); 396 if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) 397 this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0); 398 399 DWORD symOptions = this->pSGO(); // SymGetOptions 400 symOptions |= SYMOPT_LOAD_LINES; 401 symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; 402 //symOptions |= SYMOPT_NO_PROMPTS; 403 // SymSetOptions 404 symOptions = this->pSSO(symOptions); 405 406 char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0}; 407 if (this->pSGSP != NULL) 408 { 409 if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) 410 this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); 411 } 412 char szUserName[1024] = {0}; 413 DWORD dwSize = 1024; 414 GetUserNameA(szUserName, &dwSize); 415 this->m_parent->OnSymInit(buf, symOptions, szUserName); 416 417 return TRUE; 418 } 419 420 StackWalker* m_parent; 421 422 HMODULE m_hDbhHelp; 423 HANDLE m_hProcess; 424 LPSTR m_szSymPath; 425 426 #pragma pack(push, 8) 427 struct IMAGEHLP_MODULE64_V3 428 { 429 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) 430 DWORD64 BaseOfImage; // base load address of module 431 DWORD ImageSize; // virtual size of the loaded module 432 DWORD TimeDateStamp; // date/time stamp from pe header 433 DWORD CheckSum; // checksum from the pe header 434 DWORD NumSyms; // number of symbols in the symbol table 435 SYM_TYPE SymType; // type of symbols loaded 436 CHAR ModuleName[32]; // module name 437 CHAR ImageName[256]; // image name 438 CHAR LoadedImageName[256]; // symbol file name 439 // new elements: 07-Jun-2002 440 CHAR LoadedPdbName[256]; // pdb file name 441 DWORD CVSig; // Signature of the CV record in the debug directories 442 CHAR CVData[MAX_PATH * 3]; // Contents of the CV record 443 DWORD PdbSig; // Signature of PDB 444 GUID PdbSig70; // Signature of PDB (VC 7 and up) 445 DWORD PdbAge; // DBI age of pdb 446 BOOL PdbUnmatched; // loaded an unmatched pdb 447 BOOL DbgUnmatched; // loaded an unmatched dbg 448 BOOL LineNumbers; // we have line number information 449 BOOL GlobalSymbols; // we have internal symbol information 450 BOOL TypeInfo; // we have type information 451 // new elements: 17-Dec-2003 452 BOOL SourceIndexed; // pdb supports source server 453 BOOL Publics; // contains public symbols 454 }; 455 456 struct IMAGEHLP_MODULE64_V2 457 { 458 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) 459 DWORD64 BaseOfImage; // base load address of module 460 DWORD ImageSize; // virtual size of the loaded module 461 DWORD TimeDateStamp; // date/time stamp from pe header 462 DWORD CheckSum; // checksum from the pe header 463 DWORD NumSyms; // number of symbols in the symbol table 464 SYM_TYPE SymType; // type of symbols loaded 465 CHAR ModuleName[32]; // module name 466 CHAR ImageName[256]; // image name 467 CHAR LoadedImageName[256]; // symbol file name 468 }; 469 #pragma pack(pop) 470 471 // SymCleanup() 472 typedef BOOL(__stdcall* tSC)(IN HANDLE hProcess); 473 tSC pSC; 474 475 // SymFunctionTableAccess64() 476 typedef PVOID(__stdcall* tSFTA)(HANDLE hProcess, DWORD64 AddrBase); 477 tSFTA pSFTA; 478 479 // SymGetLineFromAddr64() 480 typedef BOOL(__stdcall* tSGLFA)(IN HANDLE hProcess, 481 IN DWORD64 dwAddr, 482 OUT PDWORD pdwDisplacement, 483 OUT PIMAGEHLP_LINE64 Line); 484 tSGLFA pSGLFA; 485 486 // SymGetModuleBase64() 487 typedef DWORD64(__stdcall* tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr); 488 tSGMB pSGMB; 489 490 // SymGetModuleInfo64() 491 typedef BOOL(__stdcall* tSGMI)(IN HANDLE hProcess, 492 IN DWORD64 dwAddr, 493 OUT IMAGEHLP_MODULE64_V3* ModuleInfo); 494 tSGMI pSGMI; 495 496 // SymGetOptions() 497 typedef DWORD(__stdcall* tSGO)(VOID); 498 tSGO pSGO; 499 500 // SymGetSymFromAddr64() 501 typedef BOOL(__stdcall* tSGSFA)(IN HANDLE hProcess, 502 IN DWORD64 dwAddr, 503 OUT PDWORD64 pdwDisplacement, 504 OUT PIMAGEHLP_SYMBOL64 Symbol); 505 tSGSFA pSGSFA; 506 507 // SymInitialize() 508 typedef BOOL(__stdcall* tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess); 509 tSI pSI; 510 511 // SymLoadModule64() 512 typedef DWORD64(__stdcall* tSLM)(IN HANDLE hProcess, 513 IN HANDLE hFile, 514 IN PSTR ImageName, 515 IN PSTR ModuleName, 516 IN DWORD64 BaseOfDll, 517 IN DWORD SizeOfDll); 518 tSLM pSLM; 519 520 // SymSetOptions() 521 typedef DWORD(__stdcall* tSSO)(IN DWORD SymOptions); 522 tSSO pSSO; 523 524 // StackWalk64() 525 typedef BOOL(__stdcall* tSW)(DWORD MachineType, 526 HANDLE hProcess, 527 HANDLE hThread, 528 LPSTACKFRAME64 StackFrame, 529 PVOID ContextRecord, 530 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, 531 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, 532 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, 533 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); 534 tSW pSW; 535 536 // UnDecorateSymbolName() 537 typedef DWORD(__stdcall WINAPI* tUDSN)(PCSTR DecoratedName, 538 PSTR UnDecoratedName, 539 DWORD UndecoratedLength, 540 DWORD Flags); 541 tUDSN pUDSN; 542 543 typedef BOOL(__stdcall WINAPI* tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); 544 tSGSP pSGSP; 545 546 private: 547 // **************************************** ToolHelp32 ************************ 548 #define MAX_MODULE_NAME32 255 549 #define TH32CS_SNAPMODULE 0x00000008 550 #pragma pack(push, 8) 551 typedef struct tagMODULEENTRY32 552 { 553 DWORD dwSize; 554 DWORD th32ModuleID; // This module 555 DWORD th32ProcessID; // owning process 556 DWORD GlblcntUsage; // Global usage count on the module 557 DWORD ProccntUsage; // Module usage count in th32ProcessID's context 558 BYTE* modBaseAddr; // Base address of module in th32ProcessID's context 559 DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr 560 HMODULE hModule; // The hModule of this module in th32ProcessID's context 561 char szModule[MAX_MODULE_NAME32 + 1]; 562 char szExePath[MAX_PATH]; 563 } MODULEENTRY32; 564 typedef MODULEENTRY32* PMODULEENTRY32; 565 typedef MODULEENTRY32* LPMODULEENTRY32; 566 #pragma pack(pop) 567 568 BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) 569 { 570 // CreateToolhelp32Snapshot() 571 typedef HANDLE(__stdcall * tCT32S)(DWORD dwFlags, DWORD th32ProcessID); 572 // Module32First() 573 typedef BOOL(__stdcall * tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); 574 // Module32Next() 575 typedef BOOL(__stdcall * tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); 576 577 // try both dlls... 578 const TCHAR* dllname[] = {_T("kernel32.dll"), _T("tlhelp32.dll")}; 579 HINSTANCE hToolhelp = NULL; 580 tCT32S pCT32S = NULL; 581 tM32F pM32F = NULL; 582 tM32N pM32N = NULL; 583 584 HANDLE hSnap; 585 MODULEENTRY32 me; 586 me.dwSize = sizeof(me); 587 BOOL keepGoing; 588 size_t i; 589 590 for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++) 591 { 592 hToolhelp = LoadLibrary(dllname[i]); 593 if (hToolhelp == NULL) 594 continue; 595 pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); 596 pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First"); 597 pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next"); 598 if ((pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL)) 599 break; // found the functions! 600 FreeLibrary(hToolhelp); 601 hToolhelp = NULL; 602 } 603 604 if (hToolhelp == NULL) 605 return FALSE; 606 607 hSnap = pCT32S(TH32CS_SNAPMODULE, pid); 608 if (hSnap == (HANDLE)-1) 609 { 610 FreeLibrary(hToolhelp); 611 return FALSE; 612 } 613 614 keepGoing = !!pM32F(hSnap, &me); 615 int cnt = 0; 616 while (keepGoing) 617 { 618 this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr, 619 me.modBaseSize); 620 cnt++; 621 keepGoing = !!pM32N(hSnap, &me); 622 } 623 CloseHandle(hSnap); 624 FreeLibrary(hToolhelp); 625 if (cnt <= 0) 626 return FALSE; 627 return TRUE; 628 } // GetModuleListTH32 629 630 // **************************************** PSAPI ************************ 631 typedef struct _MODULEINFO 632 { 633 LPVOID lpBaseOfDll; 634 DWORD SizeOfImage; 635 LPVOID EntryPoint; 636 } MODULEINFO, *LPMODULEINFO; 637 638 BOOL GetModuleListPSAPI(HANDLE hProcess) 639 { 640 // EnumProcessModules() 641 typedef BOOL(__stdcall * tEPM)(HANDLE hProcess, HMODULE * lphModule, DWORD cb, 642 LPDWORD lpcbNeeded); 643 // GetModuleFileNameEx() 644 typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, 645 DWORD nSize); 646 // GetModuleBaseName() 647 typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, 648 DWORD nSize); 649 // GetModuleInformation() 650 typedef BOOL(__stdcall * tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize); 651 652 HINSTANCE hPsapi; 653 tEPM pEPM; 654 tGMFNE pGMFNE; 655 tGMBN pGMBN; 656 tGMI pGMI; 657 658 DWORD i; 659 //ModuleEntry e; 660 DWORD cbNeeded; 661 MODULEINFO mi; 662 HMODULE* hMods = NULL; 663 char* tt = NULL; 664 char* tt2 = NULL; 665 const SIZE_T TTBUFLEN = 8096; 666 int cnt = 0; 667 668 hPsapi = LoadLibrary(_T("psapi.dll")); 669 if (hPsapi == NULL) 670 return FALSE; 671 672 pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules"); 673 pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA"); 674 pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA"); 675 pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation"); 676 if ((pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL)) 677 { 678 // we couldn't find all functions 679 FreeLibrary(hPsapi); 680 return FALSE; 681 } 682 683 hMods = (HMODULE*)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE))); 684 tt = (char*)malloc(sizeof(char) * TTBUFLEN); 685 tt2 = (char*)malloc(sizeof(char) * TTBUFLEN); 686 if ((hMods == NULL) || (tt == NULL) || (tt2 == NULL)) 687 goto cleanup; 688 689 if (!pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded)) 690 { 691 //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); 692 goto cleanup; 693 } 694 695 if (cbNeeded > TTBUFLEN) 696 { 697 //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); 698 goto cleanup; 699 } 700 701 for (i = 0; i < cbNeeded / sizeof(hMods[0]); i++) 702 { 703 // base address, size 704 pGMI(hProcess, hMods[i], &mi, sizeof(mi)); 705 // image file name 706 tt[0] = 0; 707 pGMFNE(hProcess, hMods[i], tt, TTBUFLEN); 708 // module name 709 tt2[0] = 0; 710 pGMBN(hProcess, hMods[i], tt2, TTBUFLEN); 711 712 DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage); 713 if (dwRes != ERROR_SUCCESS) 714 this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0); 715 cnt++; 716 } 717 718 cleanup: 719 if (hPsapi != NULL) 720 FreeLibrary(hPsapi); 721 if (tt2 != NULL) 722 free(tt2); 723 if (tt != NULL) 724 free(tt); 725 if (hMods != NULL) 726 free(hMods); 727 728 return cnt != 0; 729 } // GetModuleListPSAPI 730 731 DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) 732 { 733 CHAR* szImg = _strdup(img); 734 CHAR* szMod = _strdup(mod); 735 DWORD result = ERROR_SUCCESS; 736 if ((szImg == NULL) || (szMod == NULL)) 737 result = ERROR_NOT_ENOUGH_MEMORY; 738 else 739 { 740 if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0) 741 result = GetLastError(); 742 } 743 ULONGLONG fileVersion = 0; 744 if ((m_parent != NULL) && (szImg != NULL)) 745 { 746 // try to retrieve the file-version: 747 if ((this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) 748 { 749 VS_FIXEDFILEINFO* fInfo = NULL; 750 DWORD dwHandle; 751 DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); 752 if (dwSize > 0) 753 { 754 LPVOID vData = malloc(dwSize); 755 if (vData != NULL) 756 { 757 if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) 758 { 759 UINT len; 760 TCHAR szSubBlock[] = _T("\\"); 761 if (VerQueryValue(vData, szSubBlock, (LPVOID*)&fInfo, &len) == 0) 762 fInfo = NULL; 763 else 764 { 765 fileVersion = 766 ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); 767 } 768 } 769 free(vData); 770 } 771 } 772 } 773 774 // Retrieve some additional-infos about the module 775 IMAGEHLP_MODULE64_V3 Module; 776 const char* szSymType = "-unknown-"; 777 if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) 778 { 779 switch (Module.SymType) 780 { 781 case SymNone: 782 szSymType = "-nosymbols-"; 783 break; 784 case SymCoff: // 1 785 szSymType = "COFF"; 786 break; 787 case SymCv: // 2 788 szSymType = "CV"; 789 break; 790 case SymPdb: // 3 791 szSymType = "PDB"; 792 break; 793 case SymExport: // 4 794 szSymType = "-exported-"; 795 break; 796 case SymDeferred: // 5 797 szSymType = "-deferred-"; 798 break; 799 case SymSym: // 6 800 szSymType = "SYM"; 801 break; 802 case 7: // SymDia: 803 szSymType = "DIA"; 804 break; 805 case 8: //SymVirtual: 806 szSymType = "Virtual"; 807 break; 808 default: 809 break; 810 } 811 } 812 LPCSTR pdbName = Module.LoadedImageName; 813 if (Module.LoadedPdbName[0] != 0) 814 pdbName = Module.LoadedPdbName; 815 this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName, 816 fileVersion); 817 } 818 if (szImg != NULL) 819 free(szImg); 820 if (szMod != NULL) 821 free(szMod); 822 return result; 823 } 824 825 public: 826 BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) 827 { 828 // first try toolhelp32 829 if (GetModuleListTH32(hProcess, dwProcessId)) 830 return true; 831 // then try psapi 832 return GetModuleListPSAPI(hProcess); 833 } 834 835 BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3* pModuleInfo) 836 { 837 memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3)); 838 if (this->pSGMI == NULL) 839 { 840 SetLastError(ERROR_DLL_INIT_FAILED); 841 return FALSE; 842 } 843 // First try to use the larger ModuleInfo-Structure 844 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); 845 void* pData = malloc( 846 4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... 847 if (pData == NULL) 848 { 849 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 850 return FALSE; 851 } 852 memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V3)); 853 static bool s_useV3Version = true; 854 if (s_useV3Version) 855 { 856 if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE) 857 { 858 // only copy as much memory as is reserved... 859 memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3)); 860 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); 861 free(pData); 862 return TRUE; 863 } 864 s_useV3Version = false; // to prevent unnecessary calls with the larger struct... 865 } 866 867 // could not retrieve the bigger structure, try with the smaller one (as defined in VC7.1)... 868 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); 869 memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2)); 870 if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE) 871 { 872 // only copy as much memory as is reserved... 873 memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2)); 874 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); 875 free(pData); 876 return TRUE; 877 } 878 free(pData); 879 SetLastError(ERROR_DLL_INIT_FAILED); 880 return FALSE; 881 } 882 }; 883 884 // ############################################################# 885 StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess) 886 { 887 this->m_options = OptionsAll; 888 this->m_modulesLoaded = FALSE; 889 this->m_hProcess = hProcess; 890 this->m_sw = new StackWalkerInternal(this, this->m_hProcess); 891 this->m_dwProcessId = dwProcessId; 892 this->m_szSymPath = NULL; 893 this->m_MaxRecursionCount = 1000; 894 } 895 StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) 896 { 897 this->m_options = options; 898 this->m_modulesLoaded = FALSE; 899 this->m_hProcess = hProcess; 900 this->m_sw = new StackWalkerInternal(this, this->m_hProcess); 901 this->m_dwProcessId = dwProcessId; 902 if (szSymPath != NULL) 903 { 904 this->m_szSymPath = _strdup(szSymPath); 905 this->m_options |= SymBuildPath; 906 } 907 else 908 this->m_szSymPath = NULL; 909 this->m_MaxRecursionCount = 1000; 910 } 911 912 StackWalker::~StackWalker() 913 { 914 if (m_szSymPath != NULL) 915 free(m_szSymPath); 916 m_szSymPath = NULL; 917 if (this->m_sw != NULL) 918 delete this->m_sw; 919 this->m_sw = NULL; 920 } 921 922 BOOL StackWalker::LoadModules() 923 { 924 if (this->m_sw == NULL) 925 { 926 SetLastError(ERROR_DLL_INIT_FAILED); 927 return FALSE; 928 } 929 if (m_modulesLoaded != FALSE) 930 return TRUE; 931 932 // Build the sym-path: 933 char* szSymPath = NULL; 934 if ((this->m_options & SymBuildPath) != 0) 935 { 936 const size_t nSymPathLen = 4096; 937 szSymPath = (char*)malloc(nSymPathLen); 938 if (szSymPath == NULL) 939 { 940 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 941 return FALSE; 942 } 943 szSymPath[0] = 0; 944 // Now first add the (optional) provided sympath: 945 if (this->m_szSymPath != NULL) 946 { 947 strcat_s(szSymPath, nSymPathLen, this->m_szSymPath); 948 strcat_s(szSymPath, nSymPathLen, ";"); 949 } 950 951 strcat_s(szSymPath, nSymPathLen, ".;"); 952 953 const size_t nTempLen = 1024; 954 char szTemp[nTempLen]; 955 // Now add the current directory: 956 if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) 957 { 958 szTemp[nTempLen - 1] = 0; 959 strcat_s(szSymPath, nSymPathLen, szTemp); 960 strcat_s(szSymPath, nSymPathLen, ";"); 961 } 962 963 // Now add the path for the main-module: 964 if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) 965 { 966 szTemp[nTempLen - 1] = 0; 967 for (char* p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p) 968 { 969 // locate the rightmost path separator 970 if ((*p == '\\') || (*p == '/') || (*p == ':')) 971 { 972 *p = 0; 973 break; 974 } 975 } // for (search for path separator...) 976 if (strlen(szTemp) > 0) 977 { 978 strcat_s(szSymPath, nSymPathLen, szTemp); 979 strcat_s(szSymPath, nSymPathLen, ";"); 980 } 981 } 982 if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) 983 { 984 szTemp[nTempLen - 1] = 0; 985 strcat_s(szSymPath, nSymPathLen, szTemp); 986 strcat_s(szSymPath, nSymPathLen, ";"); 987 } 988 if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) 989 { 990 szTemp[nTempLen - 1] = 0; 991 strcat_s(szSymPath, nSymPathLen, szTemp); 992 strcat_s(szSymPath, nSymPathLen, ";"); 993 } 994 if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) 995 { 996 szTemp[nTempLen - 1] = 0; 997 strcat_s(szSymPath, nSymPathLen, szTemp); 998 strcat_s(szSymPath, nSymPathLen, ";"); 999 // also add the "system32"-directory: 1000 strcat_s(szTemp, nTempLen, "\\system32"); 1001 strcat_s(szSymPath, nSymPathLen, szTemp); 1002 strcat_s(szSymPath, nSymPathLen, ";"); 1003 } 1004 1005 if ((this->m_options & SymUseSymSrv) != 0) 1006 { 1007 if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) 1008 { 1009 szTemp[nTempLen - 1] = 0; 1010 strcat_s(szSymPath, nSymPathLen, "SRV*"); 1011 strcat_s(szSymPath, nSymPathLen, szTemp); 1012 strcat_s(szSymPath, nSymPathLen, "\\websymbols"); 1013 strcat_s(szSymPath, nSymPathLen, "*https://msdl.microsoft.com/download/symbols;"); 1014 } 1015 else 1016 strcat_s(szSymPath, nSymPathLen, 1017 "SRV*c:\\websymbols*https://msdl.microsoft.com/download/symbols;"); 1018 } 1019 } // if SymBuildPath 1020 1021 // First Init the whole stuff... 1022 BOOL bRet = this->m_sw->Init(szSymPath); 1023 if (szSymPath != NULL) 1024 free(szSymPath); 1025 szSymPath = NULL; 1026 if (bRet == FALSE) 1027 { 1028 this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); 1029 SetLastError(ERROR_DLL_INIT_FAILED); 1030 return FALSE; 1031 } 1032 1033 bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId); 1034 if (bRet != FALSE) 1035 m_modulesLoaded = TRUE; 1036 return bRet; 1037 } 1038 1039 // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction 1040 // This has to be done due to a problem with the "hProcess"-parameter in x64... 1041 // Because this class is in no case multi-threading-enabled (because of the limitations 1042 // of dbghelp.dll) it is "safe" to use a static-variable 1043 static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; 1044 static LPVOID s_readMemoryFunction_UserData = NULL; 1045 1046 BOOL StackWalker::ShowCallstack(HANDLE hThread, 1047 const CONTEXT* context, 1048 PReadProcessMemoryRoutine readMemoryFunction, 1049 LPVOID pUserData) 1050 { 1051 CONTEXT c; 1052 CallstackEntry csEntry; 1053 IMAGEHLP_SYMBOL64* pSym = NULL; 1054 StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module; 1055 IMAGEHLP_LINE64 Line; 1056 int frameNum; 1057 bool bLastEntryCalled = true; 1058 int curRecursionCount = 0; 1059 1060 if (m_modulesLoaded == FALSE) 1061 this->LoadModules(); // ignore the result... 1062 1063 if (this->m_sw->m_hDbhHelp == NULL) 1064 { 1065 SetLastError(ERROR_DLL_INIT_FAILED); 1066 return FALSE; 1067 } 1068 1069 s_readMemoryFunction = readMemoryFunction; 1070 s_readMemoryFunction_UserData = pUserData; 1071 1072 if (context == NULL) 1073 { 1074 // If no context is provided, capture the context 1075 // See: https://stackwalker.codeplex.com/discussions/446958 1076 #if _WIN32_WINNT <= 0x0501 1077 // If we need to support XP, we need to use the "old way", because "GetThreadId" is not available! 1078 if (hThread == GetCurrentThread()) 1079 #else 1080 if (GetThreadId(hThread) == GetCurrentThreadId()) 1081 #endif 1082 { 1083 GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS); 1084 } 1085 else 1086 { 1087 SuspendThread(hThread); 1088 memset(&c, 0, sizeof(CONTEXT)); 1089 c.ContextFlags = USED_CONTEXT_FLAGS; 1090 1091 // TODO: Detect if you want to get a thread context of a different process, which is running a different processor architecture... 1092 // This does only work if we are x64 and the target process is x64 or x86; 1093 // It cannot work, if this process is x64 and the target process is x64... this is not supported... 1094 // See also: http://www.howzatt.demon.co.uk/articles/DebuggingInWin64.html 1095 if (GetThreadContext(hThread, &c) == FALSE) 1096 { 1097 ResumeThread(hThread); 1098 return FALSE; 1099 } 1100 } 1101 } 1102 else 1103 c = *context; 1104 1105 // init STACKFRAME for first call 1106 STACKFRAME64 s; // in/out stackframe 1107 memset(&s, 0, sizeof(s)); 1108 DWORD imageType; 1109 #ifdef _M_IX86 1110 // normally, call ImageNtHeader() and use machine info from PE header 1111 imageType = IMAGE_FILE_MACHINE_I386; 1112 s.AddrPC.Offset = c.Eip; 1113 s.AddrPC.Mode = AddrModeFlat; 1114 s.AddrFrame.Offset = c.Ebp; 1115 s.AddrFrame.Mode = AddrModeFlat; 1116 s.AddrStack.Offset = c.Esp; 1117 s.AddrStack.Mode = AddrModeFlat; 1118 #elif _M_X64 1119 imageType = IMAGE_FILE_MACHINE_AMD64; 1120 s.AddrPC.Offset = c.Rip; 1121 s.AddrPC.Mode = AddrModeFlat; 1122 s.AddrFrame.Offset = c.Rsp; 1123 s.AddrFrame.Mode = AddrModeFlat; 1124 s.AddrStack.Offset = c.Rsp; 1125 s.AddrStack.Mode = AddrModeFlat; 1126 #elif _M_IA64 1127 imageType = IMAGE_FILE_MACHINE_IA64; 1128 s.AddrPC.Offset = c.StIIP; 1129 s.AddrPC.Mode = AddrModeFlat; 1130 s.AddrFrame.Offset = c.IntSp; 1131 s.AddrFrame.Mode = AddrModeFlat; 1132 s.AddrBStore.Offset = c.RsBSP; 1133 s.AddrBStore.Mode = AddrModeFlat; 1134 s.AddrStack.Offset = c.IntSp; 1135 s.AddrStack.Mode = AddrModeFlat; 1136 #elif _M_ARM64 1137 imageType = IMAGE_FILE_MACHINE_ARM64; 1138 s.AddrPC.Offset = c.Pc; 1139 s.AddrPC.Mode = AddrModeFlat; 1140 s.AddrFrame.Offset = c.Fp; 1141 s.AddrFrame.Mode = AddrModeFlat; 1142 s.AddrStack.Offset = c.Sp; 1143 s.AddrStack.Mode = AddrModeFlat; 1144 #else 1145 #error "Platform not supported!" 1146 #endif 1147 1148 pSym = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); 1149 if (!pSym) 1150 goto cleanup; // not enough memory... 1151 memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); 1152 pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); 1153 pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; 1154 1155 memset(&Line, 0, sizeof(Line)); 1156 Line.SizeOfStruct = sizeof(Line); 1157 1158 memset(&Module, 0, sizeof(Module)); 1159 Module.SizeOfStruct = sizeof(Module); 1160 1161 for (frameNum = 0;; ++frameNum) 1162 { 1163 // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) 1164 // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can 1165 // assume that either you are done, or that the stack is so hosed that the next 1166 // deeper frame could not be found. 1167 // CONTEXT need not to be supplied if imageTyp is IMAGE_FILE_MACHINE_I386! 1168 if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, 1169 this->m_sw->pSFTA, this->m_sw->pSGMB, NULL)) 1170 { 1171 // INFO: "StackWalk64" does not set "GetLastError"... 1172 this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset); 1173 break; 1174 } 1175 1176 csEntry.offset = s.AddrPC.Offset; 1177 csEntry.name[0] = 0; 1178 csEntry.undName[0] = 0; 1179 csEntry.undFullName[0] = 0; 1180 csEntry.offsetFromSmybol = 0; 1181 csEntry.offsetFromLine = 0; 1182 csEntry.lineFileName[0] = 0; 1183 csEntry.lineNumber = 0; 1184 csEntry.loadedImageName[0] = 0; 1185 csEntry.moduleName[0] = 0; 1186 if (s.AddrPC.Offset == s.AddrReturn.Offset) 1187 { 1188 if ((this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount)) 1189 { 1190 this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); 1191 break; 1192 } 1193 curRecursionCount++; 1194 } 1195 else 1196 curRecursionCount = 0; 1197 if (s.AddrPC.Offset != 0) 1198 { 1199 // we seem to have a valid PC 1200 // show procedure info (SymGetSymFromAddr64()) 1201 if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), 1202 pSym) != FALSE) 1203 { 1204 MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name); 1205 // UnDecorateSymbolName() 1206 this->m_sw->pUDSN(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY); 1207 this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE); 1208 } 1209 else 1210 { 1211 this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); 1212 } 1213 1214 // show line number info, NT5.0-method (SymGetLineFromAddr64()) 1215 if (this->m_sw->pSGLFA != NULL) 1216 { // yes, we have SymGetLineFromAddr64() 1217 if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), 1218 &Line) != FALSE) 1219 { 1220 csEntry.lineNumber = Line.LineNumber; 1221 MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName); 1222 } 1223 else 1224 { 1225 this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); 1226 } 1227 } // yes, we have SymGetLineFromAddr64() 1228 1229 // show module info (SymGetModuleInfo64()) 1230 if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) != FALSE) 1231 { // got module info OK 1232 switch (Module.SymType) 1233 { 1234 case SymNone: 1235 csEntry.symTypeString = "-nosymbols-"; 1236 break; 1237 case SymCoff: 1238 csEntry.symTypeString = "COFF"; 1239 break; 1240 case SymCv: 1241 csEntry.symTypeString = "CV"; 1242 break; 1243 case SymPdb: 1244 csEntry.symTypeString = "PDB"; 1245 break; 1246 case SymExport: 1247 csEntry.symTypeString = "-exported-"; 1248 break; 1249 case SymDeferred: 1250 csEntry.symTypeString = "-deferred-"; 1251 break; 1252 case SymSym: 1253 csEntry.symTypeString = "SYM"; 1254 break; 1255 #if API_VERSION_NUMBER >= 9 1256 case SymDia: 1257 csEntry.symTypeString = "DIA"; 1258 break; 1259 #endif 1260 case 8: //SymVirtual: 1261 csEntry.symTypeString = "Virtual"; 1262 break; 1263 default: 1264 //_snprintf( ty, sizeof(ty), "symtype=%ld", (long) Module.SymType ); 1265 csEntry.symTypeString = NULL; 1266 break; 1267 } 1268 1269 MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName); 1270 csEntry.baseOfImage = Module.BaseOfImage; 1271 MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName); 1272 } // got module info OK 1273 else 1274 { 1275 this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); 1276 } 1277 } // we seem to have a valid PC 1278 1279 CallstackEntryType et = nextEntry; 1280 if (frameNum == 0) 1281 et = firstEntry; 1282 bLastEntryCalled = false; 1283 this->OnCallstackEntry(et, csEntry); 1284 1285 if (s.AddrReturn.Offset == 0) 1286 { 1287 bLastEntryCalled = true; 1288 this->OnCallstackEntry(lastEntry, csEntry); 1289 SetLastError(ERROR_SUCCESS); 1290 break; 1291 } 1292 } // for ( frameNum ) 1293 1294 cleanup: 1295 if (pSym) 1296 free(pSym); 1297 1298 if (bLastEntryCalled == false) 1299 this->OnCallstackEntry(lastEntry, csEntry); 1300 1301 if (context == NULL) 1302 ResumeThread(hThread); 1303 1304 return TRUE; 1305 } 1306 1307 BOOL StackWalker::ShowObject(LPVOID pObject) 1308 { 1309 // Load modules if not done yet 1310 if (m_modulesLoaded == FALSE) 1311 this->LoadModules(); // ignore the result... 1312 1313 // Verify that the DebugHelp.dll was actually found 1314 if (this->m_sw->m_hDbhHelp == NULL) 1315 { 1316 SetLastError(ERROR_DLL_INIT_FAILED); 1317 return FALSE; 1318 } 1319 1320 // SymGetSymFromAddr64() is required 1321 if (this->m_sw->pSGSFA == NULL) 1322 return FALSE; 1323 1324 // Show object info (SymGetSymFromAddr64()) 1325 DWORD64 dwAddress = DWORD64(pObject); 1326 DWORD64 dwDisplacement = 0; 1327 const SIZE_T symSize = sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN; 1328 IMAGEHLP_SYMBOL64* pSym = (IMAGEHLP_SYMBOL64*) malloc(symSize); 1329 if (!pSym) 1330 return FALSE; 1331 memset(pSym, 0, symSize); 1332 pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); 1333 pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; 1334 if (this->m_sw->pSGSFA(this->m_hProcess, dwAddress, &dwDisplacement, pSym) == FALSE) 1335 { 1336 this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), dwAddress); 1337 return FALSE; 1338 } 1339 // Object name output 1340 this->OnOutput(pSym->Name); 1341 1342 free(pSym); 1343 return TRUE; 1344 }; 1345 1346 BOOL __stdcall StackWalker::myReadProcMem(HANDLE hProcess, 1347 DWORD64 qwBaseAddress, 1348 PVOID lpBuffer, 1349 DWORD nSize, 1350 LPDWORD lpNumberOfBytesRead) 1351 { 1352 if (s_readMemoryFunction == NULL) 1353 { 1354 SIZE_T st; 1355 BOOL bRet = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st); 1356 *lpNumberOfBytesRead = (DWORD)st; 1357 //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); 1358 return bRet; 1359 } 1360 else 1361 { 1362 return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, 1363 s_readMemoryFunction_UserData); 1364 } 1365 } 1366 1367 void StackWalker::OnLoadModule(LPCSTR img, 1368 LPCSTR mod, 1369 DWORD64 baseAddr, 1370 DWORD size, 1371 DWORD result, 1372 LPCSTR symType, 1373 LPCSTR pdbName, 1374 ULONGLONG fileVersion) 1375 { 1376 CHAR buffer[STACKWALK_MAX_NAMELEN]; 1377 size_t maxLen = STACKWALK_MAX_NAMELEN; 1378 #if _MSC_VER >= 1400 1379 maxLen = _TRUNCATE; 1380 #endif 1381 if (fileVersion == 0) 1382 _snprintf_s(buffer, maxLen, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", 1383 img, mod, (LPVOID)baseAddr, size, result, symType, pdbName); 1384 else 1385 { 1386 DWORD v4 = (DWORD)(fileVersion & 0xFFFF); 1387 DWORD v3 = (DWORD)((fileVersion >> 16) & 0xFFFF); 1388 DWORD v2 = (DWORD)((fileVersion >> 32) & 0xFFFF); 1389 DWORD v1 = (DWORD)((fileVersion >> 48) & 0xFFFF); 1390 _snprintf_s( 1391 buffer, maxLen, 1392 "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", 1393 img, mod, (LPVOID)baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); 1394 } 1395 buffer[STACKWALK_MAX_NAMELEN - 1] = 0; // be sure it is NULL terminated 1396 OnOutput(buffer); 1397 } 1398 1399 void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry) 1400 { 1401 CHAR buffer[STACKWALK_MAX_NAMELEN]; 1402 size_t maxLen = STACKWALK_MAX_NAMELEN; 1403 #if _MSC_VER >= 1400 1404 maxLen = _TRUNCATE; 1405 #endif 1406 if ((eType != lastEntry) && (entry.offset != 0)) 1407 { 1408 if (entry.name[0] == 0) 1409 MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)"); 1410 if (entry.undName[0] != 0) 1411 MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName); 1412 if (entry.undFullName[0] != 0) 1413 MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName); 1414 if (entry.lineFileName[0] == 0) 1415 { 1416 MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)"); 1417 if (entry.moduleName[0] == 0) 1418 MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)"); 1419 _snprintf_s(buffer, maxLen, "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName, 1420 entry.lineFileName, entry.name); 1421 } 1422 else 1423 _snprintf_s(buffer, maxLen, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, 1424 entry.name); 1425 buffer[STACKWALK_MAX_NAMELEN - 1] = 0; 1426 OnOutput(buffer); 1427 } 1428 } 1429 1430 void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) 1431 { 1432 CHAR buffer[STACKWALK_MAX_NAMELEN]; 1433 size_t maxLen = STACKWALK_MAX_NAMELEN; 1434 #if _MSC_VER >= 1400 1435 maxLen = _TRUNCATE; 1436 #endif 1437 _snprintf_s(buffer, maxLen, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, 1438 (LPVOID)addr); 1439 buffer[STACKWALK_MAX_NAMELEN - 1] = 0; 1440 OnOutput(buffer); 1441 } 1442 1443 void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) 1444 { 1445 CHAR buffer[STACKWALK_MAX_NAMELEN]; 1446 size_t maxLen = STACKWALK_MAX_NAMELEN; 1447 #if _MSC_VER >= 1400 1448 maxLen = _TRUNCATE; 1449 #endif 1450 _snprintf_s(buffer, maxLen, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", 1451 szSearchPath, symOptions, szUserName); 1452 buffer[STACKWALK_MAX_NAMELEN - 1] = 0; 1453 OnOutput(buffer); 1454 // Also display the OS-version 1455 #if _MSC_VER <= 1200 1456 OSVERSIONINFOA ver; 1457 ZeroMemory(&ver, sizeof(OSVERSIONINFOA)); 1458 ver.dwOSVersionInfoSize = sizeof(ver); 1459 if (GetVersionExA(&ver) != FALSE) 1460 { 1461 _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s)\n", ver.dwMajorVersion, 1462 ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion); 1463 buffer[STACKWALK_MAX_NAMELEN - 1] = 0; 1464 OnOutput(buffer); 1465 } 1466 #else 1467 OSVERSIONINFOEXA ver; 1468 ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA)); 1469 ver.dwOSVersionInfoSize = sizeof(ver); 1470 #if _MSC_VER >= 1900 1471 #pragma warning(push) 1472 #pragma warning(disable : 4996) 1473 #endif 1474 if (GetVersionExA((OSVERSIONINFOA*)&ver) != FALSE) 1475 { 1476 _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", ver.dwMajorVersion, 1477 ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion, ver.wSuiteMask, 1478 ver.wProductType); 1479 buffer[STACKWALK_MAX_NAMELEN - 1] = 0; 1480 OnOutput(buffer); 1481 } 1482 #if _MSC_VER >= 1900 1483 #pragma warning(pop) 1484 #endif 1485 #endif 1486 } 1487 1488 void StackWalker::OnOutput(LPCSTR buffer) 1489 { 1490 OutputDebugStringA(buffer); 1491 }