Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83; site unisoft.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!godot!harvard!seismo!hao!hplabs!intelca!qantel!dual!unisoft!fnf From: fnf@unisoft.UUCP (Fred Fish) Newsgroups: net.sources Subject: C debugging package (1 of 2 reposted) Message-ID: <376@unisoft.UUCP> Date: Thu, 3-Jan-85 13:20:19 EST Article-I.D.: unisoft.376 Posted: Thu Jan 3 13:20:19 1985 Date-Received: Sun, 6-Jan-85 00:38:56 EST Organization: UniSoft Corp., Berkeley Lines: 1416 After receiving postings on a couple bug fixes in "dbug", several people have sent mail saying that the original source posting never reached their site. So here is another, up-to-date posting. #--------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 Makefile: sed 's/^Z//' >Makefile <<\STUNKYFLUFF Z# Z# FILE Z# Z# Makefile Makefile for dbug package Z# Z# SCCS ID Z# Z# @(#)Makefile 1.5 1/3/85 Z# Z# DESCRIPTION Z# Z# Makefile for the dbug package (under UNIX system V). Z# Z# Interesting things to make are: Z# Z# lib => Makes the runtime support library in the Z# current directory. Z# Z# lintlib => Makes the lint library in the current directory. Z# Z# install => Install pieces from current directory to Z# where they belong. Z# Z# doc => Makes the documentation in the current Z# directory. Z# Z# clean => Remove objects, temporary files, etc from Z# current directory. Z# Z# superclean => Remove everything except sccs source files. Z# Uses interactive remove for verification. Z ZAR = ar ZRM = rm ZCFLAGS = -O ZINSTALL = install -o -s -f ZINC = /usr/include ZLIB = /usr/lib ZLLIB = /usr/lib ZLINT1 = /usr/lib/lint1 ZRANLIB = ranlib Z Z.SUFFIXES: .r .r~ .c .c~ Z Z.c~.c: Z $(GET) $(GFLAGS) -p $< >$*.c Z Z.r~.r: Z $(GET) $(GFLAGS) -p $< >$*.r Z Z.c~.r: Z $(GET) $(GFLAGS) -p $< >$*.c Z sed "s/\\\/\\\e/" <$*.c >$*.r Z $(RM) -f $*.c Z Z.c.r: Z sed "s/\\\/\\\e/" <$< >$*.r Z ZEXAMPLES = example1.r example2.r example3.r ZOUTPUTS = output1.r output2.r output3.r output4.r output5.r Z ZNROFF_INC = main.r factorial.r $(OUTPUTS) $(EXAMPLES) Z Z Z# Z# The default thing to do is remake the local runtime support Z# library, local lint library, and local documentation as Z# necessary. Z# Z Zall : lib lintlib doc Z Zlib : libdbug.a Z Zlintlib : llib-ldbug.ln Z Zdoc : factorial user.t Z Z# Z# Make the local runtime support library "libdbug.a" from Z# sources. Z# Z Zlibdbug.a : dbug.o Z $(AR) rv $@ $? Z $(RANLIB) $@ Z Z# Z# Clean up the local directory. Z# Z Zclean : Z $(RM) -f *.o *.BAK nohup.out factorial $(NROFF_INC) Z Zsuperclean : Z $(RM) -i ?[!.]* Z Z# Z# Install the new header and library files. Z# Z Zinstall : $(INC)/dbug.h $(LIB)/libdbug.a $(LLIB)/llib-ldbug.ln Z Z$(INC)/dbug.h : dbug.h Z $(INSTALL) $(INC) $? Z Z$(LIB)/libdbug.a : libdbug.a Z $(INSTALL) $(LLIB) $? Z Z$(LLIB)/llib-ldbug.ln : llib-ldbug.ln Z $(INSTALL) $(LLIB) $? Z Z# Z# Make the local lint library. Z# Z Zllib-ldbug.ln : llib-ldbug.c Z $(CC) -E -C -Dlint $? | $(LINT1) -vx -Hhlint >$@ Z $(RM) -f hlint Z Z# Z# Make the test/example program "factorial". Z# Z# Note that the objects depend on the LOCAL dbug.h file and Z# the compilations are set up to find dbug.h in the current Z# directory even though the sources have "#include ". Z# This allows the examples to look like the code a user would Z# write but still be used as test cases for new versions Z# of dbug. Z Zfactorial : main.o factorial.o libdbug.a Z $(CC) -o $@ main.o factorial.o libdbug.a Z Zmain.o : main.c dbug.h Z $(CC) $(CFLAGS) -c -I. main.c Z Zfactorial.o : factorial.c dbug.h Z $(CC) $(CFLAGS) -c -I. factorial.c Z Z Z# Z# Rebuild the documentation Z# Z Zuser.t : user.r $(NROFF_INC) Z nroff -cm user.r >$@ Z Z# Z# Run the factorial program to produce the sample outputs. Z# Z Zoutput1.r: factorial Z factorial 1 2 3 4 5 >$@ Z Zoutput2.r: factorial Z factorial -#t:o 2 3 >$@ Z Zoutput3.r: factorial Z factorial -#d:t:o 3 >$@ Z Zoutput4.r: factorial Z factorial -#d,result:o 4 >$@ Z Zoutput5.r: factorial Z factorial -#d:f,factorial:F:L:o 3 >$@ Z Z# Z# All files included by user.r depend on user.r, thus Z# forcing them to be remade if user.r changes. Z# Z Z$(NROFF_INC) : user.r STUNKYFLUFF set `sum Makefile` if test 31870 != $1 then echo Makefile: Checksum error. Is: $1, should be: 31870. fi # # echo Extracting dbug.c: sed 's/^Z//' >dbug.c <<\STUNKYFLUFF Z/************************************************************************ Z * * Z * Copyright (c) 1984, Fred Fish * Z * All Rights Reserved * Z * * Z * This software and/or documentation is released into the * Z * public domain for personal, non-commercial use only. * Z * Limited rights to use, modify, and redistribute are hereby * Z * granted for non-commercial purposes, provided that all * Z * copyright notices remain intact and all changes are clearly * Z * documented. The author makes no warranty of any kind with * Z * respect to this product and explicitly disclaims any implied * Z * warranties of merchantability or fitness for any particular * Z * purpose. * Z * * Z ************************************************************************ Z */ Z Z Z/* Z * FILE Z * Z * dbug.c runtime support routines for dbug package Z * Z * SCCS Z * Z * @(#)dbug.c 1.3 1/3/85 Z * Z * DESCRIPTION Z * Z * These are the runtime support routines for the dbug package. Z * The dbug package has two main components; the user include Z * file containing various macro definitions, and the runtime Z * support routines which are called from the macro expansions. Z * Z * Externally visible functions in the runtime support module Z * use the naming convention pattern "_db_xx...xx_", thus Z * they are unlikely to collide with user defined function names. Z * Z * AUTHOR Z * Z * Fred Fish Z * (Currently at UniSoft Systems, Berkeley Ca.) Z * Z */ Z Z Z#include Z Z/* Z * Manifest constants that should not require any changes. Z */ Z Z#define FALSE 0 /* Boolean FALSE */ Z#define TRUE 1 /* Boolean TRUE */ Z#define EOS '\000' /* End Of String marker */ Z Z/* Z * Manifest constants which may be "tuned" if desired. Z */ Z Z#define PRINTBUF 1024 /* Print buffer size */ Z#define INDENT 4 /* Indentation per trace level */ Z#define MAXDEPTH 200 /* Maximum trace depth default */ Z Z/* Z * The following flags are used to determine which Z * capabilities the user has enabled with the state Z * push macro. Z */ Z Z#define TRACE_ON 000001 /* Trace enabled */ Z#define DEBUG_ON 000002 /* Debug enabled */ Z#define FILE_ON 000004 /* File name print enabled */ Z#define LINE_ON 000010 /* Line number print enabled */ Z#define DEPTH_ON 000020 /* Function nest level print enabled */ Z#define PROCESS_ON 000040 /* Process name print enabled */ Z Z#define TRACING (stack -> flags & TRACE_ON) Z#define DEBUGGING (stack -> flags & DEBUG_ON) Z#define STREQ(a,b) (strcmp(a,b) == 0) Z Z/* Z * This doesn't seem to work as a typedef for most compilers. Z */ Z Z#define VOID void Z Z/* Z * Make it easy to change storage classes if necessary. Z */ Z Z#define LOCAL static /* Names not needed by outside world */ Z#define IMPORT extern /* Names defined externally */ Z#define EXPORT /* Allocated here, available globally */ Z#define AUTO auto /* Names to be allocated on stack */ Z#define REGISTER register /* Names to be placed in registers */ Z Z/* Z * Variables which are available externally but should only Z * be accessed via the macro package facilities. Z */ Z ZEXPORT FILE *_db_fp_ = stderr; /* Output stream, default stderr */ ZEXPORT char *_db_process_ = "dbug"; /* Pointer to process name; argv[0] */ ZEXPORT int _db_on_ = FALSE; /* TRUE if debugging currently on */ Z Z/* Z * Externally supplied functions. Z */ Z ZIMPORT int fprintf (); /* Formatted print on file */ ZIMPORT int fflush (); /* Flush output for stream */ ZIMPORT char *strcpy (); /* Copy strings around */ ZIMPORT int strlen (); /* Find length of string */ ZIMPORT char *malloc (); /* Allocate memory */ ZIMPORT int atoi (); /* Convert ascii to integer */ Z Z Z/* Z * The user may specify a list of functions to trace or Z * debug. These lists are kept in a linear linked list, Z * a very simple implementation. Z */ Z Zstruct link { Z char *string; /* Pointer to link's contents */ Z struct link *next_link; /* Pointer to the next link */ Z}; Z Z Z/* Z * Debugging states can be pushed or popped off of a Z * stack which is implemented as a linked list. Note Z * that the head of the list is the current state and the Z * stack is pushed by adding a new state to the head of the Z * list or popped by removing the first link. Z */ Z Zstruct state { Z int flags; /* Current state flags */ Z int maxdepth; /* Current maximum trace depth */ Z int level; /* Current function nesting level */ Z FILE *out_file; /* Current output stream */ Z struct link *functions; /* List of functions */ Z struct link *keywords; /* List of debug keywords */ Z struct link *processes; /* List of process names */ Z struct state *next_state; /* Next state in the list */ Z}; Z ZLOCAL struct state *stack = NULL; /* Linked list of stacked states */ Z Z/* Z * Local variables not seen by user. Z */ Z ZLOCAL char *func = "?func"; /* Name of current user function */ ZLOCAL char *file = "?file"; /* Name of current user file */ ZLOCAL int init_done = FALSE; /* Set to TRUE when initialization done */ Z ZLOCAL struct link *ListParse (); ZLOCAL char *StrDup (); /* Make a fresh copy of a string */ ZLOCAL VOID OpenFile (); /* Open debug output stream */ ZLOCAL VOID CloseFile (); /* Close debug output stream */ ZLOCAL VOID PushState (); /* Push current debug state */ ZLOCAL char *DbugMalloc (); /* Allocate memory for runtime support */ ZLOCAL char *strtok (); /* Supplied in Sys V runtime environ */ Z Z/* Z * Miscellaneous printf format strings. Z */ Z Z#define MSG1 "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n" Z#define MSG2 "%s: can't open debug output stream \"%s\": " Z#define MSG3 "%s: can't close debug file: " Z#define MSG4 "%s: debugger aborting because %s\n" Z Z Z/* Z * FUNCTION Z * Z * _db_push_ push current debugger state and set up new one Z * Z * SYNOPSIS Z * Z * VOID _db_push_ (control) Z * char *control; Z * Z * DESCRIPTION Z * Z * Given pointer to a debug control string in "control", pushes Z * the current debug state, parses the control string, and sets Z * up a new debug state. Z * Z * The only attribute of the new state inherited from the previous Z * state is the current function nesting level. This can be Z * overridden by using the "r" flag in the control string. Z * Z * The debug control string is a sequence of colon separated fields Z * as follows: Z * Z * ::...: Z * Z * Each field consists of a mandatory flag character followed by Z * an optional "," and comma separated list of modifiers: Z * Z * flag[,modifier,modifier,...,modifier] Z * Z * The currently recognized flag characters are: Z * Z * d Enable output from DBUG_ macros for Z * for the current state. May be followed Z * by a list of keywords which selects output Z * only for the DBUG macros with that keyword. Z * A null list of keywords implies output for Z * all macros. Z * Z * f Limit debugging and/or tracing to the Z * list of named functions. Note that a null Z * list will disable all functions. The Z * appropriate "d" or "t" flags must still Z * be given, this flag only limits their Z * actions if they are enabled. Z * Z * F Identify the source file name for each Z * line of debug or trace output. Z * Z * L Identify the source file line number for Z * each line of debug or trace output. Z * Z * n Print the current function nesting depth for Z * each line of debug or trace output. Z * Z * p Limit debugger actions to specified processes. Z * A process must be identified with the Z * DBUG_PROCESS macro and match one in the list Z * for debugger actions to occur. Z * Z * P Print the current process name for each Z * line of debug or trace output. Z * Z * r When pushing a new state, do not inherit Z * the previous state's function nesting level. Z * Useful when the output is to start at the Z * left margin. Z * Z * t Enable function call/exit trace lines. Z * May be followed by a list (containing only Z * one modifier) giving a numeric maximum Z * trace level, beyond which no output will Z * occur for either debugging or tracing Z * macros. The default is a compile time Z * option. Z * Z * Some examples of debug control strings which might appear Z * on a shell command line (the "-#" is typically used to Z * introduce a control string to an application program) are: Z * Z * -#d:t Z * -#d:f,main,subr1:F:L:t,20 Z * -#d,input,output,files:n Z * Z */ Z Z ZVOID _db_push_ (control) Zchar *control; Z{ Z REGISTER char *scan; Z REGISTER struct link *temp; Z Z control = StrDup (control); Z PushState (); Z scan = strtok (control, ":"); Z for (; scan != NULL; scan = strtok (NULL, ":")) { Z switch (*scan++) { Z case 'd': Z _db_on_ = TRUE; Z stack -> flags |= DEBUG_ON; Z if (*scan++ == ',') { Z stack -> keywords = ListParse (scan); Z } Z break; Z case 'f': Z if (*scan++ == ',') { Z stack -> functions = ListParse (scan); Z } Z break; Z case 'F': Z stack -> flags |= FILE_ON; Z break; Z case 'L': Z stack -> flags |= LINE_ON; Z break; Z case 'n': Z stack -> flags |= DEPTH_ON; Z break; Z case 'o': Z if (*scan++ == ',') { Z temp = ListParse (scan); Z OpenFile (temp -> string); Z FreeList (temp); Z } else { Z OpenFile ("-"); Z } Z break; Z case 'p': Z if (*scan++ == ',') { Z stack -> processes = ListParse (scan); Z } Z break; Z case 'P': Z stack -> flags |= PROCESS_ON; Z break; Z case 'r': Z stack -> level = 0; Z break; Z case 't': Z stack -> flags |= TRACE_ON; Z if (*scan++ == ',') { Z temp = ListParse (scan); Z stack -> maxdepth = atoi (temp -> string); Z FreeList (temp); Z } Z break; Z } Z } Z free (control); Z} Z Z Z Z/* Z * FUNCTION Z * Z * _db_pop_ pop the debug stack Z * Z * DESCRIPTION Z * Z * Pops the debug stack, returning the debug state to its Z * condition prior to the most recent _db_push_ invocation. Z * Note that the pop will fail if it would remove the last Z * valid state from the stack. This prevents user errors Z * in the push/pop sequence from screwing up the debugger. Z * Maybe there should be some kind of warning printed if the Z * user tries to pop too many states. Z * Z */ Z ZVOID _db_pop_ () Z{ Z REGISTER struct state *discard; Z Z discard = stack; Z if (discard != NULL && discard -> next_state != NULL) { Z stack = discard -> next_state; Z _db_fp_ = stack -> out_file; Z if (discard -> keywords != NULL) { Z FreeList (discard -> keywords); Z } Z if (discard -> functions != NULL) { Z FreeList (discard -> functions); Z } Z if (discard -> processes != NULL) { Z FreeList (discard -> processes); Z } Z CloseFile (discard -> out_file); Z free (discard); Z } Z} Z Z Z/* Z * FUNCTION Z * Z * _db_enter_ process entry point to user function Z * Z * SYNOPSIS Z * Z * VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_) Z * char *_func_; points to current function name Z * char *_file_; points to current file name Z * int _line_; called from source line number Z * char **_sfunc_; save previous _func_ Z * char **_sfile_; save previous _file_ Z * int *_slevel_; save previous nesting level Z * Z * DESCRIPTION Z * Z * Called at the beginning of each user function to tell Z * the debugger that a new function has been entered. Z * Note that the pointers to the previous user function Z * name and previous user file name are stored on the Z * caller's stack (this is why the ENTER macro must be Z * the first "executable" code in a function, since it Z * allocates these storage locations). The previous nesting Z * level is also stored on the callers stack for internal Z * self consistency checks. Z * Z * Also prints a trace line if tracing is enabled and Z * increments the current function nesting depth. Z * Z * Note that this mechanism allows the debugger to know Z * what the current user function is at all times, without Z * maintaining an internal stack for the function names. Z * Z */ Z ZVOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_) Zchar *_func_; Zchar *_file_; Zint _line_; Zchar **_sfunc_; Zchar **_sfile_; Zint *_slevel_; Z{ Z if (!init_done) { Z _db_push_ (""); Z } Z *_sfunc_ = func; Z *_sfile_ = file; Z func = _func_; Z file = _file_; Z stack -> level += INDENT; Z *_slevel_ = stack -> level; Z if (DoTrace ()) { Z DoPrefix (_line_); Z Indent (stack -> level); Z fprintf (_db_fp_, ">%s\n", func); Z fflush (_db_fp_); Z } Z} Z Z Z/* Z * FUNCTION Z * Z * _db_return_ process exit from user function Z * Z * SYNOPSIS Z * Z * VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_) Z * int _line_; current source line number Z * char **_sfunc_; where previous _func_ is to be retrieved Z * char **_sfile_; where previous _file_ is to be retrieved Z * int *_slevel_; where previous level was stashed Z * Z * DESCRIPTION Z * Z * Called just before user function executes an explicit or implicit Z * return. Prints a trace line if trace is enabled, decrements Z * the current nesting level, and restores the current function and Z * file names from the defunct function's stack. Z * Z */ Z ZVOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_) Zint _line_; Zchar **_sfunc_; Zchar **_sfile_; Zint *_slevel_; Z{ Z if (!init_done) { Z _db_push_ (""); Z } Z if (stack -> level != *_slevel_ && (TRACING || DEBUGGING)) { Z fprintf (_db_fp_, MSG1, _db_process_, func); Z fflush (_db_fp_); Z } else if (DoTrace ()) { Z DoPrefix (_line_); Z Indent (stack -> level); Z fprintf (_db_fp_, "<%s\n", func); Z fflush (_db_fp_); Z } Z stack -> level = *_slevel_ - INDENT; Z func = *_sfunc_; Z file = *_sfile_; Z} Z Z Z/* Z * FUNCTION Z * Z * _db_printf_ handle print of debug lines Z * Z * SYNOPSIS Z * Z * VOID _db_printf_ (_line_, keyword, format, Z * a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) Z * int _line_; Z * char *keyword, *format; Z * int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11; Z * Z * DESCRIPTION Z * Z * When invoked via one of the DBUG macros, tests the keyword Z * to see if that macro has been selected for processing via Z * the debugger control string, and if so, handles printing Z * of the arguments via the format string. The line number Z * of the DBUG macro in the source is found in _line_. Z * Z * Note that the format string SHOULD NOT include a terminating Z * newline, this is supplied automatically. Z * Z * NOTE Z * Z * The rather ugly argument declaration is to handle some Z * magic with respect to the number of arguments passed Z * via the DBUG macros. The current maximum is 3 arguments Z * (not including the keyword and format strings). Z * Z * If the args being passed by the DBUG macro are actually Z * doubles (worst case) then there will be a total of 12 Z * ints on the stack for a PDP-11 or 6 ints on a 68000. Z * Z */ Z Z/*VARARGS3*/ ZVOID _db_printf_ (_line_, keyword, format, Z a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) Zint _line_; Zchar *keyword, *format; Zint a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11; Z{ Z if (_db_keyword_ (keyword)) { Z DoPrefix (_line_); Z if (TRACING) { Z Indent (stack -> level + INDENT); Z } else { Z fprintf (_db_fp_, "%s: ", func); Z } Z fprintf (_db_fp_, "%s: ", keyword); Z fprintf (_db_fp_, format, a0, a1, a2, a3, a4, a5, a6,a7, a8, Z a9, a10, a11); Z fprintf (_db_fp_, "\n"); Z fflush (_db_fp_); Z } Z} Z Z Z/* Z * FUNCTION Z * Z * ListParse parse list of modifiers in debug control string Z * Z * SYNOPSIS Z * Z * LOCAL struct link *ListParse (ctlp) Z * char *ctlp; Z * Z * DESCRIPTION Z * Z * Given pointer to a comma separated list of strings in "cltp", Z * parses the list, building a list and returning a pointer to it. Z * The original comma separated list is destroyed in the process of Z * building the linked list, thus it had better be a duplicate Z * if it is important. Z * Z * Note that since each link is added at the head of the list, Z * the final list will be in "reverse order", which is not Z * significant for our usage here. Z * Z */ Z ZLOCAL struct link *ListParse (ctlp) Zchar *ctlp; Z{ Z REGISTER char *start; Z REGISTER struct link *new; Z REGISTER struct link *head; Z Z head = NULL; Z while (*ctlp != EOS) { Z start = ctlp; Z while (*ctlp != EOS && *ctlp != ',') { Z ctlp++; Z } Z if (*ctlp == ',') { Z *ctlp++ = EOS; Z } Z new = (struct link *) DbugMalloc (sizeof (struct link)); Z new -> string = StrDup (start); Z new -> next_link = head; Z head = new; Z } Z return (head); Z} Z Z Z/* Z * FUNCTION Z * Z * InList test a given string for member of a given list Z * Z * SYNOPSIS Z * Z * LOCAL InList (linkp, cp) Z * struct link *linkp; Z * char *cp; Z * Z * DESCRIPTION Z * Z * Tests the string pointed to by "cp" to determine if it is in Z * the list pointed to by "linkp". Linkp points to the first Z * link in the list. If linkp is NULL then the string is treated Z * as if it is in the list (I.E all strings are in the null list). Z * This may seem rather strange at first but leads to the desired Z * operation if no list is given. The net effect is that all Z * strings will be accepted when there is no list, and when there Z * is a list, only those strings in the list will be accepted. Z * Z */ Z ZLOCAL InList (linkp, cp) Zstruct link *linkp; Zchar *cp; Z{ Z REGISTER struct link *scan; Z REGISTER int accept; Z Z if (linkp == NULL) { Z accept = TRUE; Z } else { Z accept = FALSE; Z for (scan = linkp; scan != NULL; scan = scan -> next_link) { Z if (STREQ (scan -> string, cp)) { Z accept = TRUE; Z break; Z } Z } Z } Z return (accept); Z} Z Z Z/* Z * FUNCTION Z * Z * PushState push current state onto stack and set up new one Z * Z * SYNOPSIS Z * Z * LOCAL VOID PushState () Z * Z * DESCRIPTION Z * Z * Pushes the current state on the state stack, and initializes Z * a new state. The only parameter inherited from the previous Z * state is the function nesting level. This action can be Z * inhibited if desired, via the "r" flag. Z * Z * The state stack is a linked list of states, with the new Z * state added at the head. This allows the stack to grow Z * to the limits of memory if necessary. Z * Z */ Z ZLOCAL VOID PushState () Z{ Z REGISTER struct state *new; Z Z new = (struct state *) DbugMalloc (sizeof (struct state)); Z new -> flags = 0; Z new -> maxdepth = MAXDEPTH; Z if (stack != NULL) { Z new -> level = stack -> level; Z } else { Z new -> level = 0; Z } Z new -> out_file = stderr; Z new -> functions = NULL; Z new -> keywords = NULL; Z new -> processes = NULL; Z new -> next_state = stack; Z stack = new; Z init_done = TRUE; Z} Z Z Z/* Z * FUNCTION Z * Z * DoTrace check to see if tracing is current enabled Z * Z * SYNOPSIS Z * Z * LOCAL DoTrace () Z * Z * DESCRIPTION Z * Z * Checks to see if tracing is enabled based on whether the Z * user has specified tracing, the maximum trace depth has Z * not yet been reached, the current function is selected, Z * and the current process is selected. Returns TRUE if Z * tracing is enabled, FALSE otherwise. Z * Z */ Z ZLOCAL DoTrace () Z{ Z REGISTER int trace; Z Z trace = FALSE; Z if (TRACING) { Z if (stack -> level / INDENT <= stack -> maxdepth) { Z if (InList (stack -> functions, func)) { Z if (InList (stack -> processes, _db_process_)) { Z trace = TRUE; Z } Z } Z } Z } Z return (trace); Z} Z Z Z/* Z * FUNCTION Z * Z * _db_keyword_ test keyword for member of keyword list Z * Z * SYNOPSIS Z * Z * int _db_keyword_ (keyword) Z * char *keyword; Z * Z * DESCRIPTION Z * Z * Test a keyword to determine if it is in the currently active Z * keyword list. As with the function list, a keyword is accepted Z * if the list is null, otherwise it must match one of the list Z * members. When debugging is not on, no keywords are accepted. Z * After the maximum trace level is exceeded, no keywords are Z * accepted (this behavior subject to change). Additionally, Z * the current function and process must be accepted based on Z * their respective lists. Z * Z */ Z Zint _db_keyword_ (keyword) Zchar *keyword; Z{ Z REGISTER int accept; Z Z if (!init_done) { Z _db_push_ (""); Z } Z accept = FALSE; Z if (DEBUGGING) { Z if (stack -> level / INDENT <= stack -> maxdepth) { Z if (InList (stack -> functions, func)) { Z if (InList (stack -> keywords, keyword)) { Z if (InList (stack -> processes, _db_process_)) { Z accept = TRUE; Z } Z } Z } Z } Z } Z return (accept); Z} Z Z Z/* Z * FUNCTION Z * Z * Indent indent a line to the given indentation level Z * Z * SYNOPSIS Z * Z * LOCAL Indent (indent) Z * int indent; Z * Z * DESCRIPTION Z * Z * Indent a line to the given level. Note that this is Z * a simple minded but portable implementation. Z * There are better ways. Z * Z */ Z ZLOCAL Indent (indent) Zint indent; Z{ Z REGISTER int count; Z AUTO char buffer[PRINTBUF]; Z Z for (count = 0; (count < (indent - INDENT)) && (count < (PRINTBUF - 1)); count++) { Z if ((count % INDENT) == 0) { Z buffer[count] = '|'; Z } else { Z buffer[count] = ' '; Z } Z } Z buffer[count] = EOS; Z fprintf (_db_fp_, buffer); Z fflush (_db_fp_); Z} Z Z Z/* Z * FUNCTION Z * Z * FreeList free all memory associated with a linked list Z * Z * SYNOPSIS Z * Z * LOCAL FreeList (linkp) Z * struct link *linkp; Z * Z * DESCRIPTION Z * Z * Given pointer to the head of a linked list, frees all Z * memory held by the list and the members of the list. Z * Z */ Z ZLOCAL FreeList (linkp) Zstruct link *linkp; Z{ Z REGISTER struct link *old; Z Z while (linkp != NULL) { Z old = linkp; Z linkp = linkp -> next_link; Z if (old -> string != NULL) { Z free (old -> string); Z } Z free (old); Z } Z} Z Z Z/* Z * FUNCTION Z * Z * StrDup make a duplicate of a string in new memory Z * Z * SYNOPSIS Z * Z * LOCAL char *StrDup (string) Z * char *string; Z * Z * DESCRIPTION Z * Z * Given pointer to a string, allocates sufficient memory to make Z * a duplicate copy, and copies the string to the newly allocated Z * memory. Failure to allocated sufficient memory is immediately Z * fatal. Z * Z */ Z Z ZLOCAL char *StrDup (string) Zchar *string; Z{ Z REGISTER char *new; Z Z new = DbugMalloc (strlen (string) + 1); Z (VOID) strcpy (new, string); Z return (new); Z} Z Z Z/* Z * FUNCTION Z * Z * DoPrefix print debugger line prefix prior to indentation Z * Z * SYNOPSIS Z * Z * LOCAL DoPrefix (_line_) Z * int _line_; Z * Z * DESCRIPTION Z * Z * Print prefix common to all debugger output lines, prior to Z * doing indentation if necessary. Print such information as Z * current process name, current source file name and line number, Z * and current function nesting depth. Z * Z */ Z Z ZLOCAL DoPrefix (_line_) Zint _line_; Z{ Z if (stack -> flags & PROCESS_ON) { Z fprintf (_db_fp_, "%s: ", _db_process_); Z } Z if (stack -> flags & FILE_ON) { Z fprintf (_db_fp_, "%14s: ", file); Z } Z if (stack -> flags & LINE_ON) { Z fprintf (_db_fp_, "%5d: ", _line_); Z } Z if (stack -> flags & DEPTH_ON) { Z fprintf (_db_fp_, "%4d: ", stack -> level / INDENT); Z } Z fflush (_db_fp_); Z} Z Z Z/* Z * FUNCTION Z * Z * OpenFile open new output stream for debugger output Z * Z * SYNOPSIS Z * Z * LOCAL VOID OpenFile (name) Z * char *name; Z * Z * DESCRIPTION Z * Z * Given name of a new file (or "-" for stdout) opens the file Z * and sets the output stream to the new file. Z * Z */ Z ZLOCAL VOID OpenFile (name) Zchar *name; Z{ Z REGISTER FILE *fp; Z Z if (name != NULL) { Z if (strcmp (name, "-") == 0) { Z _db_fp_ = stdout; Z stack -> out_file = _db_fp_; Z } else { Z fp = fopen (name, "a"); Z if (fp == NULL) { Z fprintf (_db_fp_, MSG2, _db_process_, name); Z perror (""); Z fflush (_db_fp_); Z } else { Z _db_fp_ = fp; Z stack -> out_file = _db_fp_; Z } Z } Z } Z} Z Z Z/* Z * FUNCTION Z * Z * CloseFile close the debug output stream Z * Z * SYNOPSIS Z * Z * LOCAL VOID CloseFile (fp) Z * FILE *fp; Z * Z * DESCRIPTION Z * Z * Closes the debug output stream unless it is standard output Z * or standard error. Z * Z */ Z ZLOCAL VOID CloseFile (fp) ZFILE *fp; Z{ Z if (fp != stderr && fp != stdout) { Z if (fclose (fp) == 0) { Z fprintf (stderr, MSG3, _db_process_); Z perror (""); Z fflush (_db_fp_); Z } Z } Z} Z Z Z/* Z * FUNCTION Z * Z * DbugExit print error message and exit Z * Z * SYNOPSIS Z * Z * LOCAL VOID DbugExit (why) Z * char *why; Z * Z * DESCRIPTION Z * Z * Prints error message using current process name, the reason for Z * aborting (typically out of memory), and exits with status 1. Z * This should probably be changed to use a status code Z * defined in the user's debugger include file. Z * Z */ Z ZLOCAL VOID DbugExit (why) Zchar *why; Z{ Z fprintf (stderr, MSG4, _db_process_, why); Z fflush (stderr); Z exit (1); Z} Z Z Z/* Z * FUNCTION Z * Z * DbugMalloc allocate memory for debugger runtime support Z * Z * SYNOPSIS Z * Z * LOCAL char *DbugMalloc (size) Z * int size; Z * Z * DESCRIPTION Z * Z * Allocate more memory for debugger runtime support functions. Z * Failure to to allocate the requested number of bytes is Z * immediately fatal to the current process. This may be Z * rather unfriendly behavior. It might be better to simply Z * print a warning message, freeze the current debugger state, Z * and continue execution. Z * Z */ Z ZLOCAL char *DbugMalloc (size) Zint size; Z{ Z register char *new; Z Z new = malloc (size); Z if (new == NULL) { Z DbugExit ("out of memory"); Z } Z return (new); Z} Z Z Z/* Z * This function may be eliminated when strtok is available Z * in the runtime environment (missing from BSD4.1). Z */ Z ZLOCAL char *strtok (s1, s2) Zchar *s1, *s2; Z{ Z static char *end = NULL; Z REGISTER char *rtnval; Z Z rtnval = NULL; Z if (s2 != NULL) { Z if (s1 != NULL) { Z end = s1; Z rtnval = strtok (NULL, s2); Z } else if (end != NULL) { Z if (*end != EOS) { Z rtnval = end; Z while (*end != *s2 && *end != EOS) {end++;} Z if (*end != EOS) { Z *end++ = EOS; Z } Z } Z } Z } Z return (rtnval); Z} STUNKYFLUFF set `sum dbug.c` if test 64744 != $1 then echo dbug.c: Checksum error. Is: $1, should be: 64744. fi # # echo Extracting dbug.h: sed 's/^Z//' >dbug.h <<\STUNKYFLUFF Z/************************************************************************ Z * * Z * Copyright (c) 1984, Fred Fish * Z * All Rights Reserved * Z * * Z * This software and/or documentation is released into the * Z * public domain for personal, non-commercial use only. * Z * Limited rights to use, modify, and redistribute are hereby * Z * granted for non-commercial purposes, provided that all * Z * copyright notices remain intact and all changes are clearly * Z * documented. The author makes no warranty of any kind with * Z * respect to this product and explicitly disclaims any implied * Z * warranties of merchantability or fitness for any particular * Z * purpose. * Z * * Z ************************************************************************ Z */ Z Z Z/* Z * FILE Z * Z * dbug.h user include file for programs using the dbug package Z * Z * SYNOPSIS Z * Z * #include Z * Z * SCCS ID Z * Z * @(#)dbug.h 1.3 1/3/85 Z * Z * DESCRIPTION Z * Z * Programs which use the dbug package must include this file. Z * It contains the appropriate macros to call support routines Z * in the dbug runtime library. Z * Z * To disable compilation of the macro expansions define the Z * preprocessor symbol "DBUG_OFF". This will result in null Z * macros expansions so that the resulting code will be smaller Z * and faster. (The difference may be smaller than you think Z * so this step is recommended only when absolutely necessary). Z * In general, tradeoffs between space and efficiency are Z * decided in favor of efficiency since space is seldom a Z * problem on the new machines). Z * Z * All externally visible symbol names follow the pattern Z * "_db_xxx..xx_" to minimize the possibility of a dbug package Z * symbol colliding with a user defined symbol. Z * Z * Because the C preprocessor will not accept macros with a variable Z * number of arguments, there are separate DBUG_ macros for Z * cases N = {0,1,...NMAX}. NMAX is currently 5. Z * Z * AUTHOR Z * Z * Fred Fish Z * (Currently employed by UniSoft Systems, Berkeley, Ca.) Z * Z */ Z Z Z/* Z * Internally used dbug variables which must be global. Z */ Z Z#ifndef DBUG_OFF Z extern int _db_on_; /* TRUE if debug currently enabled */ Z extern FILE *_db_fp_; /* Current debug output stream */ Z extern char *_db_process_; /* Name of current process */ Z extern int _db_keyword_ (); /* Accept/reject keyword */ Z extern void _db_push_ (); /* Push state, set up new state */ Z extern void _db_pop_ (); /* Pop previous debug state */ Z extern void _db_enter_ (); /* New user function entered */ Z extern void _db_return_ (); /* User function return */ Z extern void _db_printf_ (); /* Print debug output */ Z# endif Z Z Z/* Z * These macros provide a user interface into functions in the Z * dbug runtime support library. They isolate users from changes Z * in the MACROS and/or runtime support. Z * Z * The symbols "__LINE__" and "__FILE__" are expanded by the Z * preprocessor to the current source file line number and file Z * name respectively. Z * Z * WARNING --- Because the DBUG_ENTER macro allocates space on Z * the user function's stack, it must precede any executable Z * statements in the user function. Z * Z */ Z Z# ifdef DBUG_OFF Z# define DBUG_ENTER(a1) Z# define DBUG_RETURN(a1) return(a1) Z# define DBUG_VOID_RETURN return Z# define DBUG_EXECUTE(keyword,a1) Z# define DBUG_2(keyword,format) Z# define DBUG_3(keyword,format,a1) Z# define DBUG_4(keyword,format,a1,a2) Z# define DBUG_5(keyword,format,a1,a2,a3) Z# define DBUG_PUSH(a1) Z# define DBUG_POP() Z# define DBUG_PROCESS(a1) Z# define DBUG_FILE (stderr) Z# else Z# define DBUG_ENTER(a) \ Z auto char *_db_func_, *_db_file_; \ Z int _db_level_; \ Z _db_enter_ (a,__FILE__,__LINE__,&_db_func_,&_db_file_,&_db_level_) Z# define DBUG_LEAVE \ Z (_db_return_ (__LINE__, &_db_func_, &_db_file_, &_db_level_)) Z# define DBUG_RETURN(a1) return (DBUG_LEAVE, (a1)) Z/* define DBUG_RETURN(a1) {DBUG_LEAVE; return(a1);} Alternate form */ Z# define DBUG_VOID_RETURN {DBUG_LEAVE; return;} Z# define DBUG_EXECUTE(keyword,a1) \ Z if (_db_on_) {if (_db_keyword_ (keyword)) { a1 }} Z# define DBUG_2(keyword,format) \ Z if (_db_on_) {_db_printf_ (__LINE__, keyword, format);} Z# define DBUG_3(keyword,format,a1) \ Z if (_db_on_) {_db_printf_ (__LINE__, keyword, format, a1);} Z# define DBUG_4(keyword,format,a1,a2) \ Z if (_db_on_) {_db_printf_ (__LINE__, keyword, format, a1, a2);} Z# define DBUG_5(keyword,format,a1,a2,a3) \ Z if (_db_on_) {_db_printf_ (__LINE__, keyword, format, a1, a2, a3);}; Z# define DBUG_PUSH(a1) _db_push_ (a1) Z# define DBUG_POP() _db_pop_ () Z# define DBUG_PROCESS(a1) (_db_process_ = a1) Z# define DBUG_FILE (_db_fp_) Z# endif STUNKYFLUFF set `sum dbug.h` if test 22455 != $1 then echo dbug.h: Checksum error. Is: $1, should be: 22455. fi echo ALL DONE BUNKY! exit 0