Effective Debugging
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 50 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/37467 (DOI) | |
Publisher | ||
Release Date | ||
Language | ||
Producer | ||
Production Place | Miami Beach, Florida |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
Ruby Conference 201328 / 50
2
3
7
13
15
17
18
19
28
29
35
39
44
48
49
50
00:00
MereologySound effectContext awarenessSelf-organizationXML
00:31
SpacetimeMenu (computing)FingerprintRight angleContext awarenessSelf-organizationCASE <Informatik>Software bugLibrary (computing)Observational studyRevision controlProjective planeSimulationBitQuicksortClient (computing)Boss CorporationCycle (graph theory)Computer animation
01:52
Water vaporNetwork socketOcean currentGastropod shellSelf-organizationGreatest elementMultiplication signWaveMereologyCode1 (number)OvalWeb crawlerShape (magazine)SoftwareChemical equationType theoryCASE <Informatik>Bookmark (World Wide Web)
04:11
Error messageDifferent (Kate Ryan album)Level (video gaming)Uniform resource locatorCausalityRootCodePower (physics)AbstractionComputer programSoftware testingLine (geometry)Network topologyProjective planeSemiconductor memoryGreatest elementFlow separationCartesian coordinate systemCASE <Informatik>Right angleContext awarenessSocial classSoftwareSoftware bugPoint (geometry)SpacetimeVariable (mathematics)Instance (computer science)Software developer10 (number)BitDependent and independent variablesString (computer science)Library (computing)MereologyPresentation of a groupComputer fileTracing (software)Error messageSuite (music)Pointer (computer programming)Formal verificationDeterminant
07:50
Normal (geometry)Expected valueError messageOrder (biology)Point (geometry)Line (geometry)ImplementationComputer programInformationNetwork topologySeries (mathematics)Level (video gaming)Computer animation
08:55
Computer fileBitCASE <Informatik>Line (geometry)Level (video gaming)Variable (mathematics)Instance (computer science)Subject indexingPointer (computer programming)Computer wormProcess (computing)Cycle (graph theory)Right angleBasis <Mathematik>Gaussian eliminationError message
09:48
Line (geometry)Normal (geometry)RootDifferent (Kate Ryan album)Image resolutionLine (geometry)CodeCartesian coordinate systemGaussian eliminationSheaf (mathematics)Error messageProcess (computing)Goodness of fitResolvent formalismSpacetimeShared memoryCausalitySoftware developerFreewareComputer animation
10:51
Installation artOpen sourceNormal (geometry)NumberSystem callLine (geometry)Computer fileComputer animation
11:19
Goodness of fitLine (geometry)Variable (mathematics)Instance (computer science)Computer animation
11:48
DebuggerComputer wormDrop (liquid)Cartesian coordinate systemBlogCASE <Informatik>Focus (optics)Statement (computer science)Line (geometry)Fiber bundleDebuggerSystem callComputer animation
12:17
Computer wormFiber bundleDebuggerLine (geometry)Hash functionContext awarenessCodeDebuggerIntegrated development environmentFocus (optics)Intrusion detection systemNumberMoment (mathematics)Error messageComputer animation
13:06
Computer wormFiber bundleElectronic visual displayDebuggerPointer (computer programming)MathematicsComputer wormLine (geometry)Error messageSoftware testingCartesian coordinate systemElectronic visual displaySystem callVariable (mathematics)DebuggerExpressionMultiplication signMereologyBlog
13:40
SineFiber bundleDebuggerComputer wormElectronic visual displayStructural loadExpressionMereologyDebuggerMultiplication signMathematicsComputer animation
14:13
Type theoryIntegerParameter (computer programming)BitMultiplication signAbstractionContext awarenessComputer fileLevel (video gaming)Electronic visual displayDoubling the cubeFood energyLine (geometry)Stress (mechanics)ExpressionQuicksortPoint (geometry)Computer animation
15:22
Presentation of a groupDebuggerComputer wormComputer fileMathematicsGoodness of fitService (economics)Doubling the cubeExpressionSystem callLine (geometry)BitComputer animation
15:54
Computer wormPresentation of a groupDebuggerRevision controlComputer fileComputer fileCartesian coordinate systemContext awarenessDebuggerComputer wormLibrary (computing)Line (geometry)BlogLevel (video gaming)Statement (computer science)ExpressionComputer animation
16:23
Line (geometry)Normal (geometry)Web pageDebuggerComputer wormLipschitz-StetigkeitLevel (video gaming)Cartesian coordinate systemDebuggerControl flowLine (geometry)Statement (computer science)Revision controlPoint (geometry)CodeComputer animation
16:55
Revision controlComputer fileLevel (video gaming)Line (geometry)Web crawlerLine (geometry)Control flowComputer programSystem callCartesian coordinate systemPoint (geometry)Complete metric spaceComputer animation
17:26
Parameter (computer programming)Control flowPresentation of a groupComputer wormLine (geometry)Control flowElectronic visual displaySoftware testingInstance (computer science)SpacetimeComputer wormPoint (geometry)Right angleString (computer science)MathematicsSymbol tableStatement (computer science)Multiplication signComputer fileBlogExpressionNumberRow (database)Analytic continuationCodeCASE <Informatik>Software developerSoftware bugVariable (mathematics)Open sourceClique-widthError message1 (number)Different (Kate Ryan album)DebuggerSpeicheradresseGoodness of fitRegular graphRevision controlContext awarenessHash function3 (number)Computer animationLecture/Conference
20:09
DebuggerHill differential equationoutputState diagramMathematicsStatement (computer science)String (computer science)Electronic visual displaySoftware testingGreen's functionRevision controlDebuggerSuite (music)QuicksortWordConfidence intervalBlogUniform resource locatorSoftware bugDialectPoint (geometry)Software maintenanceControl flowBitComputer animation
21:07
Convex hullAddress spaceDebuggerAliasingPhysical lawAreaExecution unitOnline helpSocial classOpen sourceRevision controlAliasingExpressionElectronic visual displayIntegerLibrary (computing)Real numberNumberInstance (computer science)Parameter (computer programming)Computer fileSocial classControl flowOpen sourceMultiplication signLine (geometry)Metropolitan area networkSoftware bugFood energyComputer animation
22:26
Fiber bundleElectronic visual displayComputer wormStructural loadDebuggerPresentation of a groupGastropod shellMultiplication signObservational studyCASE <Informatik>Frame problemLine (geometry)Cartesian coordinate systemLibrary (computing)Inheritance (object-oriented programming)Computer animation
22:58
DebuggerIntegrated development environmentPublic domainSpacetimeCausalityState of matterRootSoftware bugCartesian coordinate systemDatabaseMathematicsControl flow
23:37
Electronic visual displayComputer wormGroup actionParameter (computer programming)State of matterComputer fileDrop (liquid)outputControl flowLine (geometry)Type theorySoftware bugArithmetic meanStatement (computer science)ExpressionLecture/Conference
24:10
Structural loadSoftware testingDefault (computer science)State of matterFunction (mathematics)Context awarenessDebuggerExpressionComputer fileSoftware bugLine (geometry)Type theoryControl flowPoint (geometry)Open sourceCoefficient of determinationSoftware testingRight angleElectronic mailing list
25:19
Open sourceOpen sourceCondition numberMultiplication signSpacetimeExpressionControl flowDebuggerLoop (music)Object (grammar)State of matterIterationMessage passingPoint (geometry)BlogMetropolitan area networkVariable (mathematics)INTEGRALLecture/Conference
26:00
Open sourceSoftware testingRevision controlPhysical systemVariable (mathematics)Inheritance (object-oriented programming)Debugger2 (number)Electronic visual displayContext awarenessRevision controlPatch (Unix)Point (geometry)Lecture/Conference
26:37
DebuggerAverageNormed vector spaceRevision controlSlide ruleMultiplication signPoint (geometry)BlogComputer configurationExtension (kinesiology)CASE <Informatik>Level (video gaming)Patch (Unix)Software bugCoefficient of determinationLibrary (computing)HookingType theoryDebuggerSoftware maintenanceCodeMultilaterationMathematics
28:18
DebuggerRevision controlCore dumpRepository (publishing)Variable (mathematics)DebuggerCASE <Informatik>Point (geometry)Computer configurationOpen sourceFunctional (mathematics)Software maintenanceDesign by contractGoodness of fitHookingDefault (computer science)Computer fileSoftware bugRow (database)BlogElectronic mailing listInterface (computing)GodOnline helpSoftware developer
30:10
ArchitectureComputer architectureObservational studyCASE <Informatik>Exterior algebraPlug-in (computing)Lecture/Conference
30:41
Open sourceInferenceBlogSystem callSoftware bugKey (cryptography)Revision controlDifferent (Kate Ryan album)Graph coloringGeometryLecture/Conference
31:18
Default (computer science)Control flowLine (geometry)Functional (mathematics)Type theoryPoint (geometry)AliasingComputer fileDifferent (Kate Ryan album)Cartesian coordinate systemLie groupContext awarenessForcing (mathematics)Physical systemComputer animation
31:48
Control flowContext awarenessPoint (geometry)Multiplication signControl flowCASE <Informatik>DebuggerRight angleTraffic reportingInformation privacyPointer (computer programming)Computer animation
32:25
CodeKeyboard shortcutAliasingRevision controlDebuggerCoefficient of determinationSoftware bugDebuggerMashup <Internet>Software testingLibrary (computing)Extension (kinesiology)Multitier architectureMilitary baseRevision controlBlogSoftware developerCovering spaceSource codeLecture/Conference
33:17
DebuggerBlock (periodic table)Source codeText editorSource codeBitSemiconductor memoryCASE <Informatik>Revision controlFunctional (mathematics)Axiom of choiceLibrary (computing)Java appletPhysical systemDebuggerRight angleControl flowSoftware bugMetropolitan area networkArithmetic meanProcess (computing)Observational studyElectronic visual displayComputer animationLecture/Conference
34:18
Slide ruleObservational studyPoint (geometry)Lecture/Conference
34:48
Computer-generated imagerySlide ruleCodeSpacetimeSoftwareTwitterDigital photographySource codeAttribute grammarRepository (publishing)BlogProjective planeCodeoutputMultiplication signMobile appFront and back endsType theoryShared memoryPresentation of a groupAndroid (robot)Branch (computer science)Computer animationLecture/Conference
Transcript: English(auto-generated)
00:02
Hello everyone. Thanks for coming to my talk. My name's Jonathan, and I'm going to talk
00:22
about effective debugging today. And before I get started, I wanted to explain this awesome flavor saver that I'm rocking right now. I'm part of Movember, if you've heard of this. It's an organization that's raising awareness about prostate and testicular cancer. If you're interested in helping out, feel free to come and talk to me after
00:44
the talk. All right. So, today I want to share with you my take on what it takes to be effective in doing debugging in Ruby. That's going to involve us looking at a case study. We'll look at some basic commands that you can use in almost any library, any library
01:02
in Ruby that supports debugging. After that, we'll recap what we learned, talk about some advanced commands that may not be available in all the versions of all the libraries. After that, we'll sort of look at a little bit of an overview of the debugging ecosystem so you'll know which version of which library to use with which version of Ruby. And then we'll touch on Pry. If you were in the previous talk in this room, it was an exceptional
01:23
talk. The guy gave a great, went into great detail about what makes Pry so great. We're not going to do that here, but we will talk about the intersection between Pry and some debugging libraries. So let's get started. Let's look at our case study. So in this case study, we're going to use a gem called ByBug. And here's the situation.
01:41
We've received a project from a client or a boss, and this project is a simulation of a relationship between a crab and a parasite. And this parasite is called Sacculina carcinii. This is a parasitic barnacle, and if you look at this picture of the crab and you
02:00
look at the yellow glistening, disgusting thing on the bottom of it, that's called an externa, and that's where the parasite releases the larva children into the water. And there's a really interesting aspect. If you haven't investigated, one of my favorite topics is some biological host-parasite relationships. And this is one of the more interesting ones. I've got a few more if you want to hear about them. I can share
02:23
the general details with you. But in this case, what this larva does, the Sacculina carcinii, it lands on a crab in the water, and it'll crawl along the shell of the crab until it finds some soft part of the exoskeleton. So that's either a joint or an eye socket or an eye stalk. And then what it does is it pierces that soft part of the crab and
02:45
ejects itself into the crab, you know, throwing away its thorax and abdomen. Then it crawls along the inside of the crab. It migrates its way to underneath the heart of the crab, and it starts taking nutrients and growing. This takes a couple of weeks of time. Then this is where it gets really creepy. So this is already, this parasite's in
03:02
there, and that's sketchy and scary. You know, you've got another organism living in you. But what gets really weird is that it actually starts extending tendrils into the crab's brain, and it starts to change the hormone balance of a crab. So female crabs in general, they, you know, they reproduce, so they have an egg sac, and when
03:20
they want to spread their eggs, they climb up onto a rocky shelf or rocky outcrop, and they wave their claw in the water to help distribute their eggs. Well, the same behavior is what needs to happen to distribute this parasite. But this parasite's not picky. It doesn't just infect female crabs. It also infects male crabs. In fact,
03:41
what it does is it changes the shape of the male crab to be like a female crab. The male crab now acts like a female crab. It performs the same type of behaviors, including crawling up to the top of some, you know, the high part on the bottom of the ocean where the current is strong and waving its claw to help distribute the larva. So really sketchy stuff, a really interesting
04:01
relationship, and our software is simulating this relationship. So we're going to, we're going to look at the code base and see if we can add some features to it. So here we are. We've cloned the code base down, and we're going to run our spec. So this project has a test suite, which is fantastic. It should allow us to move a little bit quicker. And the previous
04:22
developer that gave us this code base says everything is good to go. You can start adding features right away. But trust but verify. We want to make sure that everything's working the way it says it is. We're going to run the test suite. So we do that, and we have a failure. Darn. That stinks. But it's not altogether unexpected, right? Sometimes developers are optimistic about how
04:41
things work. In my experience in developing software, it's very, very tempting for us to ignore the things that we don't understand. And a big, huge long stack trace when you're getting started in Ruby is very intimidating, and people don't want to read it. But it's still intimidating for me, somebody who's been developing Ruby for five or six years. And so my first point I'd like to
05:02
make is to make sure you read the errors that you receive. Read them closely. It's very easy to not be focusing, to not have our attention at a hundred percent, because the goal here is that we want to be efficient. And if we're not giving our full attention, it's very easy for us to make mistakes that we, you know, glide over and then find out ten minutes
05:22
later that if we'd only paid attention in the very beginning we would have caught it right away. So we're gonna do that right now. We're gonna get that error, and we're gonna read that. So the failure error is that the crab's abdomen should have an external. Remember that yellow sac and image? At some point in this test, that should be present. We're gonna look at the expected value, and this is the part where my eyes as an
05:42
experienced Ruby developer will start to glaze over. There's a class name followed by its location and memory, which is this nice hexadecimal string, and then it has a bunch of instance variables. And I don't want to look at it, but I want to make sure that I know what that is. And, and so now I know that that's an instance of Sacculina Carcini, which is the
06:00
parasite. And instead of seeing an instance of that class, the parasite, we're seeing a nil value. So the next thing we want to look at after we know we, after we're sure we're confident we know what the error is in our test suite, is to examine the stack trace. Now my tendency, and when I first started developing, even still now, is to jump into the details and get to the
06:22
nitty-gritty. I would advise against that. A well-written software will, will segregate out into separate, different layers of abstraction, and at the higher level of abstractions, we can see the broad details of what the application or the program is supposed to do. If it's well-written software, that'll be the case, and we're gonna assume that here. I find that that's the best way
06:43
for us to partition the problem space where the bug can lie. So in a typical software application, you may have tens of thousands of lines of code. If you jump down into the details, there's a lot of details in the application you're gonna have to look through to find the bug. So if we can figure out a way to partition that space, and maybe do like a binary search, let's cut it in
07:02
half and determine that the bug doesn't exist in this half, and we can throw that half away, then we're, we're doing fantastic. We can quickly find the, the, the root cause much, much faster and resolve it. So for that reason, I advise for you to start at the bottom of the stack trace for the first piece of code that you're responsible for. So that doesn't include libraries.
07:22
You don't want to dig into library code unless you're sure there's an error there, and 99, 999 thousand, no, 9,999 times out of 10,000, it's always your code, right? It's never the other person's code. So in general, find the code that you own and start there. That should be the highest level abstraction. We want to make sure that things are good there, and we understand the context as we dive into those layers. So in this case, there's on
07:45
line 12 of a feature file. Now we are using a tool called Turnip. This app, this program is using Turnip, which is a, gives us Gherkin syntax. If you're not familiar with that, that's, it's also used in Cucumber, which gives us a nice
08:02
high-level overview. The features are written in plain English. The implementation, implementation details are abstracted away, and that's what we have here. And then the key point to take away from, from Turnip, if you're not familiar with it, is that we have a series of steps. They're executed in sequential order. If any step has, contains an expectation that fails,
08:21
execution will stop on that step, and that will be where the error is reported, and the stack trace is shared with us, like the one we just saw, the one we just saw. So line 12 is the line we're interested in, and if we look at line 12, it's just telling us the same thing that was in the error. We don't have any more information. It's not telling us exactly what's going. So it's, the other thing I like to point out is that it's
08:41
important to go back to my error. It's important for me to maintain a, a good understanding of what, what problem I'm trying to solve. If I get lost in between the trees, I can forget what problem was I trying to solve. So let's go back and look at our stack trace again. We're actually interested in the next level up, and in that case, that is specsteps.rb on line 32. So
09:04
let's examine that step definition file and see if we can learn a little bit more. So here we are. I've opened up the specsteps.rb file, and we are interested in line 32. If we take a second to read this line, we can see
09:20
that we have an instance variable of a crab. There's some method called payload that's being called on it, and we're, we are indexing into that via infection, and we expect whatever the value of that is to equal an instance variable that says Sacculena Carcini. So if we think about our error, we can say that there's an instance of a Sacculena Carcini, and we expect it to
09:43
be there, but we're seeing nil instead. So if we take a step back and look at our feature again, we know the failure is on line 12, then we have a really good idea of where the error is probably occurring. It's somewhere between lines 3 through 11, right? A simple process for elimination. That's our, that's our
10:04
search space. This is the problem, where the problem could reside. We need to figure out where in between lines 3 and 11 this, the error is actually occurring, determine the root cause, and resolve it. So again, at this, it's always tempting to jump down into the details of the crab and try to figure out what's going on. In this example that I'm sharing with you, it's very, it's
10:24
going to be a simple example. We could get away with that. We can do that in small applications, but in larger applications that maybe have years of development, years of different approaches to solving problems with different styles in the same code base, maybe good code coverage in some sections of the application and not in the others, that can lead you down
10:41
rabbit holes that take, that can distract you from actually getting your problem solved. So I say let's not do that. Let's keep things very simple. Let's try to stay assumption-free if we can. So we're going to use the ByBug gem. It's hosted on RubyGems, and there's two things that we have to do to use ByBug. Number one, we're going to add it to our gem file, run
11:01
bundler, and then number two, we need to make a method call. So we're looking at our step file here, and we're going to add a method call in here, but where should we put it? Remember, we have those steps when we were looking at our feature file. We saw lines 3 through 11. We're looking at the actual step definitions now. Well, here's the first two features. There is a crab, and there
11:24
is a sacculina carcinii. So there is a host, and there is a parasite. From looking at lines 2 and line 6, we can quickly see that there's not any relationship between those two instance variables. So this is probably not where our problem is going to lie. We can sort of say make a good safe assumption that the background steps, they're fine. If there was even more
11:43
going on in there, we might have to dig into there a little further, but we're good. So we're going to drop it into the very first step that's not a background step that was occurring on line 8 in the feature file, which in this case we're going to drop it in line 10. So we're dropping the debugger method call on line 10. The important thing to note about using a debugger, if
12:02
you haven't used one in the past, is that when execution reaches a debugger statement and the debugger method call, the execution of our application is going to stop and will be dropped into the debugger session. So let's see that happening. We're going to run our focused feature, and here we are. You can see that we ran bundle, and then we run our focus feature, and when we do
12:22
that, we are paused. So let's take a moment and dissect what we see. If you've not used the debugger, and most people, my familiarity when I started using debugger was GDB. That's my background. When I came to Ruby, I struggled with the fact that there wasn't great debugging support out of the gate, but if you're a person who's been using IDEs in the past, it may be a little
12:41
confusing, so we're going to break this down real quick. Here's the context. We can see 10 lines of context, and we can see the line numbers, where we're at in the left-hand column. We can see where they execution is paused. We're paused on the line 11. There's a nice little hash rocket there that shows us that to keep us knowing where we're at. So let's
13:04
examine the code in this step. So there's two lines, and I don't know what's going on in line 11. I don't need to know, but I can tell from the error message that we received that line 12 looks really interesting. The Krabs payload was nil when the test expected there to be a value. So we're
13:23
at the... we want to make sure that we can see how that value changes throughout the execution of application. So there's a nice feature in the debugger called display. This is a watched variable or display variable. We're going to write out display and then an arbitrary Ruby expression, and we're going to hit enter. Once we do that, note that now
13:42
in our debugger session, as part of that, we have something that is going to display the expression on every debugger command that we provide to it. So we will, can every step that we take through the application, every time execution is paused, we can verify has something changed with that particular Ruby expression. All right, so we're going to move forward.
14:03
We've got that set now. So we're going to use the step command. This is the next command. If you're not familiar with this, this is going to execute one Ruby command and move us forward. So we're going to enter, hit step, and hit enter. And the first thing to note is to double check. Has anything changed with our display Ruby expression? And nothing has. It still points to nothing.
14:23
But something else has changed. You may have noticed that the context for where we're at has changed. And so we're no longer in the step definition file. Now we are in another file, which is cut off a little bit on my slide, but it's the it's the parasites attach method. And we can see that's not where we want to be. So we've just dropped down
14:42
one level of distraction a little bit deeper than I think we need to be. We want to stay at the same level until we've determined that we need to dive deeper. This will keep us from falling down rabbit holes and spending time doing things that are not going to lead us to resolve an issue. Sometimes you get lucky if you try that, but if we're playing a percentages game, it's not the way to go. So I'm going to cheat a little bit here. I'm going to
15:03
type in step three. So I can pass an arbitrary argument, an integer argument, excuse me, I can pass an integer argument to step that'll tell it how many times to run. How do I know that I should run it three times? Well, I can see here that there's three lines, but it's because I wrote this and I already know that. So we're going to use that for now just to just to move along. So we're going to step
15:22
three, and that's going to take us back up to the level of abstraction that we were operating at before. So now we're paused before the execution of line 12, and we are back in our steps file and double checking that our displayed variable hasn't changed. So even though I sort of cheated and I skipped through the attach method, nothing changed in our displayed Ruby expression. So we're good. I cheated a
15:43
little bit, but that way I just cut out at that method. If that method was seven method calls and maybe hitting an external service and nothing changed, then I didn't have to go examine those things. So that's a win. So we're going to step forward again, and voila, something has changed. Our displayed Ruby expression,
16:01
which is the Crabs payload infection, has a value. This is fantastic. But our context has changed again, and this can be a little jarring when you're first using debugger. We're now on the turnip rspec.rb file. Now we have actually moved into the internal turnip library. This is not where we want to be at all. We're not even in our
16:21
application anymore. So we're interested in what happens at the turnip step levels of 8, 9, 10, and 11. We're not concerned about background. So how can we quickly stop execution in each of those lines? Well, there's one way that I've already shared with you that we could use. We could add debugger statements to line 16, 20, 24, and 28. So we have it on
16:43
line 10 that would look something like this. We'd have debugger statements. Now we're littering our code, but there's a much better way. And we can make use of another command called break, which adds a break point, which is a place that the execution of the application should stop. I'm using an abbreviated version of the break command. It's normally you use break, spelled out
17:00
B-R-E-A-K, and instead I'm just using the shorthand version. I'm going to create a break statement on line 16 of steps.rb. So I'm going to do the same thing again on line 20, again on line 24, again on line 28, and now we've got all our break points at each of the step definitions. And now I'm going to use a new command called continue, which says continue execution
17:21
the application until the program completes or I hit another break point. So we continue. We hit enter, and now we can see that we have a break. We're at break point number one. Our crabs payload has stayed the same, and we are on line 16. All right. So fantastic. Nothing has changed. You can't quite see
17:41
the memory location of this instance of Sacculina Carcini. That's fine. Trust me. It's good. It hasn't changed. So we're going to continue. Again, we're partitioning the search space. We're not concerned. We're looking to see where does this value change for the first time. So we hit continue again, and now we hit break point number two. We examine our displayed variable. It's
18:03
good. We hit continue again. Now I'm using the abbreviated version of continue, which is just the character C. And we hit break point number three. Again, our displayed arbitrary Ruby expression, in this case the crabs payload, is still the same. And then we hit continue again, and now we are at
18:22
break point number four. Still, nothing's changed in our displayed variable. This is interesting, right. We would expect, we've really just cut out about 75 percent of the search space where the error could reside. So we're looking at this line. The crabs does, at crab doesn't have an extern, this, this, this method called extern or question mark. The
18:42
last time we stepped into a method, we took a detour that wasn't necessary or informative. So let's look at another command that we can use. We're gonna look at the command called next. And the next command, what it does is it will execute the next line of code, and if that's a, if it's a method, it will wait till that method returns before pausing execution again. So this, if
19:03
this method contains a lot of things that are going on, it can take a little bit of time, but once you hit, type in next and hit enter, you'll move to the next line. So in this case, it's line twenty-nine. And there's something interesting about this line. If you were in the pry talk, you might have, you might have expected this. There, there's the, there's nothing else that could
19:22
be wrong, but if we look back to our display variable and start comparing, now that we have, we have the context between what we expect to see and what the test is expecting in the same place, we can see that we have a very common error. The string versus symbol. If you're a Rails developer, I would say that Rails facilitates
19:40
us making this mistake because it uses hash width and different access, which makes us think that strings and symbols that represent the same thing are, are the same thing, but they're not. This, since this is a regular Ruby program, this is probably the source of the bug and the code, and it's an easy one to make. So we're gonna quit out of our debugger session and test
20:01
this out. And then for some reason, all the debuggers like to make sure you're really sure and confident that you're ready to quit. So let's go dive into our steps file. We're going to make the change. We have a string for an infection. We're gonna change it to symbol, and then we're going to remove our debugger statement. We've got our steps file.
20:21
We save that, and now we can run our test suite, and success. We've got a green test suite. Now we can move forward with confidence in developing our feature set or, or doing whatever is needed. So to recap, we covered six commands. We talked about debugger. We talked about display. We talked about step, break, and continue, and next. The debugger method
20:44
pauses the execution, but actually I sort of just lied to you guys a little bit. We remember we're using the by-bug gem. By-bug doesn't support the method debugger. It actually requires that you use by-bug. If you're using 2.3.1, which is the latest version.
21:00
So anywhere we saw the word debugger used, which was actually only in one location, you would have to use by-bug. However, that should change. Just recently I, I submitted a pull request to the maintainer of by-bug, and that's been accepted. So the next version that gets pushed out to RubyGems, you can use the debugger alias. So to recap display, to go into a little more detail, you can pass in an arbitrary
21:23
Ruby expression. You can abbreviate it as D-I-S-P. The step command takes an arbitrary integer argument that tells how many times it needs to be done. The break command, the way we saw it used, you pass in a file name followed by a colon followed by a line number. You can use
21:41
the abbreviated version. And then there's actually a couple other ways. One, you can drop in a break statement for the method, for a class method by using class dot class method. You can also do the same thing for an instance method. And then we looked at the continue command. We looked at the next where you can also, we didn't see this, but you can pass in an integer as
22:00
an optional argument to next. And you can also abbreviate next as in. So let's look at a couple of advanced commands. Those commands that we just looked at, you can use that in any version of the libraries that I'm gonna talk about today, which are the majority of the debugging libraries. But we missed a couple of things that are advanced that are available in by-bug that are highly useful.
22:20
And those are finish, save, and source. So let's look at finish real quick and see how that would work. The finish, remember in our case study a couple of minutes ago when we were looking on line eleven and we stepped into the attach method and that was not the thing that we wanted to do. And so then I used step three to jump back out of it and I just happened to know that if I stepped three times we'd, we would pop that frame
22:42
off the stack. We could have used the finish command there instead. That would have runs execution until a stack frame has popped off and then repause execution of the application. And so we come back out to line twelve. Super simple. Doesn't work in some of the libraries that we're gonna talk about in a couple of minutes. Save. This one is super, this one is key.
23:02
In simple applications, you probably won't need this, but in more complex applications that have external dependencies on third-party APIs or on the database, you may establish, you may be trying to partition your search space of the problem domain to figure out where the bug is lying or where the root cause of the bug
23:20
is lying. And you may have to exit out of your debugger session and reset that state if you're debugging something in your development environment or, even worse, if you were debugging something in your production environment. So the save command allows us to save this, this state of the debugger session. So if we were to input our, our displayed expressions along
23:43
with some break statements, and we needed to change, we needed to exit out of the application and change some external state to see if we can trigger debug again. We can use the save command. It takes a optional argument that is a file name. If you don't, it'll drop it somewhere in temp. And then once you hit enter there, it actually saves it
24:02
out to that file name. I like to use something like debug.command. It's pretty obvious what that is. And then if we go look at the debug.commands file, we'll see that the statements, the lines that we just entered are there. We, our break statements are there. Our displayed value is there. As well as some more things. So let's, let's take a second
24:21
to talk about what those are. Auto eval means that if I type something, an arbitrary expression into my prompt in a debugger session, that will automatically evaluate it. That's on by default and by bug. Base name is off. So right now we're seeing the full path for all files in the output. You can turn that off if you don't need that. I
24:41
left it on here. Because that's the default. Testing, I'm not actually sure what that does. But it seems to be off. So I guess that's a good thing. Autolist is the thing that's giving us the context. That's a default of by bug. And then autirb is off. So if you like to use features of IRB, you can turn that on so that instead of landing
25:00
into a debugger prompt, you're landed into, you're dropped into an IRB prompt. And the way that works is it'll try to execute whatever you type in as a debugger command first, and if that fails then it'll execute it in the context of IRB. So pretty cool. So that's the save command. You can think about the save command as serializing the state of your debugger session out to a file. Alternatively you have source,
25:20
which does what you would think. This is deserializing the state of a debugger session. This is when we've restarted and we don't want to have to reenter all our breakpoints. One of the things that I haven't talked about in this talk is that you can pass conditionals into your break commands as to maybe if you want to only call the, hit the breakpoint on another arbitrary Ruby expression. Remember we're
25:41
trying to partition our search space so if you have a huge loop that's looping over things a thousands of times or a bunch of data that you're not interested in, you might not need to wait until the 999th iteration of your loop, maybe if you know there's a thousand objects in there. You don't want to hit that breakpoint 999 times, so you can pass an arbitrary expression to handle that. So we use the source command. We hit enter. Fantastic. We
26:02
see that our breakpoints are automatically created for us nice and quick, that the display variable is there, and that the other session variables for the debugger are reestablished. So, fantastic. You're ready to run out and go use the debugger, right? You're super excited. I can tell you guys are ready to walk right out. Hold up a second. Let's talk about
26:21
which version of Ruby was I using, because that matters. This whole, the con, the implicit context in this talk has been that we're using C Ruby. We're using MRI. And in fact we've been using Ruby 2.0 patch 2.4.7. Unfortunately it wasn't always so great. Here's your, your, your cheat sheet as to which libraries you can use with which versions
26:42
of Ruby. If you're on 1.8, you have to use Ruby dash debug. If you're on Ruby 1.9, you have a few options. None of them are super great. You have debugger. You have Ruby dash debug 1.9. You have debugger 2. If you've tried to use these in the past, you may have found issues getting some C extensions to compile that
27:01
they depend upon. And if you're using Ruby 2.0, you still have a few options. Debugger's available. Debugger 2 is available. By-bug is the one I recommend. Both debugger and debugger 2 are, they don't have full support for everything that they, that's documented. So why is this the case? So it's because debugger stink, especially in Ruby. OK, that's my provocative
27:22
slide. I'm just trying to say something, something that we can argue about later. But let's talk about why that's the case. Previous to Ruby 2.0, all of the debuggers would hook into internals to the C API. So any time you had a new version of Ruby release, any time a new patch level came out, your debugger broke, and you
27:41
couldn't use a debugger. So if you were the type of person, as I was, who likes using the debugger to quickly rectify the cognitive dissonance between my understanding of the code and how it actually works, there would be times where you wouldn't have a debugger available for your version of Ruby for a week, two weeks, and to however long it took for the maintainer to, to bump
28:02
their dependency. And what we're looking at here is the debugger change log on GitHub. And the majority of point releases were to match up with point release, patch levels that have been released for Ruby. So again, the earlier, in Ruby 1.8 and 1.9,
28:21
our, the C API was tightly coupled to, debuggers were tightly coupled to the Ruby C API. This was a problem. That's not the case anymore in Ruby 2.0. They've, they wrote, the Ruby core maintainers wrote something called trace point API, which is what gems can hook into to get, to look into the internals of Ruby. That's fantastic, because now
28:42
there's a well-defined interface, and when the internals of the Ruby C API change, the, we, they, we have a contract that we know is not going to change. So, if you're going to use, if you need to debug in Ruby 1.8, I saw on Heroku's blog post, I think it was within the last year, that there's still people who are using Ruby 1.8. There might have been even one that was still using
29:00
1.8.6. But I'm pretty sure it's still 1.8.7, so if you're using 1.8, you're gonna use Ruby dash debug. In that case, if you want the functionality that we looked at here, you need to set a few variables in a rdebug.rc file, a dot rdebug.rc file. So you want to set your autoreload, your autovow on your autolist.
29:21
If you're using by-bug and you want to change those default values, it's actually by-bug.rc as opposed to rdebug.rc. If you're using 1.9, you have options. Good luck. I don't know which one will compile for you in your situation. But those are your options. Debugger, debugger2, ruby dash debug 1.9. And then, if you're going to use 2,
29:43
just use by-bug. Debugger and debugger2, they're not fully fleshed out. The person who's maintaining debugger is not actively working in Ruby anymore, so there is an option if you want to, there's an open source repository that needs some help. You could jump in, garbage collect there. There's a good amount of work that has been done. In fact,
30:00
that's why by-bug was created. The developer, he was frustrated with some problems he was having with debugger, and so he created by-bug to resolve that. So I said I was gonna talk about Pry. One of my mini, mini, of my mini pet peeves is this tiny one here, is that a lot of people say, oh, I just use Pry to debug things. Well, Pry isn't a debugger. If you were in the last talk, it was made clear that Pry is
30:24
an alternative to IRB. What's really cool about it is the, the plugin architecture has allowed a lot of people to build really cool tools on top of Pry. It's got syntax highlighting built in, which is really cool. So I'm gonna take a second to show us real quick what that would look like if we did our case study with Pry. So instead of just having by-bug, we would
30:43
have Pry dash by-bug. So this would say that we have Pry available within our by-bug sessions. And instead of using the debugger, method call, which remember, we can't use until the next version of by-bug is released, we would use binding dot pry. So we run our feature, and execution is
31:03
paused on that line. This is very similar to what we were seeing before. The main thing to note that's different is you got pretty colors. Fantastic. You have commands like step. It's very similar. We saw that earlier. We have next. We're gonna next over that attach method. But one of the key differences to note, if you're gonna use, if you're
31:21
a Pry lover and you want to use debugging functionality with Pry, is that you don't have the alias for B by default. You have to type out break. And you have to, you have to type out the relative path of the file to where your application is running. So we have to write out specs forward slash steps dot rb. We can't just say steps dot rb and give
31:41
it a line number. Other than that, as soon as, the other interesting thing that Pry does, as soon as I hit enter on this line, it actually takes us, it creates the break point for us, and takes us and shows us the context of that break point. So we haven't actually moved, we haven't actually executed anything other than creating a break point in our debugger session,
32:00
but now we're seeing the context of it. That's a little different than we saw before, right. So we're on, now we see where our break point is. This is, this is nice in case you're like, ooh, I put the break point in the wrong spot and you can delete the, the break point or disable it. So once we actually hit continue, then we see that we've actually hit the break point, and a nice thing that, that Pry is giving us is it's showing us how many times
32:20
we hit that break point. If you do want the aliases, there is a way to do that in Pry. You can alias commands, you can drop that in a dot Pry RC. So that's fantastic. If you want to use Pry with debugger, you can use Pry dash debugger. That'll give you the debugger that has all the problems that we
32:41
talked about along with Pry in Ruby 1.9, and you can use it with Pry dash ByBug. So real quick, a couple more things about ByBug. It was a mashup of something called dbase. Dbase is what's being used in RubyMine. And so they, he, the developer, David, took the C extension portion
33:01
of dbase and combined it with the lib and the test tiers of debugger, and put that together, put in a lot of work to fix any of the open issues that were existing on debugger. And so that's why you need to use ByBug, because it actually works and does all the things it says it's gonna use and, and do. It works on Ruby 2, doesn't work on 1.9, doesn't have
33:20
any internal source code dependencies. It's fantastic. All right. So that covers CRuby, right. Now you have a good idea of which debuggers to use with which version of Ruby you need to use it for. You know the basics of how to do it, and hopefully if the version you're using supports the advanced commands you know how to do that as well. What about Rabinius? Great news. Rabinius has a debugger
33:42
built in. If you've never used Rabinius, this is fantastic. You don't have to include a separate library. It has all the functionality that we just talked about, plus a little bit more. That's awesome. What about JRuby? If you're using JRuby and you need to do, and you want to debug things, you've got the whole Java ecosystem to lean on. There's a tool called VisualVM. I've used it in the past
34:02
when I needed to do memory debugging in Ruby. And so there's, the tooling system in Java is much more mature than it is in Ruby. For, and so that's fantastic. So you have, those are your choices there. So, to recap, we, we looked at a case study. We looked at the basic commands of next,
34:22
step, break, continue and display. We looked at some advanced commands that are not available everywhere, but they're available in by-bug. That's finish, source, and save. If you're using Ruby 1.8.7, you're gonna use Ruby dash debug. If you're using 1.9, you're gonna use debugger. And if you're using 2.0, you're gonna use by-bug. If you're gonna use pry, you can. You can use them with both of
34:41
them. And in fact, in the last talk, they talked about using pry dash plus. I would highly recommend that as well. My slides are available all up on speaker deck. I'll tweet those out later too. You can see the source code repository for what we used in this project, attributions for the nasty photo of the, the host crab with its Sacculina Carcini parasite. That's the
35:03
credit there. I've got a nice stash for a reason. You can talk to me about it. If you want to get to know me, you can go check out my blog. If you want to follow me on Twitter, if you want to work with me, I work at a company called Big Nerd Ranch. We've developed IOS Android apps. We also do the back-ends for those things in Ruby. And I thank them
35:21
for giving me the time to work on this type of stuff and, and come and work on these types of presentations and share with you guys. Thanks to the organizers, and if you want to code with me and don't want to work with me, you can come code with me on GitHub. So that's it. I'm open up to questions if anybody has any. Thank you so much.