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
38 #if defined(__DARWIN__)
39 /* If we compile with -ansi on darwin, <sys/types.h> is not
40 included. So let's do it here. */
41 # include <sys/types.h>
48 #include "mm/memory.h"
50 #include "native/native.h"
52 #include "threads/lock-common.h"
53 #include "threads/threads-common.h"
55 #include "toolbox/logging.h"
57 #include "vm/exceptions.h"
58 #include "vm/global.h"
59 #include "vm/stringlocal.h"
62 #include "vmcore/options.h"
64 #if defined(ENABLE_STATISTICS)
65 # include "vmcore/statistics.h"
69 /* constants for ENABLE_MEMCHECK **********************************************/
71 #if defined(ENABLE_MEMCHECK)
72 #define MEMORY_CANARY_SIZE 16
73 #define MEMORY_CANARY_FIRST_BYTE 0xca
74 #define MEMORY_CLEAR_BYTE 0xa5
75 #endif /* defined(ENABLE_MEMCHECK) */
78 /*******************************************************************************
80 This structure is used for dump memory allocation if cacao
83 *******************************************************************************/
85 #if !defined(ENABLE_THREADS)
86 static dumpinfo_t _no_threads_dumpinfo;
89 #if defined(ENABLE_THREADS)
90 #define DUMPINFO &((threadobject *) THREADOBJECT)->dumpinfo
92 #define DUMPINFO &_no_threads_dumpinfo
96 /* global code memory variables ***********************************************/
98 #define DEFAULT_CODE_MEMORY_SIZE 128 * 1024 /* defaulting to 128kB */
100 #if defined(ENABLE_THREADS)
101 static java_object_t *lock_code_memory = NULL;
103 static void *code_memory = NULL;
104 static int code_memory_size = 0;
105 static int pagesize = 0;
108 /* memory_init *****************************************************************
110 Initialize the memory subsystem.
112 *******************************************************************************/
114 bool memory_init(void)
116 #if defined(ENABLE_THREADS)
117 /* create lock for code memory */
119 lock_code_memory = NEW(java_object_t);
121 lock_init_object_lock(lock_code_memory);
124 /* get the pagesize of this architecture */
126 pagesize = getpagesize();
128 /* everything's ok */
134 /* memory_mmap_anon ************************************************************
136 Maps anonymous memory, even on systems not defining
139 *******************************************************************************/
141 void *memory_mmap_anon(void *addr, size_t len, int prot, int flags)
145 #if defined(MAP_ANON) || defined(MAP_ANONYMOUS)
146 p = mmap(addr, len, prot,
147 # if defined(MAP_ANON)
150 MAP_ANONYMOUS | flags,
156 fd = open("/dev/zero", O_RDONLY, 0);
159 vm_abort("memory_mmap_anon: open failed: %s", strerror(errno));
161 p = mmap(addr, len, prot, flags, fd, 0);
164 #if defined(MAP_FAILED)
167 if (p == (void *) -1)
169 vm_abort("memory_mmap_anon: mmap failed: %s", strerror(errno));
175 /* memory_checked_alloc ********************************************************
177 Allocated zeroed-out memory and does an OOM check.
180 XXX If no memory could be allocated, this function justs *exists*.
182 *******************************************************************************/
184 static void *memory_checked_alloc(s4 size)
186 /* always allocate memory zeroed out */
188 void *p = calloc(size, 1);
191 vm_abort("memory_checked_alloc: calloc failed: out of memory");
197 /* memory_cnew *****************************************************************
199 Allocates memory from the heap via mmap and make the memory read-,
200 write-, and executeable.
202 *******************************************************************************/
204 void *memory_cnew(s4 size)
208 LOCK_MONITOR_ENTER(lock_code_memory);
210 size = MEMORY_ALIGN(size, ALIGNSIZE);
212 /* check if enough memory is available */
214 if (size > code_memory_size) {
215 /* set default code size */
217 code_memory_size = DEFAULT_CODE_MEMORY_SIZE;
219 /* do we need more? */
221 if (size > code_memory_size)
222 code_memory_size = size;
224 /* align the size of the memory to be allocated */
226 code_memory_size = MEMORY_ALIGN(code_memory_size, pagesize);
228 #if defined(ENABLE_STATISTICS)
230 codememusage += code_memory_size;
232 if (codememusage > maxcodememusage)
233 maxcodememusage = codememusage;
237 /* allocate the memory */
239 p = memory_mmap_anon(NULL, code_memory_size,
240 PROT_READ | PROT_WRITE | PROT_EXEC,
243 /* set global code memory pointer */
248 /* get a memory chunk of the allocated memory */
252 code_memory = (void *) ((ptrint) code_memory + size);
253 code_memory_size -= size;
255 LOCK_MONITOR_EXIT(lock_code_memory);
261 /* memory_cfree ****************************************************************
263 Frees the code memory pointed to.
265 ATTENTION: This function currently does NOTHING! Because we don't
266 have a memory management for code memory.
268 *******************************************************************************/
270 void memory_cfree(void *p, s4 size)
276 void *mem_alloc(s4 size)
283 #if defined(ENABLE_STATISTICS)
287 if (memoryusage > maxmemusage)
288 maxmemusage = memoryusage;
292 m = memory_checked_alloc(size);
294 #if defined(ENABLE_MEMCHECK)
295 /* XXX we would like to poison the memory, but callers rely on */
296 /* the zeroing. This should change sooner or later. */
297 /* memset(m, MEMORY_CLEAR_BYTE, size); */
304 void *mem_realloc(void *src, s4 len1, s4 len2)
308 /* prevent compiler warnings */
314 vm_abort("mem_realloc: reallocating memoryblock with address NULL, length != 0");
316 #if defined(ENABLE_STATISTICS)
318 memoryusage = (memoryusage - len1) + len2;
321 #if defined(ENABLE_MEMCHECK)
323 memset((u1*)dst + len2, MEMORY_CLEAR_BYTE, len1 - len2);
326 dst = realloc(src, len2);
329 vm_abort("mem_realloc: realloc failed: out of memory");
331 #if defined(ENABLE_MEMCHECK)
333 memset((u1*)dst + len1, MEMORY_CLEAR_BYTE, len2 - len1);
340 void mem_free(void *m, s4 size)
346 log_text("returned memoryblock with address NULL, length != 0");
350 #if defined(ENABLE_STATISTICS)
355 #if defined(ENABLE_MEMCHECK)
356 /* destroy the contents */
357 memset(m, MEMORY_CLEAR_BYTE, size);
364 /* memory_thread ***************************************************************
366 Prints regularly memory statistics.
368 *******************************************************************************/
370 #if defined(ENABLE_THREADS) && !defined(NDEBUG)
371 static void memory_thread(void)
375 /* If both arguments are specified, use the value of
376 ProfileMemoryUsage. */
378 if (opt_ProfileGCMemoryUsage)
379 seconds = opt_ProfileGCMemoryUsage;
381 if (opt_ProfileMemoryUsage)
382 seconds = opt_ProfileMemoryUsage;
387 threads_sleep(seconds * 1000, 0);
389 # if defined(ENABLE_STATISTICS)
390 /* Print current date and time (only when we print to the
393 if (!opt_ProfileMemoryUsageGNUPlot)
394 statistics_print_date();
396 /* print memory usage */
398 if (opt_ProfileMemoryUsage)
399 statistics_print_memory_usage();
401 /* print GC memory usage */
403 if (opt_ProfileGCMemoryUsage)
404 statistics_print_gc_memory_usage();
411 /* memory_start_thread *********************************************************
413 Starts the memory profiling thread.
415 *******************************************************************************/
417 #if defined(ENABLE_THREADS) && !defined(NDEBUG)
418 bool memory_start_thread(void)
422 name = utf_new_char("Memory Profiler");
424 /* start the memory profiling thread */
426 if (!threads_thread_start_internal(name, memory_thread))
429 /* everything's ok */
436 /* dump_check_canaries *********************************************************
438 Check canaries in dump memory.
441 di...........dumpinfo_t * of the dump area to check
442 bottomsize...dump size down to which the dump area should be checked
443 (specify 0 to check the whole dump area)
446 If any canary has been changed, this function aborts the VM with
449 *******************************************************************************/
451 #if defined(ENABLE_MEMCHECK)
452 void dump_check_canaries(dumpinfo_t *di, s4 bottomsize)
454 dump_allocation_t *da;
458 /* iterate over all dump memory allocations above bottomsize */
460 da = di->allocations;
461 while (da && da->useddumpsize >= bottomsize) {
464 pm = da->mem - MEMORY_CANARY_SIZE;
465 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
466 if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
467 fprintf(stderr, "canary bytes:");
468 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
469 fprintf(stderr, " %02x", pm[j]);
470 fprintf(stderr,"\n");
471 vm_abort("error: dump memory bottom canary killed: "
472 "%p (%d bytes allocated at %p)\n",
473 pm + i, da->size, da->mem);
476 pm = da->mem + da->size;
477 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
478 if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
479 fprintf(stderr, "canary bytes:");
480 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
481 fprintf(stderr, " %02x", pm[j]);
482 fprintf(stderr,"\n");
483 vm_abort("error: dump memory top canary killed: "
484 "%p (%d bytes allocated at %p)\n",
485 pm + i, da->size, da->mem);
491 #endif /* defined(ENABLE_MEMCHECK) */
494 /* dump_alloc ******************************************************************
496 Allocate memory in the dump area.
499 size.........size of block to allocate, in bytes
500 may be zero, in which case NULL is returned
503 pointer to allocated memory, or
504 NULL iff `size` was zero
507 XXX This function uses `memory_checked_alloc`, which *exits* if no
508 memory could be allocated.
511 dump_alloc is thread safe. Each thread has its own dump memory area.
513 dump_alloc is a fast allocator suitable for scratch memory that can be
514 collectively freed when the current activity (eg. compiling) is done.
516 You cannot selectively free dump memory. Before you start allocating it,
517 you remember the current size returned by `dump_size`. Later, when you no
518 longer need the memory, call `dump_release` with the remembered size and
519 all dump memory allocated since the call to `dump_size` will be freed.
521 *******************************************************************************/
523 void *dump_alloc(s4 size)
525 #if defined(DISABLE_DUMP)
527 /* use malloc memory for dump memory (for debugging only!) */
529 return mem_alloc(size);
531 #else /* !defined(DISABLE_DUMP) */
535 #if defined(ENABLE_MEMCHECK)
536 s4 origsize = size; /* needed for the canary system */
539 /* If no threads are used, the dumpinfo structure is a static structure */
540 /* defined at the top of this file. */
547 #if defined(ENABLE_MEMCHECK)
548 size += 2*MEMORY_CANARY_SIZE;
551 size = MEMORY_ALIGN(size, ALIGNSIZE);
553 if (di->useddumpsize + size > di->allocateddumpsize) {
554 dumpblock_t *newdumpblock;
557 /* allocate a new dumplist structure */
559 newdumpblock = memory_checked_alloc(sizeof(dumpblock_t));
561 /* If requested size is greater than the default, make the new dump */
562 /* block as big as the size requested. Else use the default size. */
564 if (size > DUMPBLOCKSIZE) {
565 newdumpblocksize = size;
568 newdumpblocksize = DUMPBLOCKSIZE;
571 /* allocate dumpblock memory */
573 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
575 newdumpblock->prev = di->currentdumpblock;
576 newdumpblock->size = newdumpblocksize;
577 di->currentdumpblock = newdumpblock;
579 /* Used dump size is previously allocated dump size, because the */
580 /* remaining free memory of the previous dump block cannot be used. */
582 di->useddumpsize = di->allocateddumpsize;
584 /* increase the allocated dump size by the size of the new dump block */
586 di->allocateddumpsize += newdumpblocksize;
588 #if defined(ENABLE_STATISTICS)
589 /* the amount of globally allocated dump memory (thread save) */
592 globalallocateddumpsize += newdumpblocksize;
596 /* current dump block base address + the size of the current dump block - */
597 /* the size of the unused memory = new start address */
599 m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
600 (di->allocateddumpsize - di->useddumpsize);
602 #if defined(ENABLE_MEMCHECK)
604 dump_allocation_t *da = NEW(dump_allocation_t);
608 /* add the allocation to our linked list of allocations */
610 da->next = di->allocations;
611 da->mem = (u1*) m + MEMORY_CANARY_SIZE;
613 da->useddumpsize = di->useddumpsize;
615 di->allocations = da;
617 /* write the canaries */
620 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
621 pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
622 pm = da->mem + da->size;
623 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
624 pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
626 /* make m point after the bottom canary */
628 m = (u1*)m + MEMORY_CANARY_SIZE;
630 /* clear the memory */
632 memset(m, MEMORY_CLEAR_BYTE, da->size);
634 #endif /* defined(ENABLE_MEMCHECK) */
636 /* increase used dump size by the allocated memory size */
638 di->useddumpsize += size;
640 #if defined(ENABLE_STATISTICS)
642 if (di->useddumpsize > maxdumpsize)
643 maxdumpsize = di->useddumpsize;
648 #endif /* defined(DISABLE_DUMP) */
652 /* dump_realloc ****************************************************************
654 Stupid realloc implementation for dump memory. Avoid, if possible.
656 *******************************************************************************/
658 void *dump_realloc(void *src, s4 len1, s4 len2)
660 #if defined(DISABLE_DUMP)
661 /* use malloc memory for dump memory (for debugging only!) */
663 return mem_realloc(src, len1, len2);
665 void *dst = dump_alloc(len2);
667 memcpy(dst, src, len1);
669 #if defined(ENABLE_MEMCHECK)
670 /* destroy the source */
671 memset(src, MEMORY_CLEAR_BYTE, len1);
679 /* dump_release ****************************************************************
681 Release dump memory above the given size.
684 size........All dump memory above this mark will be freed. Usually
685 `size` will be the return value of a `dump_size` call
689 XXX If the given size is invalid, this function *exits* with an
694 *******************************************************************************/
696 void dump_release(s4 size)
698 #if defined(DISABLE_DUMP)
700 /* use malloc memory for dump memory (for debugging only!) */
704 #else /* !defined(DISABLE_DUMP) */
708 /* If no threads are used, the dumpinfo structure is a static structure */
709 /* defined at the top of this file. */
713 if ((size < 0) || (size > di->useddumpsize))
714 vm_abort("Illegal dump release size: %d", size);
716 #if defined(ENABLE_MEMCHECK)
718 dump_allocation_t *da, *next;
722 dump_check_canaries(di, size);
724 /* iterate over all dump memory allocations about to be released */
726 da = di->allocations;
727 while (da && da->useddumpsize >= size) {
730 /* invalidate the freed memory */
732 memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
734 FREE(da, dump_allocation_t);
738 di->allocations = da;
740 #endif /* defined(ENABLE_MEMCHECK) */
742 /* reset the used dump size to the size specified */
744 di->useddumpsize = size;
746 while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
747 dumpblock_t *tmp = di->currentdumpblock;
749 di->allocateddumpsize -= tmp->size;
750 di->currentdumpblock = tmp->prev;
752 #if defined(ENABLE_STATISTICS)
753 /* the amount of globally allocated dump memory (thread save) */
756 globalallocateddumpsize -= tmp->size;
759 /* release the dump memory and the dumpinfo structure */
765 #endif /* defined(DISABLE_DUMP) */
769 /* dump_size *******************************************************************
771 Return the current size of the dump memory area. See `dump_alloc`.
773 *******************************************************************************/
777 #if defined(DISABLE_DUMP)
778 /* use malloc memory for dump memory (for debugging only!) */
782 #else /* !defined(DISABLE_DUMP) */
786 /* If no threads are used, the dumpinfo structure is a static structure */
787 /* defined at the top of this file. */
794 return di->useddumpsize;
796 #endif /* defined(DISABLE_DUMP) */
801 * These are local overrides for various environment variables in Emacs.
802 * Please do not remove this and leave it at the end of the file, where
803 * Emacs will automagically detect them.
804 * ---------------------------------------------------------------------
807 * indent-tabs-mode: t
811 * vim:noexpandtab:sw=4:ts=4: