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.

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