1 /* src/mm/memory.c - memory management
3 Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
4 C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5 E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6 J. Wenninger, Institut f. Computersprachen - TU Wien
8 This file is part of CACAO.
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2, or (at
13 your option) any later version.
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 $Id: memory.c 7370 2007-02-16 18:00:03Z twisti $
40 #if defined(__DARWIN__)
41 /* If we compile with -ansi on darwin, <sys/types.h> is not
42 included. So let's do it here. */
43 # include <sys/types.h>
50 #include "mm/memory.h"
51 #include "native/native.h"
53 #if defined(ENABLE_THREADS)
54 # include "threads/threads-common.h"
56 # include "threads/native/lock.h"
57 # include "threads/native/threads.h"
59 # include "threads/none/lock.h"
62 #include "toolbox/logging.h"
63 #include "vm/exceptions.h"
64 #include "vm/global.h"
65 #include "vm/stringlocal.h"
67 #include "vmcore/options.h"
69 #if defined(ENABLE_STATISTICS)
70 # include "vmcore/statistics.h"
74 /* constants for ENABLE_MEMCHECK **********************************************/
76 #if defined(ENABLE_MEMCHECK)
77 #define MEMORY_CANARY_SIZE 16
78 #define MEMORY_CANARY_FIRST_BYTE 0xca
79 #define MEMORY_CLEAR_BYTE 0xa5
80 #endif /* defined(ENABLE_MEMCHECK) */
83 /*******************************************************************************
85 This structure is used for dump memory allocation if cacao
88 *******************************************************************************/
90 #if !defined(ENABLE_THREADS)
91 static dumpinfo_t _no_threads_dumpinfo;
94 #if defined(ENABLE_THREADS)
95 #define DUMPINFO &((threadobject *) THREADOBJECT)->dumpinfo
97 #define DUMPINFO &_no_threads_dumpinfo
101 /* global code memory variables ***********************************************/
103 #define DEFAULT_CODE_MEMORY_SIZE 128 * 1024 /* defaulting to 128kB */
105 #if defined(ENABLE_THREADS)
106 static java_objectheader *lock_code_memory = NULL;
108 static void *code_memory = NULL;
109 static int code_memory_size = 0;
110 static int pagesize = 0;
113 /* global variables ***********************************************************/
115 #if defined(ENABLE_THREADS)
116 static threadobject *thread_memory;
120 /* memory_init *****************************************************************
122 Initialize the memory subsystem.
124 *******************************************************************************/
126 bool memory_init(void)
128 #if defined(ENABLE_THREADS)
129 /* create lock for code memory */
131 lock_code_memory = NEW(java_objectheader);
133 lock_init_object_lock(lock_code_memory);
136 /* get the pagesize of this architecture */
138 pagesize = getpagesize();
140 /* everything's ok */
146 /* memory_mmap_anon ************************************************************
148 Maps anonymous memory, even on systems not defining
151 *******************************************************************************/
153 void *memory_mmap_anon(void *addr, size_t len, int prot, int flags)
157 #if defined(MAP_ANON) || defined(MAP_ANONYMOUS)
158 p = mmap(addr, len, prot,
159 # if defined(MAP_ANON)
162 MAP_ANONYMOUS | flags,
168 fd = open("/dev/zero", O_RDONLY, 0);
171 vm_abort("memory_mmap_anon: open failed: %s", strerror(errno));
173 p = mmap(addr, len, prot, flags, fd, 0);
176 #if defined(MAP_FAILED)
179 if (p == (void *) -1)
181 vm_abort("memory_mmap_anon: mmap failed: %s", strerror(errno));
187 /* memory_checked_alloc ********************************************************
189 Allocated zeroed-out memory and does an OOM check.
192 XXX If no memory could be allocated, this function justs *exists*.
194 *******************************************************************************/
196 static void *memory_checked_alloc(s4 size)
198 /* always allocate memory zeroed out */
200 void *p = calloc(size, 1);
203 vm_abort("memory_checked_alloc: calloc failed: out of memory");
209 /* memory_cnew *****************************************************************
211 Allocates memory from the heap via mmap and make the memory read-,
212 write-, and executeable.
214 *******************************************************************************/
216 void *memory_cnew(s4 size)
220 LOCK_MONITOR_ENTER(lock_code_memory);
222 size = MEMORY_ALIGN(size, ALIGNSIZE);
224 /* check if enough memory is available */
226 if (size > code_memory_size) {
227 /* set default code size */
229 code_memory_size = DEFAULT_CODE_MEMORY_SIZE;
231 /* do we need more? */
233 if (size > code_memory_size)
234 code_memory_size = size;
236 /* align the size of the memory to be allocated */
238 code_memory_size = MEMORY_ALIGN(code_memory_size, pagesize);
240 #if defined(ENABLE_STATISTICS)
242 codememusage += code_memory_size;
244 if (codememusage > maxcodememusage)
245 maxcodememusage = codememusage;
249 /* allocate the memory */
251 p = memory_mmap_anon(NULL, code_memory_size,
252 PROT_READ | PROT_WRITE | PROT_EXEC,
255 /* set global code memory pointer */
260 /* get a memory chunk of the allocated memory */
264 code_memory = (void *) ((ptrint) code_memory + size);
265 code_memory_size -= size;
267 LOCK_MONITOR_EXIT(lock_code_memory);
273 /* memory_cfree ****************************************************************
275 Frees the code memory pointed to.
277 ATTENTION: This function currently does NOTHING! Because we don't
278 have a memory management for code memory.
280 *******************************************************************************/
282 void memory_cfree(void *p, s4 size)
288 void *mem_alloc(s4 size)
295 #if defined(ENABLE_STATISTICS)
299 if (memoryusage > maxmemusage)
300 maxmemusage = memoryusage;
304 m = memory_checked_alloc(size);
306 #if defined(ENABLE_MEMCHECK)
307 /* XXX we would like to poison the memory, but callers rely on */
308 /* the zeroing. This should change sooner or later. */
309 /* memset(m, MEMORY_CLEAR_BYTE, size); */
316 void *mem_realloc(void *src, s4 len1, s4 len2)
320 /* prevent compiler warnings */
326 vm_abort("mem_realloc: reallocating memoryblock with address NULL, length != 0");
328 #if defined(ENABLE_STATISTICS)
330 memoryusage = (memoryusage - len1) + len2;
333 #if defined(ENABLE_MEMCHECK)
335 memset((u1*)dst + len2, MEMORY_CLEAR_BYTE, len1 - len2);
338 dst = realloc(src, len2);
341 vm_abort("mem_realloc: realloc failed: out of memory");
343 #if defined(ENABLE_MEMCHECK)
345 memset((u1*)dst + len1, MEMORY_CLEAR_BYTE, len2 - len1);
352 void mem_free(void *m, s4 size)
358 log_text("returned memoryblock with address NULL, length != 0");
362 #if defined(ENABLE_STATISTICS)
367 #if defined(ENABLE_MEMCHECK)
368 /* destroy the contents */
369 memset(m, MEMORY_CLEAR_BYTE, size);
376 /* memory_thread ***************************************************************
378 Prints regularly memory statistics.
380 *******************************************************************************/
382 #if defined(ENABLE_THREADS) && !defined(NDEBUG)
383 static void memory_thread(void)
386 /* sleep thread for 2 seconds */
388 threads_sleep(2 * 1000, 0);
390 # if defined(ENABLE_STATISTICS)
391 /* print memory usage */
393 statistics_print_memory_usage();
395 /* print GC memory usage */
397 statistics_print_gc_memory_usage();
404 /* memory_start_thread *********************************************************
406 Starts the memory profiling thread.
408 *******************************************************************************/
410 #if defined(ENABLE_THREADS) && !defined(NDEBUG)
411 bool memory_start_thread(void)
415 name = utf_new_char("Memory Profiler");
417 thread_memory = threads_create_thread(name);
419 if (thread_memory == NULL)
422 /* actually start the memory profiling thread */
424 threads_start_thread(thread_memory, memory_thread);
426 /* everything's ok */
433 /* dump_check_canaries *********************************************************
435 Check canaries in dump memory.
438 di...........dumpinfo_t * of the dump area to check
439 bottomsize...dump size down to which the dump area should be checked
440 (specify 0 to check the whole dump area)
443 If any canary has been changed, this function aborts the VM with
446 *******************************************************************************/
448 #if defined(ENABLE_MEMCHECK)
449 void dump_check_canaries(dumpinfo_t *di, s4 bottomsize)
451 dump_allocation_t *da;
455 /* iterate over all dump memory allocations above bottomsize */
457 da = di->allocations;
458 while (da && da->useddumpsize >= bottomsize) {
461 pm = da->mem - MEMORY_CANARY_SIZE;
462 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
463 if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
464 fprintf(stderr, "canary bytes:");
465 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
466 fprintf(stderr, " %02x", pm[j]);
467 fprintf(stderr,"\n");
468 vm_abort("error: dump memory bottom canary killed: "
469 "%p (%d bytes allocated at %p)\n",
470 pm + i, da->size, da->mem);
473 pm = da->mem + da->size;
474 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
475 if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
476 fprintf(stderr, "canary bytes:");
477 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
478 fprintf(stderr, " %02x", pm[j]);
479 fprintf(stderr,"\n");
480 vm_abort("error: dump memory top canary killed: "
481 "%p (%d bytes allocated at %p)\n",
482 pm + i, da->size, da->mem);
488 #endif /* defined(ENABLE_MEMCHECK) */
491 /* dump_alloc ******************************************************************
493 Allocate memory in the dump area.
496 size.........size of block to allocate, in bytes
497 may be zero, in which case NULL is returned
500 pointer to allocated memory, or
501 NULL iff `size` was zero
504 XXX This function uses `memory_checked_alloc`, which *exits* if no
505 memory could be allocated.
508 dump_alloc is thread safe. Each thread has its own dump memory area.
510 dump_alloc is a fast allocator suitable for scratch memory that can be
511 collectively freed when the current activity (eg. compiling) is done.
513 You cannot selectively free dump memory. Before you start allocating it,
514 you remember the current size returned by `dump_size`. Later, when you no
515 longer need the memory, call `dump_release` with the remembered size and
516 all dump memory allocated since the call to `dump_size` will be freed.
518 *******************************************************************************/
520 void *dump_alloc(s4 size)
522 #if defined(DISABLE_DUMP)
524 /* use malloc memory for dump memory (for debugging only!) */
526 return mem_alloc(size);
528 #else /* !defined(DISABLE_DUMP) */
532 #if defined(ENABLE_MEMCHECK)
533 s4 origsize = size; /* needed for the canary system */
536 /* If no threads are used, the dumpinfo structure is a static structure */
537 /* defined at the top of this file. */
544 #if defined(ENABLE_MEMCHECK)
545 size += 2*MEMORY_CANARY_SIZE;
548 size = MEMORY_ALIGN(size, ALIGNSIZE);
550 if (di->useddumpsize + size > di->allocateddumpsize) {
551 dumpblock_t *newdumpblock;
554 /* allocate a new dumplist structure */
556 newdumpblock = memory_checked_alloc(sizeof(dumpblock_t));
558 /* If requested size is greater than the default, make the new dump */
559 /* block as big as the size requested. Else use the default size. */
561 if (size > DUMPBLOCKSIZE) {
562 newdumpblocksize = size;
565 newdumpblocksize = DUMPBLOCKSIZE;
568 /* allocate dumpblock memory */
570 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
572 newdumpblock->prev = di->currentdumpblock;
573 newdumpblock->size = newdumpblocksize;
574 di->currentdumpblock = newdumpblock;
576 /* Used dump size is previously allocated dump size, because the */
577 /* remaining free memory of the previous dump block cannot be used. */
579 di->useddumpsize = di->allocateddumpsize;
581 /* increase the allocated dump size by the size of the new dump block */
583 di->allocateddumpsize += newdumpblocksize;
585 #if defined(ENABLE_STATISTICS)
586 /* the amount of globally allocated dump memory (thread save) */
589 globalallocateddumpsize += newdumpblocksize;
593 /* current dump block base address + the size of the current dump block - */
594 /* the size of the unused memory = new start address */
596 m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
597 (di->allocateddumpsize - di->useddumpsize);
599 #if defined(ENABLE_MEMCHECK)
601 dump_allocation_t *da = NEW(dump_allocation_t);
605 /* add the allocation to our linked list of allocations */
607 da->next = di->allocations;
608 da->mem = (u1*) m + MEMORY_CANARY_SIZE;
610 da->useddumpsize = di->useddumpsize;
612 di->allocations = da;
614 /* write the canaries */
617 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
618 pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
619 pm = da->mem + da->size;
620 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
621 pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
623 /* make m point after the bottom canary */
625 m = (u1*)m + MEMORY_CANARY_SIZE;
627 /* clear the memory */
629 memset(m, MEMORY_CLEAR_BYTE, da->size);
631 #endif /* defined(ENABLE_MEMCHECK) */
633 /* increase used dump size by the allocated memory size */
635 di->useddumpsize += size;
637 #if defined(ENABLE_STATISTICS)
639 if (di->useddumpsize > maxdumpsize)
640 maxdumpsize = di->useddumpsize;
645 #endif /* defined(DISABLE_DUMP) */
649 /* dump_realloc ****************************************************************
651 Stupid realloc implementation for dump memory. Avoid, if possible.
653 *******************************************************************************/
655 void *dump_realloc(void *src, s4 len1, s4 len2)
657 #if defined(DISABLE_DUMP)
658 /* use malloc memory for dump memory (for debugging only!) */
660 return mem_realloc(src, len1, len2);
662 void *dst = dump_alloc(len2);
664 memcpy(dst, src, len1);
666 #if defined(ENABLE_MEMCHECK)
667 /* destroy the source */
668 memset(src, MEMORY_CLEAR_BYTE, len1);
676 /* dump_release ****************************************************************
678 Release dump memory above the given size.
681 size........All dump memory above this mark will be freed. Usually
682 `size` will be the return value of a `dump_size` call
686 XXX If the given size is invalid, this function *exits* with an
691 *******************************************************************************/
693 void dump_release(s4 size)
695 #if defined(DISABLE_DUMP)
697 /* use malloc memory for dump memory (for debugging only!) */
701 #else /* !defined(DISABLE_DUMP) */
705 /* If no threads are used, the dumpinfo structure is a static structure */
706 /* defined at the top of this file. */
710 if ((size < 0) || (size > di->useddumpsize))
711 vm_abort("Illegal dump release size: %d", size);
713 #if defined(ENABLE_MEMCHECK)
715 dump_allocation_t *da, *next;
719 dump_check_canaries(di, size);
721 /* iterate over all dump memory allocations about to be released */
723 da = di->allocations;
724 while (da && da->useddumpsize >= size) {
727 /* invalidate the freed memory */
729 memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
731 FREE(da, dump_allocation_t);
735 di->allocations = da;
737 #endif /* defined(ENABLE_MEMCHECK) */
739 /* reset the used dump size to the size specified */
741 di->useddumpsize = size;
743 while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
744 dumpblock_t *tmp = di->currentdumpblock;
746 di->allocateddumpsize -= tmp->size;
747 di->currentdumpblock = tmp->prev;
749 #if defined(ENABLE_STATISTICS)
750 /* the amount of globally allocated dump memory (thread save) */
753 globalallocateddumpsize -= tmp->size;
756 /* release the dump memory and the dumpinfo structure */
762 #endif /* defined(DISABLE_DUMP) */
766 /* dump_size *******************************************************************
768 Return the current size of the dump memory area. See `dump_alloc`.
770 *******************************************************************************/
774 #if defined(DISABLE_DUMP)
775 /* use malloc memory for dump memory (for debugging only!) */
779 #else /* !defined(DISABLE_DUMP) */
783 /* If no threads are used, the dumpinfo structure is a static structure */
784 /* defined at the top of this file. */
791 return di->useddumpsize;
793 #endif /* defined(DISABLE_DUMP) */
798 * These are local overrides for various environment variables in Emacs.
799 * Please do not remove this and leave it at the end of the file, where
800 * Emacs will automagically detect them.
801 * ---------------------------------------------------------------------
804 * indent-tabs-mode: t
808 * vim:noexpandtab:sw=4:ts=4: