AmendHub

Download (as of this amendment):

jcs

/

amend

/

amendments

/

135

util: Import new malloc debugging, ditch malloc notes


jcs made amendment 135 over 2 years ago
--- util.c Thu Feb 1 17:24:25 2024 +++ util.c Fri Feb 16 13:38:19 2024 @@ -83,28 +83,6 @@ enum { APPICON_ALERT }; -/* - * Define to audit each malloc and free and verify that a pointer isn't - * double-freed. The list of outstanding allocations can be checked by - * looking through malloc_map. - */ -//#define MALLOC_DEBUG - -#ifdef MALLOC_DEBUG -/* - * List of allocations, updated at xmalloc() and xfree(). If an address - * passed to xfree() isn't in the list, it indicates a double-free. - */ -#define MALLOC_MAP_CHUNK_SIZE 1024 -struct malloc_map_e { - unsigned long addr; - unsigned long size; - char note[MALLOC_NOTE_SIZE]; -} *malloc_map = NULL; -unsigned long malloc_map_size = 0; -static bool malloc_map_compact = false; -#endif - void vwarn(short alert_func, const char *format, va_list ap); /* @@ -119,15 +97,6 @@ util_init(void) HLock(alert_ditl_h); memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl)); HUnlock(alert_ditl_h); - -#ifdef MALLOC_DEBUG - malloc_map_size = MALLOC_MAP_CHUNK_SIZE; - malloc_map = (struct malloc_map_e *)NewPtr(malloc_map_size * - sizeof(struct malloc_map_e)); - if (malloc_map == NULL) - panic("NewPtr(%lu) failed", MALLOC_MAP_CHUNK_SIZE); - memset(malloc_map, 0, malloc_map_size); -#endif } /* @@ -136,64 +105,141 @@ util_init(void) #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) +#ifdef MALLOC_DEBUG +/* + * malloc tracking, storing a stack trace of each xmalloc call and its + * size, which can be dumped with xalloc_print(). xfree also gains the + * ability to detect junk frees. + * + * Requires project options 'Always generate stack frames', + * 'Macsbug Names', and 'Long format' to be enabled. + */ +#if !__option(force_frame) || !__option(long_macsbug_names) +#error +#endif + +#define XALLOC_SYMBOL_SIZE 32 +#define XALLOC_NSYMBOLS 64 +#define XALLOC_MAX_STACK_DEPTH 10 +static char *symbols = NULL; +struct xalloc { + char *stack[XALLOC_MAX_STACK_DEPTH]; + unsigned long addr; + unsigned long size; +}; +#define XALLOC_NXALLOCS 512 +static struct xalloc *xallocs = NULL; +#endif + void * -xmalloc(size_t size, char *note) +xmalloc(size_t size) { void *ptr; #ifdef MALLOC_DEBUG - struct malloc_map_e *new_malloc_map; - unsigned short n, j; + static char cursym[XALLOC_SYMBOL_SIZE], *tsym, *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 (ptr == NULL) - panic("xmalloc(%lu) failed", size); + return NULL; #ifdef MALLOC_DEBUG - if (malloc_map_compact) { - for (n = 0; n < malloc_map_size; n++) { - if (malloc_map[n].addr != 0) - continue; - - for (j = n + 1; j < malloc_map_size; j++) { - if (malloc_map[j].addr == 0) - continue; - - malloc_map[n] = malloc_map[j]; - memset(&malloc_map[j], 0, sizeof(struct malloc_map_e)); - break; - } + asm { + move.l a6,_a6 + }; + + if (symbols == NULL) { + symbols = NewPtr(n = XALLOC_SYMBOL_SIZE * XALLOC_NSYMBOLS); + if (symbols == NULL) + panic("NewPtr failed for xalloc symbols"); + memset(symbols, 0, n); + } + if (xallocs == NULL) { + xallocs = (struct xalloc *)NewPtr(n = (XALLOC_NXALLOCS * + sizeof(struct xalloc))); + if (xallocs == NULL) + panic("NewPtr failed for xallocs"); + memset(xallocs, 0, n); + } + + xa = NULL; + for (n = 0; n < XALLOC_NXALLOCS; n++) { + if (xallocs[n].addr == 0) { + xa = &xallocs[n]; + break; } - - malloc_map_compact = false; } + if (xa == NULL) + panic("out of xalloc space"); - for (n = 0; n <= malloc_map_size; n++) { - if (n == malloc_map_size) { - malloc_map_size += MALLOC_MAP_CHUNK_SIZE; - warn("xmalloc(%lu): out of malloc map entries, maybe a " - "memory leak, resizing to %ld", size, malloc_map_size); - new_malloc_map = (struct malloc_map_e *)NewPtr( - malloc_map_size * sizeof(struct malloc_map_e)); - if (new_malloc_map == NULL) - panic("out of memory resizing malloc map"); - memcpy(new_malloc_map, malloc_map, - (malloc_map_size - MALLOC_MAP_CHUNK_SIZE) * - sizeof(struct malloc_map_e)); - DisposePtr(malloc_map); - malloc_map = new_malloc_map; + /* + * 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 < XALLOC_MAX_STACK_DEPTH; nframe++) { + ret = *(unsigned long *)(_a6 + 4); + code = (unsigned char *)ret; + + cursym[0] = '\0'; + for (; ; code += 2) { + if (code[0] == 0x4e && code[1] == 0x75) { /* RTS */ + code += 3; + strlcpy(cursym, (char *)code, sizeof(cursym)); + break; + } } - if (malloc_map[n].addr == 0) { - malloc_map[n].addr = (unsigned long)ptr; - malloc_map[n].size = size; - strlcpy(malloc_map[n].note, note, sizeof(malloc_map[n].note)); + + if (!cursym[0]) break; + + sym = NULL; + for (n = 0; n < XALLOC_NSYMBOLS; n++) { + tsym = symbols + (XALLOC_SYMBOL_SIZE * n); + + if (tsym[0] == '\0') + break; + if (strcmp(tsym, cursym) == 0) { + sym = tsym; + break; + } } - n = n; + if (sym == NULL) { + for (n = 0; n < XALLOC_NSYMBOLS; n++) { + tsym = symbols + (XALLOC_SYMBOL_SIZE * n); + if (tsym[0] != '\0') + continue; + + strlcpy(tsym, cursym, XALLOC_SYMBOL_SIZE); + sym = tsym; + 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; @@ -203,81 +249,63 @@ void xfree(void *ptrptr) { unsigned long *addr = (unsigned long *)ptrptr; - void *ptr = (void *)*addr; + void *ptr; #ifdef MALLOC_DEBUG - unsigned long n; + size_t n; +#endif - for (n = 0; n <= malloc_map_size; n++) { - if (n == malloc_map_size) - panic("xfree(0x%lx): can't find in alloc map, likely " - "double free()", *addr); - if (malloc_map[n].addr == *addr) { - malloc_map[n].addr = 0; - malloc_map[n].size = 0; - malloc_map[n].note[0] = '\0'; + if (ptrptr == NULL) + panic("xfree(NULL)"); + + ptr = (void *)*addr; + if (ptr == NULL) + panic("xfree(&NULL) likely a double-free"); + +#ifdef MALLOC_DEBUG + for (n = 0; n < XALLOC_NXALLOCS; n++) { + if (xallocs[n].addr == (unsigned long)ptr) { + memset(&xallocs[n], 0, sizeof(struct xalloc)); break; } } + if (n == XALLOC_NXALLOCS) + panic("xfree() without xalloc, possibly a junk free", + (unsigned long)ptr); #endif DisposePtr(ptr); - *addr = 0L; } void * -xmalloczero(size_t size, char *note) +xmalloczero(size_t size) { void *ptr; - ptr = xmalloc(size, note); - memset(ptr, 0, size); + ptr = xmalloc(size); + if (ptr != NULL) + memset(ptr, 0, size); return ptr; } void * -xcalloc(size_t nmemb, size_t size, char *note) +xcalloc(size_t nmemb, size_t size) { - void *ptr; - if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) panic("xcalloc(%lu, %lu) overflow", nmemb, size); - ptr = xmalloczero(nmemb * size, note); - if (ptr == NULL) - panic("xcalloc(%lu, %lu) failed", nmemb, size); - - return ptr; + + return xmalloczero(nmemb * size); } void * xrealloc(void *src, size_t size) { void *ptr, *tsrc; -#ifdef MALLOC_DEBUG - unsigned long n; -#endif - char note[MALLOC_NOTE_SIZE] = "realloc from null"; -#ifdef MALLOC_DEBUG - if (src != NULL) { - for (n = 0; n <= malloc_map_size; n++) { - if (n == malloc_map_size) { - panic("xrealloc(%lu): can't find in alloc map, likely " - "double free()", (unsigned long)src); - return NULL; - } - if (malloc_map[n].addr == (unsigned long)src) { - strlcpy(note, malloc_map[n].note, sizeof(note)); - break; - } - } - } -#endif - - ptr = xmalloc(size, note); - if (src != NULL) { + ptr = xmalloc(size); + if (ptr != NULL && src != NULL) { memcpy(ptr, src, size); tsrc = src; xfree(&tsrc); @@ -292,25 +320,26 @@ xreallocarray(void *optr, size_t nmemb, size_t size) if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) panic("xreallocarray(%lu, %lu) failed", nmemb, size); + return xrealloc(optr, size * nmemb); } char * -xstrdup(const char *str, char *note) +xstrdup(const char *str) { char *cp; size_t len; len = strlen(str); + cp = xmalloc(len + 1); + if (cp != NULL) + strlcpy(cp, str, len + 1); - cp = xmalloc(len + 1, note); - strlcpy(cp, str, len + 1); - return cp; } char * -xstrndup(const char *str, size_t maxlen, char *note) +xstrndup(const char *str, size_t maxlen) { char *copy; const char *cp; @@ -321,14 +350,57 @@ xstrndup(const char *str, size_t maxlen, char *note) ; len = (size_t)(cp - str); - copy = xmalloc(len + 1, note); - (void)memcpy(copy, str, len); - copy[len] = '\0'; + copy = xmalloc(len + 1); + if (copy != NULL) { + (void)memcpy(copy, str, len); + copy[len] = '\0'; + } 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 < XALLOC_NXALLOCS; n++) { + if (xallocs[n].addr == 0) + continue; + + total += xallocs[n].size; + nallocs++; + + chain[0] = '\0'; + len = 0; + for (j = XALLOC_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 + /* * String functions */ @@ -341,7 +413,7 @@ getline(char *str, size_t len, char **ret) for (i = 0; i < len; i++) { if (str[i] == '\r' || i == len - 1) { if (*ret == NULL) - *ret = xmalloc(i + 1, "getline"); + *ret = xmalloc(i + 1); memcpy(*ret, str, i + 1); (*ret)[i] = '\0'; return i + 1; @@ -803,7 +875,7 @@ xGetStringAsChar(short id) h = xGetString(id); HLock(h); l = (*h)[0]; - out = xmalloc(l + 1, "xGetStringAsChar"); + out = xmalloc(l + 1); memcpy((void *)out, (void *)(*h + 1), l); out[l] = '\0'; ReleaseResource(h); @@ -858,7 +930,7 @@ getpath(short vRefNum, Str255 fileName, Str255 *ret, b return 0; } - name = xmalloc(FILENAME_MAX, "getpath"); + name = xmalloc(FILENAME_MAX); wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum; wdir.ioWDIndex = 0; @@ -892,8 +964,8 @@ getpath(short vRefNum, Str255 fileName, Str255 *ret, b wcinfo.ioDrParID = wdir.ioWDDirID; wcinfo.ioDrDirID = wdir.ioWDDirID; - tmp = xmalloc(FILENAME_MAX, "getpath"); - tmpret = xmalloc(FILENAME_MAX, "getpath"); + tmp = xmalloc(FILENAME_MAX); + tmpret = xmalloc(FILENAME_MAX); /* go backwards, prepending each folder's parent */ while (wcinfo.ioDrParID != 1) { @@ -946,7 +1018,7 @@ stat(char *path, struct stat *sb) char *ppath; short ret; - ppath = xstrdup(path, "stat"); + ppath = xstrdup(path); CtoPstr(ppath); ret = FStat((unsigned char *)ppath, sb); xfree(&ppath); @@ -1089,7 +1161,7 @@ copy_file_contents(short source_ref, short dest_ref) if (error) return error; - buf = xmalloc(1024, "copy_file_contents"); + buf = xmalloc(1024); while (source_size > 0) { count = 1024; --- util.h Thu Feb 1 17:24:12 2024 +++ util.h Fri Feb 16 10:33:08 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))) @@ -89,15 +90,18 @@ struct stat { void util_init(void); -void * xmalloc(size_t, char *note); +void * xmalloc(size_t); void xfree(void *ptrptr); void xfree_verify(void); -void * xmalloczero(size_t, char *note); -void * xcalloc(size_t, size_t, char *note); +void * xmalloczero(size_t); +void * xcalloc(size_t, size_t); void * xrealloc(void *src, size_t size); void * xreallocarray(void *, size_t, size_t); -char * xstrdup(const char *, char *note); -char * xstrndup(const char *str, size_t maxlen, char *note); +char * xstrdup(const char *); +char * xstrndup(const char *str, size_t maxlen); +#ifdef MALLOC_DEBUG +void xalloc_print(size_t (*printer)(const char *, ...)); +#endif short getline(char *str, size_t len, char **ret); const char * ordinal(unsigned short n);