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 Netscape Communications Corp.
17 * Portions created by the Initial Developer are Copyright (C) 1998
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
);
78 htmlify(const char *in
)
85 // Count the number of '<' and '>' in the input.
86 while ((p
= strpbrk(p
, "<>")))
92 // Knowing the number of '<' and '>', we can calculate the space
93 // needed for the output string.
94 newlen
= strlen(in
) + n
* 3 + 1;
95 out
= new char[newlen
];
97 // Copy the input to the output, with substitutions.
125 applicationName
= NULL
;
134 firstLogEntry
= lastLogEntry
= 0;
139 numExternalSymbols
= 0;
140 lowestSymbolAddr
= 0;
141 highestSymbolAddr
= 0;
150 void leaky::usageError()
152 fprintf(stderr
, "Usage: %s prog log\n", (char*) applicationName
);
156 void leaky::initialize(int argc
, char** argv
)
158 applicationName
= argv
[0];
159 applicationName
= strrchr(applicationName
, '/');
160 if (!applicationName
) {
161 applicationName
= argv
[0];
168 while ((arg
= getopt(argc
, argv
, "adEe:gh:i:r:Rs:tqx")) != -1) {
183 exclusions
.add(optarg
);
189 if (!includes
.IsEmpty()) {
194 includes
.add(optarg
);
195 if (!roots
.IsEmpty()) {
202 stackDepth
= atoi(optarg
);
203 if (stackDepth
< 2) {
214 if (errflg
|| ((argc
- optind
) < 2)) {
217 progFile
= argv
[optind
++];
218 logFile
= argv
[optind
];
221 static void* mapFile(int fd
, u_int flags
, off_t
* sz
)
224 if (fstat(fd
, &sb
) < 0) {
228 void* base
= mmap(0, (int)sb
.st_size
, flags
, MAP_PRIVATE
, fd
, 0);
237 void leaky::LoadMap()
239 malloc_map_entry mme
;
242 int fd
= ::open(M_MAPFILE
, O_RDONLY
);
244 perror("open: " M_MAPFILE
);
248 int nb
= read(fd
, &mme
, sizeof(mme
));
249 if (nb
!= sizeof(mme
)) break;
250 nb
= read(fd
, name
, mme
.nameLen
);
251 if (nb
!= (int)mme
.nameLen
) break;
252 name
[mme
.nameLen
] = 0;
254 printf("%s @ %lx\n", name
, mme
.address
);
257 LoadMapEntry
* lme
= new LoadMapEntry
;
258 lme
->address
= mme
.address
;
259 lme
->name
= strdup(name
);
270 setupSymbols(progFile
);
272 // open up the log file
273 mappedLogFile
= ::open(logFile
, O_RDONLY
);
274 if (mappedLogFile
< 0) {
279 firstLogEntry
= (malloc_log_entry
*) mapFile(mappedLogFile
, PROT_READ
, &size
);
280 lastLogEntry
= (malloc_log_entry
*)((char*)firstLogEntry
+ size
);
287 //----------------------------------------------------------------------
290 static int symbolOrder(void const* a
, void const* b
)
292 Symbol
const* ap
= (Symbol
const *)a
;
293 Symbol
const* bp
= (Symbol
const *)b
;
294 return ap
->address
== bp
->address
? 0 :
295 (ap
->address
> bp
->address
? 1 : -1);
298 void leaky::ReadSharedLibrarySymbols()
300 LoadMapEntry
* lme
= loadMap
;
301 while (NULL
!= lme
) {
302 ReadSymbols(lme
->name
, lme
->address
);
307 void leaky::setupSymbols(const char *fileName
)
309 // Read in symbols from the program
310 ReadSymbols(fileName
, 0);
312 // Read in symbols from the .so's
313 ReadSharedLibrarySymbols();
316 printf("A total of %d symbols were loaded\n", usefulSymbols
);
320 qsort(externalSymbols
, usefulSymbols
, sizeof(Symbol
), symbolOrder
);
321 lowestSymbolAddr
= externalSymbols
[0].address
;
322 highestSymbolAddr
= externalSymbols
[usefulSymbols
-1].address
;
325 // Binary search the table, looking for a symbol that covers this
327 int leaky::findSymbolIndex(u_long addr
)
330 u_int limit
= usefulSymbols
- 1;
331 Symbol
* end
= &externalSymbols
[limit
];
332 while (base
<= limit
) {
333 u_int midPoint
= (base
+ limit
)>>1;
334 Symbol
* sp
= &externalSymbols
[midPoint
];
335 if (addr
< sp
->address
) {
339 limit
= midPoint
- 1;
342 if (addr
< (sp
+1)->address
) {
354 Symbol
* leaky::findSymbol(u_long addr
)
356 int idx
= findSymbolIndex(addr
);
361 return &externalSymbols
[idx
];
365 //----------------------------------------------------------------------
367 bool leaky::excluded(malloc_log_entry
* lep
)
369 if (exclusions
.IsEmpty()) {
373 char** pcp
= &lep
->pcs
[0];
374 u_int n
= lep
->numpcs
;
375 for (u_int i
= 0; i
< n
; i
++, pcp
++) {
376 Symbol
* sp
= findSymbol((u_long
) *pcp
);
377 if (sp
&& exclusions
.contains(sp
->name
)) {
384 bool leaky::included(malloc_log_entry
* lep
)
386 if (includes
.IsEmpty()) {
390 char** pcp
= &lep
->pcs
[0];
391 u_int n
= lep
->numpcs
;
392 for (u_int i
= 0; i
< n
; i
++, pcp
++) {
393 Symbol
* sp
= findSymbol((u_long
) *pcp
);
394 if (sp
&& includes
.contains(sp
->name
)) {
401 //----------------------------------------------------------------------
403 void leaky::displayStackTrace(FILE* out
, malloc_log_entry
* lep
)
405 char** pcp
= &lep
->pcs
[0];
406 u_int n
= (lep
->numpcs
< stackDepth
) ? lep
->numpcs
: stackDepth
;
407 for (u_int i
= 0; i
< n
; i
++, pcp
++) {
408 u_long addr
= (u_long
) *pcp
;
409 Symbol
* sp
= findSymbol(addr
);
411 fputs(sp
->name
, out
);
413 fprintf(out
, "[%p]", (char*)addr
);
417 fprintf(out
, "<%p>", (char*)addr
);
424 void leaky::dumpEntryToLog(malloc_log_entry
* lep
)
426 printf("%ld\t", lep
->delTime
);
428 displayStackTrace(stdout
, lep
);
431 void leaky::generateReportHTML(FILE *fp
, int *countArray
, int count
)
433 fprintf(fp
,"<html><head><title>Jprof Profile Report</title></head><body>\n");
434 fprintf(fp
,"<h1><center>Jprof Profile Report</center></h1>\n");
435 fprintf(fp
,"<center>");
436 fprintf(fp
,"<A href=#flat>flat</A><b> | </b><A href=#hier>hierarchical</A>");
437 fprintf(fp
,"</center><P><P><P>\n");
439 int *rankingTable
= new int[usefulSymbols
];
441 for(int cnt
=usefulSymbols
; --cnt
>=0; rankingTable
[cnt
]=cnt
);
443 // Drat. I would use ::qsort() but I would need a global variable and my
444 // intro-pascal professor threatened to flunk anyone who used globals.
445 // She damaged me for life :-) (That was 1986. See how much influence
446 // she had. I don't remember her name but I always feel guilty about globals)
448 // Shell Sort. 581130733 is the max 31 bit value of h = 3h+1
450 for(mx
=usefulSymbols
/9, h
=581130733; h
>0; h
/=3) {
452 for(i
= h
-1; i
<usefulSymbols
; i
++) {
453 int j
, tmp
=rankingTable
[i
], val
= countArray
[tmp
];
454 for(j
= i
; (j
>=h
) && (countArray
[rankingTable
[j
-h
]]<val
); j
-=h
) {
455 rankingTable
[j
] = rankingTable
[j
-h
];
457 rankingTable
[j
] = tmp
;
462 // Ok, We are sorted now. Let's go through the table until we get to
463 // functions that were never called. Right now we don't do much inside
464 // this loop. Later we can get callers and callees into it like gprof
467 "<h2><A NAME=hier></A><center><a href=\"http://lxr.mozilla.org/mozilla/source/tools/jprof/README.html#hier\">Hierarchical Profile</a></center></h2><hr>\n");
468 fprintf(fp
, "<pre>\n");
469 fprintf(fp
, "%5s %5s %4s %s\n",
470 "index", "Count", "Hits", "Function Name");
472 for(i
=0; i
<usefulSymbols
&& countArray
[rankingTable
[i
]]>0; i
++) {
473 Symbol
*sp
=&externalSymbols
[rankingTable
[i
]];
475 sp
->cntP
.printReport(fp
, this);
477 char *symname
= htmlify(sp
->name
);
478 fprintf(fp
, "%6d %3d <a name=%d>%8d</a> <b>%s</b>\n", rankingTable
[i
],
479 sp
->timerHit
, rankingTable
[i
], countArray
[rankingTable
[i
]],
483 sp
->cntC
.printReport(fp
, this);
485 fprintf(fp
, "<hr>\n");
487 fprintf(fp
,"</pre>\n");
489 // OK, Now we want to print the flat profile. To do this we resort on
492 // Cut-N-Paste Shell sort from above. The Ranking Table has already been
493 // populated, so we do not have to reinitialize it.
494 for(mx
=usefulSymbols
/9, h
=581130733; h
>0; h
/=3) {
496 for(i
= h
-1; i
<usefulSymbols
; i
++) {
497 int j
, tmp
=rankingTable
[i
], val
= externalSymbols
[tmp
].timerHit
;
499 (j
>=h
) && (externalSymbols
[rankingTable
[j
-h
]].timerHit
<val
); j
-=h
) {
500 rankingTable
[j
] = rankingTable
[j
-h
];
502 rankingTable
[j
] = tmp
;
507 // Pre-count up total counter hits, to get a percentage.
508 // I wanted the total before walking the list, if this
509 // double-pass over externalSymbols gets slow we can
510 // do single-pass and print this out after the loop finishes.
511 int totalTimerHits
= 0;
513 i
<usefulSymbols
&& externalSymbols
[rankingTable
[i
]].timerHit
>0; i
++) {
514 Symbol
*sp
=&externalSymbols
[rankingTable
[i
]];
515 totalTimerHits
+= sp
->timerHit
;
518 fprintf(fp
,"<h2><A NAME=flat></A><center><a href=\"http://lxr.mozilla.org/mozilla/source/tools/jprof/README.html#flat\">Flat Profile</a></center></h2><br>\n");
519 fprintf(fp
, "<pre>\n");
521 fprintf(fp
, "Total hit count: %d\n", totalTimerHits
);
522 fprintf(fp
, "Count %%Total Function Name\n");
523 // Now loop for as long as we have timer hits
525 i
<usefulSymbols
&& externalSymbols
[rankingTable
[i
]].timerHit
>0; i
++) {
527 Symbol
*sp
=&externalSymbols
[rankingTable
[i
]];
529 char *symname
= htmlify(sp
->name
);
530 fprintf(fp
, "<a href=\"#%d\">%3d %-2.1f %s</a>\n",
531 rankingTable
[i
], sp
->timerHit
,
532 ((float)sp
->timerHit
/(float)totalTimerHits
)*100.0, symname
);
536 fprintf(fp
,"</pre></body></html>\n");
539 void leaky::analyze()
541 int *countArray
= new int[usefulSymbols
];
542 int *flagArray
= new int[usefulSymbols
];
544 //Zero our function call counter
545 memset(countArray
, 0, sizeof(countArray
[0])*usefulSymbols
);
547 // The flag array is used to prevent counting symbols multiple times
548 // if functions are called recursively. In order to keep from having
549 // to zero it on each pass through the loop, we mark it with the value
550 // of stacks on each trip through the loop. This means we can determine
551 // if we have seen this symbol for this stack trace w/o having to reset
552 // from the prior stacktrace.
553 memset(flagArray
, -1, sizeof(flagArray
[0])*usefulSymbols
);
555 // This loop walks through all the call stacks we recorded
557 for(malloc_log_entry
* lep
=firstLogEntry
;
559 lep
= reinterpret_cast<malloc_log_entry
*>(&lep
->pcs
[lep
->numpcs
])) {
561 if (excluded(lep
) || !included(lep
))
564 ++stacks
; // How many stack frames did we collect
566 // This loop walks through every symbol in the call stack. By walking it
567 // backwards we know who called the function when we get there.
568 u_int n
= (lep
->numpcs
< stackDepth
) ? lep
->numpcs
: stackDepth
;
569 char** pcp
= &lep
->pcs
[n
-1];
570 int idx
=-1, parrentIdx
=-1; // Init idx incase n==0
571 for(int i
=n
-1; i
>=0; --i
, --pcp
, parrentIdx
=idx
) {
572 idx
= findSymbolIndex(reinterpret_cast<u_long
>(*pcp
));
575 // Skip over bogus __restore_rt frames that realtime profiling
577 if (i
> 0 && !strcmp(externalSymbols
[idx
].name
, "__restore_rt")) {
580 idx
= findSymbolIndex(reinterpret_cast<u_long
>(*pcp
));
586 // If we have not seen this symbol before count it and mark it as seen
587 if(flagArray
[idx
]!=stacks
&& ((flagArray
[idx
]=stacks
) || true)) {
591 // We know who we are and we know who our parrent is. Count this
593 externalSymbols
[parrentIdx
].regChild(idx
);
594 externalSymbols
[idx
].regParrent(parrentIdx
);
599 // idx should be the function that we were in when we received the signal.
601 ++externalSymbols
[idx
].timerHit
;
605 generateReportHTML(stdout
, countArray
, stacks
);
608 void FunctionCount::printReport(FILE *fp
, leaky
*lk
)
610 const char *fmt
= " <A href=\"#%d\">%6d %s</A>\n";
612 int nmax
, tmax
=((~0U)>>1);
616 for(int j
=getSize(); --j
>=0;) {
617 int cnt
= getCount(j
);
619 int idx
= getIndex(j
);
620 char *symname
= htmlify(lk
->indexToName(idx
));
621 fprintf(fp
, fmt
, idx
, getCount(j
), symname
);
623 } else if(cnt
<tmax
&& cnt
>nmax
) {
627 } while((tmax
=nmax
)>0);