dbg_mlc.c 41.2 KB
Newer Older
1
/*
eg's avatar
eg committed
2 3 4
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
 * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
 * Copyright (c) 1997 by Silicon Graphics.  All rights reserved.
eg's avatar
eg committed
5
 * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
6
 * Copyright (C) 2007 Free Software Foundation, Inc
eg's avatar
eg committed
7 8 9 10 11 12 13 14 15 16 17
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose,  provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */

18
#include "private/dbg_mlc.h"
19

20 21 22 23
#ifndef MSWINCE
# include <errno.h>
#endif
#include <string.h>
eg's avatar
eg committed
24 25

#ifndef SHORT_DBG_HDRS
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
  /* Check whether object with base pointer p has debugging info. */
  /* p is assumed to point to a legitimate object in our part     */
  /* of the heap.                                                 */
  /* This excludes the check as to whether the back pointer is    */
  /* odd, which is added by the GC_HAS_DEBUG_INFO macro.          */
  /* Note that if DBG_HDRS_ALL is set, uncollectible objects      */
  /* on free lists may not have debug information set.  Thus it's */
  /* not always safe to return TRUE (1), even if the client does  */
  /* its part.  Return -1 if the object with debug info has been  */
  /* marked as deallocated.                                       */
  GC_INNER int GC_has_other_debug_info(ptr_t p)
  {
    ptr_t body = (ptr_t)((oh *)p + 1);
    word sz = GC_size(p);

    if (HBLKPTR(p) != HBLKPTR((ptr_t)body)
eg's avatar
eg committed
42
        || sz < DEBUG_BYTES + EXTRA_BYTES) {
43
      return 0;
eg's avatar
eg committed
44
    }
45 46 47
    if (((oh *)p) -> oh_sf != (START_FLAG ^ (word)body)
        && ((word *)p)[BYTES_TO_WORDS(sz)-1] != (END_FLAG ^ (word)body)) {
      return 0;
eg's avatar
eg committed
48
    }
49 50 51
    if (((oh *)p)->oh_sz == sz) {
      /* Object may have had debug info, but has been deallocated     */
      return -1;
eg's avatar
eg committed
52
    }
53 54 55 56 57 58 59 60 61 62 63 64 65
    return 1;
  }
#endif /* !SHORT_DBG_HDRS */

#ifdef LINT2
  long GC_random(void)
  {
    static unsigned seed = 1; /* not thread-safe */

    /* Linear congruential pseudo-random numbers generator.     */
    seed = (seed * 1103515245U + 12345) & GC_RAND_MAX; /* overflow is ok */
    return (long)seed;
  }
eg's avatar
eg committed
66 67 68 69
#endif

#ifdef KEEP_BACK_PTRS

70 71 72
#ifdef LINT2
# define RANDOM() GC_random()
#else
eg's avatar
eg committed
73
# include <stdlib.h>
74
# define GC_RAND_MAX RAND_MAX
eg's avatar
eg committed
75

76
# if defined(__GLIBC__) || defined(SOLARIS) \
eg's avatar
eg committed
77
     || defined(HPUX) || defined(IRIX5) || defined(OSF1)
eg's avatar
eg committed
78 79 80 81
#   define RANDOM() random()
# else
#   define RANDOM() (long)rand()
# endif
82
#endif /* !LINT2 */
eg's avatar
eg committed
83 84

  /* Store back pointer to source in dest, if that appears to be possible. */
85 86 87
  /* This is not completely safe, since we may mistakenly conclude that    */
  /* dest has a debugging wrapper.  But the error probability is very      */
  /* small, and this shouldn't be used in production code.                 */
eg's avatar
eg committed
88
  /* We assume that dest is the real base pointer.  Source will usually    */
89 90
  /* be a pointer to the interior of an object.                            */
  GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest)
eg's avatar
eg committed
91 92 93 94 95 96
  {
    if (GC_HAS_DEBUG_INFO(dest)) {
      ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source);
    }
  }

97 98
  GC_INNER void GC_marked_for_finalization(ptr_t dest)
  {
eg's avatar
eg committed
99 100 101
    GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest);
  }

102 103 104 105 106 107 108 109
  /* Store information about the object referencing dest in *base_p     */
  /* and *offset_p.                                                     */
  /*   source is root ==> *base_p = address, *offset_p = 0              */
  /*   source is heap object ==> *base_p != 0, *offset_p = offset       */
  /*   Returns 1 on success, 0 if source couldn't be determined.        */
  /* Dest can be any address within a heap object.                      */
  GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void *dest, void **base_p,
                                                  size_t *offset_p)
eg's avatar
eg committed
110 111 112 113
  {
    oh * hdr = (oh *)GC_base(dest);
    ptr_t bp;
    ptr_t bp_base;
114 115 116 117 118 119 120

#   ifdef LINT2
      /* Explicitly instruct the code analysis tool that                */
      /* GC_get_back_ptr_info is not expected to be called with an      */
      /* incorrect "dest" value.                                        */
      if (!hdr) ABORT("Invalid GC_get_back_ptr_info argument");
#   endif
eg's avatar
eg committed
121
    if (!GC_HAS_DEBUG_INFO((ptr_t) hdr)) return GC_NO_SPACE;
122
    bp = GC_REVEAL_POINTER(hdr -> oh_back_ptr);
eg's avatar
eg committed
123 124 125 126
    if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD;
    if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG;
    if (NOT_MARKED == bp) return GC_UNREFERENCED;
#   if ALIGNMENT == 1
127 128
      /* Heuristically try to fix off by 1 errors we introduced by      */
      /* insisting on even addresses.                                   */
eg's avatar
eg committed
129
      {
130 131 132 133 134 135 136 137 138 139
        ptr_t alternate_ptr = bp + 1;
        ptr_t target = *(ptr_t *)bp;
        ptr_t alternate_target = *(ptr_t *)alternate_ptr;

        if ((word)alternate_target >= (word)GC_least_plausible_heap_addr
            && (word)alternate_target <= (word)GC_greatest_plausible_heap_addr
            && ((word)target < (word)GC_least_plausible_heap_addr
                || (word)target > (word)GC_greatest_plausible_heap_addr)) {
            bp = alternate_ptr;
        }
eg's avatar
eg committed
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
      }
#   endif
    bp_base = GC_base(bp);
    if (0 == bp_base) {
      *base_p = bp;
      *offset_p = 0;
      return GC_REFD_FROM_ROOT;
    } else {
      if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh);
      *base_p = bp_base;
      *offset_p = bp - bp_base;
      return GC_REFD_FROM_HEAP;
    }
  }

155 156 157 158
  /* Generate a random heap address.            */
  /* The resulting address is in the heap, but  */
  /* not necessarily inside a valid object.     */
  GC_API void * GC_CALL GC_generate_random_heap_address(void)
