Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

D3630c5a12aa3e85748670729444d5a9?s=128

Jonathan Wallace

November 08, 2013
Tweet

Transcript

  1. Effective Debugging

  2. None
  3. Overview • Case Study • Recap and advanced commands •

    Debugging Libraries • Pry
  4. Case Study - use byebug - here’s the situation. -

    you’ve been handed an existing project by your boss..
  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
  6. - this project has a test suite - previous developer

    completed feature and hands off a “green” test suite (or so he says)
  7. None
  8. None
  9. None
  10. None
  11. - turnip / rspec, explain that steps are executed in

    order. - execution stops rspec expectation is not met.
  12. - not enough information on line 12 to tell us

    why it failed - let’s look at the stack trace in the rspec output
  13. let’s examine the step definition on line 32 to see

    if it can tell us more
  14. - here we are in the step definition file

  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..
  16. - here’s the feature - and we know the failure

    is on line 12...
  17. - but we don’t know when the crab’s payload should

    have its infection - it could happen on lines 3 through 11..
  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’..
  19. - we have to do two things to use the

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

    the ‘byebug’ gem
  21. - call the debugger method which pauses our application. -

    but where should we put it?...
  22. - there’s no relationship between host, the crab, and parasite,

    the sacculina carcini
  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
  24. None
  25. - let’s take a moment to discuss what we see

    in the debugger
  26. - we see ten lines of context

  27. - the line numbers appear in the first column

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

  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...
  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
  31. None
  32. None
  33. None
  34. - first thing to note is that our displayed ruby

    expression is still nil - secondly, whoa, where are we..
  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
  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.
  37. None
  38. None
  39. None
  40. None
  41. - yeah, we have a value for the payload

  42. - uh oh, where are we?

  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? ...
  44. - well, i can think of one way, we could

    add ‘debugger’ statements at lines 16, 20, 24, and 28...
  45. - so instead of just one debugger statement, we would

    have five...
  46. - however, there’s a better way. let’s make use of

    the capabilities provided by the debugger tool.
  47. None
  48. None
  49. None
  50. None
  51. None
  52. None
  53. None
  54. None
  55. None
  56. None
  57. None
  58. None
  59. None
  60. None
  61. None
  62. None
  63. None
  64. - here we are at breakpoint 4

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

    from 0x007
  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’...
  67. None
  68. None
  69. None
  70. - hasn’t changed from 0x007. - but wait, what’s that?!?!

  71. - string vs. symbol!

  72. None
  73. None
  74. None
  75. None
  76. None
  77. None
  78. None
  79. None
  80. Recap and Advanced Commands - here’s the situation. - you’ve

    been handed an existing project by your boss..
  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.
  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.
  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’
  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.’
  85. None
  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.
  87. Case Study Recap • debugger • display • step •

    break • next • debugger • disp • step • break • next • debugger • disp • step • break - also called watch
  88. Case Study Recap • debugger • display <ruby expression> •

    step • break • next • debugger • disp • step • break • next • debugger • disp • step • break
  89. Case Study Recap • debugger • disp <ruby expression> •

    step • break • next • debugger • disp • step • break • next • debugger • disp • step • break
  90. Case Study Recap • debugger • disp[lay] • step •

    break • next • debugger • disp • step • break • next • debugger • disp • step • break
  91. Case Study Recap • debugger • disp[lay] • step [n]

    • break • next • debugger • disp • step • break • next • debugger • disp • step • break
  92. Case Study Recap • debugger • disp[lay] • step •

    break • next • debugger • disp • step • break • debugger • disp • step • break • next
  93. Case Study Recap • debugger • disp[lay] • step •

    break <file name>:<line number> • next • debugger • disp • step • break • debugger • disp • step • break • next
  94. Case Study Recap • debugger • disp[lay] • step •

    b <file name>:<line number> • next • debugger • disp • step • break • debugger • disp • step • break • next
  95. Case Study Recap • debugger • disp[lay] • step •

    b Class.class_method • next • debugger • disp • step • break • debugger • disp • step • break • next
  96. Case Study Recap • debugger • disp[lay] • step •

    b Class#instance_method • next • debugger • disp • step • break • debugger • disp • step • break • next
  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
  98. Case Study Recap • debugger • disp[lay] • step •

    break • next • debugger • disp • step • debugger • disp • step • break • next
  99. Case Study Recap • debugger • disp[lay] • step •

    break • next [n] • debugger • disp • step • debugger • disp • step • break • next
  100. Case Study Recap • debugger • disp[lay] • step •

    break • n [n] • debugger • disp • step • debugger • disp • step • break • next
  101. Advanced Commands • finish • save • source - what

    did we miss?
  102. What did we miss? • finish • source • debugger

    • disp • step • break • next
  103. - remember in our case study when we were on

    line eleven and we “stepped” into the attach method on @sacculini_carcini?...
  104. - and at that time we stepped 3? - what

    if we had a loop, or many many lines of code?...
  105. - so instead of step, we use “finish” which execute

    code until the current stack has completed
  106. None
  107. What did we miss? • finish • save • source

    • debugger • disp • step • break • next • debugger • disp • step • break • next
  108. None
  109. None
  110. None
  111. None
  112. None
  113. None
  114. None
  115. - automatically evaluate ruby expressions - show full file paths

    or just the filename - - display context - auto start irb when prompt is shown
  116. What did we miss? • finish • save • source

    • debugger • disp • step • break • next
  117. None
  118. None
  119. None
  120. None
  121. None
  122. What else did we miss? ! • which versions of

    ruby?
  123. None
  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
  125. DEBUGGERS STINK! provocative slide!

  126. Why? - why? coupling! - let’s take a look at

    the change log for the debugger gem...
  127. None
  128. None
  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
  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
  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
  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
  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
  134. None
  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
  136. - add pry and pry-byebug to your Gemfile

  137. None
  138. None
  139. - similar display, column numbers on the left, hash rocket

    indicates the current line - pretty colors
  140. - binding.pry stops us before the binding.pry statement on line

    10 so we don’t need “; true”
  141. None
  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
  143. - after hitting enter, we see info about the break

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

    break point.
  145. None
  146. - pry tells us how often the break point is

    hit - we have a few more things to note about pry...
  147. - no shortcuts for commands unless you define them

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

    • 2.0 -- pry + pry-byebug
  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
  150. why byebug?

  151. - only for ruby 2.0. otherwise works as debugger in

    terms of commands, etc.
  152. Others? • Rubinius? • JRuby?

  153. None
  154. - for JRuby, feel free to use any of the

    Java tools for debugging
  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)
  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
  157. None
  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