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