CSC/ECE 517 Fall 2012/ch2b 2w-1w65 am: Difference between revisions
(42 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
==Introduction== | ==Introduction== | ||
=== | This is a wiki article for the material presented in the Coursera SaaS video series, [https://www.youtube.com/watch?v=Ee5vfe0mLb8 3.13 Debugging by Dr. Armando Fox and Dr. David Patterson]. We aim to cover all the material presented in the video with definitions and examples | ||
===Why debugging in Rails can be tricky?=== | |||
*We are used to printing the error on the terminal in most applications(Example: [http://www.cplusplus.com/reference/clibrary/cstdio/stderr/ STDERR]). This may not be possible for a web application since its primary form of input and output is through HTTP requests and responses. | |||
*Errors early in flow might manifest itself late. | |||
Consider the following hierarchy seen in a Rails application: | |||
URI -> Route -> Controller -> Model -> View -> Renderer | |||
URI -> Route -> Controller -> | |||
Here, something that goes wrong in the controller might not manifest itself until the renderer. The root cause of the error might have happened a long time ago | Here, something that goes wrong in the controller might not manifest itself until the renderer. The root cause of the error might have happened a long time ago. | ||
*Errors can be hard to localize/reproduce if it affects only some users or routes. | |||
There are several approaches that can be used for debugging based on the mode of operation, that is development mode or production mode: | |||
'''Printing to terminal:'''This is useful for developers, when they are writing code they can print errors to the terminal and then examine them. We cannot use this approach in production mode.<br> | |||
'''Logging:''' This is a more general approach to debugging which can be used in both modes. The logged entries are permanent record of what the application is doing. The recorded errors are saved in a log file and can be referenced later.<br> | |||
This is a more general approach. The logged entries are permanent record of what | |||
'''Interactive Debugging:''' Using this tool you can stop the application in its tracks and inspect the state of the variables or other parameters to locate the error.<br> | |||
Here is a summary of the various approaches and the modes in which they can be used: <br> | |||
{| class="wikitable" | |||
|- | |||
! Approach !! Development !! Production | |||
|- | |||
| Printing to Terminal || ✓ || | |||
|- | |||
| Logging || ✓ || ✓ | |||
|- | |||
| Interactive Debugging || ✓ || | |||
|} | |||
==RASP== | ==RASP== | ||
Steps to follow when faced with an error can be concisely represented by RASP according to Dr. Fox. | |||
===What is RASP?=== | |||
RASP is an acronym which briefs the steps which a developer could follow if she were facing an error message. It stands for: | |||
#'''Read''' the error message. | |||
#'''Ask''' your colleague | |||
#'''Search''' using [http://stackoverflow.com/ Stack Overflow] or any other search engine. | |||
#'''Post''' on [http://stackoverflow.com/ Stack Overflow] or class forum | |||
Reading the error message carefully is the first step in debugging. The error messages in Rails are incredibly detailed and it gives us a lot of information, reading it carefully can help us locate the file and line in which you are facing the error. But what if the line looks alright to you?<br> | |||
The next step is to ask a colleague an informed question. In case you are doing pair programming, this is a good step to take. The question has to be of the form "I am trying to do this and I expected to get this but I got this other thing instead of foo."<br> | |||
If the previous steps fail then searching using stack overflow or Google is next, especially if its an error which is particular to a version of gems or OS.<br> | |||
In case you don't find a solution to your problem the final step is to get minimal but complete information which reproduces the error message which you are experiencing and post it.<br> | |||
Here is an example question and solution from stack overflow:<br> | |||
[[File:Ques.png]]<br><br> | |||
As we can see the question follows the guidelines explained above. It gives minimal but complete information about the error. It does not give the entire description of the error. It is important to post minimal information so that the person who answers the question can understand your problem quickly.<br> | |||
[[File:Ans.png]]<br><br> | |||
The answer is posted within an hour of posting the question. This shows that posting on stack overflow is a fast way of getting expert help with debugging. | |||
==Understanding Error Messages in Rails== | |||
Rails usually provides a very verbose description of error messages. Dr. Fox tells us that error messages are our friends, they try to not only show us what went wrong but a great deal of detail of where it went wrong. Let us look at an example error message.<br> | |||
Example: | |||
An error message was deliberately caused by entering a route which wouldn't work. The application which we are using is the [http://en.wikipedia.org/wiki/Backchannel backchannel] application and we are trying to find a post with id=20 (it doesn't exist). This causes rails to throw the following error message. | |||
<code>'''ActiveRecord::RecordNotFound in PostsController#show'''</code> | |||
= | <code>Couldn't find Post with id=20<br> | ||
Rails.root: C:/Users/BruceWayne/Downloads/BackChannelApplication<br> | |||
Application Trace | Framework Trace | Full Trace<br></code> | |||
'''What is the error message trying to tell us?'''<br> | |||
It tells us that ActiveRecord raised the error, and the error was raised in the show method of the Posts Controller. Rails tried to find a post with id=20 but it failed. | |||
===Application Trace=== | ===Application Trace=== | ||
=== | The application trace will give us the file, line number and the method in which the error was thrown. For the above example the application trace will look like this:<br> | ||
<code>app/controllers/posts_controller.rb:17:in `show'</code> | |||
You can also get the application trace manually at any point through the following command: | |||
begin | |||
raise object.inspect | |||
rescue | |||
Rails.logger.warn $!.backtrace.to_yaml | |||
end | |||
This will cause a deliberate error along with an application trace of how you got there. | |||
===Stack Trace=== | |||
Full stack trace shows us the entire stack trace of right when the application started until the problem is encountered. Usually stack traces are very lengthy but they can be very useful to solve problems. Here is a partial stack trace from the back channel application error which we saw before:<br> | |||
<code>activerecord (3.2.8) lib/active_record/relation/finder_methods.rb:341:in `find_one'<br> | |||
activerecord (3.2.8) lib/active_record/relation/finder_methods.rb:312:in `find_with_ids'<br> | |||
activerecord (3.2.8) lib/active_record/relation/finder_methods.rb:107:in `find'<br> | |||
C:in `find'<br> | |||
app/controllers/posts_controller.rb:17:in `show'<br> | |||
actionpack (3.2.8) lib/action_controller/metal/implicit_render.rb:4:in `send_action'<br> | |||
actionpack (3.2.8) lib/abstract_controller/base.rb:167:in `process_action'<br></code> | |||
We can see the stack trace right from where the Rails application started, we can see calls to <code>activerecord</code> in the beginning. It is unlikely that there would be a problem in those files. As we continue looking into the stack trace we see the find method, we can click on the link in the trace and it will take us to the line in which the error was found. Most often the first few lines of the stack trace will give us the cause behind the error. | |||
Stack trace can be logged using the following lines of code: | |||
begin | |||
raise | |||
rescue => e | |||
logger.error e.message | |||
e.backtrace.each { |line| logger.error line } | |||
end | |||
===Env Dump and Session Dump=== | ===Env Dump and Session Dump=== | ||
=== | Session dump gives all the information of the user's session. This is useful if the error which needs to be fixed is particular to a user or a group of users. | ||
<code>_csrf_token: "LiTiqHSVFOr8uT0+pFJhNCFjUc3YikuJy6F77QG+O1Y="<br> | |||
comment_id: 1<br> | |||
post_id: 1<br> | |||
session_id: "c562769a55b39620228ce6064aaa1637"<br> | |||
user: #<User id: 4, name: "seriously", username: "seriously", email: "seriously@seriously.com",<br> encrypted_password: "cb32e64bd4cb11c7424ea4ea75731450e99ed120d4bab155a42...", salt: "DlLGMQzwgU", is_admin: nil,<br> created_at: "2012-11-18 01:08:35", updated_at: "2012-11-18 01:08:35"></code><br> | |||
A session dump can be obtained using the following command: | |||
<%= debug session %> | |||
Environment dump gives details of the rails runtime environment. If the error is localized to users who use the same version of a host or a protocol, we could find that out using the environment dump. | |||
<code>GATEWAY_INTERFACE: "CGI/1.1" <br> | |||
HTTP_ACCEPT: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"<br> | |||
HTTP_ACCEPT_CHARSET: "ISO-8859-1,utf-8;q=0.7,*;q=0.3"<br> | |||
HTTP_ACCEPT_ENCODING: "gzip,deflate,sdch"<br> | |||
HTTP_ACCEPT_LANGUAGE: "en-US,en;q=0.8"<br> | |||
REMOTE_ADDR: "127.0.0.1"<br> | |||
REMOTE_HOST: "Wayne-PC"<br> | |||
SERVER_NAME: "localhost"<br> | |||
SERVER_PROTOCOL: "HTTP/1.1"<br></code> | |||
===A Very Common Error Message in Ruby=== | |||
<code>undefined method ‘foo’ for nil:NilClass</code><br> | |||
Most often, it means that an assignment failed and we didn't check for errors.<br> | |||
Example: <code>@m = Movie.find_by_id(id) #could be nil</code><br> | |||
When you try to call m.title after an assignment like the one above, it will throw this error. | |||
== | ==Rails Debugger== | ||
===What is | ===What is a debugger?=== | ||
Sometimes printing errors to the console or logging is not enough to find out the root cause behind an error message. It is during these times when the debugger is a developer's best friend. The debugger helps us to step forward or backward in the code, execute or skip lines of code, examine the state of a variable or a parameter which has been passed. This will help the developer pinpoint to the actual source of the problem and fix it.<br><br> | |||
'''How to install the debugger?'''<br> | |||
In Rails, ruby-debug is a gem. We can install it like any other gem. To use the gem in a Rails application we need to add the following line: <br> | |||
<code>require "ruby-debug"</code><br> | |||
in the <code>config/environments/development.rb</code> file.<br> | |||
In Ruby 1.9, you can use this command to install a compatible version of the debugger gem:<br> | |||
<code>gem install debugger</code><br><br> | |||
'''Running the debugger<br>''' | |||
To use the debugger in the rails application we need to start the server with the <code>--debugger</code> option. <br> | |||
<code>$ rails server --debugger</code> | |||
If we don't do this we would encounter an error like this:<br> | |||
<code>***** Debugger requested, but was not available: Start server with --debugger to enable *****</code> <br> | |||
We can invoke the debugger from any method by using the <code>debugger</code> keyword in the method.<br><br> | |||
Example:<br> | |||
---- | |||
<code>class PostsController < ApplicationController | |||
def new | |||
debugger | |||
@post = Post.new | |||
end | |||
end</code> | |||
---- | |||
Once the debugger method is called a debugger shell is started inside the terminal window where the application server was launched, and the cursor will be placed at ruby-debug’s prompt <code>(rdb:n)</code> where n is the thread number. The prompt will show the next line of code which is waiting to run. | |||
==Conclusion== | ==Conclusion== | ||
Rails is a relatively new platform for web development, it is continually evolving which effectively means debugging is harder. However by leveraging the knowledge of people who have encountered similar errors before we can resolve a subset of problems, the ones particular to versions of ruby and various gems. We have tools like debugger and application trace to help us solve problems which are specific to our application. To know more about using the debugger, please refer to the Further Reading section of the article. | |||
==References== | |||
# https://www.youtube.com/watch?v=Ee5vfe0mLb8 | |||
# http://railstips.org/blog/archives/2011/08/31/stupid-simple-debugging/ | |||
# http://guides.rubyonrails.org/debugging_rails_applications.html | |||
# http://railscasts.com/episodes/54-debugging-with-ruby-debug | |||
# http://bashdb.sourceforge.net/ruby-debug.html | |||
# http://cheat.errtheblog.com/s/rdebug/ | |||
# http://railscasts.com/episodes/24-the-stack-trace | |||
# http://www.scribd.com/doc/86869289/3/Rails-Cookery-3 | |||
# http://www.ruby-forum.com/topic/67687 | |||
# http://stackoverflow.com/questions/8408936/rake-dbmigrate-error | |||
==Further Reading== | ==Further Reading== | ||
# [http://guides.rubyonrails.org/debugging_rails_applications.html Rails Guide - Debugging Rails Applications] | |||
# [http://www.sitepoint.com/debug-rails-app-ruby-debug/ Debug your rails app with ruby-debug] | |||
# [http://rubyforge.org/projects/ruby-debug/ Ruby Forge - Documentation for ruby-debug] |
Latest revision as of 19:08, 26 November 2012
Introduction
This is a wiki article for the material presented in the Coursera SaaS video series, 3.13 Debugging by Dr. Armando Fox and Dr. David Patterson. We aim to cover all the material presented in the video with definitions and examples
Why debugging in Rails can be tricky?
- We are used to printing the error on the terminal in most applications(Example: STDERR). This may not be possible for a web application since its primary form of input and output is through HTTP requests and responses.
- Errors early in flow might manifest itself late.
Consider the following hierarchy seen in a Rails application:
URI -> Route -> Controller -> Model -> View -> Renderer
Here, something that goes wrong in the controller might not manifest itself until the renderer. The root cause of the error might have happened a long time ago.
- Errors can be hard to localize/reproduce if it affects only some users or routes.
There are several approaches that can be used for debugging based on the mode of operation, that is development mode or production mode:
Printing to terminal:This is useful for developers, when they are writing code they can print errors to the terminal and then examine them. We cannot use this approach in production mode.
Logging: This is a more general approach to debugging which can be used in both modes. The logged entries are permanent record of what the application is doing. The recorded errors are saved in a log file and can be referenced later.
Interactive Debugging: Using this tool you can stop the application in its tracks and inspect the state of the variables or other parameters to locate the error.
Here is a summary of the various approaches and the modes in which they can be used:
Approach | Development | Production |
---|---|---|
Printing to Terminal | ✓ | |
Logging | ✓ | ✓ |
Interactive Debugging | ✓ |
RASP
Steps to follow when faced with an error can be concisely represented by RASP according to Dr. Fox.
What is RASP?
RASP is an acronym which briefs the steps which a developer could follow if she were facing an error message. It stands for:
- Read the error message.
- Ask your colleague
- Search using Stack Overflow or any other search engine.
- Post on Stack Overflow or class forum
Reading the error message carefully is the first step in debugging. The error messages in Rails are incredibly detailed and it gives us a lot of information, reading it carefully can help us locate the file and line in which you are facing the error. But what if the line looks alright to you?
The next step is to ask a colleague an informed question. In case you are doing pair programming, this is a good step to take. The question has to be of the form "I am trying to do this and I expected to get this but I got this other thing instead of foo."
If the previous steps fail then searching using stack overflow or Google is next, especially if its an error which is particular to a version of gems or OS.
In case you don't find a solution to your problem the final step is to get minimal but complete information which reproduces the error message which you are experiencing and post it.
Here is an example question and solution from stack overflow:
As we can see the question follows the guidelines explained above. It gives minimal but complete information about the error. It does not give the entire description of the error. It is important to post minimal information so that the person who answers the question can understand your problem quickly.
The answer is posted within an hour of posting the question. This shows that posting on stack overflow is a fast way of getting expert help with debugging.
Understanding Error Messages in Rails
Rails usually provides a very verbose description of error messages. Dr. Fox tells us that error messages are our friends, they try to not only show us what went wrong but a great deal of detail of where it went wrong. Let us look at an example error message.
Example: An error message was deliberately caused by entering a route which wouldn't work. The application which we are using is the backchannel application and we are trying to find a post with id=20 (it doesn't exist). This causes rails to throw the following error message.
ActiveRecord::RecordNotFound in PostsController#show
Couldn't find Post with id=20
Rails.root: C:/Users/BruceWayne/Downloads/BackChannelApplication
Application Trace | Framework Trace | Full Trace
What is the error message trying to tell us?
It tells us that ActiveRecord raised the error, and the error was raised in the show method of the Posts Controller. Rails tried to find a post with id=20 but it failed.
Application Trace
The application trace will give us the file, line number and the method in which the error was thrown. For the above example the application trace will look like this:
app/controllers/posts_controller.rb:17:in `show'
You can also get the application trace manually at any point through the following command:
begin raise object.inspect rescue Rails.logger.warn $!.backtrace.to_yaml end
This will cause a deliberate error along with an application trace of how you got there.
Stack Trace
Full stack trace shows us the entire stack trace of right when the application started until the problem is encountered. Usually stack traces are very lengthy but they can be very useful to solve problems. Here is a partial stack trace from the back channel application error which we saw before:
activerecord (3.2.8) lib/active_record/relation/finder_methods.rb:341:in `find_one'
activerecord (3.2.8) lib/active_record/relation/finder_methods.rb:312:in `find_with_ids'
activerecord (3.2.8) lib/active_record/relation/finder_methods.rb:107:in `find'
C:in `find'
app/controllers/posts_controller.rb:17:in `show'
actionpack (3.2.8) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
actionpack (3.2.8) lib/abstract_controller/base.rb:167:in `process_action'
We can see the stack trace right from where the Rails application started, we can see calls to activerecord
in the beginning. It is unlikely that there would be a problem in those files. As we continue looking into the stack trace we see the find method, we can click on the link in the trace and it will take us to the line in which the error was found. Most often the first few lines of the stack trace will give us the cause behind the error.
Stack trace can be logged using the following lines of code:
begin raise rescue => e logger.error e.message e.backtrace.each { |line| logger.error line } end
Env Dump and Session Dump
Session dump gives all the information of the user's session. This is useful if the error which needs to be fixed is particular to a user or a group of users.
_csrf_token: "LiTiqHSVFOr8uT0+pFJhNCFjUc3YikuJy6F77QG+O1Y="
comment_id: 1
post_id: 1
session_id: "c562769a55b39620228ce6064aaa1637"
user: #<User id: 4, name: "seriously", username: "seriously", email: "seriously@seriously.com",
encrypted_password: "cb32e64bd4cb11c7424ea4ea75731450e99ed120d4bab155a42...", salt: "DlLGMQzwgU", is_admin: nil,
created_at: "2012-11-18 01:08:35", updated_at: "2012-11-18 01:08:35">
A session dump can be obtained using the following command:
<%= debug session %>
Environment dump gives details of the rails runtime environment. If the error is localized to users who use the same version of a host or a protocol, we could find that out using the environment dump.
GATEWAY_INTERFACE: "CGI/1.1"
HTTP_ACCEPT: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
HTTP_ACCEPT_CHARSET: "ISO-8859-1,utf-8;q=0.7,*;q=0.3"
HTTP_ACCEPT_ENCODING: "gzip,deflate,sdch"
HTTP_ACCEPT_LANGUAGE: "en-US,en;q=0.8"
REMOTE_ADDR: "127.0.0.1"
REMOTE_HOST: "Wayne-PC"
SERVER_NAME: "localhost"
SERVER_PROTOCOL: "HTTP/1.1"
A Very Common Error Message in Ruby
undefined method ‘foo’ for nil:NilClass
Most often, it means that an assignment failed and we didn't check for errors.
Example: @m = Movie.find_by_id(id) #could be nil
When you try to call m.title after an assignment like the one above, it will throw this error.
Rails Debugger
What is a debugger?
Sometimes printing errors to the console or logging is not enough to find out the root cause behind an error message. It is during these times when the debugger is a developer's best friend. The debugger helps us to step forward or backward in the code, execute or skip lines of code, examine the state of a variable or a parameter which has been passed. This will help the developer pinpoint to the actual source of the problem and fix it.
How to install the debugger?
In Rails, ruby-debug is a gem. We can install it like any other gem. To use the gem in a Rails application we need to add the following line:
require "ruby-debug"
in the config/environments/development.rb
file.
In Ruby 1.9, you can use this command to install a compatible version of the debugger gem:
gem install debugger
Running the debugger
To use the debugger in the rails application we need to start the server with the --debugger
option.
$ rails server --debugger
If we don't do this we would encounter an error like this:
***** Debugger requested, but was not available: Start server with --debugger to enable *****
We can invoke the debugger from any method by using the debugger
keyword in the method.
Example:
class PostsController < ApplicationController
def new
debugger
@post = Post.new
end
end
Once the debugger method is called a debugger shell is started inside the terminal window where the application server was launched, and the cursor will be placed at ruby-debug’s prompt (rdb:n)
where n is the thread number. The prompt will show the next line of code which is waiting to run.
Conclusion
Rails is a relatively new platform for web development, it is continually evolving which effectively means debugging is harder. However by leveraging the knowledge of people who have encountered similar errors before we can resolve a subset of problems, the ones particular to versions of ruby and various gems. We have tools like debugger and application trace to help us solve problems which are specific to our application. To know more about using the debugger, please refer to the Further Reading section of the article.
References
- https://www.youtube.com/watch?v=Ee5vfe0mLb8
- http://railstips.org/blog/archives/2011/08/31/stupid-simple-debugging/
- http://guides.rubyonrails.org/debugging_rails_applications.html
- http://railscasts.com/episodes/54-debugging-with-ruby-debug
- http://bashdb.sourceforge.net/ruby-debug.html
- http://cheat.errtheblog.com/s/rdebug/
- http://railscasts.com/episodes/24-the-stack-trace
- http://www.scribd.com/doc/86869289/3/Rails-Cookery-3
- http://www.ruby-forum.com/topic/67687
- http://stackoverflow.com/questions/8408936/rake-dbmigrate-error