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, "<");
98 q += 4;
99 }
100 else if (*p == '>')
101 {
102 strcpy(q, ">");
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