debug malloc to track mem leaks
authorOlivier Matz <zer0@droids-corp.org>
Fri, 28 Oct 2016 14:13:25 +0000 (16:13 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Fri, 28 Oct 2016 14:13:25 +0000 (16:13 +0200)
lib/build/test
lib/ecoli_malloc.c
lib/ecoli_malloc.h
lib/ecoli_test.c
lib/ecoli_tk_int.c
lib/ecoli_tk_or.c
lib/ecoli_tk_or.h
lib/ecoli_tk_seq.c

index 68efeb7..34b89ac 100755 (executable)
Binary files a/lib/build/test and b/lib/build/test differ
index 11782a7..0bdbf21 100644 (file)
@@ -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;
 
index ddf1828..4c49649 100644 (file)
 
 #include <sys/types.h>
 
-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_;                                                           \
        })
 
 
index 161c52c..e3942c6 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <string.h>
 
+#include <ecoli_malloc.h>
 #include <ecoli_test.h>
 #include <ecoli_tk.h>
 
@@ -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;
 }
index bc6ee87..41ed54a 100644 (file)
@@ -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);
index a81d669..9276c70 100644 (file)
@@ -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;
 }
index 130ec4d..a04c6a3 100644 (file)
@@ -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
index a1d3e68..4c182c4 100644 (file)
 
 // 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;
 }