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