xref: /plan9/sys/src/cmd/gs/src/ilocate.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1995-2000, 2004 artofcode LLC.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: ilocate.c,v 1.14 2005/10/12 11:05:11 leonardo Exp $ */
18 /* Object locating and validating for Ghostscript memory manager */
19 #include "ghost.h"
20 #include "memory_.h"
21 #include "ierrors.h"
22 #include "gsexit.h"
23 #include "gsstruct.h"
24 #include "iastate.h"
25 #include "idict.h"
26 #include "igc.h"	/* for gc_state_t and gcst_get_memory_ptr() */
27 #include "igcstr.h"	/* for prototype */
28 #include "iname.h"
29 #include "ipacked.h"
30 #include "isstate.h"
31 #include "iutil.h"	/* for packed_get */
32 #include "ivmspace.h"
33 #include "store.h"
34 
35 /* ================ Locating ================ */
36 
37 /* Locate a pointer in the chunks of a space being collected. */
38 /* This is only used for string garbage collection and for debugging. */
39 chunk_t *
gc_locate(const void * ptr,gc_state_t * gcst)40 gc_locate(const void *ptr, gc_state_t * gcst)
41 {
42     const gs_ref_memory_t *mem;
43     const gs_ref_memory_t *other;
44 
45     if (chunk_locate(ptr, &gcst->loc))
46 	return gcst->loc.cp;
47     mem = gcst->loc.memory;
48 
49     /*
50      * Try the stable allocator of this space, or, if the current memory
51      * is the stable one, the non-stable allocator of this space.
52      */
53 
54     if ((other = (const gs_ref_memory_t *)mem->stable_memory) != mem ||
55 	(other = gcst->spaces_indexed[mem->space >> r_space_shift]) != mem
56 	) {
57 	gcst->loc.memory = other;
58 	gcst->loc.cp = 0;
59 	if (chunk_locate(ptr, &gcst->loc))
60 	    return gcst->loc.cp;
61     }
62 
63     /*
64      * Try the other space, if there is one, including its stable allocator
65      * and all save levels.  (If the original space is system space, try
66      * local space.)
67      */
68 
69     if (gcst->space_local != gcst->space_global) {
70 	gcst->loc.memory = other =
71 	    (mem->space == avm_local ? gcst->space_global : gcst->space_local);
72 	gcst->loc.cp = 0;
73 	if (chunk_locate(ptr, &gcst->loc))
74 	    return gcst->loc.cp;
75 	/* Try its stable allocator. */
76 	if (other->stable_memory != (const gs_memory_t *)other) {
77 	    gcst->loc.memory = (gs_ref_memory_t *)other->stable_memory;
78 	    gcst->loc.cp = 0;
79 	    if (chunk_locate(ptr, &gcst->loc))
80 		return gcst->loc.cp;
81 	    gcst->loc.memory = other;
82 	}
83 	/* Try other save levels of this space. */
84 	while (gcst->loc.memory->saved != 0) {
85 	    gcst->loc.memory = &gcst->loc.memory->saved->state;
86 	    gcst->loc.cp = 0;
87 	    if (chunk_locate(ptr, &gcst->loc))
88 		return gcst->loc.cp;
89 	}
90     }
91 
92     /*
93      * Try system space.  This is simpler because it isn't subject to
94      * save/restore and doesn't have a separate stable allocator.
95      */
96 
97     if (mem != gcst->space_system) {
98 	gcst->loc.memory = gcst->space_system;
99 	gcst->loc.cp = 0;
100 	if (chunk_locate(ptr, &gcst->loc))
101 	    return gcst->loc.cp;
102     }
103 
104     /*
105      * Try other save levels of the initial space, or of global space if the
106      * original space was system space.  In the latter case, try all
107      * levels, and its stable allocator.
108      */
109 
110     switch (mem->space) {
111     default:			/* system */
112 	other = gcst->space_global;
113 	if (other->stable_memory != (const gs_memory_t *)other) {
114 	    gcst->loc.memory = (gs_ref_memory_t *)other->stable_memory;
115 	    gcst->loc.cp = 0;
116 	    if (chunk_locate(ptr, &gcst->loc))
117 		return gcst->loc.cp;
118 	}
119 	gcst->loc.memory = other;
120 	break;
121     case avm_global:
122 	gcst->loc.memory = gcst->space_global;
123 	break;
124     case avm_local:
125 	gcst->loc.memory = gcst->space_local;
126 	break;
127     }
128     for (;;) {
129 	if (gcst->loc.memory != mem) {	/* don't do twice */
130 	    gcst->loc.cp = 0;
131 	    if (chunk_locate(ptr, &gcst->loc))
132 		return gcst->loc.cp;
133 	}
134 	if (gcst->loc.memory->saved == 0)
135 	    break;
136 	gcst->loc.memory = &gcst->loc.memory->saved->state;
137     }
138 
139     /* Restore locator to a legal state and report failure. */
140 
141     gcst->loc.memory = mem;
142     gcst->loc.cp = 0;
143     return 0;
144 }
145 
146 /* ================ Debugging ================ */
147 
148 #ifdef DEBUG
149 
150 /* Define the structure for temporarily saving allocator state. */
151 typedef struct alloc_temp_save_s {
152 	chunk_t cc;
153 	uint rsize;
154 	ref rlast;
155 } alloc_temp_save_t;
156 /* Temporarily save the state of an allocator. */
157 private void
alloc_temp_save(alloc_temp_save_t * pats,gs_ref_memory_t * mem)158 alloc_temp_save(alloc_temp_save_t *pats, gs_ref_memory_t *mem)
159 {
160     chunk_t *pcc = mem->pcc;
161     obj_header_t *rcur = mem->cc.rcur;
162 
163     if (pcc != 0) {
164 	pats->cc = *pcc;
165 	*pcc = mem->cc;
166     }
167     if (rcur != 0) {
168 	pats->rsize = rcur[-1].o_size;
169 	rcur[-1].o_size = mem->cc.rtop - (byte *) rcur;
170 	/* Create the final ref, reserved for the GC. */
171 	pats->rlast = ((ref *) mem->cc.rtop)[-1];
172 	make_mark((ref *) mem->cc.rtop - 1);
173     }
174 }
175 /* Restore the temporarily saved state. */
176 private void
alloc_temp_restore(alloc_temp_save_t * pats,gs_ref_memory_t * mem)177 alloc_temp_restore(alloc_temp_save_t *pats, gs_ref_memory_t *mem)
178 {
179     chunk_t *pcc = mem->pcc;
180     obj_header_t *rcur = mem->cc.rcur;
181 
182     if (rcur != 0) {
183 	rcur[-1].o_size = pats->rsize;
184 	((ref *) mem->cc.rtop)[-1] = pats->rlast;
185     }
186     if (pcc != 0)
187 	*pcc = pats->cc;
188 }
189 
190 /* Validate the contents of an allocator. */
191 void
ialloc_validate_spaces(const gs_dual_memory_t * dmem)192 ialloc_validate_spaces(const gs_dual_memory_t * dmem)
193 {
194     int i;
195     gc_state_t state;
196     alloc_temp_save_t
197 	save[countof(dmem->spaces_indexed)],
198 	save_stable[countof(dmem->spaces_indexed)];
199     gs_ref_memory_t *mem;
200 
201     state.spaces = dmem->spaces;
202     state.loc.memory = state.space_local;
203     state.loc.cp = 0;
204 
205     /* Save everything we need to reset temporarily. */
206 
207     for (i = 0; i < countof(save); i++)
208 	if ((mem = dmem->spaces_indexed[i]) != 0) {
209 	    alloc_temp_save(&save[i], mem);
210 	    if (mem->stable_memory != (gs_memory_t *)mem)
211 		alloc_temp_save(&save_stable[i],
212 				(gs_ref_memory_t *)mem->stable_memory);
213 	}
214 
215     /* Validate memory. */
216 
217     for (i = 0; i < countof(save); i++)
218 	if ((mem = dmem->spaces_indexed[i]) != 0) {
219 	    ialloc_validate_memory(mem, &state);
220 	    if (mem->stable_memory != (gs_memory_t *)mem)
221 		ialloc_validate_memory((gs_ref_memory_t *)mem->stable_memory,
222 				       &state);
223 	}
224 
225     /* Undo temporary changes. */
226 
227     for (i = 0; i < countof(save); i++)
228 	if ((mem = dmem->spaces_indexed[i]) != 0) {
229 	    if (mem->stable_memory != (gs_memory_t *)mem)
230 		alloc_temp_restore(&save_stable[i],
231 				   (gs_ref_memory_t *)mem->stable_memory);
232 	    alloc_temp_restore(&save[i], mem);
233 	}
234 }
235 void
ialloc_validate_memory(const gs_ref_memory_t * mem,gc_state_t * gcst)236 ialloc_validate_memory(const gs_ref_memory_t * mem, gc_state_t * gcst)
237 {
238     const gs_ref_memory_t *smem;
239     int level;
240 
241     for (smem = mem, level = 0; smem != 0;
242 	 smem = &smem->saved->state, --level
243 	) {
244 	const chunk_t *cp;
245 	int i;
246 
247 	if_debug3('6', "[6]validating memory 0x%lx, space %d, level %d\n",
248 		  (ulong) mem, mem->space, level);
249 	/* Validate chunks. */
250 	for (cp = smem->cfirst; cp != 0; cp = cp->cnext)
251 	    ialloc_validate_chunk(cp, gcst);
252 	/* Validate freelists. */
253 	for (i = 0; i < num_freelists; ++i) {
254 	    uint free_size = i << log2_obj_align_mod;
255 	    const obj_header_t *pfree;
256 
257 	    for (pfree = mem->freelists[i]; pfree != 0;
258 		 pfree = *(const obj_header_t * const *)pfree
259 		) {
260 		uint size = pfree[-1].o_size;
261 
262 		if (pfree[-1].o_type != &st_free) {
263 		    lprintf3("Non-free object 0x%lx(%u) on freelist %i!\n",
264 			     (ulong) pfree, size, i);
265 		    break;
266 		}
267 		if ((i == LARGE_FREELIST_INDEX && size < max_freelist_size) ||
268 		 (i != LARGE_FREELIST_INDEX &&
269 		 (size < free_size - obj_align_mask || size > free_size))) {
270 		    lprintf3("Object 0x%lx(%u) size wrong on freelist %i!\n",
271 			     (ulong) pfree, size, i);
272 		    break;
273 		}
274 	    }
275 	}
276     };
277 }
278 
279 /* Check the validity of an object's size. */
280 inline private bool
object_size_valid(const obj_header_t * pre,uint size,const chunk_t * cp)281 object_size_valid(const obj_header_t * pre, uint size, const chunk_t * cp)
282 {
283     return (pre->o_alone ? (const byte *)pre == cp->cbase :
284 	    size <= cp->ctop - (const byte *)(pre + 1));
285 }
286 
287 /* Validate all the objects in a chunk. */
288 #if IGC_PTR_STABILITY_CHECK
289 void ialloc_validate_pointer_stability(const obj_header_t * ptr_from,
290 				   const obj_header_t * ptr_to);
291 private void ialloc_validate_ref(const ref *, gc_state_t *, const obj_header_t *pre_fr);
292 private void ialloc_validate_ref_packed(const ref_packed *, gc_state_t *, const obj_header_t *pre_fr);
293 #else
294 private void ialloc_validate_ref(const ref *, gc_state_t *);
295 private void ialloc_validate_ref_packed(const ref_packed *, gc_state_t *);
296 #endif
297 void
ialloc_validate_chunk(const chunk_t * cp,gc_state_t * gcst)298 ialloc_validate_chunk(const chunk_t * cp, gc_state_t * gcst)
299 {
300     if_debug_chunk('6', "[6]validating chunk", cp);
301     SCAN_CHUNK_OBJECTS(cp);
302     DO_ALL
303 	if (pre->o_type == &st_free) {
304 	    if (!object_size_valid(pre, size, cp))
305 		lprintf3("Bad free object 0x%lx(%lu), in chunk 0x%lx!\n",
306 			 (ulong) (pre + 1), (ulong) size, (ulong) cp);
307 	} else
308 	    ialloc_validate_object(pre + 1, cp, gcst);
309     if_debug3('7', " [7]validating %s(%lu) 0x%lx\n",
310 	      struct_type_name_string(pre->o_type),
311 	      (ulong) size, (ulong) pre);
312     if (pre->o_type == &st_refs) {
313 	const ref_packed *rp = (const ref_packed *)(pre + 1);
314 	const char *end = (const char *)rp + size;
315 
316 	while ((const char *)rp < end) {
317 #	    if IGC_PTR_STABILITY_CHECK
318 	    ialloc_validate_ref_packed(rp, gcst, pre);
319 #	    else
320 	    ialloc_validate_ref_packed(rp, gcst);
321 #	    endif
322 	    rp = packed_next(rp);
323 	}
324     } else {
325 	struct_proc_enum_ptrs((*proc)) = pre->o_type->enum_ptrs;
326 	uint index = 0;
327 	enum_ptr_t eptr;
328 	gs_ptr_type_t ptype;
329 
330 	if (proc != gs_no_struct_enum_ptrs)
331 	    for (; (ptype = (*proc) (gcst_get_memory_ptr(gcst),
332 				     pre + 1, size, index, &eptr,
333 				     pre->o_type, gcst)) != 0; ++index) {
334 		if (eptr.ptr == 0)
335 		    DO_NOTHING;
336 		else if (ptype == ptr_struct_type) {
337 		    ialloc_validate_object(eptr.ptr, NULL, gcst);
338 #		    if IGC_PTR_STABILITY_CHECK
339 			ialloc_validate_pointer_stability(pre,
340 			    (const obj_header_t *)eptr.ptr - 1);
341 #		    endif
342 		} else if (ptype == ptr_ref_type)
343 #		    if IGC_PTR_STABILITY_CHECK
344 		    ialloc_validate_ref_packed(eptr.ptr, gcst, pre);
345 #		    else
346 		    ialloc_validate_ref_packed(eptr.ptr, gcst);
347 #		    endif
348 	    }
349     }
350     END_OBJECTS_SCAN
351 }
352 /* Validate a ref. */
353 #if IGC_PTR_STABILITY_CHECK
354 private void
ialloc_validate_ref_packed(const ref_packed * rp,gc_state_t * gcst,const obj_header_t * pre_fr)355 ialloc_validate_ref_packed(const ref_packed * rp, gc_state_t * gcst, const obj_header_t *pre_fr)
356 {
357     const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
358 
359     if (r_is_packed(rp)) {
360 	ref unpacked;
361 
362 	packed_get(cmem, rp, &unpacked);
363 	ialloc_validate_ref(&unpacked, gcst, pre_fr);
364     } else {
365 	ialloc_validate_ref((const ref *)rp, gcst, pre_fr);
366     }
367 }
368 #else
369 private void
ialloc_validate_ref_packed(const ref_packed * rp,gc_state_t * gcst)370 ialloc_validate_ref_packed(const ref_packed * rp, gc_state_t * gcst)
371 {
372     const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
373 
374     if (r_is_packed(rp)) {
375 	ref unpacked;
376 
377 	packed_get(cmem, rp, &unpacked);
378 	ialloc_validate_ref(&unpacked, gcst);
379     } else {
380 	ialloc_validate_ref((const ref *)rp, gcst);
381     }
382 }
383 #endif
384 private void
ialloc_validate_ref(const ref * pref,gc_state_t * gcst,const obj_header_t * pre_fr)385 ialloc_validate_ref(const ref * pref, gc_state_t * gcst
386 #		    if IGC_PTR_STABILITY_CHECK
387 			, const obj_header_t *pre_fr
388 #		    endif
389 		    )
390 {
391     const void *optr;
392     const ref *rptr;
393     const char *tname;
394     uint size;
395     const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
396 
397     if (!gs_debug_c('?'))
398 	return;			/* no check */
399     if (r_space(pref) == avm_foreign)
400 	return;
401     switch (r_type(pref)) {
402 	case t_file:
403 	    optr = pref->value.pfile;
404 	    goto cks;
405 	case t_device:
406 	    optr = pref->value.pdevice;
407 	    goto cks;
408 	case t_fontID:
409 	case t_struct:
410 	case t_astruct:
411 	    optr = pref->value.pstruct;
412 cks:	    if (optr != 0) {
413 		ialloc_validate_object(optr, NULL, gcst);
414 #		if IGC_PTR_STABILITY_CHECK
415 		    ialloc_validate_pointer_stability(pre_fr,
416 			    (const obj_header_t *)optr - 1);
417 #		endif
418 	    }
419 	    break;
420 	case t_name:
421 	    if (name_index_ptr(cmem, name_index(cmem, pref)) != pref->value.pname) {
422 		lprintf3("At 0x%lx, bad name %u, pname = 0x%lx\n",
423 			 (ulong) pref, (uint)name_index(cmem, pref),
424 			 (ulong) pref->value.pname);
425 		break;
426 	    } {
427 		ref sref;
428 
429 		name_string_ref(cmem, pref, &sref);
430 		if (r_space(&sref) != avm_foreign &&
431 		    !gc_locate(sref.value.const_bytes, gcst)
432 		    ) {
433 		    lprintf4("At 0x%lx, bad name %u, pname = 0x%lx, string 0x%lx not in any chunk\n",
434 			     (ulong) pref, (uint) r_size(pref),
435 			     (ulong) pref->value.pname,
436 			     (ulong) sref.value.const_bytes);
437 		}
438 	    }
439 	    break;
440 	case t_string:
441 	    if (r_size(pref) != 0 && !gc_locate(pref->value.bytes, gcst))
442 		lprintf3("At 0x%lx, string ptr 0x%lx[%u] not in any chunk\n",
443 			 (ulong) pref, (ulong) pref->value.bytes,
444 			 (uint) r_size(pref));
445 	    break;
446 	case t_array:
447 	    if (r_size(pref) == 0)
448 		break;
449 	    rptr = pref->value.refs;
450 	    size = r_size(pref);
451 	    tname = "array";
452 cka:	    if (!gc_locate(rptr, gcst)) {
453 		lprintf3("At 0x%lx, %s 0x%lx not in any chunk\n",
454 			 (ulong) pref, tname, (ulong) rptr);
455 		break;
456 	    } {
457 		uint i;
458 
459 		for (i = 0; i < size; ++i) {
460 		    const ref *elt = rptr + i;
461 
462 		    if (r_is_packed(elt))
463 			lprintf5("At 0x%lx, %s 0x%lx[%u] element %u is not a ref\n",
464 				 (ulong) pref, tname, (ulong) rptr, size, i);
465 		}
466 	    }
467 	    break;
468 	case t_shortarray:
469 	case t_mixedarray:
470 	    if (r_size(pref) == 0)
471 		break;
472 	    optr = pref->value.packed;
473 	    if (!gc_locate(optr, gcst))
474 		lprintf2("At 0x%lx, packed array 0x%lx not in any chunk\n",
475 			 (ulong) pref, (ulong) optr);
476 	    break;
477 	case t_dictionary:
478 	    {
479 		const dict *pdict = pref->value.pdict;
480 
481 		if (!r_has_type(&pdict->values, t_array) ||
482 		    !r_is_array(&pdict->keys) ||
483 		    !r_has_type(&pdict->count, t_integer) ||
484 		    !r_has_type(&pdict->maxlength, t_integer)
485 		    )
486 		    lprintf2("At 0x%lx, invalid dict 0x%lx\n",
487 			     (ulong) pref, (ulong) pdict);
488 		rptr = (const ref *)pdict;
489 	    }
490 	    size = sizeof(dict) / sizeof(ref);
491 	    tname = "dict";
492 	    goto cka;
493     }
494 }
495 
496 #if IGC_PTR_STABILITY_CHECK
497 /* Validate an pointer stability. */
498 void
ialloc_validate_pointer_stability(const obj_header_t * ptr_fr,const obj_header_t * ptr_to)499 ialloc_validate_pointer_stability(const obj_header_t * ptr_fr,
500 				   const obj_header_t * ptr_to)
501 {
502     static const char *sn[] = {"undef", "undef", "system", "undef",
503 		"global_stable", "global", "local_stable", "local"};
504 
505     if (ptr_fr->d.o.space_id < ptr_to->d.o.space_id) {
506 	const char *sn_fr = (ptr_fr->d.o.space_id < count_of(sn)
507 			? sn[ptr_fr->d.o.space_id] : "unknown");
508 	const char *sn_to = (ptr_to->d.o.space_id < count_of(sn)
509 			? sn[ptr_to->d.o.space_id] : "unknown");
510 
511 	lprintf6("Reference to a less stable object 0x%lx<%s> "
512 	         "in the space \'%s\' from 0x%lx<%s> in the space \'%s\' !\n",
513 		 (ulong) ptr_to, ptr_to->d.o.t.type->sname, sn_to,
514 		 (ulong) ptr_fr, ptr_fr->d.o.t.type->sname, sn_fr);
515     }
516 }
517 #endif
518 
519 /* Validate an object. */
520 void
ialloc_validate_object(const obj_header_t * ptr,const chunk_t * cp,gc_state_t * gcst)521 ialloc_validate_object(const obj_header_t * ptr, const chunk_t * cp,
522 		       gc_state_t * gcst)
523 {
524     const obj_header_t *pre = ptr - 1;
525     ulong size = pre_obj_contents_size(pre);
526     gs_memory_type_ptr_t otype = pre->o_type;
527     const char *oname;
528 
529     if (!gs_debug_c('?'))
530 	return;			/* no check */
531     if (cp == 0 && gcst != 0) {
532 	gc_state_t st;
533 
534 	st = *gcst;		/* no side effects! */
535 	if (!(cp = gc_locate(pre, &st))) {
536 	    lprintf1("Object 0x%lx not in any chunk!\n",
537 		     (ulong) ptr);
538 	    return;		/*gs_abort(); */
539 	}
540     }
541     if (otype == &st_free) {
542 	lprintf3("Reference to free object 0x%lx(%lu), in chunk 0x%lx!\n",
543 		 (ulong) ptr, (ulong) size, (ulong) cp);
544 	gs_abort(gcst->heap);
545     }
546     if ((cp != 0 && !object_size_valid(pre, size, cp)) ||
547 	otype->ssize == 0 ||
548 	size % otype->ssize != 0 ||
549 	(oname = struct_type_name_string(otype),
550 	 *oname < 33 || *oname > 126)
551 	) {
552 	lprintf2("Bad object 0x%lx(%lu),\n",
553 		 (ulong) ptr, (ulong) size);
554 	dprintf2(" ssize = %u, in chunk 0x%lx!\n",
555 		 otype->ssize, (ulong) cp);
556 	gs_abort(gcst->heap);
557     }
558 }
559 
560 #else /* !DEBUG */
561 
562 void
ialloc_validate_spaces(const gs_dual_memory_t * dmem)563 ialloc_validate_spaces(const gs_dual_memory_t * dmem)
564 {
565 }
566 
567 void
ialloc_validate_memory(const gs_ref_memory_t * mem,gc_state_t * gcst)568 ialloc_validate_memory(const gs_ref_memory_t * mem, gc_state_t * gcst)
569 {
570 }
571 
572 void
ialloc_validate_chunk(const chunk_t * cp,gc_state_t * gcst)573 ialloc_validate_chunk(const chunk_t * cp, gc_state_t * gcst)
574 {
575 }
576 
577 void
ialloc_validate_object(const obj_header_t * ptr,const chunk_t * cp,gc_state_t * gcst)578 ialloc_validate_object(const obj_header_t * ptr, const chunk_t * cp,
579 		       gc_state_t * gcst)
580 {
581 }
582 
583 #endif /* (!)DEBUG */
584