tools/jprof/leaky.cpp

changeset 0
6474c204b198
     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, "&lt;");
   1.101 +      q += 4;
   1.102 +    }
   1.103 +    else if (*p == '>')
   1.104 +    {
   1.105 +      strcpy(q, "&gt;");
   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 +}

mercurial