/* * Example memory allocator with machine parseable logging. * * Also sizes for reallocs and frees are logged so that the memory * behavior can be essentially replayed to accurately determine e.g. * optimal pool sizes for a pooled allocator. * * Allocation structure: * * [ alloc_hdr | user area ] * * ^ ^ * | `--- pointer returned to Duktape * `--- underlying malloc ptr */ #include "duktape.h" #include #include #include #include #include "duk_alloc_logging.h" #define ALLOC_LOG_FILE "/tmp/duk-alloc-log.txt" typedef struct { /* The double value in the union is there to ensure alignment is * good for IEEE doubles too. In many 32-bit environments 4 bytes * would be sufficiently aligned and the double value is unnecessary. */ union { size_t sz; double d; } u; } alloc_hdr; static FILE *log_file = NULL; static void write_log(const char *fmt, ...) { va_list ap; if (!log_file) { log_file = fopen(ALLOC_LOG_FILE, "wb"); if (!log_file) { return; } } va_start(ap, fmt); vfprintf(log_file, fmt, ap); va_end(ap); } void *duk_alloc_logging(void *udata, duk_size_t size) { alloc_hdr *hdr; void *ret; (void) udata; /* Suppress warning. */ if (size == 0) { write_log("A NULL %ld\n", (long) size); return NULL; } hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr)); if (!hdr) { write_log("A FAIL %ld\n", (long) size); return NULL; } hdr->u.sz = size; ret = (void *) (hdr + 1); write_log("A %p %ld\n", ret, (long) size); return ret; } void *duk_realloc_logging(void *udata, void *ptr, duk_size_t size) { alloc_hdr *hdr; size_t old_size; void *t; void *ret; (void) udata; /* Suppress warning. */ /* Handle the ptr-NULL vs. size-zero cases explicitly to minimize * platform assumptions. You can get away with much less in specific * well-behaving environments. */ if (ptr) { hdr = (alloc_hdr *) (void *) ((unsigned char *) ptr - sizeof(alloc_hdr)); old_size = hdr->u.sz; if (size == 0) { write_log("R %p %ld NULL 0\n", ptr, (long) old_size); free((void *) hdr); return NULL; } else { t = realloc((void *) hdr, size + sizeof(alloc_hdr)); if (!t) { write_log("R %p %ld FAIL %ld\n", ptr, (long) old_size, (long) size); return NULL; } hdr = (alloc_hdr *) t; hdr->u.sz = size; ret = (void *) (hdr + 1); write_log("R %p %ld %p %ld\n", ptr, (long) old_size, ret, (long) size); return ret; } } else { if (size == 0) { write_log("R NULL 0 NULL 0\n"); return NULL; } else { hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr)); if (!hdr) { write_log("R NULL 0 FAIL %ld\n", (long) size); return NULL; } hdr->u.sz = size; ret = (void *) (hdr + 1); write_log("R NULL 0 %p %ld\n", ret, (long) size); return ret; } } } void duk_free_logging(void *udata, void *ptr) { alloc_hdr *hdr; (void) udata; /* Suppress warning. */ if (!ptr) { write_log("F NULL 0\n"); return; } hdr = (alloc_hdr *) (void *) ((unsigned char *) ptr - sizeof(alloc_hdr)); write_log("F %p %ld\n", ptr, (long) hdr->u.sz); free((void *) hdr); }