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