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