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.

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