* Removed all Id tags.
[cacao.git] / src / mm / memory.c
index 5f91f00ccdfe55d42df9295e0f5ba0399b4ce573..9c3034491c051994ef2c9aeaf20061b81fb5308d 100644 (file)
@@ -1,6 +1,6 @@
-/* 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 4798 2006-04-20 19:18:43Z edwin $
-
 */
 
 
+#include "config.h"
+
 #include <assert.h>
 #include <errno.h>
 #include <stdio.h>
 #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 "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) */
 
 
 /*******************************************************************************
 
 *******************************************************************************/
 
-#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
@@ -92,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 *****************************************************************
@@ -109,13 +113,17 @@ 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
-#endif /* defined(USE_THREADS) */
+       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 */
 
@@ -123,6 +131,47 @@ bool memory_init(void)
 }
 
 
+/* 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.
@@ -138,8 +187,8 @@ static void *memory_checked_alloc(s4 size)
 
        void *p = calloc(size, 1);
 
-       if (!p)
-               exceptions_throw_outofmemory_exit();
+       if (p == NULL)
+               vm_abort("memory_checked_alloc: calloc failed: out of memory");
 
        return p;
 }
@@ -147,84 +196,87 @@ 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 */
+               if (size > code_memory_size)
+                       code_memory_size = size;
 
-               pagesize = getpagesize();
+               /* align the size of the memory to be allocated */
 
-               /* allocate normal heap memory */
-
-               if ((p = memory_checked_alloc(codememsize + pagesize - 1)) == NULL)
-                       return NULL;
+               code_memory_size = MEMORY_ALIGN(code_memory_size, pagesize);
 
 #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 */
-
-               p = (void *) (((ptrint) p + pagesize - 1) & ~(pagesize - 1));
+               /* allocate the memory */
 
-               /* 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;
 
@@ -237,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;
 }
 
 
@@ -245,22 +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 (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)
-               exceptions_throw_outofmemory_exit();
+       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;
 }
@@ -281,10 +352,145 @@ void mem_free(void *m, s4 size)
                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.
@@ -324,8 +530,11 @@ void *dump_alloc(s4 size)
 
 #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.                                       */
@@ -335,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.     */
@@ -386,6 +599,40 @@ 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; 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;
@@ -419,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
 }
@@ -451,34 +703,48 @@ void dump_release(s4 size)
 
 #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 (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;
@@ -515,14 +781,14 @@ s4 dump_size(void)
 
 #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;