diff options
Diffstat (limited to 'tools/jprof/leaky.cpp')
-rw-r--r-- | tools/jprof/leaky.cpp | 863 |
1 files changed, 0 insertions, 863 deletions
diff --git a/tools/jprof/leaky.cpp b/tools/jprof/leaky.cpp deleted file mode 100644 index d8e5322f5d..0000000000 --- a/tools/jprof/leaky.cpp +++ /dev/null @@ -1,863 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "leaky.h" -#include "intcnt.h" - -#include <sys/types.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#ifndef NTO -#include <getopt.h> -#endif -#include <assert.h> -#include <stdlib.h> -#include <stdio.h> - -#ifdef NTO -#include <mem.h> -#endif - -#ifndef FALSE -#define FALSE 0 -#endif -#ifndef TRUE -#define TRUE 1 -#endif - -static const u_int DefaultBuckets = 10007; // arbitrary, but prime -static const u_int MaxBuckets = 1000003; // arbitrary, but prime - -//---------------------------------------------------------------------- - -int main(int argc, char** argv) -{ - leaky* l = new leaky; - - l->initialize(argc, argv); - l->outputfd = stdout; - - for (int i = 0; i < l->numLogFiles; i++) { - if (l->output_dir || l->numLogFiles > 1) { - char name[2048]; // XXX fix - if (l->output_dir) - snprintf(name,sizeof(name),"%s/%s.html",l->output_dir,argv[l->logFileIndex + i]); - else - snprintf(name,sizeof(name),"%s.html",argv[l->logFileIndex + i]); - - fprintf(stderr,"opening %s\n",name); - l->outputfd = fopen(name,"w"); - // if an error we won't process the file - } - if (l->outputfd) { // paranoia - l->open(argv[l->logFileIndex + i]); - - if (l->outputfd != stderr) { - fclose(l->outputfd); - l->outputfd = nullptr; - } - } - } - - return 0; -} - -char * -htmlify(const char *in) -{ - const char *p = in; - char *out, *q; - int n = 0; - size_t newlen; - - // Count the number of '<' and '>' in the input. - while ((p = strpbrk(p, "<>"))) - { - ++n; - ++p; - } - - // Knowing the number of '<' and '>', we can calculate the space - // needed for the output string. - newlen = strlen(in) + n * 3 + 1; - out = new char[newlen]; - - // Copy the input to the output, with substitutions. - p = in; - q = out; - do - { - if (*p == '<') - { - strcpy(q, "<"); - q += 4; - } - else if (*p == '>') - { - strcpy(q, ">"); - q += 4; - } - else - { - *q++ = *p; - } - p++; - } while (*p); - *q = '\0'; - - return out; -} - -leaky::leaky() -{ - applicationName = nullptr; - progFile = nullptr; - - quiet = true; - showAddress = false; - showThreads = false; - stackDepth = 100000; - onlyThread = 0; - cleo = false; - - mappedLogFile = -1; - firstLogEntry = lastLogEntry = 0; - - sfd = -1; - externalSymbols = 0; - usefulSymbols = 0; - numExternalSymbols = 0; - lowestSymbolAddr = 0; - highestSymbolAddr = 0; - - loadMap = nullptr; - - collect_last = false; - collect_start = -1; - collect_end = -1; -} - -leaky::~leaky() -{ -} - -void leaky::usageError() -{ - 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); - fprintf(stderr, - "\t-v: verbose\n" - "\t-t | --threads: split threads\n" - "\t--only-thread n: only profile thread N\n" - "\t-i include-id: stack must include specified id\n" - "\t-e exclude-id: stack must NOT include specified id\n" - "\t-s stackdepth: Limit depth looked at from captured stack frames\n" - "\t--last: only profile the last capture section\n" - "\t--start n [--end m]: profile n to m (or end) capture sections\n" - "\t--cleo: format output for 'cleopatra' display\n" - "\t--output-dir dir: write output files to dir\n" - "\tIf there's one log, output goes to stdout unless --output-dir is set\n" - "\tIf there are more than one log, output files will be named with .html added\n" - ); - exit(-1); -} - -static struct option longopts[] = { - { "threads", 0, nullptr, 't' }, - { "only-thread", 1, nullptr, 'T' }, - { "last", 0, nullptr, 'l' }, - { "start", 1, nullptr, 'x' }, - { "end", 1, nullptr, 'n' }, - { "cleo",0, nullptr, 'c' }, - { "output-dir", 1, nullptr, 'd' }, - { nullptr, 0, nullptr, 0 }, -}; - -void leaky::initialize(int argc, char** argv) -{ - applicationName = argv[0]; - applicationName = strrchr(applicationName, '/'); - if (!applicationName) { - applicationName = argv[0]; - } else { - applicationName++; - } - - int arg; - int errflg = 0; - int longindex = 0; - - onlyThread = 0; - output_dir = nullptr; - cleo = false; - - // XXX tons of cruft here left over from tracemalloc - // XXX The -- options shouldn't need short versions, or they should be documented - while (((arg = getopt_long(argc, argv, "adEe:gh:i:r:Rs:tT:qvx:ln:",longopts,&longindex)) != -1)) { - switch (arg) { - case '?': - default: - fprintf(stderr,"error: unknown option %c\n",optopt); - errflg++; - break; - case 'a': - break; - case 'A': // not implemented - showAddress = true; - break; - case 'c': - cleo = true; - break; - case 'd': - output_dir = optarg; // reference to an argv pointer - break; - case 'R': - break; - case 'e': - exclusions.add(optarg); - break; - case 'g': - break; - case 'r': // not implemented - roots.add(optarg); - if (!includes.IsEmpty()) { - errflg++; - } - break; - case 'i': - includes.add(optarg); - if (!roots.IsEmpty()) { - errflg++; - } - break; - case 'h': - break; - case 's': - stackDepth = atoi(optarg); - if (stackDepth < 2) { - stackDepth = 2; - } - break; - case 'x': - // --start - collect_start = atoi(optarg); - break; - case 'n': - // --end - collect_end = atoi(optarg); - break; - case 'l': - // --last - collect_last = true; - break; - case 'q': - break; - case 'v': - quiet = !quiet; - break; - case 't': - showThreads = true; - break; - case 'T': - showThreads = true; - onlyThread = atoi(optarg); - break; - } - } - if (errflg || ((argc - optind) < 2)) { - usageError(); - } - progFile = argv[optind++]; - logFileIndex = optind; - numLogFiles = argc - optind; - if (!quiet) - fprintf(stderr,"numlogfiles = %d\n",numLogFiles); -} - -static void* mapFile(int fd, u_int flags, off_t* sz) -{ - struct stat sb; - if (fstat(fd, &sb) < 0) { - perror("fstat"); - exit(-1); - } - void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0); - if (!base) { - perror("mmap"); - exit(-1); - } - *sz = sb.st_size; - return base; -} - -void leaky::LoadMap() -{ - malloc_map_entry mme; - char name[1000]; - - if (!loadMap) { - // all files use the same map - int fd = ::open(M_MAPFILE, O_RDONLY); - if (fd < 0) { - perror("open: " M_MAPFILE); - exit(-1); - } - for (;;) { - int nb = read(fd, &mme, sizeof(mme)); - if (nb != sizeof(mme)) break; - nb = read(fd, name, mme.nameLen); - if (nb != (int)mme.nameLen) break; - name[mme.nameLen] = 0; - if (!quiet) { - fprintf(stderr,"%s @ %lx\n", name, mme.address); - } - - LoadMapEntry* lme = new LoadMapEntry; - lme->address = mme.address; - lme->name = strdup(name); - lme->next = loadMap; - loadMap = lme; - } - close(fd); - } -} - -void leaky::open(char *logFile) -{ - int threadArray[100]; // should auto-expand - int last_thread = -1; - int numThreads = 0; - int section = -1; - bool collecting = false; - - LoadMap(); - - setupSymbols(progFile); - - // open up the log file - if (mappedLogFile) - ::close(mappedLogFile); - - mappedLogFile = ::open(logFile, O_RDONLY); - if (mappedLogFile < 0) { - perror("open"); - exit(-1); - } - off_t size; - firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size); - lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size); - - if (!collect_last || collect_start < 0) { - collecting = true; - } - - // First, restrict it to the capture sections specified (all, last, start/end) - // This loop walks through all the call stacks we recorded - for (malloc_log_entry* lep=firstLogEntry; - lep < lastLogEntry; - lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) { - - if (lep->flags & JP_FIRST_AFTER_PAUSE) { - section++; - if (collect_last) { - firstLogEntry = lep; - numThreads = 0; - collecting = true; - } - if (collect_start == section) { - collecting = true; - firstLogEntry = lep; - } - if (collect_end == section) { - collecting = false; - lastLogEntry = lep; - } - if (!quiet) - fprintf(stderr,"New section %d: first=%p, last=%p, collecting=%d\n", - section,(void*)firstLogEntry,(void*)lastLogEntry,collecting); - } - - // Capture thread info at the same time - - // Find all the threads captured - - // pthread/linux docs say the signal can be delivered to any thread in - // the process. In practice, it appears in Linux that it's always - // delivered to the thread that called setitimer(), and each thread can - // have a separate itimer. There's a support library for gprof that - // overlays pthread_create() to set timers in any threads you spawn. - if (showThreads && collecting) { - if (lep->thread != last_thread) - { - int i; - for (i=0; i<numThreads; i++) - { - if (lep->thread == threadArray[i]) - break; - } - if (i == numThreads && - i < (int) (sizeof(threadArray)/sizeof(threadArray[0]))) - { - threadArray[i] = lep->thread; - numThreads++; - if (!quiet) - fprintf(stderr,"new thread %d\n",lep->thread); - } - } - } - } - if (!quiet) - fprintf(stderr,"Done collecting: sections %d: first=%p, last=%p, numThreads=%d\n", - section,(void*)firstLogEntry,(void*)lastLogEntry,numThreads); - - if (!cleo) { - fprintf(outputfd,"<html><head><title>Jprof Profile Report</title></head><body>\n"); - fprintf(outputfd,"<h1><center>Jprof Profile Report</center></h1>\n"); - } - - if (showThreads) - { - fprintf(stderr,"Num threads %d\n",numThreads); - - if (!cleo) { - fprintf(outputfd,"<hr>Threads:<p><pre>\n"); - for (int i=0; i<numThreads; i++) - { - fprintf(outputfd," <a href=\"#thread_%d\">%d</a> ", - threadArray[i],threadArray[i]); - if ((i+1)%10 == 0) - fprintf(outputfd,"<br>\n"); - } - fprintf(outputfd,"</pre>"); - } - - for (int i=0; i<numThreads; i++) - { - if (!onlyThread || onlyThread == threadArray[i]) - analyze(threadArray[i]); - } - } - else - { - analyze(0); - } - - if (!cleo) - fprintf(outputfd,"</pre></body></html>\n"); -} - -//---------------------------------------------------------------------- - - -static int symbolOrder(void const* a, void const* b) -{ - Symbol const** ap = (Symbol const **)a; - Symbol const** bp = (Symbol const **)b; - return (*ap)->address == (*bp)->address ? 0 : - ((*ap)->address > (*bp)->address ? 1 : -1); -} - -void leaky::ReadSharedLibrarySymbols() -{ - LoadMapEntry* lme = loadMap; - while (nullptr != lme) { - ReadSymbols(lme->name, lme->address); - lme = lme->next; - } -} - -void leaky::setupSymbols(const char *fileName) -{ - if (usefulSymbols == 0) { - // only read once! - - // Read in symbols from the program - ReadSymbols(fileName, 0); - - // Read in symbols from the .so's - ReadSharedLibrarySymbols(); - - if (!quiet) { - fprintf(stderr,"A total of %d symbols were loaded\n", usefulSymbols); - } - - // Now sort them - qsort(externalSymbols, usefulSymbols, sizeof(Symbol *), symbolOrder); - lowestSymbolAddr = externalSymbols[0]->address; - highestSymbolAddr = externalSymbols[usefulSymbols-1]->address; - } -} - -// Binary search the table, looking for a symbol that covers this -// address. -int leaky::findSymbolIndex(u_long addr) -{ - u_int base = 0; - u_int limit = usefulSymbols - 1; - Symbol** end = &externalSymbols[limit]; - while (base <= limit) { - u_int midPoint = (base + limit)>>1; - Symbol** sp = &externalSymbols[midPoint]; - if (addr < (*sp)->address) { - if (midPoint == 0) { - return -1; - } - limit = midPoint - 1; - } else { - if (sp+1 < end) { - if (addr < (*(sp+1))->address) { - return midPoint; - } - } else { - return midPoint; - } - base = midPoint + 1; - } - } - return -1; -} - -Symbol* leaky::findSymbol(u_long addr) -{ - int idx = findSymbolIndex(addr); - - if(idx<0) { - return nullptr; - } else { - return externalSymbols[idx]; - } -} - -//---------------------------------------------------------------------- - -bool leaky::excluded(malloc_log_entry* lep) -{ - if (exclusions.IsEmpty()) { - return false; - } - - char** pcp = &lep->pcs[0]; - u_int n = lep->numpcs; - for (u_int i = 0; i < n; i++, pcp++) { - Symbol* sp = findSymbol((u_long) *pcp); - if (sp && exclusions.contains(sp->name)) { - return true; - } - } - return false; -} - -bool leaky::included(malloc_log_entry* lep) -{ - if (includes.IsEmpty()) { - return true; - } - - char** pcp = &lep->pcs[0]; - u_int n = lep->numpcs; - for (u_int i = 0; i < n; i++, pcp++) { - Symbol* sp = findSymbol((u_long) *pcp); - if (sp && includes.contains(sp->name)) { - return true; - } - } - return false; -} - -//---------------------------------------------------------------------- - -void leaky::displayStackTrace(FILE* out, malloc_log_entry* lep) -{ - char** pcp = &lep->pcs[0]; - u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth; - for (u_int i = 0; i < n; i++, pcp++) { - u_long addr = (u_long) *pcp; - Symbol* sp = findSymbol(addr); - if (sp) { - fputs(sp->name, out); - if (showAddress) { - fprintf(out, "[%p]", (char*)addr); - } - } - else { - fprintf(out, "<%p>", (char*)addr); - } - fputc(' ', out); - } - fputc('\n', out); -} - -void leaky::dumpEntryToLog(malloc_log_entry* lep) -{ - printf("%ld\t", lep->delTime); - printf(" --> "); - displayStackTrace(outputfd, lep); -} - -void leaky::generateReportHTML(FILE *fp, int *countArray, int count, int thread) -{ - fprintf(fp,"<center>"); - if (showThreads) - { - fprintf(fp,"<hr><A NAME=thread_%d><b>Thread: %d</b></A><p>", - thread,thread); - } - fprintf(fp,"<A href=#flat_%d>flat</A><b> | </b><A href=#hier_%d>hierarchical</A>", - thread,thread); - fprintf(fp,"</center><P><P><P>\n"); - - int totalTimerHits = count; - int *rankingTable = new int[usefulSymbols]; - - for(int cnt=usefulSymbols; --cnt>=0; rankingTable[cnt]=cnt); - - // Drat. I would use ::qsort() but I would need a global variable and my - // intro-pascal professor threatened to flunk anyone who used globals. - // She damaged me for life :-) (That was 1986. See how much influence - // she had. I don't remember her name but I always feel guilty about globals) - - // Shell Sort. 581130733 is the max 31 bit value of h = 3h+1 - int mx, i, h; - for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) { - if(h<mx) { - for(i = h-1; i<usefulSymbols; i++) { - int j, tmp=rankingTable[i], val = countArray[tmp]; - for(j = i; (j>=h) && (countArray[rankingTable[j-h]]<val); j-=h) { - rankingTable[j] = rankingTable[j-h]; - } - rankingTable[j] = tmp; - } - } - } - - // Ok, We are sorted now. Let's go through the table until we get to - // functions that were never called. Right now we don't do much inside - // this loop. Later we can get callers and callees into it like gprof - // does - fprintf(fp, - "<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", - thread); - fprintf(fp, "<pre>\n"); - fprintf(fp, "%6s %6s %4s %s\n", - "index", "Count", "Hits", "Function Name"); - - for(i=0; i<usefulSymbols && countArray[rankingTable[i]]>0; i++) { - Symbol **sp=&externalSymbols[rankingTable[i]]; - - (*sp)->cntP.printReport(fp, this, rankingTable[i], totalTimerHits); - - char *symname = htmlify((*sp)->name); - fprintf(fp, "%6d %6d (%3.1f%%)%s <a name=%d>%8d (%3.1f%%)</a>%s <b>%s</b>\n", - rankingTable[i], - (*sp)->timerHit, ((*sp)->timerHit*1000/totalTimerHits)/10.0, - ((*sp)->timerHit*1000/totalTimerHits)/10.0 >= 10.0 ? "" : " ", - rankingTable[i], countArray[rankingTable[i]], - (countArray[rankingTable[i]]*1000/totalTimerHits)/10.0, - (countArray[rankingTable[i]]*1000/totalTimerHits)/10.0 >= 10.0 ? "" : " ", - symname); - delete [] symname; - - (*sp)->cntC.printReport(fp, this, rankingTable[i], totalTimerHits); - - fprintf(fp, "<hr>\n"); - } - fprintf(fp,"</pre>\n"); - - // OK, Now we want to print the flat profile. To do this we resort on - // the hit count. - - // Cut-N-Paste Shell sort from above. The Ranking Table has already been - // populated, so we do not have to reinitialize it. - for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) { - if(h<mx) { - for(i = h-1; i<usefulSymbols; i++) { - int j, tmp=rankingTable[i], val = externalSymbols[tmp]->timerHit; - for(j = i; - (j>=h) && (externalSymbols[rankingTable[j-h]]->timerHit<val); j-=h) { - rankingTable[j] = rankingTable[j-h]; - } - rankingTable[j] = tmp; - } - } - } - - // Pre-count up total counter hits, to get a percentage. - // I wanted the total before walking the list, if this - // double-pass over externalSymbols gets slow we can - // do single-pass and print this out after the loop finishes. - totalTimerHits = 0; - for(i=0; - i<usefulSymbols && externalSymbols[rankingTable[i]]->timerHit>0; i++) { - Symbol **sp=&externalSymbols[rankingTable[i]]; - totalTimerHits += (*sp)->timerHit; - } - if (totalTimerHits == 0) - totalTimerHits = 1; - - if (totalTimerHits != count) - fprintf(stderr,"Hit count mismatch: count=%d; totalTimerHits=%d", - count,totalTimerHits); - - 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", - thread); - fprintf(fp, "<pre>\n"); - - fprintf(fp, "Total hit count: %d\n", totalTimerHits); - fprintf(fp, "Count %%Total Function Name\n"); - // Now loop for as long as we have timer hits - for(i=0; - i<usefulSymbols && externalSymbols[rankingTable[i]]->timerHit>0; i++) { - - Symbol **sp=&externalSymbols[rankingTable[i]]; - - char *symname = htmlify((*sp)->name); - fprintf(fp, "<a href=\"#%d\">%3d %-2.1f %s</a>\n", - rankingTable[i], (*sp)->timerHit, - ((float)(*sp)->timerHit/(float)totalTimerHits)*100.0, symname); - delete [] symname; - } -} - -void leaky::analyze(int thread) -{ - int *countArray = new int[usefulSymbols]; - int *flagArray = new int[usefulSymbols]; - - //Zero our function call counter - memset(countArray, 0, sizeof(countArray[0])*usefulSymbols); - - // reset hit counts - for(int i=0; i<usefulSymbols; i++) { - externalSymbols[i]->timerHit = 0; - externalSymbols[i]->regClear(); - } - - // The flag array is used to prevent counting symbols multiple times - // if functions are called recursively. In order to keep from having - // to zero it on each pass through the loop, we mark it with the value - // of stacks on each trip through the loop. This means we can determine - // if we have seen this symbol for this stack trace w/o having to reset - // from the prior stacktrace. - memset(flagArray, -1, sizeof(flagArray[0])*usefulSymbols); - - if (cleo) - fprintf(outputfd,"m-Start\n"); - - // This loop walks through all the call stacks we recorded - // --last, --start and --end can restrict it, as can excludes/includes - stacks = 0; - for(malloc_log_entry* lep=firstLogEntry; - lep < lastLogEntry; - lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) { - - if ((thread != 0 && lep->thread != thread) || - excluded(lep) || !included(lep)) - { - continue; - } - - ++stacks; // How many stack frames did we collect - - u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth; - char** pcp = &lep->pcs[n-1]; - int idx=-1, parrentIdx=-1; // Init idx incase n==0 - if (cleo) { - // This loop walks through every symbol in the call stack. By walking it - // backwards we know who called the function when we get there. - char type = 's'; - for (int i=n-1; i>=0; --i, --pcp) { - idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); - - if(idx>=0) { - // Skip over bogus __restore_rt frames that realtime profiling - // can introduce. - if (i > 0 && !strcmp(externalSymbols[idx]->name, "__restore_rt")) { - --pcp; - --i; - idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); - if (idx < 0) { - continue; - } - } - Symbol **sp=&externalSymbols[idx]; - char *symname = htmlify((*sp)->name); - fprintf(outputfd,"%c-%s\n",type,symname); - delete [] symname; - } - // else can't find symbol - ignore - type = 'c'; - } - } else { - // This loop walks through every symbol in the call stack. By walking it - // backwards we know who called the function when we get there. - for (int i=n-1; i>=0; --i, --pcp) { - idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); - - if(idx>=0) { - // Skip over bogus __restore_rt frames that realtime profiling - // can introduce. - if (i > 0 && !strcmp(externalSymbols[idx]->name, "__restore_rt")) { - --pcp; - --i; - idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); - if (idx < 0) { - continue; - } - } - - // If we have not seen this symbol before count it and mark it as seen - if(flagArray[idx]!=stacks && ((flagArray[idx]=stacks) || true)) { - ++countArray[idx]; - } - - // We know who we are and we know who our parrent is. Count this - if(parrentIdx>=0) { - externalSymbols[parrentIdx]->regChild(idx); - externalSymbols[idx]->regParrent(parrentIdx); - } - // inside if() so an unknown in the middle of a stack won't break - // the link! - parrentIdx=idx; - } - } - - // idx should be the function that we were in when we received the signal. - if(idx>=0) { - ++externalSymbols[idx]->timerHit; - } - - } - } - if (!cleo) - generateReportHTML(outputfd, countArray, stacks, thread); -} - -void FunctionCount::printReport(FILE *fp, leaky *lk, int parent, int total) -{ - const char *fmt = " <A href=\"#%d\">%8d (%3.1f%%)%s %s</A>%s\n"; - - int nmax, tmax=((~0U)>>1); - - do { - nmax=0; - for(int j=getSize(); --j>=0;) { - int cnt = getCount(j); - if(cnt==tmax) { - int idx = getIndex(j); - char *symname = htmlify(lk->indexToName(idx)); - fprintf(fp, fmt, idx, getCount(j), - getCount(j)*100.0/total, - getCount(j)*100.0/total >= 10.0 ? "" : " ", - symname, - parent == idx ? " (self)" : ""); - delete [] symname; - } else if(cnt<tmax && cnt>nmax) { - nmax=cnt; - } - } - } while((tmax=nmax)>0); -} |