e166443055fd4d7f57c76ab1dbba499bf45d81c4
[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 4921 2006-05-15 14:24:36Z twisti $
33
34 */
35
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/mman.h>
44
45 #if defined(__DARWIN__)
46 /* If we compile with -ansi on darwin, <sys/types.h> is not included. So      */
47 /* let's do it here.                                                          */
48 # include <sys/types.h>
49 #endif
50
51 #include "config.h"
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/threads.h"
61 #endif
62
63 #include "toolbox/logging.h"
64 #include "vm/exceptions.h"
65 #include "vm/global.h"
66 #include "vm/options.h"
67 #include "vm/statistics.h"
68 #include "vm/stringlocal.h"
69
70
71 /*******************************************************************************
72
73   This structure is used for dump memory allocation if cacao
74   runs without threads.
75
76 *******************************************************************************/
77
78 #if !defined(ENABLE_THREADS)
79 static dumpinfo _no_threads_dumpinfo;
80 #endif
81
82 #if defined(ENABLE_THREADS)
83 #define DUMPINFO    &((threadobject *) THREADOBJECT)->dumpinfo
84 #else
85 #define DUMPINFO    &_no_threads_dumpinfo
86 #endif
87
88
89 /* global code memory variables ***********************************************/
90
91 #define DEFAULT_CODEMEM_SIZE    128 * 1024  /* defaulting to 128kB            */
92
93 #if defined(ENABLE_THREADS)
94 static java_objectheader *codememlock = NULL;
95 #endif
96 static int                codememsize = 0;
97 static void              *codememptr  = NULL;
98
99
100 /* memory_init *****************************************************************
101
102    Initialize the memory subsystem.
103
104 *******************************************************************************/
105
106 bool memory_init(void)
107 {
108 #if defined(ENABLE_THREADS)
109         codememlock = NEW(java_objectheader);
110
111         lock_init_object_lock(codememlock);
112 #endif
113
114         /* everything's ok */
115
116         return true;
117 }
118
119
120 /* memory_checked_alloc ********************************************************
121
122    Allocated zeroed-out memory and does an OOM check.
123
124    ERROR HANDLING:
125       XXX If no memory could be allocated, this function justs *exists*.
126
127 *******************************************************************************/
128
129 static void *memory_checked_alloc(s4 size)
130 {
131         /* always allocate memory zeroed out */
132
133         void *p = calloc(size, 1);
134
135         if (!p)
136                 exceptions_throw_outofmemory_exit();
137
138         return p;
139 }
140
141
142 /* memory_cnew *****************************************************************
143
144    Allocates memory from the heap, aligns it to architecutres PAGESIZE
145    and make the memory read-, write-, and executeable.
146
147 *******************************************************************************/
148
149 void *memory_cnew(s4 size)
150 {
151         void *p;
152         int   pagesize;
153
154 #if defined(ENABLE_THREADS)
155         builtin_monitorenter(codememlock);
156 #endif
157
158         size = ALIGN(size, ALIGNSIZE);
159
160         /* check if enough memory is available */
161
162         if (size > codememsize) {
163                 /* set default code size */
164
165                 codememsize = DEFAULT_CODEMEM_SIZE;
166
167                 /* do we need more? */
168
169                 if (size > codememsize)
170                         codememsize = size;
171
172                 /* get the pagesize of this architecture */
173
174                 pagesize = getpagesize();
175
176                 /* allocate normal heap memory */
177
178                 if ((p = memory_checked_alloc(codememsize + pagesize - 1)) == NULL)
179                         return NULL;
180
181 #if defined(ENABLE_STATISTICS)
182                 if (opt_stat) {
183                         codememusage += codememsize + pagesize - 1;
184
185                         if (codememusage > maxcodememusage)
186                                 maxcodememusage = codememusage;
187                 }
188 #endif
189
190                 /* align the memory allocated to a multiple of PAGESIZE,
191                    mprotect requires this */
192
193                 p = (void *) (((ptrint) p + pagesize - 1) & ~(pagesize - 1));
194
195                 /* make the memory read-, write-, and executeable */
196
197                 if (mprotect(p, codememsize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
198                         throw_cacao_exception_exit(string_java_lang_InternalError,
199                                                                            strerror(errno));
200
201                 /* set global code memory pointer */
202
203                 codememptr = p;
204         }
205
206         /* get a memory chunk of the allocated memory */
207
208         p = codememptr;
209         codememptr = (void *) ((ptrint) codememptr + size);
210         codememsize -= size;
211
212 #if defined(ENABLE_THREADS)
213         builtin_monitorexit(codememlock);
214 #endif
215
216         return p;
217 }
218
219
220 void *mem_alloc(s4 size)
221 {
222         if (size == 0)
223                 return NULL;
224
225 #if defined(ENABLE_STATISTICS)
226         if (opt_stat) {
227                 memoryusage += size;
228
229                 if (memoryusage > maxmemusage)
230                         maxmemusage = memoryusage;
231         }
232 #endif
233
234         return memory_checked_alloc(size);
235 }
236
237
238 void *mem_realloc(void *src, s4 len1, s4 len2)
239 {
240         void *dst;
241
242         if (!src) {
243                 if (len1 != 0) {
244                         log_text("reallocating memoryblock with address NULL, length != 0");
245                         assert(0);
246                 }
247         }
248
249 #if defined(ENABLE_STATISTICS)
250         if (opt_stat)
251                 memoryusage = (memoryusage - len1) + len2;
252 #endif
253
254         dst = realloc(src, len2);
255
256         if (!dst)
257                 exceptions_throw_outofmemory_exit();
258
259         return dst;
260 }
261
262
263 void mem_free(void *m, s4 size)
264 {
265         if (!m) {
266                 if (size == 0)
267                         return;
268
269                 log_text("returned memoryblock with address NULL, length != 0");
270                 assert(0);
271         }
272
273 #if defined(ENABLE_STATISTICS)
274         if (opt_stat)
275                 memoryusage -= size;
276 #endif
277
278         free(m);
279 }
280
281
282 /* dump_alloc ******************************************************************
283
284    Allocate memory in the dump area.
285
286    IN:
287       size.........size of block to allocate, in bytes
288                                    may be zero, in which case NULL is returned
289
290    RETURN VALUE:
291       pointer to allocated memory, or
292           NULL iff `size` was zero
293
294    ERROR HANDLING:
295       XXX This function uses `memory_checked_alloc`, which *exits* if no 
296           memory could be allocated.
297
298    THREADS:
299       dump_alloc is thread safe. Each thread has its own dump memory area.
300
301    dump_alloc is a fast allocator suitable for scratch memory that can be
302    collectively freed when the current activity (eg. compiling) is done.
303
304    You cannot selectively free dump memory. Before you start allocating it, 
305    you remember the current size returned by `dump_size`. Later, when you no 
306    longer need the memory, call `dump_release` with the remembered size and
307    all dump memory allocated since the call to `dump_size` will be freed.
308
309 *******************************************************************************/
310
311 void *dump_alloc(s4 size)
312 {
313 #if defined(DISABLE_DUMP)
314
315         /* use malloc memory for dump memory (for debugging only!) */
316
317         return mem_alloc(size);
318
319 #else /* !defined(DISABLE_DUMP) */
320
321         void     *m;
322         dumpinfo *di;
323
324         /* If no threads are used, the dumpinfo structure is a static structure   */
325         /* defined at the top of this file.                                       */
326
327         di = DUMPINFO;
328
329         if (size == 0)
330                 return NULL;
331
332         size = ALIGN(size, ALIGNSIZE);
333
334         if (di->useddumpsize + size > di->allocateddumpsize) {
335                 dumpblock *newdumpblock;
336                 s4         newdumpblocksize;
337
338                 /* allocate a new dumplist structure */
339
340                 newdumpblock = memory_checked_alloc(sizeof(dumpblock));
341
342                 /* If requested size is greater than the default, make the new dump   */
343                 /* block as big as the size requested. Else use the default size.     */
344
345                 if (size > DUMPBLOCKSIZE) {
346                         newdumpblocksize = size;
347
348                 } else {
349                         newdumpblocksize = DUMPBLOCKSIZE;
350                 }
351
352                 /* allocate dumpblock memory */
353
354                 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
355
356                 newdumpblock->prev = di->currentdumpblock;
357                 newdumpblock->size = newdumpblocksize;
358                 di->currentdumpblock = newdumpblock;
359
360                 /* Used dump size is previously allocated dump size, because the      */
361                 /* remaining free memory of the previous dump block cannot be used.   */
362
363                 di->useddumpsize = di->allocateddumpsize;
364
365                 /* increase the allocated dump size by the size of the new dump block */
366
367                 di->allocateddumpsize += newdumpblocksize;
368
369 #if defined(ENABLE_STATISTICS)
370                 /* the amount of globally allocated dump memory (thread save) */
371
372                 if (opt_stat)
373                         globalallocateddumpsize += newdumpblocksize;
374 #endif
375         }
376
377         /* current dump block base address + the size of the current dump block - */
378         /* the size of the unused memory = new start address                      */
379
380         m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
381                 (di->allocateddumpsize - di->useddumpsize);
382
383         /* increase used dump size by the allocated memory size */
384
385         di->useddumpsize += size;
386
387 #if defined(ENABLE_STATISTICS)
388         if (opt_stat)
389                 if (di->useddumpsize > maxdumpsize)
390                         maxdumpsize = di->useddumpsize;
391 #endif
392
393         return m;
394
395 #endif /* defined(DISABLE_DUMP) */
396 }
397
398
399 /* dump_realloc ****************************************************************
400
401    Stupid realloc implementation for dump memory. Avoid, if possible.
402
403 *******************************************************************************/
404
405 void *dump_realloc(void *src, s4 len1, s4 len2)
406 {
407 #if defined(DISABLE_DUMP)
408         /* use malloc memory for dump memory (for debugging only!) */
409
410         return mem_realloc(src, len1, len2);
411 #else
412         void *dst = dump_alloc(len2);
413
414         memcpy(dst, src, len1);
415
416         return dst;
417 #endif
418 }
419
420
421 /* dump_release ****************************************************************
422
423    Release dump memory above the given size.
424
425    IN:
426        size........All dump memory above this mark will be freed. Usually
427                        `size` will be the return value of a `dump_size` call
428                                    made earlier.
429
430         ERROR HANDLING:
431            XXX If the given size is invalid, this function *exits* with an
432                error message.
433                                    
434         See `dump_alloc`.
435
436 *******************************************************************************/
437
438 void dump_release(s4 size)
439 {
440 #if defined(DISABLE_DUMP)
441
442         /* use malloc memory for dump memory (for debugging only!) */
443
444         /* do nothing */
445
446 #else /* !defined(DISABLE_DUMP) */
447
448         dumpinfo *di;
449
450         /* If no threads are used, the dumpinfo structure is a static structure   */
451         /* defined at the top of this file.                                       */
452
453         di = DUMPINFO;
454
455         if (size < 0 || size > di->useddumpsize)
456                 throw_cacao_exception_exit(string_java_lang_InternalError,
457                                                                    "Illegal dump release size %d", size);
458
459         /* reset the used dump size to the size specified */
460
461         di->useddumpsize = size;
462
463         while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
464                 dumpblock *tmp = di->currentdumpblock;
465
466 #if 0
467                 /* XXX TWISTI: can someone explain this to me? */
468 #ifdef TRACECALLARGS
469                 /* Keep the first dumpblock if we don't free memory. Otherwise
470                  * a new dumpblock is allocated each time and we run out of
471                  * memory.
472                  */
473                 if (!oldtop->prev) break;
474 #endif
475 #endif
476
477                 di->allocateddumpsize -= tmp->size;
478                 di->currentdumpblock = tmp->prev;
479
480 #if defined(ENABLE_STATISTICS)
481                 /* the amount of globally allocated dump memory (thread save) */
482
483                 if (opt_stat)
484                         globalallocateddumpsize -= tmp->size;
485 #endif
486
487                 /* release the dump memory and the dumpinfo structure */
488
489                 free(tmp->dumpmem);
490                 free(tmp);
491         }
492
493 #endif /* defined(DISABLE_DUMP) */
494 }
495
496
497 /* dump_size *******************************************************************
498
499    Return the current size of the dump memory area. See `dump_alloc`.
500
501 *******************************************************************************/
502
503 s4 dump_size(void)
504 {
505 #if defined(DISABLE_DUMP)
506         /* use malloc memory for dump memory (for debugging only!) */
507
508         return 0;
509
510 #else /* !defined(DISABLE_DUMP) */
511
512         dumpinfo *di;
513
514         /* If no threads are used, the dumpinfo structure is a static structure   */
515         /* defined at the top of this file.                                       */
516
517         di = DUMPINFO;
518
519         if (!di)
520                 return 0;
521
522         return di->useddumpsize;
523
524 #endif /* defined(DISABLE_DUMP) */
525 }
526
527
528 /*
529  * These are local overrides for various environment variables in Emacs.
530  * Please do not remove this and leave it at the end of the file, where
531  * Emacs will automagically detect them.
532  * ---------------------------------------------------------------------
533  * Local variables:
534  * mode: c
535  * indent-tabs-mode: t
536  * c-basic-offset: 4
537  * tab-width: 4
538  * End:
539  * vim:noexpandtab:sw=4:ts=4:
540  */