Skip to content

Commit 5c2be17

Browse files
committed
Only localize TracePoint to thread if specified
TracePoint#enable takes three arguments: * target: a target scope in which to enable tracing. In CRuby, this must be convertable to an instruction sequence, presumably so the instructions can be modified to trigger the event hooks. * target_line: a specific line number within the target scope to trace, I presume. The documentation does not specifically discuss this parameter. * target_thread: the sole thread on which this TracePoint should be enabled. By default, it is enabled globally. Our original code always initialized the TracePoint as being associated with the current thread at construction time, which caused it to only be triggered for events in that thread. This patch defers setting the trace thread until TracePoint#enable is called with a non-nil target_thread argument. Note that this patch also allows passing the target and target_line arguments, but they are ignored and will warn if non-nil. Fixes jruby#8697
1 parent 0fd67cc commit 5c2be17

1 file changed

Lines changed: 102 additions & 51 deletions

File tree

core/src/main/java/org/jruby/ext/tracepoint/TracePoint.java

Lines changed: 102 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.jruby.RubyClass;
88
import org.jruby.RubyObject;
99
import org.jruby.RubySymbol;
10+
import org.jruby.RubyThread;
1011
import org.jruby.anno.JRubyMethod;
1112
import org.jruby.exceptions.RaiseException;
1213
import org.jruby.runtime.Block;
@@ -77,60 +78,70 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] _events, fina
7778

7879
if (!block.isGiven()) throw argumentError(context, "must be called with a block");
7980

