Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception#set_backtrace accept arrays of Backtrace::Location #10017

Merged
merged 1 commit into from Mar 14, 2024

Conversation

casperisfine
Copy link
Contributor

@casperisfine casperisfine commented Feb 19, 2024

[Feature #13557]

Setting the backtrace with an array of strings is lossy. The resulting exception will return nil on #backtrace_locations.

By accepting an array of Backtrace::Location instance, we can rebuild a Backtrace instance and have a fully functioning Exception.

NOTE: Exception's instance variables id_bt and id_bt_locations are a bit inconsistent right now and would deserve a cleanup.

Copy link
Contributor

@KJTsanaktsidis KJTsanaktsidis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks reasonable to me.

NOTE: Exception's instance variables id_bt and id_bt_locations are a bit inconsistent right now and would deserve a cleanup.

Agree this could use some love. Why do we even have both given that exc_backtrace seems happy enough for either strings or Thread::Backtrace::Locations to be in id_bt?

vm_backtrace.c Outdated
RB_GC_GUARD(locobj);
}

RB_GC_GUARD(btobj);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's unnecessary to guard this if you're returning it since btobj obviously must either be in the stack or register state for the entirety of this frame since it's being returned?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I started to be a bit liberal with GC_GUARD...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I learn about how compilers work the more scared I am that on day some compiler will do something really clever like store a pointer to the middle of a struct in a register, rather than the actual VALUE, and break everything :/

I wonder if anybody has tried compiling Ruby with LTO, maybe aggressive enough cross-TU inlining could make this happen…

@casperisfine
Copy link
Contributor Author

Why do we even have both given that exc_backtrace seems happy enough for either strings or Thread::Backtrace::Locations to be in id_bt?

Yeah, that surprised me as well, I'd need to do some spelunking to see when it was introduced.

@casperisfine casperisfine force-pushed the set-backtrace-locations branch 5 times, most recently from 3be24b7 to b25eb28 Compare February 25, 2024 15:55
@casperisfine
Copy link
Contributor Author

NOTE: Exception's instance variables id_bt and id_bt_locations are a bit inconsistent right now and would deserve a cleanup.

So I dug a bit more into this since various Exceptions and Marshal related tests were failing.

I used the following test script:

begin
  raise "foo"
rescue => exc
end
p [:orig, exc.backtrace, :bt, exc.bt, :bt_locations, exc.bt_locations]

begin
  raise StandardError, "second", ["second_line"]
rescue => exc2
end
p [:raise, exc2.backtrace, :bt, exc2.bt, :bt_locations, exc2.bt_locations]

exc.set_backtrace("foo")
p [:set_backtrace, exc.backtrace, :bt, exc.bt, :bt_locations, exc.bt_locations]
[:orig, ["./test.rb:2:in '<main>'"], :bt, #<Thread::Backtrace:0x00000001037321d0>, :bt_locations, #<Thread::Backtrace:0x00000001037321d0>]
[:raise, ["second_line"], :bt, ["second_line"], :bt_locations, nil]
[:set_backtrace, ["foo"], :bt, ["foo"], :bt_locations, #<Thread::Backtrace:0x00000001037321d0>]

So it's a bit weird, but bt is either the original Backtrace or an array of strings, and bt_locations is either nil if the exception never had a real backtrace in the first place, or it's original Backtrace.

This would be hard to cleanup because a bunch of test asserts on the Marshal format of Exception, so changing the type of either ivar would break backward compatibility. And in the end leaning into that format, really cut the PR size, so...

@ko1 ko1 added the Playground Experimental: Post link to try Ruby with PR changes label Mar 14, 2024
Copy link

github-actions bot commented Mar 14, 2024

Try on Playground: https://ruby.github.io/play-ruby?run=8278867369
This is an automated comment by pr-playground.yml workflow.

@ko1
Copy link
Contributor

ko1 commented Mar 14, 2024

def foo
  raise
end

begin
  foo
rescue => e
  e.set_backtrace(caller_locations + [""])
end

=begin
-e:8:in 'Exception#set_backtrace': wrong argument type String (expected frame_info) (TypeError)

e.set_backtrace(caller_locations + [""])
^^^^^^^^^^^^^^^^^^^^^^^
from -e:8:in '<main>'
from -e:5:in '<main>'
-e:2:in 'Object#foo': unhandled exception
from -e:6:in '<main>'
=end

----

def foo
  raise
end

begin
  foo
rescue => e
  e.set_backtrace([""] + caller_locations)
end

=begin
-e:8:in 'Exception#set_backtrace': backtrace must be an Array of String or an Array of Thread::Backtrace::Location (TypeError)

e.set_backtrace([""] + caller_locations)
^^^^^^^^^^^^^^^^^^^^^^^
from -e:8:in '<main>'
from -e:5:in '<main>'
-e:2:in 'Object#foo': unhandled exception
from -e:6:in '<main>'
=end

can we make them same error?

'Exception#set_backtrace': backtrace must be an Array of String or an Array of Thread::Backtrace::Location (TypeError)

@casperisfine
Copy link
Contributor Author

can we make them same error?

I think so, I'll try.

@casperisfine casperisfine force-pushed the set-backtrace-locations branch 2 times, most recently from 0612524 to 2d9f192 Compare March 14, 2024 08:10
@casperisfine
Copy link
Contributor Author

Alright, the error message is now consistent. I'll merge when CI pass.

@byroot byroot enabled auto-merge (rebase) March 14, 2024 08:11
[Feature #13557]

Setting the backtrace with an array of strings is lossy. The resulting
exception will return nil on `#backtrace_locations`.

By accepting an array of `Backtrace::Location` instance, we can rebuild
a `Backtrace` instance and have a fully functioning Exception.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
auto-merge was automatically disabled March 14, 2024 10:06

Head branch was pushed to by a user without write access

@byroot byroot enabled auto-merge (rebase) March 14, 2024 10:07
@byroot byroot merged commit 315bde5 into ruby:master Mar 14, 2024
101 of 102 checks passed
@ko1
Copy link
Contributor

ko1 commented Mar 14, 2024

Could you modify docs and maybe NEWS?

casperisfine pushed a commit to Shopify/ruby that referenced this pull request Mar 14, 2024
Followup: ruby#10017

[Feature #13557]
@casperisfine
Copy link
Contributor Author

Could you modify docs and maybe NEWS?

Of course: #10255

Let me know what you think.

casperisfine pushed a commit to Shopify/ruby that referenced this pull request Mar 14, 2024
byroot added a commit that referenced this pull request Mar 18, 2024
Followup: #10017

[Feature #13557]
andrykonchin pushed a commit to ruby/spec that referenced this pull request Apr 1, 2024
artur-intech pushed a commit to artur-intech/ruby that referenced this pull request Apr 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Playground Experimental: Post link to try Ruby with PR changes
4 participants