Placeholder Image

Subtitles section Play video

  • 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.

GDB, the GNU Project Debugger, is a powerful debugging tool for C,

Subtitles and vocabulary

Click the word to look it up Click the word to find further inforamtion about it