Skip to main content

The Art of Debugging

Writing unorganised code for something to work is an easy task, just put anything anywhere and see if the result is coming or not. If the result comes out in first attempt then you are really lucky but normal people like me are not. Then what we should do to check where the problem is occuring, we put some print statements (or console.log) to check the result in various places and observe where the output is deviating from its path. Well, it itself is a good practise of debugging the code but it does not work always especially not with large projects. So what should we do then ? According to me, we should give debugger a chance to find the bugs in our program. Debugging is an important part of writing programs because it teaches you to write unit tests for the parts of the program where it can fail.

To perform debugging, we need a debugger and thankfully we have one, gdb (GNU debugger). In this article we will start by seeing some basic debugging techniques which involves logging the output and assertions. Then in the next article, we'll use gdb which will do the heavy lifting for us and will tell us what is going in our program internally. So the topics we'll be covering in this article will be :-

Method of debugging and what to avoid

The basic approach to debugging our program follows the sequence of following steps :

  1. Know what your program is supposed to do.
  2. Detect when it doesn’t.
  3. Fix it.
This seems pretty simple, right ? In reality it is not that much simple. Detection the bugging point is a tedious job itself. Also there are some tempting mistakes which we do when we see wrong output. First mistake is, we don't follow the first step and just randomly tweak the code until it works. Don't do this because it will eventually mess up your code and even if it works(which i doubt), you will end up with a hard-to-maintain code. The second mistake is to attempt to intuit where things are going wrong by staring at the code or the program’s output. Like if see a segmentation fault and we know that we have used pointers in our program then we keep staring at the pointers code and try to see where the reference is going wrong. Stop doing this and let the computer find itself the mistake using debugging tools.

Basic debugging

Usually there are programs which consist only 50-100 lines of code and 2-3 functions then there basic debugging techniques can be applied to find the bug easily. If basic debugging doesn't help you then move straight to use gdb instead of applying these techniques too much. The 2 basic methods of basic debugging are:

  • Logging output to console
  • Assertions
Lets see them one by one

Output Logging

Output logging basically means printing the output our program generates bewteen various parts. In C, we use printf function to print out the variables and we can see their state in between our program lifecycle. But priting the variables using printf has some disadvantages. Some of these disadvantages are:

  • We can't change the output without editing the code.
  • A lot of print statements can create mess and we'll get confused which part is generating which output.
  • The output can be misleading: in particular, printf output is usually buffered, which means that if your program dies suddenly there may be output still in the buffer that is never flushed to stdout. This can be very confusing, and can lead you to believe that your program fails earlier than it actually does
There are some rules of thumb to follow when using output logging technique.
  • Use fprintf: Instead of printf statement, use fprintf(stderr, ...). It will keep your regular output seperate from debugging output.
  • Flush the output: If you still want to use printf then call fflush(stdout) to prevent buffering problem.
  • Use #ifdef: Wrap your code inside #ifdef It will allow you to turn your debugging code on/off according to your needs.
Lets see an example of it in a quicksort program.
#include<stdio.h>
#define DEBUG
void qsort(int *arr, int l, int r)
{
 int i ;
    if(r>l)
    {
        int index = partition(arr, l, r ) ;
        #ifdef DEBUG
        for(i=0 ; i<5 ; i++){
            printf("%d ", arr[i]) ;
        }
        printf("\n") ;
        fflush(stdout) ;
        #endif
        qsort(arr, l, index) ;
        qsort(arr, index, r) ;
    }
}
int partition(int *arr, int l, int r)
    {
    int i=l, j=l ;
    int pivot = arr[r-1] ;
    while(j<r)
        {
        if(arr[j]<pivot)
            {
            int temp = arr[i] ;
            arr[i++] = arr[j] ;
            arr[j] = temp ;
        }
        j++ ;
    }
    int temp = arr[r-1] ;
    arr[r-1] = arr[i] ;
    arr[i] = temp ;
    return i ;
}
int main(){
    int i, arr[5] ;
    for(i=0 ; i<5 ; i++){
        scanf("%d", &arr[i]) ;
    }
    qsort(arr, 0, 5) ;
    for(i=0 ; i<5 ; i++){
        printf("%d ", arr[i]) ;
    }
    return 0 ;
}
This is a simple quicksort function but there is some error in it. To debug it, i have added a loop which prints my array after each partition in the qsort function. Also i have wrapped that piece of code inside a #ifdef block. I have switched on the debugging by defining DEBUG using #define DEBUG. You can comment that line if you want to turn off debugging(which means the code inside #ifdef block won't execute). Now if you try to run it then you will infinite array prints. Also those arrays are all sorted. So it suggests that our code is working fine but it is not stopping after sorting the array. What might be the reason for endless recursion. One can think may be our recursion condition is not right. And that is the bug in our program. If you change the recursion condition if(r>l> to if(r>l+1), you program will start behaving correctly. Now you can turn the debugging off by commenting #define debug.

