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