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 * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
6 * Copyright (C) 2007 Free Software Foundation, Inc
8 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
9 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
11 * Permission is hereby granted to use or copy this program
12 * for any purpose, provided the above notices are retained on all copies.
13 * Permission to modify the code and to distribute modified code is granted,
14 * provided the above notices are retained, and a notice that the code was
15 * modified is included with the above copyright notice.
18 #include "private/dbg_mlc.h"
25 #ifndef SHORT_DBG_HDRS
26 /* Check whether object with base pointer p has debugging info. */
27 /* p is assumed to point to a legitimate object in our part */
29 /* This excludes the check as to whether the back pointer is */
30 /* odd, which is added by the GC_HAS_DEBUG_INFO macro. */
31 /* Note that if DBG_HDRS_ALL is set, uncollectable objects */
32 /* on free lists may not have debug information set. Thus it's */
33 /* not always safe to return TRUE (1), even if the client does */
34 /* its part. Return -1 if the object with debug info has been */
35 /* marked as deallocated. */
36 GC_INNER int GC_has_other_debug_info(ptr_t p)
38 ptr_t body = (ptr_t)((oh *)p + 1);
41 if (HBLKPTR(p) != HBLKPTR((ptr_t)body)
42 || sz < DEBUG_BYTES + EXTRA_BYTES) {
45 if (((oh *)p) -> oh_sf != (START_FLAG ^ (word)body)
46 && ((word *)p)[BYTES_TO_WORDS(sz)-1] != (END_FLAG ^ (word)body)) {
49 if (((oh *)p)->oh_sz == sz) {
50 /* Object may have had debug info, but has been deallocated */
55 #endif /* !SHORT_DBG_HDRS */
61 # if defined(__GLIBC__) || defined(SOLARIS) \
62 || defined(HPUX) || defined(IRIX5) || defined(OSF1)
63 # define RANDOM() random()
65 # define RANDOM() (long)rand()
68 /* Store back pointer to source in dest, if that appears to be possible. */
69 /* This is not completely safe, since we may mistakenly conclude that */
70 /* dest has a debugging wrapper. But the error probability is very */
71 /* small, and this shouldn't be used in production code. */
72 /* We assume that dest is the real base pointer. Source will usually */
73 /* be a pointer to the interior of an object. */
74 GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest)
76 if (GC_HAS_DEBUG_INFO(dest)) {
77 ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source);
81 GC_INNER void GC_marked_for_finalization(ptr_t dest)
83 GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest);
86 /* Store information about the object referencing dest in *base_p */
88 /* source is root ==> *base_p = address, *offset_p = 0 */
89 /* source is heap object ==> *base_p != 0, *offset_p = offset */
90 /* Returns 1 on success, 0 if source couldn't be determined. */
91 /* Dest can be any address within a heap object. */
92 GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void *dest, void **base_p,
95 oh * hdr = (oh *)GC_base(dest);
100 /* Explicitly instruct the code analysis tool that */
101 /* GC_get_back_ptr_info is not expected to be called with an */
102 /* incorrect "dest" value. */
103 if (!hdr) ABORT("Invalid GC_get_back_ptr_info argument");
105 if (!GC_HAS_DEBUG_INFO((ptr_t) hdr)) return GC_NO_SPACE;
106 bp = GC_REVEAL_POINTER(hdr -> oh_back_ptr);
107 if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD;
108 if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG;
109 if (NOT_MARKED == bp) return GC_UNREFERENCED;
111 /* Heuristically try to fix off by 1 errors we introduced by */
112 /* insisting on even addresses. */
114 ptr_t alternate_ptr = bp + 1;
115 ptr_t target = *(ptr_t *)bp;
116 ptr_t alternate_target = *(ptr_t *)alternate_ptr;
118 if ((word)alternate_target >= (word)GC_least_plausible_heap_addr
119 && (word)alternate_target <= (word)GC_greatest_plausible_heap_addr
120 && ((word)target < (word)GC_least_plausible_heap_addr
121 || (word)target > (word)GC_greatest_plausible_heap_addr)) {
126 bp_base = GC_base(bp);
130 return GC_REFD_FROM_ROOT;
132 if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh);
134 *offset_p = bp - bp_base;
135 return GC_REFD_FROM_HEAP;
139 /* Generate a random heap address. */
140 /* The resulting address is in the heap, but */
141 /* not necessarily inside a valid object. */
142 GC_API void * GC_CALL GC_generate_random_heap_address(void)
146 word heap_offset = RANDOM();
148 if (GC_heapsize > RAND_MAX) {
149 heap_offset *= RAND_MAX;
150 heap_offset += RANDOM();
152 heap_offset %= GC_heapsize;
153 /* This doesn't yield a uniform distribution, especially if */
154 /* e.g. RAND_MAX = 1.5* GC_heapsize. But for typical cases, */
155 /* it's not too bad. */
157 if (i >= GC_n_heap_sects)
158 ABORT("GC_generate_random_heap_address: size inconsistency");
160 size = GC_heap_sects[i].hs_bytes;
161 if (heap_offset < size) {
167 return GC_heap_sects[i].hs_start + heap_offset;
170 /* Generate a random address inside a valid marked heap object. */
171 GC_API void * GC_CALL GC_generate_random_valid_address(void)
176 result = GC_generate_random_heap_address();
177 base = GC_base(result);
178 } while (base == 0 || !GC_is_marked(base));
182 /* Print back trace for p */
183 GC_API void GC_CALL GC_print_backtrace(void *p)
191 GC_print_heap_obj(GC_base(current));
194 source = GC_get_back_ptr_info(current, &base, &offset);
195 if (GC_UNREFERENCED == source) {
196 GC_err_printf("Reference could not be found\n");
199 if (GC_NO_SPACE == source) {
200 GC_err_printf("No debug info in object: Can't find reference\n");
203 GC_err_printf("Reachable via %d levels of pointers from ", i);
205 case GC_REFD_FROM_ROOT:
206 GC_err_printf("root at %p\n\n", base);
208 case GC_REFD_FROM_REG:
209 GC_err_printf("root in register\n\n");
211 case GC_FINALIZER_REFD:
212 GC_err_printf("list of finalizable objects\n\n");
214 case GC_REFD_FROM_HEAP:
215 GC_err_printf("offset %ld in object:\n", (long)offset);
216 /* Take GC_base(base) to get real base, i.e. header. */
217 GC_print_heap_obj(GC_base(base));
220 GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n");
228 /* Force a garbage collection and generate/print a backtrace */
229 /* from a random heap address. */
230 GC_INNER void GC_generate_random_backtrace_no_gc(void)
233 current = GC_generate_random_valid_address();
234 GC_printf("\n****Chosen address %p in object\n", current);
235 GC_print_backtrace(current);
238 GC_API void GC_CALL GC_generate_random_backtrace(void)
240 if (GC_try_to_collect(GC_never_stop_func) == 0) {
241 GC_err_printf("Cannot generate a backtrace: "
242 "garbage collection is disabled!\n");
245 GC_generate_random_backtrace_no_gc();
248 #endif /* KEEP_BACK_PTRS */
250 # define CROSSES_HBLK(p, sz) \
251 (((word)((p) + sizeof(oh) + (sz) - 1) ^ (word)(p)) >= HBLKSIZE)
253 /* Store debugging info into p. Return displaced pointer. */
254 /* This version assumes we do hold the allocation lock. */
255 STATIC ptr_t GC_store_debug_info_inner(ptr_t p, word sz GC_ATTR_UNUSED,
256 const char *string, int linenum)
258 word * result = (word *)((oh *)p + 1);
260 GC_ASSERT(GC_size(p) >= sizeof(oh) + sz);
261 GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz)));
262 # ifdef KEEP_BACK_PTRS
263 ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED);
265 # ifdef MAKE_BACK_GRAPH
266 ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0);
268 ((oh *)p) -> oh_string = string;
269 ((oh *)p) -> oh_int = (word)linenum;
270 # ifndef SHORT_DBG_HDRS
271 ((oh *)p) -> oh_sz = sz;
272 ((oh *)p) -> oh_sf = START_FLAG ^ (word)result;
273 ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] =
274 result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result;
276 return((ptr_t)result);
279 GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *string,
286 result = GC_store_debug_info_inner(p, sz, string, linenum);
291 #ifndef SHORT_DBG_HDRS
292 /* Check the object with debugging info at ohdr. */
293 /* Return NULL if it's OK. Else return clobbered */
295 STATIC ptr_t GC_check_annotated_obj(oh *ohdr)
297 ptr_t body = (ptr_t)(ohdr + 1);
298 word gc_sz = GC_size((ptr_t)ohdr);
299 if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) {
300 return((ptr_t)(&(ohdr -> oh_sz)));
302 if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) {
303 return((ptr_t)(&(ohdr -> oh_sf)));
305 if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) {
306 return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1));
308 if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)]
309 != (END_FLAG ^ (word)body)) {
310 return((ptr_t)((word *)body + SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz)));
314 #endif /* !SHORT_DBG_HDRS */
316 STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0};
318 GC_API void GC_CALL GC_register_describe_type_fn(int kind,
319 GC_describe_type_fn fn)
321 GC_describe_type_fns[kind] = fn;
324 #define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int)
326 #ifndef SHORT_DBG_HDRS
327 # define IF_NOT_SHORTDBG_HDRS(x) x
328 # define COMMA_IFNOT_SHORTDBG_HDRS(x) /* comma */, x
330 # define IF_NOT_SHORTDBG_HDRS(x) /* empty */
331 # define COMMA_IFNOT_SHORTDBG_HDRS(x) /* empty */
334 /* Print a human-readable description of the object to stderr. */
335 /* p points to somewhere inside an object with the debugging info. */
336 STATIC void GC_print_obj(ptr_t p)
338 oh * ohdr = (oh *)GC_base(p);
343 char buffer[GC_TYPE_DESCR_LEN + 1];
345 GC_ASSERT(I_DONT_HOLD_LOCK());
347 if (!ohdr) ABORT("Invalid GC_print_obj argument");
350 q = (ptr_t)(ohdr + 1);
351 /* Print a type description for the object whose client-visible */
353 hhdr = GC_find_header(q);
354 kind = hhdr -> hb_obj_kind;
355 if (0 != GC_describe_type_fns[kind] && GC_is_marked(ohdr)) {
356 /* This should preclude free list objects except with */
357 /* thread-local allocation. */
358 buffer[GC_TYPE_DESCR_LEN] = 0;
359 (GC_describe_type_fns[kind])(q, buffer);
360 GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0);
365 kind_str = "PTRFREE";
371 kind_str = "UNCOLLECTABLE";
373 # ifdef ATOMIC_UNCOLLECTABLE
375 kind_str = "ATOMIC_UNCOLLECTABLE";
379 kind_str = "STUBBORN";
383 /* The alternative is to use snprintf(buffer) but it is */
384 /* not quite portable (see vsnprintf in misc.c). */
388 if (NULL != kind_str) {
389 GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,") " %s)\n",
390 (ptr_t)ohdr + sizeof(oh),
391 ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
392 COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
395 GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,")
396 " kind=%d descr=0x%lx)\n", (ptr_t)ohdr + sizeof(oh),
397 ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
398 COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
399 kind, (unsigned long)hhdr->hb_descr);
401 PRINT_CALL_CHAIN(ohdr);
404 STATIC void GC_debug_print_heap_obj_proc(ptr_t p)
406 GC_ASSERT(I_DONT_HOLD_LOCK());
407 if (GC_HAS_DEBUG_INFO(p)) {
410 GC_default_print_heap_obj_proc(p);
414 #ifndef SHORT_DBG_HDRS
415 /* Use GC_err_printf and friends to print a description of the object */
416 /* whose client-visible address is p, and which was smashed at */
417 /* clobbered_addr. */
418 STATIC void GC_print_smashed_obj(const char *msg, ptr_t p,
419 ptr_t clobbered_addr)
421 oh * ohdr = (oh *)GC_base(p);
423 GC_ASSERT(I_DONT_HOLD_LOCK());
425 if (!ohdr) ABORT("Invalid GC_print_smashed_obj argument");
427 if ((word)clobbered_addr <= (word)(&ohdr->oh_sz)
428 || ohdr -> oh_string == 0) {
430 "%s %p in or near object at %p(<smashed>, appr. sz = %lu)\n",
431 msg, clobbered_addr, p,
432 (unsigned long)(GC_size((ptr_t)ohdr) - DEBUG_BYTES));
434 GC_err_printf("%s %p in or near object at %p (%s:%d, sz=%lu)\n",
435 msg, clobbered_addr, p,
436 (word)(ohdr -> oh_string) < HBLKSIZE ? "(smashed string)" :
437 ohdr -> oh_string[0] == '\0' ? "EMPTY(smashed?)" :
439 GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz));
440 PRINT_CALL_CHAIN(ohdr);
445 #ifndef SHORT_DBG_HDRS
446 STATIC void GC_check_heap_proc (void);
447 STATIC void GC_print_all_smashed_proc (void);
449 STATIC void GC_do_nothing(void) {}
452 STATIC void GC_start_debugging_inner(void)
454 GC_ASSERT(I_HOLD_LOCK());
455 # ifndef SHORT_DBG_HDRS
456 GC_check_heap = GC_check_heap_proc;
457 GC_print_all_smashed = GC_print_all_smashed_proc;
459 GC_check_heap = GC_do_nothing;
460 GC_print_all_smashed = GC_do_nothing;
462 GC_print_heap_obj = GC_debug_print_heap_obj_proc;
463 GC_debugging_started = TRUE;
464 GC_register_displacement_inner((word)sizeof(oh));
467 GC_INNER void GC_start_debugging(void)
472 GC_start_debugging_inner();
476 size_t GC_debug_header_size = sizeof(oh);
478 GC_API void GC_CALL GC_debug_register_displacement(size_t offset)
483 GC_register_displacement_inner(offset);
484 GC_register_displacement_inner((word)sizeof(oh) + offset);
488 GC_API void * GC_CALL GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS)
491 /* Note that according to malloc() specification, if size is 0 then */
492 /* malloc() returns either NULL, or a unique pointer value that can */
493 /* later be successfully passed to free(). We always do the latter. */
494 result = GC_malloc(lb + DEBUG_BYTES);
497 GC_err_printf("GC_debug_malloc(%lu) returning NULL (%s:%d)\n",
498 (unsigned long)lb, s, i);
501 if (!GC_debugging_started) {
502 GC_start_debugging();
504 ADD_CALL_CHAIN(result, ra);
505 return (GC_store_debug_info(result, (word)lb, s, i));
508 GC_API void * GC_CALL GC_debug_malloc_ignore_off_page(size_t lb,
511 void * result = GC_malloc_ignore_off_page(lb + DEBUG_BYTES);
514 GC_err_printf("GC_debug_malloc_ignore_off_page(%lu)"
515 " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
518 if (!GC_debugging_started) {
519 GC_start_debugging();
521 ADD_CALL_CHAIN(result, ra);
522 return (GC_store_debug_info(result, (word)lb, s, i));
525 GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb,
528 void * result = GC_malloc_atomic_ignore_off_page(lb + DEBUG_BYTES);
531 GC_err_printf("GC_debug_malloc_atomic_ignore_off_page(%lu)"
532 " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
535 if (!GC_debugging_started) {
536 GC_start_debugging();
538 ADD_CALL_CHAIN(result, ra);
539 return (GC_store_debug_info(result, (word)lb, s, i));
543 /* An allocation function for internal use. Normally internally */
544 /* allocated objects do not have debug information. But in this */
545 /* case, we need to make sure that all objects have debug headers. */
546 /* We assume debugging was started in collector initialization, and */
547 /* we already hold the GC lock. */
548 GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k)
550 void * result = GC_generic_malloc_inner(lb + DEBUG_BYTES, k);
553 GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
557 if (!GC_debugging_started) {
558 GC_start_debugging_inner();
560 ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
561 return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
564 GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb,
567 void * result = GC_generic_malloc_inner_ignore_off_page(
568 lb + DEBUG_BYTES, k);
571 GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
575 if (!GC_debugging_started) {
576 GC_start_debugging_inner();
578 ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
579 return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
581 #endif /* DBG_HDRS_ALL */
583 #ifdef STUBBORN_ALLOC
584 GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS)
586 void * result = GC_malloc_stubborn(lb + DEBUG_BYTES);
589 GC_err_printf("GC_debug_malloc_stubborn(%lu)"
590 " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
593 if (!GC_debugging_started) {
594 GC_start_debugging();
596 ADD_CALL_CHAIN(result, ra);
597 return (GC_store_debug_info(result, (word)lb, s, i));
600 GC_API void GC_CALL GC_debug_change_stubborn(const void *p)
602 const void * q = GC_base_C(p);
606 GC_err_printf("Bad argument: %p to GC_debug_change_stubborn\n", p);
607 ABORT("GC_debug_change_stubborn: bad arg");
610 if (hhdr -> hb_obj_kind != STUBBORN) {
611 GC_err_printf("GC_debug_change_stubborn arg not stubborn: %p\n", p);
612 ABORT("GC_debug_change_stubborn: arg not stubborn");
614 GC_change_stubborn(q);
617 GC_API void GC_CALL GC_debug_end_stubborn_change(const void *p)
619 const void * q = GC_base_C(p);
623 GC_err_printf("Bad argument: %p to GC_debug_end_stubborn_change\n", p);
624 ABORT("GC_debug_end_stubborn_change: bad arg");
627 if (hhdr -> hb_obj_kind != STUBBORN) {
628 GC_err_printf("debug_end_stubborn_change arg not stubborn: %p\n", p);
629 ABORT("GC_debug_end_stubborn_change: arg not stubborn");
631 GC_end_stubborn_change(q);
634 #else /* !STUBBORN_ALLOC */
636 GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS)
638 return GC_debug_malloc(lb, OPT_RA s, i);
641 GC_API void GC_CALL GC_debug_change_stubborn(
642 const void * p GC_ATTR_UNUSED) {}
644 GC_API void GC_CALL GC_debug_end_stubborn_change(
645 const void * p GC_ATTR_UNUSED) {}
646 #endif /* !STUBBORN_ALLOC */
648 GC_API void * GC_CALL GC_debug_malloc_atomic(size_t lb, GC_EXTRA_PARAMS)
650 void * result = GC_malloc_atomic(lb + DEBUG_BYTES);
653 GC_err_printf("GC_debug_malloc_atomic(%lu) returning NULL (%s:%d)\n",
654 (unsigned long)lb, s, i);
657 if (!GC_debugging_started) {
658 GC_start_debugging();
660 ADD_CALL_CHAIN(result, ra);
661 return (GC_store_debug_info(result, (word)lb, s, i));
664 GC_API char * GC_CALL GC_debug_strdup(const char *str, GC_EXTRA_PARAMS)
670 GC_err_printf("strdup(NULL) behavior is undefined\n");
674 lb = strlen(str) + 1;
675 copy = GC_debug_malloc_atomic(lb, OPT_RA s, i);
682 BCOPY(str, copy, lb);
686 GC_API char * GC_CALL GC_debug_strndup(const char *str, size_t size,
690 size_t len = strlen(str); /* str is expected to be non-NULL */
693 copy = GC_debug_malloc_atomic(len + 1, OPT_RA s, i);
700 BCOPY(str, copy, len);
705 #ifdef GC_REQUIRE_WCSDUP
706 # include <wchar.h> /* for wcslen() */
708 GC_API wchar_t * GC_CALL GC_debug_wcsdup(const wchar_t *str, GC_EXTRA_PARAMS)
710 size_t lb = (wcslen(str) + 1) * sizeof(wchar_t);
711 wchar_t *copy = GC_debug_malloc_atomic(lb, OPT_RA s, i);
718 BCOPY(str, copy, lb);
721 #endif /* GC_REQUIRE_WCSDUP */
723 GC_API void * GC_CALL GC_debug_malloc_uncollectable(size_t lb,
726 void * result = GC_malloc_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES);
729 GC_err_printf("GC_debug_malloc_uncollectable(%lu)"
730 " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
733 if (!GC_debugging_started) {
734 GC_start_debugging();
736 ADD_CALL_CHAIN(result, ra);
737 return (GC_store_debug_info(result, (word)lb, s, i));
740 #ifdef ATOMIC_UNCOLLECTABLE
741 GC_API void * GC_CALL GC_debug_malloc_atomic_uncollectable(size_t lb,
745 GC_malloc_atomic_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES);
748 GC_err_printf("GC_debug_malloc_atomic_uncollectable(%lu)"
749 " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
752 if (!GC_debugging_started) {
753 GC_start_debugging();
755 ADD_CALL_CHAIN(result, ra);
756 return (GC_store_debug_info(result, (word)lb, s, i));
758 #endif /* ATOMIC_UNCOLLECTABLE */
760 #ifndef GC_FREED_MEM_MARKER
761 # if CPP_WORDSZ == 32
762 # define GC_FREED_MEM_MARKER 0xdeadbeef
764 # define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef)
768 GC_API void GC_CALL GC_debug_free(void * p)
775 GC_err_printf("Attempt to free invalid pointer %p\n", p);
776 ABORT("Invalid pointer passed to free()");
778 if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
780 "GC_debug_free called on pointer %p w/o debugging info\n", p);
782 # ifndef SHORT_DBG_HDRS
783 ptr_t clobbered = GC_check_annotated_obj((oh *)base);
784 word sz = GC_size(base);
785 if (clobbered != 0) {
786 GC_have_errors = TRUE;
787 if (((oh *)base) -> oh_sz == sz) {
788 GC_print_smashed_obj(
789 "GC_debug_free: found previously deallocated (?) object at",
791 return; /* ignore double free */
793 GC_print_smashed_obj("GC_debug_free: found smashed location at",
797 /* Invalidate size (mark the object as deallocated) */
798 ((oh *)base) -> oh_sz = sz;
799 # endif /* SHORT_DBG_HDRS */
802 # ifndef SHORT_DBG_HDRS
803 && ((ptr_t)p - (ptr_t)base != sizeof(oh) || !GC_findleak_delay_free)
809 if (hhdr -> hb_obj_kind == UNCOLLECTABLE
810 # ifdef ATOMIC_UNCOLLECTABLE
811 || hhdr -> hb_obj_kind == AUNCOLLECTABLE
817 size_t obj_sz = BYTES_TO_WORDS(hhdr -> hb_sz - sizeof(oh));
819 for (i = 0; i < obj_sz; ++i)
820 ((word *)p)[i] = GC_FREED_MEM_MARKER;
821 GC_ASSERT((word *)p + i == (word *)(base + hhdr -> hb_sz));
823 } /* !GC_find_leak */
826 #if defined(THREADS) && defined(DBG_HDRS_ALL)
827 /* Used internally; we assume it's called correctly. */
828 GC_INNER void GC_debug_free_inner(void * p)
830 ptr_t base = GC_base(p);
831 GC_ASSERT((ptr_t)p - (ptr_t)base == sizeof(oh));
833 if (!base) ABORT("Invalid GC_debug_free_inner argument");
835 # ifndef SHORT_DBG_HDRS
836 /* Invalidate size */
837 ((oh *)base) -> oh_sz = GC_size(base);
843 GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS)
849 return(GC_debug_malloc(lb, OPT_RA s, i));
853 GC_err_printf("Attempt to reallocate invalid pointer %p\n", p);
854 ABORT("Invalid pointer passed to realloc()");
856 if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
858 "GC_debug_realloc called on pointer %p w/o debugging info\n", p);
859 return(GC_realloc(p, lb));
862 switch (hhdr -> hb_obj_kind) {
863 # ifdef STUBBORN_ALLOC
865 result = GC_debug_malloc_stubborn(lb, OPT_RA s, i);
869 result = GC_debug_malloc(lb, OPT_RA s, i);
872 result = GC_debug_malloc_atomic(lb, OPT_RA s, i);
875 result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i);
877 # ifdef ATOMIC_UNCOLLECTABLE
879 result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i);
883 result = NULL; /* initialized to prevent warning. */
884 ABORT_RET("GC_debug_realloc: encountered bad kind");
887 if (result != NULL) {
889 # ifdef SHORT_DBG_HDRS
890 old_sz = GC_size(base) - sizeof(oh);
892 old_sz = ((oh *)base) -> oh_sz;
894 BCOPY(p, result, old_sz < lb ? old_sz : lb);
900 #ifndef SHORT_DBG_HDRS
902 /* List of smashed (clobbered) locations. We defer printing these, */
903 /* since we can't always print them nicely with the allocation lock */
904 /* held. We put them here instead of in GC_arrays, since it may be */
905 /* useful to be able to look at them with the debugger. */
907 # define MAX_SMASHED 20
909 STATIC ptr_t GC_smashed[MAX_SMASHED] = {0};
910 STATIC unsigned GC_n_smashed = 0;
912 STATIC void GC_add_smashed(ptr_t smashed)
914 GC_ASSERT(GC_is_marked(GC_base(smashed)));
915 /* FIXME: Prevent adding an object while printing smashed list. */
916 GC_smashed[GC_n_smashed] = smashed;
917 if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed;
918 /* In case of overflow, we keep the first MAX_SMASHED-1 */
919 /* entries plus the last one. */
920 GC_have_errors = TRUE;
923 /* Print all objects on the list. Clear the list. */
924 STATIC void GC_print_all_smashed_proc(void)
928 GC_ASSERT(I_DONT_HOLD_LOCK());
929 if (GC_n_smashed == 0) return;
930 GC_err_printf("GC_check_heap_block: found %u smashed heap objects:\n",
932 for (i = 0; i < GC_n_smashed; ++i) {
933 ptr_t base = (ptr_t)GC_base(GC_smashed[i]);
936 if (!base) ABORT("Invalid GC_smashed element");
938 GC_print_smashed_obj("", base + sizeof(oh), GC_smashed[i]);
944 /* Check all marked objects in the given block for validity */
945 /* Avoid GC_apply_to_each_object for performance reasons. */
946 STATIC void GC_check_heap_block(struct hblk *hbp, word dummy GC_ATTR_UNUSED)
948 struct hblkhdr * hhdr = HDR(hbp);
949 size_t sz = hhdr -> hb_sz;
954 if (sz > MAXOBJBYTES) {
957 plim = hbp->hb_body + HBLKSIZE - sz;
959 /* go through all words in block */
960 for (bit_no = 0; (word)p <= (word)plim;
961 bit_no += MARK_BIT_OFFSET(sz), p += sz) {
962 if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO((ptr_t)p)) {
963 ptr_t clobbered = GC_check_annotated_obj((oh *)p);
965 GC_add_smashed(clobbered);
970 /* This assumes that all accessible objects are marked, and that */
971 /* I hold the allocation lock. Normally called by collector. */
972 STATIC void GC_check_heap_proc(void)
974 GC_STATIC_ASSERT((sizeof(oh) & (GRANULE_BYTES - 1)) == 0);
975 /* FIXME: Should we check for twice that alignment? */
976 GC_apply_to_all_blocks(GC_check_heap_block, 0);
979 GC_INNER GC_bool GC_check_leaked(ptr_t base)
986 # if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
987 (*(word *)base & 1) != 0 &&
989 GC_has_other_debug_info(base) >= 0)
990 return TRUE; /* object has leaked */
992 /* Validate freed object's content. */
993 p = (word *)(base + sizeof(oh));
994 obj_sz = BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh));
995 for (i = 0; i < obj_sz; ++i)
996 if (p[i] != GC_FREED_MEM_MARKER) {
997 GC_set_mark_bit(base); /* do not reclaim it in this cycle */
998 GC_add_smashed((ptr_t)(&p[i])); /* alter-after-free detected */
999 break; /* don't report any other smashed locations in the object */
1002 return FALSE; /* GC_debug_free() has been called */
1005 #endif /* !SHORT_DBG_HDRS */
1007 #ifndef GC_NO_FINALIZATION
1010 GC_finalization_proc cl_fn;
1014 STATIC void * GC_make_closure(GC_finalization_proc fn, void * data)
1016 struct closure * result =
1017 # ifdef DBG_HDRS_ALL
1018 (struct closure *) GC_debug_malloc(sizeof (struct closure),
1021 (struct closure *) GC_malloc(sizeof (struct closure));
1024 result -> cl_fn = fn;
1025 result -> cl_data = data;
1027 return((void *)result);
1030 /* An auxiliary fns to make finalization work correctly with displaced */
1031 /* pointers introduced by the debugging allocators. */
1032 STATIC void GC_CALLBACK GC_debug_invoke_finalizer(void * obj, void * data)
1034 struct closure * cl = (struct closure *) data;
1035 (*(cl -> cl_fn))((void *)((char *)obj + sizeof(oh)), cl -> cl_data);
1038 /* Special finalizer_proc value to detect GC_register_finalizer() failure. */
1039 #define OFN_UNSET (GC_finalization_proc)(signed_word)-1
1041 /* Set ofn and ocd to reflect the values we got back. */
1042 static void store_old(void *obj, GC_finalization_proc my_old_fn,
1043 struct closure *my_old_cd, GC_finalization_proc *ofn,
1046 if (0 != my_old_fn) {
1047 if (my_old_fn == OFN_UNSET) {
1048 /* register_finalizer() failed; (*ofn) and (*ocd) are unchanged. */
1051 if (my_old_fn != GC_debug_invoke_finalizer) {
1052 GC_err_printf("Debuggable object at %p had a non-debug finalizer\n",
1054 /* This should probably be fatal. */
1056 if (ofn) *ofn = my_old_cd -> cl_fn;
1057 if (ocd) *ocd = my_old_cd -> cl_data;
1065 GC_API void GC_CALL GC_debug_register_finalizer(void * obj,
1066 GC_finalization_proc fn,
1067 void * cd, GC_finalization_proc *ofn,
1070 GC_finalization_proc my_old_fn = OFN_UNSET;
1072 ptr_t base = GC_base(obj);
1074 /* We won't collect it, hence finalizer wouldn't be run. */
1079 if ((ptr_t)obj - base != sizeof(oh)) {
1080 GC_err_printf("GC_debug_register_finalizer called with"
1081 " non-base-pointer %p\n", obj);
1084 GC_register_finalizer(base, 0, 0, &my_old_fn, &my_old_cd);
1086 cd = GC_make_closure(fn, cd);
1087 if (cd == 0) return; /* out of memory */
1088 GC_register_finalizer(base, GC_debug_invoke_finalizer,
1089 cd, &my_old_fn, &my_old_cd);
1091 store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1094 GC_API void GC_CALL GC_debug_register_finalizer_no_order
1095 (void * obj, GC_finalization_proc fn,
1096 void * cd, GC_finalization_proc *ofn,
1099 GC_finalization_proc my_old_fn = OFN_UNSET;
1101 ptr_t base = GC_base(obj);
1103 /* We won't collect it, hence finalizer wouldn't be run. */
1108 if ((ptr_t)obj - base != sizeof(oh)) {
1109 GC_err_printf("GC_debug_register_finalizer_no_order called with"
1110 " non-base-pointer %p\n", obj);
1113 GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd);
1115 cd = GC_make_closure(fn, cd);
1116 if (cd == 0) return; /* out of memory */
1117 GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer,
1118 cd, &my_old_fn, &my_old_cd);
1120 store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1123 GC_API void GC_CALL GC_debug_register_finalizer_unreachable
1124 (void * obj, GC_finalization_proc fn,
1125 void * cd, GC_finalization_proc *ofn,
1128 GC_finalization_proc my_old_fn = OFN_UNSET;
1130 ptr_t base = GC_base(obj);
1132 /* We won't collect it, hence finalizer wouldn't be run. */
1137 if ((ptr_t)obj - base != sizeof(oh)) {
1138 GC_err_printf("GC_debug_register_finalizer_unreachable called with"
1139 " non-base-pointer %p\n", obj);
1142 GC_register_finalizer_unreachable(base, 0, 0, &my_old_fn, &my_old_cd);
1144 cd = GC_make_closure(fn, cd);
1145 if (cd == 0) return; /* out of memory */
1146 GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer,
1147 cd, &my_old_fn, &my_old_cd);
1149 store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1152 GC_API void GC_CALL GC_debug_register_finalizer_ignore_self
1153 (void * obj, GC_finalization_proc fn,
1154 void * cd, GC_finalization_proc *ofn,
1157 GC_finalization_proc my_old_fn = OFN_UNSET;
1159 ptr_t base = GC_base(obj);
1161 /* We won't collect it, hence finalizer wouldn't be run. */
1166 if ((ptr_t)obj - base != sizeof(oh)) {
1167 GC_err_printf("GC_debug_register_finalizer_ignore_self called with"
1168 " non-base-pointer %p\n", obj);
1171 GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd);
1173 cd = GC_make_closure(fn, cd);
1174 if (cd == 0) return; /* out of memory */
1175 GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer,
1176 cd, &my_old_fn, &my_old_cd);
1178 store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1181 #endif /* !GC_NO_FINALIZATION */
1183 GC_API void * GC_CALL GC_debug_malloc_replacement(size_t lb)
1185 return GC_debug_malloc(lb, GC_DBG_RA "unknown", 0);
1188 GC_API void * GC_CALL GC_debug_realloc_replacement(void *p, size_t lb)
1190 return GC_debug_realloc(p, lb, GC_DBG_RA "unknown", 0);