/* (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) (c) Copyright 2000-2004 Convergence (integrated media) GmbH All rights reserved. Written by Denis Oliver Kropp , Andreas Hundt , Sven Neumann , Ville Syrjälä and Claudio Ciccani . This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #if DIRECT_BUILD_DEBUGS /* Build with debug support? */ #include #include #include #include #include #include #include #include #include #include #include D_DEBUG_DOMAIN( Direct_Mem, "Direct/Mem", "libdirect memory allocation (debugging)" ); /**********************************************************************************************************************/ typedef struct { const void *mem; size_t bytes; const char *func; const char *file; int line; DirectTraceBuffer *trace; } MemDesc; /**********************************************************************************************************************/ static int alloc_count = 0; static int alloc_capacity = 0; static MemDesc *alloc_list = NULL; static pthread_mutex_t alloc_lock = PTHREAD_MUTEX_INITIALIZER; /**********************************************************************************************************************/ void direct_print_memleaks( void ) { unsigned int i; /* Debug only. */ pthread_mutex_lock( &alloc_lock ); if (alloc_count && (!direct_config || direct_config->debugmem)) { direct_log_printf( NULL, "Local memory allocations remaining (%d): \n", alloc_count ); for (i=0; ibytes, desc->mem, desc->func, desc->file, desc->line ); if (desc->trace) direct_trace_print_stack( desc->trace ); } } pthread_mutex_unlock( &alloc_lock ); } /**********************************************************************************************************************/ /* FIXME: Replace array by linked list, or at least avoid the memmove on free of an item. */ static MemDesc * allocate_mem_desc( void ) { int cap = alloc_capacity; if (!cap) cap = 64; else if (cap == alloc_count) cap <<= 1; if (cap != alloc_capacity) { void *new_list = malloc( sizeof(MemDesc) * cap ); if (!new_list) { D_WARN( "could not allocate more descriptors (%d->%d)", alloc_capacity, cap ); return NULL; } direct_memcpy( new_list, alloc_list, sizeof(MemDesc) * alloc_count ); free( alloc_list ); alloc_capacity = cap; alloc_list = new_list; } return &alloc_list[alloc_count++]; } static inline void fill_mem_desc( MemDesc *desc, const void *mem, int bytes, const char *func, const char *file, int line, DirectTraceBuffer *trace ) { desc->mem = mem; desc->bytes = bytes; desc->func = func; desc->file = file; desc->line = line; desc->trace = trace; } /**********************************************************************************************************************/ void * direct_malloc( const char* file, int line, const char *func, size_t bytes ) { void *mem; MemDesc *desc; D_DEBUG_AT( Direct_Mem, " +%6zu bytes [%s:%d in %s()]\n", bytes, file, line, func ); mem = malloc( bytes ); if (!mem) return NULL; if (!direct_config->debugmem) return mem; /* Debug only. */ pthread_mutex_lock( &alloc_lock ); desc = allocate_mem_desc(); pthread_mutex_unlock( &alloc_lock ); if (desc) fill_mem_desc( desc, mem, bytes, func, file, line, direct_trace_copy_buffer(NULL) ); return mem; } void * direct_calloc( const char* file, int line, const char *func, size_t count, size_t bytes ) { void *mem; MemDesc *desc; D_DEBUG_AT( Direct_Mem, " +%6zu bytes [%s:%d in %s()]\n", count * bytes, file, line, func ); mem = calloc( count, bytes ); if (!mem) return NULL; if (!direct_config->debugmem) return mem; /* Debug only. */ pthread_mutex_lock( &alloc_lock ); desc = allocate_mem_desc(); pthread_mutex_unlock( &alloc_lock ); if (desc) fill_mem_desc( desc, mem, count * bytes, func, file, line, direct_trace_copy_buffer(NULL) ); return mem; } void * direct_realloc( const char *file, int line, const char *func, const char *what, void *mem, size_t bytes ) { int i; if (!mem) return direct_malloc( file, line, func, bytes ); if (!bytes) { direct_free( file, line, func, what, mem ); return NULL; } if (!direct_config->debugmem) { D_DEBUG_AT( Direct_Mem, " *%6zu bytes [%s:%d in %s()] '%s'\n", bytes, file, line, func, what ); return realloc( mem, bytes ); } /* Debug only. */ pthread_mutex_lock( &alloc_lock ); for (i=0; imem == mem) { void *new_mem = realloc( mem, bytes ); D_DEBUG_AT( Direct_Mem, " %c%6zu bytes [%s:%d in %s()] (%s%zu) <- %p -> %p '%s'\n", (bytes > desc->bytes) ? '>' : '<', bytes, file, line, func, (bytes > desc->bytes) ? "+" : "", bytes - desc->bytes, mem, new_mem, what); if (desc->trace) { direct_trace_free_buffer( desc->trace ); desc->trace = NULL; } if (!new_mem) { D_WARN( "could not reallocate memory (%p: %zu->%zu)", mem, desc->bytes, bytes ); alloc_count--; /* FIXME: This can be very slow. */ if (i < alloc_count) direct_memmove( desc, desc + 1, (alloc_count - i) * sizeof(MemDesc) ); } else fill_mem_desc( desc, new_mem, bytes, func, file, line, direct_trace_copy_buffer(NULL) ); pthread_mutex_unlock( &alloc_lock ); return new_mem; } } pthread_mutex_unlock( &alloc_lock ); D_ERROR( "Direct/Mem: Not reallocating unknown %p (%s) from [%s:%d in %s()] - corrupt/incomplete list?\n", mem, what, file, line, func ); return direct_malloc( file, line, func, bytes ); } void direct_free( const char *file, int line, const char *func, const char *what, void *mem ) { unsigned int i; if (!mem) { D_WARN( "%s (NULL) called", __FUNCTION__ ); return; } if (!direct_config->debugmem) { D_DEBUG_AT( Direct_Mem, " - number of bytes of %s [%s:%d in %s()] -> %p\n", what, file, line, func, mem ); free( mem ); return; } /* Debug only. */ pthread_mutex_lock( &alloc_lock ); for (i=0; imem == mem) { free( mem ); D_DEBUG_AT( Direct_Mem, " -%6zu bytes [%s:%d in %s()] -> %p '%s'\n", desc->bytes, file, line, func, mem, what ); if (desc->trace) direct_trace_free_buffer( desc->trace ); alloc_count--; /* FIXME: This can be very slow. */ if (i < alloc_count) direct_memmove( desc, desc + 1, (alloc_count - i) * sizeof(MemDesc) ); pthread_mutex_unlock( &alloc_lock ); return; } } pthread_mutex_unlock( &alloc_lock ); D_ERROR( "Direct/Mem: Not freeing unknown %p (%s) from [%s:%d in %s()] - corrupt/incomplete list?\n", mem, what, file, line, func ); } char * direct_strdup( const char* file, int line, const char *func, const char *string ) { void *mem; MemDesc *desc; size_t length = strlen( string ) + 1; mem = malloc( length ); D_DEBUG_AT( Direct_Mem, " +%6zu bytes [%s:%d in %s()] -> %p \"%30s\"\n", length, file, line, func, mem, string ); if (!mem) return NULL; direct_memcpy( mem, string, length ); if (!direct_config->debugmem) return mem; /* Debug only. */ pthread_mutex_lock( &alloc_lock ); desc = allocate_mem_desc(); pthread_mutex_unlock( &alloc_lock ); if (desc) fill_mem_desc( desc, mem, length, func, file, line, direct_trace_copy_buffer(NULL) ); return mem; } /**********************************************************************************************************************/ #else void direct_print_memleaks( void ) { } #endif