From: Olivier Matz Date: Fri, 28 Oct 2016 14:13:25 +0000 (+0200) Subject: debug malloc to track mem leaks X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=81b21b1fdbd587fc2ac9ea465bc945efa45ab7b4;p=protos%2Flibecoli.git debug malloc to track mem leaks --- diff --git a/lib/build/test b/lib/build/test index 68efeb7..34b89ac 100755 Binary files a/lib/build/test and b/lib/build/test differ diff --git a/lib/ecoli_malloc.c b/lib/ecoli_malloc.c index 11782a7..0bdbf21 100644 --- a/lib/ecoli_malloc.c +++ b/lib/ecoli_malloc.c @@ -47,17 +47,25 @@ int ec_malloc_register(ec_malloc_t usr_malloc, ec_free_t usr_free, return 0; } -void *__ec_malloc(size_t size) +void ec_malloc_unregister(void) { - return ec_malloc_handler.malloc(size); + ec_malloc_handler.malloc = NULL; + ec_malloc_handler.free = NULL; + ec_malloc_handler.realloc = NULL; } -void __ec_free(void *ptr) +void *__ec_malloc(size_t size, const char *file, unsigned int line) { - ec_malloc_handler.free(ptr); + return ec_malloc_handler.malloc(size, file, line); } -void *__ec_calloc(size_t nmemb, size_t size) +void __ec_free(void *ptr, const char *file, unsigned int line) +{ + ec_malloc_handler.free(ptr, file, line); +} + +void *__ec_calloc(size_t nmemb, size_t size, const char *file, + unsigned int line) { void *ptr; size_t total; @@ -68,7 +76,7 @@ void *__ec_calloc(size_t nmemb, size_t size) return NULL; } - ptr = __ec_malloc(total); + ptr = __ec_malloc(total, file, line); if (ptr == NULL) return NULL; @@ -76,17 +84,17 @@ void *__ec_calloc(size_t nmemb, size_t size) return ptr; } -void *__ec_realloc(void *ptr, size_t size) +void *__ec_realloc(void *ptr, size_t size, const char *file, unsigned int line) { - return ec_malloc_handler.realloc(ptr, size); + return ec_malloc_handler.realloc(ptr, size, file, line); } -char *__ec_strdup(const char *s) +char *__ec_strdup(const char *s, const char *file, unsigned int line) { size_t sz = strlen(s) + 1; char *s2; - s2 = __ec_malloc(sz); + s2 = __ec_malloc(sz, file, line); if (s2 == NULL) return NULL; @@ -95,12 +103,12 @@ char *__ec_strdup(const char *s) return s2; } -char *__ec_strndup(const char *s, size_t n) +char *__ec_strndup(const char *s, size_t n, const char *file, unsigned int line) { size_t sz = strnlen(s, n); char *s2; - s2 = __ec_malloc(sz + 1); + s2 = __ec_malloc(sz + 1, file, line); if (s2 == NULL) return NULL; diff --git a/lib/ecoli_malloc.h b/lib/ecoli_malloc.h index ddf1828..4c49649 100644 --- a/lib/ecoli_malloc.h +++ b/lib/ecoli_malloc.h @@ -30,9 +30,10 @@ #include -typedef void *(*ec_malloc_t)(size_t size); -typedef void (*ec_free_t)(void *ptr); -typedef void *(*ec_realloc_t)(void *ptr, size_t size); +typedef void *(*ec_malloc_t)(size_t size, const char *file, unsigned int line); +typedef void (*ec_free_t)(void *ptr, const char *file, unsigned int line); +typedef void *(*ec_realloc_t)(void *ptr, size_t size, const char *file, + unsigned int line); struct ec_malloc_handler { ec_malloc_t malloc; @@ -44,68 +45,71 @@ extern struct ec_malloc_handler ec_malloc_handler; int ec_malloc_register(ec_malloc_t usr_malloc, ec_free_t usr_free, ec_realloc_t usr_realloc); +void ec_malloc_unregister(void); /* internal */ -void *__ec_malloc(size_t size); -void __ec_free(void *ptr); -void *__ec_calloc(size_t nmemb, size_t size); -void *__ec_realloc(void *ptr, size_t size); -char *__ec_strdup(const char *s); -char *__ec_strndup(const char *s, size_t n); +void *__ec_malloc(size_t size, const char *file, unsigned int line); +void __ec_free(void *ptr, const char *file, unsigned int line); +void *__ec_calloc(size_t nmemb, size_t size, const char *file, + unsigned int line); +void *__ec_realloc(void *ptr, size_t size, const char *file, unsigned int line); +char *__ec_strdup(const char *s, const char *file, unsigned int line); +char *__ec_strndup(const char *s, size_t n, const char *file, + unsigned int line); /* we use macros here to ensure the file/line stay correct when * debugging the standard malloc with valgrind */ -#define ec_malloc(sz) ({ \ - void *ret_; \ - if (ec_malloc_handler.malloc == NULL) \ - ret_ = malloc(sz); \ - else \ - ret_ = __ec_malloc(sz); \ - ret_; \ +#define ec_malloc(sz) ({ \ + void *ret_; \ + if (ec_malloc_handler.malloc == NULL) \ + ret_ = malloc(sz); \ + else \ + ret_ = __ec_malloc(sz, __FILE__, __LINE__); \ + ret_; \ }) -#define ec_free(ptr) ({ \ - if (ec_malloc_handler.free == NULL) \ - free(ptr); \ - else \ - __ec_free(ptr); \ +#define ec_free(ptr) ({ \ + if (ec_malloc_handler.free == NULL) \ + free(ptr); \ + else \ + __ec_free(ptr, __FILE__, __LINE__); \ }) -#define ec_realloc(ptr, sz) ({ \ - void *ret_; \ - if (ec_malloc_handler.realloc == NULL) \ - ret_ = realloc(ptr, sz); \ - else \ - ret_ = __ec_realloc(ptr, sz); \ - ret_; \ +#define ec_realloc(ptr, sz) ({ \ + void *ret_; \ + if (ec_malloc_handler.realloc == NULL) \ + ret_ = realloc(ptr, sz); \ + else \ + ret_ = __ec_realloc(ptr, sz, __FILE__, __LINE__); \ + ret_; \ }) -#define ec_calloc(n, sz) ({ \ - void *ret_; \ - if (ec_malloc_handler.malloc == NULL) \ - ret_ = calloc(n, sz); \ - else \ - ret_ = __ec_calloc(n, sz); \ - ret_; \ +#define ec_calloc(n, sz) ({ \ + void *ret_; \ + if (ec_malloc_handler.malloc == NULL) \ + ret_ = calloc(n, sz); \ + else \ + ret_ = __ec_calloc(n, sz, __FILE__, __LINE__); \ + ret_; \ }) -#define ec_strdup(s) ({ \ - void *ret_; \ - if (ec_malloc_handler.malloc == NULL) \ - ret_ = strdup(s); \ - else \ - ret_ = __ec_strdup(s); \ - ret_; \ +#define ec_strdup(s) ({ \ + void *ret_; \ + if (ec_malloc_handler.malloc == NULL) \ + ret_ = strdup(s); \ + else \ + ret_ = __ec_strdup(s, __FILE__, __LINE__); \ + ret_; \ }) -#define ec_strndup(s, n) ({ \ - void *ret_; \ - if (ec_malloc_handler.malloc == NULL) \ - ret_ = strndup(s, n); \ - else \ - ret_ = __ec_strndup(s, n); \ - ret_; \ +#define ec_strndup(s, n) ({ \ + void *ret_; \ + if (ec_malloc_handler.malloc == NULL) \ + ret_ = strndup(s, n); \ + else \ + ret_ = __ec_strndup(s, n, __FILE__, __LINE__); \ + ret_; \ }) diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index 161c52c..e3942c6 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -92,6 +93,140 @@ int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input, return ret; } +TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr); +static struct debug_alloc_hdr_list debug_alloc_hdr_list = + TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list); + +struct debug_alloc_hdr { + TAILQ_ENTRY(debug_alloc_hdr) next; + const char *file; + unsigned int line; + size_t size; + unsigned int cookie; +}; + +static void *debug_malloc(size_t size, const char *file, unsigned int line) +{ + struct debug_alloc_hdr *hdr; + size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int); + void *ret; + + hdr = malloc(new_size); + if (hdr == NULL) { + ret = NULL; + } else { + hdr->file = file; + hdr->line = line; + hdr->size = size; + hdr->cookie = 0x12345678; + TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next); + ret = hdr + 1; + } + + printf("%s:%d: info: malloc(%zd) -> %p\n", file, line, size, ret); + + return ret; +} + +static void debug_free(void *ptr, const char *file, unsigned int line) +{ + struct debug_alloc_hdr *hdr, *h; + + (void)file; + (void)line; + + printf("%s:%d: info: free(%p)\n", file, line, ptr); + + if (ptr == NULL) + return; + + hdr = (ptr - sizeof(*hdr)); + if (hdr->cookie != 0x12345678) { + printf("%s:%d: error: free(%p): bad start cookie\n", + file, line, ptr); + abort(); + } + + TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) { + if (h == hdr) + break; + } + + if (h == NULL) { + printf("%s:%d: error: free(%p): bad ptr\n", + file, line, ptr); + abort(); + } + + TAILQ_REMOVE(&debug_alloc_hdr_list, hdr, next); + free(hdr); +} + +void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line) +{ + struct debug_alloc_hdr *hdr = (ptr - sizeof(*hdr)); + struct debug_alloc_hdr *h; + size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int); + void *ret; + + if (ptr != NULL) { + if (hdr->cookie != 0x12345678) { + printf("%s:%d: error: realloc(%p): bad start cookie\n", + file, line, ptr); + abort(); + } + + TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) { + if (h == hdr) + break; + } + + if (h == NULL) { + printf("%s:%d: error: realloc(%p): bad ptr\n", + file, line, ptr); + abort(); + } + + TAILQ_REMOVE(&debug_alloc_hdr_list, h, next); + hdr = realloc(hdr, new_size); + if (hdr == NULL) { + TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, h, next); + ret = NULL; + } else { + ret = hdr + 1; + } + } else { + hdr = realloc(NULL, new_size); + if (hdr == NULL) + ret = NULL; + else + ret= hdr + 1; + } + + if (hdr != NULL) { + hdr->file = file; + hdr->line = line; + hdr->size = size; + hdr->cookie = 0x12345678; + TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next); + } + + printf("%s:%d: info: realloc(%p, %zd) -> %p\n", + file, line, ptr, size, ret); + + return ret; +} + +void debug_alloc_dump(void) +{ + struct debug_alloc_hdr *hdr; + + TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) { + printf("%s:%d: error: memory leak size=%zd ptr=%p\n", + hdr->file, hdr->line, hdr->size, hdr + 1); + } +} + /* int ec_test_check_tk_complete_list(const struct ec_tk *tk, */ /* const char *input, ...) */ @@ -100,7 +235,13 @@ int ec_test_all(void) struct ec_test *test; int ret = 0; - // XXX register a new malloc to trac memleaks + TAILQ_INIT(&debug_alloc_hdr_list); + + /* register a new malloc to trac memleaks */ + if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) { + printf("cannot register new malloc\n"); + return -1; + } TAILQ_FOREACH(test, &test_list, next) { if (test->test() == 0) { @@ -111,5 +252,8 @@ int ec_test_all(void) } } + ec_malloc_unregister(); + debug_alloc_dump(); + return ret; } diff --git a/lib/ecoli_tk_int.c b/lib/ecoli_tk_int.c index bc6ee87..41ed54a 100644 --- a/lib/ecoli_tk_int.c +++ b/lib/ecoli_tk_int.c @@ -141,6 +141,7 @@ static int testcase(void) if (ec_tk_int_getval(tk, s) != 0) TEST_ERR(); } + ec_parsed_tk_free(p); p = ec_tk_parse(tk, "10"); s = ec_parsed_tk_to_string(p); @@ -150,7 +151,7 @@ static int testcase(void) if (ec_tk_int_getval(tk, s) != 10) TEST_ERR(); } - + ec_parsed_tk_free(p); ec_tk_free(tk); tk = ec_tk_int_new(NULL, -1, LLONG_MAX, 16); @@ -174,7 +175,7 @@ static int testcase(void) if (ec_tk_int_getval(tk, s) != 16) TEST_ERR(); } - + ec_parsed_tk_free(p); ec_tk_free(tk); tk = ec_tk_int_new(NULL, LLONG_MIN, 0, 10); diff --git a/lib/ecoli_tk_or.c b/lib/ecoli_tk_or.c index a81d669..9276c70 100644 --- a/lib/ecoli_tk_or.c +++ b/lib/ecoli_tk_or.c @@ -92,7 +92,10 @@ static struct ec_completed_tk *complete(const struct ec_tk *gen_tk, static void free_priv(struct ec_tk *gen_tk) { struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; + unsigned int i; + for (i = 0; i < tk->len; i++) + ec_tk_free(tk->table[i]); ec_free(tk->table); } @@ -108,16 +111,12 @@ struct ec_tk *ec_tk_or_new(const char *id) tk = (struct ec_tk_or *)ec_tk_new(id, &or_ops, sizeof(*tk)); if (tk == NULL) - goto fail; + return NULL; tk->table = NULL; tk->len = 0; return &tk->gen; - -fail: - ec_free(tk); - return NULL; } struct ec_tk *ec_tk_or_new_list(const char *id, ...) @@ -145,26 +144,26 @@ struct ec_tk *ec_tk_or_new_list(const char *id, ...) return &tk->gen; fail: - ec_free(tk); // XXX use tk_free? we need to delete all child on error + ec_tk_free(&tk->gen); /* will also free children */ va_end(ap); return NULL; } -int ec_tk_or_add(struct ec_tk *tk, struct ec_tk *child) +int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child) { - struct ec_tk_or *or = (struct ec_tk_or *)tk; + struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; struct ec_tk **table; assert(tk != NULL); assert(child != NULL); - table = realloc(or->table, (or->len + 1) * sizeof(*or->table)); + table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table)); if (table == NULL) return -1; - or->table = table; - table[or->len] = child; - or->len ++; + tk->table = table; + table[tk->len] = child; + tk->len ++; return 0; } diff --git a/lib/ecoli_tk_or.h b/lib/ecoli_tk_or.h index 130ec4d..a04c6a3 100644 --- a/lib/ecoli_tk_or.h +++ b/lib/ecoli_tk_or.h @@ -41,8 +41,10 @@ struct ec_tk_or { struct ec_tk *ec_tk_or_new(const char *id); /* list must be terminated with EC_TK_ENDLIST */ +/* all token given in the list will be freed when freeing this one */ struct ec_tk *ec_tk_or_new_list(const char *id, ...); +/* all token given in the list will be freed when freeing this one */ int ec_tk_or_add(struct ec_tk *tk, struct ec_tk *child); #endif diff --git a/lib/ecoli_tk_seq.c b/lib/ecoli_tk_seq.c index a1d3e68..4c182c4 100644 --- a/lib/ecoli_tk_seq.c +++ b/lib/ecoli_tk_seq.c @@ -37,20 +37,20 @@ // XXX to handle the quote, it will be done in tk_shseq // it will unquote the string and parse each token separately -static struct ec_parsed_tk *parse(const struct ec_tk *tk, +static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, const char *str) { - struct ec_tk_seq *seq = (struct ec_tk_seq *)tk; + struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; struct ec_parsed_tk *parsed_tk, *child_parsed_tk; size_t len = 0; unsigned int i; - parsed_tk = ec_parsed_tk_new(tk); + parsed_tk = ec_parsed_tk_new(gen_tk); if (parsed_tk == NULL) return NULL; - for (i = 0; i < seq->len; i++) { - child_parsed_tk = ec_tk_parse(seq->table[i], str + len); + for (i = 0; i < tk->len; i++) { + child_parsed_tk = ec_tk_parse(tk->table[i], str + len); if (child_parsed_tk == NULL) goto fail; @@ -67,8 +67,19 @@ static struct ec_parsed_tk *parse(const struct ec_tk *tk, return NULL; } +static void free_priv(struct ec_tk *gen_tk) +{ + struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; + unsigned int i; + + for (i = 0; i < tk->len; i++) + ec_tk_free(tk->table[i]); + ec_free(tk->table); +} + static struct ec_tk_ops seq_ops = { .parse = parse, + .free_priv = free_priv, }; struct ec_tk *ec_tk_seq_new(const char *id) @@ -77,16 +88,12 @@ struct ec_tk *ec_tk_seq_new(const char *id) tk = (struct ec_tk_seq *)ec_tk_new(id, &seq_ops, sizeof(*tk)); if (tk == NULL) - goto fail; + return NULL; tk->table = NULL; tk->len = 0; return &tk->gen; - -fail: - ec_free(tk); - return NULL; } struct ec_tk *ec_tk_seq_new_list(const char *id, ...) @@ -114,26 +121,26 @@ struct ec_tk *ec_tk_seq_new_list(const char *id, ...) return &tk->gen; fail: - ec_free(tk); // XXX use tk_free? we need to delete all child on error + ec_tk_free(&tk->gen); /* will also free children */ va_end(ap); return NULL; } -int ec_tk_seq_add(struct ec_tk *tk, struct ec_tk *child) +int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child) { - struct ec_tk_seq *seq = (struct ec_tk_seq *)tk; + struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; struct ec_tk **table; assert(tk != NULL); assert(child != NULL); - table = realloc(seq->table, seq->len + 1); + table = ec_realloc(tk->table, tk->len + 1); if (table == NULL) return -1; - seq->table = table; - table[seq->len] = child; - seq->len ++; + tk->table = table; + table[tk->len] = child; + tk->len ++; return 0; }