Tuesday, May 15, 2012

GDB V/S BUGS - III

Finally, the last post for gdb!
In this section, we will go through the terms like backtrace, core dump and stack overflow.
Starting with the 'backtrace' command, as the name suggests, it is used while running the prog, to know about the previous function calls made, i.e we can get to know, from where the control has come to the current function.It gives us the list of stack frames(function calls ,yet to be returned and stored on the the stack in stack frames sequentially), each with a number.This number is used to get info about any stack frame.'Info' command is used for further info on these frames.
e.g. 'info args 4' lists the list of arguments with their values, passed to the function call, stored on the stack frame #4.
similarly,'info locals #' gives a detailed info about the local variables used in the respective call.If you want to get the whole info in one go, with some extra info,like:
• the address of the frame
• the address of the next frame down (called by this frame)
• the address of the next frame up (caller of this frame)
• the program counter saved in it (the address of execution in the caller
frame)
• which registers were saved in the frame

the command is 'info frame #'.These all commands can help you figure out, specially in the recursive programs, the control flow of the program, the sequence of calls with their details, which can greatly help you to chase the bugs.
Using these, you can also find where your program stopped working i.e the details of the program when ?? 
The command 'where' is an additional alias for backtrace.

The next significant term is stack overflow:
A stack overflow occurs when too much memory is used on the call stack. The call stack contains a limited amount of memory, often determined at the start of the program.When a program attempts to use more space than is available on the call stack (that is, when it attempts to access memory beyond the call stack's bounds, which is essentially a 'buffer overflow'), the stack is said to overflow, typically resulting in a program crash.
This is also one of the main reasons of Segfault.
Following is an example which causes stack overflow(recursion without a base case)
 int main()
 {                                    
    main();
    return 0;
 }

lets come to the last term of the session now, core dump:
A core dump is a file that consists of the recorded state of the working memory of a computer program at a specific time, generally when the program has terminated abnormally (crashed), including the processor registers, which may include the program counter and stack pointer, memory management information, and other processor and operating system flags and information. These dumps traditionally get created as "core".Core dumps are often used to assist in diagnosing and debugging errors in computer programs.Gdb itself can use these dumps to debug the programs, using the 'working history' of the program stored in these files.

So, this marks the end of this post and gdb as well!
Any queries, you can post here, or mail me at yash.girdhar@gmail.com.
adios :)

Sunday, May 13, 2012

GDB V/S BUGS - II

ok..time to continue divulging!

Lets commence this post with conditional breakpoints.Now, where there are so many benefits, bp's themselves can become tedious sometime.We have to keep stepping,..stepping and stepping...
So,in situations, where we have an idea of what the error could be..like not allocating memory to a pointer,crossing bounds of an array or stack overflow, we can avoid unnecessary breaks in our program, like at every iteration of a loop or at each call of a recursive function.So, we need to specify the condition for breaking at these points.Using 'conditional breakpoints' allow us to carry through this.
Syntax is just similar, except you have to supply a condition or an C-expression,which if true, will trigger the bp.
e.g. break 10 i >10 -> prog will stop at 10th line only if i exceeds 10.
This will most likely avoid all unnecessary stepping.
We can also use gdb variables for these conditions.Next two lines will explain this:
(gdb) set $i = 0
(gdb) break main: if ++$i == 10
':'->because we are specifying condition at a fn.Rest all is clear.

