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