/processing/crashrptprobe/MinidumpReader.cpp

http://crashrpt.googlecode.com/ · C++ · 879 lines · 654 code · 118 blank · 107 comment · 64 complexity · 9c69896e0de2e60298c019a5109d1aff MD5 · raw file

  1. /*************************************************************************************
  2. This file is a part of CrashRpt library.
  3. Copyright (c) 2003-2013 The CrashRpt project authors. All Rights Reserved.
  4. Use of this source code is governed by a BSD-style license
  5. that can be found in the License.txt file in the root of the source
  6. tree. All contributing project authors may
  7. be found in the Authors.txt file in the root of the source tree.
  8. ***************************************************************************************/
  9. #include "stdafx.h"
  10. #include "MinidumpReader.h"
  11. #include <assert.h>
  12. #include "Utility.h"
  13. #include "strconv.h"
  14. #include "md5.h"
  15. CMiniDumpReader* g_pMiniDumpReader = NULL;
  16. // Callback function prototypes
  17. BOOL CALLBACK ReadProcessMemoryProc64(
  18. HANDLE hProcess,
  19. DWORD64 lpBaseAddress,
  20. PVOID lpBuffer,
  21. DWORD nSize,
  22. LPDWORD lpNumberOfBytesRead);
  23. PVOID CALLBACK FunctionTableAccessProc64(
  24. HANDLE hProcess,
  25. DWORD64 AddrBase);
  26. DWORD64 CALLBACK GetModuleBaseProc64(
  27. HANDLE hProcess,
  28. DWORD64 Address);
  29. BOOL CALLBACK SymRegisterCallbackProc64(
  30. HANDLE hProcess,
  31. ULONG ActionCode,
  32. ULONG64 CallbackData,
  33. ULONG64 UserContext
  34. );
  35. CMiniDumpReader::CMiniDumpReader()
  36. {
  37. m_bLoaded = FALSE;
  38. m_bReadSysInfoStream = FALSE;
  39. m_bReadExceptionStream = FALSE;
  40. m_bReadModuleListStream = FALSE;
  41. m_bReadMemoryListStream = FALSE;
  42. m_bReadThreadListStream = FALSE;
  43. m_hFileMiniDump = INVALID_HANDLE_VALUE;
  44. m_hFileMapping = NULL;
  45. m_pMiniDumpStartPtr = NULL;
  46. }
  47. CMiniDumpReader::~CMiniDumpReader()
  48. {
  49. Close();
  50. }
  51. int CMiniDumpReader::Open(CString sFileName, CString sSymSearchPath)
  52. {
  53. static DWORD dwProcessID = 0;
  54. if(m_bLoaded)
  55. {
  56. // Already loaded
  57. return 0;
  58. }
  59. m_sFileName = sFileName;
  60. m_sSymSearchPath = sSymSearchPath;
  61. m_hFileMiniDump = CreateFile(
  62. sFileName,
  63. FILE_GENERIC_READ,
  64. 0,
  65. NULL,
  66. OPEN_EXISTING,
  67. NULL,
  68. NULL);
  69. if(m_hFileMiniDump==INVALID_HANDLE_VALUE)
  70. {
  71. Close();
  72. return 1;
  73. }
  74. m_hFileMapping = CreateFileMapping(
  75. m_hFileMiniDump,
  76. NULL,
  77. PAGE_READONLY,
  78. 0,
  79. 0,
  80. 0);
  81. if(m_hFileMapping==NULL)
  82. {
  83. Close();
  84. return 2;
  85. }
  86. m_pMiniDumpStartPtr = MapViewOfFile(
  87. m_hFileMapping,
  88. FILE_MAP_READ,
  89. 0,
  90. 0,
  91. 0);
  92. if(m_pMiniDumpStartPtr==NULL)
  93. {
  94. Close();
  95. return 3;
  96. }
  97. m_DumpData.m_hProcess = (HANDLE)(++dwProcessID);
  98. DWORD dwOptions = 0;
  99. //dwOptions |= SYMOPT_DEFERRED_LOADS; // Symbols are not loaded until a reference is made requiring the symbols be loaded.
  100. dwOptions |= SYMOPT_EXACT_SYMBOLS; // Do not load an unmatched .pdb file.
  101. dwOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; // Do not display system dialog boxes when there is a media failure such as no media in a drive.
  102. dwOptions |= SYMOPT_UNDNAME; // All symbols are presented in undecorated form.
  103. SymSetOptions(dwOptions);
  104. strconv_t strconv;
  105. BOOL bSymInit = SymInitializeW(
  106. m_DumpData.m_hProcess,
  107. strconv.t2w(sSymSearchPath),
  108. FALSE);
  109. if(!bSymInit)
  110. {
  111. m_DumpData.m_hProcess = NULL;
  112. Close();
  113. return 5;
  114. }
  115. /*SymRegisterCallbackW64(
  116. m_DumpData.m_hProcess,
  117. SymRegisterCallbackProc64,
  118. (ULONG64)this);*/
  119. m_bReadSysInfoStream = !ReadSysInfoStream();
  120. m_bReadModuleListStream = !ReadModuleListStream();
  121. m_bReadThreadListStream = !ReadThreadListStream();
  122. m_bReadMemoryListStream = !ReadMemoryListStream();
  123. m_bReadExceptionStream = !ReadExceptionStream();
  124. m_bLoaded = true;
  125. return 0;
  126. }
  127. //BOOL CALLBACK SymRegisterCallbackProc64(
  128. // HANDLE hProcess,
  129. // ULONG ActionCode,
  130. // ULONG64 CallbackData,
  131. // ULONG64 UserContext
  132. //)
  133. //{
  134. // UNREFERENCED_PARAMETER(hProcess);
  135. // CMiniDumpReader* pMdmpReader = (CMiniDumpReader*)UserContext;
  136. //
  137. // switch(ActionCode)
  138. // {
  139. // case CBA_DEBUG_INFO:
  140. // {
  141. // LPCTSTR szMessage = (LPCTSTR)CallbackData;
  142. // pMdmpReader->m_DumpData.m_LoadLog.push_back(szMessage);
  143. // }
  144. // return TRUE;
  145. // case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
  146. // {
  147. // // Ignore
  148. // }
  149. // return FALSE;
  150. // case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
  151. // {
  152. // IMAGEHLP_DEFERRED_SYMBOL_LOADW64* pLoadInfo =
  153. // (IMAGEHLP_DEFERRED_SYMBOL_LOADW64*)CallbackData;
  154. // CString sMessage;
  155. // sMessage.Format(_T("Completed loading symbols for %s."), pLoadInfo->FileName);
  156. // //pMdmpReader->m_DumpData.m_LoadLog.push_back(sMessage);
  157. // }
  158. // return TRUE;
  159. // case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
  160. // {
  161. // IMAGEHLP_DEFERRED_SYMBOL_LOADW64* pLoadInfo =
  162. // (IMAGEHLP_DEFERRED_SYMBOL_LOADW64*)CallbackData;
  163. // CString sMessage;
  164. // sMessage.Format(_T("Failed to loaded symbols for %s."), pLoadInfo->FileName);
  165. // pMdmpReader->m_DumpData.m_LoadLog.push_back(sMessage);
  166. // }
  167. // return TRUE;
  168. // case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
  169. // {
  170. // IMAGEHLP_DEFERRED_SYMBOL_LOADW64* pLoadInfo =
  171. // (IMAGEHLP_DEFERRED_SYMBOL_LOADW64*)CallbackData;
  172. // CString sMessage;
  173. // sMessage.Format(_T("Partially loaded symbols for %s."), pLoadInfo->FileName);
  174. // pMdmpReader->m_DumpData.m_LoadLog.push_back(sMessage);
  175. // }
  176. // return TRUE;
  177. // case CBA_DEFERRED_SYMBOL_LOAD_START:
  178. // {
  179. // IMAGEHLP_DEFERRED_SYMBOL_LOADW64* pLoadInfo =
  180. // (IMAGEHLP_DEFERRED_SYMBOL_LOADW64*)CallbackData;
  181. // CString sMessage;
  182. // sMessage.Format(_T("Started loading symbols for %s."), pLoadInfo->FileName);
  183. // //pMdmpReader->m_DumpData.m_LoadLog.push_back(sMessage);
  184. // }
  185. // return TRUE;
  186. // }
  187. //
  188. // return FALSE;
  189. //}
  190. void CMiniDumpReader::Close()
  191. {
  192. UnmapViewOfFile(m_pMiniDumpStartPtr);
  193. if(m_hFileMapping!=NULL)
  194. {
  195. CloseHandle(m_hFileMapping);
  196. }
  197. if(m_hFileMiniDump!=INVALID_HANDLE_VALUE)
  198. {
  199. CloseHandle(m_hFileMiniDump);
  200. m_hFileMiniDump = INVALID_HANDLE_VALUE;
  201. }
  202. m_pMiniDumpStartPtr = NULL;
  203. if(m_DumpData.m_hProcess!=NULL)
  204. {
  205. SymCleanup(m_DumpData.m_hProcess);
  206. }
  207. }
  208. BOOL CMiniDumpReader::CheckDbgHelpApiVersion()
  209. {
  210. // Set valid dbghelp API version
  211. API_VERSION CompiledApiVer;
  212. CompiledApiVer.MajorVersion = 6;
  213. CompiledApiVer.MinorVersion = 1;
  214. CompiledApiVer.Revision = 11;
  215. CompiledApiVer.Reserved = 0;
  216. LPAPI_VERSION pActualApiVer = ImagehlpApiVersionEx(&CompiledApiVer);
  217. if(CompiledApiVer.MajorVersion!=pActualApiVer->MajorVersion ||
  218. CompiledApiVer.MinorVersion!=pActualApiVer->MinorVersion ||
  219. CompiledApiVer.Revision!=pActualApiVer->Revision)
  220. {
  221. return FALSE; // Not exact version of dbghelp.dll! Expected v6.11.
  222. }
  223. return TRUE;
  224. }
  225. // Extracts a UNICODE string stored in minidump file by its relative address
  226. CString CMiniDumpReader::GetMinidumpString(LPVOID start_addr, RVA rva)
  227. {
  228. MINIDUMP_STRING* pms = (MINIDUMP_STRING*)((LPBYTE)start_addr+rva);
  229. //CString sModule = CString(pms->Buffer, pms->Length);
  230. CString sModule = pms->Buffer;
  231. return sModule;
  232. }
  233. int CMiniDumpReader::ReadSysInfoStream()
  234. {
  235. LPVOID pStreamStart = NULL;
  236. ULONG uStreamSize = 0;
  237. MINIDUMP_DIRECTORY* pmd = NULL;
  238. BOOL bRead = FALSE;
  239. bRead = MiniDumpReadDumpStream(m_pMiniDumpStartPtr, SystemInfoStream,
  240. &pmd, &pStreamStart, &uStreamSize);
  241. if(bRead)
  242. {
  243. MINIDUMP_SYSTEM_INFO* pSysInfo = (MINIDUMP_SYSTEM_INFO*)pStreamStart;
  244. m_DumpData.m_uProcessorArchitecture = pSysInfo->ProcessorArchitecture;
  245. m_DumpData.m_uchNumberOfProcessors = pSysInfo->NumberOfProcessors;
  246. m_DumpData.m_uchProductType = pSysInfo->ProductType;
  247. m_DumpData.m_ulVerMajor = pSysInfo->MajorVersion;
  248. m_DumpData.m_ulVerMinor = pSysInfo->MinorVersion;
  249. m_DumpData.m_ulVerBuild = pSysInfo->BuildNumber;
  250. m_DumpData.m_sCSDVer = GetMinidumpString(m_pMiniDumpStartPtr, pSysInfo->CSDVersionRva);
  251. // Clean up
  252. pStreamStart = NULL;
  253. uStreamSize = 0;
  254. pmd = NULL;
  255. }
  256. else
  257. {
  258. return 1;
  259. }
  260. return 0;
  261. }
  262. int CMiniDumpReader::ReadExceptionStream()
  263. {
  264. LPVOID pStreamStart = NULL;
  265. ULONG uStreamSize = 0;
  266. MINIDUMP_DIRECTORY* pmd = NULL;
  267. BOOL bRead = FALSE;
  268. bRead = MiniDumpReadDumpStream(
  269. m_pMiniDumpStartPtr,
  270. ExceptionStream,
  271. &pmd,
  272. &pStreamStart,
  273. &uStreamSize);
  274. if(bRead)
  275. {
  276. MINIDUMP_EXCEPTION_STREAM* pExceptionStream = (MINIDUMP_EXCEPTION_STREAM*)pStreamStart;
  277. if(pExceptionStream!=NULL &&
  278. uStreamSize>=sizeof(MINIDUMP_EXCEPTION_STREAM))
  279. {
  280. m_DumpData.m_uExceptionThreadId = pExceptionStream->ThreadId;
  281. m_DumpData.m_uExceptionCode = pExceptionStream->ExceptionRecord.ExceptionCode;
  282. m_DumpData.m_uExceptionAddress = pExceptionStream->ExceptionRecord.ExceptionAddress;
  283. m_DumpData.m_pExceptionThreadContext =
  284. (CONTEXT*)(((LPBYTE)m_pMiniDumpStartPtr)+pExceptionStream->ThreadContext.Rva);
  285. CString sMsg;
  286. int nExcModuleRowID = GetModuleRowIdByAddress(m_DumpData.m_uExceptionAddress);
  287. if(nExcModuleRowID>=0)
  288. {
  289. sMsg.Format(_T("Unhandled exception at 0x%I64x in %s: 0x%x : %s"),
  290. m_DumpData.m_uExceptionAddress,
  291. m_DumpData.m_Modules[nExcModuleRowID].m_sModuleName,
  292. m_DumpData.m_uExceptionCode,
  293. _T("Exception description.")
  294. );
  295. }
  296. else
  297. {
  298. }
  299. m_DumpData.m_LoadLog.push_back(sMsg);
  300. }
  301. }
  302. else
  303. {
  304. CString sMsg;
  305. sMsg = _T("No exception information found in minidump.");
  306. m_DumpData.m_LoadLog.push_back(sMsg);
  307. return 1;
  308. }
  309. return 0;
  310. }
  311. int CMiniDumpReader::ReadModuleListStream()
  312. {
  313. LPVOID pStreamStart = NULL;
  314. ULONG uStreamSize = 0;
  315. MINIDUMP_DIRECTORY* pmd = NULL;
  316. BOOL bRead = FALSE;
  317. strconv_t strconv;
  318. bRead = MiniDumpReadDumpStream(
  319. m_pMiniDumpStartPtr,
  320. ModuleListStream,
  321. &pmd,
  322. &pStreamStart,
  323. &uStreamSize);
  324. if(bRead)
  325. {
  326. MINIDUMP_MODULE_LIST* pModuleStream = (MINIDUMP_MODULE_LIST*)pStreamStart;
  327. if(pModuleStream!=NULL)
  328. {
  329. ULONG32 uNumberOfModules = pModuleStream->NumberOfModules;
  330. ULONG32 i;
  331. for(i=0; i<uNumberOfModules; i++)
  332. {
  333. MINIDUMP_MODULE* pModule =
  334. (MINIDUMP_MODULE*)((LPBYTE)pModuleStream->Modules+i*sizeof(MINIDUMP_MODULE));
  335. CString sModuleName = GetMinidumpString(m_pMiniDumpStartPtr, pModule->ModuleNameRva);
  336. LPCWSTR szModuleName = strconv.t2w(sModuleName);
  337. DWORD64 dwBaseAddr = pModule->BaseOfImage;
  338. DWORD64 dwImageSize = pModule->SizeOfImage;
  339. CString sShortModuleName = sModuleName;
  340. int pos = -1;
  341. pos = sModuleName.ReverseFind('\\');
  342. if(pos>=0)
  343. sShortModuleName = sShortModuleName.Mid(pos+1);
  344. /*DWORD64 dwLoadResult = */SymLoadModuleExW(
  345. m_DumpData.m_hProcess,
  346. NULL,
  347. (PWSTR)szModuleName,
  348. NULL,
  349. dwBaseAddr,
  350. (DWORD)dwImageSize,
  351. NULL,
  352. 0);
  353. IMAGEHLP_MODULE64 modinfo;
  354. memset(&modinfo, 0, sizeof(IMAGEHLP_MODULE64));
  355. modinfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
  356. BOOL bModuleInfo = SymGetModuleInfo64(m_DumpData.m_hProcess,
  357. dwBaseAddr,
  358. &modinfo);
  359. MdmpModule m;
  360. if(!bModuleInfo)
  361. {
  362. m.m_bImageUnmatched = TRUE;
  363. m.m_bNoSymbolInfo = TRUE;
  364. m.m_bPdbUnmatched = TRUE;
  365. m.m_pVersionInfo = NULL;
  366. m.m_sImageName = sModuleName;
  367. m.m_sModuleName = sShortModuleName;
  368. m.m_uBaseAddr = dwBaseAddr;
  369. m.m_uImageSize = dwImageSize;
  370. }
  371. else
  372. {
  373. m.m_uBaseAddr = modinfo.BaseOfImage;
  374. m.m_uImageSize = modinfo.ImageSize;
  375. m.m_sModuleName = sShortModuleName;
  376. m.m_sImageName = modinfo.ImageName;
  377. m.m_sLoadedImageName = modinfo.LoadedImageName;
  378. m.m_sLoadedPdbName = modinfo.LoadedPdbName;
  379. m.m_pVersionInfo = &pModule->VersionInfo;
  380. m.m_bPdbUnmatched = modinfo.PdbUnmatched;
  381. BOOL bTimeStampMatched = pModule->TimeDateStamp == modinfo.TimeDateStamp;
  382. m.m_bImageUnmatched = !bTimeStampMatched;
  383. m.m_bNoSymbolInfo = !modinfo.GlobalSymbols;
  384. }
  385. m_DumpData.m_Modules.push_back(m);
  386. m_DumpData.m_ModuleIndex[m.m_uBaseAddr] = m_DumpData.m_Modules.size()-1;
  387. CString sMsg;
  388. if(m.m_bImageUnmatched)
  389. sMsg.Format(_T("Loaded '*%s'"), sModuleName);
  390. else
  391. sMsg.Format(_T("Loaded '%s'"), m.m_sLoadedImageName);
  392. if(m.m_bImageUnmatched)
  393. sMsg += _T(", No matching binary found.");
  394. else if(m.m_bPdbUnmatched)
  395. sMsg += _T(", No matching PDB file found.");
  396. else
  397. {
  398. if(m.m_bNoSymbolInfo)
  399. sMsg += _T(", No symbols loaded.");
  400. else
  401. sMsg += _T(", Symbols loaded.");
  402. }
  403. m_DumpData.m_LoadLog.push_back(sMsg);
  404. }
  405. }
  406. }
  407. else
  408. {
  409. return 1;
  410. }
  411. return 0;
  412. }
  413. int CMiniDumpReader::GetModuleRowIdByBaseAddr(DWORD64 dwBaseAddr)
  414. {
  415. std::map<DWORD64, size_t>::iterator it = m_DumpData.m_ModuleIndex.find(dwBaseAddr);
  416. if(it!=m_DumpData.m_ModuleIndex.end())
  417. return (int)it->second;
  418. return -1;
  419. }
  420. int CMiniDumpReader::GetModuleRowIdByAddress(DWORD64 dwAddress)
  421. {
  422. UINT i;
  423. for(i=0;i<m_DumpData.m_Modules.size();i++)
  424. {
  425. if(m_DumpData.m_Modules[i].m_uBaseAddr<=dwAddress &&
  426. dwAddress<m_DumpData.m_Modules[i].m_uBaseAddr+m_DumpData.m_Modules[i].m_uImageSize)
  427. return i;
  428. }
  429. return -1;
  430. }
  431. int CMiniDumpReader::GetThreadRowIdByThreadId(DWORD dwThreadId)
  432. {
  433. std::map<DWORD, size_t>::iterator it = m_DumpData.m_ThreadIndex.find(dwThreadId);
  434. if(it!=m_DumpData.m_ThreadIndex.end())
  435. return (int)it->second;
  436. return -1;
  437. }
  438. int CMiniDumpReader::ReadMemoryListStream()
  439. {
  440. LPVOID pStreamStart = NULL;
  441. ULONG uStreamSize = 0;
  442. MINIDUMP_DIRECTORY* pmd = NULL;
  443. BOOL bRead = FALSE;
  444. bRead = MiniDumpReadDumpStream(
  445. m_pMiniDumpStartPtr,
  446. MemoryListStream,
  447. &pmd,
  448. &pStreamStart,
  449. &uStreamSize);
  450. if(bRead)
  451. {
  452. MINIDUMP_MEMORY_LIST* pMemStream = (MINIDUMP_MEMORY_LIST*)pStreamStart;
  453. if(pMemStream!=NULL)
  454. {
  455. ULONG32 uNumberOfMemRanges = pMemStream->NumberOfMemoryRanges;
  456. ULONG i;
  457. for(i=0; i<uNumberOfMemRanges; i++)
  458. {
  459. MINIDUMP_MEMORY_DESCRIPTOR* pMemDesc = (MINIDUMP_MEMORY_DESCRIPTOR*)(&pMemStream->MemoryRanges[i]);
  460. MdmpMemRange mr;
  461. mr.m_u64StartOfMemoryRange = pMemDesc->StartOfMemoryRange;
  462. mr.m_uDataSize = pMemDesc->Memory.DataSize;
  463. mr.m_pStartPtr = (LPBYTE)m_pMiniDumpStartPtr+pMemDesc->Memory.Rva;
  464. m_DumpData.m_MemRanges.push_back(mr);
  465. }
  466. }
  467. }
  468. else
  469. {
  470. return 1;
  471. }
  472. return 0;
  473. }
  474. int CMiniDumpReader::ReadThreadListStream()
  475. {
  476. LPVOID pStreamStart = NULL;
  477. ULONG uStreamSize = 0;
  478. MINIDUMP_DIRECTORY* pmd = NULL;
  479. BOOL bRead = FALSE;
  480. bRead = MiniDumpReadDumpStream(
  481. m_pMiniDumpStartPtr,
  482. ThreadListStream,
  483. &pmd,
  484. &pStreamStart,
  485. &uStreamSize);
  486. if(bRead)
  487. {
  488. MINIDUMP_THREAD_LIST* pThreadList = (MINIDUMP_THREAD_LIST*)pStreamStart;
  489. if(pThreadList!=NULL &&
  490. uStreamSize>=sizeof(MINIDUMP_THREAD_LIST))
  491. {
  492. ULONG32 uThreadCount = pThreadList->NumberOfThreads;
  493. ULONG32 i;
  494. for(i=0; i<uThreadCount; i++)
  495. {
  496. MINIDUMP_THREAD* pThread = (MINIDUMP_THREAD*)(&pThreadList->Threads[i]);
  497. MdmpThread mt;
  498. mt.m_dwThreadId = pThread->ThreadId;
  499. mt.m_pThreadContext = (CONTEXT*)(((LPBYTE)m_pMiniDumpStartPtr)+pThread->ThreadContext.Rva);
  500. m_DumpData.m_Threads.push_back(mt);
  501. m_DumpData.m_ThreadIndex[mt.m_dwThreadId] = m_DumpData.m_Threads.size()-1;
  502. }
  503. }
  504. }
  505. else
  506. {
  507. return 1;
  508. }
  509. return 0;
  510. }
  511. int CMiniDumpReader::StackWalk(DWORD dwThreadId)
  512. {
  513. int nThreadIndex = GetThreadRowIdByThreadId(dwThreadId);
  514. if(m_DumpData.m_Threads[nThreadIndex].m_bStackWalk == TRUE)
  515. return 0; // Already done
  516. CONTEXT* pThreadContext = NULL;
  517. if(m_DumpData.m_Threads[nThreadIndex].m_dwThreadId==m_DumpData.m_uExceptionThreadId)
  518. pThreadContext = m_DumpData.m_pExceptionThreadContext;
  519. else
  520. pThreadContext = m_DumpData.m_Threads[nThreadIndex].m_pThreadContext;
  521. if(pThreadContext==NULL)
  522. return 1;
  523. // Make modifiable context
  524. CONTEXT Context;
  525. memcpy(&Context, pThreadContext, sizeof(CONTEXT));
  526. g_pMiniDumpReader = this;
  527. // Init stack frame with correct initial values
  528. // See this:
  529. // http://www.codeproject.com/KB/threads/StackWalker.aspx
  530. //
  531. // Given a current dbghelp, your code should:
  532. // 1. Always use StackWalk64
  533. // 2. Always set AddrPC to the current instruction pointer (Eip on x86, Rip on x64 and StIIP on IA64)
  534. // 3. Always set AddrStack to the current stack pointer (Esp on x86, Rsp on x64 and IntSp on IA64)
  535. // 4. Set AddrFrame to the current frame pointer when meaningful. On x86 this is Ebp, on x64 you
  536. // can use Rbp (but is not used by VC2005B2; instead it uses Rdi!) and on IA64 you can use RsBSP.
  537. // StackWalk64 will ignore the value when it isn't needed for unwinding.
  538. // 5. Set AddrBStore to RsBSP for IA64.
  539. STACKFRAME64 sf;
  540. memset(&sf, 0, sizeof(STACKFRAME64));
  541. sf.AddrPC.Mode = AddrModeFlat;
  542. sf.AddrFrame.Mode = AddrModeFlat;
  543. sf.AddrStack.Mode = AddrModeFlat;
  544. sf.AddrBStore.Mode = AddrModeFlat;
  545. DWORD dwMachineType = 0;
  546. switch(m_DumpData.m_uProcessorArchitecture)
  547. {
  548. #ifdef _X86_
  549. case PROCESSOR_ARCHITECTURE_INTEL:
  550. dwMachineType = IMAGE_FILE_MACHINE_I386;
  551. sf.AddrPC.Offset = pThreadContext->Eip;
  552. sf.AddrStack.Offset = pThreadContext->Esp;
  553. sf.AddrFrame.Offset = pThreadContext->Ebp;
  554. break;
  555. #endif
  556. #ifdef _AMD64_
  557. case PROCESSOR_ARCHITECTURE_AMD64:
  558. dwMachineType = IMAGE_FILE_MACHINE_AMD64;
  559. sf.AddrPC.Offset = pThreadContext->Rip;
  560. sf.AddrStack.Offset = pThreadContext->Rsp;
  561. sf.AddrFrame.Offset = pThreadContext->Rbp;
  562. break;
  563. #endif
  564. #ifdef _IA64_
  565. case PROCESSOR_ARCHITECTURE_AMD64:
  566. dwMachineType = IMAGE_FILE_MACHINE_IA64;
  567. sf.AddrPC.Offset = pThreadContext->StIIP;
  568. sf.AddrStack.Offset = pThreadContext->IntSp;
  569. sf.AddrFrame.Offset = pThreadContext->RsBSP;
  570. sf.AddrBStore.Offset = pThreadContext->RsBSP;
  571. break;
  572. #endif
  573. default:
  574. {
  575. assert(0);
  576. return 1; // Unsupported architecture
  577. }
  578. }
  579. for(;;)
  580. {
  581. BOOL bWalk = ::StackWalk64(
  582. dwMachineType, // machine type
  583. m_DumpData.m_hProcess, // our process handle
  584. (HANDLE)dwThreadId, // thread ID
  585. &sf, // stack frame
  586. dwMachineType==IMAGE_FILE_MACHINE_I386?NULL:(&Context), // used for non-I386 machines
  587. ReadProcessMemoryProc64, // our routine
  588. FunctionTableAccessProc64, // our routine
  589. GetModuleBaseProc64, // our routine
  590. NULL // safe to be NULL
  591. );
  592. if(!bWalk)
  593. break;
  594. MdmpStackFrame stack_frame;
  595. stack_frame.m_dwAddrPCOffset = sf.AddrPC.Offset;
  596. // Get module info
  597. IMAGEHLP_MODULE64 mi;
  598. memset(&mi, 0, sizeof(IMAGEHLP_MODULE64));
  599. mi.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
  600. BOOL bGetModuleInfo = SymGetModuleInfo64(m_DumpData.m_hProcess, sf.AddrPC.Offset, &mi);
  601. if(bGetModuleInfo)
  602. {
  603. stack_frame.m_nModuleRowID = GetModuleRowIdByBaseAddr(mi.BaseOfImage);
  604. }
  605. // Get symbol info
  606. DWORD64 dwDisp64;
  607. BYTE buffer[4096];
  608. SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer;
  609. sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
  610. sym_info->MaxNameLen = 4096-sizeof(SYMBOL_INFO)-1;
  611. BOOL bGetSym = SymFromAddr(
  612. m_DumpData.m_hProcess,
  613. sf.AddrPC.Offset,
  614. &dwDisp64,
  615. sym_info);
  616. if(bGetSym)
  617. {
  618. stack_frame.m_sSymbolName = CString(sym_info->Name, sym_info->NameLen);
  619. stack_frame.m_dw64OffsInSymbol = dwDisp64;
  620. }
  621. // Get source filename and line
  622. DWORD dwDisplacement;
  623. IMAGEHLP_LINE64 line;
  624. BOOL bGetLine = SymGetLineFromAddr64(
  625. m_DumpData.m_hProcess,
  626. sf.AddrPC.Offset,
  627. &dwDisplacement,
  628. &line);
  629. if(bGetLine)
  630. {
  631. stack_frame.m_sSrcFileName = line.FileName;
  632. stack_frame.m_nSrcLineNumber = line.LineNumber;
  633. }
  634. m_DumpData.m_Threads[nThreadIndex].m_StackTrace.push_back(stack_frame);
  635. }
  636. CString sStackTrace;
  637. UINT i;
  638. for(i=0; i<m_DumpData.m_Threads[nThreadIndex].m_StackTrace.size(); i++)
  639. {
  640. MdmpStackFrame& frame = m_DumpData.m_Threads[nThreadIndex].m_StackTrace[i];
  641. if(frame.m_sSymbolName.IsEmpty())
  642. continue;
  643. CString sModuleName;
  644. CString sAddrPCOffset;
  645. CString sSymbolName;
  646. CString sOffsInSymbol;
  647. CString sSourceFile;
  648. CString sSourceLine;
  649. if(frame.m_nModuleRowID>=0)
  650. {
  651. sModuleName = m_DumpData.m_Modules[frame.m_nModuleRowID].m_sModuleName;
  652. }
  653. sSymbolName = frame.m_sSymbolName;
  654. sAddrPCOffset.Format(_T("0x%I64x"), frame.m_dwAddrPCOffset);
  655. sSourceFile = frame.m_sSrcFileName;
  656. sSourceLine.Format(_T("%d"), frame.m_nSrcLineNumber);
  657. sOffsInSymbol.Format(_T("0x%I64x"), frame.m_dw64OffsInSymbol);
  658. CString str;
  659. str = sModuleName;
  660. if(!str.IsEmpty())
  661. str += _T("!");
  662. if(sSymbolName.IsEmpty())
  663. str += sAddrPCOffset;
  664. else
  665. {
  666. str += sSymbolName;
  667. str += _T("+");
  668. str += sOffsInSymbol;
  669. }
  670. if(!sSourceFile.IsEmpty())
  671. {
  672. int pos = sSourceFile.ReverseFind('\\');
  673. if(pos>=0)
  674. sSourceFile = sSourceFile.Mid(pos+1);
  675. str += _T(" [ ");
  676. str += sSourceFile;
  677. str += _T(": ");
  678. str += sSourceLine;
  679. str += _T(" ] ");
  680. }
  681. sStackTrace += str;
  682. sStackTrace += _T("\n");
  683. }
  684. if(!sStackTrace.IsEmpty())
  685. {
  686. strconv_t strconv;
  687. LPCSTR szStackTrace = strconv.t2utf8(sStackTrace);
  688. MD5 md5;
  689. MD5_CTX md5_ctx;
  690. unsigned char md5_hash[16];
  691. md5.MD5Init(&md5_ctx);
  692. md5.MD5Update(&md5_ctx, (unsigned char*)szStackTrace, (unsigned int)strlen(szStackTrace));
  693. md5.MD5Final(md5_hash, &md5_ctx);
  694. for(i=0; i<16; i++)
  695. {
  696. CString number;
  697. number.Format(_T("%02x"), md5_hash[i]);
  698. m_DumpData.m_Threads[nThreadIndex].m_sStackTraceMD5 += number;
  699. }
  700. }
  701. m_DumpData.m_Threads[nThreadIndex].m_bStackWalk = TRUE;
  702. return 0;
  703. }
  704. // This callback function is used by StackWalk64. It provides access to
  705. // ranges of memory stored in minidump file
  706. BOOL CALLBACK ReadProcessMemoryProc64(
  707. HANDLE hProcess,
  708. DWORD64 lpBaseAddress,
  709. PVOID lpBuffer,
  710. DWORD nSize,
  711. LPDWORD lpNumberOfBytesRead)
  712. {
  713. *lpNumberOfBytesRead = 0;
  714. // Validate input parameters
  715. if(hProcess!=g_pMiniDumpReader->m_DumpData.m_hProcess ||
  716. lpBaseAddress==NULL ||
  717. lpBuffer==NULL ||
  718. nSize==0)
  719. {
  720. // Invalid parameter
  721. return FALSE;
  722. }
  723. ULONG i;
  724. for(i=0; i<g_pMiniDumpReader->m_DumpData.m_MemRanges.size(); i++)
  725. {
  726. MdmpMemRange& mr = g_pMiniDumpReader->m_DumpData.m_MemRanges[i];
  727. if(lpBaseAddress>=mr.m_u64StartOfMemoryRange &&
  728. lpBaseAddress<mr.m_u64StartOfMemoryRange+mr.m_uDataSize)
  729. {
  730. DWORD64 dwOffs = lpBaseAddress-mr.m_u64StartOfMemoryRange;
  731. LONG64 lBytesRead = 0;
  732. if(mr.m_uDataSize-dwOffs>nSize)
  733. lBytesRead = nSize;
  734. else
  735. lBytesRead = mr.m_uDataSize-dwOffs;
  736. if(lBytesRead<=0 || nSize<lBytesRead)
  737. return FALSE;
  738. *lpNumberOfBytesRead = (DWORD)lBytesRead;
  739. memcpy(lpBuffer, (LPBYTE)mr.m_pStartPtr+dwOffs, (size_t)lBytesRead);
  740. return TRUE;
  741. }
  742. }
  743. return FALSE;
  744. }
  745. // This callback function is used by StackWalk64. It provides access to
  746. // function table stored in minidump file
  747. PVOID CALLBACK FunctionTableAccessProc64(
  748. HANDLE hProcess,
  749. DWORD64 AddrBase)
  750. {
  751. return SymFunctionTableAccess64(hProcess, AddrBase);
  752. }
  753. // This callback function is used by StackWalk64. It provides access to
  754. // module list stored in minidump file
  755. DWORD64 CALLBACK GetModuleBaseProc64(
  756. HANDLE hProcess,
  757. DWORD64 Address)
  758. {
  759. return SymGetModuleBase64(hProcess, Address);
  760. }