Synopsis - Cross-Reference
File: /src/Synopsis/gc/dbg_mlc.c1/* 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 7 * 8 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 9 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 10 * 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. 16 */ 17 18#include <errno.h> 19#include <string.h> 20#include "private/dbg_mlc.h" 21 22void GC_default_print_heap_obj_proc(); 23GC_API void GC_register_finalizer_no_order 24 (void * obj, GC_finalization_proc fn, void * cd, 25 GC_finalization_proc *ofn, void * *ocd); 26 27 28#ifndef SHORT_DBG_HDRS 29/* Check whether object with base pointer p has debugging info */ 30/* p is assumed to point to a legitimate object in our part */ 31/* of the heap. */ 32/* This excludes the check as to whether the back pointer is */ 33/* odd, which is added by the GC_HAS_DEBUG_INFO macro. */ 34/* Note that if DBG_HDRS_ALL is set, uncollectable objects */ 35/* on free lists may not have debug information set. Thus it's */ 36/* not always safe to return TRUE, even if the client does */ 37/* its part. */ 38GC_bool GC_has_other_debug_info(ptr_t p) 39{ 40 register oh * ohdr = (oh *)p; 41 register ptr_t body = (ptr_t)(ohdr + 1); 42 register word sz = GC_size((ptr_t) ohdr); 43 44 if (HBLKPTR((ptr_t)ohdr) != HBLKPTR((ptr_t)body) 45 || sz < DEBUG_BYTES + EXTRA_BYTES) { 46 return(FALSE); 47 } 48 if (ohdr -> oh_sz == sz) { 49 /* Object may have had debug info, but has been deallocated */ 50 return(FALSE); 51 } 52 if (ohdr -> oh_sf == (START_FLAG ^ (word)body)) return(TRUE); 53 if (((word *)ohdr)[BYTES_TO_WORDS(sz)-1] == (END_FLAG ^ (word)body)) { 54 return(TRUE); 55 } 56 return(FALSE); 57} 58#endif 59 60#ifdef KEEP_BACK_PTRS 61 62# include <stdlib.h> 63 64# if defined(LINUX) || defined(SOLARIS) \ 65 || defined(HPUX) || defined(IRIX5) || defined(OSF1) 66# define RANDOM() random() 67# else 68# define RANDOM() (long)rand() 69# endif 70 71 /* Store back pointer to source in dest, if that appears to be possible. */ 72 /* This is not completely safe, since we may mistakenly conclude that */ 73 /* dest has a debugging wrapper. But the error probability is very */ 74 /* small, and this shouldn't be used in production code. */ 75 /* We assume that dest is the real base pointer. Source will usually */ 76 /* be a pointer to the interior of an object. */ 77 void GC_store_back_pointer(ptr_t source, ptr_t dest) 78 { 79 if (GC_HAS_DEBUG_INFO(dest)) { 80 ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source); 81 } 82 } 83 84 void GC_marked_for_finalization(ptr_t dest) { 85 GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest); 86 } 87 88 /* Store information about the object referencing dest in *base_p */ 89 /* and *offset_p. */ 90 /* source is root ==> *base_p = address, *offset_p = 0 */ 91 /* source is heap object ==> *base_p != 0, *offset_p = offset */ 92 /* Returns 1 on success, 0 if source couldn't be determined. */ 93 /* Dest can be any address within a heap object. */ 94 GC_ref_kind GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p) 95 { 96 oh * hdr = (oh *)GC_base(dest); 97 ptr_t bp; 98 ptr_t bp_base; 99 if (!GC_HAS_DEBUG_INFO((ptr_t) hdr)) return GC_NO_SPACE; 100 bp = REVEAL_POINTER(hdr -> oh_back_ptr); 101 if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD; 102 if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG; 103 if (NOT_MARKED == bp) return GC_UNREFERENCED; 104# if ALIGNMENT == 1 105 /* Heuristically try to fix off by 1 errors we introduced by */ 106 /* insisting on even addresses. */ 107 { 108 ptr_t alternate_ptr = bp + 1; 109 ptr_t target = *(ptr_t *)bp; 110 ptr_t alternate_target = *(ptr_t *)alternate_ptr; 111 112 if (alternate_target >= GC_least_plausible_heap_addr 113 && alternate_target <= GC_greatest_plausible_heap_addr 114 && (target < GC_least_plausible_heap_addr 115 || target > GC_greatest_plausible_heap_addr)) { 116 bp = alternate_ptr; 117 } 118 } 119# endif 120 bp_base = GC_base(bp); 121 if (0 == bp_base) { 122 *base_p = bp; 123 *offset_p = 0; 124 return GC_REFD_FROM_ROOT; 125 } else { 126 if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh); 127 *base_p = bp_base; 128 *offset_p = bp - bp_base; 129 return GC_REFD_FROM_HEAP; 130 } 131 } 132 133 /* Generate a random heap address. */ 134 /* The resulting address is in the heap, but */ 135 /* not necessarily inside a valid object. */ 136 void *GC_generate_random_heap_address(void) 137 { 138 int i; 139 long heap_offset = RANDOM(); 140 if (GC_heapsize > RAND_MAX) { 141 heap_offset *= RAND_MAX; 142 heap_offset += RANDOM(); 143 } 144 heap_offset %= GC_heapsize; 145 /* This doesn't yield a uniform distribution, especially if */ 146 /* e.g. RAND_MAX = 1.5* GC_heapsize. But for typical cases, */ 147 /* it's not too bad. */ 148 for (i = 0; i < GC_n_heap_sects; ++ i) { 149 size_t size = GC_heap_sects[i].hs_bytes; 150 if (heap_offset < size) { 151 return GC_heap_sects[i].hs_start + heap_offset; 152 } else { 153 heap_offset -= size; 154 } 155 } 156 ABORT("GC_generate_random_heap_address: size inconsistency"); 157 /*NOTREACHED*/ 158 return 0; 159 } 160 161 /* Generate a random address inside a valid marked heap object. */ 162 void *GC_generate_random_valid_address(void) 163 { 164 ptr_t result; 165 ptr_t base; 166 for (;;) { 167 result = GC_generate_random_heap_address(); 168 base = GC_base(result); 169 if (0 == base) continue; 170 if (!GC_is_marked(base)) continue; 171 return result; 172 } 173 } 174 175 /* Print back trace for p */ 176 void GC_print_backtrace(void *p) 177 { 178 void *current = p; 179 int i; 180 GC_ref_kind source; 181 size_t offset; 182 void *base; 183 184 GC_print_heap_obj(GC_base(current)); 185 GC_err_printf("\n"); 186 for (i = 0; ; ++i) { 187 source = GC_get_back_ptr_info(current, &base, &offset); 188 if (GC_UNREFERENCED == source) { 189 GC_err_printf("Reference could not be found\n"); 190 goto out; 191 } 192 if (GC_NO_SPACE == source) { 193 GC_err_printf("No debug info in object: Can't find reference\n"); 194 goto out; 195 } 196 GC_err_printf("Reachable via %d levels of pointers from ", 197 (unsigned long)i); 198 switch(source) { 199 case GC_REFD_FROM_ROOT: 200 GC_err_printf("root at %p\n\n", base); 201 goto out; 202 case GC_REFD_FROM_REG: 203 GC_err_printf("root in register\n\n"); 204 goto out; 205 case GC_FINALIZER_REFD: 206 GC_err_printf("list of finalizable objects\n\n"); 207 goto out; 208 case GC_REFD_FROM_HEAP: 209 GC_err_printf("offset %ld in object:\n", (unsigned long)offset); 210 /* Take GC_base(base) to get real base, i.e. header. */ 211 GC_print_heap_obj(GC_base(base)); 212 GC_err_printf("\n"); 213 break; 214 } 215 current = base; 216 } 217 out:; 218 } 219 220 /* Force a garbage collection and generate a backtrace from a */ 221 /* random heap address. */ 222 void GC_generate_random_backtrace_no_gc(void) 223 { 224 void * current; 225 current = GC_generate_random_valid_address(); 226 GC_printf("\n****Chose address %p in object\n", current); 227 GC_print_backtrace(current); 228 } 229 230 void GC_generate_random_backtrace(void) 231 { 232 GC_gcollect(); 233 GC_generate_random_backtrace_no_gc(); 234 } 235 236#endif /* KEEP_BACK_PTRS */ 237 238# define CROSSES_HBLK(p, sz) \ 239 (((word)(p + sizeof(oh) + sz - 1) ^ (word)p) >= HBLKSIZE) 240/* Store debugging info into p. Return displaced pointer. */ 241/* Assumes we don't hold allocation lock. */ 242ptr_t GC_store_debug_info(ptr_t p, word sz, const char *string, word integer) 243{ 244 register word * result = (word *)((oh *)p + 1); 245 DCL_LOCK_STATE; 246 247 /* There is some argument that we should dissble signals here. */ 248 /* But that's expensive. And this way things should only appear */ 249 /* inconsistent while we're in the handler. */ 250 LOCK(); 251 GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); 252 GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz))); 253# ifdef KEEP_BACK_PTRS 254 ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED); 255# endif 256# ifdef MAKE_BACK_GRAPH 257 ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0); 258# endif 259 ((oh *)p) -> oh_string = string; 260 ((oh *)p) -> oh_int = integer; 261# ifndef SHORT_DBG_HDRS 262 ((oh *)p) -> oh_sz = sz; 263 ((oh *)p) -> oh_sf = START_FLAG ^ (word)result; 264 ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] = 265 result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result; 266# endif 267 UNLOCK(); 268 return((ptr_t)result); 269} 270 271#ifdef DBG_HDRS_ALL 272/* Store debugging info into p. Return displaced pointer. */ 273/* This version assumes we do hold the allocation lock. */ 274ptr_t GC_store_debug_info_inner(ptr_t p, word sz, char *string, word integer) 275{ 276 register word * result = (word *)((oh *)p + 1); 277 278 /* There is some argument that we should disable signals here. */ 279 /* But that's expensive. And this way things should only appear */ 280 /* inconsistent while we're in the handler. */ 281 GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); 282 GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz))); 283# ifdef KEEP_BACK_PTRS 284 ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED); 285# endif 286# ifdef MAKE_BACK_GRAPH 287 ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0); 288# endif 289 ((oh *)p) -> oh_string = string; 290 ((oh *)p) -> oh_int = integer; 291# ifndef SHORT_DBG_HDRS 292 ((oh *)p) -> oh_sz = sz; 293 ((oh *)p) -> oh_sf = START_FLAG ^ (word)result; 294 ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] = 295 result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result; 296# endif 297 return((ptr_t)result); 298} 299#endif 300 301#ifndef SHORT_DBG_HDRS 302/* Check the object with debugging info at ohdr */ 303/* return NIL if it's OK. Else return clobbered */ 304/* address. */ 305ptr_t GC_check_annotated_obj(oh *ohdr) 306{ 307 register ptr_t body = (ptr_t)(ohdr + 1); 308 register word gc_sz = GC_size((ptr_t)ohdr); 309 if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) { 310 return((ptr_t)(&(ohdr -> oh_sz))); 311 } 312 if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) { 313 return((ptr_t)(&(ohdr -> oh_sf))); 314 } 315 if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) { 316 return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1)); 317 } 318 if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)] 319 != (END_FLAG ^ (word)body)) { 320 return((ptr_t)((word *)body + SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz))); 321 } 322 return(0); 323} 324#endif /* !SHORT_DBG_HDRS */ 325 326static GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0}; 327 328void GC_register_describe_type_fn(int kind, GC_describe_type_fn fn) 329{ 330 GC_describe_type_fns[kind] = fn; 331} 332 333/* Print a type description for the object whose client-visible address */ 334/* is p. */ 335void GC_print_type(ptr_t p) 336{ 337 hdr * hhdr = GC_find_header(p); 338 char buffer[GC_TYPE_DESCR_LEN + 1]; 339 int kind = hhdr -> hb_obj_kind; 340 341 if (0 != GC_describe_type_fns[kind] && GC_is_marked(GC_base(p))) { 342 /* This should preclude free list objects except with */ 343 /* thread-local allocation. */ 344 buffer[GC_TYPE_DESCR_LEN] = 0; 345 (GC_describe_type_fns[kind])(p, buffer); 346 GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0); 347 GC_err_puts(buffer); 348 } else { 349 switch(kind) { 350 case PTRFREE: 351 GC_err_puts("PTRFREE"); 352 break; 353 case NORMAL: 354 GC_err_puts("NORMAL"); 355 break; 356 case UNCOLLECTABLE: 357 GC_err_puts("UNCOLLECTABLE"); 358 break; 359# ifdef ATOMIC_UNCOLLECTABLE 360 case AUNCOLLECTABLE: 361 GC_err_puts("ATOMIC UNCOLLECTABLE"); 362 break; 363# endif 364 case STUBBORN: 365 GC_err_puts("STUBBORN"); 366 break; 367 default: 368 GC_err_printf("kind %d, descr 0x%lx", kind, 369 (unsigned long)(hhdr -> hb_descr)); 370 } 371 } 372} 373 374 375 376void GC_print_obj(ptr_t p) 377{ 378 register oh * ohdr = (oh *)GC_base(p); 379 380 GC_ASSERT(I_DONT_HOLD_LOCK()); 381 GC_err_printf("%p (", ((ptr_t)ohdr + sizeof(oh))); 382 GC_err_puts(ohdr -> oh_string); 383# ifdef SHORT_DBG_HDRS 384 GC_err_printf(":%ld, ", (unsigned long)(ohdr -> oh_int)); 385# else 386 GC_err_printf(":%ld, sz=%ld, ", (unsigned long)(ohdr -> oh_int), 387 (unsigned long)(ohdr -> oh_sz)); 388# endif 389 GC_print_type((ptr_t)(ohdr + 1)); 390 GC_err_puts(")\n"); 391 PRINT_CALL_CHAIN(ohdr); 392} 393 394void GC_debug_print_heap_obj_proc(ptr_t p) 395{ 396 GC_ASSERT(I_DONT_HOLD_LOCK()); 397 if (GC_HAS_DEBUG_INFO(p)) { 398 GC_print_obj(p); 399 } else { 400 GC_default_print_heap_obj_proc(p); 401 } 402} 403 404#ifndef SHORT_DBG_HDRS 405void GC_print_smashed_obj(ptr_t p, ptr_t clobbered_addr) 406{ 407 register oh * ohdr = (oh *)GC_base(p); 408 409 GC_ASSERT(I_DONT_HOLD_LOCK()); 410 GC_err_printf("%p in object at %p(", clobbered_addr, p); 411 if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz)) 412 || ohdr -> oh_string == 0) { 413 GC_err_printf("<smashed>, appr. sz = %ld)\n", 414 (GC_size((ptr_t)ohdr) - DEBUG_BYTES)); 415 } else { 416 if (ohdr -> oh_string[0] == '\0') { 417 GC_err_puts("EMPTY(smashed?)"); 418 } else { 419 GC_err_puts(ohdr -> oh_string); 420 } 421 GC_err_printf(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int), 422 (unsigned long)(ohdr -> oh_sz)); 423 PRINT_CALL_CHAIN(ohdr); 424 } 425} 426#endif 427 428void GC_check_heap_proc (void); 429 430void GC_print_all_smashed_proc (void); 431 432void GC_do_nothing(void) {} 433 434void GC_start_debugging(void) 435{ 436# ifndef SHORT_DBG_HDRS 437 GC_check_heap = GC_check_heap_proc; 438 GC_print_all_smashed = GC_print_all_smashed_proc; 439# else 440 GC_check_heap = GC_do_nothing; 441 GC_print_all_smashed = GC_do_nothing; 442# endif 443 GC_print_heap_obj = GC_debug_print_heap_obj_proc; 444 GC_debugging_started = TRUE; 445 GC_register_displacement((word)sizeof(oh)); 446} 447 448size_t GC_debug_header_size = sizeof(oh); 449 450void GC_debug_register_displacement(size_t offset) 451{ 452 GC_register_displacement(offset); 453 GC_register_displacement((word)sizeof(oh) + offset); 454} 455 456void * GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS) 457{ 458 void * result = GC_malloc(lb + DEBUG_BYTES); 459 460 if (result == 0) { 461 GC_err_printf("GC_debug_malloc(%lu) returning NIL (", 462 (unsigned long) lb); 463 GC_err_puts(s); 464 GC_err_printf(":%ld)\n", (unsigned long)i); 465 return(0); 466 } 467 if (!GC_debugging_started) { 468 GC_start_debugging(); 469 } 470 ADD_CALL_CHAIN(result, ra); 471 return (GC_store_debug_info(result, (word)lb, s, (word)i)); 472} 473 474void * GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) 475{ 476 void * result = GC_malloc_ignore_off_page(lb + DEBUG_BYTES); 477 478 if (result == 0) { 479 GC_err_printf("GC_debug_malloc_ignore_off_page(%lu) returning NIL (", 480 (unsigned long) lb); 481 GC_err_puts(s); 482 GC_err_printf(":%lu)\n", (unsigned long)i); 483 return(0); 484 } 485 if (!GC_debugging_started) { 486 GC_start_debugging(); 487 } 488 ADD_CALL_CHAIN(result, ra); 489 return (GC_store_debug_info(result, (word)lb, s, (word)i)); 490} 491 492void * GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) 493{ 494 void * result = GC_malloc_atomic_ignore_off_page(lb + DEBUG_BYTES); 495 496 if (result == 0) { 497 GC_err_printf("GC_debug_malloc_atomic_ignore_off_page(%lu)" 498 " returning NIL (", (unsigned long) lb); 499 GC_err_puts(s); 500 GC_err_printf(":%lu)\n", (unsigned long)i); 501 return(0); 502 } 503 if (!GC_debugging_started) { 504 GC_start_debugging(); 505 } 506 ADD_CALL_CHAIN(result, ra); 507 return (GC_store_debug_info(result, (word)lb, s, (word)i)); 508} 509 510# ifdef DBG_HDRS_ALL 511/* 512 * An allocation function for internal use. 513 * Normally internally allocated objects do not have debug information. 514 * But in this case, we need to make sure that all objects have debug 515 * headers. 516 * We assume debugging was started in collector initialization, 517 * and we already hold the GC lock. 518 */ 519 void * GC_debug_generic_malloc_inner(size_t lb, int k) 520 { 521 void * result = GC_generic_malloc_inner(lb + DEBUG_BYTES, k); 522 523 if (result == 0) { 524 GC_err_printf("GC internal allocation (%lu bytes) returning NIL\n", 525 (unsigned long) lb); 526 return(0); 527 } 528 ADD_CALL_CHAIN(result, GC_RETURN_ADDR); 529 return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", (word)0)); 530 } 531 532 void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, int k) 533 { 534 void * result = GC_generic_malloc_inner_ignore_off_page( 535 lb + DEBUG_BYTES, k); 536 537 if (result == 0) { 538 GC_err_printf("GC internal allocation (%lu bytes) returning NIL\n", 539 (unsigned long) lb); 540 return(0); 541 } 542 ADD_CALL_CHAIN(result, GC_RETURN_ADDR); 543 return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", (word)0)); 544 } 545# endif 546 547#ifdef STUBBORN_ALLOC 548void * GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) 549{ 550 void * result = GC_malloc_stubborn(lb + DEBUG_BYTES); 551 552 if (result == 0) { 553 GC_err_printf("GC_debug_malloc(%lu) returning NIL (", 554 (unsigned long) lb); 555 GC_err_puts(s); 556 GC_err_printf(":%lu)\n", (unsigned long)i); 557 return(0); 558 } 559 if (!GC_debugging_started) { 560 GC_start_debugging(); 561 } 562 ADD_CALL_CHAIN(result, ra); 563 return (GC_store_debug_info(result, (word)lb, s, (word)i)); 564} 565 566void GC_debug_change_stubborn(void *p) 567{ 568 void * q = GC_base(p); 569 hdr * hhdr; 570 571 if (q == 0) { 572 GC_err_printf("Bad argument: %p to GC_debug_change_stubborn\n", p); 573 ABORT("GC_debug_change_stubborn: bad arg"); 574 } 575 hhdr = HDR(q); 576 if (hhdr -> hb_obj_kind != STUBBORN) { 577 GC_err_printf("GC_debug_change_stubborn arg not stubborn: %p\n", p); 578 ABORT("GC_debug_change_stubborn: arg not stubborn"); 579 } 580 GC_change_stubborn(q); 581} 582 583void GC_debug_end_stubborn_change(void *p) 584{ 585 register void * q = GC_base(p); 586 register hdr * hhdr; 587 588 if (q == 0) { 589 GC_err_printf("Bad argument: %p to GC_debug_end_stubborn_change\n", p); 590 ABORT("GC_debug_end_stubborn_change: bad arg"); 591 } 592 hhdr = HDR(q); 593 if (hhdr -> hb_obj_kind != STUBBORN) { 594 GC_err_printf("debug_end_stubborn_change arg not stubborn: %p\n", p); 595 ABORT("GC_debug_end_stubborn_change: arg not stubborn"); 596 } 597 GC_end_stubborn_change(q); 598} 599 600#else /* !STUBBORN_ALLOC */ 601 602void * GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) 603{ 604 return GC_debug_malloc(lb, OPT_RA s, i); 605} 606 607void GC_debug_change_stubborn(void *p) 608{ 609} 610 611void GC_debug_end_stubborn_change(void *p) 612{ 613} 614 615#endif /* !STUBBORN_ALLOC */ 616 617void * GC_debug_malloc_atomic(size_t lb, GC_EXTRA_PARAMS) 618{ 619 void * result = GC_malloc_atomic(lb + DEBUG_BYTES); 620 621 if (result == 0) { 622 GC_err_printf("GC_debug_malloc_atomic(%lu) returning NIL (", 623 (unsigned long) lb); 624 GC_err_puts(s); 625 GC_err_printf(":%lu)\n", (unsigned long)i); 626 return(0); 627 } 628 if (!GC_debugging_started) { 629 GC_start_debugging(); 630 } 631 ADD_CALL_CHAIN(result, ra); 632 return (GC_store_debug_info(result, (word)lb, s, (word)i)); 633} 634 635char *GC_debug_strdup(const char *str, GC_EXTRA_PARAMS) 636{ 637 char *copy; 638 if (str == NULL) return NULL; 639 copy = GC_debug_malloc_atomic(strlen(str) + 1, OPT_RA s, i); 640 if (copy == NULL) { 641 errno = ENOMEM; 642 return NULL; 643 } 644 strcpy(copy, str); 645 return copy; 646} 647 648void * GC_debug_malloc_uncollectable(size_t lb, GC_EXTRA_PARAMS) 649{ 650 void * result = GC_malloc_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES); 651 652 if (result == 0) { 653 GC_err_printf("GC_debug_malloc_uncollectable(%lu) returning NIL (", 654 (unsigned long) lb); 655 GC_err_puts(s); 656 GC_err_printf(":%lu)\n", (unsigned long)i); 657 return(0); 658 } 659 if (!GC_debugging_started) { 660 GC_start_debugging(); 661 } 662 ADD_CALL_CHAIN(result, ra); 663 return (GC_store_debug_info(result, (word)lb, s, (word)i)); 664} 665 666#ifdef ATOMIC_UNCOLLECTABLE 667void * GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS) 668{ 669 void * result = 670 GC_malloc_atomic_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES); 671 672 if (result == 0) { 673 GC_err_printf( 674 "GC_debug_malloc_atomic_uncollectable(%lu) returning NIL (", 675 (unsigned long) lb); 676 GC_err_puts(s); 677 GC_err_printf(":%lu)\n", (unsigned long)i); 678 return(0); 679 } 680 if (!GC_debugging_started) { 681 GC_start_debugging(); 682 } 683 ADD_CALL_CHAIN(result, ra); 684 return (GC_store_debug_info(result, (word)lb, s, (word)i)); 685} 686#endif /* ATOMIC_UNCOLLECTABLE */ 687 688void GC_debug_free(void * p) 689{ 690 ptr_t base; 691 ptr_t clobbered; 692 693 if (0 == p) return; 694 base = GC_base(p); 695 if (base == 0) { 696 GC_err_printf("Attempt to free invalid pointer %p\n", p); 697 ABORT("free(invalid pointer)"); 698 } 699 if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { 700 GC_err_printf( 701 "GC_debug_free called on pointer %p wo debugging info\n", p); 702 } else { 703# ifndef SHORT_DBG_HDRS 704 clobbered = GC_check_annotated_obj((oh *)base); 705 if (clobbered != 0) { 706 if (((oh *)base) -> oh_sz == GC_size(base)) { 707 GC_err_printf( 708 "GC_debug_free: found previously deallocated (?) object at "); 709 } else { 710 GC_err_printf("GC_debug_free: found smashed location at "); 711 } 712 GC_print_smashed_obj(p, clobbered); 713 } 714 /* Invalidate size */ 715 ((oh *)base) -> oh_sz = GC_size(base); 716# endif /* SHORT_DBG_HDRS */ 717 } 718 if (GC_find_leak) { 719 GC_free(base); 720 } else { 721 hdr * hhdr = HDR(p); 722 GC_bool uncollectable = FALSE; 723 724 if (hhdr -> hb_obj_kind == UNCOLLECTABLE) { 725 uncollectable = TRUE; 726 } 727# ifdef ATOMIC_UNCOLLECTABLE 728 if (hhdr -> hb_obj_kind == AUNCOLLECTABLE) { 729 uncollectable = TRUE; 730 } 731# endif 732 if (uncollectable) { 733 GC_free(base); 734 } else { 735 size_t i; 736 size_t obj_sz = BYTES_TO_WORDS(hhdr -> hb_sz - sizeof(oh)); 737 738 for (i = 0; i < obj_sz; ++i) ((word *)p)[i] = 0xdeadbeef; 739 GC_ASSERT((word *)p + i == (word *)(base + hhdr -> hb_sz)); 740 } 741 } /* !GC_find_leak */ 742} 743 744#ifdef THREADS 745 746extern void GC_free_inner(void * p); 747 748/* Used internally; we assume it's called correctly. */ 749void GC_debug_free_inner(void * p) 750{ 751 GC_free_inner(GC_base(p)); 752} 753#endif 754 755void * GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS) 756{ 757 void * base = GC_base(p); 758 ptr_t clobbered; 759 void * result; 760 size_t copy_sz = lb; 761 size_t old_sz; 762 hdr * hhdr; 763 764 if (p == 0) return(GC_debug_malloc(lb, OPT_RA s, i)); 765 if (base == 0) { 766 GC_err_printf("Attempt to reallocate invalid pointer %p\n", p); 767 ABORT("realloc(invalid pointer)"); 768 } 769 if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { 770 GC_err_printf( 771 "GC_debug_realloc called on pointer %p wo debugging info\n", p); 772 return(GC_realloc(p, lb)); 773 } 774 hhdr = HDR(base); 775 switch (hhdr -> hb_obj_kind) { 776# ifdef STUBBORN_ALLOC 777 case STUBBORN: 778 result = GC_debug_malloc_stubborn(lb, OPT_RA s, i); 779 break; 780# endif 781 case NORMAL: 782 result = GC_debug_malloc(lb, OPT_RA s, i); 783 break; 784 case PTRFREE: 785 result = GC_debug_malloc_atomic(lb, OPT_RA s, i); 786 break; 787 case UNCOLLECTABLE: 788 result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i); 789 break; 790# ifdef ATOMIC_UNCOLLECTABLE 791 case AUNCOLLECTABLE: 792 result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); 793 break; 794# endif 795 default: 796 GC_err_printf("GC_debug_realloc: encountered bad kind\n"); 797 ABORT("bad kind"); 798 } 799# ifdef SHORT_DBG_HDRS 800 old_sz = GC_size(base) - sizeof(oh); 801# else 802 clobbered = GC_check_annotated_obj((oh *)base); 803 if (clobbered != 0) { 804 GC_err_printf("GC_debug_realloc: found smashed location at "); 805 GC_print_smashed_obj(p, clobbered); 806 } 807 old_sz = ((oh *)base) -> oh_sz; 808# endif 809 if (old_sz < copy_sz) copy_sz = old_sz; 810 if (result == 0) return(0); 811 BCOPY(p, result, copy_sz); 812 GC_debug_free(p); 813 return(result); 814} 815 816#ifndef SHORT_DBG_HDRS 817 818/* List of smashed objects. We defer printing these, since we can't */ 819/* always print them nicely with the allocation lock held. */ 820/* We put them here instead of in GC_arrays, since it may be useful to */ 821/* be able to look at them with the debugger. */ 822#define MAX_SMASHED 20 823ptr_t GC_smashed[MAX_SMASHED]; 824unsigned GC_n_smashed = 0; 825 826void GC_add_smashed(ptr_t smashed) 827{ 828 GC_ASSERT(GC_is_marked(GC_base(smashed))); 829 GC_smashed[GC_n_smashed] = smashed; 830 if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed; 831 /* In case of overflow, we keep the first MAX_SMASHED-1 */ 832 /* entries plus the last one. */ 833 GC_have_errors = TRUE; 834} 835 836/* Print all objects on the list. Clear the list. */ 837void GC_print_all_smashed_proc(void) 838{ 839 unsigned i; 840 841 GC_ASSERT(I_DONT_HOLD_LOCK()); 842 if (GC_n_smashed == 0) return; 843 GC_err_printf("GC_check_heap_block: found smashed heap objects:\n"); 844 for (i = 0; i < GC_n_smashed; ++i) { 845 GC_print_smashed_obj(GC_base(GC_smashed[i]), GC_smashed[i]); 846 GC_smashed[i] = 0; 847 } 848 GC_n_smashed = 0; 849} 850 851/* Check all marked objects in the given block for validity */ 852/* Avoid GC_apply_to_each_object for performance reasons. */ 853/*ARGSUSED*/ 854void GC_check_heap_block(struct hblk *hbp, word dummy) 855{ 856 struct hblkhdr * hhdr = HDR(hbp); 857 size_t sz = hhdr -> hb_sz; 858 size_t bit_no; 859 char *p, *plim; 860 861 p = hbp->hb_body; 862 bit_no = 0; 863 if (sz > MAXOBJBYTES) { 864 plim = p; 865 } else { 866 plim = hbp->hb_body + HBLKSIZE - sz; 867 } 868 /* go through all words in block */ 869 while( p <= plim ) { 870 if( mark_bit_from_hdr(hhdr, bit_no) 871 && GC_HAS_DEBUG_INFO((ptr_t)p)) { 872 ptr_t clobbered = GC_check_annotated_obj((oh *)p); 873 874 if (clobbered != 0) GC_add_smashed(clobbered); 875 } 876 bit_no += MARK_BIT_OFFSET(sz); 877 p += sz; 878 } 879} 880 881 882/* This assumes that all accessible objects are marked, and that */ 883/* I hold the allocation lock. Normally called by collector. */ 884void GC_check_heap_proc(void) 885{ 886# ifndef SMALL_CONFIG 887 /* Ignore gcc no effect warning on the following. */ 888 GC_STATIC_ASSERT((sizeof(oh) & (GRANULE_BYTES - 1)) == 0); 889 /* FIXME: Should we check for twice that alignment? */ 890# endif 891 GC_apply_to_all_blocks(GC_check_heap_block, (word)0); 892} 893 894#endif /* !SHORT_DBG_HDRS */ 895 896struct closure { 897 GC_finalization_proc cl_fn; 898 void * cl_data; 899}; 900 901void * GC_make_closure(GC_finalization_proc fn, void * data) 902{ 903 struct closure * result = 904# ifdef DBG_HDRS_ALL 905 (struct closure *) GC_debug_malloc(sizeof (struct closure), 906 GC_EXTRAS); 907# else 908 (struct closure *) GC_malloc(sizeof (struct closure)); 909# endif 910 911 result -> cl_fn = fn; 912 result -> cl_data = data; 913 return((void *)result); 914} 915 916void GC_debug_invoke_finalizer(void * obj, void * data) 917{ 918 register struct closure * cl = (struct closure *) data; 919 920 (*(cl -> cl_fn))((void *)((char *)obj + sizeof(oh)), cl -> cl_data); 921} 922 923/* Set ofn and ocd to reflect the values we got back. */ 924static void store_old (void *obj, GC_finalization_proc my_old_fn, 925 struct closure *my_old_cd, GC_finalization_proc *ofn, 926 void **ocd) 927{ 928 if (0 != my_old_fn) { 929 if (my_old_fn != GC_debug_invoke_finalizer) { 930 GC_err_printf("Debuggable object at %p had non-debug finalizer.\n", 931 obj); 932 /* This should probably be fatal. */ 933 } else { 934 if (ofn) *ofn = my_old_cd -> cl_fn; 935 if (ocd) *ocd = my_old_cd -> cl_data; 936 } 937 } else { 938 if (ofn) *ofn = 0; 939 if (ocd) *ocd = 0; 940 } 941} 942 943void GC_debug_register_finalizer(void * obj, GC_finalization_proc fn, 944 void * cd, GC_finalization_proc *ofn, 945 void * *ocd) 946{ 947 GC_finalization_proc my_old_fn; 948 void * my_old_cd; 949 ptr_t base = GC_base(obj); 950 if (0 == base) return; 951 if ((ptr_t)obj - base != sizeof(oh)) { 952 GC_err_printf( 953 "GC_debug_register_finalizer called with non-base-pointer %p\n", 954 obj); 955 } 956 if (0 == fn) { 957 GC_register_finalizer(base, 0, 0, &my_old_fn, &my_old_cd); 958 } else { 959 GC_register_finalizer(base, GC_debug_invoke_finalizer, 960 GC_make_closure(fn,cd), &my_old_fn, &my_old_cd); 961 } 962 store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); 963} 964 965void GC_debug_register_finalizer_no_order 966 (void * obj, GC_finalization_proc fn, 967 void * cd, GC_finalization_proc *ofn, 968 void * *ocd) 969{ 970 GC_finalization_proc my_old_fn; 971 void * my_old_cd; 972 ptr_t base = GC_base(obj); 973 if (0 == base) return; 974 if ((ptr_t)obj - base != sizeof(oh)) { 975 GC_err_printf( 976 "GC_debug_register_finalizer_no_order called with " 977 "non-base-pointer %p\n", 978 obj); 979 } 980 if (0 == fn) { 981 GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd); 982 } else { 983 GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer, 984 GC_make_closure(fn,cd), &my_old_fn, 985 &my_old_cd); 986 } 987 store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); 988} 989 990void GC_debug_register_finalizer_unreachable 991 (void * obj, GC_finalization_proc fn, 992 void * cd, GC_finalization_proc *ofn, 993 void * *ocd) 994{ 995 GC_finalization_proc my_old_fn; 996 void * my_old_cd; 997 ptr_t base = GC_base(obj); 998 if (0 == base) return; 999 if ((ptr_t)obj - base != sizeof(oh)) { 1000 GC_err_printf( 1001 "GC_debug_register_finalizer_unreachable called with " 1002 "non-base-pointer %p\n", 1003 obj); 1004 } 1005 if (0 == fn) { 1006 GC_register_finalizer_unreachable(base, 0, 0, &my_old_fn, &my_old_cd); 1007 } else { 1008 GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer, 1009 GC_make_closure(fn,cd), &my_old_fn, 1010 &my_old_cd); 1011 } 1012 store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); 1013} 1014 1015void GC_debug_register_finalizer_ignore_self 1016 (void * obj, GC_finalization_proc fn, 1017 void * cd, GC_finalization_proc *ofn, 1018 void * *ocd) 1019{ 1020 GC_finalization_proc my_old_fn; 1021 void * my_old_cd; 1022 ptr_t base = GC_base(obj); 1023 if (0 == base) return; 1024 if ((ptr_t)obj - base != sizeof(oh)) { 1025 GC_err_printf( 1026 "GC_debug_register_finalizer_ignore_self called with " 1027 "non-base-pointer %p\n", obj); 1028 } 1029 if (0 == fn) { 1030 GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd); 1031 } else { 1032 GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer, 1033 GC_make_closure(fn,cd), &my_old_fn, 1034 &my_old_cd); 1035 } 1036 store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); 1037} 1038 1039#ifdef GC_ADD_CALLER 1040# define RA GC_RETURN_ADDR, 1041#else 1042# define RA 1043#endif 1044 1045void * GC_debug_malloc_replacement(size_t lb) 1046{ 1047 return GC_debug_malloc(lb, RA "unknown", 0); 1048} 1049 1050void * GC_debug_realloc_replacement(void *p, size_t lb) 1051{ 1052 return GC_debug_realloc(p, lb, RA "unknown", 0); 1053}