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 8295 2007-08-11 17:57:24Z michi $
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"
52 #include "native/native.h"
54 #include "threads/lock-common.h"
55 #include "threads/threads-common.h"
57 #include "toolbox/logging.h"
59 #include "vm/exceptions.h"
60 #include "vm/global.h"
61 #include "vm/stringlocal.h"
64 #include "vmcore/options.h"
66 #if defined(ENABLE_STATISTICS)
67 # include "vmcore/statistics.h"
71 /* constants for ENABLE_MEMCHECK **********************************************/
73 #if defined(ENABLE_MEMCHECK)
74 #define MEMORY_CANARY_SIZE 16
75 #define MEMORY_CANARY_FIRST_BYTE 0xca
76 #define MEMORY_CLEAR_BYTE 0xa5
77 #endif /* defined(ENABLE_MEMCHECK) */
80 /*******************************************************************************
82 This structure is used for dump memory allocation if cacao
85 *******************************************************************************/
87 #if !defined(ENABLE_THREADS)
88 static dumpinfo_t _no_threads_dumpinfo;
91 #if defined(ENABLE_THREADS)
92 #define DUMPINFO &((threadobject *) THREADOBJECT)->dumpinfo
94 #define DUMPINFO &_no_threads_dumpinfo
98 /* global code memory variables ***********************************************/
100 #define DEFAULT_CODE_MEMORY_SIZE 128 * 1024 /* defaulting to 128kB */
102 #if defined(ENABLE_THREADS)
103 static java_object_t *lock_code_memory = NULL;
105 static void *code_memory = NULL;
106 static int code_memory_size = 0;
107 static int pagesize = 0;
110 /* memory_init *****************************************************************
112 Initialize the memory subsystem.
114 *******************************************************************************/
116 bool memory_init(void)
118 #if defined(ENABLE_THREADS)
119 /* create lock for code memory */
121 lock_code_memory = NEW(java_object_t);
123 lock_init_object_lock(lock_code_memory);
126 /* get the pagesize of this architecture */
128 pagesize = getpagesize();
130 /* everything's ok */
136 /* memory_mmap_anon ************************************************************
138 Maps anonymous memory, even on systems not defining
141 *******************************************************************************/
143 void *memory_mmap_anon(void *addr, size_t len, int prot, int flags)
147 #if defined(MAP_ANON) || defined(MAP_ANONYMOUS)
148 p = mmap(addr, len, prot,
149 # if defined(MAP_ANON)
152 MAP_ANONYMOUS | flags,
158 fd = open("/dev/zero", O_RDONLY, 0);
161 vm_abort("memory_mmap_anon: open failed: %s", strerror(errno));
163 p = mmap(addr, len, prot, flags, fd, 0);
166 #if defined(MAP_FAILED)
169 if (p == (void *) -1)
171 vm_abort("memory_mmap_anon: mmap failed: %s", strerror(errno));
177 /* memory_checked_alloc ********************************************************
179 Allocated zeroed-out memory and does an OOM check.
182 XXX If no memory could be allocated, this function justs *exists*.
184 *******************************************************************************/
186 static void *memory_checked_alloc(s4 size)
188 /* always allocate memory zeroed out */
190 void *p = calloc(size, 1);
193 vm_abort("memory_checked_alloc: calloc failed: out of memory");
199 /* memory_cnew *****************************************************************
201 Allocates memory from the heap via mmap and make the memory read-,
202 write-, and executeable.
204 *******************************************************************************/
206 void *memory_cnew(s4 size)
210 LOCK_MONITOR_ENTER(lock_code_memory);
212 size = MEMORY_ALIGN(size, ALIGNSIZE);
214 /* check if enough memory is available */
216 if (size > code_memory_size) {
217 /* set default code size */
219 code_memory_size = DEFAULT_CODE_MEMORY_SIZE;
221 /* do we need more? */
223 if (size > code_memory_size)
224 code_memory_size = size;
226 /* align the size of the memory to be allocated */
228 code_memory_size = MEMORY_ALIGN(code_memory_size, pagesize);
230 #if defined(ENABLE_STATISTICS)
232 codememusage += code_memory_size;
234 if (codememusage > maxcodememusage)
235 maxcodememusage = codememusage;
239 /* allocate the memory */
241 p = memory_mmap_anon(NULL, code_memory_size,
242 PROT_READ | PROT_WRITE | PROT_EXEC,
245 /* set global code memory pointer */
250 /* get a memory chunk of the allocated memory */
254 code_memory = (void *) ((ptrint) code_memory + size);
255 code_memory_size -= size;
257 LOCK_MONITOR_EXIT(lock_code_memory);
263 /* memory_cfree ****************************************************************
265 Frees the code memory pointed to.
267 ATTENTION: This function currently does NOTHING! Because we don't
268 have a memory management for code memory.
270 *******************************************************************************/
272 void memory_cfree(void *p, s4 size)
278 void *mem_alloc(s4 size)
285 #if defined(ENABLE_STATISTICS)
289 if (memoryusage > maxmemusage)
290 maxmemusage = memoryusage;
294 m = memory_checked_alloc(size);
296 #if defined(ENABLE_MEMCHECK)
297 /* XXX we would like to poison the memory, but callers rely on */
298 /* the zeroing. This should change sooner or later. */
299 /* memset(m, MEMORY_CLEAR_BYTE, size); */
306 void *mem_realloc(void *src, s4 len1, s4 len2)
310 /* prevent compiler warnings */
316 vm_abort("mem_realloc: reallocating memoryblock with address NULL, length != 0");
318 #if defined(ENABLE_STATISTICS)
320 memoryusage = (memoryusage - len1) + len2;
323 #if defined(ENABLE_MEMCHECK)
325 memset((u1*)dst + len2, MEMORY_CLEAR_BYTE, len1 - len2);
328 dst = realloc(src, len2);
331 vm_abort("mem_realloc: realloc failed: out of memory");
333 #if defined(ENABLE_MEMCHECK)
335 memset((u1*)dst + len1, MEMORY_CLEAR_BYTE, len2 - len1);
342 void mem_free(void *m, s4 size)
348 log_text("returned memoryblock with address NULL, length != 0");
352 #if defined(ENABLE_STATISTICS)
357 #if defined(ENABLE_MEMCHECK)
358 /* destroy the contents */
359 memset(m, MEMORY_CLEAR_BYTE, size);
366 /* memory_thread ***************************************************************
368 Prints regularly memory statistics.
370 *******************************************************************************/
372 #if defined(ENABLE_THREADS) && !defined(NDEBUG)
373 static void memory_thread(void)
377 /* If both arguments are specified, use the value of
378 ProfileMemoryUsage. */
380 if (opt_ProfileGCMemoryUsage)
381 seconds = opt_ProfileGCMemoryUsage;
383 if (opt_ProfileMemoryUsage)
384 seconds = opt_ProfileMemoryUsage;
389 threads_sleep(seconds * 1000, 0);
391 # if defined(ENABLE_STATISTICS)
392 /* Print current date and time (only when we print to the
395 if (!opt_ProfileMemoryUsageGNUPlot)
396 statistics_print_date();
398 /* print memory usage */
400 if (opt_ProfileMemoryUsage)
401 statistics_print_memory_usage();
403 /* print GC memory usage */
405 if (opt_ProfileGCMemoryUsage)
406 statistics_print_gc_memory_usage();
413 /* memory_start_thread *********************************************************
415 Starts the memory profiling thread.
417 *******************************************************************************/
419 #if defined(ENABLE_THREADS) && !defined(NDEBUG)
420 bool memory_start_thread(void)
424 name = utf_new_char("Memory Profiler");
426 /* start the memory profiling thread */
428 if (!threads_thread_start_internal(name, memory_thread))
431 /* everything's ok */
438 /* dump_check_canaries *********************************************************
440 Check canaries in dump memory.
443 di...........dumpinfo_t * of the dump area to check
444 bottomsize...dump size down to which the dump area should be checked
445 (specify 0 to check the whole dump area)
448 If any canary has been changed, this function aborts the VM with
451 *******************************************************************************/
453 #if defined(ENABLE_MEMCHECK)
454 void dump_check_canaries(dumpinfo_t *di, s4 bottomsize)
456 dump_allocation_t *da;
460 /* iterate over all dump memory allocations above bottomsize */
462 da = di->allocations;
463 while (da && da->useddumpsize >= bottomsize) {
466 pm = da->mem - MEMORY_CANARY_SIZE;
467 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
468 if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
469 fprintf(stderr, "canary bytes:");
470 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
471 fprintf(stderr, " %02x", pm[j]);
472 fprintf(stderr,"\n");
473 vm_abort("error: dump memory bottom canary killed: "
474 "%p (%d bytes allocated at %p)\n",
475 pm + i, da->size, da->mem);
478 pm = da->mem + da->size;
479 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
480 if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
481 fprintf(stderr, "canary bytes:");
482 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
483 fprintf(stderr, " %02x", pm[j]);
484 fprintf(stderr,"\n");
485 vm_abort("error: dump memory top canary killed: "
486 "%p (%d bytes allocated at %p)\n",
487 pm + i, da->size, da->mem);
493 #endif /* defined(ENABLE_MEMCHECK) */
496 /* dump_alloc ******************************************************************
498 Allocate memory in the dump area.
501 size.........size of block to allocate, in bytes
502 may be zero, in which case NULL is returned
505 pointer to allocated memory, or
506 NULL iff `size` was zero
509 XXX This function uses `memory_checked_alloc`, which *exits* if no
510 memory could be allocated.
513 dump_alloc is thread safe. Each thread has its own dump memory area.
515 dump_alloc is a fast allocator suitable for scratch memory that can be
516 collectively freed when the current activity (eg. compiling) is done.
518 You cannot selectively free dump memory. Before you start allocating it,
519 you remember the current size returned by `dump_size`. Later, when you no
520 longer need the memory, call `dump_release` with the remembered size and
521 all dump memory allocated since the call to `dump_size` will be freed.
523 *******************************************************************************/
525 void *dump_alloc(s4 size)
527 #if defined(DISABLE_DUMP)
529 /* use malloc memory for dump memory (for debugging only!) */
531 return mem_alloc(size);
533 #else /* !defined(DISABLE_DUMP) */
537 #if defined(ENABLE_MEMCHECK)
538 s4 origsize = size; /* needed for the canary system */
541 /* If no threads are used, the dumpinfo structure is a static structure */
542 /* defined at the top of this file. */
549 #if defined(ENABLE_MEMCHECK)
550 size += 2*MEMORY_CANARY_SIZE;
553 size = MEMORY_ALIGN(size, ALIGNSIZE);
555 if (di->useddumpsize + size > di->allocateddumpsize) {
556 dumpblock_t *newdumpblock;
559 /* allocate a new dumplist structure */
561 newdumpblock = memory_checked_alloc(sizeof(dumpblock_t));
563 /* If requested size is greater than the default, make the new dump */
564 /* block as big as the size requested. Else use the default size. */
566 if (size > DUMPBLOCKSIZE) {
567 newdumpblocksize = size;
570 newdumpblocksize = DUMPBLOCKSIZE;
573 /* allocate dumpblock memory */
575 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
577 newdumpblock->prev = di->currentdumpblock;
578 newdumpblock->size = newdumpblocksize;
579 di->currentdumpblock = newdumpblock;
581 /* Used dump size is previously allocated dump size, because the */
582 /* remaining free memory of the previous dump block cannot be used. */
584 di->useddumpsize = di->allocateddumpsize;
586 /* increase the allocated dump size by the size of the new dump block */
588 di->allocateddumpsize += newdumpblocksize;
590 #if defined(ENABLE_STATISTICS)
591 /* the amount of globally allocated dump memory (thread save) */
594 globalallocateddumpsize += newdumpblocksize;
598 /* current dump block base address + the size of the current dump block - */
599 /* the size of the unused memory = new start address */
601 m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
602 (di->allocateddumpsize - di->useddumpsize);
604 #if defined(ENABLE_MEMCHECK)
606 dump_allocation_t *da = NEW(dump_allocation_t);
610 /* add the allocation to our linked list of allocations */
612 da->next = di->allocations;
613 da->mem = (u1*) m + MEMORY_CANARY_SIZE;
615 da->useddumpsize = di->useddumpsize;
617 di->allocations = da;
619 /* write the canaries */
622 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
623 pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
624 pm = da->mem + da->size;
625 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
626 pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
628 /* make m point after the bottom canary */
630 m = (u1*)m + MEMORY_CANARY_SIZE;
632 /* clear the memory */
634 memset(m, MEMORY_CLEAR_BYTE, da->size);
636 #endif /* defined(ENABLE_MEMCHECK) */
638 /* increase used dump size by the allocated memory size */
640 di->useddumpsize += size;
642 #if defined(ENABLE_STATISTICS)
644 if (di->useddumpsize > maxdumpsize)
645 maxdumpsize = di->useddumpsize;
650 #endif /* defined(DISABLE_DUMP) */
654 /* dump_realloc ****************************************************************
656 Stupid realloc implementation for dump memory. Avoid, if possible.
658 *******************************************************************************/
660 void *dump_realloc(void *src, s4 len1, s4 len2)
662 #if defined(DISABLE_DUMP)
663 /* use malloc memory for dump memory (for debugging only!) */
665 return mem_realloc(src, len1, len2);
667 void *dst = dump_alloc(len2);
669 memcpy(dst, src, len1);
671 #if defined(ENABLE_MEMCHECK)
672 /* destroy the source */
673 memset(src, MEMORY_CLEAR_BYTE, len1);
681 /* dump_release ****************************************************************
683 Release dump memory above the given size.
686 size........All dump memory above this mark will be freed. Usually
687 `size` will be the return value of a `dump_size` call
691 XXX If the given size is invalid, this function *exits* with an
696 *******************************************************************************/
698 void dump_release(s4 size)
700 #if defined(DISABLE_DUMP)
702 /* use malloc memory for dump memory (for debugging only!) */
706 #else /* !defined(DISABLE_DUMP) */
710 /* If no threads are used, the dumpinfo structure is a static structure */
711 /* defined at the top of this file. */
715 if ((size < 0) || (size > di->useddumpsize))
716 vm_abort("Illegal dump release size: %d", size);
718 #if defined(ENABLE_MEMCHECK)
720 dump_allocation_t *da, *next;
724 dump_check_canaries(di, size);
726 /* iterate over all dump memory allocations about to be released */
728 da = di->allocations;
729 while (da && da->useddumpsize >= size) {
732 /* invalidate the freed memory */
734 memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
736 FREE(da, dump_allocation_t);
740 di->allocations = da;
742 #endif /* defined(ENABLE_MEMCHECK) */
744 /* reset the used dump size to the size specified */
746 di->useddumpsize = size;
748 while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
749 dumpblock_t *tmp = di->currentdumpblock;
751 di->allocateddumpsize -= tmp->size;
752 di->currentdumpblock = tmp->prev;
754 #if defined(ENABLE_STATISTICS)
755 /* the amount of globally allocated dump memory (thread save) */
758 globalallocateddumpsize -= tmp->size;
761 /* release the dump memory and the dumpinfo structure */
767 #endif /* defined(DISABLE_DUMP) */
771 /* dump_size *******************************************************************
773 Return the current size of the dump memory area. See `dump_alloc`.
775 *******************************************************************************/
779 #if defined(DISABLE_DUMP)
780 /* use malloc memory for dump memory (for debugging only!) */
784 #else /* !defined(DISABLE_DUMP) */
788 /* If no threads are used, the dumpinfo structure is a static structure */
789 /* defined at the top of this file. */
796 return di->useddumpsize;
798 #endif /* defined(DISABLE_DUMP) */
803 * These are local overrides for various environment variables in Emacs.
804 * Please do not remove this and leave it at the end of the file, where
805 * Emacs will automagically detect them.
806 * ---------------------------------------------------------------------
809 * indent-tabs-mode: t
813 * vim:noexpandtab:sw=4:ts=4: