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