/******************************************************************************* * * HEADER: memalloc * ******************************************************************************** * * DESCRIPTION: Memory allocation and tracing routines * ******************************************************************************** * * $Project: /Convert-Binary-C $ * $Author: mhx $ * $Date: 2006/08/26 16:19:24 +0200 $ * $Revision: 21 $ * $Source: /util/memalloc.h $ * ******************************************************************************** * * Copyright (c) 2002-2006 Marcus Holland-Moritz. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of either the Artistic License or 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. * * THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * *******************************************************************************/ /** * \file memalloc.h * \brief Memory allocation and tracing routines * * The functions in this file provide an interface to * the standard malloc / free functions, but in addition * you can selectively enable tracing of your memory * allocation. This may be useful to detect memory leaks * or usage of already freed memory blocks. * * A Perl script is supplied to analyze the output of * the memory tracing routines. * * To enable the tracing capability, the library must be * compiled with the #DEBUG_MEMALLOC preprocessor flag. Then, * you can selectively enable the tracing for each file or * project by using the SetDebugMemAlloc() routine. * * The following code shows an example: * * \include Alloc.c * * Then, a file like this will be written to stdout: * * \verbinclude mem_debug.dat * * This output is easy to understand. It tells you that * * -# in file \c Alloc.c, line 9, there were 16 bytes allocated at address 0x400031C0, * -# in file \c Alloc.c, line 10, address 0x400031C0 was verified, * -# in file \c Alloc.c, line 11, the memory block at address 0x400031C0 was freed, * -# in file \c Alloc.c, line 12, address 0x400031C0 was verified again. * * These files usually become very large if you work a lot with * dynamic memory allocation. So it would be rather hard to step * through that file on your own. For that reason, there's a Perl * script called \c check_alloc.pl that will take \c mem_debug.dat * as input and print all errors discovered and summary statistics: * * \verbinclude mem_debug.out * * As you can see, the last call to AssertValidPtr() caused an error * because the block that was checked has already been freed. The * other output is only useful if you have lots of dynamic memory * allocation, for example: * * \verbinclude memdb_large.out * * This will tell you that a total of 32404 memory blocks have been * successfully allocated and freed, a maximum of 13305 memory blocks * were in use simultanously, the peak memory usage was 183675 bytes, * the smallest and largest block that were allocated were 2 and 29 * bytes in size, respectively, and there were no memory leaks detected. * */ #ifndef _UTIL_MEMALLOC_H #define _UTIL_MEMALLOC_H #include #ifdef UTIL_HAVE_CONFIG_H # include "config.h" #endif #if !(defined(UTIL_MALLOC) && defined(UTIL_CALLOC) && defined(UTIL_REALLOC) && defined(UTIL_FREE)) # include # define UTIL_MALLOC(size) malloc(size) # define UTIL_CALLOC(count, size) calloc(count, size) # define UTIL_REALLOC(ptr, size) realloc(ptr, size) # define UTIL_FREE(ptr) free(ptr) #endif #define DB_MEMALLOC_TRACE 0x00000001 #define DB_MEMALLOC_ASSERT 0x00000002 #ifdef DEBUG_MEMALLOC void *_memAlloc( size_t size, const char *file, int line ); void *_memCAlloc( size_t nobj, size_t size, const char *file, int line ); void *_memReAlloc( void *p, size_t size, const char *file, int line ); void _memFree( void *p, const char *file, int line ); void _assertValidPtr( const void *p, const char *file, int line ); void _assertValidBlock( const void *p, size_t size, const char *file, int line ); int SetDebugMemAlloc( void (*dbfunc)(const char *, ...), unsigned long dbflags ); #else void *_memAlloc( size_t size ); void *_memCAlloc( size_t nobj, size_t size ); void *_memReAlloc( void *p, size_t size ); void _memFree( void *p ); #endif /***************************************************************/ /* DOCUMENTATION */ /***************************************************************/ #ifdef DOXYGEN /** * Make memory allocation routines abort when out of memory * * Set this preprocessor flag if you want the Alloc(), CAlloc() * and ReAlloc() functions as well as the fast macros AllocF(), * CAllocF() and ReAllocF() to abort if the system runs out of * memory. */ #define ABORT_IF_NO_MEM /** * Compile with debugging support */ #define DEBUG_MEMALLOC /** * Compile with tracing / leak detection support * * This may slow down memory allocation if lots of blocks * are simultaneously allocted. It will also increase the * memory requirements of your application. * * On the plus side, you get run-time memory allocation tracing, * assertion checking, leak detection and memory statistics. * You can control the amount of statistics by setting the * MEMALLOC_STAT_LEVEL environment variable to a value between * 0 and 3, with increasing amount of output. * * If an assertion fails, the program will usually abort. * You can choose not to abort the program by setting * MEMALLOC_SOFT_ASSERT to a non-zero value in your * environment. * * If you want the memory allocator to keep information about * freed blocks, set MEMALLOC_CHECK_FREED to a non-zero value. * This can give more detailed trace output at the cost of * slower execution. * * If you like to see hex dumps of non-freed memory blocks, * you can set MEMALLOC_SHOW_DUMPS to a non-zero value. * * Only works if DEBUG_MEMALLOC is also defined. */ #define TRACE_MEMALLOC /** * Build with memory allocator that automatically purges * allocated / freed memory blocks. * * Only works if DEBUG_MEMALLOC is also defined. */ #define AUTOPURGE_MEMALLOC /** * Build without support for the Alloc(), CAlloc() and * ReAlloc() functions. Memory management is completely * carried out through the use of the fast allocation * macros AllocF(), CAllocF() and ReAllocF(). */ #define NO_SLOW_MEMALLOC_CALLS /** * Allocate a memory block * * Allocates a memory block of \a size bytes. If the files * were compiled with the #ABORT_IF_NO_MEM preprocessor flag, * the function aborts if no memory can be allocated. * * \param size Size of the memory block in bytes. * * \return A pointer to the allocated memory block, or NULL * if memory couldn't be allocated. */ void *Alloc( size_t size ); /** * Allocate a memory block and initialize to zero * * Allocates a memory block to hold \a nobj times * \a size bytes. If the files were compiled with the * #ABORT_IF_NO_MEM preprocessor flag, the function * aborts if no memory can be allocated. * * \param nobj Number of objects. * * \param size Size of one object in bytes. * * \return A pointer to the allocated memory block, or NULL * if memory couldn't be allocated. */ void *CAlloc( size_t nobj, size_t size ); /** * Reallocate a memory block * * Reallocates a memory block of \a size bytes. If the files * were compiled with the #ABORT_IF_NO_MEM preprocessor flag, * the function aborts if no memory can be allocated. * * \param ptr Pointer to an allocated memory block. * * \param size Size of new memory block in bytes. * * \return A pointer to the reallocated memory block, or NULL * if memory couldn't be reallocated. */ void *ReAlloc( void *ptr, size_t size ); /** * Fast Alloc Macro * * Allocates a memory block of \a size bytes. If the files * were compiled with the #ABORT_IF_NO_MEM preprocessor flag, * the function aborts if no memory can be allocated. * * \param cast Pointer cast. * * \param ptr Pointer to memory block. * * \param size Size of the memory block in bytes. */ #define AllocF( cast, ptr, size ) /** * Fast CAlloc Macro * * Allocates a memory block to hold \a nobj times * \a size bytes. If the files were compiled with the * #ABORT_IF_NO_MEM preprocessor flag, the function * aborts if no memory can be allocated. * * \param cast Pointer cast. * * \param ptr Pointer to memory block. * * \param nobj Number of objects. * * \param size Size of one object in bytes. */ #define CAllocF( cast, ptr, nobj, size ) /** * Fast ReAlloc Macro * * Reallocates a memory block of \a size bytes. If the files * were compiled with the #ABORT_IF_NO_MEM preprocessor flag, * the function aborts if no memory can be allocated. * * \param cast Pointer cast. * * \param ptr Pointer to memory block. * * \param size Size of new memory block in bytes. */ #define ReAllocF( cast, ptr, size ) /** * Free a memory block * * Frees a memory block that has been previously allocated * using the Alloc() function. * * \param ptr Pointer to a previously allocated * memory block. */ void Free( void *ptr ); /** * Trace pointer access. * * This may prove useful for checking if \a ptr points to * an existing, previously allocated, not yet freed memory * block. * * \param ptr Pointer to be traced. */ void AssertValidPtr( void *ptr ); /** * Trace memory block access. * * Allows checking if a certain memory block lies within * a previously allocated memory block. * * \param ptr Pointer to memory block. * * \param size Size of memory block. */ void AssertValidBlock( void *ptr, size_t size ); /** * Configure debugging support. * * \param dbfunc Pointer to a printf() like function * for writing the debug output. * * \param dbflags Binary ORed debugging flags. Currently, * you can request memory allocation tracing * with \c DB_MEMALLOC_TRACE and pointer * assertions with \c DB_MEMALLOC_ASSERT. */ int SetDebugMemAlloc( void (*dbfunc)(char *, ...), unsigned long dbflags ); #else /* !DOXYGEN */ /***************************************************************/ /* END OF DOCUMENTATION */ /***************************************************************/ #ifdef ABORT_IF_NO_MEM # define abortMEMALLOC( call, size, expr ) \ do { \ if( (expr) == NULL && size > 0 ) { \ fprintf(stderr, "%s(%d): out of memory!\n", call, size); \ abort(); \ } \ } while(0) #else # define abortMEMALLOC( call, size, expr ) do { (void) (expr); } while(0) #endif #ifdef DEBUG_MEMALLOC # ifndef NO_SLOW_MEMALLOC_CALLS # define ReAlloc( ptr, size ) _memReAlloc( ptr, size, __FILE__, __LINE__ ) # define CAlloc( nobj, size ) _memCAlloc( nobj, size, __FILE__, __LINE__ ) # define Alloc( size ) _memAlloc( size, __FILE__, __LINE__ ) # endif # define Free( ptr ) _memFree( ptr, __FILE__, __LINE__ ) # define AssertValidPtr( ptr ) _assertValidPtr( ptr, __FILE__, __LINE__ ) # define AssertValidBlock( ptr, size ) _assertValidBlock( ptr, size, __FILE__, __LINE__ ) # define ReAllocF( cast, ptr, size ) \ do { ptr = (cast) _memReAlloc( ptr, size, __FILE__, __LINE__ ); } while(0) # define CAllocF( cast, ptr, nobj, size ) \ do { ptr = (cast) _memCAlloc( nobj, size, __FILE__, __LINE__ ); } while(0) # define AllocF( cast, ptr, size ) \ do { ptr = (cast) _memAlloc( size, __FILE__, __LINE__ ); } while(0) #else /* !DEBUG_MEMALLOC */ # ifndef NO_SLOW_MEMALLOC_CALLS # ifdef ABORT_IF_NO_MEM # define ReAlloc( ptr, size ) _memReAlloc( ptr, size ) # define CAlloc( nobj, size ) _memCAlloc( nobj, size ) # define Alloc( size ) _memAlloc( size ) # else # define ReAlloc( ptr, size ) UTIL_REALLOC( ptr, size ) # define CAlloc( nobj, size ) UTIL_CALLOC( nobj, size ) # define Alloc( size ) UTIL_MALLOC( size ) # endif # endif # define Free( ptr ) do { if( ptr ) UTIL_FREE( ptr ); } while(0) # define AssertValidPtr( ptr ) (void) 0 # define AssertValidBlock( ptr, size ) (void) 0 # define SetDebugMemAlloc( func, flags ) 0 # define ReAllocF( cast, ptr, size ) \ abortMEMALLOC( "ReAllocF", size, ptr = (cast) UTIL_REALLOC( ptr, size ) ) # define CAllocF( cast, ptr, nobj, size ) \ abortMEMALLOC( "CAllocF", nobj*size, ptr = (cast) UTIL_CALLOC( nobj, size ) ) # define AllocF( cast, ptr, size ) \ abortMEMALLOC( "AllocF", size, ptr = (cast) UTIL_MALLOC( size ) ) #endif /* DEBUG_MEMALLOC */ #endif /* DOXYGEN */ #endif