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