Step 3: A more complicated example

Now, let's try something more complicated, involving programs with different source files and code that uses objects and pointers. We'll see that gdb actually understands C++.

  1. Remove the a.out file from the previous exercise.

  2. Compile a buggy implementation of linked lists. (Do not use the files from Lab 6): g++ -g -gstabs+ -ansi -Wall List7test.cpp List7.cpp Run the program, you should see something like: Testing Insert... Should Print: -9 -7 -5 -4 -4 -3 -1 -1 -1 0 1 1 1 2 2 3 5 5 6 7 8 9 Actual Print: -9 -7 -5 -4 -4 -3 -1 -1 -1 0 1 1 1 2 2 3 5 5 6 7 8 9 Testing Remove... Remove 5... Should Print: -9 -7 -5 -4 -4 -3 -1 -1 -1 0 1 1 1 2 2 3 5 6 7 8 9 Actual Print: -9 -7 -5 -4 -4 -3 -1 -1 -1 0 1 1 1 2 2 3 5 6 7 8 9 Remove -1... Should Print: -9 -7 -5 -4 -4 -3 -1 -1 0 1 1 1 2 2 3 5 6 7 8 9 Actual Print: -9 -7 -5 -4 -4 -3 -1 -1 0 1 1 1 2 2 3 5 6 7 8 9 Remove 9... Segmentation fault (core dumped) Apparently, the first two removes worked correctly, but the third one did not.

  3. Run the program inside gdb, by typing gdb a.out and then "run" at the gdb prompt. Make sure you are using the a.out from this program and not the previous one. The last few lines of gdb's output should say something like: Program received signal SIGSEGV, Segmentation fault. 0x0000000000400c4d in List::remove (this=0x7fffffffdf50, data=9) at List7.cpp:96 96 current->next = ptr->next ; The remove() function is apparently the culprit. Not surprising.

  4. We can list the source code from different files this way: list List7.cpp:1,20 list List7test.cpp:1,20 list main list remove list List::remove When the source code is spread out over multiple files, listing by function name is much more convenient.

  5. To set a break point at a line number in a particular file: break List7.cpp:96 We can also break after entering a function: break List::remove

  6. Run the program using "run". It should break on line 88 after entering the remove function. Type "where". You should see something like: #0 List::remove (this=0x7fffffffdf50, data=5) at List7.cpp:88 #1 0x0000000000400fe7 in main () at List7test.cpp:40 This says that the execution of the program is stopped in function List::remove() at line number 88 of List7.cpp. Also, often quite important, it says remove() was called from main() at line 40 of List7test.cpp. This is called the "backtrace of the stack frames".

  7. Where we stopped the program, the variable current is not yet initialized. Type: step to initialize it.

  8. We can now print out various local variables in remove. Unfortunately, everything other than data is a pointer, so printing them just gives obscure hexadecimal values. Fortunately, gdb understands C/C++ operators *, & and ->. Try these print statements: print data print m_head print current print *m_head print *current print current->next print *(current->next) We can also print out values of local variables in main(). Try: print main::List1 print &main::List1 Sometimes we forget the type of the variables we are using: ptype current ptype *current ptype current->m_data ptype main::List1 Note the * at the end of the reported type of current. This says current is a Node pointer rather than a Node.

    We can even use the "this" pointer:

    print this print &main::List1 The output confirms that the host/calling object is indeed List1 from main().

  9. Type "where" and "info breakpoints" to review where we are. There should be a breakpoint on line 96. This is the part of the while loop that is executed when the node to be removed is found. Type: list List7.cpp:96 continue

  10. Now execution has terminated in the portion of the code that is buggy. See if you can figure out what happened... These print commands should help: print *current print *current->next print ptr print *ptr print *ptr->next

  11. Type "continue" (or use the return key) 4 times. The program should trigger a segmentation fault. Type in these commands: list print data print ptr print current print *current You should see why this program crashes.

  12. Use "quit" to get out of gdb.

One command we didn't go over here is the "next" command. This command is very much like the "step" command. The difference is when the next line of code is a function call. In this case, "step" will stop at the first line of the function being called, whereas "next" will wait for the function call to finish.