Subtitles section Play video Print subtitles GDB, the GNU Project Debugger, is a powerful debugging tool for C, along with many other languages. It allows you to poke around inside your C programs while they're executing, and it also gives you the opportunity to see exactly what happens when your program crashes. It's pretty nifty, right? GDB is free software, and it runs on many popular UNIX and Windows-based operating systems, so it's a very widespread tool. >> You should learn to love it. GDB pros have a much easier time tracking down bugs than those who muddle through using guesses and infinite amounts of printout statements. GDB is a command-line tool, which means you can interact with it in a terminal issuing commands via the keyboard instead of clicking buttons with your mouse. >> To start up GDB, you literally just type gdb at the prompt and hit enter. You'll see some lines printed out to the screen showing you the version of GDB that you're running, its copyright information, and at the end you'll see the GDB prompt: (gdb). This lets you know that GDB is ready for commands. At this point, the most important thing to know how to do is quit. Fortunately, this is pretty simple. The quit command does just that. As a shortcut, you can just use q too. As fun as booting up GDB and then promptly quitting is, let's now talk about using GDB to help debug a program. >> To start, I've got a program here in factorial.c that gets an int and attempts to compute its factorial. In case you haven't seen factorials before or don't remember them, The factorial of the number n is equal to the product of n--(n - 1), (n - 2), and so on-- until you hit 1. Therefore, the factorial of 3 is 3 * 2 * 1, or 6, and the factorial of 4 is 4 * 3 * 2 * 1, or 24. The factorial of zero is an odd case, it's 1, and the factorials of negative integers aren't defined. Anyway, something about my factorial program is funky. When I run it, it prints out weird numbers that have nothing to do with factorials. >> So, we can use GDB to help figure out what's going on. GDB operates on executable files, which are the binary files produced by the compilation process. That is, we can't run GDB on our .c or .h source code files like factorial.c. We want to run it on just factorial instead. If the program required any command-line arguments, this is where we'd specify them. In this case, factorial doesn't require any command-line arguments, so we just type run or r for short. >> This will start the factorial program running. When the program stops running, I'll get my GDB prompt back. Okay, let's try the same thing again, factorial of 4. All right, we see that we're getting the same kind of junk here in GDB. Now that the program has finished, we can't go in and access any of its state, so we'll need to start it running again before we can see what's happening. However, we need a way to stop it while it's in the middle of its run. >> To do that, we use what's called a breakpoint. Breakpoints tell GDB to pause the program at a particular function or source code line so that we can examine the state of the program, the values of variables, the state of memory and such, at that point. Since I don't really know where things are going wrong, >> I just want to start debugging right at the very beginning, right when main begins. We'll set a breakpoint at the beginning of main using the break command. We can also use b to abbreviate break. Now let's start the program running again. Here we are at the beginning of main, just like GDB tells us. The line of code that's about to execute but hasn't yet is the printf line. We can tell GDB to execute this line of code and go to the next line with the next or n command. >> All right, now GDB tells us that we're on the GetInt line. I know that it seems like the printf line didn't run since we don't see "Enter a positive integer" print out on the screen, but it did actually run. What we're seeing is the operating system suppress writing anything to the screen until it absolutely has to, which why debugging with printouts can sometimes seem unreliable. Anyway, let's again go to the next line of code and enter in an int. Again, let's type 4. So this looks weird. We're on line 12 according to GDB, but the next line that's about to execute is just a curly brace. >> That just means we're at the end of a loop, our do while loop in fact, and GDB is telling us that the termination condition, namely none less than zero, will execute next. If this ever gets a little confusing, we can pull up the source code in GDB with the list or l command. This prints out the source code that's centered around the line that we're currently on. If we type list or l again, we'll see the next set of lines print out. We can do this until we hit the end of the file. >> To get back to where we were, we can supply list with a line number, in this case, line 12. Anyway, let's move on. Now we're on the 4 loop. Let's make sure that our num variable contains 4. We do this with the print, or p, command. So, GDB tells us that num is indeed storing 4, as we expected. The $1 that GDB prints out is a special GDB variable that is now set to store the number 4 as well. You can ignore this for now, but these GDB variables come in super handy in more advanced cases when you want to recall what you've done in the past. Anyway, moving on with next, we see that we start moving through the for loop. Let's keep going through here with n bit by bit. Rather than typing n each time, you can also just hit enter. When you hit enter without typing anything, GDB just repeats the previous command. So now we've hit the printf call. It looks like we've indeed gone through our for loop 4 times, which is what we want to do in order to multiply by 1, 2, 3, and 4. >> Everything seems like it's working, except when we hit next again we get this huge number instead of 24. If we print out the value of factorial using p, we see that factorial does have this massive number in it. Something's definitely going wrong. At this point, though, we are almost at the end of the program, and it's too late to fix anything. >> However, we can restart the program by typing r again and then y to confirm. Now we're back at our breakpoint at the beginning of main. We know that everything seems to be fine with reading in the n. so we can jump ahead with n. Alternatively, we can set a new breakpoint after the do while loop and jump there. Let's do that. Looks like line 14 comes just after the loop. Let's set a breakpoint there. It's good practice to specify the file name in this breakpoint command since GDB can get confused if you're working with multiple files. To move ahead of this breakpoint, we'll use the continue or c command. >> Okay, here we are at the for loop. Let's go 1 more line into the for loop, and then we'll start printing variables to see what's going on. Let's make sure that i is indeed 1, as expected. Yup, that's all good. What about factorial though? Whoa, that's no good. We've got a big negative number here. How'd that happen? Well, if we look back at the code, we see that we never initialized it, so we've just got trash in there. That will definitely throw off our calculation. >> Fortunately, we don't have to leave GDB to fix this. We can initialize it right here and fix it in the code later using the print command. We'll initialize it to 1 since the factorials of zero and 1 are both 1, and if we initialize it to zero, then we'd always end up with zero as our result. You can set any variable this way, which is super handy. Now, let's continue our program. Let's make sure everything's where it's supposed to be. Num should be 4, i should be 1, and factorial should be 1 too. We can shortcut this process and print all of our local variables with the super-helpful command info locals, which prints out all of our in-scope local variables. Anyway, it looks like everything's good to go. >> Let's do another go-around of the loop just to make sure. Okay, everything looks great. Now we can use the continue command to go to the end. Sweet! 4 factorial printed out 24 as expected. Now we can go fix this in our code. Rather than quit out of GDB, we'll use another terminal tab to do this. Going back to our GDB tab, we now need to recompile our executable. One of the best things about GDB is that you don't need to leave GDB to run make. So that we don't keep hitting the old breakpoints, let's disable them with the, you guessed it, disable command. This will disable all of our breakpoints. Now, we can restart the program with r and make sure everything's okay. Looks like everything's good to go. Factorial of 4 prints out 24, just like we thought. GDB is one of the most helpful tools you've got in your toolbox. >> There are a ton more things you can do with GDB, far more than you can do with simple printouts. Next time your program isn't doing what you'd like, try running GDB to figure out what's going on inside. With a little bit of practice, you'll be able to drill down right on your bug in no time. My name is Nate Hardison. This is CS50.
B1 factorial program breakpoint line loop print GDB 12 2 Amy.Lin posted on 2017/01/25 More Share Save Report Video vocabulary