diff --git a/mozilla/tools/leaky/LibPreload.cpp b/mozilla/tools/leaky/LibPreload.cpp new file mode 100644 index 00000000000..cea4071aa19 --- /dev/null +++ b/mozilla/tools/leaky/LibPreload.cpp @@ -0,0 +1,29 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 "TestPreload.h" +#include "config.h" +#include + +// This is a fake implementation of malloc that layers on top of the +// native platforms malloc routine (ideally using weak symbols). What +// it does if given a specail size request it returns a fake address +// result, otherwise it uses the underlying malloc. +void* malloc(size_t aSize) +{ + if (aSize == LD_PRELOAD_TEST_MALLOC_SIZE) { + return (void*) LD_PRELOAD_TEST_VALUE; + } + else { + return REAL_MALLOC(aSize); + } +} diff --git a/mozilla/tools/leaky/Makefile b/mozilla/tools/leaky/Makefile new file mode 100644 index 00000000000..792d6674798 --- /dev/null +++ b/mozilla/tools/leaky/Makefile @@ -0,0 +1,74 @@ +#! gmake +# +# The contents of this file are subject to the Mozilla Public License +# Version 1.0 (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. + +CXX = c++ +OPTIMIZER = -g +CXXFLAGS = $(OPTIMIZER) -Wall +CXXF = $(CXX) $(CXXFLAGS) + +MKSHLIB = $(CXX) -shared + +# Stuff to build the leaky executable +LEAKY_CPPSRCS = \ + bfd.cpp \ + coff.cpp \ + dict.cpp \ + elf.cpp \ + leaky.cpp \ + strset.cpp \ + $(NULL) +LEAKY_OBJECTS = $(LEAKY_CPPSRCS:.cpp=.o) +LEAKY_LIBS = -lbfd -liberty + +# Stuff to build the library used to wrap malloc +LIBMALLOC_CPPSRCS = libmalloc.cpp +LIBMALLOC_OBJECTS = $(LIBMALLOC_CPPSRCS:.cpp=.o) +LIBMALLOC = libleaky.so + +# Stuff to build test programs +LIBPRELOAD = libpreload.so + +TARGETS = leaky $(LIBMALLOC) TestLeaky TestPreload $(LIBPRELOAD) ShowLibs + +.SUFFIXES: .cpp + +default all: $(TARGETS) + +clean: + rm -f core malloc-log malloc-map *.o + +clobber: clean + rm -f $(TARGETS) + +.cpp.o: + $(CXXF) -c $< + +leaky: $(LEAKY_OBJECTS) + $(CXXF) -o $@ $(LEAKY_OBJECTS) $(LEAKY_LIBS) + +$(LIBMALLOC): $(LIBMALLOC_OBJECTS) + rm -f $@ + $(MKSHLIB) -o $@ $(LIBMALLOC_OBJECTS) + +TestLeaky: TestLeaky.cpp + $(CXXF) -o $@ TestLeaky.cpp + +TestPreload: TestPreload.cpp + $(CXXF) -o $@ TestPreload.cpp + +$(LIBPRELOAD): LibPreload.o + $(MKSHLIB) -o $@ LibPreload.o + +ShowLibs: ShowLibs.cpp + $(CXXF) -o $@ ShowLibs.cpp diff --git a/mozilla/tools/leaky/ShowLibs.cpp b/mozilla/tools/leaky/ShowLibs.cpp new file mode 100644 index 00000000000..05e04c1e2ac --- /dev/null +++ b/mozilla/tools/leaky/ShowLibs.cpp @@ -0,0 +1,34 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 +#include + +#ifdef linux +#include +#endif + +// A simple test program that dumps out the loaded shared +// libraries. This is essential for leaky to work properly when shared +// libraries are used. + +int main(int argc, char** argv) +{ +#ifdef linux + link_map* map = _dl_loaded; + while (NULL != map) { + printf("addr=%08x name=%s\n", map->l_addr, map->l_name); + map = map->l_next; + } +#endif + return 0; +} diff --git a/mozilla/tools/leaky/TestLeaky.cpp b/mozilla/tools/leaky/TestLeaky.cpp new file mode 100644 index 00000000000..a6f1fccf2b6 --- /dev/null +++ b/mozilla/tools/leaky/TestLeaky.cpp @@ -0,0 +1,47 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 +#include + +void s1(int, int) +{ + malloc(100); +} + +void s2() +{ + s1(1, 2); + malloc(100); +} + +void s3() +{ + s2(); + malloc(100); + malloc(200); +} + +void s4() +{ + s3(); + char* cp = new char[300]; + cp = cp; +} + +int main() +{ + s1(1, 2); + s2(); + s3(); + s4(); +} diff --git a/mozilla/tools/leaky/TestPreload.cpp b/mozilla/tools/leaky/TestPreload.cpp new file mode 100644 index 00000000000..8883fdc8f7e --- /dev/null +++ b/mozilla/tools/leaky/TestPreload.cpp @@ -0,0 +1,33 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 "TestPreload.h" +#include +#include + +// This is a simple test program that verifies that you can use +// LD_PRELOAD to override the implementation of malloc for your +// system. It depends on PreloadLib.cpp providing an alternate +// implementation of malloc that has a special hack to return a +// constant address given a particular size request. + +int main(int argc, char** argv) +{ + char* p = (char*) malloc(LD_PRELOAD_TEST_MALLOC_SIZE); + if (p == (char*)LD_PRELOAD_TEST_VALUE) { + printf("LD_PRELOAD worked - we are using our malloc\n"); + } + else { + printf("LD_PRELOAD failed\n"); + } + return 0; +} diff --git a/mozilla/tools/leaky/TestPreload.h b/mozilla/tools/leaky/TestPreload.h new file mode 100644 index 00000000000..db97a3be6cb --- /dev/null +++ b/mozilla/tools/leaky/TestPreload.h @@ -0,0 +1,15 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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. + +#define LD_PRELOAD_TEST_MALLOC_SIZE 0x12345 + +#define LD_PRELOAD_TEST_VALUE 0x12345 diff --git a/mozilla/tools/leaky/bfd.cpp b/mozilla/tools/leaky/bfd.cpp new file mode 100644 index 00000000000..a4c0624a1d2 --- /dev/null +++ b/mozilla/tools/leaky/bfd.cpp @@ -0,0 +1,100 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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* bfd = bfd_openr(aFileName, NULL); + if (NULL == bfd) { + return; + } + char **matching; + if (!bfd_check_format_matches(bfd, bfd_object, &matching)) { + bfd_close(bfd); + return; + } + + asymbol* store; + store = bfd_make_empty_symbol(bfd); + + // read mini symbols + PTR minisyms; + unsigned int size; + long symcount = bfd_read_minisymbols(bfd, kDynamic, &minisyms, &size); + + int initialSymbols = usefulSymbols; + if (NULL == externalSymbols) { + externalSymbols = (Symbol*) malloc(sizeof(Symbol) * 10000); + numExternalSymbols = 10000; + } + Symbol* sp = externalSymbols + usefulSymbols; + Symbol* last = externalSymbols + numExternalSymbols; + + // Scan symbols + bfd_byte* from = (bfd_byte *) minisyms; + bfd_byte* fromend = from + symcount * size; + for (; from < fromend; from += size) { + asymbol *sym; + sym = bfd_minisymbol_to_symbol(bfd, kDynamic, (const PTR) from, store); + + symbol_info syminfo; + bfd_get_symbol_info (bfd, sym, &syminfo); + +// if ((syminfo.type == 'T') || (syminfo.type == 't')) { + const char* nm = bfd_asymbol_name(sym); + if (nm) { + char* dnm = cplus_demangle(nm, 1); + sp->name = dnm ? dnm : strdup(nm); + sp->address = syminfo.value + aBaseAddress; + sp++; + if (sp >= last) { + long n = numExternalSymbols + 10000; + externalSymbols = (Symbol*) + realloc(externalSymbols, (size_t) (sizeof(Symbol) * n)); + last = externalSymbols + n; + sp = externalSymbols + numExternalSymbols; + numExternalSymbols = n; + } + } +// } + } + + + bfd_close(bfd); + + 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/leaky/coff.cpp b/mozilla/tools/leaky/coff.cpp new file mode 100644 index 00000000000..7e0dc8ba440 --- /dev/null +++ b/mozilla/tools/leaky/coff.cpp @@ -0,0 +1,107 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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/leaky/config.h b/mozilla/tools/leaky/config.h new file mode 100644 index 00000000000..ef50248fd7c --- /dev/null +++ b/mozilla/tools/leaky/config.h @@ -0,0 +1,35 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 30 + +#include + +#ifdef linux +#define USE_BFD +#undef NEED_WRAPPERS + +#define REAL_MALLOC(_x) __libc_malloc(_x) +#define REAL_REALLOC(_x,_y) __libc_realloc(_x,_y) +#define REAL_FREE(_x) __libc_free(_x) + +extern "C" { + void* __libc_malloc(size_t); + void* __libc_realloc(void*, size_t); + void __libc_free(void*); +}; + +#endif /* linux */ + +#endif /* config_h___ */ diff --git a/mozilla/tools/leaky/dict.cpp b/mozilla/tools/leaky/dict.cpp new file mode 100644 index 00000000000..86627dd7b73 --- /dev/null +++ b/mozilla/tools/leaky/dict.cpp @@ -0,0 +1,82 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 +#include "dict.h" + +MallocDict::MallocDict(int nb) +{ + numBuckets = nb; + buckets = (MallocDictEntry**) calloc(numBuckets, sizeof(MallocDictEntry*)); + rewind(); +} + +void MallocDict::rewind(void) +{ + iterNextBucket = -1; + iterNextEntry = 0; +} + +malloc_log_entry* MallocDict::next(void) +{ + if (iterNextEntry) { + iterNextEntry = iterNextEntry->next; + } + while (!iterNextEntry) { + iterNextBucket++; + if (iterNextBucket >= numBuckets) { + return 0; + } + iterNextEntry = buckets[iterNextBucket]; + } + return iterNextEntry->logEntry; +} + +malloc_log_entry** MallocDict::find(u_long addr) +{ + u_long hash = addr % numBuckets; + MallocDictEntry* mde = buckets[hash]; + while (mde) { + if (mde->addr == addr) { + return &mde->logEntry; + } + mde = mde->next; + } + return 0; +} + +void MallocDict::add(u_long addr, malloc_log_entry *lep) +{ + u_long hash = addr % numBuckets; + MallocDictEntry** mdep = &buckets[hash]; + MallocDictEntry* mde = new MallocDictEntry; + mde->addr = addr; + mde->logEntry = lep; + mde->next = *mdep; + *mdep = mde; +} + +void MallocDict::remove(u_long addr) +{ + u_long hash = addr % numBuckets; + MallocDictEntry** mdep = &buckets[hash]; + MallocDictEntry* mde; + + while (NULL != (mde = *mdep)) { + if (mde->addr == addr) { + *mdep = mde->next; +/*XXX delete mde; */ + return; + } + mdep = &mde->next; + } +} diff --git a/mozilla/tools/leaky/dict.h b/mozilla/tools/leaky/dict.h new file mode 100644 index 00000000000..5f4f822b381 --- /dev/null +++ b/mozilla/tools/leaky/dict.h @@ -0,0 +1,41 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 __dict_h_ +#define __dict_h_ + +#include +#include "libmalloc.h" + +// key is u_long value is malloc_log_entry* +struct MallocDict { + MallocDict(int buckets); + + void rewind(void); + malloc_log_entry* next(void); + + malloc_log_entry** find(u_long addr); + void add(u_long addr, malloc_log_entry *log); + void remove(u_long addr); + + struct MallocDictEntry { + u_long addr; + malloc_log_entry* logEntry; + MallocDictEntry* next; + } **buckets; + int numBuckets; + + int iterNextBucket; + MallocDictEntry* iterNextEntry; +}; + +#endif /* __dict_h_ */ diff --git a/mozilla/tools/leaky/elf.cpp b/mozilla/tools/leaky/elf.cpp new file mode 100644 index 00000000000..a6a8ec37d83 --- /dev/null +++ b/mozilla/tools/leaky/elf.cpp @@ -0,0 +1,141 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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/leaky/leaky.cpp b/mozilla/tools/leaky/leaky.cpp new file mode 100644 index 00000000000..cf01efbb730 --- /dev/null +++ b/mozilla/tools/leaky/leaky.cpp @@ -0,0 +1,452 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 "dict.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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() +{ + treeOutput = FALSE; + sortByFrequency = FALSE; + dumpAll = FALSE; + quiet = FALSE; + showAll = FALSE; + showAddress = FALSE; + stackDepth = 100000; + + fd = -1; + last = base = 0; + buckets = DefaultBuckets; + dict = 0; + + mallocs = 0; + reallocs = 0; + frees = 0; + totalMalloced = 0; + totalLeaked = 0; + errors = 0; + + sfd = -1; + externalSymbols = 0; + + loadMap = NULL; +} + +leaky::~leaky() +{ + delete dict; +} + +void leaky::usageError() +{ + fprintf(stderr, + "Usage: %s [-d|-t] [-e name] [-aAEfq] [-s depth] [-h hash-buckets] prog log\n", + (char*) applicationName); + exit(-1); +} + +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:fh:s:tq")) != -1) { + switch (arg) { + case '?': + errflg++; + break; + case 'a': + showAll = TRUE; + break; + case 'A': + showAddress = TRUE; + break; + case 'd': + dumpAll = TRUE; + if (treeOutput) errflg++; + break; + case 'e': + exclusions.add(optarg); + break; + case 'f': + sortByFrequency = TRUE; + break; + case 'h': + buckets = atoi(optarg); + if ((buckets < 0) || (buckets > MaxBuckets)) { + buckets = MaxBuckets; + fprintf(stderr, "%s: buckets is invalid, using %d\n", + (char*) applicationName, + buckets); + } + break; + case 's': + stackDepth = atoi(optarg); + if (stackDepth < 2) { + stackDepth = 2; + } + break; + case 't': + treeOutput = TRUE; + if (dumpAll) errflg++; + break; + case 'q': + quiet = TRUE; + break; + } + } + if (errflg || ((argc - optind) < 2)) { + usageError(); + } + progFile = argv[optind++]; + logFile = argv[optind]; + + dict = new MallocDict(buckets); +} + +static void* mapFile(int fd, u_int flags, off_t* sz) +{ + struct stat sb; + if (fstat(fd, &sb) < 0) { + perror("fstat"); + exit(-1); + } + void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0); + if (!base) { + perror("mmap"); + exit(-1); + } + *sz = sb.st_size; + return base; +} + +void leaky::LoadMap() +{ + malloc_map_entry mme; + char name[1000]; + + int fd = ::open("malloc-map", O_RDONLY); + if (fd < 0) { + perror("open: malloc-map"); + 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 + fd = ::open(logFile, O_RDONLY); + if (fd < 0) { + perror("open"); + exit(-1); + } + off_t size; + base = (malloc_log_entry*) mapFile(fd, PROT_READ, &size); + last = (malloc_log_entry*)((char*)base + size); + + analyze(); + + if (dumpAll) dumpLog(); +#if 0 + if (treeOutput) dumpToTree(); +#endif + 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; +} + +char const* leaky::findSymbol(u_long addr) +{ + if ((addr < lowestSymbolAddr) || + (addr > highestSymbolAddr)) { + static char buf[20]; + sprintf(buf, "<0x%lx>", addr); + return buf; + } + + // binary search the table, looking for a symbol that covers this + // address. + u_int base = 0; + u_int limit = usefulSymbols - 1; + Symbol* end = &externalSymbols[limit]; + while (base <= limit) { + u_int midPoint = (base + limit)>>1; + Symbol* sp = &externalSymbols[midPoint]; + if (addr < sp->address) { + if (midPoint == 0) { + return 0; + } + limit = midPoint - 1; + } else { + if (sp+1 < end) { + if (addr < (sp+1)->address) { + return sp->name; + } + } else { + return sp->name; + } + base = midPoint + 1; + } + } + + return 0; +} + +//---------------------------------------------------------------------- + +int leaky::excluded(malloc_log_entry* lep) +{ + char** pcp = &lep->pcs[0]; + u_int n = lep->numpcs; + for (u_int i = 0; i < n; i++, pcp++) { + char const* sym = findSymbol((u_long) *pcp); + if (exclusions.contains(sym)) { + return TRUE; + } + } + return FALSE; +} + +//---------------------------------------------------------------------- + +void leaky::displayStackTrace(malloc_log_entry* lep) +{ + char** pcp = &lep->pcs[0]; + u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth; + for (u_int i = 0; i < n; i++, pcp++) { + char const* sym = findSymbol((u_long) *pcp); + if (!sym) { + break; + } + if (showAddress) { + printf("%s[%p] ", sym, *pcp); + } + else { + printf("%s ", sym); + } + } + printf("\n"); +} + +char* typeFromLog[] = { + "malloc", + "realloc", + "free", + "new", + "delete", + "addref", + "release" +}; + +void leaky::dumpEntryToLog(malloc_log_entry* lep) +{ + printf("%-10s %08lx %5ld %08lx (%ld)-->", + typeFromLog[lep->type], + lep->address, lep->size, lep->oldaddress, + lep->numpcs); + displayStackTrace(lep); +} + +void leaky::dumpLog() +{ + if (showAll) { + malloc_log_entry* lep = base; + while (lep < last) { + dumpEntryToLog(lep); + lep = (malloc_log_entry*) &lep->pcs[lep->numpcs]; + } + } else { + malloc_log_entry* lep; + dict->rewind(); + while (NULL != (lep = dict->next())) { + if (!excluded(lep)) { + dumpEntryToLog(lep); + } + } + } +} + +//---------------------------------------------------------------------- + +void leaky::insertAddress(u_long address, malloc_log_entry* lep) +{ + malloc_log_entry** lepp = dict->find(address); + if (lepp) { + assert(*lepp); + if (!quiet) { + printf("Address %lx allocated twice\n", address); + displayStackTrace(lep); + } + errors++; + } else { + dict->add(address, lep); + } +} + +void leaky::removeAddress(u_long address, malloc_log_entry* lep) +{ + malloc_log_entry** lepp = dict->find(address); + if (!lepp) { + if (!quiet) { + printf("Free of unallocated %lx\n", address); + displayStackTrace(lep); + } + errors++; + } else { + dict->remove(address); + } +} + +void leaky::analyze() +{ + malloc_log_entry* lep = base; + while (lep < last) { + switch (lep->type) { + case malloc_log_malloc: + case malloc_log_new: + mallocs++; + if (lep->address) { + totalMalloced += lep->size; + insertAddress((u_long) lep->address, lep); + } + break; + case malloc_log_realloc: + if (lep->oldaddress) { + removeAddress((u_long) lep->oldaddress, lep); + } + if (lep->address) { + insertAddress((u_long) lep->address, lep); + } + reallocs++; + break; + case malloc_log_free: + case malloc_log_delete: + if (lep->address) { + removeAddress((u_long) lep->address, lep); + } + frees++; + break; + } + lep = (malloc_log_entry*) &lep->pcs[lep->numpcs]; + } + + dict->rewind(); + while (NULL != (lep = dict->next())) { + totalLeaked += lep->size; + } + + if (!quiet) { + printf("# of mallocs = %ld\n", mallocs); + printf("# of reallocs = %ld\n", reallocs); + printf("# of frees = %ld\n", frees); + printf("# of errors = %ld\n", errors); + printf("Total bytes allocated = %ld\n", totalMalloced); + printf("Total bytes leaked = %ld\n", totalLeaked); + printf("Average bytes per malloc = %g\n", + float(totalMalloced)/mallocs); + } +} diff --git a/mozilla/tools/leaky/leaky.h b/mozilla/tools/leaky/leaky.h new file mode 100644 index 00000000000..b48553e20b5 --- /dev/null +++ b/mozilla/tools/leaky/leaky.h @@ -0,0 +1,100 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 "dict.h" +#include "strset.h" + +struct Symbol { + char *name; + u_long address; +}; + +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 treeOutput; + int sortByFrequency; + int dumpAll; + int quiet; + int showAll; + int showAddress; + u_int stackDepth; + + int fd; + malloc_log_entry* base; + malloc_log_entry* last; + u_int buckets; + MallocDict* dict; + + u_long mallocs; + u_long reallocs; + u_long frees; + u_long totalMalloced; + u_long errors; + u_long totalLeaked; + + int sfd; + Symbol* externalSymbols; + u_int usefulSymbols; + u_int numExternalSymbols; + StrSet exclusions; + u_long lowestSymbolAddr; + u_long highestSymbolAddr; + + LoadMapEntry* loadMap; + + void usageError(); + + void LoadMap(); + + void analyze(); + + void dumpLog(); + void dumpEntryToLog(malloc_log_entry* lep); + +#if 0 + void dumpToTree(); + void dumpEntryToTree(FILE* fp, malloc_log_entry* lep); +#endif + + void insertAddress(u_long address, malloc_log_entry* lep); + void removeAddress(u_long address, malloc_log_entry* lep); + + void displayStackTrace(malloc_log_entry* lep); + + void ReadSymbols(const char* fileName, u_long aBaseAddress); + void ReadSharedLibrarySymbols(); + void setupSymbols(const char* fileName); + char const* findSymbol(u_long address); + int excluded(malloc_log_entry* lep); +}; + +#endif /* __leaky_h_ */ diff --git a/mozilla/tools/leaky/leaky.html b/mozilla/tools/leaky/leaky.html new file mode 100644 index 00000000000..9e17f91d12f --- /dev/null +++ b/mozilla/tools/leaky/leaky.html @@ -0,0 +1,90 @@ + + + + + + + + +

