Outline of C programming Tools references: Unix for Programmers and Users by Glass Prof. Dennis Shasha's class notes Prof. Thomas Anantharaman's class notes "Make: A program for maintaining Computer Programs," S.I. Feldman, Bell Labs 1978 Utilities being covered 1. Make 2. dbx/gdb debuggers 3. SCCS 4. prof 5. ar 1. MAKE Utility Reference: "Make: A program for maintaining Computer Programs, S.I. Feldman," Bell Labs 1978 "In a programming project, it is easy to lose track of which files need to be reprocessed or recompiled after a change is made to some part of the source." The MAKE utility can automatically updates these tasks for the user based on a series of dependency rules stored in a special format file called a "makefile." The makefile tells MAKE the sequence of commands that create certain files and the list of files that require other files to be current before the operations can be done. The MAKE utility checks the times at which the various components were last modified, figures out the minimum amount of recompilation that has to be done to make a consistent new version, then runs the processes. The format of a makefile or Makefile is a set of lines for each program to be produced of the form: targetList:dependencyList commandList targetList is a list of target files, that is the names of the executable files that will be produced by the compilation. It is then followed by a colon (:), and then a dependencyList which is a list of source files needed to produce targets. commandList is a list of zero or more command lines, each preceded by a tab character, needed to reconstruct the target files from the dependency files. example: prog:prog.c cc -o prog prog.c 1.1 Invoking make: %make -f makefile (to specify name of makefile) or % make target (names the target to be produced, which is one of the programs listed in the makefile before the colon, make can be invoked with one or more targets) %make (when make is invoked without an argument, it picks the first target listed in the makefile.) 1.2 Sequence of execution: MAKE will execute all of the compilation commands associated with the "target" in the "makefile" if either one of two conditions is satisfied 1. the file "target" does not exist 2. the file "target" exists, but one of the sourcefiles in the dependencyList "sourcefile1..." has been modified more recently than the file "target" example of a makefile from chapter 11 in Glass: main1: main1.o reverse.o cc main1.o reverse.o -o main1 main1.o: main1.c reverse.h cc -c main1.c reverse.o: reverse.c reverse.h cc -c reverse.c reverse.o reverse.c reverse.h 1.3 Suggestions and warnings make -n orders make to print out the commands it would issue without actually taking the time to execute them make -ts cause the relevant files to be up to date. Careful use of this feature is needed make -d causes make to print very verbose output, recommended only as lat resort. 1.4 More details on MAKE: (Prof. Thomas Anantharanam's notes) 1/ all commands listed in the commandList are executed as bourne shell commands 2/provides macro substitution facility for examples CFLAGS= -O causes the optimizing C compiler to be used 3/ ability to encapsulate commands in a single file for easy administration example: install: cp main1 /usr/bin/myprog ; rm main1 ---------------------------------------------------------------------------------- GNU make is usually /usr/local/bin/make Function | GNU make | SUN make ------------------------------------------------------------------------------ Basic Rule target : dependency1 ... Same sh commandline1 sh commandline2 ... Variables VAR = Same ... $(VAR) ... Pattern replacement $(VAR:s1=s2) $(VAR:p1%s1=p2%s2) in Variables Pattern Rule t1%t2 : d1%d2 Same ... $* ... Automatic Variable $@ == target Same in commandline $* == % pattern $* == % pattern OR target w/out suffix $< == first dependency $< == all dependencies $^ == all dependencies Not Defined Automatic Variable in dependency list $$@ means target Same Predefined Pattern %.o : %.c Same Rules (Small Sample) % : SCCS Same % : RCS N/A Variables used in CFLAGS = Same Predefined Patterns CPPFLAGS = Same (Small Sample) CC = Same Automatic #include include $(OBJ:.o=.d) .KEEP_STATE: dependency generation %.d: %.c for C programs /bin/sh -ec 'gcc -MM -MG $(CPPFLAGS) $*.c | sed '\''s/$*\.o[ :]*/$@ &/g'\'' > $@' (Just include these # The previous line is for GNU make only line(s) in the # Handles missing .h files # All .h files must be makefile) # (with RCS or SCCS) # present. # Doesn't notice if a # Notices if a makefile # makefile command changes # command changes. ------------------------------------------------------------------------------ ======================================================================== 2. GDB and DBX debuggers The GDB or DBX debuggers let you examine the internal workings of your code while the program runs. debuggers allow you to set breakpoints to stop the program's execution at a particular point of interest and examine variables. to work with a debugger, you first have to recompile the program with the proper debugging options. Use the -g command line parameter to cc or CC, for example cc -g -c foo.c Two ways to use a debugger -run the debugger on your program, executing the program from within the debugger and see what happens -post mortem mode; program has crashed and core dumped In post-mortem debugging, you often won't be able to find out exactly what happened, but you usually get a stack trace. A stack trace shows the chain of function calls where the program exited ungracefully but does not always pinpoint what caused the problem. usage: %gdb prog %dbx prog you then enter the debugger gdb gives its own prompt -run command will start execution of your program until program hits a fatal error, finishes execution or you hit control ^C -help provides a listing of all gdb commands. -list displays the source code around a current source code line number. -list 55 displays the source code around line number 55 of your program. -list proc.c 55 displays the source code of program proc.c around line number 55. -where function that got the program to this line, stack trace print var1 display the value of variable var1 Setting breakpoints, by specifying either line numbers or function names break 33 stops before executing line number 33 break func stops before executing the first line of code of function func. continue to continue execution You can step one line of code at the time, or multiple lines step will step one line of code step 20 will go 20 lines of code quit will let you exit the debugger Once you hit a fatal error, you cannot continue debugging. DBX has similar functionality to place a breakpoint, use the command stop (it is break with gdb) stop in func to set a breakpoint at start of function func. stop at line_numb to set a breakpoint at line_number. When you hit a fatal error, need to do some backtracking, by checking which statement of your program caused the error, looking at the variables involved and how they were modified to find out what caused the error. It requires patience and experience. Prof. Thomas Anantharanam's notes Debugging : A) Automatic Compile time error checking: Using GNU gcc: (Must declare all functions) % gcc -Wall -O -c .c Using cc : (Must declare all functions) % cc -O -c .c # On by default : add -erroff to turn off lint: (Dont have to declare all functions) % lint -Nlevel=4 NOTE : lint is almost obsolete, since all good C-code should declare all functions. B) Interactive UNIX debuggers Compile programs with -g flag. All commands except the first is typed to the debugger's command prompt. Expression can be either a variable or a C-expression Function | GNU gdb | dbx ------------------------------------------------------------------------------ Start up debugger % gdb program [core|PID] % dbx program [core] exit debugger quit quit run program run args run args Interrupt program -C -C print expression print expression print expression print expression each display expression time program stops execute one [N] line[s] step [N] step of code execute next one [N] next [N] next line[s] of code counting function call as 1 line stop program at break [file:]line stop at [file:]line specified point break [file:]func stop in [file:]func break if stop if print each line as trace program executes trace [file:]func (where specified) trace [file:]line stop program when some watch expression stop change variable variable is changed print lines of code list list where program is stopped display current and backtrace where parent functions active when program stopped ------------------------------------------------------------------------------ C) Automatic run time error-checking [You won't be tested on this] Using SUN dbx: To make dbx automatically check for all array bounds and allocation errors : % dbx -C program (dbx) check -access -all (dbx) run args [dbx will stop after each array bound or allocation error] =========================================================================== 3. SCCS is the UNIX Source Code Control System provides the ability to store/access and protect all of the versions of source code files. SCCS provides the following benefits 1. if program has multiple versions, it keeps track only of differences between multiple versions. 2. allows only one person at the time to do the editing 3. provides a way to look at the history of program development Basic operations - use the admin utility to create an SCCS file. the SCCS file cannot be edited or compiled directly, it contains special info. - use the get utility to edit a file specifying for reading or writing (-e) option. -use the delta utility to return a new version of a file to the SCCS system. -prs allows you to obtain a file's history. -sact allows you to see the current editing activities on a particular SCCS file. (Prof. Thomas Anantharanam's Notes) Version Control: ID Numbering : 1.1 -> 1.2 -> 1.3 ... -> 2.1 -> 2.2 -> 2.3 ... \ -> 1.2.1.1 -> 1.2.1.2 -> ... 1.2.2.1 -> 1.2.2.2 ... ID's with 2 digits are called "main line" or "trunk" versions. ID's with 4 (in RCS 4 or more) digits are called "branch" versions. Function | GNU RCS | SCCS ------------------------------------------------------------------------------ setup mkdir RCS mkdir SCCS check in new foo.c ci foo.c sccs create foo.c check in update to foo.c ci foo.c sccs delta foo.c get readonly foo.c co foo.c sccs get foo.c get writeable foo.c co -l foo.c sccs edit foo.c Specify next version ID co -l foo.c sccs edit -r foo.c (Default : increment ci -r foo.c sccs delta foo.c last ID digit by one) # id is ID without # the last digit! compare foo.c with rcsdiff [-r] foo.c sccs diffs [-r] \ checked in foo.c foo.c [-r if not latest] list versions of foo.c rlog foo.c sccs print foo.c Insert version in code char *ID = "$Id$; char *ID = "%A%"; Display these versions ident what in compiled program. ===================================================================== 4. The UNIX profiler PROF it is important to find out where a program is spending its time. we can use the prof utility on an executable, that must be compiled with the -p option of cc usage: prof -ln [executablefile [profileFile]] -l option orders information by name -n option orders information by cumulative time. profileFile is a file used to store information gathered by PROF it is not clear how valuable is the tool with programs written in C++ ==================================================================== 5. AR: the UNIX Archive System allows you to: -create a special archive-format file which ends in a .a suffix -add/rm/replace/append any kind of files to an archive -obtain an archive's table of contents Creating an archive: -adding a file: ar r archivename {file}+ -appending a file ar q archivename {file}+ -obtaining a table of contents ar t archivename {file}+ -deleting a file ar d archivename {file}+ -extracting a file ar x archivename {file}+ To place object modules in order in an archive, use the ranlib utility ranlib {archive}+ adds a table of contents to each specified archive.