X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fmm%2Fmemory.c;h=9c3034491c051994ef2c9aeaf20061b81fb5308d;hb=9f859ad50d3d5d98c185d40b86b2179bc4dc9aeb;hp=a873550a081199446365e9f6d16cd6327dab803b;hpb=f5742f84de48793d979858ae4b02afc974f4fb0e;p=cacao.git diff --git a/src/mm/memory.c b/src/mm/memory.c index a873550a0..9c3034491 100644 --- a/src/mm/memory.c +++ b/src/mm/memory.c @@ -1,9 +1,9 @@ -/* src/mm/memory.c - +/* src/mm/memory.c - memory management - Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates, - R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner, - C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger, - Institut f. Computersprachen - TU Wien + Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien This file is part of CACAO. @@ -19,20 +19,14 @@ 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., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. - - Contact: cacao@complang.tuwien.ac.at - - Authors: Reinhard Grafl - - Changes: Christian Thalinger - - $Id: memory.c 3552 2005-11-03 20:43:17Z twisti $ + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. */ +#include "config.h" + #include #include #include @@ -42,47 +36,57 @@ #include #if defined(__DARWIN__) -/* If we compile with -ansi on darwin, is not included. So */ -/* let's do it here. */ +/* If we compile with -ansi on darwin, is not + included. So let's do it here. */ # include #endif -#include "config.h" #include "vm/types.h" #include "arch.h" #include "mm/memory.h" + #include "native/native.h" -#if defined(USE_THREADS) -# if defined(NATIVE_THREADS) -# include "threads/native/threads.h" -# else -# include "threads/green/threads.h" -# endif -#endif +#include "threads/lock-common.h" +#include "threads/threads-common.h" #include "toolbox/logging.h" + #include "vm/exceptions.h" #include "vm/global.h" -#include "vm/options.h" -#include "vm/statistics.h" #include "vm/stringlocal.h" +#include "vm/vm.h" + +#include "vmcore/options.h" + +#if defined(ENABLE_STATISTICS) +# include "vmcore/statistics.h" +#endif + + +/* constants for ENABLE_MEMCHECK **********************************************/ + +#if defined(ENABLE_MEMCHECK) +#define MEMORY_CANARY_SIZE 16 +#define MEMORY_CANARY_FIRST_BYTE 0xca +#define MEMORY_CLEAR_BYTE 0xa5 +#endif /* defined(ENABLE_MEMCHECK) */ /******************************************************************************* This structure is used for dump memory allocation if cacao - runswithout threads. + runs without threads. *******************************************************************************/ -#if !defined(USE_THREADS) || (defined(USE_THREADS) && !defined(NATIVE_THREADS)) -static dumpinfo _no_threads_dumpinfo; +#if !defined(ENABLE_THREADS) +static dumpinfo_t _no_threads_dumpinfo; #endif -#if defined(USE_THREADS) && defined(NATIVE_THREADS) +#if defined(ENABLE_THREADS) #define DUMPINFO &((threadobject *) THREADOBJECT)->dumpinfo #else #define DUMPINFO &_no_threads_dumpinfo @@ -91,13 +95,14 @@ static dumpinfo _no_threads_dumpinfo; /* global code memory variables ***********************************************/ -#define DEFAULT_CODEMEM_SIZE 128 * 1024 /* defaulting to 128kB */ +#define DEFAULT_CODE_MEMORY_SIZE 128 * 1024 /* defaulting to 128kB */ -#if defined(USE_THREADS) -static java_objectheader *codememlock = NULL; +#if defined(ENABLE_THREADS) +static java_object_t *lock_code_memory = NULL; #endif -static int codememsize = 0; -static void *codememptr = NULL; +static void *code_memory = NULL; +static int code_memory_size = 0; +static int pagesize = 0; /* memory_init ***************************************************************** @@ -108,24 +113,72 @@ static void *codememptr = NULL; bool memory_init(void) { -#if defined(USE_THREADS) - codememlock = NEW(java_objectheader); +#if defined(ENABLE_THREADS) + /* create lock for code memory */ -# if defined(NATIVE_THREADS) - initObjectLock(codememlock); -# endif + lock_code_memory = NEW(java_object_t); + + lock_init_object_lock(lock_code_memory); #endif + /* get the pagesize of this architecture */ + + pagesize = getpagesize(); + /* everything's ok */ return true; } +/* memory_mmap_anon ************************************************************ + + Maps anonymous memory, even on systems not defining + MAP_ANON(YMOUS). + +*******************************************************************************/ + +void *memory_mmap_anon(void *addr, size_t len, int prot, int flags) +{ + void *p; + +#if defined(MAP_ANON) || defined(MAP_ANONYMOUS) + p = mmap(addr, len, prot, +# if defined(MAP_ANON) + MAP_ANON | flags, +# else + MAP_ANONYMOUS | flags, +# endif + -1, 0); +#else + int fd; + + fd = open("/dev/zero", O_RDONLY, 0); + + if (fd == -1) + vm_abort("memory_mmap_anon: open failed: %s", strerror(errno)); + + p = mmap(addr, len, prot, flags, fd, 0); +#endif + +#if defined(MAP_FAILED) + if (p == MAP_FAILED) +#else + if (p == (void *) -1) +#endif + vm_abort("memory_mmap_anon: mmap failed: %s", strerror(errno)); + + return p; +} + + /* memory_checked_alloc ******************************************************** Allocated zeroed-out memory and does an OOM check. + ERROR HANDLING: + XXX If no memory could be allocated, this function justs *exists*. + *******************************************************************************/ static void *memory_checked_alloc(s4 size) @@ -134,9 +187,8 @@ static void *memory_checked_alloc(s4 size) void *p = calloc(size, 1); - if (!p) - throw_cacao_exception_exit(string_java_lang_InternalError, - "Out of memory"); + if (p == NULL) + vm_abort("memory_checked_alloc: calloc failed: out of memory"); return p; } @@ -144,88 +196,91 @@ static void *memory_checked_alloc(s4 size) /* memory_cnew ***************************************************************** - Allocates memory from the heap, aligns it to architecutres PAGESIZE - and make the memory read-, write-, and executeable. + Allocates memory from the heap via mmap and make the memory read-, + write-, and executeable. *******************************************************************************/ void *memory_cnew(s4 size) { void *p; - int pagesize; -#if defined(USE_THREADS) - builtin_monitorenter(codememlock); -#endif + LOCK_MONITOR_ENTER(lock_code_memory); - size = ALIGN(size, ALIGNSIZE); + size = MEMORY_ALIGN(size, ALIGNSIZE); /* check if enough memory is available */ - if (size > codememsize) { + if (size > code_memory_size) { /* set default code size */ - codememsize = DEFAULT_CODEMEM_SIZE; + code_memory_size = DEFAULT_CODE_MEMORY_SIZE; /* do we need more? */ - if (size > codememsize) - codememsize = size; - - /* get the pagesize of this architecture */ - - pagesize = getpagesize(); + if (size > code_memory_size) + code_memory_size = size; - /* allocate normal heap memory */ + /* align the size of the memory to be allocated */ - if ((p = memory_checked_alloc(codememsize + pagesize - 1)) == NULL) - return NULL; + code_memory_size = MEMORY_ALIGN(code_memory_size, pagesize); -#if defined(STATISTICS) +#if defined(ENABLE_STATISTICS) if (opt_stat) { - codememusage += codememsize + pagesize - 1; + codememusage += code_memory_size; if (codememusage > maxcodememusage) maxcodememusage = codememusage; } #endif - /* align the memory allocated to a multiple of PAGESIZE, - mprotect requires this */ + /* allocate the memory */ - p = (void *) (((ptrint) p + pagesize - 1) & ~(pagesize - 1)); - - /* make the memory read-, write-, and executeable */ - - if (mprotect(p, codememsize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) - throw_cacao_exception_exit(string_java_lang_InternalError, - strerror(errno)); + p = memory_mmap_anon(NULL, code_memory_size, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE); /* set global code memory pointer */ - codememptr = p; + code_memory = p; } /* get a memory chunk of the allocated memory */ - p = codememptr; - codememptr = (void *) ((ptrint) codememptr + size); - codememsize -= size; + p = code_memory; -#if defined(USE_THREADS) - builtin_monitorexit(codememlock); -#endif + code_memory = (void *) ((ptrint) code_memory + size); + code_memory_size -= size; + + LOCK_MONITOR_EXIT(lock_code_memory); return p; } +/* memory_cfree **************************************************************** + + Frees the code memory pointed to. + + ATTENTION: This function currently does NOTHING! Because we don't + have a memory management for code memory. + +*******************************************************************************/ + +void memory_cfree(void *p, s4 size) +{ + /* do nothing */ +} + + void *mem_alloc(s4 size) { + void *m; + if (size == 0) return NULL; -#if defined(STATISTICS) +#if defined(ENABLE_STATISTICS) if (opt_stat) { memoryusage += size; @@ -234,7 +289,15 @@ void *mem_alloc(s4 size) } #endif - return memory_checked_alloc(size); + m = memory_checked_alloc(size); + +#if defined(ENABLE_MEMCHECK) + /* XXX we would like to poison the memory, but callers rely on */ + /* the zeroing. This should change sooner or later. */ + /* memset(m, MEMORY_CLEAR_BYTE, size); */ +#endif + + return m; } @@ -242,23 +305,33 @@ void *mem_realloc(void *src, s4 len1, s4 len2) { void *dst; - if (!src) { - if (len1 != 0) { - log_text("reallocating memoryblock with address NULL, length != 0"); - assert(0); - } - } + /* prevent compiler warnings */ + + dst = NULL; -#if defined(STATISTICS) + if (src == NULL) + if (len1 != 0) + vm_abort("mem_realloc: reallocating memoryblock with address NULL, length != 0"); + +#if defined(ENABLE_STATISTICS) if (opt_stat) memoryusage = (memoryusage - len1) + len2; #endif +#if defined(ENABLE_MEMCHECK) + if (len2 < len1) + memset((u1*)dst + len2, MEMORY_CLEAR_BYTE, len1 - len2); +#endif + dst = realloc(src, len2); - if (!dst) - throw_cacao_exception_exit(string_java_lang_InternalError, - "Out of memory"); + if (dst == NULL) + vm_abort("mem_realloc: realloc failed: out of memory"); + +#if defined(ENABLE_MEMCHECK) + if (len2 > len1) + memset((u1*)dst + len1, MEMORY_CLEAR_BYTE, len2 - len1); +#endif return dst; } @@ -274,30 +347,194 @@ void mem_free(void *m, s4 size) assert(0); } -#if defined(STATISTICS) +#if defined(ENABLE_STATISTICS) if (opt_stat) memoryusage -= size; #endif +#if defined(ENABLE_MEMCHECK) + /* destroy the contents */ + memset(m, MEMORY_CLEAR_BYTE, size); +#endif + free(m); } +/* memory_thread *************************************************************** + + Prints regularly memory statistics. + +*******************************************************************************/ + +#if defined(ENABLE_THREADS) && !defined(NDEBUG) +static void memory_thread(void) +{ + int32_t seconds; + + /* If both arguments are specified, use the value of + ProfileMemoryUsage. */ + + if (opt_ProfileGCMemoryUsage) + seconds = opt_ProfileGCMemoryUsage; + + if (opt_ProfileMemoryUsage) + seconds = opt_ProfileMemoryUsage; + + while (true) { + /* sleep thread */ + + threads_sleep(seconds * 1000, 0); + +# if defined(ENABLE_STATISTICS) + /* Print current date and time (only when we print to the + stdout). */ + + if (!opt_ProfileMemoryUsageGNUPlot) + statistics_print_date(); + + /* print memory usage */ + + if (opt_ProfileMemoryUsage) + statistics_print_memory_usage(); + + /* print GC memory usage */ + + if (opt_ProfileGCMemoryUsage) + statistics_print_gc_memory_usage(); +# endif + } +} +#endif + + +/* memory_start_thread ********************************************************* + + Starts the memory profiling thread. + +*******************************************************************************/ + +#if defined(ENABLE_THREADS) && !defined(NDEBUG) +bool memory_start_thread(void) +{ + utf *name; + + name = utf_new_char("Memory Profiler"); + + /* start the memory profiling thread */ + + if (!threads_thread_start_internal(name, memory_thread)) + return false; + + /* everything's ok */ + + return true; +} +#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) +void dump_check_canaries(dumpinfo_t *di, s4 bottomsize) +{ + dump_allocation_t *da; + u1 *pm; + s4 i, j; + + /* iterate over all dump memory allocations above bottomsize */ + + da = di->allocations; + while (da && da->useddumpsize >= bottomsize) { + /* check canaries */ + + pm = da->mem - MEMORY_CANARY_SIZE; + for (i=0; isize, da->mem); + } + + pm = da->mem + da->size; + for (i=0; isize, da->mem); + } + + da = da->next; + } +} +#endif /* defined(ENABLE_MEMCHECK) */ + + /* dump_alloc ****************************************************************** - XXX + 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: + dump_alloc is thread safe. Each thread has its own dump memory area. + + dump_alloc 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 `dump_size`. Later, when you no + longer need the memory, call `dump_release` with the remembered size and + all dump memory allocated since the call to `dump_size` will be freed. *******************************************************************************/ void *dump_alloc(s4 size) { #if defined(DISABLE_DUMP) + /* use malloc memory for dump memory (for debugging only!) */ return mem_alloc(size); -#else - void *m; - dumpinfo *di; + +#else /* !defined(DISABLE_DUMP) */ + + void *m; + dumpinfo_t *di; +#if defined(ENABLE_MEMCHECK) + s4 origsize = size; /* needed for the canary system */ +#endif /* If no threads are used, the dumpinfo structure is a static structure */ /* defined at the top of this file. */ @@ -307,15 +544,19 @@ void *dump_alloc(s4 size) if (size == 0) return NULL; - size = ALIGN(size, ALIGNSIZE); +#if defined(ENABLE_MEMCHECK) + size += 2*MEMORY_CANARY_SIZE; +#endif + + size = MEMORY_ALIGN(size, ALIGNSIZE); if (di->useddumpsize + size > di->allocateddumpsize) { - dumpblock *newdumpblock; + dumpblock_t *newdumpblock; s4 newdumpblocksize; /* allocate a new dumplist structure */ - newdumpblock = memory_checked_alloc(sizeof(dumpblock)); + newdumpblock = 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. */ @@ -344,7 +585,7 @@ void *dump_alloc(s4 size) di->allocateddumpsize += newdumpblocksize; -#if defined(STATISTICS) +#if defined(ENABLE_STATISTICS) /* the amount of globally allocated dump memory (thread save) */ if (opt_stat) @@ -358,24 +599,59 @@ void *dump_alloc(s4 size) m = di->currentdumpblock->dumpmem + di->currentdumpblock->size - (di->allocateddumpsize - di->useddumpsize); +#if defined(ENABLE_MEMCHECK) + { + dump_allocation_t *da = NEW(dump_allocation_t); + s4 i; + u1 *pm; + + /* add the allocation to our linked list of allocations */ + + da->next = di->allocations; + da->mem = (u1*) m + MEMORY_CANARY_SIZE; + da->size = origsize; + da->useddumpsize = di->useddumpsize; + + di->allocations = da; + + /* write the canaries */ + + pm = (u1*)m; + for (i=0; imem + da->size; + for (i=0; isize); + } +#endif /* defined(ENABLE_MEMCHECK) */ + /* increase used dump size by the allocated memory size */ di->useddumpsize += size; -#if defined(STATISTICS) +#if defined(ENABLE_STATISTICS) if (opt_stat) if (di->useddumpsize > maxdumpsize) maxdumpsize = di->useddumpsize; #endif - + return m; + #endif /* defined(DISABLE_DUMP) */ } /* dump_realloc **************************************************************** - XXX + Stupid realloc implementation for dump memory. Avoid, if possible. *******************************************************************************/ @@ -390,6 +666,11 @@ void *dump_realloc(void *src, s4 len1, s4 len2) memcpy(dst, src, len1); +#if defined(ENABLE_MEMCHECK) + /* destroy the source */ + memset(src, MEMORY_CLEAR_BYTE, len1); +#endif + return dst; #endif } @@ -397,50 +678,78 @@ void *dump_realloc(void *src, s4 len1, s4 len2) /* dump_release **************************************************************** - XXX + 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 `dump_size` call + made earlier. + + ERROR HANDLING: + XXX If the given size is invalid, this function *exits* with an + error message. + + See `dump_alloc`. *******************************************************************************/ void dump_release(s4 size) { #if defined(DISABLE_DUMP) + /* use malloc memory for dump memory (for debugging only!) */ /* do nothing */ -#else - dumpinfo *di; + +#else /* !defined(DISABLE_DUMP) */ + + dumpinfo_t *di; /* If no threads are used, the dumpinfo structure is a static structure */ /* defined at the top of this file. */ di = DUMPINFO; - if (size < 0 || size > di->useddumpsize) - throw_cacao_exception_exit(string_java_lang_InternalError, - "Illegal dump release size %d", size); + if ((size < 0) || (size > di->useddumpsize)) + vm_abort("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 && da->useddumpsize >= size) { + next = da->next; + + /* invalidate the freed memory */ + + 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->useddumpsize = size; while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) { - dumpblock *tmp = di->currentdumpblock; - -#if 0 - /* XXX TWISTI: can someone explain this to me? */ -#ifdef TRACECALLARGS - /* Keep the first dumpblock if we don't free memory. Otherwise - * a new dumpblock is allocated each time and we run out of - * memory. - */ - if (!oldtop->prev) break; -#endif -#endif + dumpblock_t *tmp = di->currentdumpblock; di->allocateddumpsize -= tmp->size; di->currentdumpblock = tmp->prev; -#if defined(STATISTICS) +#if defined(ENABLE_STATISTICS) /* the amount of globally allocated dump memory (thread save) */ if (opt_stat) @@ -452,13 +761,14 @@ void dump_release(s4 size) free(tmp->dumpmem); free(tmp); } + #endif /* defined(DISABLE_DUMP) */ } /* dump_size ******************************************************************* - XXX + Return the current size of the dump memory area. See `dump_alloc`. *******************************************************************************/ @@ -468,18 +778,21 @@ s4 dump_size(void) /* use malloc memory for dump memory (for debugging only!) */ return 0; -#else - dumpinfo *di; + +#else /* !defined(DISABLE_DUMP) */ + + dumpinfo_t *di; /* If no threads are used, the dumpinfo structure is a static structure */ /* defined at the top of this file. */ di = DUMPINFO; - if (!di) + if (di == NULL) return 0; return di->useddumpsize; + #endif /* defined(DISABLE_DUMP) */ } @@ -495,4 +808,5 @@ s4 dump_size(void) * c-basic-offset: 4 * tab-width: 4 * End: + * vim:noexpandtab:sw=4:ts=4: */