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.

702 lines
19 KiB

  1. /*****************************************************************************\
  2. * DHCMP - Compare DH.EXE outputs.
  3. *
  4. * Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
  5. *
  6. * DHCMP is a character-mode tool which processes DH output file(s) into forms
  7. * which may be more useful in investigate memory leaks etc.
  8. *
  9. * DH is a useful tool which displays heap allocations in a properly enabled
  10. * system, but the output is sometimes hard to analyze and interpret.
  11. * The output is a list of allocation backtraces: each backtrace contains up to
  12. * MAX_BT call-sites, and is accompanied by the number of bytes allocated.
  13. *
  14. * 02-01-95 IanJa bugfixes and handle BackTraceNNNNN identifiers from dh.exe
  15. * 03/22/95 IanJa modify to cope with current DH output format.
  16. * 07/27/98 t-mattba added -v switch
  17. \*****************************************************************************/
  18. char *pszHow =
  19. " DHCMP has two modes:\n"
  20. "\n"
  21. " 1) DHCMP [-d] dh_dump1.txt dh_dump2.txt\n"
  22. " This compares two DH dumps, useful for finding leaks.\n"
  23. " dh_dump1.txt & dh_dump2.txt are obtained before and after some test\n"
  24. " scenario. DHCMP matches the backtraces from each file and calculates\n"
  25. " the increase in bytes allocated for each backtrace. These are then\n"
  26. " displayed in descending order of size of leak\n"
  27. " The first line of each backtrace output shows the size of the leak in\n"
  28. " bytes, followed by the (last-first) difference in parentheses.\n"
  29. " Leaks of size 0 are not shown.\n"
  30. "\n"
  31. " 2) DHCMP [-d] dh_dump.txt\n"
  32. " For each allocation backtrace, the number of bytes allocated will be\n"
  33. " attributed to each callsite (each line of the backtrace). The number\n"
  34. " of bytes allocated per callsite are summed and the callsites are then\n"
  35. " displayed in descending order of bytes allocated. This is useful for\n"
  36. " finding a leak that is reached via many different codepaths.\n"
  37. " ntdll!RtlAllocateHeap@12 will appear first when analyzing DH dumps of\n"
  38. " csrss.exe, since all allocation will have gone through that routine.\n"
  39. " Similarly, ProcessApiRequest will be very prominent too, since that\n"
  40. " appears in most allocation backtraces. Hence the useful thing to do\n"
  41. " with mode 2 output is to use dhcmp to comapre two of them:\n"
  42. " dhcmp dh_dump1.txt > tmp1.txt\n"
  43. " dhcmp dh_dump2.txt > tmp2.txt\n"
  44. " dhcmp tmp1.txt tmp2.txt\n"
  45. " the output will show the differences.\n"
  46. "\n"
  47. " Flags:\n"
  48. " -d Output in decimal (default is hexadecimal)\n"
  49. // " -t Find Totals (NOT YET IMPLEMENTED)\n"
  50. " -v Verbose output: include the actual backtraces as well as summary information\n"
  51. " (Verbose output is only interesting in mode 1 above.)\n"
  52. " -? This help\n";
  53. #include <windows.h>
  54. #include <stdio.h>
  55. #include <stdlib.h>
  56. #include <string.h>
  57. #define NHASH 47
  58. #define TRUE 1
  59. #define FALSE 0
  60. typedef int BOOL;
  61. #define TYPE_WHOLESTACK 0
  62. #define TYPE_FUNCTIONS 1
  63. #define MAXLINELENGTH 4096
  64. #define MAXFUNCNAMELENGTH 1024
  65. #define MAX_BT 48 /* max length of back trace stack */
  66. void AddToName(char *fnname, unsigned __int64 nb, int sign);
  67. void SetAllocs(char *fnname, unsigned __int64 nb, int sign);
  68. void Process(char *fnam, int sign, int type);
  69. void SortAll();
  70. void AddToStackTrace(char *fnname, char *line);
  71. void ResetStackTrace(char *fnname);
  72. /*
  73. * Hashing
  74. */
  75. int MakeHash(char *pName);
  76. void InitHashTab();
  77. #define DUMPF_FIRST (1)
  78. #define DUMPF_SECOND (2)
  79. #define DUMPF_RESULT (4)
  80. #define DUMPF_ALL (DUMPF_FIRST | DUMPF_SECOND | DUMPF_RESULT)
  81. void DumpNodes(int Flags);
  82. #define F_DECIMAL 0x0001
  83. #define F_TOTAL 0x0002
  84. #define F_VERBOSE 0x0004
  85. //
  86. // Globals
  87. //
  88. int gFlags = 0;
  89. BOOL DHCMP(ULONG argc, PCHAR * argv) {
  90. int n, DumpType;
  91. InitHashTab();
  92. for (n = 1; n < (int)argc; n++) {
  93. if ((argv[n][0] == '-') || (argv[n][0] == '/')) {
  94. /*
  95. * Flags
  96. */
  97. switch (argv[n][1]) {
  98. case 'd':
  99. gFlags |= F_DECIMAL;
  100. break;
  101. //NOT YET IMPLEMENTED
  102. //case 't':
  103. // gFlags |= F_TOTAL;
  104. // break;
  105. case 'v':
  106. gFlags |= F_VERBOSE;
  107. break;
  108. case '?':
  109. default:
  110. return FALSE;
  111. }
  112. } else {
  113. /*
  114. * No more flags
  115. */
  116. break;
  117. }
  118. }
  119. if ((argc - n) == 2) {
  120. DumpType = DUMPF_ALL;
  121. Process(argv[n], -1, TYPE_WHOLESTACK);
  122. Process(argv[n+1], +1, TYPE_WHOLESTACK);
  123. } else if ((argc - n) == 1) {
  124. //
  125. // F_VERBOSE is not meaningful when groveling only one dump.
  126. //
  127. gFlags &= ~F_VERBOSE;
  128. DumpType = DUMPF_RESULT;
  129. Process(argv[n], +1, TYPE_FUNCTIONS);
  130. } else {
  131. return FALSE;
  132. }
  133. // printf("==================== BEFORE SORTING ====================\n");
  134. // DumpNodes(DUMPF_ALL);
  135. SortAll();
  136. // printf("==================== AFTER SORTING ====================\n");
  137. DumpNodes(DumpType);
  138. return TRUE;
  139. }
  140. void Process(char *fname, int sign, int type) {
  141. FILE *stream;
  142. char linebuff[MAXLINELENGTH];
  143. char fnnamebuff[MAXFUNCNAMELENGTH];
  144. char BackTraceBuff[MAXFUNCNAMELENGTH * MAX_BT] = {0};
  145. char *p;
  146. int lineno = 0;
  147. BOOL skip = TRUE; // start out skipping lines
  148. int iT;
  149. unsigned __int64 ulT = 0L;
  150. unsigned __int64 nBytes = 0L;
  151. unsigned __int64 ulConsumed;
  152. unsigned __int64 lAllocs;
  153. // printf("PROCESS %s %d %d\n", fname, sign, type);
  154. stream = fopen(fname, "r");
  155. if (stream == NULL) {
  156. fprintf(stderr, "Can't open %s for reading\n", fname);
  157. exit (2);
  158. }
  159. nBytes = 0;
  160. while (fgets(linebuff, sizeof(linebuff), stream) != NULL) {
  161. lineno++;
  162. //fprintf(stderr, "Line #%d\r", lineno);
  163. if (linebuff[0] == '*') {
  164. //
  165. // If we find a "hogs" line, stack traces follow, any other line
  166. // started by "*" should cause us to go back to searching for a
  167. // hogs block.
  168. //
  169. if (strstr(linebuff,
  170. "Hogs")) {
  171. skip = FALSE;
  172. } else {
  173. skip = TRUE;
  174. }
  175. continue;
  176. }
  177. if (skip) {
  178. //
  179. // Skip is enabled, skip this line, it is data about the heap
  180. // between 'heap information' and 'heap hogs' lines.
  181. //
  182. continue;
  183. }
  184. if (linebuff[0] != ' ')
  185. {
  186. //
  187. // Scan for byte count and find out how many characters have
  188. // been consumed by this action.
  189. //
  190. ulConsumed = 0;
  191. iT = sscanf(linebuff, "%I64x bytes in %I64x", &ulT, &lAllocs);
  192. if (iT > 0)
  193. {
  194. nBytes = ulT;
  195. p = strstr(linebuff, "BackTrace");
  196. if (!p)
  197. {
  198. //
  199. // What's this ?
  200. //
  201. continue;
  202. }
  203. strcpy(BackTraceBuff, p);
  204. p = strchr(BackTraceBuff, '\n');
  205. if (p)
  206. {
  207. *p = '\0';
  208. }
  209. if (type == TYPE_FUNCTIONS)
  210. {
  211. //
  212. // BackTraceBuff is now saved for use with the rest of the
  213. // trace.
  214. //
  215. continue;
  216. }
  217. AddToName(BackTraceBuff, nBytes, sign);
  218. if(iT == 1)
  219. {
  220. lAllocs = 1;
  221. }
  222. SetAllocs(BackTraceBuff, lAllocs, sign);
  223. ResetStackTrace(BackTraceBuff);
  224. }
  225. }
  226. else if (nBytes != 0)
  227. {
  228. /*
  229. * If TYPE_WHOLESTACK, then add the count to each line of the
  230. * stack backtrace.
  231. */
  232. if (sscanf(linebuff, " %[^+]+0x", fnnamebuff) == 1) {
  233. if (type == TYPE_FUNCTIONS) {
  234. AddToName(fnnamebuff, nBytes, sign);
  235. }
  236. if ((gFlags & F_VERBOSE) == F_VERBOSE) {
  237. AddToStackTrace(BackTraceBuff, linebuff);
  238. }
  239. continue;
  240. } else {
  241. nBytes = 0;
  242. }
  243. }
  244. }
  245. /*
  246. * make sure to account for the final one.
  247. */
  248. if (type == TYPE_WHOLESTACK) {
  249. AddToName(BackTraceBuff, nBytes, sign);
  250. }
  251. if (fname != NULL) {
  252. fclose(stream);
  253. }
  254. }
  255. /*
  256. * Hashing
  257. */
  258. typedef struct tagNODE {
  259. char *pName;
  260. __int64 lValue;
  261. __int64 lFirst;
  262. __int64 lSecond;
  263. char BackTrace[MAX_BT][MAXFUNCNAMELENGTH];
  264. long lPosition;
  265. __int64 lAllocsFirst;
  266. __int64 lAllocsSecond;
  267. struct tagNODE *pNext;
  268. } NODE, *PNODE;
  269. VOID
  270. DumpStackTrace (
  271. PNODE pNode
  272. );
  273. VOID
  274. DumpLogDescription (
  275. VOID
  276. );
  277. PNODE HashTab[NHASH];
  278. void InitHashTab() {
  279. int i;
  280. for (i = 0; i < NHASH; i++) {
  281. HashTab[i] = NULL;
  282. }
  283. }
  284. int MakeHash(char *pName) {
  285. int hash = 0;
  286. while (*pName) {
  287. hash += *pName;
  288. pName++;
  289. }
  290. return hash % NHASH;
  291. }
  292. void DumpNodes(int Flags) {
  293. PNODE pNode;
  294. int i;
  295. unsigned __int64 ulTotal = 0;
  296. char *fmt1;
  297. char *fmt2;
  298. char *fmt3;
  299. char *fmt4;
  300. char *fmt5;
  301. char *fmt6;
  302. char *fmt7;
  303. DumpLogDescription ();
  304. if ((gFlags & F_VERBOSE) == F_VERBOSE) {
  305. if (gFlags & F_DECIMAL) {
  306. fmt1 = "% 8I64d %s\n";
  307. fmt2 = "% 8I64d bytes by: %s\n";
  308. fmt3 = "+% 8I64d ( %6I64d - %6I64d) %6I64d allocs\t%s\n";
  309. fmt4 = "-% 8I64d ( %6I64d - %6I64d) %6I64d allocs\t%s\n";
  310. fmt5 = "\nTotal increase == %I64d\n";
  311. fmt6 = "+% 8I64d ( %6I64d - %6I64d)\t%s\tallocations\n";
  312. fmt7 = "-% 8I64d ( %6I64d - %6I64d)\t%s\tallocations\n";
  313. } else {
  314. fmt1 = "%08I64x %s\n";
  315. fmt2 = "%08I64x bytes by: %s\n";
  316. fmt3 = "+% 8I64x ( %5I64x - %5I64x) %6I64x allocs\t%s\n";
  317. fmt4 = "-% 8I64x ( %5I64x - %5I64x) %6I64x allocs\t%s\n";
  318. fmt5 = "\nTotal increase == %I64x\n";
  319. fmt6 = "+% 8I64x ( %5I64x - %5I64x)\t%s\tallocations\n";
  320. fmt7 = "-% 8I64x ( %5I64x - %5I64x)\t%s\tallocations\n";
  321. }
  322. } else {
  323. if (gFlags & F_DECIMAL) {
  324. fmt1 = "% 8I64d %s\n";
  325. fmt2 = "% 8I64d bytes by: %s\n";
  326. fmt3 = "+% 8I64d ( %6I64d - %6I64d) %6I64d allocs\t%s\n";
  327. fmt4 = "\n-% 8I64d ( %6I64d - %6I64d) %6I64d allocs\t%s";
  328. fmt5 = "\nTotal increase == %I64d\n";
  329. } else {
  330. fmt1 = "%08I64x %s\n";
  331. fmt2 = "%08I64x bytes by: %s\n";
  332. fmt3 = "+% 8I64x ( %5I64x - %5I64x) %6I64x allocs\t%s\n";
  333. fmt4 = "\n-% 8I64x ( %5I64x - %5I64x) %6I64x allocs\t%s";
  334. fmt5 = "\nTotal increase == %I64x\n";
  335. }
  336. }
  337. for (i = 0; i < NHASH; i++) {
  338. // printf("========= HASH %d ==========\n", i);
  339. for (pNode = HashTab[i]; pNode != NULL; pNode = pNode->pNext) {
  340. switch (Flags) {
  341. case DUMPF_FIRST:
  342. printf(fmt1, pNode->lFirst, pNode->pName);
  343. break;
  344. case DUMPF_SECOND:
  345. printf(fmt1, pNode->lSecond, pNode->pName);
  346. break;
  347. case DUMPF_RESULT:
  348. printf(fmt2, pNode->lValue, pNode->pName);
  349. break;
  350. case DUMPF_ALL:
  351. if (pNode->lValue > 0) {
  352. printf(fmt3, pNode->lValue,
  353. pNode->lSecond, pNode->lFirst, (pNode->lAllocsSecond), pNode->pName);
  354. } else if (pNode->lValue < 0) {
  355. printf(fmt4, -pNode->lValue,
  356. pNode->lSecond, pNode->lFirst, (pNode->lAllocsSecond), pNode->pName);
  357. }
  358. if((gFlags & F_VERBOSE) == F_VERBOSE) {
  359. if(pNode->lAllocsSecond-pNode->lAllocsFirst > 0) {
  360. printf(fmt6, pNode->lAllocsSecond-pNode->lAllocsFirst,
  361. pNode->lAllocsSecond, pNode->lAllocsFirst, pNode->pName);
  362. } else if(pNode->lAllocsSecond-pNode->lAllocsFirst < 0) {
  363. printf(fmt7, -(pNode->lAllocsSecond-pNode->lAllocsFirst),
  364. pNode->lAllocsSecond, pNode->lAllocsFirst, pNode->pName);
  365. }
  366. }
  367. break;
  368. }
  369. ulTotal += pNode->lValue;
  370. if(((gFlags & F_VERBOSE) == F_VERBOSE) && (pNode->lValue != 0)) {
  371. DumpStackTrace(pNode);
  372. }
  373. }
  374. }
  375. if (Flags == DUMPF_ALL) {
  376. printf(fmt5, ulTotal);
  377. }
  378. }
  379. PNODE FindNode(char *pName) {
  380. int i;
  381. PNODE pNode;
  382. i = MakeHash(pName);
  383. pNode = HashTab[i];
  384. while (pNode) {
  385. if (strcmp(pName, pNode->pName) == 0) {
  386. return pNode;
  387. }
  388. pNode = pNode->pNext;
  389. }
  390. // Not found
  391. // fprintf(stderr, "NEW %s\n", pName);
  392. pNode = malloc(sizeof(NODE));
  393. if (!pNode) {
  394. fprintf(stderr, "malloc failed in FindNode\n");
  395. exit(2);
  396. }
  397. pNode->pName = _strdup(pName);
  398. if (!pNode->pName) {
  399. fprintf(stderr, "strdup failed in FindNode\n");
  400. exit(2);
  401. }
  402. pNode->pNext = HashTab[i];
  403. HashTab[i] = pNode;
  404. pNode->lValue = 0L;
  405. pNode->lFirst = 0L;
  406. pNode->lSecond = 0L;
  407. pNode->lPosition = 0L;
  408. pNode->lAllocsFirst = 0L;
  409. pNode->lAllocsSecond = 0L;
  410. return pNode;
  411. }
  412. void AddToName(char *fnname, unsigned __int64 nb, int sign) {
  413. PNODE pNode;
  414. // fprintf(stderr, "%s += %lx\n", fnname, nb);
  415. pNode = FindNode(fnname);
  416. pNode->lValue += nb * sign;
  417. if (sign == -1) {
  418. pNode->lFirst += nb;
  419. } else {
  420. pNode->lSecond += nb;
  421. }
  422. // fprintf(stderr, "%s == %lx\n", fnname, pNode->lValue);
  423. }
  424. void SetAllocs(char *fnname, unsigned __int64 nb, int sign) {
  425. PNODE pNode;
  426. // fprintf(stderr, "%s += %lx\n", fnname, nb);
  427. pNode = FindNode(fnname);
  428. if (sign == -1) {
  429. pNode->lAllocsFirst = nb;
  430. } else {
  431. pNode->lAllocsSecond = nb;
  432. }
  433. // fprintf(stderr, "%s == %lx\n", fnname, pNode->lValue);
  434. }
  435. void ResetStackTrace(char *fnname) {
  436. PNODE pNode;
  437. pNode = FindNode(fnname);
  438. pNode->lPosition = 0L;
  439. }
  440. void AddToStackTrace(char *fnname, char *line)
  441. {
  442. PNODE pNode;
  443. pNode = FindNode(fnname);
  444. //
  445. // Make sure we don't write too much data in the BackTrace field.
  446. //
  447. if (pNode -> lPosition >= MAX_BT) {
  448. //
  449. // MAX_BT should be the number of entries in a stack trace that
  450. // DH/UMDH captures. If we trigger this we have tried to attach
  451. // more than MAX_BT entries in this stack.
  452. //
  453. fprintf(stderr,
  454. "More than %d entries in this stack trace, "
  455. "did the max change ?\n",
  456. MAX_BT);
  457. exit(EXIT_FAILURE);
  458. }
  459. strcpy(pNode->BackTrace[pNode->lPosition++], line);
  460. }
  461. /*
  462. * Insert pNode into the list at ppNodeHead.
  463. * Sort in ascending order.
  464. * Insert pNode BEFORE the first item >= pNode.
  465. */
  466. void Reinsert(PNODE pNode, PNODE *ppNodeHead) {
  467. PNODE *ppT;
  468. ppT = ppNodeHead;
  469. while (*ppT && (pNode->lValue < (*ppT)->lValue)) {
  470. ppT = &((*ppT)->pNext);
  471. }
  472. /*
  473. * Insert pNode before *ppT
  474. */
  475. pNode->pNext = *ppT;
  476. *ppT = pNode;
  477. }
  478. void SortList(PNODE *ppNodeHead) {
  479. PNODE pNode;
  480. PNODE pNext;
  481. pNode = *ppNodeHead;
  482. if (pNode == NULL) {
  483. return;
  484. }
  485. pNext = pNode->pNext;
  486. if (pNext == NULL) {
  487. return;
  488. }
  489. while (TRUE) {
  490. while (pNext != NULL) {
  491. if (pNode->lValue < pNext->lValue) {
  492. /*
  493. * cut the unordered node from the list
  494. */
  495. pNode->pNext = pNext->pNext;
  496. Reinsert(pNext, ppNodeHead);
  497. break;
  498. }
  499. pNode = pNext;
  500. pNext = pNode->pNext;
  501. }
  502. if (pNext == NULL) {
  503. return;
  504. }
  505. pNode = *ppNodeHead;
  506. pNext = pNode->pNext;
  507. }
  508. }
  509. /*
  510. * Merge ordered list 1 into ordered list 2
  511. * Leaves list 1 empty; list 2 ordered
  512. */
  513. void MergeLists(PNODE *ppNode1, PNODE *ppNode2) {
  514. PNODE *pp1;
  515. PNODE *pp2;
  516. PNODE p1;
  517. PNODE p2;
  518. pp1 = ppNode1;
  519. pp2 = ppNode2;
  520. while (TRUE) {
  521. p1 = *pp1;
  522. p2 = *pp2;
  523. if (p1 == NULL) {
  524. return;
  525. }
  526. if (p2 == NULL) {
  527. *pp2 = *pp1;
  528. *pp1 = NULL;
  529. return;
  530. }
  531. if (p1->lValue > p2->lValue) {
  532. *pp1 = p1->pNext;
  533. p1->pNext = p2;
  534. *pp2 = p1;
  535. pp2 = &(p1->pNext);
  536. } else {
  537. pp2 = &(p2->pNext);
  538. }
  539. }
  540. }
  541. void SortAll() {
  542. int i;
  543. for (i = 0; i < NHASH; i++) {
  544. SortList(&HashTab[i]);
  545. }
  546. // printf(" ======================== SORTED ========================\n");
  547. // DumpNodes(DUMPF_ALL);
  548. for (i = 0; i < NHASH-1; i++) {
  549. // printf(" ======================== MERGING %d and %d ======================== \n", i, i+1);
  550. MergeLists(&HashTab[i], &HashTab[i+1]);
  551. // DumpNodes(DUMPF_ALL);
  552. }
  553. }
  554. VOID
  555. DumpStackTrace (
  556. PNODE pNode
  557. )
  558. {
  559. int n;
  560. printf ("\n");
  561. for (n = 0; n < pNode->lPosition; n += 1) {
  562. printf ("%s", pNode->BackTrace[n]);
  563. }
  564. printf ("\n");
  565. }
  566. CHAR LogDescription [] =
  567. "// \n"
  568. "// Each log entry has the following syntax: \n"
  569. "// \n"
  570. "// + BYTES_DELTA (NEW_BYTES - OLD_BYTES) NEW_COUNT allocs BackTrace TRACEID \n"
  571. "// + COUNT_DELTA (NEW_COUNT - OLD_COUNT) BackTrace TRACEID allocations \n"
  572. "// ... stack trace ... \n"
  573. "// \n"
  574. "// where: \n"
  575. "// \n"
  576. "// BYTES_DELTA - increase in bytes between before and after log \n"
  577. "// NEW_BYTES - bytes in after log \n"
  578. "// OLD_BYTES - bytes in before log \n"
  579. "// COUNT_DELTA - increase in allocations between before and after log \n"
  580. "// NEW_COUNT - number of allocations in after log \n"
  581. "// OLD_COUNT - number of allocations in before log \n"
  582. "// TRACEID - decimal index of the stack trace in the trace database \n"
  583. "// (can be used to search for allocation instances in the original \n"
  584. "// UMDH logs). \n"
  585. "// \n\n";
  586. VOID
  587. DumpLogDescription (
  588. VOID
  589. )
  590. {
  591. fputs (LogDescription, stdout);
  592. }