// ---------------------------------------------------------------------------
// - cmem.cxx -
// - standard system library - c memory function implementation -
// ---------------------------------------------------------------------------
// - This program is free software; you can redistribute it and/or modify -
// - it provided that this copyright notice is kept intact. -
// - -
// - This program 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. In no event shall -
// - the copyright holder be liable for any direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software. -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2007 amaury darsch -
// ---------------------------------------------------------------------------
#include "csys.hpp"
#include "csio.hpp"
#include "cthr.hpp"
#include "cmem.hpp"
#include "cmem.hxx"
namespace afnix {
// the memory access control lock
static void* mtxmem = 0;
// the structure for traced memory
struct s_gptr {
s_gptr* p_prev;
s_gptr* p_next;
void* p_bptr;
long d_size;
long d_magic;
};
// this function computes the number of pages for a given size
static long get_psize (const long size) {
long psize = c_pagesize ();
long result = size / psize;
if ((size % psize) != 0) result++;
return (result * psize);
}
// this function computes the number of pages for a given size
static long get_psize (const long size, const long foff) {
long psize = c_pagesize ();
long result = size / psize + ((foff == 0) ? 0 : 1);
if ((size % psize) != 0) result++;
return (result * psize);
}
// this function adjust an offset on a page boundary
static long get_osize (const long size) {
long psize = c_pagesize ();
long result = size / psize;
return (result * psize);
}
// simple function to align on a 8 bytes basis
static unsigned long align (const unsigned long size) {
unsigned long pad = size % 8;
if (pad == 0) return size;
return (((size / 8) + 1) * 8);
}
// the size with the padding
unsigned long offset = align (sizeof (s_gptr));
// the root pointer for traced memory
static s_gptr* groot = 0;
// the memory tracing counter and the magic number
static long gacnt = 0;
static long gfcnt = 0;
const long GALLOC_MAGIC = 0x0fabcdef;
// the cleanup counter and function array
typedef void (*t_cfunc) (void);
static long gccnt = 0;
static t_cfunc* gcfcn = 0;
// the initialize flag for memory tracing
static bool gflag = false;
static bool gmchk = (c_getenv ("AFNIX_GALLOC_CHECK") != nilp);
static bool gpstk = (c_getenv ("AFNIX_GALLOC_DEBUG") != nilp);
static bool gctrc = (c_getenv ("AFNIX_GALLOC_TRACE") != nilp);
static bool gdymd = (c_getenv ("AFNIX_GALLOC_DYNMD") != nilp);
static bool gctrl = gmchk || gpstk || gctrc;
static bool gdctr = false;
// enable/disable dynamic memory debug
void c_setmdbg (const bool flag) {
// do nothing if the dynamic debugging
// was not previsouly authorized
if (gdymd == false) return;
// get the memory mutex
c_mtxlock (mtxmem);
// set the control flag
gctrl = flag;
// set the dynamic control
gdctr = true;
// release memeory mutex
c_mtxunlock (mtxmem);
}
// this function report the garbage memory at exit
static void galloc_report (void) {
while (groot != 0) {
if (groot->d_magic != GALLOC_MAGIC) {
fprintf (stderr, "galloc: invalid pointer at %p\n", groot);
abort ();
}
void* handle = ((char*) groot) + offset;
fprintf (stderr, "garbage allocation of %ld bytes\n",groot->d_size);
fprintf (stderr, "\tobject: %p\n",handle);
c_printtrace (groot->p_bptr);
groot = groot->p_next;
}
fprintf (stderr, "total allocated memory: %ld\n", gacnt);
fprintf (stderr, "total freed memory: %ld\n", gfcnt);
}
// this function calls the memory cleanup functions and the report
// function for garbage memory
static void galloc_cleanup (void) {
// call the cleanup functions
for (long i = 0; i < gccnt; i++) {
t_cfunc func = (t_cfunc) gcfcn[i];
func ();
}
// do the report
galloc_report ();
// clean the mutex
c_mtxdestroy (mtxmem);
}
// this function initalize the memory tracing
static void galloc_init (void) {
if ((gctrl == false) || (gflag == true)) return;
c_atexit (galloc_cleanup);
gflag = true;
mtxmem = c_mtxcreate ();
}
// this function clean the memory debug stack
static void galloc_clean (s_gptr* handle) {
// get the lock before cleaning up
c_mtxlock (mtxmem);
// unlink the structure and free it
s_gptr* prev = handle->p_prev;
s_gptr* next = handle->p_next;
if (prev == 0) {
groot = next;
if (groot != 0) groot->p_prev = 0;
} else {
prev->p_next = next;
if (next != 0) next->p_prev = prev;
}
gfcnt += handle->d_size;
// check if we print the stack trace for the allocation
if (gpstk == true) {
fprintf (stderr, "destruction of %ld bytes\n", handle->d_size);
fprintf (stderr, "object: %p\n", handle);
c_printtrace (handle->p_bptr);
}
c_destroytrace (handle->p_bptr);
free (handle);
// release the lock
c_mtxunlock (mtxmem);
}
// allocate some memory for tracing
void* c_galloc (const long size) {
// do nothing in non tracing mode
if (gctrl == false) return malloc (size);
// handle first the check mode
if (gmchk == true) {
void* result = malloc (size + 8);
(* (long long int*) result) = 0;
return ((char*) result + 8);
}
// initialize the memory tracing
if (gflag == false) galloc_init ();
// get the lock before allocation
c_mtxlock (mtxmem);
// allocate the memory block
s_gptr* handle = (s_gptr*) malloc (size + offset);
if (groot != 0) groot->p_prev = handle;
handle->p_next = groot;
handle->p_prev = 0;
handle->d_size = size;
handle->d_magic = GALLOC_MAGIC;
handle->p_bptr = c_backtrace ();
groot = handle;
gacnt += size;
// re-align result
void* result = ((char*) handle) + offset;
// check if we print the stack trace for the allocation
if (gpstk == true) {
fprintf (stderr, "allocation of %ld bytes\n", size);
fprintf (stderr, "object: %p\n", handle);
c_printtrace (handle->p_bptr);
}
c_mtxunlock (mtxmem);
return result;
}
// free some memory in tracing mode
void c_gfree (void* ptr) {
// handle the simple free vs complex one
if (gctrl == false) {
if (gdctr == false) {
// here the memory debugger was never turned on dynamically
free (ptr);
} else {
// here the memory debugger was turned off dynamically
s_gptr* handle = (s_gptr*) ((char*) (ptr) - offset);
if (handle->d_magic == GALLOC_MAGIC) {
galloc_clean (handle);
} else {
free (ptr);
}
}
return;
}
// handle memory check first
if (gmchk == true) {
c_mtxlock (mtxmem);
void* handle = (char*) ptr - 8;
if ((* (long long int*) handle) != 0) {
fprintf (stderr, "galloc: invalid memory free\n");
c_mtxunlock (mtxmem);
return;
}
*(long long int*) handle = 1;
c_mtxunlock (mtxmem);
return;
}
// get the structure and check the magic number
s_gptr* handle = (s_gptr*) ((char*) (ptr) - offset);
if (handle->d_magic != GALLOC_MAGIC) {
if (gdctr == false) {
// here the memory debugger was never turned on dynamically
fprintf (stderr, "galloc: invalid pointer to free at %p\n", ptr);
abort ();
} else {
// assume here the pointer was allocated before turning on
// the memory debugger
free (ptr);
}
return;
}
// clean the handle
galloc_clean (handle);
}
// register a memory cleanup function
void c_gcleanup (void (*func) (void)) {
// just use atexit if no tracing
if (gctrl == false) {
c_atexit (func);
return;
}
// allocate a new array of cleanup functions
t_cfunc* array = (t_cfunc*) malloc ((gccnt+1) * sizeof (t_cfunc));
// copy the old array to the new one
for (long i = 0; i < gccnt; i++) array[i] = gcfcn[i];
array[gccnt++] = func;
free (gcfcn);
gcfcn = array;
}
// allocate some memory with malloc
void* c_malloc (const long size) {
if (size <= 0) return 0;
return malloc (size);
}
// free some memory with normal free
void c_free (void* handle) {
if (handle == 0) return;
free (handle);
}
// map some memory with a file descriptor
void* c_mmap (const int sid, const long size, const long foff) {
if ((sid == -1) || (size == 0)) return nilp;
// get the memory to allocate in page
long psize = get_psize (size, foff);
// get the offset aligned to pages
long osize = get_osize (foff);
long opage = foff - osize;
char* ptr = (char*) mmap (0, psize, PROT_READ|PROT_WRITE, MAP_PRIVATE,
sid, osize);
if (ptr == MAP_FAILED) return nilp;
return (ptr + opage);
}
// unmap a memory block
void c_munmap (void* ptr, const long size) {
long psize = get_psize (size);
munmap ((caddr_t) ptr, psize);
}
}
#ifdef AFNIX_HAVE_SYSCONF
namespace afnix {
// return the system memory page
long c_pagesize (void) {
return sysconf (_SC_PAGESIZE);
}
}
#endif
#ifdef AFNIX_HAVE_PAGESIZE
namespace afnix {
// return the system memory page
long c_pagesize (void) {
return getpagesize ();
}
}
#endif
#ifdef AFNIX_HAVE_MAPANON
namespace afnix {
// allocate a block of memory
void* c_mmap (const long size) {
long psize = get_psize (size);
void* ptr = mmap (0, psize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,
-1, 0);
return (ptr == MAP_FAILED) ? nilp : ptr;
}
}
#else
namespace afnix {
// allocate a block of memory
void* c_mmap (const long size) {
int fd = open ("/dev/zero", O_RDWR);
if (fd == -1) return nilp;
long psize = get_psize (size);
void* ptr = mmap (0,psize,PROT_READ|PROT_WRITE,MAP_PRIVATE, fd ,0);
close (fd);
return (ptr == MAP_FAILED) ? nilp : ptr;
}
}
#endif
#ifdef AFNIX_HAVE_MREMAP
namespace afnix {
// reallocate a block of memory
void* c_mremap (void* optr, const long osize, const long nsize) {
long posize = get_psize (osize);
long pnsize = get_psize (nsize);
void* ptr = mremap ((caddr_t) optr,posize,pnsize,MREMAP_MAYMOVE);
return (ptr == MAP_FAILED) ? nilp : ptr;
}
}
#else
namespace afnix {
// reallocate a block of memory - on sun we have to allocate and copy
void* c_mremap (void* optr, const long osize, const long nsize) {
if (nsize <= osize) return optr;
void* nptr = c_mmap (nsize);
unsigned char* cnptr = (unsigned char*) nptr;
unsigned char* coptr = (unsigned char*) optr;
for (long i = 0; i < osize; i++) *cnptr++ = *coptr++;
c_munmap (optr, osize);;
return nptr;
}
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1