Now, there is something called 'breakpoint commands' in gdb.Yup, we can define a set of commands to be executed at a bp.This is a potent tool of gdb, I must say.
These commands can be any c expression or gdb commands.These commands can be used to turn on/off other breakpoints,jumping over a part of code, correct the code, to control automatically displayed expressions and many other things.
Two special commands of these are 'silent' & 'continue'.Silent causes gdb to skip the usual printing when arriving at a breakpoint, and continue, continues execution of your application.
So, proceeding with a basic example, following lines illustrate how to relate with other bp's:
We want to set bp on the print function in our program, but the condition is that run should stop on the print function that is present only in some function, say 'run'.Here is the solution:
(gdb) break print (bp #1)
(gdb) break run  (bp #2)
(gdb) commands
the following line will be displayed prompting you for the commands:
"Type commands for when breakpoint 2 is hit, one per line.
End with a line saying just "end"."
enter the following commands
>silent
>enable 1
>continue
>end

(gdb) break 48    (bp #3)        Break at end of run function
(gdb) commands
Type commands for when breakpoint 3 is hit, one per line.
End with a line saying just "end".
>silent
>disable 1
>continue
>end

Now, the execution stops at print only when it is discovered inside run function!.We are using silent and continue in these command sets, so that they doesn't need any intervention from the user.
With some modification, you can use the above function to skip over a part of code.Use gdb 'help' to get to know more about the commands("help commands").

Carrying on, Suppose you have forgotten to allocate memory to a struct pointer and you discovered it while running the program in gdb.Now what?
If your ans is--'Simple,jst open the file again,edit it,compile & run the gdb again'--yes,this post is specially for you..
Now this is the best part:fixing errors inside the debugger itself.Above example will best illustrate it:
your code:
#9    node *start;    //no memory allocated
#10    start->n=5; 

commands to rectify this:
(gdb) break 10        (bp #4)        Break on line just after the declaration
(gdb) commands
Type commands for when breakpoint 4 is hit, one per line.
End with a line saying just "end".
>silent
>print start = (node *) malloc(sizeof(node))
>continue
>end

These breakpoint commands stop before the 10th line, allocate the memory, and continue with the 10th line, to chase the other bugs !. So, you fix the bug in the debugger and go on automatically.
BUT, important to note that this change will not be applied to the original file.So, don't forget to propagate the changes back to your original source code to permanently fix the problem!

So,that's it for this post.Next post will be devoted to the recursive programs(yes, I said 'recursive programs').
Sayonara till then :)






 

Saturday, May 12, 2012

GDB V/S BUGS - I

Gdb vs Bugs..yup,this is the most apt title I could earmark for this post.In this post,I'll unwrap the power of gdb in debugging our C-programs.

To start with,let me give you an idea about how gdb can help you with your programs.So, with gdb,you can inspect what your program is doing at a certain point during execution,with different test variables.You can check or change a variable's value,skip or add a part of the code,dyanamically resolve a bug in the code,all these in b/w the execution.In progs including recursion,at any stage you can analyse all the previous calls,stored on the stack.On the whole,you CAN know whats happening inside your program.

So,lets kickoff with some brief introduction:
GDB(The GNU debugger),originated by Richard stallmen,is a free software under the GPL license,and can be used for other languages also, like c++,fortran.
In C, to make our executable file work with gdb,we have to use an extra '-g' flag,when we compile the code.This enables the gdb to implant some "debugging symbols" in the file, that help it with running the code.After that,to start gdb,jst run the command:'gdb "executable name"'.
e.g gdb a.out
gdb has an interactive shell, much like the terminal one. It can recall history with the arrow keys, executes the previous command with a 'return' hit, auto-complete words (most of the time) with the TAB key, and has other nice features.

Enough with the introduction part,lets jump to our program now:
To execute our program in gdb,we have the "run" command.It simply runs our program till the end or the first 'breakpoint'(explained later),if set, provided there is no error in the program and if it has errors,we should get some output like the line or function with its parameters, where the program crashed.
Imp to note that for seg fault,program receives "SIGSEGV" signal from the kernel.

Now, for debugging, we come to the most important term of gdb,'Breakpoints',rather "The Breakpoints"(recalling their importance).Breakpoints, as the name suggests,are the points in our program,set by us, that enable us to stop the program in between and look around it.This is necessary for systematic investication of aur program, to debug it.
We can set these breakpoints by the 'break' command."break space line_num" sets a breakpoint at that line number.We can also break the program at a specefic function,
e.g 'break main' will stop the program whenever the main function starts.
We can set as many breakpoints as we want and after setting a breakpoint(bp),we will get some info about that bp,including its number.You will need this number for referring to this bp later.Now,once you have set the bp's, run again.Execution will stop at the point specified by you.

After hitting the bp,you can inspect your program status at that point.To further execute the program,we have different functions.One is 'continue',which will run the program till next bp(if none is there,till the end).Second is "Step", which gives you a fine-grained control, by running the program line-by-line.So,you can now check your program's status even after each line.Similar to step,the third funtion is 'next',except this one doesn't execute each line of a sub-routine, rather treats it as a single instruction.But typing step or next each time can be tidious..ryt.
No worries,gdb has a solution for this also.If you hit RETURN,it will run the same command you just entered.

Stopped the program,time to check it out!
But how?
You can use "print" command to print any variables's value(print variable_name).Talking of checking the prog,there is one more important term-"watchpoints".Whereas breakpoints interrupt the program at a particular line or function, watchpoints act on variables.They pause the program whenever a watched variable’s value is modified.
e.g. 'watch i' will interrupt the program whenever i changes and print the new and old values.
But it relies on the variable's scope,relative to where we are at the time of watch.
There are other commands like info breakpoints,delete, whose name indicate their functions.

There are other things also in gdb like conditional breakpoints,commands combined with breakpoints,fixing program errors dynamically,jumping over misbehaving code,backtrace(mainly in recursive programs),core dumps,relating the breakpoints and many other.These all will be included in my upcoming posts.

Any queries regarding the above post are welcomed.
So, adios till then! and this time, I am sure this post will be helpful.




 

Tuesday, May 8, 2012

FILE HANDLING IN C

So,finally a post after some time..!
and in this post,I'll be discussing about performing simple operations with files in C, including creating,reading & updating.

Starting with some basic stuff, C views each file simply as a sequencial stream of bytes i.e whenever a file is opened in a C program, a stream is associated with the file.Use of these streams is to provide communication channels between files & programs and Each file ends with a 'end-of-file' marker i.e "Ctrl+D" in linux and "Ctrl+Z" in windows.You will find use of this end-of-file marker in many of your C programs, working with files.
 
It is quite interesting to know that whenever we execute any C program,three files and their associated streams are automatically opened-'Standard Input'(enables the program to read data through the keyboard),'Standard Output'(to print data on the screen) & Standard Error'(to print the error messages).These three streams are manipulated using the file pointers-'stdin','stdout' & 'stderr',respectively.
The stream stdout,stdin or in fact,any output stream is buffered,i.e. data coming to that stream is stored in a buffer and is not printed until the buffer is full.To avoid this,we can use fflush() function. 

Opening a file returns a pointer to the 'FILE' structure defined in <stdio.h>(defined in detail in previous post),that contains information used to process a file.This structure includes a 'file descriptor' that is used as an index into an operating system array(File Open Table).Each index(FCB-file control block) of this array is used to administer a particular file.

Now,time for some practical things:
With respect to performing operations on file,I'll be discussing merely the functions used for different operations,not the whole programs.

So,lets start with creating a file:
The standard library function 'fopen' is used to create a file or refer to an existing file,through a file pointer.
Syntax:        fp=fopen("filename","open-mode").
The above statement opens the file 'filename' for the specified mode & associates the file pointer fp to it. If the file doesn't exists,it will create a new one.
The second argument of fopen specifies the mode in which we want to open the file.There are mainly three modes-"r"(reading),"w"(writing:discards the previous contents of the file),"a"(append).Other extensions of these modes also exist like:"r+"(for update(reading and writing),"rb","wb","ab";'b' character is added to indicate that the file is a binary file. 
fopen returns NULL if the given file doesn't exists & it is not able to create a new file(reasons include, system running short of memory)

But what to do after opening the file??
So aur next operation is scanning & writing to a file, which is done using the functions 'fscanf' & 'fprintf'.
Both these functions are similiar to scanf & printf functions,except these have first argument as the required file pointer.If the file pointer is given as stdin/stdout,these will work as scanf & printf.
Syntax:     fscanf(fp,"%d",&number)
        fprintf(fp,"%d",number)
fscanf returns EOF if a problem occurs whie reading data,otherwise the number of data items succesfully read.

Other functions also exist for exist for scanning a charater or a string from a file.These are fgetc & fgets.These also have the same syntax as that of getchar or gets.
Synatx:        fgetc(fp)
        fgets(str,max_size,fp)
str refers to the string variable in which the scanned string will be stored,max_size,as the name indicates, is the max size of the string that can be scanned.Note that 'fgets' can be used to read a file linewise by providing appropriate size of the string.
Similarly,for writing,we have analogous 'fputc' function.

Now,while reading a file,we may need to shift the file pointer to the starting of file.This can be done by "rewind" function.
Syntax:        rewind(fp)

All the functions I've discussed above are mainly used for sequential files, i.e. where data is stored sequentially(characterwise).Therfore,in these type of files, its very difficult to randomly edit the file,because even numbers also are stored characterwise(i.e they don't occupy full 4 bytes resulting in varying size of different numbers).Therefore,editing can result in overwriting of the data.
So,we use other type of files known as random-access-files.In these type of files,length of different records are fixed i.e variables of each data-type are provided their respective size.Nowadays, these type of files are used almost everywhere..
Funcions used for reading & writing to a random-access-file are 'fread' & 'fwrite'.
Syntax:        fread(&n,sizeof(n-datatype),1,fp)
n refers to the variable in which we want to store the scanned data,'1' refers to the no. of elements of type 'n' to be scanned:this is mainly used to scan multiple elements of an array.
Similar syntax is for fwrite:         fwrite(&n,sizeof(n-dataype),1,fp)
If 'n' is an array,it can be used to write multiple elements of this array,by changing '1' to the required number.
fwrite returns the number of items it successfully output.If this number is less than the 3rd argument,there is a problem.Same is for fread.

Finally,the most important thing,how to reach the record that we want to edit??
The function used for shifting the file pointer, is 'fseek'.
Syntax:        fseek(fp,long int offset,int whence)
offset refers to the number of bytes to seek from the location 'whence' in the file.Argument whence can have three values->SEEK_SET(starting of the file),SEEK_CUR(current location of the pointer),SEEK_END(end of the file).
fseek returns a non-zero value if the seek opearion can't be performed.

These all are the functions that you will require for working with files.Any queries regarding the same are welcomed.
 
and like always,hope this post is helpful :)