diff --git a/mozilla/tools/jprof/Makefile.in b/mozilla/tools/jprof/Makefile.in new file mode 100644 index 00000000000..48d500ae71f --- /dev/null +++ b/mozilla/tools/jprof/Makefile.in @@ -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 +# + +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 diff --git a/mozilla/tools/jprof/README.html b/mozilla/tools/jprof/README.html new file mode 100644 index 00000000000..5e216f31242 --- /dev/null +++ b/mozilla/tools/jprof/README.html @@ -0,0 +1,82 @@ + +The Jprof Profiler +

The Jprof Profiler


+

Introduction

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

+

Operation

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

+

Use

+Jprof is not too difficult to use. First it must be enabled by adding: +
+--enable-jprof
+
+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: + +
+env JPROF_FLAGS=JP_START LD_LIBRARY_PATH=. ./mozilla-bin
+
+ +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: +
+./jprof mozilla-bin ./jprof-log > tmp.html
+
+If you view the generated HTML in a browser you will see a lot of blocks +that look like this: + +
+
+ 70 g_main_dispatch + 76269 70 gdk_event_dispatch + 59 handle_gdk_event(_GdkEvent *, void *) + 11 gdk_events_queue +
+
+ +

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

+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). +

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

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

+

Source Code

+I hope to get the code into mozilla soon. Until then you can download it from +goliath It +comes in two pieces, a patch to apply to the existing mozilla code, and a +tarball containing new files. diff --git a/mozilla/tools/jprof/bfd.cpp b/mozilla/tools/jprof/bfd.cpp new file mode 100644 index 00000000000..bde3aa78f23 --- /dev/null +++ b/mozilla/tools/jprof/bfd.cpp @@ -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 +#include +#include + +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 */ diff --git a/mozilla/tools/jprof/coff.cpp b/mozilla/tools/jprof/coff.cpp new file mode 100644 index 00000000000..d3f9037744f --- /dev/null +++ b/mozilla/tools/jprof/coff.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +#ifdef IRIX4 +extern "C" { + extern char *demangle(char const* in); +}; +#else +#include +#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 */ diff --git a/mozilla/tools/jprof/config.h b/mozilla/tools/jprof/config.h new file mode 100644 index 00000000000..10e7ecd97f8 --- /dev/null +++ b/mozilla/tools/jprof/config.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___ */ diff --git a/mozilla/tools/jprof/elf.cpp b/mozilla/tools/jprof/elf.cpp new file mode 100644 index 00000000000..76e093bad28 --- /dev/null +++ b/mozilla/tools/jprof/elf.cpp @@ -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 +#include +#include +#include +#include +#include + +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 */ diff --git a/mozilla/tools/jprof/intcnt.cpp b/mozilla/tools/jprof/intcnt.cpp new file mode 100644 index 00000000000..6a8340610e1 --- /dev/null +++ b/mozilla/tools/jprof/intcnt.cpp @@ -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(indexiPair[mid].idx) { + if(mid +#include +#include +#include +#include +#include +#ifndef NTO +#include +#endif +#include +#include +#include + +#ifdef NTO +#include +#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,"Jprof Profile Report\n"); + fprintf(fp,"

Jprof Profile Report

\n"); + fprintf(fp,"
"); + fprintf(fp,"Flat hierarchical"); + fprintf(fp,"

