/tools/leaky/leaky.cpp

http://github.com/zpao/v8monkey · C++ · 784 lines · 650 code · 77 blank · 57 comment · 130 complexity · d17df948fc0ab444846f8dbf0d789842 MD5 · raw file

  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Mozilla Public License Version
  5. * 1.1 (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. * http://www.mozilla.org/MPL/
  8. *
  9. * Software distributed under the License is distributed on an "AS IS" basis,
  10. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. * for the specific language governing rights and limitations under the
  12. * License.
  13. *
  14. * The Original Code is mozilla.org code.
  15. *
  16. * The Initial Developer of the Original Code is Kipp E.B. Hickman.
  17. * Portions created by the Initial Developer are Copyright (C) 1999
  18. * the Initial Developer. All Rights Reserved.
  19. *
  20. * Contributor(s):
  21. *
  22. * Alternatively, the contents of this file may be used under the terms of
  23. * either the GNU General Public License Version 2 or later (the "GPL"), or
  24. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  25. * in which case the provisions of the GPL or the LGPL are applicable instead
  26. * of those above. If you wish to allow use of your version of this file only
  27. * under the terms of either the GPL or the LGPL, and not to allow others to
  28. * use your version of this file under the terms of the MPL, indicate your
  29. * decision by deleting the provisions above and replace them with the notice
  30. * and other provisions required by the GPL or the LGPL. If you do not delete
  31. * the provisions above, a recipient may use your version of this file under
  32. * the terms of any one of the MPL, the GPL or the LGPL.
  33. *
  34. * ***** END LICENSE BLOCK ***** */
  35. #include "leaky.h"
  36. #include "dict.h"
  37. #include <sys/types.h>
  38. #include <sys/mman.h>
  39. #include <sys/stat.h>
  40. #include <fcntl.h>
  41. #include <unistd.h>
  42. #include <string.h>
  43. #ifndef NTO
  44. #include <getopt.h>
  45. #endif
  46. #include <assert.h>
  47. #include <stdlib.h>
  48. #include <stdio.h>
  49. #ifdef NTO
  50. #include <mem.h>
  51. #endif
  52. #ifndef FALSE
  53. #define FALSE 0
  54. #endif
  55. #ifndef TRUE
  56. #define TRUE 1
  57. #endif
  58. static const u_int DefaultBuckets = 10007; // arbitrary, but prime
  59. static const u_int MaxBuckets = 1000003; // arbitrary, but prime
  60. //----------------------------------------------------------------------
  61. int main(int argc, char** argv)
  62. {
  63. leaky* l = new leaky;
  64. l->initialize(argc, argv);
  65. l->open();
  66. return 0;
  67. }
  68. leaky::leaky()
  69. {
  70. applicationName = NULL;
  71. logFile = NULL;
  72. progFile = NULL;
  73. dumpLeaks = FALSE;
  74. dumpGraph = FALSE;
  75. dumpHTML = FALSE;
  76. quiet = FALSE;
  77. dumpEntireLog = FALSE;
  78. showAddress = FALSE;
  79. stackDepth = 100000;
  80. dumpRefcnts = false;
  81. mappedLogFile = -1;
  82. firstLogEntry = lastLogEntry = 0;
  83. buckets = DefaultBuckets;
  84. dict = NULL;
  85. refcntDict = NULL;
  86. mallocs = 0;
  87. reallocs = 0;
  88. frees = 0;
  89. totalMalloced = 0;
  90. errors = 0;
  91. totalLeaked = 0;
  92. sfd = -1;
  93. externalSymbols = 0;
  94. usefulSymbols = 0;
  95. numExternalSymbols = 0;
  96. lowestSymbolAddr = 0;
  97. highestSymbolAddr = 0;
  98. loadMap = NULL;
  99. }
  100. leaky::~leaky()
  101. {
  102. delete dict;
  103. }
  104. void leaky::usageError()
  105. {
  106. fprintf(stderr,
  107. "Usage: %s [-aAEdfgqxR] [-e name] [-s depth] [-h hash-buckets] [-r root|-i symbol] prog log\n",
  108. (char*) applicationName);
  109. exit(-1);
  110. }
  111. void leaky::initialize(int argc, char** argv)
  112. {
  113. applicationName = argv[0];
  114. applicationName = strrchr(applicationName, '/');
  115. if (!applicationName) {
  116. applicationName = argv[0];
  117. } else {
  118. applicationName++;
  119. }
  120. int arg;
  121. int errflg = 0;
  122. while ((arg = getopt(argc, argv, "adEe:gh:i:r:Rs:tqx")) != -1) {
  123. switch (arg) {
  124. case '?':
  125. errflg++;
  126. break;
  127. case 'a':
  128. dumpEntireLog = TRUE;
  129. break;
  130. case 'A':
  131. showAddress = TRUE;
  132. break;
  133. case 'd':
  134. dumpLeaks = TRUE;
  135. if (dumpGraph) errflg++;
  136. break;
  137. case 'R':
  138. dumpRefcnts = true;
  139. break;
  140. case 'e':
  141. exclusions.add(optarg);
  142. break;
  143. case 'g':
  144. dumpGraph = TRUE;
  145. if (dumpLeaks) errflg++;
  146. break;
  147. case 'r':
  148. roots.add(optarg);
  149. if (!includes.IsEmpty()) {
  150. errflg++;
  151. }
  152. break;
  153. case 'i':
  154. includes.add(optarg);
  155. if (!roots.IsEmpty()) {
  156. errflg++;
  157. }
  158. break;
  159. case 'h':
  160. buckets = atoi(optarg);
  161. if ((buckets < 0) || (buckets > MaxBuckets)) {
  162. buckets = MaxBuckets;
  163. fprintf(stderr, "%s: buckets is invalid, using %d\n",
  164. (char*) applicationName,
  165. buckets);
  166. }
  167. break;
  168. case 's':
  169. stackDepth = atoi(optarg);
  170. if (stackDepth < 2) {
  171. stackDepth = 2;
  172. }
  173. break;
  174. case 'x':
  175. dumpHTML = TRUE;
  176. break;
  177. case 'q':
  178. quiet = TRUE;
  179. break;
  180. }
  181. }
  182. if (errflg || ((argc - optind) < 2)) {
  183. usageError();
  184. }
  185. progFile = argv[optind++];
  186. logFile = argv[optind];
  187. dict = new MallocDict(buckets);
  188. if (dumpRefcnts) {
  189. refcntDict = new MallocDict(buckets);
  190. }
  191. }
  192. static void* mapFile(int fd, u_int flags, off_t* sz)
  193. {
  194. struct stat sb;
  195. if (fstat(fd, &sb) < 0) {
  196. perror("fstat");
  197. exit(-1);
  198. }
  199. void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0);
  200. if (!base) {
  201. perror("mmap");
  202. exit(-1);
  203. }
  204. *sz = sb.st_size;
  205. return base;
  206. }
  207. void leaky::LoadMap()
  208. {
  209. malloc_map_entry mme;
  210. char name[1000];
  211. int fd = ::open("malloc-map", O_RDONLY);
  212. if (fd < 0) {
  213. perror("open: malloc-map");
  214. exit(-1);
  215. }
  216. for (;;) {
  217. int nb = read(fd, &mme, sizeof(mme));
  218. if (nb != sizeof(mme)) break;
  219. nb = read(fd, name, mme.nameLen);
  220. if (nb != (int)mme.nameLen) break;
  221. name[mme.nameLen] = 0;
  222. if (!quiet) {
  223. printf("%s @ %lx\n", name, mme.address);
  224. }
  225. LoadMapEntry* lme = new LoadMapEntry;
  226. lme->address = mme.address;
  227. lme->name = strdup(name);
  228. lme->next = loadMap;
  229. loadMap = lme;
  230. }
  231. close(fd);
  232. }
  233. void leaky::open()
  234. {
  235. LoadMap();
  236. setupSymbols(progFile);
  237. // open up the log file
  238. mappedLogFile = ::open(logFile, O_RDONLY);
  239. if (mappedLogFile < 0) {
  240. perror("open");
  241. exit(-1);
  242. }
  243. off_t size;
  244. firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size);
  245. lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size);
  246. analyze();
  247. if (dumpLeaks || dumpEntireLog || dumpRefcnts) {
  248. dumpLog();
  249. }
  250. else if (dumpGraph) {
  251. buildLeakGraph();
  252. dumpLeakGraph();
  253. }
  254. exit(0);
  255. }
  256. //----------------------------------------------------------------------
  257. static ptrdiff_t symbolOrder(void const* a, void const* b)
  258. {
  259. Symbol const* ap = (Symbol const *)a;
  260. Symbol const* bp = (Symbol const *)b;
  261. return ap->address - bp->address;
  262. }
  263. void leaky::ReadSharedLibrarySymbols()
  264. {
  265. LoadMapEntry* lme = loadMap;
  266. while (NULL != lme) {
  267. ReadSymbols(lme->name, lme->address);
  268. lme = lme->next;
  269. }
  270. }
  271. void leaky::setupSymbols(const char *fileName)
  272. {
  273. // Read in symbols from the program
  274. ReadSymbols(fileName, 0);
  275. // Read in symbols from the .so's
  276. ReadSharedLibrarySymbols();
  277. if (!quiet) {
  278. printf("A total of %d symbols were loaded\n", usefulSymbols);
  279. }
  280. // Now sort them
  281. qsort(externalSymbols, usefulSymbols, sizeof(Symbol), symbolOrder);
  282. lowestSymbolAddr = externalSymbols[0].address;
  283. highestSymbolAddr = externalSymbols[usefulSymbols-1].address;
  284. }
  285. // Binary search the table, looking for a symbol that covers this
  286. // address.
  287. Symbol* leaky::findSymbol(u_long addr)
  288. {
  289. u_int base = 0;
  290. u_int limit = usefulSymbols - 1;
  291. Symbol* end = &externalSymbols[limit];
  292. while (base <= limit) {
  293. u_int midPoint = (base + limit)>>1;
  294. Symbol* sp = &externalSymbols[midPoint];
  295. if (addr < sp->address) {
  296. if (midPoint == 0) {
  297. return NULL;
  298. }
  299. limit = midPoint - 1;
  300. } else {
  301. if (sp+1 < end) {
  302. if (addr < (sp+1)->address) {
  303. return sp;
  304. }
  305. } else {
  306. return sp;
  307. }
  308. base = midPoint + 1;
  309. }
  310. }
  311. return NULL;
  312. }
  313. //----------------------------------------------------------------------
  314. bool leaky::excluded(malloc_log_entry* lep)
  315. {
  316. if (exclusions.IsEmpty()) {
  317. return false;
  318. }
  319. char** pcp = &lep->pcs[0];
  320. u_int n = lep->numpcs;
  321. for (u_int i = 0; i < n; i++, pcp++) {
  322. Symbol* sp = findSymbol((u_long) *pcp);
  323. if (sp && exclusions.contains(sp->name)) {
  324. return true;
  325. }
  326. }
  327. return false;
  328. }
  329. bool leaky::included(malloc_log_entry* lep)
  330. {
  331. if (includes.IsEmpty()) {
  332. return true;
  333. }
  334. char** pcp = &lep->pcs[0];
  335. u_int n = lep->numpcs;
  336. for (u_int i = 0; i < n; i++, pcp++) {
  337. Symbol* sp = findSymbol((u_long) *pcp);
  338. if (sp && includes.contains(sp->name)) {
  339. return true;
  340. }
  341. }
  342. return false;
  343. }
  344. //----------------------------------------------------------------------
  345. void leaky::displayStackTrace(FILE* out, malloc_log_entry* lep)
  346. {
  347. char** pcp = &lep->pcs[0];
  348. u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
  349. for (u_int i = 0; i < n; i++, pcp++) {
  350. u_long addr = (u_long) *pcp;
  351. Symbol* sp = findSymbol(addr);
  352. if (sp) {
  353. fputs(sp->name, out);
  354. if (showAddress) {
  355. fprintf(out, "[%p]", (char*)addr);
  356. }
  357. }
  358. else {
  359. fprintf(out, "<%p>", (char*)addr);
  360. }
  361. fputc(' ', out);
  362. }
  363. fputc('\n', out);
  364. }
  365. char* typeFromLog[] = {
  366. "malloc",
  367. "realloc",
  368. "free",
  369. "new",
  370. "delete",
  371. "addref",
  372. "release"
  373. };
  374. void leaky::dumpEntryToLog(malloc_log_entry* lep)
  375. {
  376. printf("%-10s %08lx %5ld ",
  377. typeFromLog[lep->type],
  378. lep->address, lep->size);
  379. if (IsRefcnt(lep)) {
  380. printf("%08ld", lep->oldaddress);
  381. }
  382. else {
  383. printf("%08lx", lep->oldaddress);
  384. }
  385. printf(" --> ");
  386. displayStackTrace(stdout, lep);
  387. }
  388. bool leaky::ShowThisEntry(malloc_log_entry* lep)
  389. {
  390. if ((!dumpRefcnts || IsRefcnt(lep)) && !excluded(lep) && included(lep)) {
  391. return true;
  392. }
  393. return false;
  394. }
  395. void leaky::dumpLog()
  396. {
  397. if (dumpRefcnts) {
  398. malloc_log_entry* lep;
  399. refcntDict->rewind();
  400. while (NULL != (lep = refcntDict->next())) {
  401. if (ShowThisEntry(lep)) {
  402. // Now we get slow...
  403. u_long addr = lep->address;
  404. malloc_log_entry* lep2 = firstLogEntry;
  405. while (lep2 < lastLogEntry) {
  406. if (lep2->address == addr) {
  407. dumpEntryToLog(lep2);
  408. }
  409. lep2 = (malloc_log_entry*) &lep2->pcs[lep2->numpcs];
  410. }
  411. }
  412. }
  413. }
  414. else {
  415. if (dumpEntireLog) {
  416. malloc_log_entry* lep = firstLogEntry;
  417. while (lep < lastLogEntry) {
  418. if (ShowThisEntry(lep)) {
  419. dumpEntryToLog(lep);
  420. }
  421. lep = (malloc_log_entry*) &lep->pcs[lep->numpcs];
  422. }
  423. } else {
  424. malloc_log_entry* lep;
  425. dict->rewind();
  426. while (NULL != (lep = dict->next())) {
  427. if (ShowThisEntry(lep)) {
  428. dumpEntryToLog(lep);
  429. }
  430. }
  431. }
  432. }
  433. }
  434. //----------------------------------------------------------------------
  435. void leaky::insertAddress(u_long address, malloc_log_entry* lep)
  436. {
  437. malloc_log_entry** lepp = dict->find(address);
  438. if (lepp) {
  439. assert(*lepp);
  440. if (!quiet) {
  441. printf("Address %lx allocated twice\n", address);
  442. displayStackTrace(stdout, lep);
  443. }
  444. errors++;
  445. } else {
  446. dict->add(address, lep);
  447. }
  448. }
  449. void leaky::removeAddress(u_long address, malloc_log_entry* lep)
  450. {
  451. malloc_log_entry** lepp = dict->find(address);
  452. if (!lepp) {
  453. if (!quiet) {
  454. printf("Free of unallocated %lx\n", address);
  455. displayStackTrace(stdout, lep);
  456. }
  457. errors++;
  458. } else {
  459. dict->remove(address);
  460. }
  461. }
  462. void leaky::analyze()
  463. {
  464. malloc_log_entry* lep = firstLogEntry;
  465. while (lep < lastLogEntry) {
  466. switch (lep->type) {
  467. case malloc_log_malloc:
  468. case malloc_log_new:
  469. mallocs++;
  470. if (lep->address) {
  471. totalMalloced += lep->size;
  472. insertAddress((u_long) lep->address, lep);
  473. }
  474. break;
  475. case malloc_log_realloc:
  476. if (lep->oldaddress) {
  477. removeAddress((u_long) lep->oldaddress, lep);
  478. }
  479. if (lep->address) {
  480. insertAddress((u_long) lep->address, lep);
  481. }
  482. reallocs++;
  483. break;
  484. case malloc_log_free:
  485. case malloc_log_delete:
  486. if (lep->address) {
  487. removeAddress((u_long) lep->address, lep);
  488. }
  489. frees++;
  490. break;
  491. case malloc_log_addref:
  492. if (dumpRefcnts) {
  493. if (lep->size == 0) {
  494. // Initial addref
  495. u_long addr = (u_long) lep->address;
  496. malloc_log_entry** lepp = refcntDict->find(addr);
  497. if (!lepp) {
  498. refcntDict->add(addr, lep);
  499. }
  500. }
  501. }
  502. break;
  503. case malloc_log_release:
  504. if (dumpRefcnts) {
  505. if (lep->oldaddress == 0) {
  506. // Final release
  507. u_long addr = (u_long) lep->address;
  508. malloc_log_entry** lepp = refcntDict->find(addr);
  509. if (lepp) {
  510. refcntDict->remove(addr);
  511. }
  512. }
  513. }
  514. break;
  515. }
  516. lep = (malloc_log_entry*) &lep->pcs[lep->numpcs];
  517. }
  518. dict->rewind();
  519. while (NULL != (lep = dict->next())) {
  520. totalLeaked += lep->size;
  521. }
  522. if (!quiet) {
  523. printf("# of mallocs = %ld\n", mallocs);
  524. printf("# of reallocs = %ld\n", reallocs);
  525. printf("# of frees = %ld\n", frees);
  526. printf("# of errors = %ld\n", errors);
  527. printf("Total bytes allocated = %ld\n", totalMalloced);
  528. printf("Total bytes leaked = %ld\n", totalLeaked);
  529. printf("Average bytes per malloc = %g\n",
  530. float(totalMalloced)/mallocs);
  531. }
  532. }
  533. void leaky::buildLeakGraph()
  534. {
  535. // For each leak
  536. malloc_log_entry* lep;
  537. dict->rewind();
  538. while (NULL != (lep = dict->next())) {
  539. if (ShowThisEntry(lep)) {
  540. char** basepcp = &lep->pcs[0];
  541. char** pcp = &lep->pcs[lep->numpcs - 1];
  542. // Find root for this allocation
  543. Symbol* sym = findSymbol((u_long) *pcp);
  544. TreeNode* node = sym->root;
  545. if (!node) {
  546. sym->root = node = new TreeNode(sym);
  547. // Add root to list of roots
  548. if (roots.IsEmpty()) {
  549. node->nextRoot = rootList;
  550. rootList = node;
  551. }
  552. }
  553. pcp--;
  554. // Build tree underneath the root
  555. for (; pcp >= basepcp; pcp--) {
  556. // Share nodes in the tree until there is a divergence
  557. sym = findSymbol((u_long) *pcp);
  558. if (!sym) {
  559. break;
  560. }
  561. TreeNode* nextNode = node->GetDirectDescendant(sym);
  562. if (!nextNode) {
  563. // Make a new node at the point of divergence
  564. nextNode = node->AddDescendant(sym);
  565. }
  566. // See if the symbol is to be a user specified root. If it is,
  567. // and we haven't already stuck it on the root-list do so now.
  568. if (!sym->root && !roots.IsEmpty() && roots.contains(sym->name)) {
  569. sym->root = nextNode;
  570. nextNode->nextRoot = rootList;
  571. rootList = nextNode;
  572. }
  573. if (pcp == basepcp) {
  574. nextNode->bytesLeaked += lep->size;
  575. }
  576. else {
  577. node->descendantBytesLeaked += lep->size;
  578. }
  579. node = nextNode;
  580. }
  581. }
  582. }
  583. }
  584. Symbol* leaky::findLeakGraphRoot(Symbol* aStart, Symbol* aEnd)
  585. {
  586. while (aStart < aEnd) {
  587. if (aStart->root) {
  588. return aStart;
  589. }
  590. aStart++;
  591. }
  592. return NULL;
  593. }
  594. void leaky::dumpLeakGraph()
  595. {
  596. if (dumpHTML) {
  597. printf("<html><head><title>Leaky Graph</title>\n");
  598. printf("<style src=\"resource:/res/leaky/leaky.css\"></style>\n");
  599. printf("<script src=\"resource:/res/leaky/leaky.js\"></script>\n");
  600. printf("</head><body><div class=\"key\">\n");
  601. printf("Key:<br>\n");
  602. printf("<span class=b>Bytes directly leaked</span><br>\n");
  603. printf("<span class=d>Bytes leaked by descendants</span></div>\n");
  604. }
  605. #if 0
  606. Symbol* base = externalSymbols;
  607. Symbol* end = externalSymbols + usefulSymbols;
  608. while (base < end) {
  609. Symbol* sym = findLeakGraphRoot(base, end);
  610. if (!sym) break;
  611. dumpLeakTree(sym->root, 0);
  612. base = sym + 1;
  613. }
  614. #else
  615. TreeNode* root = rootList;
  616. while (root) {
  617. dumpLeakTree(root, 0);
  618. root = root->nextRoot;
  619. }
  620. #endif
  621. if (dumpHTML) {
  622. printf("</body></html>\n");
  623. }
  624. }
  625. void leaky::dumpLeakTree(TreeNode* aNode, int aIndent)
  626. {
  627. Symbol* sym = aNode->symbol;
  628. if (dumpHTML) {
  629. printf("<div class=\"n\">\n");
  630. if (aNode->HasDescendants()) {
  631. printf("<img onmouseout=\"O(event);\" onmouseover=\"I(event);\" ");
  632. printf("onclick=\"C(event);\" src=\"resource:/res/leaky/%s.gif\">",
  633. aIndent > 1 ? "close" : "open");
  634. }
  635. printf("<span class=s>%s</span><span class=b>%ld</span>",
  636. sym->name,
  637. aNode->bytesLeaked);
  638. printf("<span class=d>%ld</span>\n",
  639. aNode->descendantBytesLeaked);
  640. }
  641. else {
  642. indentBy(aIndent);
  643. printf("%s bytesLeaked=%ld (%ld from kids)\n",
  644. sym->name,
  645. aNode->bytesLeaked,
  646. aNode->descendantBytesLeaked);
  647. }
  648. TreeNode* node = aNode->descendants;
  649. int kidNum = 0;
  650. while (node) {
  651. sym = node->symbol;
  652. dumpLeakTree(node, aIndent + 1);
  653. kidNum++;
  654. node = node->nextSibling;
  655. }
  656. if (dumpHTML) {
  657. printf("</div>");
  658. }
  659. }
  660. //----------------------------------------------------------------------
  661. TreeNode* TreeNode::freeList;
  662. void* TreeNode::operator new(size_t size) CPP_THROW_NEW
  663. {
  664. if (!freeList) {
  665. TreeNode* newNodes = (TreeNode*) new char[sizeof(TreeNode) * 5000];
  666. if (!newNodes) {
  667. return NULL;
  668. }
  669. TreeNode* n = newNodes;
  670. TreeNode* end = newNodes + 5000 - 1;
  671. while (n < end) {
  672. n->nextSibling = n + 1;
  673. n++;
  674. }
  675. n->nextSibling = NULL;
  676. freeList = newNodes;
  677. }
  678. TreeNode* rv = freeList;
  679. freeList = rv->nextSibling;
  680. return (void*) rv;
  681. }
  682. void TreeNode::operator delete(void* ptr)
  683. {
  684. TreeNode* node = (TreeNode*) ptr;
  685. if (node) {
  686. node->nextSibling = freeList;
  687. freeList = node;
  688. }
  689. }
  690. TreeNode* TreeNode::GetDirectDescendant(Symbol* aSymbol)
  691. {
  692. TreeNode* node = descendants;
  693. while (node) {
  694. if (node->symbol == aSymbol) {
  695. return node;
  696. }
  697. node = node->nextSibling;
  698. }
  699. return NULL;
  700. }
  701. TreeNode* TreeNode::AddDescendant(Symbol* aSymbol)
  702. {
  703. TreeNode* node = new TreeNode(aSymbol);
  704. node->nextSibling = descendants;
  705. descendants = node;
  706. return node;
  707. }