зеркало из https://github.com/mozilla/gecko-dev.git
Fix Bug 115189
Trace-Malloc records line number information on platforms which have it. r=dp sr=alecf
This commit is contained in:
Родитель
c7f72d0cc6
Коммит
00c321d9fb
|
@ -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, "<p>callsite with the most kids: %s</p>",
|
||||
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,
|
||||
"<tr><td>%s</td><td>0x%08lX</td></tr>\n",
|
||||
site->method ? tmgraphnode_name(site->method) : "???",
|
||||
site->method ? tmmethodnode_name(site->method) : "???",
|
||||
(unsigned long) site->offset);
|
||||
site = site->parent;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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, "<a target=_content href=\"./%s\">%s</a>\n", aHref, aText);
|
||||
PR_fprintf(globals.mRequest.mFD, "<a target=\"%s\" href=\"./%s\">%s</a>\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), "<a href=\"http://lxr.mozilla.org/mozilla/source/%s#%u\" target=\"_st_lxr\">(%s:%u)</a>", namesite->method->sourcefile + 8, namesite->method->linenumber, sourceFile, namesite->method->linenumber);
|
||||
PR_snprintf(textBuf, sizeof(textBuf), "<b>%s</b>%s", methodName, lxrHREFBuf);
|
||||
}
|
||||
else if(NULL != sourceFile)
|
||||
{
|
||||
PR_snprintf(textBuf, sizeof(textBuf), "<b>%s</b>(%s:%u)", methodName, sourceFile, namesite->method->linenumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
PR_snprintf(textBuf, sizeof(textBuf), "<b>%s</b>+%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)
|
|||
"<div align=right>\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, "</div>\n<hr>\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:<p>\n", tmgraphnode_name(aCallsite->method), aCallsite->offset, (PRUint32)aCallsite->entry.key);
|
||||
sourceFile = resolveSourceFile(aCallsite->method);
|
||||
if(NULL != sourceFile)
|
||||
{
|
||||
PR_fprintf(globals.mRequest.mFD, "<b>%s</b>", tmmethodnode_name(aCallsite->method));
|
||||
PR_fprintf(globals.mRequest.mFD, "<a href=\"http://lxr.mozilla.org/mozilla/source/%s#%u\" target=\"_st_lxr\">(%s:%u)</a>", aCallsite->method->sourcefile, aCallsite->method->linenumber, sourceFile, aCallsite->method->linenumber);
|
||||
PR_fprintf(globals.mRequest.mFD, " Callsite Details:<p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
PR_fprintf(globals.mRequest.mFD, "<b>%s</b>+%u(%u) Callsite Details:<p>\n", tmmethodnode_name(aCallsite->method), aCallsite->offset, (PRUint32)aCallsite->entry.key);
|
||||
}
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "<table border=0>\n");
|
||||
PR_fprintf(globals.mRequest.mFD, "<tr><td>Composite Byte Size:</td><td align=right>%u</td></tr>\n", thisRun->mStats.mSize);
|
||||
|
@ -5022,16 +5093,16 @@ int displayIndex(void)
|
|||
PR_fprintf(globals.mRequest.mFD, "<ul>");
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>");
|
||||
htmlAnchor("root_callsites.html", "Root Callsites");
|
||||
htmlAnchor("root_callsites.html", "Root Callsites", NULL);
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>");
|
||||
htmlAnchor("top_callsites.html", "Top Callsites Report");
|
||||
htmlAnchor("top_callsites.html", "Top Callsites Report", NULL);
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>");
|
||||
htmlAnchor("top_allocations.html", "Top Allocations Report");
|
||||
htmlAnchor("top_allocations.html", "Top Allocations Report", NULL);
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>");
|
||||
htmlAnchor("memory_leaks.html", "Memory Leak Report");
|
||||
htmlAnchor("memory_leaks.html", "Memory Leak Report", NULL);
|
||||
|
||||
#if WANT_GRAPHS
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>Graphs");
|
||||
|
@ -5039,16 +5110,16 @@ int displayIndex(void)
|
|||
PR_fprintf(globals.mRequest.mFD, "<ul>");
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>");
|
||||
htmlAnchor("footprint_graph.html", "Footprint");
|
||||
htmlAnchor("footprint_graph.html", "Footprint", NULL);
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>");
|
||||
htmlAnchor("lifespan_graph.html", "Allocation Lifespans");
|
||||
htmlAnchor("lifespan_graph.html", "Allocation Lifespans", NULL);
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>");
|
||||
htmlAnchor("times_graph.html", "Allocation Times");
|
||||
htmlAnchor("times_graph.html", "Allocation Times", NULL);
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n<li>");
|
||||
htmlAnchor("weight_graph.html", "Allocation Weights");
|
||||
htmlAnchor("weight_graph.html", "Allocation Weights", NULL);
|
||||
|
||||
PR_fprintf(globals.mRequest.mFD, "\n</ul>\n");
|
||||
#endif /* WANT_GRAPHS */
|
||||
|
|
|
@ -147,9 +147,20 @@ static int get_tmevent(FILE *fp, tmevent *event)
|
|||
event->u.libname = s;
|
||||
break;
|
||||
|
||||
case TM_EVENT_FILENAME:
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
event->u.srcname = s;
|
||||
break;
|
||||
|
||||
case TM_EVENT_METHOD:
|
||||
if (!get_uint32(fp, &event->u.method.library))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.method.filename))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.method.linenumber))
|
||||
return 0;
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
|
@ -273,6 +284,11 @@ static void generic_freetable(void *pool, void *item)
|
|||
free(item);
|
||||
}
|
||||
|
||||
static PLHashEntry *filename_allocentry(void *pool, const void *key)
|
||||
{
|
||||
return calloc(1, sizeof(PLHashEntry));
|
||||
}
|
||||
|
||||
static PLHashEntry *callsite_allocentry(void *pool, const void *key)
|
||||
{
|
||||
return calloc(1, sizeof(tmcallsite));
|
||||
|
@ -295,6 +311,25 @@ static PLHashEntry *graphnode_allocentry(void *pool, const void *key)
|
|||
return &node->entry;
|
||||
}
|
||||
|
||||
static PLHashEntry *method_allocentry(void *pool, const void *key)
|
||||
{
|
||||
tmmethodnode *node = (tmmethodnode*) malloc(sizeof(tmmethodnode));
|
||||
if (!node)
|
||||
return NULL;
|
||||
node->graphnode.in = node->graphnode.out = NULL;
|
||||
node->graphnode.up = node->graphnode.down = node->graphnode.next = NULL;
|
||||
node->graphnode.low = 0;
|
||||
node->graphnode.allocs.bytes.direct = node->graphnode.allocs.bytes.total = 0;
|
||||
node->graphnode.allocs.calls.direct = node->graphnode.allocs.calls.total = 0;
|
||||
node->graphnode.frees.bytes.direct = node->graphnode.frees.bytes.total = 0;
|
||||
node->graphnode.frees.calls.direct = node->graphnode.frees.calls.total = 0;
|
||||
node->graphnode.sqsum = 0;
|
||||
node->graphnode.sort = -1;
|
||||
node->sourcefile = NULL;
|
||||
node->linenumber = 0;
|
||||
return &node->graphnode.entry;
|
||||
}
|
||||
|
||||
static void graphnode_freeentry(void *pool, PLHashEntry *he, PRUintn flag)
|
||||
{
|
||||
/* Always free the value, which points to a strdup'd string. */
|
||||
|
@ -316,6 +351,11 @@ static void component_freeentry(void *pool, PLHashEntry *he, PRUintn flag)
|
|||
}
|
||||
}
|
||||
|
||||
static PLHashAllocOps filename_hashallocops = {
|
||||
generic_alloctable, generic_freetable,
|
||||
filename_allocentry, graphnode_freeentry
|
||||
};
|
||||
|
||||
static PLHashAllocOps callsite_hashallocops = {
|
||||
generic_alloctable, generic_freetable,
|
||||
callsite_allocentry, graphnode_freeentry
|
||||
|
@ -326,6 +366,11 @@ static PLHashAllocOps graphnode_hashallocops = {
|
|||
graphnode_allocentry, graphnode_freeentry
|
||||
};
|
||||
|
||||
static PLHashAllocOps method_hashallocops = {
|
||||
generic_alloctable, generic_freetable,
|
||||
method_allocentry, graphnode_freeentry
|
||||
};
|
||||
|
||||
static PLHashAllocOps component_hashallocops = {
|
||||
generic_alloctable, generic_freetable,
|
||||
graphnode_allocentry, component_freeentry
|
||||
|
@ -349,11 +394,14 @@ tmreader *tmreader_new(const char *program, void *data)
|
|||
tmr->libraries = PL_NewHashTable(100, hash_serial, PL_CompareValues,
|
||||
PL_CompareStrings, &graphnode_hashallocops,
|
||||
NULL);
|
||||
tmr->filenames = PL_NewHashTable(100, hash_serial, PL_CompareValues,
|
||||
PL_CompareStrings, &filename_hashallocops,
|
||||
NULL);
|
||||
tmr->components = PL_NewHashTable(10000, PL_HashString, PL_CompareStrings,
|
||||
PL_CompareValues, &component_hashallocops,
|
||||
NULL);
|
||||
tmr->methods = PL_NewHashTable(10000, hash_serial, PL_CompareValues,
|
||||
PL_CompareStrings, &graphnode_hashallocops,
|
||||
PL_CompareStrings, &method_hashallocops,
|
||||
NULL);
|
||||
tmr->callsites = PL_NewHashTable(200000, hash_serial, PL_CompareValues,
|
||||
PL_CompareValues, &callsite_hashallocops,
|
||||
|
@ -361,7 +409,8 @@ tmreader *tmreader_new(const char *program, void *data)
|
|||
tmr->calltree_root.entry.value = (void*) strdup("root");
|
||||
|
||||
if (!tmr->libraries || !tmr->components || !tmr->methods ||
|
||||
!tmr->callsites || !tmr->calltree_root.entry.value) {
|
||||
!tmr->callsites || !tmr->calltree_root.entry.value ||
|
||||
!tmr->filenames) {
|
||||
tmreader_destroy(tmr);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -372,6 +421,8 @@ void tmreader_destroy(tmreader *tmr)
|
|||
{
|
||||
if (tmr->libraries)
|
||||
PL_HashTableDestroy(tmr->libraries);
|
||||
if (tmr->filenames)
|
||||
PL_HashTableDestroy(tmr->filenames);
|
||||
if (tmr->components)
|
||||
PL_HashTableDestroy(tmr->components);
|
||||
if (tmr->methods)
|
||||
|
@ -408,6 +459,7 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
strncmp(buf, magic, sizeof buf) != 0) {
|
||||
fprintf(stderr, "%s: bad magic string %s at start of %s.\n",
|
||||
tmr->program, buf, filename);
|
||||
fprintf(stderr, "either the data file is out of date,\nor your tools are out of date.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -444,12 +496,34 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
break;
|
||||
}
|
||||
|
||||
case TM_EVENT_METHOD: {
|
||||
case TM_EVENT_FILENAME: {
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry **hep, *he;
|
||||
|
||||
key = (const void*) event.serial;
|
||||
hash = hash_serial(key);
|
||||
hep = PL_HashTableRawLookup(tmr->filenames, hash, key);
|
||||
he = *hep;
|
||||
PR_ASSERT(!he);
|
||||
if (he) exit(2);
|
||||
|
||||
he = PL_HashTableRawAdd(tmr->filenames, hep, hash, key,
|
||||
event.u.srcname);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TM_EVENT_METHOD: {
|
||||
const void *key, *sourcekey;
|
||||
PLHashNumber hash, sourcehash;
|
||||
PLHashEntry **hep, *he, **sourcehep, *sourcehe;
|
||||
char *name, *head, *mark, save;
|
||||
tmgraphnode *meth, *comp, *lib;
|
||||
tmgraphnode *comp, *lib;
|
||||
tmmethodnode *meth;
|
||||
|
||||
key = (const void*) event.serial;
|
||||
hash = hash_serial(key);
|
||||
|
@ -464,7 +538,14 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
perror(tmr->program);
|
||||
return -1;
|
||||
}
|
||||
meth = (tmgraphnode*) he;
|
||||
meth = (tmmethodnode*) he;
|
||||
|
||||
meth->linenumber = event.u.method.linenumber;
|
||||
sourcekey = (const void*)event.u.method.filename;
|
||||
sourcehash = hash_serial(sourcekey);
|
||||
sourcehep = PL_HashTableRawLookup(tmr->filenames, sourcehash, sourcekey);
|
||||
sourcehe = *sourcehep;
|
||||
meth->sourcefile = filename_name(sourcehe);
|
||||
|
||||
head = name;
|
||||
mark = strchr(name, ':');
|
||||
|
@ -512,9 +593,9 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
}
|
||||
*mark = save;
|
||||
|
||||
meth->up = comp;
|
||||
meth->next = comp->down;
|
||||
comp->down = meth;
|
||||
meth->graphnode.up = comp;
|
||||
meth->graphnode.next = comp->down;
|
||||
comp->down = &(meth->graphnode);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -523,7 +604,7 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
PLHashNumber hash, mhash;
|
||||
PLHashEntry **hep, *he;
|
||||
tmcallsite *site, *parent;
|
||||
tmgraphnode *meth;
|
||||
tmmethodnode *meth;
|
||||
|
||||
key = (const void*) event.serial;
|
||||
hash = hash_serial(key);
|
||||
|
@ -558,7 +639,7 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
|
||||
mkey = (const void*) event.u.site.method;
|
||||
mhash = hash_serial(mkey);
|
||||
meth = (tmgraphnode*)
|
||||
meth = (tmmethodnode*)
|
||||
*PL_HashTableRawLookup(tmr->methods, mhash, mkey);
|
||||
site->method = meth;
|
||||
site->offset = event.u.site.offset;
|
||||
|
@ -575,7 +656,8 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
tmcallsite *site;
|
||||
uint32 size, oldsize;
|
||||
double delta, sqdelta, sqszdelta;
|
||||
tmgraphnode *meth, *comp, *lib;
|
||||
tmgraphnode *comp, *lib;
|
||||
tmmethodnode *meth;
|
||||
|
||||
site = tmreader_callsite(tmr, event.serial);
|
||||
if (!site) {
|
||||
|
@ -592,17 +674,17 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
site->allocs.calls.direct++;
|
||||
meth = site->method;
|
||||
if (meth) {
|
||||
meth->allocs.bytes.direct += delta;
|
||||
meth->graphnode.allocs.bytes.direct += delta;
|
||||
sqdelta = delta * delta;
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
sqszdelta = ((double)size * size)
|
||||
- ((double)oldsize * oldsize);
|
||||
meth->sqsum += sqszdelta;
|
||||
meth->graphnode.sqsum += sqszdelta;
|
||||
} else {
|
||||
meth->sqsum += sqdelta;
|
||||
meth->allocs.calls.direct++;
|
||||
meth->graphnode.sqsum += sqdelta;
|
||||
meth->graphnode.allocs.calls.direct++;
|
||||
}
|
||||
comp = meth->up;
|
||||
comp = meth->graphnode.up;
|
||||
if (comp) {
|
||||
comp->allocs.bytes.direct += delta;
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
|
@ -629,7 +711,8 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
case TM_EVENT_FREE: {
|
||||
tmcallsite *site;
|
||||
uint32 size;
|
||||
tmgraphnode *meth, *comp, *lib;
|
||||
tmgraphnode *comp, *lib;
|
||||
tmmethodnode *meth;
|
||||
|
||||
site = tmreader_callsite(tmr, event.serial);
|
||||
if (!site) {
|
||||
|
@ -642,9 +725,9 @@ int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
site->frees.calls.direct++;
|
||||
meth = site->method;
|
||||
if (meth) {
|
||||
meth->frees.bytes.direct += size;
|
||||
meth->frees.calls.direct++;
|
||||
comp = meth->up;
|
||||
meth->graphnode.frees.bytes.direct += size;
|
||||
meth->graphnode.frees.calls.direct++;
|
||||
comp = meth->graphnode.up;
|
||||
if (comp) {
|
||||
comp->frees.bytes.direct += size;
|
||||
comp->frees.calls.direct++;
|
||||
|
@ -678,6 +761,16 @@ tmgraphnode *tmreader_library(tmreader *tmr, uint32 serial)
|
|||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->libraries, hash, key);
|
||||
}
|
||||
|
||||
tmgraphnode *tmreader_filename(tmreader *tmr, uint32 serial)
|
||||
{
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->filenames, hash, key);
|
||||
}
|
||||
|
||||
tmgraphnode *tmreader_component(tmreader *tmr, const char *name)
|
||||
{
|
||||
PLHashNumber hash;
|
||||
|
@ -686,14 +779,14 @@ tmgraphnode *tmreader_component(tmreader *tmr, const char *name)
|
|||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->components, hash, name);
|
||||
}
|
||||
|
||||
tmgraphnode *tmreader_method(tmreader *tmr, uint32 serial)
|
||||
tmmethodnode *tmreader_method(tmreader *tmr, uint32 serial)
|
||||
{
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->methods, hash, key);
|
||||
return (tmmethodnode*) *PL_HashTableRawLookup(tmr->methods, hash, key);
|
||||
}
|
||||
|
||||
tmcallsite *tmreader_callsite(tmreader *tmr, uint32 serial)
|
||||
|
|
|
@ -49,14 +49,18 @@ typedef struct tmgraphlink tmgraphlink;
|
|||
typedef struct tmgraphedge tmgraphedge;
|
||||
typedef struct tmgraphnode tmgraphnode;
|
||||
typedef struct tmcallsite tmcallsite;
|
||||
typedef struct tmmethodnode tmmethodnode;
|
||||
|
||||
struct tmevent {
|
||||
char type;
|
||||
uint32 serial;
|
||||
union {
|
||||
char *libname;
|
||||
char *srcname;
|
||||
struct {
|
||||
uint32 library;
|
||||
uint32 filename;
|
||||
uint32 linenumber;
|
||||
char *name;
|
||||
} method;
|
||||
struct {
|
||||
|
@ -105,10 +109,18 @@ struct tmgraphnode {
|
|||
int sort; /* sorted index in node table, -1 if no table */
|
||||
};
|
||||
|
||||
struct tmmethodnode {
|
||||
tmgraphnode graphnode;
|
||||
char *sourcefile;
|
||||
uint32 linenumber;
|
||||
};
|
||||
|
||||
#define tmgraphnode_name(node) ((char*) (node)->entry.value)
|
||||
#define tmmethodnode_name(node) ((char*) (node)->graphnode.entry.value)
|
||||
|
||||
#define tmlibrary_serial(lib) ((uint32) (lib)->entry.key)
|
||||
#define tmcomponent_name(comp) ((const char*) (comp)->entry.key)
|
||||
#define filename_name(hashentry) ((char*)hashentry->value)
|
||||
|
||||
/* Half a graphedge, not including per-edge allocation stats. */
|
||||
struct tmgraphlink {
|
||||
|
@ -139,7 +151,7 @@ struct tmcallsite {
|
|||
tmcallsite *parent; /* calling site */
|
||||
tmcallsite *siblings; /* other sites reached from parent */
|
||||
tmcallsite *kids; /* sites reached from here */
|
||||
tmgraphnode *method; /* method node in tmr->methods graph */
|
||||
tmmethodnode *method; /* method node in tmr->methods graph */
|
||||
uint32 offset; /* pc offset from start of method */
|
||||
tmallcounts allocs;
|
||||
tmallcounts frees;
|
||||
|
@ -152,6 +164,7 @@ struct tmreader {
|
|||
const char *program;
|
||||
void *data;
|
||||
PLHashTable *libraries;
|
||||
PLHashTable *filenames;
|
||||
PLHashTable *components;
|
||||
PLHashTable *methods;
|
||||
PLHashTable *callsites;
|
||||
|
@ -174,8 +187,9 @@ extern int tmreader_eventloop(tmreader *tmr, const char *filename,
|
|||
|
||||
/* Map serial number or name to graphnode or callsite. */
|
||||
extern tmgraphnode *tmreader_library(tmreader *tmr, uint32 serial);
|
||||
extern tmgraphnode *tmreader_filename(tmreader *tmr, uint32 serial);
|
||||
extern tmgraphnode *tmreader_component(tmreader *tmr, const char *name);
|
||||
extern tmgraphnode *tmreader_method(tmreader *tmr, uint32 serial);
|
||||
extern tmmethodnode *tmreader_method(tmreader *tmr, uint32 serial);
|
||||
extern tmcallsite *tmreader_callsite(tmreader *tmr, uint32 serial);
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
//
|
||||
PR_BEGIN_EXTERN_C
|
||||
|
||||
typedef DWORD (__stdcall *SYMSETOPTIONSPROC)(DWORD);
|
||||
extern SYMSETOPTIONSPROC _SymSetOptions;
|
||||
|
||||
typedef BOOL (__stdcall *SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
|
||||
extern SYMINITIALIZEPROC _SymInitialize;
|
||||
|
||||
|
@ -86,6 +89,9 @@ extern SYMGETMODULEINFO _SymGetModuleInfo;
|
|||
typedef BOOL ( __stdcall *ENUMLOADEDMODULES)( HANDLE, PENUMLOADED_MODULES_CALLBACK, PVOID);
|
||||
extern ENUMLOADEDMODULES _EnumerateLoadedModules;
|
||||
|
||||
typedef BOOL (__stdcall *SYMGETLINEFROMADDRPROC)(HANDLE, DWORD, PDWORD, PIMAGEHLP_LINE);
|
||||
extern SYMGETLINEFROMADDRPROC _SymGetLineFromAddr;
|
||||
|
||||
PRBool EnsureSymInitialized();
|
||||
|
||||
/*
|
||||
|
@ -96,8 +102,9 @@ PRBool EnsureSymInitialized();
|
|||
* SymInitialize was called, and thus the module information
|
||||
* and symbol information is not available.
|
||||
* This code rectifies that problem.
|
||||
* Line information is optional.
|
||||
*/
|
||||
BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aModuleInfo);
|
||||
BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aModuleInfo, PIMAGEHLP_LINE aLineInfo);
|
||||
|
||||
PR_END_EXTERN_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);
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -862,6 +862,8 @@ static void InitTraceLog(void)
|
|||
|
||||
PR_BEGIN_EXTERN_C
|
||||
|
||||
SYMSETOPTIONSPROC _SymSetOptions;
|
||||
|
||||
SYMINITIALIZEPROC _SymInitialize;
|
||||
|
||||
SYMCLEANUPPROC _SymCleanup;
|
||||
|
@ -882,6 +884,8 @@ SYMGETMODULEINFO _SymGetModuleInfo;
|
|||
|
||||
ENUMLOADEDMODULES _EnumerateLoadedModules;
|
||||
|
||||
SYMGETLINEFROMADDRPROC _SymGetLineFromAddr;
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
static PRBool
|
||||
|
@ -893,6 +897,9 @@ EnsureImageHlpInitialized()
|
|||
HMODULE module = ::LoadLibrary("IMAGEHLP.DLL");
|
||||
if (!module) return PR_FALSE;
|
||||
|
||||
_SymSetOptions = (SYMSETOPTIONSPROC) ::GetProcAddress(module, "SymSetOptions");
|
||||
if (!_SymSetOptions) return PR_FALSE;
|
||||
|
||||
_SymInitialize = (SYMINITIALIZEPROC) ::GetProcAddress(module, "SymInitialize");
|
||||
if (!_SymInitialize) return PR_FALSE;
|
||||
|
||||
|
@ -923,6 +930,9 @@ EnsureImageHlpInitialized()
|
|||
_EnumerateLoadedModules = (ENUMLOADEDMODULES)GetProcAddress(module, "EnumerateLoadedModules");
|
||||
if (!_EnumerateLoadedModules) return PR_FALSE;
|
||||
|
||||
_SymGetLineFromAddr = (SYMGETLINEFROMADDRPROC)GetProcAddress(module, "SymGetLineFromAddr");
|
||||
if (!_SymGetLineFromAddr) return PR_FALSE;
|
||||
|
||||
gInitialized = PR_TRUE;
|
||||
}
|
||||
|
||||
|
@ -971,18 +981,25 @@ static BOOL CALLBACK callbackEspecial(LPSTR aModuleName, ULONG aModuleBase, ULON
|
|||
* and symbol information is not available.
|
||||
* This code rectifies that problem.
|
||||
*/
|
||||
BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aModuleInfo)
|
||||
BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aModuleInfo, PIMAGEHLP_LINE aLineInfo)
|
||||
{
|
||||
BOOL retval = FALSE;
|
||||
|
||||
/*
|
||||
* Init the vars if we have em.
|
||||
*/
|
||||
aModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE);
|
||||
if (nsnull != aLineInfo) {
|
||||
aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give it a go.
|
||||
* It may already be loaded.
|
||||
*/
|
||||
retval = _SymGetModuleInfo(aProcess, aAddr, aModuleInfo);
|
||||
|
||||
if(FALSE == retval)
|
||||
{
|
||||
if (FALSE == retval) {
|
||||
BOOL enumRes = FALSE;
|
||||
|
||||
/*
|
||||
|
@ -1000,6 +1017,17 @@ BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aMo
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we got module info, we may attempt line info as well.
|
||||
* We will not report failure if this does not work.
|
||||
*/
|
||||
if (FALSE != retval && nsnull != aLineInfo && nsnull != _SymGetLineFromAddr) {
|
||||
DWORD displacement = 0;
|
||||
BOOL lineRes = FALSE;
|
||||
|
||||
lineRes = _SymGetLineFromAddr(aProcess, aAddr, &displacement, aLineInfo);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -1011,6 +1039,7 @@ EnsureSymInitialized()
|
|||
if (! gInitialized) {
|
||||
if (! EnsureImageHlpInitialized())
|
||||
return PR_FALSE;
|
||||
_SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
|
||||
gInitialized = _SymInitialize(GetCurrentProcess(), 0, TRUE);
|
||||
}
|
||||
return gInitialized;
|
||||
|
@ -1100,7 +1129,7 @@ nsTraceRefcnt::WalkTheStack(FILE* aStream)
|
|||
IMAGEHLP_MODULE modInfo;
|
||||
modInfo.SizeOfStruct = sizeof(modInfo);
|
||||
BOOL modInfoRes = TRUE;
|
||||
modInfoRes = SymGetModuleInfoEspecial(myProcess, frame.AddrPC.Offset, &modInfo);
|
||||
modInfoRes = SymGetModuleInfoEspecial(myProcess, frame.AddrPC.Offset, &modInfo, nsnull);
|
||||
|
||||
char buf[sizeof(IMAGEHLP_SYMBOL) + 512];
|
||||
PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL) buf;
|
||||
|
|
|
@ -862,6 +862,8 @@ static void InitTraceLog(void)
|
|||
|
||||
PR_BEGIN_EXTERN_C
|
||||
|
||||
SYMSETOPTIONSPROC _SymSetOptions;
|
||||
|
||||
SYMINITIALIZEPROC _SymInitialize;
|
||||
|
||||
SYMCLEANUPPROC _SymCleanup;
|
||||
|
@ -882,6 +884,8 @@ SYMGETMODULEINFO _SymGetModuleInfo;
|
|||
|
||||
ENUMLOADEDMODULES _EnumerateLoadedModules;
|
||||
|
||||
SYMGETLINEFROMADDRPROC _SymGetLineFromAddr;
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
static PRBool
|
||||
|
@ -893,6 +897,9 @@ EnsureImageHlpInitialized()
|
|||
HMODULE module = ::LoadLibrary("IMAGEHLP.DLL");
|
||||
if (!module) return PR_FALSE;
|
||||
|
||||
_SymSetOptions = (SYMSETOPTIONSPROC) ::GetProcAddress(module, "SymSetOptions");
|
||||
if (!_SymSetOptions) return PR_FALSE;
|
||||
|
||||
_SymInitialize = (SYMINITIALIZEPROC) ::GetProcAddress(module, "SymInitialize");
|
||||
if (!_SymInitialize) return PR_FALSE;
|
||||
|
||||
|
@ -923,6 +930,9 @@ EnsureImageHlpInitialized()
|
|||
_EnumerateLoadedModules = (ENUMLOADEDMODULES)GetProcAddress(module, "EnumerateLoadedModules");
|
||||
if (!_EnumerateLoadedModules) return PR_FALSE;
|
||||
|
||||
_SymGetLineFromAddr = (SYMGETLINEFROMADDRPROC)GetProcAddress(module, "SymGetLineFromAddr");
|
||||
if (!_SymGetLineFromAddr) return PR_FALSE;
|
||||
|
||||
gInitialized = PR_TRUE;
|
||||
}
|
||||
|
||||
|
@ -971,18 +981,25 @@ static BOOL CALLBACK callbackEspecial(LPSTR aModuleName, ULONG aModuleBase, ULON
|
|||
* and symbol information is not available.
|
||||
* This code rectifies that problem.
|
||||
*/
|
||||
BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aModuleInfo)
|
||||
BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aModuleInfo, PIMAGEHLP_LINE aLineInfo)
|
||||
{
|
||||
BOOL retval = FALSE;
|
||||
|
||||
/*
|
||||
* Init the vars if we have em.
|
||||
*/
|
||||
aModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE);
|
||||
if (nsnull != aLineInfo) {
|
||||
aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give it a go.
|
||||
* It may already be loaded.
|
||||
*/
|
||||
retval = _SymGetModuleInfo(aProcess, aAddr, aModuleInfo);
|
||||
|
||||
if(FALSE == retval)
|
||||
{
|
||||
if (FALSE == retval) {
|
||||
BOOL enumRes = FALSE;
|
||||
|
||||
/*
|
||||
|
@ -1000,6 +1017,17 @@ BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aMo
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we got module info, we may attempt line info as well.
|
||||
* We will not report failure if this does not work.
|
||||
*/
|
||||
if (FALSE != retval && nsnull != aLineInfo && nsnull != _SymGetLineFromAddr) {
|
||||
DWORD displacement = 0;
|
||||
BOOL lineRes = FALSE;
|
||||
|
||||
lineRes = _SymGetLineFromAddr(aProcess, aAddr, &displacement, aLineInfo);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -1011,6 +1039,7 @@ EnsureSymInitialized()
|
|||
if (! gInitialized) {
|
||||
if (! EnsureImageHlpInitialized())
|
||||
return PR_FALSE;
|
||||
_SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
|
||||
gInitialized = _SymInitialize(GetCurrentProcess(), 0, TRUE);
|
||||
}
|
||||
return gInitialized;
|
||||
|
@ -1100,7 +1129,7 @@ nsTraceRefcnt::WalkTheStack(FILE* aStream)
|
|||
IMAGEHLP_MODULE modInfo;
|
||||
modInfo.SizeOfStruct = sizeof(modInfo);
|
||||
BOOL modInfoRes = TRUE;
|
||||
modInfoRes = SymGetModuleInfoEspecial(myProcess, frame.AddrPC.Offset, &modInfo);
|
||||
modInfoRes = SymGetModuleInfoEspecial(myProcess, frame.AddrPC.Offset, &modInfo, nsnull);
|
||||
|
||||
char buf[sizeof(IMAGEHLP_SYMBOL) + 512];
|
||||
PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL) buf;
|
||||
|
|
Загрузка…
Ссылка в новой задаче