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