$30 off During Our Annual Pro Sale. View Details »

Effective Debugging Rubyconf

Effective Debugging Rubyconf

Debugging is an art. And to be an effective artist, an artist must be intimately familiar with their tools. In this talk, we'll start gently and finish strong to ensure that there's something for everyone. We'll cover when to use a debugger, which debugger to use, and how to use the debugger, and how to quickly configure your debugger for maximum utility. We'll touch briefly on pry and why pry is not a debugger, except when it is.

Developers are always looking for ways to boost productivity. Effective debugging allows one to more quickly discover inaccuracies between our expectations and how the software actually behaves.

Jonathan Wallace

November 08, 2013
Tweet

More Decks by Jonathan Wallace

Other Decks in Programming

Transcript

  1. Effective Debugging

    View Slide

  2. View Slide

  3. Overview
    • Case Study

    • Recap and advanced commands

    • Debugging Libraries

    • Pry

    View Slide

  4. Case Study
    - use byebug
    - here’s the situation.
    - you’ve been handed an existing project by your boss..

    View Slide

  5. Sacculina Carcini
    - parasitic barnacle
    - takes over the host; host no longer molts; male crabs act like female crabs
    - http://www.flickr.com/photos/81858878@N00/9025250716/in/photolist-eKwLAY

    View Slide

  6. - this project has a test suite
    - previous developer completed feature and hands off a “green” test suite (or so he says)

    View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. - turnip / rspec, explain that steps are executed in order.
    - execution stops rspec expectation is not met.

    View Slide

  12. - not enough information on line 12 to tell us why it failed
    - let’s look at the stack trace in the rspec output

    View Slide

  13. let’s examine the step definition on line 32 to see if it can tell us more

    View Slide

  14. - here we are in the step definition file

    View Slide

  15. - the internal state of the crab should have a key that points to the parasite
    - let’s take a step back and review the feature again..

    View Slide

  16. - here’s the feature
    - and we know the failure is on line 12...

    View Slide

  17. - but we don’t know when the crab’s payload should have its infection
    - it could happen on lines 3 through 11..

    View Slide

  18. - at this point, it may be tempting to investigate the internal details of the crab class and its payload and how that works but that would be premature at
    this point. instead of anticipating where the problems lies, we’re going to allow our tool, the debugger, to direct our investigations. we’re not making
    assumptions as to the root cause of the error. let’s stay “assumption free.”
    - so, let’s use the ruby gem ‘debugger’..

    View Slide

  19. - we have to do two things to use the ‘byebug’ gem

    View Slide

  20. - update our gem file to include a reference to the ‘byebug’ gem

    View Slide

  21. - call the debugger method which pauses our application.
    - but where should we put it?...

    View Slide

  22. - there’s no relationship between host, the crab, and parasite, the sacculina carcini

    View Slide

  23. - instead, let’s place our debugger statement at the first step where the parasite and the host interact.
    - note that execution is paused on the ruby expression immediately following the debugger
    method call.
    - let’s run the debugger

    View Slide

  24. View Slide

  25. - let’s take a moment to discuss what we see in the debugger

    View Slide

  26. - we see ten lines of context

    View Slide

  27. - the line numbers appear in the first column

    View Slide

  28. - here’s the current line where the application is paused

    View Slide

  29. - and that is indicated by the presence of the hash rocket.
    - great. now we know where we are in the debugger session, let’s examine the code in this step...

    View Slide

  30. - we have two lines.
    - ah, line 12 looks interesting. do you remember what the original failure? it was related to the crab’s payload.
    - the crab’s payload was nil when the test expected there to be a value
    - let’s add the crab’s payload as a display (or watched) variable

    View Slide

  31. View Slide

  32. View Slide

  33. View Slide

  34. - first thing to note is that our displayed ruby expression is still nil
    - secondly, whoa, where are we..

    View Slide

  35. - we’re actually in a new file. we’re inside the attach method of the parasite.
    - the debugger command “step” goes into a method definition. we “step” into

    View Slide

  36. - a little different than last time. we provide a number after step. this indicates how many times to run the step command. this is useful when you want to
    issue the same command multiple times. this will run “step” 3 times for us.

    View Slide

  37. View Slide

  38. View Slide

  39. View Slide

  40. View Slide

  41. - yeah, we have a value for the payload

    View Slide

  42. - uh oh, where are we?

    View Slide

  43. - we’re interested in what happens at the turnip step levels of lines 8, 9, 10 and 11.
    - how can we quickly stop execution at those lines? ...

    View Slide

  44. - well, i can think of one way, we could add ‘debugger’ statements at lines 16, 20, 24, and 28...

    View Slide

  45. - so instead of just one debugger statement, we would have five...

    View Slide

  46. - however, there’s a better way. let’s make use of the capabilities provided by the debugger tool.

    View Slide

  47. View Slide

  48. View Slide

  49. View Slide

  50. View Slide

  51. View Slide

  52. View Slide

  53. View Slide

  54. View Slide

  55. View Slide

  56. View Slide

  57. View Slide

  58. View Slide

  59. View Slide

  60. View Slide

  61. View Slide

  62. View Slide

  63. View Slide

  64. - here we are at breakpoint 4

    View Slide

  65. - the crab payload in our display list hasn’t changed from 0x007

    View Slide

  66. - the last time we stepped into a method, we took a detour that wasn’t necessary or informative.
    - let’s explore another debugger command, ‘next’...

    View Slide

  67. View Slide

  68. View Slide

  69. View Slide

  70. - hasn’t changed from 0x007.
    - but wait, what’s that?!?!

    View Slide

  71. - string vs. symbol!

    View Slide

  72. View Slide

  73. View Slide

  74. View Slide

  75. View Slide

  76. View Slide

  77. View Slide

  78. View Slide

  79. View Slide

  80. Recap and Advanced
    Commands
    - here’s the situation.
    - you’ve been handed an existing project by your boss..

    View Slide

  81. Case Study Recap
    • debugger

    • display

    • step

    • break

    • continue

    • next
    - debugger is a method that you may place in your application that will pause its execution allowing you to examine state, modify state, set further break
    points.

    View Slide

  82. Case Study Recap
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next
    - debugger is a method that you may place in your application that will pause its execution allowing you to examine state, modify state, set further break
    points.

    View Slide

  83. Case Study Recap
    • debugger byebug

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next
    If you’re using the latest version of byebug, which is 2.3.1, then you can’t use the ‘debugger’ alias. You must use ‘byebug’

    View Slide

  84. Case Study Recap
    •!!!! s/debugger/byebug/g !!!!

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next
    So everywhere we saw ‘debugger’ used (only on line 10 in spec/steps.rb), you would use ‘byebug.’

    View Slide

  85. View Slide

  86. My PR was accepted yesterday. A new version of the gem hasn’t been pushed to rubygems.org yet but it is coming soon.

    View Slide

  87. Case Study Recap
    • debugger

    • display

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    - also called watch

    View Slide

  88. Case Study Recap
    • debugger

    • display

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    View Slide

  89. Case Study Recap
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    View Slide

  90. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    View Slide

  91. Case Study Recap
    • debugger

    • disp[lay]

    • step [n]

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    View Slide

  92. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  93. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • break <file name>:

    • next
    • debugger

    • disp

    • step

    • break

    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  94. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • b <file name>:

    • next
    • debugger

    • disp

    • step

    • break

    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  95. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • b Class.class_method

    • next
    • debugger

    • disp

    • step

    • break

    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  96. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • b Class#instance_method

    • next
    • debugger

    • disp

    • step

    • break

    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  97. Case Study Recap
    • debugger

    • display

    • step

    • break

    • continue

    • next
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    - continue execution until the application completes or we hit another breakpoint

    View Slide

  98. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  99. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • break

    • next [n]
    • debugger

    • disp

    • step

    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  100. Case Study Recap
    • debugger

    • disp[lay]

    • step

    • break

    • n [n]
    • debugger

    • disp

    • step

    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  101. Advanced Commands
    • finish

    • save

    • source
    - what did we miss?

    View Slide

  102. What did we miss?
    • finish

    • source
    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  103. - remember in our case study when we were on line eleven and we “stepped” into the attach method on @sacculini_carcini?...

    View Slide

  104. - and at that time we stepped 3?
    - what if we had a loop, or many many lines of code?...

    View Slide

  105. - so instead of step, we use “finish” which execute code until the current stack has completed

    View Slide

  106. View Slide

  107. What did we miss?
    • finish

    • save

    • source
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  108. View Slide

  109. View Slide

  110. View Slide

  111. View Slide

  112. View Slide

  113. View Slide

  114. View Slide

  115. - automatically evaluate ruby expressions
    - show full file paths or just the filename
    -
    - display context
    - auto start irb when prompt is shown

    View Slide

  116. What did we miss?
    • finish

    • save

    • source
    • debugger

    • disp

    • step

    • break

    • next

    View Slide

  117. View Slide

  118. View Slide

  119. View Slide

  120. View Slide

  121. View Slide

  122. What else did we miss?
    !
    • which versions of ruby?

    View Slide

  123. View Slide

  124. Debugger Libraries
    • 1.8 -- ruby-debug

    • 1.9 -- debugger, ruby-debug19, debugger2

    • 2.0 -- debugger, byebug, debugger2
    - lag time between new version of ruby and a fully supported debugger
    - debugger2 / byebug - use external C APIs

    View Slide

  125. DEBUGGERS STINK!
    provocative slide!

    View Slide

  126. Why?
    - why? coupling!
    - let’s take a look at the change log for the debugger gem...

    View Slide

  127. View Slide

  128. View Slide

  129. Ruby’s C API
    - in the past, most debugging tools are tightly coupled to the C internals.
    - because the debuggers were hooking into internals, every time ruby changed, you needed a new debugger

    View Slide

  130. Debugger Versions
    • 1.8 -- ruby-debug

    • 1.9 -- debugger, debugger2, ruby-debug19

    • 2.0 -- debugger, debugger2, byebug
    • debugger

    • disp

    • step

    • break

    • next
    - lag time between new version of ruby and a fully supported debugger
    - debugger2 / byebug - use external C APIs

    View Slide

  131. ~/.rdebugrc
    • set autoreload

    • set autoeval

    • set autolist

    - for ruby 1.8.7
    - if you’re using debugger, byebug or debugger2, these are the defaults

    View Slide

  132. Debugger Versions
    • 1.8 -- ruby-debug

    • 1.9 -- debugger, debugger2, ruby-debug19

    • 2.0 -- debugger, debugger2, byebug
    • debugger

    • disp

    • step

    • break

    • next
    • debugger

    • disp

    • step

    - lag time between new version of ruby and a fully supported debugger
    - debugger2 / byebug - use external C APIs

    View Slide

  133. Debugger Versions
    • 1.8 -- ruby-debug

    • 1.9 -- debugger, debugger2, ruby-debug19

    • 2.0 -- debugger, debugger2, byebug
    • debugger

    • disp

    • step

    • break

    • debugger

    • disp

    • step

    • break

    • next
    - lag time between new version of ruby and a fully supported debugger
    - debugger2 / byebug - use external C APIs

    View Slide

  134. View Slide

  135. Pry
    • “powerful alternative to [...] IRB [...]”

    • syntax highlighting

    • “flexible plugin architecture”
    - here’s the important thing, PRY is a REPL (read eval print loop)
    - let’s take a look; two things to do

    View Slide

  136. - add pry and pry-byebug to your Gemfile

    View Slide

  137. View Slide

  138. View Slide

  139. - similar display, column numbers on the left, hash rocket indicates the current line
    - pretty colors

    View Slide

  140. - binding.pry stops us before the binding.pry statement on
    line 10 so we don’t need “; true”

    View Slide

  141. View Slide

  142. - notice we have to use “break”, no shortcuts
    - and we must use the relative path from the project directory to when specifying the file

    View Slide

  143. - after hitting enter, we see info about the break point...

    View Slide

  144. - in addition we see the context too for the break point.

    View Slide

  145. View Slide

  146. - pry tells us how often the break point is hit
    - we have a few more things to note about pry...

    View Slide

  147. - no shortcuts for commands unless you define them

    View Slide

  148. Debugger (+ Pry)
    Versions
    • 1.9 -- pry + pry-debugger

    • 2.0 -- pry + pry-byebug

    View Slide

  149. byebug
    - mashup of debase, another debugger for ruby 2.0 (C ext part) and debugger (lib and test dirs)
    - i didn’t investigate debase much because there’s not a lot of documentation

    View Slide

  150. why byebug?

    View Slide

  151. - only for ruby 2.0. otherwise works as debugger in terms of commands, etc.

    View Slide

  152. Others?
    • Rubinius?

    • JRuby?

    View Slide

  153. View Slide

  154. - for JRuby, feel free to use any of the Java tools for debugging

    View Slide

  155. Overview
    • Case Study

    • Recap and advanced commands

    • Debugging Libraries

    • Pry
    - next, step, break, continue, display
    - finish, source, save
    - ruby-debug 1.8.7, debugger for 1.9, byebug for 2.0
    - can use pry w/debugger and byebug)

    View Slide

  156. Slides and Code and Image
    • https://speakerdeck.com/jwallace/effective-
    debugging-rubyconf

    • https://github.com/wallace/
    sacculina_carcini.git

    • http://www.flickr.com/photos/
    81858878@N00/9025250716/in/photolist-
    eKwLAY

    View Slide

  157. View Slide

  158. Get to know me!
    KNOW ME http://blog.jonathanrwallace.com/about
    FOLLOW ME @jonathanwallace
    WORK WITH ME http://www.bignerdranch.com
    CODE WITH ME github/wallace
    - thanks to Big Nerd Ranch, organizers and people who helped me with this talk

    View Slide