eg's avatar
eg committed
159
  {
160 161
    size_t i;
    word heap_offset = RANDOM();
162 163 164 165

    if (GC_heapsize > GC_RAND_MAX) {
        heap_offset *= GC_RAND_MAX;
        heap_offset += RANDOM();
eg's avatar
eg committed
166 167
    }
    heap_offset %= GC_heapsize;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
        /* This doesn't yield a uniform distribution, especially if     */
        /* e.g. RAND_MAX = 1.5* GC_heapsize.  But for typical cases,    */
        /* it's not too bad.                                            */
    for (i = 0;; ++i) {
        size_t size;

        if (i >= GC_n_heap_sects)
          ABORT("GC_generate_random_heap_address: size inconsistency");

        size = GC_heap_sects[i].hs_bytes;
        if (heap_offset < size) {
            break;
        } else {
            heap_offset -= size;
        }
    }
    return GC_heap_sects[i].hs_start + heap_offset;
eg's avatar
eg committed
185 186 187
  }

  /* Generate a random address inside a valid marked heap object. */
188
  GC_API void * GC_CALL GC_generate_random_valid_address(void)
eg's avatar
eg committed
189 190 191
  {
    ptr_t result;
    ptr_t base;
192 193 194 195 196
    do {
      result = GC_generate_random_heap_address();
      base = GC_base(result);
    } while (base == 0 || !GC_is_marked(base));
    return result;
eg's avatar
eg committed
197 198 199
  }

  /* Print back trace for p */
200
  GC_API void GC_CALL GC_print_backtrace(void *p)
eg's avatar
eg committed
201 202 203 204 205 206 207 208
  {
    void *current = p;
    int i;
    GC_ref_kind source;
    size_t offset;
    void *base;

    GC_print_heap_obj(GC_base(current));
209

eg's avatar
eg committed
210 211 212
    for (i = 0; ; ++i) {
      source = GC_get_back_ptr_info(current, &base, &offset);
      if (GC_UNREFERENCED == source) {
213 214
        GC_err_printf("Reference could not be found\n");
        goto out;
eg's avatar
eg committed
215 216
      }
      if (GC_NO_SPACE == source) {
217 218
        GC_err_printf("No debug info in object: Can't find reference\n");
        goto out;
eg's avatar
eg committed
219
      }
220
      GC_err_printf("Reachable via %d levels of pointers from ", i);
eg's avatar
eg committed
221
      switch(source) {
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
        case GC_REFD_FROM_ROOT:
          GC_err_printf("root at %p\n\n", base);
          goto out;
        case GC_REFD_FROM_REG:
          GC_err_printf("root in register\n\n");
          goto out;
        case GC_FINALIZER_REFD:
          GC_err_printf("list of finalizable objects\n\n");
          goto out;
        case GC_REFD_FROM_HEAP:
          GC_err_printf("offset %ld in object:\n", (long)offset);
          /* Take GC_base(base) to get real base, i.e. header. */
          GC_print_heap_obj(GC_base(base));
          break;
        default:
          GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n");
          goto out;
eg's avatar
eg committed
239 240 241 242 243 244
      }
      current = base;
    }
    out:;
  }

245 246 247
  /* Force a garbage collection and generate/print a backtrace  */
  /* from a random heap address.                                */
  GC_INNER void GC_generate_random_backtrace_no_gc(void)
eg's avatar
eg committed
248 249 250
  {
    void * current;
    current = GC_generate_random_valid_address();
251
    GC_printf("\n****Chosen address %p in object\n", current);
eg's avatar
eg committed
252 253
    GC_print_backtrace(current);
  }
254 255

  GC_API void GC_CALL GC_generate_random_backtrace(void)
eg's avatar
eg committed
256
  {
257 258 259 260 261
    if (GC_try_to_collect(GC_never_stop_func) == 0) {
      GC_err_printf("Cannot generate a backtrace: "
                    "garbage collection is disabled!\n");
      return;
    }
eg's avatar
eg committed
262 263
    GC_generate_random_backtrace_no_gc();
  }
264

eg's avatar
eg committed
265 266 267
#endif /* KEEP_BACK_PTRS */

# define CROSSES_HBLK(p, sz) \
268 269 270 271 272 273
        (((word)((p) + sizeof(oh) + (sz) - 1) ^ (word)(p)) >= HBLKSIZE)

/* Store debugging info into p.  Return displaced pointer.         */
/* This version assumes we do hold the allocation lock.            */
STATIC ptr_t GC_store_debug_info_inner(ptr_t p, word sz GC_ATTR_UNUSED,
                                       const char *string, int linenum)
eg's avatar
eg committed
274
{
275 276
    word * result = (word *)((oh *)p + 1);

eg's avatar
eg committed
277 278 279 280 281 282 283 284 285
    GC_ASSERT(GC_size(p) >= sizeof(oh) + sz);
    GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz)));
#   ifdef KEEP_BACK_PTRS
      ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED);
#   endif
#   ifdef MAKE_BACK_GRAPH
      ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0);
#   endif
    ((oh *)p) -> oh_string = string;
286
    ((oh *)p) -> oh_int = (word)linenum;
eg's avatar
eg committed
287 288 289 290 291 292 293 294 295
#   ifndef SHORT_DBG_HDRS
      ((oh *)p) -> oh_sz = sz;
      ((oh *)p) -> oh_sf = START_FLAG ^ (word)result;
      ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] =
         result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result;
#   endif
    return((ptr_t)result);
}

296 297
GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *string,
                                   int linenum)
eg's avatar
eg committed
298
{
299 300 301 302 303 304 305
    ptr_t result;
    DCL_LOCK_STATE;

    LOCK();
    result = GC_store_debug_info_inner(p, sz, string, linenum);
    UNLOCK();
    return result;
eg's avatar
eg committed
306 307 308
}

#ifndef SHORT_DBG_HDRS
309 310 311 312 313 314 315
  /* Check the object with debugging info at ohdr.      */
  /* Return NULL if it's OK.  Else return clobbered     */
  /* address.                                           */
  STATIC ptr_t GC_check_annotated_obj(oh *ohdr)
  {
    ptr_t body = (ptr_t)(ohdr + 1);
    word gc_sz = GC_size((ptr_t)ohdr);
eg's avatar
eg committed
316 317 318 319 320 321 322 323 324 325 326
    if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) {
        return((ptr_t)(&(ohdr -> oh_sz)));
    }
    if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) {
        return((ptr_t)(&(ohdr -> oh_sf)));
    }
    if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) {
        return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1));
    }
    if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)]
        != (END_FLAG ^ (word)body)) {
327
        return((ptr_t)((word *)body + SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz)));
eg's avatar
eg committed
328 329
    }
    return(0);
330
  }
eg's avatar
eg committed
331 332
#endif /* !SHORT_DBG_HDRS */

333
STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0};
eg's avatar
eg committed
334

