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.

584 lines
16 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999 - 2000
  6. //
  7. // File: dmpfile.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. // DmpFile.cpp: implementation of the CDmpFile class.
  11. //
  12. //////////////////////////////////////////////////////////////////////
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include "DmpFile.h"
  16. #include "Globals.h"
  17. #include "UtilityFunctions.h"
  18. #include "ProgramOptions.h"
  19. #include "ProcessInfo.h"
  20. #include "Modules.h"
  21. #include "FileData.h"
  22. #include "ModuleInfoCache.h"
  23. #include "ModuleInfo.h"
  24. // Let's implement the DebugOutputCallback for the DBGENG... it'll be cool to have the debugger
  25. // spit out info to us when it is running...
  26. STDMETHODIMP
  27. OutputCallbacks::QueryInterface(
  28. THIS_
  29. IN REFIID InterfaceId,
  30. OUT PVOID* Interface
  31. )
  32. {
  33. *Interface = NULL;
  34. if (IsEqualIID(InterfaceId, IID_IUnknown) ||
  35. IsEqualIID(InterfaceId, IID_IDebugOutputCallbacks))
  36. {
  37. *Interface = (IDebugOutputCallbacks *)this;
  38. AddRef();
  39. return S_OK;
  40. }
  41. else
  42. {
  43. return E_NOINTERFACE;
  44. }
  45. }
  46. STDMETHODIMP_(ULONG)
  47. OutputCallbacks::AddRef(
  48. THIS
  49. )
  50. {
  51. // This class is designed to be static so
  52. // there's no true refcount.
  53. return 1;
  54. }
  55. STDMETHODIMP_(ULONG)
  56. OutputCallbacks::Release(
  57. THIS
  58. )
  59. {
  60. // This class is designed to be static so
  61. // there's no true refcount.
  62. return 0;
  63. }
  64. STDMETHODIMP
  65. OutputCallbacks::Output(
  66. THIS_
  67. IN ULONG Mask,
  68. IN PCSTR Text
  69. )
  70. {
  71. HRESULT Status = S_OK;
  72. if (!g_lpProgramOptions->GetMode(CProgramOptions::QuietMode) && (Mask & DEBUG_OUTPUT_NORMAL))
  73. {
  74. printf(Text);
  75. }
  76. return Status;
  77. }
  78. //////////////////////////////////////////////////////////////////////
  79. // Construction/Destruction
  80. //////////////////////////////////////////////////////////////////////
  81. CDmpFile::CDmpFile()
  82. {
  83. m_szDmpFilePath = NULL;
  84. m_szSymbolPath = NULL;
  85. m_fDmpInitialized = false;
  86. m_pIDebugClient = NULL;
  87. m_pIDebugControl = NULL;
  88. m_pIDebugSymbols = NULL;
  89. m_pIDebugDataSpaces = NULL;
  90. m_DumpClass = DEBUG_CLASS_UNINITIALIZED;
  91. m_DumpClassQualifier = 0;
  92. }
  93. CDmpFile::~CDmpFile()
  94. {
  95. if (m_fDmpInitialized)
  96. {
  97. // Let's ensure that our debug output is set to normal (at least)
  98. //m_pIDebugClient->GetOutputMask(&OutMask);
  99. //OutMask = ~DEBUG_OUTPUT_NORMAL;
  100. m_pIDebugClient->SetOutputMask(0);
  101. // Let's be as least intrusive as possible...
  102. m_pIDebugClient->EndSession(DEBUG_END_ACTIVE_DETACH);
  103. }
  104. if (m_szDmpFilePath)
  105. delete [] m_szDmpFilePath;
  106. if (m_szSymbolPath)
  107. delete [] m_szSymbolPath;
  108. }
  109. OutputCallbacks g_OutputCb;
  110. bool CDmpFile::Initialize(CFileData * lpOutputFile)
  111. {
  112. HRESULT Hr;
  113. ULONG g_ExecStatus = DEBUG_STATUS_NO_DEBUGGEE;
  114. LPTSTR tszExpandedString = NULL;
  115. bool fReturn = false;
  116. // Let's save off big objects so we don't have to keep passing this to
  117. // our methods...
  118. m_lpOutputFile = lpOutputFile;
  119. // The DBGENG is somewhat ANSI oriented...
  120. m_szDmpFilePath = CUtilityFunctions::CopyTSTRStringToAnsi(g_lpProgramOptions->GetDmpFilePath(), m_szDmpFilePath, 0);
  121. // Create our interface pointer to do our Debug Work...
  122. if (FAILED(Hr = DebugCreate(IID_IDebugClient, (void **)&m_pIDebugClient)))
  123. goto cleanup;
  124. // Let's query for IDebugControl interface (we need it to determine debug type easily)...
  125. // Let's query for IDebugSymbols interface as we need it to receive module info...
  126. // Let's query for IDebugDataSpaces interface as we need it to read DMP memory...
  127. if (
  128. FAILED(Hr = m_pIDebugClient->QueryInterface(IID_IDebugControl,(void **)&m_pIDebugControl)) ||
  129. FAILED(Hr = m_pIDebugClient->QueryInterface(IID_IDebugSymbols,(void **)&m_pIDebugSymbols)) ||
  130. FAILED(Hr = m_pIDebugClient->QueryInterface(IID_IDebugDataSpaces,(void **)&m_pIDebugDataSpaces))
  131. )
  132. {
  133. _tprintf(TEXT("Error: DBGENG Interfaces required were not found!\n"));
  134. goto cleanup;
  135. }
  136. // Set callbacks.
  137. if (FAILED(Hr = m_pIDebugClient->SetOutputCallbacks(&g_OutputCb)) //||
  138. //FAILED(Hr = m_pIDebugClient->SetEventCallbacks(&g_DebugEventCallbacks))
  139. )
  140. {
  141. //
  142. //
  143. //
  144. _tprintf(TEXT("Error: DBGENG - Unable to SetOutputCallbacks!\n"));
  145. goto cleanup;
  146. }
  147. DWORD OutMask;
  148. // Let's ensure that our debug output is set to normal (at least)
  149. OutMask = m_pIDebugClient->GetOutputMask(&OutMask);
  150. m_pIDebugClient->SetOutputMask(OutMask | DEBUG_OUTPUT_NORMAL);
  151. // Set our symbol path... this is required prior to a "reload" of modules...
  152. // The DBGENG is somewhat ASCII oriented... we need an environment-expanded string converted
  153. // to an ASCII string...
  154. tszExpandedString = CUtilityFunctions::ExpandPath(g_lpProgramOptions->GetSymbolPath());
  155. if (!tszExpandedString)
  156. goto cleanup;
  157. m_szSymbolPath = CUtilityFunctions::CopyTSTRStringToAnsi( tszExpandedString, m_szSymbolPath, 0);
  158. // It's a bit premature to set this now... but it's required by DBGENG.DLL before a reload...
  159. if (FAILED(Hr = m_pIDebugSymbols->SetSymbolPath(m_szSymbolPath)))
  160. {
  161. goto cleanup;
  162. }
  163. // Let's open the dump...
  164. if (FAILED(Hr = m_pIDebugClient->OpenDumpFile(m_szDmpFilePath)))
  165. {
  166. goto cleanup;
  167. }
  168. // Get Initial Execution state.
  169. if (FAILED(Hr = m_pIDebugControl->GetExecutionStatus(&g_ExecStatus)))
  170. {
  171. _tprintf(TEXT("Unable to get execution status! Hr=0x%x\n"), Hr);
  172. goto cleanup;
  173. }
  174. if (g_ExecStatus != DEBUG_STATUS_NO_DEBUGGEE)
  175. {
  176. // I think we'll work just fine?
  177. _tprintf(TEXT("Debug Session is already active!\n"));
  178. // goto cleanup;
  179. }
  180. // What type of dump did we get?
  181. if (FAILED(Hr = m_pIDebugControl->GetDebuggeeType(&m_DumpClass, &m_DumpClassQualifier)))
  182. {
  183. goto cleanup;
  184. }
  185. //
  186. m_pIDebugClient->SetOutputMask(0); // Temporarily suppress this stuff...
  187. //
  188. // All the good stuff happens here... modules load, etc.. we could suppress all the output
  189. // but it's cool to watch...
  190. //
  191. if (FAILED(Hr = m_pIDebugControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)))
  192. {
  193. goto cleanup;
  194. }
  195. // Restore output!
  196. m_pIDebugClient->SetOutputMask(OutMask | DEBUG_OUTPUT_NORMAL);
  197. // Yee haa... we got something...
  198. m_fDmpInitialized = true;
  199. fReturn = true;
  200. cleanup:
  201. if (tszExpandedString)
  202. delete [] tszExpandedString;
  203. return fReturn;
  204. }
  205. bool CDmpFile::CollectData(CProcessInfo ** lplpProcessInfo, CModules ** lplpModules, CModuleInfoCache * lpModuleInfoCache)
  206. {
  207. bool fReturn = false;
  208. // Okay... first order of business is to decide what we need to collect...
  209. // Collect information from the file based on it's type...
  210. if (IsUserDmpFile())
  211. {
  212. // Second, order of business is to prepare for collecting info about the
  213. // process in the USER.DMP file...
  214. (*lplpProcessInfo) = new CProcessInfo();
  215. if ((*lplpProcessInfo) == NULL)
  216. goto cleanup;
  217. if (!(*lplpProcessInfo)->Initialize(lpModuleInfoCache, NULL, m_lpOutputFile, this))
  218. goto cleanup;
  219. } else
  220. {
  221. (*lplpModules) = new CModules();
  222. if ((*lplpModules) == NULL)
  223. goto cleanup;
  224. if (!(*lplpModules)->Initialize(lpModuleInfoCache, NULL, m_lpOutputFile, this))
  225. goto cleanup;
  226. }
  227. if (!EumerateModulesFromDmp(lpModuleInfoCache, *lplpProcessInfo, *lplpModules))
  228. goto cleanup;
  229. fReturn = true;
  230. cleanup:
  231. return fReturn;
  232. }
  233. //
  234. // Combined DMP Enumeration Code
  235. //
  236. bool CDmpFile::EumerateModulesFromDmp(CModuleInfoCache * lpModuleInfoCache, CProcessInfo * lpProcessInfo, CModules * lpModules)
  237. {
  238. //
  239. // Consult DumpModuleTable in Ntsym.cpp for ideas...
  240. //
  241. CModuleInfo * lpModuleInfo;
  242. HRESULT Hr;
  243. ULONG ulNumberOfLoadedModules;
  244. ULONG ulNumberOfUnloadedModules;
  245. ULONG64 Base;
  246. char szImageNameBuffer[_MAX_PATH];
  247. TCHAR tszModulePath[_MAX_PATH];
  248. // TCHAR tszModuleFilePath[_MAX_PATH];
  249. TCHAR tszModuleFileName[_MAX_FNAME];
  250. TCHAR tszModuleFileExtension[_MAX_EXT];
  251. bool fNew, fProcessNameFound = false;
  252. bool fUserDmp = IsUserDmpFile();
  253. // How many modules were found?
  254. if (FAILED(Hr = m_pIDebugSymbols->GetNumberModules(&ulNumberOfLoadedModules, &ulNumberOfUnloadedModules)))
  255. {
  256. _tprintf(TEXT("Unable to enumerate any modules in the DMP file!\n"));
  257. return false;
  258. }
  259. if (!g_lpProgramOptions->GetMode(CProgramOptions::QuietMode))
  260. {
  261. _tprintf(TEXT("\n%-8s %-8s %-30s %s\n"), TEXT("Start"),
  262. TEXT("End"),
  263. TEXT("Module Name"),
  264. TEXT("Time/Date"));
  265. }
  266. //
  267. // Enumerate through the modules in the DMP file...
  268. //
  269. for (unsigned int i = 0; i < ulNumberOfLoadedModules; i++)
  270. {
  271. // First, we get the Base address by our index
  272. if (FAILED(Hr = m_pIDebugSymbols->GetModuleByIndex(i, &Base)))
  273. {
  274. _tprintf(TEXT("Failed getting base address of module number %d\n"), i);
  275. continue; // try the next?
  276. }
  277. // Second, we get the name from our base address
  278. ULONG ulImageNameSize;
  279. //
  280. // This can return both the ImageNameBuffer and a ModuleNameBuffer...
  281. // The ImageNameBuffer typically contains the entire module name like (MODULE.DLL),
  282. // whereas the ModuleNameBuffer is typically just the module name like (MODULE).
  283. //
  284. if (FAILED(Hr = m_pIDebugSymbols->GetModuleNames( DEBUG_ANY_ID, // Use Base address
  285. Base, // Base address from above
  286. szImageNameBuffer,
  287. _MAX_PATH,
  288. &ulImageNameSize,
  289. NULL,
  290. 0,
  291. NULL,
  292. NULL,
  293. 0,
  294. NULL)))
  295. {
  296. _tprintf(TEXT("Failed getting name of module at base 0x%x\n"), Base);
  297. continue; // try the next?
  298. }
  299. // Convert the string to something we can use...
  300. CUtilityFunctions::CopyAnsiStringToTSTR(szImageNameBuffer, tszModulePath, _MAX_PATH);
  301. // Third, we can now get whatever we want from memory...
  302. if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszModulePath))
  303. continue;
  304. // Okay, let's go ahead and get a ModuleInfo Object from our cache...
  305. // If pfNew returns TRUE, then this object is new and we'll need
  306. // to populate it with data...
  307. lpModuleInfo = lpModuleInfoCache->AddNewModuleInfoObject(tszModulePath, &fNew);
  308. if (false == fNew)
  309. {
  310. // We may have the object in the cache... now we need to
  311. // save a pointer to this object in our Process Info list
  312. if (fUserDmp )
  313. {
  314. lpProcessInfo->AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
  315. } else
  316. {
  317. lpModules->AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
  318. }
  319. continue;
  320. }
  321. // Not in the cache... so we need to init it, and get the module info...
  322. if (!lpModuleInfo->Initialize(NULL, m_lpOutputFile, this))
  323. {
  324. return false; // Hmmm... memory error?
  325. }
  326. //
  327. // Okay, get the module info from the DMP file...
  328. //
  329. if (lpModuleInfo->GetModuleInfo(tszModulePath, true, Base) )
  330. {
  331. // We may have the object in the cache... now we need to
  332. // save a pointer to this object in our Process Info list
  333. if (fUserDmp)
  334. {
  335. lpProcessInfo->AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
  336. } else
  337. {
  338. lpModules->AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
  339. }
  340. } else
  341. {
  342. // Continue back to try another module on error...
  343. continue;
  344. }
  345. // Try and patch up the original name of the module...
  346. // Save the current module path as the DBG stuff
  347. // We'll tack on .DBG to roll through our own code correctly...
  348. _tsplitpath(tszModulePath, NULL, NULL, tszModuleFileName, tszModuleFileExtension);
  349. //_tcscpy(tszModulePath, tszModuleFileName);
  350. //_tcscpy(tszModuleFileName, tszModulePath);
  351. /* if (*tszModuleFileExtension == '\0')
  352. {
  353. _tcscat(tszModulePath, TEXT(".DBG"));
  354. } else
  355. {
  356. _tcscat(tszModulePath, tszModuleFileExtension);
  357. }
  358. */
  359. if ( (lpModuleInfo->GetPESymbolInformation() == CModuleInfo::SYMBOLS_DBG) ||
  360. (lpModuleInfo->GetPESymbolInformation() == CModuleInfo::SYMBOLS_DBG_AND_PDB) )
  361. {
  362. // Append .DBG to our module name
  363. _tcscat(tszModuleFileName, TEXT(".DBG"));
  364. lpModuleInfo->SetDebugDirectoryDBGPath(tszModuleFileName);
  365. /*
  366. // Ordinarily this seems very dangerous.. but the size of the new string
  367. // will be less than the original, so this should be safe... we hope?!
  368. if (_tcsicmp(&tszModulePath[_tcslen(tszModulePath)-4], TEXT(".DBG")) == 0)
  369. {
  370. _tsplitpath(tszModulePath, NULL, tszModuleFilePath, tszModuleFileName, NULL);
  371. if (_tcslen(tszModuleFilePath)==4)
  372. {
  373. tszModuleFilePath[_tcslen(tszModuleFilePath)-1] = 0;
  374. _tcscpy(tszModulePath, tszModuleFileName);
  375. _tcscat(tszModulePath, TEXT("."));
  376. _tcscat(tszModulePath, tszModuleFilePath);
  377. } else if ( lpModuleInfo->IsDLL() )
  378. {
  379. _tcscpy(tszModulePath, tszModuleFileName);
  380. _tcscat(tszModulePath, TEXT(".DLL"));
  381. } else
  382. {
  383. _tcscpy(tszModulePath, tszModuleFileName);
  384. _tcscat(tszModulePath, TEXT(".EXE"));
  385. }
  386. } else
  387. {
  388. // We didn't find a .DBG extension... let's tack on a guess...
  389. if ( lpModuleInfo->IsDLL() )
  390. {
  391. _tcscat(tszModulePath, TEXT(".DLL"));
  392. } else
  393. {
  394. _tcscat(tszModulePath, TEXT(".EXE"));
  395. }
  396. }
  397. */
  398. } else if (lpModuleInfo->GetPESymbolInformation() == CModuleInfo::SYMBOLS_PDB)
  399. {
  400. if (lpModuleInfo->GetDebugDirectoryPDBPath())
  401. {
  402. /* // Try and translate the module name to something friendlier
  403. _tsplitpath(lpModuleInfo->GetDebugDirectoryPDBPath(), NULL, NULL, tszModuleFileName, NULL);
  404. // Compose the name by appending the extension (we hope if it is not a EXE it will
  405. // be a DLL (with that extension)..
  406. if ( lpModuleInfo->IsDLL() )
  407. {
  408. _tcscpy(tszModulePath, tszModuleFileName);
  409. _tcscat(tszModulePath, TEXT(".DLL"));
  410. } else
  411. {
  412. _tcscpy(tszModulePath, tszModuleFileName);
  413. _tcscat(tszModulePath, TEXT(".EXE"));
  414. }
  415. */
  416. } else
  417. {
  418. //
  419. // Unfortunately, we can't find the PDB Imagepath in the DMP file... so we'll
  420. // just guess what it would be...
  421. //
  422. // Append .PDB to our module name
  423. _tcscat(tszModuleFileName, TEXT(".PDB"));
  424. lpModuleInfo->SetPEDebugDirectoryPDBPath(tszModuleFileName);
  425. // Compose the name by appending the extension (we hope if it is not a EXE it will
  426. // be a DLL (with that extension)... Also, by this point we MAY have found an image
  427. // name like EXE\MODULE.DBG... we want to strip off the trailing .DBG before appending...
  428. /* unsigned int cbModulePathLength = _tcslen(tszModulePath);
  429. if ( cbModulePathLength > 4) // Look to see if this exceeds chars before doing this next operation..
  430. {
  431. if (_tcsicmp(&tszModulePath[cbModulePathLength-4], TEXT(".DBG")) == 0)
  432. {
  433. // We found a .DBG extension... let's nuke it...
  434. tszModulePath[cbModulePathLength-4] = '\0';
  435. }
  436. }
  437. // Append the appropriate extension...
  438. if ( lpModuleInfo->IsDLL() )
  439. {
  440. _tcscat(tszModulePath, TEXT(".DLL"));
  441. } else
  442. {
  443. _tcscat(tszModulePath, TEXT(".EXE"));
  444. }
  445. */
  446. }
  447. }
  448. // Now, let's remove the extra path bits...
  449. _tsplitpath(tszModulePath, NULL, NULL, tszModuleFileName, tszModuleFileExtension);
  450. _tcscpy(tszModulePath, tszModuleFileName);
  451. _tcscat(tszModulePath, tszModuleFileExtension);
  452. // Save the current module path as the DBG stuff
  453. lpModuleInfo->SetPEImageModulePath(tszModulePath);
  454. // Save the current module name as well...
  455. lpModuleInfo->SetPEImageModuleName(tszModulePath);
  456. // Hey... if this is not a DLL, then it's probably the EXE!!!
  457. if (fUserDmp && !fProcessNameFound)
  458. {
  459. if (!lpModuleInfo->IsDLL() )
  460. {
  461. lpProcessInfo->SetProcessName(tszModulePath);
  462. fProcessNameFound = true;
  463. }
  464. }
  465. // Filter out garbage.
  466. if (!g_lpProgramOptions->GetMode(CProgramOptions::QuietMode))
  467. {
  468. time_t time = lpModuleInfo->GetPEImageTimeDateStamp();
  469. if (time)
  470. {
  471. _tprintf(TEXT("%08x %08x %-30s %s"), (ULONG)Base,
  472. (ULONG)Base+(ULONG)lpModuleInfo->GetPEImageSizeOfImage(),
  473. tszModulePath,
  474. _tctime(&time));
  475. } else
  476. {
  477. _tprintf(TEXT("%08x %08x %-30s Unknown\n"), (ULONG)Base,
  478. (ULONG)Base+(ULONG)lpModuleInfo->GetPEImageSizeOfImage(),
  479. tszModulePath);
  480. }
  481. }
  482. }
  483. return (ulNumberOfLoadedModules != 0);
  484. }