-/* src/mm/memory.c -
+/* src/mm/memory.c - memory management
- Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
+ 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
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- Contact: cacao@cacaojvm.org
-
- Authors: Reinhard Grafl
-
- Changes: Christian Thalinger
- Edwin Steiner
-
- $Id: memory.c 5169 2006-07-25 11:38:18Z twisti $
-
*/
#include <sys/mman.h>
#if defined(__DARWIN__)
-/* If we compile with -ansi on darwin, <sys/types.h> is not included. So */
-/* let's do it here. */
+/* If we compile with -ansi on darwin, <sys/types.h> is not
+ included. So let's do it here. */
# include <sys/types.h>
#endif
#include "arch.h"
#include "mm/memory.h"
+
#include "native/native.h"
-#if defined(ENABLE_THREADS)
-# include "threads/native/lock.h"
-# include "threads/native/threads.h"
-#else
-# include "threads/none/lock.h"
-#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) */
+
/*******************************************************************************
*******************************************************************************/
#if !defined(ENABLE_THREADS)
-static dumpinfo _no_threads_dumpinfo;
+static dumpinfo_t _no_threads_dumpinfo;
#endif
#if defined(ENABLE_THREADS)
#define DEFAULT_CODE_MEMORY_SIZE 128 * 1024 /* defaulting to 128kB */
#if defined(ENABLE_THREADS)
-static java_objectheader *lock_code_memory = NULL;
+static java_object_t *lock_code_memory = NULL;
#endif
-static void *code_memory = NULL;
-static int code_memory_size = 0;
-static int pagesize = 0;
+static void *code_memory = NULL;
+static int code_memory_size = 0;
+static int pagesize = 0;
/* memory_init *****************************************************************
bool memory_init(void)
{
#if defined(ENABLE_THREADS)
- lock_code_memory = NEW(java_objectheader);
+ /* create lock for code memory */
+
+ lock_code_memory = NEW(java_object_t);
lock_init_object_lock(lock_code_memory);
#endif
}
+/* 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.
void *p = calloc(size, 1);
if (p == NULL)
- exceptions_throw_outofmemory_exit();
+ vm_abort("memory_checked_alloc: calloc failed: out of memory");
return p;
}
LOCK_MONITOR_ENTER(lock_code_memory);
- size = ALIGN(size, ALIGNSIZE);
+ size = MEMORY_ALIGN(size, ALIGNSIZE);
/* check if enough memory is available */
/* align the size of the memory to be allocated */
- code_memory_size = ALIGN(code_memory_size, pagesize);
+ code_memory_size = MEMORY_ALIGN(code_memory_size, pagesize);
#if defined(ENABLE_STATISTICS)
if (opt_stat) {
/* allocate the memory */
- p = mmap(NULL,
- (size_t) code_memory_size,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE |
-#if defined(MAP_ANONYMOUS)
- MAP_ANONYMOUS,
-#elif defined(MAP_ANON)
- MAP_ANON,
-#else
- 0,
-#endif
- -1,
- (off_t) 0);
-
-#if defined(MAP_FAILED)
- if (p == MAP_FAILED)
-#else
- if (p == (void *) -1)
-#endif
- vm_abort("mmap failed: %s", strerror(errno));
+ p = memory_mmap_anon(NULL, code_memory_size,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE);
/* set global code memory pointer */
void *mem_alloc(s4 size)
{
+ void *m;
+
if (size == 0)
return NULL;
}
#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;
}
{
void *dst;
- if (!src) {
- if (len1 != 0) {
- log_text("reallocating memoryblock with address NULL, length != 0");
- assert(0);
- }
- }
+ /* prevent compiler warnings */
+
+ dst = NULL;
+
+ 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 == NULL)
- exceptions_throw_outofmemory_exit();
+ 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;
}
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; 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 = 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) */
+
+
/* dump_alloc ******************************************************************
Allocate memory in the dump area.
#else /* !defined(DISABLE_DUMP) */
- void *m;
- dumpinfo *di;
+ 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. */
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. */
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; i<MEMORY_CANARY_SIZE; ++i)
+ pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
+ pm = 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 */
+
+ m = (u1*)m + MEMORY_CANARY_SIZE;
+
+ /* clear the memory */
+
+ memset(m, MEMORY_CLEAR_BYTE, da->size);
+ }
+#endif /* defined(ENABLE_MEMCHECK) */
+
/* increase used dump size by the allocated memory size */
di->useddumpsize += size;
memcpy(dst, src, len1);
+#if defined(ENABLE_MEMCHECK)
+ /* destroy the source */
+ memset(src, MEMORY_CLEAR_BYTE, len1);
+#endif
+
return dst;
#endif
}
#else /* !defined(DISABLE_DUMP) */
- dumpinfo *di;
+ dumpinfo_t *di;
/* If no threads are used, the dumpinfo structure is a static structure */
/* defined at the top of this file. */
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;
+ dumpblock_t *tmp = di->currentdumpblock;
di->allocateddumpsize -= tmp->size;
di->currentdumpblock = tmp->prev;
#else /* !defined(DISABLE_DUMP) */
- dumpinfo *di;
+ 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;