335 336
GC_API void GC_CALL GC_register_describe_type_fn(int kind,
                                                 GC_describe_type_fn fn)
eg's avatar
eg committed
337 338 339 340
{
  GC_describe_type_fns[kind] = fn;
}

341
#define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int)
eg's avatar
eg committed
342

343 344 345 346 347 348 349
#ifndef SHORT_DBG_HDRS
# define IF_NOT_SHORTDBG_HDRS(x) x
# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* comma */, x
#else
# define IF_NOT_SHORTDBG_HDRS(x) /* empty */
# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* empty */
#endif
eg's avatar
eg committed
350

351 352 353
/* Print a human-readable description of the object to stderr.          */
/* p points to somewhere inside an object with the debugging info.      */
STATIC void GC_print_obj(ptr_t p)
eg's avatar
eg committed
354
{
355 356 357 358 359 360 361
    oh * ohdr = (oh *)GC_base(p);
    ptr_t q;
    hdr * hhdr;
    int kind;
    char *kind_str;
    char buffer[GC_TYPE_DESCR_LEN + 1];

362
    GC_ASSERT(I_DONT_HOLD_LOCK());
363 364
#   ifdef LINT2
      if (!ohdr) ABORT("Invalid GC_print_obj argument");
eg's avatar
eg committed
365
#   endif
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

    q = (ptr_t)(ohdr + 1);
    /* Print a type description for the object whose client-visible     */
    /* address is q.                                                    */
    hhdr = GC_find_header(q);
    kind = hhdr -> hb_obj_kind;
    if (0 != GC_describe_type_fns[kind] && GC_is_marked(ohdr)) {
        /* This should preclude free list objects except with   */
        /* thread-local allocation.                             */
        buffer[GC_TYPE_DESCR_LEN] = 0;
        (GC_describe_type_fns[kind])(q, buffer);
        GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0);
        kind_str = buffer;
    } else {
        switch(kind) {
          case PTRFREE:
            kind_str = "PTRFREE";
            break;
          case NORMAL:
            kind_str = "NORMAL";
            break;
          case UNCOLLECTABLE:
            kind_str = "UNCOLLECTABLE";
            break;
#         ifdef GC_ATOMIC_UNCOLLECTABLE
            case AUNCOLLECTABLE:
              kind_str = "ATOMIC_UNCOLLECTABLE";
              break;
#         endif
          case STUBBORN:
            kind_str = "STUBBORN";
            break;
          default:
            kind_str = NULL;
                /* The alternative is to use snprintf(buffer) but it is */
                /* not quite portable (see vsnprintf in misc.c).        */
        }
    }

    if (NULL != kind_str) {
        GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,") " %s)\n",
                      (void *)((ptr_t)ohdr + sizeof(oh)),
                      ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
                      COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
                      kind_str);
    } else {
        GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,")
                      " kind=%d descr=0x%lx)\n",
                      (void *)((ptr_t)ohdr + sizeof(oh)),
                      ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
                      COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
                      kind, (unsigned long)hhdr->hb_descr);
    }
eg's avatar
eg committed
419 420 421
    PRINT_CALL_CHAIN(ohdr);
}

422
STATIC void GC_debug_print_heap_obj_proc(ptr_t p)
eg's avatar
eg committed
423
{
424
    GC_ASSERT(I_DONT_HOLD_LOCK());
eg's avatar
eg committed
425
    if (GC_HAS_DEBUG_INFO(p)) {
426
        GC_print_obj(p);
eg's avatar
eg committed
427
    } else {
428
        GC_default_print_heap_obj_proc(p);
eg's avatar
eg committed
429 430 431 432
    }
}

#ifndef SHORT_DBG_HDRS
433 434 435 436 437 438 439 440
  /* Use GC_err_printf and friends to print a description of the object */
  /* whose client-visible address is p, and which was smashed at        */
  /* clobbered_addr.                                                    */
  STATIC void GC_print_smashed_obj(const char *msg, ptr_t p,
                                   ptr_t clobbered_addr)
  {
    oh * ohdr = (oh *)GC_base(p);

441
    GC_ASSERT(I_DONT_HOLD_LOCK());
442 443 444 445
#   ifdef LINT2
      if (!ohdr) ABORT("Invalid GC_print_smashed_obj argument");
#   endif
    if ((word)clobbered_addr <= (word)(&ohdr->oh_sz)
eg's avatar
eg committed
446
        || ohdr -> oh_string == 0) {
447 448 449 450
        GC_err_printf(
                "%s %p in or near object at %p(<smashed>, appr. sz = %lu)\n",
                msg, (void *)clobbered_addr, (void *)p,
                (unsigned long)(GC_size((ptr_t)ohdr) - DEBUG_BYTES));
eg's avatar
eg committed
451
    } else {
452 453 454 455 456 457
        GC_err_printf("%s %p in or near object at %p (%s:%d, sz=%lu)\n",
                msg, (void *)clobbered_addr, (void *)p,
                (word)(ohdr -> oh_string) < HBLKSIZE ? "(smashed string)" :
                ohdr -> oh_string[0] == '\0' ? "EMPTY(smashed?)" :
                                                ohdr -> oh_string,
                GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz));
eg's avatar
eg committed
458 459
        PRINT_CALL_CHAIN(ohdr);
    }
460
  }
eg's avatar
eg committed
461

462 463 464 465 466
  STATIC void GC_check_heap_proc (void);
  STATIC void GC_print_all_smashed_proc (void);
#else
  STATIC void GC_do_nothing(void) {}
#endif
eg's avatar
eg committed
467

468
STATIC void GC_start_debugging_inner(void)
eg's avatar
eg committed
469
{
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
  GC_ASSERT(I_HOLD_LOCK());
# ifndef SHORT_DBG_HDRS
    GC_check_heap = GC_check_heap_proc;
    GC_print_all_smashed = GC_print_all_smashed_proc;
# else
    GC_check_heap = GC_do_nothing;
    GC_print_all_smashed = GC_do_nothing;
# endif
  GC_print_heap_obj = GC_debug_print_heap_obj_proc;
  GC_debugging_started = TRUE;
  GC_register_displacement_inner((word)sizeof(oh));
}

GC_INNER void GC_start_debugging(void)
{
  DCL_LOCK_STATE;

  LOCK();
  GC_start_debugging_inner();
  UNLOCK();
eg's avatar
eg committed
490 491
}

eg's avatar
eg committed
492 493
size_t GC_debug_header_size = sizeof(oh);

