Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / tools / jprof / leaky.cpp
blobbcc6c58a2f1574ec58667372676f92aff9d4ba93
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
12 * License.
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.
20 * Contributor(s):
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 ***** */
36 #include "leaky.h"
37 #include "intcnt.h"
39 #include <sys/types.h>
40 #include <sys/mman.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <string.h>
45 #ifndef NTO
46 #include <getopt.h>
47 #endif
48 #include <assert.h>
49 #include <stdlib.h>
50 #include <stdio.h>
52 #ifdef NTO
53 #include <mem.h>
54 #endif
56 #ifndef FALSE
57 #define FALSE 0
58 #endif
59 #ifndef TRUE
60 #define TRUE 1
61 #endif
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)
70 leaky* l = new leaky;
72 l->initialize(argc, argv);
73 l->open();
74 return 0;
77 char *
78 htmlify(const char *in)
80 const char *p = in;
81 char *out, *q;
82 int n = 0;
83 size_t newlen;
85 // Count the number of '<' and '>' in the input.
86 while ((p = strpbrk(p, "<>")))
88 ++n;
89 ++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.
98 p = in;
99 q = out;
102 if (*p == '<')
104 strcpy(q, "&lt;");
105 q += 4;
107 else if (*p == '>')
109 strcpy(q, "&gt;");
110 q += 4;
112 else
114 *q++ = *p;
116 p++;
117 } while (*p);
118 *q = '\0';
120 return out;
123 leaky::leaky()
125 applicationName = NULL;
126 logFile = NULL;
127 progFile = NULL;
129 quiet = TRUE;
130 showAddress = FALSE;
131 stackDepth = 100000;
133 mappedLogFile = -1;
134 firstLogEntry = lastLogEntry = 0;
136 sfd = -1;
137 externalSymbols = 0;
138 usefulSymbols = 0;
139 numExternalSymbols = 0;
140 lowestSymbolAddr = 0;
141 highestSymbolAddr = 0;
143 loadMap = NULL;
146 leaky::~leaky()
150 void leaky::usageError()
152 fprintf(stderr, "Usage: %s prog log\n", (char*) applicationName);
153 exit(-1);
156 void leaky::initialize(int argc, char** argv)
158 applicationName = argv[0];
159 applicationName = strrchr(applicationName, '/');
160 if (!applicationName) {
161 applicationName = argv[0];
162 } else {
163 applicationName++;
166 int arg;
167 int errflg = 0;
168 while ((arg = getopt(argc, argv, "adEe:gh:i:r:Rs:tqx")) != -1) {
169 switch (arg) {
170 case '?':
171 errflg++;
172 break;
173 case 'a':
174 break;
175 case 'A':
176 showAddress = TRUE;
177 break;
178 case 'd':
179 break;
180 case 'R':
181 break;
182 case 'e':
183 exclusions.add(optarg);
184 break;
185 case 'g':
186 break;
187 case 'r':
188 roots.add(optarg);
189 if (!includes.IsEmpty()) {
190 errflg++;
192 break;
193 case 'i':
194 includes.add(optarg);
195 if (!roots.IsEmpty()) {
196 errflg++;
198 break;
199 case 'h':
200 break;
201 case 's':
202 stackDepth = atoi(optarg);
203 if (stackDepth < 2) {
204 stackDepth = 2;
206 break;
207 case 'x':
208 break;
209 case 'q':
210 quiet = TRUE;
211 break;
214 if (errflg || ((argc - optind) < 2)) {
215 usageError();
217 progFile = argv[optind++];
218 logFile = argv[optind];
221 static void* mapFile(int fd, u_int flags, off_t* sz)
223 struct stat sb;
224 if (fstat(fd, &sb) < 0) {
225 perror("fstat");
226 exit(-1);
228 void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0);
229 if (!base) {
230 perror("mmap");
231 exit(-1);
233 *sz = sb.st_size;
234 return base;
237 void leaky::LoadMap()
239 malloc_map_entry mme;
240 char name[1000];
242 int fd = ::open(M_MAPFILE, O_RDONLY);
243 if (fd < 0) {
244 perror("open: " M_MAPFILE);
245 exit(-1);
247 for (;;) {
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;
253 if (!quiet) {
254 printf("%s @ %lx\n", name, mme.address);
257 LoadMapEntry* lme = new LoadMapEntry;
258 lme->address = mme.address;
259 lme->name = strdup(name);
260 lme->next = loadMap;
261 loadMap = lme;
263 close(fd);
266 void leaky::open()
268 LoadMap();
270 setupSymbols(progFile);
272 // open up the log file
273 mappedLogFile = ::open(logFile, O_RDONLY);
274 if (mappedLogFile < 0) {
275 perror("open");
276 exit(-1);
278 off_t size;
279 firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size);
280 lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size);
282 analyze();
284 exit(0);
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);
303 lme = lme->next;
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();
315 if (!quiet) {
316 printf("A total of %d symbols were loaded\n", usefulSymbols);
319 // Now sort them
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
326 // address.
327 int leaky::findSymbolIndex(u_long addr)
329 u_int base = 0;
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) {
336 if (midPoint == 0) {
337 return -1;
339 limit = midPoint - 1;
340 } else {
341 if (sp+1 < end) {
342 if (addr < (sp+1)->address) {
343 return midPoint;
345 } else {
346 return midPoint;
348 base = midPoint + 1;
351 return -1;
354 Symbol* leaky::findSymbol(u_long addr)
356 int idx = findSymbolIndex(addr);
358 if(idx<0) {
359 return NULL;
360 } else {
361 return &externalSymbols[idx];
365 //----------------------------------------------------------------------
367 bool leaky::excluded(malloc_log_entry* lep)
369 if (exclusions.IsEmpty()) {
370 return false;
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)) {
378 return true;
381 return false;
384 bool leaky::included(malloc_log_entry* lep)
386 if (includes.IsEmpty()) {
387 return true;
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)) {
395 return true;
398 return false;
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);
410 if (sp) {
411 fputs(sp->name, out);
412 if (showAddress) {
413 fprintf(out, "[%p]", (char*)addr);
416 else {
417 fprintf(out, "<%p>", (char*)addr);
419 fputc(' ', out);
421 fputc('\n', out);
424 void leaky::dumpEntryToLog(malloc_log_entry* lep)
426 printf("%ld\t", lep->delTime);
427 printf(" --> ");
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
449 int mx, i, h;
450 for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) {
451 if(h<mx) {
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
465 // does
466 fprintf(fp,
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]],
480 symname);
481 delete [] symname;
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
490 // the hit count.
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) {
495 if(h<mx) {
496 for(i = h-1; i<usefulSymbols; i++) {
497 int j, tmp=rankingTable[i], val = externalSymbols[tmp].timerHit;
498 for(j = i;
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;
512 for(i=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
524 for(i=0;
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);
533 delete [] 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
556 stacks = 0;
557 for(malloc_log_entry* lep=firstLogEntry;
558 lep < lastLogEntry;
559 lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) {
561 if (excluded(lep) || !included(lep))
562 continue;
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));
574 if(idx>=0) {
575 // Skip over bogus __restore_rt frames that realtime profiling
576 // can introduce.
577 if (i > 0 && !strcmp(externalSymbols[idx].name, "__restore_rt")) {
578 --pcp;
579 --i;
580 idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp));
581 if (idx < 0) {
582 continue;
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)) {
588 ++countArray[idx];
591 // We know who we are and we know who our parrent is. Count this
592 if(parrentIdx>=0) {
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.
600 if(idx>=0) {
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);
614 do {
615 nmax=0;
616 for(int j=getSize(); --j>=0;) {
617 int cnt = getCount(j);
618 if(cnt==tmax) {
619 int idx = getIndex(j);
620 char *symname = htmlify(lk->indexToName(idx));
621 fprintf(fp, fmt, idx, getCount(j), symname);
622 delete [] symname;
623 } else if(cnt<tmax && cnt>nmax) {
624 nmax=cnt;
627 } while((tmax=nmax)>0);