Improve default backtraces
--------------------------

                 Key: JRUBY-5504
                 URL: http://jira.codehaus.org/browse/JRUBY-5504
             Project: JRuby
          Issue Type: Improvement
            Reporter: Charles Oliver Nutter
            Assignee: Thomas E Enebo
             Fix For: JRuby 1.7


I'm interested in improving the default JRuby backtrace. The current backtrace 
is identical to MRI's, and newer traces like Rubinius's have shown there are 
nicer ways to render the backtrace.

This bug will track ideas on how to improve the backtrace to be more useful and 
readable for users.

To start, here's the backtrace as of JRuby 1.6.0.RC2:

{noformat}
~/projects/jruby ➔ jruby -e "1.times { catch(:x) { 
File.open('build.xml') {|f| 1 / 0} } }"
org/jruby/RubyFixnum.java:490:in `op_div': divided by 0 (ZeroDivisionError)
        from -e:1:in `__file__'
        from org/jruby/RubyIO.java:1078:in `open'
        from -e:1:in `__file__'
        from org/jruby/RubyKernel.java:1191:in `rbCatch'
        from -e:1:in `__file__'
        from org/jruby/RubyFixnum.java:252:in `times'
        from -e:1:in `(root)'
{noformat}

A few items of note:

# The full .java filename plus line number are present for known entry points 
from Ruby into Java-based core classes.
# For core classes, the Java method name is used instead of the bound Ruby name 
(rbCatch, op_div)
# The interesting bits - the file and line number - are scattered back and 
forth over each line, and especially different for core class calls.

Here's the Rubinius trace (and a screenshot to show the color highlighting it 
does when outputting to a TTY: http://i.min.us/ijfo16.png):

{noformat}
~/projects/jruby ➔ rbx -e "1.times { catch(:x) { File.open('build.xml') 
{|f| 1 / 0} } }"
An exception occurred evaluating command line code
    divided by 0 (ZeroDivisionError)

Backtrace:
            Object#__script__ {} at -e:1
                         IO.open at kernel/common/io.rb:260
            Object#__script__ {} at -e:1
         Kernel(Object)#catch {} at kernel/common/throw_catch.rb:34
  Rubinius::ThrownValue.register at kernel/common/throw_catch.rb:13
            Kernel(Object)#catch at kernel/common/throw_catch.rb:33
            Object#__script__ {} at -e:1
           Integer(Fixnum)#times at kernel/common/integer.rb:138
            Object#__script__ {} at -e:1
   Kernel(Rubinius::Loader)#eval at kernel/common/eval.rb:112
          Rubinius::Loader#evals at kernel/loader.rb:420
           Rubinius::Loader#main at kernel/loader.rb:525
           Rubinius::Loader.main at kernel/loader.rb:552
               Object#__script__ at kernel/loader.rb:564
{noformat}

The key details here:

* Top line to indicate an exception occurred and where it happened (unsure what 
other values there are besides "command line code"
* Exception message and type on the second line
* Blank line to offset backtrace, followed by "Backtrace:" header

Then the backtrace lines themselves:

* Color highlighted to emphasize user code but show internal code lines (Note 
that C++ source lines are *not* shown...only .rb lines)
* Module or class the method came from (with included class for modules in 
parens) starts the line
* Script body is shown as "__script__" (similar to JRuby's __file__), with {} 
showing if it occurred in a block
* Centered after the class and method name
* file:line comes after centered "at"

The centering is obviously the most noticeable characteristic, though the color 
is immediately apparent on terminals that support it. I believe they check 
termcap to see if color is possible, which would be difficult for us to do 
(would require native calls).

There are a few down sides:

* Very nonstandard...pretty, but everything's in a different place.
* More internal code shown than in a standard trace, which adds a visual noise.
* Color helps, but color sometimes looks bad on certain terminals (like the 
really hard-to-read blue on OS X).
* Showing module *and* the class into which it was included is convenient
* (not shown) Method aliases are shown with alias name and original name

The same trace in JRuby, similar but omitting some difficult bits (there's also 
color, slightly different configuration, not shown):

{noformat}
~/projects/jruby ➔ jruby -Xbacktrace.style=rubinius -e "1.times { 
catch(:x) { File.open('build.xml') {|f| 1 / 0} } }"
An exception has occurred:
    divided by 0

Backtrace:
      org.jruby.RubyFixnum#op_div at org/jruby/RubyFixnum.java:490
         ruby.__dash_e__#__file__ at -e:1
            org.jruby.RubyIO#open at org/jruby/RubyIO.java:1078
         ruby.__dash_e__#__file__ at -e:1
     org.jruby.RubyKernel#rbCatch at org/jruby/RubyKernel.java:1191
         ruby.__dash_e__#__file__ at -e:1
       org.jruby.RubyFixnum#times at org/jruby/RubyFixnum.java:252
           ruby.__dash_e__#(root) at -e:1
{noformat}

Note differences:

* The class in which a method was defined is not always available, since in 
compiled code we build the backtrace based on compile-time information (see the 
ruby.__dash_e__#__file__ lines, indicating the -e script compiled at command 
line). When interpreted, this information is available, but it's very difficult 
to show it at compiled code where we no longer track it (for perf reasons).
* Core class .java files are shown, but the output is less cumbersome due to 
the centering
* Block-sourced lines are not shown, but could be (the information is available 
in both interpreted and compiled native backtraces)

Here are a couple potential improvements that are acceptable for JRuby 1.6 
final.

First, with the Ruby names in place of the core class Java names:

{noformat}
~/projects/jruby ➔ jruby -e "1.times { catch(:x) { 
File.open('build.xml') {|f| 1 / 0} } }"
org/jruby/RubyFixnum.java:490:in `/': divided by 0 (ZeroDivisionError)
        from -e:1:in `__file__'
        from org/jruby/RubyIO.java:1078:in `open'
        from -e:1:in `__file__'
        from org/jruby/RubyKernel.java:1191:in `catch'
        from -e:1:in `__file__'
        from org/jruby/RubyFixnum.java:252:in `times'
        from -e:1:in `(root)'
{noformat}