494
GC_API void GC_CALL GC_debug_register_displacement(size_t offset)
eg's avatar
eg committed
495
{
496 497 498 499 500 501
  DCL_LOCK_STATE;

  LOCK();
  GC_register_displacement_inner(offset);
  GC_register_displacement_inner((word)sizeof(oh) + offset);
  UNLOCK();
eg's avatar
eg committed
502 503
}

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
#ifdef GC_ADD_CALLER
# if defined(HAVE_DLADDR) && defined(GC_HAVE_RETURN_ADDR_PARENT)
#   include <dlfcn.h>

    STATIC void GC_caller_func_offset(word ad, const char **symp, int *offp)
    {
      Dl_info caller;

      if (ad && dladdr((void *)ad, &caller) && caller.dli_sname != NULL) {
        *symp = caller.dli_sname;
        *offp = (int)((char *)ad - (char *)caller.dli_saddr);
      }
      if (NULL == *symp) {
        *symp = "unknown";
      }
    }
# else
#   define GC_caller_func_offset(ad, symp, offp) (void)(*(symp) = "unknown")
# endif
#endif /* GC_ADD_CALLER */

GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc(size_t lb,
                                                     GC_EXTRA_PARAMS)
eg's avatar
eg committed
527
{
528 529 530 531 532 533 534 535 536 537 538
    void * result;

    /* Note that according to malloc() specification, if size is 0 then */
    /* malloc() returns either NULL, or a unique pointer value that can */
    /* later be successfully passed to free(). We always do the latter. */
    result = GC_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES));
#   ifdef GC_ADD_CALLER
      if (s == NULL) {
        GC_caller_func_offset(ra, &s, &i);
      }
#   endif
eg's avatar
eg committed
539
    if (result == 0) {
540 541
        GC_err_printf("GC_debug_malloc(%lu) returning NULL (%s:%d)\n",
                      (unsigned long)lb, s, i);
eg's avatar
eg committed
542 543 544
        return(0);
    }
    if (!GC_debugging_started) {
545
        GC_start_debugging();
eg's avatar
eg committed
546 547
    }
    ADD_CALL_CHAIN(result, ra);
548
    return (GC_store_debug_info(result, (word)lb, s, i));
eg's avatar
eg committed
549 550
}

551 552
GC_API GC_ATTR_MALLOC void * GC_CALL
    GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS)
eg's avatar
eg committed
553
{
554 555
    void * result = GC_malloc_ignore_off_page(SIZET_SAT_ADD(lb, DEBUG_BYTES));

eg's avatar
eg committed
556
    if (result == 0) {
557 558
        GC_err_printf("GC_debug_malloc_ignore_off_page(%lu)"
                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
eg's avatar
eg committed
559 560 561
        return(0);
    }
    if (!GC_debugging_started) {
562
        GC_start_debugging();
eg's avatar
eg committed
563 564
    }
    ADD_CALL_CHAIN(result, ra);
565
    return (GC_store_debug_info(result, (word)lb, s, i));
eg's avatar
eg committed
566 567
}

568 569
GC_API GC_ATTR_MALLOC void * GC_CALL
    GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS)
eg's avatar
eg committed
570
{
571 572 573
    void * result = GC_malloc_atomic_ignore_off_page(
                                SIZET_SAT_ADD(lb, DEBUG_BYTES));

eg's avatar
eg committed
574 575
    if (result == 0) {
        GC_err_printf("GC_debug_malloc_atomic_ignore_off_page(%lu)"
576
                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
eg's avatar
eg committed
577 578 579
        return(0);
    }
    if (!GC_debugging_started) {
580
        GC_start_debugging();
eg's avatar
eg committed
581 582
    }
    ADD_CALL_CHAIN(result, ra);
583
    return (GC_store_debug_info(result, (word)lb, s, i));
eg's avatar
eg committed
584 585
}

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
STATIC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS)
{
    void * result = GC_generic_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES), knd);

    if (NULL == result) {
        GC_err_printf(
                "GC_debug_generic_malloc(%lu, %d) returning NULL (%s:%d)\n",
                (unsigned long)lb, knd, s, i);
        return NULL;
    }
    if (!GC_debugging_started) {
        GC_start_debugging();
    }
    ADD_CALL_CHAIN(result, ra);
    return GC_store_debug_info(result, (word)lb, s, i);
}

#ifdef DBG_HDRS_ALL
  /* An allocation function for internal use.  Normally internally      */
  /* allocated objects do not have debug information.  But in this      */
  /* case, we need to make sure that all objects have debug headers.    */
  /* We assume debugging was started in collector initialization, and   */
  /* we already hold the GC lock.                                       */
  GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k)
eg's avatar
eg committed
610
  {
611 612 613
    void * result = GC_generic_malloc_inner(
                                SIZET_SAT_ADD(lb, DEBUG_BYTES), k);

eg's avatar
eg committed
614
    if (result == 0) {
615 616
        GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
                       (unsigned long) lb);
eg's avatar
eg committed
617 618
        return(0);
    }
619 620 621
    if (!GC_debugging_started) {
        GC_start_debugging_inner();
    }
eg's avatar
eg committed
622
    ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
623
    return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
eg's avatar
eg committed
624 625
  }

626 627
  GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb,
                                                                int k)
eg's avatar
eg committed
628
  {
eg's avatar
eg committed
629
    void * result = GC_generic_malloc_inner_ignore_off_page(
630 631
                                SIZET_SAT_ADD(lb, DEBUG_BYTES), k);

eg's avatar
eg committed
632
    if (result == 0) {
633 634
        GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
                       (unsigned long) lb);
eg's avatar
eg committed
635 636
        return(0);
    }
637 638 639
    if (!GC_debugging_started) {
        GC_start_debugging_inner();
    }
eg's avatar
eg committed
640
    ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
641
    return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
eg's avatar
eg committed
642
  }
643
#endif /* DBG_HDRS_ALL */
eg's avatar
eg committed
644 645

