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):
- 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
- 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