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

Effective Debugging - Big Nerd Ranch Tech Talk

Effective Debugging - Big Nerd Ranch Tech Talk

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

December 06, 2013
Tweet

Transcript

  1. Effective Debugging All the techniques and tactics described herein are

    ways to account for the limited working memory and cognitive power of humans.
  2. Definitions First thing, let’s start with definitions.

  3. What is debugging? We often thinking of debugging as “squashing

    bugs” but it is actually resolving root causes of symptoms. To summarize, debugging is identifying the root cause of the actual behavior of the system when it differs from the desired behavior.
  4. What is effective debugging? Effective debugging is efficiently identifying the

    root cause of the actual behavior of the system when it differs from the desired behavior.
  5. Overview • Definitions • Have a Process • Know Your

    Tools • Available Debugging Libraries • Case Study • Recap and advanced commands • Pry
  6. Have a Process

  7. Have a Process * shamelessly borrowed from Debugging by David

    Agans
  8. Have a Process • Understand the System • Make It

    Fail • Quit Thinking and Look • Divide and Conquer • Change One Thing at a Time • Keep an Audit Trail • Check the Plug • Get a Fresh View • If You Didn't Fix It, It Ain't Fixed
  9. Understand the System Read your bootstrap documentation, application README, tests,

    and READMEs of any libraries your using. Run the tests even. If this isn’t an API and it has a client side interface, practice using it as a user. ! Once my team spent two days debugging a “memory leak” performance regression because we didn’t know there was a gem that disabled GC!
  10. Make it Fail You must be able to reproduce the

    bug. What is the minimum set of user input and data necessary to reproduce it. If you can’t reproduce it, you won’t be able to verify the root cause or causes fixed it!
  11. Quit Thinking and Look Developers well acquainted with an application

    will often make an assumption and jump to a conclusion as to “what is wrong.” Check your ego and read the error. Read the stack trace. Read the functional spec.
  12. Divide and Conquer Binary searches are much faster than a

    bubble sort. Would you sequentially search through the dictionary for the definition of a word you’re looking for? NOPE.
  13. Change One Thing at a Time Again, these points are

    all obvious. If you change more than one thing, how can you be sure what fixed?
  14. Keep an Audit Trail Terrible bugs may take a terrible

    amount of time to resolve. If you spend days on a one bug, you need to be clear about what you’ve already tried. This will come in handy in later steps too.
  15. Check the Plug Question your assumptions. Work from the outside

    in. Start from the basics.
  16. Get a Fresh View Rubber ducking allows someone else to

    check your assumptions. They’ll double check your process in a different way. Maybe they’re an expert or have experience in a different area. Two heads are better than one.
  17. If You Didn't Fix It, It Ain't Fixed If you

    forgot or skipped the “Make It Fail” step, you’re not going to know if it was your work that fixed it. Verify that your fix really fixed it.
  18. Have a Process • Understand the System • Make It

    Fail • Quit Thinking and Look • Divide and Conquer • Change One Thing at a Time • Keep an Audit Trail • Check the Plug • Get a Fresh View • If You Didn't Fix It, It Ain't Fixed
  19. Know Your Tools * in Ruby

  20. Available Debugger Libraries • 1.8 -- ruby-debug • 1.9 --

    debugger, ruby-debug19, debugger2 • 2.0 -- debugger, byebug, debugger2 There’s a lot of options for debuggers in the ruby ecosystem.
  21. DEBUGGERS STINK! But not really, it is because..

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

    the change log for the debugger gem...
  23. None
  24. None
  25. 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
  26. Debugger Versions • 1.8 -- ruby-debug • 1.9 -- debugger,

    debugger2, ruby-debug19 • 2.0 -- debugger, debugger2, byebug - lag time between new version of ruby and a fully supported debugger - debugger2 / byebug - use external C APIs
  27. ~/.rdebugrc • set autoreload • set autoeval • set autolist


    - for ruby 1.8.7 - if you’re using debugger, byebug or debugger2, these are the defaults
  28. Debugger Versions • 1.8 -- ruby-debug • 1.9 -- debugger,

    debugger2, ruby-debug19 • 2.0 -- debugger, debugger2, byebug - lag time between new version of ruby and a fully supported debugger - debugger2 / byebug - use external C APIs
  29. Debugger Versions • 1.8 -- ruby-debug • 1.9 -- debugger,

    debugger2, ruby-debug19 • 2.0 -- debugger, debugger2, byebug - lag time between new version of ruby and a fully supported debugger - debugger2 / byebug - use external C APIs
  30. Case Study - use byebug - here’s the situation. -

    you’ve been handed an existing project by your boss..
  31. Have a Process • Understand the System • Make It

    Fail • Quit Thinking and Look • Divide and Conquer • Change One Thing at a Time • Keep an Audit Trail • Check the Plug • Get a Fresh View • If You Didn't Fix It, It Ain't Fixed
  32. 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
  33. - this project has a test suite - previous developer

    completed feature and hands off a “green” test suite (or so he says)
  34. None
  35. None
  36. None
  37. None
  38. - turnip / rspec, explain that steps are executed in

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

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

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

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

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

    have its infection - it could happen on lines 3 through 11..
  45. - 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’..
  46. None
  47. - update our gem file to include a reference to

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

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

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

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

  54. - the line numbers appear in the first column

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

  56. - 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...
  57. - 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
  58. None
  59. None
  60. None
  61. - first thing to note is that our displayed ruby

    expression is still nil - secondly, whoa, where are we..
  62. - 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
  63. - 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.
  64. None
  65. None
  66. None
  67. None
  68. - yeah, we have a value for the payload

  69. - uh oh, where are we?

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

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

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

    the capabilities provided by the debugger tool.
  74. None
  75. None
  76. None
  77. None
  78. None
  79. None
  80. None
  81. None
  82. None
  83. None
  84. None
  85. None
  86. None
  87. None
  88. None
  89. None
  90. None
  91. - here we are at breakpoint 4

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

    from 0x007
  93. - 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’...
  94. None
  95. None
  96. None
  97. - hasn’t changed from 0x007. - but wait, what’s that?!?!

  98. - string vs. symbol!

  99. None
  100. None
  101. None
  102. None
  103. None
  104. None
  105. None
  106. None
  107. Recap and Advanced Commands - here’s the situation. - you’ve

    been handed an existing project by your boss..
  108. 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.
  109. Case Study Recap • 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.
  110. Case Study Recap • debugger • display • step •

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

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

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

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

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

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

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

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

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

    b Class#instance_method • next
  120. Case Study Recap • debugger • display • step •

    break • continue • next - continue execution until the application completes or we hit another breakpoint
  121. Case Study Recap • debugger • disp[lay] • step •

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

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

    break • n [n]
  124. Advanced Commands • finish • save • source - what

    did we miss?
  125. What did we miss? • finish • source

  126. - remember in our case study when we were on

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

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

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

  131. None
  132. None
  133. None
  134. None
  135. None
  136. None
  137. None
  138. - automatically evaluate ruby expressions - show full file paths

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

  140. None
  141. None
  142. None
  143. None
  144. None
  145. None
  146. 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
  147. - add pry and pry-byebug to your Gemfile

  148. None
  149. None
  150. - similar display, column numbers on the left, hash rocket

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

    10 so we don’t need “; true”
  152. None
  153. - notice we have to use “break”, no shortcuts -

    and we must use the relative path from the project directory to when specifying the file
  154. - after hitting enter, we see info about the break

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

    break point.
  156. None
  157. - pry tells us how often the break point is

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

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

    • 2.0 -- pry + pry-byebug
  160. Others? • Rubinius? • JRuby?

  161. None
  162. - for JRuby, feel free to use any of the

    Java tools for debugging
  163. Other tools? • #source_location • puts '*'*80 • pp •

    gem which • ap - awesome_print • editor plugins (ctags in vim, cucumber step definitions)
  164. Slides and Code and Image • https://speakerdeck.com/jwallace/effective- debugging-bnr-tech-talk • https://github.com/wallace/

    sacculina_carcini.git • http://www.flickr.com/photos/ 81858878@N00/9025250716/in/photolist- eKwLAY
  165. Credits and References and Further Reading • http://blog.regehr.org/archives/199 • http://blog.regehr.org/archives/849

    • http://www.amazon.com/Debugging-David-J-Agans-ebook/ dp/B002H5GSZ2/ref=sr_1_1? s=books&ie=UTF8&qid=1386325548&sr=1-1&keywords=d ebugging%3A+the+9+indispensable+rules+for+finding+even +the+most+elusive+software+and+hardware+problems • http://blog.bignerdranch.com/4390-slide-design-developers/ • https://kuler.adobe.com/vintage-card-color-theme-3165833/
  166. 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