#ifdef STUBBORN_ALLOC
646 647 648 649 650
  GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_stubborn(size_t lb,
                                                        GC_EXTRA_PARAMS)
  {
    void * result = GC_malloc_stubborn(SIZET_SAT_ADD(lb, DEBUG_BYTES));

eg's avatar
eg committed
651
    if (result == 0) {
652 653
        GC_err_printf("GC_debug_malloc_stubborn(%lu)"
                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
eg's avatar
eg committed
654 655 656
        return(0);
    }
    if (!GC_debugging_started) {
657
        GC_start_debugging();
eg's avatar
eg committed
658 659
    }
    ADD_CALL_CHAIN(result, ra);
660 661
    return (GC_store_debug_info(result, (word)lb, s, i));
  }
eg's avatar
eg committed
662

663 664 665
  GC_API void GC_CALL GC_debug_change_stubborn(const void *p)
  {
    const void * q = GC_base_C(p);
eg's avatar
eg committed
666
    hdr * hhdr;
667

eg's avatar
eg committed
668
    if (q == 0) {
669
        ABORT_ARG1("GC_debug_change_stubborn: bad arg", ": %p", p);
eg's avatar
eg committed
670 671 672
    }
    hhdr = HDR(q);
    if (hhdr -> hb_obj_kind != STUBBORN) {
673
        ABORT_ARG1("GC_debug_change_stubborn: arg not stubborn", ": %p", p);
eg's avatar
eg committed
674 675
    }
    GC_change_stubborn(q);
676 677 678 679 680 681
  }

  GC_API void GC_CALL GC_debug_end_stubborn_change(const void *p)
  {
    const void * q = GC_base_C(p);
    hdr * hhdr;
eg's avatar
eg committed
682 683

    if (q == 0) {
684
        ABORT_ARG1("GC_debug_end_stubborn_change: bad arg", ": %p", p);
eg's avatar
eg committed
685 686 687
    }
    hhdr = HDR(q);
    if (hhdr -> hb_obj_kind != STUBBORN) {
688 689
        ABORT_ARG1("GC_debug_end_stubborn_change: arg not stubborn",
                   ": %p", p);
eg's avatar
eg committed
690 691
    }
    GC_end_stubborn_change(q);
692
  }
eg's avatar
eg committed
693 694 695

#else /* !STUBBORN_ALLOC */

696 697 698
  GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_stubborn(size_t lb,
                                                        GC_EXTRA_PARAMS)
  {
eg's avatar
eg committed
699
    return GC_debug_malloc(lb, OPT_RA s, i);
700
  }
eg's avatar
eg committed
701

702 703
  GC_API void GC_CALL GC_debug_change_stubborn(
                                const void * p GC_ATTR_UNUSED) {}
eg's avatar
eg committed
704

705 706
  GC_API void GC_CALL GC_debug_end_stubborn_change(
                                const void * p GC_ATTR_UNUSED) {}
eg's avatar
eg committed
707 708
#endif /* !STUBBORN_ALLOC */

709 710
GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_atomic(size_t lb,
                                                            GC_EXTRA_PARAMS)
eg's avatar
eg committed
711
{
712 713
    void * result = GC_malloc_atomic(SIZET_SAT_ADD(lb, DEBUG_BYTES));

eg's avatar
eg committed
714
    if (result == 0) {
715 716
        GC_err_printf("GC_debug_malloc_atomic(%lu) returning NULL (%s:%d)\n",
                      (unsigned long)lb, s, i);
eg's avatar
eg committed
717 718 719 720 721 722
        return(0);
    }
    if (!GC_debugging_started) {
        GC_start_debugging();
    }
    ADD_CALL_CHAIN(result, ra);
723
    return (GC_store_debug_info(result, (word)lb, s, i));
eg's avatar
eg committed
724 725
}

726 727
GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strdup(const char *str,
                                                     GC_EXTRA_PARAMS)
728
{
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
  char *copy;
  size_t lb;
  if (str == NULL) {
    if (GC_find_leak)
      GC_err_printf("strdup(NULL) behavior is undefined\n");
    return NULL;
  }

  lb = strlen(str) + 1;
  copy = GC_debug_malloc_atomic(lb, OPT_RA s, i);
  if (copy == NULL) {
#   ifndef MSWINCE
      errno = ENOMEM;
#   endif
    return NULL;
  }
  BCOPY(str, copy, lb);
  return copy;
}

GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strndup(const char *str,
                                                size_t size, GC_EXTRA_PARAMS)
{
  char *copy;
  size_t len = strlen(str); /* str is expected to be non-NULL  */
  if (len > size)
    len = size;
  copy = GC_debug_malloc_atomic(len + 1, OPT_RA s, i);
  if (copy == NULL) {
#   ifndef MSWINCE
759
      errno = ENOMEM;
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
#   endif
    return NULL;
  }
  BCOPY(str, copy, len);
  copy[len] = '\0';
  return copy;
}

#ifdef GC_REQUIRE_WCSDUP
# include <wchar.h> /* for wcslen() */

  GC_API GC_ATTR_MALLOC wchar_t * GC_CALL GC_debug_wcsdup(const wchar_t *str,
                                                          GC_EXTRA_PARAMS)
  {
    size_t lb = (wcslen(str) + 1) * sizeof(wchar_t);
    wchar_t *copy = GC_debug_malloc_atomic(lb, OPT_RA s, i);
    if (copy == NULL) {
#     ifndef MSWINCE
        errno = ENOMEM;
#     endif
780 781
      return NULL;
    }
782
    BCOPY(str, copy, lb);
783
    return copy;
784 785
  }
#endif /* GC_REQUIRE_WCSDUP */
786

787 788
GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_uncollectable(size_t lb,
                                                        GC_EXTRA_PARAMS)
eg's avatar
eg committed
789
{
790 791 792
    void * result = GC_malloc_uncollectable(
                                SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES));

eg's avatar
eg committed
793
    if (result == 0) {
794 795
        GC_err_printf("GC_debug_malloc_uncollectable(%lu)"
                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
eg's avatar
eg committed
796 797 798 799 800 801
        return(0);
    }
    if (!GC_debugging_started) {
        GC_start_debugging();
    }
    ADD_CALL_CHAIN(result, ra);
802
    return (GC_store_debug_info(result, (word)lb, s, i));
eg's avatar
eg committed
803 804
}

805 806 807 808 809 810 811
#ifdef GC_ATOMIC_UNCOLLECTABLE
  GC_API GC_ATTR_MALLOC void * GC_CALL
        GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS)
  {
    void * result = GC_malloc_atomic_uncollectable(
                                SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES));

eg's avatar
eg committed
812
    if (result == 0) {
813 814
        GC_err_printf("GC_debug_malloc_atomic_uncollectable(%lu)"
                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
eg's avatar
eg committed
815 816 817 818 819 820
        return(0);
    }
    if (!GC_debugging_started) {
        GC_start_debugging();
    }
    ADD_CALL_CHAIN(result, ra);
821 822 823 824 825 826 827 828 829 830 831
    return (GC_store_debug_info(result, (word)lb, s, i));
  }
#endif /* GC_ATOMIC_UNCOLLECTABLE */

#ifndef GC_FREED_MEM_MARKER
# if CPP_WORDSZ == 32
#   define GC_FREED_MEM_MARKER 0xdeadbeef
# else
#   define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef)
# endif
#endif
eg's avatar
eg committed
832

833
GC_API void GC_CALL GC_debug_free(void * p)
eg's avatar
eg committed
834
{
eg's avatar
eg committed
835
    ptr_t base;
eg's avatar
eg committed
836
    if (0 == p) return;
837

eg's avatar
eg committed
838 839
    base = GC_base(p);
    if (base == 0) {
840 841 842 843 844 845 846 847 848
#     if defined(REDIRECT_MALLOC) \
         && ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \
             || defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS) \
             || defined(MSWIN32))
        /* In some cases, we should ignore objects that do not belong   */
        /* to the GC heap.  See the comment in GC_free.                 */
        if (!GC_is_heap_ptr(p)) return;