80-
final ThreadContext threadToTrace = context;
81-
hook = new EventHook() {
82-
@Override
83-
public void event(ThreadContext context, RubyEvent event, String file, int line, String name, IRubyObject type) {
84-
if (!enabled || threadToTrace != context || context.isWithinTrace()) return;
81+
hook = new TracePointEventHook(eventSet, block);
82+
83+
return context.nil;
84+
}
8585

86-
int savedCallInfo = resetCallInfo(context);
86+
private class TracePointEventHook extends EventHook {
87+
protected ThreadContext threadToTrace;
88+
private final Block block;
89+
private final EnumSet<RubyEvent> eventSet;
8790

88-
synchronized (this) {
89-
inside = true;
91+
public TracePointEventHook(EnumSet<RubyEvent> eventSet, Block block) {
92+
this.block = block;
93+
this.eventSet = eventSet;
94+
}
9095

91-
if (file == null) file = "(ruby)";
92-
if (type == null) type = context.fals;
96+
@Override
97+
public void event(ThreadContext context, RubyEvent event, String file, int line, String name, IRubyObject type) {
98+
if (!enabled || (threadToTrace != null && threadToTrace != context) || context.isWithinTrace()) return;
9399

94-
IRubyObject binding;
95-
if (event == RubyEvent.THREAD_BEGIN || event == RubyEvent.THREAD_END) {
96-
binding = context.nil;
97-
} else {
98-
binding = RubyBinding.newBinding(context.runtime, context.currentBinding());
99-
}
100+
int savedCallInfo = resetCallInfo(context);
100101

101-
context.preTrace();
102+
synchronized (this) {
103+
inside = true;
102104

103-
// FIXME: get return value
104-
update(event.getName(), file, line, name, type, context.getErrorInfo(), context.nil, binding);
105+
if (file == null) file = "(ruby)";
106+
if (type == null) type = context.fals;
105107

106-
try {
107-
block.yieldSpecific(context, TracePoint.this);
108-
} finally {
109-
update(null, null, line, null, context.nil, context.nil, context.nil, context.nil);
110-
context.postTrace();
111-
inside = false;
112-
context.callInfo = savedCallInfo;
113-
}
108+
IRubyObject binding;
109+
if (event == RubyEvent.THREAD_BEGIN || event == RubyEvent.THREAD_END) {
110+
binding = context.nil;
111+
} else {
112+
binding = RubyBinding.newBinding(context.runtime, context.currentBinding());
114113
}
115-
}
116114

117-
@Override
118-
public void eventHandler(ThreadContext context, String eventName, String file, int line, String name, IRubyObject type) {
119-
event(context, RubyEvent.fromName(eventName), file, line, name, type);
120-
}
115+
context.preTrace();
121116

122-
@Override
123-
public boolean isInterestedInEvent(RubyEvent event) {
124-
return eventSet.contains(event);
125-
}
117+
// FIXME: get return value
118+
update(event.getName(), file, line, name, type, context.getErrorInfo(), context.nil, binding);
126119

127-
@Override
128-
public EnumSet<RubyEvent> eventSet() {
129-
return eventSet;
120+
try {
121+
block.yieldSpecific(context, TracePoint.this);
122+
} finally {
123+
update(null, null, line, null, context.nil, context.nil, context.nil, context.nil);
124+
context.postTrace();
125+
inside = false;
126+
context.callInfo = savedCallInfo;
127+
}
130128
}
131-
};
132-
133-
return context.nil;
129+
}
130+
131+
@Override
132+
public void eventHandler(ThreadContext context, String eventName, String file, int line, String name, IRubyObject type) {
133+
event(context, RubyEvent.fromName(eventName), file, line, name, type);
134+
}
135+
136+
@Override
137+
public boolean isInterestedInEvent(RubyEvent event) {
138+
return eventSet.contains(event);
139+
}
140+
141+
@Override
142+
public EnumSet<RubyEvent> eventSet() {
143+
return eventSet;
144+
}
134145
}
135146

136147
@JRubyMethod
@@ -149,12 +160,48 @@ public IRubyObject defined_class(ThreadContext context) {
149160

150161
@JRubyMethod
151162
public IRubyObject disable(ThreadContext context, Block block) {
152-
return doToggle(context, block, false);
163+
return doToggle(context, null, block, false);
153164
}
154-
165+
155166
@JRubyMethod
156167
public IRubyObject enable(ThreadContext context, Block block) {
157-
return doToggle(context, block, true);
168+
return doToggle(context, null, block, true);
169+
}
170+
171+
@JRubyMethod
172+
public IRubyObject enable(ThreadContext context, IRubyObject target, Block block) {
173+
// TODO: implement target
174+
if (!target.isNil()) {
175+
context.runtime.getWarnings().warning("target argument to TracePoint.enable is unsupported");
176+
}
177+
178+
return doToggle(context, null, block, true);
179+
}
180+
181+
@JRubyMethod
182+
public IRubyObject enable(ThreadContext context, IRubyObject target, IRubyObject targetLine, Block block) {
183+
// TODO: implement target, target_line
184+
if (!target.isNil() || !targetLine.isNil()) {
185+
context.runtime.getWarnings().warning("target and target_line arguments to TracePoint.enable are unsupported");
186+
}
187+
188+
return doToggle(context, null, block, true);
189+
}
190+
191+
@JRubyMethod
192+
public IRubyObject enable(ThreadContext context, IRubyObject target, IRubyObject targetLine, IRubyObject _targetThread, Block block) {
193+
// TODO: implement target, target_line
194+
if (!target.isNil() || !targetLine.isNil()) {
195+
context.runtime.getWarnings().warning("target and target_line arguments to TracePoint.enable are unsupported");
196+
}
197+
198+
RubyThread targetThread = null;
199+
if (_targetThread != asSymbol(context, "default")) {
200+
if (!(_targetThread instanceof RubyThread th)) throw argumentError(context, "target must be a Thread");
201+
targetThread = th;
202+
}
203+
204+
return doToggle(context, targetThread, block, true);
158205
}
159206

160207
@JRubyMethod(name = "enabled?")
@@ -244,32 +291,36 @@ private void update(String eventName, String file, int line, String name, IRubyO
244291
this.binding = binding;
245292
}
246293

247-
private synchronized IRubyObject doToggle(ThreadContext context, Block block, boolean toggle) {
294+
private synchronized IRubyObject doToggle(ThreadContext context, RubyThread _targetThread, Block block, boolean toggle) {
248295
if (block.isGiven()) {
249296
boolean old = enabled;
250297
try {
251-
updateEnabled(context, toggle);
298+
updateEnabled(context, _targetThread, toggle);
252299

253300
return block.yieldSpecific(context);
254301
} finally {
255-
updateEnabled(context, old);
302+
updateEnabled(context, _targetThread, old);
256303
}
257304
}
258305

259306
IRubyObject old = asBoolean(context, enabled);
260-
updateEnabled(context, toggle);
307+
updateEnabled(context, _targetThread, toggle);
261308

262309
return old;
263310
}
264311

265-
public void updateEnabled(ThreadContext context, boolean toggle) {
312+
public void updateEnabled(ThreadContext context, RubyThread targetThread, boolean toggle) {
266313
if (toggle == enabled) return; // don't re-add or re-remove hook
267314

268315
enabled = toggle;
269316

270317
if (toggle) {
318+
if (targetThread != null && !targetThread.isNil()) {
319+
hook.threadToTrace = targetThread.getContext();
320+
}
271321
context.traceEvents.addEventHook(context, hook);
272322
} else {
323+
hook.threadToTrace = null;
273324
context.traceEvents.removeEventHook(hook);
274325
}
275326
}
@@ -282,7 +333,7 @@ private static JavaSites.TracePointSites sites(ThreadContext context) {
282333
return context.sites.TracePoint;
283334
}
284335

285-
private EventHook hook;
336+
private TracePointEventHook hook;
286337
private volatile boolean enabled = false;
287338
private String eventName;
288339
private String file;

0 commit comments

Comments
 (0)