dbg_mlc.c raw

   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   * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
   6   * Copyright (c) 2007 Free Software Foundation, Inc.
   7   * Copyright (c) 2008-2022 Ivan Maidanski
   8   *
   9   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  10   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
  11   *
  12   * Permission is hereby granted to use or copy this program
  13   * for any purpose, provided the above notices are retained on all copies.
  14   * Permission to modify the code and to distribute modified code is granted,
  15   * provided the above notices are retained, and a notice that the code was
  16   * modified is included with the above copyright notice.
  17   */
  18  
  19  #include "private/dbg_mlc.h"
  20  
  21  #ifndef MSWINCE
  22  #  include <errno.h>
  23  #endif
  24  #include <string.h>
  25  
  26  #ifdef KEEP_BACK_PTRS
  27  
  28  /*
  29   * Use a custom trivial `random()` implementation as the standard one might
  30   * lead to crashes (if used from a multi-threaded code) or to a compiler
  31   * warning about the deterministic result.
  32   */
  33  static int
  34  GC_rand(void)
  35  {
  36    static GC_RAND_STATE_T seed;
  37  
  38    return GC_RAND_NEXT(&seed);
  39  }
  40  
  41  #  define RANDOM() (long)GC_rand()
  42  
  43  GC_INNER void
  44  GC_store_back_pointer(ptr_t source, ptr_t dest)
  45  {
  46    if (GC_HAS_DEBUG_INFO(dest)) {
  47  #  ifdef PARALLEL_MARK
  48      GC_cptr_store((volatile ptr_t *)&((oh *)dest)->oh_back_ptr,
  49                    (ptr_t)HIDE_BACK_PTR(source));
  50  #  else
  51      ((oh *)dest)->oh_back_ptr = HIDE_BACK_PTR(source);
  52  #  endif
  53    }
  54  }
  55  
  56  GC_INNER void
  57  GC_marked_for_finalization(ptr_t dest)
  58  {
  59    GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest);
  60  }
  61  
  62  GC_API GC_ref_kind GC_CALL
  63  GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p)
  64  {
  65    oh *ohdr = (oh *)GC_base(dest);
  66    ptr_t bp, bp_base;
  67  
  68  #  ifdef LINT2
  69    /*
  70     * Explicitly instruct the code analysis tool that `GC_get_back_ptr_info`
  71     * is not expected to be called with an incorrect `dest` value.
  72     */
  73    if (!ohdr)
  74      ABORT("Invalid GC_get_back_ptr_info argument");
  75  #  endif
  76    if (!GC_HAS_DEBUG_INFO((ptr_t)ohdr))
  77      return GC_NO_SPACE;
  78    bp = (ptr_t)GC_REVEAL_POINTER(ohdr->oh_back_ptr);
  79    if (MARKED_FOR_FINALIZATION == bp)
  80      return GC_FINALIZER_REFD;
  81    if (MARKED_FROM_REGISTER == bp)
  82      return GC_REFD_FROM_REG;
  83    if (NOT_MARKED == bp)
  84      return GC_UNREFERENCED;
  85  #  if ALIGNMENT == 1
  86    /*
  87     * Heuristically try to fix off-by-one errors we introduced by
  88     * insisting on even addresses.
  89     */
  90    {
  91      ptr_t alternate_ptr = bp + 1;
  92      ptr_t target = *(ptr_t *)bp;
  93      ptr_t alternate_target = *(ptr_t *)alternate_ptr;
  94  
  95      if (GC_least_real_heap_addr < ADDR(alternate_target)
  96          && ADDR(alternate_target) < GC_greatest_real_heap_addr
  97          && (GC_least_real_heap_addr >= ADDR(target)
  98              || ADDR(target) >= GC_greatest_real_heap_addr)) {
  99        bp = alternate_ptr;
 100      }
 101    }
 102  #  endif
 103    bp_base = (ptr_t)GC_base(bp);
 104    if (NULL == bp_base) {
 105      *base_p = bp;
 106      *offset_p = 0;
 107      return GC_REFD_FROM_ROOT;
 108    } else {
 109      if (GC_HAS_DEBUG_INFO(bp_base))
 110        bp_base += sizeof(oh);
 111      *base_p = bp_base;
 112      *offset_p = (size_t)(bp - bp_base);
 113      return GC_REFD_FROM_HEAP;
 114    }
 115  }
 116  
 117  GC_API void *GC_CALL
 118  GC_generate_random_heap_address(void)
 119  {
 120    size_t i;
 121    word heap_offset = (word)RANDOM();
 122  
 123    if (GC_heapsize > (word)GC_RAND_MAX) {
 124      heap_offset *= GC_RAND_MAX;
 125      heap_offset += (word)RANDOM();
 126    }
 127  
 128    /*
 129     * This does not yield a uniform distribution, especially if e.g.
 130     * `RAND_MAX` is `1.5 * GC_heapsize`.  But for typical cases,  it is
 131     * not too bad.
 132     */
 133    heap_offset %= GC_heapsize;
 134  
 135    for (i = 0;; ++i) {
 136      size_t size;
 137  
 138      if (i >= GC_n_heap_sects)
 139        ABORT("GC_generate_random_heap_address: size inconsistency");
 140  
 141      size = GC_heap_sects[i].hs_bytes;
 142      if (heap_offset < size)
 143        break;
 144      heap_offset -= size;
 145    }
 146    return GC_heap_sects[i].hs_start + heap_offset;
 147  }
 148  
 149  GC_API void *GC_CALL
 150  GC_generate_random_valid_address(void)
 151  {
 152    ptr_t result;
 153    ptr_t base;
 154  
 155    do {
 156      result = (ptr_t)GC_generate_random_heap_address();
 157      base = (ptr_t)GC_base(result);
 158    } while (NULL == base || !GC_is_marked(base));
 159    return result;
 160  }
 161  
 162  GC_API void GC_CALL
 163  GC_print_backtrace(void *p)
 164  {
 165    void *current = p;
 166    int i;
 167  
 168    GC_ASSERT(I_DONT_HOLD_LOCK());
 169    GC_print_heap_obj((ptr_t)GC_base(current));
 170  
 171    for (i = 0;; ++i) {
 172      void *base;
 173      size_t offset;
 174      GC_ref_kind source = GC_get_back_ptr_info(current, &base, &offset);
 175  
 176      if (GC_UNREFERENCED == source) {
 177        GC_err_printf("Reference could not be found\n");
 178        break;
 179      }
 180      if (GC_NO_SPACE == source) {
 181        GC_err_printf("No debug info in object: Can't find reference\n");
 182        break;
 183      }
 184      GC_err_printf("Reachable via %d levels of pointers from ", i);
 185      switch (source) {
 186      case GC_REFD_FROM_ROOT:
 187        GC_err_printf("root at %p\n\n", base);
 188        return;
 189      case GC_REFD_FROM_REG:
 190        GC_err_printf("root in register\n\n");
 191        return;
 192      case GC_FINALIZER_REFD:
 193        GC_err_printf("list of finalizable objects\n\n");
 194        return;
 195      case GC_REFD_FROM_HEAP:
 196        GC_err_printf("offset %ld in object:\n", (long)offset);
 197        /* Take `GC_base(base)` to get real base, i.e. header. */
 198        GC_print_heap_obj((ptr_t)GC_base(base));
 199        break;
 200      default:
 201        GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n");
 202        return;
 203      }
 204      current = base;
 205    }
 206  }
 207  
 208  GC_API void GC_CALL
 209  GC_generate_random_backtrace(void)
 210  {
 211    void *current;
 212  
 213    GC_ASSERT(I_DONT_HOLD_LOCK());
 214    if (GC_try_to_collect(GC_never_stop_func) == 0) {
 215      GC_err_printf("Cannot generate a backtrace: "
 216                    "garbage collection is disabled!\n");
 217      return;
 218    }
 219  
 220    /* Generate/print a backtrace from a random heap address. */
 221    LOCK();
 222    current = GC_generate_random_valid_address();
 223    UNLOCK();
 224    GC_printf("\n***Chosen address %p in object\n", current);
 225    GC_print_backtrace(current);
 226  }
 227  
 228  #endif /* KEEP_BACK_PTRS */
 229  
 230  #define CROSSES_HBLK(p, sz) \
 231    ((ADDR((p) + (sizeof(oh) - 1) + (sz)) ^ ADDR(p)) >= HBLKSIZE)
 232  
 233  /*
 234   * Store debugging info into `p`.  Return displaced pointer.  Assume we hold
 235   * the allocator lock.
 236   */
 237  STATIC void *
 238  GC_store_debug_info_inner(void *base, size_t sz, const char *string,
 239                            int linenum)
 240  {
 241    GC_uintptr_t *result = (GC_uintptr_t *)((oh *)base + 1);
 242  
 243    GC_ASSERT(I_HOLD_LOCK());
 244    GC_ASSERT(GC_size(base) >= sizeof(oh) + sz);
 245    GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK((ptr_t)base, sz)));
 246  #ifdef KEEP_BACK_PTRS
 247    ((oh *)base)->oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED);
 248  #endif
 249  #ifdef MAKE_BACK_GRAPH
 250    ((oh *)base)->oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0);
 251  #endif
 252    ((oh *)base)->oh_string = string;
 253    ((oh *)base)->oh_int = linenum;
 254  #ifdef SHORT_DBG_HDRS
 255    UNUSED_ARG(sz);
 256  #else
 257    ((oh *)base)->oh_sz = (GC_uintptr_t)sz;
 258    ((oh *)base)->oh_sf = START_FLAG ^ (GC_uintptr_t)result;
 259    ((GC_uintptr_t *)base)[BYTES_TO_PTRS(GC_size(base)) - 1]
 260        = result[BYTES_TO_PTRS_ROUNDUP(sz)] = END_FLAG ^ (GC_uintptr_t)result;
 261  #endif
 262    return result;
 263  }
 264  
 265  #ifndef SHORT_DBG_HDRS
 266  /*
 267   * Check the object with debugging info at `ohdr`.  Return `NULL` if it
 268   * is OK.  Else return clobbered address.
 269   */
 270  STATIC ptr_t
 271  GC_check_annotated_obj(oh *ohdr)
 272  {
 273    ptr_t body = (ptr_t)(ohdr + 1);
 274    size_t gc_sz = GC_size(ohdr);
 275    size_t lpw_up;
 276  
 277    if (ohdr->oh_sz + DEBUG_BYTES > (GC_uintptr_t)gc_sz) {
 278      return (ptr_t)(&ohdr->oh_sz);
 279    }
 280    if (ohdr->oh_sf != (START_FLAG ^ (GC_uintptr_t)body)) {
 281      return (ptr_t)(&ohdr->oh_sf);
 282    }
 283  
 284    {
 285      size_t lpw_m1 = BYTES_TO_PTRS(gc_sz) - 1;
 286  
 287      if (((GC_uintptr_t *)ohdr)[lpw_m1] != (END_FLAG ^ (GC_uintptr_t)body)) {
 288        return (ptr_t)(&((GC_uintptr_t *)ohdr)[lpw_m1]);
 289      }
 290    }
 291    lpw_up = BYTES_TO_PTRS_ROUNDUP((size_t)ohdr->oh_sz);
 292    if (((GC_uintptr_t *)body)[lpw_up] != (END_FLAG ^ (GC_uintptr_t)body)) {
 293      return (ptr_t)(&((GC_uintptr_t *)body)[lpw_up]);
 294    }
 295    return NULL;
 296  }
 297  #endif /* !SHORT_DBG_HDRS */
 298  
 299  STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = { 0 };
 300  
 301  GC_API void GC_CALL
 302  GC_register_describe_type_fn(int kind, GC_describe_type_fn fn)
 303  {
 304    GC_ASSERT((unsigned)kind < MAXOBJKINDS);
 305    GC_describe_type_fns[kind] = fn;
 306  }
 307  
 308  #ifndef SHORT_DBG_HDRS
 309  #  define IF_NOT_SHORTDBG_HDRS(x) x
 310  #  define COMMA_IFNOT_SHORTDBG_HDRS(x) /* comma */ , x
 311  #else
 312  #  define IF_NOT_SHORTDBG_HDRS(x)
 313  #  define COMMA_IFNOT_SHORTDBG_HDRS(x)
 314  #endif
 315  
 316  /*
 317   * Print a human-readable description of the object to `stderr`.
 318   * The object is assumed to have the debugging info.
 319   */
 320  STATIC void
 321  GC_print_obj(ptr_t base)
 322  {
 323    oh *ohdr = (oh *)base;
 324    ptr_t q;
 325    hdr *hhdr;
 326    int kind;
 327    const char *kind_str;
 328    char buffer[GC_TYPE_DESCR_LEN + 1];
 329  
 330    GC_ASSERT(I_DONT_HOLD_LOCK());
 331  #ifdef LINT2
 332    if (!ohdr)
 333      ABORT("Invalid GC_print_obj argument");
 334  #endif
 335  
 336    q = (ptr_t)(ohdr + 1);
 337    /*
 338     * Print a type description for the object whose client-visible address
 339     * is `q`.
 340     */
 341    hhdr = GC_find_header(q);
 342    kind = hhdr->hb_obj_kind;
 343    if (GC_describe_type_fns[kind] != 0 && GC_is_marked(ohdr)) {
 344      /*
 345       * This should preclude free-list objects except with thread-local
 346       * allocation.
 347       */
 348      buffer[GC_TYPE_DESCR_LEN] = 0;
 349      (*GC_describe_type_fns[kind])(q, buffer);
 350      GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0);
 351      kind_str = buffer;
 352    } else {
 353      switch (kind) {
 354      case PTRFREE:
 355        kind_str = "PTRFREE";
 356        break;
 357      case NORMAL:
 358        kind_str = "NORMAL";
 359        break;
 360      case UNCOLLECTABLE:
 361        kind_str = "UNCOLLECTABLE";
 362        break;
 363  #ifdef GC_ATOMIC_UNCOLLECTABLE
 364      case AUNCOLLECTABLE:
 365        kind_str = "ATOMIC_UNCOLLECTABLE";
 366        break;
 367  #endif
 368      default:
 369        kind_str = NULL;
 370        /*
 371         * The alternative is to use `snprintf(buffer)` but the latter is
 372         * not quite portable (see `vsnprintf` in `misc.c` file).
 373         */
 374      }
 375    }
 376  
 377    if (NULL != kind_str) {
 378      GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz= %lu,") " %s)\n",
 379                    (void *)((ptr_t)ohdr + sizeof(oh)), ohdr->oh_string,
 380                    GET_OH_LINENUM(ohdr) /*, */
 381                    COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
 382                    kind_str);
 383    } else {
 384      GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(
 385                        " sz= %lu,") " kind= %d, descr= 0x%lx)\n",
 386                    (void *)((ptr_t)ohdr + sizeof(oh)), ohdr->oh_string,
 387                    GET_OH_LINENUM(ohdr) /*, */
 388                    COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
 389                    kind, (unsigned long)hhdr->hb_descr);
 390    }
 391    PRINT_CALL_CHAIN(ohdr);
 392  }
 393  
 394  STATIC void
 395  GC_debug_print_heap_obj_proc(ptr_t base)
 396  {
 397    GC_ASSERT(I_DONT_HOLD_LOCK());
 398    if (GC_HAS_DEBUG_INFO(base)) {
 399      GC_print_obj(base);
 400    } else {
 401      GC_default_print_heap_obj_proc(base);
 402    }
 403  }
 404  
 405  #ifndef SHORT_DBG_HDRS
 406  STATIC void GC_check_heap_proc(void);
 407  #elif !defined(NO_FIND_LEAK)
 408  static void
 409  do_nothing(void)
 410  {
 411  }
 412  #endif /* SHORT_DBG_HDRS */
 413  
 414  #if defined(NO_FIND_LEAK) && defined(SHORT_DBG_HDRS)
 415  static GC_bool debugging_initialized = FALSE;
 416  #else
 417  #  define debugging_initialized GC_debugging_started
 418  #endif
 419  
 420  /*
 421   * Turn on the debugging mode.  Should not be called if
 422   * `debugging_initialized` is already set.
 423   */
 424  STATIC void
 425  GC_start_debugging_inner(void)
 426  {
 427    GC_ASSERT(I_HOLD_LOCK());
 428  #ifndef SHORT_DBG_HDRS
 429    GC_check_heap = GC_check_heap_proc;
 430    GC_print_all_smashed = GC_print_all_smashed_proc;
 431  #elif !defined(NO_FIND_LEAK)
 432    GC_check_heap = do_nothing;
 433    GC_print_all_smashed = do_nothing;
 434  #endif
 435    GC_print_heap_obj = GC_debug_print_heap_obj_proc;
 436    debugging_initialized = TRUE;
 437    GC_register_displacement_inner(sizeof(oh));
 438  #if defined(CPPCHECK)
 439    GC_noop1(GC_debug_header_size);
 440  #endif
 441  }
 442  
 443  /*
 444   * Check the allocation is successful, store debugging info into `base`,
 445   * start the debugging mode (if not yet), and return displaced pointer.
 446   */
 447  static void *
 448  store_debug_info(void *base, size_t lb, const char *fn, GC_EXTRA_PARAMS)
 449  {
 450    void *result;
 451  
 452    if (NULL == base) {
 453      GC_err_printf("%s(%lu) returning NULL (%s:%d)\n", fn, (unsigned long)lb, s,
 454                    i);
 455      return NULL;
 456    }
 457    LOCK();
 458    if (!debugging_initialized)
 459      GC_start_debugging_inner();
 460    result = GC_store_debug_info_inner(base, lb, s, i);
 461    ADD_CALL_CHAIN(base, ra);
 462    UNLOCK();
 463    return result;
 464  }
 465  
 466  const size_t GC_debug_header_size = sizeof(oh);
 467  
 468  GC_API size_t GC_CALL
 469  GC_get_debug_header_size(void)
 470  {
 471    return sizeof(oh);
 472  }
 473  
 474  GC_API void GC_CALL
 475  GC_debug_register_displacement(size_t offset)
 476  {
 477    LOCK();
 478    GC_register_displacement_inner(offset);
 479    GC_register_displacement_inner(sizeof(oh) + offset);
 480    UNLOCK();
 481  }
 482  
 483  #ifdef GC_ADD_CALLER
 484  #  if defined(HAVE_DLADDR) && defined(GC_HAVE_RETURN_ADDR_PARENT) \
 485        && defined(FUNCPTR_IS_DATAPTR)
 486  #    include <dlfcn.h>
 487  
 488  STATIC void
 489  GC_caller_func_offset(GC_return_addr_t ra, const char **symp, int *offp)
 490  {
 491    Dl_info caller;
 492  
 493    if (ra != 0 && dladdr((void *)ra, &caller) && caller.dli_sname != NULL) {
 494      *symp = caller.dli_sname;
 495      *offp = (int)((ptr_t)ra - (ptr_t)caller.dli_saddr);
 496    }
 497    if (NULL == *symp) {
 498      *symp = "unknown";
 499      /* Note: `*offp` is unchanged. */
 500    }
 501  }
 502  #  else
 503  #    define GC_caller_func_offset(ra, symp, offp) (void)(*(symp) = "unknown")
 504  #  endif
 505  #endif /* GC_ADD_CALLER */
 506  
 507  GC_API GC_ATTR_MALLOC void *GC_CALL
 508  GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS)
 509  {
 510    void *base;
 511  
 512    /*
 513     * Note that according to `malloc()` specification, if size (`lb`) is
 514     * zero, then `malloc()` returns either `NULL`, or a unique pointer
 515     * value that can later be successfully passed to `free()`.
 516     * We always do the latter.
 517     */
 518  #if defined(_FORTIFY_SOURCE) && !defined(__clang__)
 519    /* Workaround to avoid "exceeds maximum object size" gcc warning. */
 520    base = GC_malloc(lb < GC_SIZE_MAX - DEBUG_BYTES ? lb + DEBUG_BYTES
 521                                                    : GC_SIZE_MAX >> 1);
 522  #else
 523    base = GC_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES));
 524  #endif
 525  #ifdef GC_ADD_CALLER
 526    if (NULL == s) {
 527      GC_caller_func_offset(ra, &s, &i);
 528    }
 529  #endif
 530    return store_debug_info(base, lb, "GC_debug_malloc", OPT_RA s, i);
 531  }
 532  
 533  GC_API GC_ATTR_MALLOC void *GC_CALL
 534  GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS)
 535  {
 536    void *base = GC_malloc_ignore_off_page(SIZET_SAT_ADD(lb, DEBUG_BYTES));
 537  
 538    return store_debug_info(base, lb, "GC_debug_malloc_ignore_off_page",
 539                            OPT_RA s, i);
 540  }
 541  
 542  GC_API GC_ATTR_MALLOC void *GC_CALL
 543  GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS)
 544  {
 545    void *base
 546        = GC_malloc_atomic_ignore_off_page(SIZET_SAT_ADD(lb, DEBUG_BYTES));
 547  
 548    return store_debug_info(base, lb, "GC_debug_malloc_atomic_ignore_off_page",
 549                            OPT_RA s, i);
 550  }
 551  
 552  STATIC void *
 553  GC_debug_generic_malloc(size_t lb, int kind, GC_EXTRA_PARAMS)
 554  {
 555    void *base = GC_generic_malloc_aligned(SIZET_SAT_ADD(lb, DEBUG_BYTES), kind,
 556                                           0 /* `flags` */, 0 /* `align_m1` */);
 557  
 558    return store_debug_info(base, lb, "GC_debug_generic_malloc", OPT_RA s, i);
 559  }
 560  
 561  #ifdef DBG_HDRS_ALL
 562  GC_INNER void *
 563  GC_debug_generic_malloc_inner(size_t lb, int kind, unsigned flags)
 564  {
 565    void *base, *result;
 566  
 567    GC_ASSERT(I_HOLD_LOCK());
 568    base = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), kind, flags);
 569    if (NULL == base) {
 570      GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
 571                    (unsigned long)lb);
 572      return NULL;
 573    }
 574    if (!debugging_initialized)
 575      GC_start_debugging_inner();
 576    result = GC_store_debug_info_inner(base, lb, "INTERNAL", 0);
 577    ADD_CALL_CHAIN_INNER(base);
 578    return result;
 579  }
 580  #endif /* DBG_HDRS_ALL */
 581  
 582  #ifndef CPPCHECK
 583  GC_API void *GC_CALL
 584  GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS)
 585  {
 586    return GC_debug_malloc(lb, OPT_RA s, i);
 587  }
 588  
 589  GC_API void GC_CALL
 590  GC_debug_change_stubborn(const void *p)
 591  {
 592    UNUSED_ARG(p);
 593  }
 594  #endif /* !CPPCHECK */
 595  
 596  GC_API void GC_CALL
 597  GC_debug_end_stubborn_change(const void *p)
 598  {
 599    const void *q = GC_base_C(p);
 600  
 601    if (NULL == q) {
 602      ABORT_ARG1("GC_debug_end_stubborn_change: bad arg", ": %p", p);
 603    }
 604    GC_end_stubborn_change(q);
 605  }
 606  
 607  GC_API void GC_CALL
 608  GC_debug_ptr_store_and_dirty(void *p, const void *q)
 609  {
 610    *(void **)GC_is_visible(p)
 611        = GC_is_valid_displacement(GC_CAST_AWAY_CONST_PVOID(q));
 612    GC_debug_end_stubborn_change(p);
 613    REACHABLE_AFTER_DIRTY(q);
 614  }
 615  
 616  GC_API GC_ATTR_MALLOC void *GC_CALL
 617  GC_debug_malloc_atomic(size_t lb, GC_EXTRA_PARAMS)
 618  {
 619    void *base = GC_malloc_atomic(SIZET_SAT_ADD(lb, DEBUG_BYTES));
 620  
 621    return store_debug_info(base, lb, "GC_debug_malloc_atomic", OPT_RA s, i);
 622  }
 623  
 624  GC_API GC_ATTR_MALLOC char *GC_CALL
 625  GC_debug_strdup(const char *str, GC_EXTRA_PARAMS)
 626  {
 627    char *copy;
 628    size_t lb;
 629    if (str == NULL) {
 630      if (GC_find_leak_inner)
 631        GC_err_printf("strdup(NULL) behavior is undefined\n");
 632      return NULL;
 633    }
 634  
 635    lb = strlen(str) + 1;
 636    copy = (char *)GC_debug_malloc_atomic(lb, OPT_RA s, i);
 637    if (copy == NULL) {
 638  #ifndef MSWINCE
 639      errno = ENOMEM;
 640  #endif
 641      return NULL;
 642    }
 643    BCOPY(str, copy, lb);
 644    return copy;
 645  }
 646  
 647  GC_API GC_ATTR_MALLOC char *GC_CALL
 648  GC_debug_strndup(const char *str, size_t size, GC_EXTRA_PARAMS)
 649  {
 650    char *copy;
 651    /* `str` is expected to be non-`NULL`. */
 652    size_t len = strlen(str);
 653  
 654    if (len > size)
 655      len = size;
 656    copy = (char *)GC_debug_malloc_atomic(len + 1, OPT_RA s, i);
 657    if (copy == NULL) {
 658  #ifndef MSWINCE
 659      errno = ENOMEM;
 660  #endif
 661      return NULL;
 662    }
 663    if (len > 0)
 664      BCOPY(str, copy, len);
 665    copy[len] = '\0';
 666    return copy;
 667  }
 668  
 669  #ifdef GC_REQUIRE_WCSDUP
 670  #  include <wchar.h> /*< for `wcslen()` */
 671  
 672  GC_API GC_ATTR_MALLOC wchar_t *GC_CALL
 673  GC_debug_wcsdup(const wchar_t *str, GC_EXTRA_PARAMS)
 674  {
 675    size_t lb = (wcslen(str) + 1) * sizeof(wchar_t);
 676    wchar_t *copy = (wchar_t *)GC_debug_malloc_atomic(lb, OPT_RA s, i);
 677    if (copy == NULL) {
 678  #  ifndef MSWINCE
 679      errno = ENOMEM;
 680  #  endif
 681      return NULL;
 682    }
 683    BCOPY(str, copy, lb);
 684    return copy;
 685  }
 686  #endif /* GC_REQUIRE_WCSDUP */
 687  
 688  GC_API GC_ATTR_MALLOC void *GC_CALL
 689  GC_debug_malloc_uncollectable(size_t lb, GC_EXTRA_PARAMS)
 690  {
 691    void *base
 692        = GC_malloc_uncollectable(SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES));
 693  
 694    return store_debug_info(base, lb, "GC_debug_malloc_uncollectable", OPT_RA s,
 695                            i);
 696  }
 697  
 698  #ifdef GC_ATOMIC_UNCOLLECTABLE
 699  GC_API GC_ATTR_MALLOC void *GC_CALL
 700  GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS)
 701  {
 702    void *base = GC_malloc_atomic_uncollectable(
 703        SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES));
 704  
 705    return store_debug_info(base, lb, "GC_debug_malloc_atomic_uncollectable",
 706                            OPT_RA s, i);
 707  }
 708  #endif /* GC_ATOMIC_UNCOLLECTABLE */
 709  
 710  #ifdef LINT2
 711  #  include "private/gc_alloc_ptrs.h"
 712  #endif
 713  
 714  GC_API void GC_CALL
 715  GC_debug_free(void *p)
 716  {
 717    ptr_t base;
 718    if (0 == p)
 719      return;
 720  
 721    base = (ptr_t)GC_base(p);
 722    if (NULL == base) {
 723  #if defined(REDIRECT_MALLOC)                                           \
 724      && ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \
 725          || defined(REDIR_MALLOC_AND_LINUXTHREADS)                      \
 726          || (defined(SOLARIS) && defined(THREADS)) || defined(MSWIN32))
 727      /*
 728       * In some cases, we should ignore objects that do not belong to
 729       * the collector heap.  See the comment in `GC_free()`.
 730       */
 731      if (!GC_is_heap_ptr(p))
 732        return;
 733  #endif
 734      ABORT_ARG1("Invalid pointer passed to free()", ": %p", p);
 735    }
 736    if ((word)((ptr_t)p - base) != sizeof(oh)) {
 737  #if defined(REDIRECT_FREE) && defined(USE_PROC_FOR_LIBRARIES)
 738      /*
 739       * TODO: Suppress the warning if `free()` caller is in `libpthread`
 740       * or `libdl`.
 741       */
 742  #endif
 743      /*
 744       * TODO: Suppress the warning for objects allocated by `GC_memalign`
 745       * and friends (these ones do not have the debugging counterpart).
 746       */
 747      GC_err_printf("GC_debug_free called on pointer %p w/o debugging info\n",
 748                    p);
 749    } else {
 750  #ifndef SHORT_DBG_HDRS
 751      ptr_t clobbered = GC_check_annotated_obj((oh *)base);
 752      size_t sz = GC_size(base);
 753  
 754      if (clobbered != NULL) {
 755        /* No "release" barrier is needed. */
 756        GC_SET_HAVE_ERRORS();
 757        if (((oh *)base)->oh_sz == (GC_uintptr_t)sz) {
 758          GC_print_smashed_obj(
 759              "GC_debug_free: found previously deallocated (?) object at", p,
 760              clobbered);
 761          /* Ignore double free. */
 762          return;
 763        } else {
 764          GC_print_smashed_obj("GC_debug_free: found smashed location at", p,
 765                               clobbered);
 766        }
 767      }
 768      /* Invalidate the size (mark the object as deallocated). */
 769      ((oh *)base)->oh_sz = (GC_uintptr_t)sz;
 770  #endif /* !SHORT_DBG_HDRS */
 771    }
 772  #ifndef NO_FIND_LEAK
 773    if (GC_find_leak_inner
 774  #  ifndef SHORT_DBG_HDRS
 775        && ((word)((ptr_t)p - base) != sizeof(oh) || !GC_findleak_delay_free)
 776  #  endif
 777    ) {
 778      GC_free(base);
 779    } else
 780  #endif
 781    /* else */ {
 782      const hdr *hhdr = HDR(p);
 783  
 784      if (hhdr->hb_obj_kind == UNCOLLECTABLE
 785  #ifdef GC_ATOMIC_UNCOLLECTABLE
 786          || hhdr->hb_obj_kind == AUNCOLLECTABLE
 787  #endif
 788      ) {
 789        GC_free(base);
 790      } else {
 791        size_t sz = hhdr->hb_sz;
 792        size_t i;
 793        size_t lpw = BYTES_TO_PTRS(sz - sizeof(oh));
 794  
 795        for (i = 0; i < lpw; ++i)
 796          ((GC_uintptr_t *)p)[i] = GC_FREED_MEM_MARKER;
 797        GC_ASSERT((GC_uintptr_t *)p + i == (GC_uintptr_t *)(base + sz));
 798        /*
 799         * Update the counter even though the real deallocation
 800         * is deferred.
 801         */
 802        LOCK();
 803  #ifdef LINT2
 804        GC_incr_bytes_freed(sz);
 805  #else
 806        GC_bytes_freed += sz;
 807  #endif
 808        UNLOCK();
 809      }
 810    }
 811  }
 812  
 813  #if defined(THREADS) && defined(DBG_HDRS_ALL)
 814  GC_INNER void
 815  GC_debug_free_inner(void *p)
 816  {
 817    ptr_t base = (ptr_t)GC_base(p);
 818  
 819    GC_ASSERT(I_HOLD_LOCK());
 820    GC_ASSERT((word)((ptr_t)p - base) == sizeof(oh));
 821  #  ifdef LINT2
 822    if (!base)
 823      ABORT("Invalid GC_debug_free_inner argument");
 824  #  endif
 825  #  ifndef SHORT_DBG_HDRS
 826    /* Invalidate the size. */
 827    ((oh *)base)->oh_sz = (GC_uintptr_t)GC_size(base);
 828  #  endif
 829    GC_free_inner(base);
 830  }
 831  #endif
 832  
 833  GC_API void *GC_CALL
 834  GC_debug_realloc(void *p, size_t lb, GC_EXTRA_PARAMS)
 835  {
 836    ptr_t base;
 837    void *result;
 838    const hdr *hhdr;
 839  
 840    if (NULL == p) {
 841      return GC_debug_malloc(lb, OPT_RA s, i);
 842    }
 843    if (0 == lb) /* `&& p != NULL` */ {
 844      GC_debug_free(p);
 845      return NULL;
 846    }
 847  
 848  #ifdef GC_ADD_CALLER
 849    if (NULL == s) {
 850      GC_caller_func_offset(ra, &s, &i);
 851    }
 852  #endif
 853    base = (ptr_t)GC_base(p);
 854    if (NULL == base) {
 855      ABORT_ARG1("Invalid pointer passed to realloc()", ": %p", p);
 856    }
 857    if ((word)((ptr_t)p - base) != sizeof(oh)) {
 858      GC_err_printf("GC_debug_realloc called on pointer %p w/o debugging info\n",
 859                    p);
 860      return GC_realloc(p, lb);
 861    }
 862    hhdr = HDR(base);
 863    result
 864        = GC_debug_generic_or_special_malloc(lb, hhdr->hb_obj_kind, OPT_RA s, i);
 865    if (result != NULL) {
 866      size_t old_sz;
 867  #ifdef SHORT_DBG_HDRS
 868      old_sz = GC_size(base) - sizeof(oh);
 869  #else
 870      old_sz = (size_t)(((oh *)base)->oh_sz);
 871  #endif
 872      if (old_sz > 0)
 873        BCOPY(p, result, old_sz < lb ? old_sz : lb);
 874      GC_debug_free(p);
 875    }
 876    return result;
 877  }
 878  
 879  GC_API GC_ATTR_MALLOC void *GC_CALL
 880  GC_debug_generic_or_special_malloc(size_t lb, int kind, GC_EXTRA_PARAMS)
 881  {
 882    switch (kind) {
 883    case PTRFREE:
 884      return GC_debug_malloc_atomic(lb, OPT_RA s, i);
 885    case NORMAL:
 886      return GC_debug_malloc(lb, OPT_RA s, i);
 887    case UNCOLLECTABLE:
 888      return GC_debug_malloc_uncollectable(lb, OPT_RA s, i);
 889  #ifdef GC_ATOMIC_UNCOLLECTABLE
 890    case AUNCOLLECTABLE:
 891      return GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i);
 892  #endif
 893    default:
 894      return GC_debug_generic_malloc(lb, kind, OPT_RA s, i);
 895    }
 896  }
 897  
 898  #ifndef SHORT_DBG_HDRS
 899  
 900  /*
 901   * Check all marked objects in the given block for validity.
 902   * Note: avoid `GC_apply_to_each_object` for performance reasons.
 903   */
 904  STATIC void GC_CALLBACK
 905  GC_check_heap_block(struct hblk *hbp, void *dummy)
 906  {
 907    const hdr *hhdr = HDR(hbp);
 908    ptr_t p = hbp->hb_body;
 909    ptr_t plim;
 910    size_t sz = hhdr->hb_sz;
 911    size_t bit_no;
 912  
 913    UNUSED_ARG(dummy);
 914    GC_ASSERT((ptr_t)hhdr->hb_block == p);
 915    plim = sz > MAXOBJBYTES ? p : p + HBLKSIZE - sz;
 916    /* Go through all objects in block. */
 917    for (bit_no = 0; ADDR_GE(plim, p); bit_no += MARK_BIT_OFFSET(sz), p += sz) {
 918      if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO(p)) {
 919        ptr_t clobbered = GC_check_annotated_obj((oh *)p);
 920  
 921        if (clobbered != NULL)
 922          GC_add_smashed(clobbered);
 923      }
 924    }
 925  }
 926  
 927  /*
 928   * This assumes that all accessible objects are marked.
 929   * Normally called by collector.
 930   */
 931  STATIC void
 932  GC_check_heap_proc(void)
 933  {
 934    GC_ASSERT(I_HOLD_LOCK());
 935    GC_STATIC_ASSERT((sizeof(oh) & (GC_GRANULE_BYTES - 1)) == 0);
 936    /* FIXME: Should we check for twice that alignment? */
 937    GC_apply_to_all_blocks(GC_check_heap_block, NULL);
 938  }
 939  
 940  #endif /* !SHORT_DBG_HDRS */
 941  
 942  #ifndef GC_NO_FINALIZATION
 943  
 944  struct closure {
 945    GC_finalization_proc cl_fn;
 946    void *cl_data;
 947  };
 948  
 949  STATIC void *
 950  GC_make_closure(GC_finalization_proc fn, void *data)
 951  {
 952    struct closure *result =
 953  #  ifdef DBG_HDRS_ALL
 954        (struct closure *)GC_debug_malloc(sizeof(struct closure), GC_EXTRAS);
 955  #  else
 956        (struct closure *)GC_malloc(sizeof(struct closure));
 957  #  endif
 958    if (result != NULL) {
 959      result->cl_fn = fn;
 960      result->cl_data = data;
 961    }
 962    return result;
 963  }
 964  
 965  /*
 966   * An auxiliary function to make finalization work correctly with
 967   * displaced pointers introduced by the debugging allocators.
 968   */
 969  STATIC void GC_CALLBACK
 970  GC_debug_invoke_finalizer(void *obj, void *data)
 971  {
 972    struct closure *cl = (struct closure *)data;
 973  
 974    cl->cl_fn((ptr_t)obj + sizeof(oh), cl->cl_data);
 975  }
 976  
 977  /* Special `finalizer_proc` value to detect `GC_register_finalizer` failure. */
 978  #  define OFN_UNSET ((GC_finalization_proc)(~(GC_funcptr_uint)0))
 979  
 980  /* Set `ofn` and `ocd` to reflect the values we got back. */
 981  static void
 982  store_old(void *obj, GC_finalization_proc my_old_fn, struct closure *my_old_cd,
 983            GC_finalization_proc *ofn, void **ocd)
 984  {
 985    if (my_old_fn != 0) {
 986      if (my_old_fn == OFN_UNSET) {
 987        /* `GC_register_finalizer()` failed; `*ofn` and `*ocd` are unchanged. */
 988        return;
 989      }
 990      if (my_old_fn != GC_debug_invoke_finalizer) {
 991        GC_err_printf("Debuggable object at %p had a non-debug finalizer\n",
 992                      obj);
 993        /* This should probably be fatal. */
 994      } else {
 995        if (ofn)
 996          *ofn = my_old_cd->cl_fn;
 997        if (ocd)
 998          *ocd = my_old_cd->cl_data;
 999      }
1000    } else {
1001      if (ofn)
1002        *ofn = 0;
1003      if (ocd)
1004        *ocd = NULL;
1005    }
1006  }
1007  
1008  GC_API void GC_CALL
1009  GC_debug_register_finalizer(void *obj, GC_finalization_proc fn, void *cd,
1010                              GC_finalization_proc *ofn, void **ocd)
1011  {
1012    GC_finalization_proc my_old_fn = OFN_UNSET;
1013    void *my_old_cd = NULL; /*< to avoid "might be uninitialized" warning */
1014    ptr_t base = (ptr_t)GC_base(obj);
1015  
1016    if (NULL == base) {
1017      /* We will not collect it, hence finalizer would not be run. */
1018      if (ocd)
1019        *ocd = NULL;
1020      if (ofn)
1021        *ofn = 0;
1022      return;
1023    }
1024    if ((ptr_t)obj - base != sizeof(oh)) {
1025      GC_err_printf("GC_debug_register_finalizer called with"
1026                    " non-base-pointer %p\n",
1027                    obj);
1028    }
1029    if (0 == fn) {
1030      GC_register_finalizer(base, 0, NULL, &my_old_fn, &my_old_cd);
1031    } else {
1032      cd = GC_make_closure(fn, cd);
1033      if (NULL == cd) {
1034        /* Out of memory; `*ofn` and `*ocd` are unchanged. */
1035        return;
1036      }
1037      GC_register_finalizer(base, GC_debug_invoke_finalizer, cd, &my_old_fn,
1038                            &my_old_cd);
1039    }
1040    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1041  }
1042  
1043  GC_API void GC_CALL
1044  GC_debug_register_finalizer_no_order(void *obj, GC_finalization_proc fn,
1045                                       void *cd, GC_finalization_proc *ofn,
1046                                       void **ocd)
1047  {
1048    GC_finalization_proc my_old_fn = OFN_UNSET;
1049    void *my_old_cd = NULL;
1050    ptr_t base = (ptr_t)GC_base(obj);
1051    if (NULL == base) {
1052      if (ocd)
1053        *ocd = NULL;
1054      if (ofn)
1055        *ofn = 0;
1056      return;
1057    }
1058    if ((ptr_t)obj - base != sizeof(oh)) {
1059      GC_err_printf("GC_debug_register_finalizer_no_order called with"
1060                    " non-base-pointer %p\n",
1061                    obj);
1062    }
1063    if (0 == fn) {
1064      GC_register_finalizer_no_order(base, 0, NULL, &my_old_fn, &my_old_cd);
1065    } else {
1066      cd = GC_make_closure(fn, cd);
1067      if (NULL == cd) {
1068        /* Out of memory. */
1069        return;
1070      }
1071      GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer, cd,
1072                                     &my_old_fn, &my_old_cd);
1073    }
1074    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1075  }
1076  
1077  GC_API void GC_CALL
1078  GC_debug_register_finalizer_unreachable(void *obj, GC_finalization_proc fn,
1079                                          void *cd, GC_finalization_proc *ofn,
1080                                          void **ocd)
1081  {
1082    GC_finalization_proc my_old_fn = OFN_UNSET;
1083    void *my_old_cd = NULL;
1084    ptr_t base = (ptr_t)GC_base(obj);
1085    if (NULL == base) {
1086      if (ocd)
1087        *ocd = NULL;
1088      if (ofn)
1089        *ofn = 0;
1090      return;
1091    }
1092    if ((ptr_t)obj - base != sizeof(oh)) {
1093      GC_err_printf("GC_debug_register_finalizer_unreachable called with"
1094                    " non-base-pointer %p\n",
1095                    obj);
1096    }
1097    if (0 == fn) {
1098      GC_register_finalizer_unreachable(base, 0, NULL, &my_old_fn, &my_old_cd);
1099    } else {
1100      cd = GC_make_closure(fn, cd);
1101      if (NULL == cd) {
1102        /* Out of memory. */
1103        return;
1104      }
1105      GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer, cd,
1106                                        &my_old_fn, &my_old_cd);
1107    }
1108    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1109  }
1110  
1111  GC_API void GC_CALL
1112  GC_debug_register_finalizer_ignore_self(void *obj, GC_finalization_proc fn,
1113                                          void *cd, GC_finalization_proc *ofn,
1114                                          void **ocd)
1115  {
1116    GC_finalization_proc my_old_fn = OFN_UNSET;
1117    void *my_old_cd = NULL;
1118    ptr_t base = (ptr_t)GC_base(obj);
1119    if (NULL == base) {
1120      if (ocd)
1121        *ocd = NULL;
1122      if (ofn)
1123        *ofn = 0;
1124      return;
1125    }
1126    if ((ptr_t)obj - base != sizeof(oh)) {
1127      GC_err_printf("GC_debug_register_finalizer_ignore_self called with"
1128                    " non-base-pointer %p\n",
1129                    obj);
1130    }
1131    if (0 == fn) {
1132      GC_register_finalizer_ignore_self(base, 0, NULL, &my_old_fn, &my_old_cd);
1133    } else {
1134      cd = GC_make_closure(fn, cd);
1135      if (NULL == cd) {
1136        /* Out of memory. */
1137        return;
1138      }
1139      GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer, cd,
1140                                        &my_old_fn, &my_old_cd);
1141    }
1142    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1143  }
1144  
1145  #  ifndef GC_TOGGLE_REFS_NOT_NEEDED
1146  GC_API int GC_CALL
1147  GC_debug_toggleref_add(void *obj, int is_strong_ref)
1148  {
1149    ptr_t base = (ptr_t)GC_base(obj);
1150  
1151    if ((ptr_t)obj - base != sizeof(oh)) {
1152      GC_err_printf("GC_debug_toggleref_add called with"
1153                    " non-base-pointer %p\n",
1154                    obj);
1155    }
1156    return GC_toggleref_add(base, is_strong_ref);
1157  }
1158  #  endif /* !GC_TOGGLE_REFS_NOT_NEEDED */
1159  
1160  #endif /* !GC_NO_FINALIZATION */
1161  
1162  GC_API GC_ATTR_MALLOC void *GC_CALL
1163  GC_debug_malloc_replacement(size_t lb)
1164  {
1165    return GC_debug_malloc(lb, GC_DBG_EXTRAS);
1166  }
1167  
1168  GC_API void *GC_CALL
1169  GC_debug_realloc_replacement(void *p, size_t lb)
1170  {
1171    return GC_debug_realloc(p, lb, GC_DBG_EXTRAS);
1172  }
1173  
1174  #ifdef GC_GCJ_SUPPORT
1175  #  include "gc/gc_gcj.h"
1176  
1177  GC_API GC_ATTR_MALLOC void *GC_CALL
1178  GC_debug_gcj_malloc(size_t lb, const void *vtable_ptr, GC_EXTRA_PARAMS)
1179  {
1180    void *base, *result;
1181  
1182    /* We are careful to avoid extra calls those could confuse the backtrace. */
1183    LOCK();
1184    /* A mechanism to invoke finalizers (same as in `GC_core_gcj_malloc`). */
1185    if (GC_gc_no != GC_last_finalized_no) {
1186      UNLOCK();
1187      GC_notify_or_invoke_finalizers();
1188      LOCK();
1189      GC_last_finalized_no = GC_gc_no;
1190    }
1191  
1192    base = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES),
1193                                   GC_gcj_debug_kind, 0 /* `flags` */);
1194    if (NULL == base) {
1195      GC_oom_func oom_fn = GC_oom_fn;
1196      UNLOCK();
1197      GC_err_printf("GC_debug_gcj_malloc(%lu, %p) returning NULL (%s:%d)\n",
1198                    (unsigned long)lb, vtable_ptr, s, i);
1199      return (*oom_fn)(lb);
1200    }
1201    *((const void **)((ptr_t)base + sizeof(oh))) = vtable_ptr;
1202    if (!debugging_initialized)
1203      GC_start_debugging_inner();
1204    result = GC_store_debug_info_inner(base, lb, s, i);
1205    ADD_CALL_CHAIN(base, ra);
1206    UNLOCK();
1207    GC_dirty(result);
1208    REACHABLE_AFTER_DIRTY(vtable_ptr);
1209    return result;
1210  }
1211  #endif /* GC_GCJ_SUPPORT */
1212