#     endif
      ABORT_ARG1("Invalid pointer passed to free()", ": %p", p);
eg's avatar
eg committed
849 850
    }
    if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
851 852
      GC_err_printf(
               "GC_debug_free called on pointer %p w/o debugging info\n", p);
eg's avatar
eg committed
853 854
    } else {
#     ifndef SHORT_DBG_HDRS
855 856
        ptr_t clobbered = GC_check_annotated_obj((oh *)base);
        word sz = GC_size(base);
eg's avatar
eg committed
857
        if (clobbered != 0) {
858 859 860 861 862 863
          GC_have_errors = TRUE;
          if (((oh *)base) -> oh_sz == sz) {
            GC_print_smashed_obj(
                  "GC_debug_free: found previously deallocated (?) object at",
                  p, clobbered);
            return; /* ignore double free */
eg's avatar
eg committed
864
          } else {
865 866
            GC_print_smashed_obj("GC_debug_free: found smashed location at",
                                 p, clobbered);
eg's avatar
eg committed
867 868
          }
        }
869 870
        /* Invalidate size (mark the object as deallocated) */
        ((oh *)base) -> oh_sz = sz;
eg's avatar
eg committed
871 872
#     endif /* SHORT_DBG_HDRS */
    }
873 874 875 876 877 878
    if (GC_find_leak
#       ifndef SHORT_DBG_HDRS
          && ((ptr_t)p - (ptr_t)base != sizeof(oh) || !GC_findleak_delay_free)
#       endif
        ) {
      GC_free(base);
eg's avatar
eg committed
879
    } else {
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
      hdr * hhdr = HDR(p);
      if (hhdr -> hb_obj_kind == UNCOLLECTABLE
#         ifdef GC_ATOMIC_UNCOLLECTABLE
            || hhdr -> hb_obj_kind == AUNCOLLECTABLE
#         endif
          ) {
        GC_free(base);
      } else {
        size_t i;
        size_t obj_sz = BYTES_TO_WORDS(hhdr -> hb_sz - sizeof(oh));

        for (i = 0; i < obj_sz; ++i)
          ((word *)p)[i] = GC_FREED_MEM_MARKER;
        GC_ASSERT((word *)p + i == (word *)(base + hhdr -> hb_sz));
      }
eg's avatar
eg committed
895 896 897
    } /* !GC_find_leak */
}

898 899 900 901
#if defined(THREADS) && defined(DBG_HDRS_ALL)
  /* Used internally; we assume it's called correctly.    */
  GC_INNER void GC_debug_free_inner(void * p)
  {
902 903
    ptr_t base = GC_base(p);
    GC_ASSERT((ptr_t)p - (ptr_t)base == sizeof(oh));
904 905 906
#   ifdef LINT2
      if (!base) ABORT("Invalid GC_debug_free_inner argument");
#   endif
907
#   ifndef SHORT_DBG_HDRS
908 909
      /* Invalidate size */
      ((oh *)base) -> oh_sz = GC_size(base);
910 911
#   endif
    GC_free_inner(base);
912
  }
eg's avatar
eg committed
913 914
#endif

915
GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS)
eg's avatar
eg committed
916
{
917
    void * base;
eg's avatar
eg committed
918 919
    void * result;
    hdr * hhdr;
920 921 922 923 924 925 926 927 928 929 930 931 932 933

    if (p == 0) {
      return GC_debug_malloc(lb, OPT_RA s, i);
    }
    if (0 == lb) /* and p != NULL */ {
      GC_debug_free(p);
      return NULL;
    }

#   ifdef GC_ADD_CALLER
      if (s == NULL) {
        GC_caller_func_offset(ra, &s, &i);
      }
#   endif
934
    base = GC_base(p);
eg's avatar
eg committed
935
    if (base == 0) {
936
        ABORT_ARG1("Invalid pointer passed to realloc()", ": %p", p);
eg's avatar
eg committed
937 938
    }
    if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
eg's avatar
eg committed
939
        GC_err_printf(
940
              "GC_debug_realloc called on pointer %p w/o debugging info\n", p);
eg's avatar
eg committed
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956
        return(GC_realloc(p, lb));
    }
    hhdr = HDR(base);
    switch (hhdr -> hb_obj_kind) {
#    ifdef STUBBORN_ALLOC
      case STUBBORN:
        result = GC_debug_malloc_stubborn(lb, OPT_RA s, i);
        break;
#    endif
      case NORMAL:
        result = GC_debug_malloc(lb, OPT_RA s, i);
        break;
      case PTRFREE:
        result = GC_debug_malloc_atomic(lb, OPT_RA s, i);
        break;
      case UNCOLLECTABLE:
957 958 959
        result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i);
        break;
#    ifdef GC_ATOMIC_UNCOLLECTABLE
eg's avatar
eg committed
960
      case AUNCOLLECTABLE:
961 962
        result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i);
        break;
eg's avatar
eg committed
963 964
#    endif
      default:
965
        result = NULL; /* initialized to prevent warning. */
966 967 968 969 970 971 972 973 974 975 976 977
        ABORT_RET("GC_debug_realloc: encountered bad kind");
    }

    if (result != NULL) {
      size_t old_sz;
#     ifdef SHORT_DBG_HDRS
        old_sz = GC_size(base) - sizeof(oh);
#     else
        old_sz = ((oh *)base) -> oh_sz;
#     endif
      BCOPY(p, result, old_sz < lb ? old_sz : lb);
      GC_debug_free(p);
eg's avatar
eg committed
978 979 980 981
    }
    return(result);
}

982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
GC_API GC_ATTR_MALLOC void * GC_CALL
    GC_debug_generic_or_special_malloc(size_t lb, int knd, GC_EXTRA_PARAMS)
{
    switch (knd) {
#     ifdef STUBBORN_ALLOC
        case STUBBORN:
            return GC_debug_malloc_stubborn(lb, OPT_RA s, i);
#     endif
        case PTRFREE:
            return GC_debug_malloc_atomic(lb, OPT_RA s, i);
        case NORMAL:
            return GC_debug_malloc(lb, OPT_RA s, i);
        case UNCOLLECTABLE:
            return GC_debug_malloc_uncollectable(lb, OPT_RA s, i);
#     ifdef GC_ATOMIC_UNCOLLECTABLE
        case AUNCOLLECTABLE:
            return GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i);
#     endif
        default:
            return GC_debug_generic_malloc(lb, knd, OPT_RA s, i);
    }
}

eg's avatar
eg committed
1005 1006
#ifndef SHORT_DBG_HDRS

1007 1008 1009 1010 1011 1012 1013 1014 1015
/* List of smashed (clobbered) locations.  We defer printing these,     */
/* since we can't always print them nicely with the allocation lock     */
/* held.  We put them here instead of in GC_arrays, since it may be     */
/* useful to be able to look at them with the debugger.                 */
#ifndef MAX_SMASHED
# define MAX_SMASHED 20
#endif
STATIC ptr_t GC_smashed[MAX_SMASHED] = {0};
STATIC unsigned GC_n_smashed = 0;
eg's avatar
eg committed
1016

