/*
* $Id: aug_alloc.c,v 1.1.10.3 2005/07/20 17:26:16 andrei Exp $
*
* POSTGRES module, portions of this code were templated using
* the mysql module, thus it's similarity.
*
*
* Copyright (C) 2003 August.Net Services, LLC
*
* This file is part of ser, a free SIP server.
*
* ser is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* History
* -------
* 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
*
*/
/*
** ________________________________________________________________________
**
**
** $RCSfile: aug_alloc.c,v $
** $Revision: 1.1.10.3 $
**
** Last change $Date: 2005/07/20 17:26:16 $
** Last change $Author: andrei $
** $State: Exp $
** $Locker: $
**
** Original author: Andrew Fullford
**
** Copyright (C) August Associates 1995
**
** ________________________________________________________________________
*/
#include "aug_std.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef double MemAlign;
typedef augUInt32 MemMagic;
typedef union MemHead MemHead;
typedef struct MemOpt MemOpt;
typedef struct MemDestructor MemDestructor; /* not yet implemented */
/*
** One of these MemHead structs is allocated at the head of
** each alloc, plus an extra magic number at the end area.
** This gives an allocation overhead of:
**
** malloc_overhead + sizeof MemHead + sizeof MemMagic
**
** "Notes" entry for the man page: the allocation overhead is way
** too high. (On a 32bit machine and assuming a malloc overhead
** of 8 bytes, the total will be 8 + 32 + 4 = 44 bytes).
*/
struct MemHeadStruct
{
MemHead *parent, *sibling, *child;
MemOpt *options;
char *end;
char *file;
augUInt32 line;
MemMagic magic;
};
/*
** Attempt to guarantee alignment.
*/
union MemHead
{
struct MemHeadStruct m;
MemAlign align[1];
};
/*
** MemOpt holds optional features. The only current example
** is the memory destructor state.
*/
struct MemOpt
{
MemMagic magic;
MemDestructor *destructor_list;
};
/*
** These magic numbers are used to validate headers, force memory
** to a known state, etc.
*/
#define MEM_MAGIC_BOUND 0xC0EDBABE
#define MEM_MAGIC_FILL 0xDEADC0DE
static int mem_bad(MemHead *mem, char *where, char *file, int line)
{
aug_abort(file, line, "Corrupted memory in %s", where);
return 0;
}
/*
** Calculate the MemHead address given an aug_alloc() pointer.
*/
#define MEM_CROWN(alloc) ((MemHead *)(((char *)alloc) - sizeof (MemHead)))
#define MEM_DECAPITATE(mem) ((void *)(((char *)mem) + sizeof (MemHead)))
static MemMagic mem_magic = MEM_MAGIC_BOUND;
#define MEM_TAIL(p) (memcmp((p)->m.end,(char*)&mem_magic,sizeof mem_magic)==0)
#define MEM_CHECK(p,w) ((p) && \
((p)->m.magic != MEM_MAGIC_BOUND || !MEM_TAIL(p)) && \
mem_bad((p),(w),file,line))
/* Initialize stats structure with estimated overhead */
static augAllocStats mem_stats = {sizeof (MemHead) + sizeof (MemMagic) + 8, 0};
static augNoMemFunc *mem_nomem_func = 0;
static void mem_nomem(size_t size, char *func, char *file, int line)
{
static augBool active = augFALSE;
char *module;
if(!func)
func = "unknown function";
if(active)
fprintf(stderr, "\r\n\nPANIC: nomem bounce\r\n\n");
else
{
active = augTRUE;
if(mem_nomem_func)
(*mem_nomem_func)(size, func, file, line);
}
fprintf(stderr, "\r\n\n");
module = aug_module();
if(module && *module)
fprintf(stderr, "FATAL in %s: ", module);
else
fprintf(stderr, "FATAL: ");
fprintf(stderr, "%s failure allocating %lu bytes ", func,
(unsigned long)size);
if(file && *file)
fprintf(stderr, "from +%d %s \r\n", line, file);
else
fprintf(stderr, "(unknown location) \r\n");
fprintf(stderr, " Current allocations: %10lu \r\n",
(mem_stats.alloc_ops - mem_stats.free_ops));
fprintf(stderr, " Total allocations: %10lu \r\n",
mem_stats.alloc_ops);
fprintf(stderr, " Total reallocations: %10lu \r\n",
mem_stats.realloc_ops);
fprintf(stderr, " Total frees: %10lu \r\n",
mem_stats.free_ops);
fprintf(stderr, "Estimated total heap use (KBytes): %10lu \r\n",
(mem_stats.current_bytes_allocated +
(mem_stats.alloc_ops - mem_stats.free_ops) *
mem_stats.estimated_overhead_per_alloc + 512)/1024);
fprintf(stderr, "\n");
aug_exit(augEXIT_NOMEM);
}
static void *mem_alloc(size_t size, void *parent, char *file, int line)
{
MemHead *mem, *par;
DABNAME("mem_alloc");
if(parent)
{
par = MEM_CROWN(parent);
MEM_CHECK(par, "parent");
MEM_CHECK(par->m.child, "sibling");
MEM_CHECK(par->m.sibling, "uncle");
}
else
par = 0;
mem_stats.current_bytes_allocated += size;
mem_stats.alloc_ops++;
/* Adjust for overhead */
size += sizeof (MemHead);
mem = malloc(size + sizeof (MemMagic));
if(!mem)
mem_nomem(size, "aug_alloc", file, line);
if(DABLEVEL(DAB_STD))
{
unsigned long *p;
p = (unsigned long *)mem;
while((char *)p <= (char *)mem + size)
*p++ = MEM_MAGIC_FILL;
}
mem->m.magic = MEM_MAGIC_BOUND;
mem->m.file = file;
mem->m.line = line;
mem->m.end = (char *)mem + size;
mem->m.options = 0;
mem->m.child = 0;
mem->m.parent = par;
if(par)
{
if((mem->m.sibling = par->m.child))
mem->m.sibling->m.parent = mem;
par->m.child = mem;
}
else
mem->m.sibling = 0;
memcpy(mem->m.end, (char *)&mem_magic, sizeof mem_magic);
return MEM_DECAPITATE(mem);
}
static void mem_free(MemHead *mem)
{
size_t size;
DABNAME("mem_free");
while(mem)
{
MemHead *next = mem->m.sibling;
if(mem->m.child)
mem_free(mem->m.child);
size = (char *)mem->m.end - (char *)mem;
size -= sizeof (MemHead) + sizeof (MemMagic);
mem_stats.current_bytes_allocated -= size;
mem_stats.free_ops++;
if(DABLEVEL(DAB_STD))
{
unsigned long *p = (unsigned long *)(mem+1);
while((char *)p <= mem->m.end)
*p++ = MEM_MAGIC_FILL;
p = (unsigned long *)mem;
while(p < (unsigned long *)(mem+1))
*p++ = MEM_MAGIC_FILL;
}
free(mem);
mem = next;
}
}
static augBool mem_find(MemHead *mem, MemHead *p)
{
while(mem)
{
MemHead *next;
if(mem == p)
return augTRUE;
next = mem->m.sibling;
if(mem->m.child)
if(mem_find(mem->m.child, p))
return augTRUE;
mem = next;
}
return augFALSE;
}
augExport augNoMemFunc *aug_set_nomem_func(augNoMemFunc *new_func)
{
augNoMemFunc *old = mem_nomem_func;
DABNAME("aug_set_nomem_func");
mem_nomem_func = new_func;
DABTRACE("New nomem func %08lx, previous %08lx",
(unsigned long)mem_nomem_func, (unsigned long)old);
return old;
}
augExport augAllocStats *aug_alloc_stats(void)
{
return &mem_stats;
}
augExport void *aug_alloc_loc(size_t size, void *parent, char *file, int line)
{
void *alloc;
DABNAME("aug_alloc");
DAB("size %lu, parent %08lx [+%d %s]",
(unsigned long)size, (unsigned long)parent, line, file);
alloc = mem_alloc(size, parent, file, line);
DABL(80)("size %lu with header, caller mem at %08lx",
MEM_CROWN(alloc)->m.end - (char *)MEM_CROWN(alloc),
(unsigned long)alloc);
return alloc;
}
augExport void *aug_realloc_loc(size_t size, void *prev, char *file, int line)
{
void *alloc;
size_t prev_size;
MemHead *mem, *par, *kid, *sib, *new;
DABNAME("aug_realloc");
if(!prev)
aug_abort(file, line, "Attempt to realloc a NULL pointer");
mem = MEM_CROWN(prev);
MEM_CHECK(mem, "previous alloc");
par = mem->m.parent; MEM_CHECK(par, "realloc parent");
kid = mem->m.child; MEM_CHECK(kid, "realloc child");
sib = mem->m.sibling; MEM_CHECK(sib, "realloc sibling");
prev_size = (mem->m.end - (char *)mem) - sizeof (MemHead);
DAB("prior size %lu, new %lu [+%d %s]",
(unsigned long)prev_size, (unsigned long)size,
line, file);
DABL(80)("prior mem %08lx", (unsigned long)mem);
mem_stats.current_bytes_allocated += size - prev_size;
mem_stats.realloc_ops++;
size += sizeof (MemHead);
new = realloc(mem, size + sizeof (MemMagic));
if(!new)
mem_nomem(size, "aug_realloc", file, line);
new->m.end = (char *)new + size;
memcpy(new->m.end, (char *)&mem_magic, sizeof mem_magic);
if(par)
{
if(par->m.sibling == mem)
par->m.sibling = new;
else
par->m.child = new;
}
if(kid)
kid->m.parent = new;
if(sib)
sib->m.parent = new;
alloc = MEM_DECAPITATE(new);
DABL(80)("size %lu with header, caller mem at %08lx",
new->m.end - (char *)new, (unsigned long)alloc);
return alloc;
}
augExport void aug_free_loc(const void *alloc, char *file, int line)
{
MemHead *mem, *par;
DABNAME("aug_free");
if(!alloc)
aug_abort(file, line, "Attempt to free a NULL pointer");
DAB("Freeing %08lx [+%d %s]", (unsigned long)alloc, line, file);
mem = MEM_CROWN(alloc);
MEM_CHECK(mem, "alloc to free");
par = mem->m.parent;
MEM_CHECK(par, "parent in free");
if(par)
{
if(par->m.sibling == mem)
par->m.sibling = mem->m.sibling;
else
par->m.child = mem->m.sibling;
}
if(mem->m.sibling)
{
mem->m.sibling->m.parent = par;
mem->m.sibling = 0;
}
mem_free(mem);
}
augExport void aug_foster_loc(void *alloc, void *parent, char *file, int line)
{
MemHead *mem, *fpar, *ppar, *sib;
DABNAME("aug_foster");
DAB("Foster %08lx to %08lx [+%d %s]",
alloc, parent, line, file);
if(!alloc)
aug_abort(file, line, "Attempt to foster a NULL pointer");
mem = MEM_CROWN(alloc);
MEM_CHECK(mem, "alloc to foster");
if(parent)
{
fpar = MEM_CROWN(parent);
MEM_CHECK(fpar, "foster parent");
}
else
fpar = 0;
ppar = mem->m.parent; MEM_CHECK(ppar, "prior parent");
sib = mem->m.sibling; MEM_CHECK(ppar, "sibling in foster");
if(fpar == ppar)
{
DABTRACE("No change in parent (%08lx)", (unsigned long)fpar);
return;
}
if(mem == fpar)
aug_abort(file, line, "Attempt to adopt self");
/*
** Check for incest - isnew parent actually our child?
*/
if(mem_find(mem->m.child, fpar))
aug_abort(file, line, "Attempt to adopt a parent");
/*
** Leave home.
*/
if(!ppar)
{
DABBULK("Leaving orphanage");
if(mem->m.sibling)
mem->m.sibling->m.parent = 0;
}
else if(ppar->m.sibling == mem)
{
DABBULK("Older child");
ppar->m.sibling = mem->m.sibling;
if(ppar->m.sibling)
ppar->m.sibling->m.parent = ppar;
}
else
{
DABBULK("Youngest child");
ppar->m.child = mem->m.sibling;
if(ppar->m.child)
ppar->m.child->m.parent = ppar;
}
/*
** Find new home.
*/
mem->m.parent = fpar;
if(fpar)
{
mem->m.sibling = fpar->m.child;
fpar->m.child = mem;
if(mem->m.sibling)
mem->m.sibling->m.parent = mem;
}
else
mem->m.sibling = 0;
}
augExport char *aug_strdup_loc(const char *str, void *parent, char *file,
int line)
{
char *new;
size_t size;
DABNAME("aug_strdup");
if(!str)
aug_abort(file, line, "Attempt to duplicate a NULL string");
size = strlen(str)+1;
DAB("string length %lu [+%d %s]", (unsigned long)size, line, file);
new = mem_alloc(size, parent, file, line);
DABL(80)("size %lu with header, caller mem at %08lx",
MEM_CROWN(new)->m.end - (char *)MEM_CROWN(new),
(unsigned long)new);
strcpy(new, str);
return new;
}
augExport char **aug_vecdup_loc(char **vec, void *parent, char *file, int line)
{
char **nv, **v, *c;
size_t size;
int vsize;
DABNAME("aug_vecdup");
if(!vec)
aug_abort(file, line, "Attempt to duplicate a NULL vector");
size = 0;
for(v = vec; *v; v++)
size += strlen(*v) + 1;
vsize = v - vec;
DABL(80)("%d elements, total string size %d", vsize, size);
vsize++;
nv = (char **)mem_alloc(vsize * sizeof *v + size, parent, file, line);
c = (char *)(nv + vsize);
for(v = nv; *vec; v++, vec++)
{
strcpy(c, *vec);
*v = c;
c += strlen(c) + 1;
}
*v = 0;
return nv;
}
#ifdef TEST
static void nomem(size_t size, char *func, char *file, int line)
{
fprintf(stderr, "\nNOMEM on %lu bytes via %s, called from %s line %d\n",
(unsigned long)size, func, file, line);
/*
** Normally would exit from here, but might as well test the
** default trap.
*/
return;
}
main(int argc, char **argv)
{
int i;
void *par;
char *mem, *m, **v;
aug_setmodule(argv[0]);
printf("<MemHead size %lu> should equal <struct MemHead %lu>\n",
sizeof (MemHead), sizeof (struct MemHead));
par = aug_alloc(20, 0);
for(i = 0; i < 20; i++)
{
if(i == 10)
mem = aug_strdup("Hello, world\n", par);
else
(void)aug_alloc(3000, par);
}
mem = aug_realloc(10000, mem);
for(i = 0; i < 20; i++)
{
if(i == 10)
m = aug_strdup("Hello, world\n", mem);
else
(void)aug_alloc(3000, par);
}
v = aug_vecdup(argv, par);
printf("Program args:");
while(*v)
{
printf(" %s", *v++);
fflush(stdout);
}
printf("\n");
aug_foster(m, par);
if(argc > 1)
{
printf("Checking anti-incest test ... this should abort\n");
aug_foster(par, mem);
}
for(i = 0; i < 20; i++)
{
if(i == 10)
m = aug_strdup("Hello, world\n", mem);
else
(void)aug_alloc(3000, mem);
}
mem = aug_realloc(10000, mem);
aug_foster(m, par);
aug_free(mem);
printf("If you can read this, the test completed ok\n");
printf("Now testing NOMEM func - this should abort after a while ... ");
fflush(stdout);
aug_set_nomem_func(nomem);
while(m = aug_alloc(128*1024, par))
continue;
/* Should never get to here */
aug_free(par);
aug_exit(augEXIT_YES);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1