Assertions

We just saw the ouput logging technique and how to use it smartly and carefully. Now lets see another technique of basic debugging. In this technique we'll use C assert library. Every non-trivial C program should include <assert.h>, which gives you the assert macro. The assert macro tests if a condition is true and halts your program with an error message if it isn’t. A basic example could be:

#include<stdio.h>
#include<assert.h>
int main(){
 assert(2+2 == 5) ;
 return 0 ;
}
When you will run the above program, the assertion will fail and you will see an error message. Assertions are useful because you won't have to change your code to get more debugging output. Assertions can be used to verify assumptions made by the program and print a diagnostic message if this assumption is false.

So far we have seen how can we debug our sweet simple programs but when our programs grow in size, they become more nasty. And to handle those, we'll be needing a more powerful tool to help us. In the next article, we'll see that mighty tool (gdb) and will see how to use it, when to use it and other tools to use along with gdb. If you have any query then drop a comment below and i'll be willing to help you out.

Comments

Popular posts from this blog

Image Search Engine Using Python

Images provide a lot more information than audio or text. Image processing is the prime field of research for robotics as well as search engines. In this article we will explore the concept of finding similarity between digital images using python. Then we will use our program to find top 10 search results inside a dataset of images for a given picture. It won't be as good as google's search engine because of the technique we will be using to find similarity between images. But what we are going to make will be pretty cool. So lets start. Setting up the Environment Our Algorithm How the code looks Lets build the GUI Additional Techniques Setting up the Environment The code we are going to write requires a few tools which we need to install first. I will try to be as precise as i can and if you get stuck into installing some tool then you can drop a comment below and i will help you sort out the problem. So here are the tools and the steps to install ...

Understanding Python Decorators

If you have ever wondered what those @something mean above a python function or method then you are going to have your answers now. This @something line of code is actually called a decorator. I have red from various articles about them but some of them were not able to clarify the concept of a decorator and what we can achieve with them. So in this post we'll learn a lot about python decorators. Here is a list of topics we'll be covering. What is python decorator Understanding the concept Multiple decorators on same function class method decorator Where can we use decorators What is python decorator A python decorator is nothing but a function which accepts your given function as a parameter and returns a replacement function. So its like something this def decorator(your_func): def replacement(your_func_args): #do some other work return replacement @decorator your_func(your_func_args): #your_func code Now when your_func gets called then...

Cordova viewport problem solved

Include the viewport settings in Cordova If you are facing the auto zooming problem of cordova then go read on the full article. Cordova actually ignores the viewport meta tag which causes the pixel density problem. So we need to tell cordova that viewport tag is equally important as other tags. To do this, we need to add some code to a file which is specify in the article. Corodva messes with pixels If you are using the latest cordova version or creating the cordova app for latest android versions then you may have faced the zoom malfunctioning.I also faced it when creating an app. Many of you may have already searched the web and found the answer of changing the meta tag attributes to get it working. But adding target-densitydpi=medium-dpi does not solve the problem for latest android versions. It may work for gingerbread but not for kitkat and others. So the final solution which i found was one of the stackexchange answer but rarely found. So i am gonna two things here, i ...