commit 242ddf1724ff8a5e53afccd48b8610bdcf613ddc Author: Damien Miller Date: Mon Mar 5 12:11:29 2012 +1100 simple leak tracer, using libexecinfo diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ad01bc2 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +# $OpenBSD$ + +LIB= leakmalloc +SRCS= leakmalloc.c +HDRS= leakmalloc.h +#MAN= leakmalloc.3 +NOMAN=1 +NOPIC=1 +NOPROFILE=1 + +CDIAGFLAGS= -Wall +CDIAGFLAGS+= -Werror +CDIAGFLAGS+= -Wstrict-prototypes +CDIAGFLAGS+= -Wmissing-prototypes +CDIAGFLAGS+= -Wmissing-declarations +CDIAGFLAGS+= -Wshadow +CDIAGFLAGS+= -Wpointer-arith +CDIAGFLAGS+= -Wcast-qual +CDIAGFLAGS+= -Wsign-compare +CDIAGFLAGS+= -Wcast-align +CDIAGFLAGS+= -Wbad-function-cast + +CPPFLAGS+= -I/usr/local/include +DEBUG=-ggdb3 +COPTS=-O0 +INSTALL_STRIP= + +.include diff --git a/leakmalloc.c b/leakmalloc.c new file mode 100644 index 0000000..63feebe --- /dev/null +++ b/leakmalloc.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2012 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LEAKMALLOC_NO_REDIRECT +#include "leakmalloc.h" + +static int initialised; + +struct alloc { + RB_ENTRY(alloc) entry; + void *addr; + size_t len; + void *bt[BT_MAX_DEPTH]; + int depth; +}; + +static int +alloc_cmp(struct alloc *a, struct alloc *b) +{ + return (a->addr == b->addr) ? 0 : (a->addr < b->addr ? -1 : 1); +} + +RB_HEAD(alloc_tree, alloc); +RB_GENERATE_STATIC(alloc_tree, alloc, entry, alloc_cmp); +static struct alloc_tree alloc_tree = RB_INITIALIZER(&alloc_tree); + +/* Called atexit to dump unfreed leak objects */ +static void +dump_leaks(void) +{ + struct alloc *alloc; + int i; + + RB_FOREACH(alloc, alloc_tree, &alloc_tree) { + printf("LEAK %p %zu TRACE", alloc->addr, alloc->len); + for (i = 0; i < alloc->depth; i++) + printf(" %p", alloc->bt[i]); + printf("\n"); + } +} + +void __record_leak(void *addr, size_t len, void *oaddr); +void +__record_leak(void *addr, size_t len, void *oaddr) +{ + struct alloc oalloc, *alloc; + + if (!initialised) { + atexit(dump_leaks); + initialised = 1; + } + + if (addr == NULL || addr == oaddr) + return; + if (oaddr == NULL) { + /* + * malloc/calloc: allocate a leak object and fill in the + * trace. + */ + if ((alloc = calloc(1, sizeof(*alloc))) == NULL) + errx(1, "%s: calloc failed", __func__); + alloc->addr = addr; + alloc->len = len; + if ((alloc->depth = backtrace(alloc->bt, BT_MAX_DEPTH)) == -1) + errx(1, "%s: backtrace failed", __func__); + if (RB_INSERT(alloc_tree, &alloc_tree, alloc) != NULL) + errx(1, "%s: alloc for %p already exists", + __func__, addr); + } else { + oalloc.addr = oaddr; + alloc = RB_FIND(alloc_tree, &alloc_tree, &oalloc); + if (addr == NULL) { + /* + * free: delete the tracked leak. + */ + if (alloc == NULL) + return; /* Ignore untracked memory */ + RB_REMOVE(alloc_tree, &alloc_tree, alloc); + free(alloc); + } else { + /* + * realloc: update the original address so we can + * trace it when it is freed. + */ + if (alloc == NULL) + errx(1, "%s: original addr missing", __func__); + alloc->addr = addr; + alloc->len = len; + } + } +} + +char * +leak_strdup(const char *s) +{ + char *ret = strdup(s); + + __record_leak(ret, ret == NULL ? 0 : strlen(ret), NULL); + return ret; +} + +void * +leak_malloc(size_t len) +{ + void *ret = malloc(len); + + __record_leak(ret, len, NULL); + return ret; +} + +void * +leak_calloc(size_t nmemb, size_t size) +{ + void *ret = calloc(nmemb, size); + + __record_leak(ret, nmemb * size, NULL); + return ret; +} + +void * +leak_realloc(void *s, size_t len) +{ + void *ret = realloc(s, len); + + __record_leak(ret, len, s); + return ret; +} + +void +leak_free(void *s) +{ + free(s); + __record_leak(NULL, 0, s); +} + diff --git a/leakmalloc.h b/leakmalloc.h new file mode 100644 index 0000000..5a0248b --- /dev/null +++ b/leakmalloc.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LEAKMALLOC_H +#define _LEAKMALLOC_H + +#include + +char *leak_strdup(const char *s); +void *leak_malloc(size_t len); +void *leak_calloc(size_t nmemb, size_t size); +void *leak_realloc(void *s, size_t len); +void leak_free(void *s); + +#ifndef LEAKMALLOC_NO_REDIRECT +#define malloc leak_malloc +#define strdup leak_strdup +#define calloc leak_calloc +#define realloc leak_realloc +#define free leak_free +#endif /* LEAKMALLOC_NO_REDIRECT */ + +#endif /* _LEAKMALLOC_H */ +