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