Debugging with gdb

Debugging a program is a real tedious job which can consume time more than you took to write the program. And if you are not on the right track of debugging then you are probably gonna end up writing the whole program again. If you have red my previous article on debugging (The Art of Debugging) then you already have an idea of the debugging process and what are some basic methods to do it. We also talked about how are those basic techniques not gonna help us with larger and trickier programs. To make our job easy with those programs, there is a debugging tool : GNU Debugger or simply gdb. GDB is a powerful debugging tool with all the features you require while debugging your program. There are some basic set of features which are available in every debugging tool so if you want to use some other debugger then you'll find it easy to use after you get to know about gdb. There is some great stuff we'll be studying today so sit tight. Let's have a look at the index we'll be covering.

Introduction to GDB

We have already seen its full form so now lets see what actually gdb is. GDB is like any other debugging tool which helps you to find out what is going inside your program. It helps you to check for variable states, memory states, stack trace and much more. GDB was actually made for GNU operating system but then it was made portable and now it can run on any UNIX-based operating system(Linux, Ubuntu, Redhat, Lubuntu) and windows operating system. It supports C and C++ programs. Although it also supports D, Fortran and 1-2 more languages but the support is partial. So its best if you use it to debug C and C++ programs. GDB requires some special symbols in your program to let it know about the program but don't worry, you won't have to add those symbols yourself. The GNU GCC compiler has a flag -g3 or -g which inserts the symbols automatically. We'll see all of this in the later section. GDB can do mainly four things to help you catch the bug.

  • Start your program, specifying anything that might affect its behavior.
  • Make your program stop on specified conditions.
  • Examine what has happened, when your program has stopped.
  • Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.
We'll see all these things in action while we will debug our own program. Till then lets just get ourselves acquainted with gdb.

Debugging Terminology

It will be very helpful if we first learn about some concepts before working with a debugger. We can't get to know about all the features a debugger provide because there are too many but we can enrich ourselves with some common and basic features.

  • Breakpoints: A break point is the point at which the debugger will stop your program. For eg., when you run your program in gdb, you can set a breakpoint at a function so that gdb stops at that function and you can execute your program line by line from there.
  • Watchpoints: A watch point is a break point which is not specific to a line in your program, instead it stops the program when some change happens to the supplied expression or variable. For instance, you can set a watch point to stop the program execution whenever your variable crosses a specific value.
  • Stepping: Stepping means to execute your program step by step. GDB has 2 types of step. We'll see both of them in the next section
There are more but those are self explaining so we don't need to elaborate them.

My Favourite gdb commands

GDB runs on commands. You tell it what to do, when to do by commands. There are a lot of commands of gdb but we won't use much of them frequently. But there are some commands which are used again and again and those commands provide those basic features which almost every debugging tool provides. So if we get handy with these frequently used commands then we'll able to work our way with most of the other debuggers. At this point, it is not necessary that you understand all these but soon we'll be using them.

  • help: Get a description of gdb’s commands.
  • run: Runs your program. You can give it arguments that get passed in to your program just as if you had typed them to the shell. Also used to restart your program from the beginning if it is already running.
  • quit: Leave gdb, killing your program if necessary.
  • break: Set a breakpoint. Some examples: - break somefunction stops before executing the first line somefunction. - break 117 stops before executing line number 117.
  • watch: Set a watchpoint. Some example: - watch myvar stops when the myvar value changes. watch myvar>100 stops when myvar value crosses 100 if it is initially less than 100.
  • list: Show part of your source file with line numbers (handy for figuring out where to put breakpoints). Examples: - list somefunc lists all lines of somefunc. - list 117-123 lists lines 117 through 123.
  • next: Execute the next line of the program, including completing any procedure calls in that line.
  • step: Execute the next step of the program, which is either the next line if it contains no procedure calls, or the entry into the called procedure.
  • finish: Continue until you get out of the current procedure (or hit a breakpoint). Useful for getting out of something you stepped into that you didn’t want to step into.
  • cont: (Or continue). Continue until (a) the end of the program, (b) a fatal error like a Segmentation Fault or Bus Error, or (c) a breakpoint. If you give it a numeric argument (e.g., cont 1000) it will skip over that many breakpoints before stopping.
  • print: Print the value of some expression, e.g. print i.
  • display: Like print, but runs automatically every time the program stops. Useful for watching values that change often.

Debugging a program

We have already learnt a lot about gdb. Now lets see it in action. In this section we'll debug a simple program which has a function to swap 2 numbers in the array. There are some bugs in the program so lets find out those using gdb. (Note that we could use basic techniques of debugging also in this case.)

#include<stdio.h>
void swap(int a, int b){
    int temp = a ;
    a = b ;
    b = temp ;
}
int main(){
    int i, arr[4]={1, 2, 3, 4} ;
    printf("Before Rotating left: ") ;
    for(i=0 ; i<4 ; i++){
        printf("%d ", arr[i]) ;
    }
    printf("\n") ;
    for(i=0 ; i<4 ; i++){
        swap(arr[i], arr[i+1]) ;
    }
    printf("After Rotating left: ") ;
    for(i=0 ; i<4 ; i++){
        printf("%d ", arr[i]) ;
    }
    printf("\n") ;
    return 0 ;
}
Save the above program in a file gdb.c. Now lets compile the above program but this time with a special flag to insert debugging symbols for gdb. Execute this command in the directory where gdb.js is saved gcc -g gdb.c It will generate an executable with name a.out in the same directory. Try to run the executable by ./a.out. What did just happen ? Our program is doing nothing. The list is not rotating. But why ? Lets ask gdb to help us.

Run the gdb by gdb a.out. It will start the gdb prompt where we'll start debugging our program. Now type run and execute. It will run the whole program without breaking at some point. It is just like normally executing our program. Lets add some breakpoints by typing break main break swap. Now run the program. Firstly it will stop at mainThere is nothing much to see at main starting point. So continue executing by typing cont. Now it will stop at swap function which is called at first iteration of the loop. You can see the passed in parameter values. a=1, b=2. Now lets see if the numbers are being swapped or not. Execute next 3 times and check the values of a and b by typing print a print b. Well the values have been swapped. Good. So our function is doing well. Now lets see whether the array values are swapping or not.

Execute next and you will be back to your loop. Lets see our array values by print arr[0] print arr[1].Oops. The values haven't been changed. But whats wrong ? Our function is doing well so what's the problem. Okay i got it. And i hope you too have got the problem. We are not passing our array values by reference. We are just passing them by value. So lets change that.

void swap(int *a, int *b){
    int temp = *a ;
    *a = *b ;
    *b = temp ;
}
//
//previous code
//
        swap(&arr[i], &arr[i+1]) ;
//
//rest code
Again execute the code and see the results. Our expected list is 2 3 4 1 but we are getting 2 3 4 0. Where that 1 is going. How it got converted to zero. Let me not reveal the suspense. I hand it over to you guys to find out what the second bug is. Hint : Iterate the whole loop and see whats going on inside the swap loop. Just use the basic commands and find out the bug. Many of you have already gotten it by just looking but still i will recommend you to find it using gdb so that you can get familiar with it.

Did you find this article a bit too much about the gdb ? Well its just the top of it. There are so much of it which we have not even touched. So if you want to go further with gdb then you can visit the GDB documentaion page. Also if you want a simple ebook with above gdb commands and their example then you can subscribe to our newsletter so that i can mail you that ebook. One more thing, sharing is caring. So share this if you think its worth reading or share your thoughts about it by dropping a comment below.

Comments

Popular posts from this blog

Search box for blog or website

Image Search Engine Using Python

8 Amazing Animal's Sleep Facts