AmendHub

Download:

jcs

/

subtext

/

amendments

/

587

util: Add dynamic malloc tracking

The previous malloc note stuff was removed since it required every
xmalloc call to include a string, which was laborious to use and
added a lot of code and memory space having to store all those
strings.
 
Now when MALLOC_DEBUG is defined, the stack is walked to find the
symbols of each function along the way to xmalloc and pointers to
elements of the symbol table are stored, minimizing the amount of
strings that need to be stored in memory, and providing a way to see
the full stack trace of each allocation with xalloc_print. We can
also check in xfree that the allocation was still in our list to
avoid a double free or a junk free.

jcs made amendment 587 9 months ago
--- util.c Thu Feb 1 17:37:17 2024 +++ util.c Thu Feb 15 15:18:31 2024 @@ -107,18 +107,112 @@ util_init(void) #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) +#ifdef MALLOC_DEBUG + +#define SYMBOL_SIZE 32 +static char symbols[64][SYMBOL_SIZE]; + +#define MAX_STACK_DEPTH 10 +struct xalloc { + char *stack[MAX_STACK_DEPTH]; + unsigned long addr; + unsigned long size; +}; +static struct xalloc xallocs[256] = { 0 }; +#endif + void * xmalloc(size_t size) { void *ptr; - +#ifdef MALLOC_DEBUG + char tsym[SYMBOL_SIZE], *sym; + struct xalloc *xa; + unsigned long _a6, ret; + unsigned char *code; + size_t n, cn, j; + short nframe; +#endif + if (size == 0) panic("xmalloc: zero size"); ptr = NewPtr(size); -#if 0 if (ptr == NULL) - warn("Insufficient memory available: xmalloc(%lu) failed", size); + return NULL; + +#ifdef MALLOC_DEBUG + asm { + move.l a6,_a6 + }; + + xa = NULL; + for (n = 0; n < nitems(xallocs); n++) { + if (xallocs[n].addr == 0) { + xa = &xallocs[n]; + break; + } + } + if (xa == NULL) + panic("out of xalloc space"); + + /* + * A6 register points to previous stack A6. Above A6 is our return + * address. Walk the code after the return address looking for an RTS + * instruction, then walk 3 bytes after that and we should have that + * function's symbol. Then jump to the previous A6 that our A6 points + * to and do it again to find the previous function symbol, repeating + * until we hit "main" or "uthread_begin". + */ + for (nframe = 0; nframe < MAX_STACK_DEPTH; nframe++) { + ret = *(unsigned long *)(_a6 + 4); + code = (unsigned char *)ret; + + tsym[0] = '\0'; + for (; ; code += 2) { + if (code[0] == 0x4e && code[1] == 0x75) { /* RTS */ + code += 3; + strlcpy(tsym, (char *)code, sizeof(tsym)); + break; + } + } + + if (!tsym[0]) + break; + + sym = NULL; + for (n = 0; n < nitems(symbols); n++) { + if (symbols[n][0] == '\0') + break; + if (strcmp(symbols[n], tsym) == 0) { + sym = (char *)&symbols[n]; + break; + } + } + if (sym == NULL) { + for (n = 0; n < nitems(symbols); n++) { + if (symbols[n][0] != '\0') + continue; + + strlcpy(symbols[n], tsym, sizeof(symbols[n])); + sym = (char *)&symbols[n]; + break; + } + } + if (sym == NULL) + break; + + xa->stack[nframe] = sym; + + if (strcmp(sym, "main") == 0 || strcmp(sym, "uthread_begin") == 0) + break; + + /* walk back up the chain */ + _a6 = *(unsigned long *)_a6; + } + + xa->addr = (unsigned long)ptr; + xa->size = size; #endif return ptr; @@ -129,6 +223,9 @@ xfree(void *ptrptr) { unsigned long *addr = (unsigned long *)ptrptr; void *ptr; +#ifdef MALLOC_DEBUG + size_t n; +#endif if (ptrptr == NULL) panic("xfree(NULL)"); @@ -136,7 +233,19 @@ xfree(void *ptrptr) ptr = (void *)*addr; if (ptr == NULL) panic("xfree(&NULL) likely a double-free"); - + +#ifdef MALLOC_DEBUG + for (n = 0; n < nitems(xallocs); n++) { + if (xallocs[n].addr == (unsigned long)ptr) { + memset(&xallocs[n], 0, sizeof(xallocs[n])); + break; + } + } + if (n == nitems(xallocs)) + panic("xfree() without xalloc, possibly a junk free", + (unsigned long)ptr); +#endif + DisposePtr(ptr); *addr = 0L; } @@ -222,6 +331,48 @@ xstrndup(const char *str, size_t maxlen) return copy; } + +#ifdef MALLOC_DEBUG +void +xalloc_print(size_t (*printer)(const char *, ...)) +{ + ssize_t n, j, len; + size_t total = 0, nallocs = 0; + char chain[128]; + + for (n = 0; n < nitems(xallocs); n++) { + if (xallocs[n].addr == 0) + continue; + + total += xallocs[n].size; + nallocs++; + + chain[0] = '\0'; + len = 0; + for (j = MAX_STACK_DEPTH - 1; j >= 0; j--) { + if (xallocs[n].stack[j] == 0) + continue; + + if (chain[0] != '\0') { + if (len >= sizeof(chain) - 2) + break; + chain[len++] = ' '; + chain[len++] = ':'; + chain[len++] = ' '; + chain[len++] = '\0'; + } + + len = strlcat(chain, xallocs[n].stack[j], sizeof(chain)); + if (len >= sizeof(chain)) + break; + } + + printer("%ld @ %s", xallocs[n].size, chain); + } + + printer("%ld bytes in %ld allocations in use", total, nallocs); +} +#endif /* if var having used_size can't fit add, grow it by grow_amount */ bool --- util.h Thu Feb 1 17:37:30 2024 +++ util.h Thu Feb 15 15:29:35 2024 @@ -21,6 +21,9 @@ #include <limits.h> #include <time.h> +/* uncomment to enable malloc tracking */ +// #define MALLOC_DEBUG + #ifndef SIZE_MAX #define SIZE_MAX ULONG_MAX #endif @@ -28,8 +31,6 @@ #define nitems(what) (sizeof((what)) / sizeof((what)[0])) #define member_size(type, member) sizeof(((type *)0)->member) -#define MALLOC_NOTE_SIZE 32 - #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define BOUND(a, min, max) ((a) > (max) ? (max) : ((a) < (min) ? (min) : (a))) @@ -93,6 +94,9 @@ void * xrealloc(void *src, size_t size); void * xreallocarray(void *, size_t, size_t); char * xstrdup(const char *); char * xstrndup(const char *str, size_t maxlen); +#ifdef MALLOC_DEBUG +void xalloc_print(size_t (*printer)(const char *, ...)); +#endif bool grow_to_fit(void *var, size_t *var_size, size_t used_size, size_t add, size_t grow_amount);