+About Leaky

+Leaky is a program which will help you find memory leaks, and as of late, +help you debug reference count problems with xpcom objects. +

To use leaky you must first build it. I've made it work only on x86 +linux. To work on other platforms you will need to: +

    +
  1. +Implement CrawlStack in libmalloc.cpp
  2. + +
  3. +Implement DumpAddressMap in libmalloc.cpp and in ShowLibs.cpp
  4. + +
  5. +Either support LD_PRELOAD in your dynamic linker *or* produce a library +that wraps your libc malloc (see config.h for some clues)
  6. + +
  7. +Implement symbol table reading code (see coff.cpp, elf.cpp and bfd.cpp +for examples; at the time of writing this document only bfd.cpp was known +to work)
  8. +
+After its built, you can use TestPreload and TestMalloc and ShowLibs to +debug your implementation. +

By setting the LIBMALLOC_LOG environment variable you control how much +information is logged during the programs execution. See libmalloc.h for +a definition of the values to use. If you are using LD_PRELOAD, here is +one way to run your program: +

env LD_PRELOAD=/full/path/to/libleaky.so LIBMALLOC_LOG=1 +my-program
+The debugging malloc library creates two files - "malloc-log" and "malloc-map". +The malloc-log file can be quite large for large programs (e.g. mozilla) +so be prepared to have alot of disk space. The malloc-map is tiny. +

