Project

General

Profile

Actions

Feature #13557

closed

there's no way to pass backtrace locations as a massaged backtrace

Added by sylvain.joyeux (Sylvain Joyeux) over 7 years ago. Updated 9 months ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:81102]

Description

When re-raising exceptions, it is sometimes useful to "massage" the backtrace (especially in DSLs).

There is currently no way to do it using only backtrace locations. This causes the new exception to have #backtrace_locations return nil, and thus makes backtrace_locations unreliable as a whole.

Example:

def test
    raise ArgumentError, "", caller_locations
end

begin
    test
rescue ArgumentError => e
    p e.backtrace_locations
end

attempting to pass caller_location to Kernel#raise in the test method fails with bla.rb:2:in `set_backtrace': backtrace must be Array of String (TypeError)

Updated by sylvain.joyeux (Sylvain Joyeux) over 7 years ago

Did a mistake. The code example uses caller_locations (and therefore causes the TypeError exception) while I meant it to use caller and therefore cause e.backtrace_locations to be nil

Updated by Eregon (Benoit Daloze) over 7 years ago

+1 This would be a nice feature and let the VM still keep the backtrace information in an flexible way (instead of dumping it to a String like Exception#backtrace).

Actions #3

Updated by jeremyevans0 (Jeremy Evans) over 5 years ago

  • Tracker changed from Bug to Feature
  • ruby -v deleted (ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux], ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux])
  • Backport deleted (2.2: UNKNOWN, 2.3: UNKNOWN, 2.4: UNKNOWN)

Updated by byroot (Jean Boussier) 10 months ago

I'll try to find some time to implement this in the near future, because I just ran into this today, and resorted to the following hack:

unless exception.backtrace
  begin
    raise exception
  rescue exception.class => raised_exception
    raised_exception.backtrace.shift
    raised_exception.backtrace_locations.shift
    exception = raised_exception
  end
end

I think both Kernel#raise and Exception#set_backtrace would need to accept array of Thread::Backtrace::Location.

Updated by Dan0042 (Daniel DeLorme) 10 months ago

I think both Kernel#raise and Exception#set_backtrace would need to accept array of Thread::Backtrace::Location.

That's an idea I fully support. Although when I need to modify an error's backtrace 95% of the time it's because I want to skip the first few frames, like raise Error, "message", caller[3..-1]. So it would be nice to have a shortcut idiom for this, like perhaps raise Error, "message", skip: 3

Updated by byroot (Jean Boussier) 10 months ago

Proposed patch here if someone feels like reviewing it: https://github.com/ruby/ruby/pull/10017

Updated by byroot (Jean Boussier) 10 months ago

So it would be nice to have a shortcut idiom for this, like perhaps raise Error, "message", skip: 3

With my patch it would be:

raise Error, "message", caller_locations(3)

Which I think is simple enough to not warrant and extra skip: parameter to raise.

Updated by Dan0042 (Daniel DeLorme) 10 months ago

byroot (Jean Boussier) wrote in #note-7:

Which I think is simple enough to not warrant and extra skip: parameter to raise.

Good point, I'm convinced.

Actions #9

Updated by nobu (Nobuyoshi Nakada) 9 months ago

  • Description updated (diff)

Updated by ko1 (Koichi Sasada) 9 months ago

I'm not against but could you summarize usages?

Updated by byroot (Jean Boussier) 9 months ago

@ko1 (Koichi Sasada) all the same use cases arrays of strings are currently used for:

E.g. raising an error with an existing backtrace:

raise NicerError, "some message", original_error.backtrace_locations

Or with some frames skipped:

raise SomeError, "some message", caller_locations(2) # skip 2 frames

Creating an exception with a thread backtrace:


async_error = SomeError.new("thread timed out")
async_error.set_backtrace(other_thread.backtrace_locations)
report(async_error)

And probably plenty others I'm not thinking of. This is only an improved version of passing set_backtrace(exc.backtrace).

Actions #12

Updated by byroot (Jean Boussier) 9 months ago

  • Status changed from Open to Closed

Applied in changeset git|315bde5a0f95562f58405a43456ec6715ef20d32.


Exception#set_backtrace accept arrays of Backtrace::Location

[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é

Actions

Also available in: Atom PDF

Like1
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0