summaryrefslogtreecommitdiff
path: root/ext/objspace/object_tracing.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/objspace/object_tracing.c')
-rw-r--r--ext/objspace/object_tracing.c99
1 files changed, 51 insertions, 48 deletions
diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c
index 0156642ef2..74d793a6e2 100644
--- a/ext/objspace/object_tracing.c
+++ b/ext/objspace/object_tracing.c
@@ -22,7 +22,6 @@ struct traceobj_arg {
int running;
int keep_remains;
VALUE newobj_trace;
- VALUE freeobj_trace;
st_table *object_table; /* obj (VALUE) -> allocation_info */
st_table *str_table; /* cstr -> refcount */
struct traceobj_arg *prev_traceobj_arg;
@@ -91,18 +90,16 @@ newobj_i(VALUE tpval, void *data)
VALUE klass = rb_tracearg_defined_class(tparg);
struct allocation_info *info;
const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
- VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
+ VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_mod_name(klass) : Qnil;
const char *class_path_cstr = RTEST(class_path) ? make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
st_data_t v;
if (st_lookup(arg->object_table, (st_data_t)obj, &v)) {
+ /* keep_remains kept this slot's entry after its object was freed. The
+ * allocator has now reused that address, so recycle the dead entry's
+ * info. A living entry here would mean two live objects at one address. */
info = (struct allocation_info *)v;
- if (arg->keep_remains) {
- if (info->living) {
- /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
- }
- }
- /* reuse info */
+ assert(!info->living);
delete_unique_str(arg->str_table, info->path);
delete_unique_str(arg->str_table, info->class_path);
}
@@ -121,37 +118,6 @@ newobj_i(VALUE tpval, void *data)
st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
}
-static void
-freeobj_i(VALUE tpval, void *data)
-{
- struct traceobj_arg *arg = (struct traceobj_arg *)data;
- rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
- st_data_t obj = (st_data_t)rb_tracearg_object(tparg);
- st_data_t v;
- struct allocation_info *info;
-
- /* Modifying the st table can cause allocations, which can trigger GC.
- * Since freeobj_i is called during GC, it must not trigger another GC. */
- VALUE gc_disabled = rb_gc_disable_no_rest();
-
- if (arg->keep_remains) {
- if (st_lookup(arg->object_table, obj, &v)) {
- info = (struct allocation_info *)v;
- info->living = 0;
- }
- }
- else {
- if (st_delete(arg->object_table, &obj, &v)) {
- info = (struct allocation_info *)v;
- delete_unique_str(arg->str_table, info->path);
- delete_unique_str(arg->str_table, info->class_path);
- ruby_xfree(info);
- }
- }
-
- if (gc_disabled == Qfalse) rb_gc_enable();
-}
-
static int
free_keys_i(st_data_t key, st_data_t value, st_data_t data)
{
@@ -171,7 +137,6 @@ allocation_info_tracer_mark(void *ptr)
{
struct traceobj_arg *trace_arg = (struct traceobj_arg *)ptr;
rb_gc_mark(trace_arg->newobj_trace);
- rb_gc_mark(trace_arg->freeobj_trace);
}
static void
@@ -198,12 +163,46 @@ allocation_info_tracer_memsize(const void *ptr)
}
static int
+allocation_info_tracer_weak_reference_i(st_data_t key, st_data_t value, st_data_t data)
+{
+ struct traceobj_arg *arg = (struct traceobj_arg *)data;
+ struct allocation_info *info = (struct allocation_info *)value;
+
+ if (rb_gc_handle_weak_references_alive_p((VALUE)key)) {
+ return ST_CONTINUE;
+ }
+
+ /* Object was collected. keep_remains keeps the dead entry for reporting. */
+ if (arg->keep_remains) {
+ info->living = 0;
+ return ST_CONTINUE;
+ }
+ else {
+ delete_unique_str(arg->str_table, info->path);
+ delete_unique_str(arg->str_table, info->class_path);
+ ruby_xfree(info);
+ return ST_DELETE;
+ }
+}
+
+static void
+allocation_info_tracer_weak_reference(void *ptr)
+{
+ struct traceobj_arg *arg = (struct traceobj_arg *)ptr;
+
+ st_foreach(arg->object_table, allocation_info_tracer_weak_reference_i, (st_data_t)arg);
+}
+
+static int
allocation_info_tracer_compact_update_object_table_i(st_data_t key, st_data_t value, st_data_t data)
{
st_table *table = (st_table *)data;
+ struct allocation_info *info = (struct allocation_info *)value;
- if (!rb_gc_pointer_to_heap_p(key)) {
- return ST_DELETE;
+ /* In keep_remains mode the table keeps entries for freed objects. Their keys
+ * are dangling, so skip them instead of passing them to rb_gc_location. */
+ if (!info->living) {
+ return ST_CONTINUE;
}
if (key != rb_gc_location(key)) {
@@ -240,6 +239,7 @@ static const rb_data_type_t allocation_info_tracer_type = {
allocation_info_tracer_free, /* Never called because global */
allocation_info_tracer_memsize,
allocation_info_tracer_compact,
+ allocation_info_tracer_weak_reference,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -258,9 +258,10 @@ get_traceobj_arg(void)
tmp_trace_arg->running = 0;
tmp_trace_arg->keep_remains = tmp_keep_remains;
tmp_trace_arg->newobj_trace = 0;
- tmp_trace_arg->freeobj_trace = 0;
tmp_trace_arg->object_table = st_init_numtable();
tmp_trace_arg->str_table = st_init_strtable();
+
+ rb_gc_declare_weak_references(obj);
}
return tmp_trace_arg;
}
@@ -282,10 +283,8 @@ trace_object_allocations_start(VALUE self)
else {
if (arg->newobj_trace == 0) {
arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg);
- arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg);
}
rb_tracepoint_enable(arg->newobj_trace);
- rb_tracepoint_enable(arg->freeobj_trace);
}
return Qnil;
@@ -313,9 +312,6 @@ trace_object_allocations_stop(VALUE self)
if (arg->newobj_trace != 0) {
rb_tracepoint_disable(arg->newobj_trace);
}
- if (arg->freeobj_trace != 0) {
- rb_tracepoint_disable(arg->freeobj_trace);
- }
}
return Qnil;
@@ -411,6 +407,13 @@ object_allocations_reporter(FILE *out, void *ptr)
fprintf(out, "== object_allocations_reporter: END\n");
}
+/*
+ * call-seq: trace_object_allocations_debug_start
+ *
+ * Starts tracing object allocations for GC debugging.
+ * If you encounter the BUG "... is T_NONE" (and so on) on your
+ * application, please try this method at the beginning of your app.
+ */
static VALUE
trace_object_allocations_debug_start(VALUE self)
{