v19i024: dmake - dmake version 3.7, Part03/37
Dennis Vadura
dvadura at watdragon.waterloo.edu
Fri May 10 05:26:21 AEST 1991
Submitted-by: Dennis Vadura <dvadura at watdragon.waterloo.edu>
Posting-number: Volume 19, Issue 24
Archive-name: dmake/part03
Supersedes: dmake-3.6: Volume 15, Issue 52-77
---- Cut Here and feed the following to sh ----
#!/bin/sh
# this is dmake.shar.03 (part 3 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file dmake/dbug/dbug/dbug.c continued
#
if test ! -r _shar_seq_.tmp; then
echo 'Please unpack part 1 first!'
exit 1
fi
(read Scheck
if test "$Scheck" != 3; then
echo Please unpack part "$Scheck" next!
exit 1
else
exit 0
fi
) < _shar_seq_.tmp || exit 1
if test -f _shar_wnt_.tmp; then
sed 's/^X//' << 'SHAR_EOF' >> 'dmake/dbug/dbug/dbug.c' &&
X * current process name, current source file name and line number,
X * and current function nesting depth.
X *
X */
X
X
LOCAL VOID DoPrefix (_line_)
int _line_;
{
X lineno++;
X if (stack -> flags & NUMBER_ON) {
X (VOID) fprintf (_db_fp_, "%5d: ", lineno);
X }
X if (stack -> flags & PROCESS_ON) {
X (VOID) fprintf (_db_fp_, "%s: ", _db_process_);
X }
X if (stack -> flags & FILE_ON) {
X (VOID) fprintf (_db_fp_, "%14s: ", file);
X }
X if (stack -> flags & LINE_ON) {
X (VOID) fprintf (_db_fp_, "%5d: ", _line_);
X }
X if (stack -> flags & DEPTH_ON) {
X (VOID) fprintf (_db_fp_, "%4d: ", stack -> level);
X }
X (VOID) fflush (_db_fp_);
}
X
X
/*
X * FUNCTION
X *
X * OpenFile open new output stream for debugger output
X *
X * SYNOPSIS
X *
X * LOCAL VOID OpenFile (name)
X * char *name;
X *
X * DESCRIPTION
X *
X * Given name of a new file (or "-" for stdout) opens the file
X * and sets the output stream to the new file.
X *
X */
X
LOCAL VOID OpenFile (name)
char *name;
{
X REGISTER FILE *fp;
X REGISTER BOOLEAN newfile;
X
X if (name != NULL) {
X if (strcmp (name, "-") == 0) {
X _db_fp_ = stdout;
X stack -> out_file = _db_fp_;
X } else {
X if (!Writable (name)) {
X (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
X perror ("");
X (VOID) fflush (_db_fp_);
X (VOID) XDelay (stack -> delay);
X } else {
X if (EXISTS (name)) {
X newfile = FALSE;
X } else {
X newfile = TRUE;
X }
X fp = fopen (name, "a");
X if (fp == NULL) {
X (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
X perror ("");
X (VOID) fflush (_db_fp_);
X (VOID) XDelay (stack -> delay);
X } else {
X _db_fp_ = fp;
X stack -> out_file = fp;
X if (newfile) {
X ChangeOwner (name);
X }
X }
X }
X }
X }
}
X
X
/*
X * FUNCTION
X *
X * OpenProfile open new output stream for profiler output
X *
X * SYNOPSIS
X *
X * LOCAL VOID OpenProfile (name)
X * char *name;
X *
X * DESCRIPTION
X *
X * Given name of a new file, opens the file
X * and sets the profiler output stream to the new file.
X *
X * It is currently unclear whether the prefered behavior is
X * to truncate any existing file, or simply append to it.
X * The latter behavior would be desirable for collecting
X * accumulated runtime history over a number of separate
X * runs. It might take some changes to the analyzer program
X * though, and the notes that Binayak sent with the profiling
X * diffs indicated that append was the normal mode, but this
X * does not appear to agree with the actual code. I haven't
X * investigated at this time [fnf; 24-Jul-87].
X */
X
LOCAL VOID OpenProfile (name)
char *name;
{
X REGISTER FILE *fp;
X REGISTER BOOLEAN newfile;
X
X if (name != NULL) {
X if (!Writable (name)) {
X (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
X perror ("");
X (VOID) fflush (_db_fp_);
X (VOID) XDelay (stack -> delay);
X } else {
X if (EXISTS (name)) {
X newfile = FALSE;
X } else {
X newfile = TRUE;
X }
X fp = fopen (name, "w");
X if (fp == NULL) {
X (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
X perror ("");
X (VOID) fflush (_db_fp_);
X (VOID) XDelay (stack -> delay);
X } else {
X _db_pfp_ = fp;
X stack -> prof_file = fp;
X if (newfile) {
X ChangeOwner (name);
X }
X }
X }
X }
}
X
X
/*
X * FUNCTION
X *
X * CloseFile close the debug output stream
X *
X * SYNOPSIS
X *
X * LOCAL VOID CloseFile (fp)
X * FILE *fp;
X *
X * DESCRIPTION
X *
X * Closes the debug output stream unless it is standard output
X * or standard error.
X *
X */
X
LOCAL VOID CloseFile (fp)
FILE *fp;
{
X if (fp != stderr && fp != stdout) {
X if (fclose (fp) == EOF) {
X (VOID) fprintf (stderr, ERR_CLOSE, _db_process_);
X perror ("");
X (VOID) fflush (stderr);
X (VOID) XDelay (stack -> delay);
X }
X }
}
X
X
/*
X * FUNCTION
X *
X * DbugExit print error message and exit
X *
X * SYNOPSIS
X *
X * LOCAL VOID DbugExit (why)
X * char *why;
X *
X * DESCRIPTION
X *
X * Prints error message using current process name, the reason for
X * aborting (typically out of memory), and exits with status 1.
X * This should probably be changed to use a status code
X * defined in the user's debugger include file.
X *
X */
X
LOCAL VOID DbugExit (why)
char *why;
{
X (VOID) fprintf (stderr, ERR_ABORT, _db_process_, why);
X (VOID) fflush (stderr);
X (VOID) XDelay (stack -> delay);
X exit (1);
}
X
X
/*
X * FUNCTION
X *
X * DbugMalloc allocate memory for debugger runtime support
X *
X * SYNOPSIS
X *
X * LOCAL char *DbugMalloc (size)
X * int size;
X *
X * DESCRIPTION
X *
X * Allocate more memory for debugger runtime support functions.
X * Failure to to allocate the requested number of bytes is
X * immediately fatal to the current process. This may be
X * rather unfriendly behavior. It might be better to simply
X * print a warning message, freeze the current debugger state,
X * and continue execution.
X *
X */
X
LOCAL char *DbugMalloc (size)
int size;
{
X register char *new;
X
X new = malloc ( size );
X if (new == NULL) {
X DbugExit ("out of memory");
X }
X return (new);
}
X
X
/*
X * This function may be eliminated when strtok is available
X * in the runtime environment (missing from BSD4.1).
X */
X
LOCAL char *strtok (s1, s2)
char *s1, *s2;
{
X static char *end = NULL;
X REGISTER char *rtnval;
X
X rtnval = NULL;
X if (s2 != NULL) {
X if (s1 != NULL) {
X end = s1;
X rtnval = strtok ((char *) NULL, s2);
X } else if (end != NULL) {
X if (*end != EOS) {
X rtnval = end;
X while (*end != *s2 && *end != EOS) {end++;}
X if (*end != EOS) {
X *end++ = EOS;
X }
X }
X }
X }
X return (rtnval);
}
X
X
/*
X * FUNCTION
X *
X * BaseName strip leading pathname components from name
X *
X * SYNOPSIS
X *
X * LOCAL char *BaseName (pathname)
X * char *pathname;
X *
X * DESCRIPTION
X *
X * Given pointer to a complete pathname, locates the base file
X * name at the end of the pathname and returns a pointer to
X * it.
X *
X */
X
LOCAL char *BaseName (pathname)
char *pathname;
{
X register char *base;
X
X base = strrchr (pathname, '/');
X if (base++ == NULL) {
X base = pathname;
X }
X return (base);
}
X
X
/*
X * FUNCTION
X *
X * Writable test to see if a pathname is writable/creatable
X *
X * SYNOPSIS
X *
X * LOCAL BOOLEAN Writable (pathname)
X * char *pathname;
X *
X * DESCRIPTION
X *
X * Because the debugger might be linked in with a program that
X * runs with the set-uid-bit (suid) set, we have to be careful
X * about opening a user named file for debug output. This consists
X * of checking the file for write access with the real user id,
X * or checking the directory where the file will be created.
X *
X * Returns TRUE if the user would normally be allowed write or
X * create access to the named file. Returns FALSE otherwise.
X *
X */
X
LOCAL BOOLEAN Writable (pathname)
char *pathname;
{
X REGISTER BOOLEAN granted;
#ifdef unix
X REGISTER char *lastslash;
#endif
X
#ifndef unix
X granted = TRUE;
#else
X granted = FALSE;
X if (EXISTS (pathname)) {
X if (WRITABLE (pathname)) {
X granted = TRUE;
X }
X } else {
X lastslash = strrchr (pathname, '/');
X if (lastslash != NULL) {
X *lastslash = EOS;
X } else {
X pathname = ".";
X }
X if (WRITABLE (pathname)) {
X granted = TRUE;
X }
X if (lastslash != NULL) {
X *lastslash = '/';
X }
X }
#endif
X return (granted);
}
X
X
/*
X * This function may be eliminated when strrchr is available
X * in the runtime environment (missing from BSD4.1).
X * Alternately, you can use rindex() on BSD systems.
X */
X
LOCAL char *strrchr (s, c)
char *s;
char c;
{
X REGISTER char *scan;
X
X for (scan = s; *scan != EOS; scan++) {;}
X while (scan > s && *--scan != c) {;}
X if (*scan != c) {
X scan = NULL;
X }
X return (scan);
}
X
X
/*
X * FUNCTION
X *
X * ChangeOwner change owner to real user for suid programs
X *
X * SYNOPSIS
X *
X * LOCAL VOID ChangeOwner (pathname)
X *
X * DESCRIPTION
X *
X * For unix systems, change the owner of the newly created debug
X * file to the real owner. This is strictly for the benefit of
X * programs that are running with the set-user-id bit set.
X *
X * Note that at this point, the fact that pathname represents
X * a newly created file has already been established. If the
X * program that the debugger is linked to is not running with
X * the suid bit set, then this operation is redundant (but
X * harmless).
X *
X */
X
LOCAL VOID ChangeOwner (pathname)
char *pathname;
{
#ifdef unix
X if (chown (pathname, getuid (), getgid ()) == -1) {
X (VOID) fprintf (stderr, ERR_CHOWN, _db_process_, pathname);
X perror ("");
X (VOID) fflush (stderr);
X (VOID) XDelay (stack -> delay);
X }
#endif
}
X
X
/*
X * FUNCTION
X *
X * _db_setjmp_ save debugger environment
X *
X * SYNOPSIS
X *
X * VOID _db_setjmp_ ()
X *
X * DESCRIPTION
X *
X * Invoked as part of the user's DBUG_SETJMP macro to save
X * the debugger environment in parallel with saving the user's
X * environment.
X *
X */
X
VOID _db_setjmp_ ()
{
X jmplevel = stack -> level;
X jmpfunc = func;
X jmpfile = file;
}
X
X
/*
X * FUNCTION
X *
X * _db_longjmp_ restore previously saved debugger environment
X *
X * SYNOPSIS
X *
X * VOID _db_longjmp_ ()
X *
X * DESCRIPTION
X *
X * Invoked as part of the user's DBUG_LONGJMP macro to restore
X * the debugger environment in parallel with restoring the user's
X * previously saved environment.
X *
X */
X
VOID _db_longjmp_ ()
{
X stack -> level = jmplevel;
X if (jmpfunc) {
X func = jmpfunc;
X }
X if (jmpfile) {
X file = jmpfile;
X }
}
X
X
/*
X * FUNCTION
X *
X * DelayArg convert D flag argument to appropriate value
X *
X * SYNOPSIS
X *
X * LOCAL int DelayArg (value)
X * int value;
X *
X * DESCRIPTION
X *
X * Converts delay argument, given in tenths of a second, to the
X * appropriate numerical argument used by the system to delay
X * that that many tenths of a second. For example, on the
X * AMIGA, there is a system call "Delay()" which takes an
X * argument in ticks (50 per second). On unix, the sleep
X * command takes seconds. Thus a value of "10", for one
X * second of delay, gets converted to 50 on the amiga, and 1
X * on unix. Other systems will need to use a timing loop.
X *
X */
X
LOCAL int DelayArg (value)
int value;
{
X int delayarg = 0;
X
#ifdef unix
X delayarg = value / 10; /* Delay is in seconds for sleep () */
#endif
#ifdef AMIGA
X delayarg = (HZ * value) / 10; /* Delay in ticks for XDelay () */
#endif
X return (delayarg);
}
X
X
/*
X * A dummy delay stub for systems that do not support delays.
X * With a little work, this can be turned into a timing loop.
X */
X
#ifndef unix
#ifndef AMIGA
XXDelay ()
{
}
#endif
#endif
X
X
/*
X * FUNCTION
X *
X * perror perror simulation for systems that don't have it
X *
X * SYNOPSIS
X *
X * LOCAL VOID perror (s)
X * char *s;
X *
X * DESCRIPTION
X *
X * Perror produces a message on the standard error stream which
X * provides more information about the library or system error
X * just encountered. The argument string s is printed, followed
X * by a ':', a blank, and then a message and a newline.
X *
X * An undocumented feature of the unix perror is that if the string
X * 's' is a null string (NOT a NULL pointer!), then the ':' and
X * blank are not printed.
X *
X * This version just complains about an "unknown system error".
X *
X */
X
#if !unix && !(AMIGA || LATTICE || __TURBOC__ )
LOCAL VOID perror (s)
#if __STDC__
const char *s;
#else
char *s;
#endif
{
X if (s && *s != EOS) {
X (VOID) fprintf (stderr, "%s: ", s);
X }
X (VOID) fprintf (stderr, "<unknown system error>\n");
}
#endif /* !unix && !(AMIGA && LATTICE) */
X
/*
X * Here we need the definitions of the clock routine. Add your
X * own for whatever system that you have.
X */
X
#if unix
X
# include <sys/param.h>
# if BSD4_3 || sun
X
/*
X * Definition of the Clock() routine for 4.3 BSD.
X */
X
#include <sys/time.h>
#include <sys/resource.h>
X
/*
X * Returns the user time in milliseconds used by this process so
X * far.
X */
X
LOCAL unsigned long Clock ()
{
X struct rusage ru;
X
X (VOID) getrusage (RUSAGE_SELF, &ru);
X return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000));
}
X
#else
X
LOCAL unsigned long Clock ()
{
X return (0);
}
X
# endif
X
#else
X
#if AMIGA
X
struct DateStamp { /* Yes, this is a hack, but doing it right */
X long ds_Days; /* is incredibly ugly without splitting this */
X long ds_Minute; /* off into a separate file */
X long ds_Tick;
};
X
static int first_clock = TRUE;
static struct DateStamp begin;
static struct DateStamp elapsed;
X
LOCAL unsigned long Clock ()
{
X register struct DateStamp *now;
X register unsigned long millisec = 0;
X extern VOID *AllocMem ();
X
X now = (struct DateStamp *) AllocMem ((long) sizeof (struct DateStamp), 0L);
X if (now != NULL) {
X if (first_clock == TRUE) {
X first_clock = FALSE;
X (VOID) DateStamp (now);
X begin = *now;
X }
X (VOID) DateStamp (now);
X millisec = 24 * 3600 * (1000 / HZ) * (now -> ds_Days - begin.ds_Days);
X millisec += 60 * (1000 / HZ) * (now -> ds_Minute - begin.ds_Minute);
X millisec += (1000 / HZ) * (now -> ds_Tick - begin.ds_Tick);
X (VOID) FreeMem (now, (long) sizeof (struct DateStamp));
X }
X return (millisec);
}
X
#else
X
LOCAL unsigned long Clock ()
{
X return (0);
}
X
#endif /* AMIGA */
X
#endif /* unix */
X
#ifdef AMIGA
XXDelay(x)
int x;
{
X if (x) Delay(x); /* fix Delay bug in AmigaDOS */
}
#endif
X
SHAR_EOF
chmod 0640 dmake/dbug/dbug/dbug.c ||
echo 'restore of dmake/dbug/dbug/dbug.c failed'
Wc_c="`wc -c < 'dmake/dbug/dbug/dbug.c'`"
test 44504 -eq "$Wc_c" ||
echo 'dmake/dbug/dbug/dbug.c: original size 44504, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dmake/dbug/dbug/dbug.h ==============
if test -f 'dmake/dbug/dbug/dbug.h' -a X"$1" != X"-c"; then
echo 'x - skipping dmake/dbug/dbug/dbug.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
sed 's/^X//' << 'SHAR_EOF' > 'dmake/dbug/dbug/dbug.h' &&
/******************************************************************************
X * *
X * N O T I C E *
X * *
X * Copyright Abandoned, 1987, Fred Fish *
X * *
X * *
X * This previously copyrighted work has been placed into the public *
X * domain by the author and may be freely used for any purpose, *
X * private or commercial. *
X * *
X * Because of the number of inquiries I was receiving about the use *
X * of this product in commercially developed works I have decided to *
X * simply make it public domain to further its unrestricted use. I *
X * specifically would be most happy to see this material become a *
X * part of the standard Unix distributions by AT&T and the Berkeley *
X * Computer Science Research Group, and a standard part of the GNU *
X * system from the Free Software Foundation. *
X * *
X * I would appreciate it, as a courtesy, if this notice is left in *
X * all copies and derivative works. Thank you. *
X * *
X * The author makes no warranty of any kind with respect to this *
X * product and explicitly disclaims any implied warranties of mer- *
X * chantability or fitness for any particular purpose. *
X * *
X ******************************************************************************
X */
X
X
/*
X * FILE
X *
X * dbug.h user include file for programs using the dbug package
X *
X * SYNOPSIS
X *
X * #include <local/dbug.h>
X *
X * SCCS ID
X *
X * @(#)dbug.h 1.11 9/5/87
X *
X * DESCRIPTION
X *
X * Programs which use the dbug package must include this file.
X * It contains the appropriate macros to call support routines
X * in the dbug runtime library.
X *
X * To disable compilation of the macro expansions define the
X * preprocessor symbol "DBUG_OFF". This will result in null
X * macros expansions so that the resulting code will be smaller
X * and faster. (The difference may be smaller than you think
X * so this step is recommended only when absolutely necessary).
X * In general, tradeoffs between space and efficiency are
X * decided in favor of efficiency since space is seldom a
X * problem on the new machines).
X *
X * All externally visible symbol names follow the pattern
X * "_db_xxx..xx_" to minimize the possibility of a dbug package
X * symbol colliding with a user defined symbol.
X *
X * The DBUG_<N> style macros are obsolete and should not be used
X * in new code. Macros to map them to instances of DBUG_PRINT
X * are provided for compatibility with older code. They may go
X * away completely in subsequent releases.
X *
X * AUTHOR
X *
X * Fred Fish
X * (Currently employed by Motorola Computer Division, Tempe, Az.)
X * hao!noao!mcdsun!fnf
X * (602) 438-3614
X *
X */
X
X
/*
X * Internally used dbug variables which must be global.
X */
X
#ifndef DBUG_OFF
X extern int _db_on_; /* TRUE if debug currently enabled */
X extern FILE *_db_fp_; /* Current debug output stream */
X extern char *_db_process_; /* Name of current process */
X extern int _db_keyword_ (); /* Accept/reject keyword */
X extern void _db_push_ (); /* Push state, set up new state */
X extern void _db_pop_ (); /* Pop previous debug state */
X extern void _db_enter_ (); /* New user function entered */
X extern void _db_return_ (); /* User function return */
X extern void _db_pargs_ (); /* Remember args for line */
X extern void _db_doprnt_ (); /* Print debug output */
X extern void _db_setjmp_ (); /* Save debugger environment */
X extern void _db_longjmp_ (); /* Restore debugger environment */
# endif
X
X
/*
X * These macros provide a user interface into functions in the
X * dbug runtime support library. They isolate users from changes
X * in the MACROS and/or runtime support.
X *
X * The symbols "__LINE__" and "__FILE__" are expanded by the
X * preprocessor to the current source file line number and file
X * name respectively.
X *
X * WARNING --- Because the DBUG_ENTER macro allocates space on
X * the user function's stack, it must precede any executable
X * statements in the user function.
X *
X */
X
# ifdef DBUG_OFF
# define DBUG_ENTER(a1)
# define DBUG_MALLOC(a1)
# define DBUG_RETURN(a1) return(a1)
# define DBUG_VOID_RETURN return
# define DBUG_EXECUTE(keyword,a1)
# define DBUG_PRINT(keyword,arglist)
# define DBUG_2(keyword,format) /* Obsolete */
# define DBUG_3(keyword,format,a1) /* Obsolete */
# define DBUG_4(keyword,format,a1,a2) /* Obsolete */
# define DBUG_5(keyword,format,a1,a2,a3) /* Obsolete */
# define DBUG_PUSH(a1)
# define DBUG_POP()
# define DBUG_PROCESS(a1)
# define DBUG_FILE (stderr)
# define DBUG_SETJMP setjmp
# define DBUG_LONGJMP longjmp
# else
# define DBUG_ENTER(a) \
X auto char *_db_func_, *_db_file_; \
X int _db_level_; \
X _db_enter_ (a,__FILE__,__LINE__,&_db_func_,&_db_file_,&_db_level_)
# define DBUG_MALLOC(a) \
X auto char *_db_func_, *_db_file_; \
X int _db_level_; \
X malloc_init();\
X _db_enter_ (a,__FILE__,__LINE__,&_db_func_,&_db_file_,&_db_level_)
# define DBUG_LEAVE \
X (_db_return_ (__LINE__, &_db_func_, &_db_file_, &_db_level_))
# define DBUG_RETURN(a1) return (DBUG_LEAVE, (a1))
/* define DBUG_RETURN(a1) {DBUG_LEAVE; return(a1);} Alternate form */
# define DBUG_VOID_RETURN DBUG_LEAVE; return
# define DBUG_EXECUTE(keyword,a1) \
X {if (_db_on_) {if (_db_keyword_ (keyword)) { a1 }}}
# define DBUG_PRINT(keyword,arglist) \
X {if (_db_on_) {_db_pargs_(__LINE__,keyword); _db_doprnt_ arglist;}}
# define DBUG_2(keyword,format) \
X DBUG_PRINT(keyword,(format)) /* Obsolete */
# define DBUG_3(keyword,format,a1) \
X DBUG_PRINT(keyword,(format,a1)) /* Obsolete */
# define DBUG_4(keyword,format,a1,a2) \
X DBUG_PRINT(keyword,(format,a1,a2)) /* Obsolete */
# define DBUG_5(keyword,format,a1,a2,a3) \
X DBUG_PRINT(keyword,(format,a1,a2,a3)) /* Obsolete */
# define DBUG_PUSH(a1) _db_push_ (a1)
# define DBUG_POP() _db_pop_ ()
# define DBUG_PROCESS(a1) (_db_process_ = a1)
# define DBUG_FILE (_db_fp_)
# define DBUG_SETJMP(a1) (_db_setjmp_ (), setjmp (a1))
# define DBUG_LONGJMP(a1,a2) (_db_longjmp_ (), longjmp (a1, a2))
# endif
X
SHAR_EOF
chmod 0640 dmake/dbug/dbug/dbug.h ||
echo 'restore of dmake/dbug/dbug/dbug.h failed'
Wc_c="`wc -c < 'dmake/dbug/dbug/dbug.h'`"
test 6259 -eq "$Wc_c" ||
echo 'dmake/dbug/dbug/dbug.h: original size 6259, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dmake/dbug/dbug/dbug.p ==============
if test -f 'dmake/dbug/dbug/dbug.p' -a X"$1" != X"-c"; then
echo 'x - skipping dmake/dbug/dbug/dbug.p (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
sed 's/^X//' << 'SHAR_EOF' > 'dmake/dbug/dbug/dbug.p' &&
X
X
X
X D B U G
X
X C Program Debugging Package
X
X by
X
X Fred Fish
X
X
X
X
X IIIINNNNTTTTRRRROOOODDDDUUUUCCCCTTTTIIIIOOOONNNN
X
X
X Almost every program development environment worthy of
X the name provides some sort of debugging facility. Usually
X this takes the form of a program which is capable of
X controlling execution of other programs and examining the
X internal state of other executing programs. These types of
X programs will be referred to as external debuggers since the
X debugger is not part of the executing program. Examples of
X this type of debugger include the aaaaddddbbbb and ssssddddbbbb debuggers
X provided with the UUUUNNNNIIIIXXXX811119 operating system.
X
X
X One of the problems associated with developing programs
X in an environment with good external debuggers is that
X developed programs tend to have little or no internal
X instrumentation. This is usually not a problem for the
X developer since he is, or at least should be, intimately
X familiar with the internal organization, data structures,
X and control flow of the program being debugged. It is a
X serious problem for maintenance programmers, who are
X unlikely to have such familiarity with the program being
X maintained, modified, or ported to another environment. It
X is also a problem, even for the developer, when the program
X is moved to an environment with a primitive or unfamiliar
X debugger, or even no debugger.
X
X
X On the other hand, _d_b_u_g is an example of an internal
X debugger. Because it requires internal instrumentation of a
X program, and its usage does not depend on any special
X capabilities of the execution environment, it is always
X available and will execute in any environment that the
X program itself will execute in. In addition, since it is a
X complete package with a specific user interface, all
X programs which use it will be provided with similar
X debugging capabilities. This is in sharp contrast to other
X
X
X __________
X
X 1. UNIX is a trademark of AT&T Bell Laboratories.
X
X
X
X
X - 1 -
X
X
X
X
X
X
X
X DBUG User Manual October 29, 1986
X
X
X
X forms of internal instrumentation where each developer has
X their own, usually less capable, form of internal debugger.
X In summary, because _d_b_u_g is an internal debugger it provides
X consistency across operating environments, and because it is
X available to all developers it provides consistency across
X all programs in the same environment.
X
X
X The _d_b_u_g package imposes only a slight speed penalty on
X executing programs, typically much less than 10 percent, and
X a modest size penalty, typically 10 to 20 percent. By
X defining a specific C preprocessor symbol both of these can
X be reduced to zero with no changes required to the source
X code.
X
X
X The following list is a quick summary of the
X capabilities of the _d_b_u_g package. Each capability can be
X individually enabled or disabled at the time a program is
X invoked by specifying the appropriate command line
X arguments.
X
X o Execution trace showing function level control
X flow in a semi-graphically manner using
X indentation to indicate nesting depth.
X
X o Output the values of all, or any subset of, key
X internal variables.
X
X o Limit actions to a specific set of named
X functions.
X
X o Limit function trace to a specified nesting depth.
X
X o Label each output line with source file name and
X line number.
X
X o Label each output line with name of current
X process.
X
X o Push or pop internal debugging state to allow
X execution with built in debugging defaults.
X
X o Redirect the debug output stream to standard
X output (stdout) or a named file. The default
X output stream is standard error (stderr). The
X redirection mechanism is completely independent of
X normal command line redirection to avoid output
X conflicts.
X
X
X
X
X
X - 2 -
X
X
X
X
X
X
X
X DBUG User Manual October 29, 1986
X
X
X
X PPPPRRRRIIIIMMMMIIIITTTTIIIIVVVVEEEE DDDDEEEEBBBBUUUUGGGGGGGGIIIINNNNGGGG TTTTEEEECCCCHHHHNNNNIIIIQQQQUUUUEEEESSSS
X
X
X Internal instrumentation is already a familiar concept
X to most programmers, since it is usually the first debugging
X technique learned. Typically, "print statements" are
X inserted in the source code at interesting points, the code
X is recompiled and executed, and the resulting output is
X examined in an attempt to determine where the problem is.
X
X The procedure is iterative, with each iteration yielding
X more and more output, and hopefully the source of the
X problem is discovered before the output becomes too large to
X deal with or previously inserted statements need to be
X removed. Figure 1 is an example of this type of primitive
X debugging technique.
X
X
X
X #include <stdio.h>
X
X main (argc, argv)
X int argc;
X char *argv[];
X {
X printf ("argv[0] = %d\n", argv[0]);
X /*
X * Rest of program
X */
X printf ("== done ==\n");
X }
X
X
X Figure 1
X Primitive Debugging Technique
X
X
X
X
X
X Eventually, and usually after at least several
X iterations, the problem will be found and corrected. At
X this point, the newly inserted print statements must be
X dealt with. One obvious solution is to simply delete them
X all. Beginners usually do this a few times until they have
X to repeat the entire process every time a new bug pops up.
X The second most obvious solution is to somehow disable the
X output, either through the source code comment facility,
X creation of a debug variable to be switched on or off, or by
X using the C preprocessor. Figure 2 is an example of all
X three techniques.
X
X
X
X - 3 -
X
X
X
X
X
X
X
X DBUG User Manual October 29, 1986
X
X
X
X
X
X #include <stdio.h>
X
X int debug = 0;
X
X main (argc, argv)
X int argc;
X char *argv[];
X {
X /* printf ("argv = %x\n", argv) */
X if (debug) printf ("argv[0] = %d\n", argv[0]);
X /*
X * Rest of program
X */
X #ifdef DEBUG
X printf ("== done ==\n");
X #endif
X }
X
X
X Figure 2
X Debug Disable Techniques
X
X
X
X
X
X Each technique has its advantages and disadvantages
X with respect to dynamic vs static activation, source code
X overhead, recompilation requirements, ease of use, program
X readability, etc. Overuse of the preprocessor solution
X quickly leads to problems with source code readability and
X maintainability when multiple ####iiiiffffddddeeeeffff symbols are to be
X defined or undefined based on specific types of debug
X desired. The source code can be made slightly more readable
X by suitable indentation of the ####iiiiffffddddeeeeffff arguments to match the
X indentation of the code, but not all C preprocessors allow
X this. The only requirement for the standard UUUUNNNNIIIIXXXX C
X preprocessor is for the '#' character to appear in the first
X column, but even this seems like an arbitrary and
X unreasonable restriction. Figure 3 is an example of this
X usage.
X
X
X
X
X
X
X
X
X
X
X
X - 4 -
X
X
X
X
X
X
X
X DBUG User Manual October 29, 1986
X
X
X
X
X
X #include <stdio.h>
X
X main (argc, argv)
X int argc;
X char *argv[];
X {
X # ifdef DEBUG
X printf ("argv[0] = %d\n", argv[0]);
X # endif
X /*
X * Rest of program
X */
X # ifdef DEBUG
X printf ("== done ==\n");
X # endif
X }
X
X
X Figure 3
X More Readable Preprocessor Usage
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X - 5 -
X
X
X
X
X
X
X
X DBUG User Manual October 29, 1986
X
X
X
X FFFFUUUUNNNNCCCCTTTTIIIIOOOONNNN TTTTRRRRAAAACCCCEEEE EEEEXXXXAAAAMMMMPPPPLLLLEEEE
X
X
X We will start off learning about the capabilities of
X the _d_b_u_g package by using a simple minded program which
X computes the factorial of a number. In order to better
X demonstrate the function trace mechanism, this program is
X implemented recursively. Figure 4 is the main function for
X this factorial program.
X
X
X
X #include <stdio.h>
X /* User programs should use <local/dbug.h> */
X #include "dbug.h"
X
X main (argc, argv)
X int argc;
X char *argv[];
X {
X register int result, ix;
X extern int factorial (), atoi ();
X
X DBUG_ENTER ("main");
X DBUG_PROCESS (argv[0]);
X for (ix = 1; ix < argc && argv[ix][0] == '-'; ix++) {
X switch (argv[ix][1]) {
X case '#':
X DBUG_PUSH (&(argv[ix][2]));
X break;
X }
X }
X for (; ix < argc; ix++) {
X DBUG_PRINT ("args", ("argv[%d] = %s", ix, argv[ix]));
X result = factorial (atoi (argv[ix]));
X printf ("%d\n", result);
X }
X DBUG_RETURN (0);
X }
X
X
X Figure 4
X Factorial Program Mainline
X
X
X
X
X
X The mmmmaaaaiiiinnnn function is responsible for processing any
X command line option arguments and then computing and
X printing the factorial of each non-option argument.
X
X
X
X - 6 -
X
X
X
X
X
X
X
X DBUG User Manual October 29, 1986
X
X
X
X First of all, notice that all of the debugger functions
X are implemented via preprocessor macros. This does not
X detract from the readability of the code and makes disabling
X all debug compilation trivial (a single preprocessor symbol,
X DDDDBBBBUUUUGGGG____OOOOFFFFFFFF, forces the macro expansions to be null).
X
X Also notice the inclusion of the header file ddddbbbbuuuugggg....hhhh
X from the local header file directory. (The version included
X here is the test version in the dbug source distribution
X directory). This file contains all the definitions for the
X debugger macros, which all have the form DDDDBBBBUUUUGGGG____XXXXXXXX............XXXXXXXX.
X
X
X The DDDDBBBBUUUUGGGG____EEEENNNNTTTTEEEERRRR macro informs that debugger that we have
X entered the function named mmmmaaaaiiiinnnn. It must be the very first
X "executable" line in a function, after all declarations and
X before any other executable line. The DDDDBBBBUUUUGGGG____PPPPRRRROOOOCCCCEEEESSSSSSSS macro is
X generally used only once per program to inform the debugger
X what name the program was invoked with. The DDDDBBBBUUUUGGGG____PPPPUUUUSSSSHHHH macro
X modifies the current debugger state by saving the previous
X state and setting a new state based on the control string
X passed as its argument. The DDDDBBBBUUUUGGGG____PPPPRRRRIIIINNNNTTTT macro is used to
X print the values of each argument for which a factorial is
X to be computed. The DDDDBBBBUUUUGGGG____RRRREEEETTTTUUUURRRRNNNN macro tells the debugger
X that the end of the current function has been reached and
X returns a value to the calling function. All of these
X macros will be fully explained in subsequent sections.
X
X To use the debugger, the factorial program is invoked
X with a command line of the form:
X
X factorial -#d:t 1 2 3
X
X The mmmmaaaaiiiinnnn function recognizes the "-#d:t" string as a
X debugger control string, and passes the debugger arguments
X ("d:t") to the _d_b_u_g runtime support routines via the
X DDDDBBBBUUUUGGGG____PPPPUUUUSSSSHHHH macro. This particular string enables output from
X the DDDDBBBBUUUUGGGG____PPPPRRRRIIIINNNNTTTT macro with the 'd' flag and enables function
X tracing with the 't' flag. The factorial function is then
X called three times, with the arguments "1", "2", and "3".
X Note that the DBUG_PRINT takes exactly ttttwwwwoooo arguments, with
X the second argument (a format string and list of printable
X values) enclosed in parenthesis.
X
X Debug control strings consist of a header, the "-#",
X followed by a colon separated list of debugger arguments.
X Each debugger argument is a single character flag followed
X by an optional comma separated list of arguments specific to
X the given flag. Some examples are:
X
X
X
X
X
X - 7 -
X
X
X
X
X
X
X
X DBUG User Manual October 29, 1986
X
X
X
X -#d:t:o
X -#d,in,out:f,main:F:L
X
X Note that previously enabled debugger actions can be
X disabled by the control string "-#".
X
X
X The definition of the factorial function, symbolized as
X "N!", is given by:
X
X N! = N * N-1 * ... 2 * 1
X
X Figure 5 is the factorial function which implements this
X algorithm recursively. Note that this is not necessarily
X the best way to do factorials and error conditions are
X ignored completely.
X
X
X
X #include <stdio.h>
X /* User programs should use <local/dbug.h> */
X #include "dbug.h"
X
X int factorial (value)
X register int value;
X {
X DBUG_ENTER ("factorial");
X DBUG_PRINT ("find", ("find %d factorial", value));
X if (value > 1) {
X value *= factorial (value - 1);
X }
X DBUG_PRINT ("result", ("result is %d", value));
X DBUG_RETURN (value);
X }
X
X
X Figure 5
X Factorial Function
X
X
X
X
X
X One advantage (some may not consider it so) to using
X the _d_b_u_g package is that it strongly encourages fully
X structured coding with only one entry and one exit point in
X each function. Multiple exit points, such as early returns
X to escape a loop, may be used, but each such point requires
X the use of an appropriate DDDDBBBBUUUUGGGG____RRRREEEETTTTUUUURRRRNNNN or DDDDBBBBUUUUGGGG____VVVVOOOOIIIIDDDD____RRRREEEETTTTUUUURRRRNNNN
X macro.
X
X
X
X
X - 8 -
X
X
X
X
X
X
X
X DBUG User Manual October 29, 1986
X
X
X
X To build the factorial program on a UUUUNNNNIIIIXXXX system,
X compile and link with the command:
X
X cc -o factorial main.c factorial.c -ldbug
X
X The "-ldbug" argument tells the loader to link in the
X runtime support modules for the _d_b_u_g package. Executing the
X factorial program with a command of the form:
X
X factorial 1 2 3 4 5
X
X generates the output shown in figure 6.
X
X
X
X 1
X 2
X 6
X 24
X 120
X
X
X Figure 6
X factorial 1 2 3 4 5
SHAR_EOF
true || echo 'restore of dmake/dbug/dbug/dbug.p failed'
fi
echo 'End of part 3, continue with part 4'
echo 4 > _shar_seq_.tmp
exit 0
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent at sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent at uunet.uu.net.
More information about the Comp.sources.misc
mailing list