diff options
Diffstat (limited to 'dbug/user.r')
-rw-r--r-- | dbug/user.r | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/dbug/user.r b/dbug/user.r new file mode 100644 index 00000000000..e8321243962 --- /dev/null +++ b/dbug/user.r @@ -0,0 +1,937 @@ +.\" @(#)user.r 1.13 10/29/86 +.\" +.\" DBUG (Macro Debugger Package) nroff source +.\" +.\" nroff -mm user.r >user.t +.\" +.\" =================================================== +.\" +.\" === Some sort of black magic, but I forget... +.tr ~ +.\" === Hyphenation control (1 = on) +.\".nr Hy 1 +.\" === Force all first level headings to start on new page +.nr Ej 1 +.\" === Set for breaks after headings for levels 1-3 +.nr Hb 3 +.\" === Set for space after headings for levels 1-3 +.nr Hs 3 +.\" === Set standard indent for one/half inch +.nr Si 10 +.\" === Set page header +.PH "/DBUG User Manual//\*(DT/" +.\" === Set page footer +.PF "// - % - //" +.\" === Set page offset +.\".po 0.60i +.\" === Set line length +.\".ll 6.5i +.TL +D B U G +.P 0 +C Program Debugging Package +.P 0 +by +.AU "Fred Fish" +.AF "" +.SA 1 +.\" === All paragraphs indented. +.nr Pt 1 +.AS 1 +This document introduces +.I dbug , +a macro based C debugging +package which has proven to be a very flexible and useful tool +for debugging, testing, and porting C programs. + +.P +All of the features of the +.I dbug +package can be enabled or disabled dynamically at execution time. +This means that production programs will run normally when +debugging is not enabled, and eliminates the need to maintain two +separate versions of a program. + +.P +Many of the things easily accomplished with conventional debugging +tools, such as symbolic debuggers, are difficult or impossible with this +package, and vice versa. +Thus the +.I dbug +package should +.I not +be thought of as a replacement or substitute for +other debugging tools, but simply as a useful +.I addition +to the +program development and maintenance environment. + +.AE +.MT 4 +.SK +.B +INTRODUCTION +.R + +.P +Almost every program development environment worthy of the name +provides some sort of debugging facility. +Usually this takes the form of a program which is capable of +controlling execution of other programs and examining the internal +state of other executing programs. +These types of programs will be referred to as external debuggers +since the debugger is not part of the executing program. +Examples of this type of debugger include the +.B adb +and +.B sdb +debuggers provided with the +.B UNIX\*F +.FS +UNIX is a trademark of AT&T Bell Laboratories. +.FE +operating system. + +.P +One of the problems associated with developing programs in an environment +with good external debuggers is that developed programs tend to have +little or no internal instrumentation. +This is usually not a problem for the developer since he is, +or at least should be, intimately familiar with the internal organization, +data structures, and control flow of the program being debugged. +It is a serious problem for maintenance programmers, who +are unlikely to have such familiarity with the program being +maintained, modified, or ported to another environment. +It is also a problem, even for the developer, when the program is +moved to an environment with a primitive or unfamiliar debugger, +or even no debugger. + +.P +On the other hand, +.I dbug +is an example of an internal debugger. +Because it requires internal instrumentation of a program, +and its usage does not depend on any special capabilities of +the execution environment, it is always available and will +execute in any environment that the program itself will +execute in. +In addition, since it is a complete package with a specific +user interface, all programs which use it will be provided +with similar debugging capabilities. +This is in sharp contrast to other forms of internal instrumentation +where each developer has their own, usually less capable, form +of internal debugger. +In summary, +because +.I dbug +is an internal debugger it provides consistency across operating +environments, +and because it is available to all developers it provides +consistency across all programs in the same environment. + +.P +The +.I dbug +package imposes only a slight speed penalty on executing +programs, typically much less than 10 percent, and a modest size +penalty, typically 10 to 20 percent. +By defining a specific C preprocessor symbol both of these +can be reduced to zero with no changes required to the +source code. + +.P +The following list is a quick summary of the capabilities +of the +.I dbug +package. +Each capability can be individually enabled or disabled +at the time a program is invoked by specifying the appropriate +command line arguments. +.SP 1 +.ML o 1i +.LI +Execution trace showing function level control flow in a +semi-graphically manner using indentation to indicate nesting +depth. +.LI +Output the values of all, or any subset of, key internal variables. +.LI +Limit actions to a specific set of named functions. +.LI +Limit function trace to a specified nesting depth. +.LI +Label each output line with source file name and line number. +.LI +Label each output line with name of current process. +.LI +Push or pop internal debugging state to allow execution with +built in debugging defaults. +.LI +Redirect the debug output stream to standard output (stdout) +or a named file. +The default output stream is standard error (stderr). +The redirection mechanism is completely independent of +normal command line redirection to avoid output conflicts. +.LE + +.SK +.B +PRIMITIVE DEBUGGING TECHNIQUES +.R + +.P +Internal instrumentation is already a familiar concept +to most programmers, since it is usually the first debugging +technique learned. +Typically, "print\ statements" are inserted in the source +code at interesting points, the code is recompiled and executed, +and the resulting output is examined in an attempt to determine +where the problem is. + +The procedure is iterative, with each iteration yielding more +and more output, and hopefully the source of the problem is +discovered before the output becomes too large to deal with +or previously inserted statements need to be removed. +Figure 1 is an example of this type of primitive debugging +technique. +.DS I N +.SP 2 +.so example1.r +.SP 2 +.ll -5 +.ce +Figure 1 +.ce +Primitive Debugging Technique +.ll +5 +.SP 2 +.DE + +.P +Eventually, and usually after at least several iterations, the +problem will be found and corrected. +At this point, the newly inserted print statements must be +dealt with. +One obvious solution is to simply delete them all. +Beginners usually do this a few times until they have to +repeat the entire process every time a new bug pops up. +The second most obvious solution is to somehow disable +the output, either through the source code comment facility, +creation of a debug variable to be switched on or off, or by using the +C preprocessor. +Figure 2 is an example of all three techniques. +.DS I N +.SP 2 +.so example2.r +.SP 2 +.ll -5 +.ce +Figure 2 +.ce +Debug Disable Techniques +.ll +5 +.SP 2 +.DE + +.P +Each technique has its advantages and disadvantages with respect +to dynamic vs static activation, source code overhead, recompilation +requirements, ease of use, program readability, etc. +Overuse of the preprocessor solution quickly leads to problems with +source code readability and maintainability when multiple +.B #ifdef +symbols are to be defined or undefined based on specific types +of debug desired. +The source code can be made slightly more readable by suitable indentation +of the +.B #ifdef +arguments to match the indentation of the code, but +not all C preprocessors allow this. +The only requirement for the standard +.B UNIX +C preprocessor is for the '#' character to appear +in the first column, but even this seems +like an arbitrary and unreasonable restriction. +Figure 3 is an example of this usage. +.DS I N +.SP 2 +.so example3.r +.SP 2 +.ll -5 +.ce +Figure 3 +.ce +More Readable Preprocessor Usage +.ll +5 +.SP 2 +.DE + +.SK +.B +FUNCTION TRACE EXAMPLE +.R + +.P +We will start off learning about the capabilities of the +.I dbug +package by using a simple minded program which computes the +factorial of a number. +In order to better demonstrate the function trace mechanism, this +program is implemented recursively. +Figure 4 is the main function for this factorial program. +.DS I N +.SP 2 +.so main.r +.SP 2 +.ll -5 +.ce +Figure 4 +.ce +Factorial Program Mainline +.ll +5 +.SP 2 +.DE + +.P +The +.B main +function is responsible for processing any command line +option arguments and then computing and printing the factorial of +each non-option argument. +.P +First of all, notice that all of the debugger functions are implemented +via preprocessor macros. +This does not detract from the readability of the code and makes disabling +all debug compilation trivial (a single preprocessor symbol, +.B DBUG_OFF , +forces the macro expansions to be null). +.P +Also notice the inclusion of the header file +.B dbug.h +from the local header file directory. +(The version included here is the test version in the dbug source +distribution directory). +This file contains all the definitions for the debugger macros, which +all have the form +.B DBUG_XX...XX . + +.P +The +.B DBUG_ENTER +macro informs that debugger that we have entered the +function named +.B main . +It must be the very first "executable" line in a function, after +all declarations and before any other executable line. +The +.B DBUG_PROCESS +macro is generally used only once per program to +inform the debugger what name the program was invoked with. +The +.B DBUG_PUSH +macro modifies the current debugger state by +saving the previous state and setting a new state based on the +control string passed as its argument. +The +.B DBUG_PRINT +macro is used to print the values of each argument +for which a factorial is to be computed. +The +.B DBUG_RETURN +macro tells the debugger that the end of the current +function has been reached and returns a value to the calling +function. +All of these macros will be fully explained in subsequent sections. +.P +To use the debugger, the factorial program is invoked with a command +line of the form: +.DS CB N +factorial -#d:t 1 2 3 +.DE +The +.B main +function recognizes the "-#d:t" string as a debugger control +string, and passes the debugger arguments ("d:t") to the +.I dbug +runtime support routines via the +.B DBUG_PUSH +macro. +This particular string enables output from the +.B DBUG_PRINT +macro with the 'd' flag and enables function tracing with the 't' flag. +The factorial function is then called three times, with the arguments +"1", "2", and "3". +Note that the DBUG_PRINT takes exactly +.B two +arguments, with the second argument (a format string and list +of printable values) enclosed in parenthesis. +.P +Debug control strings consist of a header, the "-#", followed +by a colon separated list of debugger arguments. +Each debugger argument is a single character flag followed +by an optional comma separated list of arguments specific +to the given flag. +Some examples are: +.DS CB N +-#d:t:o +-#d,in,out:f,main:F:L +.DE +Note that previously enabled debugger actions can be disabled by the +control string "-#". + +.P +The definition of the factorial function, symbolized as "N!", is +given by: +.DS CB N +N! = N * N-1 * ... 2 * 1 +.DE +Figure 5 is the factorial function which implements this algorithm +recursively. +Note that this is not necessarily the best way to do factorials +and error conditions are ignored completely. +.DS I N +.SP 2 +.so factorial.r +.SP 2 +.ll -5 +.ce +Figure 5 +.ce +Factorial Function +.ll +5 +.SP 2 +.DE + +.P +One advantage (some may not consider it so) to using the +.I dbug +package is that it strongly encourages fully structured coding +with only one entry and one exit point in each function. +Multiple exit points, such as early returns to escape a loop, +may be used, but each such point requires the use of an +appropriate +.B DBUG_RETURN +or +.B DBUG_VOID_RETURN +macro. + +.P +To build the factorial program on a +.B UNIX +system, compile and +link with the command: +.DS CB N +cc -o factorial main.c factorial.c -ldbug +.DE +The "-ldbug" argument tells the loader to link in the +runtime support modules for the +.I dbug +package. +Executing the factorial program with a command of the form: +.DS CB N +factorial 1 2 3 4 5 +.DE +generates the output shown in figure 6. +.DS I N +.SP 2 +.so output1.r +.SP 2 +.ll -5 +.ce +Figure 6 +.ce +factorial 1 2 3 4 5 +.ll +5 +.SP 2 +.DE + +.P +Function level tracing is enabled by passing the debugger +the 't' flag in the debug control string. +Figure 7 is the output resulting from the command +"factorial\ -#t:o\ 3\ 2". +.DS I N +.SP 2 +.so output2.r +.SP 2 +.ll -5 +.ce +Figure 7 +.ce +factorial -#t:o 3 2 +.ll +5 +.SP 2 +.DE + +.P +Each entry to or return from a function is indicated by '>' for the +entry point and '<' for the exit point, connected by +vertical bars to allow matching points to be easily found +when separated by large distances. + +.P +This trace output indicates that there was an initial call +to factorial from main (to compute 2!), followed by +a single recursive call to factorial to compute 1!. +The main program then output the result for 2! and called the +factorial function again with the second argument, 3. +Factorial called itself recursively to compute 2! and 1!, then +returned control to main, which output the value for 3! and exited. + +.P +Note that there is no matching entry point "main>" for the +return point "<main" because at the time the +.B DBUG_ENTER +macro was reached in main, tracing was not enabled yet. +It was only after the macro +.B DBUG_PUSH +was executing that tracing became enabled. +This implies that the argument list should be processed as early as +possible since all code preceding the first call to +.B DBUG_PUSH +is +essentially invisible to +.B dbug +(this can be worked around by +inserting a temporary +.B DBUG_PUSH(argv[1]) +immediately after the +.B DBUG_ENTER("main") +macro. + +.P +One last note, +the trace output normally comes out on the standard error. +Since the factorial program prints its result on the standard +output, there is the possibility of the output on the terminal +being scrambled if the two streams are not synchronized. +Thus the debugger is told to write its output on the standard +output instead, via the 'o' flag character. +Note that no 'o' implies the default (standard error), a 'o' +with no arguments means standard output, and a 'o' +with an argument means used the named file. +I.E, "factorial\ -#t:o,logfile\ 3\ 2" would write the trace +output in "logfile". +Because of +.B UNIX +implementation details, programs usually run +faster when writing to stdout rather than stderr, though this +is not a prime consideration in this example. + +.SK +.B +USE OF DBUG_PRINT MACRO +.R + +.P +The mechanism used to produce "printf" style output is the +.B DBUG_PRINT +macro. + +.P +To allow selection of output from specific macros, the first argument +to every +.B DBUG_PRINT +macro is a +.I dbug +keyword. +When this keyword appears in the argument list of the 'd' flag in +a debug control string, as in "-#d,keyword1,keyword2,...:t", +output from the corresponding macro is enabled. +The default when there is no 'd' flag in the control string is to +enable output from all +.B DBUG_PRINT +macros. + +.P +Typically, a program will be run once, with no keywords specified, +to determine what keywords are significant for the current problem +(the keywords are printed in the macro output line). +Then the program will be run again, with the desired keywords, +to examine only specific areas of interest. + +.P +The second argument to a +.B DBUG_PRINT +macro is a standard printf style +format string and one or more arguments to print, all +enclosed in parenthesis so that they collectively become a single macro +argument. +This is how variable numbers of printf arguments are supported. +Also note that no explicit newline is required at the end of the format string. +As a matter of style, two or three small +.B DBUG_PRINT +macros are preferable +to a single macro with a huge format string. +Figure 8 shows the output for default tracing and debug. +.DS I N +.SP 2 +.so output3.r +.SP 2 +.ll -5 +.ce +Figure 8 +.ce +factorial -#d:t:o 3 +.ll +5 +.SP 2 +.DE + +.P +The output from the +.B DBUG_PRINT +macro is indented to match the trace output +for the function in which the macro occurs. +When debugging is enabled, but not trace, the output starts at the left +margin, without indentation. + +.P +To demonstrate selection of specific macros for output, figure +9 shows the result when the factorial program is invoked with +the debug control string "-#d,result:o". +.DS I N +.SP 2 +.so output4.r +.SP 2 +.ll -5 +.ce +Figure 9 +.ce +factorial -#d,result:o 4 +.ll +5 +.SP 2 +.DE + +.P +It is sometimes desirable to restrict debugging and trace actions +to a specific function or list of functions. +This is accomplished with the 'f' flag character in the debug +control string. +Figure 10 is the output of the factorial program when run with the +control string "-#d:f,factorial:F:L:o". +The 'F' flag enables printing of the source file name and the 'L' +flag enables printing of the source file line number. +.DS I N +.SP 2 +.so output5.r +.SP 2 +.ll -5 +.ce +Figure 10 +.ce +factorial -#d:f,factorial:F:L:o 3 +.ll +5 +.SP 2 +.DE + +.P +The output in figure 10 shows that the "find" macro is in file +"factorial.c" at source line 8 and the "result" macro is in the same +file at source line 12. + +.SK +.B +SUMMARY OF MACROS +.R + +.P +This section summarizes the usage of all currently defined macros +in the +.I dbug +package. +The macros definitions are found in the user include file +.B dbug.h +from the standard include directory. + +.SP 2 +.BL 20 +.LI DBUG_ENTER\ +Used to tell the runtime support module the name of the function +being entered. +The argument must be of type "pointer to character". +The +DBUG_ENTER +macro must precede all executable lines in the +function just entered, and must come after all local declarations. +Each +DBUG_ENTER +macro must have a matching +DBUG_RETURN +or +DBUG_VOID_RETURN +macro +at the function exit points. +DBUG_ENTER +macros used without a matching +DBUG_RETURN +or +DBUG_VOID_RETURN +macro +will cause warning messages from the +.I dbug +package runtime support module. +.SP 1 +EX:\ DBUG_ENTER\ ("main"); +.SP 1 +.LI DBUG_RETURN\ +Used at each exit point of a function containing a +DBUG_ENTER +macro +at the entry point. +The argument is the value to return. +Functions which return no value (void) should use the +DBUG_VOID_RETURN +macro. +It +is an error to have a +DBUG_RETURN +or +DBUG_VOID_RETURN +macro in a function +which has no matching +DBUG_ENTER +macro, and the compiler will complain +if the macros are actually used (expanded). +.SP 1 +EX:\ DBUG_RETURN\ (value); +.br +EX:\ DBUG_VOID_RETURN; +.SP 1 +.LI DBUG_PROCESS\ +Used to name the current process being executed. +A typical argument for this macro is "argv[0]", though +it will be perfectly happy with any other string. +.SP 1 +EX:\ DBUG_PROCESS\ (argv[0]); +.SP 1 +.LI DBUG_PUSH\ +Sets a new debugger state by pushing the current +.B dbug +state onto an +internal stack and setting up the new state using the debug control +string passed as the macro argument. +The most common usage is to set the state specified by a debug +control string retrieved from the argument list. +Note that the leading "-#" in a debug control string specified +as a command line argument must +.B not +be passed as part of the macro argument. +The proper usage is to pass a pointer to the first character +.B after +the "-#" string. +.SP 1 +EX:\ DBUG_PUSH\ (\&(argv[i][2])); +.br +EX:\ DBUG_PUSH\ ("d:t"); +.br +EX:\ DBUG_PUSH\ (""); +.SP 1 +.LI DBUG_POP\ +Restores the previous debugger state by popping the state stack. +Attempting to pop more states than pushed will be ignored and no +warning will be given. +The +DBUG_POP +macro has no arguments. +.SP 1 +EX:\ DBUG_POP\ (); +.SP 1 +.LI DBUG_FILE\ +The +DBUG_FILE +macro is used to do explicit I/O on the debug output +stream. +It is used in the same manner as the symbols "stdout" and "stderr" +in the standard I/O package. +.SP 1 +EX:\ fprintf\ (DBUG_FILE,\ "Doing my own I/O!\n"); +.SP 1 +.LI DBUG_EXECUTE\ +The DBUG_EXECUTE macro is used to execute any arbitrary C code. +The first argument is the debug keyword, used to trigger execution +of the code specified as the second argument. +This macro must be used cautiously because, like the +DBUG_PRINT +macro, +it is automatically selected by default whenever the 'd' flag has +no argument list (I.E., a "-#d:t" control string). +.SP 1 +EX:\ DBUG_EXECUTE\ ("abort",\ abort\ ()); +.SP 1 +.LI DBUG_N\ +These macros, where N is in the range 2-5, are currently obsolete +and will be removed in a future release. +Use the new DBUG_PRINT macro. +.LI DBUG_PRINT\ +Used to do printing via the "fprintf" library function on the +current debug stream, +DBUG_FILE. +The first argument is a debug keyword, the second is a format string +and the corresponding argument list. +Note that the format string and argument list are all one macro argument +and +.B must +be enclosed in parenthesis. +.SP 1 +EX:\ DBUG_PRINT\ ("eof",\ ("end\ of\ file\ found")); +.br +EX:\ DBUG_PRINT\ ("type",\ ("type\ is\ %x", type)); +.br +EX:\ DBUG_PRINT\ ("stp",\ ("%x\ ->\ %s", stp, stp\ ->\ name)); +.LI DBUG_SETJMP\ +Used in place of the setjmp() function to first save the current +debugger state and then execute the standard setjmp call. +This allows to the debugger to restore it's state when the +DBUG_LONGJMP macro is used to invoke the standard longjmp() call. +Currently all instances of DBUG_SETJMP must occur within the +same function and at the same function nesting level. +.SP 1 +EX:\ DBUG_SETJMP\ (env); +.LI DBUG_LONGJMP\ +Used in place of the longjmp() function to first restore the +previous debugger state at the time of the last DBUG_SETJMP +and then execute the standard longjmp() call. +Note that currently all DBUG_LONGJMP macros restore the state +at the time of the last DBUG_SETJMP. +It would be possible to maintain separate DBUG_SETJMP and DBUG_LONGJMP +pairs by having the debugger runtime support module use the first +argument to differentiate the pairs. +.SP 1 +EX:\ DBUG_LONGJMP\ (env,val); +.LE + +.SK +.B +DEBUG CONTROL STRING +.R + +.P +The debug control string is used to set the state of the debugger +via the +.B DBUG_PUSH +macro. +This section summarizes the currently available debugger options +and the flag characters which enable or disable them. +Argument lists enclosed in '[' and ']' are optional. +.SP 2 +.BL 22 +.LI d[,keywords] +Enable output from macros with specified keywords. +A null list of keywords implies that all keywords are selected. +.LI D[,time] +Delay for specified time after each output line, to let output drain. +Time is given in tenths of a second (value of 10 is one second). +Default is zero. +.LI f[,functions] +Limit debugger actions to the specified list of functions. +A null list of functions implies that all functions are selected. +.LI F +Mark each debugger output line with the name of the source file +containing the macro causing the output. +.LI L +Mark each debugger output line with the source file line number of +the macro causing the output. +.LI n +Mark each debugger output line with the current function nesting depth. +.LI N +Sequentially number each debugger output line starting at 1. +This is useful for reference purposes when debugger output is +interspersed with program output. +.LI o[,file] +Redirect the debugger output stream to the specified file. +The default output stream is stderr. +A null argument list causes output to be redirected to stdout. +.LI p[,processes] +Limit debugger actions to the specified processes. +A null list implies all processes. +This is useful for processes which run child processes. +Note that each debugger output line can be marked with the name of +the current process via the 'P' flag. +The process name must match the argument passed to the +.B DBUG_PROCESS +macro. +.LI P +Mark each debugger output line with the name of the current process. +Most useful when used with a process which runs child processes that +are also being debugged. +Note that the parent process must arrange for the debugger control +string to be passed to the child processes. +.LI r +Used in conjunction with the +.B DBUG_PUSH +macro to reset the current +indentation level back to zero. +Most useful with +.B DBUG_PUSH +macros used to temporarily alter the +debugger state. +.LI t[,N] +Enable function control flow tracing. +The maximum nesting depth is specified by N, and defaults to +200. +.LE +.SK +.B +HINTS AND MISCELLANEOUS +.R + +.P +One of the most useful capabilities of the +.I dbug +package is to compare the executions of a given program in two +different environments. +This is typically done by executing the program in the environment +where it behaves properly and saving the debugger output in a +reference file. +The program is then run with identical inputs in the environment where +it misbehaves and the output is again captured in a reference file. +The two reference files can then be differentially compared to +determine exactly where execution of the two processes diverges. + +.P +A related usage is regression testing where the execution of a current +version is compared against executions of previous versions. +This is most useful when there are only minor changes. + +.P +It is not difficult to modify an existing compiler to implement +some of the functionality of the +.I dbug +package automatically, without source code changes to the +program being debugged. +In fact, such changes were implemented in a version of the +Portable C Compiler by the author in less than a day. +However, it is strongly encouraged that all newly +developed code continue to use the debugger macros +for the portability reasons noted earlier. +The modified compiler should be used only for testing existing +programs. + +.SK +.B +CAVEATS +.R + +.P +The +.I dbug +package works best with programs which have "line\ oriented" +output, such as text processors, general purpose utilities, etc. +It can be interfaced with screen oriented programs such as +visual editors by redefining the appropriate macros to call +special functions for displaying the debugger results. +Of course, this caveat is not applicable if the debugger output +is simply dumped into a file for post-execution examination. + +.P +Programs which use memory allocation functions other than +.B malloc +will usually have problems using the standard +.I dbug +package. +The most common problem is multiply allocated memory. +.SP 2 +.\" .DE nroff dident like this. davida 900108 +.CS + + |