Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

867 lines
25 KiB

  1. /*
  2. Copyright 1999 Microsoft Corporation
  3. Simplified PCHealth stack trace output
  4. Walter Smith (wsmith)
  5. Rajesh Soy (nsoy) - modified 4/27/2000
  6. Rajesh Soy (nsoy) - reorganized code and cleaned it up, added comments 06/06/2000
  7. */
  8. #ifdef THIS_FILE
  9. #undef THIS_FILE
  10. #endif
  11. static char __szTraceSourceFile[] = __FILE__;
  12. #define THIS_FILE __szTraceSourceFile
  13. #include "stdafx.h"
  14. #define NOTRACE
  15. #include "logging.h"
  16. #include <imagehlp.h>
  17. #include <dbgtrace.h>
  18. using namespace std;
  19. // Forward declarations
  20. struct MODINFO;
  21. //
  22. // This code is largely stolen from PC Health's fault handler.
  23. // At some point we should probably refactor that and share the code.
  24. // However, this code has been redone to handle exceptions properly and
  25. // eliminate useless stuff.
  26. //
  27. struct MODINFO {
  28. TCHAR szFilename[MAX_PATH];
  29. TCHAR szFileDesc[MAX_PATH];
  30. TCHAR szVersion[MAX_PATH];
  31. TCHAR szCreationDate[MAX_PATH];
  32. TCHAR szMfr[MAX_PATH];
  33. DWORD dwFilesize;
  34. DWORD dwCheckSum;
  35. DWORD dwPDBSignature;
  36. DWORD dwPDBAge;
  37. TCHAR szPDBFile[MAX_PATH];
  38. UINT_PTR BaseAddress;
  39. };
  40. typedef MODINFO* PMODINFO;
  41. struct MPC_STACKFRAME {
  42. const MODINFO* pModule;
  43. DWORD dwSection;
  44. UINT_PTR Offset;
  45. };
  46. typedef MPC_STACKFRAME* PMPC_STACKFRAME;
  47. //
  48. // PDB debug directory structures
  49. //
  50. typedef struct NB10I // NB10 debug info
  51. {
  52. DWORD nb10; // NB10
  53. DWORD off; // offset, always 0
  54. DWORD sig;
  55. DWORD age;
  56. } NB10I;
  57. typedef struct cvinfo
  58. {
  59. NB10I nb10;
  60. char rgb[0x200 - sizeof(NB10I)];
  61. } CVINFO;
  62. typedef map<UINT_PTR,MODINFO> MODINFOMap;
  63. //
  64. // Routines defined here
  65. //
  66. void GetFileInfo( LPTSTR szFile, LPTSTR szWhat, LPTSTR szValue);
  67. void GetFileDateAndSize(MODINFO* pModule);
  68. BOOL DebugDirectoryIsUseful(LPVOID Pointer, ULONG Size);
  69. void GetPDBDebugInfo(MODINFO* pModule);
  70. bool GetLogicalAddress(PVOID addr, DWORD* pdwSection, UINT_PTR* pOffset, UINT_PTR* pbaseAddr);
  71. void AddDLLSubnode(SimpleXMLNode* pParentElt, LPCWSTR pTag, const MODINFO* pInfo);
  72. void AddFunctionSubnode(SimpleXMLNode* pParentElt, MPC_STACKFRAME* pFrame);
  73. void GenerateXMLStackTrace(PSTACKTRACEDATA pstd, SimpleXMLNode* pTopElt);
  74. //
  75. // ModuleCache class: Class to extrace information from binaries
  76. //
  77. class ModuleCache {
  78. public:
  79. ModuleCache() { }
  80. ~ModuleCache() { }
  81. const MODINFO* GetModuleInfo(UINT_PTR baseAddr);
  82. const MODINFO* GetEXEInfo();
  83. void AddDLLInfoSubnode(SimpleXMLNode* pParentElt);
  84. private:
  85. MODINFOMap mimap;
  86. };
  87. //
  88. // ModuleCache::GetModuleInfo: Extracts the following information given a base address
  89. // ModuleName, CompanyName, FileVersion, FileDescription, FileDateAndSize and PDBDebugInfo
  90. //
  91. const MODINFO*
  92. ModuleCache::GetModuleInfo(
  93. UINT_PTR baseAddr // [in] - baseAddress of binary
  94. )
  95. {
  96. TraceFunctEnter("ModuleCache::GetModuleInfo");
  97. //
  98. // Locate the base address in modinfo map if present
  99. //
  100. MODINFOMap::const_iterator it = mimap.find(baseAddr);
  101. DWORD dwRetVal = 0;
  102. if (it == mimap.end())
  103. {
  104. MODINFO mi;
  105. TCHAR szModName[ MAX_PATH ];
  106. mi.BaseAddress = baseAddr;
  107. mi.dwCheckSum = 0;
  108. //
  109. // Obtain the ModuleFileName
  110. //
  111. DebugTrace(0, "Calling GetModuleFileName");
  112. dwRetVal = GetModuleFileName((HMODULE) baseAddr, szModName, MAX_PATH ) ;
  113. if(0 == dwRetVal)
  114. {
  115. FatalTrace(0, "GetModuleFileName failed. Error: %ld", GetLastError());
  116. ThrowIfZero( dwRetVal );
  117. }
  118. ZeroMemory( mi.szFilename, MAX_PATH );
  119. //
  120. // Parse the Module name. GetModuleFileName returns filepaths of the form \??\filepath
  121. // if the user is not logged on.
  122. //
  123. if(( szModName[0] == '\\')&&( szModName[1] == '?') && ( szModName[2] == '?') && ( szModName[3] == '\\'))
  124. {
  125. DebugTrace(0, "Stripping the funny characters from infront of the module name");
  126. _tcscpy( mi.szFilename, &szModName[4]);
  127. }
  128. else
  129. {
  130. DebugTrace(0, "Normal module name");
  131. _tcscpy( mi.szFilename, szModName);
  132. }
  133. DebugTrace(0, "ModuleName: %ls", mi.szFilename);
  134. //
  135. // Obtain the CompanyName
  136. //
  137. DebugTrace(0, "Obtaining CompanyName");
  138. GetFileInfo(mi.szFilename, TEXT("CompanyName"), mi.szMfr);
  139. //
  140. // Obtain FileVersion
  141. //
  142. DebugTrace(0, "Obtaining FileVersion");
  143. GetFileInfo(mi.szFilename, TEXT("FileVersion"), mi.szVersion);
  144. //
  145. // Obtain FileDescription
  146. //
  147. DebugTrace(0, "Obtaining FileDescription");
  148. GetFileInfo(mi.szFilename, TEXT("FileDescription"), mi.szFileDesc);
  149. //
  150. // Obtain FileDateAndSize
  151. //
  152. DebugTrace(0, "Calling GetFileDateAndSize");
  153. GetFileDateAndSize(&mi);
  154. //
  155. // Obtain PDBDebugInfo
  156. //
  157. DebugTrace(0, "Calling GetPDBDebugInfo");
  158. GetPDBDebugInfo(&mi);
  159. //
  160. // Insert MODINFO into the ModInfo map
  161. //
  162. DebugTrace(0, "Calling mimap.insert");
  163. it = mimap.insert(MODINFOMap::value_type(baseAddr, mi)).first;
  164. }
  165. TraceFunctLeave();
  166. return &(*it).second;
  167. }
  168. //
  169. // ModuleCache::GetEXEInfo: Obtain information on binaries that are Exes
  170. //
  171. const MODINFO*
  172. ModuleCache::GetEXEInfo()
  173. {
  174. return GetModuleInfo((UINT_PTR) GetModuleHandle(NULL));
  175. }
  176. //
  177. // ModuleCache::AddDLLInfoSubnode: adds the information contained in the MODInfo Map
  178. // into a DLLINFO XML subnode
  179. void
  180. ModuleCache::AddDLLInfoSubnode(
  181. SimpleXMLNode* pParentElt // [in] - parent XML node
  182. )
  183. {
  184. _ASSERT(pParentElt != NULL);
  185. //
  186. // Create a DLLINFO subnode
  187. //
  188. SimpleXMLNode* pTopElt = pParentElt->AppendChild(wstring(L"DLLINFO"));
  189. //
  190. // Add DLL subnodes under DLLINFO node for each item contained in the MODInfo Map
  191. //
  192. for (MODINFOMap::const_iterator it = mimap.begin(); it != mimap.end(); it++)
  193. {
  194. AddDLLSubnode(pTopElt, L"DLL", &(*it).second);
  195. }
  196. }
  197. //
  198. //
  199. // GetFileInfo: This routine uses Version.Dll API to get requested file info
  200. // info is returned as a string in szValue
  201. //
  202. void
  203. GetFileInfo(
  204. LPTSTR szFile, // [in] file to get info for
  205. LPTSTR szWhat, // [in] may specify "FileVersion",
  206. // "CompanyName" or "FileDescription"
  207. LPTSTR szValue // [out] file info obtained
  208. )
  209. {
  210. DWORD dwSize;
  211. DWORD dwScratch;
  212. DWORD* pdwLang = NULL;
  213. DWORD dwLang;
  214. TCHAR szLang[MAX_PATH] = TEXT("");
  215. TCHAR szLocalValue[MAX_PATH] = TEXT("");
  216. LPTSTR szLocal;
  217. lstrcpy(szValue, TEXT(""));
  218. _ASSERT(szFile != NULL);
  219. _ASSERT(szWhat != NULL);
  220. _ASSERT(szValue != NULL);
  221. //
  222. // get fileinfo data size
  223. //
  224. dwSize = GetFileVersionInfoSize(szFile, &dwScratch);
  225. if (dwSize == 0)
  226. return;
  227. auto_ptr<BYTE> pFileInfo(new BYTE[dwSize]);
  228. //
  229. // get fileinfo data
  230. //
  231. ThrowIfZero(GetFileVersionInfo(szFile, 0, dwSize, (PVOID) pFileInfo.get()));
  232. //
  233. // set default language to english
  234. //
  235. dwLang = 0x040904E4;
  236. pdwLang = &dwLang;
  237. //
  238. // read language identifier and code page of file
  239. //
  240. if (VerQueryValue(pFileInfo.get(),
  241. TEXT("\\VarFileInfo\\Translation"),
  242. (PVOID *) &pdwLang,
  243. (UINT *) &dwScratch))
  244. {
  245. //
  246. // prepare query string - specify what we need ("FileVersion",
  247. // "CompanyName" or "FileDescription")
  248. //
  249. _stprintf(szLang,
  250. TEXT("\\StringFileInfo\\%04X%04X\\%s"),
  251. LOWORD(*pdwLang),
  252. HIWORD(*pdwLang),
  253. szWhat);
  254. szLocal = szLocalValue;
  255. //
  256. // query for the value using codepage from file
  257. //
  258. if (VerQueryValue(pFileInfo.get(),
  259. szLang,
  260. (PVOID *) &szLocal,
  261. (UINT *) &dwScratch))
  262. {
  263. lstrcpy(szValue,szLocal);
  264. return;
  265. }
  266. }
  267. //
  268. // if that fails, try Unicode
  269. //
  270. _stprintf(szLang,
  271. TEXT("\\StringFileInfo\\%04X04B0\\%s"),
  272. GetUserDefaultLangID(),
  273. szWhat);
  274. if (!VerQueryValue(pFileInfo.get(),
  275. szLang,
  276. (PVOID *) &szLocal,
  277. (UINT *) &dwScratch))
  278. {
  279. //
  280. // if that fails too, try Multilingual
  281. //
  282. _stprintf(szLang,
  283. TEXT("\\StringFileInfo\\%04X04E4\\%s"),
  284. GetUserDefaultLangID(),
  285. szWhat);
  286. if (!VerQueryValue(pFileInfo.get(),
  287. szLang,
  288. (PVOID *) &szLocal,
  289. (UINT *) &dwScratch))
  290. {
  291. //
  292. // and if that fails as well, try nullPage
  293. //
  294. _stprintf(szLang,
  295. TEXT("\\StringFileInfo\\%04X0000\\%s"),
  296. GetUserDefaultLangID(),
  297. szWhat);
  298. if (!VerQueryValue(pFileInfo.get(),
  299. szLang,
  300. (PVOID *) &szLocal,
  301. (UINT *) &dwScratch))
  302. {
  303. // giving up
  304. szValue[0] = 0;
  305. return;
  306. }
  307. }
  308. }
  309. //
  310. // successful; copy to return string
  311. //
  312. lstrcpy(szValue,szLocal);
  313. }
  314. //
  315. // GetFileDateAndSize: get file creation date and size
  316. //
  317. void
  318. GetFileDateAndSize(
  319. MODINFO* pModule // [in] [out] pointer to module node
  320. // fills file date and size fields
  321. )
  322. {
  323. TraceFunctEnter("GetFileDateAndSize");
  324. _ASSERT(pModule != NULL);
  325. SYSTEMTIME STCreationTime;
  326. WIN32_FIND_DATA FindData;
  327. lstrcpy(pModule->szCreationDate,TEXT(""));
  328. pModule->dwFilesize = 0;
  329. HANDLE hFind = FindFirstFile(pModule->szFilename,&FindData);
  330. if(INVALID_HANDLE_VALUE == hFind)
  331. {
  332. FatalTrace(0, "FindFirstFile on %ls failed. Error: %ld", pModule->szFilename, GetLastError());
  333. ThrowIfTrue(hFind == INVALID_HANDLE_VALUE);
  334. }
  335. // NO THROWS -- hFind will leak
  336. _ASSERT(FindData.ftCreationTime.dwLowDateTime ||
  337. FindData.ftCreationTime.dwHighDateTime);
  338. FileTimeToSystemTime(&(FindData.ftCreationTime),&STCreationTime);
  339. _stprintf(pModule->szCreationDate, _T("%d-%02d-%02dT%02d:%02d:%02d.%03d"),
  340. STCreationTime.wYear, STCreationTime.wMonth, STCreationTime.wDay,
  341. STCreationTime.wHour, STCreationTime.wMinute, STCreationTime.wSecond,
  342. STCreationTime.wMilliseconds);
  343. pModule->dwFilesize = (FindData.nFileSizeHigh * MAXDWORD) + FindData.nFileSizeLow;
  344. FindClose(hFind);
  345. TraceFunctLeave();
  346. }
  347. //
  348. // DebugDirectoryIsUseful: Check if this is an userful debug directory
  349. //
  350. BOOL DebugDirectoryIsUseful(LPVOID Pointer, ULONG Size)
  351. {
  352. return (Pointer != NULL) &&
  353. (Size >= sizeof(IMAGE_DEBUG_DIRECTORY)) &&
  354. ((Size % sizeof(IMAGE_DEBUG_DIRECTORY)) == 0);
  355. }
  356. //
  357. // GetPDBDebugInfo: Looks up the Debug Directory to get PDB Debug Info
  358. //
  359. void
  360. GetPDBDebugInfo(
  361. MODINFO* pModule // [in] [out] pointer to module node
  362. // fills PDB Signature, Age and Filename
  363. )
  364. {
  365. TraceFunctEnter("GetPDBDebugInfo");
  366. _ASSERT(pModule != NULL);
  367. HANDLE hMapping = NULL;
  368. PIMAGE_NT_HEADERS pntHeaders = NULL;
  369. void* pvImageBase = NULL;
  370. //
  371. // Open the Binary for reading
  372. //
  373. HANDLE hFile = CreateFile(pModule->szFilename,
  374. GENERIC_READ,
  375. FILE_SHARE_READ,
  376. NULL,
  377. OPEN_EXISTING,
  378. 0,
  379. NULL);
  380. if (INVALID_HANDLE_VALUE != hFile )
  381. {
  382. //
  383. // Create a FileMapping
  384. //
  385. hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  386. if (NULL != hMapping)
  387. {
  388. //
  389. // Map view to Memory
  390. //
  391. pvImageBase = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
  392. if (NULL != pvImageBase)
  393. {
  394. __try
  395. {
  396. //
  397. // Obtain the NtImage Headers
  398. //
  399. pntHeaders = ImageNtHeader(pvImageBase);
  400. if(NULL == pntHeaders)
  401. {
  402. goto done;
  403. }
  404. if (pntHeaders->OptionalHeader.MajorLinkerVersion >= 3 ||
  405. pntHeaders->OptionalHeader.MinorLinkerVersion >= 5)
  406. {
  407. //
  408. // Find the debug directory entries
  409. //
  410. ULONG cbDebugDirectories;
  411. PIMAGE_DEBUG_DIRECTORY pDebugDirectories = (PIMAGE_DEBUG_DIRECTORY)
  412. ImageDirectoryEntryToData(pvImageBase, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugDirectories);
  413. if (DebugDirectoryIsUseful(pDebugDirectories, cbDebugDirectories))
  414. {
  415. //
  416. // Find the codeview information
  417. //
  418. ULONG cDebugDirectories = cbDebugDirectories / sizeof(IMAGE_DEBUG_DIRECTORY);
  419. ULONG iDirectory;
  420. for (iDirectory=0; iDirectory<cDebugDirectories; iDirectory++)
  421. {
  422. PIMAGE_DEBUG_DIRECTORY pDir = &pDebugDirectories[iDirectory];
  423. if (pDir->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
  424. {
  425. LPVOID pv = (PCHAR)pvImageBase + pDir->PointerToRawData;
  426. ULONG cb = pDir->SizeOfData;
  427. //
  428. // Is it the NAME of a .PDB file rather than CV info itself?
  429. //
  430. NB10I* pnb10 = (NB10I*)pv;
  431. pModule->dwPDBSignature = pnb10->sig;
  432. pModule->dwPDBAge = pnb10->age;
  433. if (pnb10->nb10 == '01BN')
  434. {
  435. //
  436. // Got a PDB name, which immediately follows the NB10I; we take everything.
  437. //
  438. mbstowcs(pModule->szPDBFile, (char *)(pnb10+1), MAX_PATH);
  439. }
  440. else
  441. {
  442. //
  443. // Got the PDB Signature and Age
  444. // This information is used by the backend to
  445. // locate the PDB symbol file for this binary
  446. // to resolve symbols
  447. //
  448. pModule->dwPDBSignature = pnb10->sig;
  449. pModule->dwPDBAge = pnb10->age;
  450. }
  451. }
  452. }
  453. }
  454. }
  455. }
  456. __except(EXCEPTION_EXECUTE_HANDLER)
  457. {
  458. FatalTrace(0, "Unable to extract PDB information from binary");
  459. }
  460. }
  461. }
  462. }
  463. done:
  464. if(NULL != pvImageBase)
  465. {
  466. UnmapViewOfFile(pvImageBase);
  467. pvImageBase = NULL;
  468. }
  469. if(NULL != hMapping)
  470. {
  471. CloseHandle(hMapping);
  472. hMapping = NULL;
  473. }
  474. if(NULL != hFile)
  475. {
  476. CloseHandle(hFile);
  477. hFile = NULL;
  478. }
  479. TraceFunctLeave();
  480. }
  481. //
  482. // GetLogicalAddress: converts virtual address to section and offset by searching module's section table
  483. //
  484. bool
  485. GetLogicalAddress(
  486. PVOID addr, // [in] address to be resolved
  487. DWORD* pdwSection, // [out] section number
  488. UINT_PTR* pOffset, // [out] offset in section
  489. UINT_PTR* pbaseAddr // [out] base address of module
  490. )
  491. {
  492. MEMORY_BASIC_INFORMATION mbi;
  493. UINT_PTR baseAddr; // local base address of module
  494. IMAGE_DOS_HEADER* pDosHdr; // PE File Headers
  495. IMAGE_NT_HEADERS* pNTHdr;
  496. IMAGE_SECTION_HEADER* pSectionHdr;
  497. UINT_PTR rva; // relative virtual address of "addr"
  498. UINT_PTR sectionStart;
  499. UINT_PTR sectionEnd;
  500. UINT i;
  501. DWORD dwDump;
  502. DWORD dwRW;
  503. DWORD dwRetVQ;
  504. bool fFound = false;
  505. static DWORD s_dwSystemPageSize = 0;
  506. TraceFunctEnter("GetLogicalAddress");
  507. if (s_dwSystemPageSize == 0) {
  508. SYSTEM_INFO si;
  509. GetSystemInfo(&si);
  510. s_dwSystemPageSize = si.dwPageSize;
  511. }
  512. *pdwSection = 0;
  513. *pOffset = 0;
  514. *pbaseAddr = 0;
  515. //
  516. // addr should not be NULL
  517. //
  518. if(NULL == addr)
  519. {
  520. FatalTrace(0, "addr is NULL");
  521. goto done;
  522. }
  523. //
  524. // get page info for page containing "addr"
  525. //
  526. DebugTrace(0, "Calling VirtualQuery");
  527. dwRetVQ = VirtualQuery(addr, &mbi, sizeof(mbi));
  528. if(0 == dwRetVQ)
  529. {
  530. FatalTrace(0, "dwRetVQ is 0. Error: %ld", GetLastError());
  531. ThrowIfZero(dwRetVQ);
  532. }
  533. //
  534. // Just in case this goes wild on us...
  535. //
  536. __try {
  537. baseAddr = (UINT_PTR) mbi.AllocationBase;
  538. //
  539. // get relative virtual address corresponding to addr
  540. //
  541. rva = (UINT_PTR) addr - baseAddr;
  542. //
  543. // read Dos header of PE file
  544. //
  545. pDosHdr = (IMAGE_DOS_HEADER*) baseAddr;
  546. //
  547. // read NT header of PE file
  548. //
  549. pNTHdr = (IMAGE_NT_HEADERS*) (baseAddr + pDosHdr->e_lfanew);
  550. //
  551. // get section header address
  552. //
  553. pSectionHdr = (IMAGE_SECTION_HEADER*) ((UINT_PTR) IMAGE_FIRST_SECTION(pNTHdr) -
  554. (UINT_PTR) pNTHdr +
  555. baseAddr + pDosHdr->e_lfanew);
  556. //
  557. // step through section table to get to the section containing rva
  558. //
  559. DebugTrace(0, "stepping through section table...");
  560. for (i=0; i< pNTHdr->FileHeader.NumberOfSections; i++)
  561. {
  562. //
  563. // get section boundaries
  564. //
  565. sectionStart = pSectionHdr->VirtualAddress;
  566. sectionEnd = sectionStart +
  567. max(pSectionHdr->SizeOfRawData, pSectionHdr->Misc.VirtualSize);
  568. //
  569. // check if section envelopes rva
  570. //
  571. if ((rva >= sectionStart) && (rva <= sectionEnd))
  572. {
  573. *pdwSection = i+1;
  574. *pOffset = rva-sectionStart;
  575. *pbaseAddr = baseAddr;
  576. fFound = true;
  577. break;
  578. }
  579. //
  580. // move pointer to next section
  581. //
  582. pSectionHdr = pSectionHdr + sizeof(IMAGE_SECTION_HEADER);
  583. }
  584. }
  585. __except (EXCEPTION_EXECUTE_HANDLER) {
  586. _ASSERT(0);
  587. *pbaseAddr = NULL;
  588. }
  589. if (!fFound)
  590. _ASSERT(0);
  591. done:
  592. TraceFunctLeave();
  593. return fFound;
  594. }
  595. //
  596. // AddDLLSubnode: Inserts a DLL subnode to the given parent XML node
  597. //
  598. void
  599. AddDLLSubnode(
  600. SimpleXMLNode* pParentElt, // [in] - parent XML node
  601. LPCWSTR pTag, // [in] - name of subnode tag
  602. const MODINFO* pInfo // [in] - Module Information
  603. )
  604. {
  605. USES_CONVERSION;
  606. TraceFunctEnter("AddDLLSubnode");
  607. _ASSERT(pParentElt != NULL);
  608. _ASSERT(pTag != NULL);
  609. _ASSERT(pInfo != NULL);
  610. if(NULL == pInfo)
  611. {
  612. FatalTrace(0, "pInfo is NULL");
  613. throw E_FAIL;
  614. }
  615. //
  616. // Create the XML subnode
  617. //
  618. SimpleXMLNode* pNode = pParentElt->AppendChild(wstring(pTag));
  619. //
  620. // Set the various attributes of the DLL subnode
  621. //
  622. pNode->SetAttribute(wstring(L"FILENAME"), wstring(T2CW(pInfo->szFilename)));
  623. pNode->SetAttribute(wstring(L"VERSION"), wstring(T2CW(pInfo->szVersion)));
  624. pNode->SetAttribute(wstring(L"CREATIONDATE"), wstring(T2CW(pInfo->szCreationDate)));
  625. pNode->SetAttribute(wstring(L"CHECKSUM"), Hexify(pInfo->dwCheckSum));
  626. pNode->SetAttribute(wstring(L"PDBSIGNATURE"), Hexify(pInfo->dwPDBSignature));
  627. pNode->SetAttribute(wstring(L"PDBAGE"), Hexify(pInfo->dwPDBAge));
  628. pNode->SetAttribute(wstring(L"PDBFILE"), wstring(T2CW(pInfo->szPDBFile)));
  629. pNode->SetAttribute(wstring(L"FILESIZE"), Hexify(pInfo->dwFilesize));
  630. pNode->SetAttribute(wstring(L"BASEADDRESS"), Hexify(pInfo->BaseAddress));
  631. pNode->SetAttribute(wstring(L"MANUFACTURER"), wstring(T2CW(pInfo->szMfr)));
  632. pNode->SetAttribute(wstring(L"DESCRIPTION"), wstring(T2CW(pInfo->szFileDesc)));
  633. TraceFunctLeave();
  634. }
  635. //
  636. // AddFunctionSubnode: Adds a FUNCTION subnode to the given XML node
  637. //
  638. void
  639. AddFunctionSubnode(
  640. SimpleXMLNode* pParentElt, // [in] - parent XML node
  641. MPC_STACKFRAME* pFrame // [in] - Stack Frame
  642. )
  643. {
  644. USES_CONVERSION;
  645. _ASSERT(pParentElt != NULL);
  646. _ASSERT(pFrame != NULL);
  647. //
  648. // Create the FUNCTION subnode
  649. //
  650. SimpleXMLNode* pNode = pParentElt->AppendChild(wstring(L"FUNCTION"));
  651. //
  652. // Add the Attributes of the FUNCTION subnode
  653. //
  654. pNode->SetAttribute(wstring(L"FILENAME"), wstring(T2CW(pFrame->pModule->szFilename)));
  655. pNode->SetAttribute(wstring(L"SECTION"), Decimalify(pFrame->dwSection));
  656. pNode->SetAttribute(wstring(L"OFFSET"), Decimalify(pFrame->Offset));
  657. }
  658. //
  659. // GenerateXMLStackTrace: Generate a stack trace in PCHealth-standard XML format
  660. //
  661. void
  662. GenerateXMLStackTrace(
  663. PSTACKTRACEDATA pstd, // [in] - pointer to call stack
  664. SimpleXMLNode* pTopElt // [in] - pointer to STACKTRACE XML node
  665. )
  666. {
  667. TraceFunctEnter("GenerateXMLStackTrace");
  668. _ASSERT(pTopElt != NULL);
  669. ModuleCache modCache;
  670. //
  671. // Set the Tag Name
  672. //
  673. pTopElt->tag = wstring(L"STACKTRACE");
  674. //
  675. // Obtain the Info on the binary being commented
  676. //
  677. DebugTrace(0, "Calling modCache.GetEXEInfo");
  678. const MODINFO* pExeInfo = modCache.GetEXEInfo();
  679. //
  680. // Add the info collected as a EXEINFO subnode under STACKTRACE
  681. //
  682. DebugTrace(0, "Calling AddDLLSubnode");
  683. AddDLLSubnode(pTopElt, L"EXEINFO", pExeInfo);
  684. //
  685. // Create a CALLSTACK subnode under STACKTRACE, but only if pstd is not NULL
  686. //
  687. if (pstd != NULL) {
  688. DebugTrace(0, "Adding CALLSTACK element");
  689. SimpleXMLNode* pCallStackElt = pTopElt->AppendChild(wstring(L"CALLSTACK"));
  690. ULONG_PTR stackBase;
  691. stackBase = (ULONG_PTR)pstd;
  692. DWORD dwValue;
  693. ULONG ulFrames;
  694. ulFrames = pstd->nCallers;
  695. bool fProceed = 1;
  696. PVOID caller;
  697. int iFrame = 0;
  698. //
  699. // Step through the call stack
  700. //
  701. caller = (int *)pstd->callers;
  702. while(caller != 0)
  703. {
  704. MPC_STACKFRAME frame;
  705. UINT_PTR modBase;
  706. //
  707. // Obtain the Logical Address for each caller
  708. //
  709. DebugTrace(0, "GetLogicalAddress");
  710. if (GetLogicalAddress(caller, &frame.dwSection, &frame.Offset, &modBase))
  711. {
  712. //
  713. // Get Module Information for this caller
  714. //
  715. frame.pModule = modCache.GetModuleInfo(modBase);
  716. //
  717. // Add the ModuleInformation obtained as a FUNCTION subnode under the CALLSTACK node
  718. //
  719. DebugTrace(0, "Calling AddFunctionSubnode");
  720. AddFunctionSubnode(pCallStackElt, &frame);
  721. }
  722. DebugTrace(0, "iFrame: %ld", iFrame);
  723. caller = pstd->callers[iFrame];
  724. iFrame++;
  725. }
  726. //
  727. // Now, add the DLLINFO subnode. This must happen after all the GetModuleInfo calls so the cache
  728. // is populated with all the necessary info.
  729. //
  730. DebugTrace(0, "Calling AddDLLInfoSubnode");
  731. modCache.AddDLLInfoSubnode(pTopElt);
  732. }
  733. TraceFunctLeave();
  734. }