1 /*
2 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3 * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
4 * Copyright (c) 1997 by Silicon Graphics. All rights reserved.
5 *
6 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
7 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
8 *
9 * Permission is hereby granted to use or copy this program
10 * for any purpose, provided the above notices are retained on all copies.
11 * Permission to modify the code and to distribute modified code is granted,
12 * provided the above notices are retained, and a notice that the code was
13 * modified is included with the above copyright notice.
14 */
15 /* Boehm, October 9, 1995 1:16 pm PDT */
16 # include "gc_priv.h"
17
18 /* Do we want to and know how to save the call stack at the time of */
19 /* an allocation? How much space do we want to use in each object? */
20
21 # define START_FLAG ((word)0xfedcedcb)
22 # define END_FLAG ((word)0xbcdecdef)
23 /* Stored both one past the end of user object, and one before */
24 /* the end of the object as seen by the allocator. */
25
26
27 /* Object header */
28 typedef struct {
29 char * oh_string; /* object descriptor string */
30 word oh_int; /* object descriptor integers */
31 # ifdef SAVE_CALL_CHAIN
32 struct callinfo oh_ci[NFRAMES];
33 # endif
34 word oh_sz; /* Original malloc arg. */
35 word oh_sf; /* start flag */
36 } oh;
37 /* The size of the above structure is assumed not to dealign things, */
38 /* and to be a multiple of the word length. */
39
40 #define DEBUG_BYTES (sizeof (oh) + sizeof (word))
41 #undef ROUNDED_UP_WORDS
42 #define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1)
43
44
45 #ifdef SAVE_CALL_CHAIN
46 # define ADD_CALL_CHAIN(base) GC_save_callers(((oh *)(base)) -> oh_ci)
47 # define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci)
48 #else
49 # define ADD_CALL_CHAIN(base)
50 # define PRINT_CALL_CHAIN(base)
51 #endif
52
53 /* Check whether object with base pointer p has debugging info */
54 /* p is assumed to point to a legitimate object in our part */
55 /* of the heap. */
GC_has_debug_info(p)56 bool GC_has_debug_info(p)
57 ptr_t p;
58 {
59 register oh * ohdr = (oh *)p;
60 register ptr_t body = (ptr_t)(ohdr + 1);
61 register word sz = GC_size((ptr_t) ohdr);
62
63 if (HBLKPTR((ptr_t)ohdr) != HBLKPTR((ptr_t)body)
64 || sz < sizeof (oh)) {
65 return(FALSE);
66 }
67 if (ohdr -> oh_sz == sz) {
68 /* Object may have had debug info, but has been deallocated */
69 return(FALSE);
70 }
71 if (ohdr -> oh_sf == (START_FLAG ^ (word)body)) return(TRUE);
72 if (((word *)ohdr)[BYTES_TO_WORDS(sz)-1] == (END_FLAG ^ (word)body)) {
73 return(TRUE);
74 }
75 return(FALSE);
76 }
77
78 /* Store debugging info into p. Return displaced pointer. */
79 /* Assumes we don't hold allocation lock. */
GC_store_debug_info(p,sz,string,integer)80 ptr_t GC_store_debug_info(p, sz, string, integer)
81 register ptr_t p; /* base pointer */
82 word sz; /* bytes */
83 char * string;
84 word integer;
85 {
86 register word * result = (word *)((oh *)p + 1);
87 DCL_LOCK_STATE;
88
89 /* There is some argument that we should dissble signals here. */
90 /* But that's expensive. And this way things should only appear */
91 /* inconsistent while we're in the handler. */
92 LOCK();
93 ((oh *)p) -> oh_string = string;
94 ((oh *)p) -> oh_int = integer;
95 ((oh *)p) -> oh_sz = sz;
96 ((oh *)p) -> oh_sf = START_FLAG ^ (word)result;
97 ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] =
98 result[ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result;
99 UNLOCK();
100 return((ptr_t)result);
101 }
102
103 /* Check the object with debugging info at p */
104 /* return NIL if it's OK. Else return clobbered */
105 /* address. */
GC_check_annotated_obj(ohdr)106 ptr_t GC_check_annotated_obj(ohdr)
107 register oh * ohdr;
108 {
109 register ptr_t body = (ptr_t)(ohdr + 1);
110 register word gc_sz = GC_size((ptr_t)ohdr);
111 if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) {
112 return((ptr_t)(&(ohdr -> oh_sz)));
113 }
114 if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) {
115 return((ptr_t)(&(ohdr -> oh_sf)));
116 }
117 if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) {
118 return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1));
119 }
120 if (((word *)body)[ROUNDED_UP_WORDS(ohdr -> oh_sz)]
121 != (END_FLAG ^ (word)body)) {
122 return((ptr_t)((word *)body + ROUNDED_UP_WORDS(ohdr -> oh_sz)));
123 }
124 return(0);
125 }
126
GC_print_obj(p)127 void GC_print_obj(p)
128 ptr_t p;
129 {
130 register oh * ohdr = (oh *)GC_base(p);
131
132 GC_err_printf1("0x%lx (", (unsigned long)ohdr + sizeof(oh));
133 GC_err_puts(ohdr -> oh_string);
134 GC_err_printf2(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int),
135 (unsigned long)(ohdr -> oh_sz));
136 PRINT_CALL_CHAIN(ohdr);
137 }
GC_print_smashed_obj(p,clobbered_addr)138 void GC_print_smashed_obj(p, clobbered_addr)
139 ptr_t p, clobbered_addr;
140 {
141 register oh * ohdr = (oh *)GC_base(p);
142
143 GC_err_printf2("0x%lx in object at 0x%lx(", (unsigned long)clobbered_addr,
144 (unsigned long)p);
145 if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz))
146 || ohdr -> oh_string == 0) {
147 GC_err_printf1("<smashed>, appr. sz = %ld)\n",
148 GC_size((ptr_t)ohdr) - DEBUG_BYTES);
149 } else {
150 if (ohdr -> oh_string[0] == '\0') {
151 GC_err_puts("EMPTY(smashed?)");
152 } else {
153 GC_err_puts(ohdr -> oh_string);
154 }
155 GC_err_printf2(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int),
156 (unsigned long)(ohdr -> oh_sz));
157 PRINT_CALL_CHAIN(ohdr);
158 }
159 }
160
161 void GC_check_heap_proc();
162
GC_start_debugging()163 void GC_start_debugging()
164 {
165 GC_check_heap = GC_check_heap_proc;
166 GC_debugging_started = TRUE;
167 GC_register_displacement((word)sizeof(oh));
168 }
169
170 # if defined(__STDC__) || defined(__cplusplus)
GC_debug_register_displacement(GC_word offset)171 void GC_debug_register_displacement(GC_word offset)
172 # else
173 void GC_debug_register_displacement(offset)
174 GC_word offset;
175 # endif
176 {
177 GC_register_displacement(offset);
178 GC_register_displacement((word)sizeof(oh) + offset);
179 }
180
181 # ifdef __STDC__
GC_debug_malloc(size_t lb,char * s,int i)182 GC_PTR GC_debug_malloc(size_t lb, char * s, int i)
183 # else
184 GC_PTR GC_debug_malloc(lb, s, i)
185 size_t lb;
186 char * s;
187 int i;
188 # endif
189 {
190 GC_PTR result = GC_malloc(lb + DEBUG_BYTES);
191
192 if (result == 0) {
193 GC_err_printf1("GC_debug_malloc(%ld) returning NIL (",
194 (unsigned long) lb);
195 GC_err_puts(s);
196 GC_err_printf1(":%ld)\n", (unsigned long)i);
197 return(0);
198 }
199 if (!GC_debugging_started) {
200 GC_start_debugging();
201 }
202 ADD_CALL_CHAIN(result);
203 return (GC_store_debug_info(result, (word)lb, s, (word)i));
204 }
205
206 #ifdef STUBBORN_ALLOC
207 # ifdef __STDC__
GC_debug_malloc_stubborn(size_t lb,char * s,int i)208 GC_PTR GC_debug_malloc_stubborn(size_t lb, char * s, int i)
209 # else
210 GC_PTR GC_debug_malloc_stubborn(lb, s, i)
211 size_t lb;
212 char * s;
213 int i;
214 # endif
215 {
216 GC_PTR result = GC_malloc_stubborn(lb + DEBUG_BYTES);
217
218 if (result == 0) {
219 GC_err_printf1("GC_debug_malloc(%ld) returning NIL (",
220 (unsigned long) lb);
221 GC_err_puts(s);
222 GC_err_printf1(":%ld)\n", (unsigned long)i);
223 return(0);
224 }
225 if (!GC_debugging_started) {
226 GC_start_debugging();
227 }
228 ADD_CALL_CHAIN(result);
229 return (GC_store_debug_info(result, (word)lb, s, (word)i));
230 }
231
GC_debug_change_stubborn(p)232 void GC_debug_change_stubborn(p)
233 GC_PTR p;
234 {
235 register GC_PTR q = GC_base(p);
236 register hdr * hhdr;
237
238 if (q == 0) {
239 GC_err_printf1("Bad argument: 0x%lx to GC_debug_change_stubborn\n",
240 (unsigned long) p);
241 ABORT("GC_debug_change_stubborn: bad arg");
242 }
243 hhdr = HDR(q);
244 if (hhdr -> hb_obj_kind != STUBBORN) {
245 GC_err_printf1("GC_debug_change_stubborn arg not stubborn: 0x%lx\n",
246 (unsigned long) p);
247 ABORT("GC_debug_change_stubborn: arg not stubborn");
248 }
249 GC_change_stubborn(q);
250 }
251
GC_debug_end_stubborn_change(p)252 void GC_debug_end_stubborn_change(p)
253 GC_PTR p;
254 {
255 register GC_PTR q = GC_base(p);
256 register hdr * hhdr;
257
258 if (q == 0) {
259 GC_err_printf1("Bad argument: 0x%lx to GC_debug_end_stubborn_change\n",
260 (unsigned long) p);
261 ABORT("GC_debug_end_stubborn_change: bad arg");
262 }
263 hhdr = HDR(q);
264 if (hhdr -> hb_obj_kind != STUBBORN) {
265 GC_err_printf1("debug_end_stubborn_change arg not stubborn: 0x%lx\n",
266 (unsigned long) p);
267 ABORT("GC_debug_end_stubborn_change: arg not stubborn");
268 }
269 GC_end_stubborn_change(q);
270 }
271
272 #endif /* STUBBORN_ALLOC */
273
274 # ifdef __STDC__
GC_debug_malloc_atomic(size_t lb,char * s,int i)275 GC_PTR GC_debug_malloc_atomic(size_t lb, char * s, int i)
276 # else
277 GC_PTR GC_debug_malloc_atomic(lb, s, i)
278 size_t lb;
279 char * s;
280 int i;
281 # endif
282 {
283 GC_PTR result = GC_malloc_atomic(lb + DEBUG_BYTES);
284
285 if (result == 0) {
286 GC_err_printf1("GC_debug_malloc_atomic(%ld) returning NIL (",
287 (unsigned long) lb);
288 GC_err_puts(s);
289 GC_err_printf1(":%ld)\n", (unsigned long)i);
290 return(0);
291 }
292 if (!GC_debugging_started) {
293 GC_start_debugging();
294 }
295 ADD_CALL_CHAIN(result);
296 return (GC_store_debug_info(result, (word)lb, s, (word)i));
297 }
298
299 # ifdef __STDC__
GC_debug_malloc_uncollectable(size_t lb,char * s,int i)300 GC_PTR GC_debug_malloc_uncollectable(size_t lb, char * s, int i)
301 # else
302 GC_PTR GC_debug_malloc_uncollectable(lb, s, i)
303 size_t lb;
304 char * s;
305 int i;
306 # endif
307 {
308 GC_PTR result = GC_malloc_uncollectable(lb + DEBUG_BYTES);
309
310 if (result == 0) {
311 GC_err_printf1("GC_debug_malloc_uncollectable(%ld) returning NIL (",
312 (unsigned long) lb);
313 GC_err_puts(s);
314 GC_err_printf1(":%ld)\n", (unsigned long)i);
315 return(0);
316 }
317 if (!GC_debugging_started) {
318 GC_start_debugging();
319 }
320 ADD_CALL_CHAIN(result);
321 return (GC_store_debug_info(result, (word)lb, s, (word)i));
322 }
323
324 #ifdef ATOMIC_UNCOLLECTABLE
325 # ifdef __STDC__
GC_debug_malloc_atomic_uncollectable(size_t lb,char * s,int i)326 GC_PTR GC_debug_malloc_atomic_uncollectable(size_t lb, char * s, int i)
327 # else
328 GC_PTR GC_debug_malloc_atomic_uncollectable(lb, s, i)
329 size_t lb;
330 char * s;
331 int i;
332 # endif
333 {
334 GC_PTR result = GC_malloc_atomic_uncollectable(lb + DEBUG_BYTES);
335
336 if (result == 0) {
337 GC_err_printf1(
338 "GC_debug_malloc_atomic_uncollectable(%ld) returning NIL (",
339 (unsigned long) lb);
340 GC_err_puts(s);
341 GC_err_printf1(":%ld)\n", (unsigned long)i);
342 return(0);
343 }
344 if (!GC_debugging_started) {
345 GC_start_debugging();
346 }
347 ADD_CALL_CHAIN(result);
348 return (GC_store_debug_info(result, (word)lb, s, (word)i));
349 }
350 #endif /* ATOMIC_UNCOLLECTABLE */
351
352 # ifdef __STDC__
GC_debug_free(GC_PTR p)353 void GC_debug_free(GC_PTR p)
354 # else
355 void GC_debug_free(p)
356 GC_PTR p;
357 # endif
358 {
359 register GC_PTR base = GC_base(p);
360 register ptr_t clobbered;
361
362 if (base == 0) {
363 GC_err_printf1("Attempt to free invalid pointer %lx\n",
364 (unsigned long)p);
365 if (p != 0) ABORT("free(invalid pointer)");
366 }
367 if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
368 GC_err_printf1(
369 "GC_debug_free called on pointer %lx wo debugging info\n",
370 (unsigned long)p);
371 } else {
372 clobbered = GC_check_annotated_obj((oh *)base);
373 if (clobbered != 0) {
374 if (((oh *)base) -> oh_sz == GC_size(base)) {
375 GC_err_printf0(
376 "GC_debug_free: found previously deallocated (?) object at ");
377 } else {
378 GC_err_printf0("GC_debug_free: found smashed object at ");
379 }
380 GC_print_smashed_obj(p, clobbered);
381 }
382 /* Invalidate size */
383 ((oh *)base) -> oh_sz = GC_size(base);
384 }
385 # ifdef FIND_LEAK
386 GC_free(base);
387 # else
388 {
389 register hdr * hhdr = HDR(p);
390 bool uncollectable = FALSE;
391
392 if (hhdr -> hb_obj_kind == UNCOLLECTABLE) {
393 uncollectable = TRUE;
394 }
395 # ifdef ATOMIC_UNCOLLECTABLE
396 if (hhdr -> hb_obj_kind == AUNCOLLECTABLE) {
397 uncollectable = TRUE;
398 }
399 # endif
400 if (uncollectable) GC_free(base);
401 }
402 # endif
403 }
404
405 # ifdef __STDC__
GC_debug_realloc(GC_PTR p,size_t lb,char * s,int i)406 GC_PTR GC_debug_realloc(GC_PTR p, size_t lb, char *s, int i)
407 # else
408 GC_PTR GC_debug_realloc(p, lb, s, i)
409 GC_PTR p;
410 size_t lb;
411 char *s;
412 int i;
413 # endif
414 {
415 register GC_PTR base = GC_base(p);
416 register ptr_t clobbered;
417 register GC_PTR result;
418 register size_t copy_sz = lb;
419 register size_t old_sz;
420 register hdr * hhdr;
421
422 if (p == 0) return(GC_debug_malloc(lb, s, i));
423 if (base == 0) {
424 GC_err_printf1(
425 "Attempt to reallocate invalid pointer %lx\n", (unsigned long)p);
426 ABORT("realloc(invalid pointer)");
427 }
428 if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
429 GC_err_printf1(
430 "GC_debug_realloc called on pointer %lx wo debugging info\n",
431 (unsigned long)p);
432 return(GC_realloc(p, lb));
433 }
434 hhdr = HDR(base);
435 switch (hhdr -> hb_obj_kind) {
436 # ifdef STUBBORN_ALLOC
437 case STUBBORN:
438 result = GC_debug_malloc_stubborn(lb, s, i);
439 break;
440 # endif
441 case NORMAL:
442 result = GC_debug_malloc(lb, s, i);
443 break;
444 case PTRFREE:
445 result = GC_debug_malloc_atomic(lb, s, i);
446 break;
447 case UNCOLLECTABLE:
448 result = GC_debug_malloc_uncollectable(lb, s, i);
449 break;
450 # ifdef ATOMIC_UNCOLLECTABLE
451 case AUNCOLLECTABLE:
452 result = GC_debug_malloc_atomic_uncollectable(lb, s, i);
453 break;
454 # endif
455 default:
456 GC_err_printf0("GC_debug_realloc: encountered bad kind\n");
457 ABORT("bad kind");
458 }
459 clobbered = GC_check_annotated_obj((oh *)base);
460 if (clobbered != 0) {
461 GC_err_printf0("GC_debug_realloc: found smashed object at ");
462 GC_print_smashed_obj(p, clobbered);
463 }
464 old_sz = ((oh *)base) -> oh_sz;
465 if (old_sz < copy_sz) copy_sz = old_sz;
466 if (result == 0) return(0);
467 BCOPY(p, result, copy_sz);
468 GC_debug_free(p);
469 return(result);
470 }
471
472 /* Check all marked objects in the given block for validity */
473 /*ARGSUSED*/
GC_check_heap_block(hbp,dummy)474 void GC_check_heap_block(hbp, dummy)
475 register struct hblk *hbp; /* ptr to current heap block */
476 word dummy;
477 {
478 register struct hblkhdr * hhdr = HDR(hbp);
479 register word sz = hhdr -> hb_sz;
480 register int word_no;
481 register word *p, *plim;
482
483 p = (word *)(hbp->hb_body);
484 word_no = HDR_WORDS;
485 if (sz > MAXOBJSZ) {
486 plim = p;
487 } else {
488 plim = (word *)((((word)hbp) + HBLKSIZE) - WORDS_TO_BYTES(sz));
489 }
490 /* go through all words in block */
491 while( p <= plim ) {
492 if( mark_bit_from_hdr(hhdr, word_no)
493 && GC_has_debug_info((ptr_t)p)) {
494 ptr_t clobbered = GC_check_annotated_obj((oh *)p);
495
496 if (clobbered != 0) {
497 GC_err_printf0(
498 "GC_check_heap_block: found smashed object at ");
499 GC_print_smashed_obj((ptr_t)p, clobbered);
500 }
501 }
502 word_no += sz;
503 p += sz;
504 }
505 }
506
507
508 /* This assumes that all accessible objects are marked, and that */
509 /* I hold the allocation lock. Normally called by collector. */
GC_check_heap_proc()510 void GC_check_heap_proc()
511 {
512 # ifndef SMALL_CONFIG
513 if (sizeof(oh) & (2 * sizeof(word) - 1) != 0) {
514 ABORT("Alignment problem: object header has inappropriate size\n");
515 }
516 # endif
517 GC_apply_to_all_blocks(GC_check_heap_block, (word)0);
518 }
519
520 struct closure {
521 GC_finalization_proc cl_fn;
522 GC_PTR cl_data;
523 };
524
525 # ifdef __STDC__
GC_make_closure(GC_finalization_proc fn,void * data)526 void * GC_make_closure(GC_finalization_proc fn, void * data)
527 # else
528 GC_PTR GC_make_closure(fn, data)
529 GC_finalization_proc fn;
530 GC_PTR data;
531 # endif
532 {
533 struct closure * result =
534 (struct closure *) GC_malloc(sizeof (struct closure));
535
536 result -> cl_fn = fn;
537 result -> cl_data = data;
538 return((GC_PTR)result);
539 }
540
541 # ifdef __STDC__
GC_debug_invoke_finalizer(void * obj,void * data)542 void GC_debug_invoke_finalizer(void * obj, void * data)
543 # else
544 void GC_debug_invoke_finalizer(obj, data)
545 char * obj;
546 char * data;
547 # endif
548 {
549 register struct closure * cl = (struct closure *) data;
550
551 (*(cl -> cl_fn))((GC_PTR)((char *)obj + sizeof(oh)), cl -> cl_data);
552 }
553
554
555 # ifdef __STDC__
GC_debug_register_finalizer(GC_PTR obj,GC_finalization_proc fn,GC_PTR cd,GC_finalization_proc * ofn,GC_PTR * ocd)556 void GC_debug_register_finalizer(GC_PTR obj, GC_finalization_proc fn,
557 GC_PTR cd, GC_finalization_proc *ofn,
558 GC_PTR *ocd)
559 # else
560 void GC_debug_register_finalizer(obj, fn, cd, ofn, ocd)
561 GC_PTR obj;
562 GC_finalization_proc fn;
563 GC_PTR cd;
564 GC_finalization_proc *ofn;
565 GC_PTR *ocd;
566 # endif
567 {
568 ptr_t base = GC_base(obj);
569 if (0 == base || (ptr_t)obj - base != sizeof(oh)) {
570 GC_err_printf1(
571 "GC_register_finalizer called with non-base-pointer 0x%lx\n",
572 obj);
573 }
574 GC_register_finalizer(base, GC_debug_invoke_finalizer,
575 GC_make_closure(fn,cd), ofn, ocd);
576 }
577
578 # ifdef __STDC__
GC_debug_register_finalizer_ignore_self(GC_PTR obj,GC_finalization_proc fn,GC_PTR cd,GC_finalization_proc * ofn,GC_PTR * ocd)579 void GC_debug_register_finalizer_ignore_self
580 (GC_PTR obj, GC_finalization_proc fn,
581 GC_PTR cd, GC_finalization_proc *ofn,
582 GC_PTR *ocd)
583 # else
584 void GC_debug_register_finalizer_ignore_self
585 (obj, fn, cd, ofn, ocd)
586 GC_PTR obj;
587 GC_finalization_proc fn;
588 GC_PTR cd;
589 GC_finalization_proc *ofn;
590 GC_PTR *ocd;
591 # endif
592 {
593 ptr_t base = GC_base(obj);
594 if (0 == base || (ptr_t)obj - base != sizeof(oh)) {
595 GC_err_printf1(
596 "GC_register_finalizer_ignore_self called with non-base-pointer 0x%lx\n",
597 obj);
598 }
599 GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer,
600 GC_make_closure(fn,cd), ofn, ocd);
601 }
602