Added support for thread suspension!
[cacao.git] / src / mm / memory.c
index a43ae9b3ebecfb8270f5c37ff6eb35780316a8e6..7d68fef5a90a9d0154f0b501dd6ea903d2612e0a 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 5126 2006-07-12 22:55:05Z twisti $
+   $Id: memory.c 7394 2007-02-23 22:47:06Z michi $
 
 */
 
@@ -45,8 +38,8 @@
 #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
 
@@ -58,6 +51,8 @@
 #include "native/native.h"
 
 #if defined(ENABLE_THREADS)
+# include "threads/threads-common.h"
+
 # include "threads/native/lock.h"
 # include "threads/native/threads.h"
 #else
 #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
 
 
 /*******************************************************************************
@@ -81,7 +79,7 @@
 *******************************************************************************/
 
 #if !defined(ENABLE_THREADS)
-static dumpinfo _no_threads_dumpinfo;
+static dumpinfo_t _no_threads_dumpinfo;
 #endif
 
 #if defined(ENABLE_THREADS)
@@ -93,13 +91,21 @@ 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(ENABLE_THREADS)
+static java_objectheader *lock_code_memory = NULL;
+#endif
+static void              *code_memory      = NULL;
+static int                code_memory_size = 0;
+static int                pagesize         = 0;
+
+
+/* global variables ***********************************************************/
 
 #if defined(ENABLE_THREADS)
-static java_objectheader *codememlock = NULL;
+static threadobject *thread_memory;
 #endif
-static int                codememsize = 0;
-static void              *codememptr  = NULL;
 
 
 /* memory_init *****************************************************************
@@ -111,17 +117,64 @@ static void              *codememptr  = NULL;
 bool memory_init(void)
 {
 #if defined(ENABLE_THREADS)
-       codememlock = NEW(java_objectheader);
+       /* create lock for code memory */
 
-       lock_init_object_lock(codememlock);
+       lock_code_memory = NEW(java_objectheader);
+
+       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.
@@ -138,7 +191,7 @@ static void *memory_checked_alloc(s4 size)
        void *p = calloc(size, 1);
 
        if (p == NULL)
-               exceptions_throw_outofmemory_exit();
+               vm_abort("memory_checked_alloc: calloc failed: out of memory");
 
        return p;
 }
@@ -146,79 +199,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;
 
-       LOCK_MONITOR_ENTER(codememlock);
+       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;
+               if (size > code_memory_size)
+                       code_memory_size = size;
 
-               /* get the pagesize of this architecture */
+               /* align the size of the memory to be allocated */
 
-               pagesize = getpagesize();
-
-               /* 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)
-                       vm_abort("mprotect 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 */
 
-               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;
+
+       code_memory       = (void *) ((ptrint) code_memory + size);
+       code_memory_size -= size;
 
-       LOCK_MONITOR_EXIT(codememlock);
+       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;
 
@@ -231,7 +292,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;
 }
 
 
@@ -239,22 +308,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 == 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;
 }
@@ -275,10 +355,130 @@ 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)
+{
+       while (true) {
+               /* sleep thread for 2 seconds */
+
+               threads_sleep(2 * 1000, 0);
+
+# if defined(ENABLE_STATISTICS)
+               /* print memory usage */
+
+               statistics_print_memory_usage();
+
+               /* print GC memory usage */
+
+               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");
+
+       thread_memory = threads_create_thread(name);
+
+       if (thread_memory == NULL)
+               return false;
+
+       /* actually start the memory profiling thread */
+
+       threads_start_thread(thread_memory, memory_thread);
+
+       /* 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.
@@ -318,8 +518,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.                                       */
@@ -329,15 +532,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.     */
@@ -380,6 +587,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;
@@ -413,6 +654,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
 }
@@ -445,7 +691,7 @@ 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.                                       */
@@ -455,12 +701,38 @@ void dump_release(s4 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;
+               dumpblock_t *tmp = di->currentdumpblock;
 
                di->allocateddumpsize -= tmp->size;
                di->currentdumpblock = tmp->prev;
@@ -497,14 +769,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;