From 552180e2033ce70889433d876bba97208c4a02a9 Mon Sep 17 00:00:00 2001 From: "blythe%netscape.com" Date: Wed, 9 Jan 2002 19:03:01 +0000 Subject: [PATCH] Fix Bug 115189 Trace-Malloc records line number information on platforms which have it. r=dp sr=alecf git-svn-id: svn://10.0.0.236/trunk@111713 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/tools/trace-malloc/bloatblame.c | 32 ++-- .../trace-malloc/lib/nsDebugHelpWin32.cpp | 8 +- .../tools/trace-malloc/lib/nsTraceMalloc.c | 149 +++++++++++++++++- .../tools/trace-malloc/lib/nsTraceMalloc.h | 12 +- mozilla/tools/trace-malloc/spacetrace.c | 111 ++++++++++--- mozilla/tools/trace-malloc/tmreader.c | 137 +++++++++++++--- mozilla/tools/trace-malloc/tmreader.h | 18 ++- mozilla/xpcom/base/nsDebugHelpWin32.cpp | 8 +- mozilla/xpcom/base/nsStackFrameWin.h | 9 +- mozilla/xpcom/base/nsTraceMalloc.c | 149 +++++++++++++++++- mozilla/xpcom/base/nsTraceMalloc.h | 12 +- mozilla/xpcom/base/nsTraceRefcnt.cpp | 37 ++++- mozilla/xpcom/base/nsTraceRefcntImpl.cpp | 37 ++++- 13 files changed, 639 insertions(+), 80 deletions(-) diff --git a/mozilla/tools/trace-malloc/bloatblame.c b/mozilla/tools/trace-malloc/bloatblame.c index 150c90939bb..7d24f009ee4 100644 --- a/mozilla/tools/trace-malloc/bloatblame.c +++ b/mozilla/tools/trace-malloc/bloatblame.c @@ -82,27 +82,29 @@ static void compute_callsite_totals(tmcallsite *site) static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp) { tmcallsite *parent; - tmgraphnode *meth, *pmeth, *comp, *pcomp, *lib, *plib; + tmgraphnode *comp, *pcomp, *lib, *plib; + tmmethodnode *meth, *pmeth; int old_meth_low, old_comp_low, old_lib_low, nkids; tmcallsite *kid; parent = site->parent; - meth = comp = lib = NULL; + comp = lib = NULL; + meth = NULL; if (parent) { meth = site->method; if (meth) { pmeth = parent->method; if (pmeth && pmeth != meth) { - if (!meth->low) { - meth->allocs.bytes.total += site->allocs.bytes.total; - meth->allocs.calls.total += site->allocs.calls.total; + if (!meth->graphnode.low) { + meth->graphnode.allocs.bytes.total += site->allocs.bytes.total; + meth->graphnode.allocs.calls.total += site->allocs.calls.total; } - if (!tmgraphnode_connect(pmeth, meth, site)) + if (!tmgraphnode_connect(&(pmeth->graphnode), &(meth->graphnode), site)) goto bad; - comp = meth->up; + comp = meth->graphnode.up; if (comp) { - pcomp = pmeth->up; + pcomp = pmeth->graphnode.up; if (pcomp && pcomp != comp) { if (!comp->low) { comp->allocs.bytes.total @@ -136,16 +138,16 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp comp->low = level; } } - old_meth_low = meth->low; + old_meth_low = meth->graphnode.low; if (!old_meth_low) - meth->low = level; + meth->graphnode.low = level; } } if (do_tree_dump) { fprintf(fp, "%c%*s%3d %3d %s %lu %ld\n", site->kids ? '+' : '-', level, "", level, kidnum, - meth ? tmgraphnode_name(meth) : "???", + meth ? tmmethodnode_name(meth) : "???", (unsigned long)site->allocs.bytes.direct, (long)site->allocs.bytes.total); } @@ -158,7 +160,7 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp if (meth) { if (!old_meth_low) - meth->low = 0; + meth->graphnode.low = 0; if (comp) { if (!old_comp_low) comp->low = 0; @@ -255,7 +257,7 @@ static int mean_size_compare(const void *p1, const void *p2) div1 = (double)node1->allocs.calls.direct; div2 = (double)node2->allocs.calls.direct; if (div1 == 0 || div2 == 0) - return div2 - div1; + return (int)(div2 - div1); key1 = (double)node1->allocs.bytes.direct / div1; key2 = (double)node2->allocs.bytes.direct / div2; if (key1 < key2) @@ -554,7 +556,7 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event) tmreader_callsite(tmr, event->u.stats.calltree_maxkids_parent); if (site && site->method) { fprintf(stdout, "

