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