8#include "internal/object.h"
9#include "internal/array.h"
10#include "internal/class.h"
14#include "gc/gc_impl.h"
20static bool wbcheck_debug_enabled =
false;
23static bool wbcheck_verify_after_wb_enabled =
false;
26static bool wbcheck_warn_useless_wb_enabled =
false;
29wbcheck_debug(
const char *format, ...)
31 if (!wbcheck_debug_enabled)
return;
34 va_start(args, format);
35 vfprintf(stderr, format, args);
39#define WBCHECK_DEBUG(...) do { \
40 if (wbcheck_debug_enabled) { \
41 wbcheck_debug(__VA_ARGS__); \
46wbcheck_debug_obj_info_dump(
VALUE obj)
48 if (!wbcheck_debug_enabled)
return;
50 fprintf(stderr,
"%s\n", rb_raw_obj_info(buff,
sizeof(buff), obj));
54static void lock_and_maybe_gc(
void *objspace_ptr);
55static void force_gc(
void *objspace_ptr);
59wbcheck_configure_from_env(
void)
62 const char *debug_env = getenv(
"WBCHECK_DEBUG");
63 if (debug_env && (strcmp(debug_env,
"1") == 0 || strcmp(debug_env,
"true") == 0)) {
64 wbcheck_debug_enabled =
true;
68 const char *verify_after_wb_env = getenv(
"WBCHECK_VERIFY_AFTER_WB");
69 if (verify_after_wb_env && (strcmp(verify_after_wb_env,
"1") == 0 || strcmp(verify_after_wb_env,
"true") == 0)) {
70 wbcheck_verify_after_wb_enabled =
true;
74 const char *warn_useless_wb_env = getenv(
"WBCHECK_WARN_USELESS_WB");
75 if (warn_useless_wb_env && (strcmp(warn_useless_wb_env,
"1") == 0 || strcmp(warn_useless_wb_env,
"true") == 0)) {
76 wbcheck_warn_useless_wb_enabled =
true;
81static size_t heap_sizes[] = {
100#define HEAP_COUNT ((int)(sizeof(heap_sizes) / sizeof(heap_sizes[0])) - 1)
101#define MAX_HEAP_SIZE (heap_sizes[(HEAP_COUNT) - 1])
106 WBCHECK_STATE_MARKED,
108} wbcheck_object_state_t;
119 WBCHECK_PHASE_MUTATOR,
120 WBCHECK_PHASE_SNAPSHOT,
121 WBCHECK_PHASE_FULL_GC
133wbcheck_object_list_init_with_capacity(
size_t capacity)
136 if (!list) rb_bug(
"wbcheck: failed to allocate object list structure");
138 if (capacity < 4) capacity = 4;
139 list->items = malloc(capacity *
sizeof(
VALUE));
140 if (!list->items) rb_bug(
"wbcheck: failed to allocate object list array");
141 list->capacity = capacity;
147wbcheck_object_list_init(
void)
149 return wbcheck_object_list_init_with_capacity(4);
155 if (list->count >= list->capacity) {
156 size_t new_capacity = list->capacity == 0 ? 4 : list->capacity * 2;
157 VALUE *new_items = realloc(list->items, new_capacity *
sizeof(
VALUE));
158 if (!new_items) rb_bug(
"wbcheck: failed to reallocate object list array");
159 list->items = new_items;
160 list->capacity = new_capacity;
162 list->items[list->count++] = obj;
178 if (!wbcheck_debug_enabled)
return;
179 for (
size_t i = 0; i < list->count; i++) {
181 fprintf(stderr,
"-> %s\n", rb_raw_obj_info(buff,
sizeof(buff), list->items[i]));
188 for (
size_t i = 0; i < list->count; i++) {
189 if (list->items[i] == obj) {
204 wbcheck_object_state_t state;
205 wbcheck_color_t color;
212 WBCHECK_FINAL_JOB_DFREE,
213 WBCHECK_FINAL_JOB_FINALIZE,
217 void (*func)(
void *);
221 VALUE finalizer_array;
235 wbcheck_phase_t phase;
239 size_t missed_write_barrier_parents;
240 size_t missed_write_barrier_children;
241 size_t simulated_gc_count;
242 bool measure_total_time;
244 rb_nativethread_lock_t finalizer_lock;
257static void gc_run_finalizers(
void *data);
262wbcheck_get_object_info(
VALUE obj)
265 GC_ASSERT(wbcheck_global_objspace);
268 if (st_lookup(wbcheck_global_objspace->object_table, (st_data_t)obj, &value)) {
272 fprintf(stderr,
"wbcheck: object not found in tracking table\n");
274 fprintf(stderr,
"%s\n", rb_raw_obj_info(buff,
sizeof(buff), obj));
277 ((
volatile VALUE *)obj)[0];
280 rb_bug(
"wbcheck: object not found in tracking table");
290 size_t snapshot_count = gc_mark_snapshot ? gc_mark_snapshot->count : 0;
291 size_t wb_count = writebarrier_children ? writebarrier_children->count : 0;
293 fprintf(stderr,
"WBCHECK ERROR: Missed write barrier detected!\n");
294 fprintf(stderr,
" Parent object: %p (wb_protected: %s)\n",
295 (
void *)parent_obj, parent_info->wb_protected ?
"true" :
"false");
297 fprintf(stderr,
" %s\n", rb_raw_obj_info(buff,
sizeof(buff), parent_obj));
298 fprintf(stderr,
" Reference counts - snapshot: %zu, writebarrier: %zu, current: %zu, missed: %zu\n",
299 snapshot_count, wb_count, current_refs->count, missed_refs->count);
301 for (
size_t i = 0; i < missed_refs->count; i++) {
302 VALUE missed_ref = missed_refs->items[i];
304 fprintf(stderr,
" Missing reference to: %p\n %s\n", (
void *)missed_ref, rb_raw_obj_info(buff,
sizeof(buff), missed_ref));
307 fprintf(stderr,
"\n");
308 objspace->missed_write_barrier_parents++;
309 objspace->missed_write_barrier_children += missed_refs->count;
317 if (
objspace->missed_write_barrier_parents > 0) {
319 rb_bug(
"wbcheck: missed write barrier detected (%zu object(s), %zu reference(s))",
320 objspace->missed_write_barrier_parents,
321 objspace->missed_write_barrier_children);
331 size_t snapshot_count = gc_mark_snapshot ? gc_mark_snapshot->count : 0;
332 size_t wb_count = writebarrier_children ? writebarrier_children->count : 0;
334 WBCHECK_DEBUG(
"wbcheck: comparing references for object %p\n", (
void *)parent_obj);
335 WBCHECK_DEBUG(
"wbcheck: current refs: %zu, snapshot refs: %zu, wb refs: %zu\n",
336 current_refs->count, snapshot_count, wb_count);
342 size_t snapshot_idx = 0;
345 for (
size_t i = 0; i < current_refs->count; i++) {
346 VALUE current_ref = current_refs->items[i];
355 bool found_in_snapshot =
false;
356 if (gc_mark_snapshot && snapshot_count > 0) {
357 size_t start_idx = snapshot_idx;
359 if (gc_mark_snapshot->items[snapshot_idx] == current_ref) {
360 found_in_snapshot =
true;
362 if (snapshot_idx >= snapshot_count) snapshot_idx = 0;
366 if (snapshot_idx >= snapshot_count) snapshot_idx = 0;
367 }
while (snapshot_idx != start_idx);
370 if (found_in_snapshot) {
382 if (current_ref == parent_obj) {
388 if (writebarrier_children && wbcheck_object_list_contains(writebarrier_children, current_ref)) {
395 missed_refs = wbcheck_object_list_init();
397 wbcheck_object_list_append(missed_refs, current_ref);
402 wbcheck_report_error(objspace_ptr, parent_obj, current_refs, gc_mark_snapshot, writebarrier_children, missed_refs);
403 wbcheck_object_list_free(missed_refs);
408wbcheck_register_object(
void *objspace_ptr,
VALUE obj,
size_t alloc_size,
bool wb_protected)
415 if (!info) rb_bug(
"wbcheck_register_object: failed to allocate object info");
417 info->alloc_size = alloc_size;
418 info->wb_protected = wb_protected;
419 info->finalizers = 0;
420 info->gc_mark_snapshot = NULL;
421 info->mark_maybe_snapshot = NULL;
422 info->writebarrier_children = NULL;
423 info->state = WBCHECK_STATE_CLEAR;
424 info->color = WBCHECK_COLOR_BLACK;
427 st_insert(
objspace->object_table, (st_data_t)obj, (st_data_t)info);
431wbcheck_unregister_object(
void *objspace_ptr,
VALUE obj)
436 if (st_delete(
objspace->object_table, (st_data_t *)&obj, (st_data_t *)&info)) {
438 wbcheck_object_list_free(info->gc_mark_snapshot);
439 wbcheck_object_list_free(info->mark_maybe_snapshot);
440 wbcheck_object_list_free(info->writebarrier_children);
443 rb_bug(
"wbcheck_unregister_object: object not found in table");
449rb_gc_impl_objspace_alloc(
void)
451 wbcheck_configure_from_env();
454 if (!
objspace) rb_bug(
"wbcheck: failed to allocate objspace");
456 objspace->object_table = st_init_numtable();
459 rb_bug(
"wbcheck: failed to create object table");
462 objspace->objects_to_capture = wbcheck_object_list_init();
463 objspace->objects_to_verify = wbcheck_object_list_init();
465 objspace->current_maybe_refs = NULL;
466 objspace->mark_queue = wbcheck_object_list_init();
467 objspace->weak_references = wbcheck_object_list_init();
468 objspace->phase = WBCHECK_PHASE_MUTATOR;
472 objspace->missed_write_barrier_parents = 0;
473 objspace->missed_write_barrier_children = 0;
475 objspace->measure_total_time =
true;
481rb_gc_impl_objspace_init(
void *objspace_ptr)
495rb_gc_impl_ractor_cache_alloc(
void *objspace_ptr,
void *ractor)
502rb_gc_impl_zjit_new_obj_fastpath(
void *objspace_ptr,
size_t alloc_size,
VALUE flags,
VALUE klass,
509rb_gc_impl_set_params(
void *objspace_ptr)
515gc_verify_internal_consistency(
VALUE self)
523 VALUE gc_constants = rb_hash_new();
525 rb_hash_aset(gc_constants,
ID2SYM(rb_intern(
"RVALUE_SIZE")),
SIZET2NUM(
sizeof(
struct RBasic) +
sizeof(
VALUE[RBIMPL_RVALUE_EMBED_LEN_MAX])));
527 rb_hash_aset(gc_constants,
ID2SYM(rb_intern(
"RVALUE_OVERHEAD")),
INT2NUM(0));
528 rb_hash_aset(gc_constants,
ID2SYM(rb_intern(
"RVARGC_MAX_ALLOCATE_SIZE")),
SIZET2NUM(rb_gc_impl_max_allocation_size()));
529 rb_hash_aset(gc_constants,
ID2SYM(rb_intern(
"HEAP_COUNT")),
LONG2FIX(HEAP_COUNT));
530 rb_hash_aset(gc_constants,
ID2SYM(rb_intern(
"SIZE_POOL_COUNT")),
LONG2FIX(HEAP_COUNT));
531 rb_hash_aset(gc_constants,
ID2SYM(rb_intern(
"RVALUE_OLD_AGE")),
INT2FIX(3));
533 rb_define_const(
rb_mGC,
"INTERNAL_CONSTANTS", gc_constants);
548rb_gc_impl_shutdown_free_objects(
void *objspace_ptr)
554rb_gc_impl_objspace_free(
void *objspace_ptr)
560rb_gc_impl_ractor_cache_free(
void *objspace_ptr,
void *cache)
567rb_gc_impl_start(
void *objspace_ptr,
bool full_mark,
bool immediate_mark,
bool immediate_sweep,
bool compact)
576 unsigned int lev = RB_GC_VM_LOCK();
578 force_gc(objspace_ptr);
579 RB_GC_VM_UNLOCK(lev);
583rb_gc_impl_during_gc_p(
void *objspace_ptr)
586 return objspace->phase != WBCHECK_PHASE_MUTATOR;
590wbcheck_prepare_heap_i(
VALUE obj,
void *data)
592 rb_gc_prepare_heap_process_object(obj);
596rb_gc_impl_prepare_heap(
void *objspace_ptr)
598 rb_gc_impl_each_object(objspace_ptr, wbcheck_prepare_heap_i, NULL);
602rb_gc_impl_gc_enable(
void *objspace_ptr)
609rb_gc_impl_gc_disable(
void *objspace_ptr,
bool finish_current_gc)
616rb_gc_impl_gc_enabled_p(
void *objspace_ptr)
623rb_gc_impl_stress_set(
void *objspace_ptr,
VALUE flag)
630rb_gc_impl_stress_get(
void *objspace_ptr)
637rb_gc_impl_config_get(
void *objspace_ptr)
639 return rb_hash_new();
643rb_gc_impl_config_set(
void *objspace_ptr,
VALUE hash)
648rb_gc_impl_get_vm_context(
void *objspace_ptr)
661 size_t snapshot_count = (info->gc_mark_snapshot) ? info->gc_mark_snapshot->count : 0;
662 size_t wb_children_count = (info->writebarrier_children) ? info->writebarrier_children->count : 0;
663 size_t capacity_hint = snapshot_count + wb_children_count;
669 objspace->current_maybe_refs = NULL;
670 objspace->phase = WBCHECK_PHASE_SNAPSHOT;
671 rb_gc_initialize_vm_context(&
objspace->vm_context);
677 objspace->phase = WBCHECK_PHASE_MUTATOR;
682 wbcheck_object_list_free(info->mark_maybe_snapshot);
683 info->mark_maybe_snapshot =
objspace->current_maybe_refs;
684 objspace->current_maybe_refs = NULL;
686 if (wbcheck_debug_enabled) {
687 WBCHECK_DEBUG(
"wbcheck: collected %zu references from %p\n", new_list->count, (
void *)obj);
689 fprintf(stderr,
"%s\n", rb_raw_obj_info(buff,
sizeof(buff), obj));
690 wbcheck_object_list_debug_print(new_list);
697wbcheck_collect_initial_references(
void *objspace_ptr,
VALUE obj)
699 WBCHECK_DEBUG(
"wbcheck: collecting initial references from %p:\n", obj);
700 wbcheck_debug_obj_info_dump(obj);
707 info->gc_mark_snapshot = new_list;
708 info->state = WBCHECK_STATE_MARKED;
712wbcheck_verify_object_references(
void *objspace_ptr,
VALUE obj)
717 if (!info->wb_protected) {
722 if (info->state == WBCHECK_STATE_CLEAR) {
727 WBCHECK_DEBUG(
"wbcheck: verifying references for object:\n");
728 wbcheck_debug_obj_info_dump(obj);
734 if (wbcheck_warn_useless_wb_enabled && info->writebarrier_children) {
735 for (
size_t i = 0; i < info->writebarrier_children->count; i++) {
736 VALUE wb_ref = info->writebarrier_children->items[i];
737 if (!wbcheck_object_list_contains(current_refs, wb_ref)) {
738 fprintf(stderr,
"WBCHECK WARNING: Potentially useless write barrier detected for object %p\n", (
void *)obj);
739 fprintf(stderr,
" Write barrier was recorded for reference to %p, but object no longer references it\n", (
void *)wb_ref);
741 fprintf(stderr,
" Parent: %s\n", rb_raw_obj_info(buff,
sizeof(buff), obj));
742 fprintf(stderr,
" Stale reference: %s\n", rb_raw_obj_info(buff,
sizeof(buff), wb_ref));
748 wbcheck_compare_references(objspace_ptr, obj, current_refs, info->gc_mark_snapshot, info->writebarrier_children);
751 wbcheck_object_list_free(info->gc_mark_snapshot);
752 wbcheck_object_list_free(info->writebarrier_children);
753 info->gc_mark_snapshot = current_refs;
754 info->writebarrier_children = NULL;
755 info->state = WBCHECK_STATE_MARKED;
765 if (!st_lookup(
objspace->object_table, (st_data_t)obj, &value)) {
766 rb_bug(
"wbcheck: asked to mark object %p not in our object table", (
void *)obj);
770 if (info->color != WBCHECK_COLOR_WHITE) {
774 info->color = WBCHECK_COLOR_GRAY;
775 wbcheck_object_list_append(
objspace->mark_queue, obj);
778 wbcheck_object_list_append(
objspace->weak_references, obj);
781 WBCHECK_DEBUG(
"wbcheck: marked gray: %p\n", (
void *)obj);
786st_foreach_reset_white(st_data_t key, st_data_t val, st_data_t arg)
789 info->color = WBCHECK_COLOR_WHITE;
795st_foreach_mark_finalizers(st_data_t key, st_data_t val, st_data_t arg)
800 if (info->finalizers) {
801 wbcheck_mark_gray(
objspace, info->finalizers);
811 WBCHECK_DEBUG(
"wbcheck: starting GC mark phase\n");
813 objspace->phase = WBCHECK_PHASE_FULL_GC;
814 rb_gc_initialize_vm_context(&
objspace->vm_context);
818 st_foreach(
objspace->object_table, st_foreach_reset_white, 0);
821 st_foreach(
objspace->object_table, st_foreach_mark_finalizers, (st_data_t)
objspace);
826 while (job != NULL) {
828 case WBCHECK_FINAL_JOB_DFREE:
830 case WBCHECK_FINAL_JOB_FINALIZE:
831 wbcheck_mark_gray(
objspace, job->as.finalize.finalizer_array);
834 rb_bug(
"wbcheck_mark_phase: unknown final job type %d", job->kind);
840 rb_gc_save_machine_context();
844 while (
objspace->mark_queue->count > 0) {
849 if (st_lookup(
objspace->object_table, (st_data_t)obj, &value)) {
851 if (info->color == WBCHECK_COLOR_GRAY) {
853 if (info->gc_mark_snapshot) {
854 for (
size_t i = 0; i < info->gc_mark_snapshot->count; i++) {
855 wbcheck_mark_gray(
objspace, info->gc_mark_snapshot->items[i]);
860 if (info->mark_maybe_snapshot) {
861 for (
size_t i = 0; i < info->mark_maybe_snapshot->count; i++) {
862 wbcheck_mark_gray(
objspace, info->mark_maybe_snapshot->items[i]);
867 info->color = WBCHECK_COLOR_BLACK;
868 WBCHECK_DEBUG(
"wbcheck: marked black: %p\n", (
void *)obj);
873 objspace->phase = WBCHECK_PHASE_MUTATOR;
875 WBCHECK_DEBUG(
"wbcheck: tri-color mark phase complete\n");
880wbcheck_sweep_callback(st_data_t key, st_data_t val, st_data_t arg,
int error)
886 if (info->color == WBCHECK_COLOR_WHITE) {
887 WBCHECK_DEBUG(
"wbcheck: sweeping unmarked object %p\n", (
void *)obj);
892 rb_gc_obj_free_vm_weak_references(obj);
895 if (info->finalizers) {
896 make_final_job(
objspace, obj, info->finalizers);
901 if (rb_gc_obj_free(
objspace, obj)) {
903 wbcheck_object_list_free(info->gc_mark_snapshot);
904 wbcheck_object_list_free(info->mark_maybe_snapshot);
905 wbcheck_object_list_free(info->writebarrier_children);
915 wbcheck_object_list_free(info->gc_mark_snapshot);
916 wbcheck_object_list_free(info->mark_maybe_snapshot);
917 wbcheck_object_list_free(info->writebarrier_children);
933 WBCHECK_DEBUG(
"wbcheck: starting sweep phase\n");
935 size_t objects_before = st_table_size(
objspace->object_table);
938 st_foreach_check(
objspace->object_table, wbcheck_sweep_callback, (st_data_t)
objspace, 0);
940 size_t objects_after = st_table_size(
objspace->object_table);
941 size_t freed_objects = objects_before - objects_after;
944 objspace->gc_threshold = objects_after * 2;
946 WBCHECK_DEBUG(
"wbcheck: sweep phase complete - freed %zu objects (%zu -> %zu), new threshold: %zu\n",
947 freed_objects, objects_before, objects_after,
objspace->gc_threshold);
956 WBCHECK_DEBUG(
"wbcheck: processing %zu weak reference objects\n",
objspace->weak_references->count);
958 for (
size_t i = 0; i <
objspace->weak_references->count; i++) {
960 rb_gc_handle_weak_references(obj);
963 objspace->weak_references->count = 0;
970 WBCHECK_DEBUG(
"wbcheck: starting full GC\n");
976 WBCHECK_DEBUG(
"wbcheck: updating snapshots for all objects\n");
977 wbcheck_foreach_object(
objspace, wbcheck_update_all_snapshots_callback,
objspace);
985 wbcheck_process_weak_references(
objspace);
993 WBCHECK_DEBUG(
"wbcheck: full GC complete\n");
997gc_step(
void *objspace_ptr,
bool force)
1004 if (!
objspace->gc_enabled && !force)
return;
1007 if (wbcheck_verify_after_wb_enabled) {
1008 for (
size_t i = 0; i <
objspace->objects_to_verify->count; i++) {
1010 wbcheck_verify_object_references(objspace_ptr, obj);
1014 objspace->objects_to_verify->count = 0;
1020 for (
size_t i = 0; i <
objspace->objects_to_capture->count; i++) {
1022 wbcheck_collect_initial_references(objspace_ptr, obj);
1026 objspace->objects_to_capture->count = 0;
1040maybe_gc(
void *objspace_ptr)
1042 gc_step(objspace_ptr,
false);
1046force_gc(
void *objspace_ptr)
1048 gc_step(objspace_ptr,
true);
1051int ruby_thread_has_gvl_p(
void);
1054lock_and_maybe_gc_gvl(
void *objspace_ptr)
1056 unsigned int lev = RB_GC_VM_LOCK();
1059 maybe_gc(objspace_ptr);
1061 RB_GC_VM_UNLOCK(lev);
1066lock_and_maybe_gc(
void *objspace_ptr)
1070 if (!ruby_thread_has_gvl_p()) {
1074 lock_and_maybe_gc_gvl(objspace_ptr);
1079rb_gc_impl_new_obj(
void *objspace_ptr,
void *cache_ptr,
VALUE klass,
VALUE flags,
bool wb_protected,
size_t alloc_size,
size_t *actual_alloc_size)
1081 unsigned int lev = RB_GC_VM_LOCK();
1085 maybe_gc(objspace_ptr);
1088 alloc_size = rb_gc_impl_size_slot_size(objspace_ptr, alloc_size);
1091 VALUE *mem = malloc(alloc_size);
1092 if (!mem) rb_bug(
"FIXME: malloc failed");
1094 *actual_alloc_size = alloc_size;
1098 RBASIC(obj)->flags = flags;
1102 wbcheck_register_object(objspace_ptr, obj, alloc_size, wb_protected);
1106 wbcheck_object_list_append(
objspace->objects_to_capture, obj);
1108 RB_GC_VM_UNLOCK(lev);
1113rb_gc_impl_obj_slot_size(
VALUE obj)
1115 unsigned int lev = RB_GC_VM_LOCK();
1118 size_t result = info->alloc_size;
1120 RB_GC_VM_UNLOCK(lev);
1125rb_gc_impl_size_slot_size(
void *objspace_ptr,
size_t size)
1127 for (
int i = 0; i < HEAP_COUNT; i++) {
1128 if (size <= heap_sizes[i])
return heap_sizes[i];
1130 rb_bug(
"size too big");
1134rb_gc_impl_size_allocatable_p(
size_t size)
1137 return size <= rb_gc_impl_max_allocation_size();
1141rb_gc_impl_max_allocation_size(
void)
1143 return MAX_HEAP_SIZE;
1148rb_gc_impl_malloc(
void *objspace_ptr,
size_t size,
bool gc_allowed)
1151 lock_and_maybe_gc(objspace_ptr);
1153 return malloc(size);
1157rb_gc_impl_calloc(
void *objspace_ptr,
size_t size,
bool gc_allowed)
1160 lock_and_maybe_gc(objspace_ptr);
1162 return calloc(1, size);
1166rb_gc_impl_realloc(
void *objspace_ptr,
void *ptr,
size_t new_size,
size_t old_size,
bool gc_allowed)
1169 lock_and_maybe_gc(objspace_ptr);
1171 return realloc(ptr, new_size);
1175rb_gc_impl_free(
void *objspace_ptr,
void *ptr,
size_t old_size)
1181rb_gc_impl_adjust_memory_usage(
void *objspace_ptr, ssize_t diff)
1190 WBCHECK_DEBUG(
"wbcheck: gc_mark called\n");
1191 wbcheck_debug_obj_info_dump(obj);
1196 case WBCHECK_PHASE_SNAPSHOT:
1199 wbcheck_object_list_append(
objspace->current_refs, obj);
1201 case WBCHECK_PHASE_FULL_GC:
1205 case WBCHECK_PHASE_MUTATOR:
1207 rb_bug(
"wbcheck: gc_mark called during mutator phase");
1213rb_gc_impl_mark(
void *objspace_ptr,
VALUE obj)
1220rb_gc_impl_mark_and_move(
void *objspace_ptr,
VALUE *ptr)
1227rb_gc_impl_mark_and_pin(
void *objspace_ptr,
VALUE obj)
1234rb_gc_impl_mark_maybe(
void *objspace_ptr,
VALUE obj)
1238 if (!rb_gc_impl_live_object_p(objspace_ptr, (
void *)obj))
return;
1241 case WBCHECK_PHASE_SNAPSHOT:
1246 if (!
objspace->current_maybe_refs) {
1247 objspace->current_maybe_refs = wbcheck_object_list_init();
1249 wbcheck_object_list_append(
objspace->current_maybe_refs, obj);
1251 case WBCHECK_PHASE_FULL_GC:
1254 case WBCHECK_PHASE_MUTATOR:
1255 rb_bug(
"wbcheck: rb_gc_impl_mark_maybe called during mutator phase");
1262rb_gc_impl_declare_weak_references(
void *objspace_ptr,
VALUE obj)
1268rb_gc_impl_handle_weak_references_alive_p(
void *objspace_ptr,
VALUE obj)
1273 if (st_lookup(
objspace->object_table, (st_data_t)obj, &value)) {
1275 return info->color != WBCHECK_COLOR_WHITE;
1283rb_gc_impl_register_pinning_obj(
void *objspace_ptr,
VALUE obj)
1289rb_gc_impl_object_moved_p(
void *objspace_ptr,
VALUE obj)
1296rb_gc_impl_location(
void *objspace_ptr,
VALUE value)
1304rb_gc_impl_writebarrier(
void *objspace_ptr,
VALUE a,
VALUE b)
1308 unsigned int lev = RB_GC_VM_LOCK_NO_BARRIER();
1316 if (info->state != WBCHECK_STATE_CLEAR) {
1320 if (!info->writebarrier_children) {
1321 info->writebarrier_children = wbcheck_object_list_init();
1325 wbcheck_object_list_append(info->writebarrier_children, b);
1327 WBCHECK_DEBUG(
"wbcheck: write barrier recorded reference from %p to %p\n", (
void *)a, (
void *)b);
1330 if (wbcheck_verify_after_wb_enabled && info->state != WBCHECK_STATE_DIRTY) {
1331 WBCHECK_DEBUG(
"wbcheck: queueing object for verification after write barrier\n");
1332 info->state = WBCHECK_STATE_DIRTY;
1333 wbcheck_object_list_append(
objspace->objects_to_verify, a);
1336 WBCHECK_DEBUG(
"wbcheck: write barrier skipped (snapshot not initialized) from %p to %p\n", (
void *)a, (
void *)b);
1339 RB_GC_VM_UNLOCK_NO_BARRIER(lev);
1343rb_gc_impl_writebarrier_unprotect(
void *objspace_ptr,
VALUE obj)
1345 WBCHECK_DEBUG(
"wbcheck: writebarrier_unprotect called on object %p\n", (
void *)obj);
1347 unsigned int lev = RB_GC_VM_LOCK_NO_BARRIER();
1350 info->wb_protected =
false;
1352 RB_GC_VM_UNLOCK_NO_BARRIER(lev);
1356rb_gc_impl_writebarrier_remember(
void *objspace_ptr,
VALUE obj)
1358 WBCHECK_DEBUG(
"wbcheck: writebarrier_remember called on object %p\n", (
void *)obj);
1360 unsigned int lev = RB_GC_VM_LOCK_NO_BARRIER();
1366 if (info->state != WBCHECK_STATE_CLEAR) {
1368 wbcheck_object_list_free(info->gc_mark_snapshot);
1369 info->gc_mark_snapshot = NULL;
1371 wbcheck_object_list_free(info->mark_maybe_snapshot);
1372 info->mark_maybe_snapshot = NULL;
1376 wbcheck_object_list_append(
objspace->objects_to_capture, obj);
1379 if (info->writebarrier_children) {
1380 wbcheck_object_list_free(info->writebarrier_children);
1381 info->writebarrier_children = NULL;
1385 info->state = WBCHECK_STATE_CLEAR;
1391 RB_GC_VM_UNLOCK_NO_BARRIER(lev);
1401wbcheck_foreach_object_i(st_data_t key, st_data_t val, st_data_t arg)
1407 return foreach_data->callback(obj, info, foreach_data->data);
1414 .callback = callback,
1418 st_foreach(
objspace->object_table, wbcheck_foreach_object_i, (st_data_t)&foreach_data);
1423wbcheck_snapshot_collector(st_data_t key, st_data_t val, st_data_t arg)
1427 wbcheck_object_list_append(snapshot, obj);
1435 size_t object_count = st_table_size(
objspace->object_table);
1437 st_foreach(
objspace->object_table, wbcheck_snapshot_collector, (st_data_t)snapshot);
1443rb_gc_impl_each_objects(
void *objspace_ptr,
int (*callback)(
void *,
void *,
size_t,
void *),
void *data)
1450 for (
size_t i = 0; i < snapshot->count; i++) {
1451 VALUE obj = snapshot->items[i];
1453 if (st_lookup(
objspace->object_table, (st_data_t)obj, &value)) {
1455 int result = callback(
1457 (
void *)((
char *)obj + info->alloc_size),
1461 if (result != 0)
break;
1465 wbcheck_object_list_free(snapshot);
1469rb_gc_impl_each_object(
void *objspace_ptr,
void (*func)(
VALUE obj,
void *data),
void *data)
1476 for (
size_t i = 0; i < snapshot->count; i++) {
1477 VALUE obj = snapshot->items[i];
1479 if (st_lookup(
objspace->object_table, (st_data_t)obj, &value)) {
1484 wbcheck_object_list_free(snapshot);
1491 job->next =
objspace->finalizer_jobs;
1502 objspace->finalizer_jobs = job->next;
1510rb_gc_impl_make_zombie(
void *objspace_ptr,
VALUE obj,
void (*dfree)(
void *),
void *data)
1512 if (dfree == NULL)
return;
1517 job->kind = WBCHECK_FINAL_JOB_DFREE;
1518 job->as.dfree.func = dfree;
1519 job->as.dfree.data = data;
1521 finalizer_jobs_push(
objspace, job);
1523 if (!ruby_free_at_exit_p()) {
1527 WBCHECK_DEBUG(
"wbcheck: made zombie for object %p with dfree function\n", (
void *)obj);
1531rb_gc_impl_define_finalizer(
void *objspace_ptr,
VALUE obj,
VALUE block)
1533 unsigned int lev = RB_GC_VM_LOCK();
1542 VALUE table = info->finalizers;
1543 VALUE result = block;
1549 info->finalizers = table;
1555 for (i = 1; i <
len; i++) {
1559 goto unlock_and_return;
1567 RB_GC_VM_UNLOCK(lev);
1572rb_gc_impl_undefine_finalizer(
void *objspace_ptr,
VALUE obj)
1574 unsigned int lev = RB_GC_VM_LOCK();
1581 info->finalizers = 0;
1584 RB_GC_VM_UNLOCK(lev);
1588rb_gc_impl_copy_finalizer(
void *objspace_ptr,
VALUE dest,
VALUE obj)
1594 unsigned int lev = RB_GC_VM_LOCK();
1599 if (src_info->finalizers) {
1603 dest_info->finalizers = table;
1607 RB_GC_VM_UNLOCK(lev);
1611wbcheck_get_final(
long i,
void *data)
1627 job->kind = WBCHECK_FINAL_JOB_FINALIZE;
1628 job->as.finalize.finalizer_array = finalizer_array;
1630 finalizer_jobs_push(
objspace, job);
1634gc_run_finalizers(
void *data)
1638 rb_gc_set_pending_interrupt();
1641 while ((job = finalizer_jobs_pop(
objspace)) != NULL) {
1642 switch (job->kind) {
1643 case WBCHECK_FINAL_JOB_DFREE:
1644 job->as.dfree.func(job->as.dfree.data);
1646 case WBCHECK_FINAL_JOB_FINALIZE: {
1647 VALUE finalizer_array = job->as.finalize.finalizer_array;
1649 rb_gc_run_obj_finalizer(
1653 (
void *)finalizer_array
1664 rb_gc_unset_pending_interrupt();
1670 if (info->finalizers) {
1671 VALUE table = info->finalizers;
1673 rb_gc_run_obj_finalizer(
RARRAY_AREF(table, 0), count, wbcheck_get_final, (
void *)table);
1676 info->finalizers = 0;
1682 wbcheck_run_finalizers_for_object(obj, info);
1689 void *objspace_ptr = data;
1690 wbcheck_verify_object_references(objspace_ptr, obj);
1697 void *objspace_ptr = data;
1700 if (info->wb_protected && info->state != WBCHECK_STATE_CLEAR) {
1701 wbcheck_verify_object_references(objspace_ptr, obj);
1705 wbcheck_object_list_free(info->gc_mark_snapshot);
1706 info->gc_mark_snapshot = current_refs;
1707 info->state = WBCHECK_STATE_MARKED;
1716 void *objspace_ptr = data;
1718 if (rb_gc_shutdown_call_finalizer_p(obj)) {
1719 WBCHECK_DEBUG(
"wbcheck: finalizing object during shutdown: %p\n", (
void *)obj);
1720 rb_gc_obj_free_vm_weak_references(obj);
1721 if (rb_gc_obj_free(objspace_ptr, obj)) {
1731rb_gc_impl_shutdown_call_finalizer(
void *objspace_ptr)
1736 wbcheck_foreach_object(
objspace, wbcheck_shutdown_call_finalizer_callback, NULL);
1739 unsigned int verify_lev = RB_GC_VM_LOCK();
1740 WBCHECK_DEBUG(
"wbcheck: verifying references for all objects after finalizers\n");
1741 wbcheck_foreach_object(
objspace, wbcheck_verify_all_references_callback, objspace_ptr);
1742 WBCHECK_DEBUG(
"wbcheck: finished verifying all object references\n");
1743 RB_GC_VM_UNLOCK(verify_lev);
1746 WBCHECK_DEBUG(
"wbcheck: no write barrier violations detected\n");
1749 unsigned int lev = RB_GC_VM_LOCK();
1750 WBCHECK_DEBUG(
"wbcheck: calling rb_gc_obj_free on objects that need shutdown finalization\n");
1751 wbcheck_foreach_object(
objspace, wbcheck_shutdown_finalizer_callback, objspace_ptr);
1752 WBCHECK_DEBUG(
"wbcheck: finished calling rb_gc_obj_free\n");
1755 WBCHECK_DEBUG(
"wbcheck: running pending finalizer jobs\n");
1757 WBCHECK_DEBUG(
"wbcheck: finished running finalizer jobs\n");
1758 RB_GC_VM_UNLOCK(lev);
1763rb_gc_impl_before_fork(
void *objspace_ptr)
1767 unsigned int lev = RB_GC_VM_LOCK();
1769 force_gc(objspace_ptr);
1770 RB_GC_VM_UNLOCK(lev);
1774rb_gc_impl_after_fork(
void *objspace_ptr, rb_pid_t pid)
1781rb_gc_impl_set_measure_total_time(
void *objspace_ptr,
VALUE flag)
1788rb_gc_impl_get_measure_total_time(
void *objspace_ptr)
1791 return objspace->measure_total_time;
1795rb_gc_impl_get_total_time(
void *objspace_ptr)
1802rb_gc_impl_gc_count(
void *objspace_ptr)
1806 return objspace->simulated_gc_count;
1812rb_gc_impl_latest_gc_info(
void *objspace_ptr,
VALUE key)
1819rb_gc_impl_stat(
void *objspace_ptr,
VALUE hash_or_sym)
1833 rb_bug(
"non-hash or symbol given");
1836#define SET(name, attr) \
1837 if (key == ID2SYM(rb_intern(#name))) \
1838 return SIZET2NUM(attr); \
1839 else if (hash != Qnil) \
1840 rb_hash_aset(hash, ID2SYM(rb_intern(#name)), SIZET2NUM(attr));
1843 SET(count,
objspace->simulated_gc_count);
1844 SET(time,
objspace->measure_total_time ?
objspace->simulated_gc_count : 0);
1845 SET(tracked_objects, st_table_size(
objspace->object_table));
1849 rb_raise(rb_eArgError,
"unknown key: %"PRIsVALUE,
rb_sym2str(key));
1858rb_gc_impl_stat_heap(
void *objspace_ptr,
VALUE heap_name,
VALUE hash_or_sym)
1861 int heap_idx =
FIX2INT(heap_name);
1862 if (heap_idx < 0 || heap_idx >= HEAP_COUNT) {
1863 rb_raise(rb_eArgError,
"size pool index out of range");
1866 if (hash_or_sym ==
ID2SYM(rb_intern(
"slot_size"))) {
1881rb_gc_impl_active_gc_name(
void)
1888#define WBCHECK_OBJECT_METADATA_ENTRY_COUNT 2
1892rb_gc_impl_object_metadata(
void *objspace_ptr,
VALUE obj)
1894 static ID ID_object_id, ID_shareable;
1896 if (!ID_object_id) {
1897 ID_object_id = rb_intern(
"object_id");
1898 ID_shareable = rb_intern(
"shareable");
1903#define SET_ENTRY(na, v) do { \
1904 GC_ASSERT(n < WBCHECK_OBJECT_METADATA_ENTRY_COUNT); \
1905 object_metadata_entries[n].name = ID_##na; \
1906 object_metadata_entries[n].val = v; \
1910 if (rb_obj_id_p(obj)) SET_ENTRY(object_id, rb_obj_id(obj));
1914 object_metadata_entries[n].name = 0;
1915 object_metadata_entries[n].val = 0;
1917 return object_metadata_entries;
1921rb_gc_impl_live_object_p(
void *objspace_ptr,
const void *ptr)
1923 GC_ASSERT(wbcheck_global_objspace);
1925 unsigned int lev = RB_GC_VM_LOCK();
1929 bool result = st_lookup(wbcheck_global_objspace->object_table, (st_data_t)ptr, &value);
1931 RB_GC_VM_UNLOCK(lev);
1936rb_gc_impl_garbage_object_p(
void *objspace_ptr,
VALUE obj)
1938 unsigned int lev = RB_GC_VM_LOCK();
1942 bool result = st_lookup(wbcheck_global_objspace->object_table, (st_data_t)obj, &value);
1944 RB_GC_VM_UNLOCK(lev);
1949rb_gc_impl_set_event_hook(
void *objspace_ptr,
const rb_event_flag_t event)
1955rb_gc_impl_copy_attributes(
void *objspace_ptr,
VALUE dest,
VALUE obj)
1959 if (!src_info->wb_protected) {
1960 rb_gc_impl_writebarrier_unprotect(objspace_ptr, dest);
1962 rb_gc_impl_copy_finalizer(objspace_ptr, dest, obj);
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
unsigned int rb_postponed_job_handle_t
The type of a handle returned from rb_postponed_job_preregister and passed to rb_postponed_job_trigge...
void rb_postponed_job_trigger(rb_postponed_job_handle_t h)
Triggers a pre-registered job registered with rb_postponed_job_preregister, scheduling it for executi...
rb_postponed_job_handle_t rb_postponed_job_preregister(unsigned int flags, rb_postponed_job_func_t func, void *data)
Pre-registers a func in Ruby's postponed job preregistration table, returning an opaque handle which ...
#define RUBY_INTERNAL_EVENT_GC_EXIT
gc_exit() is called.
#define RUBY_INTERNAL_EVENT_GC_ENTER
gc_enter() is called.
#define RUBY_INTERNAL_EVENT_GC_END_SWEEP
GC ended sweep phase.
#define RUBY_INTERNAL_EVENT_GC_END_MARK
GC ended mark phase.
#define RUBY_INTERNAL_EVENT_FREEOBJ
Object swept.
#define RUBY_INTERNAL_EVENT_GC_START
GC started.
uint32_t rb_event_flag_t
Represents event(s).
static VALUE RB_FL_TEST(VALUE obj, VALUE flags)
Tests if the given flag(s) are set or not.
static VALUE RB_FL_TEST_RAW(VALUE obj, VALUE flags)
This is an implementation detail of RB_FL_TEST().
static void RB_FL_UNSET(VALUE obj, VALUE flags)
Clears the given flag(s).
@ RUBY_FL_WEAK_REFERENCE
This object weakly refers to other objects.
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
#define ID2SYM
Old name of RB_ID2SYM.
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
#define FL_SHAREABLE
Old name of RUBY_FL_SHAREABLE.
#define SIZET2NUM
Old name of RB_SIZE2NUM.
#define LONG2FIX
Old name of RB_INT2FIX.
#define FIX2INT
Old name of RB_FIX2INT.
#define FL_FINALIZE
Old name of RUBY_FL_FINALIZE.
#define T_HASH
Old name of RUBY_T_HASH.
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
#define FL_SET
Old name of RB_FL_SET.
#define rb_ary_new3
Old name of rb_ary_new_from_args.
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
#define NIL_P
Old name of RB_NIL_P.
#define T_CLASS
Old name of RUBY_T_CLASS.
#define FL_TEST
Old name of RB_FL_TEST.
#define FL_UNSET
Old name of RB_FL_UNSET.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
#define SYMBOL_P
Old name of RB_SYMBOL_P.
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
VALUE rb_ary_dup(VALUE ary)
Duplicates an array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker)
Raises rb_eNotImpError.
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
int len
Length of the buffer.
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
(Re-)acquires the GVL.
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
#define RARRAY_LEN
Just another name of rb_array_len.
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
#define RARRAY_AREF(a, i)
#define RBASIC(obj)
Convenient casting macro.
int ruby_native_thread_p(void)
Queries if the thread which calls this function is a ruby's thread.
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
Ruby object's base components.
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
static enum ruby_value_type RB_BUILTIN_TYPE(VALUE obj)
Queries the type of the object.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.