/*
* NAME:
* memdebug.c -- memory usage debugging layer for malloc(), free(), etc.
*
* AUTHOR:
* David Relson <relson@osagesoftware.com>
*
* NOTE:
* These routines are useful, though not especially polished.
*
* Capabilities:
*
* - Count calls to malloc(), free(), realloc(), calloc().
* - For each such call, track current, maximum, and total bytes allocated.
* - Display summary statistics.
* - Display size and address of each block as allocated or freed to locate the unfreed blocks.
* - Trap routine than can be called by allocation index or allocation size.
* - Program exit when current allocation is "too much".
*/
#define NO_MEMDEBUG_MACROS
#include "common.h"
#include <stdlib.h>
#include "memdebug.h"
#include "xmalloc.h"
int memtrace= 1 * (M_MALLOC+M_FREE);
uint32_t dbg_trap_index= 36;
uint32_t dbg_size_min = 0;
uint32_t dbg_size_max = 0;
uint32_t dbg_index_min = 0; /* 1 */
uint32_t dbg_index_max = 0; /* 1000 */
uint32_t dbg_size_trap = 0; /* 100000 */
#define MB 1000000
#define GB 1000*MB
uint32_t dbg_too_much = 0; /* GB */
uint32_t dbg_size_delt = 0; /* MB */
uint32_t dbg_delt_save = 0;
const uint32_t md_tag = (uint32_t) 0xABCD55AA;
uint32_t cnt_alloc = 0;
uint32_t cnt_free = 0;
uint32_t cnt_malloc = 0;
uint32_t cnt_realloc= 0;
uint32_t cnt_calloc = 0;
uint32_t cur_malloc = 0;
uint32_t max_malloc = 0;
uint32_t tot_malloc = 0;
void md_trap(const char *why);
void md_too_much(void);
void md_trap(const char *why) { (void)why; }
void md_too_much()
{
static bool first = true;
if (first) {
first = false;
fprintf(stderr, "max_malloc = %12lu, tot_malloc = %12lu\n",
(ulong) max_malloc, (ulong) tot_malloc);
md_trap("dbg_too_much");
}
}
typedef struct memheader {
uint32_t size;
uint32_t indx;
uint32_t tag;
} mh_t;
typedef struct memtrailer {
uint32_t tag;
} mt_t;
void mh_disp(const char *s, mh_t *p);
void mh_disp(const char *s, mh_t *p)
{
if (!DEBUG_MEMORY(1))
return;
if (dbg_index_min != 0 && p->indx < dbg_index_min)
return;
if (dbg_index_max != 0 && p->indx > dbg_index_max)
return;
if (dbg_size_min != 0 && p->size < dbg_size_min)
return;
if (dbg_size_max != 0 && p->size > dbg_size_max)
return;
fprintf(dbgout, "::%3d %08lX %s %lu\n", p->indx, (ulong) (p+1), s, (ulong) p->size);
return;
}
void *
md_malloc(size_t size)
{
void *ptr;
mt_t *mt;
mh_t *mh = NULL;
++cnt_malloc;
cur_malloc += size;
if (dbg_size_delt != 0 &&
max_malloc - dbg_delt_save > dbg_size_delt) {
dbg_delt_save = max_malloc / dbg_size_delt * dbg_size_delt;
fprintf(dbgout, ":: max_malloc = %12lu\n", (ulong) max_malloc);
}
max_malloc = max(max_malloc, cur_malloc);
tot_malloc += size;
size += sizeof(mh_t) + sizeof(md_tag); /* Include size storage */
if (dbg_size_trap != 0 && size > dbg_size_trap)
md_trap("dbg_size_trap");
if (dbg_too_much != 0 && max_malloc > dbg_too_much)
md_too_much();
ptr = malloc(size);
mh = (mh_t *) ptr;
mh->size = size - sizeof(mh_t) - sizeof(md_tag);
mh->indx = ++cnt_alloc;
mh->tag = md_tag;
if (memtrace & M_MALLOC)
mh_disp( "a", mh );
if (dbg_trap_index != 0 && mh->indx == dbg_trap_index)
md_trap("dbg_index");
mt = (mt_t *)((char *)(mh+1)+mh->size);
mt->tag = md_tag;
ptr = (void *) (mh+1);
return ptr;
}
void
md_free(void *ptr)
{
mt_t *mt;
mh_t *mh = (mh_t *) ptr;
if (!ptr)
return;
mh -= 1;
ptr = (void *) mh;
++cnt_free;
if (memtrace & M_FREE)
mh_disp( "f", mh );
if (mh->tag != md_tag)
md_trap("md_tag");
if (dbg_trap_index != 0 && mh->indx == dbg_trap_index)
md_trap("dbg_trap_index");
if (mh->size > cur_malloc ||
(dbg_size_trap != 0 && mh->size > dbg_size_trap))
md_trap("dbg_size_trap");
mt = (mt_t *)((char *)(mh+1)+mh->size);
if (mt->tag != md_tag)
md_trap("md_tag");
cur_malloc -= mh->size;
mh->tag = -1;
free(ptr);
}
void memdisplay(const char *file, int lineno)
{
const char *pfx = "";
if (!DEBUG_MEMORY(0))
return;
if (file != NULL) {
pfx = "\t";
fprintf(dbgout, "%s:%d:\n", file, lineno);
}
fprintf(dbgout, "%smalloc: cur = %lu, max = %lu, tot = %lu\n", pfx,
(ulong) cur_malloc, (ulong) max_malloc, (ulong) tot_malloc );
fprintf(dbgout, "%scounts: malloc: %lu, calloc: %lu, realloc: %lu, free: %lu\n", pfx,
(ulong) cnt_malloc, (ulong) cnt_calloc, (ulong) cnt_realloc, (ulong) cnt_free);
if (cnt_alloc == cnt_free)
fprintf(dbgout, "%s none active.\n", pfx);
else
fprintf(dbgout, "%s active: %lu, average: %lu\n", pfx,
(ulong) cnt_alloc - cnt_free, (ulong) cur_malloc/(cnt_alloc - cnt_free));
fflush(dbgout);
}
void
*md_calloc(size_t nmemb, size_t size)
{
void *ptr;
mt_t *mt;
mh_t *mh;
size = size * nmemb;
nmemb = 1;
cur_malloc += size;
max_malloc = max(max_malloc, cur_malloc);
tot_malloc += size;
size += sizeof(mh_t) + sizeof(md_tag); /* Include size storage */
++cnt_calloc;
if (dbg_too_much != 0 && max_malloc > dbg_too_much)
md_too_much();
ptr = calloc(nmemb, size);
mh = (mh_t *) ptr;
mh->size = size - sizeof(mh_t) - sizeof(md_tag);
mh->indx = ++cnt_alloc;
mh->tag = md_tag;
if (memtrace & M_MALLOC)
mh_disp( "c", mh );
if (dbg_trap_index != 0 && mh->indx == dbg_trap_index)
md_trap("dbg_trap_index");
mt = (mt_t *)((char *)(mh+1)+mh->size);
mt->tag = md_tag;
ptr = (void *) (mh+1);
return ptr;
}
void
*md_realloc(void *ptr, size_t size)
{
mh_t *mh = ((mh_t *) ptr)-1, *omh;
mt_t *mt;
size_t oldsize = mh->size;
cur_malloc += size - oldsize;
max_malloc = max(max_malloc, cur_malloc);
tot_malloc += size - oldsize;
++cnt_realloc;
if (dbg_too_much != 0 && max_malloc > dbg_too_much)
md_too_much();
if (memtrace & M_FREE)
mh_disp( "r", mh );
size = size + sizeof(mh_t) + sizeof(md_tag);
omh = mh;
mh = realloc(mh, size);
if (!mh) { const char *oom = "Out of memory in " __FILE__ ":" #__LINE__ "\n"; free(omh); if(write(STDERR_FILENO, oom, strlen(oom))) { /* ignore error */ } _exit(EXIT_FAILURE); }
mh->size = size - sizeof(mh_t) - sizeof(md_tag);
mh->indx = ++cnt_realloc;
mh->tag = md_tag;
if (memtrace & M_MALLOC)
mh_disp( "r", mh );
mt = (mt_t *)((char *)mh+mh->size);
mt->tag = md_tag;
ptr = (void *) (mh+1);
return ptr;
}