зеркало из https://github.com/mozilla/pjs.git
import jprof for jlnance
This commit is contained in:
Родитель
7226280da7
Коммит
04d9dc7a3f
|
@ -0,0 +1,6 @@
|
|||
Makefile
|
||||
TestLeaky
|
||||
TestPreload
|
||||
leaky
|
||||
ShowLibs
|
||||
.deps-*
|
|
@ -0,0 +1,87 @@
|
|||
#! gmake
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
#
|
||||
# Autoconf version of original Makefile
|
||||
# Fri Sep 24 23:44:10 PDT 1999 <mcafee@netscape.com>
|
||||
#
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
PROGRAM = jprof
|
||||
|
||||
CPPSRCS = \
|
||||
bfd.cpp \
|
||||
coff.cpp \
|
||||
elf.cpp \
|
||||
leaky.cpp \
|
||||
strset.cpp \
|
||||
intcnt.cpp \
|
||||
$(NULL)
|
||||
|
||||
LIBS = \
|
||||
-lbfd \
|
||||
-liberty \
|
||||
$(NULL)
|
||||
|
||||
# Stuff to build the library used to wrap malloc
|
||||
LIBMALLOC_CPPSRCS = libmalloc.cpp
|
||||
LIBMALLOC_OBJECTS = $(LIBMALLOC_CPPSRCS:.cpp=.o)
|
||||
LIBMALLOC = libjprof.so
|
||||
|
||||
EXPORTS = \
|
||||
jprof.h \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))
|
||||
|
||||
|
||||
# include $(topsrcdir)/config/config.mk
|
||||
|
||||
OTHER_LIBRARIES = $(LIBMALLOC)
|
||||
TARGETS := $(PROGRAM) $(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)
|
||||
rm -f $(LIBMALLOC)
|
||||
|
||||
clean::
|
||||
rm -f $(LIBMALLOC_OBJECTS)
|
||||
|
||||
$(LIBMALLOC): $(LIBMALLOC_OBJECTS)
|
||||
rm -f $@
|
||||
$(MKSHLIB) -o $@ $(LIBMALLOC_OBJECTS)
|
||||
|
||||
test:
|
||||
@echo LIBMALLOC = $(LIBMALLOC)
|
||||
@echo TARGETS = $(TARGETS)
|
||||
|
||||
install::
|
||||
$(INSTALL) -m 555 $(OTHER_LIBRARIES) $(DIST)/lib
|
||||
$(INSTALL) -m 555 $(OTHER_LIBRARIES) $(DIST)/bin
|
|
@ -0,0 +1,82 @@
|
|||
<html>
|
||||
<head><title>The Jprof Profiler</title></head><body>
|
||||
<h1><center>The Jprof Profiler</h1></center><hr>
|
||||
<h2>Introduction</h2>
|
||||
Jprof is a profiling tool. I am writing it because I need to find out
|
||||
where mozilla is spending its time, and there do not seem to be any
|
||||
profilers for Linux that can handle threads and/or shared libraries.
|
||||
This code is based heavily on Kipp Hickman's leaky.
|
||||
<P>
|
||||
<h2>Operation</h2>
|
||||
To use jprof, the mozilla source code is changed to install a timer
|
||||
which periodically causes the OS to send a signal. When mozilla receives
|
||||
this signal, it does a stack crawl, and dumps the stack trace to a file.
|
||||
After mozilla has run, the jprof program can pick apart the mozilla binary
|
||||
and the log files and determine the call stack when each of the signals
|
||||
were received. If the signals are sent fast enough, it should be possible
|
||||
to figure out where mozilla is spending its time by looking at a histogram
|
||||
of the call stack.
|
||||
<P>
|
||||
<h2>Use</h2>
|
||||
Jprof is not too difficult to use. First it must be enabled by adding:
|
||||
<pre>
|
||||
--enable-jprof
|
||||
</pre>
|
||||
as an argument to configure, and rebuilding mozilla. After mozilla is built,
|
||||
you must set the environment variable JPROF_FLAGS to enable the profiling code.
|
||||
Assuming you are in the dist/bin directory where the mozilla-bin binary lives,
|
||||
you can do this by typing:
|
||||
|
||||
<pre>
|
||||
env JPROF_FLAGS=JP_START LD_LIBRARY_PATH=. ./mozilla-bin
|
||||
</pre>
|
||||
|
||||
As mozilla runs, it will write out the call stack to a file called jprof-log
|
||||
every time a timer fires. Right now the timer is set to go off
|
||||
20 times a second. This is a lot slower than it will eventually go, but its
|
||||
easier to debug this way. After you run mozilla for a while, shut it down.
|
||||
You can then create a profile by running the command:
|
||||
<pre>
|
||||
./jprof mozilla-bin ./jprof-log > tmp.html
|
||||
</pre>
|
||||
If you view the generated HTML in a browser you will see a lot of blocks
|
||||
that look like this:
|
||||
|
||||
<pre>
|
||||
<hr>
|
||||
70 <A href="#82397">g_main_dispatch</A>
|
||||
76269 70 <A name=76269>gdk_event_dispatch</a>
|
||||
59 <A href="#68487">handle_gdk_event(_GdkEvent *, void *)</A>
|
||||
11 <A href="#76265">gdk_events_queue</A>
|
||||
<hr>
|
||||
</pre>
|
||||
|
||||
<P>
|
||||
This output is supposed to look similar to gprof output, so if know how
|
||||
to read gprof, reading this should be easy. If you are not familiar
|
||||
with gprof, here is how to interpret these blocks.
|
||||
<P>
|
||||
On the right is a column of function names. One (and only one) of these
|
||||
function names is proceeded by a number on the left hand side of
|
||||
the page. In this case the function gdk_event_dispatch is proceeded
|
||||
by the number 76269. This number is the index number of the function.
|
||||
The value of the index number is not important, but its presence tells you
|
||||
which function the block refers to (in our case gdk_event_dispatch).
|
||||
<P>
|
||||
All the functions which appear in the list above gdk_event_dispatch, are
|
||||
functions which called gdk_event_dispatch. All the functions which appear
|
||||
below gdk_event_dispatch are functions that gdk_event_dispatch called. In
|
||||
our example gdk_event_dispatch called the functions handle_gdk_event and
|
||||
gdk_events_queue, and it was called by g_main_dispatch.
|
||||
<P>
|
||||
The number that is immediately to the right of the index number is times
|
||||
the function appeared in a sampled call stack. In our example the number
|
||||
is 70 which means that 70 of our call stacks contain gdk_event_dispatch.
|
||||
The numbers in front of the parent and children functions are the number
|
||||
of times those functions were parents or children of our function.
|
||||
<P>
|
||||
<h2>Source Code</h2>
|
||||
I hope to get the code into mozilla soon. Until then you can download it from
|
||||
<A href="http://goliath.inttek.net/~jlnance/mozilla/jprof">goliath</a> It
|
||||
comes in two pieces, a patch to apply to the existing mozilla code, and a
|
||||
tarball containing new files.
|
|
@ -0,0 +1,102 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
#include "leaky.h"
|
||||
|
||||
#ifdef USE_BFD
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <bfd.h>
|
||||
|
||||
extern "C" {
|
||||
char *cplus_demangle (const char *mangled, int options);
|
||||
}
|
||||
|
||||
void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress)
|
||||
{
|
||||
static bfd_boolean kDynamic = (bfd_boolean) false;
|
||||
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
bfd_init ();
|
||||
}
|
||||
|
||||
bfd* lib = bfd_openr(aFileName, NULL);
|
||||
if (NULL == lib) {
|
||||
return;
|
||||
}
|
||||
char **matching;
|
||||
if (!bfd_check_format_matches(lib, bfd_object, &matching)) {
|
||||
bfd_close(lib);
|
||||
return;
|
||||
}
|
||||
|
||||
asymbol* store;
|
||||
store = bfd_make_empty_symbol(lib);
|
||||
|
||||
// read mini symbols
|
||||
PTR minisyms;
|
||||
unsigned int size;
|
||||
long symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size);
|
||||
|
||||
int initialSymbols = usefulSymbols;
|
||||
if (NULL == externalSymbols) {
|
||||
externalSymbols = (Symbol*) malloc(sizeof(Symbol) * 10000);
|
||||
numExternalSymbols = 10000;
|
||||
}
|
||||
Symbol* sp = externalSymbols + usefulSymbols;
|
||||
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(lib, kDynamic, (const PTR) from, store);
|
||||
|
||||
symbol_info syminfo;
|
||||
bfd_get_symbol_info (lib, sym, &syminfo);
|
||||
|
||||
// if ((syminfo.type == 'T') || (syminfo.type == 't')) {
|
||||
const char* nm = bfd_asymbol_name(sym);
|
||||
if (nm && nm[0]) {
|
||||
char* dnm = NULL;
|
||||
if (strncmp("__thunk", nm, 7)) {
|
||||
dnm = cplus_demangle(nm, 1);
|
||||
}
|
||||
sp->Init(dnm ? dnm : nm, syminfo.value + aBaseAddress);
|
||||
sp++;
|
||||
if (sp >= lastSymbol) {
|
||||
long n = numExternalSymbols + 10000;
|
||||
externalSymbols = (Symbol*)
|
||||
realloc(externalSymbols, (size_t) (sizeof(Symbol) * n));
|
||||
lastSymbol = externalSymbols + n;
|
||||
sp = externalSymbols + numExternalSymbols;
|
||||
numExternalSymbols = n;
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
bfd_close(lib);
|
||||
|
||||
int interesting = sp - externalSymbols;
|
||||
if (!quiet) {
|
||||
printf("%s provided %d symbols\n", aFileName,
|
||||
interesting - initialSymbols);
|
||||
}
|
||||
usefulSymbols = interesting;
|
||||
}
|
||||
|
||||
#endif /* USE_BFD */
|
|
@ -0,0 +1,107 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
#include "leaky.h"
|
||||
|
||||
#ifdef USE_COFF
|
||||
|
||||
#define LANGUAGE_C
|
||||
#include <sym.h>
|
||||
#include <cmplrs/stsupport.h>
|
||||
#include <symconst.h>
|
||||
#include <filehdr.h>
|
||||
#include <ldfcn.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef IRIX4
|
||||
extern "C" {
|
||||
extern char *demangle(char const* in);
|
||||
};
|
||||
#else
|
||||
#include <dem.h>
|
||||
#endif
|
||||
|
||||
static char *Demangle(char *rawName)
|
||||
{
|
||||
#ifdef IRIX4
|
||||
return strdup(demangle(rawName));
|
||||
#else
|
||||
char namebuf[4000];
|
||||
demangle(rawName, namebuf);
|
||||
return strdup(namebuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
void leaky::readSymbols(const char *fileName)
|
||||
{
|
||||
LDFILE *ldptr;
|
||||
|
||||
ldptr = ldopen(fileName, NULL);
|
||||
if (!ldptr) {
|
||||
fprintf(stderr, "%s: unable to open \"%s\"\n", applicationName,
|
||||
fileName);
|
||||
exit(-1);
|
||||
}
|
||||
if (PSYMTAB(ldptr) == 0) {
|
||||
fprintf(stderr, "%s: \"%s\": has no symbol table\n", applicationName,
|
||||
fileName);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
long isymMax = SYMHEADER(ldptr).isymMax;
|
||||
long iextMax = SYMHEADER(ldptr).iextMax;
|
||||
long iMax = isymMax + iextMax;
|
||||
|
||||
long alloced = 10000;
|
||||
Symbol* syms = (Symbol*) malloc(sizeof(Symbol) * 10000);
|
||||
Symbol* sp = syms;
|
||||
Symbol* last = syms + alloced;
|
||||
SYMR symr;
|
||||
|
||||
for (long isym = 0; isym < iMax; isym++) {
|
||||
if (ldtbread(ldptr, isym, &symr) != SUCCESS) {
|
||||
fprintf(stderr, "%s: can't read symbol #%d\n", applicationName,
|
||||
isym);
|
||||
exit(-1);
|
||||
}
|
||||
if (isym < isymMax) {
|
||||
if ((symr.st == stStaticProc)
|
||||
|| ((symr.st == stProc) &&
|
||||
((symr.sc == scText) || (symr.sc == scAbs)))
|
||||
|| ((symr.st == stBlock) &&
|
||||
(symr.sc == scText))) {
|
||||
// Text symbol. Set name field to point to the symbol name
|
||||
sp->name = Demangle(ldgetname(ldptr, &symr));
|
||||
sp->address = symr.value;
|
||||
sp++;
|
||||
if (sp >= last) {
|
||||
long n = alloced + 10000;
|
||||
syms = (Symbol*)
|
||||
realloc(syms, (size_t) (sizeof(Symbol) * n));
|
||||
last = syms + n;
|
||||
sp = syms + alloced;
|
||||
alloced = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int interesting = sp - syms;
|
||||
if (!quiet) {
|
||||
printf("Total of %d symbols\n", interesting);
|
||||
}
|
||||
usefulSymbols = interesting;
|
||||
externalSymbols = syms;
|
||||
}
|
||||
|
||||
#endif /* USE_COFF */
|
|
@ -0,0 +1,141 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
#include "leaky.h"
|
||||
|
||||
#ifdef USE_ELF
|
||||
|
||||
#include "leaky.h"
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <libelf/libelf.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
void leaky::readSymbols(const char *fileName)
|
||||
{
|
||||
int fd = ::open(fileName, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "%s: unable to open \"%s\"\n", applicationName,
|
||||
fileName);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
elf_version(EV_CURRENT);
|
||||
Elf *elf = elf_begin(fd, ELF_C_READ, 0);
|
||||
if (!elf) {
|
||||
fprintf(stderr, "%s: \"%s\": has no symbol table\n", applicationName,
|
||||
fileName);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
long alloced = 10000;
|
||||
Symbol* syms = (Symbol*) malloc(sizeof(Symbol) * 10000);
|
||||
Symbol* sp = syms;
|
||||
Symbol* last = syms + alloced;
|
||||
|
||||
// Get each of the relevant sections and add them to the list of
|
||||
// symbols.
|
||||
Elf32_Ehdr *ehdr = elf32_getehdr(elf);
|
||||
if (!ehdr) {
|
||||
fprintf(stderr, "%s: elf library lossage\n", applicationName);
|
||||
exit(-1);
|
||||
}
|
||||
#if 0
|
||||
Elf32_Half ndx = ehdr->e_shstrndx;
|
||||
#endif
|
||||
|
||||
Elf_Scn *scn = 0;
|
||||
int strtabndx = -1;
|
||||
for (int i = 1; (scn = elf_nextscn(elf, scn)) != 0; i++) {
|
||||
Elf32_Shdr *shdr = elf32_getshdr(scn);
|
||||
#if 0
|
||||
char *name = elf_strptr(elf, ndx, (size_t) shdr->sh_name);
|
||||
printf("Section %s (%d 0x%x)\n", name ? name : "(null)",
|
||||
shdr->sh_type, shdr->sh_type);
|
||||
#endif
|
||||
if (shdr->sh_type == SHT_STRTAB) {
|
||||
/* We assume here that string tables preceed symbol tables... */
|
||||
strtabndx = i;
|
||||
continue;
|
||||
}
|
||||
#if 0
|
||||
if (shdr->sh_type == SHT_DYNAMIC) {
|
||||
/* Dynamic */
|
||||
Elf_Data *data = elf_getdata(scn, 0);
|
||||
if (!data || !data->d_size) {
|
||||
printf("No data...");
|
||||
continue;
|
||||
}
|
||||
|
||||
Elf32_Dyn *dyn = (Elf32_Dyn*) data->d_buf;
|
||||
Elf32_Dyn *lastdyn =
|
||||
(Elf32_Dyn*) ((char*) data->d_buf + data->d_size);
|
||||
for (; dyn < lastdyn; dyn++) {
|
||||
printf("tag=%d value=0x%x\n", dyn->d_tag, dyn->d_un.d_val);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if ((shdr->sh_type == SHT_SYMTAB) ||
|
||||
(shdr->sh_type == SHT_DYNSYM)) {
|
||||
/* Symbol table */
|
||||
Elf_Data *data = elf_getdata(scn, 0);
|
||||
if (!data || !data->d_size) {
|
||||
printf("No data...");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* In theory we now have the symbols... */
|
||||
Elf32_Sym *esym = (Elf32_Sym*) data->d_buf;
|
||||
Elf32_Sym *lastsym =
|
||||
(Elf32_Sym*) ((char*) data->d_buf + data->d_size);
|
||||
for (; esym < lastsym; esym++) {
|
||||
#if 0
|
||||
char *nm = elf_strptr(elf, strtabndx, (size_t)esym->st_name);
|
||||
printf("%20s 0x%08x %02x %02x\n",
|
||||
nm, esym->st_value, ELF32_ST_BIND(esym->st_info),
|
||||
ELF32_ST_TYPE(esym->st_info));
|
||||
#endif
|
||||
if ((esym->st_value == 0) ||
|
||||
(ELF32_ST_BIND(esym->st_info) == STB_WEAK) ||
|
||||
(ELF32_ST_BIND(esym->st_info) == STB_NUM) ||
|
||||
(ELF32_ST_TYPE(esym->st_info) != STT_FUNC)) {
|
||||
continue;
|
||||
}
|
||||
#if 1
|
||||
char *nm = elf_strptr(elf, strtabndx, (size_t)esym->st_name);
|
||||
#endif
|
||||
sp->name = nm ? strdup(nm) : "(no name)";
|
||||
sp->address = esym->st_value;
|
||||
sp++;
|
||||
if (sp >= last) {
|
||||
long n = alloced + 10000;
|
||||
syms = (Symbol*)
|
||||
realloc(syms, (size_t) (sizeof(Symbol) * n));
|
||||
last = syms + n;
|
||||
sp = syms + alloced;
|
||||
alloced = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int interesting = sp - syms;
|
||||
if (!quiet) {
|
||||
printf("Total of %d symbols\n", interesting);
|
||||
}
|
||||
usefulSymbols = interesting;
|
||||
externalSymbols = syms;
|
||||
}
|
||||
|
||||
#endif /* USE_ELF */
|
|
@ -0,0 +1,67 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is James L. Nance.
|
||||
|
||||
#include "intcnt.h"
|
||||
|
||||
IntCount::IntCount() : numInts(0), iPair(0) { }
|
||||
IntCount::~IntCount() { delete [] iPair;}
|
||||
int IntCount::getSize() {return numInts;}
|
||||
int IntCount::getCount(int pos) {return iPair[pos].cnt;}
|
||||
int IntCount::getIndex(int pos) {return iPair[pos].idx;}
|
||||
|
||||
int IntCount::countAdd(int index, int increment)
|
||||
{
|
||||
if(numInts) {
|
||||
// Do a binary search to find the element
|
||||
int divPoint = 0;
|
||||
|
||||
if(index>iPair[numInts-1].idx) {
|
||||
divPoint = numInts;
|
||||
} else if(index<iPair[0].idx) {
|
||||
divPoint = 0;
|
||||
} else {
|
||||
int low=0, high=numInts-1;
|
||||
int mid = (low+high)/2;
|
||||
while(1) {
|
||||
mid = (low+high)/2;
|
||||
|
||||
if(index<iPair[mid].idx) {
|
||||
high = mid;
|
||||
} else if(index>iPair[mid].idx) {
|
||||
if(mid<numInts-1 && index<iPair[mid+1].idx) {
|
||||
divPoint = mid+1;
|
||||
break;
|
||||
} else {
|
||||
low = mid+1;
|
||||
}
|
||||
} else if(index==iPair[mid].idx) {
|
||||
return iPair[mid].cnt += increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IntPair *tpair = new IntPair[numInts+1];
|
||||
for(int i=0; i<divPoint; i++) tpair[i] = iPair[i];
|
||||
for(int i=divPoint; i<numInts; i++) tpair[i+1] = iPair[i];
|
||||
++numInts;
|
||||
delete [] iPair;
|
||||
iPair = tpair;
|
||||
iPair[divPoint].idx = index;
|
||||
iPair[divPoint].cnt = increment;
|
||||
return increment;
|
||||
} else {
|
||||
iPair = new IntPair[1];
|
||||
numInts = 1;
|
||||
iPair[0].idx = index;
|
||||
return iPair[0].cnt = increment;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is James L. Nance.
|
||||
|
||||
#ifndef INTCNT_H
|
||||
#define INTCNT_H
|
||||
|
||||
class IntCount
|
||||
{
|
||||
public:
|
||||
IntCount();
|
||||
~IntCount();
|
||||
int countAdd(int index, int increment=1);
|
||||
int countGet(int index);
|
||||
int getSize();
|
||||
int getCount(int pos);
|
||||
int getIndex(int pos);
|
||||
|
||||
private:
|
||||
IntCount(const IntCount&); // No copy constructor
|
||||
|
||||
int numInts;
|
||||
struct IntPair{int idx; int cnt;} *iPair;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,523 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
#include "leaky.h"
|
||||
#include "intcnt.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#ifndef NTO
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef NTO
|
||||
#include <mem.h>
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
static const u_int DefaultBuckets = 10007; // arbitrary, but prime
|
||||
static const u_int MaxBuckets = 1000003; // arbitrary, but prime
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
leaky* l = new leaky;
|
||||
|
||||
l->initialize(argc, argv);
|
||||
l->open();
|
||||
return 0;
|
||||
}
|
||||
|
||||
leaky::leaky()
|
||||
{
|
||||
applicationName = NULL;
|
||||
logFile = NULL;
|
||||
progFile = NULL;
|
||||
|
||||
quiet = TRUE;
|
||||
showAddress = FALSE;
|
||||
stackDepth = 100000;
|
||||
|
||||
mappedLogFile = -1;
|
||||
firstLogEntry = lastLogEntry = 0;
|
||||
|
||||
sfd = -1;
|
||||
externalSymbols = 0;
|
||||
usefulSymbols = 0;
|
||||
numExternalSymbols = 0;
|
||||
lowestSymbolAddr = 0;
|
||||
highestSymbolAddr = 0;
|
||||
|
||||
loadMap = NULL;
|
||||
}
|
||||
|
||||
leaky::~leaky()
|
||||
{
|
||||
}
|
||||
|
||||
void leaky::usageError()
|
||||
{
|
||||
fprintf(stderr, "Usage: %s 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];
|
||||
} else {
|
||||
applicationName++;
|
||||
}
|
||||
|
||||
int arg;
|
||||
int errflg = 0;
|
||||
while ((arg = getopt(argc, argv, "adEe:gh:i:r:Rs:tqx")) != -1) {
|
||||
switch (arg) {
|
||||
case '?':
|
||||
errflg++;
|
||||
break;
|
||||
case 'a':
|
||||
break;
|
||||
case 'A':
|
||||
showAddress = TRUE;
|
||||
break;
|
||||
case 'd':
|
||||
break;
|
||||
case 'R':
|
||||
break;
|
||||
case 'e':
|
||||
exclusions.add(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
break;
|
||||
case 'r':
|
||||
roots.add(optarg);
|
||||
if (!includes.IsEmpty()) {
|
||||
errflg++;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
includes.add(optarg);
|
||||
if (!roots.IsEmpty()) {
|
||||
errflg++;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
break;
|
||||
case 's':
|
||||
stackDepth = atoi(optarg);
|
||||
if (stackDepth < 2) {
|
||||
stackDepth = 2;
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
break;
|
||||
case 'q':
|
||||
quiet = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (errflg || ((argc - optind) < 2)) {
|
||||
usageError();
|
||||
}
|
||||
progFile = argv[optind++];
|
||||
logFile = argv[optind];
|
||||
}
|
||||
|
||||
static void* mapFile(int fd, u_int flags, off_t* sz)
|
||||
{
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb) < 0) {
|
||||
perror("fstat");
|
||||
exit(-1);
|
||||
}
|
||||
void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0);
|
||||
if (!base) {
|
||||
perror("mmap");
|
||||
exit(-1);
|
||||
}
|
||||
*sz = sb.st_size;
|
||||
return base;
|
||||
}
|
||||
|
||||
void leaky::LoadMap()
|
||||
{
|
||||
malloc_map_entry mme;
|
||||
char name[1000];
|
||||
|
||||
int fd = ::open(M_MAPFILE, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open: " M_MAPFILE);
|
||||
exit(-1);
|
||||
}
|
||||
for (;;) {
|
||||
int nb = read(fd, &mme, sizeof(mme));
|
||||
if (nb != sizeof(mme)) break;
|
||||
nb = read(fd, name, mme.nameLen);
|
||||
if (nb != (int)mme.nameLen) break;
|
||||
name[mme.nameLen] = 0;
|
||||
if (!quiet) {
|
||||
printf("%s @ %lx\n", name, mme.address);
|
||||
}
|
||||
|
||||
LoadMapEntry* lme = new LoadMapEntry;
|
||||
lme->address = mme.address;
|
||||
lme->name = strdup(name);
|
||||
lme->next = loadMap;
|
||||
loadMap = lme;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void leaky::open()
|
||||
{
|
||||
LoadMap();
|
||||
|
||||
setupSymbols(progFile);
|
||||
|
||||
// open up the log file
|
||||
mappedLogFile = ::open(logFile, O_RDONLY);
|
||||
if (mappedLogFile < 0) {
|
||||
perror("open");
|
||||
exit(-1);
|
||||
}
|
||||
off_t size;
|
||||
firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size);
|
||||
lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size);
|
||||
|
||||
analyze();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void leaky::ReadSharedLibrarySymbols()
|
||||
{
|
||||
LoadMapEntry* lme = loadMap;
|
||||
while (NULL != lme) {
|
||||
ReadSymbols(lme->name, lme->address);
|
||||
lme = lme->next;
|
||||
}
|
||||
}
|
||||
|
||||
void leaky::setupSymbols(const char *fileName)
|
||||
{
|
||||
// Read in symbols from the program
|
||||
ReadSymbols(fileName, 0);
|
||||
|
||||
// Read in symbols from the .so's
|
||||
ReadSharedLibrarySymbols();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Binary search the table, looking for a symbol that covers this
|
||||
// address.
|
||||
int leaky::findSymbolIndex(u_long addr)
|
||||
{
|
||||
u_int base = 0;
|
||||
u_int limit = usefulSymbols - 1;
|
||||
Symbol* end = &externalSymbols[limit];
|
||||
while (base <= limit) {
|
||||
u_int midPoint = (base + limit)>>1;
|
||||
Symbol* sp = &externalSymbols[midPoint];
|
||||
if (addr < sp->address) {
|
||||
if (midPoint == 0) {
|
||||
return -1;
|
||||
}
|
||||
limit = midPoint - 1;
|
||||
} else {
|
||||
if (sp+1 < end) {
|
||||
if (addr < (sp+1)->address) {
|
||||
return midPoint;
|
||||
}
|
||||
} else {
|
||||
return midPoint;
|
||||
}
|
||||
base = midPoint + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Symbol* leaky::findSymbol(u_long addr)
|
||||
{
|
||||
int idx = findSymbolIndex(addr);
|
||||
|
||||
if(idx<0) {
|
||||
return NULL;
|
||||
} else {
|
||||
return &externalSymbols[idx];
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
bool leaky::excluded(malloc_log_entry* lep)
|
||||
{
|
||||
if (exclusions.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = lep->numpcs;
|
||||
for (u_int i = 0; i < n; i++, pcp++) {
|
||||
Symbol* sp = findSymbol((u_long) *pcp);
|
||||
if (sp && exclusions.contains(sp->name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool leaky::included(malloc_log_entry* lep)
|
||||
{
|
||||
if (includes.IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = lep->numpcs;
|
||||
for (u_int i = 0; i < n; i++, pcp++) {
|
||||
Symbol* sp = findSymbol((u_long) *pcp);
|
||||
if (sp && includes.contains(sp->name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void leaky::displayStackTrace(FILE* out, malloc_log_entry* lep)
|
||||
{
|
||||
char** pcp = &lep->pcs[0];
|
||||
u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
|
||||
for (u_int i = 0; i < n; i++, pcp++) {
|
||||
u_long addr = (u_long) *pcp;
|
||||
Symbol* sp = findSymbol(addr);
|
||||
if (sp) {
|
||||
fputs(sp->name, out);
|
||||
if (showAddress) {
|
||||
fprintf(out, "[%p]", (char*)addr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(out, "<%p>", (char*)addr);
|
||||
}
|
||||
fputc(' ', out);
|
||||
}
|
||||
fputc('\n', out);
|
||||
}
|
||||
|
||||
void leaky::dumpEntryToLog(malloc_log_entry* lep)
|
||||
{
|
||||
printf("%ld\t", lep->delTime);
|
||||
printf(" --> ");
|
||||
displayStackTrace(stdout, lep);
|
||||
}
|
||||
|
||||
void leaky::generateReportHTML(FILE *fp, int *countArray, int count)
|
||||
{
|
||||
fprintf(fp,"<html><head><title>Jprof Profile Report</title></head><body>\n");
|
||||
fprintf(fp,"<h1><center>Jprof Profile Report</center></h1>\n");
|
||||
fprintf(fp,"<center>");
|
||||
fprintf(fp,"<A href=#flat>Flat</A> <A href=#hier>hierarchical</A>");
|
||||
fprintf(fp,"</center><P><P><P>\n");
|
||||
|
||||
int *rankingTable = new int[usefulSymbols];
|
||||
|
||||
for(int cnt=usefulSymbols; --cnt>=0; rankingTable[cnt]=cnt);
|
||||
|
||||
// Drat. I would use ::qsort() but I would need a global variable and my
|
||||
// into-pascal professor threatened to flunk anyone who used globals.
|
||||
// She dammaged me for life :-) (That was 1986. See how much influence
|
||||
// she had. I dont remember her name but I always feel guilty about globals)
|
||||
|
||||
// Shell Sort. 581130733 is the max 31 bit value of h = 3h+1
|
||||
for(int mx=usefulSymbols/9, h=581130733; h>0; h/=3) {
|
||||
if(h<mx) {
|
||||
for(int i = h-1; i<usefulSymbols; i++) {
|
||||
int j, tmp=rankingTable[i], val = countArray[tmp];
|
||||
for(j = i; (j>=h) && (countArray[rankingTable[j-h]]<val); j-=h) {
|
||||
rankingTable[j] = rankingTable[j-h];
|
||||
}
|
||||
rankingTable[j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, We are sorted now. Lets go through the table until we get to
|
||||
// functions that were never called. Right now we dont do much inside
|
||||
// this loop. Later we can get callers and callees into it like gprof
|
||||
// does
|
||||
fprintf(fp,
|
||||
"<h2><A NAME=hier></A><center>Hierarchical Profile</center></h2><hr>\n");
|
||||
fprintf(fp, "<pre>\n");
|
||||
fprintf(fp, "%5s %5s %4s %s\n",
|
||||
"index", "Count", "Hits", "Function Name");
|
||||
|
||||
for(int i=0; i<usefulSymbols && countArray[rankingTable[i]]>0; i++) {
|
||||
Symbol *sp=&externalSymbols[rankingTable[i]];
|
||||
|
||||
sp->cntP.printReport(fp, this);
|
||||
|
||||
fprintf(fp, "%6d %3d %8d <A name=%d>%s</a>\n", rankingTable[i],
|
||||
sp->timerHit, countArray[rankingTable[i]], rankingTable[i],sp->name);
|
||||
|
||||
sp->cntC.printReport(fp, this);
|
||||
|
||||
fprintf(fp, "<hr>\n");
|
||||
}
|
||||
fprintf(fp,"</pre>\n");
|
||||
|
||||
// OK, Now we want to print the flat profile. To do this we resort on
|
||||
// the hit count.
|
||||
|
||||
// Cut-N-Paste Shell sort from above. The Ranking Table has already been
|
||||
// populated, so we do not have to reinitialize it.
|
||||
for(int mx=usefulSymbols/9, h=581130733; h>0; h/=3) {
|
||||
if(h<mx) {
|
||||
for(int i = h-1; i<usefulSymbols; i++) {
|
||||
int j, tmp=rankingTable[i], val = externalSymbols[tmp].timerHit;
|
||||
for(j = i;
|
||||
(j>=h) && (externalSymbols[rankingTable[j-h]].timerHit<val); j-=h) {
|
||||
rankingTable[j] = rankingTable[j-h];
|
||||
}
|
||||
rankingTable[j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(fp,"<h2><A NAME=flat></A><center>Flat Profile</center></h2><br>\n");
|
||||
fprintf(fp, "<pre>\n");
|
||||
fprintf(fp, "Count Function Name\n");
|
||||
// Now loop for as long as we have timer hits
|
||||
for(int i=0;
|
||||
i<usefulSymbols && externalSymbols[rankingTable[i]].timerHit>0; i++) {
|
||||
|
||||
Symbol *sp=&externalSymbols[rankingTable[i]];
|
||||
|
||||
fprintf(fp, "%3d <A href=\"#%d\">%s</a>\n",
|
||||
sp->timerHit, rankingTable[i], sp->name);
|
||||
}
|
||||
|
||||
fprintf(fp,"</pre></body></html>\n");
|
||||
}
|
||||
|
||||
void leaky::analyze()
|
||||
{
|
||||
int *countArray = new int[usefulSymbols];
|
||||
int *flagArray = new int[usefulSymbols];
|
||||
|
||||
//Zero our function call counter
|
||||
memset(countArray, 0, sizeof(countArray[0])*usefulSymbols);
|
||||
|
||||
// The flag array is used to prevent counting symbols multiple times
|
||||
// if functions are called recursivly. In order to keep from having
|
||||
// to zero it on each pass through the loop, we mark it with the value
|
||||
// of stacks on each trip through the loop. This means we can determine
|
||||
// if we have seen this symbol for this stack trace w/o having to reset
|
||||
// from the prior stacktrace.
|
||||
memset(flagArray, -1, sizeof(flagArray[0])*usefulSymbols);
|
||||
|
||||
// This loop walks through all the call stacks we recorded
|
||||
stacks = 0;
|
||||
for(malloc_log_entry* lep=firstLogEntry;
|
||||
lep < lastLogEntry;
|
||||
lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) {
|
||||
|
||||
++stacks; // How many stack frames did we collect
|
||||
|
||||
// This loop walks through every symbol in the call stack. By walking it
|
||||
// backwards we know who called the function when we get there.
|
||||
u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth;
|
||||
char** pcp = &lep->pcs[n-1];
|
||||
int idx, parrentIdx = -1;
|
||||
for(int i=n-1; i>=0; --i, --pcp, parrentIdx=idx) {
|
||||
idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp));
|
||||
if(idx>=0) {
|
||||
|
||||
// If we have not seen this symbol before count it and mark it as seen
|
||||
if(flagArray[idx]!=stacks && ((flagArray[idx]=stacks) || true)) {
|
||||
++countArray[idx];
|
||||
}
|
||||
|
||||
// We know who we are and we know who our parrent is. Count this
|
||||
if(parrentIdx>=0) {
|
||||
externalSymbols[parrentIdx].regChild(idx);
|
||||
externalSymbols[idx].regParrent(parrentIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// idx should be the function that we were in when we recieved the signal.
|
||||
if(idx>=0) {
|
||||
++externalSymbols[idx].timerHit;
|
||||
}
|
||||
}
|
||||
|
||||
generateReportHTML(stdout, countArray, stacks);
|
||||
}
|
||||
|
||||
void FunctionCount::printReport(FILE *fp, leaky *lk)
|
||||
{
|
||||
const char *fmt = " %6d <A href=\"#%d\">%s</A>\n";
|
||||
|
||||
int nmax, tmax=((~0U)>>1);
|
||||
|
||||
do {
|
||||
nmax=0;
|
||||
for(int j=getSize(); --j>=0;) {
|
||||
int cnt = getCount(j);
|
||||
if(cnt==tmax) {
|
||||
int idx = getIndex(j);
|
||||
fprintf(fp, fmt, getCount(j), idx,
|
||||
const_cast<char*>(lk->indexToName(idx)));
|
||||
} else if(cnt<tmax && cnt>nmax) {
|
||||
nmax=cnt;
|
||||
}
|
||||
}
|
||||
} while((tmax=nmax)>0);
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
#ifndef __leaky_h_
|
||||
#define __leaky_h_
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include "libmalloc.h"
|
||||
#include "strset.h"
|
||||
#include "intcnt.h"
|
||||
|
||||
typedef unsigned int u_int;
|
||||
|
||||
struct Symbol;
|
||||
struct leaky;
|
||||
|
||||
class FunctionCount : public IntCount
|
||||
{
|
||||
public:
|
||||
void printReport(FILE *fp, leaky *lk);
|
||||
};
|
||||
|
||||
struct Symbol {
|
||||
char* name;
|
||||
u_long address;
|
||||
int timerHit;
|
||||
FunctionCount cntP, cntC;
|
||||
|
||||
int regChild(int id) {return cntC.countAdd(id, 1);}
|
||||
int regParrent(int id) {return cntP.countAdd(id, 1);}
|
||||
|
||||
Symbol() : timerHit(0) {}
|
||||
void Init(const char* aName, u_long aAddress) {
|
||||
name = aName ? strdup(aName) : (char *)"";
|
||||
address = aAddress;
|
||||
}
|
||||
};
|
||||
|
||||
struct LoadMapEntry {
|
||||
char* name; // name of .so
|
||||
u_long address; // base address where it was mapped in
|
||||
LoadMapEntry* next;
|
||||
};
|
||||
|
||||
struct leaky {
|
||||
leaky();
|
||||
~leaky();
|
||||
|
||||
void initialize(int argc, char** argv);
|
||||
void open();
|
||||
|
||||
char* applicationName;
|
||||
char* logFile;
|
||||
char* progFile;
|
||||
|
||||
int quiet;
|
||||
int showAddress;
|
||||
u_int stackDepth;
|
||||
|
||||
int mappedLogFile;
|
||||
malloc_log_entry* firstLogEntry;
|
||||
malloc_log_entry* lastLogEntry;
|
||||
|
||||
u_long stacks;
|
||||
|
||||
int sfd;
|
||||
Symbol* externalSymbols;
|
||||
u_int usefulSymbols;
|
||||
u_int numExternalSymbols;
|
||||
StrSet exclusions;
|
||||
u_long lowestSymbolAddr;
|
||||
u_long highestSymbolAddr;
|
||||
|
||||
LoadMapEntry* loadMap;
|
||||
|
||||
StrSet roots;
|
||||
StrSet includes;
|
||||
|
||||
void usageError();
|
||||
|
||||
void LoadMap();
|
||||
|
||||
void analyze();
|
||||
|
||||
void dumpEntryToLog(malloc_log_entry* lep);
|
||||
|
||||
void insertAddress(u_long address, malloc_log_entry* lep);
|
||||
void removeAddress(u_long address, malloc_log_entry* lep);
|
||||
|
||||
void displayStackTrace(FILE* out, malloc_log_entry* lep);
|
||||
|
||||
void ReadSymbols(const char* fileName, u_long aBaseAddress);
|
||||
void ReadSharedLibrarySymbols();
|
||||
void setupSymbols(const char* fileName);
|
||||
Symbol* findSymbol(u_long address);
|
||||
bool excluded(malloc_log_entry* lep);
|
||||
bool included(malloc_log_entry* lep);
|
||||
const char* indexToName(int idx) {return externalSymbols[idx].name;}
|
||||
|
||||
private:
|
||||
void generateReportHTML(FILE *fp, int *countArray, int count);
|
||||
int findSymbolIndex(u_long address);
|
||||
};
|
||||
|
||||
#endif /* __leaky_h_ */
|
|
@ -0,0 +1,250 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
// Portions Copyright 1999 by Jim Nance
|
||||
|
||||
// The linux glibc hides part of sigaction if _POSIX_SOURCE is defined
|
||||
#if defined(linux)
|
||||
#undef _POSIX_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "libmalloc.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
|
||||
#ifdef NTO
|
||||
#include <sys/link.h>
|
||||
extern r_debug _r_debug;
|
||||
#else
|
||||
#include <link.h>
|
||||
#endif
|
||||
|
||||
#ifdef NTO
|
||||
#define JB_BP 0x08
|
||||
#include <setjmp.h>
|
||||
#endif
|
||||
|
||||
|
||||
static int gLogFD = -1;
|
||||
|
||||
static void startSignalCounter(unsigned long milisec);
|
||||
|
||||
static void writeStrStdout(const char* str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
if(len) {
|
||||
int wc, tot=0;
|
||||
do {
|
||||
wc = write(1, str, len);
|
||||
} while(wc>0 && (tot+=wc)<len);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
#if defined(i386)
|
||||
static void CrawlStack(malloc_log_entry* me, jmp_buf jb, char* first)
|
||||
{
|
||||
#ifdef NTO
|
||||
u_long* bp = (u_long*) (jb[0].__savearea[JB_BP]);
|
||||
#else
|
||||
u_long* bp = (u_long*) (jb[0].__jmpbuf[JB_BP]);
|
||||
#endif
|
||||
u_long numpcs = 0;
|
||||
|
||||
// This is a linux hack we have to do to figure out where the signal was
|
||||
// called from
|
||||
me->pcs[numpcs++] = first;
|
||||
|
||||
int skip = 3;
|
||||
while (numpcs < MAX_STACK_CRAWL) {
|
||||
u_long* nextbp = (u_long*) *bp++;
|
||||
u_long pc = *bp;
|
||||
if ((pc < 0x08000000) || (pc > 0x7fffffff) || (nextbp < bp)) {
|
||||
break;
|
||||
}
|
||||
if (--skip < 0) {
|
||||
me->pcs[numpcs++] = (char*) pc;
|
||||
}
|
||||
bp = nextbp;
|
||||
}
|
||||
me->numpcs = numpcs;
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
#if defined(linux) || defined(NTO)
|
||||
static void DumpAddressMap()
|
||||
{
|
||||
// Turn off the timer so we dont get interrupts during shutdown
|
||||
startSignalCounter(0);
|
||||
|
||||
int mfd = open(M_MAPFILE, O_CREAT|O_WRONLY|O_TRUNC, 0666);
|
||||
if (mfd >= 0) {
|
||||
malloc_map_entry mme;
|
||||
link_map* map = _r_debug.r_map;
|
||||
while (NULL != map) {
|
||||
if (0 != map->l_addr) {
|
||||
mme.nameLen = strlen(map->l_name);
|
||||
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;
|
||||
}
|
||||
close(mfd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
Log(u_long aTime, char *first)
|
||||
{
|
||||
// Static is simply to make debugging tollerable
|
||||
static malloc_log_entry me;
|
||||
|
||||
me.delTime = aTime;
|
||||
|
||||
jmp_buf jb;
|
||||
setjmp(jb);
|
||||
CrawlStack(&me, jb, first);
|
||||
|
||||
write(gLogFD, &me, offsetof(malloc_log_entry, pcs) + me.numpcs*sizeof(char*));
|
||||
}
|
||||
|
||||
/* Lets interrupt at 10 Hz. This is so my log files don't get too large.
|
||||
* This can be changed to a faster value latter. This timer is not
|
||||
* programmed to reset, even though it is capable of doing so. This is
|
||||
* to keep from getting interrupts from inside of the handler.
|
||||
*/
|
||||
static void startSignalCounter(unsigned long milisec)
|
||||
{
|
||||
struct itimerval tvalue;
|
||||
|
||||
tvalue.it_interval.tv_sec = 0;
|
||||
tvalue.it_interval.tv_usec = 0;
|
||||
tvalue.it_value.tv_sec = milisec/1000;
|
||||
tvalue.it_value.tv_usec = (milisec%1000)*1000;
|
||||
|
||||
setitimer(ITIMER_PROF, &tvalue, NULL);
|
||||
}
|
||||
|
||||
static long timerMiliSec = 50;
|
||||
|
||||
static void StackHook(
|
||||
int signum,
|
||||
siginfo_t *info,
|
||||
void *mystry)
|
||||
{
|
||||
static struct timeval tFirst;
|
||||
static int first=1;
|
||||
size_t milisec;
|
||||
|
||||
if(first && !(first=0)) {
|
||||
gettimeofday(&tFirst, 0);
|
||||
milisec = 0;
|
||||
} else {
|
||||
struct timeval tNow;
|
||||
gettimeofday(&tNow, 0);
|
||||
double usec = 1e6*(tNow.tv_sec - tFirst.tv_sec);
|
||||
usec += (tNow.tv_usec - tFirst.tv_usec);
|
||||
milisec = static_cast<size_t>(usec*1e-3);
|
||||
}
|
||||
|
||||
// The mystry[19] thing is a hack to figure out where we were called from.
|
||||
// By playing around with the debugger it looks like [19] contains the
|
||||
// information I need.
|
||||
Log(milisec, ((char**)mystry)[19]);
|
||||
startSignalCounter(timerMiliSec);
|
||||
}
|
||||
|
||||
void setupProfilingStuff(void)
|
||||
{
|
||||
static int gFirstTime = 1;
|
||||
if(gFirstTime && !(gFirstTime=0)) {
|
||||
int startTimer = 1;
|
||||
int doNotStart = 1;
|
||||
char *tst = getenv("JPROF_FLAGS");
|
||||
|
||||
/* Options from JPROF_FLAGS environment variable:
|
||||
* JP_DEFER -> Wait for a SIGPROF from userland before starting
|
||||
* to generate them internally
|
||||
* JP_START -> Install the signal handler
|
||||
* JP_PERIOD -> Time between prifiler ticks
|
||||
*/
|
||||
if(tst) {
|
||||
if(strstr(tst, "JP_DEFER")) startTimer = 0;
|
||||
if(strstr(tst, "JP_START")) doNotStart = 0;
|
||||
|
||||
char *delay = strstr(tst,"JP_PERIOD=");
|
||||
if(delay) {
|
||||
double tmp = strtod(delay+10, NULL);
|
||||
if(tmp>1e-3) {
|
||||
timerMiliSec = static_cast<unsigned long>(1000 * tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!doNotStart) {
|
||||
|
||||
if(gLogFD<0) {
|
||||
gLogFD = open(M_LOGFILE, O_CREAT|O_WRONLY|O_TRUNC, 0666);
|
||||
if(gLogFD<0) {
|
||||
fprintf(stderr, "Unable to create " M_LOGFILE);
|
||||
perror(":");
|
||||
} else {
|
||||
struct sigaction action;
|
||||
sigset_t mset;
|
||||
char buffer[1024];
|
||||
|
||||
// Dump out the address map when we terminate
|
||||
atexit(DumpAddressMap);
|
||||
|
||||
sigemptyset(&mset);
|
||||
action.sa_handler = NULL;
|
||||
action.sa_sigaction = StackHook;
|
||||
action.sa_mask = mset;
|
||||
action.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
sigaction(SIGPROF, &action, NULL);
|
||||
sprintf(buffer, "Jprof: Initialized signal hander and set "
|
||||
"timer for %lu mili-seconds\n", timerMiliSec);
|
||||
writeStrStdout(buffer);
|
||||
|
||||
if(startTimer) {
|
||||
writeStrStdout("Jprof: started timer\n");
|
||||
startSignalCounter(timerMiliSec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("setupProfilingStuff() called multiple times\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
#include "strset.h"
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
StrSet::StrSet()
|
||||
{
|
||||
strings = 0;
|
||||
numstrings = 0;
|
||||
}
|
||||
|
||||
void StrSet::add(const char* s)
|
||||
{
|
||||
if (strings) {
|
||||
strings = (char**) realloc(strings, (numstrings + 1) * sizeof(char*));
|
||||
} else {
|
||||
strings = (char**) malloc(sizeof(char*));
|
||||
}
|
||||
strings[numstrings] = strdup(s);
|
||||
numstrings++;
|
||||
}
|
||||
|
||||
int StrSet::contains(const char* s)
|
||||
{
|
||||
char** sp = strings;
|
||||
int i = numstrings;
|
||||
|
||||
while (--i >= 0) {
|
||||
char *ss = *sp++;
|
||||
if (ss[0] == s[0]) {
|
||||
if (strcmp(ss, s) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
#ifndef __strset_h_
|
||||
#define __strset_h_
|
||||
|
||||
struct StrSet {
|
||||
StrSet();
|
||||
|
||||
void add(const char* string);
|
||||
int contains(const char* string);
|
||||
bool IsEmpty() const { return 0 == numstrings; }
|
||||
|
||||
char** strings;
|
||||
int numstrings;
|
||||
};
|
||||
|
||||
#endif /* __strset_h_ */
|
|
@ -0,0 +1,25 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
#ifndef config_h___
|
||||
#define config_h___
|
||||
|
||||
#define MAX_STACK_CRAWL 100
|
||||
#define M_LOGFILE "jprof-log"
|
||||
#define M_MAPFILE "jprof-map"
|
||||
|
||||
#if defined(linux) || defined(NTO)
|
||||
#define USE_BFD
|
||||
#undef NEED_WRAPPERS
|
||||
|
||||
#endif /* linux */
|
||||
|
||||
#endif /* config_h___ */
|
|
@ -0,0 +1,18 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is James L. Nance.
|
||||
|
||||
#ifndef jprof_h___
|
||||
#define jprof_h___
|
||||
|
||||
void setupProfilingStuff(void);
|
||||
|
||||
#endif /* jprof_h___ */
|
|
@ -0,0 +1,250 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
// Portions Copyright 1999 by Jim Nance
|
||||
|
||||
// The linux glibc hides part of sigaction if _POSIX_SOURCE is defined
|
||||
#if defined(linux)
|
||||
#undef _POSIX_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "libmalloc.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
|
||||
#ifdef NTO
|
||||
#include <sys/link.h>
|
||||
extern r_debug _r_debug;
|
||||
#else
|
||||
#include <link.h>
|
||||
#endif
|
||||
|
||||
#ifdef NTO
|
||||
#define JB_BP 0x08
|
||||
#include <setjmp.h>
|
||||
#endif
|
||||
|
||||
|
||||
static int gLogFD = -1;
|
||||
|
||||
static void startSignalCounter(unsigned long milisec);
|
||||
|
||||
static void writeStrStdout(const char* str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
if(len) {
|
||||
int wc, tot=0;
|
||||
do {
|
||||
wc = write(1, str, len);
|
||||
} while(wc>0 && (tot+=wc)<len);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
#if defined(i386)
|
||||
static void CrawlStack(malloc_log_entry* me, jmp_buf jb, char* first)
|
||||
{
|
||||
#ifdef NTO
|
||||
u_long* bp = (u_long*) (jb[0].__savearea[JB_BP]);
|
||||
#else
|
||||
u_long* bp = (u_long*) (jb[0].__jmpbuf[JB_BP]);
|
||||
#endif
|
||||
u_long numpcs = 0;
|
||||
|
||||
// This is a linux hack we have to do to figure out where the signal was
|
||||
// called from
|
||||
me->pcs[numpcs++] = first;
|
||||
|
||||
int skip = 3;
|
||||
while (numpcs < MAX_STACK_CRAWL) {
|
||||
u_long* nextbp = (u_long*) *bp++;
|
||||
u_long pc = *bp;
|
||||
if ((pc < 0x08000000) || (pc > 0x7fffffff) || (nextbp < bp)) {
|
||||
break;
|
||||
}
|
||||
if (--skip < 0) {
|
||||
me->pcs[numpcs++] = (char*) pc;
|
||||
}
|
||||
bp = nextbp;
|
||||
}
|
||||
me->numpcs = numpcs;
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
#if defined(linux) || defined(NTO)
|
||||
static void DumpAddressMap()
|
||||
{
|
||||
// Turn off the timer so we dont get interrupts during shutdown
|
||||
startSignalCounter(0);
|
||||
|
||||
int mfd = open(M_MAPFILE, O_CREAT|O_WRONLY|O_TRUNC, 0666);
|
||||
if (mfd >= 0) {
|
||||
malloc_map_entry mme;
|
||||
link_map* map = _r_debug.r_map;
|
||||
while (NULL != map) {
|
||||
if (0 != map->l_addr) {
|
||||
mme.nameLen = strlen(map->l_name);
|
||||
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;
|
||||
}
|
||||
close(mfd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
Log(u_long aTime, char *first)
|
||||
{
|
||||
// Static is simply to make debugging tollerable
|
||||
static malloc_log_entry me;
|
||||
|
||||
me.delTime = aTime;
|
||||
|
||||
jmp_buf jb;
|
||||
setjmp(jb);
|
||||
CrawlStack(&me, jb, first);
|
||||
|
||||
write(gLogFD, &me, offsetof(malloc_log_entry, pcs) + me.numpcs*sizeof(char*));
|
||||
}
|
||||
|
||||
/* Lets interrupt at 10 Hz. This is so my log files don't get too large.
|
||||
* This can be changed to a faster value latter. This timer is not
|
||||
* programmed to reset, even though it is capable of doing so. This is
|
||||
* to keep from getting interrupts from inside of the handler.
|
||||
*/
|
||||
static void startSignalCounter(unsigned long milisec)
|
||||
{
|
||||
struct itimerval tvalue;
|
||||
|
||||
tvalue.it_interval.tv_sec = 0;
|
||||
tvalue.it_interval.tv_usec = 0;
|
||||
tvalue.it_value.tv_sec = milisec/1000;
|
||||
tvalue.it_value.tv_usec = (milisec%1000)*1000;
|
||||
|
||||
setitimer(ITIMER_PROF, &tvalue, NULL);
|
||||
}
|
||||
|
||||
static long timerMiliSec = 50;
|
||||
|
||||
static void StackHook(
|
||||
int signum,
|
||||
siginfo_t *info,
|
||||
void *mystry)
|
||||
{
|
||||
static struct timeval tFirst;
|
||||
static int first=1;
|
||||
size_t milisec;
|
||||
|
||||
if(first && !(first=0)) {
|
||||
gettimeofday(&tFirst, 0);
|
||||
milisec = 0;
|
||||
} else {
|
||||
struct timeval tNow;
|
||||
gettimeofday(&tNow, 0);
|
||||
double usec = 1e6*(tNow.tv_sec - tFirst.tv_sec);
|
||||
usec += (tNow.tv_usec - tFirst.tv_usec);
|
||||
milisec = static_cast<size_t>(usec*1e-3);
|
||||
}
|
||||
|
||||
// The mystry[19] thing is a hack to figure out where we were called from.
|
||||
// By playing around with the debugger it looks like [19] contains the
|
||||
// information I need.
|
||||
Log(milisec, ((char**)mystry)[19]);
|
||||
startSignalCounter(timerMiliSec);
|
||||
}
|
||||
|
||||
void setupProfilingStuff(void)
|
||||
{
|
||||
static int gFirstTime = 1;
|
||||
if(gFirstTime && !(gFirstTime=0)) {
|
||||
int startTimer = 1;
|
||||
int doNotStart = 1;
|
||||
char *tst = getenv("JPROF_FLAGS");
|
||||
|
||||
/* Options from JPROF_FLAGS environment variable:
|
||||
* JP_DEFER -> Wait for a SIGPROF from userland before starting
|
||||
* to generate them internally
|
||||
* JP_START -> Install the signal handler
|
||||
* JP_PERIOD -> Time between prifiler ticks
|
||||
*/
|
||||
if(tst) {
|
||||
if(strstr(tst, "JP_DEFER")) startTimer = 0;
|
||||
if(strstr(tst, "JP_START")) doNotStart = 0;
|
||||
|
||||
char *delay = strstr(tst,"JP_PERIOD=");
|
||||
if(delay) {
|
||||
double tmp = strtod(delay+10, NULL);
|
||||
if(tmp>1e-3) {
|
||||
timerMiliSec = static_cast<unsigned long>(1000 * tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!doNotStart) {
|
||||
|
||||
if(gLogFD<0) {
|
||||
gLogFD = open(M_LOGFILE, O_CREAT|O_WRONLY|O_TRUNC, 0666);
|
||||
if(gLogFD<0) {
|
||||
fprintf(stderr, "Unable to create " M_LOGFILE);
|
||||
perror(":");
|
||||
} else {
|
||||
struct sigaction action;
|
||||
sigset_t mset;
|
||||
char buffer[1024];
|
||||
|
||||
// Dump out the address map when we terminate
|
||||
atexit(DumpAddressMap);
|
||||
|
||||
sigemptyset(&mset);
|
||||
action.sa_handler = NULL;
|
||||
action.sa_sigaction = StackHook;
|
||||
action.sa_mask = mset;
|
||||
action.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
sigaction(SIGPROF, &action, NULL);
|
||||
sprintf(buffer, "Jprof: Initialized signal hander and set "
|
||||
"timer for %lu mili-seconds\n", timerMiliSec);
|
||||
writeStrStdout(buffer);
|
||||
|
||||
if(startTimer) {
|
||||
writeStrStdout("Jprof: started timer\n");
|
||||
startSignalCounter(timerMiliSec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("setupProfilingStuff() called multiple times\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// The contents of this file are subject to the Mozilla Public License
|
||||
// Version 1.1 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License
|
||||
// at http://www.mozilla.org/MPL/
|
||||
//
|
||||
// Software distributed under the License is distributed on an "AS IS"
|
||||
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
// the License for the specific language governing rights and
|
||||
// limitations under the License.
|
||||
//
|
||||
// The Initial Developer of the Original Code is Kipp E.B. Hickman.
|
||||
|
||||
#ifndef libmalloc_h___
|
||||
#define libmalloc_h___
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
typedef unsigned long u_long;
|
||||
|
||||
// Format of a malloc log entry. This is whats written out to the
|
||||
// "malloc-log" file.
|
||||
struct malloc_log_entry {
|
||||
u_long delTime;
|
||||
u_long numpcs;
|
||||
char* pcs[MAX_STACK_CRAWL];
|
||||
};
|
||||
|
||||
// type's
|
||||
#define malloc_log_stack 7
|
||||
|
||||
// Format of a malloc map entry; after this struct is nameLen+1 bytes of
|
||||
// name data.
|
||||
struct malloc_map_entry {
|
||||
u_long nameLen;
|
||||
u_long address; // base address
|
||||
};
|
||||
|
||||
// A method that can be called if you want to programmatically control
|
||||
// the malloc logging. Note that you must link with the library to do
|
||||
// this (or use dlsym after dynamically loading the library...)
|
||||
extern u_long SetMallocFlags(u_long flags);
|
||||
|
||||
// The environment variable LIBMALLOC_LOG should be set to an integer
|
||||
// value whose meaning is as follows:
|
||||
|
||||
// Enable logging
|
||||
#define LIBMALLOC_LOG 0x1
|
||||
|
||||
// Don't free memory when set
|
||||
#define LIBMALLOC_NOFREE 0x2
|
||||
|
||||
// Check heap for corruption after every malloc/free/realloc
|
||||
#define LIBMALLOC_CHECK 0x4
|
||||
|
||||
// Log reference count calls (addref/release)
|
||||
#define LIBMALLOC_LOG_RC 0x8
|
||||
|
||||
// Log a stack trace
|
||||
#define LIBMALLOC_LOG_TRACE 0x10
|
||||
|
||||
void __log_addref(void* p, int oldrc, int newrc);
|
||||
void __log_release(void* p, int oldrc, int newrc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}; /* end of extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* libmalloc_h___ */
|
Загрузка…
Ссылка в новой задаче