| /* |
| * 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 <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.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); |
| } |