1 /********************************************************************/
2 /*                                                                  */
3 /*  heaputl.c     Procedures for heap allocation and maintenance.   */
4 /*  Copyright (C) 1989 - 2011  Thomas Mertes                        */
5 /*                                                                  */
6 /*  This file is part of the Seed7 Runtime Library.                 */
7 /*                                                                  */
8 /*  The Seed7 Runtime Library is free software; you can             */
9 /*  redistribute it and/or modify it under the terms of the GNU     */
10 /*  Lesser General Public License as published by the Free Software */
11 /*  Foundation; either version 2.1 of the License, or (at your      */
12 /*  option) any later version.                                      */
13 /*                                                                  */
14 /*  The Seed7 Runtime Library is distributed in the hope that it    */
15 /*  will be useful, but WITHOUT ANY WARRANTY; without even the      */
16 /*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /*  PURPOSE.  See the GNU Lesser General Public License for more    */
18 /*  details.                                                        */
19 /*                                                                  */
20 /*  You should have received a copy of the GNU Lesser General       */
21 /*  Public License along with this program; if not, write to the    */
22 /*  Free Software Foundation, Inc., 51 Franklin Street,             */
23 /*  Fifth Floor, Boston, MA  02110-1301, USA.                       */
24 /*                                                                  */
25 /*  Module: Seed7 Runtime Library                                   */
26 /*  File: seed7/src/heaputl.c                                       */
27 /*  Changes: 1992 - 1994, 2008, 2010  Thomas Mertes                 */
28 /*  Content: Procedures for heap allocation and maintenance.        */
29 /*                                                                  */
30 /********************************************************************/
31 
32 #define LOG_FUNCTIONS 0
33 #define VERBOSE_EXCEPTIONS 0
34 
35 #include "version.h"
36 
37 #include "stdlib.h"
38 #include "stdio.h"
39 #if HAS_GETRLIMIT && defined STACK_SIZE
40 /* In FreeBSD it is necessary to include <sys/types.h> before <sys/resource.h> */
41 #include "sys/types.h"
42 #include "sys/resource.h"
43 #endif
44 
45 #include "common.h"
46 #include "data_rtl.h"
47 #include "rtl_err.h"
48 
49 #undef EXTERN
50 #define EXTERN
51 #define DO_INIT
52 #include "heaputl.h"
53 
54 /* Some compilers/linkers only support determining the stack size   */
55 /* by defining a global variable. This variable must be set to the  */
56 /* desired stack size. In this case the makefile defines the macro  */
57 /* STACK_SIZE_DEFINITION which contains this variable definition.   */
58 #ifdef STACK_SIZE_DEFINITION
59 STACK_SIZE_DEFINITION;
60 #endif
61 
62 #if CHECK_STACK
63 char *stack_base = 0;
64 memSizeType max_stack_size = 0;
65 boolType interpreter_exception = FALSE;
66 #else
67 extern boolType interpreter_exception;
68 #endif
69 
70 
71 
72 /**
73  *  Set the stack size of the program.
74  *  On some operating systems the default stack size is too small.
75  *  On such systems 'setupStack' is used to request a bigger stack.
76  */
setupStack(void)77 void setupStack (void)
78 
79   {
80 #if HAS_GETRLIMIT && defined STACK_SIZE
81     struct rlimit rlim;
82 #endif
83 #if CHECK_STACK
84     char aVariable;
85 #endif
86 
87   /* setupStack */
88     logFunction(printf("setupStack\n"););
89 #if HAS_GETRLIMIT && defined STACK_SIZE
90     /* printf("STACK_SIZE:      %ld\n", STACK_SIZE); */
91     if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
92       /* printf("old stack limit: %ld/%ld\n", (long) rlim.rlim_cur, (long) rlim.rlim_max); */
93       if (rlim.rlim_cur != RLIM_INFINITY && (rlim_t) STACK_SIZE > rlim.rlim_cur) {
94         if (rlim.rlim_max == RLIM_INFINITY || (rlim_t) STACK_SIZE <= rlim.rlim_max) {
95           rlim.rlim_cur = (rlim_t) STACK_SIZE;
96         } else {
97           rlim.rlim_cur = rlim.rlim_max;
98         } /* if */
99         setrlimit(RLIMIT_STACK, &rlim);
100         /* if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
101           printf("new stack limit: %ld/%ld\n", (long) rlim.rlim_cur, (long) rlim.rlim_max);
102         } ** if */
103       } /* if */
104     } /* if */
105 #endif
106 #if CHECK_STACK
107     stack_base = &aVariable;
108     /* printf("base:  " F_U_MEM(8) "\n", (memSizeType) stack_base); */
109 #endif
110     logFunction(printf("setupStack -->\n"););
111   } /* setupStack */
112 
113 
114 
115 #if CHECK_STACK
checkStack(boolType inLogMacro)116 boolType checkStack (boolType inLogMacro)
117 
118   {
119     char aVariable;
120     boolType stackOverflow = FALSE;
121 
122   /* checkStack */
123 #if STACK_GROWS_UPWARD
124     if (&aVariable - stack_base > max_stack_size &&
125         stack_base != 0) {
126       max_stack_size = (memSizeType) (&aVariable - stack_base);
127     } /* if */
128 #else
129     if (stack_base - &aVariable > max_stack_size &&
130         stack_base != 0) {
131       max_stack_size = (memSizeType) (stack_base - &aVariable);
132     } /* if */
133 #endif
134     /* This check is outside the maximum check on purpose. */
135     /* A new maximum can occur from a logFunction, but the */
136     /* check for the stack size limit can happen later,    */
137     /* when the function is called from the interpreter.   */
138     if (unlikely(max_stack_size > CHECKED_STACK_SIZE_LIMIT &&
139                  inLogMacro != interpreter_exception)) {
140       /* The logFunctions (if inLogMacro is TRUE) should   */
141       /* only trigger an exception for compiled programs   */
142       /* (if interpreter_exception is FALSE). In the       */
143       /* interpreter the stack checking is called from the */
144       /* function exec_action() (in this case inLogMacro   */
145       /* is FALSE and interpreter_exception is TRUE).      */
146       printf("\n*** Stack size above limit\n");
147       printf("size:    " F_U_MEM(8) "\n", max_stack_size);
148       printf("limit:   " F_U_MEM(8) "\n", (memSizeType) CHECKED_STACK_SIZE_LIMIT);
149       printf("base:    " F_U_MEM(8) "\n", (memSizeType) stack_base);
150       printf("current: " F_U_MEM(8) "\n", (memSizeType) &aVariable);
151       if (inLogMacro) {
152         raise_error(MEMORY_ERROR);
153       } /* if */
154       stackOverflow = TRUE;
155     } /* if */
156     return stackOverflow;
157   } /* checkStack */
158 
159 
160 
getMaxStackSize(void)161 memSizeType getMaxStackSize (void)
162 
163   { /* getMaxStackSize */
164     return max_stack_size;
165   } /* getMaxStackSize */
166 
167 #endif
168 
169 
170 
171 #if WITH_STRI_CAPACITY
172 /**
173  *  Enlarge the capacity of a string.
174  *  This function is called from the macro GROW_STRI, if the
175  *  capacity of a string is not sufficient. GrowStri enlarges the
176  *  capacity such that at least 'len' characters fit into it.
177  *  It is assumed that 'stri' will grow further, therefore the
178  *  capacity is usually doubled.
179  *  @param stri String for which the capacity is enlarged.
180  *  @param len Length of the string that will be assigned.
181  *  @return the enlarged string, or NULL if the allocation failed.
182  */
growStri(striType stri,memSizeType len)183 striType growStri (striType stri, memSizeType len)
184 
185   {
186     memSizeType newCapacity;
187     striType result;
188 
189   /* growStri */
190     if (unlikely(len > MAX_STRI_LEN)) {
191       result = NULL;
192     } else {
193       if (2 * stri->capacity >= len) {
194         newCapacity = 2 * stri->capacity;
195       } else {
196         newCapacity = 2 * len;
197       } /* if */
198       if (newCapacity < MIN_GROW_SHRINK_CAPACITY) {
199         newCapacity = MIN_GROW_SHRINK_CAPACITY;
200       } else if (unlikely(newCapacity > MAX_STRI_LEN)) {
201         newCapacity = MAX_STRI_LEN;
202       } /* if */
203       /* printf("growStri(" FMT_X_MEM ", " FMT_U_MEM
204              ") size=" FMT_U_MEM ", capacity=" FMT_U_MEM ", newCapacity="
205              FMT_U_MEM ", siz_stri=" FMT_U_MEM ", sizeof=" FMT_U_MEM "\n",
206              (memSizeType) stri, len, stri->size, stri->capacity, newCapacity,
207              SIZ_STRI(newCapacity), sizeof(striRecord));
208       fflush(stdout); */
209       result = REALLOC_HEAP(stri, striType, SIZ_STRI(newCapacity));
210       if (unlikely(result == NULL)) {
211         do {
212           newCapacity = (newCapacity + len) / 2;
213           /* printf("newCapacity: " FMT_U_MEM ", siz_stri=" FMT_U_MEM "\n",
214               newCapacity, SIZ_STRI(newCapacity));
215           fflush(stdout); */
216           result = REALLOC_HEAP(stri, striType, SIZ_STRI(newCapacity));
217         } while (result == NULL && newCapacity != len);
218       } /* if */
219       if (likely(result != NULL)) {
220 #if ALLOW_STRITYPE_SLICES
221         result->mem = result->mem1;
222 #endif
223         COUNT3_STRI(result->capacity, newCapacity);
224         result->capacity = newCapacity;
225       } else {
226         logError(printf("growStri(" FMT_X_MEM " (capacity=" FMT_U_MEM "), "
227                         FMT_U_MEM ") failed\n",
228                         (memSizeType) stri, stri->capacity, len););
229         /* heapStatistic(); */
230       } /* if */
231     } /* if */
232     logFunction(printf("growStri --> " FMT_X_MEM "\n", (memSizeType) result);
233                 fflush(stdout););
234     return result;
235   } /* growStri */
236 
237 
238 
239 /**
240  *  Reduce the capacity of a string.
241  *  This function is called from the macro SHRINK_STRI, if the
242  *  capacity of a string is much too large. ShrinkStri reduces the
243  *  capacity, but it leaves room, such that it can grow again.
244  *  @param stri String for which the capacity is reduced.
245  *  @param len Length of the string that will be assigned.
246  *  @return the reduced string.
247  */
shrinkStri(striType stri,memSizeType len)248 striType shrinkStri (striType stri, memSizeType len)
249 
250   {
251     memSizeType newCapacity;
252     striType result;
253 
254   /* shrinkStri */
255     newCapacity = 2 * len;
256     /* printf("shrinkStri(%lX, %lu) size=%u, capacity=%u, newCapacity=%u, siz_stri=%u, sizeof=%u\n",
257          stri, len, stri->size, stri->capacity, newCapacity, SIZ_STRI(newCapacity), sizeof(striRecord));
258     fflush(stdout); */
259     result = REALLOC_HEAP(stri, striType, SIZ_STRI(newCapacity));
260 #if ALLOW_STRITYPE_SLICES
261     result->mem = result->mem1;
262 #endif
263     COUNT3_STRI(result->capacity, newCapacity);
264     result->capacity = newCapacity;
265     return result;
266   } /* shrinkStri */
267 #endif
268 
269 
270 
271 #ifdef OUT_OF_ORDER
freeStriFreelist(void)272 void freeStriFreelist (void)
273 
274   {
275     freeListElemType elem;
276     striType stri;
277 #if WITH_STRI_CAPACITY
278     memSizeType capacity;
279 #endif
280 
281   /* freeStriFreelist */
282 #if WITH_STRI_CAPACITY
283     for (capacity = 0; capacity < STRI_FREELIST_ARRAY_SIZE; capacity++) {
284       elem = sflist[capacity];
285       while (elem != NULL) {
286         stri = (striType) elem;
287         elem = elem->next;
288         HEAP_FREE_STRI(stri, capacity);
289         sflist_allowed[capacity]++;
290       } /* while */
291       sflist[capacity] = NULL;
292     } /* for */
293 #else
294     elem = sflist;
295     while (elem != NULL) {
296       stri = (striType) elem;
297       elem = elem->next;
298       HEAP_FREE_STRI(stri, 1);
299       sflist_allowed++;
300     } /* while */
301     sflist = NULL;
302 #endif
303   } /* freeStriFreelist */
304 
305 
306 
freelistStatistic(void)307 void freelistStatistic (void)
308 
309   {
310     freeListElemType elem;
311 #if WITH_STRI_CAPACITY
312     memSizeType capacity;
313     unsigned int sflist_size[STRI_FREELIST_ARRAY_SIZE];
314 #else
315     unsigned int sflist_size;
316 #endif
317 
318   /* freelistStatistic */
319 #if WITH_STRI_CAPACITY
320     for (capacity = 0; capacity < STRI_FREELIST_ARRAY_SIZE; capacity++) {
321       sflist_size[capacity] = sflist_allowed[capacity];
322       elem = sflist[capacity];
323       while (elem != NULL) {
324         elem = elem->next;
325         sflist_size[capacity]++;
326       } /* while */
327       printf("sflist_size[" FMT_U_MEM "]: %u\n", capacity, sflist_size[capacity]);
328     } /* for */
329 #else
330     sflist_size = sflist_allowed;
331     elem = sflist;
332     while (elem != NULL) {
333       elem = elem->next;
334       sflist_size++;
335     } /* while */
336     printf("sflist_size: %u\n", sflist_size);
337 #endif
338   } /* freelistStatistic */
339 #endif
340 
341 
342 
343 #if !DO_HEAP_STATISTIC
heapStatistic(void)344 void heapStatistic (void)
345 
346   { /* heapStatistic */
347     printf("heap statistic not supported - Set DO_HEAP_STATISTIC in config.h, when you compile Seed7.\n");
348   } /* heapStatistic */
349 #endif
350