Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/17/84 chuqui version 1.9 3/12/85; site unisoft.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!think!harvard!caip!lll-crg!lll-lcc!unisoft!fnf From: fnf@unisoft.UUCP Newsgroups: net.sources Subject: dbug (part 3 of 4 REPOSTED) Message-ID: <624@unisoft.UUCP> Date: Mon, 16-Dec-85 21:38:39 EST Article-I.D.: unisoft.624 Posted: Mon Dec 16 21:38:39 1985 Date-Received: Wed, 18-Dec-85 05:29:56 EST Distribution: net Organization: UniSoft Systems, Berkeley Lines: 1537 Seems like I blew it and tried to post the nroff'd document with lots of ^H's and a few escapes. Anyway, here is one stripped of all control characters. You may have to fiddle with it to get it to print correctly, as I might have messed up the page boundries. #--------CUT---------CUT---------CUT---------CUT--------# ######################################################### # # # This is a shell archive file. To extract files: # # # # 1) Make a directory for the files. # # 2) Write a file, such as "file.shar", containing # # this archive file into the directory. # # 3) Type "sh file.shar". Do not use csh. # # # ######################################################### # # echo Extracting user.t: sed 's/^Z//' >user.t <<\STUNKYFLUFF Z Z Z Z D B U G Z Z C Program Debugging Package Z Z by Z Z Fred Fish Z Z Z Z Z INTRODUCTION Z Z Z Almost every program development environment worthy of Z the name provides some sort of debugging facility. Usually Z this takes the form of a program which is capable of Z controlling execution of other programs and examining the Z internal state of other executing programs. These types of Z programs will be referred to as external debuggers since the Z debugger is not part of the executing program. Examples of Z this type of debugger include the adb and sdb debuggers Z provided with the UNIX operating system. Z Z Z One of the problems associated with developing programs Z in an environment with good external debuggers is that Z developed programs tend to have little or no internal Z instrumentation. This is usually not a problem for the Z developer since he is, or at least should be, intimately Z familiar with the internal organization, data structures, Z and control flow of the program being debugged. It is a Z serious problem for maintenance programmers, who are Z unlikely to have such familiarity with the program being Z maintained, modified, or ported to another environment. It Z is also a problem, even for the developer, when the program Z is moved to an environment with a primitive or unfamiliar Z debugger, or even no debugger. Z Z Z On the other hand, dbug is an example of an internal Z debugger. Because it requires internal instrumentation of a Z program, and its usage does not depend on any special Z capabilities of the execution environment, it is always Z available and will execute in any environment that the Z program itself will execute in. In addition, since it is a Z complete package with a specific user interface, all Z programs which use it will be provided with similar Z debugging capabilities. This is in sharp contrast to other Z Z Z __________ Z Z 1. UNIX is a trademark of AT&T Bell Laboratories. Z Z Z Z Z - 1 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z forms of internal instrumentation where each developer has Z their own, usually less capable, form of internal debugger. Z In summary, because dbug is an internal debugger it provides Z consistency across operating environments, and because it is Z available to all developers it provides consistency across Z all programs in the same environment. Z Z Z The dbug package imposes only a slight speed penalty on Z executing programs, typically much less than 10 percent, and Z a modest size penalty, typically 10 to 20 percent. By Z defining a specific C preprocessor symbol both of these can Z be reduced to zero with no changes required to the source Z code. Z Z Z The following list is a quick summary of the Z capabilities of the dbug package. Each capability can be Z individually enabled or disabled at the time a program is Z invoked by specifying the appropriate command line Z arguments. Z Z o Execution trace showing function level control Z flow in a semi-graphically manner using Z indentation to indicate nesting depth. Z Z o Output the values of all, or any subset of, key Z internal variables. Z Z o Limit actions to a specific set of named Z functions. Z Z o Limit function trace to a specified nesting depth. Z Z o Label each output line with source file name and Z line number. Z Z o Label each output line with name of current Z process. Z Z o Push or pop internal debugging state to allow Z execution with built in debugging defaults. Z Z o Redirect the debug output stream to standard Z output (stdout) or a named file. The default Z output stream is standard error (stderr). The Z redirection mechanism is completely independent of Z normal command line redirection to avoid output Z conflicts. Z Z Z Z Z Z - 2 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z PRIMITIVE DEBUGGING TECHNIQUES Z Z Z Internal instrumentation is already a familiar concept Z to most programmers, since it is usually the first debugging Z technique learned. Typically, "print statements" are Z inserted in the source code at interesting points, the code Z is recompiled and executed, and the resulting output is Z examined in an attempt to determine where the problem is. Z Z The procedure is iterative, with each iteration yielding Z more and more output, and hopefully the source of the Z problem is discovered before the output becomes too large to Z deal with or previously inserted statements need to be Z removed. Figure 1 is an example of this type of primitive Z debugging technique. Z Z Z Z #include Z Z main (argc, argv) Z int argc; Z char *argv[]; Z { Z printf ("argv[0] = %d\n", argv[0]); Z /* Z * Rest of program Z */ Z printf ("== done ==\n"); Z } Z Z Z Figure 1 Z Primitive Debugging Technique Z Z Z Z Z Z Eventually, and usually after at least several Z iterations, the problem will be found and corrected. At Z this point, the newly inserted print statements must be Z dealt with. One obvious solution is to simply delete them Z all. Beginners usually do this a few times until they have Z to repeat the entire process every time a new bug pops up. Z The second most obvious solution is to somehow disable the Z output, either through the source code comment facility, Z creation of a debug variable to be switched on or off, or by Z using the C preprocessor. Figure 2 is an example of all Z three techniques. Z Z Z Z - 3 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z Z Z #include Z Z int debug = 0; Z Z main (argc, argv) Z int argc; Z char *argv[]; Z { Z /* printf ("argv = %x\n", argv) */ Z if (debug) printf ("argv[0] = %d\n", argv[0]); Z /* Z * Rest of program Z */ Z #ifdef DEBUG Z printf ("== done ==\n"); Z #endif Z } Z Z Z Figure 2 Z Debug Disable Techniques Z Z Z Z Z Z Each technique has its advantages and disadvantages Z with respect to dynamic vs static activation, source code Z overhead, recompilation requirements, ease of use, program Z readability, etc. Overuse of the preprocessor solution Z quickly leads to problems with source code readability and Z maintainability when multiple #ifdef symbols are to be Z defined or undefined based on specific types of debug Z desired. The source code can be made slightly more readable Z by suitable indentation of the #ifdef arguments to match the Z indentation of the code, but not all C preprocessors allow Z this. The only requirement for the standard UNIX C Z preprocessor is for the '#' character to appear in the first Z column, but even this seems like an arbitrary and Z unreasonable restriction. Figure 3 is an example of this Z usage. Z Z Z Z Z Z Z Z Z Z Z Z - 4 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z Z Z #include Z Z main (argc, argv) Z int argc; Z char *argv[]; Z { Z # ifdef DEBUG Z printf ("argv[0] = %d\n", argv[0]); Z # endif Z /* Z * Rest of program Z */ Z # ifdef DEBUG Z printf ("== done ==\n"); Z # endif Z } Z Z Z Figure 3 Z More Readable Preprocessor Usage Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z - 5 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z FUNCTION TRACE EXAMPLE Z Z Z We will start off learning about the capabilities of Z the dbug package by using a simple minded program which Z computes the factorial of a number. In order to better Z demonstrate the function trace mechanism, this program is Z implemented recursively. Figure 4 is the main function for Z this factorial program. Z Z Z Z #include Z /* User programs should use */ Z #include "dbug.h" Z Z main (argc, argv) Z int argc; Z char *argv[]; Z { Z register int result, ix; Z extern int factorial (), atoi (); Z Z DBUG_ENTER ("main"); Z DBUG_PROCESS (argv[0]); Z for (ix = 1; ix < argc && argv[ix][0] == '-'; ix++) { Z switch (argv[ix][1]) { Z case '#': Z DBUG_PUSH (&(argv[ix][2])); Z break; Z } Z } Z for (; ix < argc; ix++) { Z DBUG_4 ("args", "argv[%d] = %s", ix, argv[ix]); Z result = factorial (atoi (argv[ix])); Z printf ("%d\n", result); Z } Z DBUG_RETURN (0); Z } Z Z Z Figure 4 Z Factorial Program Mainline Z Z Z Z Z Z The main function is responsible for processing any Z command line option arguments and then computing and Z printing the factorial of each non-option argument. Z Z Z Z - 6 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z First of all, notice that all of the debugger functions Z are implemented via preprocessor macros. This does not Z detract from the readability of the code and makes disabling Z all debug compilation trivial (a single preprocessor symbol, Z DBUG_OFF, forces the macro expansions to be null). Z Z Also notice the inclusion of the header file dbug.h Z from the local header file directory. (The version included Z here is the test version in the dbug source distribution Z directory). This file contains all the definitions for the Z debugger macros, which all have the form DBUG_XX...XX. Z Z Z The DBUG_ENTER macro informs that debugger that we have Z entered the function named main. It must be the very first Z "executable" line in a function, after all declarations and Z before any other executable line. The DBUG_PROCESS macro is Z generally used only once per program to inform the debugger Z what name the program was invoked with. The DBUG_PUSH macro Z modifies the current debugger state by saving the previous Z state and setting a new state based on the control string Z passed as its argument. The DBUG_4 macro is used to print Z the values of each argument for which a factorial is to be Z computed. The DBUG_RETURN macro tells the debugger that the Z end of the current function has been reached and returns a Z value to the calling function. All of these macros will be Z fully explained in subsequent sections. Z Z To use the debugger, the factorial program is invoked Z with a command line of the form: Z Z factorial -#d:t 1 2 3 Z Z The main function recognizes the "-#d:t" string as a Z debugger control string, and passes the debugger arguments Z ("d:t") to the dbug runtime support routines via the Z DBUG_PUSH macro. This particular string enables output from Z the DBUG_4 macro with the 'd' flag and enables function Z tracing with the 't' flag. The factorial function is then Z called three times, with the arguments "1", "2", and "3". Z Z Z Debug control strings consist of a header, the "-#", Z followed by a colon separated list of debugger arguments. Z Each debugger argument is a single character flag followed Z by an optional comma separated list of argments specific to Z the given flag. Some examples are: Z Z -#d:t:o Z -#d,in,out:f,main:F:L Z Z Z Z Z - 7 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z Note that previously enabled debugger actions can be Z disabled by the control string "-#". Z Z Z The definition of the factorial function, symbolized as Z "N!", is given by: Z Z N! = N * N-1 * ... 2 * 1 Z Z Figure 5 is the factorial function which implements this Z algorithm recursively. Note that this is not necessarily Z the best way to do factorials and error conditions are Z ignored completely. Z Z Z Z #include Z /* User programs should use */ Z #include "dbug.h" Z Z int factorial (value) Z register int value; Z { Z DBUG_ENTER ("factorial"); Z DBUG_3 ("find", "find %d factorial", value); Z if (value > 1) { Z value *= factorial (value - 1); Z } Z DBUG_3 ("result", "result is %d", value); Z DBUG_RETURN (value); Z } Z Z Z Figure 5 Z Factorial Function Z Z Z Z Z Z One advantage (some may not consider it so) to using Z the dbug package is that it strongly encourages fully Z structured coding with only one entry and one exit point in Z each function. Multiple exit points, such as early returns Z to escape a loop, may be used, but each such point requires Z the use of an appropriate DBUG_RETURN or DBUG_VOID_RETURN Z macro. Z Z Z To build the factorial program on a UNIX system, Z compile and link with the command: Z Z Z Z - 8 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z cc -o factorial main.c factorial.c -ldbug Z Z The "-ldbug" argument tells the loader to link in the Z runtime support modules for the dbug package. Executing the Z factorial program with a command of the form: Z Z factorial 1 2 3 4 5 Z Z generates the output shown in figure 6. Z Z Z Z 1 Z 2 Z 6 Z 24 Z 120 Z Z Z Figure 6 Z factorial 1 2 3 4 5 Z Z Z Z Z Z Function level tracing is enabled by passing the Z debugger the 't' flag in the debug control string. Figure 7 Z is the output resulting from the command "factorial - Z #t:o 3 2". Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z - 9 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z Z Z | >factorial Z | | >factorial Z | | factorial Z | | >factorial Z | | | >factorial Z | | | ' for the entry point and '<' for the exit point, Z connected by vertical bars to allow matching points to be Z easily found when separated by large distances. Z Z Z This trace output indicates that there was an initial Z call to factorial from main (to compute 2!), followed by a Z single recursive call to factorial to compute 1!. The main Z program then output the result for 2! and called the Z factorial function again with the second argument, 3. Z Factorial called itself recursively to compute 2! and 1!, Z then returned control to main, which output the value for 3! Z and exited. Z Z Z Note that there is no matching entry point "main>" for Z the return point "factorial Z | | find: find 3 factorial Z | | >factorial Z | | | find: find 2 factorial Z | | | >factorial Z | | | | find: find 1 factorial Z | | | | result: result is 1 Z | | | %s", stp, Z stp -> name); Z Z DBUG_SETJMP Used in place of the setjmp() function Z to first save the current debugger state Z and then execute the standard setjmp Z call. This allows to the debugger to Z restore it's state when the DBUG_LONGJMP Z macro is used to invoke the standard Z longjmp() call. Currently all instances Z of DBUG_SETJMP must occur within the Z same function and at the same function Z nesting level. Z Z EX: DBUG_SETJMP (env); Z Z DBUG_LONGJMP Used in place of the longjmp() function Z to first restore the previous debugger Z state at the time of the last Z DBUG_SETJMP and then execute the Z standard longjmp() call. Note that Z currently all DBUG_LONGJMP macros Z restore the state at the time of the Z last DBUG_SETJMP. It would be possible Z to maintain separate DBUG_SETJMP and Z DBUG_LONGJMP pairs by having the Z debugger runtime support module use the Z first argument to differentiate the Z pairs. Z Z EX: DBUG_LONGJMP (env,val); Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z - 17 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z DEBUG CONTROL STRING Z Z Z The debug control string is used to set the state of Z the debugger via the DBUG_PUSH macro. This section Z summarizes the currently available debugger options and the Z flag characters which enable or disable them. Argument Z lists enclosed in '[' and ']' are optional. Z Z Z d[,keywords] Enable output from macros with Z specified keywords. A null list of Z keywords implies that all keywords are Z selected. Z Z D[,time] Delay for specified time after each Z output line, to let output drain. Z Time is given in tenths of a second Z (value of 10 is one second). Default Z is zero. Z Z f[,functions] Limit debugger actions to the Z specified list of functions. A null Z list of functions implies that all Z functions are selected. Z Z F Mark each debugger output line with Z the name of the source file containing Z the macro causing the output. Z Z L Mark each debugger output line with Z the source file line number of the Z macro causing the output. Z Z n Mark each debugger output line with Z the current function nesting depth. Z Z N Sequentially number each debugger Z output line starting at 1. This is Z useful for reference purposes when Z debugger output is interspersed with Z program output. Z Z o[,file] Redirect the debugger output stream to Z the specified file. The default Z output stream is stderr. A null Z argument list causes output to be Z redirected to stdout. Z Z p[,processes] Limit debugger actions to the Z specified processes. A null list Z Z Z Z - 18 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z implies all processes. This is useful Z for processes which run child Z processes. Note that each debugger Z output line can be marked with the Z name of the current process via the Z 'P' flag. The process name must match Z the argument passed to the Z DBUG_PROCESS macro. Z Z P Mark each debugger output line with Z the name of the current process. Most Z useful when used with a process which Z runs child processes that are also Z being debugged. Note that the parent Z process must arrange for the debugger Z control string to be passed to the Z child processes. Z Z r Used in conjunction with the DBUG_PUSH Z macro to reset the current indentation Z level back to zero. Most useful with Z DBUG_PUSH macros used to temporarily Z alter the debugger state. Z Z t[,N] Enable function control flow tracing. Z The maximum nesting depth is specified Z by N, and defaults to 200. Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z - 19 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z HINTS AND MISCELLANEOUS Z Z Z One of the most useful capabilities of the dbug package Z is to compare the executions of a given program in two Z different environments. This is typically done by executing Z the program in the environment where it behaves properly and Z saving the debugger output in a reference file. The program Z is then run with identical inputs in the environment where Z it misbehaves and the output is again captured in a Z reference file. The two reference files can then be Z differentially compared to determine exactly where execution Z of the two processes diverges. Z Z Z A related usage is regression testing where the Z execution of a current version is compared against Z executions of previous versions. This is most useful when Z there are only minor changes. Z Z Z It is not difficult to modify an existing compiler to Z implement some of the functionality of the dbug package Z automatically, without source code changes to the program Z being debugged. In fact, such changes were implemented in a Z version of the Portable C Compiler by the author in less Z than a day. However, it is strongly encouraged that all Z newly developed code continue to use the debugger macros for Z the portability reasons noted earlier. The modified Z compiler should be used only for testing existing programs. Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z - 20 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z CAVEATS Z Z Z The dbug package works best with programs which have Z "line oriented" output, such as text processors, general Z purpose utilities, etc. It can be interfaced with screen Z oriented programs such as visual editors by redefining the Z appropriate macros to call special functions for displaying Z the debugger results. Of course, this caveat is not Z applicable if the debugger output is simply dumped into a Z file for post-execution examination. Z Z Z Programs which use memory allocation functions other Z than malloc will usually have problems using the standard Z dbug package. The most common problem is multiply allocated Z memory. Z Z Z Beware that some of the macros are not context Z independent. Some of them contain if statements with no Z else statements. In particular, the following code segment Z will not behave as expected: Z Z Z Z if (something_interesting) Z DBUG_2 ("ins", "something interesting"); Z else Z do_normal_processing (); Z Z Z Z The user code else will bind to the macro's if to produce Z code equivalent to: Z Z Z Z if (something_interesting) Z if (debugging_on) Z printf ("something interesting"); Z else Z do_normal_processing (); Z Z Z Z The author has received numerous suggestions for changes Z which would eliminate this danger, most based on obscure Z features of the language or preprocessor, but has rejected Z most of these for portability or efficiency reasons. (The Z author also considers it to be poor programming practice to Z Z Z Z - 21 - Z Z Z Z Z Z Z Z DBUG User Manual (preliminary) December 11, 1985 Z Z Z Z write code with any control flow statements that do not use Z braces.) However, if you absolutely must prevent this Z behavior try changing the macro definitions to something of Z the form: Z Z Z Z #define DBUG_2(keyword,format) \ Z do { \ Z if (_db_on_) { \ Z _db_printf_ (__LINE__, keyword, format); \ Z } \ Z } while (0) Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z - 22 - Z Z Z Z Z Z Z Z Z Z D B U G Z Z C Program Debugging Package Z Z by Z Z Fred Fish Z Z Z Z ABSTRACT Z Z Z Z This document introduces dbug, a macro based C debugging Z package which has proven to be a very flexible and useful Z tool for debugging, testing, and porting C programs. Z Z Z All of the features of the dbug package can be enabled Z or disabled dynamically at execution time. This means that Z production programs will run normally when debugging is not Z enabled, and eliminates the need to maintain two separate Z versions of a program. Z Z Z Many of the things easily accomplished with Z conventional debugging tools, such as symbolic debuggers, Z are difficult or impossible with this package, and vice Z versa. Thus the dbug package should not be thought of as a Z replacement or substitute for other debugging tools, but Z simply as a useful addition to the program development and Z maintenance environment. Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z STUNKYFLUFF set `sum user.t` if test 50553 != $1 then echo user.t: Checksum error. Is: $1, should be: 50553. fi echo ALL DONE BUNKY! exit 0