* This is a rather huge commit, which changes the build order of
[cacao.git] / src / mm / memory.c
1 /* src/mm/memory.c - memory management
2
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
7
8    This file is part of CACAO.
9
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.
14
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.
19
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
23    02110-1301, USA.
24
25    $Id: memory.c 7246 2007-01-29 18:49:05Z twisti $
26
27 */
28
29
30 #include "config.h"
31
32 #include <assert.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/mman.h>
39
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>
44 #endif
45
46 #include "vm/types.h"
47
48 #include "arch.h"
49
50 #include "mm/memory.h"
51 #include "native/native.h"
52
53 #if defined(ENABLE_THREADS)
54 # include "threads/native/lock.h"
55 # include "threads/native/threads.h"
56 #else
57 # include "threads/none/lock.h"
58 #endif
59
60 #include "toolbox/logging.h"
61 #include "vm/exceptions.h"
62 #include "vm/global.h"
63 #include "vm/stringlocal.h"
64 #include "vm/vm.h"
65 #include "vmcore/options.h"
66
67 #if defined(ENABLE_STATISTICS)
68 # include "vmcore/statistics.h"
69 #endif
70
71
72 /* constants for ENABLE_MEMCHECK **********************************************/
73
74 #if defined(ENABLE_MEMCHECK)
75 #define MEMORY_CANARY_SIZE          16
76 #define MEMORY_CANARY_FIRST_BYTE    0xca
77 #define MEMORY_CLEAR_BYTE           0xa5
78 #endif /* defined(ENABLE_MEMCHECK) */
79
80
81 /*******************************************************************************
82
83   This structure is used for dump memory allocation if cacao
84   runs without threads.
85
86 *******************************************************************************/
87
88 #if !defined(ENABLE_THREADS)
89 static dumpinfo_t _no_threads_dumpinfo;
90 #endif
91
92 #if defined(ENABLE_THREADS)
93 #define DUMPINFO    &((threadobject *) THREADOBJECT)->dumpinfo
94 #else
95 #define DUMPINFO    &_no_threads_dumpinfo
96 #endif
97
98
99 /* global code memory variables ***********************************************/
100
101 #define DEFAULT_CODE_MEMORY_SIZE    128 * 1024 /* defaulting to 128kB         */
102
103 #if defined(ENABLE_THREADS)
104 static java_objectheader *lock_code_memory = NULL;
105 #endif
106 static void              *code_memory      = NULL;
107 static int                code_memory_size = 0;
108 static int                pagesize         = 0;
109
110
111 /* memory_init *****************************************************************
112
113    Initialize the memory subsystem.
114
115 *******************************************************************************/
116
117 bool memory_init(void)
118 {
119 #if defined(ENABLE_THREADS)
120         lock_code_memory = NEW(java_objectheader);
121
122         lock_init_object_lock(lock_code_memory);
123 #endif
124
125         /* get the pagesize of this architecture */
126
127         pagesize = getpagesize();
128
129         /* everything's ok */
130
131         return true;
132 }
133
134
135 /* memory_mmap_anon ************************************************************
136
137    Maps anonymous memory, even on systems not defining
138    MAP_ANON(YMOUS).
139
140 *******************************************************************************/
141
142 void *memory_mmap_anon(void *addr, size_t len, int prot, int flags)
143 {
144         void *p;
145
146 #if defined(MAP_ANON) || defined(MAP_ANONYMOUS)
147         p = mmap(addr, len, prot,
148 # if defined(MAP_ANON)
149                          MAP_ANON | flags,
150 # else
151                          MAP_ANONYMOUS | flags,
152 # endif
153                          -1, 0);
154 #else
155         int fd;
156
157         fd = open("/dev/zero", O_RDONLY, 0);
158
159         if (fd == -1)
160                 vm_abort("memory_mmap_anon: open failed: %s", strerror(errno));
161
162         p = mmap(addr, len, prot, flags, fd, 0);
163 #endif
164
165 #if defined(MAP_FAILED)
166         if (p == MAP_FAILED)
167 #else
168         if (p == (void *) -1)
169 #endif
170                 vm_abort("memory_mmap_anon: mmap failed: %s", strerror(errno));
171
172         return p;
173 }
174
175
176 /* memory_checked_alloc ********************************************************
177
178    Allocated zeroed-out memory and does an OOM check.
179
180    ERROR HANDLING:
181       XXX If no memory could be allocated, this function justs *exists*.
182
183 *******************************************************************************/
184
185 static void *memory_checked_alloc(s4 size)
186 {
187         /* always allocate memory zeroed out */
188
189         void *p = calloc(size, 1);
190
191         if (p == NULL)
192                 vm_abort("memory_checked_alloc: calloc failed: out of memory");
193
194         return p;
195 }
196
197
198 /* memory_cnew *****************************************************************
199
200    Allocates memory from the heap via mmap and make the memory read-,
201    write-, and executeable.
202
203 *******************************************************************************/
204
205 void *memory_cnew(s4 size)
206 {
207         void *p;
208
209         LOCK_MONITOR_ENTER(lock_code_memory);
210
211         size = MEMORY_ALIGN(size, ALIGNSIZE);
212
213         /* check if enough memory is available */
214
215         if (size > code_memory_size) {
216                 /* set default code size */
217
218                 code_memory_size = DEFAULT_CODE_MEMORY_SIZE;
219
220                 /* do we need more? */
221
222                 if (size > code_memory_size)
223                         code_memory_size = size;
224
225                 /* align the size of the memory to be allocated */
226
227                 code_memory_size = MEMORY_ALIGN(code_memory_size, pagesize);
228
229 #if defined(ENABLE_STATISTICS)
230                 if (opt_stat) {
231                         codememusage += code_memory_size;
232
233                         if (codememusage > maxcodememusage)
234                                 maxcodememusage = codememusage;
235                 }
236 #endif
237
238                 /* allocate the memory */
239
240                 p = memory_mmap_anon(NULL, code_memory_size,
241                                                          PROT_READ | PROT_WRITE | PROT_EXEC,
242                                                          MAP_PRIVATE);
243
244                 /* set global code memory pointer */
245
246                 code_memory = p;
247         }
248
249         /* get a memory chunk of the allocated memory */
250
251         p = code_memory;
252
253         code_memory       = (void *) ((ptrint) code_memory + size);
254         code_memory_size -= size;
255
256         LOCK_MONITOR_EXIT(lock_code_memory);
257
258         return p;
259 }
260
261
262 /* memory_cfree ****************************************************************
263
264    Frees the code memory pointed to.
265
266    ATTENTION: This function currently does NOTHING!  Because we don't
267    have a memory management for code memory.
268
269 *******************************************************************************/
270
271 void memory_cfree(void *p, s4 size)
272 {
273         /* do nothing */
274 }
275
276
277 void *mem_alloc(s4 size)
278 {
279         void *m;
280
281         if (size == 0)
282                 return NULL;
283
284 #if defined(ENABLE_STATISTICS)
285         if (opt_stat) {
286                 memoryusage += size;
287
288                 if (memoryusage > maxmemusage)
289                         maxmemusage = memoryusage;
290         }
291 #endif
292
293         m = memory_checked_alloc(size);
294
295 #if defined(ENABLE_MEMCHECK)
296         /* XXX we would like to poison the memory, but callers rely on */
297         /* the zeroing. This should change sooner or later.            */
298         /* memset(m, MEMORY_CLEAR_BYTE, size); */
299 #endif
300
301         return m;
302 }
303
304
305 void *mem_realloc(void *src, s4 len1, s4 len2)
306 {
307         void *dst;
308
309         /* prevent compiler warnings */
310
311         dst = NULL;
312
313         if (src == NULL)
314                 if (len1 != 0)
315                         vm_abort("mem_realloc: reallocating memoryblock with address NULL, length != 0");
316
317 #if defined(ENABLE_STATISTICS)
318         if (opt_stat)
319                 memoryusage = (memoryusage - len1) + len2;
320 #endif
321
322 #if defined(ENABLE_MEMCHECK)
323         if (len2 < len1)
324                 memset((u1*)dst + len2, MEMORY_CLEAR_BYTE, len1 - len2);
325 #endif
326
327         dst = realloc(src, len2);
328
329         if (dst == NULL)
330                 vm_abort("mem_realloc: realloc failed: out of memory");
331
332 #if defined(ENABLE_MEMCHECK)
333         if (len2 > len1)
334                 memset((u1*)dst + len1, MEMORY_CLEAR_BYTE, len2 - len1);
335 #endif
336
337         return dst;
338 }
339
340
341 void mem_free(void *m, s4 size)
342 {
343         if (!m) {
344                 if (size == 0)
345                         return;
346
347                 log_text("returned memoryblock with address NULL, length != 0");
348                 assert(0);
349         }
350
351 #if defined(ENABLE_STATISTICS)
352         if (opt_stat)
353                 memoryusage -= size;
354 #endif
355
356 #if defined(ENABLE_MEMCHECK)
357         /* destroy the contents */
358         memset(m, MEMORY_CLEAR_BYTE, size);
359 #endif
360
361         free(m);
362 }
363
364
365 /* dump_check_canaries *********************************************************
366
367    Check canaries in dump memory.
368
369    IN:
370       di...........dumpinfo_t * of the dump area to check
371           bottomsize...dump size down to which the dump area should be checked
372                        (specify 0 to check the whole dump area)
373
374    ERROR HANDLING:
375       If any canary has been changed, this function aborts the VM with
376           an error message.
377
378 *******************************************************************************/
379
380 #if defined(ENABLE_MEMCHECK)
381 void dump_check_canaries(dumpinfo_t *di, s4 bottomsize)
382 {
383         dump_allocation_t *da;
384         u1 *pm;
385         s4 i, j;
386
387         /* iterate over all dump memory allocations above bottomsize */
388
389         da = di->allocations;
390         while (da && da->useddumpsize >= bottomsize) {
391                 /* check canaries */
392
393                 pm = da->mem - MEMORY_CANARY_SIZE;
394                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
395                         if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
396                                 fprintf(stderr, "canary bytes:");
397                                 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
398                                         fprintf(stderr, " %02x", pm[j]);
399                                 fprintf(stderr,"\n");
400                                 vm_abort("error: dump memory bottom canary killed: "
401                                                  "%p (%d bytes allocated at %p)\n",
402                                                 pm + i, da->size, da->mem);
403                         }
404
405                 pm = da->mem + da->size;
406                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
407                         if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
408                                 fprintf(stderr, "canary bytes:");
409                                 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
410                                         fprintf(stderr, " %02x", pm[j]);
411                                 fprintf(stderr,"\n");
412                                 vm_abort("error: dump memory top canary killed: "
413                                                  "%p (%d bytes allocated at %p)\n",
414                                                 pm + i, da->size, da->mem);
415                         }
416
417                 da = da->next;
418         }
419 }
420 #endif /* defined(ENABLE_MEMCHECK) */
421
422
423 /* dump_alloc ******************************************************************
424
425    Allocate memory in the dump area.
426
427    IN:
428       size.........size of block to allocate, in bytes
429                                    may be zero, in which case NULL is returned
430
431    RETURN VALUE:
432       pointer to allocated memory, or
433           NULL iff `size` was zero
434
435    ERROR HANDLING:
436       XXX This function uses `memory_checked_alloc`, which *exits* if no 
437           memory could be allocated.
438
439    THREADS:
440       dump_alloc is thread safe. Each thread has its own dump memory area.
441
442    dump_alloc is a fast allocator suitable for scratch memory that can be
443    collectively freed when the current activity (eg. compiling) is done.
444
445    You cannot selectively free dump memory. Before you start allocating it, 
446    you remember the current size returned by `dump_size`. Later, when you no 
447    longer need the memory, call `dump_release` with the remembered size and
448    all dump memory allocated since the call to `dump_size` will be freed.
449
450 *******************************************************************************/
451
452 void *dump_alloc(s4 size)
453 {
454 #if defined(DISABLE_DUMP)
455
456         /* use malloc memory for dump memory (for debugging only!) */
457
458         return mem_alloc(size);
459
460 #else /* !defined(DISABLE_DUMP) */
461
462         void       *m;
463         dumpinfo_t *di;
464 #if defined(ENABLE_MEMCHECK)
465         s4          origsize = size; /* needed for the canary system */
466 #endif
467
468         /* If no threads are used, the dumpinfo structure is a static structure   */
469         /* defined at the top of this file.                                       */
470
471         di = DUMPINFO;
472
473         if (size == 0)
474                 return NULL;
475
476 #if defined(ENABLE_MEMCHECK)
477         size += 2*MEMORY_CANARY_SIZE;
478 #endif
479
480         size = MEMORY_ALIGN(size, ALIGNSIZE);
481
482         if (di->useddumpsize + size > di->allocateddumpsize) {
483                 dumpblock_t *newdumpblock;
484                 s4         newdumpblocksize;
485
486                 /* allocate a new dumplist structure */
487
488                 newdumpblock = memory_checked_alloc(sizeof(dumpblock_t));
489
490                 /* If requested size is greater than the default, make the new dump   */
491                 /* block as big as the size requested. Else use the default size.     */
492
493                 if (size > DUMPBLOCKSIZE) {
494                         newdumpblocksize = size;
495
496                 } else {
497                         newdumpblocksize = DUMPBLOCKSIZE;
498                 }
499
500                 /* allocate dumpblock memory */
501
502                 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
503
504                 newdumpblock->prev = di->currentdumpblock;
505                 newdumpblock->size = newdumpblocksize;
506                 di->currentdumpblock = newdumpblock;
507
508                 /* Used dump size is previously allocated dump size, because the      */
509                 /* remaining free memory of the previous dump block cannot be used.   */
510
511                 di->useddumpsize = di->allocateddumpsize;
512
513                 /* increase the allocated dump size by the size of the new dump block */
514
515                 di->allocateddumpsize += newdumpblocksize;
516
517 #if defined(ENABLE_STATISTICS)
518                 /* the amount of globally allocated dump memory (thread save) */
519
520                 if (opt_stat)
521                         globalallocateddumpsize += newdumpblocksize;
522 #endif
523         }
524
525         /* current dump block base address + the size of the current dump block - */
526         /* the size of the unused memory = new start address                      */
527
528         m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
529                 (di->allocateddumpsize - di->useddumpsize);
530
531 #if defined(ENABLE_MEMCHECK)
532         {
533                 dump_allocation_t *da = NEW(dump_allocation_t);
534                 s4 i;
535                 u1 *pm;
536
537                 /* add the allocation to our linked list of allocations */
538
539                 da->next = di->allocations;
540                 da->mem = (u1*) m + MEMORY_CANARY_SIZE;
541                 da->size = origsize;
542                 da->useddumpsize = di->useddumpsize;
543
544                 di->allocations = da;
545
546                 /* write the canaries */
547
548                 pm = (u1*)m;
549                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
550                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
551                 pm = da->mem + da->size;
552                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
553                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
554
555                 /* make m point after the bottom canary */
556
557                 m = (u1*)m + MEMORY_CANARY_SIZE;
558
559                 /* clear the memory */
560
561                 memset(m, MEMORY_CLEAR_BYTE, da->size);
562         }
563 #endif /* defined(ENABLE_MEMCHECK) */
564
565         /* increase used dump size by the allocated memory size */
566
567         di->useddumpsize += size;
568
569 #if defined(ENABLE_STATISTICS)
570         if (opt_stat)
571                 if (di->useddumpsize > maxdumpsize)
572                         maxdumpsize = di->useddumpsize;
573 #endif
574
575         return m;
576
577 #endif /* defined(DISABLE_DUMP) */
578 }
579
580
581 /* dump_realloc ****************************************************************
582
583    Stupid realloc implementation for dump memory. Avoid, if possible.
584
585 *******************************************************************************/
586
587 void *dump_realloc(void *src, s4 len1, s4 len2)
588 {
589 #if defined(DISABLE_DUMP)
590         /* use malloc memory for dump memory (for debugging only!) */
591
592         return mem_realloc(src, len1, len2);
593 #else
594         void *dst = dump_alloc(len2);
595
596         memcpy(dst, src, len1);
597
598 #if defined(ENABLE_MEMCHECK)
599         /* destroy the source */
600         memset(src, MEMORY_CLEAR_BYTE, len1);
601 #endif
602
603         return dst;
604 #endif
605 }
606
607
608 /* dump_release ****************************************************************
609
610    Release dump memory above the given size.
611
612    IN:
613        size........All dump memory above this mark will be freed. Usually
614                        `size` will be the return value of a `dump_size` call
615                                    made earlier.
616
617         ERROR HANDLING:
618            XXX If the given size is invalid, this function *exits* with an
619                error message.
620                                    
621         See `dump_alloc`.
622
623 *******************************************************************************/
624
625 void dump_release(s4 size)
626 {
627 #if defined(DISABLE_DUMP)
628
629         /* use malloc memory for dump memory (for debugging only!) */
630
631         /* do nothing */
632
633 #else /* !defined(DISABLE_DUMP) */
634
635         dumpinfo_t *di;
636
637         /* If no threads are used, the dumpinfo structure is a static structure   */
638         /* defined at the top of this file.                                       */
639
640         di = DUMPINFO;
641
642         if ((size < 0) || (size > di->useddumpsize))
643                 vm_abort("Illegal dump release size: %d", size);
644
645 #if defined(ENABLE_MEMCHECK)
646         {
647                 dump_allocation_t *da, *next;
648
649                 /* check canaries */
650
651                 dump_check_canaries(di, size);
652
653                 /* iterate over all dump memory allocations about to be released */
654
655                 da = di->allocations;
656                 while (da && da->useddumpsize >= size) {
657                         next = da->next;
658
659                         /* invalidate the freed memory */
660
661                         memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
662
663                         FREE(da, dump_allocation_t);
664
665                         da = next;
666                 }
667                 di->allocations = da;
668         }
669 #endif /* defined(ENABLE_MEMCHECK) */
670
671         /* reset the used dump size to the size specified */
672
673         di->useddumpsize = size;
674
675         while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
676                 dumpblock_t *tmp = di->currentdumpblock;
677
678                 di->allocateddumpsize -= tmp->size;
679                 di->currentdumpblock = tmp->prev;
680
681 #if defined(ENABLE_STATISTICS)
682                 /* the amount of globally allocated dump memory (thread save) */
683
684                 if (opt_stat)
685                         globalallocateddumpsize -= tmp->size;
686 #endif
687
688                 /* release the dump memory and the dumpinfo structure */
689
690                 free(tmp->dumpmem);
691                 free(tmp);
692         }
693
694 #endif /* defined(DISABLE_DUMP) */
695 }
696
697
698 /* dump_size *******************************************************************
699
700    Return the current size of the dump memory area. See `dump_alloc`.
701
702 *******************************************************************************/
703
704 s4 dump_size(void)
705 {
706 #if defined(DISABLE_DUMP)
707         /* use malloc memory for dump memory (for debugging only!) */
708
709         return 0;
710
711 #else /* !defined(DISABLE_DUMP) */
712
713         dumpinfo_t *di;
714
715         /* If no threads are used, the dumpinfo structure is a static structure   */
716         /* defined at the top of this file.                                       */
717
718         di = DUMPINFO;
719
720         if (di == NULL)
721                 return 0;
722
723         return di->useddumpsize;
724
725 #endif /* defined(DISABLE_DUMP) */
726 }
727
728
729 /*
730  * These are local overrides for various environment variables in Emacs.
731  * Please do not remove this and leave it at the end of the file, where
732  * Emacs will automagically detect them.
733  * ---------------------------------------------------------------------
734  * Local variables:
735  * mode: c
736  * indent-tabs-mode: t
737  * c-basic-offset: 4
738  * tab-width: 4
739  * End:
740  * vim:noexpandtab:sw=4:ts=4:
741  */