Skip to content

Commit 4fb537b

Browse files
authored
Make tracepoints with set_trace_func or TracePoint.new ractor local (ruby#15468)
Before this change, GC'ing any Ractor object caused you to lose all enabled tracepoints across all ractors (even main). Now tracepoints are ractor-local and this doesn't happen. Internal events are still global. Fixes [Bug #19112]
1 parent d209e6f commit 4fb537b

File tree

16 files changed

+694
-226
lines changed

16 files changed

+694
-226
lines changed

depend

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7606,6 +7606,7 @@ iseq.$(OBJEXT): {$(VPATH)}onigmo.h
76067606
iseq.$(OBJEXT): {$(VPATH)}oniguruma.h
76077607
iseq.$(OBJEXT): {$(VPATH)}prism_compile.h
76087608
iseq.$(OBJEXT): {$(VPATH)}ractor.h
7609+
iseq.$(OBJEXT): {$(VPATH)}ractor_core.h
76097610
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
76107611
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
76117612
iseq.$(OBJEXT): {$(VPATH)}rubyparser.h
@@ -19760,6 +19761,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}onigmo.h
1976019761
vm_trace.$(OBJEXT): {$(VPATH)}oniguruma.h
1976119762
vm_trace.$(OBJEXT): {$(VPATH)}prism_compile.h
1976219763
vm_trace.$(OBJEXT): {$(VPATH)}ractor.h
19764+
vm_trace.$(OBJEXT): {$(VPATH)}ractor_core.h
1976319765
vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h
1976419766
vm_trace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
1976519767
vm_trace.$(OBJEXT): {$(VPATH)}rubyparser.h

imemo.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,6 @@ mark_and_move_method_entry(rb_method_entry_t *ment, bool reference_updating)
306306
if (!rb_gc_checking_shareable()) {
307307
rb_gc_mark_and_move(&def->body.bmethod.proc);
308308
}
309-
if (def->body.bmethod.hooks) {
310-
rb_hook_list_mark_and_move(def->body.bmethod.hooks);
311-
}
312309
break;
313310
case VM_METHOD_TYPE_ALIAS:
314311
rb_gc_mark_and_move_ptr(&def->body.alias.original_me);

iseq.c

Lines changed: 96 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "iseq.h"
4040
#include "ruby/util.h"
4141
#include "vm_core.h"
42+
#include "ractor_core.h"
4243
#include "vm_callinfo.h"
4344
#include "yjit.h"
4445
#include "ruby/ractor.h"
@@ -161,6 +162,24 @@ iseq_clear_ic_references(const rb_iseq_t *iseq)
161162
}
162163
}
163164

165+
166+
rb_hook_list_t *
167+
rb_iseq_local_hooks(const rb_iseq_t *iseq, rb_ractor_t *r, bool create)
168+
{
169+
rb_hook_list_t *hook_list = NULL;
170+
st_data_t val;
171+
if (st_lookup(rb_ractor_targeted_hooks(r), (st_data_t)iseq, &val)) {
172+
hook_list = (rb_hook_list_t*)val;
173+
RUBY_ASSERT(hook_list->type == hook_list_type_targeted_iseq);
174+
}
175+
else if (create) {
176+
hook_list = RB_ZALLOC(rb_hook_list_t);
177+
hook_list->type = hook_list_type_targeted_iseq;
178+
st_insert(rb_ractor_targeted_hooks(r), (st_data_t)iseq, (st_data_t)hook_list);
179+
}
180+
return hook_list;
181+
}
182+
164183
void
165184
rb_iseq_free(const rb_iseq_t *iseq)
166185
{
@@ -213,10 +232,6 @@ rb_iseq_free(const rb_iseq_t *iseq)
213232
ruby_xfree(body);
214233
}
215234

216-
if (iseq && ISEQ_EXECUTABLE_P(iseq) && iseq->aux.exec.local_hooks) {
217-
rb_hook_list_free(iseq->aux.exec.local_hooks);
218-
}
219-
220235
RUBY_FREE_LEAVE("iseq");
221236
}
222237