Once your program has completed execution you can use leaky to look +for memory leaks, or at least use it to dump the log. For memory leaks, +you use leaky like this: +

leaky -d <program-name-goes-here> malloc-log
+Leaky will then display all of the call sites where memory was leaked. +To look at the entire log file contents, not just the leaks add "-a" to +the arguments: +
leaky -d -a <program-name-goes-here> malloc-log
+For debugging reference count issues, here is what I do: +
    +
  1. +Set LIBMALLOC_LOG to "8"
  2. + +
  3. +Modify your source code so that your class::Addref and class::Release methods +call __log_addref and __log_release, as appropriate. See libmalloc.h for +their signatures.
  4. + +
  5. +Run your program so that you get the log data. Its often convenient to +run your program in the debugger and then set a breakpoint at an interesting +location where you think some object is being leaked or over-freed. Then +when the debugger gets there tell it to execute DumpAddressMap. In gdb +you do this:
  6. + +
        +
      (gdb) p DumpAddressMap() +
       
    + +
  7. +Then use leaky to capture the addref and release calls to a log file:
  8. + +
        +
      leaky -d -a <program-name-goes-here> malloc-log > log +
       
    + +
  9. +Then use "grep" to search the log for a specific object by grepping for +its memory address...
  10. + +
  11. +On a typical *short* run of mozilla, I'll end up with a malloc-log +file of around 5 to 10 megabytes and the resulting converted log file will +be 10 to 20 times that so be prepared to have alot of disk space. It helps +a great deal to narrow down your problem space to reduce the log file size...
  12. +
