* Merged with 0da121c758b9.
[cacao.git] / src / mm / dumpmemory.c
1 /* src/mm/dumpmemory.c - dump memory management
2
3    Copyright (C) 1996-2005, 2006, 2007, 2008
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <stdint.h>
29
30 #include "mm/dumpmemory.h"
31 #include "mm/memory.h"
32
33 #include "threads/threads-common.h"
34
35 #include "vmcore/options.h"
36
37 #if defined(ENABLE_STATISTICS)
38 # include "vmcore/statistics.h"
39 #endif
40
41 #include "vmcore/system.h"
42
43 #include "vm/vm.h"
44
45
46 /*******************************************************************************
47
48   This structure is used for dump memory allocation if cacao
49   runs without threads.
50
51 *******************************************************************************/
52
53 #if !defined(ENABLE_THREADS)
54 static dumpinfo_t _no_threads_dumpinfo;
55 #endif
56
57 #if defined(ENABLE_THREADS)
58 #define DUMPINFO    &((threadobject *) THREADOBJECT)->dumpinfo
59 #else
60 #define DUMPINFO    &_no_threads_dumpinfo
61 #endif
62
63
64 /* dump_check_canaries *********************************************************
65
66    Check canaries in dump memory.
67
68    IN:
69       di...........dumpinfo_t * of the dump area to check
70           bottomsize...dump size down to which the dump area should be checked
71                        (specify 0 to check the whole dump area)
72
73    ERROR HANDLING:
74       If any canary has been changed, this function aborts the VM with
75           an error message.
76
77 *******************************************************************************/
78
79 #if defined(ENABLE_MEMCHECK)
80 static void dump_check_canaries(dumpinfo_t *di, s4 bottomsize)
81 {
82         dump_allocation_t *da;
83         uint8_t           *pm;
84         int                i, j;
85
86         /* iterate over all dump memory allocations above bottomsize */
87
88         da = di->allocations;
89
90         while (da && da->used >= bottomsize) {
91                 /* check canaries */
92
93                 pm = ((uint8_t *) da->mem) - MEMORY_CANARY_SIZE;
94
95                 for (i = 0; i < MEMORY_CANARY_SIZE; ++i) {
96                         if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
97                                 fprintf(stderr, "canary bytes:");
98
99                                 for (j = 0; j < MEMORY_CANARY_SIZE; ++j)
100                                         fprintf(stderr, " %02x", pm[j]);
101
102                                 fprintf(stderr,"\n");
103
104                                 vm_abort("error: dump memory bottom canary killed: "
105                                                  "%p (%d bytes allocated at %p)\n",
106                                                  pm + i, da->size, da->mem);
107                         }
108                 }
109
110                 pm = ((uint8_t *) da->mem) + da->size;
111
112                 for (i = 0; i < MEMORY_CANARY_SIZE; ++i) {
113                         if (pm[i] != i + MEMORY_CANARY_FIRST_BYTE) {
114                                 fprintf(stderr, "canary bytes:");
115
116                                 for (j = 0; j < MEMORY_CANARY_SIZE; ++j)
117                                         fprintf(stderr, " %02x", pm[j]);
118
119                                 fprintf(stderr, "\n");
120
121                                 vm_abort("error: dump memory top canary killed: "
122                                                  "%p (%d bytes allocated at %p)\n",
123                                                  pm + i, da->size, da->mem);
124                         }
125                 }
126
127                 da = da->next;
128         }
129 }
130 #endif /* defined(ENABLE_MEMCHECK) */
131
132
133 /* dumpmemory_alloc ************************************************************
134
135    ATTENTION: This function must only be called from dumpmemory_get!
136
137    Allocate a new dump memory block.
138
139    IN:
140       di ..... dumpinfo_t of the current thread
141       size ... required memory size
142
143 *******************************************************************************/
144
145 void dumpmemory_alloc(dumpinfo_t *di, size_t size)
146 {
147         dumpblock_t *db;
148         size_t       newblocksize;
149
150         /* Allocate a new dumpblock_t structure. */
151
152         db = memory_checked_alloc(sizeof(dumpblock_t));
153
154         /* If requested size is greater than the default, make the new
155            dump block as big as the size requested. Else use the default
156            size. */
157
158         if (size > DUMPBLOCKSIZE) {
159                 newblocksize = size;
160         }
161         else {
162                 newblocksize = DUMPBLOCKSIZE;
163         }
164
165         /* allocate dumpblock memory */
166
167         db->dumpmem = memory_checked_alloc(newblocksize);
168
169         db->size  = newblocksize;
170         db->prev  = di->block;
171         di->block = db;
172
173         /* Used dump size is previously allocated dump size, because the
174            remaining free memory of the previous dump block cannot be
175            used. */
176
177         di->used = di->allocated;
178
179         /* Increase the allocated dump size by the size of the new dump
180            block. */
181
182         di->allocated += newblocksize;
183
184 #if defined(ENABLE_STATISTICS)
185         /* The amount of globally allocated dump memory (thread save). */
186
187         if (opt_stat)
188                 globalallocateddumpsize += newblocksize;
189 #endif
190 }
191
192
193 /* dumpmemory_get **************************************************************
194
195    Allocate memory in the dump area.
196
197    IN:
198       size.........size of block to allocate, in bytes
199                                    may be zero, in which case NULL is returned
200
201    RETURN VALUE:
202       pointer to allocated memory, or
203           NULL iff `size` was zero
204
205    ERROR HANDLING:
206       XXX This function uses `memory_checked_alloc`, which *exits* if
207           no memory could be allocated.
208
209    THREADS:
210       dumpmemory_get is thread safe. Each thread has its own dump
211       memory area.
212
213    This function is a fast allocator suitable for scratch memory that
214    can be collectively freed when the current activity (eg. compiling)
215    is done.
216
217    You cannot selectively free dump memory. Before you start
218    allocating it, you remember the current size returned by
219    `dumpmemory_marker`. Later, when you no longer need the memory,
220    call `dumpmemory_release` with the remembered size and all dump
221    memory allocated since the call to `dumpmemory_marker` will be
222    freed.
223
224 *******************************************************************************/
225
226 void *dumpmemory_get(size_t size)
227 {
228 #if defined(DISABLE_DUMP)
229
230         /* use malloc memory for dump memory (for debugging only!) */
231
232         return mem_alloc(size);
233
234 #else /* !defined(DISABLE_DUMP) */
235
236         void       *p;
237         dumpinfo_t *di;
238 #if defined(ENABLE_MEMCHECK)
239         s4          origsize = size; /* needed for the canary system */
240 #endif
241
242         di = DUMPINFO;
243
244         if (size == 0)
245                 return NULL;
246
247 #if defined(ENABLE_MEMCHECK)
248         size += 2 * MEMORY_CANARY_SIZE;
249 #endif
250
251         size = MEMORY_ALIGN(size, ALIGNSIZE);
252
253         /* Check if we have enough memory in the current memory block. */
254
255         if (di->used + size > di->allocated) {
256                 /* If not, allocate a new one. */
257
258                 dumpmemory_alloc(di, size);
259         }
260
261         /* current dump block base address + the size of the current dump
262            block - the size of the unused memory = new start address  */
263
264         p = ((uint8_t *) di->block->dumpmem) + di->block->size -
265                 (di->allocated - di->used);
266
267 #if defined(ENABLE_MEMCHECK)
268         {
269                 dump_allocation_t *da = NEW(dump_allocation_t);
270                 uint8_t           *pm;
271                 int                i;
272
273                 /* add the allocation to our linked list of allocations */
274
275                 da->next = di->allocations;
276                 da->mem  = ((uint8_t *) p) + MEMORY_CANARY_SIZE;
277                 da->size = origsize;
278                 da->used = di->used;
279
280                 di->allocations = da;
281
282                 /* write the canaries */
283
284                 pm = (uint8_t *) p;
285
286                 for (i = 0; i < MEMORY_CANARY_SIZE; ++i)
287                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
288
289                 pm = ((uint8_t *) da->mem) + da->size;
290
291                 for (i = 0; i < MEMORY_CANARY_SIZE; ++i)
292                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
293
294                 /* make m point after the bottom canary */
295
296                 p = ((uint8_t *) p) + MEMORY_CANARY_SIZE;
297
298                 /* clear the memory */
299
300                 (void) system_memset(p, MEMORY_CLEAR_BYTE, da->size);
301         }
302 #endif /* defined(ENABLE_MEMCHECK) */
303
304         /* Increase used dump size by the allocated memory size. */
305
306         di->used += size;
307
308 #if defined(ENABLE_STATISTICS)
309         if (opt_stat)
310                 if (di->used > maxdumpsize)
311                         maxdumpsize = di->used;
312 #endif
313
314         return p;
315
316 #endif /* defined(DISABLE_DUMP) */
317 }
318
319
320 /* dumpmemory_realloc **********************************************************
321
322    Stupid realloc implementation for dump memory. Avoid, if possible.
323
324 *******************************************************************************/
325
326 void *dumpmemory_realloc(void *src, s4 len1, s4 len2)
327 {
328 #if defined(DISABLE_DUMP)
329         /* use malloc memory for dump memory (for debugging only!) */
330
331         return mem_realloc(src, len1, len2);
332 #else
333         void *dst;
334
335         dst = dumpmemory_get(len2);
336
337         (void) system_memcpy(dst, src, len1);
338
339 #if defined(ENABLE_MEMCHECK)
340         /* destroy the source */
341
342         (void) system_memset(src, MEMORY_CLEAR_BYTE, len1);
343 #endif
344
345         return dst;
346 #endif
347 }
348
349
350 /* dumpmemory_release **********************************************************
351
352    Release dump memory above the given size.
353
354    IN:
355        size........All dump memory above this mark will be freed. Usually
356                        `size` will be the return value of a `dumpmemory_marker`
357                                    call made earlier.
358
359         ERROR HANDLING:
360            XXX If the given size is invalid, this function *exits* with an
361                error message.
362                                    
363         See `dump_alloc`.
364
365 *******************************************************************************/
366
367 void dumpmemory_release(s4 size)
368 {
369 #if defined(DISABLE_DUMP)
370
371         /* use malloc memory for dump memory (for debugging only!) */
372
373         /* do nothing */
374
375 #else /* !defined(DISABLE_DUMP) */
376
377         dumpinfo_t *di;
378
379         di = DUMPINFO;
380
381         if ((size < 0) || (size > di->used))
382                 vm_abort("dump_release: Illegal dump release size: %d", size);
383
384 #if defined(ENABLE_MEMCHECK)
385         {
386                 dump_allocation_t *da, *next;
387
388                 /* check canaries */
389
390                 dump_check_canaries(di, size);
391
392                 /* iterate over all dump memory allocations about to be released */
393
394                 da = di->allocations;
395
396                 while ((da != NULL) && (da->used >= size)) {
397                         next = da->next;
398
399                         /* invalidate the freed memory */
400
401                         (void) system_memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
402
403                         FREE(da, dump_allocation_t);
404
405                         da = next;
406                 }
407                 di->allocations = da;
408         }
409 #endif /* defined(ENABLE_MEMCHECK) */
410
411         /* Reset the used dump size to the size specified. */
412
413         di->used = size;
414
415         while ((di->block != NULL) && di->allocated - di->block->size >= di->used) {
416                 dumpblock_t *tmp = di->block;
417
418                 di->allocated -= tmp->size;
419                 di->block      = tmp->prev;
420
421 #if defined(ENABLE_STATISTICS)
422                 /* the amount of globally allocated dump memory (thread save) */
423
424                 if (opt_stat)
425                         globalallocateddumpsize -= tmp->size;
426 #endif
427
428                 /* Release the dump memory and the dumpinfo structure. */
429
430                 system_free(tmp->dumpmem);
431                 system_free(tmp);
432         }
433
434 #endif /* defined(DISABLE_DUMP) */
435 }
436
437
438 /* dumpmemory_marker ***********************************************************
439
440    Returns a marker of the dump memory area.  This marker is actually
441    the used size of the dump memory area.
442
443    RETURN VALUE:
444        marker of the current dump memory status
445
446 *******************************************************************************/
447
448 int32_t dumpmemory_marker(void)
449 {
450 #if defined(DISABLE_DUMP)
451         /* use malloc memory for dump memory (for debugging only!) */
452
453         return 0;
454
455 #else /* !defined(DISABLE_DUMP) */
456
457         dumpinfo_t *di;
458
459         di = DUMPINFO;
460
461         if (di == NULL)
462                 return 0;
463
464         return di->used;
465
466 #endif /* defined(DISABLE_DUMP) */
467 }
468
469
470 /*
471  * These are local overrides for various environment variables in Emacs.
472  * Please do not remove this and leave it at the end of the file, where
473  * Emacs will automagically detect them.
474  * ---------------------------------------------------------------------
475  * Local variables:
476  * mode: c
477  * indent-tabs-mode: t
478  * c-basic-offset: 4
479  * tab-width: 4
480  * End:
481  * vim:noexpandtab:sw=4:ts=4:
482  */