(linenum→info "unix/slp.c:2238")

gauche/0.8.12/gc/dbg_mlc.c

    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:  *
    7:  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
    8:  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
    9:  *
   10:  * Permission is hereby granted to use or copy this program
   11:  * for any purpose,  provided the above notices are retained on all copies.
   12:  * Permission to modify the code and to distribute modified code is granted,
   13:  * provided the above notices are retained, and a notice that the code was
   14:  * modified is included with the above copyright notice.
   15:  */
   16: 
   17: #include <errno.h>
   18: #include <string.h>
   19: #include "private/dbg_mlc.h"
   20: 
   21: void GC_default_print_heap_obj_proc();
   22: GC_API void GC_register_finalizer_no_order
   23:         GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
   24:                   GC_finalization_proc *ofn, GC_PTR *ocd));
   25: 
   26: 
   27: #ifndef SHORT_DBG_HDRS
   28: /* Check whether object with base pointer p has debugging info  */ 
   29: /* p is assumed to point to a legitimate object in our part     */
   30: /* of the heap.                                                 */
   31: /* This excludes the check as to whether the back pointer is    */
   32: /* odd, which is added by the GC_HAS_DEBUG_INFO macro.          */
   33: /* Note that if DBG_HDRS_ALL is set, uncollectable objects      */
   34: /* on free lists may not have debug information set.  Thus it's */
   35: /* not always safe to return TRUE, even if the client does      */
   36: /* its part.                                                    */
   37: GC_bool GC_has_other_debug_info(p)
   38: ptr_t p;
   39: {
   40:     register oh * ohdr = (oh *)p;
   41:     register ptr_t body = (ptr_t)(ohdr + 1);
   42:     register word sz = GC_size((ptr_t) ohdr);
   43:     
   44:     if (HBLKPTR((ptr_t)ohdr) != HBLKPTR((ptr_t)body)
   45:         || sz < DEBUG_BYTES + EXTRA_BYTES) {
   46:         return(FALSE);
   47:     }
   48:     if (ohdr -> oh_sz == sz) {
   49:         /* Object may have had debug info, but has been deallocated        */
   50:         return(FALSE);
   51:     }
   52:     if (ohdr -> oh_sf == (START_FLAG ^ (word)body)) return(TRUE);
   53:     if (((word *)ohdr)[BYTES_TO_WORDS(sz)-1] == (END_FLAG ^ (word)body)) {
   54:         return(TRUE);
   55:     }
   56:     return(FALSE);
   57: }
   58: #endif
   59: 
   60: #ifdef KEEP_BACK_PTRS
   61: 
   62: # include <stdlib.h>
   63: 
   64: # if defined(LINUX) || defined(SUNOS4) || defined(SUNOS5) \
   65:      || defined(HPUX) || defined(IRIX5) || defined(OSF1)
   66: #   define RANDOM() random()
   67: # else
   68: #   define RANDOM() (long)rand()
   69: # endif
   70: 
   71:   /* Store back pointer to source in dest, if that appears to be possible. */
   72:   /* This is not completely safe, since we may mistakenly conclude that    */
   73:   /* dest has a debugging wrapper.  But the error probability is very      */
   74:   /* small, and this shouldn't be used in production code.                 */
   75:   /* We assume that dest is the real base pointer.  Source will usually    */
   76:   /* be a pointer to the interior of an object.                            */
   77:   void GC_store_back_pointer(ptr_t source, ptr_t dest)
   78:   {
   79:     if (GC_HAS_DEBUG_INFO(dest)) {
   80:       ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source);
   81:     }
   82:   }
   83: 
   84:   void GC_marked_for_finalization(ptr_t dest) {
   85:     GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest);
   86:   }
   87: 
   88:   /* Store information about the object referencing dest in *base_p     */
   89:   /* and *offset_p.                                                     */
   90:   /*   source is root ==> *base_p = address, *offset_p = 0              */
   91:   /*   source is heap object ==> *base_p != 0, *offset_p = offset       */
   92:   /*   Returns 1 on success, 0 if source couldn't be determined.        */
   93:   /* Dest can be any address within a heap object.                      */
   94:   GC_ref_kind GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p)
   95:   {
   96:     oh * hdr = (oh *)GC_base(dest);
   97:     ptr_t bp;
   98:     ptr_t bp_base;
   99:     if (!GC_HAS_DEBUG_INFO((ptr_t) hdr)) return GC_NO_SPACE;
  100:     bp = REVEAL_POINTER(hdr -> oh_back_ptr);
  101:     if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD;
  102:     if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG;
  103:     if (NOT_MARKED == bp) return GC_UNREFERENCED;
  104: #   if ALIGNMENT == 1
  105:       /* Heuristically try to fix off by 1 errors we introduced by      */
  106:       /* insisting on even addresses.                                   */
  107:       {
  108:         ptr_t alternate_ptr = bp + 1;
  109:         ptr_t target = *(ptr_t *)bp;
  110:         ptr_t alternate_target = *(ptr_t *)alternate_ptr;
  111: 
  112:         if (alternate_target >= GC_least_plausible_heap_addr
  113:             && alternate_target <= GC_greatest_plausible_heap_addr
  114:             && (target < GC_least_plausible_heap_addr
  115:                 || target > GC_greatest_plausible_heap_addr)) {
  116:             bp = alternate_ptr;
  117:         }
  118:       }
  119: #   endif
  120:     bp_base = GC_base(bp);
  121:     if (0 == bp_base) {
  122:       *base_p = bp;
  123:       *offset_p = 0;
  124:       return GC_REFD_FROM_ROOT;
  125:     } else {
  126:       if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh);
  127:       *base_p = bp_base;
  128:       *offset_p = bp - bp_base;
  129:       return GC_REFD_FROM_HEAP;
  130:     }
  131:   }
  132: 
  133:   /* Generate a random heap address.            */
  134:   /* The resulting address is in the heap, but  */
  135:   /* not necessarily inside a valid object.     */
  136:   void *GC_generate_random_heap_address(void)
  137:   {
  138:     int i;
  139:     long heap_offset = RANDOM();
  140:     if (GC_heapsize > RAND_MAX) {
  141:         heap_offset *= RAND_MAX;
  142:         heap_offset += RANDOM();
  143:     }
  144:     heap_offset %= GC_heapsize;
  145:         /* This doesn't yield a uniform distribution, especially if        */
  146:         /* e.g. RAND_MAX = 1.5* GC_heapsize.  But for typical cases,    */
  147:         /* it's not too bad.                                            */
  148:     for (i = 0; i < GC_n_heap_sects; ++ i) {
  149:         int size = GC_heap_sects[i].hs_bytes;
  150:         if (heap_offset < size) {
  151:             return GC_heap_sects[i].hs_start + heap_offset;
  152:         } else {
  153:             heap_offset -= size;
  154:         }
  155:     }
  156:     ABORT("GC_generate_random_heap_address: size inconsistency");
  157:     /*NOTREACHED*/
  158:     return 0;
  159:   }
  160: 
  161:   /* Generate a random address inside a valid marked heap object. */
  162:   void *GC_generate_random_valid_address(void)
  163:   {
  164:     ptr_t result;
  165:     ptr_t base;
  166:     for (;;) {
  167:         result = GC_generate_random_heap_address();
  168:         base = GC_base(result);
  169:         if (0 == base) continue;
  170:         if (!GC_is_marked(base)) continue;
  171:         return result;
  172:     }
  173:   }
  174: 
  175:   /* Print back trace for p */
  176:   void GC_print_backtrace(void *p)
  177:   {
  178:     void *current = p;
  179:     int i;
  180:     GC_ref_kind source;
  181:     size_t offset;
  182:     void *base;
  183: 
  184:     GC_print_heap_obj(GC_base(current));
  185:     GC_err_printf0("\n");
  186:     for (i = 0; ; ++i) {
  187:       source = GC_get_back_ptr_info(current, &base, &offset);
  188:       if (GC_UNREFERENCED == source) {
  189:         GC_err_printf0("Reference could not be found\n");
  190:         goto out;
  191:       }
  192:       if (GC_NO_SPACE == source) {
  193:         GC_err_printf0("No debug info in object: Can't find reference\n");
  194:         goto out;
  195:       }
  196:       GC_err_printf1("Reachable via %d levels of pointers from ",
  197:                  (unsigned long)i);
  198:       switch(source) {
  199:         case GC_REFD_FROM_ROOT:
  200:           GC_err_printf1("root at 0x%lx\n\n", (unsigned long)base);
  201:           goto out;
  202:         case GC_REFD_FROM_REG:
  203:           GC_err_printf0("root in register\n\n");
  204:           goto out;
  205:         case GC_FINALIZER_REFD:
  206:           GC_err_printf0("list of finalizable objects\n\n");
  207:           goto out;
  208:         case GC_REFD_FROM_HEAP:
  209:           GC_err_printf1("offset %ld in object:\n", (unsigned long)offset);
  210:           /* Take GC_base(base) to get real base, i.e. header. */
  211:           GC_print_heap_obj(GC_base(base));
  212:           GC_err_printf0("\n");
  213:           break;
  214:       }
  215:       current = base;
  216:     }
  217:     out:;
  218:   }
  219: 
  220:   /* Force a garbage collection and generate a backtrace from a */
  221:   /* random heap address.                                       */
  222:   void GC_generate_random_backtrace_no_gc(void)
  223:   {
  224:     void * current;
  225:     current = GC_generate_random_valid_address();
  226:     GC_printf1("\n****Chose address 0x%lx in object\n", (unsigned long)current);
  227:     GC_print_backtrace(current);
  228:   }
  229:     
  230:   void GC_generate_random_backtrace(void)
  231:   {
  232:     GC_gcollect();
  233:     GC_generate_random_backtrace_no_gc();
  234:   }
  235:     
  236: #endif /* KEEP_BACK_PTRS */
  237: 
  238: # define CROSSES_HBLK(p, sz) \
  239:         (((word)(p + sizeof(oh) + sz - 1) ^ (word)p) >= HBLKSIZE)
  240: /* Store debugging info into p.  Return displaced pointer. */
  241: /* Assumes we don't hold allocation lock.                  */
  242: ptr_t GC_store_debug_info(p, sz, string, integer)
  243: register ptr_t p;       /* base pointer */
  244: word sz;        /* bytes */
  245: GC_CONST char * string;
  246: word integer;
  247: {
  248:     register word * result = (word *)((oh *)p + 1);
  249:     DCL_LOCK_STATE;
  250:     
  251:     /* There is some argument that we should dissble signals here.      */
  252:     /* But that's expensive.  And this way things should only appear    */
  253:     /* inconsistent while we're in the handler.                         */
  254:     LOCK();
  255:     GC_ASSERT(GC_size(p) >= sizeof(oh) + sz);
  256:     GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz)));
  257: #   ifdef KEEP_BACK_PTRS
  258:       ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED);
  259: #   endif
  260: #   ifdef MAKE_BACK_GRAPH
  261:       ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0);
  262: #   endif
  263:     ((oh *)p) -> oh_string = string;
  264:     ((oh *)p) -> oh_int = integer;
  265: #   ifndef SHORT_DBG_HDRS
  266:       ((oh *)p) -> oh_sz = sz;
  267:       ((oh *)p) -> oh_sf = START_FLAG ^ (word)result;
  268:       ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] =
  269:          result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result;
  270: #   endif
  271:     UNLOCK();
  272:     return((ptr_t)result);
  273: }
  274: 
  275: #ifdef DBG_HDRS_ALL
  276: /* Store debugging info into p.  Return displaced pointer.         */
  277: /* This version assumes we do hold the allocation lock.            */
  278: ptr_t GC_store_debug_info_inner(p, sz, string, integer)
  279: register ptr_t p;       /* base pointer */
  280: word sz;        /* bytes */
  281: char * string;
  282: word integer;
  283: {
  284:     register word * result = (word *)((oh *)p + 1);
  285:     
  286:     /* There is some argument that we should disable signals here.      */
  287:     /* But that's expensive.  And this way things should only appear    */
  288:     /* inconsistent while we're in the handler.                         */
  289:     GC_ASSERT(GC_size(p) >= sizeof(oh) + sz);
  290:     GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz)));
  291: #   ifdef KEEP_BACK_PTRS
  292:       ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED);
  293: #   endif
  294: #   ifdef MAKE_BACK_GRAPH
  295:       ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0);
  296: #   endif
  297:     ((oh *)p) -> oh_string = string;
  298:     ((oh *)p) -> oh_int = integer;
  299: #   ifndef SHORT_DBG_HDRS
  300:       ((oh *)p) -> oh_sz = sz;
  301:       ((oh *)p) -> oh_sf = START_FLAG ^ (word)result;
  302:       ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] =
  303:          result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result;
  304: #   endif
  305:     return((ptr_t)result);
  306: }
  307: #endif
  308: 
  309: #ifndef SHORT_DBG_HDRS
  310: /* Check the object with debugging info at ohdr         */
  311: /* return NIL if it's OK.  Else return clobbered        */
  312: /* address.                                             */
  313: ptr_t GC_check_annotated_obj(ohdr)
  314: register oh * ohdr;
  315: {
  316:     register ptr_t body = (ptr_t)(ohdr + 1);
  317:     register word gc_sz = GC_size((ptr_t)ohdr);
  318:     if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) {
  319:         return((ptr_t)(&(ohdr -> oh_sz)));
  320:     }
  321:     if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) {
  322:         return((ptr_t)(&(ohdr -> oh_sf)));
  323:     }
  324:     if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) {
  325:         return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1));
  326:     }
  327:     if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)]
  328:         != (END_FLAG ^ (word)body)) {
  329:         return((ptr_t)((word *)body + SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)));
  330:     }
  331:     return(0);
  332: }
  333: #endif /* !SHORT_DBG_HDRS */
  334: 
  335: static GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0};
  336: 
  337: void GC_register_describe_type_fn(kind, fn)
  338: int kind;
  339: GC_describe_type_fn fn;
  340: {
  341:   GC_describe_type_fns[kind] = fn;
  342: }
  343: 
  344: /* Print a type description for the object whose client-visible address */
  345: /* is p.                                                                */
  346: void GC_print_type(p)
  347: ptr_t p;
  348: {
  349:     hdr * hhdr = GC_find_header(p);
  350:     char buffer[GC_TYPE_DESCR_LEN + 1];
  351:     int kind = hhdr -> hb_obj_kind;
  352: 
  353:     if (0 != GC_describe_type_fns[kind] && GC_is_marked(GC_base(p))) {
  354:         /* This should preclude free list objects except with  */
  355:         /* thread-local allocation.                            */
  356:         buffer[GC_TYPE_DESCR_LEN] = 0;
  357:         (GC_describe_type_fns[kind])(p, buffer);
  358:         GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0);
  359:         GC_err_puts(buffer);
  360:     } else {
  361:         switch(kind) {
  362:           case PTRFREE:
  363:             GC_err_puts("PTRFREE");
  364:             break;
  365:           case NORMAL:
  366:             GC_err_puts("NORMAL");
  367:             break;
  368:           case UNCOLLECTABLE:
  369:             GC_err_puts("UNCOLLECTABLE");
  370:             break;
  371: #         ifdef ATOMIC_UNCOLLECTABLE
  372:             case AUNCOLLECTABLE:
  373:               GC_err_puts("ATOMIC UNCOLLECTABLE");
  374:               break;
  375: #         endif
  376:           case STUBBORN:
  377:             GC_err_puts("STUBBORN");
  378:             break;
  379:           default:
  380:             GC_err_printf2("kind %ld, descr 0x%lx", kind, hhdr -> hb_descr);
  381:         }
  382:     }
  383: }
  384: 
  385:     
  386: 
  387: void GC_print_obj(p)
  388: ptr_t p;
  389: {
  390:     register oh * ohdr = (oh *)GC_base(p);
  391:     
  392:     GC_ASSERT(!I_HOLD_LOCK());
  393:     GC_err_printf1("0x%lx (", ((unsigned long)ohdr + sizeof(oh)));
  394:     GC_err_puts(ohdr -> oh_string);
  395: #   ifdef SHORT_DBG_HDRS
  396:       GC_err_printf1(":%ld, ", (unsigned long)(ohdr -> oh_int));
  397: #   else
  398:       GC_err_printf2(":%ld, sz=%ld, ", (unsigned long)(ohdr -> oh_int),
  399:                                         (unsigned long)(ohdr -> oh_sz));
  400: #   endif
  401:     GC_print_type((ptr_t)(ohdr + 1));
  402:     GC_err_puts(")\n");
  403:     PRINT_CALL_CHAIN(ohdr);
  404: }
  405: 
  406: # if defined(__STDC__) || defined(__cplusplus)
  407:     void GC_debug_print_heap_obj_proc(ptr_t p)
  408: # else
  409:     void GC_debug_print_heap_obj_proc(p)
  410:     ptr_t p;
  411: # endif
  412: {
  413:     GC_ASSERT(!I_HOLD_LOCK());
  414:     if (GC_HAS_DEBUG_INFO(p)) {
  415:         GC_print_obj(p);
  416:     } else {
  417:         GC_default_print_heap_obj_proc(p);
  418:     }
  419: }
  420: 
  421: #ifndef SHORT_DBG_HDRS
  422: void GC_print_smashed_obj(p, clobbered_addr)
  423: ptr_t p, clobbered_addr;
  424: {
  425:     register oh * ohdr = (oh *)GC_base(p);
  426:     
  427:     GC_ASSERT(!I_HOLD_LOCK());
  428:     GC_err_printf2("0x%lx in object at 0x%lx(", (unsigned long)clobbered_addr,
  429:                                                 (unsigned long)p);
  430:     if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz))
  431:         || ohdr -> oh_string == 0) {
  432:         GC_err_printf1("<smashed>, appr. sz = %ld)\n",
  433:                        (GC_size((ptr_t)ohdr) - DEBUG_BYTES));
  434:     } else {
  435:         if (ohdr -> oh_string[0] == '\0') {
  436:             GC_err_puts("EMPTY(smashed?)");
  437:         } else {
  438:             GC_err_puts(ohdr -> oh_string);
  439:         }
  440:         GC_err_printf2(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int),
  441:                                           (unsigned long)(ohdr -> oh_sz));
  442:         PRINT_CALL_CHAIN(ohdr);
  443:     }
  444: }
  445: #endif
  446: 
  447: void GC_check_heap_proc GC_PROTO((void));
  448: 
  449: void GC_print_all_smashed_proc GC_PROTO((void));
  450: 
  451: void GC_do_nothing() {}
  452: 
  453: void GC_start_debugging()
  454: {
  455: #   ifndef SHORT_DBG_HDRS
  456:       GC_check_heap = GC_check_heap_proc;
  457:       GC_print_all_smashed = GC_print_all_smashed_proc;
  458: #   else
  459:       GC_check_heap = GC_do_nothing;
  460:       GC_print_all_smashed = GC_do_nothing;
  461: #   endif
  462:     GC_print_heap_obj = GC_debug_print_heap_obj_proc;
  463:     GC_debugging_started = TRUE;
  464:     GC_register_displacement((word)sizeof(oh));
  465: }
  466: 
  467: size_t GC_debug_header_size = sizeof(oh);
  468: 
  469: # if defined(__STDC__) || defined(__cplusplus)
  470:     void GC_debug_register_displacement(GC_word offset)
  471: # else
  472:     void GC_debug_register_displacement(offset) 
  473:     GC_word offset;
  474: # endif
  475: {
  476:     GC_register_displacement(offset);
  477:     GC_register_displacement((word)sizeof(oh) + offset);
  478: }
  479: 
  480: # ifdef __STDC__
  481:     GC_PTR GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS)
  482: # else
  483:     GC_PTR GC_debug_malloc(lb, s, i)
  484:     size_t lb;
  485:     char * s;
  486:     int i;
  487: #   ifdef GC_ADD_CALLER
  488:         --> GC_ADD_CALLER not implemented for K&R C
  489: #   endif
  490: # endif
  491: {
  492:     GC_PTR result = GC_malloc(lb + DEBUG_BYTES);
  493:     
  494:     if (result == 0) {
  495:         GC_err_printf1("GC_debug_malloc(%ld) returning NIL (",
  496:                        (unsigned long) lb);
  497:         GC_err_puts(s);
  498:         GC_err_printf1(":%ld)\n", (unsigned long)i);
  499:         return(0);
  500:     }
  501:     if (!GC_debugging_started) {
  502:         GC_start_debugging();
  503:     }
  504:     ADD_CALL_CHAIN(result, ra);
  505:     return (GC_store_debug_info(result, (word)lb, s, (word)i));
  506: }
  507: 
  508: # ifdef __STDC__
  509:     GC_PTR GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS)
  510: # else
  511:     GC_PTR GC_debug_malloc_ignore_off_page(lb, s, i)
  512:     size_t lb;
  513:     char * s;
  514:     int i;
  515: #   ifdef GC_ADD_CALLER
  516:         --> GC_ADD_CALLER not implemented for K&R C
  517: #   endif
  518: # endif
  519: {
  520:     GC_PTR result = GC_malloc_ignore_off_page(lb + DEBUG_BYTES);
  521:     
  522:     if (result == 0) {
  523:         GC_err_printf1("GC_debug_malloc_ignore_off_page(%ld) returning NIL (",
  524:                        (unsigned long) lb);
  525:         GC_err_puts(s);
  526:         GC_err_printf1(":%ld)\n", (unsigned long)i);
  527:         return(0);
  528:     }
  529:     if (!GC_debugging_started) {
  530:         GC_start_debugging();
  531:     }
  532:     ADD_CALL_CHAIN(result, ra);
  533:     return (GC_store_debug_info(result, (word)lb, s, (word)i));
  534: }
  535: 
  536: # ifdef __STDC__
  537:     GC_PTR GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS)
  538: # else
  539:     GC_PTR GC_debug_malloc_atomic_ignore_off_page(lb, s, i)
  540:     size_t lb;
  541:     char * s;
  542:     int i;
  543: #   ifdef GC_ADD_CALLER
  544:         --> GC_ADD_CALLER not implemented for K&R C
  545: #   endif
  546: # endif
  547: {
  548:     GC_PTR result = GC_malloc_atomic_ignore_off_page(lb + DEBUG_BYTES);
  549:     
  550:     if (result == 0) {
  551:         GC_err_printf1("GC_debug_malloc_atomic_ignore_off_page(%ld)"
  552:                        " returning NIL (", (unsigned long) lb);
  553:         GC_err_puts(s);
  554:         GC_err_printf1(":%ld)\n", (unsigned long)i);
  555:         return(0);
  556:     }
  557:     if (!GC_debugging_started) {