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