* src/threads/threads-common.c (threads_thread_create_internal):
[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 7831 2007-04-26 12:48:16Z twisti $
26
27 */
28
29
30 #include "config.h"
31
32 #include <assert.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/mman.h>
39
40 #if defined(__DARWIN__)
41 /* If we compile with -ansi on darwin, <sys/types.h> is not
42    included. So let's do it here. */
43 # include <sys/types.h>
44 #endif
45
46 #include "vm/types.h"
47
48 #include "arch.h"
49
50 #include "mm/memory.h"
51
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_objectheader *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_objectheader);
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         while (true) {
376                 /* sleep thread for 2 seconds */
377
378                 threads_sleep(2 * 1000, 0);
379
380 # if defined(ENABLE_STATISTICS)
381                 /* print memory usage */
382
383                 statistics_print_memory_usage();
384
385                 /* print GC memory usage */
386
387                 statistics_print_gc_memory_usage();
388 # endif
389         }
390 }
391 #endif
392
393
394 /* memory_start_thread *********************************************************
395
396    Starts the memory profiling thread.
397
398 *******************************************************************************/
399
400 #if defined(ENABLE_THREADS) && !defined(NDEBUG)
401 bool memory_start_thread(void)
402 {
403         utf *name;
404
405         name = utf_new_char("Memory Profiler");
406
407         /* start the memory profiling thread */
408
409         if (!threads_thread_start_internal(name, memory_thread))
410                 return false;
411
412         /* everything's ok */
413
414         return true;
415 }
416 #endif
417
418
419 /* dump_check_canaries *********************************************************
420
421    Check canaries in dump memory.
422
423    IN:
424       di...........dumpinfo_t * of the dump area to check
425           bottomsize...dump size down to which the dump area should be checked
426                        (specify 0 to check the whole dump area)
427
428    ERROR HANDLING:
429       If any canary has been changed, this function aborts the VM with
430           an error message.
431
432 *******************************************************************************/
433
434 #if defined(ENABLE_MEMCHECK)
435 void dump_check_canaries(dumpinfo_t *di, s4 bottomsize)
436 {
437         dump_allocation_t *da;
438         u1 *pm;
439         s4 i, j;
440
441         /* iterate over all dump memory allocations above bottomsize */
442
443         da = di->allocations;
444         while (da && da->useddumpsize >= bottomsize) {
445                 /* check canaries */
446
447                 pm = da->mem - MEMORY_CANARY_SIZE;
448                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
449                         if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
450                                 fprintf(stderr, "canary bytes:");
451                                 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
452                                         fprintf(stderr, " %02x", pm[j]);
453                                 fprintf(stderr,"\n");
454                                 vm_abort("error: dump memory bottom canary killed: "
455                                                  "%p (%d bytes allocated at %p)\n",
456                                                 pm + i, da->size, da->mem);
457                         }
458
459                 pm = da->mem + da->size;
460                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
461                         if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
462                                 fprintf(stderr, "canary bytes:");
463                                 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
464                                         fprintf(stderr, " %02x", pm[j]);
465                                 fprintf(stderr,"\n");
466                                 vm_abort("error: dump memory top canary killed: "
467                                                  "%p (%d bytes allocated at %p)\n",
468                                                 pm + i, da->size, da->mem);
469                         }
470
471                 da = da->next;
472         }
473 }
474 #endif /* defined(ENABLE_MEMCHECK) */
475
476
477 /* dump_alloc ******************************************************************
478
479    Allocate memory in the dump area.
480
481    IN:
482       size.........size of block to allocate, in bytes
483                                    may be zero, in which case NULL is returned
484
485    RETURN VALUE:
486       pointer to allocated memory, or
487           NULL iff `size` was zero
488
489    ERROR HANDLING:
490       XXX This function uses `memory_checked_alloc`, which *exits* if no 
491           memory could be allocated.
492
493    THREADS:
494       dump_alloc is thread safe. Each thread has its own dump memory area.
495
496    dump_alloc is a fast allocator suitable for scratch memory that can be
497    collectively freed when the current activity (eg. compiling) is done.
498
499    You cannot selectively free dump memory. Before you start allocating it, 
500    you remember the current size returned by `dump_size`. Later, when you no 
501    longer need the memory, call `dump_release` with the remembered size and
502    all dump memory allocated since the call to `dump_size` will be freed.
503
504 *******************************************************************************/
505
506 void *dump_alloc(s4 size)
507 {
508 #if defined(DISABLE_DUMP)
509
510         /* use malloc memory for dump memory (for debugging only!) */
511
512         return mem_alloc(size);
513
514 #else /* !defined(DISABLE_DUMP) */
515
516         void       *m;
517         dumpinfo_t *di;
518 #if defined(ENABLE_MEMCHECK)
519         s4          origsize = size; /* needed for the canary system */
520 #endif
521
522         /* If no threads are used, the dumpinfo structure is a static structure   */
523         /* defined at the top of this file.                                       */
524
525         di = DUMPINFO;
526
527         if (size == 0)
528                 return NULL;
529
530 #if defined(ENABLE_MEMCHECK)
531         size += 2*MEMORY_CANARY_SIZE;
532 #endif
533
534         size = MEMORY_ALIGN(size, ALIGNSIZE);
535
536         if (di->useddumpsize + size > di->allocateddumpsize) {
537                 dumpblock_t *newdumpblock;
538                 s4         newdumpblocksize;
539
540                 /* allocate a new dumplist structure */
541
542                 newdumpblock = memory_checked_alloc(sizeof(dumpblock_t));
543
544                 /* If requested size is greater than the default, make the new dump   */
545                 /* block as big as the size requested. Else use the default size.     */
546
547                 if (size > DUMPBLOCKSIZE) {
548                         newdumpblocksize = size;
549
550                 } else {
551                         newdumpblocksize = DUMPBLOCKSIZE;
552                 }
553
554                 /* allocate dumpblock memory */
555
556                 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
557
558                 newdumpblock->prev = di->currentdumpblock;
559                 newdumpblock->size = newdumpblocksize;
560                 di->currentdumpblock = newdumpblock;
561
562                 /* Used dump size is previously allocated dump size, because the      */
563                 /* remaining free memory of the previous dump block cannot be used.   */
564
565                 di->useddumpsize = di->allocateddumpsize;
566
567                 /* increase the allocated dump size by the size of the new dump block */
568
569                 di->allocateddumpsize += newdumpblocksize;
570
571 #if defined(ENABLE_STATISTICS)
572                 /* the amount of globally allocated dump memory (thread save) */
573
574                 if (opt_stat)
575                         globalallocateddumpsize += newdumpblocksize;
576 #endif
577         }
578
579         /* current dump block base address + the size of the current dump block - */
580         /* the size of the unused memory = new start address                      */
581
582         m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
583                 (di->allocateddumpsize - di->useddumpsize);
584
585 #if defined(ENABLE_MEMCHECK)
586         {
587                 dump_allocation_t *da = NEW(dump_allocation_t);
588                 s4 i;
589                 u1 *pm;
590
591                 /* add the allocation to our linked list of allocations */
592
593                 da->next = di->allocations;
594                 da->mem = (u1*) m + MEMORY_CANARY_SIZE;
595                 da->size = origsize;
596                 da->useddumpsize = di->useddumpsize;
597
598                 di->allocations = da;
599
600                 /* write the canaries */
601
602                 pm = (u1*)m;
603                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
604                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
605                 pm = da->mem + da->size;
606                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
607                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
608
609                 /* make m point after the bottom canary */
610
611                 m = (u1*)m + MEMORY_CANARY_SIZE;
612
613                 /* clear the memory */
614
615                 memset(m, MEMORY_CLEAR_BYTE, da->size);
616         }
617 #endif /* defined(ENABLE_MEMCHECK) */
618
619         /* increase used dump size by the allocated memory size */
620
621         di->useddumpsize += size;
622
623 #if defined(ENABLE_STATISTICS)
624         if (opt_stat)
625                 if (di->useddumpsize > maxdumpsize)
626                         maxdumpsize = di->useddumpsize;
627 #endif
628
629         return m;
630
631 #endif /* defined(DISABLE_DUMP) */
632 }
633
634
635 /* dump_realloc ****************************************************************
636
637    Stupid realloc implementation for dump memory. Avoid, if possible.
638
639 *******************************************************************************/
640
641 void *dump_realloc(void *src, s4 len1, s4 len2)
642 {
643 #if defined(DISABLE_DUMP)
644         /* use malloc memory for dump memory (for debugging only!) */
645
646         return mem_realloc(src, len1, len2);
647 #else
648         void *dst = dump_alloc(len2);
649
650         memcpy(dst, src, len1);
651
652 #if defined(ENABLE_MEMCHECK)
653         /* destroy the source */
654         memset(src, MEMORY_CLEAR_BYTE, len1);
655 #endif
656
657         return dst;
658 #endif
659 }
660
661
662 /* dump_release ****************************************************************
663
664    Release dump memory above the given size.
665
666    IN:
667        size........All dump memory above this mark will be freed. Usually
668                        `size` will be the return value of a `dump_size` call
669                                    made earlier.
670
671         ERROR HANDLING:
672            XXX If the given size is invalid, this function *exits* with an
673                error message.
674                                    
675         See `dump_alloc`.
676
677 *******************************************************************************/
678
679 void dump_release(s4 size)
680 {
681 #if defined(DISABLE_DUMP)
682
683         /* use malloc memory for dump memory (for debugging only!) */
684
685         /* do nothing */
686
687 #else /* !defined(DISABLE_DUMP) */
688
689         dumpinfo_t *di;
690
691         /* If no threads are used, the dumpinfo structure is a static structure   */
692         /* defined at the top of this file.                                       */
693
694         di = DUMPINFO;
695
696         if ((size < 0) || (size > di->useddumpsize))
697                 vm_abort("Illegal dump release size: %d", size);
698
699 #if defined(ENABLE_MEMCHECK)
700         {
701                 dump_allocation_t *da, *next;
702
703                 /* check canaries */
704
705                 dump_check_canaries(di, size);
706
707                 /* iterate over all dump memory allocations about to be released */
708
709                 da = di->allocations;
710                 while (da && da->useddumpsize >= size) {
711                         next = da->next;
712
713                         /* invalidate the freed memory */
714
715                         memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
716
717                         FREE(da, dump_allocation_t);
718
719                         da = next;
720                 }
721                 di->allocations = da;
722         }
723 #endif /* defined(ENABLE_MEMCHECK) */
724
725         /* reset the used dump size to the size specified */
726
727         di->useddumpsize = size;
728
729         while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
730                 dumpblock_t *tmp = di->currentdumpblock;
731
732                 di->allocateddumpsize -= tmp->size;
733                 di->currentdumpblock = tmp->prev;
734
735 #if defined(ENABLE_STATISTICS)
736                 /* the amount of globally allocated dump memory (thread save) */
737
738                 if (opt_stat)
739                         globalallocateddumpsize -= tmp->size;
740 #endif
741
742                 /* release the dump memory and the dumpinfo structure */
743
744                 free(tmp->dumpmem);
745                 free(tmp);
746         }
747
748 #endif /* defined(DISABLE_DUMP) */
749 }
750
751
752 /* dump_size *******************************************************************
753
754    Return the current size of the dump memory area. See `dump_alloc`.
755
756 *******************************************************************************/
757
758 s4 dump_size(void)
759 {
760 #if defined(DISABLE_DUMP)
761         /* use malloc memory for dump memory (for debugging only!) */
762
763         return 0;
764
765 #else /* !defined(DISABLE_DUMP) */
766
767         dumpinfo_t *di;
768
769         /* If no threads are used, the dumpinfo structure is a static structure   */
770         /* defined at the top of this file.                                       */
771
772         di = DUMPINFO;
773
774         if (di == NULL)
775                 return 0;
776
777         return di->useddumpsize;
778
779 #endif /* defined(DISABLE_DUMP) */
780 }
781
782
783 /*
784  * These are local overrides for various environment variables in Emacs.
785  * Please do not remove this and leave it at the end of the file, where
786  * Emacs will automagically detect them.
787  * ---------------------------------------------------------------------
788  * Local variables:
789  * mode: c
790  * indent-tabs-mode: t
791  * c-basic-offset: 4
792  * tab-width: 4
793  * End:
794  * vim:noexpandtab:sw=4:ts=4:
795  */