1017
STATIC void GC_add_smashed(ptr_t smashed)
eg's avatar
eg committed
1018 1019
{
    GC_ASSERT(GC_is_marked(GC_base(smashed)));
1020
    /* FIXME: Prevent adding an object while printing smashed list.     */
eg's avatar
eg committed
1021 1022
    GC_smashed[GC_n_smashed] = smashed;
    if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed;
1023 1024
      /* In case of overflow, we keep the first MAX_SMASHED-1   */
      /* entries plus the last one.                             */
eg's avatar
eg committed
1025 1026 1027
    GC_have_errors = TRUE;
}

1028
/* Print all objects on the list.  Clear the list.      */
1029
STATIC void GC_print_all_smashed_proc(void)
eg's avatar
eg committed
1030 1031 1032
{
    unsigned i;

1033
    GC_ASSERT(I_DONT_HOLD_LOCK());
eg's avatar
eg committed
1034
    if (GC_n_smashed == 0) return;
1035 1036
    GC_err_printf("GC_check_heap_block: found %u smashed heap objects:\n",
                  GC_n_smashed);
eg's avatar
eg committed
1037
    for (i = 0; i < GC_n_smashed; ++i) {
1038 1039 1040 1041 1042 1043 1044
        ptr_t base = (ptr_t)GC_base(GC_smashed[i]);

#       ifdef LINT2
          if (!base) ABORT("Invalid GC_smashed element");
#       endif
        GC_print_smashed_obj("", base + sizeof(oh), GC_smashed[i]);
        GC_smashed[i] = 0;
eg's avatar
eg committed
1045 1046 1047 1048
    }
    GC_n_smashed = 0;
}

1049 1050 1051
/* Check all marked objects in the given block for validity     */
/* Avoid GC_apply_to_each_object for performance reasons.       */
STATIC void GC_check_heap_block(struct hblk *hbp, word dummy GC_ATTR_UNUSED)
eg's avatar
eg committed
1052
{
eg's avatar
eg committed
1053 1054
    struct hblkhdr * hhdr = HDR(hbp);
    size_t sz = hhdr -> hb_sz;
1055
    size_t bit_no;
eg's avatar
eg committed
1056
    char *p, *plim;
1057

eg's avatar
eg committed
1058 1059
    p = hbp->hb_body;
    if (sz > MAXOBJBYTES) {
1060
      plim = p;
eg's avatar
eg committed
1061
    } else {
1062
      plim = hbp->hb_body + HBLKSIZE - sz;
eg's avatar
eg committed
1063 1064
    }
    /* go through all words in block */
1065 1066 1067 1068 1069 1070 1071 1072
    for (bit_no = 0; (word)p <= (word)plim;
         bit_no += MARK_BIT_OFFSET(sz), p += sz) {
      if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO((ptr_t)p)) {
        ptr_t clobbered = GC_check_annotated_obj((oh *)p);
        if (clobbered != 0)
          GC_add_smashed(clobbered);
      }
    }
eg's avatar
eg committed
1073 1074
}

1075 1076
/* This assumes that all accessible objects are marked, and that        */
/* I hold the allocation lock.  Normally called by collector.           */
1077
STATIC void GC_check_heap_proc(void)
eg's avatar
eg committed
1078
{
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
  GC_STATIC_ASSERT((sizeof(oh) & (GRANULE_BYTES - 1)) == 0);
  /* FIXME: Should we check for twice that alignment?   */
  GC_apply_to_all_blocks(GC_check_heap_block, 0);
}

GC_INNER GC_bool GC_check_leaked(ptr_t base)
{
  size_t i;
  size_t obj_sz;
  word *p;

  if (
#     if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
        (*(word *)base & 1) != 0 &&
#     endif
      GC_has_other_debug_info(base) >= 0)
    return TRUE; /* object has leaked */

  /* Validate freed object's content. */
  p = (word *)(base + sizeof(oh));
  obj_sz = BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh));
  for (i = 0; i < obj_sz; ++i)
    if (p[i] != GC_FREED_MEM_MARKER) {
        GC_set_mark_bit(base); /* do not reclaim it in this cycle */
        GC_add_smashed((ptr_t)(&p[i])); /* alter-after-free detected */
        break; /* don't report any other smashed locations in the object */
    }

  return FALSE; /* GC_debug_free() has been called */
eg's avatar
eg committed
1108 1109 1110 1111
}

#endif /* !SHORT_DBG_HDRS */

1112 1113
#ifndef GC_NO_FINALIZATION

eg's avatar
eg committed
1114 1115
struct closure {
    GC_finalization_proc cl_fn;
eg's avatar
eg committed
1116
    void * cl_data;
eg's avatar
eg committed
1117 1118
};

1119
STATIC void * GC_make_closure(GC_finalization_proc fn, void * data)
eg's avatar
eg committed
1120 1121
{
    struct closure * result =
eg's avatar
eg committed
1122 1123
#   ifdef DBG_HDRS_ALL
      (struct closure *) GC_debug_malloc(sizeof (struct closure),
1124
                                         GC_EXTRAS);
eg's avatar
eg committed
1125 1126 1127
#   else
      (struct closure *) GC_malloc(sizeof (struct closure));
#   endif
1128 1129 1130 1131
    if (result != 0) {
      result -> cl_fn = fn;
      result -> cl_data = data;
    }
eg's avatar
eg committed
1132
    return((void *)result);
eg's avatar
eg committed
1133 1134
}

1135 1136 1137
/* An auxiliary fns to make finalization work correctly with displaced  */
/* pointers introduced by the debugging allocators.                     */
STATIC void GC_CALLBACK GC_debug_invoke_finalizer(void * obj, void * data)
eg's avatar
eg committed
1138
{
1139
    struct closure * cl = (struct closure *) data;
eg's avatar
eg committed
1140
    (*(cl -> cl_fn))((void *)((char *)obj + sizeof(oh)), cl -> cl_data);
1141
}
eg's avatar
eg committed
1142

1143 1144 1145 1146 1147 1148 1149
/* Special finalizer_proc value to detect GC_register_finalizer() failure. */
#define OFN_UNSET ((GC_finalization_proc)~(signed_word)0)

/* Set ofn and ocd to reflect the values we got back.   */
static void store_old(void *obj, GC_finalization_proc my_old_fn,
                      struct closure *my_old_cd, GC_finalization_proc *ofn,
                      void **ocd)
