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