/* src/mm/dumpmemory.c - dump memory management Copyright (C) 1996-2005, 2006, 2007, 2008 CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO This file is part of CACAO. This program 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, or (at your option) any later version. 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. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include "mm/dumpmemory.h" #include "mm/memory.h" #include "threads/thread.h" #include "vmcore/options.h" #if defined(ENABLE_STATISTICS) # include "vmcore/statistics.h" #endif #include "vmcore/system.h" #include "vm/vm.hpp" /******************************************************************************* This structure is used for dump memory allocation if cacao runs without threads. *******************************************************************************/ #if !defined(ENABLE_THREADS) static dumpinfo_t _no_threads_dumpinfo; #endif #if defined(ENABLE_THREADS) #define DUMPINFO &((threadobject *) THREADOBJECT)->dumpinfo #else #define DUMPINFO &_no_threads_dumpinfo #endif /* dump_check_canaries ********************************************************* Check canaries in dump memory. IN: di...........dumpinfo_t * of the dump area to check bottomsize...dump size down to which the dump area should be checked (specify 0 to check the whole dump area) ERROR HANDLING: If any canary has been changed, this function aborts the VM with an error message. *******************************************************************************/ #if defined(ENABLE_MEMCHECK) static void dump_check_canaries(dumpinfo_t *di, s4 bottomsize) { dump_allocation_t *da; uint8_t *pm; int i, j; /* iterate over all dump memory allocations above bottomsize */ da = di->allocations; while (da && da->used >= bottomsize) { /* check canaries */ pm = ((uint8_t *) da->mem) - MEMORY_CANARY_SIZE; for (i = 0; i < MEMORY_CANARY_SIZE; ++i) { if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) { fprintf(stderr, "canary bytes:"); for (j = 0; j < MEMORY_CANARY_SIZE; ++j) fprintf(stderr, " %02x", pm[j]); fprintf(stderr,"\n"); vm_abort("error: dump memory bottom canary killed: " "%p (%d bytes allocated at %p)\n", pm + i, da->size, da->mem); } } pm = ((uint8_t *) da->mem) + da->size; for (i = 0; i < MEMORY_CANARY_SIZE; ++i) { if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) { fprintf(stderr, "canary bytes:"); for (j = 0; j < MEMORY_CANARY_SIZE; ++j) fprintf(stderr, " %02x", pm[j]); fprintf(stderr, "\n"); vm_abort("error: dump memory top canary killed: " "%p (%d bytes allocated at %p)\n", pm + i, da->size, da->mem); } } da = da->next; } } #endif /* defined(ENABLE_MEMCHECK) */ /* dumpmemory_alloc ************************************************************ ATTENTION: This function must only be called from dumpmemory_get! Allocate a new dump memory block. IN: di ..... dumpinfo_t of the current thread size ... required memory size *******************************************************************************/ void dumpmemory_alloc(dumpinfo_t *di, size_t size) { dumpblock_t *db; size_t newblocksize; /* Allocate a new dumpblock_t structure. */ db = memory_checked_alloc(sizeof(dumpblock_t)); /* If requested size is greater than the default, make the new dump block as big as the size requested. Else use the default size. */ if (size > DUMPBLOCKSIZE) { newblocksize = size; } else { newblocksize = DUMPBLOCKSIZE; } /* allocate dumpblock memory */ db->dumpmem = memory_checked_alloc(newblocksize); db->size = newblocksize; db->prev = di->block; di->block = db; /* Used dump size is previously allocated dump size, because the remaining free memory of the previous dump block cannot be used. */ di->used = di->allocated; /* Increase the allocated dump size by the size of the new dump block. */ di->allocated += newblocksize; #if defined(ENABLE_STATISTICS) /* The amount of globally allocated dump memory (thread save). */ if (opt_stat) globalallocateddumpsize += newblocksize; #endif } /* dumpmemory_get ************************************************************** Allocate memory in the dump area. IN: size.........size of block to allocate, in bytes may be zero, in which case NULL is returned RETURN VALUE: pointer to allocated memory, or NULL iff `size` was zero ERROR HANDLING: XXX This function uses `memory_checked_alloc`, which *exits* if no memory could be allocated. THREADS: dumpmemory_get is thread safe. Each thread has its own dump memory area. This function is a fast allocator suitable for scratch memory that can be collectively freed when the current activity (eg. compiling) is done. You cannot selectively free dump memory. Before you start allocating it, you remember the current size returned by `dumpmemory_marker`. Later, when you no longer need the memory, call `dumpmemory_release` with the remembered size and all dump memory allocated since the call to `dumpmemory_marker` will be freed. *******************************************************************************/ void *dumpmemory_get(size_t size) { #if defined(DISABLE_DUMP) /* use malloc memory for dump memory (for debugging only!) */ return mem_alloc(size); #else /* !defined(DISABLE_DUMP) */ void *p; dumpinfo_t *di; #if defined(ENABLE_MEMCHECK) s4 origsize = size; /* needed for the canary system */ #endif di = DUMPINFO; if (size == 0) return NULL; #if defined(ENABLE_MEMCHECK) size += 2 * MEMORY_CANARY_SIZE; #endif size = MEMORY_ALIGN(size, ALIGNSIZE); /* Check if we have enough memory in the current memory block. */ if (di->used + size > di->allocated) { /* If not, allocate a new one. */ dumpmemory_alloc(di, size); } /* current dump block base address + the size of the current dump block - the size of the unused memory = new start address */ p = ((uint8_t *) di->block->dumpmem) + di->block->size - (di->allocated - di->used); #if defined(ENABLE_MEMCHECK) { dump_allocation_t *da = NEW(dump_allocation_t); uint8_t *pm; int i; /* add the allocation to our linked list of allocations */ da->next = di->allocations; da->mem = ((uint8_t *) p) + MEMORY_CANARY_SIZE; da->size = origsize; da->used = di->used; di->allocations = da; /* write the canaries */ pm = (uint8_t *) p; for (i = 0; i < MEMORY_CANARY_SIZE; ++i) pm[i] = i + MEMORY_CANARY_FIRST_BYTE; pm = ((uint8_t *) da->mem) + da->size; for (i = 0; i < MEMORY_CANARY_SIZE; ++i) pm[i] = i + MEMORY_CANARY_FIRST_BYTE; /* make m point after the bottom canary */ p = ((uint8_t *) p) + MEMORY_CANARY_SIZE; /* clear the memory */ (void) system_memset(p, MEMORY_CLEAR_BYTE, da->size); } #endif /* defined(ENABLE_MEMCHECK) */ /* Increase used dump size by the allocated memory size. */ di->used += size; #if defined(ENABLE_STATISTICS) if (opt_stat) if (di->used > maxdumpsize) maxdumpsize = di->used; #endif return p; #endif /* defined(DISABLE_DUMP) */ } /* dumpmemory_realloc ********************************************************** Stupid realloc implementation for dump memory. Avoid, if possible. *******************************************************************************/ void *dumpmemory_realloc(void *src, s4 len1, s4 len2) { #if defined(DISABLE_DUMP) /* use malloc memory for dump memory (for debugging only!) */ return mem_realloc(src, len1, len2); #else void *dst; dst = dumpmemory_get(len2); (void) system_memcpy(dst, src, len1); #if defined(ENABLE_MEMCHECK) /* destroy the source */ (void) system_memset(src, MEMORY_CLEAR_BYTE, len1); #endif return dst; #endif } /* dumpmemory_release ********************************************************** Release dump memory above the given size. IN: size........All dump memory above this mark will be freed. Usually `size` will be the return value of a `dumpmemory_marker` call made earlier. ERROR HANDLING: XXX If the given size is invalid, this function *exits* with an error message. See `dump_alloc`. *******************************************************************************/ void dumpmemory_release(s4 size) { #if defined(DISABLE_DUMP) /* use malloc memory for dump memory (for debugging only!) */ /* do nothing */ #else /* !defined(DISABLE_DUMP) */ dumpinfo_t *di; di = DUMPINFO; if ((size < 0) || (size > di->used)) vm_abort("dump_release: Illegal dump release size: %d", size); #if defined(ENABLE_MEMCHECK) { dump_allocation_t *da, *next; /* check canaries */ dump_check_canaries(di, size); /* iterate over all dump memory allocations about to be released */ da = di->allocations; while ((da != NULL) && (da->used >= size)) { next = da->next; /* invalidate the freed memory */ (void) system_memset(da->mem, MEMORY_CLEAR_BYTE, da->size); FREE(da, dump_allocation_t); da = next; } di->allocations = da; } #endif /* defined(ENABLE_MEMCHECK) */ /* Reset the used dump size to the size specified. */ di->used = size; while ((di->block != NULL) && di->allocated - di->block->size >= di->used) { dumpblock_t *tmp = di->block; di->allocated -= tmp->size; di->block = tmp->prev; #if defined(ENABLE_STATISTICS) /* the amount of globally allocated dump memory (thread save) */ if (opt_stat) globalallocateddumpsize -= tmp->size; #endif /* Release the dump memory and the dumpinfo structure. */ system_free(tmp->dumpmem); system_free(tmp); } #endif /* defined(DISABLE_DUMP) */ } /* dumpmemory_marker *********************************************************** Returns a marker of the dump memory area. This marker is actually the used size of the dump memory area. RETURN VALUE: marker of the current dump memory status *******************************************************************************/ int32_t dumpmemory_marker(void) { #if defined(DISABLE_DUMP) /* use malloc memory for dump memory (for debugging only!) */ return 0; #else /* !defined(DISABLE_DUMP) */ dumpinfo_t *di; di = DUMPINFO; if (di == NULL) return 0; return di->used; #endif /* defined(DISABLE_DUMP) */ } /* * These are local overrides for various environment variables in Emacs. * Please do not remove this and leave it at the end of the file, where * Emacs will automagically detect them. * --------------------------------------------------------------------- * Local variables: * mode: c * indent-tabs-mode: t * c-basic-offset: 4 * tab-width: 4 * End: * vim:noexpandtab:sw=4:ts=4: */