callsite with the most kids: %s

", - tmgraphnode_name(site->method)); + tmmethodnode_name(site->method)); } } @@ -568,7 +570,7 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event) while (site) { fprintf(stdout, "%s0x%08lX\n", - site->method ? tmgraphnode_name(site->method) : "???", + site->method ? tmmethodnode_name(site->method) : "???", (unsigned long) site->offset); site = site->parent; } diff --git a/mozilla/tools/trace-malloc/lib/nsDebugHelpWin32.cpp b/mozilla/tools/trace-malloc/lib/nsDebugHelpWin32.cpp index 64ff47fab14..4de14637b71 100644 --- a/mozilla/tools/trace-malloc/lib/nsDebugHelpWin32.cpp +++ b/mozilla/tools/trace-malloc/lib/nsDebugHelpWin32.cpp @@ -116,8 +116,12 @@ dhwEnsureSymInitialized() if (! gInitialized) { if (! dhwEnsureImageHlpInitialized()) return PR_FALSE; - // dhwSymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); - dhwSymSetOptions(SYMOPT_UNDNAME); + dhwSymSetOptions( +#if defined(NS_TRACE_MALLOC) + SYMOPT_LOAD_LINES | +#endif + SYMOPT_UNDNAME); + // dhwSymSetOptions(SYMOPT_UNDNAME); if (! dhwSymInitialize(::GetCurrentProcess(), NULL, TRUE)) return PR_FALSE; gInitialized = PR_TRUE; diff --git a/mozilla/tools/trace-malloc/lib/nsTraceMalloc.c b/mozilla/tools/trace-malloc/lib/nsTraceMalloc.c index 241ee43da97..160e952b120 100644 --- a/mozilla/tools/trace-malloc/lib/nsTraceMalloc.c +++ b/mozilla/tools/trace-malloc/lib/nsTraceMalloc.c @@ -392,6 +392,36 @@ static void log_string(logfile *fp, const char *str) log_byte(fp, '\0'); } +static void log_filename(logfile* aFP, const char* aFilename) +{ + int nameLen = strlen(aFilename); + + if (512 > nameLen) { + const char* skipTry1 = "mozilla"; + char* found = nsnull; + char* convert = nsnull; + char dup[512]; + + strcpy(dup, aFilename); + + found = strstr(dup, skipTry1); + if (nsnull == found) { + found = dup; + } + + for (convert = found; '\0' != *convert; convert++) { + if ('\\' == *convert) { + *convert = '/'; + } + } + + log_string(aFP, found); + } + else { + log_string(aFP, aFilename); + } +} + static void log_uint32(logfile *fp, uint32 ival) { if (ival < 0x80) { @@ -497,6 +527,7 @@ static uint32 library_serial_generator = 0; static uint32 method_serial_generator = 0; static uint32 callsite_serial_generator = 0; static uint32 tmstats_serial_generator = 0; +static uint32 filename_serial_generator = 0; /* Root of the tree of callsites, the sum of all (cycle-compressed) stacks. */ static callsite calltree_root = {0, 0, LFD_SET_STATIC_INITIALIZER, NULL, NULL, 0, NULL, NULL, NULL}; @@ -581,6 +612,9 @@ static PLHashAllocOps lfdset_hashallocops = { /* Table of library pathnames mapped to to logged 'L' record serial numbers. */ static PLHashTable *libraries = NULL; +/* Table of filename pathnames mapped to logged 'G' record serial numbers. */ +static PLHashTable *filenames = nsnull; + /* Table mapping method names to logged 'N' record serial numbers. */ static PLHashTable *methods = NULL; @@ -617,6 +651,10 @@ static callsite *calltree(int skip) PLHashEntry **hep, *he; lfdset_entry *le; char* noname = "noname"; + IMAGEHLP_LINE imagehelpLine; + const char* filename = nsnull; + uint32 linenumber = 0; + uint32 filename_serial = 0; imagehelp.SizeOfStruct = sizeof(imagehelp); framenum = 0; @@ -736,12 +774,22 @@ static callsite *calltree(int skip) * callsite info. XXX static syms are masked by nearest lower global * Load up the info for the dll. */ + memset(&imagehelpLine, 0, sizeof(imagehelpLine)); if (!SymGetModuleInfoEspecial(myProcess, frame[framenum].AddrPC.Offset, - &imagehelp)) { + &imagehelp, &imagehelpLine)) { library = noname; + filename = noname; + linenumber = 0; } else { library = imagehelp.ModuleName; + filename = imagehelpLine.FileName; + linenumber = imagehelpLine.LineNumber; + + if ('\0' == filename) { + filename = noname; + linenumber = 0; + } } /* Check whether we need to emit a library trace record. */ @@ -791,6 +839,49 @@ static callsite *calltree(int skip) } } + /* Check whether we need to emit a filename trace record. */ + filename_serial = 0; + if (filename) { + if (!filenames) { + filenames = PL_NewHashTable(100, PL_HashString, + PL_CompareStrings, PL_CompareValues, + &lfdset_hashallocops, NULL); + if (nsnull == filenames) { + tmstats.btmalloc_failures++; + return nsnull; + } + } + hash = PL_HashString(filename); + hep = PL_HashTableRawLookup(filenames, hash, filename); + he = *hep; + if (he) { + filename_serial = (uint32) he->value; + le = (lfdset_entry *) he; + if (LFD_TEST(fp->lfd, &le->lfdset)) { + /* We already logged an event on fp for this filename. */ + le = NULL; + } + } else { + filename = strdup(filename); + if (filename) { + filename_serial = ++filename_serial_generator; + he = PL_HashTableRawAdd(filenames, hep, hash, filename, + (void*) filename_serial); + } + if (!he) { + tmstats.btmalloc_failures++; + return NULL; + } + le = (lfdset_entry *) he; + } + if (le) { + /* Need to log an event to fp for this filename. */ + log_event1(fp, TM_EVENT_FILENAME, filename_serial); + log_filename(fp, filename); + LFD_SET(fp->lfd, &le->lfdset); + } + } + symbol = (PIMAGEHLP_SYMBOL) buf; symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); symbol->MaxNameLength = sizeof(buf) - sizeof(IMAGEHLP_SYMBOL); @@ -858,7 +949,7 @@ static callsite *calltree(int skip) le = (lfdset_entry *) he; } if (le) { - log_event2(fp, TM_EVENT_METHOD, method_serial, library_serial); + log_event4(fp, TM_EVENT_METHOD, method_serial, library_serial, filename_serial, linenumber); log_string(fp, method); LFD_SET(fp->lfd, &le->lfdset); } @@ -930,6 +1021,9 @@ static callsite *calltree(uint32 *bp) PLHashNumber hash; PLHashEntry **hep, *he; lfdset_entry *le; + uint32 filename_serial; + uint32 linenumber; + const char* filename; /* Reverse the stack frame list to avoid recursion. */ bpup = NULL; @@ -1005,6 +1099,13 @@ static callsite *calltree(uint32 *bp) return NULL; } + /* + * One day, if someone figures out how to get filename and line + * number info, this is the place to fill it all in. + */ + filename = "noname"; + linenumber = 0; + /* Check whether we need to emit a library trace record. */ library_serial = 0; library = info.dli_fname; @@ -1052,6 +1153,48 @@ static callsite *calltree(uint32 *bp) } } + /* Check whether we need to emit a filename trace record. */ + filename_serial = 0; + if (filename) { + if (!filenames) { + filenames = PL_NewHashTable(100, PL_HashString, + PL_CompareStrings, PL_CompareValues, + &lfdset_hashallocops, NULL); + if (nsnull == filenames) { + tmstats.btmalloc_failures++; + return nsnull; + } + } + hash = PL_HashString(filename); + hep = PL_HashTableRawLookup(filenames, hash, filename); + he = *hep; + if (he) { + filename_serial = (uint32) he->value; + le = (lfdset_entry *) he; + if (LFD_TEST(fp->lfd, &le->lfdset)) { + /* We already logged an event on fp for this filename. */ + le = NULL; + } + } else { + if (filename) { + filename_serial = ++filename_serial_generator; + he = PL_HashTableRawAdd(filenames, hep, hash, filename, + (void*) filename_serial); + } + if (!he) { + tmstats.btmalloc_failures++; + return NULL; + } + le = (lfdset_entry *) he; + } + if (le) { + /* Need to log an event to fp for this filename. */ + log_event1(fp, TM_EVENT_FILENAME, filename_serial); + log_filename(fp, filename); + LFD_SET(fp->lfd, &le->lfdset); + } + } + /* Now find the demangled method name and pc offset in it. */ symbol = info.dli_sname; offset = (char*)pc - (char*)info.dli_saddr; @@ -1110,7 +1253,7 @@ static callsite *calltree(uint32 *bp) le = (lfdset_entry *) he; } if (le) { - log_event2(fp, TM_EVENT_METHOD, method_serial, library_serial); + log_event4(fp, TM_EVENT_METHOD, method_serial, library_serial, filename_serial, linenumber); log_string(fp, method); LFD_SET(fp->lfd, &le->lfdset); } diff --git a/mozilla/tools/trace-malloc/lib/nsTraceMalloc.h b/mozilla/tools/trace-malloc/lib/nsTraceMalloc.h index 53f0e5482f1..c71b4378c82 100644 --- a/mozilla/tools/trace-malloc/lib/nsTraceMalloc.h +++ b/mozilla/tools/trace-malloc/lib/nsTraceMalloc.h @@ -45,7 +45,7 @@ PR_BEGIN_EXTERN_C * NS_TraceMallocStartup comment (below) for magic number differences in log * file structure. */ -#define NS_TRACE_MALLOC_MAGIC "XPCOM\nTMLog07\r\n\032" +#define NS_TRACE_MALLOC_MAGIC "XPCOM\nTMLog08\r\n\032" #define NS_TRACE_MALLOC_MAGIC_SIZE 16 /** @@ -122,9 +122,19 @@ typedef struct nsTMStats { * old address, old size * 'F' site serial, interval, address, free size * + * Event Operands (magic TMLog07) + * no one documented their changes. + * best of luck.... + * + * Event Operands (magic TMLog08) + * 'G' filename serial, source filename string. + * 'N' method serial, library serial, source filename serial, + * source file linenumber, demangled name string + * * See tools/trace-malloc/bloatblame.c for an example log-file reader. */ #define TM_EVENT_LIBRARY 'L' +#define TM_EVENT_FILENAME 'G' #define TM_EVENT_METHOD 'N' #define TM_EVENT_CALLSITE 'S' #define TM_EVENT_MALLOC 'M' diff --git a/mozilla/tools/trace-malloc/spacetrace.c b/mozilla/tools/trace-malloc/spacetrace.c index 316a163470b..e948b8a3985 100644 --- a/mozilla/tools/trace-malloc/spacetrace.c +++ b/mozilla/tools/trace-malloc/spacetrace.c @@ -1360,7 +1360,7 @@ int hasCallsiteMatch(tmcallsite* aCallsite, const char* aMatch, int aDirection) do { - methodName = tmgraphnode_name(aCallsite->method); + methodName = tmmethodnode_name(aCallsite->method); if(NULL != methodName && NULL != strstr(methodName, aMatch)) { /* @@ -2350,6 +2350,7 @@ void tmEventHandler(tmreader* aReader, tmevent* aEvent) case TM_EVENT_METHOD: case TM_EVENT_STATS: case TM_EVENT_TIMESTAMP: + case TM_EVENT_FILENAME: break; /* @@ -2470,8 +2471,13 @@ void tmEventHandler(tmreader* aReader, tmevent* aEvent) ** ** Output an HTML anchor, or just the text depending on the mode. */ -void htmlAnchor(const char* aHref, const char* aText) +void htmlAnchor(const char* aHref, const char* aText, const char* aTarget) { + if(NULL == aTarget || '\0' == aTarget[0]) + { + aTarget = "_st_content"; + } + if(NULL != aHref && '\0' != *aHref && NULL != aText && '\0' != *aText) { int anchorLive = 1; @@ -2518,7 +2524,7 @@ void htmlAnchor(const char* aHref, const char* aText) */ if(0 != anchorLive) { - PR_fprintf(globals.mRequest.mFD, "%s\n", aHref, aText); + PR_fprintf(globals.mRequest.mFD, "%s\n", aTarget, aHref, aText); } else { @@ -2549,7 +2555,7 @@ void htmlAllocationAnchor(STAllocation* aAllocation, const char* aText) */ PR_snprintf(buffer, sizeof(buffer), "allocation_%u.html", aAllocation->mRunIndex); - htmlAnchor(buffer, aText); + htmlAnchor(buffer, aText, NULL); } else { @@ -2557,6 +2563,39 @@ void htmlAllocationAnchor(STAllocation* aAllocation, const char* aText) } } +/* +** resolveSourceFile +** +** Easy way to get a readable/short name. +** NULL if not present, not resolvable. +*/ +const char* resolveSourceFile(tmmethodnode* aMethod) +{ + const char* retval = NULL; + + if(NULL != aMethod) + { + const char* methodSays = NULL; + + methodSays = aMethod->sourcefile; + + if(NULL != methodSays && '\0' != methodSays[0] && 0 != strcmp("noname", methodSays)) + { + retval = strrchr(methodSays, '/'); + if(NULL != retval) + { + retval++; + } + else + { + retval = methodSays; + } + } + } + + return retval; +} + /* ** htmlCallsiteAnchor ** @@ -2570,7 +2609,7 @@ void htmlCallsiteAnchor(tmcallsite* aCallsite, const char* aText, int aRealName) { if(NULL != aCallsite) { - char textBuf[256]; + char textBuf[512]; char hrefBuf[128]; tmcallsite* namesite = aCallsite; @@ -2611,23 +2650,44 @@ void htmlCallsiteAnchor(tmcallsite* aCallsite, const char* aText, int aRealName) if(NULL == aText || '\0' == *aText) { const char* methodName = NULL; + const char* sourceFile = NULL; if(NULL != namesite->method) { - methodName = tmgraphnode_name(namesite->method); + methodName = tmmethodnode_name(namesite->method); } else { methodName = "==NONAME=="; } - PR_snprintf(textBuf, sizeof(textBuf), "%s+%u(%u)", methodName, namesite->offset, (PRUint32)namesite->entry.key); + /* + ** Decide which format to use to identify the callsite. + ** If we can detect availability, hook up the filename with lxr information. + */ + sourceFile = resolveSourceFile(namesite->method); + if(NULL != sourceFile && 0 == strncmp("mozilla/", namesite->method->sourcefile, 8)) + { + char lxrHREFBuf[512]; + + PR_snprintf(lxrHREFBuf, sizeof(lxrHREFBuf), "(%s:%u)", namesite->method->sourcefile + 8, namesite->method->linenumber, sourceFile, namesite->method->linenumber); + PR_snprintf(textBuf, sizeof(textBuf), "%s%s", methodName, lxrHREFBuf); + } + else if(NULL != sourceFile) + { + PR_snprintf(textBuf, sizeof(textBuf), "%s(%s:%u)", methodName, sourceFile, namesite->method->linenumber); + } + else + { + PR_snprintf(textBuf, sizeof(textBuf), "%s+%u(%u)", methodName, namesite->offset, (PRUint32)namesite->entry.key); + } + aText = textBuf; } PR_snprintf(hrefBuf, sizeof(hrefBuf), "callsite_%u.html", (PRUint32)aCallsite->entry.key); - htmlAnchor(hrefBuf, aText); + htmlAnchor(hrefBuf, aText, NULL); } else { @@ -2651,14 +2711,14 @@ void htmlHeader(const char* aTitle) "
\n" , aTitle); - htmlAnchor("index.html", "[Index]"); - htmlAnchor("options.html", "[Options]"); + htmlAnchor("index.html", "[Index]", NULL); + htmlAnchor("options.html", "[Options]", NULL); /* ** This is a dubious feature at best. */ #if WANT_QUIT - htmlAnchor("quit.html", "[Quit]"); + htmlAnchor("quit.html", "[Quit]", NULL); #endif PR_fprintf(globals.mRequest.mFD, "
\n
\n"); @@ -3867,8 +3927,19 @@ int displayCallsiteDetails(tmcallsite* aCallsite) { STRun* sortedRun = NULL; STRun* thisRun = CALLSITE_RUN(aCallsite); + const char* sourceFile = NULL; - PR_fprintf(globals.mRequest.mFD, "%s+%u(%u) Callsite Details:

\n", tmgraphnode_name(aCallsite->method), aCallsite->offset, (PRUint32)aCallsite->entry.key); + sourceFile = resolveSourceFile(aCallsite->method); + if(NULL != sourceFile) + { + PR_fprintf(globals.mRequest.mFD, "%s", tmmethodnode_name(aCallsite->method)); + PR_fprintf(globals.mRequest.mFD, "(%s:%u)", aCallsite->method->sourcefile, aCallsite->method->linenumber, sourceFile, aCallsite->method->linenumber); + PR_fprintf(globals.mRequest.mFD, " Callsite Details:

\n"); + } + else + { + PR_fprintf(globals.mRequest.mFD, "%s+%u(%u) Callsite Details:

\n", tmmethodnode_name(aCallsite->method), aCallsite->offset, (PRUint32)aCallsite->entry.key); + } PR_fprintf(globals.mRequest.mFD, "\n"); PR_fprintf(globals.mRequest.mFD, "\n", thisRun->mStats.mSize); @@ -5022,16 +5093,16 @@ int displayIndex(void) PR_fprintf(globals.mRequest.mFD, "
Composite Byte Size:%u