+ + + diff --git a/mozilla/tools/leaky/libmalloc.cpp b/mozilla/tools/leaky/libmalloc.cpp new file mode 100644 index 00000000000..a4e822b3825 --- /dev/null +++ b/mozilla/tools/leaky/libmalloc.cpp @@ -0,0 +1,535 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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 "libmalloc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#ifdef NEED_WRAPPERS + void* __wrap_malloc(size_t); + void* __wrap_realloc(void*, size_t); + void __wrap_free(void*); + void* __wrap___builtin_new(size_t); + void __wrap___builtin_delete(void*); + void* __wrap___builtin_vec_new(size_t); + void __wrap___builtin_vec_delete(void*); + void* __wrap_PR_Malloc(size_t); + void* __wrap_PR_Calloc(size_t, size_t); + void* __wrap_PR_Realloc(void*, size_t); + void __wrap_PR_Free(void*); +#endif +} + +static int gLogFD = -1; +static u_long gFlags; + +#define MARKER0 0xFFFFFFFF +#define MARKER1 0xEEEEEEEE +#define MARKER2_0 0xD8D8D8D8 +#define MARKER2_1 0xE8E8E8E8 + +#define PATTERN_BYTE 0xFE +#define FREE_PATTERN_BYTE 0xDE + +struct Header { + u_long marker0; + size_t rawSize; // user requested size + size_t size; // size rounded up + u_long marker1; +}; + +struct Trailer { + u_long marker2[2]; +}; + +//---------------------------------------------------------------------- + +#if defined(i386) +static void CrawlStack(malloc_log_entry* me, jmp_buf jb) +{ + u_long* bp = (u_long*) (jb[0].__jmpbuf[JB_BP]); + u_long numpcs = 0; + int skip = 2; + 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 + +//---------------------------------------------------------------------- + +#ifdef linux +static void DumpAddressMap() +{ + int mfd = open("malloc-map", O_CREAT|O_WRONLY|O_TRUNC, 0666); + if (mfd >= 0) { + malloc_map_entry mme; + link_map* map = _dl_loaded; + 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); + } + map = map->l_next; + } + close(mfd); + } +} +#endif + +//---------------------------------------------------------------------- + +static int Verify(Header* h) +{ + // Sanity check the header first + if ((h->marker0 != MARKER0) || + (h->marker1 != MARKER1) || + (h->rawSize > h->size)) { + DumpAddressMap(); + abort(); + } + + // Sanity check the trailer second + Trailer* t = (Trailer*) ((char*)(h + 1) + h->size); + if ((t->marker2[0] != MARKER2_0) || + (t->marker2[1] != MARKER2_1)) { + DumpAddressMap(); + abort(); + } + + // Verify there were no overruns + size_t fill = h->size - h->rawSize; + if (0 != fill) { + unsigned char* cp = ((unsigned char*)(h + 1)) + h->rawSize; + unsigned char* end = cp + fill; + while (cp < end) { + unsigned char ch = *cp++; + if (ch != PATTERN_BYTE) { + DumpAddressMap(); + abort(); + } + } + } + return 1; +} + +static void +Log(int aType, void* aAddr, size_t aSize, void* aOldAddr) +{ + malloc_log_entry me; + + me.type = (u_long) aType; + me.address = (u_long) aAddr; + me.size = (u_long) aSize; + me.oldaddress = (u_long) aOldAddr; + + jmp_buf jb; + setjmp(jb); + CrawlStack(&me, jb); + + write(gLogFD, &me, sizeof(me) - MAX_STACK_CRAWL*sizeof(char*) + + me.numpcs*sizeof(char*)); +} + +static void* +MallocHook(size_t aSize, u_long aLogType) +{ + size_t roundedSize = aSize; + roundedSize = ((roundedSize + 4) >> 2) << 2; + + void* ptr = REAL_MALLOC(sizeof(Header) + roundedSize + sizeof(Trailer)); + + if (NULL != ptr) { + Header* h = (Header*) ptr; + h->rawSize = aSize; + h->size = roundedSize; + h->marker0 = MARKER0; + h->marker1 = MARKER1; + + ptr = (void*) ((char*)(h+1)); + + // Fill entire memory with a pattern to help detect overruns and + // usage of un-written memory + if (0 != aSize) { + memset(ptr, PATTERN_BYTE, aSize); + } + size_t fill = roundedSize - aSize; + if (0 != fill) { + memset((char*)ptr + aSize, PATTERN_BYTE, fill); + } + + Trailer* t = (Trailer*) ((char*)ptr + roundedSize); + t->marker2[0] = MARKER2_0; + t->marker2[1] = MARKER2_1; + + if (LIBMALLOC_LOG & gFlags) { + Log(aLogType, ptr, aSize, 0); + } + } + + return ptr; +} + +static void +FreeHook(void* aAddr, u_long aLogType) +{ + if (0 == aAddr) { + return; + } + if (LIBMALLOC_LOG & gFlags) { + Log(aLogType, aAddr, 0, 0); + } + Header* h = (Header*) ((char*)aAddr - sizeof(Header)); + if (Verify(h)) { + // Munge the header so that a dup-free will fail the verify + h->marker0 = 0xDEADBEEF; + h->marker1 = 0xDEADBEEF; + + // Munge the body of the allocation so that if the user + // still has a live reference they get messed up + void* ptr = (void*) ((char*)(h+1)); + memset(ptr, FREE_PATTERN_BYTE, h->rawSize); + REAL_FREE(h); + } + else { + REAL_FREE(aAddr); + } +} + +static void* +ReallocHook(void* aOldAddr, size_t aSize) +{ + if (0 == aOldAddr) { + return MallocHook(aSize, malloc_log_malloc); + } + Header* oldh = (Header*) ((char*)aOldAddr - sizeof(Header)); + if (!Verify(oldh)) { + return REAL_REALLOC(aOldAddr, aSize); + } + size_t oldSize = oldh->rawSize; + + size_t roundedSize = aSize; + roundedSize = ((roundedSize + 4) >> 2) << 2; + + void* ptr = REAL_MALLOC(sizeof(Header) + roundedSize + sizeof(Trailer)); + + if (NULL != ptr) { + Header* h = (Header*) ptr; + h->rawSize = aSize; + h->size = roundedSize; + h->marker0 = MARKER0; + h->marker1 = MARKER1; + + ptr = (void*) ((char*)(h+1)); + + // Fill extra memory with a pattern to help detect overruns + size_t fill = roundedSize - aSize; + if (0 != fill) { + memset((char*)ptr + aSize, PATTERN_BYTE, fill); + } + + Trailer* t = (Trailer*) ((char*)ptr + roundedSize); + t->marker2[0] = MARKER2_0; + t->marker2[1] = MARKER2_1; + + // Copy old memory into new memory (don't copy too much!) + size_t copy = oldSize; + if (copy > aSize) copy = aSize; + memcpy(ptr, aOldAddr, copy); + + REAL_FREE(oldh); + + if (LIBMALLOC_LOG & gFlags) { + Log(malloc_log_realloc, ptr, aSize, aOldAddr); + } + } + return ptr; +} + +u_long +SetMallocFlags(u_long aFlags) +{ + u_long old = gFlags; + gFlags = aFlags; + + if ((-1 == gLogFD) && ((LIBMALLOC_LOG|LIBMALLOC_LOG_RC) & gFlags)) { + gLogFD = open("malloc-log", O_CREAT|O_WRONLY|O_TRUNC, 0666); + if (gLogFD < 0) { + gFlags &= ~LIBMALLOC_LOG; + printf("unable to create malloc-log: %d\n", errno); + } + } + if ((gLogFD >= 0) && (0 == ((LIBMALLOC_LOG|LIBMALLOC_LOG_RC) & gFlags))) { + close(gLogFD); + gLogFD = -1; + } + + // Try to guarantee that the address map is always dumped + atexit(DumpAddressMap); + + return old; +} + +static int gFirstTime = 1; + +static void Init() +{ + gFirstTime = 0; + u_long flags = 0; + char* s = getenv("LIBMALLOC_LOG"); + if (s) { + flags = atoi(s); + if (LIBMALLOC_LOG & flags) { + char m1[] = "dbgmalloc: enabled memory logging\n"; + write(1, m1, sizeof(m1)-1); + } + if (LIBMALLOC_LOG_RC & flags) { + char m2[] = "dbgmalloc: enabled refcnt logging\n"; + write(1, m2, sizeof(m2)-1); + } + } + SetMallocFlags(flags); +} + +//---------------------------------------------------------------------- + +#ifdef NEED_WRAPPERS +void* __wrap_malloc(size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return MallocHook(aSize, malloc_log_malloc); +} + +void* __wrap_realloc(void* aPtr, size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return ReallocHook(aPtr, aSize); +} + +void __wrap_free(void* aPtr) +{ + if (gFirstTime) { + Init(); + } + FreeHook(aPtr, malloc_log_free); +} + +void* __wrap___builtin_new(size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return MallocHook(aSize, malloc_log_new); +} + +void __wrap___builtin_delete(void* aPtr) +{ + if (gFirstTime) { + Init(); + } + FreeHook(aPtr, malloc_log_delete); +} + +void* __wrap___builtin_vec_new(size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return MallocHook(aSize, malloc_log_new); +} + +void __wrap___builtin_vec_delete(void* aPtr) +{ + if (gFirstTime) { + Init(); + } + FreeHook(aPtr, malloc_log_delete); +} + +void* __wrap_PR_Malloc(size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return MallocHook(aSize, malloc_log_malloc); +} + +void* __wrap_PR_Calloc(size_t aSize, size_t aBsize) +{ + if (gFirstTime) { + Init(); + } + size_t size = aSize*aBsize; + void* ptr = MallocHook(size, malloc_log_malloc); + if (NULL != ptr) { + memset(ptr, 0, size); + } + return ptr; +} + +void* __wrap_PR_Realloc(void* aPtr, size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return ReallocHook(aPtr, aSize); +} + +void __wrap_PR_Free(void* aPtr) +{ + if (gFirstTime) { + Init(); + } + FreeHook(aPtr, malloc_log_free); +} +#endif + +//---------------------------------------- + +// Strong symbols so that libc references are routed to us + +void* malloc(size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return MallocHook(aSize, malloc_log_malloc); +} + +void* realloc(void* aPtr, size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return ReallocHook(aPtr, aSize); +} + +void free(void* aPtr) +{ + if (gFirstTime) { + Init(); + } + FreeHook(aPtr, malloc_log_free); +} + +void* calloc(size_t aSize, size_t aBsize) +{ + if (gFirstTime) { + Init(); + } + size_t size = aSize*aBsize; + void* ptr = MallocHook(size, malloc_log_malloc); + if (NULL != ptr) { + memset(ptr, 0, size); + } + return ptr; +} + +void cfree(void* ptr) +{ + if (gFirstTime) { + Init(); + } + FreeHook(ptr, malloc_log_free); +} + +void* memalign(size_t alignment, size_t size) +{ + ::abort(); +} + +void* valloc(size_t size) +{ + ::abort(); +} + +void* pvalloc(size_t size) +{ + ::abort(); +} + +void* __builtin_new(size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return MallocHook(aSize, malloc_log_new); +} + +void __builtin_delete(void* aPtr) +{ + if (gFirstTime) { + Init(); + } + FreeHook(aPtr, malloc_log_delete); +} + +void* __builtin_vec_new(size_t aSize) +{ + if (gFirstTime) { + Init(); + } + return MallocHook(aSize, malloc_log_new); +} + +void __builtin_vec_delete(void* aPtr) +{ + if (gFirstTime) { + Init(); + } + FreeHook(aPtr, malloc_log_delete); +} + +void +__log_addref(void* p, int oldrc, int newrc) +{ + if (gFirstTime) { + Init(); + } + if (LIBMALLOC_LOG_RC & gFlags) { + Log(malloc_log_addref, p, size_t(oldrc), (void*)newrc); + } +} + +void +__log_release(void* p, int oldrc, int newrc) +{ + if (gFirstTime) { + Init(); + } + if (LIBMALLOC_LOG_RC & gFlags) { + Log(malloc_log_release, p, size_t(oldrc), (void*)newrc); + } +} diff --git a/mozilla/tools/leaky/libmalloc.h b/mozilla/tools/leaky/libmalloc.h new file mode 100644 index 00000000000..34e72504dd9 --- /dev/null +++ b/mozilla/tools/leaky/libmalloc.h @@ -0,0 +1,79 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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" + +// Format of a malloc log entry. This is whats written out to the +// "malloc-log" file. +struct malloc_log_entry { + u_long type; + u_long address; + u_long size; + u_long oldaddress; + u_long numpcs; + char* pcs[MAX_STACK_CRAWL]; +}; + +// type's +#define malloc_log_malloc 0 +#define malloc_log_realloc 1 +#define malloc_log_free 2 +#define malloc_log_new 3 +#define malloc_log_delete 4 +#define malloc_log_addref 5 +#define malloc_log_release 6 + +// 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 + +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/leaky/strset.cpp b/mozilla/tools/leaky/strset.cpp new file mode 100644 index 00000000000..e1fde0f79e7 --- /dev/null +++ b/mozilla/tools/leaky/strset.cpp @@ -0,0 +1,48 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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/leaky/strset.h b/mozilla/tools/leaky/strset.h new file mode 100644 index 00000000000..2a63c49687b --- /dev/null +++ b/mozilla/tools/leaky/strset.h @@ -0,0 +1,26 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (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); + + char** strings; + int numstrings; +}; + +#endif /* __strset_h_ */