\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=h) && (countArray[rankingTable[j-h]]

Hierarchical Profile

\n"); + fprintf(fp, "
\n");
+  fprintf(fp, "%5s %5s    %4s %s\n",
+  "index", "Count", "Hits", "Function Name");
+
+  for(int i=0; i0; i++) {
+    Symbol *sp=&externalSymbols[rankingTable[i]];
+    
+    sp->cntP.printReport(fp, this);
+
+    fprintf(fp, "%6d %3d %8d %s\n", rankingTable[i],
+    sp->timerHit, countArray[rankingTable[i]], rankingTable[i],sp->name);
+
+    sp->cntC.printReport(fp, this);
+
+    fprintf(fp, "
\n"); + } + fprintf(fp,"
\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=h) && (externalSymbols[rankingTable[j-h]].timerHit
Flat Profile

\n"); + fprintf(fp, "
\n");
+  fprintf(fp, "Count   Function Name\n");
+  // Now loop for as long as we have timer hits
+  for(int i=0;
+    i0; i++) {
+
+    Symbol *sp=&externalSymbols[rankingTable[i]];
+    
+    fprintf(fp, "%3d     %s\n",
+    sp->timerHit, rankingTable[i], sp->name);
+  }
+
+  fprintf(fp,"
\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(&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(*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 %s\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(lk->indexToName(idx))); + } else if(cntnmax) { + nmax=cnt; + } + } + } while((tmax=nmax)>0); +} diff --git a/mozilla/tools/jprof/leaky.h b/mozilla/tools/jprof/leaky.h new file mode 100644 index 00000000000..9fb2093a5b3 --- /dev/null +++ b/mozilla/tools/jprof/leaky.h @@ -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 +#include +#include +#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_ */ diff --git a/mozilla/tools/jprof/libmalloc.cpp b/mozilla/tools/jprof/libmalloc.cpp new file mode 100644 index 00000000000..8fb61670a00 --- /dev/null +++ b/mozilla/tools/jprof/libmalloc.cpp @@ -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 +#include +#include +#include +#include +#include + +#include "libmalloc.h" +#include +#include +#include +#include +#include + + +#ifdef NTO +#include +extern r_debug _r_debug; +#else +#include +#endif + +#ifdef NTO +#define JB_BP 0x08 +#include +#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)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(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(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"); + } +} diff --git a/mozilla/tools/jprof/libmalloc.h b/mozilla/tools/jprof/libmalloc.h new file mode 100644 index 00000000000..782341d3449 --- /dev/null +++ b/mozilla/tools/jprof/libmalloc.h @@ -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 +#include + +#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___ */ diff --git a/mozilla/tools/jprof/strset.cpp b/mozilla/tools/jprof/strset.cpp new file mode 100644 index 00000000000..f0bb7464950 --- /dev/null +++ b/mozilla/tools/jprof/strset.cpp @@ -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 +#include + +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; +} diff --git a/mozilla/tools/jprof/strset.h b/mozilla/tools/jprof/strset.h new file mode 100644 index 00000000000..8fefdf21b62 --- /dev/null +++ b/mozilla/tools/jprof/strset.h @@ -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_ */ diff --git a/mozilla/tools/jprof/stub/config.h b/mozilla/tools/jprof/stub/config.h new file mode 100644 index 00000000000..10e7ecd97f8 --- /dev/null +++ b/mozilla/tools/jprof/stub/config.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___ */ diff --git a/mozilla/tools/jprof/stub/jprof.h b/mozilla/tools/jprof/stub/jprof.h new file mode 100644 index 00000000000..fb16ef74c5d --- /dev/null +++ b/mozilla/tools/jprof/stub/jprof.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___ */ diff --git a/mozilla/tools/jprof/stub/libmalloc.cpp b/mozilla/tools/jprof/stub/libmalloc.cpp new file mode 100644 index 00000000000..8fb61670a00 --- /dev/null +++ b/mozilla/tools/jprof/stub/libmalloc.cpp @@ -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 +#include +#include +#include +#include +#include + +#include "libmalloc.h" +#include +#include +#include +#include +#include + + +#ifdef NTO +#include +extern r_debug _r_debug; +#else +#include +#endif + +#ifdef NTO +#define JB_BP 0x08 +#include +#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)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(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(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"); + } +} diff --git a/mozilla/tools/jprof/stub/libmalloc.h b/mozilla/tools/jprof/stub/libmalloc.h new file mode 100644 index 00000000000..782341d3449 --- /dev/null +++ b/mozilla/tools/jprof/stub/libmalloc.h @@ -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 +#include + +#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___ */