1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/jprof/leaky.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,863 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "leaky.h" 1.10 +#include "intcnt.h" 1.11 + 1.12 +#include <sys/types.h> 1.13 +#include <sys/mman.h> 1.14 +#include <sys/stat.h> 1.15 +#include <fcntl.h> 1.16 +#include <unistd.h> 1.17 +#include <string.h> 1.18 +#ifndef NTO 1.19 +#include <getopt.h> 1.20 +#endif 1.21 +#include <assert.h> 1.22 +#include <stdlib.h> 1.23 +#include <stdio.h> 1.24 + 1.25 +#ifdef NTO 1.26 +#include <mem.h> 1.27 +#endif 1.28 + 1.29 +#ifndef FALSE 1.30 +#define FALSE 0 1.31 +#endif 1.32 +#ifndef TRUE 1.33 +#define TRUE 1 1.34 +#endif 1.35 + 1.36 +static const u_int DefaultBuckets = 10007; // arbitrary, but prime 1.37 +static const u_int MaxBuckets = 1000003; // arbitrary, but prime 1.38 + 1.39 +//---------------------------------------------------------------------- 1.40 + 1.41 +int main(int argc, char** argv) 1.42 +{ 1.43 + leaky* l = new leaky; 1.44 + 1.45 + l->initialize(argc, argv); 1.46 + l->outputfd = stdout; 1.47 + 1.48 + for (int i = 0; i < l->numLogFiles; i++) { 1.49 + if (l->output_dir || l->numLogFiles > 1) { 1.50 + char name[2048]; // XXX fix 1.51 + if (l->output_dir) 1.52 + snprintf(name,sizeof(name),"%s/%s.html",l->output_dir,argv[l->logFileIndex + i]); 1.53 + else 1.54 + snprintf(name,sizeof(name),"%s.html",argv[l->logFileIndex + i]); 1.55 + 1.56 + fprintf(stderr,"opening %s\n",name); 1.57 + l->outputfd = fopen(name,"w"); 1.58 + // if an error we won't process the file 1.59 + } 1.60 + if (l->outputfd) { // paranoia 1.61 + l->open(argv[l->logFileIndex + i]); 1.62 + 1.63 + if (l->outputfd != stderr) { 1.64 + fclose(l->outputfd); 1.65 + l->outputfd = nullptr; 1.66 + } 1.67 + } 1.68 + } 1.69 + 1.70 + return 0; 1.71 +} 1.72 + 1.73 +char * 1.74 +htmlify(const char *in) 1.75 +{ 1.76 + const char *p = in; 1.77 + char *out, *q; 1.78 + int n = 0; 1.79 + size_t newlen; 1.80 + 1.81 + // Count the number of '<' and '>' in the input. 1.82 + while ((p = strpbrk(p, "<>"))) 1.83 + { 1.84 + ++n; 1.85 + ++p; 1.86 + } 1.87 + 1.88 + // Knowing the number of '<' and '>', we can calculate the space 1.89 + // needed for the output string. 1.90 + newlen = strlen(in) + n * 3 + 1; 1.91 + out = new char[newlen]; 1.92 + 1.93 + // Copy the input to the output, with substitutions. 1.94 + p = in; 1.95 + q = out; 1.96 + do 1.97 + { 1.98 + if (*p == '<') 1.99 + { 1.100 + strcpy(q, "<"); 1.101 + q += 4; 1.102 + } 1.103 + else if (*p == '>') 1.104 + { 1.105 + strcpy(q, ">"); 1.106 + q += 4; 1.107 + } 1.108 + else 1.109 + { 1.110 + *q++ = *p; 1.111 + } 1.112 + p++; 1.113 + } while (*p); 1.114 + *q = '\0'; 1.115 + 1.116 + return out; 1.117 +} 1.118 + 1.119 +leaky::leaky() 1.120 +{ 1.121 + applicationName = nullptr; 1.122 + progFile = nullptr; 1.123 + 1.124 + quiet = true; 1.125 + showAddress = false; 1.126 + showThreads = false; 1.127 + stackDepth = 100000; 1.128 + onlyThread = 0; 1.129 + cleo = false; 1.130 + 1.131 + mappedLogFile = -1; 1.132 + firstLogEntry = lastLogEntry = 0; 1.133 + 1.134 + sfd = -1; 1.135 + externalSymbols = 0; 1.136 + usefulSymbols = 0; 1.137 + numExternalSymbols = 0; 1.138 + lowestSymbolAddr = 0; 1.139 + highestSymbolAddr = 0; 1.140 + 1.141 + loadMap = nullptr; 1.142 + 1.143 + collect_last = false; 1.144 + collect_start = -1; 1.145 + collect_end = -1; 1.146 +} 1.147 + 1.148 +leaky::~leaky() 1.149 +{ 1.150 +} 1.151 + 1.152 +void leaky::usageError() 1.153 +{ 1.154 + fprintf(stderr, "Usage: %s [-v] [-t] [-e exclude] [-i include] [-s stackdepth] [--last] [--all] [--start n [--end m]] [--cleo] [--output-dir dir] prog log [log2 ...]\n", (char*) applicationName); 1.155 + fprintf(stderr, 1.156 + "\t-v: verbose\n" 1.157 + "\t-t | --threads: split threads\n" 1.158 + "\t--only-thread n: only profile thread N\n" 1.159 + "\t-i include-id: stack must include specified id\n" 1.160 + "\t-e exclude-id: stack must NOT include specified id\n" 1.161 + "\t-s stackdepth: Limit depth looked at from captured stack frames\n" 1.162 + "\t--last: only profile the last capture section\n" 1.163 + "\t--start n [--end m]: profile n to m (or end) capture sections\n" 1.164 + "\t--cleo: format output for 'cleopatra' display\n" 1.165 + "\t--output-dir dir: write output files to dir\n" 1.166 + "\tIf there's one log, output goes to stdout unless --output-dir is set\n" 1.167 + "\tIf there are more than one log, output files will be named with .html added\n" 1.168 + ); 1.169 + exit(-1); 1.170 +} 1.171 + 1.172 +static struct option longopts[] = { 1.173 + { "threads", 0, nullptr, 't' }, 1.174 + { "only-thread", 1, nullptr, 'T' }, 1.175 + { "last", 0, nullptr, 'l' }, 1.176 + { "start", 1, nullptr, 'x' }, 1.177 + { "end", 1, nullptr, 'n' }, 1.178 + { "cleo",0, nullptr, 'c' }, 1.179 + { "output-dir", 1, nullptr, 'd' }, 1.180 + { nullptr, 0, nullptr, 0 }, 1.181 +}; 1.182 + 1.183 +void leaky::initialize(int argc, char** argv) 1.184 +{ 1.185 + applicationName = argv[0]; 1.186 + applicationName = strrchr(applicationName, '/'); 1.187 + if (!applicationName) { 1.188 + applicationName = argv[0]; 1.189 + } else { 1.190 + applicationName++; 1.191 + } 1.192 + 1.193 + int arg; 1.194 + int errflg = 0; 1.195 + int longindex = 0; 1.196 + 1.197 + onlyThread = 0; 1.198 + output_dir = nullptr; 1.199 + cleo = false; 1.200 + 1.201 + // XXX tons of cruft here left over from tracemalloc 1.202 + // XXX The -- options shouldn't need short versions, or they should be documented 1.203 + while (((arg = getopt_long(argc, argv, "adEe:gh:i:r:Rs:tT:qvx:ln:",longopts,&longindex)) != -1)) { 1.204 + switch (arg) { 1.205 + case '?': 1.206 + default: 1.207 + fprintf(stderr,"error: unknown option %c\n",optopt); 1.208 + errflg++; 1.209 + break; 1.210 + case 'a': 1.211 + break; 1.212 + case 'A': // not implemented 1.213 + showAddress = true; 1.214 + break; 1.215 + case 'c': 1.216 + cleo = true; 1.217 + break; 1.218 + case 'd': 1.219 + output_dir = optarg; // reference to an argv pointer 1.220 + break; 1.221 + case 'R': 1.222 + break; 1.223 + case 'e': 1.224 + exclusions.add(optarg); 1.225 + break; 1.226 + case 'g': 1.227 + break; 1.228 + case 'r': // not implemented 1.229 + roots.add(optarg); 1.230 + if (!includes.IsEmpty()) { 1.231 + errflg++; 1.232 + } 1.233 + break; 1.234 + case 'i': 1.235 + includes.add(optarg); 1.236 + if (!roots.IsEmpty()) { 1.237 + errflg++; 1.238 + } 1.239 + break; 1.240 + case 'h': 1.241 + break; 1.242 + case 's': 1.243 + stackDepth = atoi(optarg); 1.244 + if (stackDepth < 2) { 1.245 + stackDepth = 2; 1.246 + } 1.247 + break; 1.248 + case 'x': 1.249 + // --start 1.250 + collect_start = atoi(optarg); 1.251 + break; 1.252 + case 'n': 1.253 + // --end 1.254 + collect_end = atoi(optarg); 1.255 + break; 1.256 + case 'l': 1.257 + // --last 1.258 + collect_last = true; 1.259 + break; 1.260 + case 'q': 1.261 + break; 1.262 + case 'v': 1.263 + quiet = !quiet; 1.264 + break; 1.265 + case 't': 1.266 + showThreads = true; 1.267 + break; 1.268 + case 'T': 1.269 + showThreads = true; 1.270 + onlyThread = atoi(optarg); 1.271 + break; 1.272 + } 1.273 + } 1.274 + if (errflg || ((argc - optind) < 2)) { 1.275 + usageError(); 1.276 + } 1.277 + progFile = argv[optind++]; 1.278 + logFileIndex = optind; 1.279 + numLogFiles = argc - optind; 1.280 + if (!quiet) 1.281 + fprintf(stderr,"numlogfiles = %d\n",numLogFiles); 1.282 +} 1.283 + 1.284 +static void* mapFile(int fd, u_int flags, off_t* sz) 1.285 +{ 1.286 + struct stat sb; 1.287 + if (fstat(fd, &sb) < 0) { 1.288 + perror("fstat"); 1.289 + exit(-1); 1.290 + } 1.291 + void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0); 1.292 + if (!base) { 1.293 + perror("mmap"); 1.294 + exit(-1); 1.295 + } 1.296 + *sz = sb.st_size; 1.297 + return base; 1.298 +} 1.299 + 1.300 +void leaky::LoadMap() 1.301 +{ 1.302 + malloc_map_entry mme; 1.303 + char name[1000]; 1.304 + 1.305 + if (!loadMap) { 1.306 + // all files use the same map 1.307 + int fd = ::open(M_MAPFILE, O_RDONLY); 1.308 + if (fd < 0) { 1.309 + perror("open: " M_MAPFILE); 1.310 + exit(-1); 1.311 + } 1.312 + for (;;) { 1.313 + int nb = read(fd, &mme, sizeof(mme)); 1.314 + if (nb != sizeof(mme)) break; 1.315 + nb = read(fd, name, mme.nameLen); 1.316 + if (nb != (int)mme.nameLen) break; 1.317 + name[mme.nameLen] = 0; 1.318 + if (!quiet) { 1.319 + fprintf(stderr,"%s @ %lx\n", name, mme.address); 1.320 + } 1.321 + 1.322 + LoadMapEntry* lme = new LoadMapEntry; 1.323 + lme->address = mme.address; 1.324 + lme->name = strdup(name); 1.325 + lme->next = loadMap; 1.326 + loadMap = lme; 1.327 + } 1.328 + close(fd); 1.329 + } 1.330 +} 1.331 + 1.332 +void leaky::open(char *logFile) 1.333 +{ 1.334 + int threadArray[100]; // should auto-expand 1.335 + int last_thread = -1; 1.336 + int numThreads = 0; 1.337 + int section = -1; 1.338 + bool collecting = false; 1.339 + 1.340 + LoadMap(); 1.341 + 1.342 + setupSymbols(progFile); 1.343 + 1.344 + // open up the log file 1.345 + if (mappedLogFile) 1.346 + ::close(mappedLogFile); 1.347 + 1.348 + mappedLogFile = ::open(logFile, O_RDONLY); 1.349 + if (mappedLogFile < 0) { 1.350 + perror("open"); 1.351 + exit(-1); 1.352 + } 1.353 + off_t size; 1.354 + firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size); 1.355 + lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size); 1.356 + 1.357 + if (!collect_last || collect_start < 0) { 1.358 + collecting = true; 1.359 + } 1.360 + 1.361 + // First, restrict it to the capture sections specified (all, last, start/end) 1.362 + // This loop walks through all the call stacks we recorded 1.363 + for (malloc_log_entry* lep=firstLogEntry; 1.364 + lep < lastLogEntry; 1.365 + lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) { 1.366 + 1.367 + if (lep->flags & JP_FIRST_AFTER_PAUSE) { 1.368 + section++; 1.369 + if (collect_last) { 1.370 + firstLogEntry = lep; 1.371 + numThreads = 0; 1.372 + collecting = true; 1.373 + } 1.374 + if (collect_start == section) { 1.375 + collecting = true; 1.376 + firstLogEntry = lep; 1.377 + } 1.378 + if (collect_end == section) { 1.379 + collecting = false; 1.380 + lastLogEntry = lep; 1.381 + } 1.382 + if (!quiet) 1.383 + fprintf(stderr,"New section %d: first=%p, last=%p, collecting=%d\n", 1.384 + section,(void*)firstLogEntry,(void*)lastLogEntry,collecting); 1.385 + } 1.386 + 1.387 + // Capture thread info at the same time 1.388 + 1.389 + // Find all the threads captured 1.390 + 1.391 + // pthread/linux docs say the signal can be delivered to any thread in 1.392 + // the process. In practice, it appears in Linux that it's always 1.393 + // delivered to the thread that called setitimer(), and each thread can 1.394 + // have a separate itimer. There's a support library for gprof that 1.395 + // overlays pthread_create() to set timers in any threads you spawn. 1.396 + if (showThreads && collecting) { 1.397 + if (lep->thread != last_thread) 1.398 + { 1.399 + int i; 1.400 + for (i=0; i<numThreads; i++) 1.401 + { 1.402 + if (lep->thread == threadArray[i]) 1.403 + break; 1.404 + } 1.405 + if (i == numThreads && 1.406 + i < (int) (sizeof(threadArray)/sizeof(threadArray[0]))) 1.407 + { 1.408 + threadArray[i] = lep->thread; 1.409 + numThreads++; 1.410 + if (!quiet) 1.411 + fprintf(stderr,"new thread %d\n",lep->thread); 1.412 + } 1.413 + } 1.414 + } 1.415 + } 1.416 + if (!quiet) 1.417 + fprintf(stderr,"Done collecting: sections %d: first=%p, last=%p, numThreads=%d\n", 1.418 + section,(void*)firstLogEntry,(void*)lastLogEntry,numThreads); 1.419 + 1.420 + if (!cleo) { 1.421 + fprintf(outputfd,"<html><head><title>Jprof Profile Report</title></head><body>\n"); 1.422 + fprintf(outputfd,"<h1><center>Jprof Profile Report</center></h1>\n"); 1.423 + } 1.424 + 1.425 + if (showThreads) 1.426 + { 1.427 + fprintf(stderr,"Num threads %d\n",numThreads); 1.428 + 1.429 + if (!cleo) { 1.430 + fprintf(outputfd,"<hr>Threads:<p><pre>\n"); 1.431 + for (int i=0; i<numThreads; i++) 1.432 + { 1.433 + fprintf(outputfd," <a href=\"#thread_%d\">%d</a> ", 1.434 + threadArray[i],threadArray[i]); 1.435 + if ((i+1)%10 == 0) 1.436 + fprintf(outputfd,"<br>\n"); 1.437 + } 1.438 + fprintf(outputfd,"</pre>"); 1.439 + } 1.440 + 1.441 + for (int i=0; i<numThreads; i++) 1.442 + { 1.443 + if (!onlyThread || onlyThread == threadArray[i]) 1.444 + analyze(threadArray[i]); 1.445 + } 1.446 + } 1.447 + else 1.448 + { 1.449 + analyze(0); 1.450 + } 1.451 + 1.452 + if (!cleo) 1.453 + fprintf(outputfd,"</pre></body></html>\n"); 1.454 +} 1.455 + 1.456 +//---------------------------------------------------------------------- 1.457 + 1.458 + 1.459 +static int symbolOrder(void const* a, void const* b) 1.460 +{ 1.461 + Symbol const** ap = (Symbol const **)a; 1.462 + Symbol const** bp = (Symbol const **)b; 1.463 + return (*ap)->address == (*bp)->address ? 0 : 1.464 + ((*ap)->address > (*bp)->address ? 1 : -1); 1.465 +} 1.466 + 1.467 +void leaky::ReadSharedLibrarySymbols() 1.468 +{ 1.469 + LoadMapEntry* lme = loadMap; 1.470 + while (nullptr != lme) { 1.471 + ReadSymbols(lme->name, lme->address); 1.472 + lme = lme->next; 1.473 + } 1.474 +} 1.475 + 1.476 +void leaky::setupSymbols(const char *fileName) 1.477 +{ 1.478 + if (usefulSymbols == 0) { 1.479 + // only read once! 1.480 + 1.481 + // Read in symbols from the program 1.482 + ReadSymbols(fileName, 0); 1.483 + 1.484 + // Read in symbols from the .so's 1.485 + ReadSharedLibrarySymbols(); 1.486 + 1.487 + if (!quiet) { 1.488 + fprintf(stderr,"A total of %d symbols were loaded\n", usefulSymbols); 1.489 + } 1.490 + 1.491 + // Now sort them 1.492 + qsort(externalSymbols, usefulSymbols, sizeof(Symbol *), symbolOrder); 1.493 + lowestSymbolAddr = externalSymbols[0]->address; 1.494 + highestSymbolAddr = externalSymbols[usefulSymbols-1]->address; 1.495 + } 1.496 +} 1.497 + 1.498 +// Binary search the table, looking for a symbol that covers this 1.499 +// address. 1.500 +int leaky::findSymbolIndex(u_long addr) 1.501 +{ 1.502 + u_int base = 0; 1.503 + u_int limit = usefulSymbols - 1; 1.504 + Symbol** end = &externalSymbols[limit]; 1.505 + while (base <= limit) { 1.506 + u_int midPoint = (base + limit)>>1; 1.507 + Symbol** sp = &externalSymbols[midPoint]; 1.508 + if (addr < (*sp)->address) { 1.509 + if (midPoint == 0) { 1.510 + return -1; 1.511 + } 1.512 + limit = midPoint - 1; 1.513 + } else { 1.514 + if (sp+1 < end) { 1.515 + if (addr < (*(sp+1))->address) { 1.516 + return midPoint; 1.517 + } 1.518 + } else { 1.519 + return midPoint; 1.520 + } 1.521 + base = midPoint + 1; 1.522 + } 1.523 + } 1.524 + return -1; 1.525 +} 1.526 + 1.527 +Symbol* leaky::findSymbol(u_long addr) 1.528 +{ 1.529 + int idx = findSymbolIndex(addr); 1.530 + 1.531 + if(idx<0) { 1.532 + return nullptr; 1.533 + } else { 1.534 + return externalSymbols[idx]; 1.535 + } 1.536 +} 1.537 + 1.538 +//---------------------------------------------------------------------- 1.539 + 1.540 +bool leaky::excluded(malloc_log_entry* lep) 1.541 +{ 1.542 + if (exclusions.IsEmpty()) { 1.543 + return false; 1.544 + } 1.545 + 1.546 + char** pcp = &lep->pcs[0]; 1.547 + u_int n = lep->numpcs; 1.548 + for (u_int i = 0; i < n; i++, pcp++) { 1.549 + Symbol* sp = findSymbol((u_long) *pcp); 1.550 + if (sp && exclusions.contains(sp->name)) { 1.551 + return true; 1.552 + } 1.553 + } 1.554 + return false; 1.555 +} 1.556 + 1.557 +bool leaky::included(malloc_log_entry* lep) 1.558 +{ 1.559 + if (includes.IsEmpty()) { 1.560 + return true; 1.561 + } 1.562 + 1.563 + char** pcp = &lep->pcs[0]; 1.564 + u_int n = lep->numpcs; 1.565 + for (u_int i = 0; i < n; i++, pcp++) { 1.566 + Symbol* sp = findSymbol((u_long) *pcp); 1.567 + if (sp && includes.contains(sp->name)) { 1.568 + return true; 1.569 + } 1.570 + } 1.571 + return false; 1.572 +} 1.573 + 1.574 +//---------------------------------------------------------------------- 1.575 + 1.576 +void leaky::displayStackTrace(FILE* out, malloc_log_entry* lep) 1.577 +{ 1.578 + char** pcp = &lep->pcs[0]; 1.579 + u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth; 1.580 + for (u_int i = 0; i < n; i++, pcp++) { 1.581 + u_long addr = (u_long) *pcp; 1.582 + Symbol* sp = findSymbol(addr); 1.583 + if (sp) { 1.584 + fputs(sp->name, out); 1.585 + if (showAddress) { 1.586 + fprintf(out, "[%p]", (char*)addr); 1.587 + } 1.588 + } 1.589 + else { 1.590 + fprintf(out, "<%p>", (char*)addr); 1.591 + } 1.592 + fputc(' ', out); 1.593 + } 1.594 + fputc('\n', out); 1.595 +} 1.596 + 1.597 +void leaky::dumpEntryToLog(malloc_log_entry* lep) 1.598 +{ 1.599 + printf("%ld\t", lep->delTime); 1.600 + printf(" --> "); 1.601 + displayStackTrace(outputfd, lep); 1.602 +} 1.603 + 1.604 +void leaky::generateReportHTML(FILE *fp, int *countArray, int count, int thread) 1.605 +{ 1.606 + fprintf(fp,"<center>"); 1.607 + if (showThreads) 1.608 + { 1.609 + fprintf(fp,"<hr><A NAME=thread_%d><b>Thread: %d</b></A><p>", 1.610 + thread,thread); 1.611 + } 1.612 + fprintf(fp,"<A href=#flat_%d>flat</A><b> | </b><A href=#hier_%d>hierarchical</A>", 1.613 + thread,thread); 1.614 + fprintf(fp,"</center><P><P><P>\n"); 1.615 + 1.616 + int totalTimerHits = count; 1.617 + int *rankingTable = new int[usefulSymbols]; 1.618 + 1.619 + for(int cnt=usefulSymbols; --cnt>=0; rankingTable[cnt]=cnt); 1.620 + 1.621 + // Drat. I would use ::qsort() but I would need a global variable and my 1.622 + // intro-pascal professor threatened to flunk anyone who used globals. 1.623 + // She damaged me for life :-) (That was 1986. See how much influence 1.624 + // she had. I don't remember her name but I always feel guilty about globals) 1.625 + 1.626 + // Shell Sort. 581130733 is the max 31 bit value of h = 3h+1 1.627 + int mx, i, h; 1.628 + for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) { 1.629 + if(h<mx) { 1.630 + for(i = h-1; i<usefulSymbols; i++) { 1.631 + int j, tmp=rankingTable[i], val = countArray[tmp]; 1.632 + for(j = i; (j>=h) && (countArray[rankingTable[j-h]]<val); j-=h) { 1.633 + rankingTable[j] = rankingTable[j-h]; 1.634 + } 1.635 + rankingTable[j] = tmp; 1.636 + } 1.637 + } 1.638 + } 1.639 + 1.640 + // Ok, We are sorted now. Let's go through the table until we get to 1.641 + // functions that were never called. Right now we don't do much inside 1.642 + // this loop. Later we can get callers and callees into it like gprof 1.643 + // does 1.644 + fprintf(fp, 1.645 + "<h2><A NAME=hier_%d></A><center><a href=\"http://mxr.mozilla.org/mozilla-central/source/tools/jprof/README.html#hier\">Hierarchical Profile</a></center></h2><hr>\n", 1.646 + thread); 1.647 + fprintf(fp, "<pre>\n"); 1.648 + fprintf(fp, "%6s %6s %4s %s\n", 1.649 + "index", "Count", "Hits", "Function Name"); 1.650 + 1.651 + for(i=0; i<usefulSymbols && countArray[rankingTable[i]]>0; i++) { 1.652 + Symbol **sp=&externalSymbols[rankingTable[i]]; 1.653 + 1.654 + (*sp)->cntP.printReport(fp, this, rankingTable[i], totalTimerHits); 1.655 + 1.656 + char *symname = htmlify((*sp)->name); 1.657 + fprintf(fp, "%6d %6d (%3.1f%%)%s <a name=%d>%8d (%3.1f%%)</a>%s <b>%s</b>\n", 1.658 + rankingTable[i], 1.659 + (*sp)->timerHit, ((*sp)->timerHit*1000/totalTimerHits)/10.0, 1.660 + ((*sp)->timerHit*1000/totalTimerHits)/10.0 >= 10.0 ? "" : " ", 1.661 + rankingTable[i], countArray[rankingTable[i]], 1.662 + (countArray[rankingTable[i]]*1000/totalTimerHits)/10.0, 1.663 + (countArray[rankingTable[i]]*1000/totalTimerHits)/10.0 >= 10.0 ? "" : " ", 1.664 + symname); 1.665 + delete [] symname; 1.666 + 1.667 + (*sp)->cntC.printReport(fp, this, rankingTable[i], totalTimerHits); 1.668 + 1.669 + fprintf(fp, "<hr>\n"); 1.670 + } 1.671 + fprintf(fp,"</pre>\n"); 1.672 + 1.673 + // OK, Now we want to print the flat profile. To do this we resort on 1.674 + // the hit count. 1.675 + 1.676 + // Cut-N-Paste Shell sort from above. The Ranking Table has already been 1.677 + // populated, so we do not have to reinitialize it. 1.678 + for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) { 1.679 + if(h<mx) { 1.680 + for(i = h-1; i<usefulSymbols; i++) { 1.681 + int j, tmp=rankingTable[i], val = externalSymbols[tmp]->timerHit; 1.682 + for(j = i; 1.683 + (j>=h) && (externalSymbols[rankingTable[j-h]]->timerHit<val); j-=h) { 1.684 + rankingTable[j] = rankingTable[j-h]; 1.685 + } 1.686 + rankingTable[j] = tmp; 1.687 + } 1.688 + } 1.689 + } 1.690 + 1.691 + // Pre-count up total counter hits, to get a percentage. 1.692 + // I wanted the total before walking the list, if this 1.693 + // double-pass over externalSymbols gets slow we can 1.694 + // do single-pass and print this out after the loop finishes. 1.695 + totalTimerHits = 0; 1.696 + for(i=0; 1.697 + i<usefulSymbols && externalSymbols[rankingTable[i]]->timerHit>0; i++) { 1.698 + Symbol **sp=&externalSymbols[rankingTable[i]]; 1.699 + totalTimerHits += (*sp)->timerHit; 1.700 + } 1.701 + if (totalTimerHits == 0) 1.702 + totalTimerHits = 1; 1.703 + 1.704 + if (totalTimerHits != count) 1.705 + fprintf(stderr,"Hit count mismatch: count=%d; totalTimerHits=%d", 1.706 + count,totalTimerHits); 1.707 + 1.708 + fprintf(fp,"<h2><A NAME=flat_%d></A><center><a href=\"http://mxr.mozilla.org/mozilla-central/source/tools/jprof/README.html#flat\">Flat Profile</a></center></h2><br>\n", 1.709 + thread); 1.710 + fprintf(fp, "<pre>\n"); 1.711 + 1.712 + fprintf(fp, "Total hit count: %d\n", totalTimerHits); 1.713 + fprintf(fp, "Count %%Total Function Name\n"); 1.714 + // Now loop for as long as we have timer hits 1.715 + for(i=0; 1.716 + i<usefulSymbols && externalSymbols[rankingTable[i]]->timerHit>0; i++) { 1.717 + 1.718 + Symbol **sp=&externalSymbols[rankingTable[i]]; 1.719 + 1.720 + char *symname = htmlify((*sp)->name); 1.721 + fprintf(fp, "<a href=\"#%d\">%3d %-2.1f %s</a>\n", 1.722 + rankingTable[i], (*sp)->timerHit, 1.723 + ((float)(*sp)->timerHit/(float)totalTimerHits)*100.0, symname); 1.724 + delete [] symname; 1.725 + } 1.726 +} 1.727 + 1.728 +void leaky::analyze(int thread) 1.729 +{ 1.730 + int *countArray = new int[usefulSymbols]; 1.731 + int *flagArray = new int[usefulSymbols]; 1.732 + 1.733 + //Zero our function call counter 1.734 + memset(countArray, 0, sizeof(countArray[0])*usefulSymbols); 1.735 + 1.736 + // reset hit counts 1.737 + for(int i=0; i<usefulSymbols; i++) { 1.738 + externalSymbols[i]->timerHit = 0; 1.739 + externalSymbols[i]->regClear(); 1.740 + } 1.741 + 1.742 + // The flag array is used to prevent counting symbols multiple times 1.743 + // if functions are called recursively. In order to keep from having 1.744 + // to zero it on each pass through the loop, we mark it with the value 1.745 + // of stacks on each trip through the loop. This means we can determine 1.746 + // if we have seen this symbol for this stack trace w/o having to reset 1.747 + // from the prior stacktrace. 1.748 + memset(flagArray, -1, sizeof(flagArray[0])*usefulSymbols); 1.749 + 1.750 + if (cleo) 1.751 + fprintf(outputfd,"m-Start\n"); 1.752 + 1.753 + // This loop walks through all the call stacks we recorded 1.754 + // --last, --start and --end can restrict it, as can excludes/includes 1.755 + stacks = 0; 1.756 + for(malloc_log_entry* lep=firstLogEntry; 1.757 + lep < lastLogEntry; 1.758 + lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) { 1.759 + 1.760 + if ((thread != 0 && lep->thread != thread) || 1.761 + excluded(lep) || !included(lep)) 1.762 + { 1.763 + continue; 1.764 + } 1.765 + 1.766 + ++stacks; // How many stack frames did we collect 1.767 + 1.768 + u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth; 1.769 + char** pcp = &lep->pcs[n-1]; 1.770 + int idx=-1, parrentIdx=-1; // Init idx incase n==0 1.771 + if (cleo) { 1.772 + // This loop walks through every symbol in the call stack. By walking it 1.773 + // backwards we know who called the function when we get there. 1.774 + char type = 's'; 1.775 + for (int i=n-1; i>=0; --i, --pcp) { 1.776 + idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); 1.777 + 1.778 + if(idx>=0) { 1.779 + // Skip over bogus __restore_rt frames that realtime profiling 1.780 + // can introduce. 1.781 + if (i > 0 && !strcmp(externalSymbols[idx]->name, "__restore_rt")) { 1.782 + --pcp; 1.783 + --i; 1.784 + idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); 1.785 + if (idx < 0) { 1.786 + continue; 1.787 + } 1.788 + } 1.789 + Symbol **sp=&externalSymbols[idx]; 1.790 + char *symname = htmlify((*sp)->name); 1.791 + fprintf(outputfd,"%c-%s\n",type,symname); 1.792 + delete [] symname; 1.793 + } 1.794 + // else can't find symbol - ignore 1.795 + type = 'c'; 1.796 + } 1.797 + } else { 1.798 + // This loop walks through every symbol in the call stack. By walking it 1.799 + // backwards we know who called the function when we get there. 1.800 + for (int i=n-1; i>=0; --i, --pcp) { 1.801 + idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); 1.802 + 1.803 + if(idx>=0) { 1.804 + // Skip over bogus __restore_rt frames that realtime profiling 1.805 + // can introduce. 1.806 + if (i > 0 && !strcmp(externalSymbols[idx]->name, "__restore_rt")) { 1.807 + --pcp; 1.808 + --i; 1.809 + idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); 1.810 + if (idx < 0) { 1.811 + continue; 1.812 + } 1.813 + } 1.814 + 1.815 + // If we have not seen this symbol before count it and mark it as seen 1.816 + if(flagArray[idx]!=stacks && ((flagArray[idx]=stacks) || true)) { 1.817 + ++countArray[idx]; 1.818 + } 1.819 + 1.820 + // We know who we are and we know who our parrent is. Count this 1.821 + if(parrentIdx>=0) { 1.822 + externalSymbols[parrentIdx]->regChild(idx); 1.823 + externalSymbols[idx]->regParrent(parrentIdx); 1.824 + } 1.825 + // inside if() so an unknown in the middle of a stack won't break 1.826 + // the link! 1.827 + parrentIdx=idx; 1.828 + } 1.829 + } 1.830 + 1.831 + // idx should be the function that we were in when we received the signal. 1.832 + if(idx>=0) { 1.833 + ++externalSymbols[idx]->timerHit; 1.834 + } 1.835 + 1.836 + } 1.837 + } 1.838 + if (!cleo) 1.839 + generateReportHTML(outputfd, countArray, stacks, thread); 1.840 +} 1.841 + 1.842 +void FunctionCount::printReport(FILE *fp, leaky *lk, int parent, int total) 1.843 +{ 1.844 + const char *fmt = " <A href=\"#%d\">%8d (%3.1f%%)%s %s</A>%s\n"; 1.845 + 1.846 + int nmax, tmax=((~0U)>>1); 1.847 + 1.848 + do { 1.849 + nmax=0; 1.850 + for(int j=getSize(); --j>=0;) { 1.851 + int cnt = getCount(j); 1.852 + if(cnt==tmax) { 1.853 + int idx = getIndex(j); 1.854 + char *symname = htmlify(lk->indexToName(idx)); 1.855 + fprintf(fp, fmt, idx, getCount(j), 1.856 + getCount(j)*100.0/total, 1.857 + getCount(j)*100.0/total >= 10.0 ? "" : " ", 1.858 + symname, 1.859 + parent == idx ? " (self)" : ""); 1.860 + delete [] symname; 1.861 + } else if(cnt<tmax && cnt>nmax) { 1.862 + nmax=cnt; 1.863 + } 1.864 + } 1.865 + } while((tmax=nmax)>0); 1.866 +}