eg's avatar
eg committed
1150 1151
{
    if (0 != my_old_fn) {
1152 1153 1154 1155
      if (my_old_fn == OFN_UNSET) {
        /* register_finalizer() failed; (*ofn) and (*ocd) are unchanged. */
        return;
      }
eg's avatar
eg committed
1156
      if (my_old_fn != GC_debug_invoke_finalizer) {
1157 1158
        GC_err_printf("Debuggable object at %p had a non-debug finalizer\n",
                      obj);
eg's avatar
eg committed
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
        /* This should probably be fatal. */
      } else {
        if (ofn) *ofn = my_old_cd -> cl_fn;
        if (ocd) *ocd = my_old_cd -> cl_data;
      }
    } else {
      if (ofn) *ofn = 0;
      if (ocd) *ocd = 0;
    }
}

1170
GC_API void GC_CALL GC_debug_register_finalizer(void * obj,
1171 1172 1173
                                        GC_finalization_proc fn,
                                        void * cd, GC_finalization_proc *ofn,
                                        void * *ocd)
eg's avatar
eg committed
1174
{
1175
    GC_finalization_proc my_old_fn = OFN_UNSET;
eg's avatar
eg committed
1176
    void * my_old_cd;
eg's avatar
eg committed
1177
    ptr_t base = GC_base(obj);
1178 1179 1180 1181 1182 1183
    if (0 == base) {
        /* We won't collect it, hence finalizer wouldn't be run. */
        if (ocd) *ocd = 0;
        if (ofn) *ofn = 0;
        return;
    }
eg's avatar
eg committed
1184
    if ((ptr_t)obj - base != sizeof(oh)) {
1185 1186
        GC_err_printf("GC_debug_register_finalizer called with"
                      " non-base-pointer %p\n", obj);
eg's avatar
eg committed
1187 1188 1189 1190
    }
    if (0 == fn) {
      GC_register_finalizer(base, 0, 0, &my_old_fn, &my_old_cd);
    } else {
1191 1192
      cd = GC_make_closure(fn, cd);
      if (cd == 0) return; /* out of memory */
eg's avatar
eg committed
1193
      GC_register_finalizer(base, GC_debug_invoke_finalizer,
1194
                            cd, &my_old_fn, &my_old_cd);
eg's avatar
eg committed
1195 1196 1197 1198
    }
    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
}

1199
GC_API void GC_CALL GC_debug_register_finalizer_no_order
1200 1201 1202
                                    (void * obj, GC_finalization_proc fn,
                                     void * cd, GC_finalization_proc *ofn,
                                     void * *ocd)
eg's avatar
eg committed
1203
{
1204
    GC_finalization_proc my_old_fn = OFN_UNSET;
eg's avatar
eg committed
1205
    void * my_old_cd;
eg's avatar
eg committed
1206
    ptr_t base = GC_base(obj);
1207 1208 1209 1210 1211 1212
    if (0 == base) {
        /* We won't collect it, hence finalizer wouldn't be run. */
        if (ocd) *ocd = 0;
        if (ofn) *ofn = 0;
        return;
    }
eg's avatar
eg committed
1213
    if ((ptr_t)obj - base != sizeof(oh)) {
1214 1215
        GC_err_printf("GC_debug_register_finalizer_no_order called with"
                      " non-base-pointer %p\n", obj);
eg's avatar
eg committed
1216 1217 1218 1219
    }
    if (0 == fn) {
      GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd);
    } else {
1220 1221
      cd = GC_make_closure(fn, cd);
      if (cd == 0) return; /* out of memory */
eg's avatar
eg committed
1222
      GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer,
1223
                                     cd, &my_old_fn, &my_old_cd);
eg's avatar
eg committed
1224 1225
    }
    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1226 1227
}

1228
GC_API void GC_CALL GC_debug_register_finalizer_unreachable
1229 1230 1231
                                    (void * obj, GC_finalization_proc fn,
                                     void * cd, GC_finalization_proc *ofn,
                                     void * *ocd)
1232
{
1233
    GC_finalization_proc my_old_fn = OFN_UNSET;
1234 1235
    void * my_old_cd;
    ptr_t base = GC_base(obj);
1236 1237 1238 1239 1240 1241
    if (0 == base) {
        /* We won't collect it, hence finalizer wouldn't be run. */
        if (ocd) *ocd = 0;
        if (ofn) *ofn = 0;
        return;
    }
1242
    if ((ptr_t)obj - base != sizeof(oh)) {
1243 1244
        GC_err_printf("GC_debug_register_finalizer_unreachable called with"
                      " non-base-pointer %p\n", obj);
1245 1246 1247 1248
    }
    if (0 == fn) {
      GC_register_finalizer_unreachable(base, 0, 0, &my_old_fn, &my_old_cd);
    } else {
1249 1250
      cd = GC_make_closure(fn, cd);
      if (cd == 0) return; /* out of memory */
1251
      GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer,
1252
                                        cd, &my_old_fn, &my_old_cd);
1253 1254 1255
    }
    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
}
eg's avatar
eg committed
1256

1257
GC_API void GC_CALL GC_debug_register_finalizer_ignore_self
1258 1259 1260
                                    (void * obj, GC_finalization_proc fn,
                                     void * cd, GC_finalization_proc *ofn,
                                     void * *ocd)
eg's avatar
eg committed
1261
{
1262
    GC_finalization_proc my_old_fn = OFN_UNSET;
eg's avatar
eg committed
1263
    void * my_old_cd;
eg's avatar
eg committed
1264
    ptr_t base = GC_base(obj);
1265 1266 1267 1268 1269 1270
    if (0 == base) {
        /* We won't collect it, hence finalizer wouldn't be run. */
        if (ocd) *ocd = 0;
        if (ofn) *ofn = 0;
        return;
    }
eg's avatar
eg committed
1271
    if ((ptr_t)obj - base != sizeof(oh)) {
1272 1273
        GC_err_printf("GC_debug_register_finalizer_ignore_self called with"
                      " non-base-pointer %p\n", obj);
eg's avatar
eg committed
1274 1275 1276 1277
    }
    if (0 == fn) {
      GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd);
    } else {
1278 1279
      cd = GC_make_closure(fn, cd);
      if (cd == 0) return; /* out of memory */
eg's avatar
eg committed
1280
      GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer,
1281
                                        cd, &my_old_fn, &my_old_cd);
eg's avatar
eg committed
1282 1283 1284 1285
    }
    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
}

1286
#endif /* !GC_NO_FINALIZATION */
eg's avatar
eg committed
1287

1288
GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_replacement(size_t lb)
eg's avatar
eg committed
1289
{
1290
    return GC_debug_malloc(lb, GC_DBG_EXTRAS);
eg's avatar
eg committed
1291 1292
}

1293
GC_API void * GC_CALL GC_debug_realloc_replacement(void *p, size_t lb)
eg's avatar
eg committed
1294
{
1295
    return GC_debug_realloc(p, lb, GC_DBG_EXTRAS);
eg's avatar
eg committed
1296
}