1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
|
Debugging Rails applications
============================
You may have heard about debugging:
_Debugging is a methodical process of finding and reducing the number of bugs, or defects, in a computer program or a piece of electronic hardware thus making it behave as expected._
Many times your code may not behave as you expect, sometimes you will try to print in logs or console values to make a diagnostic of the problem.
Unfortunately, you won't find always the answer you are looking for this way. In that case, you will need to know what's happening and adventure into Rails, in this journey the debugger will be your best companion.
If you ever wanted to learn about Rails source code but you didn't know where to start, this may be the best way, just debug any request to your application and use this guide to learn how to move in the code you have written but also go deeper into Rails code.
== Debugging with ruby-debug
=== Setup
Rails has built-in support for ruby-debug since April 28, 2007. Inside any Rails application you can invoke the debugger by calling the *debugger* method.
Let's take a look at an example:
[source, ruby]
----------------------------------------------------------------------------
class PeopleController < ApplicationController
def new
debugger
@person = Person.new
end
end
----------------------------------------------------------------------------
If you see the message in the console or logs:
[source, shell]
----------------------------------------------------------------------------
***** Debugger requested, but was not available: Start server with --debugger to enable *****
----------------------------------------------------------------------------
Make sure you have started your web server with the option --debugger:
[source, shell]
----------------------------------------------------------------------------
~/PathTo/rails_project$ script/server --debugger
----------------------------------------------------------------------------
In order to use Rails debugging you'll need to be running either *WEBrick* or *Mongrel*. For the moment, no alternative servers are supported.
=== The shell
As soon as your application calls the *debugger* method, the debugger will be started in a debugger shell inside the terminal window you've fired up your application server and you will be placed in the ruby-debug's prompt (rdb:n). The _n_ is the thread number.
If you got there by a browser request, the browser will be hanging until the debugger has finished and the trace has completely run as any normal request.
For example:
[source, shell]
----------------------------------------------------------------------------
@posts = Post.find(:all)
(rdb:7)
----------------------------------------------------------------------------
Now it's time to play and dig into our application. The first we are going to do is ask our debugger for help... so we type: *help* (You didn't see that coming, right?)
[source, shell]
----------------------------------------------------------------------------
(rdb:7) help
ruby-debug help v0.10.2
Type 'help <command-name>' for help on a specific command
Available commands:
backtrace delete enable help next quit show trace
break disable eval info p reload source undisplay
catch display exit irb pp restart step up
condition down finish list ps save thread var
continue edit frame method putl set tmate where
----------------------------------------------------------------------------
[TIP]
To view the help menu for any command use *help <command-name>* in active debug mode. For example: _help var_
The second command before we move on, is one of the most useful command: *list* (or his shorthand *l*).
This command will give us a starting point of where we are by printing 10 lines centered around the current line; the current line here is line 6 and is marked by =>.
[source, shell]
----------------------------------------------------------------------------
(rdb:7) list
[1, 10] in /PathToProject/posts_controller.rb
1 class PostsController < ApplicationController
2 # GET /posts
3 # GET /posts.xml
4 def index
5 debugger
=> 6 @posts = Post.find(:all)
7
8 respond_to do |format|
9 format.html # index.html.erb
10 format.xml { render :xml => @posts }
----------------------------------------------------------------------------
If we do it again, this time using just *l*, the next ten lines of the file will be printed out.
[source, shell]
----------------------------------------------------------------------------
(rdb:7) list
[11, 20] in /PathTo/project/app/controllers/posts_controller.rb
11 end
12 end
13
14 # GET /posts/1
15 # GET /posts/1.xml
16 def show
17 @post = Post.find(params[:id])
18
19 respond_to do |format|
20 format.html # show.html.erb
----------------------------------------------------------------------------
And so on until the end of the current file, when the end of file is reached, it will start again from the beginning of the file and continue again up to the end, acting as a circular buffer.
=== The context
When we start debugging your application, we will be placed in different contexts as you go through the different parts of the stack.
A context will be created when a stopping point or an event is reached. It has information about the suspended program which enable a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place the debugged program is stopped.
At any time we can call the *backtrace* command (or alias *where*) to print the backtrace of the application, this is very helpful to know how we got where we are. If you ever wondered about how you got somewhere in your code, then *backtrace* is your answer.
[source, shell]
----------------------------------------------------------------------------
(rdb:5) where
#0 PostsController.index
at line /PathTo/project/app/controllers/posts_controller.rb:6
#1 Kernel.send
at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
#2 ActionController::Base.perform_action_without_filters
at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
#3 ActionController::Filters::InstanceMethods.call_filters(chain#ActionController::Fil...,...)
at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb:617
...
----------------------------------------------------------------------------
You move anywhere you want in this trace using the *frame n* command, where _n_ is the specified frame number.
[source, shell]
----------------------------------------------------------------------------
(rdb:5) frame 2
#2 ActionController::Base.perform_action_without_filters
at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
----------------------------------------------------------------------------
The available variables are the same as if we were running the code line by line, after all, that's what debugging is.
Moving up and down the stack frame: You can use *up [n]* (*u* for abbreviated) and *down [n]* commands in order to change the context _n_ frames up or down the stack respectively. _n_ defaults to one.
=== Threads
The debugger can list, stop, resume and switch between running threads, the command *thread* (or the abbreviated *th*) is used an allows the following options:
* *thread* shows the current thread.
* *thread list* command is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution.
* *thread stop n* stop thread _n_.
* *thread resume n* resume thread _n_.
* *thread switch n* switch thread context to _n_.
This command is very helpful, among other occasions, when you are debugging concurrent threads and need to verify that there are no race conditions in your code.
=== Inspecting variables
Any expression can be evaluated in the current context, just type it!
In the following example we will print the instance_variables defined within the current context.
[source, shell]
----------------------------------------------------------------------------
@posts = Post.find(:all)
(rdb:11) instance_variables
["@_response", "@action_name", "@url", "@_session", "@_cookies", "@performed_render", "@_flash", "@template", "@_params", "@before_filter_chain_aborted", "@request_origin", "@_headers", "@performed_redirect", "@_request"]
----------------------------------------------------------------------------
As you may have figured out, all variables that you can access from a controller are displayed, lets run the next line, we will use *next* (we will get later into this command).
[source, shell]
----------------------------------------------------------------------------
(rdb:11) next
Processing PostsController#index (for 127.0.0.1 at 2008-09-04 19:51:34) [GET]
Session ID: BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==--b16e91b992453a8cc201694d660147bba8b0fd0e
Parameters: {"action"=>"index", "controller"=>"posts"}
/PathToProject/posts_controller.rb:8
respond_to do |format|
-------------------------------------------------------------------------------
And we'll ask again for the instance_variables.
[source, shell]
----------------------------------------------------------------------------
(rdb:11) instance_variables.include? "@posts"
true
----------------------------------------------------------------------------
Now @posts is a included in them, because the line defining it was executed.
[TIP]
You can also step into *irb* mode with the command *irb* (of course!). This way an irb session will be started within the context you invoked it. But you must know that this is an experimental feature.
To show variables and their values the *var* method is the most convenient way:
[source, shell]
----------------------------------------------------------------------------
var
(rdb:1) v[ar] const <object> show constants of object
(rdb:1) v[ar] g[lobal] show global variables
(rdb:1) v[ar] i[nstance] <object> show instance variables of object
(rdb:1) v[ar] l[ocal] show local variables
----------------------------------------------------------------------------
This is a great way for inspecting the values of the current context variables. For example:
[source, shell]
----------------------------------------------------------------------------
(rdb:9) var local
__dbg_verbose_save => false
----------------------------------------------------------------------------
You can also inspect for an object method this way:
[source, shell]
----------------------------------------------------------------------------
(rdb:9) var instance Post.new
@attributes = {"updated_at"=>nil, "body"=>nil, "title"=>nil, "published"=>nil, "created_at"...
@attributes_cache = {}
@new_record = true
----------------------------------------------------------------------------
[TIP]
Commands *p* (print) and *pp* (pretty print) can be used to evaluate Ruby expressions and display the value of variables to the console.
We can use also *display* to start watching variables, this is a good way of tracking values of a variable while the execution goes on.
[source, shell]
----------------------------------------------------------------------------
(rdb:1) display @recent_comments
1: @recent_comments =
----------------------------------------------------------------------------
The variables inside the displaying list will be printed with their values after we move in the stack. To stop displaying a variable use *undisplay n* where _n_ is the variable number (1 in the last example).
=== Step by step
Now you should know where you are in the running trace and be able to print the available variables. But lets continue and move on with the application execution.
Use *step* (abbreviated *s*) to continue running your program until the next logical stopping point and return control to ruby-debug.
[TIP]
You can also use *step+ n* and *step- n* to move forward or backward _n_ steps respectively.
You may also use *next* which is similar to step, but function or method calls that appear within the line of code are executed without stopping. As with step, you may use plus sign to move _n_ steps.
The difference between *next* and "step" is that *step* stops at the next line of code executed, doing just single step, while *next* moves to the next line without descending inside methods.
Lets run the next line in this example:
[source, ruby]
----------------------------------------------------------------------------
class Author < ActiveRecord::Base
has_one :editorial
has_many :comments
def find_recent_comments(limit = 10)
debugger
@recent_comments ||= comments.find(
:all,
:conditions => ["created_at > ?", 1.week.ago],
:limit => limit
)
end
end
----------------------------------------------------------------------------
[TIP]
You can use ruby-debug while using script/console but remember to *require "ruby-debug"* before calling *debugger* method.
[source, shell]
----------------------------------------------------------------------------
/PathTo/project $ script/console
Loading development environment (Rails 2.1.0)
>> require "ruby-debug"
=> []
>> author = Author.first
=> #<Author id: 1, first_name: "Bob", last_name: "Smith", created_at: "2008-07-31 12:46:10", updated_at: "2008-07-31 12:46:10">
>> author.find_recent_comments
/PathTo/project/app/models/author.rb:11
)
----------------------------------------------------------------------------
Now we are where we wanted to be, lets look around.
[source, shell]
----------------------------------------------------------------------------
(rdb:1) list
[6, 15] in /PathTo/project/app/models/author.rb
6 debugger
7 @recent_comments ||= comments.find(
8 :all,
9 :conditions => ["created_at > ?", 1.week.ago],
10 :limit => limit
=> 11 )
12 end
13 end
----------------------------------------------------------------------------
We are at the end of the line, but... was this line executed? We can inspect the instance variables.
[source, shell]
----------------------------------------------------------------------------
(rdb:1) var instance
@attributes = {"updated_at"=>"2008-07-31 12:46:10", "id"=>"1", "first_name"=>"Bob", "las...
@attributes_cache = {}
----------------------------------------------------------------------------
@recent_comments hasn't been defined yet, so we can assure this line hasn't been executed yet, lets move on this code.
[source, shell]
----------------------------------------------------------------------------
(rdb:1) next
/PathTo/project/app/models/author.rb:12
@recent_comments
(rdb:1) var instance
@attributes = {"updated_at"=>"2008-07-31 12:46:10", "id"=>"1", "first_name"=>"Bob", "las...
@attributes_cache = {}
@comments = []
@recent_comments = []
----------------------------------------------------------------------------
Now we can see how @comments relationship was loaded and @recent_comments defined because the line was executed.
In case we want deeper in the stack trace we can move single *steps* and go into Rails code, this is the best way for finding bugs in your code, or maybe in Ruby or Rails.
=== Breakpoints
A breakpoint makes your application stop whenever a certain point in the program is reached and the debugger shell is invoked in that line.
You can add breakpoints dynamically with the command *break* (or just *b*), there are 3 possible ways of adding breakpoints manually:
* *break line*: set breakpoint in the _line_ in the current source file.
* *break file:line [if expression]*: set breakpoint in the _line_ number inside the _file_. If an _expression_ is given it must evaluated to _true_ to fire up the debugger.
* *break class(.|\#)method [if expression]*: set breakpoint in _method_ (. and \# for class and instance method respectively) defined in _class_. The _expression_ works the same way as with file:line.
[source, shell]
----------------------------------------------------------------------------
(rdb:5) break 10
Breakpoint 1 file /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb, line 10
----------------------------------------------------------------------------
Use *info breakpoints n* or *info break n* lo list breakpoints, is _n_ is defined it shows that breakpoints, otherwise all breakpoints are listed.
[source, shell]
----------------------------------------------------------------------------
(rdb:5) info breakpoints
Num Enb What
1 y at filters.rb:10
----------------------------------------------------------------------------
Deleting breakpoints: use the command *delete n* to remove the breakpoint number _n_ or all of them if _n_ is not specified.
[source, shell]
----------------------------------------------------------------------------
(rdb:5) delete 1
(rdb:5) info breakpoints
No breakpoints.
----------------------------------------------------------------------------
Enabling/Disabling breakpoints:
* *enable breakpoints*: allow a list _breakpoints_ or all of them if none specified, to stop your program (this is the default state when you create a breakpoint).
* *disable breakpoints*: the _breakpoints_ will have no effect on your program.
=== Catching Exceptions
The command *catch exception-name* (or just *cat exception-name*) can be used to intercept an exception of type _exception-name_ when there would otherwise be is no handler for it.
To list existent catchpoints use *catch*.
=== Resuming Execution
* *continue* [line-specification] (or *c*): resume program execution, at the address where your script last stopped; any breakpoints set at that address are bypassed. The optional argument line-specification allows you to specify a line number to set a one-time breakpoint which is deleted when that breakpoint is reached.
* *finish* [frame-number] (or *fin*): execute until selected stack frame returns. If no frame number is given, we run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been performed. If a frame number is given we run until frame frames returns.
=== Editing
At any time, you may use any of this commands to edit the code you are evaluating:
* *edit [file:line]*: edit _file_ using the editor specified by the EDITOR environment variable. A specific _line_ can also be given.
* *tmate n* (abbreviated *tm*): open the current file in TextMate. It uses n-th frame if _n_ is specified.
=== Quitting
To exit the debugger, use the *quit* command (abbreviated *q*), or alias *exit*.
A simple quit tries to terminate all threads in effect. Therefore your server will be stopped and you will have to start it again.
== References
* link:http://www.datanoise.com/ruby-debug[ruby-debug Homepage]
* link:http://www.sitepoint.com/article/debug-rails-app-ruby-debug/[Article: Debugging a Rails application with ruby-debug]
* link:http://brian.maybeyoureinsane.net/blog/2007/05/07/ruby-debug-basics-screencast/[ruby-debug Basics screencast]
* link:http://railscasts.com/episodes/54-debugging-with-ruby-debug[Ryan Bate's ruby-debug screencast]
* link:http://bashdb.sourceforge.net/ruby-debug.html[Debugging with ruby-debug]
* link:http://cheat.errtheblog.com/s/rdebug/[ruby-debug cheat sheet]
|