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