/* -------- malldbg.c --------- */ /* Copyright (c) 2003 by Charles B. Falconer Licensed under the terms of the GNU LIBRARY GENERAL PUBLIC LICENSE and/or the terms of COPYING.DJ, all available at . Bug reports to Revised 2015 for use in DJGPP libs by Andris Pavenis */ #include #include #include #include /* raise, SIGABRT */ #include /* and sysquery.h */ /* This is to be used in conjunction with a version of nmalloc.c compiled with: gcc -DNDEBUG -o malloc.o -c nmalloc.c */ static int dbglevel; static FILE *dumpfile; static int initialized; /* array of hook function pointers, for cleaner interface */ /* This is purely a record of values set by sethook() */ static M_HOOKFN hookptr[HKCOUNT]; /* Number of free lists in system */ #define NFLISTS ((int)(CHAR_BIT * sizeof(size_t))) /* Loaded by initsysinfo() to access nmalloc guts */ static struct _sysquery sysinfo; /* freehdrsp is pointer to array[NFLISTS] of void* */ /* These are the headers of the actual free lists */ /* also loaded by initsysinfo() call */ static void *(*freehdrsp)[NFLISTS]; #define NONE sysinfo.nilp #define lastsbrk freehdrs[0] #define memblockp void* typedef unsigned int ulong; typedef unsigned char byte; /* conversion and access macros */ #define DATAOFFSET sysinfo.data #define MEMBLKp(p) (memblockp)((byte*)(p) - DATAOFFSET) #define PTR(m) (void*)((byte*)(m) + DATAOFFSET) /* This accesses the list of discrete memory chains */ /* which are created when sbrk returns unexpected value */ #define SBRKBGN ((void **)(sysinfo.anchors)) /* field access macros (AFTER sysinfo loaded) */ /* Examples - replace "m->prv" by "fld(m, prv)" */ /* replace "m->sz" by "szof(m)" */ /* where field is prvf, nxtf, prv, nxt */ #define fld(m, field) *((void**)((char*)m + sysinfo.field)) #define szof(m) *(ulong*)((char*)m + sysinfo.sz) #define freehdrs (*freehdrsp) /* ----------------- */ /* Set up the access to the nmalloc module */ static void initsysinfo(void) { sysinfo = _sysmalloc(); freehdrsp = (void*(*)[NFLISTS])((byte*)(sysinfo.nilp)-sizeof(void*)); if (!dumpfile) dumpfile = stderr; initialized = 1; } /* initsysinfo */ /* 1------------------1 */ /* m is the allocated ptr treated by MEMBLKp */ /* Fouls if sysinfo has not been initialized */ /* Display info about a particular memory block */ static void xshowblock(FILE *f, void *m, const char *id) { if (NULL == f) return; if (m) { fprintf(f, " %s %p", id, m); fprintf(f, " sz=%u nxt=%p prv=%p nxtf=", szof(m), fld(m, nxt), fld(m, prv)); if (fld(m, nxtf)) { if (NONE == fld(m, nxtf)) fprintf(f, "NONE prvf="); else fprintf(f, "%p prvf=", fld(m, nxtf)); if (NONE == fld(m, prvf)) fprintf(f, "NONE"); else fprintf(f, "%p", fld(m, prvf)); } else fprintf(f, "0"); } else fprintf(f, " %s NULL", id); fflush(f); /* to coexist with internal debuggery */ } /* xshowblock */ /* 1------------------1 */ /* dump the entire free chain group */ /* Fouls if sysinfo has not been initialized */ /* See main for sysinfo initialization sequence */ static void xdumpfree(FILE *f) { int i; memblockp m; ulong totfree; if (NULL == f) return; totfree = 0; for (i = 0; i < NFLISTS; i++) { if ((m = freehdrs[i])) { fprintf(f, "\n%2d: ", i); do { fprintf(f, "%p(%u)->", m, szof(m)); totfree += szof(m); m = fld(m, nxtf); } while (m && (NONE != m)); fprintf(f, "0"); m = freehdrs[i]; while (m && (NONE !=m )) { xshowblock(f, m, "\n "); m = fld(m, nxtf); } } } fprintf(f, "\nTotal Free = %u\n", totfree); fflush(f); /* to coexist with internal debuggery */ } /* xdumpfree */ /* ----------------- */ /* show the content of a block, flag it as BAD */ static void showbad(FILE *f, void * m) { void *n; if ((dbglevel >= 3) && (NULL == f)) f = dumpfile; if (f) { n = fld(m, nxt); xshowblock(f, m, "\n BAD?:"); xshowblock(f, n, "\n BAD?:"); putc('\n', f); fflush(f); } } /* showbad */ /* ----------------- */ /* scans the complete malloc structures to collect info. If f is non-NULL outputs a detailed listing returns NULL unless a bad block is found. Any bad blocks are displayed on dumpfile */ static void * mallocscan(FILE *f, struct mallinfo *mi) { unsigned long totalmem, totalfree, blks, freeblks; void *m, *n, *badblk; int i; badblk = NULL; if (!initialized) initsysinfo(); mi->smblks = mi->hblks = mi->hblkhd = mi->usmblks = 0; mi->fsmblks = mi->keepcost = 0; /* this initialization accounts for the fact that the lastsbrk field will be counted as used */ blks = 0; totalmem = 0; mi->hblkhd = totalfree = szof(freehdrs[0]); freeblks = 1; for (i = 0; (m = SBRKBGN[i]); i++) { if (f) fprintf(f, "\n\nGroup %d:", i); do { n = fld(m, nxt); if (f) xshowblock(f, m, "\n "); totalmem += szof(m); blks++; if (dbglevel && n) if (m != fld(n, prv)) { badblk = m; showbad(dumpfile, m); if (dbglevel >= 3) { fflush(dumpfile); raise(SIGABRT); } } if (fld(m, nxtf)) { /* a free block */ freeblks++; totalfree += szof(m); } } while ((m = n)); } if (f) fprintf(f, "\n"); /* return the collected info in struct mi */ mi->arena = totalmem; mi->ordblks = blks; mi->hblks = freeblks; mi->uordblks = totalmem - totalfree - DATAOFFSET * (blks - freeblks); mi->fordblks = totalfree; mi->keepcost = DATAOFFSET * blks; return badblk; } /* mallocscan */ /* ----------------- */ /* Return summary details about the arena */ struct mallinfo mallinfo(void) { struct mallinfo mi; int valid; if (!initialized) initsysinfo(); valid = (NULL == mallocscan(NULL, &mi)); (void)valid; return mi; } /* mallinfo */ /* ----------------- */ /* Verify the integrity of the arena */ int malloc_verify(void) { struct mallinfo mi; void *badblk; if (!initialized) initsysinfo(); badblk = mallocscan(NULL, &mi); if (badblk) showbad(dumpfile, badblk); return (NULL == badblk); } /* malloc_verify */ /* ----------------- */ /* dump a complete map of the arena */ void mallocmap(void) { struct mallinfo mi; void *badblk; if (!initialized) initsysinfo(); fprintf(dumpfile, "\nmallocmap at level %d\n", dbglevel); xdumpfree(dumpfile); badblk = mallocscan(dumpfile, &mi); (void)badblk; } /* mallocmap */ /* ----------------- */ /* Set the file on which to display results */ FILE *malldbgdumpfile(FILE *fp) { FILE *tmp; if (!initialized) initsysinfo(); tmp = dumpfile; if (fp) dumpfile = fp; else dumpfile = stderr; return tmp; } /* malldbgdumpfile */ /* ----------------- */ /* The following three functions are called by hooks */ /* Do malloc_verify function via hook ptr */ /* No output unless a bad block found */ /* This is suitable for setting hooks. */ static void checkarena(size_t sz, void *bk) { struct mallinfo mi; void *badblk; // if (bk) sz = sz; /* anti warn */ if (dbglevel > 1) { /* else ignore, safety */ if ((badblk = mallocscan(NULL, &mi))) showbad(dumpfile, badblk); } } /* checkarena */ /* ----------------- */ static void freenullalert(size_t sz, void *bk) { // if (bk) sz = sz; /* anti warn */ fputs("\n***Freeing NULL\n", dumpfile); } /* freenullalert */ /* ----------------- */ static void mallocfailalert(size_t sz, void *bk) { if (bk) fprintf(dumpfile, "\n***realloc failed expanding %p to %lu bytes\n", bk, (unsigned long)sz); else fprintf(dumpfile, "\n***malloc failed allocating %lu bytes\n", (unsigned long)sz); } /* mallocfailalert */ /* ----------------- */ /* Check that no hooks are presently in use */ /* uses the locally stored copy of hooks so */ /* mistakes are possible. Maybe sysinfo */ /* should contain a pointer to the real tbl */ /* Our own hooks are allowable */ static int somehookinuse(void) { int hk; for (hk = malloc_HK; hk < HKCOUNT; hk++) { /* structured for ease of modification */ if (NULL == hookptr[hk]) continue; if (checkarena == hookptr[hk]) continue; if (freenullalert == hookptr[hk]) continue; if (mallocfailalert == hookptr[hk]) continue; return 1; } return 0; } /* somehookinuse */ /* ----------------- */ /* sethook, bypassing validity checks */ static M_HOOKFN sethook(enum m_hook_kind which, M_HOOKFN newhook) { M_HOOKFN tmp; hookptr[which] = newhook; /* keep local record */ tmp = (*sysinfo.hookset)(which, newhook); return tmp; } /* sethook */ /* ----------------- */ M_HOOKFN mallsethook(enum m_hook_kind which, M_HOOKFN newhook) { if (!initialized) initsysinfo(); if (which >= HKCOUNT) return NULL; /* validity check */ if (dbglevel != 1) return NULL; /* in use, refuse */ return sethook(which, newhook); } /* mallsethook */ /* ----------------- */ static void releaseallhooks(void) { int hk; for (hk = malloc_HK; hk < HKCOUNT; hk++) sethook((enum m_hook_kind)hk, NULL); } /* freeallhooks */ /* ----------------- */ static inline void setfreenullhook(void) { sethook(free_null_HK, freenullalert); } /* setfreenullhook */ /* ----------------- */ static inline void setmallocfailhook(void) { sethook(malloc_fail_HK, mallocfailalert); } /* setmallocfailhook */ /* ----------------- */ static inline void setverifyhooks(void) { sethook(malloc_HK, checkarena); sethook(free_HK, checkarena); sethook(realloc_HK, checkarena); } /* setverifyhooks */ /* ----------------- */ /* level action 0 Only passive checks 1 Passive checks, hook setting enabled 2 Checks on each alloc/realloc, no hooks allowed 3 Same, but aborts if fault found, signals malloc_fail 4 Same, but signals on free(NULL) A level value outside 0..4 is rejected. Returns current debug_level (before any change). */ int malloc_debug(int level) { int oldlevel; if (!initialized) initsysinfo(); oldlevel = dbglevel; if ((level >= 0) && (level <= 4) && (level != oldlevel)) { if ((oldlevel < 2) && (level >= 2)) { if (somehookinuse()) { /* refuse */ fprintf(dumpfile, "\n***malldbglvl refused\n"); return oldlevel; } } /* Either all hooks free or our own, or level < 2 */ /* The change is feasible, level is changed and valid */ dbglevel = level; releaseallhooks(); switch (level) { /* falling through */ case 4: setfreenullhook(); case 3: setmallocfailhook(); case 2: setverifyhooks(); default: break; } /* switch (level) */ } /* valid level change */ return oldlevel; } /* malloc_debug */ /* -------- malldbg.c ----------- */