Debugging proposal

Hi everybody,

First up, I’m new to this list and to Ruby. Let me express my gratitude
to Matz (and others) for creating such a wonderful programming
environment. I have only been playing with Ruby for about a week, and I
have loved every minute of it. Just reading Ruby code makes me smile…

However, there is one thing that is bugging (or rather debugging?) me,
which is the lack of performant debugging support in the language.

I know about debug.rb, but it is unuseably slow for larger applications

  • due to the fact that all the breakpoint and stack frame processing
    occurs in a ruby script for each and every line, call, return, class
    definition etc. This problem becomes especially visible when trying to
    debug a Rails application running on WEBrick.

I also know about Komodo and ArachnoRuby, but they are a) not free, and
b) vendor-specific solutions to a very general problem. (I also don’t
like either product much and personally prefer Eclipse, due to my
background of wrestling with Java for the last 6 years, trying to make
it do things it probably shouldn’t do…)

So, I would like a more generic sollution for Ruby debugging.

So far, I have come up with two approaches (of which I prefer the second
one):


First option - entirely ruby-based:


I spent some time fiddling with debug.rb and came up with a way of only
starting the tracing just before it is needed. Works something like this
(example of a Rails application):

  1. make mydebug.rb available somewhere (e.g. the lib folder of a Rails
    app) 3. start ruby with ruby -r mydebug.rb script/server 4. somewhere in
    my code, I can now do this:

start_debug

… original code …

stop_debug

  1. on hitting the start_debug line, Ruby will break into the debugger in
    the console that the server was started from.

I can inspect things, call methods, list source, step in, over and out
of code - just about everything that debug.rb can do, with the exception
that my solution does not maintain full binding information for outer
stack frames upon hitting a breakpoint. This is due to the fact that the
trace_func is not set until start_debug is executed.

Stepping through code is still very very slow, though.

Mydebug.rb is available at: http://www.muermann.org/ruby/mydebug.rb
http://www.muermann.org/ruby/mydebug.rb


Second option - combination of c and ruby code


I added the following methods to Kernel (not happy with polluting Kernel

  • I need to come up with a better place for this, see further down):

  • set_breakpoint_func - very similar to what the trace_func stuff does,
    except that the function is only called when a breakpoint is hit

  • add_breakpoint - creates a breakpoint at a given line in the current
    file, or a given source file

  • remove_breakpoint - does what it says (arguments are filename and
    line)

  • breakpoints - returns a ruby array with breakpoint file and line
    information

This is several orders of magnitude faster than the ruby-based version.

Things left to do for this are:

  • add stepping (in, over, out)

  • potentially add another callback function on stack frame changes, so
    the debugger can maintain a complete history of bindings for the whole
    call stack, but this is more cute than required.

My modified eval.c is available at: http://www.muermann.org/ruby/eval.c
http://www.muermann.org/ruby/eval.c

Built against lates from CVS (1.9.0, as of 5 July 2006), my changes are
betwee //MM> and //MM< comments

Small proof-of-concept example:

http://www.muermann.org/ruby/dbg.rb
http://www.muermann.org/ruby/dbg.rb

http://www.muermann.org/ruby/debugee.rb
http://www.muermann.org/ruby/debugee.rb

dbg.rb implements a primitive debugger, which evaluates commands against
binding. Type “c” to continue execution.

Ideally, I would like to stick all this stuff in maybe a singleton
Debugger class. I am however unsure on how to do this and would really
like some help with that. The breakpoint_func is implemented using an
event_hook, like trace_func, which requires me to implement it inside
eval.c, as this functionality is (sensibly) not exposed. Unfortunately,
this prevents me from implementing the debug support in a regular
extension.

I have also not yet considered any security implications.

I’d be grateful for any thoughts on this matter.

P.s.: Ruby absolutely rocks!

Cheers,

Max

On Thu, Jul 06, 2006 at 09:50:22AM +0900, Max M. wrote:

Ideally, I would like to stick all this stuff in maybe a singleton
Debugger class. I am however unsure on how to do this and would really
like some help with that. The breakpoint_func is implemented using an
event_hook, like trace_func, which requires me to implement it inside
eval.c, as this functionality is (sensibly) not exposed. Unfortunately,
this prevents me from implementing the debug support in a regular
extension.

It is exposed (ruby-prof uses it, and I’m using it too in rcov).
Just #include <node.h>.

Max M. wrote:

Hi everybody,

First up, I’m new to this list and to Ruby. Let me express my gratitude
to Matz (and others) for creating such a wonderful programming
environment. I have only been playing with Ruby for about a week, and I
have loved every minute of it. Just reading Ruby code makes me smile…

