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