/* This file is part of the checked memory manager MALLOC. Written by Dick Grune, Vrije Universiteit, Amsterdam. $Id: Malloc.c,v 1.24 2017-01-24 19:46:57 Gebruiker Exp $ */ #include #include #include #include #include "any_int.h" #include "Malloc.h" /* make malloc.h available */ #undef malloc #undef calloc #undef realloc #undef free /*Library module source prelude */ #undef _MALLOC_CODE_ #ifndef lint #define _MALLOC_CODE_ #endif #ifdef LIB #define _MALLOC_CODE_ #endif #ifdef _MALLOC_CODE_ /* Library module source code */ #undef new #define new use_my_new /* don't call Malloc in Malloc.c */ #define my_new(type) ((type *)malloc(sizeof (type))) /* All output goes through designated files, so we block printf, etc. */ #undef printf #define printf use_fprintf #undef putchar #define putchar use_fprintf static size_t restricted_balance = 0; /* to simulate out-of-memory */ static void fprintloc(FILE *out, const char *fname, int l_nmb) { fprintf(out, "\"%s\", line %d: ", fname, l_nmb); } void _out_of_memory(const char *msg, const char *fname, int l_nmb, size_t size) { fprintloc(stderr, fname, l_nmb); fprintf(stderr, "OUT OF MEMORY"); if (msg) { fprintf(stderr, ": %s", msg); } if (size != 0) { fprintf(stderr, ", requested size = %s bytes", any_uint2string(size, 0)); } fprintf(stderr, "\n"); fflush(stderr); ReportMemoryStatus(stderr); exit(1); } /* ADMINISTRATION */ static vlong_uint total = 0; static vlong_uint balance = 0; static vlong_uint max = 0; struct alloc { /* corresponds to an allocated block */ struct alloc *next; const char *addr; size_t size; const char *fname; int l_nmb; }; #define HASH_SIZE 16381 /* largest prime under 2^16 */ static struct alloc *alloc_bucket[HASH_SIZE]; #define alloc_bucket_for(x) alloc_bucket[((unsigned int)(x)%HASH_SIZE)] static void register_alloc(char *addr, size_t size, const char *fname, int l_nmb) { /* registers the allocation of a block in the administration */ struct alloc *new; struct alloc **al_hook = &alloc_bucket_for(addr); if (addr == 0) return; new = my_new(struct alloc); new->addr = addr; new->size = size; new->fname = fname; /* no need to copy fname */ new->l_nmb = l_nmb; new->next = *al_hook; *al_hook = new; total += size; balance += size; if (balance > max) { max = balance; } } static struct alloc ** pointer_to_alloc_for(const char *addr) { struct alloc **al_hook = &alloc_bucket_for(addr); while (*al_hook) { if ((*al_hook)->addr == addr) break; al_hook = &(*al_hook)->next; } return al_hook; } static size_t register_free(char *addr) { /* registers the freeing of a block */ struct alloc **old_p = pointer_to_alloc_for(addr); struct alloc *old = *old_p; if (old == 0) return (size_t) -1; size_t old_size = old->size; *old_p = old->next; free((void *)old); balance -= old_size; return old_size; } void MemClobber(void *p, size_t size) { unsigned char *s = (unsigned char *)p; size_t i; for (i = 0; i < size; i++) { s[i] = 0125; /* 0101 0101 */ } } /* MEMORY STATUS */ struct call { /* summarizes all the allocations at a call in the program */ struct call *next; const char *fname; int l_nmb; unsigned int n_blocks; int var_size; /* all blocks have the same size or not */ size_t size; /* !var_size: the one size; var_size: sum of sizes */ }; static struct call * compacted_calls(void) { struct call *list_of_calls = 0; int i; for (i = 0; i < HASH_SIZE; i++) { struct alloc *al = alloc_bucket[i]; while (al) { struct call *cl = list_of_calls; /* try to find a call entry for this program location */ while (cl) { if ( cl->fname == al->fname && cl->l_nmb == al->l_nmb ) break; cl = cl->next; } if (cl) { /* this is known call; update */ if (cl->var_size) { cl->size += al->size; } else if (cl->size != al->size) { /* switch to var_size */ cl->var_size = 1; cl->size = cl->n_blocks*cl->size + al->size; } cl->n_blocks++; } else { /* this is a new call */ cl = my_new(struct call); cl->fname = al->fname; cl->l_nmb = al->l_nmb; cl->n_blocks = 1; cl->var_size = 0; cl->size = al->size; /* prepend to list_of_calls */ cl->next = list_of_calls; list_of_calls = cl; } al = al->next; } } return list_of_calls; } static int number_of_calls(const struct call *cl) { int res = 0; while (cl != 0) { res++; cl = cl->next; } return res; } static void report_actual_call(FILE *out, const struct call *cl) { fprintloc(out, cl->fname, cl->l_nmb); fprintf(out, "still allocated: %d block%s of size ", cl->n_blocks, (cl->n_blocks == 1 ? "" : "s") ); if (cl->var_size) { /* cl->size is the sum of the sizes */ size_t av = (cl->size+cl->n_blocks/2) / cl->n_blocks; fprintf(out, "%s on average", any_uint2string(av, 0)); if (cl->n_blocks > 1) { fprintf(out, " = %s", any_uint2string(cl->size, 0)); } } else { /* cl->size is the single size */ fprintf(out, "%s", any_uint2string(cl->size, 0)); if (cl->n_blocks > 1) { vlong_uint all = cl->size*cl->n_blocks; fprintf(out, " = %s", any_uint2string(all, 0)); } } fprintf(out, "\n"); } static void report_actual_calls(FILE *out) { const struct call *cl = compacted_calls(); /* allocates cl */ int n_calls = number_of_calls(cl); if (n_calls == 0) return; fprintf(out, "There %s %d call position%s with unreclaimed memory:\n", (n_calls == 1 ? "is" : "are"), n_calls, (n_calls == 1 ? "" : "s") ); while (cl) { report_actual_call(out, cl); struct call *next_cl = cl->next; free((void *)cl); /* frees cl */ cl = next_cl; } } void ReportMemoryStatus(FILE *out) { if (out == 0) out = stderr; report_actual_calls(out); fprintf(out, "Total memory allocated = %s", any_uint2string(total, 0)); fprintf(out, ", max. allocated = %s", any_uint2string(max, 0)); fprintf(out, ", still allocated = %s", any_uint2string(balance, 0)); fprintf(out, "\n"); fflush(out); } /* MALLOC */ void * _mreg_malloc(int chk, size_t size, const char *fname, int l_nmb) { void *res; if (restricted_balance && balance + size > restricted_balance) { res = 0; } else { res = malloc(size); } if (res == 0) { if (chk) { _out_of_memory(0, fname, l_nmb, size); /*NOTREACHED*/ } return res; } register_alloc(res, size, fname, l_nmb); #ifdef MEMCLOBBER MemClobber((char *)res, size); #endif /* MEMCLOBBER */ return res; } void * _mreg_calloc(int chk, size_t n, size_t size, const char *fname, int l_nmb) { void *res; if (restricted_balance && balance + n*size > restricted_balance) { res = 0; } else { res = calloc(n, size); } if (res == 0) { if (chk) { _out_of_memory(0, fname, l_nmb, n*size); /*NOTREACHED*/ } return res; } register_alloc(res, n*size, fname, l_nmb); return res; } void * _mreg_realloc(int chk, void *addr, size_t size, const char *fname, int l_nmb) { void *res; size_t old_size = register_free(addr); /* we report first, because the realloc() below may cause a crash */ if ( /* we are not reallocating address 0, which is allowed */ addr != 0 && /* the address was never handed out before */ old_size == (size_t) -1 ) { fprintloc(stderr, fname, l_nmb); fprintf(stderr, ">>>> unallocated block reallocated <<<<\n"); fflush(stderr); } if (restricted_balance && balance + size > restricted_balance) { res = 0; } else { res = realloc(addr, size); } if (res == 0) { if (chk) { _out_of_memory(0, fname, l_nmb, size); /*NOTREACHED*/ } return res; } register_alloc(res, size, fname, l_nmb); #ifdef MEMCLOBBER if (old_size > 0 && size > old_size) { MemClobber(((char *)res)+old_size, size-old_size); } #endif /* MEMCLOBBER */ return res; } /* ARGSUSED */ void _mreg_free(void *addr, const char *fname, int l_nmb) { size_t old_size = register_free(addr); /* we report first, because the free() below may cause a crash */ if (old_size == (size_t) -1) { fprintloc(stderr, fname, l_nmb); fprintf(stderr, ">>>> unallocated block freed "); fprintf(stderr, "or multiple free of allocated block <<<<\n"); fflush(stderr); } else { #ifdef MEMCLOBBER MemClobber((char *)addr, old_size); #endif /* MEMCLOBBER */ } free(addr); } char * _new_string(int chk, const char *s, const char *fname, int l_nmb) { return strcpy((char *)(_mreg_malloc(chk, strlen(s)+1, fname, l_nmb)), s); } /* End library module source code */ #endif /* _MALLOC_CODE_ */ #ifdef lint static void satisfy_lint(void *x) { void *v; v = _mreg_malloc(0, 0, 0, 0); v = _mreg_calloc(0, 0, 0, 0, 0); v = _mreg_realloc(0, 0, 0, 0, 0); _mreg_free(x, 0, 0); OutOfMemoryExit(0); ReportMemoryStatus(0); MemClobber(v, 0); v = _new_string(0, 0, 0, 0); satisfy_lint(v); } #endif /* lint */