* src/vm/jit/asmpart.h (asm_compare_and_swap): Added.
[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 7205 2007-01-11 22:36:29Z 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         /* prevent compiler warnings */
313
314         dst = NULL;
315
316         if (src == NULL)
317                 if (len1 != 0)
318                         vm_abort("mem_realloc: reallocating memoryblock with address NULL, length != 0");
319
320 #if defined(ENABLE_STATISTICS)
321         if (opt_stat)
322                 memoryusage = (memoryusage - len1) + len2;
323 #endif
324
325 #if defined(ENABLE_MEMCHECK)
326         if (len2 < len1)
327                 memset((u1*)dst + len2, MEMORY_CLEAR_BYTE, len1 - len2);
328 #endif
329
330         dst = realloc(src, len2);
331
332         if (dst == NULL)
333                 vm_abort("mem_realloc: realloc failed: out of memory");
334
335 #if defined(ENABLE_MEMCHECK)
336         if (len2 > len1)
337                 memset((u1*)dst + len1, MEMORY_CLEAR_BYTE, len2 - len1);
338 #endif
339
340         return dst;
341 }
342
343
344 void mem_free(void *m, s4 size)
345 {
346         if (!m) {
347                 if (size == 0)
348                         return;
349
350                 log_text("returned memoryblock with address NULL, length != 0");
351                 assert(0);
352         }
353
354 #if defined(ENABLE_STATISTICS)
355         if (opt_stat)
356                 memoryusage -= size;
357 #endif
358
359 #if defined(ENABLE_MEMCHECK)
360         /* destroy the contents */
361         memset(m, MEMORY_CLEAR_BYTE, size);
362 #endif
363
364         free(m);
365 }
366
367
368 /* dump_check_canaries *********************************************************
369
370    Check canaries in dump memory.
371
372    IN:
373       di...........dumpinfo_t * of the dump area to check
374           bottomsize...dump size down to which the dump area should be checked
375                        (specify 0 to check the whole dump area)
376
377    ERROR HANDLING:
378       If any canary has been changed, this function aborts the VM with
379           an error message.
380
381 *******************************************************************************/
382
383 #if defined(ENABLE_MEMCHECK)
384 void dump_check_canaries(dumpinfo_t *di, s4 bottomsize)
385 {
386         dump_allocation_t *da;
387         u1 *pm;
388         s4 i, j;
389
390         /* iterate over all dump memory allocations above bottomsize */
391
392         da = di->allocations;
393         while (da && da->useddumpsize >= bottomsize) {
394                 /* check canaries */
395
396                 pm = da->mem - MEMORY_CANARY_SIZE;
397                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
398                         if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
399                                 fprintf(stderr, "canary bytes:");
400                                 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
401                                         fprintf(stderr, " %02x", pm[j]);
402                                 fprintf(stderr,"\n");
403                                 vm_abort("error: dump memory bottom canary killed: "
404                                                  "%p (%d bytes allocated at %p)\n",
405                                                 pm + i, da->size, da->mem);
406                         }
407
408                 pm = da->mem + da->size;
409                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
410                         if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
411                                 fprintf(stderr, "canary bytes:");
412                                 for (j=0; j<MEMORY_CANARY_SIZE; ++j)
413                                         fprintf(stderr, " %02x", pm[j]);
414                                 fprintf(stderr,"\n");
415                                 vm_abort("error: dump memory top canary killed: "
416                                                  "%p (%d bytes allocated at %p)\n",
417                                                 pm + i, da->size, da->mem);
418                         }
419
420                 da = da->next;
421         }
422 }
423 #endif /* defined(ENABLE_MEMCHECK) */
424
425
426 /* dump_alloc ******************************************************************
427
428    Allocate memory in the dump area.
429
430    IN:
431       size.........size of block to allocate, in bytes
432                                    may be zero, in which case NULL is returned
433
434    RETURN VALUE:
435       pointer to allocated memory, or
436           NULL iff `size` was zero
437
438    ERROR HANDLING:
439       XXX This function uses `memory_checked_alloc`, which *exits* if no 
440           memory could be allocated.
441
442    THREADS:
443       dump_alloc is thread safe. Each thread has its own dump memory area.
444
445    dump_alloc is a fast allocator suitable for scratch memory that can be
446    collectively freed when the current activity (eg. compiling) is done.
447
448    You cannot selectively free dump memory. Before you start allocating it, 
449    you remember the current size returned by `dump_size`. Later, when you no 
450    longer need the memory, call `dump_release` with the remembered size and
451    all dump memory allocated since the call to `dump_size` will be freed.
452
453 *******************************************************************************/
454
455 void *dump_alloc(s4 size)
456 {
457 #if defined(DISABLE_DUMP)
458
459         /* use malloc memory for dump memory (for debugging only!) */
460
461         return mem_alloc(size);
462
463 #else /* !defined(DISABLE_DUMP) */
464
465         void       *m;
466         dumpinfo_t *di;
467 #if defined(ENABLE_MEMCHECK)
468         s4          origsize = size; /* needed for the canary system */
469 #endif
470
471         /* If no threads are used, the dumpinfo structure is a static structure   */
472         /* defined at the top of this file.                                       */
473
474         di = DUMPINFO;
475
476         if (size == 0)
477                 return NULL;
478
479 #if defined(ENABLE_MEMCHECK)
480         size += 2*MEMORY_CANARY_SIZE;
481 #endif
482
483         size = MEMORY_ALIGN(size, ALIGNSIZE);
484
485         if (di->useddumpsize + size > di->allocateddumpsize) {
486                 dumpblock_t *newdumpblock;
487                 s4         newdumpblocksize;
488
489                 /* allocate a new dumplist structure */
490
491                 newdumpblock = memory_checked_alloc(sizeof(dumpblock_t));
492
493                 /* If requested size is greater than the default, make the new dump   */
494                 /* block as big as the size requested. Else use the default size.     */
495
496                 if (size > DUMPBLOCKSIZE) {
497                         newdumpblocksize = size;
498
499                 } else {
500                         newdumpblocksize = DUMPBLOCKSIZE;
501                 }
502
503                 /* allocate dumpblock memory */
504
505                 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
506
507                 newdumpblock->prev = di->currentdumpblock;
508                 newdumpblock->size = newdumpblocksize;
509                 di->currentdumpblock = newdumpblock;
510
511                 /* Used dump size is previously allocated dump size, because the      */
512                 /* remaining free memory of the previous dump block cannot be used.   */
513
514                 di->useddumpsize = di->allocateddumpsize;
515
516                 /* increase the allocated dump size by the size of the new dump block */
517
518                 di->allocateddumpsize += newdumpblocksize;
519
520 #if defined(ENABLE_STATISTICS)
521                 /* the amount of globally allocated dump memory (thread save) */
522
523                 if (opt_stat)
524                         globalallocateddumpsize += newdumpblocksize;
525 #endif
526         }
527
528         /* current dump block base address + the size of the current dump block - */
529         /* the size of the unused memory = new start address                      */
530
531         m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
532                 (di->allocateddumpsize - di->useddumpsize);
533
534 #if defined(ENABLE_MEMCHECK)
535         {
536                 dump_allocation_t *da = NEW(dump_allocation_t);
537                 s4 i;
538                 u1 *pm;
539
540                 /* add the allocation to our linked list of allocations */
541
542                 da->next = di->allocations;
543                 da->mem = (u1*) m + MEMORY_CANARY_SIZE;
544                 da->size = origsize;
545                 da->useddumpsize = di->useddumpsize;
546
547                 di->allocations = da;
548
549                 /* write the canaries */
550
551                 pm = (u1*)m;
552                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
553                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
554                 pm = da->mem + da->size;
555                 for (i=0; i<MEMORY_CANARY_SIZE; ++i)
556                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
557
558                 /* make m point after the bottom canary */
559
560                 m = (u1*)m + MEMORY_CANARY_SIZE;
561
562                 /* clear the memory */
563
564                 memset(m, MEMORY_CLEAR_BYTE, da->size);
565         }
566 #endif /* defined(ENABLE_MEMCHECK) */
567
568         /* increase used dump size by the allocated memory size */
569
570         di->useddumpsize += size;
571
572 #if defined(ENABLE_STATISTICS)
573         if (opt_stat)
574                 if (di->useddumpsize > maxdumpsize)
575                         maxdumpsize = di->useddumpsize;
576 #endif
577
578         return m;
579
580 #endif /* defined(DISABLE_DUMP) */
581 }
582
583
584 /* dump_realloc ****************************************************************
585
586    Stupid realloc implementation for dump memory. Avoid, if possible.
587
588 *******************************************************************************/
589
590 void *dump_realloc(void *src, s4 len1, s4 len2)
591 {
592 #if defined(DISABLE_DUMP)
593         /* use malloc memory for dump memory (for debugging only!) */
594
595         return mem_realloc(src, len1, len2);
596 #else
597         void *dst = dump_alloc(len2);
598
599         memcpy(dst, src, len1);
600
601 #if defined(ENABLE_MEMCHECK)
602         /* destroy the source */
603         memset(src, MEMORY_CLEAR_BYTE, len1);
604 #endif
605
606         return dst;
607 #endif
608 }
609
610
611 /* dump_release ****************************************************************
612
613    Release dump memory above the given size.
614
615    IN:
616        size........All dump memory above this mark will be freed. Usually
617                        `size` will be the return value of a `dump_size` call
618                                    made earlier.
619
620         ERROR HANDLING:
621            XXX If the given size is invalid, this function *exits* with an
622                error message.
623                                    
624         See `dump_alloc`.
625
626 *******************************************************************************/
627
628 void dump_release(s4 size)
629 {
630 #if defined(DISABLE_DUMP)
631
632         /* use malloc memory for dump memory (for debugging only!) */
633
634         /* do nothing */
635
636 #else /* !defined(DISABLE_DUMP) */
637
638         dumpinfo_t *di;
639
640         /* If no threads are used, the dumpinfo structure is a static structure   */
641         /* defined at the top of this file.                                       */
642
643         di = DUMPINFO;
644
645         if ((size < 0) || (size > di->useddumpsize))
646                 vm_abort("Illegal dump release size: %d", size);
647
648 #if defined(ENABLE_MEMCHECK)
649         {
650                 dump_allocation_t *da, *next;
651
652                 /* check canaries */
653
654                 dump_check_canaries(di, size);
655
656                 /* iterate over all dump memory allocations about to be released */
657
658                 da = di->allocations;
659                 while (da && da->useddumpsize >= size) {
660                         next = da->next;
661
662                         /* invalidate the freed memory */
663
664                         memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
665
666                         FREE(da, dump_allocation_t);
667
668                         da = next;
669                 }
670                 di->allocations = da;
671         }
672 #endif /* defined(ENABLE_MEMCHECK) */
673
674         /* reset the used dump size to the size specified */
675
676         di->useddumpsize = size;
677
678         while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
679                 dumpblock_t *tmp = di->currentdumpblock;
680
681                 di->allocateddumpsize -= tmp->size;
682                 di->currentdumpblock = tmp->prev;
683
684 #if defined(ENABLE_STATISTICS)
685                 /* the amount of globally allocated dump memory (thread save) */
686
687                 if (opt_stat)
688                         globalallocateddumpsize -= tmp->size;
689 #endif
690
691                 /* release the dump memory and the dumpinfo structure */
692
693                 free(tmp->dumpmem);
694                 free(tmp);
695         }
696
697 #endif /* defined(DISABLE_DUMP) */
698 }
699
700
701 /* dump_size *******************************************************************
702
703    Return the current size of the dump memory area. See `dump_alloc`.
704
705 *******************************************************************************/
706
707 s4 dump_size(void)
708 {
709 #if defined(DISABLE_DUMP)
710         /* use malloc memory for dump memory (for debugging only!) */
711
712         return 0;
713
714 #else /* !defined(DISABLE_DUMP) */
715
716         dumpinfo_t *di;
717
718         /* If no threads are used, the dumpinfo structure is a static structure   */
719         /* defined at the top of this file.                                       */
720
721         di = DUMPINFO;
722
723         if (di == NULL)
724                 return 0;
725
726         return di->useddumpsize;
727
728 #endif /* defined(DISABLE_DUMP) */
729 }
730
731
732 /*
733  * These are local overrides for various environment variables in Emacs.
734  * Please do not remove this and leave it at the end of the file, where
735  * Emacs will automagically detect them.
736  * ---------------------------------------------------------------------
737  * Local variables:
738  * mode: c
739  * indent-tabs-mode: t
740  * c-basic-offset: 4
741  * tab-width: 4
742  * End:
743  * vim:noexpandtab:sw=4:ts=4:
744  */