mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Reworked tree support
This commit is contained in:
parent
fd63be5bc1
commit
d44f8afe43
@ -41,6 +41,15 @@ LIBS = \
|
||||
-liberty \
|
||||
$(NULL)
|
||||
|
||||
RESOURCES = \
|
||||
leaky.css \
|
||||
leaky.js \
|
||||
open.gif \
|
||||
open-over.gif \
|
||||
close.gif \
|
||||
close-over.gif \
|
||||
$(NULL)
|
||||
|
||||
# Stuff to build the library used to wrap malloc
|
||||
LIBMALLOC_CPPSRCS = libmalloc.cpp
|
||||
LIBMALLOC_OBJECTS = $(LIBMALLOC_CPPSRCS:.cpp=.o)
|
||||
@ -53,10 +62,27 @@ LIBPRELOAD = libpreload.so
|
||||
|
||||
# include $(topsrcdir)/config/config.mk
|
||||
|
||||
SHARED_LIBRARY = $(LIBMALLOC) $(LIBPRELOAD)
|
||||
OTHER_LIBRARIES = $(LIBMALLOC) $(LIBPRELOAD)
|
||||
TARGETS := $(PROGRAM) $(SIMPLE_PROGRAMS) $(OTHER_LIBRARIES)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
# Make sure all depends on files that rules.mk doesn't know about.
|
||||
all:: $(OTHER_LIBRARIES)
|
||||
|
||||
# Make sure install depends on files that rules.mk doesn't know about.
|
||||
install:: $(OTHER_LIBRARIES)
|
||||
|
||||
# Make sure libs depends on files that rules.mk doesn't know about.
|
||||
libs:: $(OTHER_LIBRARIES)
|
||||
|
||||
clobber::
|
||||
rm -f $(LIBMALLOC_OBJECTS) $(LIBPRELOAD_OBJECTS)
|
||||
rm -f $(LIBMALLOC) $(LIBPRELOAD)
|
||||
|
||||
clean::
|
||||
rm -f $(LIBMALLOC_OBJECTS) $(LIBPRELOAD_OBJECTS)
|
||||
|
||||
$(LIBMALLOC): $(LIBMALLOC_OBJECTS)
|
||||
rm -f $@
|
||||
$(MKSHLIB) -o $@ $(LIBMALLOC_OBJECTS)
|
||||
@ -68,3 +94,7 @@ test:
|
||||
@echo LIBMALLOC = $(LIBMALLOC)
|
||||
@echo LIBPRELOAD = $(LIBPRELOAD)
|
||||
@echo TARGETS = $(TARGETS)
|
||||
|
||||
install::
|
||||
$(INSTALL) -m 555 $(OTHER_LIBRARIES) $(DIST)/lib
|
||||
$(INSTALL) $(RESOURCES) $(DIST)/bin/res/leaky
|
||||
|
@ -20,14 +20,30 @@
|
||||
// A simple test program that dumps out the loaded shared
|
||||
// libraries. This is essential for leaky to work properly when shared
|
||||
// libraries are used.
|
||||
static void ShowLibs(struct r_debug* rd)
|
||||
{
|
||||
link_map* map = rd->r_map;
|
||||
while (NULL != map) {
|
||||
printf("addr=%08x name=%s prev=%p next=%p\n", map->l_addr, map->l_name,
|
||||
map->l_prev, map->l_next);
|
||||
map = map->l_next;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
void* h = dlopen("/usr/X11R6/lib/libX11.so", RTLD_LAZY);
|
||||
#ifdef linux
|
||||
link_map* map = _r_debug.r_map;
|
||||
while (NULL != map) {
|
||||
printf("addr=%08x name=%s\n", map->l_addr, map->l_name);
|
||||
map = map->l_next;
|
||||
printf("Direct r_debug libs:\n");
|
||||
ShowLibs(&_r_debug);
|
||||
|
||||
printf("_DYNAMICE r_debug libs:\n");
|
||||
ElfW(Dyn)* dp;
|
||||
for (dp = _DYNAMIC; dp->d_tag != DT_NULL; dp++) {
|
||||
if (dp->d_tag == DT_DEBUG) {
|
||||
struct r_debug* rd = (struct r_debug*) dp->d_un.d_ptr;
|
||||
ShowLibs(rd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
@ -38,10 +38,70 @@ void s4()
|
||||
cp = cp;
|
||||
}
|
||||
|
||||
// Test that mutually recrusive methods don't foul up the graph output
|
||||
void s6(int recurse);
|
||||
|
||||
void s5(int recurse)
|
||||
{
|
||||
malloc(100);
|
||||
if (recurse > 0) {
|
||||
s6(recurse - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void s6(int recurse)
|
||||
{
|
||||
malloc(100);
|
||||
if (recurse > 0) {
|
||||
s5(recurse - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that two pathways through the same node don't cause replicated
|
||||
// descdendants (A -> B -> C, X -> B -> D shouldn't produce a graph
|
||||
// that shows A -> B -> D!)
|
||||
|
||||
void C()
|
||||
{
|
||||
malloc(10);
|
||||
}
|
||||
|
||||
void D()
|
||||
{
|
||||
malloc(10);
|
||||
}
|
||||
|
||||
void B(int way)
|
||||
{
|
||||
malloc(10);
|
||||
if (way) {
|
||||
C();
|
||||
C();
|
||||
C();
|
||||
} else {
|
||||
D();
|
||||
}
|
||||
}
|
||||
|
||||
void A()
|
||||
{
|
||||
malloc(10);
|
||||
B(1);
|
||||
}
|
||||
|
||||
void X()
|
||||
{
|
||||
malloc(10);
|
||||
B(0);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
s1(1, 2);
|
||||
s2();
|
||||
s3();
|
||||
s4();
|
||||
s5(10);
|
||||
A();
|
||||
X();
|
||||
}
|
||||
|
@ -31,23 +31,23 @@ void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress)
|
||||
bfd_init ();
|
||||
}
|
||||
|
||||
bfd* bfd = bfd_openr(aFileName, NULL);
|
||||
if (NULL == bfd) {
|
||||
bfd* lib = bfd_openr(aFileName, NULL);
|
||||
if (NULL == lib) {
|
||||
return;
|
||||
}
|
||||
char **matching;
|
||||
if (!bfd_check_format_matches(bfd, bfd_object, &matching)) {
|
||||
bfd_close(bfd);
|
||||
if (!bfd_check_format_matches(lib, bfd_object, &matching)) {
|
||||
bfd_close(lib);
|
||||
return;
|
||||
}
|
||||
|
||||
asymbol* store;
|
||||
store = bfd_make_empty_symbol(bfd);
|
||||
store = bfd_make_empty_symbol(lib);
|
||||
|
||||
// read mini symbols
|
||||
PTR minisyms;
|
||||
unsigned int size;
|
||||
long symcount = bfd_read_minisymbols(bfd, kDynamic, &minisyms, &size);
|
||||
long symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size);
|
||||
|
||||
int initialSymbols = usefulSymbols;
|
||||
if (NULL == externalSymbols) {
|
||||
@ -55,17 +55,17 @@ void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress)
|
||||
numExternalSymbols = 10000;
|
||||
}
|
||||
Symbol* sp = externalSymbols + usefulSymbols;
|
||||
Symbol* last = externalSymbols + numExternalSymbols;
|
||||
Symbol* lastSymbol = externalSymbols + numExternalSymbols;
|
||||
|
||||
// Scan symbols
|
||||
bfd_byte* from = (bfd_byte *) minisyms;
|
||||
bfd_byte* fromend = from + symcount * size;
|
||||
for (; from < fromend; from += size) {
|
||||
asymbol *sym;
|
||||
sym = bfd_minisymbol_to_symbol(bfd, kDynamic, (const PTR) from, store);
|
||||
sym = bfd_minisymbol_to_symbol(lib, kDynamic, (const PTR) from, store);
|
||||
|
||||
symbol_info syminfo;
|
||||
bfd_get_symbol_info (bfd, sym, &syminfo);
|
||||
bfd_get_symbol_info (lib, sym, &syminfo);
|
||||
|
||||
// if ((syminfo.type == 'T') || (syminfo.type == 't')) {
|
||||
const char* nm = bfd_asymbol_name(sym);
|
||||
@ -74,11 +74,11 @@ void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress)
|
||||
// sp->name = dnm ? dnm : strdup(nm);
|
||||
sp->Init(nm, syminfo.value + aBaseAddress);
|
||||
sp++;
|
||||
if (sp >= last) {
|
||||
if (sp >= lastSymbol) {
|
||||
long n = numExternalSymbols + 10000;
|
||||
externalSymbols = (Symbol*)
|
||||
realloc(externalSymbols, (size_t) (sizeof(Symbol) * n));
|
||||
last = externalSymbols + n;
|
||||
lastSymbol = externalSymbols + n;
|
||||
sp = externalSymbols + numExternalSymbols;
|
||||
numExternalSymbols = n;
|
||||
}
|
||||
@ -87,7 +87,7 @@ void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress)
|
||||
}
|
||||
|
||||
|
||||
bfd_close(bfd);
|
||||
bfd_close(lib);
|
||||
|
||||
int interesting = sp - externalSymbols;
|
||||
if (!quiet) {
|
||||
|
@ -12,7 +12,7 @@
|
||||
#ifndef config_h___
|
||||
#define config_h___
|
||||
|
||||
#define MAX_STACK_CRAWL 100
|
||||
#define MAX_STACK_CRAWL 200
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
|
@ -54,14 +54,14 @@ leaky::leaky()
|
||||
sortByFrequency = FALSE;
|
||||
dumpAll = FALSE;
|
||||
dumpGraph = FALSE;
|
||||
dumpXML = FALSE;
|
||||
dumpHTML = FALSE;
|
||||
quiet = FALSE;
|
||||
showAll = FALSE;
|
||||
showAddress = FALSE;
|
||||
stackDepth = 100000;
|
||||
|
||||
fd = -1;
|
||||
base = last = 0;
|
||||
mappedLogFile = -1;
|
||||
firstLogEntry = lastLogEntry = 0;
|
||||
buckets = DefaultBuckets;
|
||||
dict = 0;
|
||||
|
||||
@ -148,7 +148,7 @@ void leaky::initialize(int argc, char** argv)
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
dumpXML = TRUE;
|
||||
dumpHTML = TRUE;
|
||||
break;
|
||||
case 'q':
|
||||
quiet = TRUE;
|
||||
@ -216,14 +216,14 @@ void leaky::open()
|
||||
setupSymbols(progFile);
|
||||
|
||||
// open up the log file
|
||||
fd = ::open(logFile, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
mappedLogFile = ::open(logFile, O_RDONLY);
|
||||
if (mappedLogFile < 0) {
|
||||
perror("open");
|
||||
exit(-1);
|
||||
}
|
||||
off_t size;
|
||||
base = (malloc_log_entry*) mapFile(fd, PROT_READ, &size);
|
||||
last = (malloc_log_entry*)((char*)base + size);
|
||||
firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size);
|
||||
lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size);
|
||||
|
||||
analyze();
|
||||
|
||||
@ -320,7 +320,7 @@ int leaky::excluded(malloc_log_entry* lep)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void leaky::displayStackTrace(malloc_log_entry* lep)
|
||||
void leaky::displayStackTrace(FILE* out, malloc_log_entry* lep)
|
||||
{
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
|
||||
@ -337,13 +337,13 @@ void leaky::displayStackTrace(malloc_log_entry* lep)
|
||||
symbolName = buf;
|
||||
}
|
||||
if (showAddress) {
|
||||
printf("%s[%p] ", symbolName, *pcp);
|
||||
fprintf(out, "%s[%p] ", symbolName, *pcp);
|
||||
}
|
||||
else {
|
||||
printf("%s ", symbolName);
|
||||
fprintf(out, "%s ", symbolName);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
char* typeFromLog[] = {
|
||||
@ -362,14 +362,14 @@ void leaky::dumpEntryToLog(malloc_log_entry* lep)
|
||||
typeFromLog[lep->type],
|
||||
lep->address, lep->size, lep->oldaddress,
|
||||
lep->numpcs);
|
||||
displayStackTrace(lep);
|
||||
displayStackTrace(stdout, lep);
|
||||
}
|
||||
|
||||
void leaky::dumpLog()
|
||||
{
|
||||
if (showAll) {
|
||||
malloc_log_entry* lep = base;
|
||||
while (lep < last) {
|
||||
malloc_log_entry* lep = firstLogEntry;
|
||||
while (lep < lastLogEntry) {
|
||||
dumpEntryToLog(lep);
|
||||
lep = (malloc_log_entry*) &lep->pcs[lep->numpcs];
|
||||
}
|
||||
@ -393,7 +393,7 @@ void leaky::insertAddress(u_long address, malloc_log_entry* lep)
|
||||
assert(*lepp);
|
||||
if (!quiet) {
|
||||
printf("Address %lx allocated twice\n", address);
|
||||
displayStackTrace(lep);
|
||||
displayStackTrace(stdout, lep);
|
||||
}
|
||||
errors++;
|
||||
} else {
|
||||
@ -407,7 +407,7 @@ void leaky::removeAddress(u_long address, malloc_log_entry* lep)
|
||||
if (!lepp) {
|
||||
if (!quiet) {
|
||||
printf("Free of unallocated %lx\n", address);
|
||||
displayStackTrace(lep);
|
||||
displayStackTrace(stdout, lep);
|
||||
}
|
||||
errors++;
|
||||
} else {
|
||||
@ -417,8 +417,8 @@ void leaky::removeAddress(u_long address, malloc_log_entry* lep)
|
||||
|
||||
void leaky::analyze()
|
||||
{
|
||||
malloc_log_entry* lep = base;
|
||||
while (lep < last) {
|
||||
malloc_log_entry* lep = firstLogEntry;
|
||||
while (lep < lastLogEntry) {
|
||||
switch (lep->type) {
|
||||
case malloc_log_malloc:
|
||||
case malloc_log_new:
|
||||
@ -473,28 +473,39 @@ void leaky::buildLeakGraph()
|
||||
malloc_log_entry* lep;
|
||||
dict->rewind();
|
||||
while (NULL != (lep = dict->next())) {
|
||||
Symbol* prevSymbol = NULL;
|
||||
char** basepcp = &lep->pcs[0];
|
||||
char** pcp = &lep->pcs[lep->numpcs - 1];
|
||||
|
||||
// For each pc in the leak
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
|
||||
for (u_int i = 0; i < n; i++, pcp++) {
|
||||
Symbol* currentSymbol = findSymbol((u_long) *pcp);
|
||||
if (currentSymbol) {
|
||||
currentSymbol->leaker = true;
|
||||
currentSymbol->calls++;
|
||||
if (i == 0) {
|
||||
currentSymbol->bytesDirectlyLeaked += lep->size;
|
||||
}
|
||||
else {
|
||||
currentSymbol->childBytesLeaked += lep->size;
|
||||
}
|
||||
if (prevSymbol) {
|
||||
currentSymbol->AddChild(prevSymbol);
|
||||
prevSymbol->AddParent(currentSymbol);
|
||||
}
|
||||
// Find root for this allocation
|
||||
Symbol* sym = findSymbol((u_long) *pcp);
|
||||
TreeNode* node = sym->root;
|
||||
if (!node) {
|
||||
displayStackTrace(stderr, lep);
|
||||
sym->root = node = new TreeNode(sym);
|
||||
}
|
||||
pcp--;
|
||||
|
||||
// Build tree underneath the root
|
||||
for (; pcp >= basepcp; pcp--) {
|
||||
// Share nodes in the tree until there is a divergence
|
||||
sym = findSymbol((u_long) *pcp);
|
||||
if (!sym) {
|
||||
break;
|
||||
}
|
||||
prevSymbol = currentSymbol;
|
||||
TreeNode* nextNode = node->GetDirectDescendant(sym);
|
||||
if (!nextNode) {
|
||||
// Make a new node at the point of divergence
|
||||
nextNode = node->AddDescendant(sym);
|
||||
}
|
||||
|
||||
if (pcp == basepcp) {
|
||||
nextNode->bytesLeaked += lep->size;
|
||||
}
|
||||
else {
|
||||
node->descendantBytesLeaked += lep->size;
|
||||
}
|
||||
|
||||
node = nextNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -502,7 +513,7 @@ void leaky::buildLeakGraph()
|
||||
Symbol* leaky::findLeakGraphRoot(Symbol* aStart, Symbol* aEnd)
|
||||
{
|
||||
while (aStart < aEnd) {
|
||||
if (aStart->leaker && !aStart->parents) {
|
||||
if (aStart->root) {
|
||||
return aStart;
|
||||
}
|
||||
aStart++;
|
||||
@ -512,183 +523,118 @@ Symbol* leaky::findLeakGraphRoot(Symbol* aStart, Symbol* aEnd)
|
||||
|
||||
void leaky::dumpLeakGraph()
|
||||
{
|
||||
if (dumpXML) {
|
||||
#ifdef USE_XML
|
||||
printf("<?xml version=\"1.0\"?>\n");
|
||||
printf("<?xml-stylesheet href=\"http://klink/leaky/leaky.css\" type=\"text/css\"?>\n");
|
||||
printf("<root xmlns:html=\"http://www.w3.org/TR/REC-html40\">\n");
|
||||
printf("<html:script src=\"http://klink/leaky/leaky.js\"/>\n");
|
||||
printf("<key>\n");
|
||||
printf("Key:<html:br/>\n");
|
||||
printf("<b>Bytes directly leaked</b><html:br/>\n");
|
||||
printf("<k>Bytes leaked by descendants</k></key>\n");
|
||||
#else
|
||||
if (dumpHTML) {
|
||||
printf("<html><head><title>Leaky Graph</title>\n");
|
||||
printf("<style src=\"http://klink/leaky/leaky.css\"></style>\n");
|
||||
printf("<script src=\"http://klink/leaky/leaky.js\"/></script>\n");
|
||||
printf("<style src=\"resource:/res/leaky/leaky.css\"></style>\n");
|
||||
printf("<script src=\"resource:/res/leaky/leaky.js\"/></script>\n");
|
||||
printf("</head><body><div class=\"key\">\n");
|
||||
printf("Key:<br>\n");
|
||||
printf("<span class=b>Bytes directly leaked</span><br>\n");
|
||||
printf("<span class=d>Bytes leaked by descendants</span></div>\n");
|
||||
#endif
|
||||
}
|
||||
Symbol* base = externalSymbols;
|
||||
Symbol* end = externalSymbols + usefulSymbols;
|
||||
while (base < end) {
|
||||
Symbol* root = findLeakGraphRoot(base, end);
|
||||
if (!root) break;
|
||||
if (root->NotDumped()) {
|
||||
root->SetDumped();
|
||||
dumpLeakTree(root, 0, true);
|
||||
}
|
||||
base = root + 1;
|
||||
Symbol* sym = findLeakGraphRoot(base, end);
|
||||
if (!sym) break;
|
||||
dumpLeakTree(sym->root, 0);
|
||||
base = sym + 1;
|
||||
}
|
||||
if (dumpXML) {
|
||||
#ifdef USE_XML
|
||||
printf("</root>\n");
|
||||
#else
|
||||
if (dumpHTML) {
|
||||
printf("</body></html>\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void leaky::dumpLeakTree(Symbol* aSymbol, int aIndent, bool aEven)
|
||||
void leaky::dumpLeakTree(TreeNode* aNode, int aIndent)
|
||||
{
|
||||
#if 0
|
||||
float avgBytesLeaked =
|
||||
(float) (aSymbol->bytesDirectlyLeaked + aSymbol->childBytesLeaked) /
|
||||
(float) aSymbol->calls;
|
||||
#endif
|
||||
|
||||
bool haveVisibleDescendants = false;
|
||||
SymbolNode* node = aSymbol->children;
|
||||
while (node) {
|
||||
Symbol* kid = node->symbol;
|
||||
if (kid && kid->NotDumped()) {
|
||||
haveVisibleDescendants = true;
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (dumpXML) {
|
||||
#ifdef USE_XML
|
||||
printf("<n class=\"%s\">", aEven ? "e" : "o");
|
||||
if (haveVisibleDescendants) {
|
||||
printf("<html:img onmouseout=\"O(event);\" onmouseover=\"I(event);\" onclick=\"C(event);\" src=\"http://klink/leaky/%s.gif\"/>"
|
||||
Symbol* sym = aNode->symbol;
|
||||
if (dumpHTML) {
|
||||
printf("<div class=\"n\">\n");
|
||||
if (aNode->HasDescendants()) {
|
||||
printf("<img onmouseout=\"O(event);\" onmouseover=\"I(event);\" ");
|
||||
printf("onclick=\"C(event);\" src=\"resource:/res/leaky/%s.gif\">",
|
||||
aIndent > 1 ? "close" : "open");
|
||||
}
|
||||
printf("<s>%s</s><b>%ld</b><k>%ld</k>\n",
|
||||
aSymbol->name,
|
||||
aSymbol->bytesDirectlyLeaked,
|
||||
aSymbol->childBytesLeaked);
|
||||
#else
|
||||
printf("<div class=\"n %c\">\n", aEven ? 'e' : 'o');
|
||||
if (haveVisibleDescendants) {
|
||||
printf("<img onmouseout=\"O(event);\" onmouseover=\"I(event);\" onclick=\"C(event);\" src=\"http://klink/leaky/%s.gif\">",
|
||||
aIndent > 1 ? "close" : "open");
|
||||
}
|
||||
printf("<span class=s>%s</span><span class=b>%ld</span><span class=d>%ld</span>\n",
|
||||
aSymbol->name,
|
||||
aSymbol->bytesDirectlyLeaked,
|
||||
aSymbol->childBytesLeaked);
|
||||
#endif
|
||||
printf("<span class=s>%s</span><span class=b>%ld</span>",
|
||||
sym->name,
|
||||
aNode->bytesLeaked);
|
||||
printf("<span class=d>%ld</span>\n",
|
||||
aNode->descendantBytesLeaked);
|
||||
}
|
||||
else {
|
||||
indentBy(aIndent);
|
||||
printf("%s bytesLeaked=%ld (%ld from kids)\n",
|
||||
aSymbol->name,
|
||||
aSymbol->bytesDirectlyLeaked,
|
||||
aSymbol->childBytesLeaked);
|
||||
sym->name,
|
||||
aNode->bytesLeaked,
|
||||
aNode->descendantBytesLeaked);
|
||||
}
|
||||
|
||||
node = aSymbol->children;
|
||||
TreeNode* node = aNode->descendants;
|
||||
int kidNum = 0;
|
||||
while (node) {
|
||||
Symbol* kid = node->symbol;
|
||||
if (kid && kid->NotDumped()) {
|
||||
kid->SetDumped();
|
||||
dumpLeakTree(kid, aIndent + 1, 0 == (kidNum & 1));
|
||||
kidNum++;
|
||||
}
|
||||
node = node->next;
|
||||
sym = node->symbol;
|
||||
dumpLeakTree(node, aIndent + 1);
|
||||
kidNum++;
|
||||
node = node->nextSibling;
|
||||
}
|
||||
|
||||
if (dumpXML) {
|
||||
#ifdef USE_XML
|
||||
printf("</n>");
|
||||
#else
|
||||
if (dumpHTML) {
|
||||
printf("</div>");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
SymbolNode* SymbolNode::freeList;
|
||||
TreeNode* TreeNode::freeList;
|
||||
|
||||
void* SymbolNode::operator new(size_t size)
|
||||
void* TreeNode::operator new(size_t size)
|
||||
{
|
||||
if (!freeList) {
|
||||
SymbolNode* newNodes = (SymbolNode*) new char[sizeof(SymbolNode) * 5000];
|
||||
TreeNode* newNodes = (TreeNode*) new char[sizeof(TreeNode) * 5000];
|
||||
if (!newNodes) {
|
||||
return NULL;
|
||||
}
|
||||
SymbolNode* n = newNodes;
|
||||
SymbolNode* end = newNodes + 5000 - 1;
|
||||
TreeNode* n = newNodes;
|
||||
TreeNode* end = newNodes + 5000 - 1;
|
||||
while (n < end) {
|
||||
n->next = n + 1;
|
||||
n->nextSibling = n + 1;
|
||||
n++;
|
||||
}
|
||||
n->next = NULL;
|
||||
n->nextSibling = NULL;
|
||||
freeList = newNodes;
|
||||
}
|
||||
|
||||
SymbolNode* rv = freeList;
|
||||
freeList = rv->next;
|
||||
TreeNode* rv = freeList;
|
||||
freeList = rv->nextSibling;
|
||||
|
||||
return (void*) rv;
|
||||
}
|
||||
|
||||
void SymbolNode::operator delete(void* ptr)
|
||||
void TreeNode::operator delete(void* ptr)
|
||||
{
|
||||
SymbolNode* node = (SymbolNode*) ptr;
|
||||
TreeNode* node = (TreeNode*) ptr;
|
||||
if (node) {
|
||||
node->next = freeList;
|
||||
node->nextSibling = freeList;
|
||||
freeList = node;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void Symbol::Init(const char* aName, u_long aAddress)
|
||||
TreeNode* TreeNode::GetDirectDescendant(Symbol* aSymbol)
|
||||
{
|
||||
name = aName ? strdup(aName) : "";
|
||||
address = aAddress;
|
||||
dumped = false;
|
||||
leaker = false;
|
||||
calls = 0;
|
||||
parents = NULL;
|
||||
children = NULL;
|
||||
bytesDirectlyLeaked = 0;
|
||||
childBytesLeaked = 0;
|
||||
}
|
||||
|
||||
void Symbol::AddParent(Symbol* aParent)
|
||||
{
|
||||
SymbolNode* node = new SymbolNode(aParent);
|
||||
if (node) {
|
||||
node->next = parents;
|
||||
parents = node;
|
||||
TreeNode* node = descendants;
|
||||
while (node) {
|
||||
if (node->symbol == aSymbol) {
|
||||
return node;
|
||||
}
|
||||
node = node->nextSibling;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Symbol::AddChild(Symbol* aChild)
|
||||
TreeNode* TreeNode::AddDescendant(Symbol* aSymbol)
|
||||
{
|
||||
SymbolNode* node = new SymbolNode(aChild);
|
||||
if (node) {
|
||||
node->next = children;
|
||||
children = node;
|
||||
}
|
||||
TreeNode* node = new TreeNode(aSymbol);
|
||||
node->nextSibling = descendants;
|
||||
descendants = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -1,51 +1,53 @@
|
||||
root {
|
||||
display: block;
|
||||
margin: 1em 1em;
|
||||
body {
|
||||
background-color: white;
|
||||
line-height: 1.1;
|
||||
margin: 1em;
|
||||
font-size: 10pt;
|
||||
font-family: "Arial", "Times New Roman", "Times Roman", serif;
|
||||
}
|
||||
|
||||
root > n, root > n > n, root > n > n > n {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body > .n, body > .n > .n, body > .n > .n > .n {
|
||||
display: block;
|
||||
}
|
||||
|
||||
key, .key {
|
||||
.key {
|
||||
display: block;
|
||||
margin: 1em;
|
||||
border: 2px solid green;
|
||||
width: 50%;
|
||||
margin: 0 auto 1em auto;
|
||||
font-size: 130%;
|
||||
}
|
||||
key b, key k, .key .b, .key .d { margin: 0 0 0 2em; padding: 0; }
|
||||
|
||||
n, .n {
|
||||
.key .b, .key .d {
|
||||
margin: 0 0 0 2em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.n {
|
||||
display: none;
|
||||
margin: 0 1em;
|
||||
margin: 0 0 0 1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
n.e, .n.e {
|
||||
background-color: orange;
|
||||
.n.e {
|
||||
background-color: rgb(180,180,180);
|
||||
}
|
||||
|
||||
s, .s {
|
||||
.s {
|
||||
display: inline;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
b, .b {
|
||||
.b {
|
||||
display: inline;
|
||||
background-color: yellow;
|
||||
margin: 0 1em;
|
||||
padding: 0 1em;
|
||||
margin: 0 1em 0 1em;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
k, .d {
|
||||
.d {
|
||||
display: inline;
|
||||
background-color: khaki;
|
||||
padding: 0 1em;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include "dict.h"
|
||||
#include "strset.h"
|
||||
@ -23,43 +24,44 @@ typedef unsigned int u_int;
|
||||
|
||||
struct Symbol;
|
||||
|
||||
struct SymbolNode {
|
||||
SymbolNode(Symbol* aSymbol) {
|
||||
struct TreeNode {
|
||||
TreeNode(Symbol* aSymbol) {
|
||||
symbol = aSymbol;
|
||||
next = NULL;
|
||||
nextSibling = NULL;
|
||||
descendants = NULL;
|
||||
}
|
||||
|
||||
TreeNode* GetDirectDescendant(Symbol* aSymbol);
|
||||
|
||||
bool HasDescendants() const {
|
||||
return NULL != descendants;
|
||||
}
|
||||
|
||||
TreeNode* AddDescendant(Symbol* aSymbol);
|
||||
|
||||
TreeNode* descendants;
|
||||
TreeNode* nextSibling;
|
||||
Symbol* symbol;
|
||||
|
||||
u_long bytesLeaked;
|
||||
u_long descendantBytesLeaked;
|
||||
|
||||
void* operator new(size_t size);
|
||||
void operator delete(void* ptr);
|
||||
|
||||
Symbol* symbol;
|
||||
SymbolNode* next;
|
||||
|
||||
static SymbolNode* freeList;
|
||||
static TreeNode* freeList;
|
||||
};
|
||||
|
||||
struct Symbol {
|
||||
char* name;
|
||||
u_long address;
|
||||
bool dumped;
|
||||
bool leaker;
|
||||
u_long calls;
|
||||
SymbolNode* parents;
|
||||
SymbolNode* children;
|
||||
u_long bytesDirectlyLeaked;
|
||||
u_long childBytesLeaked;
|
||||
TreeNode* root;
|
||||
|
||||
bool NotDumped() const {
|
||||
return 0 == dumped;
|
||||
void Init(const char* aName, u_long aAddress) {
|
||||
name = aName ? strdup(aName) : "";
|
||||
address = aAddress;
|
||||
root = NULL;
|
||||
}
|
||||
|
||||
void SetDumped() {
|
||||
dumped = 1;
|
||||
}
|
||||
|
||||
void Init(const char* aName, u_long aAddress);
|
||||
void AddParent(Symbol* aParent);
|
||||
void AddChild(Symbol* aChild);
|
||||
};
|
||||
|
||||
struct LoadMapEntry {
|
||||
@ -82,15 +84,15 @@ struct leaky {
|
||||
int sortByFrequency;
|
||||
int dumpAll;
|
||||
int dumpGraph;
|
||||
int dumpXML;
|
||||
int dumpHTML;
|
||||
int quiet;
|
||||
int showAll;
|
||||
int showAddress;
|
||||
u_int stackDepth;
|
||||
|
||||
int fd;
|
||||
malloc_log_entry* base;
|
||||
malloc_log_entry* last;
|
||||
int mappedLogFile;
|
||||
malloc_log_entry* firstLogEntry;
|
||||
malloc_log_entry* lastLogEntry;
|
||||
u_int buckets;
|
||||
MallocDict* dict;
|
||||
|
||||
@ -128,7 +130,7 @@ struct leaky {
|
||||
void insertAddress(u_long address, malloc_log_entry* lep);
|
||||
void removeAddress(u_long address, malloc_log_entry* lep);
|
||||
|
||||
void displayStackTrace(malloc_log_entry* lep);
|
||||
void displayStackTrace(FILE* out, malloc_log_entry* lep);
|
||||
|
||||
void ReadSymbols(const char* fileName, u_long aBaseAddress);
|
||||
void ReadSharedLibrarySymbols();
|
||||
@ -139,7 +141,7 @@ struct leaky {
|
||||
void buildLeakGraph();
|
||||
Symbol* findLeakGraphRoot(Symbol* aStart, Symbol* aEnd);
|
||||
void dumpLeakGraph();
|
||||
void dumpLeakTree(Symbol* aRoot, int aIndent, bool aEven);
|
||||
void dumpLeakTree(TreeNode* aNode, int aIndent);
|
||||
|
||||
static void indentBy(int aCount) {
|
||||
while (--aCount >= 0) fputs(" ", stdout);
|
||||
|
@ -97,6 +97,10 @@ static void DumpAddressMap()
|
||||
mme.address = map->l_addr;
|
||||
write(mfd, &mme, sizeof(mme));
|
||||
write(mfd, map->l_name, mme.nameLen);
|
||||
#if 0
|
||||
write(1, map->l_name, mme.nameLen);
|
||||
write(1, "\n", 1);
|
||||
#endif
|
||||
}
|
||||
map = map->l_next;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user