However, there is one thing that is bugging (or rather debugging?) me,
which is the lack of performant debugging support in the language.

You’re not going to like this response, but after ruby sinks in and gets
in your veins, this is how you debug:

  1. refactor

  2. unit test

  3. write to stdout / stderr / log files

  4. insert irb breakpoints (explicit method calls that drop into an irb
    shell)

  5. set up druby interface so you can control and query a process
    remotely

I’ve almost never used debug.rb or trace.rb in 5.5 years of rubying, and
#4 and #5 rarely. That, after being addicted to the MSVC++ debugger.
Still, if there were a very nice, free, fast, GUI debugger for ruby, I’d
probably give it a spin.

Anyway, enough with being flippant. Your breakpoint mechanism looks
interesting. It would be nice to connect to a running program with drb
and tell it to stop next time it hits some file/line, and then be able
to evaluate strings in the current binding. If we could do this with
only a small loss in performance (and no loss when no breakpoints are
enabled), I’d use it.

On 7/6/06, Joel VanderWerf [email protected] wrote:

You’re not going to like this response, but after ruby sinks in and gets
in your veins, this is how you debug:

  1. refactor

  2. unit test

  3. write to stdout / stderr / log files

I haven’t done too much real work on ruby yet, but I still believe
this is a very bad suggestion. Ruby (and any programming language) is
about productivity and in my whole experience (I have to agree that
most of it is spent on Java) never a sout have been more productive
than a debugging session (for enough complex stuff).

./alex

.w( the_mindstorm )p.

Hello Austin,

  1. refactor
  2. unit test
  3. write to stdout / stderr / log files
    I haven’t done too much real work on ruby yet, but I still believe
    this is a very bad suggestion. Ruby (and any programming language) is
    about productivity and in my whole experience (I have to agree that
    most of it is spent on Java) never a sout have been more productive
    than a debugging session (for enough complex stuff).

AZ> I think you misunderstood. This isn’t a suggestion. It’s just how
one
AZ> ends up debugging most of the time. Frankly, I think I have missed a
AZ> graphical debugger exactly once in my Ruby programming. Everything
AZ> else has been easier to deal with with judicious use of tracers and
AZ> logging.

Well, i and many of my customers will not agree with you.
I have to do this print/logging debugging with eiffel
many times (even with DbC) and i see minutes and minutes passing by.
(There is no useable debugger for SmartEiffel).

And also a debugger is the only wonderfull tool that helps you to
see and oberve the control flow, together with the data. No tracer can
do this. In complex and not very well documented frameworks like rails
this can save you hours. It took me minutes to understand REXML and
not hours of trail/error until you now what types/objects are expected
and returned by each function.

Unforunately productivity is something that is hard to measure and
even harder if you are convinced that your own style is already good
enough. There was only one try to deal with this. The so called
“Personal Software Process” but too less people are using it because
it requires additional time and a LOT of diszipline (no wonder that
this was developed from somebody who was a drill sergeant during his
army time).

On 7/6/06, Alexandru P. [email protected]
wrote:

On 7/6/06, Joel VanderWerf [email protected] wrote:

You’re not going to like this response, but after ruby sinks in and gets
in your veins, this is how you debug:

  1. refactor
  2. unit test
  3. write to stdout / stderr / log files
    I haven’t done too much real work on ruby yet, but I still believe
    this is a very bad suggestion. Ruby (and any programming language) is
    about productivity and in my whole experience (I have to agree that
    most of it is spent on Java) never a sout have been more productive
    than a debugging session (for enough complex stuff).

I think you misunderstood. This isn’t a suggestion. It’s just how one
ends up debugging most of the time. Frankly, I think I have missed a
graphical debugger exactly once in my Ruby programming. Everything
else has been easier to deal with with judicious use of tracers and
logging.

-austin

On 7/6/06, Austin Z. [email protected] wrote:

about productivity and in my whole experience (I have to agree that
most of it is spent on Java) never a sout have been more productive
than a debugging session (for enough complex stuff).

I think you misunderstood. This isn’t a suggestion. It’s just how one
ends up debugging most of the time.

He he… this may get different interpretations too :-).

Frankly, I think I have missed a
graphical debugger exactly once in my Ruby programming. Everything
else has been easier to deal with with judicious use of tracers and
logging.

My point was simply that the Ruby world will finally need to support
any type of “debugging” sessions, so people can choose what way they
feel more productive.

