/* $Id: w_malloc.c,v 2.19 2010/05/01 10:24:35 smissaert Exp $ */
/*
* Copyright (c) 2006-2010 Ludo Smissaert <ludo@ludikidee.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the original author nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define NHASH 19997
enum { NO , YES };
typedef struct pool Pool;
struct pool {
void *ptr; /* allocated space in question */
size_t memb; /* nmemb param for calloc */
size_t size; /* size of allocation */
char oper[20]; /* operated by */
char *fname; /* allocated by function name */
char *rname; /* reallocated by f() */
size_t order; /* which number in the row of calls */
int freed; /* freed or not */
Pool *next; /* next in the pool */
};
static long order = 0; /* order of allocations */
static long ncal = 0; /* current number of calls by malloc */
static size_t total = 0; /* total number of bytes allocated */
static Pool *ptab[NHASH]; /* hash table */
static int hist[NHASH]; /* histogram of hash table allocs */
static unsigned long
hash(unsigned long val)
{
return val % NHASH;
}
static Pool *
new_pool(const char *fname)
{
Pool *newp;
char *ns, *rn;
if ((newp = malloc(sizeof(Pool))) == NULL)
return NULL;
if ((ns = strdup(fname)) == NULL) {
free(newp);
return NULL;
}
if ((rn = strdup("NONE")) == NULL) {
free(newp);
free(ns);
return NULL;
}
newp->fname = ns;
newp->rname = rn;
newp->ptr = NULL;
newp->next = NULL;
newp->order = order++;
newp->size = 0;
return newp;
}
static void
free_pool(Pool *ptr)
{
free(ptr->fname);
free(ptr->rname);
free(ptr);
}
void *
wrapper_realloc(void *p, size_t size, const char *rname)
{
void *newp;
Pool *pptr = NULL, *nptr = NULL, *pp, *prev = NULL;
char *newr;
unsigned long h, nh;
unsigned long paddress;
if (p != NULL) {
paddress = (unsigned long) p;
h = hash(paddress);
/* search pointer to be reallocated in Pool list */
for (pptr = ptab[h]; pptr != NULL; pptr = pptr->next) {
if (pptr->ptr == p)
break;
prev = pptr;
}
if (pptr == NULL)
errx(1, "w_realloc() called with "
"a pointer not allocated by w_malloc");
pp = pptr;
} else {
if ((nptr = new_pool(rname)) == NULL) {
warn("cannot allocate pool");
return NULL;
}
pp = nptr;
nptr->memb = 1;
nptr->freed = NO;
ncal++;
snprintf(nptr->oper, sizeof(nptr->oper), "wrapper_realloc");
}
if ((newp = realloc(p, size)) == NULL) {
if (nptr != NULL)
free_pool(nptr);
return NULL; /* no reallocation: leave specs */
}
if ((newr = strdup(rname)) == NULL) {
if (nptr != NULL)
free_pool(nptr);
free(newp);
warn("%s: strdup failed", __func__);
return NULL;
}
free(pp->rname);
pp->rname = newr;
/*
* realloc successful: update specs
*/
if (pptr) { /* remove old entry, keep node */
if (prev == NULL)
ptab[h] = ptab[h]->next;
else
prev->next = pptr->next;
hist[h]--;
total -= (pptr->size * pptr->memb);
pptr->memb = 1;
}
/* make a new entry in hash table */
paddress = (unsigned long) newp;
nh = hash(paddress);
hist[nh]++;
/* link-in hash table */
pp->next = ptab[nh];
ptab[nh] = pp;
pp->size = size;
total += size;
pp->ptr = newp;
return newp;
}
void *
wrapper_malloc(size_t size, const char *fname)
{
void *p;
Pool *newp;
unsigned long h;
unsigned long paddress;
if ((p = malloc(size)) == NULL)
return NULL;
if ((newp = new_pool(fname)) == NULL) {
warn("%s: can't allocate pool", __func__);
free(p);
return NULL;
}
paddress = (unsigned long) p;
h = hash(paddress);
newp->next = ptab[h];
ptab[h] = newp;
hist[h]++;
/* set variables of this Pool to specs
* of requested call
*/
newp->ptr = p;
newp->memb = 1;
newp->size = size;
newp->freed = NO;
ncal++; /* current number of allocations */
total += size; /* total size of all current allocations */
snprintf(newp->oper, sizeof(newp->oper), "wrapper_malloc");
return p;
}
void *
wrapper_calloc(size_t nmemb, size_t size, const char *fname)
{
void *p;
Pool *newp;
unsigned long h;
unsigned long paddress;
if ((p = calloc(nmemb, size)) == NULL)
return NULL;
if ((newp = new_pool(fname)) == NULL) {
warn("%s: can't allocate pool", __func__);
free(p);
return NULL;
}
paddress = (unsigned long) p;
h = hash(paddress);
newp->next = ptab[h];
ptab[h] = newp;
hist[h]++;
/* set variables of this Pool to specs
* of requested call
*/
newp->ptr = p;
newp->memb = nmemb;
newp->size = size;
newp->freed = NO;
ncal++;
total += (size * nmemb);
snprintf(newp->oper, sizeof(newp->oper), "wrapper_calloc");
return p;
}
char *
wrapper_strdup(const char *s, const char *fname)
{
Pool *newp;
char *newf, *newr, *p;
unsigned long h;
unsigned long paddress;
if ((p = strdup(s)) == NULL)
return NULL;
if ((newp = new_pool(fname)) == NULL) {
warn("%s: can't allocate pool", __func__);
free(p);
return NULL;
}
paddress = (unsigned long) p;
h = hash(paddress);
newp->next = ptab[h];
ptab[h] = newp;
hist[h]++;
/* set variables of this Pool to specs
* of requested call
*/
newp->ptr = p;
newp->memb = 1;
newp->size = strlen(s) + 1;
newp->freed = NO;
ncal++;
total += strlen(s) + 1;
snprintf(newp->oper, sizeof(newp->oper), "wrapper_strdup");
return p;
}
void
errmesg(Pool *current)
{
fprintf(stderr, "memory location: %p\n", current->ptr);
fprintf(stderr, "requested by: %s()\n", current->fname);
fprintf(stderr, "operated by: %s()\n", current->oper);
fprintf(stderr, "reallocated by: %s()\n", current->rname);
fprintf(stderr, "size: %zd bytes\n", current->size);
fprintf(stderr, "# of blocks: %zd\n", current->memb);
fprintf(stderr, "allocation #: %zd\n", current->order);
fprintf(stderr, "\n");
sleep(1);
}
/* wrapper_free: free *p, and mark its entry in Pool as free */
void
wrapper_free(void *p)
{
Pool *cur;
unsigned long h;
unsigned long paddress;
paddress = (unsigned long) p;
h = hash(paddress);
for (cur = ptab[h]; cur != NULL; cur = cur->next)
if (cur->ptr == p)
break;
if (cur == NULL)
errx(1, "%s: w_free called with a pointer not allocated "
"by w_malloc, w_calloc, w_realloc or w_strdup.", __func__);
free(p);
free(cur->fname);
free(cur->rname);
ncal--;
hist[h]--;
total -= (cur->size * cur->memb);
cur->freed = YES;
cur->ptr = cur->fname = cur->rname = NULL;
}
/* wrapper_histogram: show a histogram of the hash table with a pager */
void
wrapper_histogram(void)
{
char tmpn[MAXPATHLEN];
char cl[BUFSIZ];
FILE *fp;
int i, j, fd = -1;
snprintf(tmpn, sizeof(tmpn), "%s", "/var/tmp/tmpXXXXXXXXXX");
if ((fd = mkstemp(tmpn)) == -1 ||
(fp = fdopen(fd, "w+")) == NULL) {
if (fd != -1)
close(fd);
warn("%s: can't create %s", __func__, tmpn);
return;
}
for (i = 0; i < NHASH; i++) {
fprintf(fp, "%4d: ", i);
for (j = 0; j < hist[i]; j++)
fprintf(fp, "*");
fprintf(fp, "\n");
}
snprintf(cl, sizeof(cl), "/usr/bin/less %s", tmpn);
if (system(cl) == -1) {
warn("%s: system failed.", __func__);
return;
}
if (fclose(fp) == EOF) {
warn("%s: can't close %s", __func__, tmpn);
return;
}
if (unlink(tmpn) == -1) {
warn("%s: can't unlink %s", __func__, tmpn);
return;
}
}
void
wrapper_checkForLeaks(void)
{
Pool *cur;
int i;
if (ncal != 0 || total != 0) {
warnx("w_checkForLeaks: there is a memory leak:\n"
"w_checkForLeaks: number of unfree'd allocations: %ld\n"
"w_checkForLeaks: with a total size: %zd bytes\n"
"w_checkForLeaks: the following allocations were not freed:\n",
ncal, total);
}
for (i = 0; i < NHASH; i++)
for (cur = ptab[i]; cur != NULL; cur = cur->next)
if (cur->freed == NO)
errmesg(cur);
}