52d4c3a3227dae8aa516120a9184598582ff70cc
[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 zu Foerderung der freien virtuellen Machine 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->useddumpsize >= 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 /* dump_alloc ******************************************************************
134
135    Allocate memory in the dump area.
136
137    IN:
138       size.........size of block to allocate, in bytes
139                                    may be zero, in which case NULL is returned
140
141    RETURN VALUE:
142       pointer to allocated memory, or
143           NULL iff `size` was zero
144
145    ERROR HANDLING:
146       XXX This function uses `memory_checked_alloc`, which *exits* if no 
147           memory could be allocated.
148
149    THREADS:
150       dump_alloc is thread safe. Each thread has its own dump memory area.
151
152    dump_alloc is a fast allocator suitable for scratch memory that can be
153    collectively freed when the current activity (eg. compiling) is done.
154
155    You cannot selectively free dump memory. Before you start allocating it, 
156    you remember the current size returned by `dump_size`. Later, when you no 
157    longer need the memory, call `dump_release` with the remembered size and
158    all dump memory allocated since the call to `dump_size` will be freed.
159
160 *******************************************************************************/
161
162 void *dump_alloc(s4 size)
163 {
164 #if defined(DISABLE_DUMP)
165
166         /* use malloc memory for dump memory (for debugging only!) */
167
168         return mem_alloc(size);
169
170 #else /* !defined(DISABLE_DUMP) */
171
172         void       *p;
173         dumpinfo_t *di;
174 #if defined(ENABLE_MEMCHECK)
175         s4          origsize = size; /* needed for the canary system */
176 #endif
177
178         /* If no threads are used, the dumpinfo structure is a static structure   */
179         /* defined at the top of this file.                                       */
180
181         di = DUMPINFO;
182
183         if (size == 0)
184                 return NULL;
185
186 #if defined(ENABLE_MEMCHECK)
187         size += 2*MEMORY_CANARY_SIZE;
188 #endif
189
190         size = MEMORY_ALIGN(size, ALIGNSIZE);
191
192         if (di->useddumpsize + size > di->allocateddumpsize) {
193                 dumpblock_t *newdumpblock;
194                 s4         newdumpblocksize;
195
196                 /* allocate a new dumplist structure */
197
198                 newdumpblock = memory_checked_alloc(sizeof(dumpblock_t));
199
200                 /* If requested size is greater than the default, make the new dump   */
201                 /* block as big as the size requested. Else use the default size.     */
202
203                 if (size > DUMPBLOCKSIZE) {
204                         newdumpblocksize = size;
205
206                 } else {
207                         newdumpblocksize = DUMPBLOCKSIZE;
208                 }
209
210                 /* allocate dumpblock memory */
211
212                 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
213
214                 newdumpblock->prev = di->currentdumpblock;
215                 newdumpblock->size = newdumpblocksize;
216                 di->currentdumpblock = newdumpblock;
217
218                 /* Used dump size is previously allocated dump size, because the      */
219                 /* remaining free memory of the previous dump block cannot be used.   */
220
221                 di->useddumpsize = di->allocateddumpsize;
222
223                 /* increase the allocated dump size by the size of the new dump block */
224
225                 di->allocateddumpsize += newdumpblocksize;
226
227 #if defined(ENABLE_STATISTICS)
228                 /* the amount of globally allocated dump memory (thread save) */
229
230                 if (opt_stat)
231                         globalallocateddumpsize += newdumpblocksize;
232 #endif
233         }
234
235         /* current dump block base address + the size of the current dump
236            block - the size of the unused memory = new start address  */
237
238         p = ((uint8_t *) di->currentdumpblock->dumpmem) +
239                 di->currentdumpblock->size -
240                 (di->allocateddumpsize - di->useddumpsize);
241
242 #if defined(ENABLE_MEMCHECK)
243         {
244                 dump_allocation_t *da = NEW(dump_allocation_t);
245                 uint8_t           *pm;
246                 int                i;
247
248                 /* add the allocation to our linked list of allocations */
249
250                 da->next         = di->allocations;
251                 da->mem          = ((uint8_t *) p) + MEMORY_CANARY_SIZE;
252                 da->size         = origsize;
253                 da->useddumpsize = di->useddumpsize;
254
255                 di->allocations = da;
256
257                 /* write the canaries */
258
259                 pm = (uint8_t *) p;
260
261                 for (i = 0; i < MEMORY_CANARY_SIZE; ++i)
262                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
263
264                 pm = ((uint8_t *) da->mem) + da->size;
265
266                 for (i = 0; i < MEMORY_CANARY_SIZE; ++i)
267                         pm[i] = i + MEMORY_CANARY_FIRST_BYTE;
268
269                 /* make m point after the bottom canary */
270
271                 p = ((uint8_t *) p) + MEMORY_CANARY_SIZE;
272
273                 /* clear the memory */
274
275                 (void) system_memset(p, MEMORY_CLEAR_BYTE, da->size);
276         }
277 #endif /* defined(ENABLE_MEMCHECK) */
278
279         /* increase used dump size by the allocated memory size */
280
281         di->useddumpsize += size;
282
283 #if defined(ENABLE_STATISTICS)
284         if (opt_stat)
285                 if (di->useddumpsize > maxdumpsize)
286                         maxdumpsize = di->useddumpsize;
287 #endif
288
289         return p;
290
291 #endif /* defined(DISABLE_DUMP) */
292 }
293
294
295 /* dump_realloc ****************************************************************
296
297    Stupid realloc implementation for dump memory. Avoid, if possible.
298
299 *******************************************************************************/
300
301 void *dump_realloc(void *src, s4 len1, s4 len2)
302 {
303 #if defined(DISABLE_DUMP)
304         /* use malloc memory for dump memory (for debugging only!) */
305
306         return mem_realloc(src, len1, len2);
307 #else
308         void *dst = dump_alloc(len2);
309
310         (void) system_memcpy(dst, src, len1);
311
312 #if defined(ENABLE_MEMCHECK)
313         /* destroy the source */
314         (void) system_memset(src, MEMORY_CLEAR_BYTE, len1);
315 #endif
316
317         return dst;
318 #endif
319 }
320
321
322 /* dump_release ****************************************************************
323
324    Release dump memory above the given size.
325
326    IN:
327        size........All dump memory above this mark will be freed. Usually
328                        `size` will be the return value of a `dump_size` call
329                                    made earlier.
330
331         ERROR HANDLING:
332            XXX If the given size is invalid, this function *exits* with an
333                error message.
334                                    
335         See `dump_alloc`.
336
337 *******************************************************************************/
338
339 void dump_release(s4 size)
340 {
341 #if defined(DISABLE_DUMP)
342
343         /* use malloc memory for dump memory (for debugging only!) */
344
345         /* do nothing */
346
347 #else /* !defined(DISABLE_DUMP) */
348
349         dumpinfo_t *di;
350
351         /* If no threads are used, the dumpinfo structure is a static structure   */
352         /* defined at the top of this file.                                       */
353
354         di = DUMPINFO;
355
356         if ((size < 0) || (size > di->useddumpsize))
357                 vm_abort("Illegal dump release size: %d", size);
358
359 #if defined(ENABLE_MEMCHECK)
360         {
361                 dump_allocation_t *da, *next;
362
363                 /* check canaries */
364
365                 dump_check_canaries(di, size);
366
367                 /* iterate over all dump memory allocations about to be released */
368
369                 da = di->allocations;
370                 while (da && da->useddumpsize >= size) {
371                         next = da->next;
372
373                         /* invalidate the freed memory */
374
375                         (void) system_memset(da->mem, MEMORY_CLEAR_BYTE, da->size);
376
377                         FREE(da, dump_allocation_t);
378
379                         da = next;
380                 }
381                 di->allocations = da;
382         }
383 #endif /* defined(ENABLE_MEMCHECK) */
384
385         /* reset the used dump size to the size specified */
386
387         di->useddumpsize = size;
388
389         while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
390                 dumpblock_t *tmp = di->currentdumpblock;
391
392                 di->allocateddumpsize -= tmp->size;
393                 di->currentdumpblock = tmp->prev;
394
395 #if defined(ENABLE_STATISTICS)
396                 /* the amount of globally allocated dump memory (thread save) */
397
398                 if (opt_stat)
399                         globalallocateddumpsize -= tmp->size;
400 #endif
401
402                 /* release the dump memory and the dumpinfo structure */
403
404                 system_free(tmp->dumpmem);
405                 system_free(tmp);
406         }
407
408 #endif /* defined(DISABLE_DUMP) */
409 }
410
411
412 /* dump_size *******************************************************************
413
414    Return the current size of the dump memory area. See `dump_alloc`.
415
416 *******************************************************************************/
417
418 s4 dump_size(void)
419 {
420 #if defined(DISABLE_DUMP)
421         /* use malloc memory for dump memory (for debugging only!) */
422
423         return 0;
424
425 #else /* !defined(DISABLE_DUMP) */
426
427         dumpinfo_t *di;
428
429         /* If no threads are used, the dumpinfo structure is a static
430            structure defined at the top of this file. */
431
432         di = DUMPINFO;
433
434         if (di == NULL)
435                 return 0;
436
437         return di->useddumpsize;
438
439 #endif /* defined(DISABLE_DUMP) */
440 }
441
442
443 /*
444  * These are local overrides for various environment variables in Emacs.
445  * Please do not remove this and leave it at the end of the file, where
446  * Emacs will automagically detect them.
447  * ---------------------------------------------------------------------
448  * Local variables:
449  * mode: c
450  * indent-tabs-mode: t
451  * c-basic-offset: 4
452  * tab-width: 4
453  * End:
454  * vim:noexpandtab:sw=4:ts=4:
455  */