1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
14 * The Original Code is mozilla.org code.
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.
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.
34 * ***** END LICENSE BLOCK ***** */
39 #include <sys/types.h>
63 static const u_int DefaultBuckets
= 10007; // arbitrary, but prime
64 static const u_int MaxBuckets
= 1000003; // arbitrary, but prime
66 //----------------------------------------------------------------------
68 int main(int argc
, char** argv
)
72 l
->initialize(argc
, argv
);
79 applicationName
= NULL
;
87 dumpEntireLog
= FALSE
;
93 firstLogEntry
= lastLogEntry
= 0;
94 buckets
= DefaultBuckets
;
108 numExternalSymbols
= 0;
109 lowestSymbolAddr
= 0;
110 highestSymbolAddr
= 0;
120 void leaky::usageError()
123 "Usage: %s [-aAEdfgqxR] [-e name] [-s depth] [-h hash-buckets] [-r root|-i symbol] prog log\n",
124 (char*) applicationName
);
128 void leaky::initialize(int argc
, char** argv
)
130 applicationName
= argv
[0];
131 applicationName
= strrchr(applicationName
, '/');
132 if (!applicationName
) {
133 applicationName
= argv
[0];
140 while ((arg
= getopt(argc
, argv
, "adEe:gh:i:r:Rs:tqx")) != -1) {
146 dumpEntireLog
= TRUE
;
153 if (dumpGraph
) errflg
++;
159 exclusions
.add(optarg
);
163 if (dumpLeaks
) errflg
++;
167 if (!includes
.IsEmpty()) {
172 includes
.add(optarg
);
173 if (!roots
.IsEmpty()) {
178 buckets
= atoi(optarg
);
179 if ((buckets
< 0) || (buckets
> MaxBuckets
)) {
180 buckets
= MaxBuckets
;
181 fprintf(stderr
, "%s: buckets is invalid, using %d\n",
182 (char*) applicationName
,
187 stackDepth
= atoi(optarg
);
188 if (stackDepth
< 2) {
200 if (errflg
|| ((argc
- optind
) < 2)) {
203 progFile
= argv
[optind
++];
204 logFile
= argv
[optind
];
206 dict
= new MallocDict(buckets
);
208 refcntDict
= new MallocDict(buckets
);
212 static void* mapFile(int fd
, u_int flags
, off_t
* sz
)
215 if (fstat(fd
, &sb
) < 0) {
219 void* base
= mmap(0, (int)sb
.st_size
, flags
, MAP_PRIVATE
, fd
, 0);
228 void leaky::LoadMap()
230 malloc_map_entry mme
;
233 int fd
= ::open("malloc-map", O_RDONLY
);
235 perror("open: malloc-map");
239 int nb
= read(fd
, &mme
, sizeof(mme
));
240 if (nb
!= sizeof(mme
)) break;
241 nb
= read(fd
, name
, mme
.nameLen
);
242 if (nb
!= (int)mme
.nameLen
) break;
243 name
[mme
.nameLen
] = 0;
245 printf("%s @ %lx\n", name
, mme
.address
);
248 LoadMapEntry
* lme
= new LoadMapEntry
;
249 lme
->address
= mme
.address
;
250 lme
->name
= strdup(name
);
261 setupSymbols(progFile
);
263 // open up the log file
264 mappedLogFile
= ::open(logFile
, O_RDONLY
);
265 if (mappedLogFile
< 0) {
270 firstLogEntry
= (malloc_log_entry
*) mapFile(mappedLogFile
, PROT_READ
, &size
);
271 lastLogEntry
= (malloc_log_entry
*)((char*)firstLogEntry
+ size
);
275 if (dumpLeaks
|| dumpEntireLog
|| dumpRefcnts
) {
278 else if (dumpGraph
) {
285 //----------------------------------------------------------------------
288 static ptrdiff_t symbolOrder(void const* a
, void const* b
)
290 Symbol
const* ap
= (Symbol
const *)a
;
291 Symbol
const* bp
= (Symbol
const *)b
;
292 return ap
->address
- bp
->address
;
295 void leaky::ReadSharedLibrarySymbols()
297 LoadMapEntry
* lme
= loadMap
;
298 while (NULL
!= lme
) {
299 ReadSymbols(lme
->name
, lme
->address
);
304 void leaky::setupSymbols(const char *fileName
)
306 // Read in symbols from the program
307 ReadSymbols(fileName
, 0);
309 // Read in symbols from the .so's
310 ReadSharedLibrarySymbols();
313 printf("A total of %d symbols were loaded\n", usefulSymbols
);
317 qsort(externalSymbols
, usefulSymbols
, sizeof(Symbol
), symbolOrder
);
318 lowestSymbolAddr
= externalSymbols
[0].address
;
319 highestSymbolAddr
= externalSymbols
[usefulSymbols
-1].address
;
322 // Binary search the table, looking for a symbol that covers this
324 Symbol
* leaky::findSymbol(u_long addr
)
327 u_int limit
= usefulSymbols
- 1;
328 Symbol
* end
= &externalSymbols
[limit
];
329 while (base
<= limit
) {
330 u_int midPoint
= (base
+ limit
)>>1;
331 Symbol
* sp
= &externalSymbols
[midPoint
];
332 if (addr
< sp
->address
) {
336 limit
= midPoint
- 1;
339 if (addr
< (sp
+1)->address
) {
351 //----------------------------------------------------------------------
353 bool leaky::excluded(malloc_log_entry
* lep
)
355 if (exclusions
.IsEmpty()) {
359 char** pcp
= &lep
->pcs
[0];
360 u_int n
= lep
->numpcs
;
361 for (u_int i
= 0; i
< n
; i
++, pcp
++) {
362 Symbol
* sp
= findSymbol((u_long
) *pcp
);
363 if (sp
&& exclusions
.contains(sp
->name
)) {
370 bool leaky::included(malloc_log_entry
* lep
)
372 if (includes
.IsEmpty()) {
376 char** pcp
= &lep
->pcs
[0];
377 u_int n
= lep
->numpcs
;
378 for (u_int i
= 0; i
< n
; i
++, pcp
++) {
379 Symbol
* sp
= findSymbol((u_long
) *pcp
);
380 if (sp
&& includes
.contains(sp
->name
)) {
387 //----------------------------------------------------------------------
389 void leaky::displayStackTrace(FILE* out
, malloc_log_entry
* lep
)
391 char** pcp
= &lep
->pcs
[0];
392 u_int n
= (lep
->numpcs
< stackDepth
) ? lep
->numpcs
: stackDepth
;
393 for (u_int i
= 0; i
< n
; i
++, pcp
++) {
394 u_long addr
= (u_long
) *pcp
;
395 Symbol
* sp
= findSymbol(addr
);
397 fputs(sp
->name
, out
);
399 fprintf(out
, "[%p]", (char*)addr
);
403 fprintf(out
, "<%p>", (char*)addr
);
410 char* typeFromLog
[] = {
420 void leaky::dumpEntryToLog(malloc_log_entry
* lep
)
422 printf("%-10s %08lx %5ld ",
423 typeFromLog
[lep
->type
],
424 lep
->address
, lep
->size
);
426 printf("%08ld", lep
->oldaddress
);
429 printf("%08lx", lep
->oldaddress
);
432 displayStackTrace(stdout
, lep
);
435 bool leaky::ShowThisEntry(malloc_log_entry
* lep
)
437 if ((!dumpRefcnts
|| IsRefcnt(lep
)) && !excluded(lep
) && included(lep
)) {
443 void leaky::dumpLog()
446 malloc_log_entry
* lep
;
447 refcntDict
->rewind();
448 while (NULL
!= (lep
= refcntDict
->next())) {
449 if (ShowThisEntry(lep
)) {
450 // Now we get slow...
451 u_long addr
= lep
->address
;
452 malloc_log_entry
* lep2
= firstLogEntry
;
453 while (lep2
< lastLogEntry
) {
454 if (lep2
->address
== addr
) {
455 dumpEntryToLog(lep2
);
457 lep2
= (malloc_log_entry
*) &lep2
->pcs
[lep2
->numpcs
];
464 malloc_log_entry
* lep
= firstLogEntry
;
465 while (lep
< lastLogEntry
) {
466 if (ShowThisEntry(lep
)) {
469 lep
= (malloc_log_entry
*) &lep
->pcs
[lep
->numpcs
];
472 malloc_log_entry
* lep
;
474 while (NULL
!= (lep
= dict
->next())) {
475 if (ShowThisEntry(lep
)) {
483 //----------------------------------------------------------------------
485 void leaky::insertAddress(u_long address
, malloc_log_entry
* lep
)
487 malloc_log_entry
** lepp
= dict
->find(address
);
491 printf("Address %lx allocated twice\n", address
);
492 displayStackTrace(stdout
, lep
);
496 dict
->add(address
, lep
);
500 void leaky::removeAddress(u_long address
, malloc_log_entry
* lep
)
502 malloc_log_entry
** lepp
= dict
->find(address
);
505 printf("Free of unallocated %lx\n", address
);
506 displayStackTrace(stdout
, lep
);
510 dict
->remove(address
);
514 void leaky::analyze()
516 malloc_log_entry
* lep
= firstLogEntry
;
517 while (lep
< lastLogEntry
) {
519 case malloc_log_malloc
:
523 totalMalloced
+= lep
->size
;
524 insertAddress((u_long
) lep
->address
, lep
);
528 case malloc_log_realloc
:
529 if (lep
->oldaddress
) {
530 removeAddress((u_long
) lep
->oldaddress
, lep
);
533 insertAddress((u_long
) lep
->address
, lep
);
538 case malloc_log_free
:
539 case malloc_log_delete
:
541 removeAddress((u_long
) lep
->address
, lep
);
545 case malloc_log_addref
:
547 if (lep
->size
== 0) {
549 u_long addr
= (u_long
) lep
->address
;
550 malloc_log_entry
** lepp
= refcntDict
->find(addr
);
552 refcntDict
->add(addr
, lep
);
557 case malloc_log_release
:
559 if (lep
->oldaddress
== 0) {
561 u_long addr
= (u_long
) lep
->address
;
562 malloc_log_entry
** lepp
= refcntDict
->find(addr
);
564 refcntDict
->remove(addr
);
570 lep
= (malloc_log_entry
*) &lep
->pcs
[lep
->numpcs
];
574 while (NULL
!= (lep
= dict
->next())) {
575 totalLeaked
+= lep
->size
;
579 printf("# of mallocs = %ld\n", mallocs
);
580 printf("# of reallocs = %ld\n", reallocs
);
581 printf("# of frees = %ld\n", frees
);
582 printf("# of errors = %ld\n", errors
);
583 printf("Total bytes allocated = %ld\n", totalMalloced
);
584 printf("Total bytes leaked = %ld\n", totalLeaked
);
585 printf("Average bytes per malloc = %g\n",
586 float(totalMalloced
)/mallocs
);
590 void leaky::buildLeakGraph()
593 malloc_log_entry
* lep
;
595 while (NULL
!= (lep
= dict
->next())) {
596 if (ShowThisEntry(lep
)) {
597 char** basepcp
= &lep
->pcs
[0];
598 char** pcp
= &lep
->pcs
[lep
->numpcs
- 1];
600 // Find root for this allocation
601 Symbol
* sym
= findSymbol((u_long
) *pcp
);
602 TreeNode
* node
= sym
->root
;
604 sym
->root
= node
= new TreeNode(sym
);
606 // Add root to list of roots
607 if (roots
.IsEmpty()) {
608 node
->nextRoot
= rootList
;
614 // Build tree underneath the root
615 for (; pcp
>= basepcp
; pcp
--) {
616 // Share nodes in the tree until there is a divergence
617 sym
= findSymbol((u_long
) *pcp
);
621 TreeNode
* nextNode
= node
->GetDirectDescendant(sym
);
623 // Make a new node at the point of divergence
624 nextNode
= node
->AddDescendant(sym
);
627 // See if the symbol is to be a user specified root. If it is,
628 // and we haven't already stuck it on the root-list do so now.
629 if (!sym
->root
&& !roots
.IsEmpty() && roots
.contains(sym
->name
)) {
630 sym
->root
= nextNode
;
631 nextNode
->nextRoot
= rootList
;
635 if (pcp
== basepcp
) {
636 nextNode
->bytesLeaked
+= lep
->size
;
639 node
->descendantBytesLeaked
+= lep
->size
;
648 Symbol
* leaky::findLeakGraphRoot(Symbol
* aStart
, Symbol
* aEnd
)
650 while (aStart
< aEnd
) {
659 void leaky::dumpLeakGraph()
662 printf("<html><head><title>Leaky Graph</title>\n");
663 printf("<style src=\"resource:/res/leaky/leaky.css\"></style>\n");
664 printf("<script src=\"resource:/res/leaky/leaky.js\"></script>\n");
665 printf("</head><body><div class=\"key\">\n");
666 printf("Key:<br>\n");
667 printf("<span class=b>Bytes directly leaked</span><br>\n");
668 printf("<span class=d>Bytes leaked by descendants</span></div>\n");
672 Symbol
* base
= externalSymbols
;
673 Symbol
* end
= externalSymbols
+ usefulSymbols
;
675 Symbol
* sym
= findLeakGraphRoot(base
, end
);
677 dumpLeakTree(sym
->root
, 0);
681 TreeNode
* root
= rootList
;
683 dumpLeakTree(root
, 0);
684 root
= root
->nextRoot
;
688 printf("</body></html>\n");
692 void leaky::dumpLeakTree(TreeNode
* aNode
, int aIndent
)
694 Symbol
* sym
= aNode
->symbol
;
696 printf("<div class=\"n\">\n");
697 if (aNode
->HasDescendants()) {
698 printf("<img onmouseout=\"O(event);\" onmouseover=\"I(event);\" ");
699 printf("onclick=\"C(event);\" src=\"resource:/res/leaky/%s.gif\">",
700 aIndent
> 1 ? "close" : "open");
702 printf("<span class=s>%s</span><span class=b>%ld</span>",
705 printf("<span class=d>%ld</span>\n",
706 aNode
->descendantBytesLeaked
);
710 printf("%s bytesLeaked=%ld (%ld from kids)\n",
713 aNode
->descendantBytesLeaked
);
716 TreeNode
* node
= aNode
->descendants
;
720 dumpLeakTree(node
, aIndent
+ 1);
722 node
= node
->nextSibling
;
730 //----------------------------------------------------------------------
732 TreeNode
* TreeNode::freeList
;
734 void* TreeNode::operator new(size_t size
) CPP_THROW_NEW
737 TreeNode
* newNodes
= (TreeNode
*) new char[sizeof(TreeNode
) * 5000];
741 TreeNode
* n
= newNodes
;
742 TreeNode
* end
= newNodes
+ 5000 - 1;
744 n
->nextSibling
= n
+ 1;
747 n
->nextSibling
= NULL
;
751 TreeNode
* rv
= freeList
;
752 freeList
= rv
->nextSibling
;
757 void TreeNode::operator delete(void* ptr
)
759 TreeNode
* node
= (TreeNode
*) ptr
;
761 node
->nextSibling
= freeList
;
766 TreeNode
* TreeNode::GetDirectDescendant(Symbol
* aSymbol
)
768 TreeNode
* node
= descendants
;
770 if (node
->symbol
== aSymbol
) {
773 node
= node
->nextSibling
;
778 TreeNode
* TreeNode::AddDescendant(Symbol
* aSymbol
)
780 TreeNode
* node
= new TreeNode(aSymbol
);
781 node
->nextSibling
= descendants
;