tools/jprof/leaky.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "leaky.h"
michael@0 7 #include "intcnt.h"
michael@0 8
michael@0 9 #include <sys/types.h>
michael@0 10 #include <sys/mman.h>
michael@0 11 #include <sys/stat.h>
michael@0 12 #include <fcntl.h>
michael@0 13 #include <unistd.h>
michael@0 14 #include <string.h>
michael@0 15 #ifndef NTO
michael@0 16 #include <getopt.h>
michael@0 17 #endif
michael@0 18 #include <assert.h>
michael@0 19 #include <stdlib.h>
michael@0 20 #include <stdio.h>
michael@0 21
michael@0 22 #ifdef NTO
michael@0 23 #include <mem.h>
michael@0 24 #endif
michael@0 25
michael@0 26 #ifndef FALSE
michael@0 27 #define FALSE 0
michael@0 28 #endif
michael@0 29 #ifndef TRUE
michael@0 30 #define TRUE 1
michael@0 31 #endif
michael@0 32
michael@0 33 static const u_int DefaultBuckets = 10007; // arbitrary, but prime
michael@0 34 static const u_int MaxBuckets = 1000003; // arbitrary, but prime
michael@0 35
michael@0 36 //----------------------------------------------------------------------
michael@0 37
michael@0 38 int main(int argc, char** argv)
michael@0 39 {
michael@0 40 leaky* l = new leaky;
michael@0 41
michael@0 42 l->initialize(argc, argv);
michael@0 43 l->outputfd = stdout;
michael@0 44
michael@0 45 for (int i = 0; i < l->numLogFiles; i++) {
michael@0 46 if (l->output_dir || l->numLogFiles > 1) {
michael@0 47 char name[2048]; // XXX fix
michael@0 48 if (l->output_dir)
michael@0 49 snprintf(name,sizeof(name),"%s/%s.html",l->output_dir,argv[l->logFileIndex + i]);
michael@0 50 else
michael@0 51 snprintf(name,sizeof(name),"%s.html",argv[l->logFileIndex + i]);
michael@0 52
michael@0 53 fprintf(stderr,"opening %s\n",name);
michael@0 54 l->outputfd = fopen(name,"w");
michael@0 55 // if an error we won't process the file
michael@0 56 }
michael@0 57 if (l->outputfd) { // paranoia
michael@0 58 l->open(argv[l->logFileIndex + i]);
michael@0 59
michael@0 60 if (l->outputfd != stderr) {
michael@0 61 fclose(l->outputfd);
michael@0 62 l->outputfd = nullptr;
michael@0 63 }
michael@0 64 }
michael@0 65 }
michael@0 66
michael@0 67 return 0;
michael@0 68 }
michael@0 69
michael@0 70 char *
michael@0 71 htmlify(const char *in)
michael@0 72 {
michael@0 73 const char *p = in;
michael@0 74 char *out, *q;
michael@0 75 int n = 0;
michael@0 76 size_t newlen;
michael@0 77
michael@0 78 // Count the number of '<' and '>' in the input.
michael@0 79 while ((p = strpbrk(p, "<>")))
michael@0 80 {
michael@0 81 ++n;
michael@0 82 ++p;
michael@0 83 }
michael@0 84
michael@0 85 // Knowing the number of '<' and '>', we can calculate the space
michael@0 86 // needed for the output string.
michael@0 87 newlen = strlen(in) + n * 3 + 1;
michael@0 88 out = new char[newlen];
michael@0 89
michael@0 90 // Copy the input to the output, with substitutions.
michael@0 91 p = in;
michael@0 92 q = out;
michael@0 93 do
michael@0 94 {
michael@0 95 if (*p == '<')
michael@0 96 {
michael@0 97 strcpy(q, "&lt;");
michael@0 98 q += 4;
michael@0 99 }
michael@0 100 else if (*p == '>')
michael@0 101 {
michael@0 102 strcpy(q, "&gt;");
michael@0 103 q += 4;
michael@0 104 }
michael@0 105 else
michael@0 106 {
michael@0 107 *q++ = *p;
michael@0 108 }
michael@0 109 p++;
michael@0 110 } while (*p);
michael@0 111 *q = '\0';
michael@0 112
michael@0 113 return out;
michael@0 114 }
michael@0 115
michael@0 116 leaky::leaky()
michael@0 117 {
michael@0 118 applicationName = nullptr;
michael@0 119 progFile = nullptr;
michael@0 120
michael@0 121 quiet = true;
michael@0 122 showAddress = false;
michael@0 123 showThreads = false;
michael@0 124 stackDepth = 100000;
michael@0 125 onlyThread = 0;
michael@0 126 cleo = false;
michael@0 127
michael@0 128 mappedLogFile = -1;
michael@0 129 firstLogEntry = lastLogEntry = 0;
michael@0 130
michael@0 131 sfd = -1;
michael@0 132 externalSymbols = 0;
michael@0 133 usefulSymbols = 0;
michael@0 134 numExternalSymbols = 0;
michael@0 135 lowestSymbolAddr = 0;
michael@0 136 highestSymbolAddr = 0;
michael@0 137
michael@0 138 loadMap = nullptr;
michael@0 139
michael@0 140 collect_last = false;
michael@0 141 collect_start = -1;
michael@0 142 collect_end = -1;
michael@0 143 }
michael@0 144
michael@0 145 leaky::~leaky()
michael@0 146 {
michael@0 147 }
michael@0 148
michael@0 149 void leaky::usageError()
michael@0 150 {
michael@0 151 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);
michael@0 152 fprintf(stderr,
michael@0 153 "\t-v: verbose\n"
michael@0 154 "\t-t | --threads: split threads\n"
michael@0 155 "\t--only-thread n: only profile thread N\n"
michael@0 156 "\t-i include-id: stack must include specified id\n"
michael@0 157 "\t-e exclude-id: stack must NOT include specified id\n"
michael@0 158 "\t-s stackdepth: Limit depth looked at from captured stack frames\n"
michael@0 159 "\t--last: only profile the last capture section\n"
michael@0 160 "\t--start n [--end m]: profile n to m (or end) capture sections\n"
michael@0 161 "\t--cleo: format output for 'cleopatra' display\n"
michael@0 162 "\t--output-dir dir: write output files to dir\n"
michael@0 163 "\tIf there's one log, output goes to stdout unless --output-dir is set\n"
michael@0 164 "\tIf there are more than one log, output files will be named with .html added\n"
michael@0 165 );
michael@0 166 exit(-1);
michael@0 167 }
michael@0 168
michael@0 169 static struct option longopts[] = {
michael@0 170 { "threads", 0, nullptr, 't' },
michael@0 171 { "only-thread", 1, nullptr, 'T' },
michael@0 172 { "last", 0, nullptr, 'l' },
michael@0 173 { "start", 1, nullptr, 'x' },
michael@0 174 { "end", 1, nullptr, 'n' },
michael@0 175 { "cleo",0, nullptr, 'c' },
michael@0 176 { "output-dir", 1, nullptr, 'd' },
michael@0 177 { nullptr, 0, nullptr, 0 },
michael@0 178 };
michael@0 179
michael@0 180 void leaky::initialize(int argc, char** argv)
michael@0 181 {
michael@0 182 applicationName = argv[0];
michael@0 183 applicationName = strrchr(applicationName, '/');
michael@0 184 if (!applicationName) {
michael@0 185 applicationName = argv[0];
michael@0 186 } else {
michael@0 187 applicationName++;
michael@0 188 }
michael@0 189
michael@0 190 int arg;
michael@0 191 int errflg = 0;
michael@0 192 int longindex = 0;
michael@0 193
michael@0 194 onlyThread = 0;
michael@0 195 output_dir = nullptr;
michael@0 196 cleo = false;
michael@0 197
michael@0 198 // XXX tons of cruft here left over from tracemalloc
michael@0 199 // XXX The -- options shouldn't need short versions, or they should be documented
michael@0 200 while (((arg = getopt_long(argc, argv, "adEe:gh:i:r:Rs:tT:qvx:ln:",longopts,&longindex)) != -1)) {
michael@0 201 switch (arg) {
michael@0 202 case '?':
michael@0 203 default:
michael@0 204 fprintf(stderr,"error: unknown option %c\n",optopt);
michael@0 205 errflg++;
michael@0 206 break;
michael@0 207 case 'a':
michael@0 208 break;
michael@0 209 case 'A': // not implemented
michael@0 210 showAddress = true;
michael@0 211 break;
michael@0 212 case 'c':
michael@0 213 cleo = true;
michael@0 214 break;
michael@0 215 case 'd':
michael@0 216 output_dir = optarg; // reference to an argv pointer
michael@0 217 break;
michael@0 218 case 'R':
michael@0 219 break;
michael@0 220 case 'e':
michael@0 221 exclusions.add(optarg);
michael@0 222 break;
michael@0 223 case 'g':
michael@0 224 break;
michael@0 225 case 'r': // not implemented
michael@0 226 roots.add(optarg);
michael@0 227 if (!includes.IsEmpty()) {
michael@0 228 errflg++;
michael@0 229 }
michael@0 230 break;
michael@0 231 case 'i':
michael@0 232 includes.add(optarg);
michael@0 233 if (!roots.IsEmpty()) {
michael@0 234 errflg++;
michael@0 235 }
michael@0 236 break;
michael@0 237 case 'h':
michael@0 238 break;
michael@0 239 case 's':
michael@0 240 stackDepth = atoi(optarg);
michael@0 241 if (stackDepth < 2) {
michael@0 242 stackDepth = 2;
michael@0 243 }
michael@0 244 break;
michael@0 245 case 'x':
michael@0 246 // --start
michael@0 247 collect_start = atoi(optarg);
michael@0 248 break;
michael@0 249 case 'n':
michael@0 250 // --end
michael@0 251 collect_end = atoi(optarg);
michael@0 252 break;
michael@0 253 case 'l':
michael@0 254 // --last
michael@0 255 collect_last = true;
michael@0 256 break;
michael@0 257 case 'q':
michael@0 258 break;
michael@0 259 case 'v':
michael@0 260 quiet = !quiet;
michael@0 261 break;
michael@0 262 case 't':
michael@0 263 showThreads = true;
michael@0 264 break;
michael@0 265 case 'T':
michael@0 266 showThreads = true;
michael@0 267 onlyThread = atoi(optarg);
michael@0 268 break;
michael@0 269 }
michael@0 270 }
michael@0 271 if (errflg || ((argc - optind) < 2)) {
michael@0 272 usageError();
michael@0 273 }
michael@0 274 progFile = argv[optind++];
michael@0 275 logFileIndex = optind;
michael@0 276 numLogFiles = argc - optind;
michael@0 277 if (!quiet)
michael@0 278 fprintf(stderr,"numlogfiles = %d\n",numLogFiles);
michael@0 279 }
michael@0 280
michael@0 281 static void* mapFile(int fd, u_int flags, off_t* sz)
michael@0 282 {
michael@0 283 struct stat sb;
michael@0 284 if (fstat(fd, &sb) < 0) {
michael@0 285 perror("fstat");
michael@0 286 exit(-1);
michael@0 287 }
michael@0 288 void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0);
michael@0 289 if (!base) {
michael@0 290 perror("mmap");
michael@0 291 exit(-1);
michael@0 292 }
michael@0 293 *sz = sb.st_size;
michael@0 294 return base;
michael@0 295 }
michael@0 296
michael@0 297 void leaky::LoadMap()
michael@0 298 {
michael@0 299 malloc_map_entry mme;
michael@0 300 char name[1000];
michael@0 301
michael@0 302 if (!loadMap) {
michael@0 303 // all files use the same map
michael@0 304 int fd = ::open(M_MAPFILE, O_RDONLY);
michael@0 305 if (fd < 0) {
michael@0 306 perror("open: " M_MAPFILE);
michael@0 307 exit(-1);
michael@0 308 }
michael@0 309 for (;;) {
michael@0 310 int nb = read(fd, &mme, sizeof(mme));
michael@0 311 if (nb != sizeof(mme)) break;
michael@0 312 nb = read(fd, name, mme.nameLen);
michael@0 313 if (nb != (int)mme.nameLen) break;
michael@0 314 name[mme.nameLen] = 0;
michael@0 315 if (!quiet) {
michael@0 316 fprintf(stderr,"%s @ %lx\n", name, mme.address);
michael@0 317 }
michael@0 318
michael@0 319 LoadMapEntry* lme = new LoadMapEntry;
michael@0 320 lme->address = mme.address;
michael@0 321 lme->name = strdup(name);
michael@0 322 lme->next = loadMap;
michael@0 323 loadMap = lme;
michael@0 324 }
michael@0 325 close(fd);
michael@0 326 }
michael@0 327 }
michael@0 328
michael@0 329 void leaky::open(char *logFile)
michael@0 330 {
michael@0 331 int threadArray[100]; // should auto-expand
michael@0 332 int last_thread = -1;
michael@0 333 int numThreads = 0;
michael@0 334 int section = -1;
michael@0 335 bool collecting = false;
michael@0 336
michael@0 337 LoadMap();
michael@0 338
michael@0 339 setupSymbols(progFile);
michael@0 340
michael@0 341 // open up the log file
michael@0 342 if (mappedLogFile)
michael@0 343 ::close(mappedLogFile);
michael@0 344
michael@0 345 mappedLogFile = ::open(logFile, O_RDONLY);
michael@0 346 if (mappedLogFile < 0) {
michael@0 347 perror("open");
michael@0 348 exit(-1);
michael@0 349 }
michael@0 350 off_t size;
michael@0 351 firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size);
michael@0 352 lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size);
michael@0 353
michael@0 354 if (!collect_last || collect_start < 0) {
michael@0 355 collecting = true;
michael@0 356 }
michael@0 357
michael@0 358 // First, restrict it to the capture sections specified (all, last, start/end)
michael@0 359 // This loop walks through all the call stacks we recorded
michael@0 360 for (malloc_log_entry* lep=firstLogEntry;
michael@0 361 lep < lastLogEntry;
michael@0 362 lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) {
michael@0 363
michael@0 364 if (lep->flags & JP_FIRST_AFTER_PAUSE) {
michael@0 365 section++;
michael@0 366 if (collect_last) {
michael@0 367 firstLogEntry = lep;
michael@0 368 numThreads = 0;
michael@0 369 collecting = true;
michael@0 370 }
michael@0 371 if (collect_start == section) {
michael@0 372 collecting = true;
michael@0 373 firstLogEntry = lep;
michael@0 374 }
michael@0 375 if (collect_end == section) {
michael@0 376 collecting = false;
michael@0 377 lastLogEntry = lep;
michael@0 378 }
michael@0 379 if (!quiet)
michael@0 380 fprintf(stderr,"New section %d: first=%p, last=%p, collecting=%d\n",
michael@0 381 section,(void*)firstLogEntry,(void*)lastLogEntry,collecting);
michael@0 382 }
michael@0 383
michael@0 384 // Capture thread info at the same time
michael@0 385
michael@0 386 // Find all the threads captured
michael@0 387
michael@0 388 // pthread/linux docs say the signal can be delivered to any thread in
michael@0 389 // the process. In practice, it appears in Linux that it's always
michael@0 390 // delivered to the thread that called setitimer(), and each thread can
michael@0 391 // have a separate itimer. There's a support library for gprof that
michael@0 392 // overlays pthread_create() to set timers in any threads you spawn.
michael@0 393 if (showThreads && collecting) {
michael@0 394 if (lep->thread != last_thread)
michael@0 395 {
michael@0 396 int i;
michael@0 397 for (i=0; i<numThreads; i++)
michael@0 398 {
michael@0 399 if (lep->thread == threadArray[i])
michael@0 400 break;
michael@0 401 }
michael@0 402 if (i == numThreads &&
michael@0 403 i < (int) (sizeof(threadArray)/sizeof(threadArray[0])))
michael@0 404 {
michael@0 405 threadArray[i] = lep->thread;
michael@0 406 numThreads++;
michael@0 407 if (!quiet)
michael@0 408 fprintf(stderr,"new thread %d\n",lep->thread);
michael@0 409 }
michael@0 410 }
michael@0 411 }
michael@0 412 }
michael@0 413 if (!quiet)
michael@0 414 fprintf(stderr,"Done collecting: sections %d: first=%p, last=%p, numThreads=%d\n",
michael@0 415 section,(void*)firstLogEntry,(void*)lastLogEntry,numThreads);
michael@0 416
michael@0 417 if (!cleo) {
michael@0 418 fprintf(outputfd,"<html><head><title>Jprof Profile Report</title></head><body>\n");
michael@0 419 fprintf(outputfd,"<h1><center>Jprof Profile Report</center></h1>\n");
michael@0 420 }
michael@0 421
michael@0 422 if (showThreads)
michael@0 423 {
michael@0 424 fprintf(stderr,"Num threads %d\n",numThreads);
michael@0 425
michael@0 426 if (!cleo) {
michael@0 427 fprintf(outputfd,"<hr>Threads:<p><pre>\n");
michael@0 428 for (int i=0; i<numThreads; i++)
michael@0 429 {
michael@0 430 fprintf(outputfd," <a href=\"#thread_%d\">%d</a> ",
michael@0 431 threadArray[i],threadArray[i]);
michael@0 432 if ((i+1)%10 == 0)
michael@0 433 fprintf(outputfd,"<br>\n");
michael@0 434 }
michael@0 435 fprintf(outputfd,"</pre>");
michael@0 436 }
michael@0 437
michael@0 438 for (int i=0; i<numThreads; i++)
michael@0 439 {
michael@0 440 if (!onlyThread || onlyThread == threadArray[i])
michael@0 441 analyze(threadArray[i]);
michael@0 442 }
michael@0 443 }
michael@0 444 else
michael@0 445 {
michael@0 446 analyze(0);
michael@0 447 }
michael@0 448
michael@0 449 if (!cleo)
michael@0 450 fprintf(outputfd,"</pre></body></html>\n");
michael@0 451 }
michael@0 452
michael@0 453 //----------------------------------------------------------------------
michael@0 454
michael@0 455
michael@0 456 static int symbolOrder(void const* a, void const* b)
michael@0 457 {
michael@0 458 Symbol const** ap = (Symbol const **)a;
michael@0 459 Symbol const** bp = (Symbol const **)b;
michael@0 460 return (*ap)->address == (*bp)->address ? 0 :
michael@0 461 ((*ap)->address > (*bp)->address ? 1 : -1);
michael@0 462 }
michael@0 463
michael@0 464 void leaky::ReadSharedLibrarySymbols()
michael@0 465 {
michael@0 466 LoadMapEntry* lme = loadMap;
michael@0 467 while (nullptr != lme) {
michael@0 468 ReadSymbols(lme->name, lme->address);
michael@0 469 lme = lme->next;
michael@0 470 }
michael@0 471 }
michael@0 472
michael@0 473 void leaky::setupSymbols(const char *fileName)
michael@0 474 {
michael@0 475 if (usefulSymbols == 0) {
michael@0 476 // only read once!
michael@0 477
michael@0 478 // Read in symbols from the program
michael@0 479 ReadSymbols(fileName, 0);
michael@0 480
michael@0 481 // Read in symbols from the .so's
michael@0 482 ReadSharedLibrarySymbols();
michael@0 483
michael@0 484 if (!quiet) {
michael@0 485 fprintf(stderr,"A total of %d symbols were loaded\n", usefulSymbols);
michael@0 486 }
michael@0 487
michael@0 488 // Now sort them
michael@0 489 qsort(externalSymbols, usefulSymbols, sizeof(Symbol *), symbolOrder);
michael@0 490 lowestSymbolAddr = externalSymbols[0]->address;
michael@0 491 highestSymbolAddr = externalSymbols[usefulSymbols-1]->address;
michael@0 492 }
michael@0 493 }
michael@0 494
michael@0 495 // Binary search the table, looking for a symbol that covers this
michael@0 496 // address.
michael@0 497 int leaky::findSymbolIndex(u_long addr)
michael@0 498 {
michael@0 499 u_int base = 0;
michael@0 500 u_int limit = usefulSymbols - 1;
michael@0 501 Symbol** end = &externalSymbols[limit];
michael@0 502 while (base <= limit) {
michael@0 503 u_int midPoint = (base + limit)>>1;
michael@0 504 Symbol** sp = &externalSymbols[midPoint];
michael@0 505 if (addr < (*sp)->address) {
michael@0 506 if (midPoint == 0) {
michael@0 507 return -1;
michael@0 508 }
michael@0 509 limit = midPoint - 1;
michael@0 510 } else {
michael@0 511 if (sp+1 < end) {
michael@0 512 if (addr < (*(sp+1))->address) {
michael@0 513 return midPoint;
michael@0 514 }
michael@0 515 } else {
michael@0 516 return midPoint;
michael@0 517 }
michael@0 518 base = midPoint + 1;
michael@0 519 }
michael@0 520 }
michael@0 521 return -1;
michael@0 522 }
michael@0 523
michael@0 524 Symbol* leaky::findSymbol(u_long addr)
michael@0 525 {
michael@0 526 int idx = findSymbolIndex(addr);
michael@0 527
michael@0 528 if(idx<0) {
michael@0 529 return nullptr;
michael@0 530 } else {
michael@0 531 return externalSymbols[idx];
michael@0 532 }
michael@0 533 }
michael@0 534
michael@0 535 //----------------------------------------------------------------------
michael@0 536
michael@0 537 bool leaky::excluded(malloc_log_entry* lep)
michael@0 538 {
michael@0 539 if (exclusions.IsEmpty()) {
michael@0 540 return false;
michael@0 541 }
michael@0 542
michael@0 543 char** pcp = &lep->pcs[0];
michael@0 544 u_int n = lep->numpcs;
michael@0 545 for (u_int i = 0; i < n; i++, pcp++) {
michael@0 546 Symbol* sp = findSymbol((u_long) *pcp);
michael@0 547 if (sp && exclusions.contains(sp->name)) {
michael@0 548 return true;
michael@0 549 }
michael@0 550 }
michael@0 551 return false;
michael@0 552 }
michael@0 553
michael@0 554 bool leaky::included(malloc_log_entry* lep)
michael@0 555 {
michael@0 556 if (includes.IsEmpty()) {
michael@0 557 return true;
michael@0 558 }
michael@0 559
michael@0 560 char** pcp = &lep->pcs[0];
michael@0 561 u_int n = lep->numpcs;
michael@0 562 for (u_int i = 0; i < n; i++, pcp++) {
michael@0 563 Symbol* sp = findSymbol((u_long) *pcp);
michael@0 564 if (sp && includes.contains(sp->name)) {
michael@0 565 return true;
michael@0 566 }
michael@0 567 }
michael@0 568 return false;
michael@0 569 }
michael@0 570
michael@0 571 //----------------------------------------------------------------------
michael@0 572
michael@0 573 void leaky::displayStackTrace(FILE* out, malloc_log_entry* lep)
michael@0 574 {
michael@0 575 char** pcp = &lep->pcs[0];
michael@0 576 u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
michael@0 577 for (u_int i = 0; i < n; i++, pcp++) {
michael@0 578 u_long addr = (u_long) *pcp;
michael@0 579 Symbol* sp = findSymbol(addr);
michael@0 580 if (sp) {
michael@0 581 fputs(sp->name, out);
michael@0 582 if (showAddress) {
michael@0 583 fprintf(out, "[%p]", (char*)addr);
michael@0 584 }
michael@0 585 }
michael@0 586 else {
michael@0 587 fprintf(out, "<%p>", (char*)addr);
michael@0 588 }
michael@0 589 fputc(' ', out);
michael@0 590 }
michael@0 591 fputc('\n', out);
michael@0 592 }
michael@0 593
michael@0 594 void leaky::dumpEntryToLog(malloc_log_entry* lep)
michael@0 595 {
michael@0 596 printf("%ld\t", lep->delTime);
michael@0 597 printf(" --> ");
michael@0 598 displayStackTrace(outputfd, lep);
michael@0 599 }
michael@0 600
michael@0 601 void leaky::generateReportHTML(FILE *fp, int *countArray, int count, int thread)
michael@0 602 {
michael@0 603 fprintf(fp,"<center>");
michael@0 604 if (showThreads)
michael@0 605 {
michael@0 606 fprintf(fp,"<hr><A NAME=thread_%d><b>Thread: %d</b></A><p>",
michael@0 607 thread,thread);
michael@0 608 }
michael@0 609 fprintf(fp,"<A href=#flat_%d>flat</A><b> | </b><A href=#hier_%d>hierarchical</A>",
michael@0 610 thread,thread);
michael@0 611 fprintf(fp,"</center><P><P><P>\n");
michael@0 612
michael@0 613 int totalTimerHits = count;
michael@0 614 int *rankingTable = new int[usefulSymbols];
michael@0 615
michael@0 616 for(int cnt=usefulSymbols; --cnt>=0; rankingTable[cnt]=cnt);
michael@0 617
michael@0 618 // Drat. I would use ::qsort() but I would need a global variable and my
michael@0 619 // intro-pascal professor threatened to flunk anyone who used globals.
michael@0 620 // She damaged me for life :-) (That was 1986. See how much influence
michael@0 621 // she had. I don't remember her name but I always feel guilty about globals)
michael@0 622
michael@0 623 // Shell Sort. 581130733 is the max 31 bit value of h = 3h+1
michael@0 624 int mx, i, h;
michael@0 625 for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) {
michael@0 626 if(h<mx) {
michael@0 627 for(i = h-1; i<usefulSymbols; i++) {
michael@0 628 int j, tmp=rankingTable[i], val = countArray[tmp];
michael@0 629 for(j = i; (j>=h) && (countArray[rankingTable[j-h]]<val); j-=h) {
michael@0 630 rankingTable[j] = rankingTable[j-h];
michael@0 631 }
michael@0 632 rankingTable[j] = tmp;
michael@0 633 }
michael@0 634 }
michael@0 635 }
michael@0 636
michael@0 637 // Ok, We are sorted now. Let's go through the table until we get to
michael@0 638 // functions that were never called. Right now we don't do much inside
michael@0 639 // this loop. Later we can get callers and callees into it like gprof
michael@0 640 // does
michael@0 641 fprintf(fp,
michael@0 642 "<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",
michael@0 643 thread);
michael@0 644 fprintf(fp, "<pre>\n");
michael@0 645 fprintf(fp, "%6s %6s %4s %s\n",
michael@0 646 "index", "Count", "Hits", "Function Name");
michael@0 647
michael@0 648 for(i=0; i<usefulSymbols && countArray[rankingTable[i]]>0; i++) {
michael@0 649 Symbol **sp=&externalSymbols[rankingTable[i]];
michael@0 650
michael@0 651 (*sp)->cntP.printReport(fp, this, rankingTable[i], totalTimerHits);
michael@0 652
michael@0 653 char *symname = htmlify((*sp)->name);
michael@0 654 fprintf(fp, "%6d %6d (%3.1f%%)%s <a name=%d>%8d (%3.1f%%)</a>%s <b>%s</b>\n",
michael@0 655 rankingTable[i],
michael@0 656 (*sp)->timerHit, ((*sp)->timerHit*1000/totalTimerHits)/10.0,
michael@0 657 ((*sp)->timerHit*1000/totalTimerHits)/10.0 >= 10.0 ? "" : " ",
michael@0 658 rankingTable[i], countArray[rankingTable[i]],
michael@0 659 (countArray[rankingTable[i]]*1000/totalTimerHits)/10.0,
michael@0 660 (countArray[rankingTable[i]]*1000/totalTimerHits)/10.0 >= 10.0 ? "" : " ",
michael@0 661 symname);
michael@0 662 delete [] symname;
michael@0 663
michael@0 664 (*sp)->cntC.printReport(fp, this, rankingTable[i], totalTimerHits);
michael@0 665
michael@0 666 fprintf(fp, "<hr>\n");
michael@0 667 }
michael@0 668 fprintf(fp,"</pre>\n");
michael@0 669
michael@0 670 // OK, Now we want to print the flat profile. To do this we resort on
michael@0 671 // the hit count.
michael@0 672
michael@0 673 // Cut-N-Paste Shell sort from above. The Ranking Table has already been
michael@0 674 // populated, so we do not have to reinitialize it.
michael@0 675 for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) {
michael@0 676 if(h<mx) {
michael@0 677 for(i = h-1; i<usefulSymbols; i++) {
michael@0 678 int j, tmp=rankingTable[i], val = externalSymbols[tmp]->timerHit;
michael@0 679 for(j = i;
michael@0 680 (j>=h) && (externalSymbols[rankingTable[j-h]]->timerHit<val); j-=h) {
michael@0 681 rankingTable[j] = rankingTable[j-h];
michael@0 682 }
michael@0 683 rankingTable[j] = tmp;
michael@0 684 }
michael@0 685 }
michael@0 686 }
michael@0 687
michael@0 688 // Pre-count up total counter hits, to get a percentage.
michael@0 689 // I wanted the total before walking the list, if this
michael@0 690 // double-pass over externalSymbols gets slow we can
michael@0 691 // do single-pass and print this out after the loop finishes.
michael@0 692 totalTimerHits = 0;
michael@0 693 for(i=0;
michael@0 694 i<usefulSymbols && externalSymbols[rankingTable[i]]->timerHit>0; i++) {
michael@0 695 Symbol **sp=&externalSymbols[rankingTable[i]];
michael@0 696 totalTimerHits += (*sp)->timerHit;
michael@0 697 }
michael@0 698 if (totalTimerHits == 0)
michael@0 699 totalTimerHits = 1;
michael@0 700
michael@0 701 if (totalTimerHits != count)
michael@0 702 fprintf(stderr,"Hit count mismatch: count=%d; totalTimerHits=%d",
michael@0 703 count,totalTimerHits);
michael@0 704
michael@0 705 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",
michael@0 706 thread);
michael@0 707 fprintf(fp, "<pre>\n");
michael@0 708
michael@0 709 fprintf(fp, "Total hit count: %d\n", totalTimerHits);
michael@0 710 fprintf(fp, "Count %%Total Function Name\n");
michael@0 711 // Now loop for as long as we have timer hits
michael@0 712 for(i=0;
michael@0 713 i<usefulSymbols && externalSymbols[rankingTable[i]]->timerHit>0; i++) {
michael@0 714
michael@0 715 Symbol **sp=&externalSymbols[rankingTable[i]];
michael@0 716
michael@0 717 char *symname = htmlify((*sp)->name);
michael@0 718 fprintf(fp, "<a href=\"#%d\">%3d %-2.1f %s</a>\n",
michael@0 719 rankingTable[i], (*sp)->timerHit,
michael@0 720 ((float)(*sp)->timerHit/(float)totalTimerHits)*100.0, symname);
michael@0 721 delete [] symname;
michael@0 722 }
michael@0 723 }
michael@0 724
michael@0 725 void leaky::analyze(int thread)
michael@0 726 {
michael@0 727 int *countArray = new int[usefulSymbols];
michael@0 728 int *flagArray = new int[usefulSymbols];
michael@0 729
michael@0 730 //Zero our function call counter
michael@0 731 memset(countArray, 0, sizeof(countArray[0])*usefulSymbols);
michael@0 732
michael@0 733 // reset hit counts
michael@0 734 for(int i=0; i<usefulSymbols; i++) {
michael@0 735 externalSymbols[i]->timerHit = 0;
michael@0 736 externalSymbols[i]->regClear();
michael@0 737 }
michael@0 738
michael@0 739 // The flag array is used to prevent counting symbols multiple times
michael@0 740 // if functions are called recursively. In order to keep from having
michael@0 741 // to zero it on each pass through the loop, we mark it with the value
michael@0 742 // of stacks on each trip through the loop. This means we can determine
michael@0 743 // if we have seen this symbol for this stack trace w/o having to reset
michael@0 744 // from the prior stacktrace.
michael@0 745 memset(flagArray, -1, sizeof(flagArray[0])*usefulSymbols);
michael@0 746
michael@0 747 if (cleo)
michael@0 748 fprintf(outputfd,"m-Start\n");
michael@0 749
michael@0 750 // This loop walks through all the call stacks we recorded
michael@0 751 // --last, --start and --end can restrict it, as can excludes/includes
michael@0 752 stacks = 0;
michael@0 753 for(malloc_log_entry* lep=firstLogEntry;
michael@0 754 lep < lastLogEntry;
michael@0 755 lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) {
michael@0 756
michael@0 757 if ((thread != 0 && lep->thread != thread) ||
michael@0 758 excluded(lep) || !included(lep))
michael@0 759 {
michael@0 760 continue;
michael@0 761 }
michael@0 762
michael@0 763 ++stacks; // How many stack frames did we collect
michael@0 764
michael@0 765 u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
michael@0 766 char** pcp = &lep->pcs[n-1];
michael@0 767 int idx=-1, parrentIdx=-1; // Init idx incase n==0
michael@0 768 if (cleo) {
michael@0 769 // This loop walks through every symbol in the call stack. By walking it
michael@0 770 // backwards we know who called the function when we get there.
michael@0 771 char type = 's';
michael@0 772 for (int i=n-1; i>=0; --i, --pcp) {
michael@0 773 idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp));
michael@0 774
michael@0 775 if(idx>=0) {
michael@0 776 // Skip over bogus __restore_rt frames that realtime profiling
michael@0 777 // can introduce.
michael@0 778 if (i > 0 && !strcmp(externalSymbols[idx]->name, "__restore_rt")) {
michael@0 779 --pcp;
michael@0 780 --i;
michael@0 781 idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp));
michael@0 782 if (idx < 0) {
michael@0 783 continue;
michael@0 784 }
michael@0 785 }
michael@0 786 Symbol **sp=&externalSymbols[idx];
michael@0 787 char *symname = htmlify((*sp)->name);
michael@0 788 fprintf(outputfd,"%c-%s\n",type,symname);
michael@0 789 delete [] symname;
michael@0 790 }
michael@0 791 // else can't find symbol - ignore
michael@0 792 type = 'c';
michael@0 793 }
michael@0 794 } else {
michael@0 795 // This loop walks through every symbol in the call stack. By walking it
michael@0 796 // backwards we know who called the function when we get there.
michael@0 797 for (int i=n-1; i>=0; --i, --pcp) {
michael@0 798 idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp));
michael@0 799
michael@0 800 if(idx>=0) {
michael@0 801 // Skip over bogus __restore_rt frames that realtime profiling
michael@0 802 // can introduce.
michael@0 803 if (i > 0 && !strcmp(externalSymbols[idx]->name, "__restore_rt")) {
michael@0 804 --pcp;
michael@0 805 --i;
michael@0 806 idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp));
michael@0 807 if (idx < 0) {
michael@0 808 continue;
michael@0 809 }
michael@0 810 }
michael@0 811
michael@0 812 // If we have not seen this symbol before count it and mark it as seen
michael@0 813 if(flagArray[idx]!=stacks && ((flagArray[idx]=stacks) || true)) {
michael@0 814 ++countArray[idx];
michael@0 815 }
michael@0 816
michael@0 817 // We know who we are and we know who our parrent is. Count this
michael@0 818 if(parrentIdx>=0) {
michael@0 819 externalSymbols[parrentIdx]->regChild(idx);
michael@0 820 externalSymbols[idx]->regParrent(parrentIdx);
michael@0 821 }
michael@0 822 // inside if() so an unknown in the middle of a stack won't break
michael@0 823 // the link!
michael@0 824 parrentIdx=idx;
michael@0 825 }
michael@0 826 }
michael@0 827
michael@0 828 // idx should be the function that we were in when we received the signal.
michael@0 829 if(idx>=0) {
michael@0 830 ++externalSymbols[idx]->timerHit;
michael@0 831 }
michael@0 832
michael@0 833 }
michael@0 834 }
michael@0 835 if (!cleo)
michael@0 836 generateReportHTML(outputfd, countArray, stacks, thread);
michael@0 837 }
michael@0 838
michael@0 839 void FunctionCount::printReport(FILE *fp, leaky *lk, int parent, int total)
michael@0 840 {
michael@0 841 const char *fmt = " <A href=\"#%d\">%8d (%3.1f%%)%s %s</A>%s\n";
michael@0 842
michael@0 843 int nmax, tmax=((~0U)>>1);
michael@0 844
michael@0 845 do {
michael@0 846 nmax=0;
michael@0 847 for(int j=getSize(); --j>=0;) {
michael@0 848 int cnt = getCount(j);
michael@0 849 if(cnt==tmax) {
michael@0 850 int idx = getIndex(j);
michael@0 851 char *symname = htmlify(lk->indexToName(idx));
michael@0 852 fprintf(fp, fmt, idx, getCount(j),
michael@0 853 getCount(j)*100.0/total,
michael@0 854 getCount(j)*100.0/total >= 10.0 ? "" : " ",
michael@0 855 symname,
michael@0 856 parent == idx ? " (self)" : "");
michael@0 857 delete [] symname;
michael@0 858 } else if(cnt<tmax && cnt>nmax) {
michael@0 859 nmax=cnt;
michael@0 860 }
michael@0 861 }
michael@0 862 } while((tmax=nmax)>0);
michael@0 863 }

mercurial