Since the line numbers are given, the fact that we've lost the real Java method 
name isn't a big deal. Note that this also only affects core class methods 
written in Java.

A second version, providing both the Ruby name and the Java name in parens 
after it:

{noformat}
~/projects/jruby ➔ jruby -e "1.times { catch(:x) { 
File.open('build.xml') {|f| 1 / 0} } }"
org/jruby/RubyFixnum.java:490:in `/ (op_div)': divided by 0 (ZeroDivisionError)
        from -e:1:in `__file__'
        from org/jruby/RubyIO.java:1078:in `open'
        from -e:1:in `__file__'
        from org/jruby/RubyKernel.java:1191:in `catch (rbCatch)'
        from -e:1:in `__file__'
        from org/jruby/RubyFixnum.java:252:in `times'
        from -e:1:in `(root)'
{noformat}

The down side here is how much longer the core class lines are.

I'm going to do one of the above two modifications for 1.6 final.

Other possible improvements...

Most of the Rubinius improvements are possible, with the exception of showing 
the module(class) (because it can't be known at load/compile time) and alias 
names (we always show (and only track) real name, not aliased). The color and 
centering improvements are possible, though color requires a bit more terminal 
introspection than we have available through normal means.

* Remove the Java class entirely and use the Ruby class name for core classes 
(this also shows Ruby method names):

{noformat}
~/projects/jruby ➔ jruby -e "1.times { catch(:x) { 
File.open('build.xml') {|f| 1 / 0} } }"
Fixnum:490:in `/': divided by 0 (ZeroDivisionError)
        from -e:1:in `__file__'
        from IO:1078:in `open'
        from -e:1:in `__file__'
        from Kernel:1191:in `catch'
        from -e:1:in `__file__'
        from Fixnum:252:in `times'
        from -e:1:in `(root)'
{noformat}

Advantages:

* No .java showing up in trace
* Shorter core class backtrace lines

Disadvantages:

* Uninitiated will not know "IO" means RubyIO.java, "Kernel" means 
"RubyKernel.java", and so on

* Center the trace with method name followed by file:line:

{noformat}
divided by 0 (ZeroDivisionError)
         `/' at org/jruby/RubyFixnum.java:490
  `__file__' at -e:1
      `open' at org/jruby/RubyIO.java:1078
  `__file__' at -e:1
     `catch' at org/jruby/RubyKernel.java:1191
  `__file__' at -e:1
     `times' at org/jruby/RubyFixnum.java:252
    `(root)' at -e:1
{noformat}

Advantages:

* Centering
* Method name is first

Disadvantages:

* Line number aligns randomly

A variation: Centered with file:line first and method after (which is more 
important to you?)

{noformat}
divided by 0 (ZeroDivisionError)
   org/jruby/RubyFixnum.java:490 in `/'
                            -e:1 in `__file__'
      org/jruby/RubyIO.java:1078 in `open'
                            -e:1 in `__file__'
  org/jruby/RubyKernel.java:1191 in `catch'
                            -e:1 in `__file__'
   org/jruby/RubyFixnum.java:252 in `times'
                            -e:1 in `(root)'
{noformat}

Another variation with centering also applied to the file:line:

{noformat}
divided by 0 (ZeroDivisionError)
  org/jruby/RubyFixnum.java:490  in `/'
                         -e:1    in `__file__'
      org/jruby/RubyIO.java:1078 in `open'
                         -e:1    in `__file__'
  org/jruby/RubyKernel.java:1191 in `catch'
                         -e:1    in `__file__'
  org/jruby/RubyFixnum.java:252  in `times'
                         -e:1    in `(root)'
{noformat}

Advantages:

* Centering x2 (but is it too much?)
* Line number easier to find

Other ideas?

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: 
http://jira.codehaus.org/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply via email to