This commit is contained in:
shaver%netscape.com 2000-01-18 03:43:26 +00:00
Родитель 7226280da7
Коммит 04d9dc7a3f
20 изменённых файлов: 1959 добавлений и 0 удалений

6
tools/jprof/.cvsignore Normal file
Просмотреть файл

@ -0,0 +1,6 @@
Makefile
TestLeaky
TestPreload
leaky
ShowLibs
.deps-*

87
tools/jprof/Makefile.in Normal file
Просмотреть файл

@ -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

82
tools/jprof/README.html Normal file
Просмотреть файл

@ -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.

102
tools/jprof/bfd.cpp Normal file
Просмотреть файл

@ -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 */

107
tools/jprof/coff.cpp Normal file
Просмотреть файл

@ -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
tools/jprof/config.h Normal file
Просмотреть файл

141
tools/jprof/elf.cpp Normal file
Просмотреть файл

@ -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 */

67
tools/jprof/intcnt.cpp Normal file
Просмотреть файл

@ -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;
}
}

34
tools/jprof/intcnt.h Normal file
Просмотреть файл

@ -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
tools/jprof/jprof.h Normal file
Просмотреть файл

523
tools/jprof/leaky.cpp Normal file
Просмотреть файл

@ -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);
}

117
tools/jprof/leaky.h Normal file
Просмотреть файл

@ -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_ */

250
tools/jprof/libmalloc.cpp Normal file
Просмотреть файл

@ -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
tools/jprof/libmalloc.h Normal file
Просмотреть файл

48
tools/jprof/strset.cpp Normal file
Просмотреть файл

@ -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;
}

27
tools/jprof/strset.h Normal file
Просмотреть файл

@ -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_ */

25
tools/jprof/stub/config.h Normal file
Просмотреть файл

@ -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___ */

18
tools/jprof/stub/jprof.h Normal file
Просмотреть файл

@ -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___ */