1 /*
2 Copyright (c) 1991-1999 Thomas T. Wetmore IV
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
5 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6 */
7 /*=============================================================
8 * pvalalloc.c -- Allocation of pvalues (intrepreter values)
9 * Copyright(c) 1991-95 by T.T. Wetmore IV; all rights reserved
10 *===========================================================*/
11
12 #include "llstdlib.h"
13 #include "table.h"
14 #include "translat.h"
15 #include "gedcom.h"
16 #include "cache.h"
17 #include "interpi.h"
18 #include "liflines.h"
19 #include "feedback.h"
20 #include "zstr.h"
21 #include "lloptions.h"
22
23 /* define following to disable block allocator for pvalues
24 this is useful to find sources of leaks with valgrind
25 */
26 /* #define NOBLOCKALLOCATOR 1 */
27
28 /*********************************************
29 * local types
30 *********************************************/
31
32 /* block of pvalues - see comments in alloc_pvalue_memory() */
33 struct tag_pv_block
34 {
35 struct tag_pv_block * next;
36 struct tag_pvalue values[100]; /* arbitrary size may be adjusted */
37 };
38 typedef struct tag_pv_block *PV_BLOCK;
39 #define BLOCK_VALUES (sizeof(((PV_BLOCK)0)->values)/sizeof(((PV_BLOCK)0)->values[0]))
40
41 /*********************************************
42 * local function prototypes
43 *********************************************/
44
45 static void free_all_pvalues(void);
46 static BOOLEAN is_pvalue_or_freed(PVALUE pval);
47
48 /*********************************************
49 * local variables
50 *********************************************/
51
52 #ifndef NOBLOCKALLOCATOR
53 static PVALUE free_list = 0;
54 static INT live_pvalues = 0;
55 static PV_BLOCK block_list = 0;
56 #endif
57 static BOOLEAN reports_time = FALSE;
58 static BOOLEAN cleaning_time = FALSE;
59 #ifdef DEBUG_REPORT_MEMORY_DETAIL
60 static BOOLEAN alloclog_save = FALSE;
61 #endif
62 #ifndef NOBLOCKALLOCATOR
63 #ifdef DEBUG_PVALUES
64 static INT debugging_pvalues = TRUE;
65 #else
66 static INT debugging_pvalues = FALSE;
67 #endif
68 #endif
69
70 /*********************************************
71 * local function definitions
72 * body of module
73 *********************************************/
74
75 /*========================================
76 * debug_check -- check every pvalue in free list
77 * This is of course slow, but invaluable for tracking
78 * down pvalue corruption bugs - so don't define
79 * DEBUG_PVALUES unless you are working on a pvalue
80 * corruption bug.
81 * Created: 2001/03, Perry Rapp
82 *======================================*/
83 #ifndef NOBLOCKALLOCATOR
84 static void
debug_check(void)85 debug_check (void)
86 {
87 PVALUE val;
88 for (val=free_list; val; val=val->value.pxd)
89 {
90 ASSERT(val->type == PFREED && val->value.pxd != val);
91 }
92 }
93 #endif
94 /*========================================
95 * alloc_pvalue_memory -- return new pvalue memory
96 * We use a custom allocator, which lowers our overhead
97 * (no heap overhead per pvalue, only per block)
98 * and also allows us to clean them all up after the
99 * report.
100 * NB: This is not traditional garbage collection - we're
101 * not doing any live/dead analysis; we depend entirely
102 * on carnal knowledge of the program.
103 * We also mark freed ones, so we can scan the blocks
104 * to find leaked ones (which used to be nearly
105 * all of them)
106 * Created: 2001/01/19, Perry Rapp
107 *======================================*/
108 PVALUE
alloc_pvalue_memory(void)109 alloc_pvalue_memory (void)
110 {
111 PVALUE val;
112
113 ASSERT(reports_time);
114 #ifdef NOBLOCKALLOCATOR
115 val = stdalloc(sizeof(*val));
116 #else
117 /*
118 We assume that all pvalues are scoped
119 within report processing. If this ceases to
120 be true, this has to be rethought.
121 Eg, we could use a bitmask in the type field to mark
122 truly heap-alloc'd pvalues, used outside of reports.
123 */
124 if (!free_list) {
125 /* no pvalues available, make a new block */
126 PV_BLOCK new_block = stdalloc(sizeof(*new_block));
127 INT i;
128 new_block->next = block_list;
129 block_list = new_block;
130 /* add all pvalues in new block to free list */
131 for (i=0; i<(INT)BLOCK_VALUES; i++) {
132 PVALUE val1 = &new_block->values[i];
133 init_pvalue_vtable(val1);
134 val1->type = PFREED;
135 val1->value.pxd = free_list;
136 free_list = val1;
137 if (debugging_pvalues)
138 debug_check();
139 }
140 }
141 /* pull pvalue off of free list */
142 val = free_list;
143 free_list = free_list->value.pxd;
144 if (debugging_pvalues)
145 debug_check();
146 live_pvalues++;
147 #endif
148 /* set type to uninitialized - caller ought to set type */
149 ptype(val) = PNULL;
150 val->value.pxd = 0;
151 return val;
152 }
153 /*========================================
154 * free_pvalue_memory -- return pvalue to free-list
155 * (see alloc_pvalue_memory comments)
156 * Created: 2001/01/19, Perry Rapp
157 *======================================*/
158 void
free_pvalue_memory(PVALUE val)159 free_pvalue_memory (PVALUE val)
160 {
161 /* see alloc_pvalue_memory for discussion of this ASSERT */
162 ASSERT(reports_time);
163 #ifdef NOBLOCKALLOCATOR
164 stdfree(val);
165 #else
166 if (ptype(val)==PFREED) {
167 /*
168 this can happen during cleaning - eg, if we find
169 orphaned values in a table before we find the orphaned
170 table
171 */
172 ASSERT(cleaning_time);
173 return;
174 }
175 /* put on free list */
176 val->type = PFREED;
177 val->value.pxd = free_list;
178 free_list = val;
179 if (debugging_pvalues)
180 debug_check();
181 live_pvalues--;
182 ASSERT(live_pvalues>=0);
183 #endif
184 }
185 /*======================================
186 * pvalues_begin -- Start of programs
187 * Created: 2001/01/20, Perry Rapp
188 *====================================*/
189 void
pvalues_begin(void)190 pvalues_begin (void)
191 {
192 ASSERT(!reports_time);
193 reports_time = TRUE;
194 #ifdef DEBUG_REPORT_MEMORY_DETAIL
195 alloclog_save = alloclog;
196 alloclog = TRUE;
197 #endif
198 }
199 /*======================================
200 * pvalues_end -- End of programs
201 * Created: 2001/01/20, Perry Rapp
202 *====================================*/
203 void
pvalues_end(void)204 pvalues_end (void)
205 {
206 ASSERT(!cleaning_time);
207 cleaning_time = TRUE;
208 free_all_pnodes(); /* some nodes hold pvalues */
209 free_all_pvalues();
210 cleaning_time = FALSE;
211 ASSERT(reports_time);
212 reports_time = FALSE;
213 clear_rptinfos();
214 #ifdef DEBUG_REPORT_MEMORY
215 {
216 INT save = alloclog;
217 alloclog = TRUE;
218 report_alloc_live_count("pvalues_end");
219 alloclog = save;
220 }
221 #ifdef DEBUG_REPORT_MEMORY_DETAIL
222 alloclog = alloclog_save;
223 #endif
224 #endif
225 }
226 /*======================================
227 * free_all_pvalues -- Free every pvalue
228 * Created: 2001/01/20, Perry Rapp
229 *====================================*/
230 static void
free_all_pvalues(void)231 free_all_pvalues (void)
232 {
233 #ifdef NOBLOCKALLOCATOR
234 #else
235 PV_BLOCK block;
236 INT found_leaks=0;
237 INT orig_leaks = live_pvalues;
238 TABLE leaktab = create_table_int(); /* count of leaks by type */
239
240 /* Notes
241 live_pvalues is the count of leaked pvalues
242 We have to go through all blocks and free all pvalues
243 in first pass, because, due to containers, pvalues can
244 cross-link between blocks (ie, a list on one block could
245 contain pointers to pvalues on other blocks)
246 */
247 /* First check out all leaked pvalues for consistency
248 * in numbers - this has to be done first, since when we
249 * delete a set,list, etc. other pvalues get deleted as well */
250 for (block = block_list; block; block = block->next) {
251 INT i;
252 for (i=0; i<(INT)BLOCK_VALUES; i++) {
253 PVALUE val1=&block->values[i];
254 CNSTRING typestr = get_pvalue_type_name(val1->type);
255 if (val1->type != PFREED) {
256 increment_table_int(leaktab, typestr);
257 ++found_leaks;
258 }
259 }
260 }
261 for (block = block_list; block; block = block->next) {
262 if (live_pvalues) {
263 INT i;
264 for (i=0; i<(INT)BLOCK_VALUES; i++) {
265 PVALUE val1=&block->values[i];
266 if (val1->type != PFREED) {
267 /* leaked */
268 ZSTR zstr = describe_pvalue(val1);
269 /* zstr is just for debugging, we don't record it */
270 delete_pvalue(val1);
271 zs_free(&zstr);
272 }
273 }
274 }
275 }
276 ASSERT(orig_leaks == found_leaks);
277 ASSERT(live_pvalues == 0);
278 /* finally, free the blocks */
279 while ((block = block_list)) {
280 PV_BLOCK next = block->next;
281 stdfree(block);
282 block_list = next;
283 }
284 free_list = 0;
285 if (found_leaks) {
286 STRING report_leak_path = getlloptstr("ReportLeakLog", NULL);
287 FILE * fp=0;
288 if (report_leak_path)
289 fp = fopen(report_leak_path, LLAPPENDTEXT);
290 if (fp) {
291 LLDATE date;
292 TABLE_ITER tabit = begin_table_iter(leaktab);
293 CNSTRING key=0;
294 INT ival=0;
295 fprintf(fp, _("PVALUE memory leak report:"));
296 get_current_lldate(&date);
297 fprintf(fp, " %s", date.datestr);
298 fprintf(fp, "\n");
299 while (next_table_int(tabit, &key, &ival)) {
300 fprintf(fp, " %s: ", key);
301 fprintf(fp, _pl("%d item leaked", "%d items leaked", ival), ival);
302 fprintf(fp, "\n");
303 }
304 end_table_iter(&tabit);
305 fclose(fp);
306 fp = 0;
307 }
308 }
309 destroy_table(leaktab);
310 #endif
311 }
312 /*======================================
313 * check_pvalue_validity -- ASSERT that pvalue is valid
314 *====================================*/
315 void
check_pvalue_validity(PVALUE val)316 check_pvalue_validity (PVALUE val)
317 {
318 if (cleaning_time) {
319 ASSERT(is_pvalue_or_freed(val));
320 } else {
321 ASSERT(is_pvalue(val));
322 }
323 }
324 /*===========================================================
325 * is_pvalue -- Checks that PVALUE is a valid type
326 *=========================================================*/
327 BOOLEAN
is_pvalue(PVALUE pval)328 is_pvalue (PVALUE pval)
329 {
330 if (!pval) return FALSE;
331 return (ptype(pval) <= PMAXLIVE);
332 }
333 /*===========================================================
334 * is_pvalue_or_freed -- Checks that PVALUE is a valid type
335 * or freed
336 *=========================================================*/
337 static BOOLEAN
is_pvalue_or_freed(PVALUE pval)338 is_pvalue_or_freed (PVALUE pval)
339 {
340 if (!pval) return FALSE;
341 if (ptype(pval) == PFREED) return TRUE;
342 return (ptype(pval) <= PMAXLIVE);
343 }
344 /*========================================
345 * create_new_pvalue -- Create new program value
346 *======================================*/
347 PVALUE
create_new_pvalue(void)348 create_new_pvalue (void)
349 {
350 return alloc_pvalue_memory();
351 }
352