Usually, they provide at least an editor, a file browser, and a method of running the application and capturing the output.More complete environments add support for generating source co
Trang 1This directory is specific to Red Hat Linux Other Linux distributions will use other directories, such as
Good sources for spec file examples are other RPM packages Look at source RPMs, which are stored
in files ending in .src.rpm Install these RPMs and look through the spec files You should find more complicated examples than you will ever need Interesting spec files include those for anonftp,
telnet, vnc, and sendmail.
In addition, the designers of the RPM system wisely decided not to try to replace common build toolssuch as makeor configure The RPM system contains many shorthand features to take advantage ofmakefiles and configurescripts
In this example, we will create a spec file for the simple myappapplication Name your spec filemyapp.spec Start the spec file with a set of definitions of the name, version number, and other information about your package For example,
Vendor: Wrox PressDistribution: AnyName: myappVersion: 1.0Release: 1Packager: neil@provider.comLicense: Copyright 2003 by Wrox PressGroup: Applications/Media
This section of an RPM spec file is often called a preamble In our preamble, the most important settings
are the Name, Version, and Release This example sets the name to myapp, the version number to 1.0,and the release of the RPM package at 1, our first attempt at making an RPM package
The Groupsetting is used to help graphical installation programs sort the thousands of Linux tions by type The Distributionis important if you build a package just for one Linux distribution,such as Red Hat or SuSE Linux
applica-Adding comments to your spec file is a good idea Like shell scripts and makefiles, the rpmbuildmand treats any line starting with a #character as a comment For example,
com-# This line is a comment
Trang 2To help users decide whether to install your package, provide a Summaryand a %description(note theinconsistency in the RPM syntax, with a percent sign before description) For example, we can describeour package as follows:
Summary: Trivial application
%description
MyApp Trivial Application
A trivial application used to demonstrate development tools
This version pretends it requires MySQL at or above 3.23
Authors: Neil Matthew and Richard Stones
The %descriptionsection can take up multiple lines (and normally should)
The spec file can contain dependency information, both what your package provides and what yourpackage depends on (You can also define what the source package depends on, such as special headerfiles necessary for compiling.)
The Providessetting defines what capabilities your system provides For example,
Provides: goodness
This example states that our package provides the imaginary capability called goodness The RPM tem will also automatically add a Providesentry for the name of the package, myappin our case TheProvidessettings are useful for multiple packages that may provide the same thing For example, theApache Web server package provides the capability webserver Other packages, such as Thy, may alsoprovide the same capability (To help deal with conflicting packages, RPM allows you to specify
sys-Conflictsand Obsoletesinformation as well.)
The most important dependency information, though, is the Requiressettings You can state all thepackages your package requires for operation For example, a Web server requires networking and secu-rity packages In our example, we define a requirement for the MySQL database, at version 3.23 orhigher The syntax for this follows:
inter-After you have defined the requirements, you need to define the sources that make up your application.For most applications, you can simply copy the following setting:
Trang 3The %{name}syntax refers to an RPM macro, in this case, the name of the package Since we previouslyset the name to myapp, the rpmbuildcommand will expand %{name}to myapp, and similarly expand
%{version}to 1.0, making for a file named myapp-1.0.tar.gz The rpmbuildcommand will look forthis file in the SOURCESdirectory described previously
Our example sets up a Buildroot, which defines a staging area to test the installation You can copy thefollowing for your packages:
This is one way that the RPM system takes advantage of the work you’ve already done in creating yourmakefile
The %installsection installs your application, any manual pages, and any support files You can oftenuse the RPM macro %makeinstall, which calls the installtarget of the makefile In our case, though,
we can manually install the files to show more RPM macros:
%installmkdir -p $RPM_BUILD_ROOT%{_bindir}
If you use a configure script to create the makefile, all the various directories get set properly into your makefile In most cases, you will not need to set up all the installation commands manually in the spec file as shown in the previous example.
Trang 4The %cleantarget cleans up the files created by the rpmbuildcommand For example,
%clean
rm -rf $RPM_BUILD_ROOT
After specifying how to build the package, you need to define all the files that will be installed RPM isvery rigid on this; it has to be rigid so that it can properly track every file from every package The
%filessection names the files to include in the package In our case, we have only two files to distribute
in the binary package: the myappexecutable and myapp.1manual page For example,
%files
%{_bindir}/myapp
%{_mandir}/myapp.1
The RPM system can run scripts before and after your package is installed For example, if your package
is a daemon process, you probably need to modify the system initialization scripts to start your daemon
Do that with a %postscript A simple example that merely sends an e-mail message follows:
%post
mail root -s “myapp installed - please register” </dev/null
Look for examples in server RPM spec files
The complete spec file for our trivial application follows:
MyApp Trivial Application
A trivial application used to demonstrate development tools
This version pretends it requires MySQL at or above 3.23
Authors: Neil Matthew and Richard Stones
Trang 5%setup -q
%buildmake
%installmkdir -p $RPM_BUILD_ROOT%{_bindir}
%files
%{_bindir}/myapp
%{_mandir}/myapp.1
We are now ready to build the RPM package
Building an RPM Package with rpmbuildBuild packages with the rpmbuildcommand, which uses the following syntax:
rpmbuild -bBuildStage spec_file
The -boption tells rpmbuildto build an RPM The extra BuildStage option is a special code that tells the
rpmbuildcommand how far to go when building We list the options in the following table
Option Usage
-ba Build all, both a binary and source RPM
-bb Build a binary RPM
-bc Build (compile) the program but do not make the full RPM
-bp Prepare for building a binary RPM
-bi Create a binary RPM and install it
-bl Check the listing of files for the RPM
-bs Build a source RPM only
Older versions of the RPM system used the rpmcommand to build packages in place of the rpmbuild
command With the more recent RPM releases, though, the rpmcommand will no longer build RPM packages You must use the rpmbuildcommand.
Trang 6To build both a binary and a source RPM, use the -baoption The source RPM allows you to recreate thebinary RPM.
The following shows the output from building our package:
$ rpmbuild -ba myapp.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.71108
gcc -g -Wall -ansi -c -o main.o main.c
gcc -g -Wall -ansi -c -o 2.o 2.c
Trang 7+ install -m755 myapp.1 /var/tmp/myapp-1.0-root/usr/share/man/myapp.1+ /usr/lib/rpm/find-debuginfo.sh /usr/src/redhat/BUILD/myapp-1.0extracting debug info from /var/tmp/myapp-1.0-root/usr/bin/myapp
1 block+ /usr/lib/rpm/redhat/brp-compress+ /usr/lib/rpm/redhat/brp-strip /usr/bin/strip+ /usr/lib/rpm/redhat/brp-strip-static-archive /usr/bin/strip+ /usr/lib/rpm/redhat/brp-strip-comment-note /usr/bin/strip /usr/bin/objdumpProcessing files: myapp-1.0-1
Provides: goodnessRequires(interp): /bin/shRequires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires(post): /bin/shRequires: libc.so.6 libc.so.6(GLIBC_2.0) mysql >= 3.23Processing files: myapp-debuginfo-1.0-1
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Checking for unpackaged file(s): /usr/lib/rpm/check-files /var/tmp/myapp-1.0-rootWrote: /usr/src/redhat/SRPMS/myapp-1.0-1.src.rpm
Wrote: /usr/src/redhat/RPMS/i386/myapp-1.0-1.i386.rpmWrote: /usr/src/redhat/RPMS/i386/myapp-debuginfo-1.0-1.i386.rpmExecuting(%clean): /bin/sh -e /var/tmp/rpm-tmp.17422
+ umask 022+ cd /usr/src/redhat/BUILD+ cd myapp-1.0
+ rm -rf /var/tmp/myapp-1.0-root+ exit 0
When the build is complete, you should see two packages: the binary RPM in the RPMSdirectory, under
an architecture subdirectory such as RPMS/i386, and a source RPM in SRPMS.The binary RPM filename will appear something like the following:
myapp-1.0-1.i386.rpm
The architecture on your system may be different
The source RPM filename will appear as follows:
Trang 8Other Package Formats
Although RPM is fast becoming the most popular way of distributing applications in a way that allowsthe user to control their installation, and deinstallation, there are competing packages out there Somesoftware is still distributed as gzipped tar files (tgz) Typically the installation steps consist of unpack-ing the archive into a temporary directory and running a script to perform the actual installation The Debian Linux distributions (and some others) support another package format, similar in function-ality to RPM, called dpkg The dpkgutility on Debian unpacks and installs package files that usuallyhave a debextension If you need to distribute an application using debfile packages, it is possible toconvert an RPM package to dpkgformat using a utility called Alien More details on Alien can be found
at http://kitenet.net/programs/alien/
Development Environments
Almost all of the tools we have looked at so far in this chapter are essentially command line tools.Developers that have experience with Windows will no doubt have some experience with integrateddevelopment environments, or IDEs for short IDEs are graphical environments that typically bringtogether some or all of the tools needed to create, debug, and run an application Usually, they provide
at least an editor, a file browser, and a method of running the application and capturing the output.More complete environments add support for generating source code files from templates for certaintypes of application, integration with a source code control system, and automatic documentation
In this chapter, we will mention a few of the IDEs available for Linux today These environments areunder active development, with the most advanced of them beginning to rival the quality of commercialofferings
xwpe
Way back in 1993 Fred Kruse developed a character-based file manager and text editor It was small,easy to use, and colorful Its style mimicked the MS-DOS tools of the day Many programmers appreci-ated its speed and low demand on precious computer resources
Called “Window Editor,” and invoked as we, the editor is able to edit multiple files at once, search fortext, automatically indent source code, and print it Later developments added support for executingand debugging programs from within the editor This version became the Window ProgrammingEnvironment (wpe) Both weand wperun on Linux, either using a character mode console or inside acolor xterm under the X Window System Versions that run under X11 are also available as xweand xwperespectively These versions retain all the function key–driven user interface of the originals
Figure 9-2 shows xwpestepping through the execution of a simple program As you can see, the editoruses color to highlight the syntax of the C source code and the current state of execution
Today, XWPE is being developed by Identical Software You can find more details and download a version for Linux at http://www.identicalsoftware.com/xwpe/
Trang 9Figure 9-2
C-Forge
C-Forge is a commercial graphical IDE for C and C++ programs from Codeforge There are a number ofversions available for Linux, including a freeware one, which has limited but still useful functionality.C-Forge will generate a suitable makefile for projects and supports revision control using CVS so thatprojects developed with it can be readily and easily maintained Figure 9-3 shows a C-Forge projectunder construction You can download the free version of C-Forge from http://www.codeforge.com/
Trang 10Figure 9-3
KDevelop
KDevelop is an IDE for C and C++ programs It includes particular support for the creation of tions to run under the K Desktop Environment (KDE), one of the two main graphical user interfaces onLinux today It can also be used for other project types, including simple C programs
applica-KDevelop is free software released under the terms of the GNU General Public License (GPL) and isavailable with many Linux distributions The latest version can be downloaded from http://www.kdevelop.org Projects created with KDevelop by default follow the standard for GNU projects For example, they will use the autoconfutility to generate makefiles that are tailored to the environ-ment for which they are being built This means that the project is ready to be distributed as sourcecode that stands a good chance of being able to be compiled on other systems
Trang 11KDevelop projects also contain templates for documentation, the GPL license text, and generic installationinstructions The number of files that are generated when making a new KDevelop project can be daunt-ing, but should be familiar to anyone who has downloaded and compiled a typical GPL application.There is support with KDevelop for CVS source code control, and applications can be both edited anddebugged without leaving the environment Figures 9-4 and 9-5 show the default KDevelop C applica-tion (yet another Hello World! program) being edited and executed.
Figure 9-4
Trang 12Figure 9-5
Other Environments
There are many other editors and IDEs either available for Linux or under development The followingtable’s list of links is not intended to be complete, but some of the developments may prove to be interesting
Enivronment Type Product URL
gbuilder An IDE for GNOME http://gbuilder.sourceforge.net/Anjuta An IDE for GNOME http://anjuta.sourceforge.net/
qtez/index.php
programmer’s editorSlickEdit A commercial multi- http://www.slickedit.com/
language code editor
Trang 13Enivronment Type Product URL
Kylix A commercial IDE for http://www.borland.com/kylix
C++ and DelphiEclipse Java-based tool http://www.eclipse.org
platform and IDE
Summar y
In this chapter, we’ve seen just a few of the Linux tools that make development and distribution of grams manageable First, and perhaps most important, we used makeand makefiles to manage multiplesource files We then looked at source code control with RCS and CVS, which lets us track changes as wedevelop our code We then covered program distribution with patch, tarwith gzip, and RPM pack-ages Finally, we took a look at some of the tools that make the edit-run-debug cycle of development alittle easier, the IDEs
Trang 15Debugging
Every significant piece of software will contain defects, typically two to five per 100 lines of code.These mistakes lead to programs and libraries that don’t perform as required, often causing a pro-gram to behave differently than it’s supposed to Bug tracking, identification, and removal canconsume a large amount of a programmer’s time during software development
In this chapter, we’ll look at software defects and consider some tools and techniques for trackingdown specific instances of erroneous behavior This isn’t the same as testing (the task of verifyingthe program’s operation in all possible conditions), although testing and debugging are, of course,related, and many bugs are discovered during the testing process
Topics we’ll cover include
❑ Types of errors
❑ General debugging techniques
❑ Debugging with GDB and other tools
❑ Specification Errors:If a program is incorrectly specified, it will inevitably fail to perform
as required Even the best programmer in the world can sometimes write the wrong gram Before you start programming (or designing), make sure that you know and under-stand clearly what your program needs to do You can detect and remove many (if not all)specification errors by reviewing the requirements and agreeing that they are correct withthose who will use the program
Trang 16pro-❑ Design Errors:Programs of any size need to be designed before they’re created It’s not usuallyenough to sit down at a computer keyboard, type source code directly, and expect the program
to work the first time Take time to think about how you will construct the program, what datastructures you’ll need, and how they will be used Try to work out the details in advance,because it can save many rewrites later on
❑ Coding Errors:Of course, everyone makes typing errors Creating the source code from yourdesign is an imperfect process This is where many bugs will creep in When you’re faced with abug in a program, don’t overlook the possibility of simply rereading the source code or askingsomeone else to It’s surprising just how many bugs you can detect and remove by talkingthrough the implementation with someone else
Try executing the core of the program on paper, a process sometimes called dry running For the
most important routines, write down the values of inputs and calculate the outputs step by step.You don’t always have to use a computer to debug, and sometimes it can be the computer caus-ing the problems Even the people who write libraries, compilers, and operating systems makemistakes! On the other hand, don’t be too quick to blame the tools; it is more likely that there’s abug in a new program than in the compiler
General Debugging Techniques
There are several distinct approaches to debugging and testing a typical Linux program We generallyrun the program and see what happens If it doesn’t work, we need to decide what to do about it Wecan change the program and try again (code inspection, trial and error), we can try to gain more infor-mation about what’s happening inside the program (instrumentation), or we can inspect the programoperation directly (controlled execution) The five stages of debugging are
❑ Testing:Finding out what defects or bugs exist
❑ Stabilization:Making the bugs reproducable
❑ Localization:Identifying the line(s) of code responsible
❑ Correction:Fixing the code
❑ Verification:Making sure the fix works
A Program with Bugs
Let’s look at an example program that contains bugs During the course of this chapter, we’ll try to debug
it The program was written during the development of a larger software system Its purpose is to test asingle function, sort, which is intended to implement a bubble sort algorithm on an array of structures of
Languages with compilers, such as C, have an advantage here in that syntax errors
can be caught at compile time, whereas interpreted languages such as the Linux shell
might detect syntax errors only when you try to run the program If the problem is
with error-handling code, it might not be easy to spot in testing.
Trang 17type item The items are sorted in ascending order of the member, key The program calls sorton a ple array to test it In the real world we would never seek to use this particular sort of algorithm, as it’s fartoo inefficient We have used it here because it is short, relatively simple to understand, and also easy toget wrong In fact, the standard C library has a function that performs the desired task already calledqsort.
sam-Unfortunately, the code is not very readable, there are no comments, and the original programmer isn’tavailable We’ll have to struggle with it on our own, starting from the basic routine debug1.c
/* 1 */ typedef struct {/* 2 */ char *data;
/* 3 */ int key;
/* 4 */ } item;
/* 5 */
/* 6 */ item array[] = {/* 7 */ {“bill”, 3},/* 8 */ {“neil”, 4},/* 9 */ {“john”, 2},/* 10 */ {“rick”, 5},/* 11 */ {“alex”, 1},/* 12 */ };
/* 13 */
/* 14 */ sort(a,n)/* 15 */ item *a;
/* 16 */ { /* 17 */ int i = 0, j = 0;
/* 25 */ a[j] = a[j+1];
/* 26 */ a[j+1] = t;
/* 27 */ s++;
/* 28 */ }/* 29 */ }
/* 30 */ n ;
/* 31 */ }/* 32 */ }/* 33 */
/* 34 */ main()/* 35 */ {/* 36 */ sort(array,5);
Trang 18Before we run this program, we’ll add some code to print out the result Otherwise, we won’t knowwhether the program has worked We will add some additional lines to display the array after it’s beensorted We call the new version debug2.c.
$ cc -o debug2 debug2.c
$ /debug2
What happens when you do this will depend on your flavor of Linux (or UNIX) and on how it’s set up
On one of the authors’ systems, we got
Clearly there’s a serious problem with this code If it runs at all, it’s failing to sort the array correctly, and
if it’s being terminated with a segmentation fault, the operating system is sending a signal to the gram saying that it has detected an illegal memory access and is prematurely terminating the program
pro-to prevent memory from being corrupted
The ability of the operating system to detect illegal memory access depends on its hardware tion and some subtleties of its memory management implementation On most systems, the memory
Trang 19configura-allocated to the program by the operating system is larger than the memory actually being used If theillegal memory access occurs in this region of memory, the hardware may not be able to detect the illegalaccess This is why not all versions of Linux and UNIX will generate a segmentation violation.
When you’re tracking down array access problems, it’s often a good idea to increase the size of array ments, as this increases the size of the error If we read a single byte beyond the end of an array of bytes,
ele-we may get away with it, as the memory allocated to the program will be rounded up to an operatingsystem–specific boundary, possibly as much as 8K
If we increase the array element size, in this case by changing the itemmember datato be an array of4,096 characters, any access to a nonexistent array element will probably be to a memory locationbeyond that allocated Each element of the array is 4K in size, so the memory we use incorrectly will be0–4K off the end
If we do this, calling the result debug3.c, we get a segmentation fault on both the authors’ flavors ofLinux
/* 2 */ char data[4096];
$ cc -o debug3 debug3.c
$ /debug3
Segmentation fault (core dumped)
It’s possible that some flavors of Linux or UNIX still won’t produce a segmentation fault When theANSI C standard states that the behavior is undefined, it truly does allow the program to do anything
It certainly looks like we have written a nonconforming C program here, and a nonconforming C gram may exhibit very strange behavior! As we will see, the fault does turn out to fall into the category
pro-of undefined behavior
Code Inspection
As we mentioned earlier, it’s often a good idea to reread your program when it fails to run as expected.For the purposes of this chapter, let’s assume that the code has been reviewed and that obvious faultshave been dealt with
There are tools that you can use to help with code reviews, the compiler being an obvious one It will tellyou if you have any syntax errors in your program
Code inspection is also a term for the more formal process of a group of developers
tracing through a few hundred lines of code in detail, but the scale really doesn’t matter; it’s still code inspection and it’s still a very useful technique.
Some library functions, such as printf, will also prevent illegal accesses in some special circumstances, such as using a null pointer.
Trang 20We’ll mention other tools, lintand Splint, a little later Like the compiler, they analyze source codeand report on code that might be incorrect.
Instrumentation
Instrumentation is the adding of code to a program for the purpose of collecting more information about
the behavior of the program as it runs It’s very common to add printfcalls, as we have done in ourexample, to print out the values of variables at different stages in a program’s execution We could use-fully add several printfcalls, but we should be aware that the process entails an additional edit andcompile whenever the program is changed, and of course, we will need to remove the code when thebugs are fixed
There are two instrumentation techniques that can help here The first uses the C preprocessor to tively include instrumentation code so that we only need to recompile the program to include or excludedebugging code We can do this quite simply with constructs such as
infor-in the case where no debugginfor-ing is required:
Some compilers also have options to raise warnings on dubious practices, such as
failing to initialize variables and using assignments in conditions For example, the
GNU compiler can be run with these options:
gcc -Wall -pedantic –ansi
They enable many warnings and additional checks for conformance to C standards.
We recommend that you get into the habit of using these options, Wallespecially.
It can generate helpful information when tracking down program faults.
Trang 21Note that these symbols are prefixed and suffixed by two underscores This is common for standard
pre-processor symbols, and you should take care to avoid choosing symbols that might clash The term current
in the above descriptions refers to the point at which the preprocessing is being performed, that is, the timeand date the compiler was run and the file processed
Try It Out—Debug InformationHere’s a program, cinfo.c, that prints information about its compilation date and time if debugging isenabled
#include <stdio.h>
int main(){
#ifdef DEBUGprintf(“Compiled: “ DATE “ at “ TIME “\n”);
printf(“This is line %d of file %s\n”, LINE , FILE );
#endifprintf(“hello world\n”);
exit(0);
}
When we compile this program with debug enabled (using -DDEBUG), we see the compilation information
$ cc -o cinfo -DDEBUG cinfo.c
$ /cinfo
Compiled: Mar 1 2003 at 18:17:32This is line 7 of file cinfo.chello world
$
Trang 22How It Works
The C preprocessor part of the compiler keeps track of the current line and file when it’s compiling
It substitutes the current (compile time) values of these variables whenever it encounters the symbols LINE and FILE The date and time of compilation are made available similarly Since
DATE and TIME are strings, we can concatenate them with format strings for printf
because ANSI C specifies that adjacent strings be treated as one
Debugging without Recompiling
Before we move on, it’s worth mentioning that there’s a way of using the printffunction to help withdebugging without using the #ifdef DEBUGtechnique, which requires a program to be recompiledbefore it can be used
The method is to add a global variable as a debug flag, allow a -doption at the command line, whichallows the user to switch debugging on even after the program has been released, and add a debug log-ging function Now you can intersperse things like this in the program code:
if (debug) {
sprintf(msg, )write_debug(msg)}
You should write debug output to stderr, or, if this isn’t practical because of the nature of the program,use the logging facilities provided by the syslogfunction
If you add traces like this to solve problems during development, just leave the code in there Providedyou use a small amount of care, this can be quite safe The benefit comes when the program has beenreleased; if users encounter a problem, they can run it with debugging on and diagnose the errors foryou Instead of reporting that the program gave the message segmentation fault, they can also reportexactly what the program was doing at the time, not just what the user was doing The difference can beimmense
There is obviously a downside to this approach; the program is larger than it needs to be In most cases,this is more an apparent problem than a real one The program will probably be 20 percent or 30 percentlarger, but in most cases this doesn’t have any real impact on performance Poor performance comesfrom increasing size by orders of magnitude, not by a mere doubling
Controlled Execution
Let’s get back to the example program We have a bug We can modify the program by adding additionalcode to print out the values of variables as the program runs, or we can use a debugger to control theprogram’s execution and view its state as execution proceeds
There are a number of debuggers available on commercial UNIX systems, depending on the vendor.Common ones are adb, sdb, and dbx The more sophisticated ones allow us to look at the state of theprogram in some detail at a source code level This is true of sdb, dbx, and also of the GNU debugger,gdb, which can be used with Linux There also exist “front ends” to gdb, which make it more user-friendly; xxgdb, tgdb, and dddare such programs Some IDEs, such as the ones we saw in Chapter 9,
Trang 23also provide debugging facilities or a front end to gdb The Emacs editor also has a facility (gdb-mode)that allows you to run gdbon your program, set breakpoints, and see which line in the source code isbeing executed.
To prepare a program for debugging, you need to compile it with one or more special compiler options.These options instruct the compiler to include extra debugging information into the program This infor-mation includes symbols and line numbers—information the debugger can use to show the user where
in the source code execution has reached
The -gflag is the usual one used to compile a program for debugging We must use it for compilingeach source file that needs to be debugged and also for the linker, so that special versions of the standard
C library can be used to provide debug support in library functions The compiler program will pass theflag to the linker automatically Debugging can be used with libraries that aren’t compiled for the pur-pose, but with less flexibility
Debug information can make the executable many (up to ten) times larger Even though the executablemay be larger (and take up more disk space) the amount of memory the program needs to run is effec-tively the same It is usually a good idea to remove debug information before you release your programs,but only after they have been debugged
Debugging with gdbWe’ll use the GNU debugger, gdb, to debug this program It’s a very capable debugger that is freelyavailable and can be used on many UNIX platforms It’s also the default debugger on Linux systems.gdbhas been ported to many other platforms and can be used to debug embedded real-time systems
GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions
Type “show copying” to see the conditions
There is absolutely no warranty for GDB Type “show warranty” for details
This GDB was configured as “i586-suse-linux”
Trang 24(gdb) help
List of classes of commands:
aliases — Aliases of other commands
breakpoints — Making program stop at certain points
data — Examining data
files — Specifying and examining files
internals — Maintenance commands
obscure — Obscure features
running — Running the program
stack — Examining the stack
status — Status inquiries
support — Support facilities
tracepoints — Tracing of program execution without stopping the program
user-defined — User-defined commands
Type “help” followed by a class name for a list of commands in that class
Type “help” followed by command name for full documentation
Command name abbreviations are allowed if unambiguous
(gdb)
gdbis itself a text-based application, but it does provide a few short cuts to help with repetitive tasks.Many versions have command-line editing with a history so that you can scroll back and execute thesame command again (try using the cursor keys) All versions support an “empty command”; hittingEnter executes the last command again This is especially useful when stepping through a program line
by line with the stepor nextcommands
(gdb) run
Starting program: /home/neil/BLP3/chapter10/debug3
Program received signal SIGSEGV, Segmentation fault
0x080483c0 in sort (a=0x8049580, n=5) at debug3.c:23
23 /* 23 */ if(a[j].key > a[j+1].key) {
(gdb)
The program runs incorrectly as before When the program faults, gdbshows us the reason and the tion We can now investigate the underlying cause of the problem
Trang 25loca-Depending on your kernel, C library, and compiler version, you may see the program fault at a slightlydifferent place, for example, on line 25, when array items are exchanged, rather than line 23, when arrayitem keys are compared If this is the case, you’ll see something like
Program received signal SIGSEGV, Segmentation fault
0x8000613 in sort (a=0x8001764, n=5) at debug3.c:25
We can see how we got to this position by using the backtracecommand
The backtracecommand may be abbreviated bt, and, for compatibility with other debuggers, thewherecommand has the same function
The offending line, 23, is a comparison of one array element with another
Trang 26Here we can see that the local variable jhas the value 4 Any values reported by gdbcommands likethis are kept for future use in pseudo variables Here the variable $1is assigned the value 4 in case weneed it later Subsequent commands will store their results as $2, $3, and so on
The fact that jhas the value 4means that the program has attempted to execute the statement
if(a[4].key > a[4+1].key)
The array that we have passed to sort, array, has only five elements, which will be indexed 0 through
4 So this statement reads from the nonexistent array[5] The loop variable jhas taken an incorrectvalue
If you are working through this example and your program faulted at line 25, your system detected aread past the end of the array only when it got to exchanging elements, executing
Listing the Program
We can view the source code of the program from within gdbby using the listcommand This printsout a portion of the code around the current position Subsequent uses of listwill print out more Youcan also give lista line number or function name as an argument and it will show the code at thatposition
Trang 2725 /* 25 */ a[j] = a[j+1];
26 /* 26 */ a[j+1] = t;
27 /* 27 */ s++;
(gdb)
We can see that on line 22 that the loop is set to execute while the variable jis less than n In this case, n
is 5, so jwill have the final value of 4, which is one too far A value of 4causes a comparison of a[4]with a[5]and possibly a swap One solution to this particular problem is to correct the loop terminationcondition to bej < n-1
Let’s make that change, call the new program debug4.c, recompile, and try again
There are two loops in the sortfunction The outer loop, with loop variable i, is run once for each ment in the array The inner loop swaps the element with those further down the list This has the effect
ele-of bubbling up the smaller elements to the top After each run ele-of the outer loop, the largest elementshould have made its way to the bottom We can confirm this by stopping the program at the outer loopand examining the state of the array
A number of commands are used for setting breakpoints These are listed by gdbwith help breakpoint:
clear — Clear breakpoint at specified line or functioncommands — Set commands to be executed when a breakpoint is hitcondition — Specify breakpoint number N to break only if COND is truedelete — Delete some breakpoints or auto-display expressions
Trang 28disable — Disable some breakpoints
enable — Enable some breakpoints
hbreak — Set a hardware assisted breakpoint
ignore — Set ignore-count of breakpoint number N to COUNT
rbreak — Set a breakpoint for all functions matching REGEXP
rwatch — Set a read watchpoint for an expression
tbreak — Set a temporary breakpoint
tcatch — Set temporary catchpoints to catch events
thbreak — Set a temporary hardware assisted breakpoint
watch — Set a watchpoint for an expression
Type “help” followed by command name for full documentation
Command name abbreviations are allowed if unambiguous
Let’s set a breakpoint at line 20 and run the program:
$ gdb debug4
(gdb) break 20
Breakpoint 1 at 0x804835d: file debug4.c, line 20
(gdb) run
Starting program: /home/neil/BLP3/chapter10/debug4
Breakpoint 1, sort (a=0x8049580, n=5) at debug4.c:20
20 /* 20 */ for(; i < n && s != 0; i++) {
We can print out the array value and then allow the program to continue with the contcommand.This allows the program to run until it hits the next breakpoint, in this case, until it executes line 20again We can have many breakpoints active at any time
(gdb) print array[0]
$1 = {data = “bill”, ‘\000’ <repeats 4091 times>, key = 3}
To print a number of consecutive items, we can use the construction @<number>to cause gdbto print anumber of array elements To print all five elements of array, we can use
(gdb) print array[0]@5
$2 = {{data = “bill”, ‘\000’ <repeats 4091 times>, key = 3}, {
data = “neil”, ‘\000’ <repeats 4091 times>, key = 4}, {data = “john”, ‘\000’ <repeats 4091 times>, key = 2}, {data = “rick”, ‘\000’ <repeats 4091 times>, key = 5}, {data = “alex”, ‘\000’ <repeats 4091 times>, key = 1}}
Note that the output has been tidied up slightly to make it easier to read Because this is the first timethrough the loop, the array is unchanged When we allow the program to continue, we see successivealterations to arrayas execution proceeds:
(gdb) cont
Continuing
Breakpoint 1, sort (a=0x8049580, n=4) at debug4.c:20
20 /* 20 */ for(; i < n && s != 0; i++) {
Trang 29(gdb) print array[0]@5
$3 = {{data = “bill”, ‘\000’ <repeats 4091 times>, key = 3}, {data = “john”, ‘\000’ <repeats 4091 times>, key = 2}, {data = “neil”, ‘\000’ <repeats 4091 times>, key = 4}, {data = “alex”, ‘\000’ <repeats 4091 times>, key = 1}, {data = “rick”, ‘\000’ <repeats 4091 times>, key = 5}}
Furthermore, we can change the breakpoint so that, instead of stopping the program, it simply displaysthe data we have requested and carries on To do this, we use the commandscommand This allows us tospecify what debugger commands to execute when a breakpoint is hit Since we have already specified adisplay, we need only set the breakpoint command to continue execution
(gdb) commands
Type commands for when breakpoint 1 is hit, one per line
End with a line saying just “end”
Breakpoint 1, sort (a=0x8049684, n=3) at debug4.c:20
20 /* 20 */ for(; i < n && s != 0; i++) {1: array[0] @ 5 = {{data = “john”, ‘\000’ <repeats 4091 times>, key = 2}, {data = “bill”, ‘\000’ <repeats 4091 times>, key = 3}, {
data = “alex”, ‘\000’ <repeats 4091 times>, key = 1}, {data = “neil”, ‘\000’ <repeats 4091 times>, key = 4}, {data = “rick”, ‘\000’ <repeats 4091 times>, key = 5}}
Breakpoint 1, sort (a=0x8049684, n=2) at debug4.c:20
20 /* 20 */ for(; i < n && s != 0; i++) {1: array[0] @ 5 = {{data = “john”, ‘\000’ <repeats 4091 times>, key = 2}, {data = “alex”, ‘\000’ <repeats 4091 times>, key = 1}, {
data = “bill”, ‘\000’ <repeats 4091 times>, key = 3}, {data = “neil”, ‘\000’ <repeats 4091 times>, key = 4}, {data = “rick”, ‘\000’ <repeats 4091 times>, key = 5}}
array[0] = {john, 2}
array[1] = {alex, 1}
Trang 30a meaningful one ought to be provided by a call to exit.
The program doesn’t seem to execute the outer loop as many times as we expected We can see that thevalue of the parameter, n, used in the loop termination condition is reducing at each breakpoint Thismeans that the loop won’t execute enough times The culprit is the decrement of non line 30:
/* 30 */ n ;
This is an attempt to optimize the program by taking advantage of the fact that at the end of each outerloop the largest element of arraywill be at the bottom, and so there is less left to sort But, as we’veseen, this interferes with the outer loop and causes problems The simplest fix (though there are others)
is to delete the offending line Let’s test whether this change will fix the problem by using the debugger
to apply a patch
Patching with the Debugger
We’ve already seen that we can use the debugger to set breakpoints and examine the value of variables
By using a breakpoint with actions, we can try out a fix, called a patch, before changing the source code
and recompiling In this case, we need to break the program on line 30 and increment the variable n.Then, when line 30 is executed, the value will be unchanged
Let’s restart the program from the beginning First, we must delete our breakpoint and display We cansee what breakpoints and displays we have enabled using the infocommand:
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1: y array[0] @ 5
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804835d in sort at debug4.c:20
breakpoint already hit 4 timescont
We can either disable these or delete them entirely If we disable them, we retain the option to re-enablethem at a later time if we need to:
Trang 31Type commands for when breakpoint 2 is hit, one per line.
End with a line saying just “end”
>set variable n = n+1
>cont
>end (gdb) run
Starting program: /home/neil/BLP3/chapter10/debug4
Breakpoint 2, sort (a=0x8049580, n=5) at debug4.c:30
The program runs to completion and prints the correct result We can now make the change and move
on to test it with more data
Learning More about gdb
The GNU debugger is an exceptionally powerful tool that can provide a lot of information about the
internal state of executing programs On systems that support a facility called hardware breakpoints, you
can use gdbto monitor changes to variables in real time Hardware breakpoints are a feature of someCPUs; these processors are able to stop automatically if certain conditions arise, typically a memoryaccess in a given region Alternatively, gdbcan watch expressions This means that, with a performance
penalty, gdbcan stop a program when an expression takes a particular value, regardless of where in theprogram the calculation took place
Breakpoints can be set with counts and conditions so that they trigger only after a fixed number of times
or when a condition is met
gdbis also able to attach itself to programs that are already running This can be very useful whenyou’re debugging client/server systems, as you can debug a misbehaving server process as it runs with-out having to stop and restart it You can compile programs with, for example, gcc -O -gto get thebenefit of optimization and debug information The downside is that optimization may reorder code
Trang 32a bit, so, as you single-step through code, you may find yourself jumping around to achieve the sameeffect as intended by the original source code.
You can also use gdbto debug programs that have crashed Linux and UNIX will often produce a coredump in a file called corewhen a program fails This is an image of the program’s memory and willcontain the values of global variables at the time of the failure You can use gdbto work out where theprogram was when it crashed Check out the gdbmanual page for more details
gdbis available under the terms of the GNU Public License and most UNIX systems can support it
We strongly recommend that you get to know it
More Debugging Tools
Apart from out-and-out debuggers such as gdb, Linux systems typically provide a number of other toolsthat you can use to aid the debugging process Some of these provide static information about a program;others provide a dynamic analysis
Static analysis provides information from the program source code only Programs such as ctags,cxref, and cflowwork with the source files and provide useful information about function callingand location
Dynamic analysis provides information about how a program behaves during execution Programs such
as profand gprofprovide information about which functions have been executed and for how long.Let’s take a look at some of these tools and their output Not all of these tools will be available on all sys-tems, although many of them have freely available versions
Lint: Removing the Fluff from Your Programs
Original UNIX systems provided a utility called lint It was essentially the front end of a C compilerwith added tests designed to apply some common sense and produce warnings It would detect caseswhere variables were used before being set and where function arguments were not used, among otherthings
More modern C compilers can, at a cost to compile-time performance, produce similar warnings lintitself has been overtaken by the standardization of C Because the tool was based on an early C compiler,
it doesn’t cope at all well with ANSI syntax There are some commercial versions of lintavailable forUNIX and at least one on the Internet for Linux, called splint This used to be known as LClint, part of
a project at MIT to produce tools for formal specifications Alint-like tool, splintcan provide usefulcode review comments.splintcan be found at http://www.splint.org
Here’s an edited sample output from splintrunning on an early version of the example program that
Trang 33Function definition is in old style syntax Standard prototype syntax ispreferred (Use -oldstyle to inhibit warning)
debug0.c: (in function sort)debug0.c:20:31: Variable s used before definition
An rvalue is used that may not be initialized to a value on some executionpath (Use -usedef to inhibit warning)
debug0.c:20:23: Left operand of & is not unsigned value (boolean):
debug0.c:20:23: Operands of & are non-integer (boolean) (in post loop test):
A function declaration does not have a parameter list (Use -noparams toinhibit warning)
debug0.c: (in function main)debug0.c:36:17: Return value (type int) ignored: sort(array, 5)Result returned by function call is not used If this is intended, can castresult to (void) to eliminate message (Use -retvalint to inhibit warning)debug0.c:37:14: Path with no return in function declared to return intdebug0.c:14:13: Function exported but not used outside debug0: sortdebug0.c:15:17: Definition of sort
Finished checking - 22 code warnings
$
The utility complains about old-style (non-ANSI) function declarations and inconsistencies betweenfunction return types and the values they do (or do not) return These don’t affect the operation of theprogram, but ought to be addressed
It has also detected two real bugs in the following code fragment:
Trang 34Both of these errors were fixed in a code review before debugging started Although this example is a little contrived for the purposes of demonstration, these are errors that regularly crop up in real worldprograms.
Function Call Tools
Three utilities—ctags, cxref, and cflow—form part of the X/Open specification and therefore must bepresent on UNIX-branded systems with software development capabilities
ctags
The ctagsprogram creates an index of functions For each function, you get a list of the places it’s used,like the index of a book
ctags [-a] [-f filename] sourcefile sourcefile
ctags -x sourcefile sourcefile
By default, ctagscreates a file, called tags, in the current directory, which contains, for each functiondeclared in any of the input source files, lines of the form
announce app_ui.c /^static void announce(void) /
Each line in the file consists of a function name, the file it’s declared in, and a regular expression that can
be used to find the function definition within the file Some editors such as Emacs can use files of thiskind to help navigate through source code
Alternatively, by using the -xoption to ctags(if available in your version), you can produce lines of asimilar form on the standard output:
find_cat 403 app_ui.c static cdc_entry find_cat(
You can redirect the output to a different file by using the option -f filenameand append it to anexisting file by specifying the -aoption
cxref
The cxrefprogram analyzes C source code and produces a cross-reference It shows where each symbol(variable, #define, and function) is mentioned in the program It produces a sorted list with each sym-bol’s definition location marked with an asterisk, as shown here:
SYMBOL FILE FUNCTION LINE
BASENID prog.c — *12 *96 124 126 146 156 166
These utilities and others mentioned in this chapter may not be present in your
Linux distribution If not, you might like to search for implementations on the
Internet A good place to start (for Linux distributions that support the RPM package
format) is http://rufus.w3.org/linux/RPM.
Trang 35BUFMAX prog.c — *44 45 90BUFSIZ /usr/include/stdio.h — *4
EOF /usr/include/stdio.h — *27argc prog.c — 36
prog.c main *37 61 81argv prog.c — 36
prog.c main *38 61calldata prog.c — *5
prog.c main 64 188calls prog.c — *19
which functions are called by them, and so on It can be useful to find out the structure of a program tounderstand how it operates and to see what impact changes to a function will have Some versions ofcflowcan operate on object files as well as source code Refer to the manual page for details of operation.Here’s some sample output taken from a version of cflow(cflow-2.0) that is available on the Internetand maintained by Marty Leisner:
Trang 36This shows us that the functions that call exit, for example, are main, show_all_lists, and usage.
Execution Profiling with prof/gprof
A technique that is often useful when you’re trying to track down performance problems with a program
is execution profiling Normally supported by special compiler options and ancillary programs, a profile of
a program shows where it’s spending its time
The profprogram (and its GNU equivalent, gprof) prints a report from an execution trace file that isproduced when a profiled program is run A profiled executable is created by specifying the –pflag (forprof) or -pgflag (for gprof) to the compiler:
$ cc -pg -o program program.c
The program is linked with a special version of the C library and is changed to include monitoring code.This may vary with specific systems, but is commonly achieved by arranging for the program to beinterrupted frequently and the execution location to be recorded The monitor data is written to a file inthe current directory, mon.out(gmon.outfor gprof)
$ /program
$ ls -ls
2 -rw-r r 1 neil users 1294 Feb 4 11:48 gmon.out
The prof/gprofprogram reads this monitoring data and produces a report Refer to the manual pagesfor detail on program options Here is some (abbreviated) gprofoutput as an example:
cumulative self self total time seconds seconds calls ms/call ms/call name18.5 0.10 0.10 8664 0.01 0.03 _doscan [4]18.5 0.20 0.10 mcount (60)14.8 0.28 0.08 43320 0.00 0.00 _number [5]9.3 0.33 0.05 8664 0.01 0.01 _format_arg [6]7.4 0.37 0.04 112632 0.00 0.00 _ungetc [8]7.4 0.41 0.04 8757 0.00 0.00 _memccpy [9]7.4 0.45 0.04 1 40.00 390.02 _main [2]3.7 0.47 0.02 53 0.38 0.38 _read [12]3.7 0.49 0.02 w4str [10]1.9 0.50 0.01 26034 0.00 0.00 _strlen [16]1.9 0.51 0.01 8664 0.00 0.00 strncmp [17]
Trang 37Asser tionsWhile it’s common to introduce debug code such as printfcalls, possibly by conditional compilation,during the development of a program, it’s sometimes impractical to leave these messages in a deliveredsystem However, it’s often the case that problems occur during the program operation that are related
to incorrect assumptions rather than coding errors These are events that “can’t happen.” For example, afunction may be written with the understanding that its input parameters will be within a certain range
If it’s passed incorrect data, it might invalidate the whole system
For these cases, where the internal logic of the system needs to be confirmed, X/Open provides theassertmacro that can be used to test that an assumption is correct and halt the program if not
#include <assert.h>
void assert(int expression)
The assertmacro evaluates the expression and, if it’s nonzero, writes some diagnostic information tothe standard error and calls abortto end the program
The header file assert.hdefines the macro depending on the definition of NDEBUG If NDEBUGisdefined when the header file is processed, assertis defined to be essentially nothing This means thatyou can turn off assertions at compile time by compiling with -DNDEBUGor by including the line
#define NDEBUG
in each source file before including assert.h.This method of use is one problem with assert If you use assertduring testing, but turn it off forproduction code, your production code could have less safety checking than when you are testing it.Leaving assertions enabled in production code is not normally an option—would you like your code topresent a customer with the unfriendly error assert failedand a stopped program? You may consider
it better to write your own error trapping routine that still checks the assertion but doesn’t need to becompletely disabled in production code
You must also be careful that there are no side effects in the assertexpression For example, if you use afunction call with a side effect, the effect won’t happen in the production code if assertions are removed.Try It Out—assert
Here’s a program, assert.c, that defines a function that must take a positive value It protects againstthe possibility of a bad argument by using an assertion
After including the assert.hheader file and a “square root” function that checks that the parameter ispositive, we can then write the mainfunction:
#include <stdio.h>
#include <math.h>
#include <assert.h>
double my_sqrt(double x){
Trang 38If we recompile the program with -DNDEBUG, the assertion is compiled out and we get a mathematicalerror when we call the sqrtfunction from my_sqrt.
$ cc -o assert -DNDEBUG assert.c -lm
One area that is a rich source of bugs that are difficult to track down is dynamic memory allocation
If you write a program that uses mallocand freeto allocate memory, it’s important that you keepgood track of the blocks you allocate and make sure that you don’t use a block that you’ve freed up.Typically, memory blocks are allocated by mallocand assigned to pointer variables If the pointer variable
is changed and there are no other pointers pointing to the memory block, it will become inaccessible This
is a memory leak and it causes your program to grow in size If you leak a lot of memory, your system will
Trang 39If you write beyond the end of an allocated block (or before the beginning of a block), you’ll very likelycorrupt the data structures used by the malloclibrary to keep track of allocations In this case, at somefuture time, a call to malloc, or even free, will cause a segmentation violation and your program willcrash Tracking down the precise point at which the fault occurred can be very difficult, as it may havehappened a long time before the event that caused the crash.
Unsurprisingly, there are tools, commercial and free, that can help with these two problem types Thereare, for example, many different versions of mallocand free, some of which contain additional code tocheck on allocations and deallocations to try to cater for the cases where a block is freed twice and someother types of misuse
ElectricFence
The ElectricFence library was developed by Bruce Perens and is available as an optional component insome Linux distributions such as RedHat and can be readily found on the Internet It attempts to use thevirtual memory facilities of Linux to protect the memory used by mallocand freeto halt the program
at the point of memory corruption
Try It Out—ElectricFenceHere’s a program, efence.c, that allocates a memory block with mallocand then writes beyond theend of the block Let’s see what happens
#include <stdio.h>
#include <stdlib.h>
int main(){
char *ptr = (char *) malloc(1024);
Trang 40Running under the debugger pinpoints the problem:
$ cc -g -o efence efence.c -lefence
$ gdb efence
(gdb) run
Starting program: /home/neil/BLP3/chapter10/efence
[New Thread 1024 (LWP 1869)]
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
Program received signal SIGSEGV, Segmentation fault
ElectricFence replaces mallocand associated functions with versions that use virtual memory features
of the computer’s processor to protect against illegal memory access When such an access occurs, a mentation violation signal is raised and the program halts
seg-valgrind
valgrindis a tool that is capable of detecting many of the problems that we have discussed In particular,
it can detect array access errors and memory leaks It may not be included with your Linux distributionsbut can be found at http://developer.kde.org/~sewardj
Programs do not even need to be recompiled to use valgrind, and you can even debug the memoryaccesses of a running program It is well worth a look; it has been used on major developments, includ-ing KDE version 3