Backport #7624

How to handle exceptions raised in event hook?

Added by Koichi Sasada over 1 year ago. Updated over 1 year ago.

[ruby-core:51128]
Status:Closed
Priority:Normal
Assignee:Usaku NAKAMURA

Description

The following program raises an exception in `return' event hook.

###
class FOO < RuntimeError; end
class BAR < RuntimeError; end
def m1; m2; end
def m2; m3; end
def m3; raise FOO; end
settracefunc(lambda{|*args|
if args[0] == 'return'
p args
raise BAR # raise in trace_func
end})
begin
m1
rescue => e
p e
ensure
p :ensure
end
###

And this program cause strange behavior in each versions.


2.0 trunk

ruby 2.0.0dev (2012-12-21 trunk 38515) [i386-mswin32_100]
["return", "test.rb", 5, :m3, #Binding:0x9781c8, Object]
test.rb:9:in block in <main>': BAR (BAR)
from test.rb:5:in
m3'
from test.rb:4:in m2'
from test.rb:3:in
m1'
from test.rb:12:in `'

1.9.3p332 (2012-11-15 revision 37660)

ruby 1.9.3p332 (2012-11-15 revision 37660) [i386-mswin32_100]
["return", "test.rb", 5, :m3, #Binding:0x2682980, Object]
["return", "test.rb", 5, :m3, #Binding:0x268278c, Object]
... # infinite loop

1.8.7

ruby 1.8.7 (2012-06-29 patchlevel 370) [i386-cygwin]
["return", "test.rb", 5, :m3, #Binding:0x80057adc, Object]
["return", "test.rb", 5, :m2, #Binding:0x80057a64, Object]
["return", "test.rb", 5, :m1, #Binding:0x80057884, Object]
test.rb:13: [BUG] unexpected local variable assignment
ruby 1.8.7 (2012-06-29 patchlevel 370) [i386-cygwin]

  1 [sig] ruby 7388 open_stackdumpfile: Dumping stack trace to ruby.exe.stackdump

[BUG] !! and line number is not correct.


JRuby

$ bin/jruby --debug -v test.rb
jruby 1.7.2.dev (1.9.3p327) 2012-12-25 dc3af39 on OpenJDK Client VM 1.6.0_18-b18 [linux-i386]
["return", "test.rb", 5, :m3, #Binding:0x1f7896f, Object]
["return", "test.rb", 4, :m2, #Binding:0x1abbec4, Object]
["return", "test.rb", 3, :m1, #Binding:0x2c03ff, Object]
#
:ensure


Ancient Ruby

1.6.5

$ versions/install-tagsv165/bin/ruby 'test.rb' 2>&1

["return", "test.rb", 3, :m3, #Binding:0xf755c7d0, Object]
["return", "test.rb", 12, :m2, #Binding:0xf755c5c8, Object]
["return", "test.rb", 5, :m1, #Binding:0xf755c424, Object]
#
:ensure

1.8.6

$ versions/install-tagsv186420_/bin/ruby 'test.rb' 2>&1

["return", "test.rb", 5, :m3, #Binding:0x7f9bc3032530, Object]
["return", "test.rb", 4, :m2, #Binding:0x7f9bc3032440, Object]
["return", "test.rb", 3, :m1, #Binding:0x7f9bc3032080, Object]
test.rb:13: [BUG] unexpected local variable assignment
ruby 1.8.6 (2010-09-02) [x86_64-linux]


1.8 and before, exceptions from trace_fook are raised as normal exceptions.
JRuby (with --debug) emulates this behavior correctly.

Also CRuby 1.9 tried to handle exceptions as normal exceptions.
However, it becomes infinite loop.

(1) exception are occurred at m3

m1
m2
m3
raise

(2) pop stack frames

m1
m2
m3

(3) try m3 return hook

m1
m2
m3
m3returnhook

(4) exception at m3returnhook

m1
m2
m3
m3returnhook
raise

(5) pop stack frames and go to (3)

m1
m2
m3

MRI 1.8 invokes hooks after pop m3.
However, MRI 1.9 invoke hooks before pop m3.
This is why infinite loop was made.


To solve this issue, current 2.0 pop all stack frames before return hook (when handling exception).

Hoever, `ensure' etc was skipped. This is also issues.
[Bug 7622], [Bug 7592]

This corner case is confusing.


There are several solution about it.

(1) Emulate 1.8's behavior.
Which JRuby does :)
No compatibility issue.

(2) Do not propagate any errors from trace block

Like thread, prohibit all of errors and escape (such as throw).

(2') Output errors if any errors are occurred

(3) Exit thread or program which current trunk done.


I think only few people use settracefunc()
because send bug report on it.

(3) seems bad because ensure clause is ignored.
This means current trunk is buggy.

(1) is best way because no incompatibility
and no discussion is needed.

(2) (and (2')) has incompatible behavior from 1.9 and 1.8.
But this approach seems good because trace hooks should not affect
main program.

I think (1) and (2) is valuable to discuss.


I'm sorry to propose such issue includes compatibility.
I want to make it clear current status and choose correct way.

193backport_exception_in_settracefunc.patch Magnifier (5.52 KB) Koichi Sasada, 12/26/2012 05:23 PM

Associated revisions

Revision 38778
Added by Usaku NAKAMURA over 1 year ago

[Backport #7624]

  • vmtrace.c (rbthreadptrexeceventhooks): added a parameter to pop
    a frame before JUMP
    TAG() if exception occurred. This change fix bug
    of Ruby 1.9. [ruby-trunk - Bug #7624]

  • vmcore.h (EXECEVENTHOOKANDPOPFRAME): add to use
    `rbthreadptrexeceventhooks()' with the pop flag.

  • vm.c (vmexec): use EXECEVENTHOOKANDPOPFRAME() while exception
    handling. While exception hadnling, if an exception is raised in
    hooks, need to pop current frame and raise this raised exception by
    hook.

  • bootstraptest/test_flow.rb: add a test.

History

#1 Updated by Koichi Sasada over 1 year ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r38601.
Koichi, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • vmtrace.c (rbthreadptrexeceventhooksandpopframe): pop a frame before JUMP_TAG() if exception occurred. This change fix bug of Ruby 1.9. [ruby-trunk - Bug #7624]
  • vmcore.h (EXECEVENTHOOKANDPOPFRAME): add to use `rbthreadptrexeceventhooksandpop_frame()'.
  • vm.c (vmexec): use EXECEVENTHOOKANDPOPFRAME() while exception handling. While exception hadnling, if an exception is raised in hooks, need to pop current frame and raise this raised exception by hook.
  • test/ruby/test_settracefunc.rb: add a test.

#2 Updated by Koichi Sasada over 1 year ago

I fixed this issue as 1.8 (1.6, JRuby) compatible.

1.9 also needs backport.

#3 Updated by Koichi Sasada over 1 year ago

I tried to move this ticket to backport193, but I can't move it (system error?).
I attached a patch for 1.9.3. Please try it.

#4 Updated by Koichi Sasada over 1 year ago

  • Tracker changed from Bug to Backport
  • Project changed from ruby-trunk to Backport93
  • Category deleted (core)
  • Target version deleted (2.0.0)

#5 Updated by Usaku NAKAMURA over 1 year ago

  • Status changed from Closed to Assigned
  • Assignee changed from Koichi Sasada to Usaku NAKAMURA

#6 Updated by Usaku NAKAMURA over 1 year ago

  • Status changed from Assigned to Closed

This issue was solved with changeset r38778.
Koichi, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


[Backport #7624]

  • vmtrace.c (rbthreadptrexeceventhooks): added a parameter to pop
    a frame before JUMP
    TAG() if exception occurred. This change fix bug
    of Ruby 1.9. [ruby-trunk - Bug #7624]

  • vmcore.h (EXECEVENTHOOKANDPOPFRAME): add to use
    `rbthreadptrexeceventhooks()' with the pop flag.

  • vm.c (vmexec): use EXECEVENTHOOKANDPOPFRAME() while exception
    handling. While exception hadnling, if an exception is raised in
    hooks, need to pop current frame and raise this raised exception by
    hook.

  • bootstraptest/test_flow.rb: add a test.

Also available in: Atom PDF