Thursday, February 7, 2008

Tutorial on ruby-debug

Installation
The installation procedure requires rubygems package and C compiler available.
Two notes worth to mention:
For Debian users, you must have ruby-dev package installed.
For Windows users, I don't have a precompiled version for Windows available yet.
The installation procedure is very simple: $ sudo gem install ruby-debug


Basic usages
There are two way you can use this library.
rdebug script.
When you start your application with rdebug script, the debugger is activated by default and the execution of your script is halted at the first line of code:$ cat test.rb
puts 'ok'
$ rdebug test.rb
./test.rb:1:puts 'ok'
(rdb:1) list
[-4, 5] in ./test.rb
=> 1 puts 'ok'
(rdb:1)
From this point you can invoke any commands available, such as you can create breakpoints and step through your code.
On demand invocation
You can require ruby-debug library from inside of your application and use a very simple API in order to summon up the debugger. Let's see how it can be done with a Rails application. I'm going to demonstrate it on my BugTrack application.
Open /config/environments/development.rb file and append the following line to the end of the file:require 'ruby-debug'
Open /app/controllers/user_cotroller.rb file and find login action. What we want is to stop our application at this point and explore what is going on in there:def login
debugger # add this line
user = User.auth(@params['login'], @params['pwd'])
if user
@session[USER_PARAM] = user
set_filter(user)
...
end
Note that I've added Kernel#debugger method call in the beginning of this method. When the execution gets to this point, we should see our debugger's command prompt.
Now start the application using webrick. Note you can't use lighttpd, because it forks fastcgi processes in the background and they don't have access to the current terminal. $ ./script/server webrick
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options
[2006-07-11 12:42:56] INFO WEBrick 1.3.1
[2006-07-11 12:42:56] INFO ruby 1.8.5 (2006-07-11) [i686-darwin8.7.1]
[2006-07-11 12:42:56] INFO WEBrick::HTTPServer#start: pid=27917 port=3000
Now let's try to login. As soon as I send a login request to the server, on the console I see the debugger prompt:./script/.../controllers/user_controller.rb:59: \
user = User.auth(@params['login'], @params['pwd'])
(rdb:2) _
It displays the name of the file .../user_controller.rb where we hit the breakpoint, the line number (59, in that case) and the line of code which will be executed next. At this point you can start playing with the debugger by entering debugger commands and executing them. For the list of all available commands use help command.
Several most-used commands.
Note that most commands have one-two character abbreviations. Again check the output of help command.
Show me the code. Use list command to browse code in the current context:(rdb:2) list
[54, 63] in ./script/../config/../app/controllers/user_controller.rb
54 end
55 end
56
57 def login
58 debugger
=> 59 user = User.auth(@params['login'], @params['pwd'])
60 if user
61 @session[USER_PARAM] = user
62 set_filter(user)
63 else
You can continue browsing by entering more list commands. In order to move in the opposite direction, use - argument. Also you can specify a line number you want to start your listing from or a range of lines separated by a dash character.
Evaluate an expression in the current context. Just type any expression you want and the debugger will show you a result of this expression evaluation or an exception: (rdb:2) params
{"action"=>"login", "controller"=>"user", "pwd"=>"letmein", \
"login"=>"admin"}
You can also use library by using *pp* command.
Find where we are at this point. Use where command to see the full stack trace.(rdb:2) where
--> #1 ./script/../config/../app/controllers/user_controller.rb:59:in `login'
#2 /usr/.../action_controller/base.rb:910:in `perform_action_without_filters'
#3 /usr/.../action_controller/filters.rb:368:in `perform_action_without_benchmark'
#4 /usr/.../action_controller/benchmarking.rb:69:in `measure'
#5 /usr/.../action_controller/benchmarking.rb:69:in `perform_action_without_rescue'
#6 /usr/.../action_controller/rescue.rb:82:in `perform_action'
...
(rdb:2)
The --> arrow indicates the current stack frame selected. It also shows the current evaluation context (see the next command).
Move up and down the stack frame. You can use up and down commands in order to change the context to the upper or to the lower frame respectively. Eventually, you can evaluate an expression in the new context. Again, you can use where command to see which frame is currently activate.(rdb:2) up 2
#3 /usr/.../action_controller/filters.rb:368:in `perform_action_without_benchmark'
(rdb:2) l
[363, 372] in /usr/.../action_controller/filters.rb
363
364 def perform_action_with_filters
365 before_action_result = before_action
366
367 unless before_action_result == false performed?
=> 368 perform_action_without_filters
369 after_action
370 end
371
372 @before_filter_chain_aborted = (before_action_result == false)
(rdb:2) before_action_result
[:verify_login, :verify_access]
(rdb:2) performed?
false
(rdb:2) down 2
Stepping the program. Use step command to make a single step, use next command to move to the next line without descending inside methods. Both commands accept an optional numeric argument which specifies how many steps to make:(rdb:2) s
script/../config/../app/models/user.rb:27: find :first,
(rdb:2) l
[22, 31] in script/../config/../app/models/user.rb
22 def status_name
23 STATUS_NAMES[self.status]
24 end
25
26 def self.auth(login, pwd)
=> 27 find :first,
28 :conditions => ["login = ? AND pwd = ? AND status = ?",
29 login, pwd, ACTIVE]
30 end
31
32 def is_admin?
Note that you can move up along the frame stack with *up* command and then use next command to continue execution at the new stack level.
Threads listing. Use thread list to display all threads and their statuses. The plus + character and the number indicates the current thread of execution:(rdb:2) thread list
1 # /usr/local/lib/ruby/1.8/webrick/server.rb:91
+2 # script/../config/../app/models/user.rb:27
31 # /usr/local/lib/ruby/1.8/drb/drb.rb:944
(rdb:2)
By the way, the debugger prompt also shows the current thread number (rdb:2).
Display variables from the current context. Use var local and var global to display local and global variables respectively.(rdb:2) var local
login => "admin"
pwd => "letmein"
(rdb:2) var global
$! => nil
$" => ["rubygems.rb", "rbconfig.rb", "rubygems/rubygems_version.rb",
...
Setting breakpoints. Use break [[file:]line] command to add a breakpoint at the given point in the file:(rdb:2) b 69
Set breakpoint 1 at ./script/.../controllers/user_controller.rb:69
or at the method execution:(rdb:2) b User.auth
Set breakpoint 1 at User.auth
Optionally, you can set an expression which defines whether the debugger should stop when it reaches the breakpoint. This expression is evaluated each time at the context of the breakpoint position:(rdb:2) b 72 if params['user'] == 'admin'
Set breakpoint 1 at ./script/.../controllers/user_controller.rb:69
To list all breakpoints use break command without parameters:(rdb:2) break
Breakpoints:
1 ./script/.../user_controller.rb:69
2 ./script/.../user_controller.rb:72 if params['user'] == 'admin'
Continue execution. To continue execution use cont command.(rdb:2) cont
127.0.0.1 - - [11/07/2006:15:09 EDT] "POST /user/login HTTP/1.1" 302 96
http://localhost:3000/bug/list -> /user/login
127.0.0.1 - - [11/07/2006:15:12 EDT] "GET /bug/list HTTP/1.1" 200 3830
http://localhost:3000/bug/list -> /bug/list
List of all commands (rdb:1) help
ruby-debug help v.0.1.3
Commands
b[reak] [fileclass:] [if expr]
b[reak] [class.] [if expr]
set breakpoint to some position,
optionally if expr == true
cat[ch] set catchpoint to an exception
cat[ch] show catchpoint
disp[lay] add expression into display expression list
undisp[lay][ nnn] delete one particular or all display expressions
b[reak] list breakpoints
del[ete][ nnn] delete some or all breakpoints
c[ont] run until program ends or hit breakpoint
r[un] alias for cont
s[tep][ nnn] step (into methods) one line or till line nnn
n[ext][ nnn] go over one line or till line nnn
w[here] display frames
f[rame] alias for where
l[ist][ (-nn-mm)] list program, - list backwards, nn-mm list given lines
up[ nn] move to higher frame
down[ nn] move to lower frame
fin[ish] return to outer frame
q[uit] exit from debugger
v[ar] g[lobal] show global variables
v[ar] l[ocal] show local variables
v[ar] i[nstance] "<"object">" show instance variables of object
v[ar] c[onst] "<" object">" show constants of object
m[ethod] i[nstance] show methods of object
m[ethod] show instance methods of class or module
th[read] l[ist] list all threads
th[read] c[ur[rent]] show current thread
th[read] [sw[itch]] switch thread context to nnn
th[read] stop stop thread nnn
th[read] resume resume thread nnn
p expression evaluate expression and print its value
pp expression evaluate expression and print its value
h[elp] print this help
evaluate
If you find a bug, please file it to the project tracker. Thank you.

Artikel yang Berkaitan

0 komentar:

Post a Comment