+ struct debug_alloc_hdr *hdr, *h;
+ struct debug_alloc_ftr *ftr;
+ size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
+ void *ret;
+
+ if (ptr != NULL) {
+ hdr = (ptr - sizeof(*hdr));
+ if (hdr->cookie != 0x12345678) {
+ EC_LOG(EC_LOG_ERR,
+ "%s:%d: error: realloc(%p): bad start cookie\n",
+ file, line, ptr);
+ abort();
+ }
+
+ ftr = (ptr + hdr->size);
+ if (ftr->cookie != 0x87654321) {
+ EC_LOG(EC_LOG_ERR,
+ "%s:%d: error: realloc(%p): bad end cookie\n",
+ file, line, ptr);
+ abort();
+ }
+
+ TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
+ if (h == hdr)
+ break;
+ }
+
+ if (h == NULL) {
+ EC_LOG(EC_LOG_ERR, "%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->seq = malloc_seq;
+ hdr->file = file;
+ hdr->line = line;
+ hdr->size = size;
+ hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
+ hdr->cookie = 0x12345678;
+ TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
+ ftr = (struct debug_alloc_ftr *)(
+ (char *)hdr + size + sizeof(*hdr));
+ ftr->cookie = 0x87654321;
+ }
+
+ EC_LOG(EC_LOG_DEBUG, "%s:%d: info: realloc(%p, %zd) -> %p seq=%d\n",
+ file, line, ptr, size, ret, malloc_seq++);
+
+ if (ret)
+ alloc_success++;
+ return ret;
+}
+
+static int debug_alloc_dump_leaks(void)
+{
+ struct debug_alloc_hdr *hdr;
+ int i;
+ char **buffer;
+
+ EC_LOG(EC_LOG_INFO, "%zd successful allocations\n", alloc_success);
+
+ if (TAILQ_EMPTY(&debug_alloc_hdr_list))
+ return 0;
+
+ TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
+ EC_LOG(EC_LOG_ERR,
+ "%s:%d: error: memory leak seq=%u size=%zd ptr=%p\n",
+ hdr->file, hdr->line, hdr->seq, hdr->size, hdr + 1);
+ buffer = backtrace_symbols(hdr->stack, hdr->stacklen);
+ if (buffer == NULL) {
+ for (i = 0; i < hdr->stacklen; i++)
+ EC_LOG(EC_LOG_ERR, " %p\n", hdr->stack[i]);
+ } else {
+ for (i = 0; i < hdr->stacklen; i++)
+ EC_LOG(EC_LOG_ERR, " %s\n",
+ buffer ? buffer[i] : "unknown");
+ }
+ free(buffer);
+ }
+
+ EC_LOG(EC_LOG_ERR,
+ " missing static syms, use: addr2line -f -e <prog> <addr>\n");
+
+ return -1;
+}
+
+static int debug_log(int type, unsigned int level, void *opaque,
+ const char *str)
+{
+ (void)type;
+ (void)opaque;
+
+ if (level > (unsigned int)log_level)
+ return 0;
+
+ if (printf("%s", str) < 0)
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int i, ret = 0, leaks;
+
+ ret = parse_args(argc, argv);
+ if (ret < 0)
+ return 1;
+
+ argc -= ret;
+ argv += ret;
+
+ srandom(seed);
+
+ /* register a new malloc to track memleaks */
+ TAILQ_INIT(&debug_alloc_hdr_list);
+ if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
+ EC_LOG(EC_LOG_ERR, "cannot register new malloc\n");
+ return 1;
+ }
+
+ if (ec_init() < 0) {
+ fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno));
+ return 1;
+ }
+ ec_log_fct_register(debug_log, NULL);
+
+ ret = 0;
+ if (argc <= 1) {
+ ret = ec_test_all();
+ } else {
+ for (i = 1; i < argc; i++)
+ ret |= ec_test_one(argv[i]);
+ }
+
+ leaks = debug_alloc_dump_leaks();
+
+ if (alloc_fail_proba == 0 && ret != 0) {
+ printf("tests failed\n");
+ return 1;
+ } else if (alloc_fail_proba != 0 && leaks != 0) {
+ printf("tests failed (memory leak)\n");
+ return 1;
+ }