/* * $Id: stdlib_free.c,v 1.13 2006-01-08 12:04:25 obarthel Exp $ * * :ts=4 * * Portable ISO 'C' (1994) runtime library for the Amiga computer * Copyright (c) 2002-2015 by Olaf Barthel * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Neither the name of Olaf Barthel nor the names of 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. */ #ifndef _STDLIB_HEADERS_H #include "stdlib_headers.h" #endif /* _STDLIB_HEADERS_H */ /****************************************************************************/ #ifndef _STDLIB_MEMORY_H #include "stdlib_memory.h" #endif /* _STDLIB_MEMORY_H */ /****************************************************************************/ #undef free #undef __free /****************************************************************************/ #ifdef __MEM_DEBUG /****************************************************************************/ STATIC char get_hex_char(int n) { char result; if(0 <= n && n <= 9) result = n + '0'; else result = n + 'A' - 10; return(result); } STATIC VOID int_to_hex(unsigned long v,char * buffer,int min_digits) { int i,j,c; i = 0; do { c = (v % 16); v = v / 16; buffer[i++] = get_hex_char(c); } while(v > 0); while(i < min_digits) buffer[i++] = '0'; for(j = 0 ; j < i / 2 ; j++) { c = buffer[i - 1 - j]; buffer[i - 1 - j] = buffer[j]; buffer[j] = c; } buffer[i] = '\0'; } STATIC VOID dump_memory(unsigned char * m,int size,int ignore) { const int mod = 20; int position,i,c; char buffer[120]; char hex[10]; buffer[0] = '\0'; for(i = 0 ; i < size ; i++) { position = i % mod; if(position == 0) { if(buffer[0] != '\0') { int len = sizeof(buffer)-1; while(len > 0 && buffer[len-1] == ' ') len--; buffer[len] = '\0'; kprintf("[%s] %s\n",__program_name,buffer); } memset(buffer,' ',sizeof(buffer)-1); int_to_hex((unsigned long)&m[i],hex,8); memmove(buffer,hex,8); hex[9] = ':'; } if(m[i] != ignore) { buffer[10 + 2 * position + 0] = get_hex_char(m[i] >> 4); buffer[10 + 2 * position + 1] = get_hex_char(m[i] & 15); c = m[i]; if(c < ' ' || (c >= 127 && c <= 160)) c = '.'; buffer[10 + 2 * mod + 1 + position] = c; } else { buffer[10 + 2 * position + 0] = '_'; buffer[10 + 2 * position + 1] = '_'; } } if(buffer[0] != '\0') { int len = sizeof(buffer)-1; while(len > 0 && buffer[len-1] == ' ') len--; buffer[len] = '\0'; kprintf("[%s] %s\n",__program_name,buffer); } } /****************************************************************************/ STATIC VOID check_memory_node(struct MemoryNode * mn,const char * file,int line) { size_t size = mn->mn_Size; unsigned char * head = (unsigned char *)(mn + 1); unsigned char * body = head + MALLOC_HEAD_SIZE; unsigned char * tail = body + size; int max_head_damage = 0; int max_tail_damage = 0; int max_body_damage = 0; int i; for(i = 1 ; i <= MALLOC_HEAD_SIZE ; i++) { if(head[MALLOC_HEAD_SIZE - i] != MALLOC_HEAD_FILL) max_head_damage = i; } if(max_head_damage > 0) { kprintf("[%s] ",__program_name); if(file != NULL) kprintf("%s:%ld:",file,line); kprintf("At least %ld bytes were damaged in front of allocation 0x%08lx..0x%08lx, size = %ld", max_head_damage, body,body + size - 1,size); if(mn->mn_File != NULL) kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); kprintf("\n"); dump_memory(head,MALLOC_HEAD_SIZE,MALLOC_HEAD_FILL); } for(i = 0 ; i < MALLOC_TAIL_SIZE ; i++) { if(tail[i] != MALLOC_TAIL_FILL) max_tail_damage = i+1; } if(max_tail_damage > 0) { kprintf("[%s] ",__program_name); if(file != NULL) kprintf("%s:%ld:",file,line); kprintf("At least %ld bytes were damaged behind allocation 0x%08lx..0x%08lx, size = %ld (with damage = %ld)", max_tail_damage, body,body + size - 1, size,size+max_tail_damage); if(mn->mn_File != NULL) kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); kprintf("\n"); dump_memory(tail,MALLOC_TAIL_SIZE,MALLOC_TAIL_FILL); } if(mn->mn_AlreadyFree) { for(i = 0 ; i < size ; i++) { if(body[i] != MALLOC_FREE_FILL) max_body_damage = i+1; } if(max_body_damage > 0) { kprintf("[%s] ",__program_name); if(file != NULL) kprintf("%s:%ld:",file,line); kprintf("At least %ld bytes were damaged in freed allocation 0x%08lx..0x%08lx, size = %ld", max_body_damage, body,body + size - 1,size); if(mn->mn_File != NULL) kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); kprintf("\n"); dump_memory(body,size,MALLOC_FREE_FILL); } } } /****************************************************************************/ void __check_memory_allocations(const char * file,int line) { struct MemoryNode * mn; __memory_lock(); for(mn = (struct MemoryNode *)__memory_list.mlh_Head ; mn->mn_MinNode.mln_Succ != NULL ; mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ) { check_memory_node(mn,file,line); } __memory_unlock(); } /****************************************************************************/ struct MemoryNode * __find_memory_node(void * address) { struct MemoryNode * result; assert( address != NULL ); __memory_lock(); #if defined(__USE_MEM_TREES) { result = __red_black_tree_find(&__memory_tree,address); } #else { struct MemoryNode * mn; result = NULL; for(mn = (struct MemoryNode *)__memory_list.mlh_Head ; mn->mn_MinNode.mln_Succ != NULL ; mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ) { if(address == mn->mn_Allocation) { result = mn; break; } } } #endif /* __USE_MEM_TREES */ __memory_unlock(); return(result); } /****************************************************************************/ #else /****************************************************************************/ struct MemoryNode * __find_memory_node(void * address) { struct MemoryNode * result; assert( address != NULL ); result = &((struct MemoryNode *)address)[-1]; return(result); } /****************************************************************************/ #endif /* __MEM_DEBUG */ /****************************************************************************/ STATIC VOID remove_and_free_memory_node(struct MemoryNode * mn) { size_t allocation_size; assert( mn != NULL ); __memory_lock(); Remove((struct Node *)mn); #if defined(__USE_MEM_TREES) && defined(__MEM_DEBUG) { __red_black_tree_remove(&__memory_tree,mn); } #endif /* __USE_MEM_TREES && __MEM_DEBUG */ #ifdef __MEM_DEBUG { allocation_size = sizeof(*mn) + MALLOC_HEAD_SIZE + mn->mn_Size + MALLOC_TAIL_SIZE; assert( allocation_size == mn->mn_AllocationSize ); memset(mn,MALLOC_FREE_FILL,allocation_size); } #else { allocation_size = sizeof(*mn) + mn->mn_Size; } #endif /* __MEM_DEBUG */ /* Are we using the slab allocator? */ if (__slab_data.sd_InUse) { assert( __slab_data.sd_MaxSlabSize > 0 ); /* Number of bytes to allocate exceeds the slab size? * Then the chunk was allocated separately. */ if(allocation_size > __slab_data.sd_MaxSlabSize) { Remove((struct Node *)&mn[-1]); __slab_data.sd_NumSingleAllocations--; } /* Otherwise the allocation should have come from a slab. */ else { struct MinList * slab_list = NULL; size_t entry_size; ULONG chunk_size; int slab_index; /* Chunks must be at least as small as a MinNode, because * that's what we use for keeping track of the chunks which * are available for allocation within each slab. */ entry_size = allocation_size; if(entry_size < sizeof(struct MinNode)) entry_size = sizeof(struct MinNode); /* Find a slab which keeps track of chunks that are no * larger than the amount of memory which needs to be * released. We end up picking the smallest chunk * size that still works. */ for(slab_index = 0, chunk_size = (1UL << slab_index) ; slab_index < 31 ; slab_index++, chunk_size += chunk_size) { assert( (chunk_size % sizeof(LONG)) == 0); if(entry_size <= chunk_size) { slab_list = &__slab_data.sd_Slabs[slab_index]; break; } } /* Find the slab which contains the memory chunk. */ if(slab_list != NULL) { struct SlabNode * sn; BYTE * first_byte; BYTE * last_byte; for(sn = (struct SlabNode *)slab_list->mlh_Head ; sn->sn_MinNode.mln_Succ != NULL ; sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ) { assert( sn->sn_ChunkSize == chunk_size ); first_byte = (BYTE *)&sn[1]; last_byte = &first_byte[__slab_data.sd_MaxSlabSize - chunk_size]; /* Is this where the chunk belongs? */ if(first_byte <= (BYTE *)mn && (BYTE *)mn <= last_byte) { AddTail((struct List *)&sn->sn_FreeList, (struct Node *)mn); assert( sn->sn_UseCount > 0 ); sn->sn_UseCount--; /* If this slab is empty, mark it as unused and * allow it to be purged. */ if(sn->sn_UseCount == 0) { AddTail((struct List *)&__slab_data.sd_EmptySlabs,(struct Node *)&sn->sn_EmptyLink); sn->sn_EmptyDecay = 1; } /* This slab now has room. Move it to front of the list * so that searching for a free chunk will pick it * first. */ if(sn != (struct SlabNode *)slab_list->mlh_Head) { Remove((struct Node *)sn); AddHead((struct List *)slab_list, (struct Node *)sn); } break; } } } } } else if (__memory_pool != NULL) { FreePooled(__memory_pool,mn,allocation_size); } else { FreeMem(mn,allocation_size); } __current_memory_allocated -= allocation_size; __current_num_memory_chunks_allocated--; __memory_unlock(); } /****************************************************************************/ void __free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED line) { assert(mn != NULL); #ifdef __MEM_DEBUG { size_t size = mn->mn_Size; check_memory_node(mn,file,line); if(NOT mn->mn_AlreadyFree) { #ifdef __MEM_DEBUG_LOG { kprintf("[%s] - %10ld 0x%08lx [",__program_name,mn->mn_Size,mn->mn_Allocation); if(mn->mn_File != NULL) kprintf("allocated at %s:%ld, ",mn->mn_File,mn->mn_Line); kprintf("freed at %s:%ld]\n",file,line); } #endif /* __MEM_DEBUG_LOG */ if(__never_free) { mn->mn_AlreadyFree = TRUE; mn->mn_FreeFile = (char *)file; mn->mn_FreeLine = line; memset(mn->mn_Allocation,MALLOC_FREE_FILL,size); } else { remove_and_free_memory_node(mn); } } else { #ifdef __MEM_DEBUG_LOG { kprintf("[%s] - %10ld 0x%08lx [",__program_name,mn->mn_Size,mn->mn_Allocation); kprintf("FAILED]\n"); } #endif /* __MEM_DEBUG_LOG */ kprintf("[%s] %s:%ld:Allocation at address 0x%08lx, size %ld", __program_name,file,line,mn->mn_Allocation,mn->mn_Size); if(mn->mn_File != NULL) kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); kprintf(", has already been freed at %s:%ld.\n",mn->mn_FreeFile,mn->mn_FreeLine); } } #else { remove_and_free_memory_node(mn); } #endif /* __MEM_DEBUG */ } /****************************************************************************/ VOID __free_memory(void * ptr,BOOL force,const char * file,int line) { struct MemoryNode * mn; assert(ptr != NULL); #ifdef __MEM_DEBUG { /*if((rand() % 16) == 0) __check_memory_allocations(file,line);*/ } #endif /* __MEM_DEBUG */ mn = __find_memory_node(ptr); #ifdef __MEM_DEBUG { if(mn != NULL) { if(force || (NOT mn->mn_NeverFree)) __free_memory_node(mn,file,line); } else { #ifdef __MEM_DEBUG_LOG { kprintf("[%s] - %10ld 0x%08lx [",__program_name,0,ptr); kprintf("FAILED]\n"); } #endif /* __MEM_DEBUG_LOG */ kprintf("[%s] %s:%ld:Address for free(0x%08lx) not known.\n",__program_name,file,line,ptr); D(("memory allocation at 0x%08lx could not be freed",ptr)); } } #else { assert( mn != NULL ); if(mn != NULL && (force || (NOT mn->mn_NeverFree))) __free_memory_node(mn,file,line); } #endif /* __MEM_DEBUG */ } /****************************************************************************/ __static void __free(void * ptr,const char * file,int line) { __memory_lock(); /* Try to get rid of now unused memory. */ if(__alloca_cleanup != NULL) (*__alloca_cleanup)(file,line); __memory_unlock(); if(ptr != NULL) __free_memory(ptr,FALSE,file,line); } /****************************************************************************/ void free(void * ptr) { __free(ptr,NULL,0); }