I am aware of the fact that your experience and Joel’s on Ruby is much
bigger than mine; but I am quite sure that people coming from other
worlds will look firstly to debugging options (being used to these)
before trying out the Ruby ways. And I don’t think they do something
wrong about it. I know people (and I have some guys even in my team)
that are feeling more productive with souts. Some of them because they
haven’t realesed how helpful a debugging session may be, some of them
just because they are. For me, it really doesn’t matter how they do
it, as long as they are successful and optimal :-).

BR,

./alex

.w( the_mindstorm )p.

On Jul 6, 2006, at 12:07 AM, Mauricio F. wrote:

It is exposed (ruby-prof uses it, and I’m using it too in rcov).
Just #include <node.h>.

Ryan D. and Doug Beaver experimented with converting debug.rb to
use event_hook via RubyInline, I don’t know how far they got.


Eric H. - [email protected] - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

On Jul 6, 2006, at 6:10 AM, Lothar S. wrote:

I have to do this print/logging debugging with eiffel
Unforunately productivity is something that is hard to measure and
even harder if you are convinced that your own style is already good
enough. There was only one try to deal with this. The so called
“Personal Software Process” but too less people are using it because
it requires additional time and a LOT of diszipline (no wonder that
this was developed from somebody who was a drill sergeant during his
army time).

The times that I need a debugger for my ruby code match exactly to
the times my ruby code is far too complicated, poorly factored, or
poorly tested.

Here’s what debugging usually entails when I partake of it:

  1. Code breaks
  2. mess around to find the breakage via the debugger
  3. Fix
  4. Goto 1

Sometimes step 2 will be the same every time because my fix wasn’t
right, but sometimes it will be slightly different. Having to
repeatedly type the same (or similar) things into the debugger is
really, really annoying.

Unless step 2 is automatic, unit testing will beat out using a
debugger. (And when it is automatic don’t you have a test case
anyways?) With test cases I can be reasonably assured I’ll find a
problem before I need to think about using the debugger. With just a
debugger I get neither automatic protection nor repeatability.


Eric H. - [email protected] - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

On Jul 5, 2006, at 5:50 PM, Max M. wrote:

I know about debug.rb, but it is unuseably slow for larger
applications

  • due to the fact that all the breakpoint and stack frame processing
    occurs in a ruby script for each and every line, call, return, class
    definition etc. This problem becomes especially visible when trying to
    debug a Rails application running on WEBrick.

Now that I’ve actually read your original proposal, it sounds like
everything you want is available in ZenDebug. You can play with it
via ‘sudo gem install ZenHacks’.

On Jul 6, 2006, at 4:43 PM, Eric H. wrote:

Ryan D. and Doug Beaver experimented with converting debug.rb to
use event_hook via RubyInline, I don’t know how far they got.

It is fully working and in ZenHacks. I might port it over to
EventHook (also in ZenHacks–it simply wraps up the underlying
mechanism and calls back into ruby) which will make it smaller and
more readable w/o sacrificing much speed.


_why: zenspider’s most intense moments of solice are immediately
following the slaughter […]
_why: that topknot’s the only thing keeping a lid on the righteous anger
bricolage: yeah, that and his flagrant obsession with dvorak

Just installed zenhacks and got the following test errors. Should I be
concerned?

Ken

503-> sudo gem install ZenHacks
Password:
Attempting local installation of ‘ZenHacks’
Local gem file not found: ZenHacks*.gem
Attempting remote installation of ‘ZenHacks’
Updating Gem source index for: http://gems.rubyf orge.org
Install required dependency RubyInline? [Yn] Y
Install required dependency ParseTree? [Yn] Y
Install required dependency RubyToC? [Yn] Y
Successfully installed ZenHacks-1.0.1
Successfully installed RubyInline-3.5.0
Successfully installed ParseTree-1.4.1
Successfully installed RubyToC-1.0.0.5
Installing RDoc documentation for ParseTree-1.4.1…

test/test_parse_tree.rb:190:38: ‘:’ not followed by identified or
operator
Installing RDoc documentation for RubyToC-1.0.0.5…

lib/ruby_to_ansi_c.rb:265:48: ‘:’ not followed by identified or
operator

On Jul 7, 2006, at 1:20 PM, Kenosis wrote:

Just installed zenhacks and got the following test errors. Should
I be
concerned?

503-> sudo gem install ZenHacks
[…]
Installing RDoc documentation for ParseTree-1.4.1…

test/test_parse_tree.rb:190:38: ‘:’ not followed by identified or
operator
Installing RDoc documentation for RubyToC-1.0.0.5…

lib/ruby_to_ansi_c.rb:265:48: ‘:’ not followed by identified or
operator

These are pretty clearly not test errors.