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.

2766 lines
80 KiB

  1. /*++
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. umdh.c
  5. Abstract:
  6. Quick and not-so-dirty user-mode dh for heap.
  7. Author(s):
  8. Tim Fleehart (TimF) 18-Jun-1999
  9. Silviu Calinoiu (SilviuC) 22-Feb-2000
  10. Revision History:
  11. TimF 18-Jun-99 Initial version
  12. SilviuC 30-Jun-00 TIMF_DBG converted to -v option
  13. SilviuC 06-Feb-00 Massage the code in preparation for speedup fixes
  14. ChrisW 22-Mar-01 Added process suspend code
  15. --*/
  16. //
  17. // Wish List
  18. //
  19. // [-] Option to dump as much as possible without any symbols
  20. // [-] Switch to dbghelp.dll library (get rid of imagehlp.dll)
  21. // [+] Fast symbol lookup
  22. // [+] Faster stack database manipulation
  23. // [-] Faster heap metadata manipulation
  24. // [+] Better memory management for huge processes
  25. // [+] More debug info for PSS issues
  26. // [+] File, line info and umdh version for each reported error (helps PSS).
  27. // [+] Cache for read from target virtual space in case we do it repeatedly.
  28. // [+] Set a symbols path automatically
  29. // [+] Continue to work even if you get errors from imagehlp functions.
  30. //
  31. // [-] Use (if present) dbgexts.dlls library (print file, line info, etc.)
  32. // [-] Integrate dhcmp type of functionality and new features
  33. // [-] No symbols required for page heap groveling (use magic patterns)
  34. // [-] Load/save raw trace database (based on start address)
  35. // [-] Consistency check for a raw trace database
  36. // [-] Log symbol file required for unresolved stacks
  37. // [-] Option to do partial dumps (e.g. only ole32 related).
  38. //
  39. //
  40. // Bugs
  41. //
  42. // [-] Partial copy error when dumping csrss.
  43. // [-] (null) function names in the dump once in a while.
  44. // [-] we can get error reads because the process is not suspended (heaps get destroyed etc.)
  45. // [-] Perf problems have been reported
  46. // [-] Work even if suspend permission not available
  47. //
  48. //
  49. // Versioning
  50. //
  51. // 5.1.001 - standard Whistler version (back compatible with Windows 2000)
  52. // 5.1.002 - umdh works now on IA64
  53. // 5.1.003 - allows target process to be suspended
  54. //
  55. #define UMDH_VERSION "5.1.003 "
  56. #define UMDH_OS_MAJOR_VERSION 5
  57. #define UMDH_OS_MINOR_VERSION 1
  58. #include <ctype.h>
  59. #include <stdlib.h>
  60. #include <stdio.h>
  61. #include <nt.h>
  62. #include <ntrtl.h>
  63. #include <nturtl.h>
  64. #include <ntos.h>
  65. #define NOWINBASEINTERLOCK
  66. #include <windows.h>
  67. #include <lmcons.h>
  68. // #include <imagehlp.h>
  69. #include <dbghelp.h>
  70. #include <heap.h>
  71. #include <heappagi.h>
  72. #include <stktrace.h>
  73. #include "types.h"
  74. #include "symbols.h"
  75. #include "miscellaneous.h"
  76. #include "database.h"
  77. #include "heapwalk.h"
  78. #include "dhcmp.h"
  79. #include "ntpsapi.h"
  80. //
  81. // FlaggedTrace holds the trace index of which we want to show all allocated
  82. // blocks, or one of two flag values, 0, to dump all, or SHOW_NO_ALLOC_BLOCKS
  83. // to dump none.
  84. //
  85. ULONG FlaggedTrace = SHOW_NO_ALLOC_BLOCKS;
  86. BOOL
  87. UmdhEnumerateModules(
  88. IN LPSTR ModuleName,
  89. IN ULONG_PTR BaseOfDll,
  90. IN PVOID UserContext
  91. )
  92. /*
  93. * UmdhEnumerateModules
  94. *
  95. * Module enumeration 'proc' for imagehlp. Call SymLoadModule on the
  96. * specified module and if that succeeds cache the module name.
  97. *
  98. * ModuleName is an LPSTR indicating the name of the module imagehlp is
  99. * enumerating for us;
  100. * BaseOfDll is the load address of the DLL, which we don't care about, but
  101. * SymLoadModule does;
  102. * UserContext is a pointer to the relevant SYMINFO, which identifies
  103. * our connection.
  104. */
  105. {
  106. DWORD64 Result;
  107. Result = SymLoadModule(Globals.Target,
  108. NULL, // hFile not used
  109. NULL, // use symbol search path
  110. ModuleName, // ModuleName from Enum
  111. BaseOfDll, // LoadAddress from Enum
  112. 0); // Let ImageHlp figure out DLL size
  113. // SilviuC: need to understand exactly what does this function return
  114. if (Result) {
  115. Error (NULL, 0,
  116. "SymLoadModule (%s, %p) failed with error %X (%u)",
  117. ModuleName, BaseOfDll,
  118. GetLastError(), GetLastError());
  119. return FALSE;
  120. }
  121. if (Globals.InfoLevel > 0) {
  122. Comment (" %s (%p) ...", ModuleName, BaseOfDll);
  123. }
  124. return TRUE;
  125. }
  126. /*
  127. * Collect the data required in the STACK_TRACE_DATA entry from the HEAP_ENTRY
  128. * in the target process.
  129. */
  130. USHORT
  131. UmdhCollectHeapEntryData(
  132. IN OUT HEAP_ENTRY *CurrentBlock,
  133. IN OUT STACK_TRACE_DATA *Std,
  134. IN OUT UCHAR *Flags
  135. )
  136. {
  137. UCHAR UnusedSize;
  138. USHORT BlockSize = 0;
  139. BOOL PageHeapBlock;
  140. PageHeapBlock = FALSE;
  141. /*
  142. * Read Flags for this entry, Size, and UnusedBytes fields to calculate the
  143. * actual size of this allocation.
  144. */
  145. if (!READVM(&(CurrentBlock -> Flags),
  146. Flags,
  147. sizeof *Flags)) {
  148. /*
  149. * Failed to read Flags field of the current block.
  150. */
  151. fprintf(stderr,
  152. "READVM(CurrentBlock Flags) failed.\n");
  153. } else if (!READVM(&(CurrentBlock -> Size),
  154. &BlockSize,
  155. sizeof BlockSize)) {
  156. fprintf(stderr,
  157. "READVM(CurrentBlock Size) failed.\n");
  158. /*
  159. * One never knows if an API will trash output parameters on failure.
  160. */
  161. BlockSize = 0;
  162. } else if (!(*Flags & HEAP_ENTRY_BUSY)) {
  163. /*
  164. * This block is not interesting if *Flags doesn't contain
  165. * HEAP_ENTRY_BUSY; it is free and need not be considered further. It
  166. * is important however to have read the block-size (above), as there
  167. * may be more allocations to consider past this free block.
  168. */
  169. ;
  170. } else if (!READVM(&(CurrentBlock -> UnusedBytes),
  171. &UnusedSize,
  172. sizeof UnusedSize)) {
  173. fprintf(stderr,
  174. "READVM(CurrentBlock UnusedSize) failed.\n");
  175. } else {
  176. // UCHAR
  177. Debug (NULL, 0,
  178. "CurrentBlock -> Flags:0x%p:0x%x\n",
  179. &(CurrentBlock-> Flags),
  180. *Flags);
  181. // USHORT
  182. Debug (NULL, 0,
  183. "CurrentBlock -> Size:0x%p:0x%x\n",
  184. &(CurrentBlock -> Size),
  185. BlockSize);
  186. // UCHAR
  187. Debug (NULL, 0,
  188. "CurrentBlock -> UnusedBytes:0x%p:0x%x\n",
  189. &(CurrentBlock -> UnusedBytes),
  190. UnusedSize);
  191. //
  192. // Try to determine the stack trace index for this allocation.
  193. //
  194. if (Globals.LightPageHeapActive) {
  195. /*
  196. * Read trace index from DPH_BLOCK_INFORMATION, which is at
  197. * (DPH_BLOCK_INFORMATION *)(CurrentBlock + 1) -> TraceIndex.
  198. */
  199. DPH_BLOCK_INFORMATION *Block, DphBlock;
  200. Block = (DPH_BLOCK_INFORMATION *)(CurrentBlock + 1);
  201. if (!READVM(Block,
  202. &DphBlock,
  203. sizeof DphBlock)) {
  204. fprintf(stderr,
  205. "READVM(DPH_BLOCK_INFORMATION) failed.\n");
  206. } else if (DphBlock.StartStamp ==
  207. DPH_NORMAL_BLOCK_START_STAMP_FREE) {
  208. /*
  209. * Ignore this record. When debug-page-heap is used, heap
  210. * blocks point to allocated blocks and 'freed' blocks. Heap
  211. * code is responsible for these 'freed' blocks not application
  212. * code.
  213. */
  214. ;
  215. } else if (DphBlock.StartStamp == 0) {
  216. /*
  217. * The first block in the heap is created specially by the
  218. * heap code and does not contain debug-page-heap
  219. * information. Ignore it.
  220. */
  221. ;
  222. } else if ((DphBlock.StartStamp !=
  223. DPH_NORMAL_BLOCK_START_STAMP_ALLOCATED)) {
  224. #if 0 //silviuc: this can happen for fixed address heaps (they are never page heap)
  225. fprintf(stderr,
  226. "Unexpected value (0x%lx) of DphBlock -> StartStamp "
  227. "read from Block %p\n",
  228. DphBlock.StartStamp,
  229. Block);
  230. #endif
  231. PageHeapBlock = FALSE;
  232. } else if ((DphBlock.EndStamp !=
  233. DPH_NORMAL_BLOCK_END_STAMP_ALLOCATED)) {
  234. #if 0 //silviuc: this can happen for fixed address heaps (they are never page heap)
  235. fprintf(stderr,
  236. "Unexpected value (0x%lx) of DphBlock -> EndStamp "
  237. "read from Block %p\n",
  238. DphBlock.EndStamp,
  239. Block);
  240. #endif
  241. PageHeapBlock = FALSE;
  242. } else {
  243. Std -> TraceIndex = DphBlock.TraceIndex;
  244. Std -> BlockAddress = DphBlock.Heap;
  245. Std -> BytesAllocated = DphBlock.ActualSize;
  246. /*
  247. * This stack is one allocation.
  248. */
  249. Std -> AllocationCount = 1;
  250. PageHeapBlock = TRUE;
  251. }
  252. if (PageHeapBlock) {
  253. // ULONG
  254. Debug (NULL, 0,
  255. "DPH Block: StartStamp:0x%p:0x%lx\n",
  256. &(Block -> StartStamp),
  257. DphBlock.StartStamp);
  258. // PVOID
  259. Debug (NULL, 0,
  260. " Heap = 0x%p\n",
  261. DphBlock.Heap);
  262. // SIZE_T
  263. Debug (NULL, 0,
  264. " RequestedSize = 0x%x\n",
  265. DphBlock.RequestedSize);
  266. // SIZE_T
  267. Debug (NULL, 0,
  268. " ActualSize = 0x%x\n",
  269. DphBlock.ActualSize);
  270. // USHORT
  271. Debug (NULL, 0,
  272. " TraceIndex = 0x%x\n",
  273. DphBlock.TraceIndex);
  274. // PVOID
  275. Debug (NULL, 0,
  276. " StackTrace = 0x%p\n",
  277. DphBlock.StackTrace);
  278. // ULONG
  279. Debug (NULL, 0,
  280. " EndStamp = 0x%lx\n",
  281. DphBlock.EndStamp);
  282. }
  283. }
  284. else if (*Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  285. /*
  286. * If HEAP_ENTRY_EXTRA information is present it is at the end of
  287. * the allocated block. Try to read the trace-index of the stack
  288. * which made the allocation.
  289. */
  290. HEAP_ENTRY_EXTRA *Hea;
  291. /*
  292. * BlockSize includes the bytes used by HEAP_ENTRY_EXTRA. The
  293. * HEAP_ENTRY_EXTRA block is at the end of the heap block. Add
  294. * the BlockSize and subtract a HEAP_EXTRA_ENTRY to get the
  295. * address of the HEAP_ENTRY_EXTRA block.
  296. */
  297. Hea = (HEAP_ENTRY_EXTRA *)(CurrentBlock + BlockSize) - 1;
  298. if (!READVM(&(Hea -> AllocatorBackTraceIndex),
  299. &(Std -> TraceIndex),
  300. sizeof Std -> TraceIndex)) {
  301. /*
  302. * Just in case READVM puts stuff here on failure.
  303. */
  304. Std -> TraceIndex = 0;
  305. fprintf(stderr,
  306. "READVM(HeapEntryExtra TraceIndex) failed.\n");
  307. } else {
  308. /*
  309. * Save the address that was returned to the allocator (rather
  310. * than the raw address of the heap block).
  311. */
  312. Std -> BlockAddress = (CurrentBlock + 1);
  313. /*
  314. * We have enough data to calculate the block size.
  315. */
  316. Std -> BytesAllocated = (BlockSize << HEAP_GRANULARITY_SHIFT);
  317. #ifndef DH_COMPATIBLE
  318. /*
  319. * DH doesn't subtract off the UnusedSize in order to be usable
  320. * interchangeably with DH we need to leave it on too. This tends
  321. * to inflate the size of an allocation reported by DH or UMDH.
  322. */
  323. Std -> BytesAllocated -= UnusedSize;
  324. #endif
  325. /*
  326. * This stack is one allocation.
  327. */
  328. Std -> AllocationCount = 1;
  329. }
  330. if (Globals.Verbose) {
  331. // USHORT
  332. fprintf(stderr,
  333. "Hea -> AllocatorBackTraceIndex:0x%p:0x%x\n",
  334. &(Hea -> AllocatorBackTraceIndex),
  335. Std -> TraceIndex);
  336. }
  337. }
  338. }
  339. return BlockSize;
  340. }
  341. VOID
  342. UmdhCollectVirtualAllocdData(
  343. IN OUT HEAP_VIRTUAL_ALLOC_ENTRY *CurrentBlock,
  344. IN OUT STACK_TRACE_DATA *Std
  345. )
  346. {
  347. if (!READVM(&(CurrentBlock -> CommitSize),
  348. &(Std -> BytesAllocated),
  349. sizeof Std -> BytesAllocated)) {
  350. fprintf(stderr,
  351. "READVM(CurrentBlock CommitSize) failed.\n");
  352. } else if (!READVM(&(CurrentBlock -> ExtraStuff.AllocatorBackTraceIndex),
  353. &(Std -> TraceIndex),
  354. sizeof Std -> TraceIndex)) {
  355. fprintf(stderr,
  356. "READVM(CurrentBlock TraceIndex) failed.\n");
  357. } else {
  358. /*
  359. * From this view, each stack represents one allocation.
  360. */
  361. Std -> AllocationCount = 1;
  362. }
  363. }
  364. VOID
  365. UmdhGetHEAPDATA(
  366. IN OUT HEAPDATA *HeapData
  367. )
  368. {
  369. HEAP_VIRTUAL_ALLOC_ENTRY *Anchor, *VaEntry;
  370. ULONG Segment;
  371. /*
  372. * List that helps keep track of heap fragmentation
  373. * statistics.
  374. */
  375. HEAP_ENTRY_LIST List;
  376. Initialize(&List);
  377. if (HeapData -> BaseAddress == NULL) {
  378. /*
  379. * This was in the process heap list but it's not active or it's
  380. * signature didn't match HEAP_SIGNATURE; skip it.
  381. */
  382. return;
  383. }
  384. /*
  385. * Examine each segment of the heap.
  386. */
  387. for (Segment = 0; Segment < HEAP_MAXIMUM_SEGMENTS; Segment++) {
  388. /*
  389. * Read address of segment, and then first and last blocks within
  390. * the segment.
  391. */
  392. HEAP_ENTRY *CurrentBlock, *LastValidEntry;
  393. HEAP_SEGMENT *HeapSegment;
  394. HEAP_UNCOMMMTTED_RANGE *pUncommittedRanges;
  395. ULONG NumberOfPages, Signature, UncommittedPages;
  396. if (!READVM(&(HeapData -> BaseAddress -> Segments[Segment]),
  397. &HeapSegment,
  398. sizeof HeapSegment)) {
  399. fprintf(stderr,
  400. "READVM(Segments[%d]) failed.\n",
  401. Segment);
  402. } else if (!HeapSegment) {
  403. /*
  404. * This segment looks empty.
  405. *
  406. * DH agrees here.
  407. */
  408. continue;
  409. } else if (!READVM(&(HeapSegment -> Signature),
  410. &Signature,
  411. sizeof Signature)) {
  412. fprintf(stderr,
  413. "READVM(HeapSegment Signature) failed.\n");
  414. } else if (Signature != HEAP_SEGMENT_SIGNATURE) {
  415. /*
  416. * Signature mismatch.
  417. */
  418. fprintf(stderr,
  419. "Heap 'segment' at %p has and unexpected signature "
  420. "of 0x%lx\n",
  421. &(HeapSegment -> Signature),
  422. Signature);
  423. } else if (!READVM(&(HeapSegment -> FirstEntry),
  424. &CurrentBlock,
  425. sizeof CurrentBlock)) {
  426. fprintf(stderr,
  427. "READVM(HeapSegment FirstEntry) failed.\n");
  428. } else if (!READVM(&(HeapSegment -> LastValidEntry),
  429. &LastValidEntry,
  430. sizeof LastValidEntry)) {
  431. fprintf(stderr,
  432. "READVM(HeapSegment LastValidEntry) failed.\n");
  433. } else if (!READVM(&(HeapSegment -> NumberOfPages),
  434. &NumberOfPages,
  435. sizeof NumberOfPages)) {
  436. fprintf(stderr,
  437. "READVM(HeapSegment NumberOfPages) failed.\n");
  438. } else if (!READVM(&(HeapSegment -> NumberOfUnCommittedPages),
  439. &UncommittedPages,
  440. sizeof UncommittedPages)) {
  441. fprintf(stderr,
  442. "READVM(HeapSegment NumberOfUnCommittedPages) failed.\n");
  443. } else if (!READVM(&(HeapSegment -> UnCommittedRanges),
  444. &pUncommittedRanges,
  445. sizeof pUncommittedRanges)) {
  446. fprintf(stderr,
  447. "READVM(HeapSegment UncommittedRanges) failed.\n");
  448. } else {
  449. /*
  450. * Examine each block in the Segment.
  451. */
  452. if (Globals.Verbose) {
  453. // HEAP_SEGMENT *
  454. fprintf(stderr,
  455. "\nHeapData -> BaseAddress -> Segments[%d]:0x%p:0x%p\n",
  456. Segment,
  457. &(HeapData -> BaseAddress -> Segments[Segment]),
  458. HeapSegment);
  459. // HEAP_ENTRY *
  460. fprintf(stderr,
  461. "HeapSegment -> FirstEntry:0x%p:0x%p\n",
  462. &(HeapSegment -> FirstEntry),
  463. CurrentBlock);
  464. // HEAP_ENTRY *
  465. fprintf(stderr,
  466. "HeapSegment -> LastValidEntry:0x%p:0x%p\n",
  467. &(HeapSegment -> LastValidEntry),
  468. LastValidEntry);
  469. // ULONG
  470. fprintf(stderr,
  471. "HeapSegment -> NumberOfPages:0x%p:0x%lx\n",
  472. &(HeapSegment -> NumberOfPages),
  473. NumberOfPages);
  474. // ULONG
  475. fprintf(stderr,
  476. "HeapSegment -> NumberOfUncommittedPages:0x%p:0x%lx\n",
  477. &(HeapSegment -> NumberOfUnCommittedPages),
  478. UncommittedPages);
  479. }
  480. /*
  481. * Each heap segment is one VA chunk.
  482. */
  483. HeapData -> VirtualAddressChunks += 1;
  484. HeapData -> BytesCommitted += (NumberOfPages - UncommittedPages) *
  485. PAGE_SIZE;
  486. /*
  487. * LastValidEntry indicate the end of the reserved region; make it
  488. * the end of the committed region. We should also be able to
  489. * calculate this value as (BaseAddress + ((NumberOfPages -
  490. * NumberOfUnCommittedPages) * PAGE_SIZE)).
  491. */
  492. while (CurrentBlock < LastValidEntry) {
  493. UCHAR Flags;
  494. USHORT BlockSize;
  495. if (Globals.Verbose) {
  496. // HEAP_ENTRY *
  497. fprintf(stderr,
  498. "\nNew LastValidEntry = %p\n",
  499. LastValidEntry);
  500. }
  501. /*
  502. * inserting all the blocks for this heap into HeapEntryList.
  503. */
  504. {
  505. UCHAR State;
  506. USHORT Size;
  507. if (!READVM(&(CurrentBlock -> Flags),
  508. &State,
  509. sizeof State)) {
  510. fprintf(stderr,
  511. "READVM (CurrentBlock Flags) failed.\n");
  512. }
  513. else if (!READVM(&(CurrentBlock -> Size),
  514. &Size,
  515. sizeof Size)) {
  516. fprintf(stderr,
  517. "READVM (CurrentBlock Size) failed.\n");
  518. }
  519. else {
  520. HEAP_ENTRY_INFO HeapEntryInfo;
  521. HeapEntryInfo.BlockState = HEAP_BLOCK_FREE;
  522. if ((State & 0x1) == HEAP_ENTRY_BUSY) {
  523. HeapEntryInfo.BlockState = HEAP_BLOCK_BUSY;
  524. }
  525. HeapEntryInfo.BlockSize = Size;
  526. HeapEntryInfo.BlockCount = 1;
  527. InsertHeapEntry(&List, &HeapEntryInfo);
  528. }
  529. }
  530. /*
  531. * If the stack sort data buffer is full, try to make it
  532. * larger.
  533. */
  534. if (HeapData -> TraceDataEntryMax == 0) {
  535. HeapData -> StackTraceData = XALLOC(SORT_DATA_BUFFER_INCREMENT *
  536. sizeof (STACK_TRACE_DATA));
  537. if (HeapData -> StackTraceData == NULL) {
  538. fprintf(stderr,
  539. "xalloc of %d bytes failed.\n",
  540. SORT_DATA_BUFFER_INCREMENT *
  541. sizeof (STACK_TRACE_DATA));
  542. } else {
  543. HeapData -> TraceDataEntryMax = SORT_DATA_BUFFER_INCREMENT;
  544. }
  545. } else if (HeapData -> TraceDataEntryCount ==
  546. HeapData -> TraceDataEntryMax) {
  547. STACK_TRACE_DATA *tmp;
  548. ULONG OriginalCount;
  549. OriginalCount = HeapData -> TraceDataEntryMax;
  550. HeapData -> TraceDataEntryMax += SORT_DATA_BUFFER_INCREMENT;
  551. tmp = XREALLOC(HeapData -> StackTraceData,
  552. HeapData -> TraceDataEntryMax *
  553. sizeof (STACK_TRACE_DATA));
  554. if (tmp == NULL) {
  555. fprintf(stderr,
  556. "realloc(%d) failed.\n",
  557. HeapData -> TraceDataEntryMax *
  558. sizeof (STACK_TRACE_DATA));
  559. /*
  560. * Undo the increase in size so we don't actually try
  561. * to use it.
  562. */
  563. HeapData -> TraceDataEntryMax -= SORT_DATA_BUFFER_INCREMENT;
  564. } else {
  565. /*
  566. * Zero newly allocated bytes in the region.
  567. */
  568. RtlZeroMemory(tmp + OriginalCount,
  569. SORT_DATA_BUFFER_INCREMENT *
  570. sizeof (STACK_TRACE_DATA));
  571. /*
  572. * Use the new pointer.
  573. */
  574. HeapData -> StackTraceData = tmp;
  575. }
  576. }
  577. /*
  578. * If there is space in the buffer, collect data.
  579. */
  580. if (HeapData -> TraceDataEntryCount <
  581. HeapData -> TraceDataEntryMax) {
  582. BlockSize = UmdhCollectHeapEntryData(CurrentBlock,
  583. &(HeapData -> StackTraceData[
  584. HeapData -> TraceDataEntryCount]),
  585. &Flags);
  586. if (BlockSize == 0) {
  587. /*
  588. * Something went wrong.
  589. */
  590. fprintf(stderr,
  591. "UmdhGetHEAPDATA got BlockSize == 0\n");
  592. fprintf(stderr,
  593. "HeapSegment = 0x%p, LastValidEntry = 0x%p\n",
  594. HeapSegment,
  595. LastValidEntry);
  596. break;
  597. } else {
  598. /*
  599. * Keep track of data in sort data buffer.
  600. */
  601. HeapData -> TraceDataEntryCount += 1;
  602. }
  603. } else {
  604. fprintf(stderr,
  605. "UmdhGetHEAPDATA ran out of TraceDataEntries\n");
  606. }
  607. if (Flags & HEAP_ENTRY_LAST_ENTRY) {
  608. /*
  609. * BlockSize is the number of units of size (sizeof
  610. * (HEAP_ENTRY)) to move forward to find the next block.
  611. * This makes the pointer arithmetic appropriate below.
  612. */
  613. CurrentBlock += BlockSize;
  614. if (pUncommittedRanges == NULL) {
  615. CurrentBlock = LastValidEntry;
  616. } else {
  617. HEAP_UNCOMMMTTED_RANGE UncommittedRange;
  618. if (!READVM(pUncommittedRanges,
  619. &UncommittedRange,
  620. sizeof UncommittedRange)) {
  621. fprintf(stderr,
  622. "READVM(pUncommittedRanges) failed.\n");
  623. /*
  624. * On failure the only reasonable thing we can do
  625. * is stop looking at this segment.
  626. */
  627. CurrentBlock = LastValidEntry;
  628. } else {
  629. if (Globals.Verbose) {
  630. // HEAP_UNCOMMITTED_RANGE
  631. fprintf(stderr,
  632. "pUncomittedRanges:0x%p:0x%x\n",
  633. pUncommittedRanges,
  634. UncommittedRange);
  635. }
  636. CurrentBlock = (PHEAP_ENTRY)((PCHAR)UncommittedRange.Address +
  637. UncommittedRange.Size);
  638. pUncommittedRanges = UncommittedRange.Next;
  639. }
  640. }
  641. } else {
  642. /*
  643. * BlockSize is the number of units of size (sizeof
  644. * (HEAP_ENTRY)) to move forward to find the next block.
  645. * This makes the pointer arithmetic appropriate below.
  646. */
  647. CurrentBlock += BlockSize;
  648. }
  649. }
  650. }
  651. }
  652. /*
  653. * Display heap fragmentation statistics.
  654. */
  655. DisplayHeapFragStatistics(Globals.OutFile, HeapData->BaseAddress, &List);
  656. DestroyList(&List);
  657. /*
  658. * Examine entries for the blocks created by NtAllocateVirtualMemory. For
  659. * these, it looks like when they are in the list they are live.
  660. */
  661. if (!READVM(&(HeapData -> BaseAddress -> VirtualAllocdBlocks.Flink),
  662. &Anchor,
  663. sizeof Anchor)) {
  664. fprintf(stderr,
  665. "READVM(reading heap VA anchor) failed.\n");
  666. } else if (!READVM(&(Anchor -> Entry.Flink),
  667. &VaEntry,
  668. sizeof VaEntry)) {
  669. fprintf(stderr,
  670. "READVM(Anchor Flink) failed.\n");
  671. } else {
  672. if (Globals.Verbose) {
  673. fprintf(stderr,
  674. "\nHeapData -> BaseAddress -> VirtualAllocdBlocks.Flink:%p:%p\n",
  675. &(HeapData -> BaseAddress -> VirtualAllocdBlocks.Flink),
  676. Anchor);
  677. fprintf(stderr,
  678. "Anchor -> Entry.Flink:%p:%p\n",
  679. &(Anchor -> Entry.Flink),
  680. VaEntry);
  681. }
  682. /*
  683. * If the list is empty
  684. * &(HeapData -> BaseAddress -> VirtualAllocdBlocks.Flink) will be equal to
  685. * HeapData -> BaseAddress -> VirtualAllocdBlocks.Flink and Anchor
  686. * will be equal to VaEntry). Advancing VaEntry each time through will
  687. * cause it to be equal to Anchor when we have examined the entire list.
  688. */
  689. while (Anchor != VaEntry) {
  690. /*
  691. * If the stack sort data buffer is full, try to make it larger.
  692. */
  693. if (HeapData -> TraceDataEntryMax == 0) {
  694. HeapData -> StackTraceData = XALLOC(SORT_DATA_BUFFER_INCREMENT *
  695. sizeof (STACK_TRACE_DATA));
  696. if (HeapData -> StackTraceData == NULL) {
  697. fprintf(stderr,
  698. "xalloc of %d bytes failed.\n",
  699. SORT_DATA_BUFFER_INCREMENT *
  700. sizeof (STACK_TRACE_DATA));
  701. } else {
  702. HeapData -> TraceDataEntryMax = SORT_DATA_BUFFER_INCREMENT;
  703. }
  704. } else if (HeapData -> TraceDataEntryCount ==
  705. HeapData -> TraceDataEntryMax) {
  706. STACK_TRACE_DATA *tmp;
  707. ULONG OriginalCount;
  708. OriginalCount = HeapData -> TraceDataEntryMax;
  709. HeapData -> TraceDataEntryMax += SORT_DATA_BUFFER_INCREMENT;
  710. tmp = XREALLOC(HeapData -> StackTraceData,
  711. HeapData -> TraceDataEntryMax * sizeof (STACK_TRACE_DATA));
  712. if (tmp == NULL) {
  713. fprintf(stderr,
  714. "realloc(%d) failed.\n",
  715. HeapData -> TraceDataEntryMax *
  716. sizeof (STACK_TRACE_DATA));
  717. /*
  718. * Undo the increase in size so we don't actually try to
  719. * use it.
  720. */
  721. HeapData -> TraceDataEntryMax -= SORT_DATA_BUFFER_INCREMENT;
  722. } else {
  723. /*
  724. * Zero newly allocated bytes in the region.
  725. */
  726. RtlZeroMemory(tmp + OriginalCount,
  727. SORT_DATA_BUFFER_INCREMENT *
  728. sizeof (STACK_TRACE_DATA));
  729. /*
  730. * Use the new pointer.
  731. */
  732. HeapData -> StackTraceData = tmp;
  733. }
  734. }
  735. /*
  736. * If there is space in the buffer, collect data.
  737. */
  738. if (HeapData -> TraceDataEntryCount < HeapData -> TraceDataEntryMax) {
  739. UmdhCollectVirtualAllocdData(VaEntry,
  740. &(HeapData -> StackTraceData[HeapData ->
  741. TraceDataEntryCount]));
  742. HeapData -> TraceDataEntryCount += 1;
  743. }
  744. /*
  745. * Count the VA chunk.
  746. */
  747. HeapData -> VirtualAddressChunks += 1;
  748. /*
  749. * Advance the next element in the list.
  750. */
  751. if (!READVM(&(VaEntry -> Entry.Flink),
  752. &VaEntry,
  753. sizeof VaEntry)) {
  754. fprintf(stderr,
  755. "READVM(VaEntry Flink) failed.\n");
  756. /*
  757. * If this read failed, we may be unable to terminate this loop
  758. * properly; do it explicitly.
  759. */
  760. break;
  761. }
  762. if (Globals.Verbose) {
  763. fprintf(stderr,
  764. "VaEntry -> Entry.Flink:%p:%p\n",
  765. &(VaEntry -> Entry.Flink),
  766. VaEntry);
  767. }
  768. }
  769. }
  770. }
  771. #define HEAP_TYPE_UNKNOWN 0
  772. #define HEAP_TYPE_NT_HEAP 1
  773. #define HEAP_TYPE_PAGE_HEAP 2
  774. BOOL
  775. UmdhDetectHeapType (
  776. PVOID HeapAddress,
  777. PDWORD HeapType
  778. )
  779. {
  780. BOOL Result;
  781. HEAP HeapData;
  782. *HeapType = HEAP_TYPE_UNKNOWN;
  783. Result = READVM (HeapAddress,
  784. &HeapData,
  785. sizeof HeapData);
  786. if (Result == FALSE) {
  787. return FALSE;
  788. }
  789. if (HeapData.Signature == 0xEEFFEEFF) {
  790. *HeapType = HEAP_TYPE_NT_HEAP;
  791. return TRUE;
  792. }
  793. else if (HeapData.Signature == 0xEEEEEEEE) {
  794. *HeapType = HEAP_TYPE_PAGE_HEAP;
  795. return TRUE;
  796. }
  797. else {
  798. *HeapType = HEAP_TYPE_UNKNOWN;
  799. return TRUE;
  800. }
  801. }
  802. BOOLEAN
  803. UmdhGetHeapsInformation (
  804. IN OUT PHEAPINFO HeapInfo
  805. )
  806. /*++
  807. Routine Description:
  808. UmdhGetHeaps
  809. Note that when the function is called it assumes the trace database
  810. was completely read from the target process.
  811. Arguments:
  812. Return Value:
  813. True if operation succeeded.
  814. --*/
  815. {
  816. NTSTATUS Status;
  817. PROCESS_BASIC_INFORMATION Pbi;
  818. PVOID Addr;
  819. BOOL Result;
  820. PHEAP * ProcessHeaps;
  821. ULONG j;
  822. ULONG PageHeapFlags;
  823. //
  824. // Get some information about the target process.
  825. //
  826. Status = NtQueryInformationProcess(Globals.Target,
  827. ProcessBasicInformation,
  828. &Pbi,
  829. sizeof Pbi,
  830. NULL);
  831. if (! NT_SUCCESS(Status)) {
  832. Error (__FILE__, __LINE__,
  833. "NtQueryInformationProcess failed with status %X\n",
  834. Status);
  835. return FALSE;
  836. }
  837. //
  838. // Dump the stack trace database pointer.
  839. //
  840. Comment ("Stack trace data base @ %p", ((PSTACK_TRACE_DATABASE)(Globals.Database))->CommitBase);
  841. Comment ("# traces in the data base %u", ((PSTACK_TRACE_DATABASE)(Globals.Database))->NumberOfEntriesAdded);
  842. //
  843. // Find out if this process is using debug-page-heap functionality.
  844. //
  845. Addr = SymbolAddress (DEBUG_PAGE_HEAP_NAME);
  846. Result = READVM(Addr,
  847. &(Globals.PageHeapActive),
  848. sizeof (Globals.PageHeapActive));
  849. if (Result == FALSE) {
  850. Error (NULL, 0,
  851. "READVM(&RtlpDebugPageHeap) failed.\n"
  852. "\nntdll.dll symbols are probably incorrect.\n");
  853. }
  854. if (Globals.PageHeapActive) {
  855. Addr = SymbolAddress (DEBUG_PAGE_HEAP_FLAGS_NAME);
  856. Result = READVM(Addr,
  857. &PageHeapFlags,
  858. sizeof PageHeapFlags);
  859. if (Result == FALSE) {
  860. Error (NULL, 0,
  861. "READVM(&RtlpDphGlobalFlags) failed.\n"
  862. "\nntdll.dll symbols are probably incorrect.\n");
  863. }
  864. if ((PageHeapFlags & PAGE_HEAP_ENABLE_PAGE_HEAP) == 0) {
  865. Globals.LightPageHeapActive = TRUE;
  866. }
  867. }
  868. //
  869. // ISSUE: SilviuC: we do not work yet if full page heap is enabled.
  870. //
  871. if (Globals.PageHeapActive && !Globals.LightPageHeapActive) {
  872. Comment ("UMDH cannot be used if full page heap or application "
  873. "verifier with full page heap is enabled for the process.");
  874. Error (NULL, 0,
  875. "UMDH cannot be used if full page heap or application "
  876. "verifier with full page heap is enabled for the process.");
  877. return FALSE;
  878. }
  879. //
  880. // Get the number of heaps from the PEB.
  881. //
  882. Result = READVM (&(Pbi.PebBaseAddress->NumberOfHeaps),
  883. &(HeapInfo->NumberOfHeaps),
  884. sizeof (HeapInfo->NumberOfHeaps));
  885. if (Result == FALSE) {
  886. Error (NULL, 0, "READVM(Peb.NumberOfHeaps) failed.\n");
  887. return FALSE;
  888. }
  889. Debug (NULL, 0,
  890. "Pbi.PebBaseAddress -> NumberOfHeaps:0x%p:0x%lx\n",
  891. &(Pbi.PebBaseAddress -> NumberOfHeaps),
  892. HeapInfo -> NumberOfHeaps);
  893. HeapInfo->Heaps = XALLOC(HeapInfo->NumberOfHeaps * sizeof (HEAPDATA));
  894. if (HeapInfo->Heaps == NULL) {
  895. Error (NULL, 0,
  896. "xalloc of %d bytes failed.\n",
  897. HeapInfo -> NumberOfHeaps * sizeof (HEAPDATA));
  898. return FALSE;
  899. }
  900. Result = READVM(&(Pbi.PebBaseAddress -> ProcessHeaps),
  901. &ProcessHeaps,
  902. sizeof ProcessHeaps);
  903. if (Result == FALSE) {
  904. XFREE (HeapInfo->Heaps);
  905. HeapInfo->Heaps = NULL;
  906. Error (NULL, 0,
  907. "READVM(Peb.ProcessHeaps) failed.\n");
  908. return FALSE;
  909. }
  910. Debug (NULL, 0,
  911. "Pbi.PebBaseAddress -> ProcessHeaps:0x%p:0x%p\n",
  912. &(Pbi.PebBaseAddress -> ProcessHeaps),
  913. ProcessHeaps);
  914. //
  915. // Iterate heaps
  916. //
  917. for (j = 0; j < HeapInfo -> NumberOfHeaps; j += 1) {
  918. PHEAP HeapBase;
  919. PHEAPDATA HeapData;
  920. ULONG Signature;
  921. USHORT ProcessHeapsListIndex;
  922. HeapData = &(HeapInfo -> Heaps[j]);
  923. //
  924. // Read the address of the heap.
  925. //
  926. Result = READVM (&(ProcessHeaps[j]),
  927. &(HeapData -> BaseAddress),
  928. sizeof HeapData -> BaseAddress);
  929. if (Result == FALSE) {
  930. Error (NULL, 0,
  931. "READVM(ProcessHeaps[%d]) failed.\n",
  932. j);
  933. Warning (NULL, 0,
  934. "Skipping heap @ %p because we cannot read it.",
  935. HeapData -> BaseAddress);
  936. //
  937. // Error while reading. Forget the address of this heap.
  938. //
  939. HeapData->BaseAddress = NULL;
  940. continue;
  941. }
  942. Debug (NULL, 0,
  943. "** ProcessHeaps[0x%x]:0x%p:0x%p\n",
  944. j,
  945. &(ProcessHeaps[j]),
  946. HeapData -> BaseAddress);
  947. HeapBase = HeapData->BaseAddress;
  948. //
  949. // What type of heap is this ? It should be an NT heap because page heaps
  950. // are not inserted into the PEB list of heaps.
  951. //
  952. {
  953. DWORD Type;
  954. BOOL DetectResult;
  955. DetectResult = UmdhDetectHeapType (HeapBase, &Type);
  956. if (! (DetectResult && Type == HEAP_TYPE_NT_HEAP)) {
  957. Error (NULL, 0,
  958. "Detected a heap that is not an NT heap @ %p",
  959. HeapBase);
  960. }
  961. }
  962. /*
  963. * Does the heap think that it is within range ? (We
  964. * already think it is.)
  965. */
  966. if (!READVM(&(HeapBase -> ProcessHeapsListIndex),
  967. &ProcessHeapsListIndex,
  968. sizeof ProcessHeapsListIndex)) {
  969. fprintf(stderr,
  970. "READVM(HeapBase ProcessHeapsListIndex) failed.\n");
  971. /*
  972. * Forget the base address of this heap.
  973. */
  974. HeapData -> BaseAddress = NULL;
  975. continue;
  976. }
  977. if (Globals.Verbose) {
  978. fprintf(stderr,
  979. "&(HeapBase -> ProcessHeapsListIndex):0x%p:0x%lx\n",
  980. &(HeapBase -> ProcessHeapsListIndex),
  981. ProcessHeapsListIndex);
  982. }
  983. /*
  984. * A comment in
  985. * ntos\rtl\heapdll.c:RtlpRemoveHeapFromProcessList
  986. * states: "Note that the heaps stored index is bias by
  987. * one", thus ">" in the following test.
  988. */
  989. if (ProcessHeapsListIndex > HeapInfo -> NumberOfHeaps) {
  990. /*
  991. * Invalid index. Forget the base address of this
  992. * heap.
  993. */
  994. fprintf(stderr,
  995. "Heap at index %d has index of %d, but max "
  996. "is %d\n",
  997. j,
  998. ProcessHeapsListIndex,
  999. HeapInfo -> NumberOfHeaps);
  1000. fprintf(stderr,
  1001. "&(Pbi.PebBaseAddress -> NumberOfHeaps) = 0x%p\n",
  1002. &(Pbi.PebBaseAddress -> NumberOfHeaps));
  1003. HeapData -> BaseAddress = NULL;
  1004. continue;
  1005. }
  1006. /*
  1007. * Check the signature to see if it is really a heap.
  1008. */
  1009. if (!READVM(&(HeapBase -> Signature),
  1010. &Signature,
  1011. sizeof Signature)) {
  1012. fprintf(stderr,
  1013. "READVM(HeapBase Signature) failed.\n");
  1014. /*
  1015. * Forget the base address of this heap.
  1016. */
  1017. HeapData -> BaseAddress = NULL;
  1018. continue;
  1019. }
  1020. else if (Signature != HEAP_SIGNATURE) {
  1021. fprintf(stderr,
  1022. "Heap at index %d does not have a correct "
  1023. "signature (0x%lx)\n",
  1024. j,
  1025. Signature);
  1026. /*
  1027. * Forget the base address of this heap.
  1028. */
  1029. HeapData -> BaseAddress = NULL;
  1030. continue;
  1031. }
  1032. /*
  1033. * And read other interesting heap bits.
  1034. */
  1035. if (!READVM(&(HeapBase -> Flags),
  1036. &(HeapData -> Flags),
  1037. sizeof HeapData -> Flags)) {
  1038. fprintf(stderr,
  1039. "READVM(HeapBase Flags) failed.\n");
  1040. /*
  1041. * Forget the base address of this heap.
  1042. */
  1043. HeapData -> BaseAddress = NULL;
  1044. continue;
  1045. }
  1046. if (Globals.Verbose) {
  1047. fprintf(stderr,
  1048. "HeapBase -> Flags:0x%p:0x%lx\n",
  1049. &(HeapBase -> Flags),
  1050. HeapData -> Flags);
  1051. }
  1052. if (!READVM(&(HeapBase -> AllocatorBackTraceIndex),
  1053. &(HeapData -> CreatorBackTraceIndex),
  1054. sizeof HeapData -> CreatorBackTraceIndex)) {
  1055. fprintf(stderr,
  1056. "READVM(HeapBase AllocatorBackTraceIndex) failed.\n");
  1057. /*
  1058. * Forget the base address of this heap.
  1059. */
  1060. HeapData -> BaseAddress = NULL;
  1061. continue;
  1062. }
  1063. if (Globals.Verbose) {
  1064. fprintf(stderr,
  1065. "HeapBase -> AllocatorBackTraceIndex:0x%p:0x%lx\n",
  1066. &(HeapBase -> AllocatorBackTraceIndex),
  1067. HeapData -> CreatorBackTraceIndex);
  1068. }
  1069. if (!READVM(&(HeapBase -> TotalFreeSize),
  1070. &(HeapData -> TotalFreeSize),
  1071. sizeof HeapData -> TotalFreeSize)) {
  1072. fprintf(stderr,
  1073. "READVM(HeapBase TotalFreeSize) failed.\n");
  1074. /*
  1075. * Forget the base address of this heap.
  1076. */
  1077. HeapData -> BaseAddress = NULL;
  1078. continue;
  1079. }
  1080. if (Globals.Verbose) {
  1081. fprintf(stderr,
  1082. "HeapBase -> TotalFreeSize:0x%p:0x%p\n",
  1083. &(HeapBase -> TotalFreeSize),
  1084. HeapData -> TotalFreeSize);
  1085. }
  1086. }
  1087. /*
  1088. * We got as much as we could.
  1089. */
  1090. return TRUE;
  1091. }
  1092. int
  1093. __cdecl
  1094. UmdhSortSTACK_TRACE_DATAByTraceIndex(
  1095. const STACK_TRACE_DATA *h1,
  1096. const STACK_TRACE_DATA *h2
  1097. )
  1098. {
  1099. LONG Result;
  1100. /*
  1101. * Sort such that items with identical TraceIndex are adjacent. (That
  1102. * this results in ascending order is irrelevant).
  1103. */
  1104. Result = h1 -> TraceIndex - h2 -> TraceIndex;
  1105. if (0 == Result) {
  1106. /*
  1107. * For two items with identical TraceIndex, sort into ascending order
  1108. * by BytesAllocated.
  1109. */
  1110. if (h1 -> BytesAllocated > h2 -> BytesAllocated) {
  1111. Result = 1;
  1112. } else if (h1 -> BytesAllocated < h2 -> BytesAllocated) {
  1113. Result = -1;
  1114. } else {
  1115. Result = 0;
  1116. }
  1117. }
  1118. return Result;
  1119. }
  1120. int
  1121. __cdecl
  1122. UmdhSortSTACK_TRACE_DATABySize(
  1123. const STACK_TRACE_DATA *h1,
  1124. const STACK_TRACE_DATA *h2
  1125. )
  1126. {
  1127. LONG Result = 0;
  1128. // if (SortByAllocs) {
  1129. /*
  1130. * Sort into descending order by AllocationCount.
  1131. */
  1132. if (h2 -> AllocationCount > h1 -> AllocationCount) {
  1133. Result = 1;
  1134. } else if (h2 -> AllocationCount < h1 -> AllocationCount) {
  1135. Result = -1;
  1136. } else {
  1137. Result = 0;
  1138. }
  1139. // }
  1140. if (!Result) {
  1141. /*
  1142. * Sort into descending order by total bytes.
  1143. */
  1144. if (((h1 -> BytesAllocated * h1 -> AllocationCount) + h1 -> BytesExtra) >
  1145. ((h2 -> BytesAllocated * h2 -> AllocationCount) + h2 -> BytesExtra)) {
  1146. Result = -1;
  1147. } else if (((h1 -> BytesAllocated * h1 -> AllocationCount) + h1 -> BytesExtra) <
  1148. ((h2 -> BytesAllocated * h2 -> AllocationCount) + h2 -> BytesExtra)) {
  1149. Result = +1;
  1150. } else {
  1151. Result = 0;
  1152. }
  1153. }
  1154. if (!Result) {
  1155. /*
  1156. * Bytes or AllocationCounts are equal, sort into ascending order by
  1157. * stack trace index.
  1158. */
  1159. Result = h1 -> TraceIndex - h2 -> TraceIndex;
  1160. }
  1161. if (!Result) {
  1162. /*
  1163. * Previous equal; sort by heap address. This should result in heap
  1164. * addresses dumpped by -d being in sorted order.
  1165. */
  1166. if (h1 -> BlockAddress < h2 -> BlockAddress) {
  1167. Result = -1;
  1168. } else {
  1169. /*
  1170. * No other sort, just make it "after".
  1171. */
  1172. Result = 1;
  1173. }
  1174. }
  1175. return Result;
  1176. }
  1177. VOID
  1178. UmdhCoalesceSTACK_TRACE_DATA(
  1179. IN OUT STACK_TRACE_DATA *Std,
  1180. IN ULONG Count
  1181. )
  1182. {
  1183. ULONG i = 0;
  1184. /*
  1185. * For every entry allocated from the same stack trace, coalesce them into
  1186. * a single entry by moving allocation count and any extra bytes into the
  1187. * first entry then zeroing the AllocationCount on the other entry.
  1188. */
  1189. while ((i + 1) < Count) {
  1190. ULONG j;
  1191. /*
  1192. * Identical entries should be adjacent, so start with the next.
  1193. */
  1194. j = i + 1;
  1195. while (j < Count) {
  1196. if (Std[i].TraceIndex == Std[j].TraceIndex) {
  1197. /*
  1198. * These two allocations were made from the same stack trace,
  1199. * coalesce.
  1200. */
  1201. if (Std[j].BytesAllocated > Std[i].BytesAllocated) {
  1202. /*
  1203. * Add any extra bytes from the second allocation so we
  1204. * can determine the total number of bytes from this trace.
  1205. */
  1206. Std[i].BytesExtra += Std[j].BytesAllocated -
  1207. Std[i].BytesAllocated;
  1208. }
  1209. /*
  1210. * Move the AllocationCount of the second trace into the first.
  1211. */
  1212. Std[i].AllocationCount += Std[j].AllocationCount;
  1213. Std[j].AllocationCount = 0;
  1214. ++j;
  1215. } else {
  1216. /*
  1217. * Mismatch; look no further.
  1218. */
  1219. break;
  1220. }
  1221. }
  1222. /*
  1223. * Advance to the next uncoalesced entry.
  1224. */
  1225. i = j;
  1226. }
  1227. }
  1228. VOID
  1229. UmdhShowHEAPDATA(
  1230. IN PHEAPDATA HeapData
  1231. )
  1232. {
  1233. Info(" Flags: %08lx", HeapData -> Flags);
  1234. Info(" Number Of Entries: %d", HeapData -> TraceDataEntryCount);
  1235. Info(" Number Of Tags: <unknown>");
  1236. Info(" Bytes Allocated: %p", HeapData -> BytesCommitted - (HeapData -> TotalFreeSize << HEAP_GRANULARITY_SHIFT));
  1237. Info(" Bytes Committed: %p",HeapData -> BytesCommitted);
  1238. Info(" Total FreeSpace: %p", HeapData -> TotalFreeSize << HEAP_GRANULARITY_SHIFT);
  1239. Info(" Number of Virtual Address chunks used: %lx", HeapData -> VirtualAddressChunks);
  1240. Info(" Address Space Used: <unknown>");
  1241. Info(" Entry Overhead: %d", sizeof (HEAP_ENTRY));
  1242. Info(" Creator: (Backtrace%05d)", HeapData -> CreatorBackTraceIndex);
  1243. UmdhDumpStackByIndex(HeapData->CreatorBackTraceIndex);
  1244. }
  1245. VOID
  1246. UmdhShowStacks(
  1247. STACK_TRACE_DATA *Std,
  1248. ULONG StackTraceCount,
  1249. ULONG Threshold
  1250. )
  1251. {
  1252. ULONG i;
  1253. for (i = 0; i < StackTraceCount; i++) {
  1254. /*
  1255. * The default Threshold is set to 0 in main(), so stacks with
  1256. * AllocationCount == 0 as a result of the Coalesce will skipped here.
  1257. */
  1258. if (Std[i].AllocationCount > Threshold) {
  1259. if ((Std[i].TraceIndex == 0) ||
  1260. ((ULONG)Std[i].TraceIndex == 0xFEEE)) {
  1261. /*
  1262. * I'm not sure where either of these come from, I suspect
  1263. * that the zero case comes from the last entry in some list.
  1264. * The too-large case being 0xFEEE, suggests that I'm looking
  1265. * at free pool. In either case we don't have any useful
  1266. * information; don't print it.
  1267. */
  1268. continue;
  1269. }
  1270. /*
  1271. * This number of allocations from this point exceeds the
  1272. * threshold, dump interesting information.
  1273. */
  1274. fprintf(Globals.OutFile, "%p bytes ",
  1275. (Std[i].AllocationCount * Std[i].BytesAllocated) +
  1276. Std[i].BytesExtra);
  1277. if (Std[i].AllocationCount > 1) {
  1278. if (Std[i].BytesExtra) {
  1279. fprintf(Globals.OutFile, "in 0x%lx allocations (@ 0x%p + 0x%p) ",
  1280. Std[i].AllocationCount,
  1281. Std[i].BytesAllocated,
  1282. Std[i].BytesExtra);
  1283. } else {
  1284. fprintf(Globals.OutFile, "in 0x%lx allocations (@ 0x%p) ",
  1285. Std[i].AllocationCount,
  1286. Std[i].BytesAllocated);
  1287. }
  1288. }
  1289. fprintf(Globals.OutFile, "by: BackTrace%05d\n",
  1290. Std[i].TraceIndex);
  1291. UmdhDumpStackByIndex(Std[i].TraceIndex);
  1292. /*
  1293. * If FlaggedTrace == the trace we are currently looking at, then
  1294. * dump the blocks that come from that trace. FlaggedTrace == 0
  1295. * indicates 'dump all stacks'.
  1296. */
  1297. if ((FlaggedTrace != SHOW_NO_ALLOC_BLOCKS) &&
  1298. ((FlaggedTrace == Std[i].TraceIndex) ||
  1299. (FlaggedTrace == 0))) {
  1300. ULONG ColumnCount, l;
  1301. fprintf(Globals.OutFile, "Allocations for trace BackTrace%05d:\n",
  1302. Std[i].TraceIndex);
  1303. ColumnCount = 0;
  1304. /*
  1305. * Here we rely on the remaining stack having AllocationCount
  1306. * == 0, so should be at greater indexes than the current
  1307. * stack.
  1308. */
  1309. for (l = i; l < StackTraceCount; l++) {
  1310. /*
  1311. * If the stack at [l] matches the stack at [i], dump it
  1312. * here.
  1313. */
  1314. if (Std[l].TraceIndex == Std[i].TraceIndex) {
  1315. fprintf(Globals.OutFile, "%p ",
  1316. Std[l].BlockAddress);
  1317. ColumnCount += 10;
  1318. if ((ColumnCount + 10) > 80) {
  1319. fprintf(Globals.OutFile, "\n");
  1320. ColumnCount = 0;
  1321. }
  1322. }
  1323. }
  1324. fprintf(Globals.OutFile, "\n\n\n");
  1325. }
  1326. }
  1327. }
  1328. }
  1329. /////////////////////////////////////////////////////////////////////
  1330. ////////////////////////////////////////////////////// resume/suspend
  1331. /////////////////////////////////////////////////////////////////////
  1332. //
  1333. // Note. We need to dynamically discover the NtSuspend/ResumeProcess
  1334. // entry points because these where not present in W2000.
  1335. //
  1336. VOID
  1337. UmdhSuspendProcess(
  1338. VOID
  1339. )
  1340. {
  1341. HINSTANCE hLibrary;
  1342. NTSTATUS NtStatus;
  1343. typedef NTSTATUS (NTAPI* NTSUSPENDPROC)(HANDLE);
  1344. NTSUSPENDPROC pSuspend;
  1345. hLibrary= LoadLibrary( TEXT("ntdll.dll") );
  1346. if( hLibrary ) {
  1347. pSuspend= (NTSUSPENDPROC) GetProcAddress( hLibrary, "NtSuspendProcess" );
  1348. if( pSuspend ) {
  1349. NtStatus= (*pSuspend)( Globals.Target );
  1350. Comment ( "NtSuspendProcess Status= %08x",NtStatus);
  1351. if (NT_SUCCESS(NtStatus)) {
  1352. Globals.TargetSuspended = TRUE;
  1353. }
  1354. }
  1355. FreeLibrary( hLibrary ); hLibrary= NULL;
  1356. }
  1357. return;
  1358. }
  1359. VOID
  1360. UmdhResumeProcess(
  1361. VOID
  1362. )
  1363. {
  1364. HINSTANCE hLibrary;
  1365. NTSTATUS NtStatus;
  1366. typedef NTSTATUS (NTAPI* NTRESUMEPROC)(HANDLE);
  1367. NTRESUMEPROC pResume;
  1368. if (Globals.TargetSuspended == FALSE) {
  1369. return;
  1370. }
  1371. hLibrary= LoadLibrary( TEXT("ntdll.dll") );
  1372. if( hLibrary ) {
  1373. pResume= (NTRESUMEPROC) GetProcAddress( hLibrary, "NtResumeProcess" );
  1374. if( pResume ) {
  1375. NtStatus= (*pResume)( Globals.Target );
  1376. Comment ( "NtResumeProcess Status= %08x",NtStatus);
  1377. if (NT_SUCCESS(NtStatus)) {
  1378. Globals.TargetSuspended = FALSE;
  1379. }
  1380. }
  1381. FreeLibrary( hLibrary ); hLibrary= NULL;
  1382. }
  1383. return;
  1384. }
  1385. /////////////////////////////////////////////////////////////////////
  1386. /////////////////////////////////////////////////////////////////////
  1387. /////////////////////////////////////////////////////////////////////
  1388. VOID
  1389. UmdhGrovel (
  1390. IN ULONG Pid,
  1391. IN ULONG Threshold
  1392. )
  1393. /*++
  1394. Routine Description:
  1395. UmdhGrovel
  1396. Arguments:
  1397. Pid = PID of target process
  1398. Threshold - ???
  1399. Return Value:
  1400. None.
  1401. --*/
  1402. {
  1403. BOOL Result;
  1404. HEAPINFO HeapInfo;
  1405. ULONG Heap;
  1406. PHEAPDATA HeapData;
  1407. Comment ("Connecting to process %u ...", Pid);
  1408. //
  1409. // Imagehlp library needs the query privilege for the process
  1410. // handle and of course we need also read privilege because
  1411. // we will read all sorts of things from the process.
  1412. //
  1413. Globals.Target = OpenProcess(
  1414. PROCESS_QUERY_INFORMATION |
  1415. PROCESS_VM_READ |
  1416. PROCESS_SUSPEND_RESUME,
  1417. FALSE,
  1418. Pid);
  1419. if (Globals.Target == NULL) {
  1420. Error (__FILE__, __LINE__,
  1421. "OpenProcess(%u) failed with error %u", Pid, GetLastError());
  1422. return;
  1423. }
  1424. //
  1425. // Attach ImageHlp and enumerate the modules.
  1426. //
  1427. Comment ("Process %u opened (handle=%d) ...", Pid, Globals.Target );
  1428. Result = SymInitialize(Globals.Target, // target process
  1429. NULL, // standard symbols search path
  1430. TRUE); // invade process space with symbols
  1431. if (Result == FALSE) {
  1432. ULONG ErrorCode = GetLastError();
  1433. if (ErrorCode >= 0x80000000) {
  1434. Error (__FILE__, __LINE__,
  1435. "imagehlp.SymInitialize() failed with error %X", ErrorCode);
  1436. }
  1437. else {
  1438. Error (__FILE__, __LINE__,
  1439. "imagehlp.SymInitialize() failed with error %u", ErrorCode);
  1440. }
  1441. goto ErrorReturn;
  1442. }
  1443. Comment ("Debug library initialized ...", Pid);
  1444. SymSetOptions(SYMOPT_CASE_INSENSITIVE |
  1445. SYMOPT_DEFERRED_LOADS |
  1446. (Globals.LineInfo ? SYMOPT_LOAD_LINES : 0) |
  1447. SYMOPT_UNDNAME);
  1448. Comment ("Debug options set: %08X", SymGetOptions());
  1449. // Result = SymRegisterCallback (Globals.Target,
  1450. // SymbolDbgHelpCallback,
  1451. // NULL);
  1452. if (Result == FALSE) {
  1453. Warning (NULL, 0, "Failed to register symbol callback function.");
  1454. }
  1455. Result = SymEnumerateModules (Globals.Target,
  1456. UmdhEnumerateModules,
  1457. Globals.Target);
  1458. if (Result == FALSE) {
  1459. Error (__FILE__, __LINE__,
  1460. "imagehlp.SymEnumerateModules() failed with error %u", GetLastError());
  1461. goto ErrorReturn;
  1462. }
  1463. Comment ("Module enumeration completed.");
  1464. //
  1465. // Initialize local trace database. Note that order is important.
  1466. // Initialize() assumes the process handle to the target process
  1467. // already exists and the symbol management package was initialized.
  1468. //
  1469. if (TraceDbInitialize (Globals.Target) == FALSE) {
  1470. goto ErrorReturn;
  1471. }
  1472. //
  1473. // Suspend target process.
  1474. //
  1475. // ISSUE: SilviuC: cannot suspend csrss.exe. Need to code to avoid that.
  1476. // UmdhSuspendProcess();
  1477. try {
  1478. //
  1479. // If we want just a raw dump then do it and return withouth getting any information
  1480. // about heaps.
  1481. //
  1482. if (Globals.RawDump) {
  1483. TraceDbDump ();
  1484. goto ErrorReturn;
  1485. }
  1486. //
  1487. // Read heap information.
  1488. //
  1489. Result = UmdhGetHeapsInformation (&HeapInfo);
  1490. if (Result == FALSE) {
  1491. Error (__FILE__, __LINE__,
  1492. "Failed to get heaps information.");
  1493. goto ErrorReturn;
  1494. }
  1495. //
  1496. // Print heap summary
  1497. //
  1498. Info ("\n - - - - - - - - - - Heap summary - - - - - - - - - -\n");
  1499. for (Heap = 0; Heap < HeapInfo.NumberOfHeaps; Heap += 1) {
  1500. HeapData = &(HeapInfo.Heaps[Heap]);
  1501. if (HeapData->BaseAddress == NULL) {
  1502. continue;
  1503. }
  1504. Info (" %p", HeapData->BaseAddress);
  1505. }
  1506. //
  1507. // Examine each heap.
  1508. //
  1509. for (Heap = 0; Heap < HeapInfo.NumberOfHeaps; Heap += 1) {
  1510. HeapData = &(HeapInfo.Heaps[Heap]);
  1511. if (HeapData->BaseAddress == NULL) {
  1512. //
  1513. // SilviuC: Can this really happen?
  1514. //
  1515. // This was in the process heap list but it's not
  1516. // active or it's signature didn't match
  1517. // HEAP_SIGNATURE; skip it.
  1518. //
  1519. Warning (__FILE__, __LINE__, "Got a null heap base address");
  1520. continue;
  1521. }
  1522. //
  1523. // Get information about this heap.
  1524. //
  1525. // Silviuc: Waht if we fail reading?
  1526. //
  1527. UmdhGetHEAPDATA(HeapData);
  1528. //
  1529. // Sort the HeapData->StackTraceData by TraceIndex.
  1530. //
  1531. qsort(HeapData->StackTraceData,
  1532. HeapData->TraceDataEntryCount,
  1533. sizeof (HeapData->StackTraceData[0]),
  1534. UmdhSortSTACK_TRACE_DATAByTraceIndex);
  1535. //
  1536. // Coalesce HeapData->StackTraceEntries by
  1537. // AllocationCount, zeroing allocation count for
  1538. // duplicate entries.
  1539. //
  1540. UmdhCoalesceSTACK_TRACE_DATA(HeapData->StackTraceData,
  1541. HeapData->TraceDataEntryCount);
  1542. //
  1543. // Sort the HeapData -> StackTraceData in ascending
  1544. // order by Size (BytesAllocated * AllocationCount) or
  1545. // if SortByAllocs is set, into descending order by
  1546. // number of allocations.
  1547. //
  1548. qsort(HeapData->StackTraceData,
  1549. HeapData->TraceDataEntryCount,
  1550. sizeof (HeapData->StackTraceData[0]),
  1551. UmdhSortSTACK_TRACE_DATABySize);
  1552. //
  1553. // Display Heap header info. The first `*' character is used by the
  1554. // dhcmp to synchronize log parsing.
  1555. //
  1556. Info ("\n*- - - - - - - - - - Start of data for heap @ %p - - - - - - - - - -\n",
  1557. HeapData->BaseAddress);
  1558. UmdhShowHEAPDATA(HeapData);
  1559. //
  1560. // The following line is required by dhcmp tool.
  1561. //
  1562. Info ("*- - - - - - - - - - Heap %p Hogs - - - - - - - - - -\n",
  1563. HeapData->BaseAddress);
  1564. //
  1565. // Display Stack trace info for stack in this heap.
  1566. //
  1567. UmdhShowStacks(HeapData->StackTraceData,
  1568. HeapData->TraceDataEntryCount,
  1569. Threshold);
  1570. Info ("\n*- - - - - - - - - - End of data for heap @ %p - - - - - - - - - -\n",
  1571. HeapData->BaseAddress);
  1572. //
  1573. // Clean up the allocations we made during this loop.
  1574. //
  1575. XFREE (HeapData->StackTraceData);
  1576. HeapData->StackTraceData = NULL;
  1577. }
  1578. XFREE(HeapInfo.Heaps);
  1579. HeapInfo.Heaps = NULL;
  1580. }
  1581. finally {
  1582. //
  1583. // Super important to resume target process even if umdh
  1584. // has a bug and crashes.
  1585. //
  1586. // UmdhResumeProcess ();
  1587. }
  1588. //
  1589. // Clean up.
  1590. //
  1591. ErrorReturn:
  1592. SymCleanup(Globals.Target);
  1593. CloseHandle(Globals.Target); Globals.Target= NULL;
  1594. }
  1595. VOID
  1596. UmdhUsage(
  1597. char *BadArg
  1598. )
  1599. {
  1600. if (BadArg) {
  1601. fprintf(stderr,
  1602. "\nUnexpected argument \"%s\"\n\n",
  1603. BadArg);
  1604. }
  1605. fprintf(stderr,
  1606. "umdh version %s \n"
  1607. "1. umdh {-p:(int)Process-id {-t:(int)Threshold} {-f:(char *)Filename} \n"
  1608. " {-d{:(int)Trace-Number}} {-v{:(char *)Filename}} \n"
  1609. " {-i:(int)Infolevel} {-l} {-r{:(int)Index}} \n"
  1610. " } \n"
  1611. "\n"
  1612. "2. umdh { {-h} {-v} File1 { File2 } } \n"
  1613. "\n"
  1614. "umdh can be used in two modes - \n"
  1615. "\n"
  1616. "When used in the first mode, it dumps the user mode heap (acts as old-umdh), \n"
  1617. "while used in the second mode acts as dhcmp. \n"
  1618. " \n"
  1619. " Options when used in MODE 1: \n"
  1620. " \n"
  1621. " -t Optional. Only dump stack that account for more allocations than \n"
  1622. " specified value. Defaults to 0; dump all stacks. \n"
  1623. "\n"
  1624. " -f Optional. Indicates output file. Destroys an existing file of the \n"
  1625. " same name. Default is to dump to stdout. \n"
  1626. "\n"
  1627. " -p Required. Indicates the Process-ID to examine. \n"
  1628. "\n"
  1629. " -d Optional. Dump address of each outstanding allocation. \n"
  1630. " Optional inclusion of an integer numeric argument causes dump of \n"
  1631. " only those blocks allocated from this BackTrace. \n"
  1632. "\n"
  1633. " -v Optional. Dumps debug output to stderr or to a file. \n"
  1634. "\n"
  1635. " -i Optional. Zero is default (no additional info). The greater the \n"
  1636. " number the more data is displayed. Supported numbers: 0, 1. \n"
  1637. "\n"
  1638. " -l Optional. Print file and line number information for traces. \n"
  1639. "\n"
  1640. " -r Optional. Print a raw dump of the trace database without any \n"
  1641. " heap information. If an index is specified then only the trace \n"
  1642. " with that particular index will be dumped. \n"
  1643. "\n"
  1644. " -x Optional. Suspend the Process while dumping heaps. \n"
  1645. "\n"
  1646. " -h Optional. Usage message. ie This message. \n"
  1647. " \n"
  1648. " Parameters are accepted in any order. \n"
  1649. " \n"
  1650. " \n"
  1651. " UMDH uses the dbghelp library to resolve symbols, therefore \n"
  1652. " _NT_SYMBOL_PATH must be set appropriately. For example: \n"
  1653. " \n"
  1654. " set _NT_SYMBOL_PATH=symsrv*symsrv.dll*\\\\symbols\\symbols \n"
  1655. " \n"
  1656. " to use the symbol server, otherwise the appropriate local or network path. \n"
  1657. " If no symbol path is set, umdh will use by default %%windir%%\\symbols. \n"
  1658. " \n"
  1659. " See http://dbg/symbols for more information about setting up symbols. \n"
  1660. " \n"
  1661. " UMDH requires also to have stack trace collection enabled for the process. \n"
  1662. " This can be done with the gflags tool. For example to enable stack trace \n"
  1663. " collection for notepad, the command is: `gflags -i notepad.exe +ust'. \n"
  1664. " \n"
  1665. "\n"
  1666. " When used in MODE 2: \n"
  1667. "\n"
  1668. " I) UMDH [-d] dh_dump1.txt dh_dump2.txt \n"
  1669. " This compares two DH dumps, useful for finding leaks. \n"
  1670. " dh_dump1.txt & dh_dump2.txt are obtained before and after some test \n"
  1671. " scenario. DHCMP matches the backtraces from each file and calculates \n"
  1672. " the increase in bytes allocated for each backtrace. These are then \n"
  1673. " displayed in descending order of size of leak \n"
  1674. " The first line of each backtrace output shows the size of the leak in \n"
  1675. " bytes, followed by the (last-first) difference in parentheses. \n"
  1676. " Leaks of size 0 are not shown. \n"
  1677. "\n"
  1678. " II) UMDH [-d] dh_dump.txt \n"
  1679. " For each allocation backtrace, the number of bytes allocated will be \n"
  1680. " attributed to each callsite (each line of the backtrace). The number \n"
  1681. " of bytes allocated per callsite are summed and the callsites are then \n"
  1682. " displayed in descending order of bytes allocated. This is useful for \n"
  1683. " finding a leak that is reached via many different codepaths. \n"
  1684. " ntdll!RtlAllocateHeap@12 will appear first when analyzing DH dumps of \n"
  1685. " csrss.exe, since all allocation will have gone through that routine. \n"
  1686. " Similarly, ProcessApiRequest will be very prominent too, since that \n"
  1687. " appears in most allocation backtraces. Hence the useful thing to do \n"
  1688. " with mode 2 output is to use dhcmp to comapre two of them: \n"
  1689. " umdh dh_dump1.txt > tmp1.txt \n"
  1690. " umdh dh_dump2.txt > tmp2.txt \n"
  1691. " umdh tmp1.txt tmp2.txt \n"
  1692. " the output will show the differences. \n"
  1693. "\n"
  1694. " Flags: \n"
  1695. " -d Output in decimal (default is hexadecimal) \n"
  1696. " -v Verbose output: include the actual backtraces as well as summary \n"
  1697. " information \n"
  1698. " (Verbose output is only interesting in mode 1 above.) \n",
  1699. UMDH_VERSION);
  1700. exit(EXIT_FAILURE);
  1701. }
  1702. /////////////////////////////////////////////////////////////////////
  1703. /////////////////////////////////////////////////////// OS versioning
  1704. /////////////////////////////////////////////////////////////////////
  1705. // return TRUE if we can run on this version
  1706. BOOL
  1707. UmdhCheckOsVersion (
  1708. )
  1709. {
  1710. OSVERSIONINFO OsInfo;
  1711. BOOL Result;
  1712. ZeroMemory (&OsInfo, sizeof OsInfo);
  1713. OsInfo.dwOSVersionInfoSize = sizeof OsInfo;
  1714. Result = GetVersionEx (&OsInfo);
  1715. if (Result == FALSE) {
  1716. Comment ( "GetVersionInfoEx() failed with error %u",
  1717. GetLastError());
  1718. return FALSE;
  1719. }
  1720. Comment ("OS version %u.%u %s",
  1721. OsInfo.dwMajorVersion, OsInfo.dwMinorVersion,
  1722. OsInfo.szCSDVersion);
  1723. Comment ("Umdh OS version %u.%u",
  1724. UMDH_OS_MAJOR_VERSION, UMDH_OS_MINOR_VERSION);
  1725. if (OsInfo.dwMajorVersion < 4) {
  1726. Comment ( "Umdh does not run on systems older than 4.0");
  1727. return FALSE;
  1728. }
  1729. else if (OsInfo.dwMajorVersion == 4) {
  1730. //
  1731. // ISSUE: silviuc: add check to run only on NT4 SP6.
  1732. //
  1733. if (OsInfo.dwMajorVersion != UMDH_OS_MAJOR_VERSION
  1734. || OsInfo.dwMinorVersion != UMDH_OS_MINOR_VERSION) {
  1735. Comment (
  1736. "Cannot run umdh for OS version %u.%u on a %u.%u system",
  1737. UMDH_OS_MAJOR_VERSION, UMDH_OS_MINOR_VERSION,
  1738. OsInfo.dwMajorVersion, OsInfo.dwMinorVersion);
  1739. return FALSE;
  1740. }
  1741. }
  1742. else if (OsInfo.dwMajorVersion == 5) {
  1743. if (OsInfo.dwMajorVersion != UMDH_OS_MAJOR_VERSION
  1744. || OsInfo.dwMinorVersion != UMDH_OS_MINOR_VERSION) {
  1745. if (! (OsInfo.dwMinorVersion == 0 && UMDH_OS_MINOR_VERSION == 1)) {
  1746. Comment (
  1747. "Cannot run umdh for OS version %u.%u on a %u.%u system",
  1748. UMDH_OS_MAJOR_VERSION, UMDH_OS_MINOR_VERSION,
  1749. OsInfo.dwMajorVersion, OsInfo.dwMinorVersion);
  1750. return FALSE;
  1751. }
  1752. }
  1753. }
  1754. else {
  1755. Warning (NULL, 0, "OS version %u.%u",
  1756. OsInfo.dwMajorVersion,
  1757. OsInfo.dwMinorVersion);
  1758. }
  1759. return TRUE;
  1760. }
  1761. /////////////////////////////////////////////////////////////////////
  1762. //////////////////////////////////////////////////////////////// main
  1763. /////////////////////////////////////////////////////////////////////
  1764. BOOL UMDH( ULONG argc, PCHAR * argv)
  1765. {
  1766. BOOLEAN WasEnabled;
  1767. CHAR CompName[MAX_COMPUTERNAME_LENGTH + 1];
  1768. DWORD CompNameLength = MAX_COMPUTERNAME_LENGTH + 1;
  1769. NTSTATUS Status;
  1770. SYSTEMTIME st;
  1771. ULONG Pid = PID_NOT_PASSED_FLAG;
  1772. ULONG Threshold = 0;
  1773. ULONG i;
  1774. LARGE_INTEGER StartStamp;
  1775. LARGE_INTEGER EndStamp;
  1776. FILE * File;
  1777. ZeroMemory( &Globals, sizeof(Globals) );
  1778. Globals.Version = UMDH_VERSION;
  1779. Globals.OutFile = stdout;
  1780. Globals.ErrorFile = stderr;
  1781. /*
  1782. * Make an effort to understand passed arguments.
  1783. */
  1784. if ((argc < 2) || (argc > 6)) {
  1785. return FALSE;
  1786. }
  1787. if (argc == 2 && strstr (argv[1], "?") != NULL) {
  1788. return FALSE;
  1789. }
  1790. i = 1;
  1791. while (i < argc) {
  1792. //
  1793. // Accept either '-' or '/' as argument specifier.
  1794. //
  1795. if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
  1796. switch (tolower(argv[i][1])) {
  1797. case 'd':
  1798. if (argv[i][2] == ':') {
  1799. FlaggedTrace = atoi(&(argv[i][3]));
  1800. }
  1801. else {
  1802. FlaggedTrace = 0;
  1803. }
  1804. break;
  1805. case 't':
  1806. if (argv[i][2] == ':') {
  1807. Threshold = atoi(&(argv[i][3]));
  1808. }
  1809. else {
  1810. return FALSE;
  1811. }
  1812. break;
  1813. case 'p':
  1814. /*
  1815. * Is the first character of the remainder of this
  1816. * argument a number ? If not don't try to send it to
  1817. * atoi.
  1818. */
  1819. if (argv[i][2] == ':') {
  1820. if (!isdigit(argv[i][3])) {
  1821. fprintf(stderr,
  1822. "\nInvalid pid specified with \"-p:\"\n");
  1823. return FALSE;
  1824. }
  1825. else {
  1826. Pid = atoi(&(argv[i][3]));
  1827. }
  1828. }
  1829. else {
  1830. return FALSE;
  1831. }
  1832. break;
  1833. case 'f':
  1834. if (argv[i][2] == ':') {
  1835. File = fopen (&(argv[i][3]), "w");
  1836. if (File == NULL) {
  1837. Comment ( "Failed to open output file `%s'",
  1838. &(argv[i][3]));
  1839. exit( EXIT_FAILURE );
  1840. }
  1841. else {
  1842. Globals.OutFile = File;
  1843. }
  1844. }
  1845. else {
  1846. return FALSE;
  1847. }
  1848. break;
  1849. //
  1850. // Possible future option for saving the trace database in a binary format.
  1851. // Not really useful right now because we still need access to the target
  1852. // process in order to get various data (modules loaded, heaps, etc.).
  1853. #if 0
  1854. case 's':
  1855. if (argv[i][2] == ':') {
  1856. Globals.DumpFileName = &(argv[i][3]);
  1857. }
  1858. else {
  1859. return FALSE;
  1860. }
  1861. break;
  1862. #endif
  1863. case 'v':
  1864. Globals.Verbose = TRUE;
  1865. if (argv[i][2] == ':') {
  1866. File = fopen (&(argv[i][3]), "w");
  1867. if (File == NULL) {
  1868. Comment ( "Failed to open error file `%s'",
  1869. &(argv[i][3]));
  1870. exit( EXIT_FAILURE );
  1871. }
  1872. else {
  1873. Globals.ErrorFile = File;
  1874. }
  1875. }
  1876. break;
  1877. case 'i':
  1878. Globals.InfoLevel = 1;
  1879. if (argv[i][2] == ':') {
  1880. Globals.InfoLevel = atoi (&(argv[i][3]));
  1881. }
  1882. break;
  1883. case 'l':
  1884. Globals.LineInfo = TRUE;
  1885. break;
  1886. case 'r':
  1887. Globals.RawDump = TRUE;
  1888. if (argv[i][2] == ':') {
  1889. Globals.RawIndex = (USHORT)(atoi (&(argv[i][3])));
  1890. }
  1891. break;
  1892. case 'x':
  1893. Globals.Suspend = TRUE;
  1894. break;
  1895. case 'h': /* FALLTHROUGH */
  1896. case '?':
  1897. return FALSE;
  1898. break;
  1899. default:
  1900. return FALSE;
  1901. break;
  1902. }
  1903. }
  1904. else {
  1905. return FALSE;
  1906. }
  1907. i++;
  1908. }
  1909. if (Pid == PID_NOT_PASSED_FLAG) {
  1910. fprintf(stderr,
  1911. "\nNo pid specified.\n");
  1912. return FALSE;
  1913. }
  1914. //
  1915. // Stamp umdh log with time and computer name.
  1916. //
  1917. GetLocalTime(&st);
  1918. GetComputerName(CompName, &CompNameLength);
  1919. Comment ("");
  1920. Comment ("UMDH: version %s: Logtime %4u-%02u-%02u %02u:%02u - Machine=%s - PID=%u",
  1921. Globals.Version,
  1922. st.wYear,
  1923. st.wMonth,
  1924. st.wDay,
  1925. st.wHour,
  1926. st.wMinute,
  1927. CompName,
  1928. Pid);
  1929. Comment ("\n");
  1930. if( !UmdhCheckOsVersion() ) {
  1931. exit(EXIT_FAILURE);;
  1932. }
  1933. QueryPerformanceCounter (&StartStamp);
  1934. //
  1935. // Try to come up with a guess for the symbols path if none is defined.
  1936. //
  1937. SetSymbolsPath ();
  1938. //
  1939. // Enable debug privilege, so that we can attach to the indicated
  1940. // process. If it fails complain but try anyway just in case the user can
  1941. // actually open the process without privilege.
  1942. //
  1943. // SilviuC: do we need debug privilege?
  1944. //
  1945. WasEnabled = TRUE;
  1946. Status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE,
  1947. TRUE,
  1948. FALSE,
  1949. &WasEnabled);
  1950. if (! NT_SUCCESS(Status)) {
  1951. Warning (__FILE__, __LINE__,
  1952. "RtlAdjustPrivilege(enable) failed with status = %X",
  1953. Status);
  1954. //
  1955. // If we could not enable the privilege, indicate that it was already
  1956. // enabled so that we do not attempt to disable it later.
  1957. //
  1958. WasEnabled = TRUE;
  1959. }
  1960. else {
  1961. Comment ("Debug privilege has been enabled.");
  1962. }
  1963. //
  1964. // Increase priority of umdh as much as possible. This has the role of
  1965. // preventing heap activity in the process being grovelled.
  1966. //
  1967. // SilviuC: we might need to enable the SE_INC_BASE_PRIORITY privilege.
  1968. //
  1969. #if 0
  1970. {
  1971. BOOL Result;
  1972. Result = SetPriorityClass (GetCurrentProcess(),
  1973. HIGH_PRIORITY_CLASS);
  1974. if (Result == FALSE) {
  1975. Warning (NULL, 0,
  1976. "SetPriorityClass failed with error %u");
  1977. }
  1978. else {
  1979. Result = SetThreadPriority (GetCurrentThread(),
  1980. THREAD_PRIORITY_HIGHEST);
  1981. if (Result == FALSE) {
  1982. Warning (NULL, 0,
  1983. "SetThreadPriority failed with error %u");
  1984. }
  1985. else {
  1986. Comment ("Priority of UMDH thread has been increased.");
  1987. }
  1988. }
  1989. }
  1990. #endif
  1991. //
  1992. // Initialize heap for persistent allocations.
  1993. //
  1994. SymbolsHeapInitialize();
  1995. //
  1996. // We may not have SeDebugPrivilege, but try anyway.
  1997. // SilviuC: we should print an error if we do not have this privilege
  1998. //
  1999. UmdhGrovel(Pid, Threshold);
  2000. //
  2001. // Disable SeDebugPrivilege if we enabled it.
  2002. //
  2003. if (! WasEnabled) {
  2004. Status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE,
  2005. FALSE,
  2006. FALSE,
  2007. &WasEnabled);
  2008. if (! NT_SUCCESS(Status)) {
  2009. Warning (__FILE__, __LINE__,
  2010. "RtlAdjustPrivilege(disable) failed with status = %X\n",
  2011. Status);
  2012. }
  2013. }
  2014. //
  2015. // Statistics
  2016. //
  2017. ReportStatistics ();
  2018. {
  2019. LARGE_INTEGER Frequency;
  2020. QueryPerformanceCounter (&EndStamp);
  2021. QueryPerformanceFrequency (&Frequency);
  2022. Debug (NULL, 0, "Start stamp %I64u", StartStamp.QuadPart);
  2023. Debug (NULL, 0, "End stamp %I64u", EndStamp.QuadPart);
  2024. Debug (NULL, 0, "Frequency %I64u", Frequency.QuadPart);
  2025. Frequency.QuadPart /= 1000; // ticks per msec
  2026. if (Frequency.QuadPart) {
  2027. Comment ("Elapse time %I64u msecs.",
  2028. (EndStamp.QuadPart - StartStamp.QuadPart) / (Frequency.QuadPart));
  2029. }
  2030. }
  2031. {
  2032. FILETIME CreateTime, ExitTime, KernelTime, UserTime;
  2033. BOOL bSta;
  2034. bSta= GetProcessTimes( NtCurrentProcess(),
  2035. &CreateTime,
  2036. &ExitTime,
  2037. &KernelTime,
  2038. &UserTime );
  2039. if( bSta ) {
  2040. LONGLONG User64, Kernel64;
  2041. DWORD dwUser, dwKernel;
  2042. Kernel64= *(LONGLONG*) &KernelTime;
  2043. User64= *(LONGLONG*) &UserTime;
  2044. dwKernel= (DWORD) (Kernel64/10000);
  2045. dwUser= (DWORD) (User64/10000);
  2046. Comment( "CPU time User: %u msecs. Kernel: %u msecs.",
  2047. dwUser, dwKernel );
  2048. }
  2049. }
  2050. //
  2051. // Cleanup
  2052. //
  2053. SymCleanup(Globals.Target);
  2054. Globals.Target = NULL;
  2055. fflush (Globals.OutFile);
  2056. fflush (Globals.ErrorFile);
  2057. if (Globals.OutFile != stdout) {
  2058. fclose (Globals.OutFile);
  2059. }
  2060. if (Globals.ErrorFile != stderr) {
  2061. fclose (Globals.ErrorFile);
  2062. }
  2063. return TRUE;
  2064. }
  2065. VOID __cdecl
  2066. #if defined (_PART_OF_DH_)
  2067. UmdhMain(
  2068. #else
  2069. main(
  2070. #endif
  2071. ULONG argc,
  2072. PCHAR *argv
  2073. )
  2074. /*
  2075. VOID __cdecl
  2076. main(
  2077. ULONG argc,
  2078. PCHAR *argv
  2079. )
  2080. */
  2081. {
  2082. /*
  2083. * Make an effort to understand passed arguments.
  2084. */
  2085. if (UMDH (argc, argv)) {
  2086. }
  2087. else if (DHCMP (argc, argv)) {
  2088. }
  2089. else {
  2090. UmdhUsage (NULL);
  2091. }
  2092. return;
  2093. }