Debugging is both a science and an art. Sometimes, the simplest toolthe code listingis the best debugging tool. At other times, however, you need to use other debugging tools. Three of these tools are splint, gprof, and gdb.
The splint command is similar to the traditional UNIX lint command: It statically examines source code for possible problems, and it also has many additional features. Even if your C code meets the standards for C and compiles cleanly, it might still contain errors. splint performs many types of checks and can provide extensive error information. For example, this simple program might compile cleanly and even run:
But the splint command might point out some serious problems with the source:
$ splint tux.c
tux.c: (in function main)
tux.c:2:19: Return value (type int) ignored: putchar(t[++j] -...
Result returned by function call is not used. If this is intended, can cast result to (void) to eliminate message. (Use -retvalint to inhibit warning) Finished checking --- 1 code warning
You can use the splint command's -strict option, like this, to get a more verbose report:
$ splint -strict tux.c
The GNU C compiler also supports diagnostics through the use of extensive warnings (through the
Wall and -pedantic options):
$ gcc -Wall tux.c tux.c:1: warning: return type defaults to "int' tux.c: In function "main':
tux.c:2: warning: implicit declaration of function "putchar'
If you would like to explore various C syntax-checking programs, navigate to http://www.ibiblio.org/pub/Linux/devel/lang7c. The splint program is derived from lclint, which you can find in the lclint-2.2a-src.tar.gz file at the website.
You use the gprof (profile) command to study how a program is spending its time. If a program is compiled and linked with -p as a flag, a mon.out file is created when it executes, with data on how often each function is called and how much time is spent in each function. gprof parses and displays this data. An analysis of the output generated by gprof helps you determine where performance bottlenecks occur. Using an optimizing compiler can speed up a program, but taking the time to use gprof's analysis and revising bottleneck functions significantly improves program performance.
The gdb tool is a symbolic debugger. When a program is compiled with the -g flag, the symbol tables are retained, and a symbolic debugger can be used to track program bugs. The basic technique is to invoke gdb after a core dump (a file containing a snapshot of the memory used by a program that has crashed) and get a stack trace. The stack trace indicates the source line where the core dump occurred and the functions that were called to reach that line. Often, this is enough to identify a problem. It is not the limit of gdb, though.
gdb also provides an environment for debugging programs interactively. Invoking gdb with a program enables you to set breakpoints, examine the values of variables, and monitor variables. If you suspect a problem near a line of code, you can set a breakpoint at that line and run gdb. When the line is reached, execution is interrupted. You can check variable values, examine the stack trace, and observe the program's environment. You can single-step through the program to check values. You can resume execution at any point. By using breakpoints, you can discover many bugs in code.
A graphical X Window interface to gdb is called the Data Display Debugger, or ddd.
Was this article helpful?