34d72150de0fd3e8bd61bd5b3e03087c4260f9c8
[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 7370 2007-02-16 18:00:03Z 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
395                 /* print GC memory usage */
396
397                 statistics_print_gc_memory_usage();
398 # endif
399         }
400 }
401 #endif
402
403
404 /* memory_start_thread *********************************************************
405
406    Starts the memory profiling thread.
407
408 *******************************************************************************/
409
410 #if defined(ENABLE_THREADS) && !defined(NDEBUG)
411 bool memory_start_thread(void)
412 {
413         utf *name;
414
415         name = utf_new_char("Memory Profiler");
416
417         thread_memory = threads_create_thread(name);
418
419         if (thread_memory == NULL)
420                 return false;
421
422         /* actually start the memory profiling thread */
423
424         threads_start_thread(thread_memory, memory_thread);
425
426         /* everything's ok */
427
428         return true;
429 }
430 #endif
431
432
433 /* dump_check_canaries *********************************************************
434
435    Check canaries in dump memory.
436
437    IN:
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)
441
442    ERROR HANDLING:
443       If any canary has been changed, this function aborts the VM with
444           an error message.
445
446 *******************************************************************************/
447
448 #if defined(ENABLE_MEMCHECK)
449 void dump_check_canaries(dumpinfo_t *di, s4 bottomsize)
450 {
451         dump_allocation_t *da;
452         u1 *pm;
453         s4 i, j;
454
455         /* iterate over all dump memory allocations above bottomsize */
456
457         da = di->allocations;
458         while (da && da->useddumpsize >= bottomsize) {
459                 /* check canaries */
460
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);
471                         }
472
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);
483                         }
484
485                 da = da->next;
486         }
487 }
488 #endif /* defined(ENABLE_MEMCHECK) */
489
490
491 /* dump_alloc ******************************************************************
492
493    Allocate memory in the dump area.
494
495    IN:
496       size.........size of block to allocate, in bytes
497                                    may be zero, in which case NULL is returned
498
499    RETURN VALUE:
500       pointer to allocated memory, or
501           NULL iff `size` was zero
502
503    ERROR HANDLING:
504       XXX This function uses `memory_checked_alloc`, which *exits* if no 
505           memory could be allocated.
506
507    THREADS:
508       dump_alloc is thread safe. Each thread has its own dump memory area.
509
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.
512
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.
517
518 *******************************************************************************/
519
520 void *dump_alloc(s4 size)
521 {
522 #if defined(DISABLE_DUMP)
523
524         /* use malloc memory for dump memory (for debugging only!) */
525
526         return mem_alloc(size);
527
528 #else /* !defined(DISABLE_DUMP) */
529
530         void       *m;
531         dumpinfo_t *di;
532 #if defined(ENABLE_MEMCHECK)
533         s4          origsize = size; /* needed for the canary system */
534 #endif
535
536         /* If no threads are used, the dumpinfo structure is a static structure   */
537         /* defined at the top of this file.                                       */
538
539         di = DUMPINFO;
540
541         if (size == 0)
542                 return NULL;
543
544 #if defined(ENABLE_MEMCHECK)
545         size += 2*MEMORY_CANARY_SIZE;
546 #endif
547
548         size = MEMORY_ALIGN(size, ALIGNSIZE);
549
550         if (di->useddumpsize + size > di->allocateddumpsize) {
551                 dumpblock_t *newdumpblock;
552                 s4         newdumpblocksize;
553
554                 /* allocate a new dumplist structure */
555
556                 newdumpblock = memory_checked_alloc(sizeof(dumpblock_t));
557
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.     */
560
561                 if (size > DUMPBLOCKSIZE) {
562                         newdumpblocksize = size;
563
564                 } else {
565                         newdumpblocksize = DUMPBLOCKSIZE;
566                 }
567
568                 /* allocate dumpblock memory */
569
570                 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
571
572                 newdumpblock->prev = di->currentdumpblock;
573                 newdumpblock->size = newdumpblocksize;
574                 di->currentdumpblock = newdumpblock;
575
576                 /* Used dump size is previously allocated dump size, because the      */
577                 /* remaining free memory of the previous dump block cannot be used.   */
578
579                 di->useddumpsize = di->allocateddumpsize;
580
581                 /* increase the allocated dump size by the size of the new dump block */
582
583                 di->allocateddumpsize += newdumpblocksize;
584
585 #if defined(ENABLE_STATISTICS)
586                 /* the amount of globally allocated dump memory (thread save) */
587
588                 if (opt_stat)
589                         globalallocateddumpsize += newdumpblocksize;
590 #endif
591         }
592
593         /* current dump block base address + the size of the current dump block - */
594         /* the size of the unused memory = new start address                      */
595
596         m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
597                 (di->allocateddumpsize - di->useddumpsize);
598
599 #if defined(ENABLE_MEMCHECK)
600         {
601                 dump_allocation_t *da = NEW(dump_allocation_t);
602                 s4 i;
603                 u1 *pm;
604
605                 /* add the allocation to our linked list of allocations */
606
607                 da->next = di->allocations;
608                 da->mem = (u1*) m + MEMORY_CANARY_SIZE;
609                 da->size = origsize;
610                 da->useddumpsize = di->useddumpsize;
611
612                 di->allocations = da;
613
614                 /* write the canaries */
615
616                 pm = (u1*)m;
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;
622
623                 /* make m point after the bottom canary */
624
625                 m = (u1*)m + MEMORY_CANARY_SIZE;
626
627                 /* clear the memory */
628
629                 memset(m, MEMORY_CLEAR_BYTE, da->size);
630         }
631 #endif /* defined(ENABLE_MEMCHECK) */
632
633         /* increase used dump size by the allocated memory size */
634
635         di->useddumpsize += size;
636
637 #if defined(ENABLE_STATISTICS)
638         if (opt_stat)
639                 if (di->useddumpsize > maxdumpsize)
640                         maxdumpsize = di->useddumpsize;
641 #endif
642
643         return m;
644
645 #endif /* defined(DISABLE_DUMP) */
646 }
647
648
649 /* dump_realloc ****************************************************************
650
651    Stupid realloc implementation for dump memory. Avoid, if possible.
652
653 *******************************************************************************/
654
655 void *dump_realloc(void *src, s4 len1, s4 len2)
656 {
657 #if defined(DISABLE_DUMP)
658         /* use malloc memory for dump memory (for debugging only!) */
659
660         return mem_realloc(src, len1, len2);
661 #else
662         void *dst = dump_alloc(len2);
663
664         memcpy(dst, src, len1);
665
666 #if defined(ENABLE_MEMCHECK)
667         /* destroy the source */
668         memset(src, MEMORY_CLEAR_BYTE, len1);
669 #endif
670
671         return dst;
672 #endif
673 }
674
675
676 /* dump_release ****************************************************************
677
678    Release dump memory above the given size.
679
680    IN:
681        size........All dump memory above this mark will be freed. Usually
682                        `size` will be the return value of a `dump_size` call
683                                    made earlier.
684
685         ERROR HANDLING:
686            XXX If the given size is invalid, this function *exits* with an
687                error message.
688                                    
689         See `dump_alloc`.
690
691 *******************************************************************************/
692
693 void dump_release(s4 size)
694 {
695 #if defined(DISABLE_DUMP)
696
697         /* use malloc memory for dump memory (for debugging only!) */
698
699         /* do nothing */
700
701 #else /* !defined(DISABLE_DUMP) */
702
703         dumpinfo_t *di;
704
705         /* If no threads are used, the dumpinfo structure is a static structure   */
706         /* defined at the top of this file.                                       */
707
708         di = DUMPINFO;
709
710         if ((size < 0) || (size > di->useddumpsize))
711                 vm_abort("Illegal dump release size: %d", size);
712
713 #if defined(ENABLE_MEMCHECK)
714         {
715                 dump_allocation_t *da, *next;
716
717                 /* check canaries */
718
719                 dump_check_canaries(di, size);
720
721                 /* iterate over all dump memory allocations about to be released */
722
723                 da = di->allocations;
724                 while (da && da->useddumpsize >= size) {
725                         next = da->next;
726
727                         /* invalidate the freed memory */
728
729                         memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
730
731                         FREE(da, dump_allocation_t);
732
733                         da = next;
734                 }
735                 di->allocations = da;
736         }
737 #endif /* defined(ENABLE_MEMCHECK) */
738
739         /* reset the used dump size to the size specified */
740
741         di->useddumpsize = size;
742
743         while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
744                 dumpblock_t *tmp = di->currentdumpblock;
745
746                 di->allocateddumpsize -= tmp->size;
747                 di->currentdumpblock = tmp->prev;
748
749 #if defined(ENABLE_STATISTICS)
750                 /* the amount of globally allocated dump memory (thread save) */
751
752                 if (opt_stat)
753                         globalallocateddumpsize -= tmp->size;
754 #endif
755
756                 /* release the dump memory and the dumpinfo structure */
757
758                 free(tmp->dumpmem);
759                 free(tmp);
760         }
761
762 #endif /* defined(DISABLE_DUMP) */
763 }
764
765
766 /* dump_size *******************************************************************
767
768    Return the current size of the dump memory area. See `dump_alloc`.
769
770 *******************************************************************************/
771
772 s4 dump_size(void)
773 {
774 #if defined(DISABLE_DUMP)
775         /* use malloc memory for dump memory (for debugging only!) */
776
777         return 0;
778
779 #else /* !defined(DISABLE_DUMP) */
780
781         dumpinfo_t *di;
782
783         /* If no threads are used, the dumpinfo structure is a static structure   */
784         /* defined at the top of this file.                                       */
785
786         di = DUMPINFO;
787
788         if (di == NULL)
789                 return 0;
790
791         return di->useddumpsize;
792
793 #endif /* defined(DISABLE_DUMP) */
794 }
795
796
797 /*
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  * ---------------------------------------------------------------------
802  * Local variables:
803  * mode: c
804  * indent-tabs-mode: t
805  * c-basic-offset: 4
806  * tab-width: 4
807  * End:
808  * vim:noexpandtab:sw=4:ts=4:
809  */