[ruby-core:108449] [Ruby master Bug#18730] Double `return` event handling with different tracepoints
From:
"alanwu (Alan Wu)" <noreply@...>
Date:
2022-04-30 00:50:11 UTC
List:
ruby-core #108449
Issue #18730 has been updated by alanwu (Alan Wu).
So having just read the code, I understand why you are seeing this behavior.
We run global handlers before local handlers, so if you have a global
handler G and a local handler L, and G enables L, they both run. It is weird
because this seems to be the only situation where two handlers interact.
Here is a script to show all 4 possible choices for global versus local for
the two interacting handlers:
```ruby
(0..0b11).each do |mode|
Class.new do
def foo
1
end
me = instance_method(:foo)
one_enable = mode[0] == 1 ? { target: me } : {}
two_enable = mode[1] == 1 ? { target: me } : {}
one_ran = false
two_ran = false
two = TracePoint.new(:return) do
two_ran = true
two.disable
end
one = TracePoint.new(:return) do
one_ran = true
two.enable(**two_enable)
one.disable
end
one.enable(**one_enable)
new.foo
p [mode, one_ran, two_ran]
end
end
```
The output has evolved over time:
```
ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-darwin19]
[0, true, true]
[1, true, false]
[2, true, true]
[3, true, false]
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-darwin19]
[0, true, false]
[1, true, false]
[2, true, false]
[3, true, false]
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
[0, true, false]
[1, true, false]
[2, true, true]
[3, true, false]
# 3.1 same as 3.0
```
I suppose 2.7 behavior is the most consistent.
----------------------------------------
Bug #18730: Double `return` event handling with different tracepoints
https://bugs.ruby-lang.org/issues/18730#change-97487
* Author: hurricup (Alexandr Evstigneev)
* Status: Open
* Priority: Normal
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
I'm not sure if this is a bug or intentional behavior, but feels a bit unexpected. Ruby 3.0.x, 3.1.x affected.
Here is the script demonstrating the issue:
```
def bar
42 #bp here
end
tp_line = TracePoint.new(:line) do |tp0|
puts "Got line event from #{tp0.path}:#{tp0.lineno}"
tp_multi1 = TracePoint.new(:return, :b_return, :line) do |tp|
if tp.lineno == 3
puts "Got first return `#{tp.event}` from #{tp.path}:#{tp.lineno}"
tp.disable
# tp0.disable # uncommenting this line changes things to the more expected
tp_multi2 = TracePoint.new(:return, :b_return, :line) do |tps|
puts "Got second return `#{tps.event}` from #{tps.path}:#{tps.lineno}"
end
tp_multi2.enable(target: RubyVM::InstructionSequence.of(method :bar))
end
end
tp_multi1.enable
end
tp_line.enable(target: RubyVM::InstructionSequence.of(method :bar))
bar
```
1. We set a line TP to the `bar` method `iseq` (consider it a line breakpoint)
2. When line event is triggered we setting another untargeted tracepoint for the same method, to catch `line`, `return` and `b_return` events (consider it attempt to step into something)
3. When return event of the `bar` method is triggered, we disabling second tracepoint and setting another one, targeted to the same method and multiple events.
Output i get:
```
Got line event from /home/hurricup/test.rb:2
Got first return `return` from /home/hurricup/test.rb:3
Got second return `return` from /home/hurricup/test.rb:3
```
The questions are:
1. why return triggered on the second tracepoint, when we already handeled it?
2. why disabling line tracepoint changes behavior?
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>