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