1 /*---------------------------------------------------------------------------
2 * The Memory Allocator wrapper.
3 *
4 *---------------------------------------------------------------------------
5 * This file #includes the source for the memory allocator selected in
6 * in config.h. If sysmalloc is selected, this file provides the basic
7 * xalloc()... implementation using malloc()... .
8 *
9 * It is the task of the memory allocator source included to provide
10 * simulations of malloc()... where required.
11 *---------------------------------------------------------------------------
12 */
13
14 #include "driver.h"
15
16 #include <ctype.h>
17 #include <stddef.h>
18 #include <stdio.h>
19
20 #include "xalloc.h"
21
22 #include "backend.h"
23 #include "gcollect.h"
24 #include "interpret.h"
25 #include "simulate.h"
26
27 #include "exec.h"
28 #include "object.h"
29 #include "mstrings.h"
30
31 /*-------------------------------------------------------------------------*/
32
33 /* Minimum boundary between stack and heap, should be sufficient for
34 * error handling (which needs about 10..15KByte).
35 * If the gap falls below this value, an error is generated and the
36 * gap is no longer checked except for true overlap with the heap.
37 */
38
39 #define HEAP_STACK_GAP (10 * ERROR_FMT_LEN)
40
41
42 /* A counter type for statistics and its functions.
43 */
44 typedef struct { unsigned long counter, size; } t_stat;
45 /* A counter type for statistics and its functions: */
46
count_add(t_stat * a,unsigned long b)47 static inline void count_add(t_stat *a, unsigned long b) {
48 a->size += b;
49 }
50
count(t_stat * a,unsigned long b)51 static inline void count(t_stat *a, unsigned long b) {
52 count_add(a, b);
53 if (b < 0)
54 --a->counter;
55 else
56 ++a->counter;
57 }
58
count_up(t_stat * a,unsigned long b)59 static inline void count_up(t_stat *a, unsigned long b) {
60 count_add(a, b);
61 ++a->counter;
62 }
63
count_up_n(t_stat * a,unsigned long b,unsigned long c)64 static inline void count_up_n(t_stat *a, unsigned long b, unsigned long c) {
65 count_add(a, b * c);
66 a->counter += b;
67 }
68
count_back(t_stat * a,unsigned long b)69 static inline void count_back(t_stat *a, unsigned long b) {
70 count_add(a, -b);
71 --a->counter;
72 }
73
count_back_n(t_stat * a,unsigned long b,unsigned long c)74 static inline void count_back_n(t_stat *a, unsigned long b, unsigned long c) {
75 count_add(a, -(b * c));
76 a->counter -= b;
77 }
78
79 typedef p_uint word_t;
80 /* Our 'word' type. This should not be changed unless you check the allocators
81 * first for assumptions that this is a p_uint.
82 */
83
84 /*-------------------------------------------------------------------------*/
85
86 /* The extra xalloc header fields.
87 * GC's write_malloc_trace() expects XM_FILE and XM_LINE at the end
88 * of the header.
89 * TODO: Let the GC use the symbolic constants.
90 */
91
92 enum xalloc_header_fields {
93 #ifdef MALLOC_LPC_TRACE
94 XM_OBJ = 0, /* (object_t*) the allocating object */
95 XM_PROG = 1, /* (int32) allocating program's id-number */
96 XM_PC = 2, /* (bytecode_p) inter_pc at the allocation */
97 # ifdef MALLOC_TRACE
98 XM_FILE = 3, /* (const char*) allocating source file */
99 XM_LINE = 4, /* (word_t) allocating line in source file */
100 XM_OVERHEAD = 5,
101 # else
102 XM_OVERHEAD = 3,
103 # endif /* MALLOC_TRACE */
104 #else /* !MALLOC_LPC_TRACE */
105 # ifdef MALLOC_TRACE
106 XM_FILE = 0, /* (const char*) allocating source file */
107 XM_LINE = 1, /* (word_t) allocating line in source file */
108 XM_OVERHEAD = 2,
109 # else
110 XM_OVERHEAD = 0,
111 # endif /* MALLOC_TRACE */
112 #endif /* MALLOC_LPC_TRACE */
113 XM_OVERHEAD_SIZE = XM_OVERHEAD * sizeof(word_t),
114 };
115
116 /*-------------------------------------------------------------------------*/
117
118 /* -- Global Variables/Arguments dealing with memory -- */
119
120 Bool out_of_memory = MY_FALSE; /* True: we are out of memory */
121 int malloc_privilege = MALLOC_USER; /* Privilege for next allocation */
122
123 char *reserved_user_area = NULL; /* Reserved memory areas */
124 char *reserved_master_area = NULL;
125 char *reserved_system_area = NULL;
126
127 mp_int reserved_user_size = RESERVED_USER_SIZE; /* The reserved sizes */
128 mp_int reserved_master_size = RESERVED_MASTER_SIZE;
129 mp_int reserved_system_size = RESERVED_SYSTEM_SIZE;
130
131 /* at startup reserve these amounts of memory for large and small blocks */
132 mp_int min_malloced = MIN_MALLOCED;
133 mp_int min_small_malloced = MIN_SMALL_MALLOCED;
134 /* this is the hard limit for memory allocations. */
135 static mp_int max_malloced = HARD_MALLOC_LIMIT_DEFAULT;
136 /* this is a soft limit for memory allocations. It serves as a kind of low
137 * watermark. If exceeded, the game driver will inform the mudlib by calling
138 * low_memory() in the master. */
139 static mp_int soft_malloc_limit = SOFT_MALLOC_LIMIT_DEFAULT;
140
141 /* Was the low_memory() already called in the master`*/
142 static Bool low_memory_applied = MY_FALSE;
143
144 int stack_direction = 0; /* 0: Unknown stack behaviour
145 * +1: Stack grows upward
146 * -1: Stack grows downward
147 */
148
149 static char * initial_stack = NULL; /* The stack at the start of the program */
150
151 static int in_malloc = 0;
152 /* >0 when the core code in the allocator is executed.
153 * This variable serves as primitive safeguard against re-entrant
154 * calls to the allocator, for example by threads.
155 */
156
157 static int going_to_exit = MY_FALSE;
158 /* When the allocator detected a fatal error, it sets this
159 * variable before starting the fatal() handling in order to
160 * prevent recursions.
161 */
162
163 #ifdef MALLOC_SBRK_TRACE
164
165 static size_t mdb_size;
166 static object_t *mdb_object;
167 # if defined(MALLOC_TRACE)
168 static const char * mdb_file;
169 static int mdb_line;
170 # endif
171 /* Persistent copy of the latest memory request information,
172 * so that the SBRK_TRACE can print it.
173 */
174
175 #endif /* MALLOC_SBRK_TRACE */
176
177 /* --- Statistics --- */
178
179 static t_stat xalloc_stat = {0,0};
180 /* Total number and size of allocations done by the driver (incl overhead).
181 */
182
183 #if defined(MALLOC_SBRK) && (defined(SBRK_OK) || defined(HAVE_MMAP))
184 static t_stat clib_alloc_stat = {0,0};
185 /* Number and size of allocations done through the clib emulation
186 * functions (incl overhead).
187 */
188 #endif
189
190 /*-------------------------------------------------------------------------*/
191 /* Forward declarations */
192
193 #ifdef MALLOC_LPC_TRACE
194 static void write_lpc_trace (int d, word_t *p, int oneline) __attribute__((nonnull(2)));
195 #endif
196
197 #ifdef GC_SUPPORT
198 static void print_block (int d, word_t *block);
199 #endif /* GC_SUPPORT */
200
201 #ifdef MALLOC_SBRK_TRACE
202
203 /*-------------------------------------------------------------------------*/
204 static void
mem_debug_log(const char * name,p_int size)205 mem_debug_log (const char * name, p_int size)
206
207 /* Mem debug log function. Log the given <size> for function <name>
208 * to stdout together with the original allocation request size.
209 */
210
211 {
212 #if defined(MALLOC_TRACE)
213 dprintf4(1, "%s %s(%d) for %d"
214 , (p_int)current_time_stamp, (p_int)name
215 , (p_int)size, (p_int)mdb_size
216 );
217 dprintf3(1, " , '%s':%d , obj %s\n"
218 , (p_int)mdb_file, (p_int)mdb_line
219 , (p_int)(mdb_object ? ( mdb_object->name ? get_txt(mdb_object->name) : "<?>"): "<null>")
220 );
221 #else
222 dprintf4(1, "%s %s(%d) for %d"
223 , (p_int)current_time_stamp, (p_int)name
224 , (p_int)size, (p_int)mdb_size
225 );
226 dprintf1(1, " , obj %s\n"
227 , (p_int)(mdb_object ? ( mdb_object->name ? get_txt(mdb_object->name) : "<?>"): "<null>")
228 );
229 #endif /* MALLOC_TRACE */
230 } /* mem_debug_log() */
231
232 /*-------------------------------------------------------------------------*/
233 static void
mdb_log_sbrk(p_int size)234 mdb_log_sbrk (p_int size)
235
236 /* esbrk() log function: esbrk() is called to allocate <size> bytes
237 * from the system. Log this information to stdout together with
238 * the original allocation request size.
239 */
240
241 {
242 mem_debug_log("esbrk", size);
243 } /* mdb_log_sbrk() */
244
245 #else
246
247 #define mdb_log_sbrk(size) NOOP
248
249 #endif /* MALLOC_SBRK_TRACE */
250
251 /*-------------------------------------------------------------------------*/
252
253 /* Include the allocator source.
254 *
255 * The allocator is free to use any information found in xalloc.
256 * In return, the allocator has to provide this interface:
257 *
258 * static POINTER mem_alloc(size_t size)
259 * to allocate a memory block
260 *
261 * static void mem_free(POINTER p)
262 * to deallocate a memory block
263 *
264 * static POINTER mem_realloc (POINTER p, size_t size)
265 * reallocate a block.
266 *
267 * static void * mem_increment_size (void *vp, size_t size)
268 * Increase a block size in place. If not possible, return NULL.
269 *
270 * static static size_t mem_block_size (POINTER p)
271 * Return the size of an allocated block.
272 * If this value is not available, implement this function as a dummy,
273 * but also #define NO_MEM_BLOCK_SIZE.
274 * For Garbage Collection or replacing malloc() this function must be
275 * valid!
276 *
277 * static static size_t mem_overhead ()
278 * Return the size of the allocators internal overhead.
279 *
280 * static void mem_mark_permanent (POINTER p)
281 * static void mem_mark_collectable (POINTER p)
282 * Mark a block as permanent resp. collectable
283 *
284 * void mem_consolidate (bool force)
285 * Do whatever consolidation is useful.
286 * <force> is true after a GC, and false when called from the backend.
287 *
288 * void mem_dump_data (strbuf_t *sbuf)
289 * void mem_dump_extdata (strbuf_t *sbuf)
290 * void mem_dinfo_data (svalue_t *svp, int value)
291 * Return the statistics data.
292 *
293 * Bool mem_dump_memory (int fd)
294 * Dump the memory layout to <fd>, or return FALSE.
295 * If <fd> is -1, just return TRUE or FALSE (this is used to check if
296 * the allocator supports memory dumps).
297 *
298 #ifdef MALLOC_EXT_STATISTICS
299 * void mem_update_stats (void)
300 * Update whatever extended statistics are available.
301 * Called every backend cycle or so to allow for the calculation of
302 * averages over time.
303 *
304 #endif
305 #ifdef GC_SUPPORT
306 * static void mem_clear_ref (POINTER p)
307 * static void mem_mark_ref (POINTER p)
308 * static Bool mem_test_ref (POINTER p)
309 * Clear, set, test the 'referenced' marker.
310 *
311 * void mem_clear_ref_flags()
312 * Clear all 'referenced' markers.
313 *
314 * void mem_free_unrefed_memory()
315 * Free all memory marked as 'unreferenced'.
316 * This routine also has to accordingly adjust xalloc_stat.
317 *
318 #ifdef MALLOC_TRACE
319 * static Bool mem_is_freed (POINTER p, size_t minsize)
320 * Return true if <p> is a free block.
321 #endif
322 #endif
323 *
324 * #define REPLACE_MALLOC
325 * if the allocator's mem_alloc()/mem_free() can be used to replace the
326 * libc allocation routines (ie. the allocator doesn't use malloc()
327 * itself). The actual replacement is provided by xalloc.
328 * #define MEM_MAIN_THREADSAFE
329 * The allocator is safe to use from the main thread.
330 * #define MEM_THREADSAFE
331 * The allocator is altogether threadsafe.
332 */
333
334 #if defined(MALLOC_smalloc)
335 # include "smalloc.c"
336 #elif defined(MALLOC_slaballoc)
337 # include "slaballoc.c"
338 #elif defined(MALLOC_sysmalloc)
339 # include "sysmalloc.c"
340 #elif defined(MALLOC_ptmalloc)
341 # include "xptmalloc.c"
342 #else
343 # error "No allocator specified."
344 #endif
345
346 #ifdef NO_MEM_BLOCK_SIZE
347 # if defined(GC_SUPPORT) || defined(REPLACE_MALLOC)
348 # error "For GC_SUPPORT or REPLACE_MALLOC, mem_block_size() must exist!"
349 # endif
350 #endif
351
352 #if defined(USE_SQLITE) && defined(SQLITE3_USES_PTHREADS) && !defined(MEM_THREADSAFE) && !defined(MEM_MAIN_THREADSAFE)
353 # warning ""
354 # warning "-----------------------------------"
355 # warning "SQLite3 uses PThreads, but the allocator"
356 # warning "is not threadsafe!"
357 # warning "-----------------------------------"
358 # warning ""
359 #endif
360
361 #if defined(MALLOC_ptmalloc) && defined(GC_SUPPORT) && defined(__FreeBSD__)
362 # warning ""
363 # warning "-----------------------------------"
364 # warning "PTMalloc selected, but the allocator"
365 # warning "doesn't support GC under FreeBSD!"
366 # warning "-----------------------------------"
367 # warning ""
368 #endif
369
370 /*-------------------------------------------------------------------------*/
371 size_t
xalloced_size(POINTER p UNUSED)372 xalloced_size (POINTER p
373 #ifdef NO_MEM_BLOCK_SIZE
374 UNUSED
375 #endif /* NO_MEM_BLOCK_SIZE */
376 )
377
378 /* Return the allocation size (incl. overhead) of the block <p>.
379 */
380
381 {
382 #ifndef NO_MEM_BLOCK_SIZE
383 return mem_block_size((word_t*)p - XM_OVERHEAD);
384 #else
385 # ifdef __MWERKS__
386 # pragma unused(p)
387 # endif
388 return 0;
389 #endif
390 } /* xalloced_size() */
391
392 /*-------------------------------------------------------------------------*/
393 size_t
xalloc_overhead(void)394 xalloc_overhead (void)
395
396 /* Return the total overhead of an allocation - xalloc and allocator.
397 */
398
399 {
400 return mem_overhead() + XM_OVERHEAD_SIZE;
401 } /* xalloc_overhead() */
402
403 /*-------------------------------------------------------------------------*/
404 static Bool
retry_alloc(size_t size MTRACE_DECL)405 retry_alloc (size_t size MTRACE_DECL)
406
407 /* An allocation for <size> bytes just failed - try to free the reserves.
408 * Return TRUE if the allocation can be retried, FALSE is not.
409 * If allocation privilege is SYSTEM and the allocation can't be retried,
410 * abort the driver.
411 */
412
413 {
414 /* Out of memory - try to recover */
415
416 static char mess1[] =
417 "Temporarily out of MEMORY. Freeing user reserve.\n";
418 static char mess2[] =
419 "Temporarily out of MEMORY. Freeing master reserve.\n";
420 static char mess3[] =
421 "Temporarily out of MEMORY. Freeing system reserve.\n";
422 static char mess4[] =
423 "Totally out of MEMORY.\n";
424 static char mess_d1[] =
425 "Low on MEMORY: Trying to allocate ";
426 static char mess_d2[] =
427 " bytes for ";
428 static char mess_d3[] =
429 " bytes request";
430 static char mess_d4[] =
431 " (";
432 static char mess_d7[] =
433 ", prog ";
434 static char mess_d6[] =
435 ")";
436 #if defined(MALLOC_TRACE)
437 static char mess_d5[] =
438 " line ";
439 #endif
440 static char mess_nl[] =
441 "\n";
442
443 /* Print the Out-Of-Mem diagnostic */
444 writes(2, mess_d1);
445 writed(2, size);
446 writes(2, mess_d2);
447 writed(2, size - XM_OVERHEAD_SIZE);
448 writes(2, mess_d3);
449 #ifdef MALLOC_TRACE
450 writes(2, mess_d4);
451 writes(2, malloc_trace_file);
452 writes(2, mess_d5);
453 writed(2, malloc_trace_line);
454 writes(2, mess_d6);
455 #endif
456 writes(2, mess_d4);
457 writes(2, current_object ? get_txt(current_object->name) : "<null>");
458 writes(2, mess_d7);
459 writes(2, current_prog ? get_txt(current_prog->name) : "<null>");
460 writes(2, mess_d6);
461 writes(2, mess_nl);
462
463 /* Free the next reserve, the try again */
464
465 if (gc_request == gcDont)
466 gc_request = gcMalloc;
467 extra_jobs_to_do = MY_TRUE;
468 if (reserved_user_area)
469 {
470 xfree(reserved_user_area);
471 reserved_user_area = NULL;
472 writes(2, mess1);
473 return MY_TRUE;
474 }
475
476 if (malloc_privilege >= MALLOC_MASTER && reserved_master_area)
477 {
478 xfree(reserved_master_area);
479 reserved_master_area = NULL;
480 writes(2, mess2);
481 return MY_TRUE;
482 }
483 if (malloc_privilege >= MALLOC_SYSTEM && reserved_system_area)
484 {
485 xfree(reserved_system_area);
486 reserved_system_area = 0;
487 writes(2, mess3);
488 return MY_TRUE;
489 }
490
491 if (malloc_privilege < MALLOC_SYSTEM)
492 {
493 out_of_memory = MY_TRUE;
494 return MY_FALSE;
495 }
496
497 /* Totally out of memory: exit */
498 max_malloced = 0; /* Disable the checking for a clean exit */
499 going_to_exit = MY_TRUE; /* Prevent recursions */
500 writes(2, mess4);
501 (void)dump_trace(MY_FALSE, NULL);
502 fatal("Out of memory (%lu bytes)\n", (unsigned long)size);
503 /* NOTREACHED */
504 return MY_FALSE;
505 } /* retry_alloc() */
506
507 /*-------------------------------------------------------------------------*/
508 static INLINE Bool
check_max_malloced(void)509 check_max_malloced (void)
510
511 /* If max_malloced is set, check if the allocated memory exceeds it.
512 * If not, return FALSE.
513 * If yes, and malloc_privilege < MALLOC_SYSTEM: return TRUE.
514 * If yes, and malloc_privilege == MALLOC_SYSTEM: abort.
515 */
516
517 {
518 #ifndef NO_MEM_BLOCK_SIZE
519 if (max_malloced > 0 && (mp_int)xalloc_stat.size > max_malloced)
520 {
521 static const char mess[] = "HARD_MALLOC_LIMIT reached.\n";
522 writes(2, mess);
523 if (malloc_privilege < MALLOC_SYSTEM)
524 return MY_TRUE;
525
526 /* Totally out of memory: exit */
527 max_malloced = 0; /* Disable the checking for a clean exit */
528 going_to_exit = MY_TRUE; /* Prevent recursions */
529 (void)dump_trace(MY_FALSE, NULL);
530 fatal("Out of memory.\n");
531 /* NOTREACHED */
532 }
533 #endif
534 return MY_FALSE;
535 } /* check_max_malloced() */
536
537 /*-------------------------------------------------------------------------*/
538 POINTER
xalloc_traced(size_t size MTRACE_DECL)539 xalloc_traced (size_t size MTRACE_DECL)
540
541 /* Allocate <size> bytes of memory like malloc() does.
542 * This function catches out of memory situations and tries to recover
543 * from them by using the reserved memory areas. If it totally runs
544 * out of memory, the program exit()s with code 3.
545 */
546
547 {
548 word_t *p;
549
550 if (going_to_exit) /* A recursive call while we're exiting */
551 exit(3);
552
553 #ifdef MALLOC_SBRK_TRACE
554 mdb_size = size;
555 mdb_object = current_object;
556 #ifdef MALLOC_TRACE
557 mdb_file = malloc_trace_file;
558 mdb_line = malloc_trace_line;
559 #endif
560 #endif /* MALLOC_SBRK_TRACE */
561
562 size += XM_OVERHEAD_SIZE;
563
564 do {
565 p = mem_alloc(size);
566 } while (p == NULL && retry_alloc(size MTRACE_PASS));
567
568 if (p == NULL)
569 {
570 return NULL;
571 }
572
573 #ifdef MALLOC_TRACE
574 p[XM_FILE] = (word_t)malloc_trace_file;
575 p[XM_LINE] = (word_t)malloc_trace_line;
576 #endif
577 #ifdef MALLOC_LPC_TRACE
578 p[XM_OBJ] = (word_t)current_object;
579 p[XM_PROG] = current_prog ? current_prog->id_number : 0;
580 p[XM_PC] = (word_t)inter_pc;
581 #endif
582 #ifdef NO_MEM_BLOCK_SIZE
583 count_up(&xalloc_stat, XM_OVERHEAD_SIZE);
584 #else
585 count_up(&xalloc_stat, mem_block_size(p));
586 if (check_max_malloced())
587 return NULL;
588 #endif
589 return (POINTER)(p + XM_OVERHEAD);
590 } /* xalloc_traced() */
591
592 /*-------------------------------------------------------------------------*/
593 void
xfree(POINTER p)594 xfree (POINTER p)
595
596 /* Free the memory block <p>.
597 */
598
599 {
600 if (NULL != p)
601 {
602 word_t *q = (word_t*)p - XM_OVERHEAD;
603 #ifdef NO_MEM_BLOCK_SIZE
604 count_back(&xalloc_stat, XM_OVERHEAD_SIZE);
605 #else
606 count_back(&xalloc_stat, mem_block_size(q));
607 #endif
608 mem_free(q);
609 }
610 } /* xfree() */
611
612 /*-------------------------------------------------------------------------*/
613 POINTER
pxalloc_traced(size_t size MTRACE_DECL)614 pxalloc_traced (size_t size MTRACE_DECL)
615
616 /* Allocate a block of <size> bytes - like xalloc(), just that the
617 * memory is not subject to GC.
618 */
619
620 {
621 POINTER temp;
622
623 temp = xalloc_traced(size MTRACE_PASS);
624 if (temp)
625 {
626 mem_mark_permanent((word_t *)temp - XM_OVERHEAD);
627 }
628 return temp;
629 } /* pxalloc_traced() */
630
631 /*-------------------------------------------------------------------------*/
632 void
pfree(POINTER p)633 pfree (POINTER p)
634
635 /* Deallocate a permanent block <p>.
636 */
637
638 {
639 if (p)
640 {
641 mem_mark_collectable((word_t *)p - XM_OVERHEAD);
642 }
643 xfree(p);
644 } /* pfree() */
645
646 /*-------------------------------------------------------------------------*/
647 POINTER
prexalloc_traced(POINTER p,size_t size MTRACE_DECL)648 prexalloc_traced (POINTER p, size_t size MTRACE_DECL)
649
650 /* Reallocate block <p> to the new size of <size> and return the pointer.
651 * The memory is not subject to GC.
652 #ifdef MALLOC_TRACE
653 * The trace arguments are admittedly unused in the function, but come
654 * in handy if the allocation code needs to be instrumented for debugging
655 * purposes.
656 #endif
657 */
658
659 {
660 POINTER temp;
661
662 if (p)
663 {
664 mem_mark_collectable((word_t *)p - XM_OVERHEAD);
665 }
666 temp = rexalloc_traced(p, size MTRACE_PASS);
667 if (temp)
668 {
669 mem_mark_permanent((word_t *)temp - XM_OVERHEAD);
670 }
671 return temp;
672 } /* prexalloc_traced() */
673
674 /*-------------------------------------------------------------------------*/
675 void *
malloc_increment_size(void * vp,size_t size)676 malloc_increment_size (void *vp, size_t size)
677
678 /* Try to extent the allocation block for <vp> in place to hold <size> more
679 * bytes. If this is not possible, return NULL, otherwise return a pointer
680 * to the start of the block extension.
681 */
682 {
683 word_t * block = (word_t*)vp - XM_OVERHEAD;
684 #ifndef NO_MEM_BLOCK_SIZE
685 size_t old_size;
686 #endif
687 void * rc;
688
689 if (going_to_exit) /* A recursive call while we're exiting */
690 exit(3);
691
692 #ifndef NO_MEM_BLOCK_SIZE
693 old_size = mem_block_size(block);
694 #endif
695
696 rc = mem_increment_size(block, size);
697
698 #ifndef NO_MEM_BLOCK_SIZE
699 if (rc != NULL)
700 {
701 count_back(&xalloc_stat, old_size);
702 count_up(&xalloc_stat, mem_block_size(block));
703 if (check_max_malloced())
704 return NULL;
705 }
706 #endif
707
708 return rc;
709 } /* malloc_increment_size() */
710
711 /*-------------------------------------------------------------------------*/
712 POINTER
rexalloc_traced(POINTER p,size_t size MTRACE_DECL UNUSED)713 rexalloc_traced (POINTER p, size_t size MTRACE_DECL
714 #ifndef MALLOC_SBRK_TRACE
715 UNUSED
716 #endif /* MALLOC_SBRK_TRACE */
717 )
718
719 /* Reallocate block <p> to the new size of <size> and return the pointer.
720 * The memory is not aligned and subject to GC.
721 #ifdef MALLOC_TRACE
722 * The trace arguments are admittedly unused in the function, but come
723 * in handy if the allocation code needs to be instrumented for debugging
724 * purposes.
725 #endif
726 */
727
728 {
729 #ifndef MALLOC_SBRK_TRACE
730 #ifdef MALLOC_TRACE
731 # ifdef __MWERKS__
732 # pragma unused(malloc_trace_file)
733 # pragma unused(malloc_trace_line)
734 # endif
735 #endif /* MALLOC_TRACE */
736 #endif /* MALLOC_SBRK_TRACE */
737
738 word_t *block, *t;
739 #ifndef NO_MEM_BLOCK_SIZE
740 size_t old_size;
741 #endif
742
743 if (going_to_exit) /* A recursive call while we're exiting */
744 exit(3);
745
746 if (!p)
747 {
748 return xalloc_traced(size MTRACE_ARG);
749 }
750
751 #ifdef MALLOC_SBRK_TRACE
752 mdb_size = size;
753 mdb_object = current_object;
754 #ifdef MALLOC_TRACE
755 mdb_file = malloc_trace_file;
756 mdb_line = malloc_trace_line;
757 #endif
758 #endif /* MALLOC_SBRK_TRACE */
759
760 size += XM_OVERHEAD_SIZE;
761 block = (word_t *)p - XM_OVERHEAD;
762
763 #ifndef NO_MEM_BLOCK_SIZE
764 old_size = mem_block_size(block);
765 t = malloc_increment_size(p, size - old_size);
766 if (t)
767 return p;
768 #endif
769
770 do {
771 t = mem_realloc(block, size);
772 } while (t == NULL && retry_alloc(size MTRACE_ARG));
773
774 if (t)
775 {
776 #ifndef NO_MEM_BLOCK_SIZE
777 count_back(&xalloc_stat, old_size);
778 count_up(&xalloc_stat, mem_block_size(t));
779 if (check_max_malloced())
780 return NULL;
781 #endif
782 t += XM_OVERHEAD;
783 }
784
785 return (POINTER)t;
786 } /* rexalloc() */
787
788 /*=========================================================================*/
789
790 /* GARBAGE COLLECTOR */
791
792 #ifdef GC_SUPPORT
793 /*-------------------------------------------------------------------------*/
794 void
x_clear_ref(POINTER p)795 x_clear_ref (POINTER p)
796
797 /* GC Support: Clear the 'referenced' flag for block <p>.
798 */
799
800 {
801 mem_clear_ref((word_t *)p - XM_OVERHEAD);
802 } /* x_clear_ref() */
803
804 /*-------------------------------------------------------------------------*/
805 int
x_mark_ref(POINTER p)806 x_mark_ref (POINTER p)
807
808 /* GC Support: Set the 'referenced' flag for block <p>.
809 * This function returns a value (1) so that it can be used in macros
810 * more easily.
811 */
812 {
813 mem_mark_ref((word_t *)p - XM_OVERHEAD);
814 return 1;
815 } /* x_mark_ref() */
816
817 /*-------------------------------------------------------------------------*/
818 Bool
x_test_ref(POINTER p)819 x_test_ref (POINTER p)
820
821 /* GC Support: Check the memory block marker for <p>, return TRUE if _not_
822 * set.
823 */
824
825 {
826 return mem_test_ref((word_t *)p - XM_OVERHEAD);
827 } /* x_test_ref() */
828
829 /*-------------------------------------------------------------------------*/
830
831 #ifdef MALLOC_TRACE
832
833 static int num_dispatched_types = 0;
834 /* Used size of the dispatch_table
835 */
836
837 static struct {
838 char *file;
839 word_t line;
840 void (*func)(int, void *, int);
841 } dispatch_table[12];
842 /* The dispatch table used to recognize and print datablocks.
843 * The recognition is simple and uses the file/line information received
844 * from sample allocations done by gcollect. If an allocation matches
845 * one entry in the table, the function is called with (filedescriptor,
846 * blockaddress, 0).
847 */
848
849 #ifdef CHECK_OBJECT_GC_REF
850 /*-------------------------------------------------------------------------*/
851 /* Some extra variables to explicitely store the location of program
852 * and object allocations.
853 */
854
855 static char * object_file;
856 static word_t object_line;
857 static char * program_file;
858 static word_t program_line;
859
860 void
note_object_allocation_info(void * block)861 note_object_allocation_info ( void *block )
862 {
863 object_file = (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD];
864 object_line = ((word_t*)block)[XM_LINE-XM_OVERHEAD];
865 }
866 void
note_program_allocation_info(void * block)867 note_program_allocation_info ( void *block )
868 {
869 program_file = (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD];
870 program_line = ((word_t*)block)[XM_LINE-XM_OVERHEAD];
871 }
872
873 Bool
is_object_allocation(void * block)874 is_object_allocation ( void *block )
875 {
876 return (object_file == (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD])
877 && (object_line == ((word_t*)block)[XM_LINE-XM_OVERHEAD]);
878 }
879 Bool
is_program_allocation(void * block)880 is_program_allocation ( void *block )
881 {
882 return (program_file == (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD])
883 && (program_line == ((word_t*)block)[XM_LINE-XM_OVERHEAD]);
884 }
885 #endif
886
887 /*-------------------------------------------------------------------------*/
888 void
store_print_block_dispatch_info(void * block,void (* func)(int,void *,int))889 store_print_block_dispatch_info (void *block
890 , void (*func)(int, void *, int)
891 )
892
893 /* Add a new block type: get the file/line information from the
894 * allocation <block> and store it with the <func>tion.
895 */
896
897 {
898 int i;
899
900 i = num_dispatched_types++;
901 if (i >= (int)(sizeof(dispatch_table)/sizeof(dispatch_table[0])))
902 {
903 writes(2, "dispatch_table for print_block() to small\n");
904 return;
905 }
906
907 dispatch_table[i].file = (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD];
908 dispatch_table[i].line = ((word_t*)block)[XM_LINE-XM_OVERHEAD];
909 dispatch_table[i].func = func;
910 } /* store_print_block_dispatch_info() */
911
912 /*-------------------------------------------------------------------------*/
913 Bool
is_freed(void * p,p_uint minsize)914 is_freed (void *p, p_uint minsize)
915
916 /* Check if block for the allocation <p> is a free block of at least
917 * <minsize>. Blocks outside the heap always qualify.
918 * The function might return false for joined blocks.
919 */
920
921 {
922 word_t *block;
923
924 block = (word_t *) p - XM_OVERHEAD;
925
926 return mem_is_freed(block, minsize + XM_OVERHEAD_SIZE);
927 } /* is_freed() */
928
929 #endif /* MALLOC_TRACE */
930
931 /*-------------------------------------------------------------------------*/
932 static void
print_block(int d,word_t * block)933 print_block (int d, word_t *block)
934
935 /* Block <block> was recognized as lost - print it onto file <d>.
936 * If possible, use the information in the dispatch_table, otherwise
937 * print the first characters as they are.
938 */
939
940 {
941 word_t size;
942 int i;
943
944 #ifdef MALLOC_TRACE
945 char *file = (char *)block[XM_FILE];
946 word_t line = block[XM_LINE];
947
948 for (i = num_dispatched_types; --i >= 0; )
949 {
950 if (dispatch_table[i].file == file
951 && dispatch_table[i].line == line)
952 {
953 (*dispatch_table[i].func)(d, (char *)(block+XM_OVERHEAD), 0);
954 write(d, "\n", 1);
955 return;
956 }
957 }
958 #endif
959
960 /* Print a hexdump, but not more than 80 bytes */
961 {
962 int limit = 80;
963 char * cp;
964
965 size = mem_block_size(block) - XM_OVERHEAD;
966 cp = (char *)(block + XM_OVERHEAD);
967
968 while (size > 0 && limit > 0)
969 {
970 /* Start of line: print the address */
971 dprintf1(d, "%x:", (p_int)cp);
972
973 /* Print the up to 16 bytes after cp as hex values */
974 for (i = 0; i < 16 && i < (int)size && i < limit; i++)
975 dprintf1(d, " %X", cp[i]);
976
977 /* Align foward to the character interpretation */
978 for (; i < 16; i++)
979 writes(d, " ");
980
981 writes(d, " ");
982
983 /* Print the same data as characters */
984 for (i = 0; i < 16 && i < (int)size && i < limit; i++)
985 {
986 if (isprint((unsigned char)cp[i]))
987 write(d, cp+i, 1);
988 else
989 writes(d, ".");
990 }
991
992 writes(d, "\n");
993
994 cp += i;
995 size -= i;
996 limit -= i;
997 }
998 }
999
1000 writes(d, "\n");
1001 } /* print_block() */
1002
1003 #endif /* GC_SUPPORT */
1004
1005 /*-------------------------------------------------------------------------*/
1006 #ifdef MALLOC_LPC_TRACE
1007
1008 static void
write_lpc_trace(int d,word_t * p,int oneline)1009 write_lpc_trace (int d, word_t *p, int oneline)
1010
1011 /* Write the object and program which allocated the memory block <p>
1012 * onto file <d>.
1013 * if <oneline> is true, all information is printed in one line.
1014 */
1015
1016 {
1017 object_t *obj, *o;
1018 bytecode_p pc;
1019 program_t *prog;
1020 int line;
1021 int32 id;
1022
1023 /* Try to find the object which allocated this block */
1024 if (!oneline)
1025 {
1026 if ( NULL != (obj = (object_t *)p[XM_OBJ]) )
1027 {
1028 writes(d, " By object: ");
1029 if (obj->flags & O_DESTRUCTED)
1030 writes(d, "(destructed) ");
1031 for (o = obj_list; o && o != obj; o = o->next_all) NOOP;
1032 if (!o)
1033 writes(d, "(not in list) ");
1034 else if (o->name)
1035 writes(d, get_txt(o->name)); /* TODO: If this cores, it has to go again */
1036 }
1037 else
1038 writes(d, " No object.");
1039 writes(d, "\n");
1040 }
1041 else
1042 {
1043 if ( NULL != (obj = (object_t *)p[XM_OBJ]) )
1044 {
1045 for (o = obj_list; o && o != obj; o = o->next_all) NOOP;
1046 if (!o || !o->name)
1047 writes(d, "?");
1048 else
1049 writes(d, get_txt(o->name)); /* TODO: If this cores, it has to go again */
1050
1051 if (obj->flags & O_DESTRUCTED)
1052 writes(d, " (destructed)");
1053 }
1054 else
1055 writes(d, "-");
1056 }
1057
1058 /* Try to find the program which allocated this block */
1059 if ( 0 != (id = p[XM_PROG]) )
1060 {
1061 pc = NULL;
1062 prog = NULL;
1063
1064 for ( o = obj_list
1065 ; o
1066 && !(o->flags & O_DESTRUCTED)
1067 && ((p_int)o->prog&1 || o->prog->id_number != id); )
1068 o = o->next_all;
1069
1070 /* Unlikely, but possible: ids might have been renumbered. */
1071 if (o)
1072 {
1073 pc = (bytecode_p)p[XM_PC];
1074 prog = o->prog;
1075 if (prog->program > pc || pc > PROGRAM_END(*prog))
1076 o = NULL;
1077 }
1078
1079 if (o)
1080 {
1081 string_t *file;
1082
1083 line = get_line_number(pc, prog, &file);
1084 if (!oneline)
1085 {
1086 dprintf2(d, " By program: %s line:%d\n", (p_int)get_txt(file), line);
1087 }
1088 else
1089 {
1090 dprintf2(d, " , %s %d", (p_int)get_txt(file), line);
1091 }
1092
1093 if (file)
1094 free_mstring(file);
1095 }
1096 else
1097 {
1098 if (!oneline)
1099 writes(d, " By program: Not found at old address.\n");
1100 }
1101 }
1102
1103 if (oneline)
1104 writes(d, "\n");
1105 } /* write_lpc_trace() */
1106
1107 #endif /* MALLOC_LPC_TRACE */
1108
1109 /*-------------------------------------------------------------------------*/
1110 void
dump_lpc_trace(int d,void * p UNUSED)1111 dump_lpc_trace (int d
1112 , void *p
1113 #ifndef MALLOC_LPC_TRACE
1114 UNUSED
1115 #endif
1116 )
1117
1118 /* Write the object and program which allocated the memory block <p>
1119 * onto file <d>.
1120 * In contrast to write_lpc_trace(), the address of the memory block is
1121 * the one returned by xalloc(), ie. pointing after the memory block
1122 * header.
1123 */
1124
1125 {
1126 #if defined(MALLOC_LPC_TRACE)
1127 write_lpc_trace(d, ((word_t *)p) - XM_OVERHEAD, MY_FALSE);
1128 #else
1129 # ifdef __MWERKS__
1130 # pragma unused(p)
1131 # endif
1132 writes(d, "No malloc lpc trace.\n");
1133 #endif /* MALLOC_LPC_TRACE */
1134 } /* dump_lpc_trace() */
1135
1136 /*-------------------------------------------------------------------------*/
1137 void
dump_malloc_trace(int d,void * adr UNUSED)1138 dump_malloc_trace (int d
1139 , void *adr
1140 #if !defined(MALLOC_TRACE) && !defined(MALLOC_LPC_TRACE)
1141 UNUSED
1142 #endif
1143 )
1144
1145 /* Write the allocation information (file, linenumber, object and such) of
1146 * the memory block <adr> onto file <d>.
1147 * <adr> is the address returned by xalloc(), ie. pointing after the memory
1148 * block header.
1149 */
1150
1151 {
1152 #if !defined(MALLOC_TRACE) && !defined(MALLOC_LPC_TRACE)
1153 # ifdef __MWERKS__
1154 # pragma unused(adr)
1155 # endif
1156 writes(d, "No malloc trace.\n");
1157 #else
1158 word_t *p = ((word_t *)adr) - XM_OVERHEAD;
1159
1160 # ifdef MALLOC_TRACE
1161 word_t size = mem_block_size(p);
1162
1163 dprintf3(d, " %s %d size 0x%x\n",
1164 p[XM_FILE], p[XM_LINE], size
1165 );
1166 # endif
1167 # ifdef MALLOC_LPC_TRACE
1168 write_lpc_trace(d, p, MY_FALSE);
1169 # endif
1170 #endif
1171 } /* dump_malloc_trace() */
1172
1173 /*=========================================================================*/
1174
1175 /* CLIB ALLOCATION FUNCTIONS */
1176
1177 #if defined(REPLACE_MALLOC) && (defined(SBRK_OK) || defined(HAVE_MMAP))
1178
1179 /*-------------------------------------------------------------------------*/
1180 POINTER
malloc(size_t size)1181 malloc (size_t size)
1182
1183 /* Allocate an empty memory block of size <sizel>.
1184 * The memory is aligned and not subject to GC.
1185 */
1186
1187 {
1188 POINTER result = pxalloc(size);
1189 if (!result)
1190 {
1191 int save_privilege = malloc_privilege;
1192 malloc_privilege = MALLOC_SYSTEM;
1193 result = pxalloc(size);
1194 malloc_privilege = save_privilege;
1195 }
1196
1197 if (result)
1198 {
1199 count_up(&clib_alloc_stat, xalloced_size(result) + mem_overhead());
1200 }
1201
1202 return result;
1203 } /* malloc() */
1204
1205 /*-------------------------------------------------------------------------*/
1206 FREE_RETURN_TYPE
free(POINTER ptr)1207 free (POINTER ptr)
1208
1209 /* Free the memory block <ptr> which was allocated with malloc().
1210 */
1211
1212 {
1213 if (ptr)
1214 {
1215 count_back(&clib_alloc_stat, xalloced_size(ptr) + mem_overhead());
1216 }
1217
1218 pfree(ptr);
1219 FREE_RETURN
1220 } /* free() */
1221
1222 /*-------------------------------------------------------------------------*/
1223 POINTER
calloc(size_t nelem,size_t sizel)1224 calloc (size_t nelem, size_t sizel)
1225
1226 /* Allocate an empty memory block for <nelem> objects of size <sizel>.
1227 * The memory is aligned and not subject to GC.
1228 */
1229
1230 {
1231 char *p;
1232
1233 if (nelem == 0 || sizel == 0)
1234 return NULL;
1235 p = malloc(nelem * sizel);
1236 if (p == NULL)
1237 return NULL;
1238 memset(p, '\0', nelem * sizel);
1239 return p;
1240 } /* calloc() */
1241
1242 /*-------------------------------------------------------------------------*/
1243 POINTER
realloc(POINTER p,size_t size)1244 realloc (POINTER p, size_t size)
1245
1246 /* Reallocate block <p> to the new size of <size> and return the pointer.
1247 * The memory is aligned and not subject to GC.
1248 */
1249
1250 {
1251 size_t old_size;
1252 POINTER t;
1253
1254 if (!p)
1255 return malloc(size);
1256
1257 old_size = xalloced_size(p) - XM_OVERHEAD_SIZE; // usable size
1258
1259 if (old_size >= size)
1260 return p;
1261
1262 t = malloc(size);
1263 if (t == NULL)
1264 return NULL;
1265
1266 memcpy(t, p, old_size);
1267 free(p);
1268
1269 return t;
1270 } /* realloc() */
1271
1272 #endif /* REPLACE_MALLOC */
1273
1274 /* ======================================================================= */
1275
1276 /*-------------------------------------------------------------------------*/
1277 void
get_stack_direction(void)1278 get_stack_direction (void)
1279
1280 /* Find the direction of the stackgrowth and store the result (+1 or -1)
1281 * into the global stack_direction.
1282 */
1283
1284 {
1285 char local; /* to get stack address */
1286
1287 if (initial_stack == NULL) /* initial call */
1288 {
1289 initial_stack = &local;
1290 get_stack_direction (); /* recurse once */
1291 }
1292 else /* recursive call */
1293 if (&local > initial_stack)
1294 stack_direction = 1; /* stack grew upward */
1295 else
1296 stack_direction = -1; /* stack grew downward */
1297 } /* get_stack_direction() */
1298
1299 /*-------------------------------------------------------------------------*/
1300 void
assert_stack_gap(void)1301 assert_stack_gap (void)
1302
1303 /* Test if the stack is far enough away from the heap area and throw
1304 * an error if not.
1305 */
1306
1307 {
1308 static enum { Initial, Normal, Error, Fatal } condition = Initial;
1309 char * stack_start, * stack_end;
1310 ptrdiff_t gap;
1311 char local; /* used to yield a stack address */
1312
1313 /* Don't check the gap after a Fatal error or if the system is
1314 * not fully initialised yet.
1315 */
1316 if (stack_direction == 0 || condition == Fatal || heap_end == NULL)
1317 return;
1318
1319 /* On the first call, test if checking the gap actually makes sense.
1320 * If the stack-gap check is not necessary, the 'condition' will be set to
1321 * Fatal, otherwise the check will be enabled by setting condition to
1322 * 'Normal'.
1323 */
1324 if (condition == Initial)
1325 {
1326 /* Currently there are no limitations on checking the heap/stack gap.
1327 */
1328 condition = Normal;
1329 }
1330
1331 /* Determine the stack limits */
1332 if (stack_direction < 0)
1333 {
1334 stack_start = &local;
1335 stack_end = initial_stack;
1336 }
1337 else
1338 {
1339 stack_start = initial_stack;
1340 stack_end = &local;
1341 }
1342
1343 /* Check if the heap and stack overlap */
1344
1345 if ((stack_end > (char *)heap_end && stack_start < (char *)heap_end)
1346 || (stack_end > (char *)heap_start && stack_start < (char *)heap_start)
1347 )
1348 {
1349 if (condition != Fatal)
1350 {
1351 condition = Fatal;
1352 fatal("Out of memory: Stack (%p..%p) overlaps heap (%p..%p).\n"
1353 , stack_start, stack_end, heap_start, heap_end);
1354 /* NOTREACHED */
1355 }
1356 return; /* else: Recursive call during fatal() handling */
1357 }
1358
1359 /* Check if the stack grows towards the heap. If it doesn't
1360 * we don't have to worry about the gap.
1361 */
1362 if ((stack_direction > 0) ? (stack_start > (char *)heap_end)
1363 : (stack_end < (char *)heap_start)
1364 )
1365 {
1366 /* No worries about the gap */
1367 condition = Normal;
1368 return;
1369 }
1370
1371 /* Heap and stack may overlap - do the normal gap checking.
1372 * Note that on machines with big address spaces the computation
1373 * may overflow.
1374 */
1375 if (stack_direction < 0)
1376 gap = (char *)stack_start - (char *)heap_end;
1377 else
1378 gap = (char *)heap_start - stack_end;
1379
1380 if (gap < 0)
1381 gap = HEAP_STACK_GAP + 1;
1382
1383 /* If the gap is big enough, mark that condition and return */
1384 if (gap >= HEAP_STACK_GAP)
1385 {
1386 condition = Normal;
1387 return;
1388 }
1389
1390 /* The gap is too small.
1391 * Throw an error only if the condition was normal before,
1392 * otherwise the error handling would again get an error.
1393 */
1394 if (condition == Normal)
1395 {
1396 condition = Error;
1397 errorf("Out of memory: Gap between stack and heap: %ld.\n"
1398 , (long)gap);
1399 /* NOTREACHED */
1400 }
1401 } /* assert_stack_gap() */
1402
1403 /*-------------------------------------------------------------------------*/
1404 void
reserve_memory(void)1405 reserve_memory (void)
1406
1407 /* Reserve the memory blocks according to reserved_xxx_area, and the
1408 * min_{small}_malloced limits.
1409 */
1410
1411 {
1412 void * ptr;
1413
1414 /* First, check if max_malloced is a sensible value.
1415 * We overestimate the requirement a bit...
1416 */
1417 if (max_malloced > 0)
1418 {
1419 mp_int required_mem = 0;
1420 mp_int required_reserve = 0;
1421
1422 if (reserved_user_size > 0) required_reserve += reserved_user_size;
1423 if (reserved_master_size > 0) required_reserve += reserved_master_size;
1424 if (reserved_system_size > 0) required_reserve += reserved_system_size;
1425
1426 if (min_malloced > 0)
1427 {
1428 if (min_malloced > required_reserve)
1429 required_mem += min_malloced;
1430 else
1431 required_mem += required_reserve + min_malloced;
1432 }
1433 else
1434 required_mem += required_reserve;
1435
1436 if (min_small_malloced > 0) required_mem += min_small_malloced;
1437
1438 if (max_malloced < required_mem)
1439 {
1440 printf("%s max_malloced is %ld bytes, "
1441 "but driver requires %ld bytes.\n"
1442 , time_stamp(), (long)max_malloced, (long)required_mem
1443 );
1444 debug_message("%s max_malloced is %ld bytes, "
1445 "but driver requires %ld bytes.\n"
1446 , time_stamp(), (long)max_malloced, (long)required_mem
1447 );
1448 max_malloced = required_mem;
1449 }
1450 }
1451
1452 if (min_malloced > 0)
1453 {
1454 ptr = xalloc(min_malloced);
1455
1456 if (ptr)
1457 xfree(ptr);
1458 else
1459 {
1460 printf("%s Failed to allocate MIN_MALLOCED block of %ld bytes.\n"
1461 , time_stamp(), (long)min_malloced);
1462 debug_message("%s Failed to allocate MIN_MALLOCED block of %ld bytes.\n"
1463 , time_stamp(), (long)min_malloced);
1464 }
1465 }
1466
1467 #ifdef MALLOC_smalloc
1468
1469 if (min_small_malloced > 0)
1470 small_chunk_size = min_small_malloced;
1471
1472 #endif /* MALLOC_smalloc */
1473
1474 if (reserved_system_size > 0)
1475 {
1476 reserved_system_area = xalloc((size_t)reserved_system_size);
1477 if (reserved_system_area == NULL)
1478 {
1479 printf("%s Failed to allocate system reserve of %ld bytes.\n"
1480 , time_stamp(), (long)reserved_system_area);
1481 debug_message("%s Failed to allocate system reserve of %ld bytes.\n"
1482 , time_stamp(), (long)reserved_system_area);
1483 }
1484 }
1485 if (reserved_master_size > 0)
1486 {
1487 reserved_master_area = xalloc((size_t)reserved_master_size);
1488 if (reserved_master_area == NULL)
1489 {
1490 printf("%s Failed to allocate master reserve of %ld bytes.\n"
1491 , time_stamp(), (long)reserved_master_area);
1492 debug_message("%s Failed to allocate master reserve of %ld bytes.\n"
1493 , time_stamp(), (long)reserved_master_area);
1494 }
1495 }
1496 if (reserved_user_size > 0)
1497 {
1498 reserved_user_area = xalloc((size_t)reserved_user_size);
1499 if (reserved_user_area == NULL)
1500 {
1501 printf("%s Failed to allocate user reserve of %ld bytes.\n"
1502 , time_stamp(), (long)reserved_user_area);
1503 debug_message("%s Failed to allocate user reserve of %ld bytes.\n"
1504 , time_stamp(), (long)reserved_user_area);
1505 }
1506 }
1507 } /* reserve_memory() */
1508
1509 /*-------------------------------------------------------------------------*/
1510 void
reallocate_reserved_areas(void)1511 reallocate_reserved_areas (void)
1512
1513 /* Try to reallocate the reserved memory areas. If this is possible,
1514 * a pending slow-shutdown is canceled and the out_of_memory flag is reset.
1515 */
1516
1517 {
1518 char *p;
1519 malloc_privilege = MALLOC_USER;
1520 if (reserved_system_size && !reserved_system_area) {
1521 if ( !(reserved_system_area = xalloc((size_t)reserved_system_size)) ) {
1522 slow_shut_down_to_do = 1;
1523 return;
1524 }
1525 else {
1526 p = "Reallocated System reserve.\n";
1527 write(1, p, strlen(p));
1528 }
1529 }
1530 if (reserved_master_size && !reserved_master_area) {
1531 if ( !(reserved_master_area = xalloc((size_t)reserved_master_size)) ) {
1532 slow_shut_down_to_do = 1;
1533 return;
1534 }
1535 else {
1536 p = "Reallocated Master reserve.\n";
1537 write(1, p, strlen(p));
1538 }
1539 }
1540 if (reserved_user_size && !reserved_user_area) {
1541 if ( !(reserved_user_area = xalloc((size_t)reserved_user_size)) ) {
1542 slow_shut_down_to_do = 6;
1543 return;
1544 }
1545 else {
1546 p = "Reallocated User reserve.\n";
1547 write(1, p, strlen(p));
1548 }
1549 }
1550 slow_shut_down_to_do = 0;
1551 out_of_memory = MY_FALSE;
1552 } /* reallocate_reserved_areas() */
1553
1554 /*-------------------------------------------------------------------------*/
1555 char *
string_copy_traced(const char * str MTRACE_DECL)1556 string_copy_traced (const char *str MTRACE_DECL)
1557
1558 /* string_copy() acts like strdup() with the additional bonus that it can
1559 * trace file/line of the calling place if MALLOC_TRACE is defined.
1560 */
1561
1562 {
1563 char *p;
1564 size_t len;
1565
1566 len = strlen(str)+1;
1567 memsafe(p = xalloc_traced(len MTRACE_PASS), len, "string_copy");
1568 if (p)
1569 {
1570 (void)memcpy(p, str, len);
1571 }
1572 return p;
1573 } /* string_copy_traced() */
1574
1575 /*-------------------------------------------------------------------------*/
1576 void
notify_lowmemory_condition(enum memory_limit_types what)1577 notify_lowmemory_condition(enum memory_limit_types what)
1578 /* Calls low_memory(what, <limit>, <memory_consumption>, <reserves> )
1579 * in the master.
1580 */
1581 {
1582 short reservestate = 0;
1583 push_number(inter_sp, what);
1584 if (what == SOFT_MALLOC_LIMIT_EXCEEDED)
1585 push_number(inter_sp, soft_malloc_limit);
1586 else if (what == HARD_MALLOC_LIMIT_EXCEEDED)
1587 push_number(inter_sp, max_malloced);
1588 else
1589 push_number(inter_sp, 0);
1590 push_number(inter_sp, xalloc_stat.size);
1591 if (reserved_user_area)
1592 reservestate |= USER_RESERVE_AVAILABLE;
1593 if (reserved_master_area)
1594 reservestate |= MASTER_RESERVE_AVAILABLE;
1595 if (reserved_system_area)
1596 reservestate |= SYSTEM_RESERVE_AVAILABLE;
1597 push_number(inter_sp, reservestate);
1598 callback_master(STR_LOW_MEMORY, 4);
1599 } /* notify_lowmemory_condition */
1600
1601 /*-------------------------------------------------------------------------*/
1602 void
check_for_soft_malloc_limit(void)1603 check_for_soft_malloc_limit (void)
1604 /* If soft_malloc_limit is set, check if the allocated memory exceeds it.
1605 * If yes and the master was not notified until now, low_memory() is called
1606 * in the mudlib master and low_memory_applied ist set to prevent any further
1607 * notifications.
1608 * If the limit is not exceeded, low_memory_applied is reset.
1609 * Should be called from the backend.
1610 */
1611 {
1612 #ifndef NO_MEM_BLOCK_SIZE
1613 if (soft_malloc_limit > 0)
1614 {
1615 // check first, if the soft limit is > than the hard limit and issue
1616 // a debug message. (Gnomi wanted to check this here to be able to set
1617 // a soft limit before a hard limit without error.)
1618 if (soft_malloc_limit >= max_malloced)
1619 {
1620 debug_message("%s The soft memory limit (%"PRIdMPINT") is bigger "
1621 "than the hard memory limit (%"PRIdMPINT") - "
1622 "disabling the soft limit!",
1623 time_stamp(), soft_malloc_limit, max_malloced);
1624 soft_malloc_limit = 0;
1625 }
1626 else if ((mp_int)xalloc_stat.size > soft_malloc_limit)
1627 {
1628 if (!low_memory_applied)
1629 {
1630 /* call low_memory(malloced_memory) in the master but first
1631 * set the flag to prevent calling every backend cycle in case
1632 * of errors. */
1633 low_memory_applied = MY_TRUE;
1634 notify_lowmemory_condition(SOFT_MALLOC_LIMIT_EXCEEDED);
1635 }
1636 }
1637 else if (low_memory_applied)
1638 {
1639 /* OK, memory consumption shrunk below the soft limit. Reset the
1640 * warning, so that the next time the limit is exceeded,
1641 * the master apply is done again. */
1642 low_memory_applied = MY_FALSE;
1643 }
1644 }
1645 #endif
1646 } /* check_for_soft_malloc_limit() */
1647
1648 /*-------------------------------------------------------------------------*/
1649 Bool
set_memory_limit(enum memory_limit_types what,mp_int limit)1650 set_memory_limit(enum memory_limit_types what, mp_int limit)
1651 /* Sets the <what> memory limit to <limit>.
1652 * <limit> for SOFT_MLIMIT has to be < HARD_MLIMIT.
1653 * return TRUE on success and FALSE otherwise.
1654 * If the current memory allocation is smaller than a new soft limit, the flag
1655 * low_memory_applied is reset.
1656 */
1657 {
1658 #ifndef NO_MEM_BLOCK_SIZE
1659 // limits smaller than the sum of reserves (and some more) are harmful and
1660 // lead anyway to immediate crashes... But we ignore this here, because
1661 // reserve_memory() will deal with the needed sizes for the reserves and the
1662 // minimum allocations. And later on in the game we will just prevent to
1663 // set the limit smaller then the already allocated memory.
1664
1665 if (what == MALLOC_SOFT_LIMIT)
1666 {
1667 if (limit < 0)
1668 return MY_FALSE;
1669
1670 soft_malloc_limit = limit;
1671 // reset flag if appropriate.
1672 if ((mp_int)xalloc_stat.size < soft_malloc_limit
1673 && low_memory_applied)
1674 low_memory_applied = MY_FALSE;
1675 }
1676 else
1677 {
1678 // setting the limit below the currently allocated memory seems to be
1679 // a very bad idea.
1680 if (limit && limit <= (mp_int)xalloc_stat.size)
1681 return MY_FALSE;
1682
1683 max_malloced = limit;
1684 }
1685 return MY_TRUE;
1686 #else
1687 return MY_FALSE;
1688 #endif // NO_MEM_BLOCK_SIZE
1689 } /* set_memory_limit */
1690
1691 /*-------------------------------------------------------------------------*/
1692 mp_int
get_memory_limit(enum memory_limit_types what)1693 get_memory_limit(enum memory_limit_types what)
1694 /* Return the value of limit <what>. */
1695 {
1696 #ifndef NO_MEM_BLOCK_SIZE
1697 if (what == MALLOC_SOFT_LIMIT)
1698 return soft_malloc_limit;
1699 else
1700 return max_malloced;
1701 #else
1702 return 0;
1703 #endif
1704 }
1705 /***************************************************************************/
1706