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.

1185 lines
36 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. heapmon.c
  5. Abstract:
  6. This program monitors the heap usage of another process and updates
  7. its display every 10 seconds
  8. Author:
  9. Steve Wood (stevewo) 01-Nov-1994
  10. Revision History:
  11. --*/
  12. #include <ntos.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #include <dbghelp.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. void
  19. Usage( void )
  20. {
  21. fputs( "Usage: HEAPMON [-?] [-1] [-p id] [-t | -a | -f | -d | [-u | -b]] [-( | -)] [-e]\n"
  22. "where: -? displays this message.\n"
  23. " -1 specifies to monitor the Win32 subsystem\n"
  24. " -p specifies the process to monitor\n"
  25. " Default is to monitor the Win32 subsystem\n"
  26. " -t sort output by tag name\n"
  27. " -a sort output by #allocations\n"
  28. " -f sort output by #frees\n"
  29. " -d sort output by #allocations - #frees\n"
  30. " -u sort output by bytes used\n"
  31. " -b same as -u\n"
  32. " -( Changes #allocations and #frees above to be #bytes\n"
  33. " allocated and freed\n"
  34. " -) same as -(\n"
  35. " -e enables display of total lines\n"
  36. " -l enables highlighing of changed lines\n"
  37. "\n"
  38. "While HEAPMON is running you can type any of the following\n"
  39. "switch characters to change the output:\n"
  40. " t - sort output by tag name\n"
  41. " a - sort output by #allocations\n"
  42. " f - sort output by #frees\n"
  43. " d - sort output by #allocations - #frees\n"
  44. " u or b - specifies the sort output by bytes used\n"
  45. " ( or ) - toggles interpretation of #allocations and #frees above\n"
  46. " to be #bytes allocated and freed.\n"
  47. " e - toggles display of total lines.\n"
  48. " l - toggles highlighing of changed lines\n"
  49. " ? or h - displays help text\n"
  50. " q - quit the program.\n"
  51. , stderr);
  52. ExitProcess( 1 );
  53. }
  54. #define TAG 0
  55. #define ALLOC 1
  56. #define FREE 2
  57. #define DIFF 3
  58. #define BYTES 4
  59. BOOL fFirstTimeThrough;
  60. BOOL fDisplayTotals;
  61. BOOL fHighlight = TRUE;
  62. BOOL fParen;
  63. BOOL fInteractive;
  64. BOOL fQuit;
  65. BOOL fHelp;
  66. ULONG DelayTimeMsec = 5000;
  67. ULONG SortBy = TAG;
  68. HANDLE InputHandle;
  69. DWORD OriginalInputMode;
  70. HANDLE OriginalOutputHandle;
  71. HANDLE OutputHandle;
  72. COORD ConsoleBufferSize;
  73. CONSOLE_SCREEN_BUFFER_INFO OriginalConsoleInfo;
  74. WORD NormalAttribute;
  75. WORD HighlightAttribute;
  76. ULONG NumberOfCols;
  77. ULONG NumberOfRows;
  78. ULONG NumberOfDetailLines;
  79. ULONG FirstDetailLine;
  80. ULONG NumberOfInputRecords;
  81. INPUT_RECORD InputRecord;
  82. typedef struct _HEAP_ENTRY {
  83. struct _HEAP_ENTRY *Next;
  84. BOOL Changed;
  85. PVOID HeapBase;
  86. PCHAR HeapName;
  87. SIZE_T BytesAllocated;
  88. SIZE_T BytesCommitted;
  89. } HEAP_ENTRY, *PHEAP_ENTRY;
  90. typedef struct _TAG_COUNTS {
  91. SIZE_T Allocs;
  92. SIZE_T Frees;
  93. SIZE_T Used;
  94. SIZE_T Allocs_Frees;
  95. SIZE_T UsedPerAlloc;
  96. } TAG_COUNTS, *PTAG_COUNTS;
  97. typedef struct _TAG_TOTALS {
  98. BOOL Changed;
  99. TAG_COUNTS Counts;
  100. TAG_COUNTS Differences;
  101. } TAG_TOTALS, *PTAG_TOTALS;
  102. typedef struct _TAG_ENTRY {
  103. struct _TAG_ENTRY *Next;
  104. PCHAR HeapName;
  105. PCHAR TagName;
  106. PVOID HeapBase;
  107. USHORT TagIndex;
  108. BOOL Changed;
  109. TAG_COUNTS Counts;
  110. TAG_COUNTS PrevCounts;
  111. TAG_COUNTS Differences;
  112. } TAG_ENTRY, *PTAG_ENTRY;
  113. ULONG HeapListLength;
  114. PHEAP_ENTRY HeapListHead;
  115. ULONG TagListLength;
  116. PTAG_ENTRY TagListHead, *TagArray;
  117. TAG_TOTALS TagTotals;
  118. VOID
  119. ShowHelpPopup( VOID );
  120. VOID
  121. UpdateDisplay( VOID );
  122. VOID
  123. DumpTagDataBase( VOID );
  124. BOOLEAN
  125. UpdateTagDataBase(
  126. PRTL_DEBUG_INFORMATION DebugInfo
  127. );
  128. BOOLEAN
  129. UpdateHeapDataBase(
  130. PRTL_DEBUG_INFORMATION DebugInfo
  131. );
  132. PCHAR
  133. GetNameForHeapBase(
  134. PVOID HeapBase
  135. );
  136. PVOID
  137. CreateNameTable(
  138. IN ULONG NumberOfBuckets
  139. );
  140. PCHAR
  141. AddNameToNameTable(
  142. IN PVOID pNameTable,
  143. IN PCHAR Name
  144. );
  145. PVOID NameTable;
  146. BOOL
  147. ProcessOptionCharacter(
  148. IN CHAR c
  149. );
  150. VOID
  151. ScreenUpdateLoop(
  152. PRTL_DEBUG_INFORMATION p
  153. );
  154. int
  155. __cdecl main(
  156. int argc,
  157. char *argv[]
  158. )
  159. {
  160. DWORD_PTR ProcessId;
  161. PCHAR s, s1;
  162. NTSTATUS Status;
  163. PRTL_DEBUG_INFORMATION p;
  164. SMALL_RECT NewWindowRect;
  165. NameTable = CreateNameTable( 37 );
  166. ProcessId = 0xFFFFFFFF;
  167. fHelp = FALSE;
  168. fInteractive = TRUE;
  169. while (--argc) {
  170. s = *++argv;
  171. if (*s == '-' || *s == '/') {
  172. while (*++s) {
  173. if (!ProcessOptionCharacter( *s )) {
  174. switch( toupper( *s ) ) {
  175. case '1':
  176. fQuit = TRUE;
  177. fInteractive = FALSE;
  178. break;
  179. case 'P':
  180. if (--argc) {
  181. ProcessId = atoi( *++argv );
  182. }
  183. else {
  184. fprintf( stderr, "HEAPMON: missing argument to -p switch\n" );
  185. fHelp = TRUE;
  186. }
  187. break;
  188. default:
  189. fprintf( stderr, "HEAPMON: invalid switch - /%c\n", *s );
  190. fHelp = TRUE;
  191. break;
  192. }
  193. }
  194. }
  195. }
  196. else {
  197. fprintf( stderr, "HEAPMON: invalid argument - %s\n", s );
  198. fHelp = TRUE;
  199. }
  200. }
  201. if (fHelp) {
  202. Usage();
  203. }
  204. if (ProcessId == -1) {
  205. HANDLE Process;
  206. OBJECT_ATTRIBUTES ObjectAttributes;
  207. UNICODE_STRING UnicodeString;
  208. PROCESS_BASIC_INFORMATION BasicInfo;
  209. RtlInitUnicodeString( &UnicodeString, L"\\WindowsSS" );
  210. InitializeObjectAttributes( &ObjectAttributes,
  211. &UnicodeString,
  212. 0,
  213. NULL,
  214. NULL
  215. );
  216. Status = NtOpenProcess( &Process,
  217. PROCESS_ALL_ACCESS,
  218. &ObjectAttributes,
  219. NULL
  220. );
  221. if (NT_SUCCESS(Status)) {
  222. Status = NtQueryInformationProcess( Process,
  223. ProcessBasicInformation,
  224. (PVOID)&BasicInfo,
  225. sizeof(BasicInfo),
  226. NULL
  227. );
  228. NtClose( Process );
  229. }
  230. if (!NT_SUCCESS(Status)) {
  231. fprintf( stderr, "HEAPMON: Unable to access Win32 server process - %08x", Status );
  232. if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  233. fprintf( stderr, "\nUse GFLAGS.EXE to ""Enable debugging of Win32 Subsystem"" and reboot.\n" );
  234. }
  235. ExitProcess( 1 );
  236. }
  237. ProcessId = BasicInfo.UniqueProcessId;
  238. }
  239. InputHandle = GetStdHandle( STD_INPUT_HANDLE );
  240. OriginalOutputHandle = GetStdHandle( STD_OUTPUT_HANDLE );
  241. if (fInteractive) {
  242. if (InputHandle == NULL ||
  243. OriginalOutputHandle == NULL ||
  244. !GetConsoleMode( InputHandle, &OriginalInputMode )
  245. ) {
  246. fInteractive = FALSE;
  247. }
  248. else {
  249. OutputHandle = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE,
  250. FILE_SHARE_WRITE | FILE_SHARE_READ,
  251. NULL,
  252. CONSOLE_TEXTMODE_BUFFER,
  253. NULL
  254. );
  255. if (OutputHandle == NULL ||
  256. !GetConsoleScreenBufferInfo( OriginalOutputHandle, &OriginalConsoleInfo ) ||
  257. !SetConsoleScreenBufferSize( OutputHandle, OriginalConsoleInfo.dwSize ) ||
  258. !SetConsoleActiveScreenBuffer( OutputHandle ) ||
  259. !SetConsoleMode( InputHandle, 0 )
  260. ) {
  261. if (OutputHandle != NULL) {
  262. CloseHandle( OutputHandle );
  263. OutputHandle = NULL;
  264. }
  265. fInteractive = FALSE;
  266. }
  267. else {
  268. NormalAttribute = 0x1F;
  269. HighlightAttribute = 0x71;
  270. NumberOfCols = OriginalConsoleInfo.dwSize.X;
  271. NumberOfRows = OriginalConsoleInfo.dwSize.Y;
  272. NumberOfDetailLines = NumberOfRows;
  273. }
  274. }
  275. }
  276. p = RtlCreateQueryDebugBuffer( 0, TRUE );
  277. if (p == NULL) {
  278. fprintf( stderr, "HEAPMON: Unable to create query buffer.\n" );
  279. ExitProcess( 1 );
  280. }
  281. if (GetPriorityClass( GetCurrentProcess() ) == NORMAL_PRIORITY_CLASS) {
  282. SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
  283. }
  284. Status = RtlQueryProcessDebugInformation( (HANDLE)ProcessId,
  285. RTL_QUERY_PROCESS_MODULES |
  286. RTL_QUERY_PROCESS_HEAP_SUMMARY |
  287. RTL_QUERY_PROCESS_HEAP_TAGS,
  288. p
  289. );
  290. if (!NT_SUCCESS( Status )) {
  291. fprintf( stderr, "HEAPMON: Unable to query heap tags from Process %u (%x)\n", ProcessId, Status );
  292. fprintf( stderr, " Be sure target process was launched with the\n" );
  293. fprintf( stderr, " 'Enable heap tagging' option enabled.\n" );
  294. fprintf( stderr, " Use the GFLAGS.EXE application to do this.\n" );
  295. ExitProcess( 1 );
  296. }
  297. ScreenUpdateLoop( p );
  298. RtlDestroyQueryDebugBuffer( p );
  299. if (fInteractive) {
  300. SetConsoleActiveScreenBuffer( OriginalOutputHandle );
  301. SetConsoleMode( InputHandle, OriginalInputMode );
  302. CloseHandle( OutputHandle );
  303. }
  304. ExitProcess( 0 );
  305. return 0;
  306. }
  307. VOID
  308. ScreenUpdateLoop(
  309. PRTL_DEBUG_INFORMATION p
  310. )
  311. {
  312. NTSTATUS Status;
  313. COORD cp;
  314. COORD newcp;
  315. COORD originalCp;
  316. LONG ScrollDelta;
  317. ULONG i, MaxLines;
  318. UCHAR LastKey = 0;
  319. fFirstTimeThrough = TRUE;
  320. while (TRUE) {
  321. if (!UpdateTagDataBase( p )) {
  322. fprintf( stderr, "HEAPMON: Unable to compute tag data base\n" );
  323. break;
  324. }
  325. fFirstTimeThrough = FALSE;
  326. if (fInteractive) {
  327. UpdateDisplay();
  328. while (WaitForSingleObject( InputHandle, DelayTimeMsec ) == STATUS_WAIT_0) {
  329. //
  330. // Check for input record
  331. //
  332. if (ReadConsoleInput( InputHandle, &InputRecord, 1, &NumberOfInputRecords ) &&
  333. InputRecord.EventType == KEY_EVENT &&
  334. InputRecord.Event.KeyEvent.bKeyDown
  335. ) {
  336. LastKey = InputRecord.Event.KeyEvent.uChar.AsciiChar;
  337. if (!ProcessOptionCharacter( LastKey )
  338. ) {
  339. if (LastKey < ' ') {
  340. ScrollDelta = 0;
  341. if (LastKey == 'C'-'A'+1) {
  342. fQuit = TRUE;
  343. }
  344. else
  345. switch (InputRecord.Event.KeyEvent.wVirtualKeyCode) {
  346. case VK_ESCAPE:
  347. fQuit = TRUE;
  348. break;
  349. case VK_PRIOR:
  350. ScrollDelta = -(LONG)(InputRecord.Event.KeyEvent.wRepeatCount * NumberOfDetailLines);
  351. break;
  352. case VK_NEXT:
  353. ScrollDelta = InputRecord.Event.KeyEvent.wRepeatCount * NumberOfDetailLines;
  354. break;
  355. case VK_UP:
  356. ScrollDelta = -InputRecord.Event.KeyEvent.wRepeatCount;
  357. break;
  358. case VK_DOWN:
  359. ScrollDelta = InputRecord.Event.KeyEvent.wRepeatCount;
  360. break;
  361. case VK_HOME:
  362. FirstDetailLine = 0;
  363. break;
  364. case VK_END:
  365. FirstDetailLine = TagListLength - NumberOfDetailLines;
  366. break;
  367. }
  368. if (ScrollDelta != 0) {
  369. if (ScrollDelta < 0) {
  370. if (FirstDetailLine <= (ULONG)-ScrollDelta) {
  371. FirstDetailLine = 0;
  372. ScrollDelta = 0;
  373. }
  374. }
  375. FirstDetailLine += ScrollDelta;
  376. if (FirstDetailLine >= (TagListLength - NumberOfDetailLines)) {
  377. FirstDetailLine = TagListLength - NumberOfDetailLines;
  378. }
  379. }
  380. if (FirstDetailLine > TagListLength) {
  381. FirstDetailLine = TagListLength;
  382. }
  383. }
  384. else {
  385. switch (toupper( LastKey )) {
  386. case 'Q':
  387. //
  388. // Go to the bottom of the current screen when
  389. // we quit.
  390. //
  391. fQuit = TRUE;
  392. break;
  393. }
  394. }
  395. }
  396. else {
  397. FirstDetailLine = 0;
  398. }
  399. break;
  400. }
  401. }
  402. if (fQuit) {
  403. break;
  404. }
  405. if (fHelp) {
  406. fHelp = FALSE;
  407. ShowHelpPopup();
  408. }
  409. }
  410. else {
  411. DumpTagDataBase();
  412. if (fQuit) {
  413. break;
  414. }
  415. Sleep( DelayTimeMsec );
  416. }
  417. Status = RtlQueryProcessDebugInformation( p->TargetProcessId,
  418. p->Flags,
  419. p
  420. );
  421. if (!NT_SUCCESS( Status )) {
  422. fprintf( stderr, "HEAPMON: Unable to update heap tags from Process %p (%x)\n", p->TargetProcessId, Status );
  423. break;
  424. }
  425. }
  426. return;
  427. }
  428. BOOL
  429. WriteConsoleLine(
  430. HANDLE OutputHandle,
  431. WORD LineNumber,
  432. LPSTR Text,
  433. BOOL Highlight
  434. )
  435. {
  436. COORD WriteCoord;
  437. DWORD NumberWritten;
  438. DWORD TextLength;
  439. WriteCoord.X = 0;
  440. WriteCoord.Y = LineNumber;
  441. if (!FillConsoleOutputCharacter( OutputHandle,
  442. ' ',
  443. NumberOfCols,
  444. WriteCoord,
  445. &NumberWritten
  446. )
  447. ) {
  448. return FALSE;
  449. }
  450. if (!FillConsoleOutputAttribute( OutputHandle,
  451. (WORD)((Highlight && fHighlight) ? HighlightAttribute : NormalAttribute),
  452. NumberOfCols,
  453. WriteCoord,
  454. &NumberWritten
  455. )
  456. ) {
  457. return FALSE;
  458. }
  459. if (Text == NULL || (TextLength = strlen( Text )) == 0) {
  460. return TRUE;
  461. }
  462. else {
  463. return WriteConsoleOutputCharacter( OutputHandle,
  464. Text,
  465. TextLength,
  466. WriteCoord,
  467. &NumberWritten
  468. );
  469. }
  470. }
  471. VOID
  472. ShowHelpPopup( VOID )
  473. {
  474. HANDLE PopupHandle;
  475. WORD n;
  476. PopupHandle = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE,
  477. FILE_SHARE_WRITE | FILE_SHARE_READ,
  478. NULL,
  479. CONSOLE_TEXTMODE_BUFFER,
  480. NULL
  481. );
  482. if (PopupHandle == NULL) {
  483. return;
  484. }
  485. SetConsoleActiveScreenBuffer( PopupHandle );
  486. n = 0;
  487. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  488. WriteConsoleLine( PopupHandle, n++, " HeapMon Help", FALSE );
  489. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  490. WriteConsoleLine( PopupHandle, n++, " columns:", FALSE );
  491. WriteConsoleLine( PopupHandle, n++, " Heap Name is the name or hex address of the heap", FALSE );
  492. WriteConsoleLine( PopupHandle, n++, " Tag Name is a string given to the heap allocation", FALSE );
  493. WriteConsoleLine( PopupHandle, n++, " For untagged allocations, the tag name is a function of the size", FALSE );
  494. WriteConsoleLine( PopupHandle, n++, " Objects= 32 - objects of size 32 bytes", FALSE );
  495. WriteConsoleLine( PopupHandle, n++, " Objects>1024 - objects larger than 1024 bytes are lumped under this tag", FALSE );
  496. WriteConsoleLine( PopupHandle, n++, " VirtualAlloc - objects larger than 1MB bytes are lumped under this tag", FALSE );
  497. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  498. WriteConsoleLine( PopupHandle, n++, " Allocations is count of all alloctions", FALSE );
  499. WriteConsoleLine( PopupHandle, n++, " ( ) is difference in Allocations column from last update", FALSE );
  500. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  501. WriteConsoleLine( PopupHandle, n++, " Frees is count of all frees", FALSE );
  502. WriteConsoleLine( PopupHandle, n++, " ( ) difference in Frees column from last update", FALSE );
  503. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  504. WriteConsoleLine( PopupHandle, n++, " Diff is (Allocations - Frees)", FALSE );
  505. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  506. WriteConsoleLine( PopupHandle, n++, " Bytes Used is the total bytes consumed in heap", FALSE );
  507. WriteConsoleLine( PopupHandle, n++, " ( ) difference in Bytes column from last update", FALSE );
  508. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  509. WriteConsoleLine( PopupHandle, n++, " switches: ", FALSE );
  510. WriteConsoleLine( PopupHandle, n++, " ? or h - gives this help", FALSE );
  511. WriteConsoleLine( PopupHandle, n++, " q - quits", FALSE );
  512. WriteConsoleLine( PopupHandle, n++, " e - toggles totals lines on and off", FALSE );
  513. WriteConsoleLine( PopupHandle, n++, " l - toggles highlighting of changed lines on and off", FALSE );
  514. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  515. WriteConsoleLine( PopupHandle, n++, " sorting switches:", FALSE );
  516. WriteConsoleLine( PopupHandle, n++, " t - tag a - allocations", FALSE );
  517. WriteConsoleLine( PopupHandle, n++, " f - frees d - difference", FALSE );
  518. WriteConsoleLine( PopupHandle, n++, " b - bytes (u is the same as b)", FALSE );
  519. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  520. WriteConsoleLine( PopupHandle, n++, " ) - toggles sort between primary value and value in ( )", FALSE );
  521. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  522. WriteConsoleLine( PopupHandle, n++, " command line switches", FALSE );
  523. WriteConsoleLine( PopupHandle, n++, " -eltafdbu) - as listed above", FALSE );
  524. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  525. WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
  526. while (TRUE) {
  527. if (WaitForSingleObject( InputHandle, DelayTimeMsec ) == STATUS_WAIT_0 &&
  528. ReadConsoleInput( InputHandle, &InputRecord, 1, &NumberOfInputRecords ) &&
  529. InputRecord.EventType == KEY_EVENT &&
  530. InputRecord.Event.KeyEvent.bKeyDown &&
  531. InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE
  532. ) {
  533. break;
  534. }
  535. }
  536. SetConsoleActiveScreenBuffer( OutputHandle );
  537. CloseHandle( PopupHandle );
  538. return;
  539. }
  540. VOID
  541. UpdateDisplay( VOID )
  542. {
  543. ULONG HeapLines, DetailLines, SummaryLines;
  544. WORD DisplayLine;
  545. PHEAP_ENTRY pHeap;
  546. PTAG_ENTRY p, *pp;
  547. CHAR Buffer[ 512 ];
  548. HeapLines = HeapListLength;
  549. if (fDisplayTotals) {
  550. SummaryLines = 2;
  551. RtlZeroMemory( &TagTotals, sizeof( TagTotals ) );
  552. }
  553. else {
  554. SummaryLines = 0;
  555. }
  556. DetailLines = NumberOfRows - HeapLines - SummaryLines - 3;
  557. NumberOfDetailLines = DetailLines;
  558. if (DetailLines > (TagListLength - FirstDetailLine)) {
  559. DetailLines = TagListLength - FirstDetailLine;
  560. }
  561. DisplayLine = 0;
  562. WriteConsoleLine( OutputHandle,
  563. DisplayLine++,
  564. "Heap Name Address Allocated Committed Free",
  565. FALSE
  566. );
  567. pHeap = HeapListHead;
  568. while (pHeap != NULL && HeapLines--) {
  569. sprintf( Buffer,
  570. "%-20.20s %p %8u %8u %8u",
  571. pHeap->HeapName,
  572. pHeap->HeapBase,
  573. pHeap->BytesAllocated,
  574. pHeap->BytesCommitted,
  575. pHeap->BytesCommitted - pHeap->BytesAllocated
  576. );
  577. WriteConsoleLine( OutputHandle,
  578. DisplayLine++,
  579. Buffer,
  580. pHeap->Changed
  581. );
  582. pHeap->Changed = FALSE;
  583. pHeap = pHeap->Next;
  584. }
  585. pp = &TagArray[ FirstDetailLine ];
  586. WriteConsoleLine( OutputHandle,
  587. DisplayLine++,
  588. "Heap Name Tag Name Allocations Frees Diff Bytes Used",
  589. FALSE
  590. );
  591. while (DetailLines--) {
  592. p = *pp++;
  593. sprintf( Buffer,
  594. "%-20.20s %-14.14s %8u (%6u) %8u (%6u) %6u %8u (%6u)",
  595. p->HeapName,
  596. p->TagName,
  597. p->Counts.Allocs,
  598. p->Differences.Allocs,
  599. p->Counts.Frees,
  600. p->Differences.Frees,
  601. p->Counts.Allocs_Frees,
  602. p->Counts.Used,
  603. p->Differences.Used
  604. );
  605. if (fDisplayTotals) {
  606. TagTotals.Counts.Allocs += p->Counts.Allocs;
  607. TagTotals.Differences.Allocs += p->Differences.Allocs;
  608. TagTotals.Counts.Frees += p->Counts.Frees;
  609. TagTotals.Differences.Frees += p->Differences.Frees;
  610. TagTotals.Counts.Allocs_Frees += p->Counts.Allocs_Frees;
  611. TagTotals.Differences.Allocs_Frees += p->Counts.Used;
  612. TagTotals.Differences.Used += p->Differences.Used;
  613. TagTotals.Changed |= p->Changed;
  614. }
  615. WriteConsoleLine( OutputHandle,
  616. DisplayLine++,
  617. Buffer,
  618. p->Changed
  619. );
  620. p->Changed = FALSE;
  621. }
  622. while (SummaryLines--) {
  623. if (SummaryLines) {
  624. WriteConsoleLine( OutputHandle,
  625. DisplayLine++,
  626. NULL,
  627. FALSE
  628. );
  629. }
  630. else {
  631. sprintf( Buffer,
  632. "%-20.20s %-14.14s %8u (%6u) %8u (%6u) %6u %8u (%6u)",
  633. "Totals",
  634. "",
  635. TagTotals.Counts.Allocs,
  636. TagTotals.Differences.Allocs,
  637. TagTotals.Counts.Frees,
  638. TagTotals.Differences.Frees,
  639. TagTotals.Counts.Allocs_Frees,
  640. TagTotals.Differences.Allocs_Frees,
  641. TagTotals.Differences.Used
  642. );
  643. WriteConsoleLine( OutputHandle,
  644. DisplayLine++,
  645. Buffer,
  646. TagTotals.Changed
  647. );
  648. }
  649. }
  650. while (DisplayLine < NumberOfRows) {
  651. WriteConsoleLine( OutputHandle,
  652. DisplayLine++,
  653. NULL,
  654. FALSE
  655. );
  656. }
  657. return;
  658. }
  659. VOID
  660. DumpTagDataBase( VOID )
  661. {
  662. ULONG i;
  663. PTAG_ENTRY p;
  664. for (i=0; i<TagListLength; i++) {
  665. p = TagArray[ i ];
  666. if (p->Changed && (p->Counts.Used != 0)) {
  667. printf( "%-14.14s%-20.20s %8u (%6u) %8u (%6u) %6u %8u (%6u)\n",
  668. p->HeapName,
  669. p->TagName,
  670. p->Counts.Allocs,
  671. p->Differences.Allocs,
  672. p->Counts.Frees,
  673. p->Differences.Frees,
  674. p->Counts.Allocs_Frees,
  675. p->Counts.Used,
  676. p->Differences.Used
  677. );
  678. p->Changed = FALSE;
  679. }
  680. }
  681. return;
  682. }
  683. __inline int DiffSizeT(SIZE_T s1, SIZE_T s2)
  684. {
  685. if (s1 == s2)
  686. return 0;
  687. if (s1 > s2)
  688. return -1;
  689. else
  690. return 1;
  691. }
  692. int
  693. __cdecl
  694. CompareTagFunction(
  695. const void *e1,
  696. const void *e2
  697. )
  698. {
  699. int Result;
  700. PTAG_ENTRY p1, p2;
  701. SIZE_T s1, s2;
  702. p1 = *(PTAG_ENTRY *)e1;
  703. p2 = *(PTAG_ENTRY *)e2;
  704. switch (SortBy) {
  705. case TAG:
  706. Result = _stricmp( p1->HeapName, p2->HeapName );
  707. if (!Result) {
  708. Result = _stricmp( p1->TagName, p2->TagName );
  709. }
  710. return Result;
  711. case ALLOC:
  712. if (fParen) {
  713. return DiffSizeT(p2->Differences.Allocs, p1->Differences.Allocs);
  714. }
  715. else {
  716. return DiffSizeT(p2->Counts.Allocs, p1->Counts.Allocs);
  717. }
  718. case FREE:
  719. if (fParen) {
  720. return DiffSizeT(p2->Differences.Frees, p1->Differences.Frees);
  721. }
  722. else {
  723. return DiffSizeT(p2->Counts.Frees, p1->Counts.Frees);
  724. }
  725. case BYTES:
  726. if (fParen) {
  727. return DiffSizeT(p2->Differences.Used, p1->Differences.Used);
  728. }
  729. else {
  730. return DiffSizeT(p2->Counts.Used, p1->Counts.Used);
  731. }
  732. case DIFF:
  733. return DiffSizeT(p2->Counts.Allocs_Frees, p1->Counts.Allocs_Frees);
  734. }
  735. return(0);
  736. }
  737. BOOLEAN
  738. UpdateTagDataBase(
  739. PRTL_DEBUG_INFORMATION DebugInfo
  740. )
  741. {
  742. PTAG_ENTRY p, p1, *pp;
  743. PLIST_ENTRY Next, Head;
  744. ULONG HeapNumber;
  745. PRTL_PROCESS_HEAPS Heaps;
  746. PRTL_HEAP_INFORMATION HeapInfo;
  747. PRTL_HEAP_TAG HeapTagEntry;
  748. PVOID HeapNameBase;
  749. PCHAR HeapName;
  750. ULONG TagIndex;
  751. UCHAR Buffer[ MAX_PATH ];
  752. BOOL CalcDifferences;
  753. if (!UpdateHeapDataBase( DebugInfo )) {
  754. return FALSE;
  755. }
  756. HeapNameBase = INVALID_HANDLE_VALUE;
  757. pp = &TagListHead;
  758. Heaps = DebugInfo->Heaps;
  759. HeapInfo = &Heaps->Heaps[ 0 ];
  760. for (HeapNumber = 0; HeapNumber < Heaps->NumberOfHeaps; HeapNumber++) {
  761. if (HeapInfo->Tags != NULL && HeapInfo->NumberOfTags > 0) {
  762. HeapTagEntry = HeapInfo->Tags;
  763. for (TagIndex=0; TagIndex<HeapInfo->NumberOfTags; TagIndex++) {
  764. p = *pp;
  765. if (p == NULL ||
  766. p->HeapBase != HeapInfo->BaseAddress ||
  767. p->TagIndex != HeapTagEntry->TagIndex
  768. ) {
  769. if (HeapTagEntry->NumberOfAllocations != 0 ||
  770. HeapTagEntry->NumberOfFrees != 0 ||
  771. HeapTagEntry->BytesAllocated != 0
  772. ) {
  773. *pp = RtlAllocateHeap( RtlProcessHeap(),
  774. HEAP_ZERO_MEMORY,
  775. sizeof( *p )
  776. );
  777. if (*pp == NULL) {
  778. return FALSE;
  779. }
  780. (*pp)->Next = p;
  781. p = *pp;
  782. if (p->Next == NULL) {
  783. pp = &p->Next;
  784. }
  785. p->HeapBase = HeapInfo->BaseAddress;
  786. if (p->HeapBase != HeapNameBase) {
  787. HeapName = GetNameForHeapBase( HeapNameBase = p->HeapBase );
  788. }
  789. p->HeapName = HeapName;
  790. p->TagIndex = HeapTagEntry->TagIndex;
  791. sprintf( Buffer, "%ws", HeapTagEntry->TagName );
  792. p->TagName = AddNameToNameTable( NameTable, Buffer );
  793. p->Changed = !fFirstTimeThrough;
  794. TagListLength += 1;
  795. CalcDifferences = FALSE;
  796. }
  797. else {
  798. p = NULL;
  799. }
  800. }
  801. else {
  802. pp = &p->Next;
  803. p->PrevCounts = p->Counts;
  804. CalcDifferences = TRUE;
  805. p->Changed = FALSE;
  806. }
  807. if (p != NULL) {
  808. p->Counts.Allocs = HeapTagEntry->NumberOfAllocations;
  809. p->Counts.Frees = HeapTagEntry->NumberOfFrees;
  810. p->Counts.Used = HeapTagEntry->BytesAllocated;
  811. p->Counts.Allocs_Frees = p->Counts.Allocs - p->Counts.Frees;
  812. if (CalcDifferences) {
  813. p->Differences.Allocs = p->Counts.Allocs - p->PrevCounts.Allocs;
  814. p->Differences.Frees = p->Counts.Frees - p->PrevCounts.Frees;
  815. p->Differences.Used = p->Counts.Used - p->PrevCounts.Used;
  816. p->Differences.Allocs_Frees = p->Counts.Allocs_Frees - p->PrevCounts.Allocs_Frees;
  817. if (p->Differences.Allocs != 0 ||
  818. p->Differences.Frees != 0 ||
  819. p->Differences.Used != 0 ||
  820. p->Differences.Allocs_Frees != 0
  821. ) {
  822. p->Changed = TRUE;
  823. }
  824. }
  825. }
  826. HeapTagEntry += 1;
  827. }
  828. }
  829. HeapInfo += 1;
  830. }
  831. if (TagArray != NULL) {
  832. RtlFreeHeap( RtlProcessHeap(), 0, TagArray );
  833. }
  834. TagArray = RtlAllocateHeap( RtlProcessHeap(), 0, TagListLength * sizeof( *TagArray ) );
  835. if (TagArray == NULL) {
  836. return FALSE;
  837. }
  838. p = TagListHead;
  839. pp = TagArray;
  840. while (p != NULL) {
  841. *pp++ = p;
  842. p = p->Next;
  843. }
  844. qsort( (void *)TagArray,
  845. (size_t)TagListLength,
  846. (size_t)sizeof( *TagArray ),
  847. CompareTagFunction
  848. );
  849. return TRUE;
  850. }
  851. BOOLEAN
  852. UpdateHeapDataBase(
  853. PRTL_DEBUG_INFORMATION DebugInfo
  854. )
  855. {
  856. PHEAP_ENTRY p, *pp;
  857. PRTL_PROCESS_HEAPS Heaps;
  858. PRTL_HEAP_INFORMATION HeapInfo;
  859. PRTL_HEAP_TAG HeapTagEntry;
  860. ULONG i;
  861. UCHAR Buffer[ MAX_PATH ];
  862. PCHAR s;
  863. pp = &HeapListHead;
  864. Heaps = DebugInfo->Heaps;
  865. HeapInfo = Heaps->Heaps;
  866. for (i=0; i<Heaps->NumberOfHeaps; i++) {
  867. p = *pp;
  868. if (p == NULL ||
  869. p->HeapBase != HeapInfo->BaseAddress
  870. ) {
  871. *pp = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *p ) );
  872. if (*pp == NULL) {
  873. return FALSE;
  874. }
  875. (*pp)->Next = p;
  876. p = *pp;
  877. if (p->Next == NULL) {
  878. pp = &p->Next;
  879. }
  880. p->HeapBase = HeapInfo->BaseAddress;
  881. HeapTagEntry = HeapInfo->Tags + HeapInfo->NumberOfPseudoTags;
  882. if (HeapInfo->NumberOfTags > HeapInfo->NumberOfPseudoTags &&
  883. HeapTagEntry->TagName[ 0 ] != UNICODE_NULL
  884. ) {
  885. sprintf( Buffer, "%ws", HeapTagEntry->TagName );
  886. }
  887. else {
  888. sprintf( Buffer, "%p", p->HeapBase );
  889. }
  890. p->HeapName = AddNameToNameTable( NameTable, Buffer );
  891. p->BytesAllocated = HeapInfo->BytesAllocated;
  892. p->BytesCommitted = HeapInfo->BytesCommitted;
  893. p->Changed = !fFirstTimeThrough;
  894. HeapListLength += 1;
  895. }
  896. else {
  897. p->Changed = FALSE;
  898. if (HeapInfo->BytesAllocated != p->BytesAllocated) {
  899. p->Changed = TRUE;
  900. p->BytesAllocated = HeapInfo->BytesAllocated;
  901. }
  902. if (HeapInfo->BytesCommitted != p->BytesCommitted) {
  903. p->Changed = TRUE;
  904. p->BytesCommitted = HeapInfo->BytesCommitted;
  905. }
  906. pp = &p->Next;
  907. }
  908. HeapInfo += 1;
  909. }
  910. return TRUE;
  911. }
  912. PCHAR
  913. GetNameForHeapBase(
  914. PVOID HeapBase
  915. )
  916. {
  917. PHEAP_ENTRY p;
  918. p = HeapListHead;
  919. while (p != NULL) {
  920. if (p->HeapBase == HeapBase) {
  921. return p->HeapName;
  922. }
  923. else {
  924. p = p->Next;
  925. }
  926. }
  927. return NULL;
  928. }
  929. typedef struct _NAME_TABLE_ENTRY {
  930. struct _NAME_TABLE_ENTRY *HashLink;
  931. UCHAR Name[ 1 ];
  932. } NAME_TABLE_ENTRY, *PNAME_TABLE_ENTRY;
  933. typedef struct _NAME_TABLE {
  934. ULONG NumberOfBuckets;
  935. PNAME_TABLE_ENTRY Buckets[1];
  936. } NAME_TABLE, *PNAME_TABLE;
  937. PVOID
  938. CreateNameTable(
  939. IN ULONG NumberOfBuckets
  940. )
  941. {
  942. PNAME_TABLE p;
  943. ULONG Size;
  944. Size = FIELD_OFFSET( NAME_TABLE, Buckets ) +
  945. (sizeof( PNAME_TABLE_ENTRY ) * NumberOfBuckets);
  946. p = (PNAME_TABLE)RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, Size );
  947. if (p != NULL) {
  948. p->NumberOfBuckets = NumberOfBuckets;
  949. }
  950. return p;
  951. }
  952. PCHAR
  953. AddNameToNameTable(
  954. IN PVOID pNameTable,
  955. IN PCHAR Name
  956. )
  957. {
  958. PNAME_TABLE NameTable = pNameTable;
  959. PNAME_TABLE_ENTRY p, *pp;
  960. ULONG Value;
  961. ULONG n, Hash;
  962. UCHAR c;
  963. PCHAR s;
  964. PNAME_TABLE_ENTRY *pa, a;
  965. s = Name;
  966. Hash = 0;
  967. while (c = *s++) {
  968. c = (UCHAR)toupper( c );
  969. Hash = Hash + (c << 1) + (c >> 1) + c;
  970. }
  971. n = (ULONG)((PCHAR)s - (PCHAR)Name);
  972. pp = &NameTable->Buckets[ Hash % NameTable->NumberOfBuckets ];
  973. while (p = *pp) {
  974. if (!_stricmp( p->Name, Name )) {
  975. break;
  976. }
  977. else {
  978. pp = &p->HashLink;
  979. }
  980. }
  981. if (p == NULL) {
  982. p = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( *p ) + n );
  983. if (p == NULL) {
  984. return NULL;
  985. }
  986. p->HashLink = NULL;
  987. strcpy( p->Name, Name );
  988. *pp = p;
  989. }
  990. return p->Name;
  991. }
  992. BOOL
  993. ProcessOptionCharacter(
  994. IN CHAR c
  995. )
  996. {
  997. switch (toupper( c )) {
  998. case 'T':
  999. SortBy = TAG;
  1000. return TRUE;
  1001. case 'A':
  1002. SortBy = ALLOC;
  1003. return TRUE;
  1004. case 'U':
  1005. case 'B':
  1006. SortBy = BYTES;
  1007. return TRUE;
  1008. case 'F':
  1009. SortBy = FREE;
  1010. return TRUE;
  1011. case 'D':
  1012. SortBy = DIFF;
  1013. return TRUE;
  1014. case '(':
  1015. case ')':
  1016. fParen = !fParen;
  1017. return TRUE;
  1018. case 'E':
  1019. fDisplayTotals = !fDisplayTotals;
  1020. return TRUE;
  1021. case 'L':
  1022. fHighlight = !fHighlight;
  1023. break;
  1024. case 'H':
  1025. case '?':
  1026. fHelp = TRUE;
  1027. return TRUE;
  1028. }
  1029. return FALSE;
  1030. }