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.

654 lines
17 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 <stdio.h>
  54. #include <stdlib.h>
  55. #include <string.h>
  56. #define NHASH 47
  57. #define TRUE 1
  58. #define FALSE 0
  59. typedef int BOOL;
  60. #define TYPE_WHOLESTACK 0
  61. #define TYPE_FUNCTIONS 1
  62. #define MAXLINELENGTH 4096
  63. #define MAXFUNCNAMELENGTH 1024
  64. #define MAX_BT 48 /* max length of back trace stack */
  65. void AddToName(char *fnname, unsigned __int64 nb, int sign);
  66. void SetAllocs(char *fnname, unsigned __int64 nb, int sign);
  67. void Process(char *fnam, int sign, int type);
  68. void SortAll();
  69. void AddToStackTrace(char *fnname, char *line);
  70. void ResetStackTrace(char *fnname);
  71. /*
  72. * Hashing
  73. */
  74. int MakeHash(char *pName);
  75. void InitHashTab();
  76. #define DUMPF_FIRST (1)
  77. #define DUMPF_SECOND (2)
  78. #define DUMPF_RESULT (4)
  79. #define DUMPF_ALL (DUMPF_FIRST | DUMPF_SECOND | DUMPF_RESULT)
  80. void DumpNodes(int Flags);
  81. #define F_DECIMAL 0x0001
  82. #define F_TOTAL 0x0002
  83. #define F_VERBOSE 0x0004
  84. //
  85. // Globals
  86. //
  87. int gFlags = 0;
  88. int cdecl main(int argc, char *argv[]) {
  89. int n, DumpType;
  90. InitHashTab();
  91. for (n = 1; n < argc; n++) {
  92. if ((argv[n][0] == '-') || (argv[n][0] == '/')) {
  93. /*
  94. * Flags
  95. */
  96. switch (argv[n][1]) {
  97. case 'd':
  98. gFlags |= F_DECIMAL;
  99. break;
  100. //NOT YET IMPLEMENTED
  101. //case 't':
  102. // gFlags |= F_TOTAL;
  103. // break;
  104. case 'v':
  105. gFlags |= F_VERBOSE;
  106. break;
  107. case '?':
  108. default:
  109. printf("%s\n", pszHow);
  110. return 1;
  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. printf("%s\n", pszHow);
  132. return 1;
  133. }
  134. // printf("==================== BEFORE SORTING ====================\n");
  135. // DumpNodes(DUMPF_ALL);
  136. SortAll();
  137. // printf("==================== AFTER SORTING ====================\n");
  138. DumpNodes(DumpType);
  139. return 0;
  140. }
  141. void Process(char *fname, int sign, int type) {
  142. FILE *stream;
  143. char linebuff[MAXLINELENGTH];
  144. char fnnamebuff[MAXFUNCNAMELENGTH];
  145. char BackTraceBuff[MAXFUNCNAMELENGTH * MAX_BT] = {0};
  146. char *p;
  147. int lineno = 0;
  148. BOOL skip = TRUE; // start out skipping lines
  149. int iT;
  150. unsigned __int64 ulT = 0L;
  151. unsigned __int64 nBytes = 0L;
  152. unsigned __int64 ulConsumed;
  153. unsigned __int64 lAllocs;
  154. // printf("PROCESS %s %d %d\n", fname, sign, type);
  155. stream = fopen(fname, "r");
  156. if (stream == NULL) {
  157. fprintf(stderr, "Can't open %s for reading\n", fname);
  158. exit (2);
  159. }
  160. nBytes = 0;
  161. while (fgets(linebuff, sizeof(linebuff), stream) != NULL) {
  162. lineno++;
  163. //fprintf(stderr, "Line #%d\r", lineno);
  164. if (linebuff[0] == '*') {
  165. //
  166. // If we find a "hogs" line, stack traces follow, any other line
  167. // started by "*" should cause us to go back to searching for a
  168. // hogs block.
  169. //
  170. if (strstr(linebuff,
  171. "Hogs")) {
  172. skip = FALSE;
  173. } else {
  174. skip = TRUE;
  175. }
  176. continue;
  177. }
  178. if (skip) {
  179. //
  180. // Skip is enabled, skip this line, it is data about the heap
  181. // between 'heap information' and 'heap hogs' lines.
  182. //
  183. continue;
  184. }
  185. if (linebuff[0] != ' ')
  186. {
  187. //
  188. // Scan for byte count and find out how many characters have
  189. // been consumed by this action.
  190. //
  191. ulConsumed = 0;
  192. iT = sscanf(linebuff, "%I64x bytes in %I64x", &ulT, &lAllocs);
  193. if (iT > 0)
  194. {
  195. nBytes = ulT;
  196. p = strstr(linebuff, "BackTrace");
  197. if (!p)
  198. {
  199. //
  200. // What's this ?
  201. //
  202. continue;
  203. }
  204. strcpy(BackTraceBuff, p);
  205. p = strchr(BackTraceBuff, '\n');
  206. if (p)
  207. {
  208. *p = '\0';
  209. }
  210. if (type == TYPE_FUNCTIONS)
  211. {
  212. //
  213. // BackTraceBuff is now saved for use with the rest of the
  214. // trace.
  215. //
  216. continue;
  217. }
  218. AddToName(BackTraceBuff, nBytes, sign);
  219. if(iT == 1)
  220. {
  221. lAllocs = 1;
  222. }
  223. SetAllocs(BackTraceBuff, lAllocs, sign);
  224. ResetStackTrace(BackTraceBuff);
  225. }
  226. }
  227. else if (nBytes != 0)
  228. {
  229. /*
  230. * If TYPE_WHOLESTACK, then add the count to each line of the
  231. * stack backtrace.
  232. */
  233. if (sscanf(linebuff, " %[^+]+0x", fnnamebuff) == 1) {
  234. if (type == TYPE_FUNCTIONS) {
  235. AddToName(fnnamebuff, nBytes, sign);
  236. }
  237. if ((gFlags & F_VERBOSE) == F_VERBOSE) {
  238. AddToStackTrace(BackTraceBuff, linebuff);
  239. }
  240. continue;
  241. } else {
  242. nBytes = 0;
  243. }
  244. }
  245. }
  246. /*
  247. * make sure to account for the final one.
  248. */
  249. if (type == TYPE_WHOLESTACK) {
  250. AddToName(BackTraceBuff, nBytes, sign);
  251. }
  252. if (fname != NULL) {
  253. fclose(stream);
  254. }
  255. }
  256. /*
  257. * Hashing
  258. */
  259. typedef struct tagNODE {
  260. char *pName;
  261. __int64 lValue;
  262. __int64 lFirst;
  263. __int64 lSecond;
  264. char BackTrace[MAX_BT][MAXFUNCNAMELENGTH];
  265. long lPosition;
  266. __int64 lAllocsFirst;
  267. __int64 lAllocsSecond;
  268. struct tagNODE *pNext;
  269. } NODE, *PNODE;
  270. void DumpStackTrace(PNODE pNode);
  271. PNODE HashTab[NHASH];
  272. void InitHashTab() {
  273. int i;
  274. for (i = 0; i < NHASH; i++) {
  275. HashTab[i] = NULL;
  276. }
  277. }
  278. int MakeHash(char *pName) {
  279. int hash = 0;
  280. while (*pName) {
  281. hash += *pName;
  282. pName++;
  283. }
  284. return hash % NHASH;
  285. }
  286. void DumpNodes(int Flags) {
  287. PNODE pNode;
  288. int i;
  289. unsigned __int64 ulTotal = 0;
  290. char *fmt1;
  291. char *fmt2;
  292. char *fmt3;
  293. char *fmt4;
  294. char *fmt5;
  295. char *fmt6;
  296. char *fmt7;
  297. if ((gFlags & F_VERBOSE) == F_VERBOSE) {
  298. if (gFlags & F_DECIMAL) {
  299. fmt1 = "% 8I64d %s\n";
  300. fmt2 = "% 8I64d bytes by: %s\n";
  301. fmt3 = "+% 8I64d ( %6I64d - %6I64d) %6I64d allocs\t%s\n";
  302. fmt4 = "-% 8I64d ( %6I64d - %6I64d) %6I64d allocs\t%s\n";
  303. fmt5 = "\nTotal increase == %I64d\n";
  304. fmt6 = "+% 8I64d ( %6I64d - %6I64d)\t%s\tallocations\n";
  305. fmt7 = "-% 8I64d ( %6I64d - %6I64d)\t%s\tallocations\n";
  306. } else {
  307. fmt1 = "%08I64x %s\n";
  308. fmt2 = "%08I64x bytes by: %s\n";
  309. fmt3 = "+% 8I64x ( %5I64x - %5I64x) %6I64x allocs\t%s\n";
  310. fmt4 = "-% 8I64x ( %5I64x - %5I64x) %6I64x allocs\t%s\n";
  311. fmt5 = "\nTotal increase == %I64x\n";
  312. fmt6 = "+% 8I64x ( %5I64x - %5I64x)\t%s\tallocations\n";
  313. fmt7 = "-% 8I64x ( %5I64x - %5I64x)\t%s\tallocations\n";
  314. }
  315. } else {
  316. if (gFlags & F_DECIMAL) {
  317. fmt1 = "% 8I64d %s\n";
  318. fmt2 = "% 8I64d bytes by: %s\n";
  319. fmt3 = "+% 8I64d ( %6I64d - %6I64d) %6I64d allocs\t%s\n";
  320. fmt4 = "\n-% 8I64d ( %6I64d - %6I64d) %6I64d allocs\t%s";
  321. fmt5 = "\nTotal increase == %I64d\n";
  322. } else {
  323. fmt1 = "%08I64x %s\n";
  324. fmt2 = "%08I64x bytes by: %s\n";
  325. fmt3 = "+% 8I64x ( %5I64x - %5I64x) %6I64x allocs\t%s\n";
  326. fmt4 = "\n-% 8I64x ( %5I64x - %5I64x) %6I64x allocs\t%s";
  327. fmt5 = "\nTotal increase == %I64x\n";
  328. }
  329. }
  330. for (i = 0; i < NHASH; i++) {
  331. // printf("========= HASH %d ==========\n", i);
  332. for (pNode = HashTab[i]; pNode != NULL; pNode = pNode->pNext) {
  333. switch (Flags) {
  334. case DUMPF_FIRST:
  335. printf(fmt1, pNode->lFirst, pNode->pName);
  336. break;
  337. case DUMPF_SECOND:
  338. printf(fmt1, pNode->lSecond, pNode->pName);
  339. break;
  340. case DUMPF_RESULT:
  341. printf(fmt2, pNode->lValue, pNode->pName);
  342. break;
  343. case DUMPF_ALL:
  344. if (pNode->lValue > 0) {
  345. printf(fmt3, pNode->lValue,
  346. pNode->lSecond, pNode->lFirst, (pNode->lAllocsSecond), pNode->pName);
  347. } else if (pNode->lValue < 0) {
  348. printf(fmt4, -pNode->lValue,
  349. pNode->lSecond, pNode->lFirst, (pNode->lAllocsSecond), pNode->pName);
  350. }
  351. if((gFlags & F_VERBOSE) == F_VERBOSE) {
  352. if(pNode->lAllocsSecond-pNode->lAllocsFirst > 0) {
  353. printf(fmt6, pNode->lAllocsSecond-pNode->lAllocsFirst,
  354. pNode->lAllocsSecond, pNode->lAllocsFirst, pNode->pName);
  355. } else if(pNode->lAllocsSecond-pNode->lAllocsFirst < 0) {
  356. printf(fmt7, -(pNode->lAllocsSecond-pNode->lAllocsFirst),
  357. pNode->lAllocsSecond, pNode->lAllocsFirst, pNode->pName);
  358. }
  359. }
  360. break;
  361. }
  362. ulTotal += pNode->lValue;
  363. if(((gFlags & F_VERBOSE) == F_VERBOSE) && (pNode->lValue != 0)) {
  364. DumpStackTrace(pNode);
  365. }
  366. }
  367. }
  368. if (Flags == DUMPF_ALL) {
  369. printf(fmt5, ulTotal);
  370. }
  371. }
  372. PNODE FindNode(char *pName) {
  373. int i;
  374. PNODE pNode;
  375. i = MakeHash(pName);
  376. pNode = HashTab[i];
  377. while (pNode) {
  378. if (strcmp(pName, pNode->pName) == 0) {
  379. return pNode;
  380. }
  381. pNode = pNode->pNext;
  382. }
  383. // Not found
  384. // fprintf(stderr, "NEW %s\n", pName);
  385. pNode = malloc(sizeof(NODE));
  386. if (!pNode) {
  387. fprintf(stderr, "malloc failed in FindNode\n");
  388. exit(2);
  389. }
  390. pNode->pName = _strdup(pName);
  391. if (!pNode->pName) {
  392. fprintf(stderr, "strdup failed in FindNode\n");
  393. exit(2);
  394. }
  395. pNode->pNext = HashTab[i];
  396. HashTab[i] = pNode;
  397. pNode->lValue = 0L;
  398. pNode->lFirst = 0L;
  399. pNode->lSecond = 0L;
  400. pNode->lPosition = 0L;
  401. pNode->lAllocsFirst = 0L;
  402. pNode->lAllocsSecond = 0L;
  403. return pNode;
  404. }
  405. void AddToName(char *fnname, unsigned __int64 nb, int sign) {
  406. PNODE pNode;
  407. // fprintf(stderr, "%s += %lx\n", fnname, nb);
  408. pNode = FindNode(fnname);
  409. pNode->lValue += nb * sign;
  410. if (sign == -1) {
  411. pNode->lFirst += nb;
  412. } else {
  413. pNode->lSecond += nb;
  414. }
  415. // fprintf(stderr, "%s == %lx\n", fnname, pNode->lValue);
  416. }
  417. void SetAllocs(char *fnname, unsigned __int64 nb, int sign) {
  418. PNODE pNode;
  419. // fprintf(stderr, "%s += %lx\n", fnname, nb);
  420. pNode = FindNode(fnname);
  421. if (sign == -1) {
  422. pNode->lAllocsFirst = nb;
  423. } else {
  424. pNode->lAllocsSecond = nb;
  425. }
  426. // fprintf(stderr, "%s == %lx\n", fnname, pNode->lValue);
  427. }
  428. void ResetStackTrace(char *fnname) {
  429. PNODE pNode;
  430. pNode = FindNode(fnname);
  431. pNode->lPosition = 0L;
  432. }
  433. void AddToStackTrace(char *fnname, char *line)
  434. {
  435. PNODE pNode;
  436. pNode = FindNode(fnname);
  437. //
  438. // Make sure we don't write too much data in the BackTrace field.
  439. //
  440. if (pNode -> lPosition >= MAX_BT) {
  441. //
  442. // MAX_BT should be the number of entries in a stack trace that
  443. // DH/UMDH captures. If we trigger this we have tried to attach
  444. // more than MAX_BT entries in this stack.
  445. //
  446. fprintf(stderr,
  447. "More than %d entries in this stack trace, "
  448. "did the max change ?\n",
  449. MAX_BT);
  450. exit(EXIT_FAILURE);
  451. }
  452. strcpy(pNode->BackTrace[pNode->lPosition++], line);
  453. }
  454. /*
  455. * Insert pNode into the list at ppNodeHead.
  456. * Sort in ascending order.
  457. * Insert pNode BEFORE the first item >= pNode.
  458. */
  459. void Reinsert(PNODE pNode, PNODE *ppNodeHead) {
  460. PNODE *ppT;
  461. ppT = ppNodeHead;
  462. while (*ppT && (pNode->lValue < (*ppT)->lValue)) {
  463. ppT = &((*ppT)->pNext);
  464. }
  465. /*
  466. * Insert pNode before *ppT
  467. */
  468. pNode->pNext = *ppT;
  469. *ppT = pNode;
  470. }
  471. void SortList(PNODE *ppNodeHead) {
  472. PNODE pNode;
  473. PNODE pNext;
  474. pNode = *ppNodeHead;
  475. if (pNode == NULL) {
  476. return;
  477. }
  478. pNext = pNode->pNext;
  479. if (pNext == NULL) {
  480. return;
  481. }
  482. while (TRUE) {
  483. while (pNext != NULL) {
  484. if (pNode->lValue < pNext->lValue) {
  485. /*
  486. * cut the unordered node from the list
  487. */
  488. pNode->pNext = pNext->pNext;
  489. Reinsert(pNext, ppNodeHead);
  490. break;
  491. }
  492. pNode = pNext;
  493. pNext = pNode->pNext;
  494. }
  495. if (pNext == NULL) {
  496. return;
  497. }
  498. pNode = *ppNodeHead;
  499. pNext = pNode->pNext;
  500. }
  501. }
  502. /*
  503. * Merge ordered list 1 into ordered list 2
  504. * Leaves list 1 empty; list 2 ordered
  505. */
  506. void MergeLists(PNODE *ppNode1, PNODE *ppNode2) {
  507. PNODE *pp1;
  508. PNODE *pp2;
  509. PNODE p1;
  510. PNODE p2;
  511. pp1 = ppNode1;
  512. pp2 = ppNode2;
  513. while (TRUE) {
  514. p1 = *pp1;
  515. p2 = *pp2;
  516. if (p1 == NULL) {
  517. return;
  518. }
  519. if (p2 == NULL) {
  520. *pp2 = *pp1;
  521. *pp1 = NULL;
  522. return;
  523. }
  524. if (p1->lValue > p2->lValue) {
  525. *pp1 = p1->pNext;
  526. p1->pNext = p2;
  527. *pp2 = p1;
  528. pp2 = &(p1->pNext);
  529. } else {
  530. pp2 = &(p2->pNext);
  531. }
  532. }
  533. }
  534. void SortAll() {
  535. int i;
  536. for (i = 0; i < NHASH; i++) {
  537. SortList(&HashTab[i]);
  538. }
  539. // printf(" ======================== SORTED ========================\n");
  540. // DumpNodes(DUMPF_ALL);
  541. for (i = 0; i < NHASH-1; i++) {
  542. // printf(" ======================== MERGING %d and %d ======================== \n", i, i+1);
  543. MergeLists(&HashTab[i], &HashTab[i+1]);
  544. // DumpNodes(DUMPF_ALL);
  545. }
  546. }
  547. void DumpStackTrace(PNODE pNode)
  548. {
  549. int n;
  550. for(n = 0; n < pNode->lPosition; n++) {
  551. printf("%s", pNode->BackTrace[n]);
  552. }
  553. }