@@ -448,10 +463,6 @@ rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
448463
else {
449464
/* executable */
450465
VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
451-
452-
if (iseq->aux.exec.local_hooks) {
453-
rb_hook_list_mark_and_move(iseq->aux.exec.local_hooks);
454-
}
455466
}
456467

457468
RUBY_MARK_LEAVE("iseq");
@@ -2438,17 +2449,22 @@ rb_iseq_event_flags(const rb_iseq_t *iseq, size_t pos)
24382449
}
24392450
}
24402451

2452+
static void rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos);
2453+
24412454
// Clear tracing event flags and turn off tracing for a given instruction as needed.
24422455
// This is currently used after updating a one-shot line coverage for the current instruction.
24432456
void
24442457
rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset)
24452458
{
2446-
struct iseq_insn_info_entry *entry = (struct iseq_insn_info_entry *)get_insn_info(iseq, pos);
2447-
if (entry) {
2448-
entry->events &= ~reset;
2449-
if (!(entry->events & iseq->aux.exec.global_trace_events)) {
2450-
void rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos);
2451-
rb_iseq_trace_flag_cleared(iseq, pos);
2459+
RB_VM_LOCKING() {
2460+
rb_vm_barrier();
2461+
2462+
struct iseq_insn_info_entry *entry = (struct iseq_insn_info_entry *)get_insn_info(iseq, pos);
2463+
if (entry) {
2464+
entry->events &= ~reset;
2465+
if (!(entry->events & iseq->aux.exec.global_trace_events)) {
2466+
rb_iseq_trace_flag_cleared(iseq, pos);
2467+
}
24522468
}
24532469
}
24542470
}
@@ -3930,14 +3946,15 @@ rb_vm_insn_decode(const VALUE encoded)
39303946

39313947
// Turn on or off tracing for a given instruction address
39323948
static inline int
3933-
encoded_iseq_trace_instrument(VALUE *iseq_encoded_insn, rb_event_flag_t turnon, bool remain_current_trace)
3949+
encoded_iseq_trace_instrument(VALUE *iseq_encoded_insn, rb_event_flag_t turnon, bool remain_traced)
39343950
{
3951+
ASSERT_vm_locking();
39353952
st_data_t key = (st_data_t)*iseq_encoded_insn;
39363953
st_data_t val;
39373954

39383955
if (st_lookup(encoded_insn_data, key, &val)) {
39393956
insn_data_t *e = (insn_data_t *)val;
3940-
if (remain_current_trace && key == (st_data_t)e->trace_encoded_insn) {
3957+
if (remain_traced && key == (st_data_t)e->trace_encoded_insn) {
39413958
turnon = 1;
39423959
}
39433960
*iseq_encoded_insn = (VALUE) (turnon ? e->trace_encoded_insn : e->notrace_encoded_insn);
@@ -3948,7 +3965,7 @@ encoded_iseq_trace_instrument(VALUE *iseq_encoded_insn, rb_event_flag_t turnon,
39483965
}
39493966

39503967
// Turn off tracing for an instruction at pos after tracing event flags are cleared
3951-
void
3968+
static void
39523969
rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos)
39533970
{
39543971
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
@@ -3974,14 +3991,16 @@ add_bmethod_events(rb_event_flag_t events)
39743991

39753992
// Note, to support call/return events for bmethods, turnon_event can have more events than tpval.
39763993
static int
3977-
iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line)
3994+
iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line, rb_ractor_t *r)
39783995
{
39793996
unsigned int pc;
39803997
int n = 0;
39813998
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
39823999
VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
4000+
rb_iseq_t *iseq_mut = (rb_iseq_t*)iseq;
39834001

39844002
VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
4003+
ASSERT_vm_locking_with_barrier();
39854004

39864005
for (pc=0; pc<body->iseq_size;) {
39874006
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pc);
@@ -4003,11 +4022,9 @@ iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events,
40034022
}
40044023

40054024
if (n > 0) {
4006-
if (iseq->aux.exec.local_hooks == NULL) {
4007-
((rb_iseq_t *)iseq)->aux.exec.local_hooks = RB_ZALLOC(rb_hook_list_t);
4008-
iseq->aux.exec.local_hooks->is_local = true;
4009-
}
4010-
rb_hook_list_connect_tracepoint((VALUE)iseq, iseq->aux.exec.local_hooks, tpval, target_line);
4025+
rb_hook_list_t *hook_list = rb_iseq_local_hooks(iseq, r, true);
4026+
rb_hook_list_connect_local_tracepoint(hook_list, tpval, target_line);
4027+
iseq_mut->aux.exec.local_hooks_cnt++;
40114028
}
40124029

40134030
return n;
@@ -4018,19 +4035,21 @@ struct trace_set_local_events_struct {
40184035
VALUE tpval;
40194036
unsigned int target_line;
40204037
int n;
4038+
rb_ractor_t *r;
40214039
};
40224040

40234041
static void
40244042
iseq_add_local_tracepoint_i(const rb_iseq_t *iseq, void *p)
40254043
{
40264044
struct trace_set_local_events_struct *data = (struct trace_set_local_events_struct *)p;
4027-
data->n += iseq_add_local_tracepoint(iseq, data->turnon_events, data->tpval, data->target_line);
4045+
data->n += iseq_add_local_tracepoint(iseq, data->turnon_events, data->tpval, data->target_line, data->r);
40284046
iseq_iterate_children(iseq, iseq_add_local_tracepoint_i, p);
40294047
}
40304048

40314049
int
40324050
rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line, bool target_bmethod)
40334051
{
4052+
ASSERT_vm_locking_with_barrier();
40344053
struct trace_set_local_events_struct data;
40354054
if (target_bmethod) {
40364055
turnon_events = add_bmethod_events(turnon_events);
@@ -4039,35 +4058,52 @@ rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t
40394058
data.tpval = tpval;
40404059
data.target_line = target_line;
40414060
data.n = 0;
4061+
data.r = GET_RACTOR();
40424062

40434063
iseq_add_local_tracepoint_i(iseq, (void *)&data);
4044-
if (0) rb_funcall(Qnil, rb_intern("puts"), 1, rb_iseq_disasm(iseq)); /* for debug */
4064+
if (0) fprintf(stderr, "Iseq disasm:\n:%s", RSTRING_PTR(rb_iseq_disasm(iseq))); /* for debug */
40454065
return data.n;
40464066
}
40474067

40484068
static int
4049-
iseq_remove_local_tracepoint(const rb_iseq_t *iseq, VALUE tpval)
4069+
iseq_remove_local_tracepoint(const rb_iseq_t *iseq, VALUE tpval, rb_ractor_t *r)
40504070
{
40514071
int n = 0;
4072+
unsigned int num_hooks_left;
4073+
unsigned int pc;
4074+
const struct rb_iseq_constant_body *body;
4075+
rb_iseq_t *iseq_mut = (rb_iseq_t*)iseq;
4076+
rb_hook_list_t *hook_list;
4077+
VALUE *iseq_encoded;
4078+
ASSERT_vm_locking_with_barrier();
40524079

4053-
if (iseq->aux.exec.local_hooks) {
4054-
unsigned int pc;
4055-
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
4056-
VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
4080+
hook_list = rb_iseq_local_hooks(iseq, r, false);
4081+
4082+
if (hook_list) {
40574083
rb_event_flag_t local_events = 0;
40584084

4059-
rb_hook_list_remove_tracepoint(iseq->aux.exec.local_hooks, tpval);
4060-
local_events = iseq->aux.exec.local_hooks->events;
4085+
rb_event_flag_t prev_events = hook_list->events;
4086+
if (rb_hook_list_remove_local_tracepoint(hook_list, tpval)) {
4087+
RUBY_ASSERT(iseq->aux.exec.local_hooks_cnt > 0);
4088+
iseq_mut->aux.exec.local_hooks_cnt--;
4089+
local_events = hook_list->events; // remaining events for this ractor
4090+
num_hooks_left = rb_hook_list_count(hook_list);
4091+
if (local_events == 0 && prev_events != 0) {
4092+
st_delete(rb_ractor_targeted_hooks(r), (st_data_t*)&iseq, NULL);
4093+
rb_hook_list_free(hook_list);
4094+
}
40614095

4062-
if (local_events == 0) {
4063-
rb_hook_list_free(iseq->aux.exec.local_hooks);
4064-
((rb_iseq_t *)iseq)->aux.exec.local_hooks = NULL;
4065-
}
4096+
if (iseq->aux.exec.local_hooks_cnt == num_hooks_left) {
4097+
body = ISEQ_BODY(iseq);
4098+
iseq_encoded = (VALUE *)body->iseq_encoded;
4099+
local_events = add_bmethod_events(local_events);
4100+
for (pc = 0; pc<body->iseq_size;) {
4101+
rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc);
4102+
pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (local_events | iseq->aux.exec.global_trace_events), false);
4103+
}
4104+
}
40664105

4067-
local_events = add_bmethod_events(local_events);
4068-
for (pc = 0; pc<body->iseq_size;) {
4069-
rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc);
4070-
pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (local_events | iseq->aux.exec.global_trace_events), false);
4106+
n++;
40714107
}
40724108
}
40734109
return n;
@@ -4076,22 +4112,25 @@ iseq_remove_local_tracepoint(const rb_iseq_t *iseq, VALUE tpval)
40764112
struct trace_clear_local_events_struct {
40774113
VALUE tpval;
40784114
int n;
4115+
rb_ractor_t *r;
40794116
};
40804117

40814118
static void
40824119
iseq_remove_local_tracepoint_i(const rb_iseq_t *iseq, void *p)
40834120
{
40844121
struct trace_clear_local_events_struct *data = (struct trace_clear_local_events_struct *)p;
4085-
data->n += iseq_remove_local_tracepoint(iseq, data->tpval);
4122+
data->n += iseq_remove_local_tracepoint(iseq, data->tpval, data->r);
40864123
iseq_iterate_children(iseq, iseq_remove_local_tracepoint_i, p);
40874124
}
40884125

40894126
int
4090-
rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval)
4127+
rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval, rb_ractor_t *r)
40914128
{
40924129
struct trace_clear_local_events_struct data;
4130+
ASSERT_vm_locking_with_barrier();
40934131
data.tpval = tpval;
40944132
data.n = 0;
4133+
data.r = r;
40954134

40964135
iseq_remove_local_tracepoint_i(iseq, (void *)&data);
40974136
return data.n;
@@ -4109,11 +4148,14 @@ rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events)
41094148
return;
41104149
}
41114150
else {
4151+
// NOTE: this does not need VM barrier if it's a new ISEQ
41124152
unsigned int pc;
41134153
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
4154+
41144155
VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
41154156
rb_event_flag_t enabled_events;
4116-
rb_event_flag_t local_events = iseq->aux.exec.local_hooks ? iseq->aux.exec.local_hooks->events : 0;
4157+
rb_hook_list_t *local_hooks = rb_iseq_local_hooks(iseq, GET_RACTOR(), false);
4158+
rb_event_flag_t local_events = local_hooks ? local_hooks->events : 0;
41174159
((rb_iseq_t *)iseq)->aux.exec.global_trace_events = turnon_events;
41184160
enabled_events = add_bmethod_events(turnon_events | local_events);
41194161

@@ -4129,6 +4171,7 @@ void rb_vm_cc_general(const struct rb_callcache *cc);
41294171
static bool
41304172
clear_attr_cc(VALUE v)
41314173
{
4174+
ASSERT_vm_locking_with_barrier();
41324175
if (imemo_type_p(v, imemo_callcache) && vm_cc_ivar_p((const struct rb_callcache *)v)) {
41334176
rb_vm_cc_general((struct rb_callcache *)v);
41344177
return true;
@@ -4141,6 +4184,7 @@ clear_attr_cc(VALUE v)
41414184
static bool
41424185
clear_bf_cc(VALUE v)
41434186
{
4187+
ASSERT_vm_locking_with_barrier();
41444188
if (imemo_type_p(v, imemo_callcache) && vm_cc_bf_p((const struct rb_callcache *)v)) {
41454189
rb_vm_cc_general((struct rb_callcache *)v);
41464190
return true;
@@ -4166,7 +4210,10 @@ clear_attr_ccs_i(void *vstart, void *vend, size_t stride, void *data)
41664210
void
41674211
rb_clear_attr_ccs(void)
41684212
{
4169-
rb_objspace_each_objects(clear_attr_ccs_i, NULL);
4213+
RB_VM_LOCKING() {
4214+
rb_vm_barrier();
4215+
rb_objspace_each_objects(clear_attr_ccs_i, NULL);
4216+
}
41704217
}
41714218

41724219
static int
@@ -4185,6 +4232,7 @@ clear_bf_ccs_i(void *vstart, void *vend, size_t stride, void *data)
41854232
void
41864233
rb_clear_bf_ccs(void)
41874234
{
4235+
ASSERT_vm_locking_with_barrier();
41884236
rb_objspace_each_objects(clear_bf_ccs_i, NULL);
41894237
}
41904238

@@ -4214,7 +4262,10 @@ trace_set_i(void *vstart, void *vend, size_t stride, void *data)
42144262
void
42154263
rb_iseq_trace_set_all(rb_event_flag_t turnon_events)
42164264
{
4217-
rb_objspace_each_objects(trace_set_i, &turnon_events);
4265+
RB_VM_LOCKING() {
4266+
rb_vm_barrier();
4267+
rb_objspace_each_objects(trace_set_i, &turnon_events);
4268+
}
42184269
}
42194270

42204271
VALUE

iseq.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,12 @@ const rb_iseq_t *rb_iseq_ibf_load_bytes(const char *cstr, size_t);
190190
VALUE rb_iseq_ibf_load_extra_data(VALUE str);
191191
void rb_iseq_init_trace(rb_iseq_t *iseq);
192192
int rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line, bool target_bmethod);
193-
int rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval);
193+
int rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval, rb_ractor_t *r);
194194
const rb_iseq_t *rb_iseq_load_iseq(VALUE fname);
195195
const rb_iseq_t *rb_iseq_compile_iseq(VALUE str, VALUE fname);
196196
int rb_iseq_opt_frozen_string_literal(void);
197+
rb_hook_list_t *rb_iseq_local_hooks(const rb_iseq_t *iseq, rb_ractor_t *r, bool create);
198+
197199

198200
#if VM_INSN_INFO_TABLE_IMPL == 2
199201
unsigned int *rb_iseq_insns_info_decode_positions(const struct rb_iseq_constant_body *body);

method.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ typedef struct rb_method_refined_struct {
166166

167167
typedef struct rb_method_bmethod_struct {
168168
VALUE proc; /* should be marked */
169-
struct rb_hook_list_struct *hooks;
170169
rb_serial_t defined_ractor_id;
170+
unsigned int local_hooks_cnt;
171171
} rb_method_bmethod_t;
172172

173173
enum method_optimized_type {
@@ -208,6 +208,8 @@ struct rb_method_definition_struct {
208208
};
209209

210210
struct rb_id_table;
211+
struct rb_ractor_struct;
212+
struct rb_hook_list_struct;
211213

212214
typedef struct rb_method_definition_struct rb_method_definition_t;
213215
STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body) <= 8);
@@ -267,5 +269,8 @@ void rb_vm_delete_cc_refinement(const struct rb_callcache *cc);
267269
void rb_clear_method_cache(VALUE klass_or_module, ID mid);
268270
void rb_clear_all_refinement_method_cache(void);
269271
void rb_invalidate_method_caches(struct rb_id_table *cm_tbl, VALUE cc_tbl);
272+
struct rb_hook_list_struct *rb_method_def_local_hooks(rb_method_definition_t *def, struct rb_ractor_struct *cr, bool create);
273+
void rb_method_definition_addref(rb_method_definition_t *def);
274+
void rb_method_definition_release(rb_method_definition_t *def);
270275

271276
#endif /* RUBY_METHOD_H */

0 commit comments

Comments
 (0)