зеркало из https://github.com/mozilla/pjs.git
Whacked to support a graph display format
This commit is contained in:
Родитель
48e53dfd90
Коммит
efd9beedfb
|
@ -72,8 +72,7 @@ void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress)
|
|||
if (nm) {
|
||||
// char* dnm = cplus_demangle(nm, 1);
|
||||
// sp->name = dnm ? dnm : strdup(nm);
|
||||
sp->name = strdup(nm);
|
||||
sp->address = syminfo.value + aBaseAddress;
|
||||
sp->Init(nm, syminfo.value + aBaseAddress);
|
||||
sp++;
|
||||
if (sp >= last) {
|
||||
long n = numExternalSymbols + 10000;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#ifndef config_h___
|
||||
#define config_h___
|
||||
|
||||
#define MAX_STACK_CRAWL 30
|
||||
#define MAX_STACK_CRAWL 100
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
|
|
|
@ -16,26 +16,28 @@
|
|||
#include <sys/types.h>
|
||||
#include "libmalloc.h"
|
||||
|
||||
// key is u_long value is malloc_log_entry*
|
||||
// key is u_long
|
||||
// value is malloc_log_entry*
|
||||
struct MallocDict {
|
||||
MallocDict(int buckets);
|
||||
MallocDict(int buckets);
|
||||
|
||||
void rewind(void);
|
||||
malloc_log_entry* next(void);
|
||||
void rewind(void);
|
||||
malloc_log_entry* next(void);
|
||||
|
||||
malloc_log_entry** find(u_long addr);
|
||||
void add(u_long addr, malloc_log_entry *log);
|
||||
void remove(u_long addr);
|
||||
malloc_log_entry** find(u_long addr);
|
||||
void add(u_long addr, malloc_log_entry *log);
|
||||
void remove(u_long addr);
|
||||
|
||||
struct MallocDictEntry {
|
||||
u_long addr;
|
||||
malloc_log_entry* logEntry;
|
||||
MallocDictEntry* next;
|
||||
} **buckets;
|
||||
int numBuckets;
|
||||
struct MallocDictEntry {
|
||||
u_long addr;
|
||||
malloc_log_entry* logEntry;
|
||||
MallocDictEntry* next;
|
||||
} **buckets;
|
||||
|
||||
int iterNextBucket;
|
||||
MallocDictEntry* iterNextEntry;
|
||||
int numBuckets;
|
||||
|
||||
int iterNextBucket;
|
||||
MallocDictEntry* iterNextEntry;
|
||||
};
|
||||
|
||||
#endif /* __dict_h_ */
|
||||
|
|
|
@ -38,142 +38,146 @@ static const u_int MaxBuckets = 1000003; // arbitrary, but prime
|
|||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
leaky* l = new leaky;
|
||||
leaky* l = new leaky;
|
||||
|
||||
l->initialize(argc, argv);
|
||||
l->open();
|
||||
return 0;
|
||||
l->initialize(argc, argv);
|
||||
l->open();
|
||||
return 0;
|
||||
}
|
||||
|
||||
leaky::leaky()
|
||||
{
|
||||
applicationName = NULL;
|
||||
logFile = NULL;
|
||||
progFile = NULL;
|
||||
applicationName = NULL;
|
||||
logFile = NULL;
|
||||
progFile = NULL;
|
||||
|
||||
treeOutput = FALSE;
|
||||
sortByFrequency = FALSE;
|
||||
dumpAll = FALSE;
|
||||
quiet = FALSE;
|
||||
showAll = FALSE;
|
||||
showAddress = FALSE;
|
||||
stackDepth = 100000;
|
||||
sortByFrequency = FALSE;
|
||||
dumpAll = FALSE;
|
||||
dumpGraph = FALSE;
|
||||
dumpXML = FALSE;
|
||||
quiet = FALSE;
|
||||
showAll = FALSE;
|
||||
showAddress = FALSE;
|
||||
stackDepth = 100000;
|
||||
|
||||
fd = -1;
|
||||
base = last = 0;
|
||||
buckets = DefaultBuckets;
|
||||
dict = 0;
|
||||
fd = -1;
|
||||
base = last = 0;
|
||||
buckets = DefaultBuckets;
|
||||
dict = 0;
|
||||
|
||||
mallocs = 0;
|
||||
reallocs = 0;
|
||||
frees = 0;
|
||||
totalMalloced = 0;
|
||||
errors = 0;
|
||||
totalLeaked = 0;
|
||||
mallocs = 0;
|
||||
reallocs = 0;
|
||||
frees = 0;
|
||||
totalMalloced = 0;
|
||||
errors = 0;
|
||||
totalLeaked = 0;
|
||||
|
||||
sfd = -1;
|
||||
externalSymbols = 0;
|
||||
usefulSymbols = 0;
|
||||
numExternalSymbols = 0;
|
||||
lowestSymbolAddr = 0;
|
||||
highestSymbolAddr = 0;
|
||||
sfd = -1;
|
||||
externalSymbols = 0;
|
||||
usefulSymbols = 0;
|
||||
numExternalSymbols = 0;
|
||||
lowestSymbolAddr = 0;
|
||||
highestSymbolAddr = 0;
|
||||
|
||||
loadMap = NULL;
|
||||
loadMap = NULL;
|
||||
}
|
||||
|
||||
leaky::~leaky()
|
||||
{
|
||||
delete dict;
|
||||
delete dict;
|
||||
}
|
||||
|
||||
void leaky::usageError()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [-d|-t] [-e name] [-aAEfq] [-s depth] [-h hash-buckets] prog log\n",
|
||||
(char*) applicationName);
|
||||
exit(-1);
|
||||
fprintf(stderr,
|
||||
"Usage: %s [-aAEdfgqx] [-e name] [-s depth] [-h hash-buckets] prog log\n",
|
||||
(char*) applicationName);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void leaky::initialize(int argc, char** argv)
|
||||
{
|
||||
applicationName = argv[0];
|
||||
applicationName = strrchr(applicationName, '/');
|
||||
if (!applicationName) {
|
||||
applicationName = argv[0];
|
||||
applicationName = strrchr(applicationName, '/');
|
||||
if (!applicationName) {
|
||||
applicationName = argv[0];
|
||||
} else {
|
||||
applicationName++;
|
||||
}
|
||||
} else {
|
||||
applicationName++;
|
||||
}
|
||||
|
||||
int arg;
|
||||
int errflg = 0;
|
||||
while ((arg = getopt(argc, argv, "adEe:fh:s:tq")) != -1) {
|
||||
switch (arg) {
|
||||
case '?':
|
||||
errflg++;
|
||||
break;
|
||||
case 'a':
|
||||
showAll = TRUE;
|
||||
break;
|
||||
case 'A':
|
||||
showAddress = TRUE;
|
||||
break;
|
||||
case 'd':
|
||||
dumpAll = TRUE;
|
||||
if (treeOutput) errflg++;
|
||||
break;
|
||||
case 'e':
|
||||
exclusions.add(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
sortByFrequency = TRUE;
|
||||
break;
|
||||
case 'h':
|
||||
buckets = atoi(optarg);
|
||||
if ((buckets < 0) || (buckets > MaxBuckets)) {
|
||||
buckets = MaxBuckets;
|
||||
fprintf(stderr, "%s: buckets is invalid, using %d\n",
|
||||
(char*) applicationName,
|
||||
buckets);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
stackDepth = atoi(optarg);
|
||||
if (stackDepth < 2) {
|
||||
stackDepth = 2;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
treeOutput = TRUE;
|
||||
if (dumpAll) errflg++;
|
||||
break;
|
||||
case 'q':
|
||||
quiet = TRUE;
|
||||
break;
|
||||
int arg;
|
||||
int errflg = 0;
|
||||
while ((arg = getopt(argc, argv, "adEe:fgh:s:tqx")) != -1) {
|
||||
switch (arg) {
|
||||
case '?':
|
||||
errflg++;
|
||||
break;
|
||||
case 'a':
|
||||
showAll = TRUE;
|
||||
break;
|
||||
case 'A':
|
||||
showAddress = TRUE;
|
||||
break;
|
||||
case 'd':
|
||||
dumpAll = TRUE;
|
||||
if (dumpGraph) errflg++;
|
||||
break;
|
||||
case 'e':
|
||||
exclusions.add(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
sortByFrequency = TRUE;
|
||||
break;
|
||||
case 'g':
|
||||
dumpGraph = TRUE;
|
||||
if (dumpAll) errflg++;
|
||||
break;
|
||||
case 'h':
|
||||
buckets = atoi(optarg);
|
||||
if ((buckets < 0) || (buckets > MaxBuckets)) {
|
||||
buckets = MaxBuckets;
|
||||
fprintf(stderr, "%s: buckets is invalid, using %d\n",
|
||||
(char*) applicationName,
|
||||
buckets);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
stackDepth = atoi(optarg);
|
||||
if (stackDepth < 2) {
|
||||
stackDepth = 2;
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
dumpXML = TRUE;
|
||||
break;
|
||||
case 'q':
|
||||
quiet = TRUE;
|
||||
break;
|
||||
}
|
||||
if (errflg || ((argc - optind) < 2)) {
|
||||
usageError();
|
||||
}
|
||||
progFile = argv[optind++];
|
||||
logFile = argv[optind];
|
||||
}
|
||||
if (errflg || ((argc - optind) < 2)) {
|
||||
usageError();
|
||||
}
|
||||
progFile = argv[optind++];
|
||||
logFile = argv[optind];
|
||||
|
||||
dict = new MallocDict(buckets);
|
||||
dict = new MallocDict(buckets);
|
||||
}
|
||||
|
||||
static void* mapFile(int fd, u_int flags, off_t* sz)
|
||||
{
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb) < 0) {
|
||||
perror("fstat");
|
||||
exit(-1);
|
||||
}
|
||||
void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0);
|
||||
if (!base) {
|
||||
perror("mmap");
|
||||
exit(-1);
|
||||
}
|
||||
*sz = sb.st_size;
|
||||
return base;
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb) < 0) {
|
||||
perror("fstat");
|
||||
exit(-1);
|
||||
}
|
||||
void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0);
|
||||
if (!base) {
|
||||
perror("mmap");
|
||||
exit(-1);
|
||||
}
|
||||
*sz = sb.st_size;
|
||||
return base;
|
||||
}
|
||||
|
||||
void leaky::LoadMap()
|
||||
|
@ -207,27 +211,30 @@ void leaky::LoadMap()
|
|||
|
||||
void leaky::open()
|
||||
{
|
||||
LoadMap();
|
||||
LoadMap();
|
||||
|
||||
setupSymbols(progFile);
|
||||
setupSymbols(progFile);
|
||||
|
||||
// open up the log file
|
||||
fd = ::open(logFile, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
exit(-1);
|
||||
}
|
||||
off_t size;
|
||||
base = (malloc_log_entry*) mapFile(fd, PROT_READ, &size);
|
||||
last = (malloc_log_entry*)((char*)base + size);
|
||||
// open up the log file
|
||||
fd = ::open(logFile, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
exit(-1);
|
||||
}
|
||||
off_t size;
|
||||
base = (malloc_log_entry*) mapFile(fd, PROT_READ, &size);
|
||||
last = (malloc_log_entry*)((char*)base + size);
|
||||
|
||||
analyze();
|
||||
analyze();
|
||||
|
||||
if (dumpAll) dumpLog();
|
||||
#if 0
|
||||
if (treeOutput) dumpToTree();
|
||||
#endif
|
||||
exit(0);
|
||||
if (dumpAll) {
|
||||
dumpLog();
|
||||
}
|
||||
else if (dumpGraph) {
|
||||
buildLeakGraph();
|
||||
dumpLeakGraph();
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -235,9 +242,9 @@ void leaky::open()
|
|||
|
||||
static ptrdiff_t symbolOrder(void const* a, void const* b)
|
||||
{
|
||||
Symbol const* ap = (Symbol const *)a;
|
||||
Symbol const* bp = (Symbol const *)b;
|
||||
return ap->address - bp->address;
|
||||
Symbol const* ap = (Symbol const *)a;
|
||||
Symbol const* bp = (Symbol const *)b;
|
||||
return ap->address - bp->address;
|
||||
}
|
||||
|
||||
void leaky::ReadSharedLibrarySymbols()
|
||||
|
@ -251,93 +258,92 @@ void leaky::ReadSharedLibrarySymbols()
|
|||
|
||||
void leaky::setupSymbols(const char *fileName)
|
||||
{
|
||||
// Read in symbols from the program
|
||||
ReadSymbols(fileName, 0);
|
||||
// Read in symbols from the program
|
||||
ReadSymbols(fileName, 0);
|
||||
|
||||
// Read in symbols from the .so's
|
||||
ReadSharedLibrarySymbols();
|
||||
// Read in symbols from the .so's
|
||||
ReadSharedLibrarySymbols();
|
||||
|
||||
if (!quiet) {
|
||||
printf("A total of %d symbols were loaded\n", usefulSymbols);
|
||||
}
|
||||
if (!quiet) {
|
||||
printf("A total of %d symbols were loaded\n", usefulSymbols);
|
||||
}
|
||||
|
||||
// Now sort them
|
||||
qsort(externalSymbols, usefulSymbols, sizeof(Symbol), symbolOrder);
|
||||
lowestSymbolAddr = externalSymbols[0].address;
|
||||
highestSymbolAddr = externalSymbols[usefulSymbols-1].address;
|
||||
// Now sort them
|
||||
qsort(externalSymbols, usefulSymbols, sizeof(Symbol), symbolOrder);
|
||||
lowestSymbolAddr = externalSymbols[0].address;
|
||||
highestSymbolAddr = externalSymbols[usefulSymbols-1].address;
|
||||
}
|
||||
|
||||
char const* leaky::findSymbol(u_long addr)
|
||||
// Binary search the table, looking for a symbol that covers this
|
||||
// address.
|
||||
Symbol* leaky::findSymbol(u_long addr)
|
||||
{
|
||||
if ((addr < lowestSymbolAddr) ||
|
||||
(addr > highestSymbolAddr)) {
|
||||
static char buf[20];
|
||||
sprintf(buf, "<0x%lx>", addr);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// binary search the table, looking for a symbol that covers this
|
||||
// address.
|
||||
u_int base = 0;
|
||||
u_int limit = usefulSymbols - 1;
|
||||
Symbol* end = &externalSymbols[limit];
|
||||
while (base <= limit) {
|
||||
u_int midPoint = (base + limit)>>1;
|
||||
Symbol* sp = &externalSymbols[midPoint];
|
||||
if (addr < sp->address) {
|
||||
if (midPoint == 0) {
|
||||
return 0;
|
||||
}
|
||||
limit = midPoint - 1;
|
||||
} else {
|
||||
if (sp+1 < end) {
|
||||
if (addr < (sp+1)->address) {
|
||||
return sp->name;
|
||||
}
|
||||
} else {
|
||||
return sp->name;
|
||||
}
|
||||
base = midPoint + 1;
|
||||
u_int base = 0;
|
||||
u_int limit = usefulSymbols - 1;
|
||||
Symbol* end = &externalSymbols[limit];
|
||||
while (base <= limit) {
|
||||
u_int midPoint = (base + limit)>>1;
|
||||
Symbol* sp = &externalSymbols[midPoint];
|
||||
if (addr < sp->address) {
|
||||
if (midPoint == 0) {
|
||||
return NULL;
|
||||
}
|
||||
limit = midPoint - 1;
|
||||
} else {
|
||||
if (sp+1 < end) {
|
||||
if (addr < (sp+1)->address) {
|
||||
return sp;
|
||||
}
|
||||
} else {
|
||||
return sp;
|
||||
}
|
||||
base = midPoint + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int leaky::excluded(malloc_log_entry* lep)
|
||||
{
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = lep->numpcs;
|
||||
for (u_int i = 0; i < n; i++, pcp++) {
|
||||
char const* sym = findSymbol((u_long) *pcp);
|
||||
if (exclusions.contains(sym)) {
|
||||
return TRUE;
|
||||
}
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = lep->numpcs;
|
||||
for (u_int i = 0; i < n; i++, pcp++) {
|
||||
Symbol* sp = findSymbol((u_long) *pcp);
|
||||
if (sp && exclusions.contains(sp->name)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void leaky::displayStackTrace(malloc_log_entry* lep)
|
||||
{
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
|
||||
for (u_int i = 0; i < n; i++, pcp++) {
|
||||
char const* sym = findSymbol((u_long) *pcp);
|
||||
if (!sym) {
|
||||
break;
|
||||
}
|
||||
if (showAddress) {
|
||||
printf("%s[%p] ", sym, *pcp);
|
||||
}
|
||||
else {
|
||||
printf("%s ", sym);
|
||||
}
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
|
||||
for (u_int i = 0; i < n; i++, pcp++) {
|
||||
u_long addr = (u_long) *pcp;
|
||||
static char buf[20];
|
||||
char* symbolName;
|
||||
Symbol* sp = findSymbol(addr);
|
||||
if (sp) {
|
||||
symbolName = sp->name;
|
||||
}
|
||||
printf("\n");
|
||||
else {
|
||||
sprintf(buf, "<0x%lx>", addr);
|
||||
symbolName = buf;
|
||||
}
|
||||
if (showAddress) {
|
||||
printf("%s[%p] ", symbolName, *pcp);
|
||||
}
|
||||
else {
|
||||
printf("%s ", symbolName);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
char* typeFromLog[] = {
|
||||
|
@ -352,109 +358,337 @@ char* typeFromLog[] = {
|
|||
|
||||
void leaky::dumpEntryToLog(malloc_log_entry* lep)
|
||||
{
|
||||
printf("%-10s %08lx %5ld %08lx (%ld)-->",
|
||||
typeFromLog[lep->type],
|
||||
lep->address, lep->size, lep->oldaddress,
|
||||
lep->numpcs);
|
||||
displayStackTrace(lep);
|
||||
printf("%-10s %08lx %5ld %08lx (%ld)-->",
|
||||
typeFromLog[lep->type],
|
||||
lep->address, lep->size, lep->oldaddress,
|
||||
lep->numpcs);
|
||||
displayStackTrace(lep);
|
||||
}
|
||||
|
||||
void leaky::dumpLog()
|
||||
{
|
||||
if (showAll) {
|
||||
malloc_log_entry* lep = base;
|
||||
while (lep < last) {
|
||||
dumpEntryToLog(lep);
|
||||
lep = (malloc_log_entry*) &lep->pcs[lep->numpcs];
|
||||
}
|
||||
} else {
|
||||
malloc_log_entry* lep;
|
||||
dict->rewind();
|
||||
while (NULL != (lep = dict->next())) {
|
||||
if (!excluded(lep)) {
|
||||
dumpEntryToLog(lep);
|
||||
}
|
||||
}
|
||||
if (showAll) {
|
||||
malloc_log_entry* lep = base;
|
||||
while (lep < last) {
|
||||
dumpEntryToLog(lep);
|
||||
lep = (malloc_log_entry*) &lep->pcs[lep->numpcs];
|
||||
}
|
||||
} else {
|
||||
malloc_log_entry* lep;
|
||||
dict->rewind();
|
||||
while (NULL != (lep = dict->next())) {
|
||||
if (!excluded(lep)) {
|
||||
dumpEntryToLog(lep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void leaky::insertAddress(u_long address, malloc_log_entry* lep)
|
||||
{
|
||||
malloc_log_entry** lepp = dict->find(address);
|
||||
if (lepp) {
|
||||
assert(*lepp);
|
||||
if (!quiet) {
|
||||
printf("Address %lx allocated twice\n", address);
|
||||
displayStackTrace(lep);
|
||||
}
|
||||
errors++;
|
||||
} else {
|
||||
dict->add(address, lep);
|
||||
malloc_log_entry** lepp = dict->find(address);
|
||||
if (lepp) {
|
||||
assert(*lepp);
|
||||
if (!quiet) {
|
||||
printf("Address %lx allocated twice\n", address);
|
||||
displayStackTrace(lep);
|
||||
}
|
||||
errors++;
|
||||
} else {
|
||||
dict->add(address, lep);
|
||||
}
|
||||
}
|
||||
|
||||
void leaky::removeAddress(u_long address, malloc_log_entry* lep)
|
||||
{
|
||||
malloc_log_entry** lepp = dict->find(address);
|
||||
if (!lepp) {
|
||||
if (!quiet) {
|
||||
printf("Free of unallocated %lx\n", address);
|
||||
displayStackTrace(lep);
|
||||
}
|
||||
errors++;
|
||||
} else {
|
||||
dict->remove(address);
|
||||
malloc_log_entry** lepp = dict->find(address);
|
||||
if (!lepp) {
|
||||
if (!quiet) {
|
||||
printf("Free of unallocated %lx\n", address);
|
||||
displayStackTrace(lep);
|
||||
}
|
||||
errors++;
|
||||
} else {
|
||||
dict->remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
void leaky::analyze()
|
||||
{
|
||||
malloc_log_entry* lep = base;
|
||||
while (lep < last) {
|
||||
switch (lep->type) {
|
||||
case malloc_log_malloc:
|
||||
case malloc_log_new:
|
||||
mallocs++;
|
||||
if (lep->address) {
|
||||
totalMalloced += lep->size;
|
||||
insertAddress((u_long) lep->address, lep);
|
||||
}
|
||||
break;
|
||||
case malloc_log_realloc:
|
||||
if (lep->oldaddress) {
|
||||
removeAddress((u_long) lep->oldaddress, lep);
|
||||
}
|
||||
if (lep->address) {
|
||||
insertAddress((u_long) lep->address, lep);
|
||||
}
|
||||
reallocs++;
|
||||
break;
|
||||
case malloc_log_free:
|
||||
case malloc_log_delete:
|
||||
if (lep->address) {
|
||||
removeAddress((u_long) lep->address, lep);
|
||||
}
|
||||
frees++;
|
||||
break;
|
||||
malloc_log_entry* lep = base;
|
||||
while (lep < last) {
|
||||
switch (lep->type) {
|
||||
case malloc_log_malloc:
|
||||
case malloc_log_new:
|
||||
mallocs++;
|
||||
if (lep->address) {
|
||||
totalMalloced += lep->size;
|
||||
insertAddress((u_long) lep->address, lep);
|
||||
}
|
||||
lep = (malloc_log_entry*) &lep->pcs[lep->numpcs];
|
||||
}
|
||||
break;
|
||||
|
||||
dict->rewind();
|
||||
while (NULL != (lep = dict->next())) {
|
||||
totalLeaked += lep->size;
|
||||
}
|
||||
case malloc_log_realloc:
|
||||
if (lep->oldaddress) {
|
||||
removeAddress((u_long) lep->oldaddress, lep);
|
||||
}
|
||||
if (lep->address) {
|
||||
insertAddress((u_long) lep->address, lep);
|
||||
}
|
||||
reallocs++;
|
||||
break;
|
||||
|
||||
if (!quiet) {
|
||||
printf("# of mallocs = %ld\n", mallocs);
|
||||
printf("# of reallocs = %ld\n", reallocs);
|
||||
printf("# of frees = %ld\n", frees);
|
||||
printf("# of errors = %ld\n", errors);
|
||||
printf("Total bytes allocated = %ld\n", totalMalloced);
|
||||
printf("Total bytes leaked = %ld\n", totalLeaked);
|
||||
printf("Average bytes per malloc = %g\n",
|
||||
float(totalMalloced)/mallocs);
|
||||
case malloc_log_free:
|
||||
case malloc_log_delete:
|
||||
if (lep->address) {
|
||||
removeAddress((u_long) lep->address, lep);
|
||||
}
|
||||
frees++;
|
||||
break;
|
||||
}
|
||||
lep = (malloc_log_entry*) &lep->pcs[lep->numpcs];
|
||||
}
|
||||
|
||||
dict->rewind();
|
||||
while (NULL != (lep = dict->next())) {
|
||||
totalLeaked += lep->size;
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
printf("# of mallocs = %ld\n", mallocs);
|
||||
printf("# of reallocs = %ld\n", reallocs);
|
||||
printf("# of frees = %ld\n", frees);
|
||||
printf("# of errors = %ld\n", errors);
|
||||
printf("Total bytes allocated = %ld\n", totalMalloced);
|
||||
printf("Total bytes leaked = %ld\n", totalLeaked);
|
||||
printf("Average bytes per malloc = %g\n",
|
||||
float(totalMalloced)/mallocs);
|
||||
}
|
||||
}
|
||||
|
||||
void leaky::buildLeakGraph()
|
||||
{
|
||||
// For each leak
|
||||
malloc_log_entry* lep;
|
||||
dict->rewind();
|
||||
while (NULL != (lep = dict->next())) {
|
||||
Symbol* prevSymbol = NULL;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
prevSymbol = currentSymbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Symbol* leaky::findLeakGraphRoot(Symbol* aStart, Symbol* aEnd)
|
||||
{
|
||||
while (aStart < aEnd) {
|
||||
if (aStart->leaker && !aStart->parents) {
|
||||
return aStart;
|
||||
}
|
||||
aStart++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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
|
||||
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("</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;
|
||||
}
|
||||
if (dumpXML) {
|
||||
#ifdef USE_XML
|
||||
printf("</root>\n");
|
||||
#else
|
||||
printf("</body></html>\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void leaky::dumpLeakTree(Symbol* aSymbol, int aIndent, bool aEven)
|
||||
{
|
||||
#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\"/>"
|
||||
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
|
||||
}
|
||||
else {
|
||||
indentBy(aIndent);
|
||||
printf("%s bytesLeaked=%ld (%ld from kids)\n",
|
||||
aSymbol->name,
|
||||
aSymbol->bytesDirectlyLeaked,
|
||||
aSymbol->childBytesLeaked);
|
||||
}
|
||||
|
||||
node = aSymbol->children;
|
||||
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;
|
||||
}
|
||||
|
||||
if (dumpXML) {
|
||||
#ifdef USE_XML
|
||||
printf("</n>");
|
||||
#else
|
||||
printf("</div>");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
SymbolNode* SymbolNode::freeList;
|
||||
|
||||
void* SymbolNode::operator new(size_t size)
|
||||
{
|
||||
if (!freeList) {
|
||||
SymbolNode* newNodes = (SymbolNode*) new char[sizeof(SymbolNode) * 5000];
|
||||
if (!newNodes) {
|
||||
return NULL;
|
||||
}
|
||||
SymbolNode* n = newNodes;
|
||||
SymbolNode* end = newNodes + 5000 - 1;
|
||||
while (n < end) {
|
||||
n->next = n + 1;
|
||||
n++;
|
||||
}
|
||||
n->next = NULL;
|
||||
freeList = newNodes;
|
||||
}
|
||||
|
||||
SymbolNode* rv = freeList;
|
||||
freeList = rv->next;
|
||||
|
||||
return (void*) rv;
|
||||
}
|
||||
|
||||
void SymbolNode::operator delete(void* ptr)
|
||||
{
|
||||
SymbolNode* node = (SymbolNode*) ptr;
|
||||
if (node) {
|
||||
node->next = freeList;
|
||||
freeList = node;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void Symbol::Init(const char* aName, u_long aAddress)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Symbol::AddChild(Symbol* aChild)
|
||||
{
|
||||
SymbolNode* node = new SymbolNode(aChild);
|
||||
if (node) {
|
||||
node->next = children;
|
||||
children = node;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,15 +14,52 @@
|
|||
#define __leaky_h_
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include "dict.h"
|
||||
#include "strset.h"
|
||||
|
||||
typedef unsigned int u_int;
|
||||
|
||||
struct Symbol;
|
||||
|
||||
struct SymbolNode {
|
||||
SymbolNode(Symbol* aSymbol) {
|
||||
symbol = aSymbol;
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
void* operator new(size_t size);
|
||||
void operator delete(void* ptr);
|
||||
|
||||
Symbol* symbol;
|
||||
SymbolNode* next;
|
||||
|
||||
static SymbolNode* freeList;
|
||||
};
|
||||
|
||||
struct Symbol {
|
||||
char *name;
|
||||
char* name;
|
||||
u_long address;
|
||||
bool dumped;
|
||||
bool leaker;
|
||||
u_long calls;
|
||||
SymbolNode* parents;
|
||||
SymbolNode* children;
|
||||
u_long bytesDirectlyLeaked;
|
||||
u_long childBytesLeaked;
|
||||
|
||||
bool NotDumped() const {
|
||||
return 0 == dumped;
|
||||
}
|
||||
|
||||
void SetDumped() {
|
||||
dumped = 1;
|
||||
}
|
||||
|
||||
void Init(const char* aName, u_long aAddress);
|
||||
void AddParent(Symbol* aParent);
|
||||
void AddChild(Symbol* aChild);
|
||||
};
|
||||
|
||||
struct LoadMapEntry {
|
||||
|
@ -42,9 +79,10 @@ struct leaky {
|
|||
char* logFile;
|
||||
char* progFile;
|
||||
|
||||
int treeOutput;
|
||||
int sortByFrequency;
|
||||
int dumpAll;
|
||||
int dumpGraph;
|
||||
int dumpXML;
|
||||
int quiet;
|
||||
int showAll;
|
||||
int showAddress;
|
||||
|
@ -95,8 +133,17 @@ struct leaky {
|
|||
void ReadSymbols(const char* fileName, u_long aBaseAddress);
|
||||
void ReadSharedLibrarySymbols();
|
||||
void setupSymbols(const char* fileName);
|
||||
char const* findSymbol(u_long address);
|
||||
Symbol* findSymbol(u_long address);
|
||||
int excluded(malloc_log_entry* lep);
|
||||
|
||||
void buildLeakGraph();
|
||||
Symbol* findLeakGraphRoot(Symbol* aStart, Symbol* aEnd);
|
||||
void dumpLeakGraph();
|
||||
void dumpLeakTree(Symbol* aRoot, int aIndent, bool aEven);
|
||||
|
||||
static void indentBy(int aCount) {
|
||||
while (--aCount >= 0) fputs(" ", stdout);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __leaky_h_ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче