Lab 8 - Debugging Pointers with gdb

The purpose of this lab is to learn how to use the gdb utility to debug pointer errors.

The gdb utility is the debugger that comes with the GNU compilers such as gcc and g++. It allows you to either step line-by-line through your code or to examine a special file called a coredump. In lab today, we will examine a coredump file caused by a bad pointer reference.

A coredump file is a special file generated when your code crashes with an error such as a Segmentation fault. It contains enough information about the state of the program that you can usually use a coredump file to determine why the program crashed while running. This is particularly useful when your program only crashes under certain circumstances.

By default on Sleipnir, a coredump file will NOT be generated when your code crashes. You must turn on the generation of a coredump file with the following command AFTER you have logged in to Sleipnir:

ulimit -c unlimited
For this lab, we will be using a simple example code, lab8_handout.cpp, which contains a pointer version of a getInput function for a single integer. The code will generate a coredump because we have forgotten to set the pointer to the variable num before we call getInput. So the pointer is still pointing to NULL when we try to set the value, which triggers a Segmentation fault.

To use gdb, you have to compile with debugging info turned on. To do this, you give the -g option to g++ to turn on debugging info. So compile and run the handout with the following commands:

g++ -g -o lab8 lab8_handout.cpp
./lab8
When you run lab8, you should see Segmentation fault (core dumped) after entering the integer. If you do not see (core dumped) rerun the ulimit command from above and then rerun lab8. Once you have the coredump file, start gdb with the following command:
gdb lab8 core
The first argument to gdb is the executable's name (lab8) and the second argument is the coredump's filename (core is the default filename). You will now be at the prompt for gdb. It should look something like the following:
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu"...

warning: Can't read pathname for load map: Input/output error.
Reading symbols from /usr/lib/libstdc++.so.6...done.
Loaded symbols for /usr/lib/libstdc++.so.6
Reading symbols from /lib/libm.so.6...done.
Loaded symbols for /lib/libm.so.6
Reading symbols from /lib/libgcc_s.so.1...done.
Loaded symbols for /lib/libgcc_s.so.1
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux-x86-64.so.2...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Core was generated by `./lab8'.
Program terminated with signal 11, Segmentation fault.
[New process 1261]
#0  0x00007f82ec506fd9 in std::istream::operator>> ()
   from /usr/lib/libstdc++.so.6
(gdb) 
The line (gdb) is the gdb prompt. When at this prompt, you can issue various gdb commands to see the state of your program. Let's first try the backtrace command, which will show all the function calls that led up to the segmentation fault. To give the backtrace command, type the following command at the gdb prompt:
bt
This should show you something like the following:
(gdb) bt
#0  0x00007f82ec506fd9 in std::istream::operator>> ()
   from /usr/lib/libstdc++.so.6
#1  0x0000000000400997 in getInput (ptr=0x0) at lab8_handout.cpp:31
#2  0x00000000004009b2 in main () at lab8_handout.cpp:19
The first field (#0, #1 and #2) tells you what line and function generated the error. The numbers are referred to as frames in gdb. So we see the error occurred in the input operator (>>) in frame #0. This was called by line 31 in the getInput function in frame #1. That line in frame #1 was called by line 19 in main in frame #2.

We can select one of these frames to look at further. Usually it's not helpful to look at frames for the C++ libraries, so we can skip frame #0. To look at frame #1, give the following command at the gdb prompt:

frame 1
This will switch us to the scope of getInput. We can then example getInput's arguments and local variables (if it has any). getInput has one argument called ptr. To print the current value of ptr, type the following command:
print ptr
This will print out that ptr is currently 0x0, which is the NULL pointer. We then know that ptr was not set appropriately. This could be because we passed the wrong pointer from main (particularly in a program with many pointers) or because we forgot to set the pointer in main.

Our next step is to see the state of the pointers in main. main is in frame 2, so to switch to that scope, type the command:

frame 2
To look at all the variables local to main at once, we can type the following command (this command does not work for arguments, just for variables declared inside the current scope):
info locals
In the output of info locals, you should see that the address for p is 0x0, which tells us that p is still a NULL pointer. This is why the code crashed. You cannot set values for the NULL pointer. Looking back at the code, we see that p was initialized to NULL at the top of main (you should get in the habit of doing this with all pointers you declare) and, as the comments indicate, p was never set to the address of num.

Before continuing on to the rest of the assignment, exit gdb by giving the following command:

quit

Lab Assignment

Copy lab8_handout.cpp to lab8.cpp. Modify the code to do the following: Make sure your new double version of getInput properly sets the pointer before the function call. Compile and run your new program, making sure you have no Segmentation faults with the new function.

Email me your completed lab8.cpp file.