9.2 - Debugging - GDB Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] welcome back to this computer science one video series in this part we'll cover a powerful debugger tool called gdb a debugger is a program that simulates or runs another program that allows you to pause and resume the execution of a program set breakpoints or conditions where the execution pauses automatically so that you can look at the program's state view variable values and watch variables for changes to their values and step through a program line by line or even instruction by instruction if you wanted to look at its assembly code the debugger that we'll use and demonstrate is the GNU debugger or simply just gdb to use gdb you'll need to compile your program using the G flag this preserves the program's identifiers x' and symbols so that as you're debugging you can see the original function and variable names normally these are lost when the program is compiled the G flag preserves them then you start gdb with your executable as follows optionally if your program requires command-line arguments you can provide them at startup using the following these can also be set inside gdb after you've started here's a list of commands that will demonstrate additional resources and gdb cheat sheets have been provided on the course website let's take a look at a full demonstration here I've got a buggy program the purpose of this program is to compute the sum of the first n prime numbers optionally it allows the user to provide n as a command-line argument but defaults to 10 we have several functions here including a sum function a function that returns an array containing the first n Prime's and a function to determine whether or not a given integer is prime or not there are lots of intentional bugs with this program so let's get started debugging it I'll go ahead and compile it with all these flags I'm using the W flag instead of the W all flag to suppress all the warnings some of the warnings are going to give our bugs away immediately which is good in practice but I want to demonstrate the debugging tool here when I run it it simply gets stuck in an infinite loop so let's go ahead and start up the debugger it will be useful to see our code so I'll type layout next to display it there's nothing here yet because the program is not running so let's start it by typing run it is running it's stuck in the infinite loop if we interrupt it using control-c then we can see that we're getting caught in this while loop on line 57 to demonstrate that we're getting caught here I'll go ahead and use next to step through it line by line a shorthand version of this is to simply type net n now to understand what's going on here and why it's getting caught in this infinite loop let's go ahead and print out some of these numbers I is zero which it received on line 55 and is 10 X is 2 so we never get past the first iteration clearly the problem is probably in the is prime function typing next is simply going to go in this loop what I want to do is I want to go into the is prime function so instead of next I'll type step that takes me to the is prime function and I can continue with next or step the difference is that next goes to the next line in the immediate scope step will actually step inside of a function so you can take a look at what's going on inside of it X is 2 here which is a prime number but we're returning false we found our first bug here the intention here was to get rid of all even numbers but 2 is an even number but that's also prime so let's rethink our design here to quit out of the debugger simply type quit now let's look at this a little bit closer this function is supposed to return true if X is prime and false otherwise the for loop is testing whether or not X is divisible by any odd integers starting at 3 going to 5 7 etc up until the square root of x so that seems right but again one of the corner cases is when we've got 2 we need to return true because 2 is a prime number let's take care of it with an additional conditional statement some other corner cases that we might not have thought of would be 1 0 or any negative numbers which can sometimes be thought of as prime for the purposes of this program let's go ahead and take care of that now otherwise the loop looks fine let me go ahead and recompile and we're still stuck in an infinite loop we're still stuck here but let's take a look at the variables this time I is now 1 and X is 4 so we did get past that first iteration I've stepped into the is prime function again we would expect it to return false and it does the problem is no longer in our is prime function instead it's a problem within our loop here the intention of this function is that we find the first n Prime's 2 3 5 7 etc we return the result when I has reached n minus 1 and we found n of them we found the first one indeed I can print out the result here but we haven't found the next one to see what's going on here I'm going to set a breakpoint I'm going to set a breakpoint the first time I come into this function and I'll restart the program now sometimes the T UI mode or text user interface mood screws up the display like this if that happens don't worry simply type refresh and it all be good again and is the default of 10 we've initialize I and X now while I is less than n while we've not found the first n Prime's we execute this loop X is 2 so this will return true and the if statement executes we assigned to two the first value in result and increment RI because we found one element but here's the problem we go up by two meaning that will only ever hit all the even numbers we need to rethink this function the problem here is that two is again a special case we should always include it and then handle the rest of the primes separately we'll always set the first prime to two and then we'll start at the second prime starting our x value at three now if something is prime we'll store it increment our index value and then increment X up to 5 7 9 etc not all of them are going to be primes but now at least we're not going to be stuck on four once again we're stuck in an infinite loop let's fire up gdb again let me go ahead and reset that breakpoint I'd get Prime's everything is as expected so let's go ahead and go into the loop 3 is prime so this should return true and it does so we set it increment I and go on to 5 before I continue I'm going to go ahead and set a watch on this variable now I don't have to continually print it out any time it changes it'll tell me that it changed and what it changed to 5 is prime so we set it increment I and increment X X has changed from 5 to 7 7 is still prime so it changes to 9 now is prime of 9 is going to return false because 9 is not prime and there's where we get stuck the problem is that we're incrementing by twos only on Prime's which worked for the first few of them but it doesn't work after we hit 9 so we've found yet another bug incrementing X should be done unconditionally in the loop let's run it again awesome we've resolved all of the infinite loop problems however 29 is the wrong answer this is a bit of ad hoc testing but I'm going to use Wolfram Alpha to see how wrong it is it should be 129 so let's fire up gdb again this time I'm going to start from the very beginning I'll set a breakpoint at the main function this way I can walk through the program from the very beginning now I didn't provide any command-line arguments here so Arg C is only 1 the executable file name let's go ahead and skip over this since I believe that we've taken care of all the issues within get Prime's before we go on I want to see what that returned to print the contents of an array you need a little bit of different syntax at 10 provides information about the size of the array so that it can treat each individual element separately here it's just a bunch of memory addresses because it's an array we also need to dereference it we dereference it and tell it to treat it as if there are 10 elements in the array 2 3 5 7 11 13 17 19 23 and 29 there are 10 primes and those indeed are the first 10 Prime's so we've already made some headway here I no longer have a suspicion that the problem is in get Prime's or is prime in other words everything looks ok up to this point so let's continue now there's the sum of 29 so I've narrowed it down to a single line here or a single function in particular the sum function there is something going wrong inside that function I'm going to clear my breakpoints and I'm going to set a new breakpoint in the sum function and I'm going to rerun the program Here I am in the sum function let's make sure that we receive the array correctly we receive the array correctly now I and total are correct but remember uninitialized variables could have garbage values now let's run through this loop it seems to be running normally but let's go ahead and put a watch on our total immediately we see that it's 3 which is wrong it changed a 5 7 11 13 17 it seems to just be getting the value stored in the array now if you look really closely you'll see the problem here equals plus instead of plus equals this is syntactically correct what it's doing is it's saying let the total be equal to whatever is stored in the array positive in other words putting a plus there is syntactically correct because it's similar to putting a minus there to make a value negative but in this case we're just making it positive so again let's fix both of those issues will initialize total to 0 and we'll fix the operator and now we're getting it right for that one particular test case the first ten Prime's summed 129 let's try it out for others the first 10 Prime's are 129 still the first 20 Prime's are 129 the sum of the first 30 Prime's are also 129 so something's going on here that you might have noticed before let's go ahead and fire up gdb now I already showed you how to set the command-line arguments before but once you're in gdb you can use set args and let's go with 20 the first one that we know is wrong let's set a breakpoint at the main and let's run it so now lines 33 through 35 should be taking into account that we've provided a command line argument Arg C is now 2 we've provided two arguments the executable file name and 20 the number of primes that we want to sum up but that doesn't get executed again it's syntactically correct to use the single equal sign here but that's not what we mean if Arg C is equal equal to 2 then we start looking at the arguments we actually would have caught this if we had bothered to use the W all flag now you might see some other problems here too typically finding a bug in a program will immediately lead you to other bugs but let's run it through gdb again you you can see the arguments that we've also provided now args he is correctly being checked for - but line 34 had no effect that's because of two issues here we're calling the 8y function but we're not doing anything with it in fact the compiler warned of us about that we're not as signing n to the value returned by a - I here's our first segmentation fault I'm not setting a breakpoint here I want it to seg fault here's another Musil command back-trace tells me exactly what functions have been called and prints out everything in one command the segmentation fault occurred on line 34 and is 10 primes is an uninitialized memory address and s which is declared down below has not yet been assigned a value so it gets zero let's print out our arguments remember there are only two arguments we can print individual ones ah argh visa to does not exist its memory address is actually zero which is the null memory address hopefully you saw that but I wanted to walk you through the process here it should be argh piece of one don't forget to regression test that is test the test cases that you knew passed before after making changes make sure that those changes don't break those passing test cases now I don't know if that's right but I'm going to go ahead and consult Wolfram Alpha again that seems correct 1593 is also correct I wonder what the sum of the first 10 million Prime's is it's a segmentation fault let me go ahead and change it so that we're not suppressing all the warnings there's still a problem in the get Prime's function it's complaining about us returning result which is a statically declared array recall that in general it's dangerous to return as statically declared array since it may be destroyed when the frame when the stack frame is popped off but also 10 million values is too large for the stack resulting in the stack overflow and the segmentation fault that we encountered if you want to make this program more robust you need to make this a dynamic array and now the warning goes away and it'll actually fit let's consult WolframAlpha that's a different number but that's also expected look at the magnitude of that number it's over 62 billion which is way larger than the two point 1 4 7 billion that we can hold in an int variable so now it's no longer bug but instead a design issue if we want to hold numbers this big we need to rethink about the types that we're using or we need to bring in an external library that supports arbitrarily large numbers the final example I want to give you is what happens when we've got the first 10 million primes now it's going to overflow just as before but also it's going to take an extremely long time to do this that's because the that's because the algorithm that we're using to determine whether or not something is prime is called the sieve of eratosthenes which is an exponential algorithm meaning that it takes an extremely long time even on small inputs in fact I would not expect this program to execute within your lifetime even if it gave you the correct results we'll revisit the issue of complexity and efficiency later on
Info
Channel: Chris Bourke
Views: 157,571
Rating: 4.9659891 out of 5
Keywords: Computer Science I, C Programming, Debugging, gdb
Id: bWH-nL7v5F4
Channel Id: undefined
Length: 23min 41sec (1421 seconds)
Published: Thu Apr 19 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.