winStackWalker.cpp
Engine/source/platformWin32/minidump/winStackWalker.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) 25 26//----------------------------------------------------------------------------------------------------------------------------------------- 27// Sourced from http://www.codeproject.com/threads/StackWalker.asp 28//----------------------------------------------------------------------------------------------------------------------------------------- 29#include "winStackWalker.h" 30 31#undef UNICODE 32 33#include <tchar.h> 34#include <Tlhelp32.h> 35#include <stdio.h> 36#include <psapi.h> 37 38#pragma comment(lib, "version.lib") // for "VerQueryValue" 39#pragma comment(lib, "dbghelp.lib") 40#pragma comment(lib, "psapi.lib") 41 42 43// secure-CRT_functions are only available starting with VC8 44#if _MSC_VER < 1400 45#define strcpy_s strcpy 46#define strcat_s(dst, len, src) strcat(dst, src) 47#define _snprintf_s _snprintf 48#define _tcscat_s _tcscat 49#endif 50 51// Entry for each Callstack-Entry 52const S32 STACKWALK_MAX_NAMELEN = 1024; // max name length for symbols 53struct CallstackEntry 54{ 55 DWORD64 offset; // if 0, we have no valid entry 56 char name[STACKWALK_MAX_NAMELEN]; 57 char undName[STACKWALK_MAX_NAMELEN]; 58 char undFullName[STACKWALK_MAX_NAMELEN]; 59 DWORD64 offsetFromSmybol; 60 DWORD64 offsetFromLine; 61 DWORD lineNumber; 62 char lineFileName[STACKWALK_MAX_NAMELEN]; 63 DWORD symType; 64 const char * symTypeString; 65 char moduleName[STACKWALK_MAX_NAMELEN]; 66 DWORD64 baseOfImage; 67 char loadedImageName[STACKWALK_MAX_NAMELEN]; 68}; 69 70 71//----------------------------------------------------------------------------------------------------------------------------------------- 72// Implementation of platform functions 73//----------------------------------------------------------------------------------------------------------------------------------------- 74void dGetStackTrace(char * traceBuffer, CONTEXT const & ContextRecord) 75{ 76 StackWalker sw; 77 sw.setOutputBuffer(traceBuffer); 78 sw.ShowCallstack(GetCurrentThread(), ContextRecord); 79 sw.setOutputBuffer(NULL); 80} 81 82 83//----------------------------------------------------------------------------------------------------------------------------------------- 84//Constructor 85//----------------------------------------------------------------------------------------------------------------------------------------- 86StackWalker::StackWalker(DWORD options, LPCSTR szSymPath) 87: m_dwProcessId(GetCurrentProcessId()), 88m_hProcess(GetCurrentProcess()), 89m_options(options), 90m_modulesLoaded(false), 91m_pOutputBuffer(NULL) 92{ 93 if (szSymPath != NULL) 94 { 95 m_szSymPath = _strdup(szSymPath); 96 m_options |= SymBuildPath; 97 } 98 else 99 m_szSymPath = NULL; 100} 101 102 103//----------------------------------------------------------------------------------------------------------------------------------------- 104//Destructor 105//----------------------------------------------------------------------------------------------------------------------------------------- 106StackWalker::~StackWalker() 107{ 108 SymCleanup(m_hProcess); 109 if (m_szSymPath != NULL) free(m_szSymPath); 110 m_szSymPath = NULL; 111} 112 113 114//----------------------------------------------------------------------------------------------------------------------------------------- 115//setOutputBuffer - Points the StackWalker at a buffer where it will write out any output. If this is set to NULL then the output 116// will only be sent to DebugString 117//----------------------------------------------------------------------------------------------------------------------------------------- 118void StackWalker::setOutputBuffer(char * buffer) 119{ 120 m_pOutputBuffer = buffer; 121} 122 123 124//----------------------------------------------------------------------------------------------------------------------------------------- 125//OnOutput 126//----------------------------------------------------------------------------------------------------------------------------------------- 127void StackWalker::OnOutput(LPCSTR buffer) 128{ 129 OutputDebugStringA(buffer); 130 if(m_pOutputBuffer) strcat(m_pOutputBuffer, buffer); 131} 132 133 134//----------------------------------------------------------------------------------------------------------------------------------------- 135//Init 136//----------------------------------------------------------------------------------------------------------------------------------------- 137bool StackWalker::Init(LPCSTR szSymPath) 138{ 139 // SymInitialize 140 if (szSymPath != NULL) m_szSymPath = _strdup(szSymPath); 141 if (SymInitialize(m_hProcess, m_szSymPath, FALSE) == FALSE) 142 { 143 this->OnDbgHelpErr("SymInitialize", GetLastError(), 0); 144 } 145 146 DWORD symOptions = SymGetOptions(); 147 symOptions |= SYMOPT_LOAD_LINES; 148 symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; 149 symOptions = SymSetOptions(symOptions); 150 151 char buf[STACKWALK_MAX_NAMELEN] = {0}; 152 if(SymGetSearchPath(m_hProcess, buf, STACKWALK_MAX_NAMELEN) == FALSE) 153 { 154 this->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); 155 } 156 157 char szUserName[1024] = {0}; 158 DWORD dwSize = 1024; 159 GetUserNameA(szUserName, &dwSize); 160 this->OnSymInit(buf, symOptions, szUserName); 161 162 return TRUE; 163} 164 165 166//----------------------------------------------------------------------------------------------------------------------------------------- 167//LoadModules 168//----------------------------------------------------------------------------------------------------------------------------------------- 169bool StackWalker::LoadModules() 170{ 171 if (m_modulesLoaded) return true; 172 173 // Build the sym-path: 174 char *szSymPath = NULL; 175 if ( (m_options & SymBuildPath) != 0) 176 { 177 const size_t nSymPathLen = 4096; 178 szSymPath = (char*) malloc(nSymPathLen); 179 if (szSymPath == NULL) 180 { 181 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 182 return false; 183 } 184 szSymPath[0] = 0; 185 // Now first add the (optional) provided sympath: 186 if (m_szSymPath != NULL) 187 { 188 strcat_s(szSymPath, nSymPathLen, m_szSymPath); 189 strcat_s(szSymPath, nSymPathLen, ";"); 190 } 191 192 strcat_s(szSymPath, nSymPathLen, ".;"); 193 194 const size_t nTempLen = 1024; 195 CHAR szTemp[nTempLen]; 196 // Now add the current directory: 197 if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) 198 { 199 szTemp[nTempLen-1] = 0; 200 strcat_s(szSymPath, nSymPathLen, szTemp); 201 strcat_s(szSymPath, nSymPathLen, ";"); 202 } 203 204 // Now add the path for the main-module: 205 if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) 206 { 207 szTemp[nTempLen-1] = 0; 208 for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p) 209 { 210 // locate the rightmost path separator 211 if ( (*p == '\\') || (*p == '/') || (*p == ':') ) 212 { 213 *p = 0; 214 break; 215 } 216 } // for (search for path separator...) 217 if (strlen(szTemp) > 0) 218 { 219 strcat_s(szSymPath, nSymPathLen, szTemp); 220 strcat_s(szSymPath, nSymPathLen, ";"); 221 } 222 } 223 if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) 224 { 225 szTemp[nTempLen-1] = 0; 226 strcat_s(szSymPath, nSymPathLen, szTemp); 227 strcat_s(szSymPath, nSymPathLen, ";"); 228 } 229 if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) 230 { 231 szTemp[nTempLen-1] = 0; 232 strcat_s(szSymPath, nSymPathLen, szTemp); 233 strcat_s(szSymPath, nSymPathLen, ";"); 234 } 235 if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) 236 { 237 szTemp[nTempLen-1] = 0; 238 strcat_s(szSymPath, nSymPathLen, szTemp); 239 strcat_s(szSymPath, nSymPathLen, ";"); 240 // also add the "system32"-directory: 241 strcat_s(szTemp, nTempLen, "\\system32"); 242 strcat_s(szSymPath, nSymPathLen, szTemp); 243 strcat_s(szSymPath, nSymPathLen, ";"); 244 } 245 246 if ( (m_options & SymBuildPath) != 0 ) 247 { 248 if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) 249 { 250 szTemp[nTempLen-1] = 0; 251 strcat_s(szSymPath, nSymPathLen, "SRV*"); 252 strcat_s(szSymPath, nSymPathLen, szTemp); 253 strcat_s(szSymPath, nSymPathLen, "\\websymbols"); 254 strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;"); 255 } 256 else 257 strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); 258 } 259 } 260 261 // First Init the whole stuff... 262 bool bRet = Init(szSymPath); 263 if (szSymPath != NULL) 264 { 265 free(szSymPath); 266 szSymPath = NULL; 267 } 268 if (bRet == false) 269 { 270 this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); 271 SetLastError(ERROR_DLL_INIT_FAILED); 272 return false; 273 } 274 275 if(GetModuleListTH32(m_hProcess, m_dwProcessId)) 276 { 277 m_modulesLoaded = true; 278 return true; 279 } 280 281 // then try psapi 282 if(GetModuleListPSAPI(m_hProcess)) 283 { 284 m_modulesLoaded = true; 285 return true; 286 } 287 288 return false; 289} 290 291 292//----------------------------------------------------------------------------------------------------------------------------------------- 293//LoadModule 294//----------------------------------------------------------------------------------------------------------------------------------------- 295DWORD StackWalker::LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) 296{ 297 CHAR *szImg = _strdup(img); 298 CHAR *szMod = _strdup(mod); 299 DWORD result = ERROR_SUCCESS; 300 if ( (szImg == NULL) || (szMod == NULL) ) 301 result = ERROR_NOT_ENOUGH_MEMORY; 302 else 303 { 304 if (SymLoadModule64(hProcess, 0, szImg, szMod, baseAddr, size) == 0) 305 result = GetLastError(); 306 } 307 ULONGLONG fileVersion = 0; 308 if(szImg != NULL) 309 { 310 // try to retrieve the file-version: 311 if ( (m_options & StackWalker::RetrieveFileVersion) != 0) 312 { 313 VS_FIXEDFILEINFO *fInfo = NULL; 314 DWORD dwHandle; 315 DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); 316 if (dwSize > 0) 317 { 318 LPVOID vData = malloc(dwSize); 319 if (vData != NULL) 320 { 321 if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) 322 { 323 UINT len; 324 char szSubBlock[] = _T("\\"); 325 if (VerQueryValueA(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0) 326 fInfo = NULL; 327 else 328 { 329 fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); 330 } 331 } 332 free(vData); 333 } 334 } 335 } 336 337 // Retrive some additional-infos about the module 338 IMAGEHLP_MODULE64 Module; 339 const char *szSymType = "-unknown-"; 340 if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) 341 { 342 switch(Module.SymType) 343 { 344 case SymNone: 345 szSymType = "-nosymbols-"; 346 break; 347 case SymCoff: 348 szSymType = "COFF"; 349 break; 350 case SymCv: 351 szSymType = "CV"; 352 break; 353 case SymPdb: 354 szSymType = "PDB"; 355 break; 356 case SymExport: 357 szSymType = "-exported-"; 358 break; 359 case SymDeferred: 360 szSymType = "-deferred-"; 361 break; 362 case SymSym: 363 szSymType = "SYM"; 364 break; 365 case 8: //SymVirtual: 366 szSymType = "Virtual"; 367 break; 368 case 9: // SymDia: 369 szSymType = "DIA"; 370 break; 371 } 372 } 373 this->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion); 374 } 375 if (szImg != NULL) free(szImg); 376 if (szMod != NULL) free(szMod); 377 return result; 378} 379 380 381// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction 382// This has to be done due to a problem with the "hProcess"-parameter in x64... 383// Because this class is in no case multi-threading-enabled (because of the limitations of dbghelp.dll) it is "safe" to use a static-variable 384static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; 385static LPVOID s_readMemoryFunction_UserData = NULL; 386 387 388//----------------------------------------------------------------------------------------------------------------------------------------- 389//ShowCallstack 390//----------------------------------------------------------------------------------------------------------------------------------------- 391bool StackWalker::ShowCallstack(HANDLE hThread, CONTEXT const & context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) 392{ 393 CONTEXT c = context; 394 IMAGEHLP_SYMBOL64 *pSym = NULL; 395 IMAGEHLP_MODULE64 Module; 396 IMAGEHLP_LINE64 Line; 397 S32 frameNum; 398 399 if (!m_modulesLoaded) LoadModules(); 400 401 402 s_readMemoryFunction = readMemoryFunction; 403 s_readMemoryFunction_UserData = pUserData; 404 405 406 407 // init STACKFRAME for first call 408 STACKFRAME64 s; // in/out stackframe 409 memset(&s, 0, sizeof(s)); 410 DWORD imageType; 411#ifdef _M_IX86 412 // normally, call ImageNtHeader() and use machine info from PE header 413 imageType = IMAGE_FILE_MACHINE_I386; 414 s.AddrPC.Offset = c.Eip; 415 s.AddrPC.Mode = AddrModeFlat; 416 s.AddrFrame.Offset = c.Ebp; 417 s.AddrFrame.Mode = AddrModeFlat; 418 s.AddrStack.Offset = c.Esp; 419 s.AddrStack.Mode = AddrModeFlat; 420#elif _M_X64 421 imageType = IMAGE_FILE_MACHINE_AMD64; 422 s.AddrPC.Offset = c.Rip; 423 s.AddrPC.Mode = AddrModeFlat; 424 s.AddrFrame.Offset = c.Rsp; 425 s.AddrFrame.Mode = AddrModeFlat; 426 s.AddrStack.Offset = c.Rsp; 427 s.AddrStack.Mode = AddrModeFlat; 428#elif _M_IA64 429 imageType = IMAGE_FILE_MACHINE_IA64; 430 s.AddrPC.Offset = c.StIIP; 431 s.AddrPC.Mode = AddrModeFlat; 432 s.AddrFrame.Offset = c.IntSp; 433 s.AddrFrame.Mode = AddrModeFlat; 434 s.AddrBStore.Offset = c.RsBSP; 435 s.AddrBStore.Mode = AddrModeFlat; 436 s.AddrStack.Offset = c.IntSp; 437 s.AddrStack.Mode = AddrModeFlat; 438#else 439#error "Platform not supported!" 440#endif 441 442 pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); 443 if (!pSym) goto cleanup; // not enough memory... 444 memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); 445 pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); 446 pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; 447 448 memset(&Line, 0, sizeof(Line)); 449 Line.SizeOfStruct = sizeof(Line); 450 451 memset(&Module, 0, sizeof(Module)); 452 Module.SizeOfStruct = sizeof(Module); 453 454 for (frameNum = 0; ; ++frameNum ) 455 { 456 // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) 457 // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can 458 // assume that either you are done, or that the stack is so hosed that the next 459 // deeper frame could not be found. 460 // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386! 461 if ( ! StackWalk64(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, SymFunctionTableAccess64, SymGetModuleBase64, NULL) ) 462 { 463 this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset); 464 break; 465 } 466 467 CallstackEntry csEntry; 468 csEntry.offset = s.AddrPC.Offset; 469 csEntry.name[0] = 0; 470 csEntry.undName[0] = 0; 471 csEntry.undFullName[0] = 0; 472 csEntry.offsetFromSmybol = 0; 473 csEntry.offsetFromLine = 0; 474 csEntry.lineFileName[0] = 0; 475 csEntry.lineNumber = 0; 476 csEntry.loadedImageName[0] = 0; 477 csEntry.moduleName[0] = 0; 478 if (s.AddrPC.Offset == s.AddrReturn.Offset) 479 { 480 OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); 481 break; 482 } 483 if (s.AddrPC.Offset != 0) 484 { 485 // we seem to have a valid PC, show procedure info 486 if (SymGetSymFromAddr64(m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) 487 { 488 // TODO: Mache dies sicher...! 489 strcpy_s(csEntry.name, pSym->Name); 490 UnDecorateSymbolName( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY ); 491 UnDecorateSymbolName( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE ); 492 } 493 else 494 { 495 this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); 496 } 497 498 // show line number info, NT5.0-method 499 if (SymGetLineFromAddr64(this->m_hProcess, s.AddrPC.Offset, (PDWORD)&(csEntry.offsetFromLine), &Line) != FALSE) 500 { 501 csEntry.lineNumber = Line.LineNumber; 502 // TODO: Mache dies sicher...! 503 strcpy_s(csEntry.lineFileName, Line.FileName); 504 } 505 else 506 { 507 this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); 508 } 509 510 // show module info 511 if( GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) ) 512 { 513 switch ( Module.SymType ) 514 { 515 case SymNone: 516 csEntry.symTypeString = "-nosymbols-"; 517 break; 518 case SymCoff: 519 csEntry.symTypeString = "COFF"; 520 break; 521 case SymCv: 522 csEntry.symTypeString = "CV"; 523 break; 524 case SymPdb: 525 csEntry.symTypeString = "PDB"; 526 break; 527 case SymExport: 528 csEntry.symTypeString = "-exported-"; 529 break; 530 case SymDeferred: 531 csEntry.symTypeString = "-deferred-"; 532 break; 533 case SymSym: 534 csEntry.symTypeString = "SYM"; 535 break; 536 case SymDia: 537 csEntry.symTypeString = "DIA"; 538 break; 539 case SymVirtual: 540 csEntry.symTypeString = "Virtual"; 541 break; 542 default: 543 //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType ); 544 csEntry.symTypeString = NULL; 545 break; 546 } 547 548 // TODO: Mache dies sicher...! 549 strcpy_s(csEntry.moduleName, Module.ModuleName); 550 csEntry.baseOfImage = Module.BaseOfImage; 551 strcpy_s(csEntry.loadedImageName, Module.LoadedImageName); 552 } 553 else 554 { 555 OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); 556 } 557 } 558 559 CallstackEntryType et = nextEntry; 560 if (frameNum == 0) et = firstEntry; 561 OnCallstackEntry(et, csEntry); 562 563 if (s.AddrReturn.Offset == 0) 564 { 565 OnCallstackEntry(lastEntry, csEntry); 566 SetLastError(ERROR_SUCCESS); 567 break; 568 } 569 } 570 571cleanup: 572 if (pSym) free( pSym ); 573 574 return true; 575} 576 577 578 579BOOL __stdcall StackWalker::myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead) 580{ 581 if (s_readMemoryFunction == NULL) 582 { 583 SIZE_T st; 584 BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st); 585 *lpNumberOfBytesRead = (DWORD) st; 586 //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); 587 return bRet; 588 } 589 else 590 { 591 return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); 592 } 593} 594 595 596//----------------------------------------------------------------------------------------------------------------------------------------- 597//OnLoadModule 598//----------------------------------------------------------------------------------------------------------------------------------------- 599void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) 600{ 601 CHAR buffer[STACKWALK_MAX_NAMELEN]; 602 if (fileVersion == 0) 603 _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); 604 else 605 { 606 DWORD v4 = (DWORD) fileVersion & 0xFFFF; 607 DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF; 608 DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF; 609 DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF; 610 _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); 611 } 612 if(OutputModules & m_options) OnOutput(buffer); 613} 614 615 616//----------------------------------------------------------------------------------------------------------------------------------------- 617//GetModuleInfo 618//----------------------------------------------------------------------------------------------------------------------------------------- 619bool StackWalker::GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64 *pModuleInfo) 620{ 621 622 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64); 623 void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... 624 if (pData == NULL) 625 { 626 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 627 return false; 628 } 629 memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64)); 630 if (SymGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULE64*) pData) != FALSE) 631 { 632 // only copy as much memory as is reserved... 633 memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64)); 634 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64); 635 free(pData); 636 return true; 637 } 638 free(pData); 639 SetLastError(ERROR_DLL_INIT_FAILED); 640 return false; 641} 642 643 644//----------------------------------------------------------------------------------------------------------------------------------------- 645//GetModuleListTH32 646//----------------------------------------------------------------------------------------------------------------------------------------- 647bool StackWalker::GetModuleListTH32(HANDLE hProcess, DWORD pid) 648{ 649 // CreateToolhelp32Snapshot() 650 typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID); 651 // Module32First() 652 typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); 653 // Module32Next() 654 typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); 655 656 // try both dlls... 657 const char *dllname[] = { "kernel32.dll", "tlhelp32.dll" }; 658 HINSTANCE hToolhelp = NULL; 659 tCT32S pCT32S = NULL; 660 tM32F pM32F = NULL; 661 tM32N pM32N = NULL; 662 663 HANDLE hSnap; 664 MODULEENTRY32 me; 665 me.dwSize = sizeof(me); 666 BOOL keepGoing; 667 size_t i; 668 669 for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ ) 670 { 671 hToolhelp = LoadLibraryA( dllname[i] ); 672 if (hToolhelp == NULL) 673 continue; 674 pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); 675 pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First"); 676 pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next"); 677 if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) ) 678 break; // found the functions! 679 FreeLibrary(hToolhelp); 680 hToolhelp = NULL; 681 } 682 683 if (hToolhelp == NULL) 684 return false; 685 686 hSnap = pCT32S( TH32CS_SNAPMODULE, pid ); 687 if (hSnap == (HANDLE) -1) 688 return false; 689 690 keepGoing = !!pM32F( hSnap, &me ); 691 S32 cnt = 0; 692 while (keepGoing) 693 { 694 this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize); 695 cnt++; 696 keepGoing = !!pM32N( hSnap, &me ); 697 } 698 CloseHandle(hSnap); 699 FreeLibrary(hToolhelp); 700 if (cnt <= 0) return false; 701 702 return true; 703} 704 705 706//----------------------------------------------------------------------------------------------------------------------------------------- 707//GetModuleListPSAPI 708//----------------------------------------------------------------------------------------------------------------------------------------- 709bool StackWalker::GetModuleListPSAPI(HANDLE hProcess) 710{ 711 DWORD i; 712 //ModuleEntry e; 713 DWORD cbNeeded; 714 MODULEINFO mi; 715 HMODULE *hMods = 0; 716 char *tt = NULL; 717 char *tt2 = NULL; 718 const SIZE_T TTBUFLEN = 8096; 719 S32 cnt = 0; 720 721 hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE)); 722 tt = (char*) malloc(sizeof(char) * TTBUFLEN); 723 tt2 = (char*) malloc(sizeof(char) * TTBUFLEN); 724 if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) ) 725 goto cleanup; 726 727 if ( !EnumProcessModules(hProcess, hMods, TTBUFLEN, &cbNeeded) ) 728 { 729 //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); 730 goto cleanup; 731 } 732 733 if ( cbNeeded > TTBUFLEN ) 734 { 735 //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); 736 goto cleanup; 737 } 738 739 for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ ) 740 { 741 // base address, size 742 GetModuleInformation(hProcess, hMods[i], &mi, sizeof mi ); 743 // image file name 744 tt[0] = 0; 745 GetModuleFileNameExA(hProcess, hMods[i], tt, TTBUFLEN ); 746 // module name 747 tt2[0] = 0; 748 GetModuleBaseNameA(hProcess, hMods[i], tt2, TTBUFLEN ); 749 750 DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage); 751 if (dwRes != ERROR_SUCCESS) 752 this->OnDbgHelpErr("LoadModule", dwRes, 0); 753 cnt++; 754 } 755 756cleanup: 757 if (tt2 != NULL) free(tt2); 758 if (tt != NULL) free(tt); 759 if (hMods != NULL) free(hMods); 760 761 return cnt != 0; 762} 763 764 765//----------------------------------------------------------------------------------------------------------------------------------------- 766//OnCallstackEntry 767//----------------------------------------------------------------------------------------------------------------------------------------- 768void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) 769{ 770 CHAR buffer[STACKWALK_MAX_NAMELEN]; 771 if ( (eType != lastEntry) && (entry.offset != 0) ) 772 { 773 if (entry.name[0] == 0) strcpy_s(entry.name, "(function-name not available)"); 774 if (entry.undName[0] != 0) strcpy_s(entry.name, entry.undName); 775 if (entry.undFullName[0] != 0) strcpy_s(entry.name, entry.undFullName); 776 if (entry.lineFileName[0] == 0) 777 { 778 strcpy_s(entry.lineFileName, "(filename not available)"); 779 if (entry.moduleName[0] == 0) strcpy_s(entry.moduleName, "(module-name not available)"); 780 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name); 781 } 782 else 783 { 784 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); 785 } 786 OnOutput(buffer); 787 } 788} 789 790 791//----------------------------------------------------------------------------------------------------------------------------------------- 792//OnDbgHelpErr 793//----------------------------------------------------------------------------------------------------------------------------------------- 794void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) 795{ 796 CHAR buffer[STACKWALK_MAX_NAMELEN]; 797 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr); 798 OnOutput(buffer); 799} 800 801 802//----------------------------------------------------------------------------------------------------------------------------------------- 803//OnSymInit 804//----------------------------------------------------------------------------------------------------------------------------------------- 805void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) 806{ 807 //Symbol Search path 808 CHAR buffer[STACKWALK_MAX_NAMELEN]; 809 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); 810 if(OutputSymPath & m_options) OnOutput(buffer); 811 812 //OS-version 813 OSVERSIONINFOEX ver; 814 ZeroMemory(&ver, sizeof(OSVERSIONINFOEX)); 815 ver.dwOSVersionInfoSize = sizeof(ver); 816 if (GetVersionEx( (OSVERSIONINFO*) &ver) != FALSE) 817 { 818 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", 819 ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, 820 ver.szCSDVersion, ver.wSuiteMask, ver.wProductType); 821 if(OutputOS & m_options) OnOutput(buffer); 822 } 823} 824 825#endif 826 827