From a840cc0a5d08237063cff5d06f927566008f57db Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Fri, 22 Apr 2016 13:34:17 +0900 Subject: [PATCH] mtrack! my little evil memory debugger in a can :) --- build.sh | 20 + mtd | 8 + mtdump.c | 219 +++++++++ mtf | 4 + mtrack.c | 1361 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtshow.c | 197 ++++++++ mtt | 5 + 7 files changed, 1814 insertions(+) create mode 100755 build.sh create mode 100755 mtd create mode 100644 mtdump.c create mode 100755 mtf create mode 100644 mtrack.c create mode 100644 mtshow.c create mode 100755 mtt diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..4424480 --- /dev/null +++ b/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh +gcc -O2 mtrack.c -shared -o mtrack.so -ldl -pthread -fPIC -g +gcc -O2 mtdump.c -o mtdump -g `pkg-config --cflags --libs eina` +gcc -O2 mtshow.c -o mtshow -g `pkg-config --cflags --libs eina` + +sudo rm -f /usr/local/bin/mtshow +sudo rm -f /usr/local/bin/mtdump +sudo rm -f /usr/local/lib/mtrack.so +sudo rm -f /usr/local/bin/mtf +sudo rm -f /usr/local/bin/mtt +sudo cp mtshow /usr/local/bin/mtshow +sudo cp mtdump /usr/local/bin/mtdump +sudo cp mtrack.so /usr/local/lib/mtrack.so +sudo cp mtf /usr/local/bin/mtf +sudo cp mtt /usr/local/bin/mtt +sudo cp mtd /usr/local/bin/mtd + +#rm -f mtrack.so +#rm -f mtdump +rm -f *~ diff --git a/mtd b/mtd new file mode 100755 index 0000000..8db6769 --- /dev/null +++ b/mtd @@ -0,0 +1,8 @@ +#!/bin/sh +export MTRACK=debug +export MTRACK_ALLOC_FILL=2 +export MTRACK_FREE_FILL=1 +export MTRACK_CANARY_SIZE=16 +export MTRACK_CANARY=3 +export LD_PRELOAD=/usr/local/lib/mtrack.so +exec $@ diff --git a/mtdump.c b/mtdump.c new file mode 100644 index 0000000..6f18d0c --- /dev/null +++ b/mtdump.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include + +static char * +pstr(const char *cmd, int lines) +{ + FILE *f; + char *str = NULL; + char buf[4096]; + int str_len = 0; + int n = 0; + int count = 0; + + f = popen(cmd, "r"); + if (!f) return NULL; + while (fgets(buf, sizeof(buf) - 1, f)) + { + int len = strlen(buf); + if ((len > 1) && (buf[len - 1] == '\n')) + { + buf[len - 1] = 0; + len--; + } + str_len += len; + str = realloc(str, str_len + 1); + if (count == 0) str[0] = 0; + strcat(str, buf); + n++; + count++; + if (n >= lines) break; + } + pclose(f); + return str; +} + +static char * +addr2func(const char *file, const char *addr) +{ + char *s; + char cmd[4096], buf[4096]; + static Eina_Hash *hash = NULL; + + if (!hash) hash = eina_hash_string_superfast_new(NULL); + snprintf(buf, sizeof(buf), "%s %s", file, addr); + s = eina_hash_find(hash, buf); + if (s) return strdup(s); + snprintf(cmd, sizeof(cmd), "addr2line -f -e %s", buf); + s = pstr(cmd, 1); + eina_hash_add(hash, buf, s); + return strdup(s); +} + +static FILE *outf = NULL; + +#define OUT(fmt, args...) fprintf(outf, fmt, ##args) + +int +main(int argc, char **argv) +{ + FILE *f; + char buf[4096]; + int items = 0; + int btnum = 0; + int expected = 0; + time_t t0; + + if (argc < 3) + { + printf("usage: mtdump MTLOGFILE OUTFILE\n"); + exit(1); + } + + f = fopen(argv[1], "r"); + if (!f) + { + perror("fopen"); + exit(1); + } + + outf = fopen(argv[2], "w"); + if (!outf) + { + perror("fopen"); + exit(1); + } + + while (fgets(buf, sizeof(buf) - 1, f)) + { + if (!strncmp(buf, "== ", 3)) expected++; + } + fseek(f, 0, SEEK_SET); + t0 = time(NULL); + while (fgets(buf, sizeof(buf) - 1, f)) + { + int len; + + len = strlen(buf); + if (len < 2) continue; + if ((len > 1) && (buf[len - 1] == '\n')) + { + buf[len - 1] = 0; + len--; + } + if (!strncmp(buf, "== ", 3)) + { + unsigned long size; + char type[4096] = ""; + unsigned long alloctime; + + if (sscanf(buf, "%*s %s %lu %*s %lu", type, &size, &alloctime) + == 3) + { + time_t t; + struct tm *tmp; + char out[256] = "??"; + int eta; + + if (items > 0) OUT("\n"); + items++; + t = time(NULL) - t0; + eta = (expected * (int)t) / items; + printf("%i/%i -- ETA %i min %i sec \r", + items, expected, eta / 60, eta % 60); + fflush(stdout); + btnum = 0; + t = alloctime; + tmp = localtime(&t); + if (tmp) + strftime(out, sizeof(out), "%F/%T", tmp); + else + strcpy(out, "??"); + OUT("%lu %s %s |", size, type, out); + } + } + else + { + if (btnum > 0) + { + if (strchr(buf, '[')) + { + // 1 of: + // /usr/local/lib/libeina.so.1(+0x16ab8)[0x7fdaccc4cab8] + // /lib/x86_64-linux-gnu/libc.so.6(__strdup+0x22)[0x7fdace0732f2] + // ello[0x402d8c] + // /usr/local/bin/ello[0x402d8c] + if (strchr(buf, '(')) + { + char file[4096], addr[4096]; + // 1 of: + // /usr/local/lib/libeina.so.1(+0x16ab8)[0x7fdaccc4cab8] + // /lib/x86_64-linux-gnu/libc.so.6(__strdup+0x22)[0x7fdace0732f2] + if (sscanf(buf, "%[^(](%[^)])%*s", file, addr) == 2) + { + if (addr[0] == '+') + { + char *s = addr2func(file, addr + 1); + if (s) + { + OUT(" %s", s); + free(s); + } + else + OUT(" ??"); + } + else + { + char *s; + + s = strchr(addr, '+'); + if (s) *s = 0; + OUT(" %s", addr); + } + } + else + OUT(" ??"); + } + else + { + // 1 of: + // ello[0x402d8c] + // /usr/local/bin/ello[0x402d8c] + if (buf[0] == '/') + { + char file[4096], addr[4096]; + + if (sscanf(buf, "%[^\\[][%[^]]]", file, addr) == 2) + { + char *s = addr2func(file, addr); + if (s) + { + OUT(" %s", s); + free(s); + } + else + OUT(" ??"); + } + else + OUT(" ??"); + } + else + OUT(" ?main?"); + } + } + else + OUT(" ??"); + } + btnum++; + } + } + fclose(f); + fclose(outf); + + exit(0); + return 0; +} diff --git a/mtf b/mtf new file mode 100755 index 0000000..ca228ab --- /dev/null +++ b/mtf @@ -0,0 +1,4 @@ +#!/bin/sh +export MTRACK=fill +export LD_PRELOAD=/usr/local/lib/mtrack.so +exec $@ diff --git a/mtrack.c b/mtrack.c new file mode 100644 index 0000000..8eb867e --- /dev/null +++ b/mtrack.c @@ -0,0 +1,1361 @@ +/* +gcc -O2 mtrack.c -shared -o mtrack.so -ldl -pthread + */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +#else +# define EAPI +#endif + +static void _mhook_do_init(void); + +/*============================================================================* +* Local * +*============================================================================*/ + +enum { + MHOOK_NONE = 0, + MHOOK_FILL = 1, + MHOOK_TRACE = 2, + MHOOK_TRACK = 3, + MHOOK_DEBUG = 4 +}; + +/** + * @cond LOCAL + */ + +#define MHOOK_CANARY_MAGIC 0xf0 + +static int _mhook_mode = MHOOK_NONE; + +static unsigned char _mhook_fill_pat_malloc = 0xaa; +static unsigned char _mhook_fill_pat_free = 0xbb; + +static unsigned char _mhook_canary_size = 0x00; +static unsigned char _mhook_canary = 0xfe; + +static int _mhook_trace_fd = -1; +static int _mhook_track_fd = -1; + +static unsigned char _mhook_buf[256]; +static int _mhook_first = 1; + +static void *(*_mhook_orig_malloc) (size_t) = NULL; +static void (*_mhook_orig_free) (void *) = NULL; +static void *(*_mhook_orig_realloc) (void *, size_t) = NULL; +static void *(*_mhook_orig_memalign) (size_t, size_t) = NULL; +static void *(*_mhook_orig_calloc) (size_t, size_t) = NULL; + +static pthread_mutex_t lk; + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + + + + + + + +/*****************************************************************************/ +/* memory null implementation */ + +static void * +_mhook_null_malloc(size_t size) +{ + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) return _mhook_buf; + result = _mhook_orig_malloc(size); + return result; +} + +static void +_mhook_null_free(void *ptr) +{ + // FIXME: hack around dlsym doing a malloc/calloc + if (ptr == _mhook_buf) return; + _mhook_orig_free(ptr); +} + +static void * +_mhook_null_realloc(void *ptr, size_t size) +{ + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (ptr == _mhook_buf) + { + fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n"); + abort(); + } + result = _mhook_orig_realloc(ptr, size); + return result; +} + +static void * +_mhook_null_memalign(size_t alignment, size_t size) +{ + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_memalign) return _mhook_buf; + result = _mhook_orig_memalign(alignment, size); + return result; +} + +static void * +_mhook_null_calloc(size_t nmemb, size_t size) +{ + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) + { + memset(_mhook_buf, 0, nmemb * size); + return _mhook_buf; + } + result = _mhook_orig_calloc(nmemb, size); + return result; +} + + + + + + + + + +/*****************************************************************************/ +/* memory filler implementation */ +#define MHOOK_FILL_HEADER_SPACE (sizeof(void *) * 2) +typedef struct _Mhook_Mem_Fill +{ + size_t size; + void *magic; +} Mhook_Mem_Fill; + +static void * +_mhook_fill_malloc(size_t size) +{ + Mhook_Mem_Fill *m; + size_t ms = size + MHOOK_FILL_HEADER_SPACE; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) return _mhook_buf; + m = _mhook_orig_malloc(ms); + if (m) + { + memset(m, _mhook_fill_pat_malloc, MHOOK_FILL_HEADER_SPACE); + result = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE; + m->size = size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + memset(result, _mhook_fill_pat_malloc, size); + } + return result; +} + +static void +_mhook_fill_free(void *ptr) +{ + Mhook_Mem_Fill *m; + + // FIXME: hack around dlsym doing a malloc/calloc + if (ptr == _mhook_buf) return; + if (ptr) + { + m = (Mhook_Mem_Fill *)(((unsigned char *)ptr) - MHOOK_FILL_HEADER_SPACE); + memset(ptr, _mhook_fill_pat_free, m->size); + memset(m, _mhook_fill_pat_free, MHOOK_FILL_HEADER_SPACE); + _mhook_orig_free(m); + } +} + +static void * +_mhook_fill_realloc(void *ptr, size_t size) +{ + Mhook_Mem_Fill *m, *mold; + void *result = NULL; + size_t ms = size + MHOOK_FILL_HEADER_SPACE; + + // FIXME: hack around dlsym doing a malloc/calloc + if (ptr == _mhook_buf) + { + fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n"); + abort(); + } + if (size == 0) + { + if (ptr) + { + m = (Mhook_Mem_Fill *)(((unsigned char *)ptr) - MHOOK_FILL_HEADER_SPACE); + memset(ptr, _mhook_fill_pat_free, m->size); + memset(m, _mhook_fill_pat_free, MHOOK_FILL_HEADER_SPACE); + _mhook_orig_free(m); + } + } + else if (ptr) + { + m = _mhook_orig_malloc(ms); + if (m) + { + mold = (Mhook_Mem_Fill *)(((unsigned char *)ptr) - MHOOK_FILL_HEADER_SPACE); + + memset(m, _mhook_fill_pat_malloc, MHOOK_FILL_HEADER_SPACE); + result = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE; + m->size = size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + if (mold->size < m->size) + { + memcpy(result, ptr, mold->size); + memset(((unsigned char *)result) + mold->size, + _mhook_fill_pat_malloc, m->size - mold->size); + } + else + memcpy(result, ptr, m->size); + + memset(ptr, _mhook_fill_pat_free, mold->size); + memset(mold, _mhook_fill_pat_free, MHOOK_FILL_HEADER_SPACE); + _mhook_orig_free(mold); + } + } + else + { + m = _mhook_orig_malloc(ms); + if (m) + { + result = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE; + m->size = size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + memset(result, _mhook_fill_pat_malloc, size); + } + } + return result; +} + +static void * +_mhook_fill_memalign(size_t alignment, size_t size) +{ + Mhook_Mem_Fill *m; + size_t ms = size + MHOOK_FILL_HEADER_SPACE + alignment; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_memalign) return _mhook_buf; + m = _mhook_orig_malloc(ms); + if (m) + { + unsigned char *p = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE; + unsigned char *palign; + size_t align, mask = ~(alignment - 1); + + palign = (unsigned char *)((unsigned long)((p + (alignment - 1))) & mask); + align = palign - p; + memset(m, _mhook_fill_pat_malloc, align); + result = ((unsigned char *)m) + align; + m->size = size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + memset(result, _mhook_fill_pat_malloc, size); + } + return result; +} + +static void * +_mhook_fill_calloc(size_t nmemb, size_t size) +{ + Mhook_Mem_Fill *m; + size_t ms = (size * nmemb) + MHOOK_FILL_HEADER_SPACE; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) + { + memset(_mhook_buf, 0, nmemb * size); + return _mhook_buf; + } + m = _mhook_orig_malloc(ms); + if (m) + { + memset(m, _mhook_fill_pat_malloc, MHOOK_FILL_HEADER_SPACE); + result = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE; + m->size = nmemb * size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + memset(result, 0, nmemb * size); + } + return result; +} + + + + + + +/*****************************************************************************/ +/* memory trace implementation */ + +static void +_mhook_trace_log(const char *str) +{ + void *bt[256]; + char buf[16384], *p; + int n, skip, i, tot; + static int inside = 0; + + if (inside) + { + if (write(_mhook_trace_fd, str, strlen(str)) < 0) perror("write"); + if (write(_mhook_trace_fd, " XX\n", 4) < 0) perror("write"); + return; + } + inside = 1; + tot = sizeof(buf); + n = backtrace(bt, 256); + strcpy(buf, str); + p = buf + strlen(str); tot -= p - buf; + strcpy(p, " =="); p += 3; tot -= 3; + if (n > 0) + { + skip = 3; + + for (i = skip; i < n; i++) + { + int len = 0; + Dl_info info; + + if (dladdr(bt[i], &info)) + { + len = snprintf(p, tot, " %s", info.dli_sname); + } + else + len = snprintf(p, tot, " ?"); + p += len; tot -= len; + } + } + p[0] = '\n'; p += 1; tot -= 1; + if (write(_mhook_trace_fd, buf, p - buf) < 0) perror("write"); + inside = 0; +} + +static void +intstr(unsigned long value, char *str, int radix) +{ + static char dig[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + int n = 0, neg = 0; + unsigned long v; + char *p, *q; + char c; + + if ((radix == 10) && (value < 0)) + { + value = -value; + neg = 1; + } + v = value; + do + { + str[n++] = dig[v % radix]; + v /= radix; + } + while (v); + if (neg) str[n++] = '-'; + str[n] = 0; + for (p = str, q = p + (n - 1); p < q; p++, q--) + { + c = *p; *p = *q; *q = c; + } +} +static void * +_mhook_trace_malloc(size_t size) +{ + char buf[256], b[64]; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) return _mhook_buf; + result = _mhook_orig_malloc(size); + strcpy(buf, "M "); + intstr((unsigned long)size, b, 16); + strcat(buf, b); + strcat(buf, " > "); + intstr((unsigned long)result, b, 16); + strcat(buf, b); + _mhook_trace_log(buf); + return result; +} + +static void +_mhook_trace_free(void *ptr) +{ + char buf[256], b[64]; + // FIXME: hack around dlsym doing a malloc/calloc + if (ptr == _mhook_buf) return; + _mhook_orig_free(ptr); + strcpy(buf, "F "); + intstr((unsigned long)ptr, b, 16); + strcat(buf, b); + _mhook_trace_log(buf); +} + +static void * +_mhook_trace_realloc(void *ptr, size_t size) +{ + char buf[256], b[64]; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (ptr == _mhook_buf) + { + fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n"); + abort(); + } + result = _mhook_orig_realloc(ptr, size); + strcpy(buf, "R "); + intstr((unsigned long)ptr, b, 16); + strcat(buf, b); + strcat(buf, " "); + intstr((unsigned long)size, b, 16); + strcat(buf, b); + strcat(buf, " > "); + intstr((unsigned long)result, b, 16); + strcat(buf, b); + _mhook_trace_log(buf); + return result; +} + +static void * +_mhook_trace_memalign(size_t alignment, size_t size) +{ + char buf[256], b[64]; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_memalign) return _mhook_buf; + result = _mhook_orig_memalign(alignment, size); + strcpy(buf, "A "); + intstr((unsigned long)alignment, b, 16); + strcat(buf, b); + strcat(buf, " "); + intstr((unsigned long)size, b, 16); + strcat(buf, b); + strcat(buf, " > "); + intstr((unsigned long)result, b, 16); + strcat(buf, b); + _mhook_trace_log(buf); + return result; +} + +static void * +_mhook_trace_calloc(size_t nmemb, size_t size) +{ + char buf[256], b[64]; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) + { + memset(_mhook_buf, 0, nmemb * size); + return _mhook_buf; + } + result = _mhook_orig_calloc(nmemb, size); + strcpy(buf, "C "); + intstr((unsigned long)(nmemb * size), b, 16); + strcat(buf, b); + strcat(buf, " > "); + intstr((unsigned long)result, b, 16); + strcat(buf, b); + _mhook_trace_log(buf); + return result; +} + + + + + + +/*****************************************************************************/ +/* memory tracker implementation */ +#define MALLOC (void *)0 +#define REALLOC (void *)1 +#define CALLOC (void *)2 +#define MEMALIGN (void *)3 + +#define MHOOK_TRACK_HEADER_SPACE (sizeof(void *) * 80) +typedef struct _Mhook_Mem_Track Mhook_Mem_Track; + +struct _Mhook_Mem_Track +{ + size_t size; + void *type; + void *magic; + void *bt[64]; + int btnum; + time_t alloctime; + Mhook_Mem_Track *prev, *next; +}; + +static Mhook_Mem_Track *mems = NULL; +static int allocs = 0; +static int frees = 0; +static int recurse = 0; + +#define BAD_CANARY \ + fprintf(stderr, "BAD CANARY for %p\n", ptr); \ + if (getenv("MTRACK_CANARY_ABORT")) abort(); \ + return + +static void +_mhook_track_dump(void) +{ + Mhook_Mem_Track *m; + + if (ftruncate(_mhook_track_fd, 0) < 0) perror("truncate"); + for (m = mems; m; m = m->next) + { + char buf[512]; + int i; + + char *type[] = { "MALLOC", "REALLOC", "CALLOC", "MEMALIGN" }; + snprintf(buf, sizeof(buf), "== %s %lu @ %lu\n", + type[(unsigned long)m->type], + (unsigned long)m->size, + (unsigned long)m->alloctime); + if (write(_mhook_track_fd, buf, strlen(buf)) < 0) + perror("write"); + if (m->btnum > 0) + backtrace_symbols_fd(m->bt, m->btnum, _mhook_track_fd); + } +} + +static void * +_mhook_track_malloc(size_t size) +{ + Mhook_Mem_Track *m; + size_t ms = size + MHOOK_TRACK_HEADER_SPACE; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) return _mhook_buf; + recurse++; + m = _mhook_orig_malloc(ms); + if (m) + { + memset(m, _mhook_fill_pat_malloc, MHOOK_TRACK_HEADER_SPACE); + result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE; + m->size = size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + memset(result, _mhook_fill_pat_malloc, size); + memset(m->bt, 0, sizeof(m->bt)); + if (recurse < 2) + { + m->btnum = backtrace + (m->bt, sizeof(m->bt) / sizeof(void *)); + m->alloctime = time(NULL); + } + m->next = m->prev = NULL; + m->type = MALLOC; + m->next = mems; + if (mems) mems->prev = m; + mems = m; + allocs++; + } + recurse--; +// printf("MALLOC %p\n", result);fflush(stdout); + return result; +} + +static void +_mhook_track_free(void *ptr) +{ + Mhook_Mem_Track *m; + + if (ptr == _mhook_buf) return; + recurse++; + if (ptr) + { + void *magic; + memset(&(magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + m = (Mhook_Mem_Track *)(((unsigned char *)ptr) - MHOOK_TRACK_HEADER_SPACE); + if (memcmp(&magic, &m->magic, sizeof(void *))) + { + BAD_CANARY; + } + if (m->prev) m->prev->next = m->next; + else mems = m->next; + if (m->next) m->next->prev = m->prev; + memset(ptr, _mhook_fill_pat_free, m->size); + memset(m, _mhook_fill_pat_free, MHOOK_TRACK_HEADER_SPACE); + _mhook_orig_free(m); + frees++; + } +// printf("FREE %p\n", ptr);fflush(stdout); + recurse--; +} + +static void * +_mhook_track_realloc(void *ptr, size_t size) +{ + Mhook_Mem_Track *m, *mold; + void *result = NULL; + size_t ms = size + MHOOK_TRACK_HEADER_SPACE; + + if (ptr == _mhook_buf) + { + fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n"); + abort(); + } + recurse++; + if (size == 0) + { + if (ptr) + { + void *magic; + memset(&(magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + m = (Mhook_Mem_Track *)(((unsigned char *)ptr) - MHOOK_TRACK_HEADER_SPACE); + if (memcmp(&magic, &m->magic, sizeof(void *))) + { + BAD_CANARY NULL; + } + if (m->prev) m->prev->next = m->next; + else mems = m->next; + if (m->next) m->next->prev = m->prev; + memset(ptr, _mhook_fill_pat_free, m->size); + memset(m, _mhook_fill_pat_free, MHOOK_TRACK_HEADER_SPACE); + _mhook_orig_free(m); + frees++; +// printf("R1:FREE %p\n", ptr);fflush(stdout); + } + } + else if (ptr) + { + m = _mhook_orig_malloc(ms); + if (m) + { + void *magic; + mold = (Mhook_Mem_Track *)(((unsigned char *)ptr) - MHOOK_TRACK_HEADER_SPACE); + memset(&(magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + if (memcmp(&magic, &mold->magic, sizeof(void *))) + { + BAD_CANARY NULL; + } + + memset(m, _mhook_fill_pat_malloc, MHOOK_TRACK_HEADER_SPACE); + result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE; + m->size = size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + if (mold->size < m->size) + { + memcpy(result, ptr, mold->size); + memset(((unsigned char *)result) + mold->size, _mhook_fill_pat_malloc, m->size - mold->size); + } + else + memcpy(result, ptr, m->size); + + memset(m->bt, 0, sizeof(m->bt)); + if (recurse < 2) + { + m->btnum = backtrace + (m->bt, sizeof(m->bt) / sizeof(void *)); + m->alloctime = time(NULL); + } + m->type = REALLOC; + + m->next = m->prev = NULL; + m->next = mems; + if (mems) mems->prev = m; + mems = m; + + if (mold->prev) mold->prev->next = mold->next; + else mems = mold->next; + if (mold->next) mold->next->prev = mold->prev; + memset(ptr, _mhook_fill_pat_free, mold->size); + memset(mold, _mhook_fill_pat_free, MHOOK_TRACK_HEADER_SPACE); + _mhook_orig_free(mold); + allocs++; + frees++; +// printf("R2:FREE %p\n", ptr);fflush(stdout); +// printf("R2:MALLOC %p\n", result);fflush(stdout); + } + } + else + { + m = _mhook_orig_malloc(ms); + if (m) + { + result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE; + m->size = size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + memset(result, _mhook_fill_pat_malloc, size); + memset(m->bt, 0, sizeof(m->bt)); + if (recurse < 2) + { + m->btnum = backtrace + (m->bt, sizeof(m->bt) / sizeof(void *)); + m->alloctime = time(NULL); + } + m->type = REALLOC; + m->next = m->prev = NULL; + m->next = mems; + if (mems) mems->prev = m; + mems = m; + allocs++; +// printf("R3:MALLOC %p\n", result);fflush(stdout); + } + } + recurse--; + return result; +} + +static void * +_mhook_track_memalign(size_t alignment, size_t size) +{ + Mhook_Mem_Track *m; + size_t ms; + void *result = NULL; + + if (alignment < 1) alignment = 1; + ms = size + MHOOK_TRACK_HEADER_SPACE + alignment; + if (!_mhook_orig_memalign) return _mhook_buf; + recurse++; + m = _mhook_orig_malloc(ms); + if (m) + { +// unsigned char *p = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE; +// unsigned char *palign; +// size_t align, mask = ~(alignment - 1); + +// align = MHOOK_TRACK_HEADER_SPACE; +// for now lets never align as asked +// palign = (unsigned char *)((unsigned long)((p + (alignment - 1))) & mask); +// align = palign - p; +// memset(m, _mhook_fill_pat_malloc, align); +// result = ((unsigned char *)m) + align; + memset(m, _mhook_fill_pat_malloc, MHOOK_TRACK_HEADER_SPACE); + result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE; + m->size = size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + memset(result, _mhook_fill_pat_malloc, size); + memset(m->bt, 0, sizeof(m->bt)); + if (recurse < 2) + { + m->btnum = backtrace + (m->bt, sizeof(m->bt) / sizeof(void *)); + m->alloctime = time(NULL); + } + m->type = MEMALIGN; + m->next = m->prev = NULL; + m->next = mems; + if (mems) mems->prev = m; + mems = m; + allocs++; + } + recurse--; +// printf("MEMALIGN %p\n", result);fflush(stdout); + return result; +} + +static void * +_mhook_track_calloc(size_t nmemb, size_t size) +{ + Mhook_Mem_Track *m; + size_t ms = (size * nmemb) + MHOOK_TRACK_HEADER_SPACE; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) + { + memset(_mhook_buf, 0, nmemb * size); + return _mhook_buf; + } + recurse++; + m = _mhook_orig_malloc(ms); + if (m) + { + memset(m, _mhook_fill_pat_malloc, MHOOK_TRACK_HEADER_SPACE); + result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE; + m->size = nmemb * size; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); + memset(result, 0, nmemb * size); + memset(m->bt, 0, sizeof(m->bt)); + if (recurse < 2) + { + m->btnum = backtrace + (m->bt, sizeof(m->bt) / sizeof(void *)); + m->alloctime = time(NULL); + } + m->type = CALLOC; + m->next = m->prev = NULL; + m->next = mems; + if (mems) mems->prev = m; + mems = m; + allocs++; + } + recurse--; +// printf("CALLOC %p\n", result);fflush(stdout); + return result; +} + + + + + + +/*****************************************************************************/ +/* memory debug implementation */ +#define MHOOK_DEBUG_CANARY_SPACE (_mhook_canary_size * 8) +#define MHOOK_DEBUG_HEADER_SPACE ((sizeof(void *) * 4) + MHOOK_DEBUG_CANARY_SPACE) +typedef struct _Mhook_Mem_Debug +{ + size_t size; + unsigned char *realptr; + void *magic; + void *blah; +} Mhook_Mem_Debug; + +#define MHOOK_DEBUG_FREEBUF_SIZE (1024 * 256) +//#define MHOOK_DEBUG_FREEBUF_SIZE (1) + +static unsigned int _mhook_debug_freebuf_slot = 0; +static void *_mhook_debug_freebuf[MHOOK_DEBUG_FREEBUF_SIZE] = { NULL }; + +static void +_mhook_debug_canary_check(Mhook_Mem_Debug *m) +{ + unsigned char *head = ((unsigned char *)m) + (sizeof(void *) * 4); + unsigned char *tail = head + MHOOK_DEBUG_CANARY_SPACE + m->size; + int i; + + for (i = 0; i < (_mhook_canary_size * 8); i++) + { + if (head[i] != _mhook_canary) + { + fprintf(stderr, "CANARY ERROR HEAD @ %p = %x\n", + head + MHOOK_DEBUG_CANARY_SPACE, head[i]); + abort(); + } + } + for (i = 0; i < (_mhook_canary_size * 8); i++) + { + if (tail[i] != _mhook_canary) + { + fprintf(stderr, "CANARY ERROR TAIL @ %p = %x\n", + head + MHOOK_DEBUG_CANARY_SPACE, tail[i]); + abort(); + } + } +} + +static void +_mhook_debug_canary_fill(void *ptr, size_t size) +{ + unsigned char *head = ptr; + unsigned char *tail = ptr; + head -= MHOOK_DEBUG_CANARY_SPACE; + tail += size; + + memset(head, _mhook_canary, MHOOK_DEBUG_CANARY_SPACE); + memset(tail, _mhook_canary, MHOOK_DEBUG_CANARY_SPACE); +} + +static void +_mhook_debug_free_check(void *ptr, size_t size) +{ + size_t i; + unsigned char *p = ptr; + + for (i = 0; i < size; i++) + { + if (p[i] != _mhook_fill_pat_free) + { + fprintf(stderr, "CANARY ERROR BODY @ %p = %x\n", + ptr, p[i]); + abort(); + } + } +} + +static void +_mhook_debug_magic_fill(Mhook_Mem_Debug *m) +{ + m->blah = (void *)0xf0f0f0f0; + memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *)); +} + +static void +_mhook_debug_real_free(void *ptr) +{ + Mhook_Mem_Debug *m; + unsigned char *realptr; + size_t psize; + + m = (Mhook_Mem_Debug *)(((unsigned char *)ptr) - MHOOK_DEBUG_HEADER_SPACE); + realptr = m->realptr; + psize = m->size; +// fprintf(stderr, "real free %p size %lu @ slot %i\n", ptr, psize, _mhook_debug_freebuf_slot); + _mhook_debug_canary_check(m); + _mhook_debug_free_check(ptr, psize); + memset(m, _mhook_fill_pat_free, MHOOK_DEBUG_HEADER_SPACE); + memset(((unsigned char *)ptr) + psize, _mhook_fill_pat_free, MHOOK_DEBUG_CANARY_SPACE); + _mhook_orig_free(realptr); +} + +static void +_mhook_debug_free2(void *ptr) +{ +// _mhook_debug_real_free(ptr); +// return; + + _mhook_debug_freebuf[_mhook_debug_freebuf_slot] = ptr; + _mhook_debug_freebuf_slot++; + if (_mhook_debug_freebuf_slot >= MHOOK_DEBUG_FREEBUF_SIZE) + { + _mhook_debug_freebuf_slot = 0; + } + if (_mhook_debug_freebuf[_mhook_debug_freebuf_slot]) + { + _mhook_debug_real_free(_mhook_debug_freebuf[_mhook_debug_freebuf_slot]); + _mhook_debug_freebuf[_mhook_debug_freebuf_slot] = NULL; + } +} + +static void * +_mhook_debug_malloc(size_t size) +{ + Mhook_Mem_Debug *m; + size_t ms = size + MHOOK_DEBUG_HEADER_SPACE + MHOOK_DEBUG_CANARY_SPACE; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) return _mhook_buf; + m = _mhook_orig_malloc(ms); + if (m) + { + result = ((unsigned char *)m) + MHOOK_DEBUG_HEADER_SPACE; + m->realptr = (unsigned char *)m; + m->size = size; + _mhook_debug_magic_fill(m); + memset(result, _mhook_fill_pat_malloc, size); + _mhook_debug_canary_fill(result, size); + } + return result; +} + +static void +_mhook_debug_free(void *ptr) +{ + Mhook_Mem_Debug *m; + size_t psize; + + // FIXME: hack around dlsym doing a malloc/calloc + if (ptr == _mhook_buf) return; + if (ptr) + { + m = (Mhook_Mem_Debug *)(((unsigned char *)ptr) - MHOOK_DEBUG_HEADER_SPACE); + psize = m->size; + + _mhook_debug_canary_check(m); + memset(ptr, _mhook_fill_pat_free, psize); + _mhook_debug_free2(ptr); + } +} + +static void * +_mhook_debug_realloc(void *ptr, size_t size) +{ + Mhook_Mem_Debug *m, *mold; + void *result = NULL; + size_t ms = size + MHOOK_DEBUG_HEADER_SPACE + MHOOK_DEBUG_CANARY_SPACE; + size_t psize; + + // FIXME: hack around dlsym doing a malloc/calloc + if (ptr == _mhook_buf) + { + fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n"); + abort(); + } + if (size == 0) + { + if (ptr) + { + m = (Mhook_Mem_Debug *)(((unsigned char *)ptr) - MHOOK_DEBUG_HEADER_SPACE); + psize = m->size; + + _mhook_debug_canary_check(m); + memset(ptr, _mhook_fill_pat_free, psize); + _mhook_debug_free2(ptr); + } + } + else if (ptr) + { + m = _mhook_orig_malloc(ms); + if (m) + { + mold = (Mhook_Mem_Debug *)(((unsigned char *)ptr) - MHOOK_DEBUG_HEADER_SPACE); + psize = mold->size; + + _mhook_debug_canary_check(mold); + result = ((unsigned char *)m) + MHOOK_DEBUG_HEADER_SPACE; + m->realptr = (unsigned char *)m; + m->size = size; + _mhook_debug_magic_fill(m); + _mhook_debug_canary_fill(result, size); + + if (psize < size) + { + memcpy(result, ptr, psize); + memset(((unsigned char *)result) + psize, + _mhook_fill_pat_malloc, size - psize); + } + else memcpy(result, ptr, size); + + _mhook_debug_canary_check(mold); + memset(ptr, _mhook_fill_pat_free, psize); + _mhook_debug_free2(ptr); + } + } + else + { + m = _mhook_orig_malloc(ms); + if (m) + { + result = ((unsigned char *)m) + MHOOK_DEBUG_HEADER_SPACE; + m->realptr = (unsigned char *)m; + m->size = size; + _mhook_debug_magic_fill(m); + memset(result, _mhook_fill_pat_malloc, size); + _mhook_debug_canary_fill(result, size); + } + } + return result; +} + +static void * +_mhook_debug_memalign(size_t alignment, size_t size) +{ + Mhook_Mem_Debug *m; + size_t ms = size + MHOOK_DEBUG_HEADER_SPACE + MHOOK_DEBUG_CANARY_SPACE + alignment; + unsigned char *mem; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_memalign) return _mhook_buf; + mem = _mhook_orig_malloc(ms); + if (mem) + { + unsigned char *palign = mem + MHOOK_DEBUG_HEADER_SPACE; + size_t valign = ((size_t)palign) % alignment; + + if (valign > 0) valign = alignment - valign; + palign += valign; + m = (Mhook_Mem_Debug *)palign - (MHOOK_DEBUG_HEADER_SPACE); + result = palign; + m->realptr = (unsigned char *)mem; + m->size = size; + _mhook_debug_magic_fill(m); + memset(result, _mhook_fill_pat_malloc, size); + _mhook_debug_canary_fill(result, size); + } + return result; +} + +static void * +_mhook_debug_calloc(size_t nmemb, size_t size) +{ + Mhook_Mem_Debug *m; + size_t ms = (size * nmemb) + MHOOK_DEBUG_HEADER_SPACE + MHOOK_DEBUG_CANARY_SPACE; + void *result = NULL; + + // FIXME: hack around dlsym doing a malloc/calloc + if (!_mhook_orig_malloc) + { + memset(_mhook_buf, 0, nmemb * size); + return _mhook_buf; + } + m = _mhook_orig_malloc(ms); + if (m) + { + result = ((unsigned char *)m) + MHOOK_DEBUG_HEADER_SPACE; + m->realptr = (unsigned char *)m; + m->size = nmemb * size; + _mhook_debug_magic_fill(m); + memset(result, 0, nmemb * size); + _mhook_debug_canary_fill(result, nmemb * size); + } + return result; +} + + + + + + +/*****************************************************************************/ +static void * +_mhook_malloc(size_t size) +{ + void *result = NULL; + + if (!_mhook_first) pthread_mutex_lock(&lk); + _mhook_do_init(); + switch (_mhook_mode) + { + case MHOOK_NONE: result = _mhook_null_malloc(size); break; + case MHOOK_FILL: result = _mhook_fill_malloc(size); break; + case MHOOK_TRACE: result = _mhook_trace_malloc(size); break; + case MHOOK_TRACK: result = _mhook_track_malloc(size); break; + case MHOOK_DEBUG: result = _mhook_debug_malloc(size); break; + } + if (!_mhook_first) pthread_mutex_unlock(&lk); + _mhook_first = 0; + return result; +} + +static void +_mhook_free(void *ptr) +{ + if (!ptr) return; + if (!_mhook_first) pthread_mutex_lock(&lk); + _mhook_do_init(); + switch (_mhook_mode) + { + case MHOOK_NONE: _mhook_null_free(ptr); break; + case MHOOK_FILL: _mhook_fill_free(ptr); break; + case MHOOK_TRACE: _mhook_trace_free(ptr); break; + case MHOOK_TRACK: _mhook_track_free(ptr); break; + case MHOOK_DEBUG: _mhook_debug_free(ptr); break; + } + if (!_mhook_first) pthread_mutex_unlock(&lk); + _mhook_first = 0; +} + +static void * +_mhook_realloc(void *ptr, size_t size) +{ + void *result = NULL; + + if ((!ptr) && (size == 0)) return NULL; + if (!_mhook_first) pthread_mutex_lock(&lk); + _mhook_do_init(); + switch (_mhook_mode) + { + case MHOOK_NONE: result = _mhook_null_realloc(ptr, size); break; + case MHOOK_FILL: result = _mhook_fill_realloc(ptr, size); break; + case MHOOK_TRACE: result = _mhook_trace_realloc(ptr, size); break; + case MHOOK_TRACK: result = _mhook_track_realloc(ptr, size); break; + case MHOOK_DEBUG: result = _mhook_debug_realloc(ptr, size); break; + } + if (!_mhook_first) pthread_mutex_unlock(&lk); + _mhook_first = 0; + return result; +} + +static void * +_mhook_memalign(size_t alignment, size_t size) +{ + void *result = NULL; + + if (!_mhook_first) pthread_mutex_lock(&lk); + _mhook_do_init(); + switch (_mhook_mode) + { + case MHOOK_NONE: result = _mhook_null_memalign(alignment, size); break; + case MHOOK_FILL: result = _mhook_fill_memalign(alignment, size); break; + case MHOOK_TRACE: result = _mhook_trace_memalign(alignment, size); break; + case MHOOK_TRACK: result = _mhook_track_memalign(alignment, size); break; + case MHOOK_DEBUG: result = _mhook_debug_memalign(alignment, size); break; + } + if (!_mhook_first) pthread_mutex_unlock(&lk); + _mhook_first = 0; + return result; +} + +static void * +_mhook_calloc(size_t nmemb, size_t size) +{ + void *result = NULL; + + if (!_mhook_first) pthread_mutex_lock(&lk); + _mhook_do_init(); + switch (_mhook_mode) + { + case MHOOK_NONE: result = _mhook_null_calloc(nmemb, size); break; + case MHOOK_FILL: result = _mhook_fill_calloc(nmemb, size); break; + case MHOOK_TRACE: result = _mhook_trace_calloc(nmemb, size); break; + case MHOOK_TRACK: result = _mhook_track_calloc(nmemb, size); break; + case MHOOK_DEBUG: result = _mhook_debug_calloc(nmemb, size); break; + } + if (!_mhook_first) pthread_mutex_unlock(&lk); + _mhook_first = 0; + return result; +} + + +/*****************************************************************************/ +static void +_mhook_signal(int sig, siginfo_t *si, void *foo) +{ + switch (_mhook_mode) + { + case MHOOK_NONE: break; + case MHOOK_FILL: break; + case MHOOK_TRACE: break; + case MHOOK_TRACK: _mhook_track_dump(); break; + case MHOOK_DEBUG: break; + } + return; + sig = 0; si = NULL; foo = NULL; +} + +/*============================================================================* +* API * +*============================================================================*/ + +static void +_mhook_init(void) +{ + const char *s = getenv("MTRACK"); + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&lk, &attr); + if (s) + { + if (!strcmp(s, "fill")) + { + _mhook_mode = MHOOK_FILL; + s = getenv("MTRACK_ALLOC_FILL"); + if (s) _mhook_fill_pat_malloc = atoi(s); + s = getenv("MTRACK_FREE_FILL"); + if (s) _mhook_fill_pat_free = atoi(s); + } + else if (!strcmp(s, "debug")) + { + _mhook_mode = MHOOK_DEBUG; + s = getenv("MTRACK_ALLOC_FILL"); + if (s) _mhook_fill_pat_malloc = atoi(s); + s = getenv("MTRACK_FREE_FILL"); + if (s) _mhook_fill_pat_free = atoi(s); + s = getenv("MTRACK_CANARY_SIZE"); + if (s) _mhook_canary_size = atoi(s); + s = getenv("MTRACK_CANARY"); + if (s) _mhook_canary = atoi(s); + } + else if (!strcmp(s, "trace")) + { + _mhook_mode = MHOOK_TRACE; + s = getenv("MTRACK_TRACE_FILE"); + if (s) + { + _mhook_trace_fd = open(s, + O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR); + if (_mhook_trace_fd < 0) + _mhook_mode = MHOOK_NONE; + else + { + if (fcntl(_mhook_trace_fd, F_SETFD, FD_CLOEXEC) < 0) + { + close(_mhook_trace_fd); + _mhook_trace_fd = -1; + _mhook_mode = MHOOK_NONE; + } + } + } + } + else if (!strcmp(s, "track")) + { + _mhook_mode = MHOOK_TRACK; + s = getenv("MTRACK_TRACE_FILE"); + if (s) + { + _mhook_track_fd = open(s, + O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR); + if (_mhook_track_fd < 0) + _mhook_mode = MHOOK_NONE; + else + { + if (fcntl(_mhook_track_fd, F_SETFD, FD_CLOEXEC) < 0) + { + close(_mhook_track_fd); + _mhook_track_fd = -1; + _mhook_mode = MHOOK_NONE; + } + else + { + struct sigaction sa; + + sa.sa_sigaction = _mhook_signal; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sigaction(SIGURG, &sa, NULL); + } + } + } + } + else _mhook_mode = MHOOK_NONE; + } +} + +EAPI void *__libc_malloc (size_t size) { return _mhook_malloc(size); } +EAPI void *malloc (size_t size) { return _mhook_malloc(size); } +EAPI void __libc_free (void *ptr) { _mhook_free(ptr); } +EAPI void free (void *ptr) { _mhook_free(ptr); } +EAPI void *__libc_realloc (void *ptr, size_t size) { return _mhook_realloc(ptr, size); } +EAPI void *realloc (void *ptr, size_t size) { return _mhook_realloc(ptr, size); } +EAPI void *__libc_memalign(size_t algn, size_t size) { return _mhook_memalign(algn, size); } +EAPI void *memalign (size_t algn, size_t size) { return _mhook_memalign(algn, size); } +EAPI void *__libc_calloc (size_t num, size_t size) { return _mhook_calloc(num, size); } +EAPI void *calloc (size_t num, size_t size) { return _mhook_calloc(num, size); } + +static void +_mhook_do_init(void) +{ + static int initted = 0; + + if (initted) return; + unsetenv("LD_PRELOAD"); + initted = 1; + _mhook_init(); + _mhook_orig_malloc = dlsym(RTLD_NEXT, "__libc_malloc"); + _mhook_orig_free = dlsym(RTLD_NEXT, "__libc_free"); + _mhook_orig_realloc = dlsym(RTLD_NEXT, "__libc_realloc"); + _mhook_orig_memalign = dlsym(RTLD_NEXT, "__libc_memalign"); + _mhook_orig_calloc = dlsym(RTLD_NEXT, "__libc_calloc"); + if (!_mhook_orig_malloc) _mhook_orig_malloc = dlsym(RTLD_NEXT, "malloc"); + if (!_mhook_orig_free) _mhook_orig_free = dlsym(RTLD_NEXT, "free"); + if (!_mhook_orig_realloc) _mhook_orig_realloc = dlsym(RTLD_NEXT, "realloc"); + if (!_mhook_orig_memalign) _mhook_orig_memalign = dlsym(RTLD_NEXT, "memalign"); + if (!_mhook_orig_calloc) _mhook_orig_calloc = dlsym(RTLD_NEXT, "calloc"); + _mhook_first = 0; +} +/** + * @} + */ diff --git a/mtshow.c b/mtshow.c new file mode 100644 index 0000000..a39602b --- /dev/null +++ b/mtshow.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include +#include +#include + +enum { + BYTES, KB, MB, SIZE, COUNT +}; + +typedef struct _Group Group; + +struct _Group +{ + char *trace; + unsigned long total; + unsigned long count; +}; + +static Eina_Hash *groups = NULL; +static Eina_List *sorted = NULL; +static int size = BYTES; +static int sort = SIZE; + +static int +listsort(const void *data1, const void *data2) +{ + Group *g = (Group *)data1; + Group *gg = (Group *)data2; + if (sort == SIZE) return (int)(g->total - gg->total); + else if (sort == COUNT) return (int)(g->count - gg->count); + return (int)(g->total - gg->total); +} + +static Eina_Bool +hashwalk(const Eina_Hash *hash, const void *key, void *data, void *fdata) +{ + Group *g = data; + sorted = eina_list_append(sorted, g); + return EINA_TRUE; +} + +int +main(int argc, char **argv) +{ + FILE *f; + char *file; + char buf[4096]; + int i; + int group = 0; + Group *g; + Eina_List *l; + unsigned long total_count = 0, total_size = 0; + + eina_init(); + if (argc < 1) + { + printf("usage: mtdump MTOUTFILE [OPT]\n" + "OPT can be 0 or more of:\n" + " -f N - group by trace of up to N functions in backtrace (0 == unlimited)\n" + " -m - show size in Mb\n" + " -k - show size in Kb\n" + " -b - show size in bytes\n" + " -s - sout by size\n" + " -c - sout by count\n" + ); + exit(1); + } + for (i = 0; i < argc; i++) + { + if (!strcmp(argv[i], "-f")) + { + i++; + group = atoi(argv[i]); + } + else if (!strcmp(argv[i], "-m")) + { + size = MB; + } + else if (!strcmp(argv[i], "-k")) + { + size = KB; + } + else if (!strcmp(argv[i], "-b")) + { + size = BYTES; + } + else if (!strcmp(argv[i], "-s")) + { + sort = SIZE; + } + else if (!strcmp(argv[i], "-c")) + { + sort = COUNT; + } + else + file = argv[i]; + } + f = fopen(file, "r"); + if (!f) + { + perror("fopen"); + exit(1); + } + + groups = eina_hash_string_superfast_new(NULL); + while (fgets(buf, sizeof(buf) - 1, f)) + { + int len; + unsigned long bytes = 0; + char *p; + + len = strlen(buf); + if (len < 2) continue; + if ((len > 1) && (buf[len - 1] == '\n')) + { + buf[len - 1] = 0; + len--; + } + sscanf(buf, "%lu", &bytes); + p = strchr(buf, '|'); + if (p) + { + char trace[4096] = "", *t; + + p += 2; + if (group == 0) + { + strcpy(trace, p); + } + else + { + i = group; + t = trace; + while (*p) + { + if (isspace(*p)) + { + i--; + if (i == 0) break; + } + *t = *p; + t++; p++; + } + *t = 0; + } + g = eina_hash_find(groups, trace); + if (!g) + { + g = calloc(1, sizeof(Group)); + g->trace = strdup(trace); + eina_hash_add(groups, trace, g); + } + g->total += bytes; + g->count++; + } + } + fclose(f); + eina_hash_foreach(groups, hashwalk, NULL); + printf("|-- MEM --|- COUNT --|----------------------------------------------\n"); + sorted = eina_list_sort(sorted, eina_list_count(sorted), listsort); + EINA_LIST_FOREACH(sorted, l, g) + { + switch (size) + { + case MB: + printf("%10lu [%8lu] %s\n", g->total / (1024 * 1024), g->count, g->trace); + break; + case KB: + printf("%10lu [%8lu] %s\n", g->total / 1024, g->count, g->trace); + break; + default: + printf("%10lu [%8lu] %s\n", g->total, g->count, g->trace); + break; + } + total_count += g->count; + total_size += g->total; + } + printf("|-- MEM --|- COUNT --|----------------------------------------------\n"); + printf("Total allocs: %lu\n", total_count); + switch (size) + { + case MB: + printf("Total memory: %lu Mb\n", total_size / (1024 * 1024)); + break; + case KB: + printf("Total memory: %lu Kb\n", total_size / 1024); + break; + default: + printf("Total memory: %lu bytes\n", total_size); + break; + } + exit(0); + return 0; +} diff --git a/mtt b/mtt new file mode 100755 index 0000000..363d7cf --- /dev/null +++ b/mtt @@ -0,0 +1,5 @@ +#!/bin/sh +export MTRACK=trace +export MTRACK_TRACE_FILE=./mtrack.log +export LD_PRELOAD=/usr/local/lib/mtrack.so +exec $@