Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Journal of jjore (6662)

Saturday July 11, 2009
12:08 AM

You'll miss Carp in Ruby

[ #39266 ]

In Perl, you have a nice built in library called Carp which gives you stack traces including the parameters on the stack. If you desire, you can dump the entire contents of any variable in any argument in your backtrace. If you really desire, you can modify your caller's variables.

This isn't possible in ruby-1.8.x due to mistakes or oversights in the design.

In perl, here's a sample of what you can do easily:

use Carp qw( cluck );
sub a { cluck('Message') }
sub b { a( 1 .. 2 ) }
sub c { b( 'a' .. 'c' ) }
c();

which prints your stack:

Message at - line 2
    main::a(1, 2) called at - line 3
    main::b('a', 'b', 'c') called at - line 4
    main::c() called at - line 5

There's other options like Carp::confess() to throw an exception with a stack trace, Carp::longmess() to make the string you see above, the module Carp::Always (http://search.cpan.org/dist/Carp-Always) to make Carp be your default behavior.

Carp is based on the caller() function which returns many pieces of information: class, filename, line, function, has args?, context, eval() text, is require?, compilation/pragma info. If you read perldebguts, you learn to also get the arguments:

sub b {
    package DB;
    () = caller 1;
    say "caller's args=@DB::args";
    $DB::args[0] = 'Zappo';
    return;
}
sub a {
    say "args=@_";
    b();
    say "args=@_";
}
a( 'flash' );
 
# args=flash
# caller's args=flash
# args=Zappo

I tried, I really tried to add this into Ruby-1.8.6. The problem is that for each ruby function, it uses actual C recursion to go deeper and it passes the arguments as an Array. For something like this:

def c(x);      end
def b(x); c(3); end
def a(x); b(2); end
a(1)

something like this happens in C:

rb_call(a,[1]) {
  rb_call(b,[2]) {
    rb_call(c,[3]) {
    }
  }
}

The problem being... how do you access the arguments without a pointer? You don't. Perl works because it is "stackless" in the sense of nested perl functions don't usually make the C stack go deeper and "stack based" in the sense that all arguments are pushed onto an application level stack.

Ruby's caller() function is like a particularly stupid perl version. You get the filenames, line numbers, and method names but that's it.

As is, it is nearly impossible to implement a useful stack trace in ruby-1.8.6. I've got a vague idea I might go learn how gdb extracts the backtrace, then walk that to find the argument arrays but OMG, that is such an ugly idea. I've got some x86 assembly books I might drag out to help with this.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • This post may be of interest to you:

    http://weblog.jamisbuck.org/2006/9/22/inspecting-a-live-ruby-process [jamisbuck.org]

    Be sure to follow the comments, too.

    • Ah, gleaning the information from backtrace() is nicer than using rb_eval() which would be my normal first pass trick. That is, in any language like Perl, Ruby, or I assume Python, if there's a C API that exposes an eval() function, you can get enormous powers just by driving eval() from gdb.

      FWIW and because I the weblog.jamisbuck.org blog software comments aren't working for me, the clean bit of fu to glark the call stack is packaged as a GDB function thusly:

      define ruby_backtrace
        set $ary = (struct