diff options
Diffstat (limited to 'dmake/dbug')
38 files changed, 9134 insertions, 0 deletions
diff --git a/dmake/dbug/dbug.mk b/dmake/dbug/dbug.mk new file mode 100644 index 000000000000..09aa82d7f37d --- /dev/null +++ b/dmake/dbug/dbug.mk @@ -0,0 +1,66 @@ +# Set the proper macros based on whether we are making the debugging version +# or not. The valid parameters to this file are: +# +# DEBUG=1 ==> enable the making of the DBUG version +# DBMALLOC=1 ==> enable DBUG version with extensive MALLOC checking +# +# DB_CFLAGS ==> CFLAGS is set to this value at the end if DEBUG=1 +# DB_LDFLAGS ==> LDFLAGS is set to this at the end if DEBUG=1 +# DB_LDLIBS ==> LDLIBS is set to this at end if DEBUG=1 +# +# The non debug versions of the above three macros are: +# +# NDB_CFLAGS +# NDB_LDFLAGS +# NDB_LDLIBS +# +# One of the set of three should have values set appropriately prior to +# sourcing this file. + +.IF $(DEBUG) + DBUG_SRC += dbug.c + DB_CFLAGS += -Idbug/dbug + + .SETDIR=dbug/dbug : $(DBUG_SRC) + + # If DBMALLOC is requested (ie non-NULL) then include the sources for + # compilation. BSD 4.3 needs the getwd.c source compiled in due to a bug + # in the clib getwd routine. + .IF $(DBMALLOC) + # Serious bug in bsd43 getwd.c would free a string and then use its + # value. The DBMALLOC code clears a string when it is free'd so the + # value was no longer valid and the returned path for the current + # directory was now completely wrong. + .IF $(OSRELEASE) == bsd43 + GETWD_SRC += getwd.c + .SETDIR=dbug : $(GETWD_SRC) + .END + + MLC_SRC += malloc.c free.c realloc.c calloc.c string.c\ + mlc_chk.c mlc_chn.c memory.c tostring.c m_perror.c\ + m_init.c mallopt.c dump.c + + .SETDIR=dbug/malloc : $(MLC_SRC) + + DB_CFLAGS += -Idbug/malloc + .END + + SRC += $(DBUG_SRC) $(MLC_SRC) $(GETWD_SRC) + HDR += db.h + + LDFLAGS += $(DB_LDFLAGS) + LDLIBS += $(DB_LDLIBS) + + __.SILENT !:= $(.SILENT) + .SILENT !:= yes + TARGET := db$(TARGET) + OBJDIR := $(OBJDIR).dbg + .SILENT !:= $(__.SILENT) + + CFLAGS += $(DB_CFLAGS) + .KEEP_STATE := _dbstate.mk +.ELSE + CFLAGS += $(NDB_CFLAGS) + LDFLAGS += $(NDB_LDFLAGS) + LDLIBS += $(NDB_LDLIBS) +.END diff --git a/dmake/dbug/dbug/dbug.c b/dmake/dbug/dbug/dbug.c new file mode 100644 index 000000000000..e63a4750ed5b --- /dev/null +++ b/dmake/dbug/dbug/dbug.c @@ -0,0 +1,1845 @@ +/****************************************************************************** + * * + * N O T I C E * + * * + * Copyright Abandoned, 1987, Fred Fish * + * * + * * + * This previously copyrighted work has been placed into the public * + * domain by the author and may be freely used for any purpose, * + * private or commercial. * + * * + * Because of the number of inquiries I was receiving about the use * + * of this product in commercially developed works I have decided to * + * simply make it public domain to further its unrestricted use. I * + * specifically would be most happy to see this material become a * + * part of the standard Unix distributions by AT&T and the Berkeley * + * Computer Science Research Group, and a standard part of the GNU * + * system from the Free Software Foundation. * + * * + * I would appreciate it, as a courtesy, if this notice is left in * + * all copies and derivative works. Thank you. * + * * + * The author makes no warranty of any kind with respect to this * + * product and explicitly disclaims any implied warranties of mer- * + * chantability or fitness for any particular purpose. * + * * + ****************************************************************************** + */ + + +/* + * FILE + * + * dbug.c runtime support routines for dbug package + * + * SCCS + * + * @(#)dbug.c 1.19 9/5/87 + * + * DESCRIPTION + * + * These are the runtime support routines for the dbug package. + * The dbug package has two main components; the user include + * file containing various macro definitions, and the runtime + * support routines which are called from the macro expansions. + * + * Externally visible functions in the runtime support module + * use the naming convention pattern "_db_xx...xx_", thus + * they are unlikely to collide with user defined function names. + * + * AUTHOR(S) + * + * Fred Fish (base code) + * (Currently at Motorola Computer Division, Tempe, Az.) + * hao!noao!mcdsun!fnf + * (602) 438-3614 + * + * Binayak Banerjee (profiling enhancements) + * seismo!bpa!sjuvax!bbanerje + */ + + +#include <stdio.h> +#ifdef amiga +#define AMIGA +#endif + +#ifdef AMIGA +#define HZ (50) /* Probably in some header somewhere */ +#endif + +/* + * Manifest constants that should not require any changes. + */ + +#define FALSE 0 /* Boolean FALSE */ +#define TRUE 1 /* Boolean TRUE */ +#define EOS '\000' /* End Of String marker */ + +/* + * Manifest constants which may be "tuned" if desired. + */ + +#define PRINTBUF 1024 /* Print buffer size */ +#define INDENT 4 /* Indentation per trace level */ +#define MAXDEPTH 200 /* Maximum trace depth default */ + +/* + * The following flags are used to determine which + * capabilities the user has enabled with the state + * push macro. + */ + +#define TRACE_ON 000001 /* Trace enabled */ +#define DEBUG_ON 000002 /* Debug enabled */ +#define FILE_ON 000004 /* File name print enabled */ +#define LINE_ON 000010 /* Line number print enabled */ +#define DEPTH_ON 000020 /* Function nest level print enabled */ +#define PROCESS_ON 000040 /* Process name print enabled */ +#define NUMBER_ON 000100 /* Number each line of output */ +#define PROFILE_ON 000200 /* Print out profiling code */ + +#define TRACING (stack -> flags & TRACE_ON) +#define DEBUGGING (stack -> flags & DEBUG_ON) +#define PROFILING (stack -> flags & PROFILE_ON) +#define STREQ(a,b) (strcmp(a,b) == 0) + +/* + * Typedefs to make things more obvious. + */ + +#define VOID void /* Can't use typedef for most compilers */ +typedef int BOOLEAN; + +/* + * Make it easy to change storage classes if necessary. + */ + +#define LOCAL static /* Names not needed by outside world */ +#define IMPORT extern /* Names defined externally */ +#define EXPORT /* Allocated here, available globally */ +#define AUTO auto /* Names to be allocated on stack */ +#define REGISTER register /* Names to be placed in registers */ + +/* + * The following define is for the variable arguments kluge, see + * the comments in _db_doprnt_(). + * + * Also note that the longer this list, the less prone to failing + * on long argument lists, but the more stuff that must be moved + * around for each call to the runtime support routines. The + * length may really be critical if the machine convention is + * to pass arguments in registers. + * + * Note that the default define allows up to 16 integral arguments, + * or 8 floating point arguments (doubles), on most machines. + * + * Someday this may be replaced with true varargs support, when + * ANSI C has had time to take root. + */ + +#define ARGLIST a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 + +/* + * The default file for profiling. Could also add another flag + * (G?) which allowed the user to specify this. + */ + +#define PROF_FILE "dbugmon.out" + +/* + * Variables which are available externally but should only + * be accessed via the macro package facilities. + */ + +EXPORT FILE *_db_fp_ = (FILE *)0; /* Output stream, default is set to + * stderr later */ +EXPORT FILE *_db_pfp_ = (FILE *)0; /* Profile stream, 'dbugmon.out' */ +EXPORT char *_db_process_ = "dbug"; /* Pointer to process name; argv[0] */ +EXPORT BOOLEAN _db_on_ = FALSE; /* TRUE if debugging currently on */ +EXPORT BOOLEAN _db_pon_ = FALSE; /* TRUE if debugging currently on */ + +/* + * Externally supplied functions. + */ + +/* Disable the manual definitions, if something is missing use #include's! */ +#if 0 + +#ifdef unix /* Only needed for unix */ +IMPORT VOID perror (); /* Print system/library error */ +IMPORT int chown (); /* Change owner of a file */ +IMPORT int getgid (); /* Get real group id */ +IMPORT int getuid (); /* Get real user id */ +IMPORT int access (); /* Test file for access */ +#else +#if !(AMIGA || LATTICE || __TURBOC__) +LOCAL VOID perror (); /* Fake system/library error print routine */ +#endif +#endif + +# if BSD4_3 || sun +IMPORT int getrusage (); +#endif + +IMPORT int atoi (); /* Convert ascii to integer */ +IMPORT VOID exit (); /* Terminate execution */ +IMPORT int fclose (); /* Close a stream */ +IMPORT FILE *fopen (); /* Open a stream */ +#if !defined(__BORLANDC__) +IMPORT int fprintf (); /* Formatted print on file */ +#endif +IMPORT VOID free (); +IMPORT char *malloc (); /* Allocate memory */ +IMPORT int strcmp (); /* Compare strings */ +IMPORT char *strcpy (); /* Copy strings around */ +IMPORT int strlen (); /* Find length of string */ + +#ifndef fflush /* This is sometimes a macro */ +IMPORT int fflush (); /* Flush output for stream */ +#endif + +#endif + + +/* + * The user may specify a list of functions to trace or + * debug. These lists are kept in a linear linked list, + * a very simple implementation. + */ + +struct link { + char *string; /* Pointer to link's contents */ + struct link *next_link; /* Pointer to the next link */ +}; + + +/* + * Debugging states can be pushed or popped off of a + * stack which is implemented as a linked list. Note + * that the head of the list is the current state and the + * stack is pushed by adding a new state to the head of the + * list or popped by removing the first link. + */ + +struct state { + int flags; /* Current state flags */ + int maxdepth; /* Current maximum trace depth */ + unsigned int delay; /* Delay after each output line */ + int level; /* Current function nesting level */ + FILE *out_file; /* Current output stream */ + FILE *prof_file; /* Current profiling stream */ + struct link *functions; /* List of functions */ + struct link *p_functions; /* List of profiled functions */ + struct link *keywords; /* List of debug keywords */ + struct link *processes; /* List of process names */ + struct state *next_state; /* Next state in the list */ +}; + +LOCAL struct state *stack = NULL; /* Linked list of stacked states */ + +/* + * Local variables not seen by user. + */ + +LOCAL int lineno = 0; /* Current debugger output line number */ +LOCAL char *func = "?func"; /* Name of current user function */ +LOCAL char *file = "?file"; /* Name of current user file */ +LOCAL BOOLEAN init_done = FALSE;/* Set to TRUE when initialization done */ + +/*#if unix || AMIGA || M_I86*/ +LOCAL int jmplevel; /* Remember nesting level at setjmp () */ +LOCAL char *jmpfunc; /* Remember current function for setjmp */ +LOCAL char *jmpfile; /* Remember current file for setjmp */ +/*#endif*/ + +LOCAL struct link *ListParse ();/* Parse a debug command string */ +LOCAL char *StrDup (); /* Make a fresh copy of a string */ +LOCAL VOID OpenFile (); /* Open debug output stream */ +LOCAL VOID OpenProfile (); /* Open profile output stream */ +LOCAL VOID CloseFile (); /* Close debug output stream */ +LOCAL VOID PushState (); /* Push current debug state */ +LOCAL VOID ChangeOwner (); /* Change file owner and group */ +LOCAL BOOLEAN DoTrace (); /* Test for tracing enabled */ +LOCAL BOOLEAN Writable (); /* Test to see if file is writable */ +LOCAL unsigned long Clock (); /* Return current user time (ms) */ +LOCAL char *DbugMalloc (); /* Allocate memory for runtime support */ +LOCAL char *BaseName (); /* Remove leading pathname components */ +LOCAL VOID DoPrefix (); /* Print debugger line prefix */ +LOCAL VOID FreeList (); /* Free memory from linked list */ +LOCAL VOID Indent (); /* Indent line to specified indent */ +LOCAL int DelayArg (int value); /* Convert D flag argument */ +LOCAL BOOLEAN DoProfile (); /* Check if profiling is enabled */ + + /* Supplied in Sys V runtime environ */ +LOCAL char *strtok (); /* Break string into tokens */ +LOCAL char *strrchr (); /* Find last occurance of char */ + +/* + * The following local variables are used to hold the state information + * between the call to _db_pargs_() and _db_doprnt_(), during + * expansion of the DBUG_PRINT macro. This is the only macro + * that currently uses these variables. The DBUG_PRINT macro + * and the new _db_doprnt_() routine replace the older DBUG_N macros + * and their corresponding runtime support routine _db_printf_(). + * + * These variables are currently used only by _db_pargs_() and + * _db_doprnt_(). + */ + +LOCAL int u_line = 0; /* User source code line number */ +LOCAL char *u_keyword = "?"; /* Keyword for current macro */ + +/* + * Miscellaneous printf format strings. + */ + +#define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n" +#define ERR_OPEN "%s: can't open debug output stream \"%s\": " +#define ERR_CLOSE "%s: can't close debug file: " +#define ERR_ABORT "%s: debugger aborting because %s\n" +#define ERR_CHOWN "%s: can't change owner/group of \"%s\": " +#define ERR_PRINTF "%s: obsolete object file for '%s', please recompile!\n" + +/* + * Macros and defines for testing file accessibility under UNIX. + */ + +#ifdef unix +# define A_EXISTS 00 /* Test for file existance */ +# define A_EXECUTE 01 /* Test for execute permission */ +# define A_WRITE 02 /* Test for write access */ +# define A_READ 03 /* Test for read access */ +# define EXISTS(pathname) (access (pathname, A_EXISTS) == 0) +# define WRITABLE(pathname) (access (pathname, A_WRITE) == 0) +#else +# define EXISTS(pathname) (FALSE) /* Assume no existance */ +#endif + +/* + * Translate some calls among different systems. + */ + +#ifdef unix +# define XDelay sleep +IMPORT unsigned int sleep (); /* Pause for given number of seconds */ +#endif + +#ifdef AMIGA +IMPORT int XDelay (); /* Pause for given number of ticks */ +#endif + + +/* + * FUNCTION + * + * _db_push_ push current debugger state and set up new one + * + * SYNOPSIS + * + * VOID _db_push_ (control) + * char *control; + * + * DESCRIPTION + * + * Given pointer to a debug control string in "control", pushes + * the current debug state, parses the control string, and sets + * up a new debug state. + * + * The only attribute of the new state inherited from the previous + * state is the current function nesting level. This can be + * overridden by using the "r" flag in the control string. + * + * The debug control string is a sequence of colon separated fields + * as follows: + * + * <field_1>:<field_2>:...:<field_N> + * + * Each field consists of a mandatory flag character followed by + * an optional "," and comma separated list of modifiers: + * + * flag[,modifier,modifier,...,modifier] + * + * The currently recognized flag characters are: + * + * d Enable output from DBUG_<N> macros for + * for the current state. May be followed + * by a list of keywords which selects output + * only for the DBUG macros with that keyword. + * A null list of keywords implies output for + * all macros. + * + * D Delay after each debugger output line. + * The argument is the number of tenths of seconds + * to delay, subject to machine capabilities. + * I.E. -#D,20 is delay two seconds. + * + * f Limit debugging and/or tracing, and profiling to the + * list of named functions. Note that a null list will + * disable all functions. The appropriate "d" or "t" + * flags must still be given, this flag only limits their + * actions if they are enabled. + * + * F Identify the source file name for each + * line of debug or trace output. + * + * g Enable profiling. Create a file called 'dbugmon.out' + * containing information that can be used to profile + * the program. May be followed by a list of keywords + * that select profiling only for the functions in that + * list. A null list implies that all functions are + * considered. + * + * L Identify the source file line number for + * each line of debug or trace output. + * + * n Print the current function nesting depth for + * each line of debug or trace output. + * + * N Number each line of dbug output. + * + * p Limit debugger actions to specified processes. + * A process must be identified with the + * DBUG_PROCESS macro and match one in the list + * for debugger actions to occur. + * + * P Print the current process name for each + * line of debug or trace output. + * + * r When pushing a new state, do not inherit + * the previous state's function nesting level. + * Useful when the output is to start at the + * left margin. + * + * t Enable function call/exit trace lines. + * May be followed by a list (containing only + * one modifier) giving a numeric maximum + * trace level, beyond which no output will + * occur for either debugging or tracing + * macros. The default is a compile time + * option. + * + * Some examples of debug control strings which might appear + * on a shell command line (the "-#" is typically used to + * introduce a control string to an application program) are: + * + * -#d:t + * -#d:f,main,subr1:F:L:t,20 + * -#d,input,output,files:n + * + * For convenience, any leading "-#" is stripped off. + * + */ + + +VOID _db_push_ (control) +char *control; +{ + REGISTER char *scan; + REGISTER struct link *temp; + + if (!_db_fp_) + _db_fp_ = stderr; /* Output stream, default stderr */ + + if (control && *control == '-') { + if (*++control == '#') { + control++; + } + } + control = StrDup (control); + PushState (); + scan = strtok (control, ":"); + for (; scan != NULL; scan = strtok ((char *)NULL, ":")) { + switch (*scan++) { + case 'd': + _db_on_ = TRUE; + stack -> flags |= DEBUG_ON; + if (*scan++ == ',') { + stack -> keywords = ListParse (scan); + } + break; + case 'D': + stack -> delay = 0; + if (*scan++ == ',') { + temp = ListParse (scan); + stack -> delay = DelayArg (atoi (temp -> string)); + FreeList (temp); + } + break; + case 'f': + if (*scan++ == ',') { + stack -> functions = ListParse (scan); + } + break; + case 'F': + stack -> flags |= FILE_ON; + break; + case 'g': + _db_pon_ = TRUE; + OpenProfile(PROF_FILE); + stack -> flags |= PROFILE_ON; + if (*scan++ == ',') { + stack -> p_functions = ListParse (scan); + } + break; + case 'L': + stack -> flags |= LINE_ON; + break; + case 'n': + stack -> flags |= DEPTH_ON; + break; + case 'N': + stack -> flags |= NUMBER_ON; + break; + case 'o': + if (*scan++ == ',') { + temp = ListParse (scan); + OpenFile (temp -> string); + FreeList (temp); + } else { + OpenFile ("-"); + } + break; + case 'p': + if (*scan++ == ',') { + stack -> processes = ListParse (scan); + } + break; + case 'P': + stack -> flags |= PROCESS_ON; + break; + case 'r': + stack -> level = 0; + break; + case 't': + stack -> flags |= TRACE_ON; + if (*scan++ == ',') { + temp = ListParse (scan); + stack -> maxdepth = atoi (temp -> string); + FreeList (temp); + } + break; + } + } + free (control); +} + + + +/* + * FUNCTION + * + * _db_pop_ pop the debug stack + * + * DESCRIPTION + * + * Pops the debug stack, returning the debug state to its + * condition prior to the most recent _db_push_ invocation. + * Note that the pop will fail if it would remove the last + * valid state from the stack. This prevents user errors + * in the push/pop sequence from screwing up the debugger. + * Maybe there should be some kind of warning printed if the + * user tries to pop too many states. + * + */ + +VOID _db_pop_ () +{ + REGISTER struct state *discard; + + discard = stack; + if (discard != NULL && discard -> next_state != NULL) { + stack = discard -> next_state; + _db_fp_ = stack -> out_file; + _db_pfp_ = stack -> prof_file; + if (discard -> keywords != NULL) { + FreeList (discard -> keywords); + } + if (discard -> functions != NULL) { + FreeList (discard -> functions); + } + if (discard -> processes != NULL) { + FreeList (discard -> processes); + } + if (discard -> p_functions != NULL) { + FreeList (discard -> p_functions); + } + CloseFile (discard -> out_file); + CloseFile (discard -> prof_file); + free ((char *) discard); + } +} + + +/* + * FUNCTION + * + * _db_enter_ process entry point to user function + * + * SYNOPSIS + * + * VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_) + * char *_func_; points to current function name + * char *_file_; points to current file name + * int _line_; called from source line number + * char **_sfunc_; save previous _func_ + * char **_sfile_; save previous _file_ + * int *_slevel_; save previous nesting level + * + * DESCRIPTION + * + * Called at the beginning of each user function to tell + * the debugger that a new function has been entered. + * Note that the pointers to the previous user function + * name and previous user file name are stored on the + * caller's stack (this is why the ENTER macro must be + * the first "executable" code in a function, since it + * allocates these storage locations). The previous nesting + * level is also stored on the callers stack for internal + * self consistency checks. + * + * Also prints a trace line if tracing is enabled and + * increments the current function nesting depth. + * + * Note that this mechanism allows the debugger to know + * what the current user function is at all times, without + * maintaining an internal stack for the function names. + * + */ + +VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_) +char *_func_; +char *_file_; +int _line_; +char **_sfunc_; +char **_sfile_; +int *_slevel_; +{ + if (!init_done) { + _db_push_ (""); + } + *_sfunc_ = func; + *_sfile_ = file; + func = _func_; + file = BaseName (_file_); + stack -> level++; + *_slevel_ = stack -> level; + if (DoProfile ()) { + (VOID) fprintf (_db_pfp_, "%s\tE\t%ld\n",func, Clock()); + (VOID) fflush (_db_pfp_); + } + if (DoTrace ()) { + DoPrefix (_line_); + Indent (stack -> level); + (VOID) fprintf (_db_fp_, ">%s\n", func); + (VOID) fflush (_db_fp_); + (VOID) XDelay (stack -> delay); + } +} + + +/* + * FUNCTION + * + * _db_return_ process exit from user function + * + * SYNOPSIS + * + * VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_) + * int _line_; current source line number + * char **_sfunc_; where previous _func_ is to be retrieved + * char **_sfile_; where previous _file_ is to be retrieved + * int *_slevel_; where previous level was stashed + * + * DESCRIPTION + * + * Called just before user function executes an explicit or implicit + * return. Prints a trace line if trace is enabled, decrements + * the current nesting level, and restores the current function and + * file names from the defunct function's stack. + * + */ + +VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_) +int _line_; +char **_sfunc_; +char **_sfile_; +int *_slevel_; +{ + if (!init_done) { + _db_push_ (""); + } + if (stack -> level != *_slevel_ && (TRACING || DEBUGGING || PROFILING)) { + (VOID) fprintf (_db_fp_, ERR_MISSING_RETURN, _db_process_, func); + (VOID) XDelay (stack -> delay); + } else if (DoProfile ()) { + (VOID) fprintf (_db_pfp_, "%s\tX\t%ld\n", func, Clock()); + (VOID) XDelay (stack -> delay); + } else if (DoTrace ()) { + DoPrefix (_line_); + Indent (stack -> level); + (VOID) fprintf (_db_fp_, "<%s\n", func); + (VOID) XDelay (stack -> delay); + } + (VOID) fflush (_db_fp_); + stack -> level = *_slevel_ - 1; + func = *_sfunc_; + file = *_sfile_; +} + + +/* + * FUNCTION + * + * _db_pargs_ log arguments for subsequent use by _db_doprnt_() + * + * SYNOPSIS + * + * VOID _db_pargs_ (_line_, keyword) + * int _line_; + * char *keyword; + * + * DESCRIPTION + * + * The new universal printing macro DBUG_PRINT, which replaces + * all forms of the DBUG_N macros, needs two calls to runtime + * support routines. The first, this function, remembers arguments + * that are used by the subsequent call to _db_doprnt_(). +* + */ + +VOID _db_pargs_ (_line_, keyword) +int _line_; +char *keyword; +{ + u_line = _line_; + u_keyword = keyword; +} + + +/* + * FUNCTION + * + * _db_doprnt_ handle print of debug lines + * + * SYNOPSIS + * + * VOID _db_doprnt_ (format, ARGLIST) + * char *format; + * long ARGLIST; + * + * DESCRIPTION + * + * When invoked via one of the DBUG macros, tests the current keyword + * set by calling _db_pargs_() to see if that macro has been selected + * for processing via the debugger control string, and if so, handles + * printing of the arguments via the format string. The line number + * of the DBUG macro in the source is found in u_line. + * + * Note that the format string SHOULD NOT include a terminating + * newline, this is supplied automatically. + * + * NOTES + * + * This runtime support routine replaces the older _db_printf_() + * routine which is temporarily kept around for compatibility. + * + * The rather ugly argument declaration is to handle some + * magic with respect to the number of arguments passed + * via the DBUG macros. The current maximum is 3 arguments + * (not including the keyword and format strings). + * + * The new <varargs.h> facility is not yet common enough to + * convert to it quite yet... + * + */ + +/*VARARGS1*/ +VOID _db_doprnt_ (format, ARGLIST) +char *format; +long ARGLIST; +{ + if (_db_keyword_ (u_keyword)) { + DoPrefix (u_line); + if (TRACING) { + Indent (stack -> level + 1); + } else { + (VOID) fprintf (_db_fp_, "%s: ", func); + } + (VOID) fprintf (_db_fp_, "%s: ", u_keyword); + (VOID) fprintf (_db_fp_, format, ARGLIST); + (VOID) fprintf (_db_fp_, "\n"); + (VOID) fflush (_db_fp_); + (VOID) XDelay (stack -> delay); + } +} + +/* + * The following routine is kept around temporarily for compatibility + * with older objects that were compiled with the DBUG_N macro form + * of the print routine. It will print a warning message on first + * usage. It will go away in subsequent releases... + */ + +/*VARARGS3*/ +VOID _db_printf_ (_line_, keyword, format, ARGLIST) +int _line_; +char *keyword, *format; +long ARGLIST; +{ + static BOOLEAN firsttime = TRUE; + + if (firsttime) { + (VOID) fprintf (stderr, ERR_PRINTF, _db_process_, file); + firsttime = FALSE; + } + _db_pargs_ (_line_, keyword); + _db_doprnt_ (format, ARGLIST); +} + + +/* + * FUNCTION + * + * ListParse parse list of modifiers in debug control string + * + * SYNOPSIS + * + * LOCAL struct link *ListParse (ctlp) + * char *ctlp; + * + * DESCRIPTION + * + * Given pointer to a comma separated list of strings in "cltp", + * parses the list, building a list and returning a pointer to it. + * The original comma separated list is destroyed in the process of + * building the linked list, thus it had better be a duplicate + * if it is important. + * + * Note that since each link is added at the head of the list, + * the final list will be in "reverse order", which is not + * significant for our usage here. + * + */ + +LOCAL struct link *ListParse (ctlp) +char *ctlp; +{ + REGISTER char *start; + REGISTER struct link *new; + REGISTER struct link *head; + + head = NULL; + while (*ctlp != EOS) { + start = ctlp; + while (*ctlp != EOS && *ctlp != ',') { + ctlp++; + } + if (*ctlp == ',') { + *ctlp++ = EOS; + } + new = (struct link *) DbugMalloc (sizeof (struct link)); + new -> string = StrDup (start); + new -> next_link = head; + head = new; + } + return (head); +} + + +/* + * FUNCTION + * + * InList test a given string for member of a given list + * + * SYNOPSIS + * + * LOCAL BOOLEAN InList (linkp, cp) + * struct link *linkp; + * char *cp; + * + * DESCRIPTION + * + * Tests the string pointed to by "cp" to determine if it is in + * the list pointed to by "linkp". Linkp points to the first + * link in the list. If linkp is NULL then the string is treated + * as if it is in the list (I.E all strings are in the null list). + * This may seem rather strange at first but leads to the desired + * operation if no list is given. The net effect is that all + * strings will be accepted when there is no list, and when there + * is a list, only those strings in the list will be accepted. + * + */ + +LOCAL BOOLEAN InList (linkp, cp) +struct link *linkp; +char *cp; +{ + REGISTER struct link *scan; + REGISTER BOOLEAN accept; + + if (linkp == NULL) { + accept = TRUE; + } else { + accept = FALSE; + for (scan = linkp; scan != NULL; scan = scan -> next_link) { + if (STREQ (scan -> string, cp)) { + accept = TRUE; + break; + } + } + } + return (accept); +} + + +/* + * FUNCTION + * + * PushState push current state onto stack and set up new one + * + * SYNOPSIS + * + * LOCAL VOID PushState () + * + * DESCRIPTION + * + * Pushes the current state on the state stack, and initializes + * a new state. The only parameter inherited from the previous + * state is the function nesting level. This action can be + * inhibited if desired, via the "r" flag. + * + * The state stack is a linked list of states, with the new + * state added at the head. This allows the stack to grow + * to the limits of memory if necessary. + * + */ + +LOCAL VOID PushState () +{ + REGISTER struct state *new; + + new = (struct state *) DbugMalloc (sizeof (struct state)); + new -> flags = 0; + new -> delay = 0; + new -> maxdepth = MAXDEPTH; + if (stack != NULL) { + new -> level = stack -> level; + } else { + new -> level = 0; + } + new -> out_file = stderr; + new -> functions = NULL; + new -> p_functions = NULL; + new -> keywords = NULL; + new -> processes = NULL; + new -> next_state = stack; + stack = new; + init_done = TRUE; +} + + +/* + * FUNCTION + * + * DoTrace check to see if tracing is current enabled + * + * SYNOPSIS + * + * LOCAL BOOLEAN DoTrace () + * + * DESCRIPTION + * + * Checks to see if tracing is enabled based on whether the + * user has specified tracing, the maximum trace depth has + * not yet been reached, the current function is selected, + * and the current process is selected. Returns TRUE if + * tracing is enabled, FALSE otherwise. + * + */ + +LOCAL BOOLEAN DoTrace () +{ + REGISTER BOOLEAN trace; + + trace = FALSE; + if (TRACING) { + if (stack -> level <= stack -> maxdepth) { + if (InList (stack -> functions, func)) { + if (InList (stack -> processes, _db_process_)) { + trace = TRUE; + } + } + } + } + return (trace); +} + + +/* + * FUNCTION + * + * DoProfile check to see if profiling is current enabled + * + * SYNOPSIS + * + * LOCAL BOOLEAN DoProfile () + * + * DESCRIPTION + * + * Checks to see if profiling is enabled based on whether the + * user has specified profiling, the maximum trace depth has + * not yet been reached, the current function is selected, + * and the current process is selected. Returns TRUE if + * profiling is enabled, FALSE otherwise. + * + */ + +LOCAL BOOLEAN DoProfile () +{ + REGISTER BOOLEAN profile; + + profile = FALSE; + if (PROFILING) { + if (stack -> level <= stack -> maxdepth) { + if (InList (stack -> p_functions, func)) { + if (InList (stack -> processes, _db_process_)) { + profile = TRUE; + } + } + } + } + return (profile); +} + + +/* + * FUNCTION + * + * _db_keyword_ test keyword for member of keyword list + * + * SYNOPSIS + * + * BOOLEAN _db_keyword_ (keyword) + * char *keyword; + * + * DESCRIPTION + * + * Test a keyword to determine if it is in the currently active + * keyword list. As with the function list, a keyword is accepted + * if the list is null, otherwise it must match one of the list + * members. When debugging is not on, no keywords are accepted. + * After the maximum trace level is exceeded, no keywords are + * accepted (this behavior subject to change). Additionally, + * the current function and process must be accepted based on + * their respective lists. + * + * Returns TRUE if keyword accepted, FALSE otherwise. + * + */ + +BOOLEAN _db_keyword_ (keyword) +char *keyword; +{ + REGISTER BOOLEAN accept; + + if (!init_done) { + _db_push_ (""); + } + accept = FALSE; + if (DEBUGGING) { + if (stack -> level <= stack -> maxdepth) { + if (InList (stack -> functions, func)) { + if (InList (stack -> keywords, keyword)) { + if (InList (stack -> processes, _db_process_)) { + accept = TRUE; + } + } + } + } + } + return (accept); +} + + +/* + * FUNCTION + * + * Indent indent a line to the given indentation level + * + * SYNOPSIS + * + * LOCAL VOID Indent (indent) + * int indent; + * + * DESCRIPTION + * + * Indent a line to the given level. Note that this is + * a simple minded but portable implementation. + * There are better ways. + * + * Also, the indent must be scaled by the compile time option + * of character positions per nesting level. + * + */ + +LOCAL VOID Indent (indent) +int indent; +{ + REGISTER int count; + AUTO char buffer[PRINTBUF]; + + indent *= INDENT; + for (count = 0; (count < (indent - INDENT)) && (count < (PRINTBUF - 1)); count++) { + if ((count % INDENT) == 0) { + buffer[count] = '|'; + } else { + buffer[count] = ' '; + } + } + buffer[count] = EOS; + (VOID) fprintf (_db_fp_, buffer); + (VOID) fflush (_db_fp_); +} + + +/* + * FUNCTION + * + * FreeList free all memory associated with a linked list + * + * SYNOPSIS + * + * LOCAL VOID FreeList (linkp) + * struct link *linkp; + * + * DESCRIPTION + * + * Given pointer to the head of a linked list, frees all + * memory held by the list and the members of the list. + * + */ + +LOCAL VOID FreeList (linkp) +struct link *linkp; +{ + REGISTER struct link *old; + + while (linkp != NULL) { + old = linkp; + linkp = linkp -> next_link; + if (old -> string != NULL) { + free (old -> string); + } + free ((char *) old); + } +} + + +/* + * FUNCTION + * + * StrDup make a duplicate of a string in new memory + * + * SYNOPSIS + * + * LOCAL char *StrDup (string) + * char *string; + * + * DESCRIPTION + * + * Given pointer to a string, allocates sufficient memory to make + * a duplicate copy, and copies the string to the newly allocated + * memory. Failure to allocated sufficient memory is immediately + * fatal. + * + */ + + +LOCAL char *StrDup (string) +char *string; +{ + REGISTER char *new; + + new = DbugMalloc (strlen (string) + 1); + (VOID) strcpy (new, string); + return (new); +} + + +/* + * FUNCTION + * + * DoPrefix print debugger line prefix prior to indentation + * + * SYNOPSIS + * + * LOCAL VOID DoPrefix (_line_) + * int _line_; + * + * DESCRIPTION + * + * Print prefix common to all debugger output lines, prior to + * doing indentation if necessary. Print such information as + * current process name, current source file name and line number, + * and current function nesting depth. + * + */ + + +LOCAL VOID DoPrefix (_line_) +int _line_; +{ + lineno++; + if (stack -> flags & NUMBER_ON) { + (VOID) fprintf (_db_fp_, "%5d: ", lineno); + } + if (stack -> flags & PROCESS_ON) { + (VOID) fprintf (_db_fp_, "%s: ", _db_process_); + } + if (stack -> flags & FILE_ON) { + (VOID) fprintf (_db_fp_, "%14s: ", file); + } + if (stack -> flags & LINE_ON) { + (VOID) fprintf (_db_fp_, "%5d: ", _line_); + } + if (stack -> flags & DEPTH_ON) { + (VOID) fprintf (_db_fp_, "%4d: ", stack -> level); + } + (VOID) fflush (_db_fp_); +} + + +/* + * FUNCTION + * + * OpenFile open new output stream for debugger output + * + * SYNOPSIS + * + * LOCAL VOID OpenFile (name) + * char *name; + * + * DESCRIPTION + * + * Given name of a new file (or "-" for stdout) opens the file + * and sets the output stream to the new file. + * + */ + +LOCAL VOID OpenFile (name) +char *name; +{ + REGISTER FILE *fp; + REGISTER BOOLEAN newfile; + + if (name != NULL) { + if (strcmp (name, "-") == 0) { + _db_fp_ = stdout; + stack -> out_file = _db_fp_; + } else { + if (!Writable (name)) { + (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name); + perror (""); + (VOID) fflush (_db_fp_); + (VOID) XDelay (stack -> delay); + } else { + if (EXISTS (name)) { + newfile = FALSE; + } else { + newfile = TRUE; + } + fp = fopen (name, "a"); + if (fp == NULL) { + (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name); + perror (""); + (VOID) fflush (_db_fp_); + (VOID) XDelay (stack -> delay); + } else { + _db_fp_ = fp; + stack -> out_file = fp; + if (newfile) { + ChangeOwner (name); + } + } + } + } + } +} + + +/* + * FUNCTION + * + * OpenProfile open new output stream for profiler output + * + * SYNOPSIS + * + * LOCAL VOID OpenProfile (name) + * char *name; + * + * DESCRIPTION + * + * Given name of a new file, opens the file + * and sets the profiler output stream to the new file. + * + * It is currently unclear whether the prefered behavior is + * to truncate any existing file, or simply append to it. + * The latter behavior would be desirable for collecting + * accumulated runtime history over a number of separate + * runs. It might take some changes to the analyzer program + * though, and the notes that Binayak sent with the profiling + * diffs indicated that append was the normal mode, but this + * does not appear to agree with the actual code. I haven't + * investigated at this time [fnf; 24-Jul-87]. + */ + +LOCAL VOID OpenProfile (name) +char *name; +{ + REGISTER FILE *fp; + REGISTER BOOLEAN newfile; + + if (name != NULL) { + if (!Writable (name)) { + (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name); + perror (""); + (VOID) fflush (_db_fp_); + (VOID) XDelay (stack -> delay); + } else { + if (EXISTS (name)) { + newfile = FALSE; + } else { + newfile = TRUE; + } + fp = fopen (name, "w"); + if (fp == NULL) { + (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name); + perror (""); + (VOID) fflush (_db_fp_); + (VOID) XDelay (stack -> delay); + } else { + _db_pfp_ = fp; + stack -> prof_file = fp; + if (newfile) { + ChangeOwner (name); + } + } + } + } +} + + +/* + * FUNCTION + * + * CloseFile close the debug output stream + * + * SYNOPSIS + * + * LOCAL VOID CloseFile (fp) + * FILE *fp; + * + * DESCRIPTION + * + * Closes the debug output stream unless it is standard output + * or standard error. + * + */ + +LOCAL VOID CloseFile (fp) +FILE *fp; +{ + if (fp != stderr && fp != stdout) { + if (fclose (fp) == EOF) { + (VOID) fprintf (stderr, ERR_CLOSE, _db_process_); + perror (""); + (VOID) fflush (stderr); + (VOID) XDelay (stack -> delay); + } + } +} + + +/* + * FUNCTION + * + * DbugExit print error message and exit + * + * SYNOPSIS + * + * LOCAL VOID DbugExit (why) + * char *why; + * + * DESCRIPTION + * + * Prints error message using current process name, the reason for + * aborting (typically out of memory), and exits with status 1. + * This should probably be changed to use a status code + * defined in the user's debugger include file. + * + */ + +LOCAL VOID DbugExit (why) +char *why; +{ + (VOID) fprintf (stderr, ERR_ABORT, _db_process_, why); + (VOID) fflush (stderr); + (VOID) XDelay (stack -> delay); + exit (1); +} + + +/* + * FUNCTION + * + * DbugMalloc allocate memory for debugger runtime support + * + * SYNOPSIS + * + * LOCAL char *DbugMalloc (size) + * int size; + * + * DESCRIPTION + * + * Allocate more memory for debugger runtime support functions. + * Failure to to allocate the requested number of bytes is + * immediately fatal to the current process. This may be + * rather unfriendly behavior. It might be better to simply + * print a warning message, freeze the current debugger state, + * and continue execution. + * + */ + +LOCAL char *DbugMalloc (size) +int size; +{ + register char *new; + + new = malloc ( size ); + if (new == NULL) { + DbugExit ("out of memory"); + } + return (new); +} + + +/* + * This function may be eliminated when strtok is available + * in the runtime environment (missing from BSD4.1). + */ + +LOCAL char *strtok (s1, s2) +char *s1, *s2; +{ + static char *end = NULL; + REGISTER char *rtnval; + + rtnval = NULL; + if (s2 != NULL) { + if (s1 != NULL) { + end = s1; + rtnval = strtok ((char *) NULL, s2); + } else if (end != NULL) { + if (*end != EOS) { + rtnval = end; + while (*end != *s2 && *end != EOS) {end++;} + if (*end != EOS) { + *end++ = EOS; + } + } + } + } + return (rtnval); +} + + +/* + * FUNCTION + * + * BaseName strip leading pathname components from name + * + * SYNOPSIS + * + * LOCAL char *BaseName (pathname) + * char *pathname; + * + * DESCRIPTION + * + * Given pointer to a complete pathname, locates the base file + * name at the end of the pathname and returns a pointer to + * it. + * + */ + +LOCAL char *BaseName (pathname) +char *pathname; +{ + register char *base; + + base = strrchr (pathname, '/'); + if (base++ == NULL) { + base = pathname; + } + return (base); +} + + +/* + * FUNCTION + * + * Writable test to see if a pathname is writable/creatable + * + * SYNOPSIS + * + * LOCAL BOOLEAN Writable (pathname) + * char *pathname; + * + * DESCRIPTION + * + * Because the debugger might be linked in with a program that + * runs with the set-uid-bit (suid) set, we have to be careful + * about opening a user named file for debug output. This consists + * of checking the file for write access with the real user id, + * or checking the directory where the file will be created. + * + * Returns TRUE if the user would normally be allowed write or + * create access to the named file. Returns FALSE otherwise. + * + */ + +LOCAL BOOLEAN Writable (pathname) +char *pathname; +{ + REGISTER BOOLEAN granted; +#ifdef unix + REGISTER char *lastslash; +#endif + +#ifndef unix + granted = TRUE; +#else + granted = FALSE; + if (EXISTS (pathname)) { + if (WRITABLE (pathname)) { + granted = TRUE; + } + } else { + lastslash = strrchr (pathname, '/'); + if (lastslash != NULL) { + *lastslash = EOS; + } else { + pathname = "."; + } + if (WRITABLE (pathname)) { + granted = TRUE; + } + if (lastslash != NULL) { + *lastslash = '/'; + } + } +#endif + return (granted); +} + + +/* + * This function may be eliminated when strrchr is available + * in the runtime environment (missing from BSD4.1). + * Alternately, you can use rindex() on BSD systems. + */ + +LOCAL char *strrchr (s, c) +char *s; +char c; +{ + REGISTER char *scan; + + for (scan = s; *scan != EOS; scan++) {;} + while (scan > s && *--scan != c) {;} + if (*scan != c) { + scan = NULL; + } + return (scan); +} + + +/* + * FUNCTION + * + * ChangeOwner change owner to real user for suid programs + * + * SYNOPSIS + * + * LOCAL VOID ChangeOwner (pathname) + * + * DESCRIPTION + * + * For unix systems, change the owner of the newly created debug + * file to the real owner. This is strictly for the benefit of + * programs that are running with the set-user-id bit set. + * + * Note that at this point, the fact that pathname represents + * a newly created file has already been established. If the + * program that the debugger is linked to is not running with + * the suid bit set, then this operation is redundant (but + * harmless). + * + */ + +LOCAL VOID ChangeOwner (pathname) +char *pathname; +{ +#ifdef unix + if (chown (pathname, getuid (), getgid ()) == -1) { + (VOID) fprintf (stderr, ERR_CHOWN, _db_process_, pathname); + perror (""); + (VOID) fflush (stderr); + (VOID) XDelay (stack -> delay); + } +#endif +} + + +/* + * FUNCTION + * + * _db_setjmp_ save debugger environment + * + * SYNOPSIS + * + * VOID _db_setjmp_ () + * + * DESCRIPTION + * + * Invoked as part of the user's DBUG_SETJMP macro to save + * the debugger environment in parallel with saving the user's + * environment. + * + */ + +VOID _db_setjmp_ () +{ + jmplevel = stack -> level; + jmpfunc = func; + jmpfile = file; +} + + +/* + * FUNCTION + * + * _db_longjmp_ restore previously saved debugger environment + * + * SYNOPSIS + * + * VOID _db_longjmp_ () + * + * DESCRIPTION + * + * Invoked as part of the user's DBUG_LONGJMP macro to restore + * the debugger environment in parallel with restoring the user's + * previously saved environment. + * + */ + +VOID _db_longjmp_ () +{ + stack -> level = jmplevel; + if (jmpfunc) { + func = jmpfunc; + } + if (jmpfile) { + file = jmpfile; + } +} + + +/* + * FUNCTION + * + * DelayArg convert D flag argument to appropriate value + * + * SYNOPSIS + * + * LOCAL int DelayArg (value) + * int value; + * + * DESCRIPTION + * + * Converts delay argument, given in tenths of a second, to the + * appropriate numerical argument used by the system to delay + * that that many tenths of a second. For example, on the + * AMIGA, there is a system call "Delay()" which takes an + * argument in ticks (50 per second). On unix, the sleep + * command takes seconds. Thus a value of "10", for one + * second of delay, gets converted to 50 on the amiga, and 1 + * on unix. Other systems will need to use a timing loop. + * + */ + +LOCAL int DelayArg (value) +int value; +{ + int delayarg = 0; + +#ifdef unix + delayarg = value / 10; /* Delay is in seconds for sleep () */ +#endif +#ifdef AMIGA + delayarg = (HZ * value) / 10; /* Delay in ticks for XDelay () */ +#endif + return (delayarg); +} + + +/* + * A dummy delay stub for systems that do not support delays. + * With a little work, this can be turned into a timing loop. + */ + +#ifndef unix +#ifndef AMIGA +XDelay () +{ +} +#endif +#endif + + +/* + * FUNCTION + * + * perror perror simulation for systems that don't have it + * + * SYNOPSIS + * + * LOCAL VOID perror (s) + * char *s; + * + * DESCRIPTION + * + * Perror produces a message on the standard error stream which + * provides more information about the library or system error + * just encountered. The argument string s is printed, followed + * by a ':', a blank, and then a message and a newline. + * + * An undocumented feature of the unix perror is that if the string + * 's' is a null string (NOT a NULL pointer!), then the ':' and + * blank are not printed. + * + * This version just complains about an "unknown system error". + * + */ + +#if !unix && !(AMIGA || LATTICE || __TURBOC__ ) +LOCAL VOID perror (s) +#if __STDC__ +const char *s; +#else +char *s; +#endif +{ + if (s && *s != EOS) { + (VOID) fprintf (stderr, "%s: ", s); + } + (VOID) fprintf (stderr, "<unknown system error>\n"); +} +#endif /* !unix && !(AMIGA && LATTICE) */ + +/* + * Here we need the definitions of the clock routine. Add your + * own for whatever system that you have. + */ + +#if unix + +# include <sys/param.h> +# if !defined(Solaris) && (BSD4_3 || sun) + +/* + * Definition of the Clock() routine for 4.3 BSD. + */ + +#include <sys/time.h> +#include <sys/resource.h> + + +/* + * Returns the user time in milliseconds used by this process so + * far. + */ + +LOCAL unsigned long Clock () +{ + struct rusage ru; + + (VOID) getrusage (RUSAGE_SELF, &ru); + return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000)); +} + +#else + +LOCAL unsigned long Clock () +{ + return (0); +} + +# endif + +#else + +#if AMIGA + +struct DateStamp { /* Yes, this is a hack, but doing it right */ + long ds_Days; /* is incredibly ugly without splitting this */ + long ds_Minute; /* off into a separate file */ + long ds_Tick; +}; + +static int first_clock = TRUE; +static struct DateStamp begin; +static struct DateStamp elapsed; + +LOCAL unsigned long Clock () +{ + register struct DateStamp *now; + register unsigned long millisec = 0; + extern VOID *AllocMem (); + + now = (struct DateStamp *) AllocMem ((long) sizeof (struct DateStamp), 0L); + if (now != NULL) { + if (first_clock == TRUE) { + first_clock = FALSE; + (VOID) DateStamp (now); + begin = *now; + } + (VOID) DateStamp (now); + millisec = 24 * 3600 * (1000 / HZ) * (now -> ds_Days - begin.ds_Days); + millisec += 60 * (1000 / HZ) * (now -> ds_Minute - begin.ds_Minute); + millisec += (1000 / HZ) * (now -> ds_Tick - begin.ds_Tick); + (VOID) FreeMem (now, (long) sizeof (struct DateStamp)); + } + return (millisec); +} + +#else + +LOCAL unsigned long Clock () +{ + return (0); +} + +#endif /* AMIGA */ + +#endif /* unix */ + +#ifdef AMIGA +XDelay(x) +int x; +{ + if (x) Delay(x); /* fix Delay bug in AmigaDOS */ +} +#endif + diff --git a/dmake/dbug/dbug/dbug.h b/dmake/dbug/dbug/dbug.h new file mode 100644 index 000000000000..0f171e0d349f --- /dev/null +++ b/dmake/dbug/dbug/dbug.h @@ -0,0 +1,164 @@ +/****************************************************************************** + * * + * N O T I C E * + * * + * Copyright Abandoned, 1987, Fred Fish * + * * + * * + * This previously copyrighted work has been placed into the public * + * domain by the author and may be freely used for any purpose, * + * private or commercial. * + * * + * Because of the number of inquiries I was receiving about the use * + * of this product in commercially developed works I have decided to * + * simply make it public domain to further its unrestricted use. I * + * specifically would be most happy to see this material become a * + * part of the standard Unix distributions by AT&T and the Berkeley * + * Computer Science Research Group, and a standard part of the GNU * + * system from the Free Software Foundation. * + * * + * I would appreciate it, as a courtesy, if this notice is left in * + * all copies and derivative works. Thank you. * + * * + * The author makes no warranty of any kind with respect to this * + * product and explicitly disclaims any implied warranties of mer- * + * chantability or fitness for any particular purpose. * + * * + ****************************************************************************** + */ + + +/* + * FILE + * + * dbug.h user include file for programs using the dbug package + * + * SYNOPSIS + * + * #include <local/dbug.h> + * + * SCCS ID + * + * @(#)dbug.h 1.11 9/5/87 + * + * DESCRIPTION + * + * Programs which use the dbug package must include this file. + * It contains the appropriate macros to call support routines + * in the dbug runtime library. + * + * To disable compilation of the macro expansions define the + * preprocessor symbol "DBUG_OFF". This will result in null + * macros expansions so that the resulting code will be smaller + * and faster. (The difference may be smaller than you think + * so this step is recommended only when absolutely necessary). + * In general, tradeoffs between space and efficiency are + * decided in favor of efficiency since space is seldom a + * problem on the new machines). + * + * All externally visible symbol names follow the pattern + * "_db_xxx..xx_" to minimize the possibility of a dbug package + * symbol colliding with a user defined symbol. + * + * The DBUG_<N> style macros are obsolete and should not be used + * in new code. Macros to map them to instances of DBUG_PRINT + * are provided for compatibility with older code. They may go + * away completely in subsequent releases. + * + * AUTHOR + * + * Fred Fish + * (Currently employed by Motorola Computer Division, Tempe, Az.) + * hao!noao!mcdsun!fnf + * (602) 438-3614 + * + */ + + +/* + * Internally used dbug variables which must be global. + */ + +#ifndef DBUG_OFF + extern int _db_on_; /* TRUE if debug currently enabled */ + extern FILE *_db_fp_; /* Current debug output stream */ + extern char *_db_process_; /* Name of current process */ + extern int _db_keyword_ (); /* Accept/reject keyword */ + extern void _db_push_ (); /* Push state, set up new state */ + extern void _db_pop_ (); /* Pop previous debug state */ + extern void _db_enter_ (); /* New user function entered */ + extern void _db_return_ (); /* User function return */ + extern void _db_pargs_ (); /* Remember args for line */ + extern void _db_doprnt_ (); /* Print debug output */ + extern void _db_setjmp_ (); /* Save debugger environment */ + extern void _db_longjmp_ (); /* Restore debugger environment */ +# endif + + +/* + * These macros provide a user interface into functions in the + * dbug runtime support library. They isolate users from changes + * in the MACROS and/or runtime support. + * + * The symbols "__LINE__" and "__FILE__" are expanded by the + * preprocessor to the current source file line number and file + * name respectively. + * + * WARNING --- Because the DBUG_ENTER macro allocates space on + * the user function's stack, it must precede any executable + * statements in the user function. + * + */ + +# 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) \ + auto char *_db_func_, *_db_file_; \ + int _db_level_; \ + _db_enter_ (a,__FILE__,__LINE__,&_db_func_,&_db_file_,&_db_level_) +# define DBUG_MALLOC(a) \ + auto char *_db_func_, *_db_file_; \ + int _db_level_; \ + malloc_init();\ + _db_enter_ (a,__FILE__,__LINE__,&_db_func_,&_db_file_,&_db_level_) +# define DBUG_LEAVE \ + (_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) \ + {if (_db_on_) {if (_db_keyword_ (keyword)) { a1 }}} +# define DBUG_PRINT(keyword,arglist) \ + {if (_db_on_) {_db_pargs_(__LINE__,keyword); _db_doprnt_ arglist;}} +# define DBUG_2(keyword,format) \ + DBUG_PRINT(keyword,(format)) /* Obsolete */ +# define DBUG_3(keyword,format,a1) \ + DBUG_PRINT(keyword,(format,a1)) /* Obsolete */ +# define DBUG_4(keyword,format,a1,a2) \ + DBUG_PRINT(keyword,(format,a1,a2)) /* Obsolete */ +# define DBUG_5(keyword,format,a1,a2,a3) \ + 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 + diff --git a/dmake/dbug/dbug/dbug.txt b/dmake/dbug/dbug/dbug.txt new file mode 100755 index 000000000000..ec032f61ebff --- /dev/null +++ b/dmake/dbug/dbug/dbug.txt @@ -0,0 +1,1452 @@ + + + + + + + D B U G + + C Program Debugging Package + + by + Fred Fish + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 1 - + + + + + + DBUG User Manual October 29, 1986 + + + + INTRODUCTION + + + 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 adb and sdb debuggers + provided with the UNIX1 operating system. + + + 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. + + + On the other hand, 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 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. + + + The 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 + + ____________________ + + 1. UNIX is a trademark of AT&T Bell Laboratories. + + - 2 - + + + + + + DBUG User Manual October 29, 1986 + + + + code. + + + The following list is a quick summary of the + capabilities of the dbug package. Each capability can be + individually enabled or disabled at the time a program is + invoked by specifying the appropriate command line + arguments. + + o Execution trace showing function level control + flow in a semi-graphically manner using + indentation to indicate nesting depth. + + o Output the values of all, or any subset of, key + internal variables. + + o Limit actions to a specific set of named + functions. + + o Limit function trace to a specified nesting depth. + + o Label each output line with source file name and + line number. + + o Label each output line with name of current + process. + + o Push or pop internal debugging state to allow + execution with built in debugging defaults. + + o 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. + + + + + + + + + + + + + + + + + + + + + - 3 - + + + + + + DBUG User Manual October 29, 1986 + + + + PRIMITIVE DEBUGGING TECHNIQUES + + + 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. + + + + #include <stdio.h> + + main (argc, argv) + int argc; + char *argv[]; + { + printf ("argv[0] = %d\n", argv[0]); + /* + * Rest of program + */ + printf ("== done ==\n"); + } + + + Figure 1 + Primitive Debugging Technique + + + + + + 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. + + + + + + - 4 - + + + + + + DBUG User Manual October 29, 1986 + + + + #include <stdio.h> + + int debug = 0; + + main (argc, argv) + int argc; + char *argv[]; + { + /* printf ("argv = %x\n", argv) */ + if (debug) printf ("argv[0] = %d\n", argv[0]); + /* + * Rest of program + */ + #ifdef DEBUG + printf ("== done ==\n"); + #endif + } + + + Figure 2 + Debug Disable Techniques + + + + + + 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 #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 #ifdef arguments to match the + indentation of the code, but not all C preprocessors allow + this. The only requirement for the standard 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. + + + + + + + + + + + + + + + + - 5 - + + + + + + DBUG User Manual October 29, 1986 + + + + #include <stdio.h> + + main (argc, argv) + int argc; + char *argv[]; + { + # ifdef DEBUG + printf ("argv[0] = %d\n", argv[0]); + # endif + /* + * Rest of program + */ + # ifdef DEBUG + printf ("== done ==\n"); + # endif + } + + + Figure 3 + More Readable Preprocessor Usage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 6 - + + + + + + DBUG User Manual October 29, 1986 + + + + FUNCTION TRACE EXAMPLE + + + We will start off learning about the capabilities of + the 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. + + + + #include <stdio.h> + /* User programs should use <local/dbug.h> */ + #include "dbug.h" + + int main (argc, argv) + int argc; + char *argv[]; + { + register int result, ix; + extern int factorial (), atoi (); + + DBUG_ENTER ("main"); + DBUG_PROCESS (argv[0]); + for (ix = 1; ix < argc && argv[ix][0] == '-'; ix++) { + switch (argv[ix][1]) { + case '#': + DBUG_PUSH (&(argv[ix][2])); + break; + } + } + for (; ix < argc; ix++) { + DBUG_PRINT ("args", ("argv[%d] = %s", ix, argv[ix])); + result = factorial (atoi (argv[ix])); + printf ("%d\n", result); + } + DBUG_RETURN (0); + } + + + Figure 4 + Factorial Program Mainline + + + + + + The main function is responsible for processing any + command line option arguments and then computing and + printing the factorial of each non-option argument. + + First of all, notice that all of the debugger functions + are implemented via preprocessor macros. This does not + + + - 7 - + + + + + + DBUG User Manual October 29, 1986 + + + + detract from the readability of the code and makes disabling + all debug compilation trivial (a single preprocessor symbol, + DBUG_OFF, forces the macro expansions to be null). + + Also notice the inclusion of the header file 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 DBUG_XX...XX. + + + The DBUG_ENTER macro informs that debugger that we have + entered the function named main. It must be the very first + "executable" line in a function, after all declarations and + before any other executable line. The DBUG_PROCESS macro is + generally used only once per program to inform the debugger + what name the program was invoked with. The 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 DBUG_PRINT macro is used to + print the values of each argument for which a factorial is + to be computed. The 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. + + To use the debugger, the factorial program is invoked + with a command line of the form: + + factorial -#d:t 1 2 3 + + The main function recognizes the "-#d:t" string as a + debugger control string, and passes the debugger arguments + ("d:t") to the dbug runtime support routines via the + DBUG_PUSH macro. This particular string enables output from + the 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 two arguments, with + the second argument (a format string and list of printable + values) enclosed in parenthesis. + + 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: + + -#d:t:o + -#d,in,out:f,main:F:L + + Note that previously enabled debugger actions can be + disabled by the control string "-#". + + + + - 8 - + + + + + + DBUG User Manual October 29, 1986 + + + + The definition of the factorial function, symbolized as + "N!", is given by: + + N! = N * N-1 * ... 2 * 1 + + 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. + + + + #include <stdio.h> + /* User programs should use <local/dbug.h> */ + #include "dbug.h" + + int factorial (value) + register int value; + { + DBUG_ENTER ("factorial"); + DBUG_PRINT ("find", ("find %d factorial", value)); + if (value > 1) { + value *= factorial (value - 1); + } + DBUG_PRINT ("result", ("result is %d", value)); + DBUG_RETURN (value); + } + + + Figure 5 + Factorial Function + + + + + + One advantage (some may not consider it so) to using + the 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 DBUG_RETURN or DBUG_VOID_RETURN + macro. + + + To build the factorial program on a UNIX system, + compile and link with the command: + + cc -o factorial main.c factorial.c -ldbug + + The "-ldbug" argument tells the loader to link in the + runtime support modules for the dbug package. Executing the + factorial program with a command of the form: + + + + - 9 - + + + + + + DBUG User Manual October 29, 1986 + + + + factorial 1 2 3 4 5 + + generates the output shown in figure 6. + + + + 1 + 2 + 6 + 24 + 120 + Figure 6 + factorial 1 2 3 4 5 + + + + + + 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". + + + + | >factorial + | | >factorial + | | <factorial + | <factorial + 2 + | >factorial + | | >factorial + | | | >factorial + | | | <factorial + | | <factorial + | <factorial + 6 + <main + + + Figure 7 + factorial -#t:o 3 2 + + + + + + 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. + + + This trace output indicates that there was an initial + + + - 10 - + + + + + + DBUG User Manual October 29, 1986 + + + + 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. + + + Note that there is no matching entry point "main>" for + the return point "<main" because at the time the DBUG_ENTER + macro was reached in main, tracing was not enabled yet. It + was only after the macro 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 DBUG_PUSH is essentially + invisible to dbug (this can be worked around by inserting a + temporary DBUG_PUSH(argv[1]) immediately after the + DBUG_ENTER("main") macro. + + + 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 UNIX implementation + details, programs usually run faster when writing to stdout + rather than stderr, though this is not a prime consideration + in this example. + + + + + + + + + + + + + + + + + + + + + + - 11 - + + + + + + DBUG User Manual October 29, 1986 + + + + USE OF DBUG_PRINT MACRO + + + The mechanism used to produce "printf" style output is + the DBUG_PRINT macro. + + + To allow selection of output from specific macros, the + first argument to every DBUG_PRINT macro is a 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 DBUG_PRINT + macros. + + + 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. + + + The second argument to a 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 DBUG_PRINT + macros are preferable to a single macro with a huge format + string. Figure 8 shows the output for default tracing and + debug. + + + + + + + + + + + + + + + + + + + + + + - 12 - + + + + + + DBUG User Manual October 29, 1986 + + + + | args: argv[2] = 3 + | >factorial + | | find: find 3 factorial + | | >factorial + | | | find: find 2 factorial + | | | >factorial + | | | | find: find 1 factorial + | | | | result: result is 1 + | | | <factorial + | | | result: result is 2 + | | <factorial + | | result: result is 6 + | <factorial + 6 + <main + + + Figure 8 + factorial -#d:t:o 3 + + + + + + The output from the 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. + + + 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". + + + + factorial: result: result is 1 + factorial: result: result is 2 + factorial: result: result is 6 + factorial: result: result is 24 + 24 + Figure 9 + factorial -#d,result:o 4 + + + + + + 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 + + + - 13 - + + + + + + DBUG User Manual October 29, 1986 + + + + the source file name and the 'L' flag enables printing of + the source file line number. + + + + factorial.c: 9: factorial: find: find 3 factorial + factorial.c: 9: factorial: find: find 2 factorial + factorial.c: 9: factorial: find: find 1 factorial + factorial.c: 13: factorial: result: result is 1 + factorial.c: 13: factorial: result: result is 2 + factorial.c: 13: factorial: result: result is 6 + 6 + + + Figure 10 + factorial -#d:f,factorial:F:L:o 3 + + + + + + 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 14 - + + + + + + DBUG User Manual October 29, 1986 + + + + SUMMARY OF MACROS + + + This section summarizes the usage of all currently + defined macros in the dbug package. The macros definitions + are found in the user include file dbug.h from the standard + include directory. + + + + 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 + dbug package runtime support module. + + EX: DBUG_ENTER ("main"); + + 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). + + EX: DBUG_RETURN (value); + EX: DBUG_VOID_RETURN; + + 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. + + EX: DBUG_PROCESS (argv[0]); + + DBUG_PUSH Sets a new debugger state by pushing the + current 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 + + + - 15 - + + + + + + DBUG User Manual October 29, 1986 + + + + 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 not be + passed as part of the macro argument. + The proper usage is to pass a pointer to + the first character after the "-#" + string. + + EX: DBUG_PUSH ((argv[i][2])); + EX: DBUG_PUSH ("d:t"); + EX: DBUG_PUSH (""); + + 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. + + EX: DBUG_POP (); + + 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. + + EX: fprintf (DBUG_FILE, "Doing my own + I/O!\n"); + + 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). + + EX: DBUG_EXECUTE ("abort", abort ()); + + 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. + + 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 + + + - 16 - + + + + + + DBUG User Manual October 29, 1986 + + + + argument list. Note that the format + string and argument list are all one + macro argument and must be enclosed in + parenthesis. + + EX: DBUG_PRINT ("eof", ("end of file found")); + EX: DBUG_PRINT ("type", ("type is %x", + type)); + EX: DBUG_PRINT ("stp", ("%x -> %s", stp, + stp -> name)); + + 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. + + EX: DBUG_SETJMP (env); + + 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. + + EX: DBUG_LONGJMP (env,val); + + + + + + + + + + + + + + + + + + - 17 - + + + + + + DBUG User Manual October 29, 1986 + + + + DEBUG CONTROL STRING + + + The debug control string is used to set the state of + the debugger via the 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. + + + d[,keywords] Enable output from macros with + specified keywords. A null list of + keywords implies that all keywords are + selected. + + 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. + + f[,functions] Limit debugger actions to the + specified list of functions. A null + list of functions implies that all + functions are selected. + + F Mark each debugger output line with + the name of the source file containing + the macro causing the output. + + L Mark each debugger output line with + the source file line number of the + macro causing the output. + + n Mark each debugger output line with + the current function nesting depth. + + N Sequentially number each debugger + output line starting at 1. This is + useful for reference purposes when + debugger output is interspersed with + program output. + + 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. + + 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 + + + - 18 - + + + + + + DBUG User Manual October 29, 1986 + + + + 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 + DBUG_PROCESS macro. + + 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. + + r Used in conjunction with the DBUG_PUSH + macro to reset the current indentation + level back to zero. Most useful with + DBUG_PUSH macros used to temporarily + alter the debugger state. + + t[,N] Enable function control flow tracing. + The maximum nesting depth is specified + by N, and defaults to 200. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 19 - + + + + + + DBUG User Manual October 29, 1986 + + + + HINTS AND MISCELLANEOUS + + + One of the most useful capabilities of the 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. + + + 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. + + + It is not difficult to modify an existing compiler to + implement some of the functionality of the 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + - 20 - + + + + + + DBUG User Manual October 29, 1986 + + + + CAVEATS + + + The 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. + + + Programs which use memory allocation functions other + than malloc will usually have problems using the standard + dbug package. The most common problem is multiply allocated + memory. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 21 - + + + + + + + + + D B U G + C Program Debugging Package + + by + Fred Fish + + + + ABSTRACT + + + This document introduces 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. + + + All of the features of the 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. + + + 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 dbug package should not be thought of as a + replacement or substitute for other debugging tools, but + simply as a useful addition to the program development and + maintenance environment. + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 22 - + + diff --git a/dmake/dbug/dbug/dbug.uue b/dmake/dbug/dbug/dbug.uue new file mode 100644 index 000000000000..da8743b7e128 --- /dev/null +++ b/dmake/dbug/dbug/dbug.uue @@ -0,0 +1,368 @@ +begin 650 dbug.Z +M'YV,"@*"&$BPH,&#" T2 2$$1!401P0FG&AP" @H<MZ<D1.F#0@B9<34.7,F +MC9LS%\.,61/F3!F)%&/*!"$F#\R9. <:D5.&# @C:>:@"4@481($1Y,Z0;"T +M*14$3Z-*03"UZA,$5[,20;"U:Q4$7\,.03"V;%2H4),B19H5*]:F3)D6C1F$ +M39LW<^B *&.GC)P\(.!DW-@1!!F^9=B\@=.FC!N]CNVDR>BF\6,0=][(H8,& +M,(@W9A0@Y%QFH)N.I06_D7QX#H@Y;QJ_UJP7M.&0(TN>!&%&91HV:>CD<3&P +MRIPZ8=BPL3D:35 0=,*L*>.:]$ SFCU^-C,PS$#5A+7?<3X&S<#G(,:$@1-& +M#)O2VT4?'//F<4;E)E'NQ5-F3!TZ:=0WD&V?D29'8(-QU(9K8;CA4QEX=&12 +M?M"A\1)")M'AUVELO!:=AML5:.&!$/;W'X7@*3@'<2!08>$<I0D'!W4#AH90 +MBAVY=L=O'8I1&D]F^,633W2\ 4(8KD&HH1P<WB8227ZY-H=)8\1HH7P&'?:D +M2P<^Y\8;>K&W68C6E>@?@+OAV :+1438!ASON19?<^C)"!^!6N;FUWENC,%& +M'8<-9%T8"!!J*!D((*JH& @PZNB1#KZ&P!R33JIHHHDZVFBC3NHIQQQ8%J0: +M:SUA%IQYUH4%%EAP-:564E@@$.NL&^ 0 P*WYKI!#I_-R!&:*,V11UYEK#D7 +M14^X49IMUJGF7K$,S@';&&F$H:%/.W)V6U^*P8%B@CF&2I!)W;DQ4&23U6>9 +M7J9J"\(9;[SATWY+-MDIE)^"\!QGUHH[T&'<+E:JFG)J&"ET1H* 1AA]#00< +M'72\-^"!('S)9[W)^:NO&WG)4<>ZU@;H!HLM.B<G>G4<E]QRIH'9'8)O/.L1 +M=@>2IC' B0E\X)1]EF:AOG.P\-F!UCI<!I+L"O5&'6S,ZZ/0&P/8AK6),7=0 +M;VW\5BW%V9I7(7P9;IC<T&<TF(8>(=<G-!G6>M=Q'6/040=/06O<H$_TV?=& +MAV8H=@>9/ZM)4QD4WNL2&20GP2YZ86@,HQP!IDR0L^]IQYMF TT=]FD]?P=N +M&XU]"O5X"8?!D\9UN '<="P7J3##I1U7'F\2 F=Z<)YU+>C/,(<WD(_Y::SY +M8V&8U!/4=Y&1AAEI'#]T8+25ZGJ#8(ZXEQN24;8NBXIK_%QRL!T)L\Q"(V8N +MS5]OF[.O0H_G6/J]*^B]G'?U-:_KD%Z?O;J.Z:6[=X))0]8 U##,I0YK6C/= +MS7"#+Z%ASGP5,U*>\.6"8TTD6>FKWIX6YB"A?2%1'Q3#!^OP091\SUP0Z@B< +M[L2=_$5-;&Q8X);\PB(A]"<,*2M-<$# DSC483(T"AN3QF:2MX$,0 *R3>-N +M]#FAW4U?=)!3REH"'S*\@4819-=A9G0P 4'*,W.8$;4RAA#UL$<,6@,0%@ED +MG?V8"(GFTE^Z*M,_J.WP>VRX0QB&93<[%(\-[9'8$W>DG'/QYTQ@BV.#/(,N +M[?5O=T7[FL8$!T48L8$[A.R0F?ZCPY$-) GF"@,9E ='H?&L2E $VI$T1I\W +MO0=$@5$)2UPR$-V)[S5B7%X:QC"0'%),B+VI$M2.I)Q)?DY.F"&/UW)X'G9E +M\G?P&54:#C,O6TXI:X"4@PQS0R$SMB>-S5O10%STG'*:2RBF@T-ZZD,'CN0% +M80,RD 4)\H5ZVO.>-RE(#(A3!2<D 0NJ]$X[15DLTZTA1$&@@@FHP)#$=(@) +M[=&,M303S@H216,YD4D+0! #$+3@HA=%"!&$4(4C..1Q(&A"@Y#3H8S&Y EQ +MB]F>9) #H<4@!SBP04BOEAW7V$:(32IB.S_6O[29RWT\V8M*O(:S;NWI=:!J +M#N$H]H8[N$%H*6,IR^+D&F\^2V@T\\A/'P-#P]%08Z!\S<>F]A>A^4@]S/P@ +M&4(XPA(&U%Q '=L$N91*:;:&E?69$K'Z!!B59$1:O?)+R';32/X])FB0\LE; +M<0BC5 :ECW\,9(R,M#+U.35?._0K%O,F6(.-P3.&Q4M4#])9@FTL?7- C1P= +M^1B+8K0@+BJ-7.F* !(B "7L60D5]?4FO-"H/BQS&W#.@ :]A+$, W-,<H3S +M&3=H;)/ BE^.A":C7:X,,&V FWFX6J$&<10&@?%+E1[C1 ?9#03)HXYSSY8: +MZ;)!.%#KKGJ4XYD8H!>>()#!?WVU7CJ03 A6RU(9F.>&PKDME\SCI44$4P;5 +M5$E:F!-6&\2P-YI4#W#4*8UZK(L0'_&P)W"3GI'TX!<CZ<YBY6F02US3PQ\. +M25 ):R-LYE8EP![&MC+)[77VYK?" >>=JKRE#W=YT($<!W2F\TR(=G>A^:SG +MFP\+9XVH/)#=-JJNOXVE<%W"IJ6FY\IH?!A@1CPX[SDH#:S1*F"D^RR?8$YY +ML:WSD?1B':G!QSO:U<YE,82]-TQG7C21\G.I988\% Y^1X*#:@)(M8&T<FH' +M XZR[":',Q#UL1;-B9':]$:108<CJ'P-&JI:.#.D+FZF!L%[N&5I=M[GM@GI +M6U4+0JZ7.;E8:6@!8>! GN\2!-/*HA@(4A8\EYKD,,2#(SR?[5T0*2LO%-HB +M9T*-$R,]X3]P^$_Z_/BGXW)G90XDFAO 6 <QP(A=H(':=!*,D[QVR(^0T^R* +M\DD1(S%!@#L\$JP#BV-?+UJ7O/PUNZ9<,=20 =<(<76?X+AOB!?$WP#7B\0' +M;JZ!HG)ZN.R/+DMU[>QJ&PW<GHF_VY,8I<YN:70(MUXT71K=[5@.J&2>Q!HN +MF\A:W" TK]C'?"2'E,MDY3YJZ=%F]QEPBUO6QJOEJ4PCVRG[1PX\><S/1961 +M"U=<U!=)F7DP!X?%7*RLABM<7BH-X)55=>L$P:ZI=2>2W^B%7'O-]H)Q>-^O +M=QL$4N@)$./&Y7L-R.E):^?1M(._M3O(= ]W*<QE/A 4Y,6*_TG!Q'Q]FL;, +M2^=E8)&0#],;IM$![H>/N;@[MO@D.YYM<IB7D# ' LO3X3!83P')<HOZ$RN/ +M)QR';W\X&!2//*>5*]00RYY=8<= NS8VRNB7Y#"U#EWZB4$?TN"EW78[O&&: +MJ9=Y[_/6MUU&T;;S=*E& ^Q1D*;_(R0UJ7'VI%(WL%3]R(HIT0-<4X[B5*?O +M!P4((( $6!54016O@A1-@ +V( )F 1G$14/: 4(0($66 0(@($:^!%<T8%$ +MH($9F(%"@ C6(*J$A9'@ IN((KJ((J^("MPA0MN((M@A91 8(:6!9D019( +M@ ]^(,QZ$]KD111@ !%>(0G"!8XF(%3@ !-^(3O9Q"@A#$=(E0><T2FAD<\ +M(4J%13O8M#6UUC-P<'JC821W\4YJ$CJ0=4HZ='>ND57&9AW,\REZD7<GH3$: +M4AX-YD/P\1ZFHRR(@UMYX"W[M1S#) (!=!EKIR'K,@<B0!"F4V4&441^<2U\ +M EM+@W,B)B^E431")%\H\GV/Q5T_0Q^',3\GAGR_42I/A%W.$UF%1S>F5SB3 +M]W2#=A IE#6 ^%KF92V,.(;P=!A+HHLU-R)6DAH9(3- 8W0$(606AF))54Y+ +M$C)],3I3YW)>$QR*Q7V.EAC*<X<(<1=)!8OPI1EW@GA0\T0*(S"NQE^%=W.H +MQ"Q7PD0Q4SFJA&?TT1<WYB,T<XQ-IWIZ\5:Q02-%8B391$M%LD!CTRY>0WL4 +M)AE+,P<LLS&/8XD>0C6-:!K0121&XB,:PQ/UTQ,L A2>EE0==4+[H4(2(X_[ +M,H@LY#D9!V>26!!VB!)YB 9[6 >AMU,Y,0)4\B>!P@.7%R N@ 8^P&\Q,3RU +M9SIG, 9.U&EVD *]ER%'TFECL /CMS 'H@),:0=;T 58Z5)[T'M<ER'<@0(B +MT)5; -= (] (E0 9<X 8B\)1GX)5LF0)AB7\@\ (J0)8%H0(#$7CO9!MJ +M I@$H0(O@)@(8I:U)P(]\)96I"QNV0-S*0)ZV7M]$(5\B1 D.3>E$0.,61 8 +M$9,- Q);0B%4,'PY^1(@)1-%T!>/(6?I>#!PZ(Y'8@9+ HD+YX=(]AJ(P1$Q +MA"'32''YQ3N44RQ25TB#<SFIXQ-/1!]8UQ_7PB)!0(8'P1G/47890HJEH2QW +M,)&4N!FETIB7<9&,V#_TDS+LXI$(<1C3)77;1A#)LBQB )&2 QM_(FW[8B37 +M!"> <1BO=(QM8#?*44-EH!O)]H8JXXY65"'?PQME\#=^]H:/\1M?XQD+TQ=X +MF# \,2.15"87FE3/B%C!"1A^)C[@21,C 3UP\(9P4$$((60PDC<^<8:U<9^1 +M(R5[<R("TI^ST1BK]C>&$12:164:4XMT #6$8R"[DQ$CX34ZEHFH9(J;"#J/ +M%$QIE <LP$I;*&U*9'CX5BW/ D\F-@?94AZEDD0' AIFD&Z)ACH\HQ]M9!$( +M4F%=1QVP470$\9E))0-WE9*NM"SG5DS-P1,QPIIIP(<5AY2=>1 ;-0/MYWX: +M,U(E=5+TMU(+^:@% 5-%LG\T95/_YWXNY9-] I2E(92W1Y1&Z:@30943Y)8@ +M -8V7M*B0),Z915>9=2Z6R7D:M[F1,QMI5J"9:]-Y:=Z9?F20=GF990*:LE +M@ >7:9=1"0**R9C+4WL3I'F)R*R/J99L":UR29?4NI9=D)G)^I>/*IB )U\A +M<ICKNIBENCRD]Q%%$'^,V:W-&IF&41^E$9F7B:X9-0+.MSR:R9F<VJ=I4)*E +M(0.CJ1 ,]!%&6J:KJ8>+JI.@\IHQ401F=I.M"57-)">BY$?$,V/E,B]X-K(- +M$ATSIC&V1#=BQ"[X0P9YT'F[! )V("4?<K,J04!&94I4NHFGB!"KX1<6(DI" +M WRQX2V )&TU!D2-6#Y(0JB]!"-0 Z\'L85LDV:XPZ1T, 8D\P3ZR$Q3UD84 +M5J)[.AO[*3(:LV0KL55'0P;5823)N2 ,.1L\)K3PH;58ACLG*SS%0SPFT;?4 +ME4SO U^SN$(#,0((P+B.FP8( +F2:P8(0+F6>RF*4@8(H+F<:[F56[E.E@<; +MMC<B.XZNXYX'07K&8V<4\YP+MKJ_,[7SXD4'%V&"XI)R@B<,M$!3,B2B]S/P +MJ+=G9BXF-C6!(I$+VUPL(XX_$K>:I3$UH5;!<:3,%VVF)H^EX;B-V[B2&[F1 +MZ[F7BRF9N[GDNV"?:[F[^FE1!$]3\[6H,H\'4;T[ZT5L5(J<Z%;B]B5ZT5D3 +M]J%YBF'YXG9W@(<FLWN\@UR>\;0@^4B70S$Z%AV/%WO%L2IA$80/."NR(BL$ +M,03&A*=OX'68\QSH8QTG, (GD!Y:V;-[,CV2=C1=TG$_,X=Y 5A_T@97]3M/ +M!T$0*B<P BT.DP;3 8EQ5)5H-%!_(<21=Q"ILX6P<1IE>F(=8WXB0S)^"A^2 +MBI*YJ+@LN5H&,45D1JD:F[ )L5$T,*FD>A"6*G\H57_W)\8#X:DR=2"AZG\Y +M=<8]^9. DJI#^09%>92V&KA+:974VJL9197 FI6F8ZW%&JPX@:P#.RYF4*\? +MB*^=J:_?"I7F*J[3NJMX>:Z,/!,C$'=O%GWXYY>CR:Z$"7V!=LKR^LCG$<D+ +M9J^4S)>6C);\.IG_:IET*; ]*<K*0\HXL9FNZL95# (S\+!-8([M*DI'BA$> +M#,('8AQ4!,;47,UV[,8&L5$U8,9A7!!IC*D'PL:;*L9P#*K]=U-U_'Y&@ #K +MW,Y)V$]QT10Z:!8VF!9#R!9ND15!6(,1:(!5$00( - "/<]DL81% (&C<&S +M(M !'= -R( ,2( #.(!,@ 5?=$&C; #<04UQR,7.29N*FLNW&"[$5%/UT9> +M!4YK!,P$T49>)D*]95=B-DOP$;W+-J<O\Y\2HXO4%&C)M$M#449+RTG5$<,] +M2U%C$Z9"MV$T1!!I-32X5W"# S%^L4!WP3$#!4MMM''<YW&ET1@QUF!ST :D +MN)V? S3>,ZCK4BK -S=3PBW#H1,+"YH@4,;[\C-*R=6Q1C,$+,)'G6\=HB;H +M-Z^GFL<@H*K*P\>MZE+*.G\'XEI*PS0^P4P\H!C[]0)D\"1];*VM?,>%'2@B +MD-DC490B,,SA",BX*LB<3,@Y8<A6^<DR,:R*C,G&*I:,R1,E02POK!>R>%]" +MDP9X -LYH20;$C6T$U. 77LIX$1%D@;*7:N=F<9?4 1.0 5%( 6/.3R8*=PX +M(=U0( 5/, 1%, 53$,AW::Z\K'[H@P+ +:LQL /Z@@>'O:N\9 (FP,E; -Q= +MD,G\>@(M< +P#=PKL *:Y\B<BJ;!,3NI?=[ZO04Q<*X@8.!NK!Z55<(GH ,/ +M2Q'>7053@ 2U9P(+[I4-+@/GFM[8_#M;N ;<[5*;V9DMSI?K'>#RS0/T+>,# +M7N 9#G\E]07?G035?<EGX(A"@Y9J&9=M^98E(.3Q7:[ZG0(FSI>]K1=O&4Q% +MDMRXVMSF+>)X4.(KGE&U+ )QN<E1_N0Y\>+X)]U24 144 52X 2U!P-D3A'" +MG.,44<PT0.=&\-?5TB$8H1$*DE*!2W/7G!!"U@8(8.B(;BB%4BC=*[EN@ "/ +M'NF\\6K\*2<P&UAI\,3H S->YV#K!EA0=C!01YF]PGU,J;XB>S"D$4?(=R*[ +MX7-,E"$4(H=ZGM3<L73F\25NT *+(6VGOCT\>>($L5$VP,W!3A#?[-B ;G_C +MG+#E/%/G/*H:_1.349B%R@9"H[^[9"5%TUG8:U8'HM>!Q6E@H];]4YZ2 6AG +M^[]I"U^I)4[C9#+]2B/ZNT >IW$9X1'6P;=<2UW?;J6P.#73X1HIZQ[-QEJ% +M%*NJV+2FUDYPMN=+^1KY(3'K_L%ZFF&BRV'87JD>R!4E2((D^,XSJ((?5/(( +M\ 5MD17MS,[LO/+M#%::<6'I,S5CD!'[P1X<(S)SVYSVIQRZ9]H@4!?AH^T? +M]S-X/"5)U$)M=+11S1NK^"_BFRF;XBAUT%M6/Q*_E?5GX ((P/5>CP8( /9B +MKS%FD._I8]ECP_1[ GI%"GQ5'M>U)V3ZB/1X]=D]H3'&J$I]YJYSGX5QU$:B +MK1_!^R]!X? B 4<WLWV:D0>ZUXSRSO9Y$QU%1$P=8AVJVV 4U\"2])X,Q%<T +M?UCMHTR4_SH-(X?9P8%=X?$@;X(4#!8C?P0F7_(*+2NSCP5>W_5=?_M>K_M= +M7_NU#V0Q(62IWQ4?S_HG^/JQ?_(&'801"!4&[<]4X>XU;R0F$59%731[M<(+ +MXS\^ SO7158WEC[B;BZ=5RJ(?NB'KNB&TNB1&^F0#NG<HQ?A]4XF9AWZ"!@R +MC)T&(0+8I5F/&'3DXAWC[T2ZN2>==1A^8CI&E>I);!#\J,R+)")[TO]E2G._ +M6QK#K_K%+_(NN(+)S^,378 ':!7YC!4$+=XAJ(%/Z(1.^(1.Z(32;_.W:! N +MD6S&ED-VICJ 41^HY"L^[3K5?_J W_E5C1#C433EGSZ"HT>N81+>=V@,^;NE +M,?RJ7_PB[X(KF/P\/M$$^,Y/Z(1.^(,^Z(/2GQ'"(R\C5]0B-C=95X>=OR>+ +M6!K1&UN2\>IFRQ/XR<4%L8A_=C#OEETJ.J'H61IB(+O5E3Z1?Q\> CG@>!#L +M(2VLZ!K!P2"=IKZ_6QK#K_K%+_(NN(+)S^,378 '6!4P&,],T?P,)?TVGV0Y +M=#]O8$Q4"3_DAK$A@NOINRZ:/QY +3Y4CM15R,4%<;I7*G,B&>^E,?RJ7_PB +M[X(KF/Q? /UI'H(:V/QKWOIL?H!5L<^?GV,.53"\D_W:U!PARCO.MV76<759 +MIW&4;FH+XQH^\CY;J*;0Z5X(P1-RPR0,@K/)H9, AM(KTVJ4/L4$41<M5;\A +M!KC3KR,>;6+MR#(0 B>!6RKD<ASN5@9\J(C4J?/,.*-&PDR6W_E^X9W'7>40 +M3TGH81+>=V@N.W7><7V1$G3?'E883N=4CM0=T@(C0 8ZH!<=!:C'K#%"!E_H +MG^B+OO[>Z^CO+^F3/G&QIK1GT& L5C"\(P(M, )DH -T\(@> CFOCB3=L4WX +M$H;MU&$> CDGT5X^P1[20I _D_U$TVFHKC%H208Z0 >8*=4N#4)?%M-AQD.I +MDZ)J)6FT,1!0BB98E.Y(*E(=/U*K'_*M7U(NN(+)S^,338#O_(1.Z(0_Z(,^ +M*/T9\;O;:3H XA_9Y"&0PUA.'"?_2'EE'QL$7!K#K_K%+_(NN(+)S^,378 ' +M6!4P&,],T?P,]?DN-G4D3 8GW#<M 8MT%B?70>EL.QJH5CBV1,)T<,)]TQ*_ +M6QI4CM1\0^E96-2.P4HK(SUH@*C0(4#4T3Y3-RB=ANH@( (Q4)<#(0(R4)<G +M.Q B, ,B(*,'X01@ A_\(K,_X]U2X.,,%1T#GY)QTSH(0 <(0 <(0 =W@ !W +M@ !W@ "9@0!O@ !O@ "<U6FHWCZG0L"QTQ_U 9V=IKX1'U9%$\4E'2E'!F\M +MU*W/BQ#DAK&:YQA^8EP^02YBTC\O$A3,*%(1&_GWX2&0<Q)=A>G6?DMJ+P?' +M"1\BT (C4)=D3V1559XV#6CTH1CG5&&F0S4^<62JG/U$TVFH+J,'P;&SD_U$ +MTVGJ&U 2?Q(2,ZPJ3#%]TQ)#IAQ5=?<E5ECFTNLB,S;7!YQB0C4^<62J_.OJ +M&7*,=K,)*57O(I/G TA;/Q!3,)""ND(,PA,8WLW"[E$@< /&_G[)OL::VE)N +M_.QR'.WI#/0XT0(C0 8Z0 <Z\ 9TW@(C0 8L8!(L ',Z\*;#HP-&H -,@)1. +M ";PP2_L\I [.I%T]AXHV_E/U;,Z;VGF!4V)GV?O(5DH:K]ZTR%1O!LBT (C +M( + 3Q&C][J8?[W<0>O(#?'C;TH9OS?T!9U<7! BX 0A4)>J5!*RF6@8GN-. +M$ *RZN:"Z00MT%&"Z0(N0!R *IBBJ3'%O,UW71I4CM0M-?ZU)/K$M4(9N<,& +M"B^0PQG:T=:?(I/+03). ";PP2]\)N]>XC+*XG6WLQP$7!H^\DYZA*()\Z#' +M7>5[GNJRAW6T9Z/!H?/=<3H8<@;35RK(-Z#+,=@#B\=!N<=]W'N-C5*0O6J2 +M?=.IBO9L@-F:;92<W7NFZB>&'=J:C0:E/9670>5(W2$H0&XZR=HX@=N%OR=4 +M26XZV>4((>$N)=W4;=W8C994CM1LL-V,Z=U2X.,,A98,1@9U^9@,YA-Q>=Q5 +MON=UB;/)H9-.WN4QD:TH0&XZ"0(^P%$X+L;DII/6.N6UWB$H0&XZZ5$<%><S +M8>;JY]U2X.,,A9917I>/&>6J%)=UB;/)H9-.WN4Q@>9JSN9NC@+DII-Q/A%S +M+NP'4<PU0.<%D>?(#?%&0.ELV\T)49]'0@8DR[*E87D#Z>Z H;_KE/-1'7"P +MH7D QFS@F)V\\])@IA_!1=/G43#;WTQ.UD[U<083Z1CT,3=4Y!KMN!R.,U1Q +M YIX(R\4HCL(7%VET3]'_$3^NA^A)8J+0V*X:&:3/G%3/!!-D+@2 R&A)8I1 +M9$KB=21)8CHLPQ-RPR1<7!"N0QUFU(FR%B]P(#13 Q@FED-D@+^086:R8Q[< +M>9X*3!T$#!]D2R"]*&D906D@,ORJ7_PB[X(KF/Q? /UI'H(:V/QKWOIL?H!5 +ML<^8,_RJ7_PB[X(KF/Q?8($56($ICQ4/./Q<D?Q? /UI'H(:V/QKWOIL?H!5 +M$82 ._WH1^=C# (X8.SOE^QKK*DMY<;/+L?1GLY 3P4=^4--4WA4CM0M14E> +M!&CO;,'W_$\9/"NA2RQD#>I,VXF1HFD'I3O6<7T.@N&H-P:\Q.O'7>40/SPN +MP$M4CM1LX *\U )-\R1(*60BT )-\R2/^.L,K'QL4#"\HQ@$Y< )HVE-UFLV +M@Q ><Z&R<1R21AOE2 9,0R,C_#,O#6; )4M4Q":'Y.HV";\&0>5('=AG_3_K +M%.IVQAVF3WT83N?'7>40WU& *JEE7 -(Z1+)1C5%O2R(IVI55?=.S[ @8 .# +MG5&BZ5(.ZU(ZY5(R<.<N%0,"-NV<6LPZ)>S'7>40WU& *JEE7 .#[IF4'FNS +MUG*GYANOCAYT]AY.XQGL(2V%8S.</T,U\S,G0 <GW#<M\5J6'[&1?Q\> CDG +M,9)SG50W@(KPHZ0#$>6M9O;P<WT'(P)4CM0=\E$(,0)TH -&(JDR( +H9\V4 +M*NS9# *\\E&4*E+Q!\[+WL9N_.QR'.WI?.PRP0<$X0-4CM3#F5%\0!!\0! ^ +M0.5(/9P9Q0<$P0<$P0-4CM3#F5%\0! \0.5(/9P9Y; NQ0<$X0-4CM3#F5%\ +M0!!\0! ^0.5(/9P9Q0<$P0<$P0<$X0-4CM3#F5%\0!!\0!!\0! \0.5(/9P9 +MQ0<$P0<$P0-4CM3#F5%\0! \0.5(/9P9I5,NQ0/#,^V<6LPW0.<%0>5(W2$M +M, )TH -&(JD.V\T)P;&STS]'[#J8PQ-RPR2\8?;>,?ZJ1&WJ88DUH3$GX ,G +MK/GPTS]'##U428XGP ,GK/GP R&A)8I+"EANH"QQ4RK1JX\ LE\T83H[+\#N +M[KZ%PYV/)=6H:Q!',R4L@QW/:;CG5&&F0S62!1@'61IX!L$7!OP404[5@6K+ +M@G@;HSSJH2'7SV=YKT>ISB?!L>>LU%D 1N5(S3=F?ZNNT^H@(@,AL-R7HQQ5 +M52K1NT0',:<2T]:?(I-G5DBN0^5(7?E&TNH@$@,A\+OP,3S&Y.>%\35QI*2Q +M2!VF-V04(P,A<+*6MC+2 [\&0>5(W5+CWQUETVNVI&/]41_S\NMU-! S(*,' +MD>?(#?%GIASE&1R6U$)M_2DRV3I&TNH@(@,A (LQ$ )=*E7FPA-RPR2E$OGW +MP;Z!&_I _8^49__)H9.:/P,A8#>1 B':B#C3[@1@PNU\EO=>8H;64AX4TC]' +MS)V7(0+#XP./V, $O+=E(#=, CU4*0(\H-V# U>5531])D#^./RJ7_PB[X(K +MF/S3'8(:R/SU;-TAJ('0+P6 ._V8@20GME2E0B[#PUVH1B%ZY!KZ>SV:Y1-Y +M4 8&YM3Z7Q!ZE+NJ@UJZ^51M]/D),_RJ7_PB[X(KF/P\/M$$^,Y/Z(1.^(,^ +MZ(.UA"2&5&J[P2_Z7Q >1R&3)1MT]AZ!6#+EY$I:QB]\]C.__DA'IO\%$=E- +M TV>8_'24IY(<BZF,Y%( CW2DND2PX:C;Z4=7"7?2*>\D_^6UED -ORJ7_PB +M[X(KF/P\/M$$^,Y/Z(1.^(,^Z(/G@4QZVC][OAQN)AE3\L2N@[E2KRDB<?6] +M=09:'V8HH)U=95XFEAER<&A5N321$KWCF5U+E)W%4G8<<<0#,?RJ7_PB[X(K +MF/P\/M$$^,Y/Z(1.^(,^Z(,H@ H@ H@ "XNNB&(@<(( <(( <(T&E:_UMV +M@ !V@ !V@ !>B0!;@ !;@ .CBNX@BM=@ !=@ !=@ #GB@ I@ I@ ":=QY8 +MJCQ4,Y'=H9M/A:0BU?$CM?HAW_HEY8(KF/S3'8(:R/SU;-TAJ('0+P4H@ H +M@ H@ !HB0 B@ B@ B</Z)ONCK[[V._OZ1+@((( ((( ((@)D(D ((D (( +MH'F?S\?=+.P>=5[&_G[)OL::VE)N_.QR'.WI#/3U*6M(DT5EX)VGIA++@G@1 +M1'W&UDIK]'3U0<#P\7J0MQ=8IQDL,@54XH]4CM2!?=;=ZAK!P<4%$>75!5L0 +M#'MVAHY?$XU%'4VJE>E;6B,$?(X .2!_SSO#.+@M!3RO/@<UWQ%ZIB_<T6>9 +MX3B*ER-5^9TN(RQ]@@:405^!6#*28_F=OR?]V30XYK*0 R+!X5.(5UVP!<&P +M9V>(1Y$:@K0X6RWI<P)O< )D#T@H,:PJS*<#X01@ A_\PBX6 P(G\ 8G3%QP +MHF7P0WI\I_\%87MW WGG@G6:L=R^=@)O<,)W:S&_KI["UR Z>S>0EZ3H"(O> +M<0)O<,+_HTB=IKZ9<S0<4[7EV4;EG\0& 7J)XP)% #4B0.5(W2$M, )TH -O +MP **<09L+ZDR\(B8L31-@QF0 R(V,QJHMBR(]UHBH!AG 'HB@*!P55D$\LX6 +M?,__E,&S<A[F;KWU8>]_!%F!QJ!RQD.I0SNZ;;A'!3G9Y3J7!W,?:2W6PR_G +M='M"0HI+PUP[K$KZ*SX!)!ND-4W;R+:$ML."ND+H9\W6;,W'+NP;%0,=]5&4 +M*E+Q!\[+WL9N_.QR'.WIC)3O_(1.Z(0�(ICQ4NS\ZH[X'%'_*M7U(NN(+) +MS^,378 '6!4P&,],T?P,]= -R- #O8-E ?W@G<]8,>V%/GQF,]8WK6(P0P8I +M!@*("#F/80:/F!=YL)*(=QY<7!#6,?RJ7_PB[X(KF/P\/M$%>(!5 8/QS!3- +MSU"?S\?33@6<I1R[9DG4>;W<H:2\8?:U>[.?CQ?'.9,$D?_=T6GJ"T\GBOH> +M6/PAW_HEY8(KF/P\/M$%>(!5 8/QS!3-SU"?3_T, @(O#68H,6^9$7LR>A!7 +M8"$P_!SSEAD2O!X@"EHPW(F=IKY0AV3U"Q\G0 8G0/: I!^7"(DT&;$$$?GW +M01!1?!+#A"3C,L0B\%$(,0)DP +SEAFQ%P,L,&^9$7LRP (NX (NH -T4)?_ +M2'EE'QN%)YV7_F;@>!"?GS#/06?O$8@E4QJDQW?^8R$P[!<ZY!H6<P)D<,)] +MTQ(;0\";J#<=$L6[ :1TMI*(QQMF/_JH[X'%'_*M7U(NN(+)S^,378 '6!4P +M&,],T?Q4 +C3WZA!-HC>Q5].Y-//9&(>8R[U(4QW:S'SEAFQQ\4%4;O-0P;Y +ME3#"Z!?$6$O;#P+SEAFQQR!)-25GT& 1MK(-3, B-C=9%R;):(\H8!WSEAFQ +MQR D"CEDY1.]UD:?_P9)BG@_K"R-7S+OTRQG_4PFYC&A5#8FT3Y39S.<W[OE +M.6^9$7N0!6"Y&'75-9&U>[/=P<0$ 1INMB3R!?P40:/]41_0V6GJ"T_>,?RJ +M7_PB[X(KF/P\/M$%>(!5 8/QS!3-SU"?3_T,<I$1G,0&8<D>D@<[%U:1%,6O +MKH[^^CS,F[X9F9"QSEZC[QA^8EP^02YBTC\O\ASAPR]\9B%K1F34*9/+ ;W] +M,9 &-_'P\?FETVGJ:\#H@1Y#BK.W\[P(87],G2^&"3F/<6Z=ANI5&3MU(&G1 +M$XA!SP;ADT7PP2_Z7Q 6<_/ 02WL IY!EV0*S(I\]C/.!V(-W+Z.X_ G89T, +MXNY4W:;<D1=Y\![<E1G/PQF(^AK5UR'#K_K%+_(NN(+)S^,378 '6!4P&,], +MT?Q4 +C37[K1!"2*5:8@-Z<[G5IWZQUH,!*E$5;]@A!1?!(C.==)A0.J5E5% +MO2R(I_FDQW<RBVJ>GL0*IMG63,W"GLT<!:@?1:DB%7_@O.QM[,;/+L?1GL[' +M+A-\ (F=-@<Z@-\D+JO'[%)\0! ^0.5(/9P9Q0<$P0<$P6!DH -.'RF22N5( +M/9P9Q0<$P0<$X0-4CM3#F5%\0!!\0!!\0! ,1@8ZX/21 JA4CM3#F5%\0!!\ +M0!!\0! ^0.5(/9P9Q0<$P0<$P0<$P0<$P6!DH -.'RD=1>5(/9P9Q0<$P0<$ +MP0<$P0<$$>4Z ,6FITJBZ5)\0!!\0!!\0! \0.5(/9P9Q0<$P0<$P0<$$>4Z +M ,6FITH.ZU)\0!!\0! \0.5(/9P9Q0<$P0<$$>4Z ,6FITHZY5)\0! \0.5( +M/9P9I5,NQ0/#,^V<6LPX0.<%0>5(W2$M, )DH -TH -&<LS=3.@_HZ2\8?;6 +M,?RJ7_PB[X(KF/P\/M$%>(!5 8/QS!3-SU#2;_-))K_EF9"G[;Z%Y]7_2'DC +M',.4[O<_/3O6\?EOD*1C<'7P?@468BXU>1YR0F?O0090<_@M([.H5@;'.9,$ +MH:0?O;Y%8QWOH9ONWFDFT3ZG G,;\WQ&!?P400425"R!E=6QDQC4>;W<4;LW +M^_EXH?E*VJ41-]?CR"NJ5E4%PSM17DL6 L.E0>5('=AG?8N36&B'=K>6'[&1 +M?Q\> CF[(0(M, )DP )1K@-O( *#G5%4CM1LH -0;'HZ ,6FITJBZ5)4CM1L +MH -0;'HZ ,6FITH.ZU)4CM1LH -0;'HZ ,6FITHZY5)4CM1LH -0;'HZ ,6F +MITHR<.<N)0-W#O2<6LPY0.?'7>40WP(C0 8L$.4Z8"1W3JG"GLT<):D?1:DB +M%7_@O.QM[,;/+L?1GLY KSBJ!!N-4:&WT;M'"F!TX_"$EW:O?C=XB&I_QG$% +MPUDAQV@W._[/<V2J//[[-J/R_CUC@'Q'9B'8,G5M= )F<,*\ 4CZ,:PJ_$M_ +M#[\*]B1AV$X=YB&0<Q)4/-?CZ%_G43 'C'@@1O:U'MAG;;AQY#%Q9$LH;6L= +MYB&0\^HB\%$(,0)DH -O2N5(S08Z8 0ZP 0Z\ 8B\+NE<0)&<,)]TQ+7HV_+ +M2B'8ZSA!Z_024_ZP2,),<,)]TQ+7HV\P>:&O7K\S21"#S_9!9W],773'GA-4 +MCM1LX )CH ,&D0,Z<-Q5ON<ZX/0.H@-.'RF22N5(/9SX1^5(S08N, 8Z8! Y +MH /'7>5[K@-.[R ZX/21 JA4CM3#B7]4CM1LX )CH ,&D0,Z<-Q5ON<ZX/0. +MH@-.'RD=1>5(/9SX1^5(S08N, 8ZH$\SH /'7>5[K@-0;'HZ ,6FITJB">.U +MS@8N, 8ZH$\SH /'7>5[K@-0;'HZ ,6FITH."^.US@8N, 8ZH$\SH /'7>5[ +MK@-0;'HZ ,6FITHZY5( 2.<34<S^];!4CM0=T@(C0 8Z\*94CM1LH -&H -, +MH -&<LS=3.@_HZ2OQ3P,>UZJ5E77SV<_(P(,1@:/^/D)<XN3&$=L#P(B0.5( +MS08N, :/N&=.%K0_3)DX (O6(0)17MJG/?VJ1"[6$5NRP?9%,_A!%P,R@'[6 +M7,W"GLT<5<8?1:DB%7_@O.QM[,;/+L?1GLY(^81.Z(3O_- -^- -R- "#?U2 +MD 4(D 4(D 4(D 4@D/)8X?+LG%(0W8 ,/= [6!;0#][YC!5/Z(1...WD)"74 +M:6I/QE;T53"\X\4O.?KI,3=9%S'T1A"77YZ?CQ>O91TO#6; )4M4]+M?_>ZW +MP6#!H?/D[IR10B[6X4L;XR>&S?:8*_6:(A)7WUMGH/6_Q?M%&?9A'_:\8?8/ +M?#>0YV9^8MB_1YV+/]C='7_37=W771PPPI'0X5#IXS$7*AO'(6FTD3GRPC0S +MR:EM5/X@-ND39VK PUC@+Y)T7C*=V&GJB[CT3[5V @*(*(J[F9#8/*PJ+ <B +M8,#P(=W4;=W8_?EFR)YT?J=5$BBC[T9G<J2CCD6]9C/8//X#H0;L>2[@_XKJ +M./]ZT4HSR:EAH)M/U5FR]L$+>1A^8CI&!>\#P;'E0><Z#OO4;=W8+?TVC[A( +MMJ&=Z.[N>_!BC.9JSN9NCCG2;05/D 1$\ 5ION9M+OTVWR_8O-64'FL0$G#< +M"6H#(=W4;=U20.?2?U@$D4.(UC4P5Q" ECG64AX'+\9HKN9L[N;/H^.P;P5/ +MD 1$\ 5ION9MGCFI1>>F4DAP55F8\8>%TQC20D5R4O:Q@:38_-)@!ERR-%P> +M<Z&R<1R21AOE2 9,LY-TCM!8H ,Z#OO4;=W8C9;:G9E;A^9JSN9NCJG0"1EF +M!B&A)8KPUD* -OYTODZ"2]+Z 6C23=W6C=W2;_-[MOENW#]'S)VU%>^=V&GJ +MJ_>\0VXZ2>< QA-RPR143.DZ7TNBSQ-RPR011.<XFQPZ61 HX'W3I'D$$=E- +M4Q!DV]+P*\;2;05/D 1$\ 5ION9MGCFI%?^!.GO:A,WX0Q ;"A^0J!#Q]P5I +MON9M;A":0><Z#OM6\ 1)0 1?D.9KWN:9DUJ7>$OC3^<_/3L+XQH6T[[E02'2 +M3=W6C=W2GQ%=BLWJB-)+NXI<X]'(!TCDDJTV@\V?CQ=5V8EQ(V<W72HH "$X +M3TT_3^<(C04ZH..PG^9KWN:U1VXZ&>?X5P18H ,Z#OM6\ 1)0 1?D.9KWN;0 +M+1/>#=[B3=[% 2,<R7/^>'59QRYH"TT'+\:N&(@#$02W2XAC\^M9BCG:R<4) +M^_G4[QK.>MYL69<5LC3,U4S+V5*H*\:^$B1QPS(+(VF \3_K)B)[$L4GP8R/ +M6@18H ,Z#OO?'=[C7=XACM[0G1,;WN$#,05EL+[>L:+9OS,?4AK1&VY",>OP +M*\97EW5:%/69/?6-4O557_58?P9:[V0?<B>/43IQ9&_'+NP;%0/;_%&4*E+Q +M!\[+WL9N_.QR'.WI#/0YL78K 8OOEEUUH$[6L:*+6!K,!HYN#/@1NTYZTR%1 +MG"9( B/06=0SR:F?SUF=IKZ_^]5XP2Z7YD5>/).<"J3O!EL?$CL0UCR2U843 +M1.?KI#<M%<6O?F(.CQB(5O:QT=+P*\:_SL!'9F $X01@ A_\PF<_XX??> 9T +M#@(BT (C\(CDXAT*;VL=%L6[4;O-D\1BC"2^=GV1$G2__DCSKQ?N'^EO@ !O +M@ !O@ !@@@!T@ !T@ !.9.<RA[2PHJN(2:J##^?7SJ=IKXRZL;.F!&^<M/# +M!:3L@5@ )(J[F9#8O-75SB[#JL(4H_Z% KZ52P<(0 <(0 <(H"'EN[ER@ !R +M@ !R@ .S#L@( (M, *EC<U1?!+,^*A%@ 4ZH..P#P4<[N$H$.+YO=\D[N1= +M[E)%@ 4ZH..P#P4<[N%H208Z0 ?;3><(C04ZH..P#P4<[N%HN=U KQ#QQ^-/ +M 6#*5_F6-3(R!<["N[H6=/TQJEE)VF%8QV+&#O1L1+62=5ODET)B<UEIT[, +MZV0?@D7\$D?A)A3E^4QMALT+.WWEJ8X6@QE_2"'/9&(E(9LRZL;"'W\\_@10 +M(/TVOS"N83&_KI[,^*A%@ 4ZH..P#P5/ 7/#?3>'']?8 1)P 0'73+P(=U& +MD 1,<-#2;_-)ED/WDS#]2N<W#QS4HA=)\ )/4%WI$ZM*ZB%;:"S8K#BJE$,^ +M02XZ5G7(]E1(@J38K&$:+R<B<'DP]XCD* *7)R2/^%HV@\VO!WD@D 0O\ 0S +M347,^*A%@ 4ZP!NU+-U&D 1,4 1"(P)$((KZT0925E4DYL9)\ )/$ )P#MTR +M(=U%@ 5%, 158-WQ#A_270184 1#4 76G3FI=1[(E$.(EI#8[(I?1,3!P1%' +M;!%6^KNED?]TGK[KHO<_$ZOSEAFQ!S4Y=#]O0.>GMK!\Y49G<KW<81T 7[O- +MD\1BC"2PU1_U 9V=IKZ_^QR?;X;L2>?->=/E"5=(E#(3.5DY!#6K<XQTKN.P +MW^,__OEO8$>+([+_$1LA4XCT=N")09WE:=.DQW?.9"'*HH]<=@)D< )TSAN +MA!(+XQH6\^N/=&3L@@))X )%X +I.! B\%'8/ )DH -T\(B1?Q\> CDG\?-T +MCM!8H ,Z#OM%@ 5%, 158-W?RF&;49='PF%C@@).#MWJ)]UN7C(A)OV@;[A) +MY>;HT6MMQ!$G,9.<*@,M4 /IF%17EW41 Q@QHY]E "*#Y-&H*\8@6;0^02[> +MX6IR,XX\X9NA1Q#SAZ38O*+>+04^SE"?S\?'+NP;%0/%_E&4*E+Q!\[+WL9N +M_.QR'.WI#/3('G\\+@4^SE"8RI']NJR%D^Z%)P)FH*^EC<W (08<<<23/G&Q +MYD76<7595X>[B\VLUQ%"(]U&D 1,4 2_"Q_YWQV=IKYTGF2 %JL@,&^9$7O' +M"1\U6A\^\3UTWL#MZV0._^KJV$;K-)UA5!_?> 9TGK[K\L-Y03). ";PP2\R +M&\/9T2_8',4E'2F_SL!'QBZ1V!W,Z:]T+OTVG[[K<K+PA?Z(7O557_4I4RF4 +MDA<(0 <(0 <($)"EX1A^8EP^81)T'DM91QJ"Q8R/6@18H ,Z#OL]_N-H60:@ +M49>/Z7PAPO;8\9R8&>?X5P18H ,Z#OL]_N-H:2=U^9AVHDK16I=T#ATNZ>1= +M[E)%@ 4ZH..PW^,_CI9Y 0=U^9C1ZE$^ )=*GA=PT*78G!?JU (^P'-EX.30 +M/1/2/05JK@1- 7% 2,^02YPHA)4JV-E0 =J\"8HH'F3/G%LB\VND_^O 3OI +M<W595X>=OR>+.).<^D2K;DAO!!\/?#>0IW!J\"9T?F8'ZOB,HQQ553!&8OF= +MO\)O0.=07.5M> (Z6VGN \.E(=U,\ 1.< 1*T 100.?2;_-)ED/WDS FX7U! +M;!VO!WET+FOU<09J\"8HH'F%R")#,#=9%S&%54A"U2 71N=3)MU3H.9*T 10 +MD#GL.2!C<'7RZ1PP/),'+EOC#XM%HV-5-_YT7C&@N!NSEAC,^*A%@ 4ZH..P +M/P5JK@1- 6U%QF9^7/2S01/X 1'H 1- 7% 2,^02YPHA)4:QV*<1)J\"8H +MH'F3/G%LB\WXD_\#03=5[H\/N:/@3N?H^6<+UT;0(4#PP7!M)&M(0^<Z#OM3 +MH.9*T 10@,0M;2%#[(HM#;]B_'J0YS#U<09J\"8HH'EG=J $X01@ A_EI?\) +M>W59%S%2-OH#(=U,\ 1.< 1*T 10<&SO3N=07.7^N(B=*+.\DZ)E"[]B#$A( +M)MU3H.9*T 10$/^8L31-TYQE)RV9_AYT#F##(_EQ!"-BPG;2/05JK@1- 5_ +MB\W2S01/X 1'H 1- 634SSY\CM2MJ&/9C/8G/T'XC$7*AO'(6FT48YDP#2E +M0;8V@\WY#XF=IKY2_<M!DG4 PG8V@\WL4>W,^*A%@ 4ZH..PSP1/X 1'H 1- +M 6U%QDL0&Z96<W"GLT<=0/&_G[)OL::VE)N_.QR'.WIC)3#SQ4&7?PAW_HE +MY8(T2-!#D/)O$<],T?Q4 /W@G<]8<=$6;=$@\(1.Z(3-3P70+P4P&,\RZ((K +M..VC%[&1?Q\> CF[\1PYQ)' *;.\LX@L1,#P >X$D>Z%-_RJ7_PB[X(KF/P\ +M/M$$^,Y/Z(1.^(,^Z(.9DUH&C!XUBO@(\61L15]%+6)SDW414UA^]!M'FOUM +M.H8Z3XXV$W& I!_#JL+Y\M-,1V<[1WL%+S&DL28#$02=IKX:<V3KZQA^8EP^ +M02XGL 4G_$0GT 4G'(FE+C+) ?PX009;P +SEAFQ-P=M601.O'/_2'G78?:9 +M\^X$T35T3A"UVSSS,F^9$7OP'O2FP30=<F301^<$,6^9$7NN(4!PHF7\LK^% +M-&^9$7L,<CK";DG4*9) GQ!$L 4LX&=M"1* A/\9!F'-<S\"]&>Z^52X3N<$ +MH:0_K"SYE3"O%'Y/1P8<81(R>N)4($ ZY!HE(9NO93"<D;LM!&@U6A])C,TH +M0&XZN67^I4K^"IPVVO@?L7>F1^?CXAHLEA',2!%FL 4L,/YST);_-D $D?W* +MUK,ZW]()@Z3"7KO-,R]'!F_<,?[P'@1"9Z@G?F2J/.D3I_/GX4I:5E[LLC)T +M3A#CSR!)94G4*9) GQ-& .AR<%"X#N[A]W1!QY!TWM(_4_X@AK>:Z/02$_F! +M>_#8;!V?;R1PA=/6H:3,^*A, .AR<%"X#N[A]W1!QY!TWM*\,_A.OW-!M]3[ +MA[UT?FRI=6;,MAO6H:3,^*CFHE)R<%"X#N[A]W1!QY!TWM*E.#=9IW&4;FHE +MIW=CB') GQ-N/@5EP(<7:FQ+O7_G8F;_TOE5+>Q*^L.DOG:;X6!ZL4_Q7DY< +M[,8YU(Y#1C% XA>.D6KA)@=E!R-RXCXD=N+93U6(=QZN(43/]2FETC5T/CEG +MK:3,*&I;P *@UY:!]WO4F3[9WZ:(YR%;J.]O0.<MS3LAQVC-@VB@9\!5M'>F +M1^<$H:1.IGB%X7JW)R0D$P0$T?/#>>*_SL!'QBYP!2.YBWA2C;K8K'UNKV(> +M@GD&!O23LP4L@+;4T9;_-D $D?W*UK,ZW]()@Z3"7KO-@VAH2QTD$P2FP30M +M=62G-^V/NE$Q@ /&_G[)OL::VE)N_.QR'.WI#/3.YDI:UEKL3AV_6TX,NF!, +M0^<$L>F>8_'2@D4_S70\E#H$41Z_D<38C+;4P2). ";PP2_L@NO_TOE5+>Q* +M^L.DSF;-Z>YR<&AW:S/"7OX@EAYSDW7L@K8#D>Y(*NPG 4GW#<M\;O(:/&( +M5?Z(^T[M6QYTWM*\TQV=IKZB@B0P@F@ AJ3"/ORJ7_PB[X(KF/P\/M$%>(!6 +MD<]80=#B'8(:^(1.Z(1/Z(1.*/T9P8R/"@6 +@<'A>O@'GY/%W0,2><M_3/E +M#V+I,3=9%R;LSB+)/,/"GD/M:+CFDD/8,G4 Q.ZUI$QT3A >PS&6YAQ-XSD6 +M+RU8Q"_L$HG$!!MT3A# LQO9'X@@X 1@PNU\AIR1J'7"CK:9PYY5R1$G41HC +M_#/9KTW"'OGWX2&0LQOX8V*Q)"WEB3\V(^SE\1L^@;;4P8R/<PXA/DDC=J +M0.ES-W76,?RJ7_PB[X(KF/P\/M$$^,Y/Z(1.^(,^Z(-T?FRI!4]T4P9\5HIS +MDW5WYR!%A?C"/FLM)P:R!$\LEA$LDLSOE$/M*)]!?>+#K_K%+_(NN(+)S^,3 +M38#O_(1.Z(0_Z(,^F#GO7K4J!AW%4G8< 3G+0>>0>%_:7QK9OS,?LI,/2P=; +MP ).T)9%X,02,_[KI#=\XS>"@FKY(:,G7NAA@ <"]#$5 XJ[<7*J5+O-D\38 +M'+U.T%X^07I\M[ZN(V P@'[6;,U _Z@;=5/&_G[)OL::VE)N_.QR'.WIC)0_ +MZ(,^"(/QS!3-3P5/Z(1.&/0-+=!!./Q<D5(0[8#WG 1/Z(1.2-#B'8(:>-$6 +M;=$7;=$6S= "'80&G?)8\<Y/Z(1...WU"6+E^$XYU(YG=D8J[5/<81TO#6; +M)4O3C"$%DS#(%XF%)W<$-V7>41*RZ=/D0@>9D?AF$"19!QG8,T>-^+OE5!V# +MZ%T.ZJ^)YD9G<O &T2QG32XBNC]T=!E(U8:#LZ$T,FE^D5SNA1"Q)1F[8?F= +MOR=*RHLG%B19EW.K^+O(Z.<=@8I?$T<><U13-TW]XUU5Z 8RYQKD(J+[0T>7 +M@50S.2[LDC5SX",;BD7JV$9*>AXB6S:]9D9R$_Z]MD19NV!^X1BI!GH&#!]T +MD!D# 21^X1BI!GIRPF9?$T>HFR7+$R19!R#&AGR1R)&W,8Q1ET)QPS)(51IR +M5Q])RAU]EAF<KJ<THCSZ.&/ 3Q%!,! \T;3EZ<5@8^D)2C=T+RB@^.I(!1^; +MCXN'Y*-#3""WE!YSDW7LTO=>A![(%XGSTA*!.\,((7<$9Y@\@9^NT?>!];OE +MY!HX6K4+QC2U9"$DEIUY'XG5M;PF@3EA/6/ 3Q&*HTKZ6Z1F$&&FQ[[RLCR% +MA4)X4/B%L_!/E9 88N[G"1L]5[_P,?[)X;<@!@(O#6; )4O3C! X5"3MZUW\ +MU3ZG G-XJXGK%"AA;;( 9K:? [V$LQO9'X@@D%94;DKBA<(R1B-WX!<Z9.YD +MY1/DLD0'T?=)GSY00!M':A%#L+2K>" VW48XQ!FTUVM<1<#F=4ML _<@@ 15 +M%9QVY(8>0AEGP#*.01]S0T5$LOV4KS'@R3)-)3!XPXGK=*'VMUDW[8_9?R"? +MCQ<-3,#P 3V;0;A2QL0$ET7EZ<+ T=3Q#A_),W));! +OR>1W33-F4-VICKX +MCSD:@FV,A0>%_RU^GB/H9\W5+.S9'&#H]5&4*E+Q!\[+WL9N_.QR'.WIC)0$ +M/00,+= 66($5:- ,+=#-3P5/Z(1...U"]M)@!ERR-%R9(0=KX!H^\DZVY%H_ +MS70;.I,$(0)!1U'G3@:/^(\R!S6RXS5((BA*PND7+SKOXAB*-9P'$6YR4':5 +M=2)91AWE\[7Q/[S-^4)R$$SE:4TU#UTD=A 4=>[SXEIJ-3M(@K-!<7^"5^6N +M$;U#\KHD;9/P:Q#K,6GY!B*?CQ?P5(@A-T9\0^DZK_E%&D: Y&C@F)V\D_T4 +M$^7P#@)/T$+TX=9E<)SHH1Y]$4GH4>\(L1YPXEUEFJV6W_E[HJ2JI-.>008? +M,R/SDB&E0_9/CSYEEQ<M('<"DHN#"T? 3Q%]'AXZ(OK,U!CBB%K*\<%&=1V4 +MKO/Q-"($;%[PA?Z)ONB&P@8(P 8(P 8(H!P(P 8(P 8(H!@(\ 8(\ 8(\,$( +M, 8(, 8(D'#/=--R1OK(6(\^S&R[81VO!WD:\])@!ERR1$6_^]5XH1>7)B!U +6JTKA=5]I *"4_\%4D\0&T1CB.!Q$ 1VO + +end diff --git a/dmake/dbug/dbug/example1.c b/dmake/dbug/dbug/example1.c new file mode 100755 index 000000000000..805b0a202872 --- /dev/null +++ b/dmake/dbug/dbug/example1.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +main (argc, argv) +int argc; +char *argv[]; +{ + printf ("argv[0] = %d\n", argv[0]); + /* + * Rest of program + */ + printf ("== done ==\n"); +} diff --git a/dmake/dbug/dbug/example2.c b/dmake/dbug/dbug/example2.c new file mode 100755 index 000000000000..66ee43c0ec84 --- /dev/null +++ b/dmake/dbug/dbug/example2.c @@ -0,0 +1,17 @@ +#include <stdio.h> + +int debug = 0; + +main (argc, argv) +int argc; +char *argv[]; +{ + /* printf ("argv = %x\n", argv) */ + if (debug) printf ("argv[0] = %d\n", argv[0]); + /* + * Rest of program + */ +#ifdef DEBUG + printf ("== done ==\n"); +#endif +} diff --git a/dmake/dbug/dbug/example3.c b/dmake/dbug/dbug/example3.c new file mode 100755 index 000000000000..0eeb75e7f377 --- /dev/null +++ b/dmake/dbug/dbug/example3.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +main (argc, argv) +int argc; +char *argv[]; +{ +# ifdef DEBUG + printf ("argv[0] = %d\n", argv[0]); +# endif + /* + * Rest of program + */ +# ifdef DEBUG + printf ("== done ==\n"); +# endif +} diff --git a/dmake/dbug/dbug/factorial.c b/dmake/dbug/dbug/factorial.c new file mode 100755 index 000000000000..42a4d848014e --- /dev/null +++ b/dmake/dbug/dbug/factorial.c @@ -0,0 +1,15 @@ +#include <stdio.h> +/* User programs should use <local/dbug.h> */ +#include "dbug.h" + +int factorial (value) + register int value; +{ + DBUG_ENTER ("factorial"); + DBUG_PRINT ("find", ("find %d factorial", value)); + if (value > 1) { + value *= factorial (value - 1); + } + DBUG_PRINT ("result", ("result is %d", value)); + DBUG_RETURN (value); +} diff --git a/dmake/dbug/dbug/main.c b/dmake/dbug/dbug/main.c new file mode 100755 index 000000000000..d7c4267d4767 --- /dev/null +++ b/dmake/dbug/dbug/main.c @@ -0,0 +1,27 @@ +#include <stdio.h> +/* User programs should use <local/dbug.h> */ +#include "dbug.h" + +int main (argc, argv) + int argc; + char *argv[]; +{ + register int result, ix; + extern int factorial (), atoi (); + + DBUG_ENTER ("main"); + DBUG_PROCESS (argv[0]); + for (ix = 1; ix < argc && argv[ix][0] == '-'; ix++) { + switch (argv[ix][1]) { + case '#': + DBUG_PUSH (&(argv[ix][2])); + break; + } + } + for (; ix < argc; ix++) { + DBUG_PRINT ("args", ("argv[%d] = %s", ix, argv[ix])); + result = factorial (atoi (argv[ix])); + printf ("%d\n", result); + } + DBUG_RETURN (0); +} diff --git a/dmake/dbug/dbug/makeman.sh b/dmake/dbug/dbug/makeman.sh new file mode 100755 index 000000000000..b2aca44b8f9f --- /dev/null +++ b/dmake/dbug/dbug/makeman.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +gcc -o factorial main.c factorial.c dbug.c + +for i in example?.c main.c factorial.c ; +do + sed -e 's!\\!\\\\!g' $i > ${i/\.c/\.r} +done + +./factorial 1 2 3 4 5 | cat > output1.r +./factorial -\#t:o 2 3 | cat > output2.r +./factorial -\#d:t:o 3 | cat > output3.r +./factorial -\#d,result:o 4 | cat > output4.r +./factorial -\#d:f,factorial:F:L:o 3 | cat >output5.r + +#nroff -mm user.r > user.t +#groff -mm user.r > user.ps +groff -mm -rcR=0 -Tlatin1 -P -bcu user.r > dbug.txt diff --git a/dmake/dbug/dbug/readme b/dmake/dbug/dbug/readme new file mode 100644 index 000000000000..8d7c2ed6ab13 --- /dev/null +++ b/dmake/dbug/dbug/readme @@ -0,0 +1,52 @@ +This directory contains DBUG the "C Program Debugging Package" by Fred Fish. + +/****************************************************************************** + * * + * N O T I C E * + * * + * Copyright Abandoned, 1987, Fred Fish * + * * + * * + * This previously copyrighted work has been placed into the public * + * domain by the author and may be freely used for any purpose, * + * private or commercial. * + * * + * Because of the number of inquiries I was receiving about the use * + * of this product in commercially developed works I have decided to * + * simply make it public domain to further its unrestricted use. I * + * specifically would be most happy to see this material become a * + * part of the standard Unix distributions by AT&T and the Berkeley * + * Computer Science Research Group, and a standard part of the GNU * + * system from the Free Software Foundation. * + * * + * I would appreciate it, as a courtesy, if this notice is left in * + * all copies and derivative works. Thank you. * + * * + * The author makes no warranty of any kind with respect to this * + * product and explicitly disclaims any implied warranties of mer- * + * chantability or fitness for any particular purpose. * + * * + ****************************************************************************** + */ + +The original package is no longer mainained, but copies can found here + <http://sourceforge.net/projects/dbug/> +or in the dbug directory of the MySQL 4.0 (and older) + <http://dev.mysql.com/downloads/> +sources. + +The files found here are: + + dbug.c - runtime support routines for dbug package + dbug.h - user include file for programs using the dbug package + user.r - nroff source for the manual page. + dbug.txt - a typeset version of the manual page containing no control + characters. Generated with makeman.sh: + Copy it as dbug.1 into a directory in your search path for + man pages to be able to view it with the man command. + makeman.sh - helper script to generate the documentation + example1.c - Additional/demo sources for the documentation. + example2.c + example3.c + main.c + factorial.c diff --git a/dmake/dbug/dbug/user.r b/dmake/dbug/dbug/user.r new file mode 100755 index 000000000000..1d58d28a8800 --- /dev/null +++ b/dmake/dbug/dbug/user.r @@ -0,0 +1,938 @@ +.\" @(#)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 - set date to source date +.\".PH "/DBUG User Manual//\*(DT/" +.PH "/DBUG User Manual//October 29, 1986" +.\" === 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 + + diff --git a/dmake/dbug/getwd.c b/dmake/dbug/getwd.c new file mode 100644 index 000000000000..56e1a03ab7ca --- /dev/null +++ b/dmake/dbug/getwd.c @@ -0,0 +1,6 @@ +char * +getwd(pathname) +char *pathname; +{ + return("delete this code if your getwd.c works correctly"); +} diff --git a/dmake/dbug/malloc/_changes b/dmake/dbug/malloc/_changes new file mode 100644 index 000000000000..888a47a8dfb5 --- /dev/null +++ b/dmake/dbug/malloc/_changes @@ -0,0 +1,9 @@ +I made the following changes to the malloc package as found in +comp.sources.unix: + + 1. created this file _changes. + 2. moved README to _readme (facilitates transfer to DOS and back to + unix) + 3. renamed testmalloc.c, malloc_chk.c, and malloc_chn.c to testmlc.c, + mlc_chk.c, and mlc_chn.c respectively. Again DOS has trouble with + long basenames in filenames. diff --git a/dmake/dbug/malloc/_readme b/dmake/dbug/malloc/_readme new file mode 100644 index 000000000000..b78b1fd6bbcd --- /dev/null +++ b/dmake/dbug/malloc/_readme @@ -0,0 +1,133 @@ +# (c) Copyright 1990 Conor P. Cahill. (uunet!virtech!cpcahil) +# You may copy, distribute, and use this software as long as this +# copyright statement is not removed. + +This package is a collection of routines which are a drop-in replacement +for the malloc(3), memory(3), string(3), and bstring(3) library functions. + +The purpose of these programs is to aid the development and/or debugging +of programs using these functions by providing a high level of consistancy +checking whenever a malloc pointer is used. Due to this increased +level of consistancy checking, these functions have a considerably larger +overhead than the standard functions, but the extra checking should be +well worth it in a development environment. + +To use these functions all you need to do is compile the library and +include it on your loader command line. You do not need to recompile +your code, only a relink is necessary. + +Features of this library: + + 1. The malloced area returned from each call to malloc is filled with + non-null bytes. This should catch any use of uninitialized malloc + area. The fill pattern for malloced area is 0x01. + + 2. When free is called numerous validity checks are made on the + pointer it is passed. In addition, the data in the malloc block + beyound the size requested on the initial malloc is checked to + verify that it is still filled with the original fill characters. + + This is usefull for catching things like: + + ptr = malloc(5); + ptr[5] = '\0'; + + /* + * You should not that this will be caught when it is + * freed not when it is done + */ + + And finally, the freed block is filled with a different fill pattern + so that you can easily determine if you are still using free'd space. + The fill pattern for free'd areas is 0x02. + + This is usefull for catching things like: + + ptr = malloc(20); + + bptr = ptr+10; + + /* do something usefule with bptr */ + + free(ptr); + + /* + * now try to do something useful with bptr, it should + * be trashed enough that it would cause real problems + * and when you went to debug the problem it would be + * filled with 0x02's and you would then know to look + * for something free'ing what bptr points to. + */ + + + 3. Whenever a bstring(3)/string(3)/memory(3) function is called, it's + parameters are checked as follows: + + If they point somewhere in the malloc arena + If the operation goes beyond requested malloc space + call malloc_warning() + + This is usefull for catching things like: + + ptr = malloc(5); + strcpy(ptr,"abcde"); + + + 4. Malloc_warning() and malloc_fatal() are used when an error condition + is detected. If the error is severe, malloc_fatal is called. + Malloc_warning is used otherwise. The decision about what is fatal + and what is a warning was made somewhat arbitrarily. + + Warning messages include: + + Calling free with a bad pointer + Calling a bstring/string/memory (3) function which will go beyond + the end of a malloc block (Note that the library function is + not modified to refuse the operation. If malloc warnings are + in the default IGNORE case, the operation will continue and + at some point cause a real problem). + + Fatal errors are: + + Detectable corruption to the malloc chain. + + + 5. The operations to perform when an error is detected are specified at + run time by the use of environment variables. + + MALLOC_WARN - specifies the warning error message handling + MALLOC_FATAL - specifies the fatal error handling + + + When one of these error conditions occur you will get an error + message and the handler will execute based upon what setting + is in the environment variables. Currently understood settings + are as follows: + + 0 - continue operations + 1 - drop core and exit + 2 - just exit + 3 - drop core, but continue executing. Core files will + be placed into core.[PID].[counter] i.e: core.00123.001 + 128 - dump malloc chain and continue + 129 - dump malloc chain, dump core, and exit + 130 - dump malloc chain, exit + 131 - dump malloc chain, dump core, continue processing + + + There is an additional environment variable MALLOC_ERRFILE which + is used to indicate the name of the file for error message output. + + For example, to set up the session to generate a core file for + every malloc warning, to drop core and exit on a malloc fatal, and + to log all messages to the file "malloc_log" do the following: + + MALLOC_WARN=131 + MALLOC_FATAL=1 + MALLOC_ERRFILE=malloc_log + + export MALLOC_WARN MALLOC_FATAL MALLOC_ERRFILE + + 6. The function malloc_dump() is available to dump the malloc chain whenever + you might want. It's only argument is a file descriptor to use to write + the data. Review the code if you need to know what data is printed. diff --git a/dmake/dbug/malloc/calloc.c b/dmake/dbug/malloc/calloc.c new file mode 100644 index 000000000000..1469b200cbf2 --- /dev/null +++ b/dmake/dbug/malloc/calloc.c @@ -0,0 +1,49 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include <stdio.h> + +/* + * Function: calloc() + * + * Purpose: to allocate and nullify a data area + * + * Arguments: nelem - number of elements + * elsize - size of each element + * + * Returns: NULL - if malloc fails + * or pointer to allocated space + * + * Narrative: determine size of area to malloc + * malloc area. + * if malloc succeeds + * fill area with nulls + * return ptr to malloc'd region + */ +#ifndef lint +static char rcs_header[] = "$Id: calloc.c,v 1.2 2006-07-25 10:07:11 rt Exp $"; +#endif + +char * +calloc(nelem,elsize) + unsigned int nelem; + unsigned int elsize; +{ + char * malloc(); + char * memset(); + char * ptr; + unsigned int size; + + size = elsize * nelem; + + if( (ptr = malloc(size)) != NULL) + { + (void) memset(ptr,'\0',(int)size); + } + + return(ptr); +} + + diff --git a/dmake/dbug/malloc/debug.h b/dmake/dbug/malloc/debug.h new file mode 100644 index 000000000000..069d1dc77639 --- /dev/null +++ b/dmake/dbug/malloc/debug.h @@ -0,0 +1,99 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +/************************************************************************/ +/* */ +/* this include sets up some macro functions which can be used while */ +/* debugging the program, and then left in the code, but turned of by */ +/* just not defining "DEBUG". This way your production version of */ +/* the program will not be filled with bunches of debugging junk */ +/* */ +/************************************************************************/ +/* + * $Id: debug.h,v 1.2 2006-07-25 10:07:24 rt Exp $ + */ + +#ifdef DEBUG + +#if DEBUG == 1 /* if default level */ +#undef DEBUG +#define DEBUG 100 /* use level 100 */ +#endif + +#include <stdio.h> + +#define DEBUG0(val,str)\ + {\ + if( DEBUG > val ) \ + fprintf(stderr,"%s(%d): %s\n",\ + __FILE__,__LINE__,str);\ + } +#define DEBUG1(val,str,a1)\ + {\ + char _debugbuf[100];\ + if( DEBUG > val )\ + {\ + sprintf(_debugbuf,str,a1);\ + fprintf(stderr,"%s(%d): %s\n",\ + __FILE__,__LINE__,_debugbuf);\ + }\ + } + +#define DEBUG2(val,str,a1,a2)\ + {\ + char _debugbuf[100];\ + if( DEBUG > val )\ + {\ + sprintf(_debugbuf,str,a1,a2);\ + fprintf(stderr,"%s(%d): %s\n",\ + __FILE__,__LINE__,_debugbuf);\ + }\ + } + +#define DEBUG3(val,str,a1,a2,a3)\ + {\ + char _debugbuf[100];\ + if( DEBUG > val )\ + {\ + sprintf(_debugbuf,str,a1,a2,a3);\ + fprintf(stderr,"%s(%d): %s\n",\ + __FILE__,__LINE__,_debugbuf);\ + }\ + } + +#define DEBUG4(val,str,a1,a2,a3,a4)\ + {\ + char _debugbuf[100];\ + if( DEBUG > val )\ + {\ + sprintf(_debugbuf,str,a1,a2,a3,a4);\ + fprintf(stderr,"%s(%d): %s\n",\ + __FILE__,__LINE__,_debugbuf);\ + }\ + } + +#define DEBUG5(val,str,a1,a2,a3,a4,a5)\ + {\ + char _debugbuf[100];\ + if( DEBUG > val )\ + {\ + sprintf(_debugbuf,str,a1,a2,a3,a4,a5);\ + fprintf(stderr,"%s(%d): %s\n",\ + __FILE__,__LINE__,_debugbuf);\ + }\ + } + +#else + +#define DEBUG0(val,s) +#define DEBUG1(val,s,a1) +#define DEBUG2(val,s,a1,a2) +#define DEBUG3(val,s,a1,a2,a3) +#define DEBUG4(val,s,a1,a2,a3,a4) +#define DEBUG5(val,s,a1,a2,a3,a4,a5) + +#endif /* DEBUG */ + + diff --git a/dmake/dbug/malloc/dump.c b/dmake/dbug/malloc/dump.c new file mode 100644 index 000000000000..70c8ac30c2d8 --- /dev/null +++ b/dmake/dbug/malloc/dump.c @@ -0,0 +1,103 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include <stdio.h> +#include "malloc.h" +#include "tostring.h" + +/* + * Function: malloc_dump() + * + * Purpose: to dump a printed copy of the malloc chain and + * associated data elements + * + * Arguments: fd - file descriptor to write data to + * + * Returns: nothing of any use + * + * Narrative: Just print out all the junk + * + * Notes: This function is implemented using low level calls because + * of the likelyhood that the malloc tree is damaged when it + * is called. (Lots of things in the c library use malloc and + * we don't want to get into a catch-22). + * + */ + +#ifndef lint +static +char rcs_hdr[] = "$Id: dump.c,v 1.2 2006-07-25 10:07:38 rt Exp $"; +#endif + + +#define ERRSTR "I/O Error on malloc dump file descriptor\n" + +#define WRITEOUT(fd,str,len) if( write(fd,str,(unsigned)len) != len ) \ + { \ + (void) write(2,ERRSTR,\ + (unsigned)strlen(ERRSTR));\ + exit(120); \ + } + +void +malloc_dump(fd) + int fd; +{ + char buffer[512]; + void exit(); + int i; + extern char * malloc_data_end; + extern char * malloc_data_start; + extern struct mlist * malloc_end; + extern struct mlist malloc_start; + struct mlist * ptr; + + WRITEOUT(fd,"MALLOC CHAIN:\n",14); + WRITEOUT(fd,"-------------------- START ----------------\n",44); + + for(i=0; i < 80; i++) + { + buffer[i] = ' '; + } + + for(ptr = &malloc_start; ptr; ptr = ptr->next) + { + (void) tostring(buffer, (int)ptr, 8, B_HEX, '0'); + (void) tostring(buffer+9, (int)ptr->next, 8, B_HEX, '0'); + (void) tostring(buffer+18, (int)ptr->prev, 8, B_HEX, '0'); + (void) tostring(buffer+27, (int)ptr->flag, 10, B_HEX, '0'); + (void) tostring(buffer+38, (int)ptr->s.size, 8, B_DEC, ' '); + (void) tostring(buffer+47, (int)ptr->s.size, 8, B_HEX, '0'); + (void) tostring(buffer+57, (int)ptr->data, 8, B_HEX, '0'); + buffer[46] = '('; + buffer[55] = ')'; + buffer[65] = '\n'; + WRITEOUT(fd,buffer,66); + } + WRITEOUT(fd,"-------------------- DONE -----------------\n",44); + + WRITEOUT(fd,"Malloc start: ",19); + (void) tostring(buffer, (int) &malloc_start, 8, B_HEX, '0'); + buffer[8] = '\n'; + WRITEOUT(fd,buffer,9); + + WRITEOUT(fd,"Malloc end: ", 19); + (void) tostring(buffer, (int) malloc_end, 8, B_HEX, '0'); + buffer[8] = '\n'; + WRITEOUT(fd,buffer,9); + + WRITEOUT(fd,"Malloc data start: ", 19); + (void) tostring(buffer, (int) malloc_data_start, 8, B_HEX, '0'); + buffer[8] = '\n'; + WRITEOUT(fd,buffer,9); + + WRITEOUT(fd,"Malloc data end: ", 19); + (void) tostring(buffer, (int) malloc_data_end, 8, B_HEX, '0'); + buffer[8] = '\n'; + WRITEOUT(fd,buffer,9); + +} /* malloc_dump(... */ + + diff --git a/dmake/dbug/malloc/free.c b/dmake/dbug/malloc/free.c new file mode 100644 index 000000000000..a8fc3ca259b6 --- /dev/null +++ b/dmake/dbug/malloc/free.c @@ -0,0 +1,150 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include <stdio.h> +#include "malloc.h" +#include "debug.h" + +/* + * Function: free() + * + * Purpose: to deallocate malloced data + * + * Arguments: ptr - pointer to data area to deallocate + * + * Returns: nothing of any value + * + * Narrative: + * verify pointer is within malloc region + * get mlist pointer from passed address + * verify magic number + * verify inuse flag + * verify pointer connections with surrounding segments + * turn off inuse flag + * verify no data overrun into non-malloced area at end of segment + * IF possible join segment with next segment + * IF possible join segment with previous segment + * Clear all data in segment (to make sure it isn't reused) + * + */ +#ifndef lint +static +char rcs_hdr[] = "$Id: free.c,v 1.2 2006-07-25 10:07:53 rt Exp $"; +#endif + +void +free(cptr) + char * cptr; +{ + char * func = "free"; + int i; + extern int malloc_checking; + extern struct mlist * malloc_end; + extern int malloc_errno; + extern char * malloc_data_end; + extern char * malloc_data_start; + void malloc_join(); + void malloc_memset(); + struct mlist * oldptr; + struct mlist * ptr; + + /* + * IF malloc chain checking is on, go do it. + */ + if( malloc_checking ) + { + (void) malloc_chain_check(1); + } + + /* + * verify that cptr is within the malloc region... + */ + if( cptr < malloc_data_start || cptr > malloc_data_end ) + { + malloc_errno = M_CODE_BAD_PTR; + malloc_warning(func); + return; + } + + /* + * convert pointer to mlist struct pointer. To do this we must + * move the pointer backwards the correct number of bytes... + */ + + ptr = (struct mlist *) (cptr - M_SIZE); + + if( (ptr->flag&M_MAGIC) != M_MAGIC ) + { + malloc_errno = M_CODE_BAD_MAGIC; + malloc_warning(func); + return; + } + + if( ! (ptr->flag & M_INUSE) ) + { + malloc_errno = M_CODE_NOT_INUSE; + malloc_warning(func); + return; + } + + if( (ptr->prev && (ptr->prev->next != ptr) ) || + (ptr->next && (ptr->next->prev != ptr) ) || + ((ptr->next == NULL) && (ptr->prev == NULL)) ) + { + malloc_errno = M_CODE_BAD_CONNECT; + malloc_warning(func); + return; + } + + ptr->flag &= ~M_INUSE; + + /* + * verify that the user did not overrun the requested number of bytes. + */ + for(i=ptr->r_size; i < ptr->s.size; i++) + { + if( ptr->data[i] != M_FILL ) + { + malloc_errno = M_CODE_OVERRUN; + malloc_warning(func); + break; + } + } + + DEBUG3(10,"pointers: prev: 0x%.7x, ptr: 0x%.7x, next: 0x%.7x", + ptr->prev, ptr, ptr->next); + + DEBUG3(10,"size: prev: %9d, ptr: %9d, next: %9d", + ptr->prev->s.size, ptr->s.size, ptr->next->s.size); + + DEBUG3(10,"flags: prev: 0x%.7x, ptr: 0x%.7x, next: 0x%.7x", + ptr->prev->flag, ptr->flag, ptr->next->flag); + + /* + * check to see if this block can be combined with the next and/or + * previous block. Since it may be joined with the previous block + * we will save a pointer to the previous block and test to verify + * if it is joined (it's next ptr will no longer point to ptr). + */ + malloc_join(ptr,ptr->next,0,0); + + oldptr = ptr->prev; + + malloc_join(ptr->prev, ptr,0,0); + + if( oldptr->next != ptr ) + { + DEBUG0(10,"Oldptr was changed"); + ptr = oldptr; + } + + /* + * fill this block with '\02's to ensure that nobody is using a + * pointer to already freed data... + */ + malloc_memset(ptr->data,M_FREE_FILL,(int)ptr->s.size); + +} + diff --git a/dmake/dbug/malloc/m_init.c b/dmake/dbug/malloc/m_init.c new file mode 100644 index 000000000000..d4fdc0788f4b --- /dev/null +++ b/dmake/dbug/malloc/m_init.c @@ -0,0 +1,79 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include <stdio.h> +#include "malloc.h" + +/* + * Function: malloc_init() + * + * Purpose: to initialize the pointers and variables use by the + * malloc() debugging library + * + * Arguments: none + * + * Returns: nothing of any value + * + * Narrative: Just initialize all the needed variables. Use mallopt + * to set options taken from the environment. + * + */ +#ifndef lint +static +char rcs_hdr[] = "$Id: m_init.c,v 1.2 2006-07-25 10:08:07 rt Exp $"; +#endif + +void +malloc_init() +{ + char * cptr; + char * getenv(); + union malloptarg m; + extern char * malloc_data_end; + extern char * malloc_data_start; + extern struct mlist * malloc_end; + extern struct mlist malloc_start; + char * sbrk(); + + /* + * If already initialized... + */ + if( malloc_data_start != (char *) 0) + { + return; + } + + + malloc_data_start = sbrk(0); + malloc_data_end = malloc_data_start; + malloc_start.s.size = 0; + malloc_end = &malloc_start; + + if( (cptr=getenv("MALLOC_WARN")) != NULL ) + { + m.i = atoi(cptr); + (void) mallopt(MALLOC_WARN,m); + } + + if( (cptr=getenv("MALLOC_FATAL")) != NULL) + { + m.i = atoi(cptr); + (void) mallopt(MALLOC_FATAL,m); + } + + if( (cptr=getenv("MALLOC_CKCHAIN")) != NULL) + { + m.i = atoi(cptr); + (void) mallopt(MALLOC_CKCHAIN,m); + } + + if( (cptr=getenv("MALLOC_ERRFILE")) != NULL) + { + m.str = cptr; + (void) mallopt(MALLOC_ERRFILE,m); + } + +} + diff --git a/dmake/dbug/malloc/m_perror.c b/dmake/dbug/malloc/m_perror.c new file mode 100644 index 000000000000..b5620182ac4e --- /dev/null +++ b/dmake/dbug/malloc/m_perror.c @@ -0,0 +1,73 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ + +#ifndef lint +static +char rcsid[] = "$Id: m_perror.c,v 1.2 2006-07-25 10:08:21 rt Exp $"; +#endif + +/* + * malloc errno error strings... + */ + +char *malloc_err_strings[] = +{ + "No errors", + "Malloc chain is corrupted, pointers out of order", + "Malloc chain is corrupted, end before end pointer", + "Pointer is not within malloc area", + "Malloc region does not have valid magic number in header", + "Pointers between this segment and ajoining segments are invalid", + "Data has overrun beyond requested number of bytes", + "Data in free'd area has been modified", + "Data are is not in use (can't be freed or realloced)", + "Unable to get additional memory from the system", + "Pointer within malloc region, but outside of malloc data bounds", + (char *) 0 +}; + +/* + * Function: malloc_perror() + * + * Purpose: to print malloc_errno error message + * + * Arguments: str - string to print with error message + * + * Returns: nothing of any value + * + * Narrative: + */ +void +malloc_perror(str) + char * str; +{ + extern int malloc_errno; + register char * s; + register char * t; + + if( str && *str) + { + for(s=str; *s; s++) + { + /* do nothing */; + } + + (void) write(2,str,(unsigned)(s-str)); + (void) write(2,": ",(unsigned)2); + } + + t = malloc_err_strings[malloc_errno]; + + for(s=t; *s; s++) + { + /* do nothing */; + } + + (void) write(2,t,(unsigned)(s-t)); + + (void) write(2,"\n",(unsigned)1); +} + diff --git a/dmake/dbug/malloc/makefile b/dmake/dbug/malloc/makefile new file mode 100644 index 000000000000..88395c7b8f6c --- /dev/null +++ b/dmake/dbug/malloc/makefile @@ -0,0 +1,77 @@ +# +# (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). +# You may copy, distribute, and use this software as long as this +# copyright statement is not removed. +# +# +# This is the Makefile for the malloc debugging library +# +# $Id: makefile,v 1.1.1.1 2000-09-22 15:33:26 hr Exp $ +# +CC=cc +# for System V systems use this CFLAGS +#CFLAGS=-g -DSYS5 +# else for BSD use: +#CFLAGS=-g +LINT=lint +SHARCMD=shar -o mallocshar -l50 -x -a -n Malloclib +SHELL=/bin/sh + +LIB=libmalloc.a + +SRCS= malloc.c \ + free.c \ + realloc.c \ + calloc.c \ + string.c \ + mlc_chk.c \ + mlc_chn.c \ + memory.c \ + tostring.c \ + m_perror.c \ + m_init.c \ + mallopt.c \ + dump.c + +OBJS= malloc.o \ + free.o \ + realloc.o \ + calloc.o \ + string.o \ + mlc_chk.o \ + mlc_chn.o \ + memory.o \ + tostring.o \ + m_perror.o \ + m_init.o \ + mallopt.o \ + dump.o + +TESTS=testmlc testmem + +all: $(LIB) $(TESTS) + +clean: + rm -f $(TESTS) pgm $(LIB) *.o *.ln + +sharfile: + $(SHARCMD) Makefile README patchlevel *.[ch3] + +$(LIB): $(OBJS) + ar ru $(LIB) $(OBJS) + -if test -s /bin/ranlib; then /bin/ranlib $(LIB); else exit 0; fi + -if test -s /usr/bin/ranlib; then /usr/bin/ranlib $(LIB); else exit 0; fi + +testmlc: $(LIB) testmlc.o + $(CC) -o $@ testmlc.o $(LIB) + +testmem: $(LIB) testmem.o + $(CC) -o $@ testmem.o $(LIB) + +lint: + $(LINT) $(CFLAGS) $(SRCS) testmlc.c testmem.c + + +$(OBJS): malloc.h + +tostring.o malloc.o dump.o: tostring.h diff --git a/dmake/dbug/malloc/malloc.3 b/dmake/dbug/malloc/malloc.3 new file mode 100644 index 000000000000..f5e1d2dc0dab --- /dev/null +++ b/dmake/dbug/malloc/malloc.3 @@ -0,0 +1,223 @@ +.TH MALLOC 3 "" "" "1.0" +.ds ]T +.\"/* +.\" * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). +.\" * You may copy, distribute, and use this software as long as this +.\" * copyright statement is not removed. +.\" */ +.\" $Id: malloc.3,v 1.1.1.1 2000-09-22 15:33:26 hr Exp $ +.SH NAME +malloc \t- debugging malloc library +.SH SYNOPSIS +.ft B +.nf +#include <malloc.h> + +char * calloc(nelem,elsize); +void free(ptr); +char * malloc(size); +int malloc_chain_check(flag); +void malloc_dump(fd); +int mallopt(cmd,val) +char * realloc(ptr,size); + +int cmd,fd,flag; +unsigned elsize,nelem,size; +char * ptr; +union malloptarg val; + +.fi +.ft R +.SH DESCRIPTION +This malloc library is a replacement for the standard library to be used +during software development/debugging. See the standard malloc(3) pages +for more information on the use of the following functions: +.nf +.in +.5i +calloc(), free(), malloc(), realloc() +.in -.5i +.fi +.sp +This library differs from the standard malloc library in the +following ways: +.P +1. Each malloc segment contains a magic number so that free can +verify that the pointer passed points to a valid malloc segment. +.P +2. Each malloc segment is filled with a non-zero pattern so that code that +depends upon malloc segments being null will fail. +.P +3. The size of each segment will be at least 1 byte larger than requested +and the extra bytes will be filled with a non-zero pattern. When free is +called, it will verify that you did not go beyond the number of bytes +you asked for. +.P +4. When a segment is freed, it will be filled with a different non-zero pattern +to ensure that the program doesn't depend upon the use of already freed data. +.P +5. Whenever any of the string or memory functions (str*, b*, mem*) are +called with a pointer that is within the malloc arena, the operation is +checked to verify that it does not overrun the malloced segment. A failure +of this check is considered a "warning level error" (described later) and +is handled accordingly. +.P +7. Run time checking can include verification of the malloc chain at each +and every call to one of the malloc functions or manually by calling the +malloc_chain_check function. +.P +6. When a problem is found, the action taken is specified at runtime by +environment variables or at compile time by the use of the mallopt() +function. +.P +There are two arbitrary levels of errors, warning and fatal, that this +library will detect. They are broken down as follows: +.P +.nf +.in +.25i +Warning messages include: +.sp +.in +.5i +.ti -.25i +Calling free with a bad pointer +.br +.ti -.25i +Calling a bstring/string/memory (3) function which will go beyond +the end of a malloc block. Note that the library function is +not modified to refuse the operation. +.sp +.in -.5i +Fatal errors are: +.in +.5i +.ti -.25i +Detectable corruption to the malloc chain. +.in -.5i +.in -.25i +.P +The error handling for each level (warning or fatal) are specified using +environment variables or mallopt(). The coding for the error handling is +as follows: +.sp +.nf +.in +.5i +.ti -.25i + 0 - continue operations +.ti -.25i + 1 - drop core and exit +.ti -.25i + 2 - just exit +.ti -.25i + 3 - drop core, but continue executing. Core files will +be placed into core.[PID].[counter] i.e: core.00123.001 +.ti -.25i +128 - dump malloc chain and continue +.ti -.25i +129 - dump malloc chain, dump core, and exit +.ti -.25i +130 - dump malloc chain, exit +.ti -.25i +131 - dump malloc chain, dump core, continue processing +.in -.5i +.P +In addition error messages can be placed into an error file. +.P +\fBmalloc_opt\fP() is used to set the malloc debugging options. The +following options can be set: +.br +.sp +.in +.5i +MALLOC_WARN - set the error handling for warning level errors. \fBval.i\fP is +an integer that can contain any one of the following values: +.sp +.in +.5i +M_HANDLE_IGNORE - ignore error +.br +M_HANDLE_ABORT - drop core and exit +.br +M_HANDLE_EXIT - just exit (no core drop) +.br +M_HANDLE_CORE - drop core, but keep on going +.br +.in -.5i +.sp +In addition, M_HANDLE_DUMP may be or'd in to cause a dump of the current +malloc chain. +.br +.sp +MALLOC_FATAL - set the error handling for fatal level errors. \fBval.i\fP is +equivalent to \fBval.i\fP for MALLOC_WARN. +.br +.sp +MALLOC_ERRFILE - set the destination for malloc error messages. \fBval.str\fP +is a pointer to a character string containing the name of the file to be used +for error messages. +.br +.sp +MALLOC_CKCHAIN - set the malloc chain checking flag. If \fBval.i\fP is +non-zero, chain checking at every call to malloc is turned on. +.br +.sp +For example, to set up the session to generate a core file for +every malloc warning, to drop core and exit on a malloc fatal, and +to log all messages to the file "malloc_log" do the following: +.sp +.nf +.in +.5i +#include <malloc.h> +malloc_opt(MALLOC_WARN,131); +malloc_opt(MALLOC_FATAL,1); +malloc_opt(MALLOC_ERRFILE,"malloc_log"); +.in -.5i +.fi +.in -.5i +.sp +\fBmalloc_opt\fP() can be used to set/alter the debugging options at any +time. +.P +\fBmalloc_dump\fP() will dump a table of the malloc arena showing all +allocated/freed segments and the first few bytes of data in each segment. +\fBfd\fP is the file descriptor to write the data to. +.P +\fBmalloc_chain_check\fP() will check the status of the malloc arena. +If \fBflag\fP is non-zero, an error found in the chain will cause a +fatal error. \fBmalloc_chain_check\fP() returns zero when there are no +problems found in the malloc chain, non-zero otherwise. +.SH "ENVIRONMENT VARIABLES" +Environment variables can be used to control error handling, error logging +and malloc chain checking at run time. The following environment variables +are used: +.P +MALLOC_WARN - specifies the error handling for warning errors +.br +MALLOC_FATAL - specifies the error handling for fatal errors +.br +MALLOC_ERRFILE - specifies the error log file for error messages. +.br +MALLOC_CKCHAIN - if 1, turns on malloc chain checking at every call to any +of the malloc functions. +.P +For example, to set up the session to generate a core file for +every malloc warning, to drop core and exit on a malloc fatal, and +to log all messages to the file "malloc_log" do the following: +.sp +.nf +.in +.5i +MALLOC_WARN=131 +MALLOC_FATAL=1 +MALLOC_ERRFILE=malloc_log + +export MALLOC_WARN MALLOC_FATAL MALLOC_ERRFILE +.in -.5i +.fi +.SH WARNINGS +This malloc library and it's associated string and memory functions are +much less efficient than the standard functions due in part to the extra +error checking. You do not want to use this library when generating a +production (i.e. releasable) version of your software. It should only +be used during development and testing. +.SH SEE ALSO +stat(2) +.SH AUTHOR +Conor P. Cahill +Virtual Technologies Incorporated +.sp +uunet!virtech!cpcahil diff --git a/dmake/dbug/malloc/malloc.c b/dmake/dbug/malloc/malloc.c new file mode 100644 index 000000000000..ff84e3efce9f --- /dev/null +++ b/dmake/dbug/malloc/malloc.c @@ -0,0 +1,627 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include <stdio.h> +#include <fcntl.h> +#include "malloc.h" +#include "tostring.h" + +/* + * Function: malloc() + * + * Purpose: memory allocator + * + * Arguments: size - size of data area needed + * + * Returns: pointer to allocated area, or NULL if unable + * to allocate addtional data. + * + * Narrative: + * + */ +#ifndef lint +static +char rcs_hdr[] = "$Id: malloc.c,v 1.2 2006-07-25 10:08:36 rt Exp $"; +#endif + +extern int malloc_checking; +char * malloc_data_start; +char * malloc_data_end; +struct mlist * malloc_end; +int malloc_errfd = 2; +int malloc_errno; +int malloc_fatal_level = M_HANDLE_CORE; +struct mlist malloc_start; +int malloc_warn_level; +void malloc_memset(); + +char * +malloc(size) + unsigned int size; +{ + char * func = "malloc"; + char * getenv(); + void malloc_fatal(); + void malloc_init(); + void malloc_split(); + void malloc_warning(); + unsigned int need; + struct mlist * oldptr; + struct mlist * ptr; + char * sbrk(); + + /* + * If this is the first call to malloc... + */ + if( malloc_data_start == (char *) 0 ) + { + malloc_init(); + } + + /* + * If malloc chain checking is on, go do it. + */ + if( malloc_checking ) + { + (void) malloc_chain_check(1); + } + + /* + * always make sure there is at least on extra byte in the malloc + * area so that we can verify that the user does not overrun the + * data area. + */ + size++; + + /* + * Now look for a free area of memory of size bytes... + */ + oldptr = NULL; + for(ptr = &malloc_start; ; ptr = ptr->next) + { + /* + * Since the malloc chain is a forward only chain, any + * pointer that we get should always be positioned in + * memory following the previous pointer. If this is not + * so, we must have a corrupted chain. + */ + if( ptr ) + { + if( ptr<oldptr ) + { + malloc_errno = M_CODE_CHAIN_BROKE; + malloc_fatal(func); + return(NULL); + } + oldptr = ptr; + } + else if( oldptr != malloc_end ) + { + /* + * This should never happen. If it does, then + * we got a real problem. + */ + malloc_errno = M_CODE_NO_END; + malloc_fatal(func); + return(NULL); + } + + + /* + * if this element is already in use... + */ + if( ptr && ((ptr->flag & M_INUSE) != 0) ) + { + continue; + } + + /* + * if there isn't room for this block.. + */ + if( ptr && (ptr->s.size < size) ) + { + continue; + } + + /* + * If ptr is null, we have run out of memory and must sbrk more + */ + if( ptr == NULL ) + { + need = (size + M_SIZE) * (size > 10*1024 ? 1:2); + if( need < M_BLOCKSIZE ) + { + need = M_BLOCKSIZE; + } + else if( need & (M_BLOCKSIZE-1) ) + { + need &= ~(M_BLOCKSIZE-1); + need += M_BLOCKSIZE; + } + ptr = (struct mlist *) sbrk((int)need); + if( ptr == (struct mlist *) -1 ) + { + malloc_errno = M_CODE_NOMORE_MEM; + malloc_fatal(func); + } + malloc_data_end = sbrk((int)0); + + ptr->prev = oldptr; + ptr->next = (struct mlist *) 0; + ptr->s.size = need - M_SIZE; + ptr->flag = M_MAGIC; + + oldptr->next = ptr; + malloc_end = ptr; + + + } /* if( ptr ==... */ + + /* + * Now ptr points to a memory location that can store + * this data, so lets go to work. + */ + + ptr->r_size = size; /* save requested size */ + ptr->flag |= M_INUSE; + + /* + * split off unneeded data area in this block, if possible... + */ + malloc_split(ptr); + + /* + * re-adjust the requested size so that it is what the user + * actually requested... + */ + + ptr->r_size--; + + /* + * just to make sure that noone is misusing malloced + * memory without initializing it, lets set it to + * all '\01's. We call local_memset() because memset() + * may be checking for malloc'd ptrs and this isn't + * a malloc'd ptr yet. + */ + malloc_memset(ptr->data,M_FILL,(int)ptr->s.size); + + return( ptr->data); + + } /* for(... */ + +} /* malloc(... */ + +/* + * Function: malloc_split() + * + * Purpose: to split a malloc segment if there is enough room at the + * end of the segment that isn't being used + * + * Arguments: ptr - pointer to segment to split + * + * Returns: nothing of any use. + * + * Narrative: + * get the needed size of the module + * round the size up to appropriat boundry + * calculate amount of left over space + * if there is enough left over space + * create new malloc block out of remainder + * if next block is free + * join the two blocks together + * fill new empty block with free space filler + * re-adjust pointers and size of current malloc block + * + * + * + * Mod History: + * 90/01/27 cpcahil Initial revision. + */ +void +malloc_split(ptr) + struct mlist * ptr; +{ + extern struct mlist * malloc_end; + void malloc_join(); + int rest; + int size; + struct mlist * tptr; + + size = ptr->r_size; + + /* + * roundup size to the appropriate boundry + */ + + M_ROUNDUP(size); + + /* + * figure out how much room is left in the array. + * if there is enough room, create a new mlist + * structure there. + */ + + if( ptr->s.size > size ) + { + rest = ptr->s.size - size; + } + else + { + rest = 0; + } + + if( rest > (M_SIZE+M_RND) ) + { + tptr = (struct mlist *) (ptr->data+size); + tptr->prev = ptr; + tptr->next = ptr->next; + tptr->flag = M_MAGIC; + tptr->s.size = rest - M_SIZE; + + /* + * If possible, join this segment with the next one + */ + + malloc_join(tptr, tptr->next,0,0); + + if( tptr->next ) + { + tptr->next->prev = tptr; + } + + malloc_memset(tptr->data,M_FREE_FILL, (int)tptr->s.size); + + ptr->next = tptr; + ptr->s.size = size; + + if( malloc_end == ptr ) + { + malloc_end = tptr; + } + } + +} /* malloc_split(... */ + +/* + * Function: malloc_join() + * + * Purpose: to join two malloc segments together (if possible) + * + * Arguments: ptr - pointer to segment to join to. + * nextptr - pointer to next segment to join to ptr. + * + * Returns: nothing of any values. + * + * Narrative: + * + * Mod History: + * 90/01/27 cpcahil Initial revision. + */ +void +malloc_join(ptr,nextptr, inuse_override, fill_flag) + struct mlist * ptr; + struct mlist * nextptr; + int inuse_override; + int fill_flag; +{ + unsigned int newsize; + + if( ptr && ! (inuse_override || (ptr->flag & M_INUSE)) && + nextptr && ! (nextptr->flag & M_INUSE) && + ((ptr->data+ptr->s.size) == (char *) nextptr) ) + { + if( malloc_end == nextptr ) + { + malloc_end = ptr; + } + ptr->next = nextptr->next; + newsize = nextptr->s.size + M_SIZE; + + /* + * if we are to fill and this segment is in use, + * fill in with M_FILL newly added space... + */ + + if(fill_flag && (ptr->flag & M_INUSE) ) + { + malloc_memset(ptr->data+ptr->s.size, + M_FILL, (int)(nextptr->s.size + M_SIZE)); + } + + ptr->s.size += newsize; + if( ptr->next ) + { + ptr->next->prev = ptr; + } + } + +} /* malloc_join(... */ + + +/* + * The following mess is just to ensure that the versions of these functions in + * the current library are included (to make sure that we don't accidentaly get + * the libc versions. (This is the lazy man's -u ld directive) + */ + +void free(); +int strcmp(); +int memcmp(); +char * realloc(); + +void (*malloc_void_funcs[])() = +{ + free, +}; + +int (*malloc_int_funcs[])() = +{ + strcmp, + memcmp, +}; + +char * (*malloc_char_star_funcs[])() = +{ + realloc, +}; + +/* + * This is malloc's own memset which is used without checking the parameters. + */ + +void +malloc_memset(ptr,byte,len) + char * ptr; + char byte; + int len; +{ + + while(len-- > 0) + { + *ptr++ = byte; + } + +} /* malloc_memset(... */ + +/* + * Function: malloc_fatal() + * + * Purpose: to display fatal error message and take approrpriate action + * + * Arguments: funcname - name of function calling this routine + * + * Returns: nothing of any value + * + * Narrative: + * + * Notes: This routine does not make use of any libc functions to build + * and/or disply the error message. This is due to the fact that + * we are probably at a point where malloc is having a real problem + * and we don't want to call any function that may use malloc. + */ +void +malloc_fatal(funcname) + char * funcname; +{ + char errbuf[128]; + void exit(); + void malloc_err_handler(); + extern char * malloc_err_strings[]; + extern int malloc_errno; + extern int malloc_fatal_level; + char * s; + char * t; + + s = errbuf; + t = "Fatal error: "; + while( *s = *t++) + { + s++; + } + t = funcname; + while( *s = *t++) + { + s++; + } + + t = "(): "; + while( *s = *t++) + { + s++; + } + + t = malloc_err_strings[malloc_errno]; + while( *s = *t++) + { + s++; + } + + *(s++) = '\n'; + + if( write(malloc_errfd,errbuf,(unsigned)(s-errbuf)) != (s-errbuf)) + { + (void) write(2,"I/O error to error file\n",(unsigned)24); + exit(110); + } + malloc_err_handler(malloc_fatal_level); + +} /* malloc_fatal(... */ + +/* + * Function: malloc_warning() + * + * Purpose: to display warning error message and take approrpriate action + * + * Arguments: funcname - name of function calling this routine + * + * Returns: nothing of any value + * + * Narrative: + * + * Notes: This routine does not make use of any libc functions to build + * and/or disply the error message. This is due to the fact that + * we are probably at a point where malloc is having a real problem + * and we don't want to call any function that may use malloc. + */ +void +malloc_warning(funcname) + char * funcname; +{ + char errbuf[128]; + void exit(); + void malloc_err_handler(); + extern char * malloc_err_strings[]; + extern int malloc_errno; + extern int malloc_warn_level; + char * s; + char * t; + + s = errbuf; + t = "Warning: "; + while( *s = *t++) + { + s++; + } + t = funcname; + while( *s = *t++) + { + s++; + } + + t = "(): "; + while( *s = *t++) + { + s++; + } + + t = malloc_err_strings[malloc_errno]; + while( *s = *t++) + { + s++; + } + + *(s++) = '\n'; + + if( write(malloc_errfd,errbuf,(unsigned)(s-errbuf)) != (s-errbuf)) + { + (void) write(2,"I/O error to error file\n",(unsigned)24); + exit(110); + } + + malloc_err_handler(malloc_warn_level); + +} /* malloc_warning(... */ + +/* + * Function: malloc_err_handler() + * + * Purpose: to take the appropriate action for warning and/or fatal + * error conditions. + * + * Arguments: level - error handling level + * + * Returns: nothing of any value + * + * Narrative: + * + * Notes: This routine does not make use of any libc functions to build + * and/or disply the error message. This is due to the fact that + * we are probably at a point where malloc is having a real problem + * and we don't want to call any function that may use malloc. + */ +void +malloc_err_handler(level) +{ + void exit(); + void malloc_dump(); + extern int malloc_errfd; + + if( level & M_HANDLE_DUMP ) + { + malloc_dump(malloc_errfd); + } + + switch( level & ~M_HANDLE_DUMP ) + { + /* + * If we are to drop a core file and exit + */ + case M_HANDLE_ABORT: + (void) abort(); + break; + + /* + * If we are to exit.. + */ + case M_HANDLE_EXIT: + exit(200); + break; + +#ifndef __MSDOS__ + /* + * If we are to dump a core, but keep going on our merry way + */ + case M_HANDLE_CORE: + { + int pid; + + /* + * fork so child can abort (and dump core) + */ + if( (pid = fork()) == 0 ) + { + (void) write(2,"Child dumping core\n", + (unsigned)9); + (void) abort(); + } + + /* + * wait for child to finish dumping core + */ + while( wait((int *)0) != pid) + { + } + + /* + * Move core file to core.pid.cnt so + * multiple cores don't overwrite each + * other. + */ + if( access("core",0) == 0 ) + { + static int corecnt; + char filenam[32]; + filenam[0] = 'c'; + filenam[1] = 'o'; + filenam[2] = 'r'; + filenam[3] = 'e'; + filenam[4] = '.'; + (void)tostring(filenam+5,getpid(), + 5, B_DEC, '0'); + filenam[10] = '.'; + (void)tostring(filenam+11,corecnt++, + 3, B_DEC, '0'); + filenam[14] = '\0'; + (void) unlink(filenam); + if( link("core",filenam) == 0) + { + (void) unlink("core"); + } + } + } +#endif + + + /* + * If we are to just ignore the error and keep on processing + */ + case M_HANDLE_IGNORE: + break; + + } /* switch(... */ + +} /* malloc_err_handler(... */ + diff --git a/dmake/dbug/malloc/malloc.h b/dmake/dbug/malloc/malloc.h new file mode 100644 index 000000000000..ed8c34f2a333 --- /dev/null +++ b/dmake/dbug/malloc/malloc.h @@ -0,0 +1,85 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +/* + * $Id: malloc.h,v 1.2 2006-07-25 10:08:50 rt Exp $ + */ +struct mlist +{ + struct mlist * next; /* next entry in chain */ + struct mlist * prev; /* prev entry in chain */ + int flag; /* inuse flag */ + unsigned int r_size; /* requested size */ + union + { + unsigned int size; /* actual size */ + double unused_just_for_alignment; + } s; + char data[4]; +}; + +#define M_SIZE ((int)(char *)((struct mlist *)0)->data) +#define M_RND 0x08 + +#define M_INUSE 0x01 +#define M_MAGIC 0x03156100 + +#define M_BLOCKSIZE (1024*8) + +#define M_FILL '\01' +#define M_FREE_FILL '\02' + +#define M_ROUNDUP(size) {\ + if( size & (M_RND-1) ) \ + { \ + size &= ~(M_RND-1); \ + size += M_RND; \ + } \ + } + +/* + * Malloc warning/fatal error handler defines... + */ +#define M_HANDLE_DUMP 0x80 /* 128 */ +#define M_HANDLE_IGNORE 0 +#define M_HANDLE_ABORT 1 +#define M_HANDLE_EXIT 2 +#define M_HANDLE_CORE 3 + +/* + * Mallopt commands and defaults + */ + +#define MALLOC_WARN 1 /* set malloc warning handling */ +#define MALLOC_FATAL 2 /* set malloc fatal handling */ +#define MALLOC_ERRFILE 3 /* specify malloc error file */ +#define MALLOC_CKCHAIN 4 /* turn on chain checking */ +union malloptarg +{ + int i; + char * str; +}; + +/* + * Malloc warning/fatal error codes + */ + +#define M_CODE_CHAIN_BROKE 1 /* malloc chain is broken */ +#define M_CODE_NO_END 2 /* chain end != endptr */ +#define M_CODE_BAD_PTR 3 /* pointer not in malloc area */ +#define M_CODE_BAD_MAGIC 4 /* bad magic number in header */ +#define M_CODE_BAD_CONNECT 5 /* chain poingers corrupt */ +#define M_CODE_OVERRUN 6 /* data overrun in malloc seg */ +#define M_CODE_REUSE 7 /* reuse of freed area */ +#define M_CODE_NOT_INUSE 8 /* pointer is not in use */ +#define M_CODE_NOMORE_MEM 9 /* no more memory available */ +#define M_CODE_OUTOF_BOUNDS 10 /* gone beyound bounds */ + +void malloc_warning(); +void malloc_fatal(); +void malloc_check_data(); +void malloc_check_str(); +void malloc_verify(); + diff --git a/dmake/dbug/malloc/mallopt.c b/dmake/dbug/malloc/mallopt.c new file mode 100644 index 000000000000..d70daf88647a --- /dev/null +++ b/dmake/dbug/malloc/mallopt.c @@ -0,0 +1,98 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include <stdio.h> +#include <fcntl.h> +#include "malloc.h" + +/* + * Function: mallopt() + * + * Purpose: to set options for the malloc debugging library + * + * Arguments: none + * + * Returns: nothing of any value + * + * Narrative: + * + */ + +#ifndef lint +static +char rcs_hdr[] = "$Id: mallopt.c,v 1.2 2006-07-25 10:09:05 rt Exp $"; +#endif + +int +mallopt(cmd,value) + int cmd; + union malloptarg value; +{ + int i; + extern int malloc_checking; + extern char * malloc_data_start; + extern int malloc_errfd; + extern int malloc_fatal_level; + void malloc_init(); + extern int malloc_warn_level; + register char * s; + + /* + * If not initialized... + */ + if( malloc_data_start == (char *) 0) + { + malloc_init(); + } + + + switch(cmd) + { + case MALLOC_WARN: + malloc_warn_level = value.i; + break; + + case MALLOC_FATAL: + malloc_fatal_level = value.i; + break; + + case MALLOC_CKCHAIN: + malloc_checking = value.i; + break; + + case MALLOC_ERRFILE: + + i = open(value.str,O_CREAT|O_APPEND|O_WRONLY,0666); + if( i == -1 ) + { + (void) write(2, + "Unable to open malloc error file: ", + (unsigned) 34); + for(s=value.str; *s; s++) + { + /* do nothing */; + } + (void) write(2,value.str, + (unsigned)(s-value.str)); + (void) write(2,"\n",(unsigned)1); + } + else + { + if( malloc_errfd != 2 ) + { + (void) close(malloc_errfd); + } + malloc_errfd = i; + } + + break; + + default: + return(1); + } + + return(0); +} + diff --git a/dmake/dbug/malloc/memory.c b/dmake/dbug/malloc/memory.c new file mode 100644 index 000000000000..b2087a76f5af --- /dev/null +++ b/dmake/dbug/malloc/memory.c @@ -0,0 +1,195 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ + +#ifndef lint +static +char rcs_hdr[] = "$Id: memory.c,v 1.2 2006-07-25 10:09:19 rt Exp $"; +#endif + +void malloc_check_data(); + +char * +memccpy(ptr1, ptr2, ch, len) + register char * ptr1; + register char * ptr2; + int len; + int ch; +{ + int check; + register int i; + char * rtn; + + /* + * I know that the assignment could be done in the following, but + * I wanted to perform a check before any assignment, so first I + * determine the length, check the pointers and then do the assignment. + */ + for( i=0; (i < len) && (ptr2[i] != ch); i++) + { + } + if( ptr2[i] == ch ) + { + check = i+1; + } + else + { + check = len; + } + + malloc_check_data("memccpy", ptr1, check); + malloc_check_data("memccpy", ptr2, check); + + /* + * if we found the character... + */ + + if( i < len ) + { + rtn = ptr1+i+1; + i++; + } + else + { + rtn = (char *) 0; + } + + while( i-- ) + { + *(ptr1++) = *(ptr2++); + } + + return(rtn); +} + +char * +memchr(ptr1,ch,len) + register char * ptr1; + register int ch; + int len; +{ + int i; + + for( i=0; (i < len) && (ptr1[i] != (char) ch); i++) + { + } + + malloc_check_data("memchr", ptr1, i); + + if( i < len ) + { + return( ptr1+i ); + } + else + { + return( (char *) 0); + } +} + +char * +memcpy(ptr1, ptr2, len) + register char * ptr1; + register char * ptr2; + register int len; +{ + char * rtn = ptr1; + + malloc_check_data("memcpy", ptr1, len); + malloc_check_data("memcpy", ptr2, len); + + /* + * while the normal memcpy does not guarrantee that it will + * handle overlapping memory correctly, we will try... + */ + if( ptr1 > ptr2 && ptr1 < (ptr2+len)) + { + ptr1 += (len-1); + ptr2 += (len-1); + while( len-- > 0 ) + { + *(ptr1--) = *(ptr2--); + } + } + else + { + while( len-- > 0 ) + { + *(ptr1++) = *(ptr2++); + } + } + + return(rtn); +} + +int +memcmp(ptr1, ptr2, len) + register char * ptr1; + register char * ptr2; + register int len; +{ + malloc_check_data("memcpy", ptr1, len); + malloc_check_data("memcpy", ptr2, len); + + while( --len >= 0 && (*ptr1 == *ptr2) ) + { + ptr1++; + ptr2++; + } + + /* + * If stopped by len, return zero + */ + if( len < 0 ) + { + return(0); + } + + return( *ptr1 - *ptr2 ); +} + +char * +memset(ptr1, ch, len) + register char * ptr1; + register int ch; + register int len; +{ + char * rtn = ptr1; + + malloc_check_data("memcpy", ptr1, len); + + while( len-- ) + { + *(ptr1++) = ch; + } + + return(rtn); +} + +char * +bcopy(ptr2,ptr1,len) + char * ptr2; + char * ptr1; + int len; +{ + return(memcpy(ptr1,ptr2,len)); +} + +char * +bzero(ptr1,len) + char * ptr1; + int len; +{ + return(memset(ptr1,'\0',len)); +} + +int +bcmp(ptr2, ptr1, len) + char * ptr1; + char * ptr2; + int len; +{ + return( memcmp(ptr1,ptr2,len) ); +} + diff --git a/dmake/dbug/malloc/mlc_chk.c b/dmake/dbug/malloc/mlc_chk.c new file mode 100644 index 000000000000..08a01172cb66 --- /dev/null +++ b/dmake/dbug/malloc/mlc_chk.c @@ -0,0 +1,256 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ + +#include <stdio.h> +#include "malloc.h" +#include "debug.h" + +#ifndef lint +static +char rcs_hdr[] = "$Id: mlc_chk.c,v 1.2 2006-07-25 10:09:34 rt Exp $"; +#endif + +extern struct mlist malloc_start; +extern struct mlist * malloc_end; +extern char * malloc_data_start; +extern char * malloc_data_end; + +/* + * Function: malloc_in_arena() + * + * Purpose: to verify address is within malloc arena. + * + * Arguments: ptr - pointer to verify + * + * Returns: TRUE - if pointer is within malloc area + * FALSE - otherwise + * + * Narrative: + * IF pointer is >= malloc area start AND <= malloc area end + * return TRUE + * ELSE + * return FALSE + * + * Mod History: + * 90/01/24 cpcahil Initial revision. + */ +int +malloc_in_arena(ptr) + char * ptr; +{ + extern char * malloc_data_start; + extern char * malloc_data_end; + int rtn = 0; + + if( ptr >= malloc_data_start && ptr <= malloc_data_end ) + { + rtn = 1; + } + + return(rtn); +} + +/* + * Function: malloc_check_str() + * + * Arguments: func - name of function calling this routine + * str - pointer to area to check + * + * Purpose: to verify that if str is within the malloc arena, the data + * it points to does not extend beyond the applicable region. + * + * Returns: Nothing of any use (function is void). + * + * Narrative: + * IF pointer is within malloc arena + * determin length of string + * call malloc_verify() to verify data is withing applicable region + * return + * + * Mod History: + * 90/01/24 cpcahil Initial revision. + * 90/01/29 cpcahil Added code to ignore recursive calls. + */ +void +malloc_check_str(func,str) + char * func; + char * str; +{ + static int layers; + register char * s; + + if( (layers++ == 0) && malloc_in_arena(str) ) + { + for( s=str; *s; s++) + { + } + + malloc_verify(func,str,s-str+1); + } + + layers--; +} + +/* + * Function: malloc_check_strn() + * + * Arguments: func - name of function calling this routine + * str - pointer to area to check + * len - max length of string + * + * Purpose: to verify that if str is within the malloc arena, the data + * it points to does not extend beyond the applicable region. + * + * Returns: Nothing of any use (function is void). + * + * Narrative: + * IF pointer is within malloc arena + * determin length of string + * call malloc_verify() to verify data is withing applicable region + * return + * + * Mod History: + * 90/01/24 cpcahil Initial revision. + * 90/01/29 cpcahil Added code to ignore recursive calls. + * 90/08/29 cpcahil added length (for strn* functions) + */ +void +malloc_check_strn(func,str,len) + char * func; + char * str; + int len; +{ + register int i; + static int layers; + register char * s; + + if( (layers++ == 0) && malloc_in_arena(str) ) + { + for( s=str,i=0; (i < len) && *s; s++) + { + } + + malloc_verify(func,str,s-str+1); + } + + layers--; +} + +/* + * Function: malloc_check_data() + * + * Arguments: func - name of function calling this routine + * ptr - pointer to area to check + * len - length to verify + * + * Purpose: to verify that if ptr is within the malloc arena, the data + * it points to does not extend beyond the applicable region. + * + * Returns: Nothing of any use (function is void). + * + * Narrative: + * IF pointer is within malloc arena + * call malloc_verify() to verify data is withing applicable region + * return + * + * Mod History: + * 90/01/24 cpcahil Initial revision. + * 90/01/29 cpcahil Added code to ignore recursive calls. + */ +void +malloc_check_data(func,ptr,len) + char * func; + char * ptr; + int len; +{ + static int layers; + + if( layers++ == 0 ) + { + DEBUG3(40,"malloc_check_data(%s,0x%x,%d) called...", + func,ptr,len); + if( malloc_in_arena(ptr) ) + { + DEBUG0(10,"pointer in malloc arena, verifying..."); + malloc_verify(func,ptr,len); + } + } + + layers--; +} + +/* + * Function: malloc_verify() + * + * Arguments: func - name of function calling the malloc check routines + * ptr - pointer to area to check + * len - length to verify + * + * Purpose: to verify that the data ptr points to does not extend beyond + * the applicable malloc region. This function is only called + * if it has been determined that ptr points into the malloc arena. + * + * Returns: Nothing of any use (function is void). + * + * Narrative: + * + * Mod History: + * 90/01/24 cpcahil Initial revision. + */ +void +malloc_verify(func,ptr,len) + char * func; + char * ptr; + int len; +{ + extern struct mlist * malloc_end; + extern int malloc_errno; + extern struct mlist malloc_start; + struct mlist * mptr; + + DEBUG3(40,"malloc_verify(%s,0x%x,%d) called...", func,ptr,len); + /* + * Find the malloc block that includes this pointer + */ + mptr = &malloc_start; + while( mptr && + ! (((char *)mptr < ptr) && ((mptr->data+mptr->s.size) > ptr) ) ) + { + mptr = mptr->next; + } + + /* + * if ptr was not in a malloc block, it must be part of + * some direct sbrk() stuff, so just return. + */ + if( ! mptr ) + { + DEBUG1(10,"ptr (0x%x) not found in malloc search", ptr); + return; + } + + /* + * Now we have a valid malloc block that contains the indicated + * pointer. We must verify that it is withing the requested block + * size (as opposed to the real block size which is rounded up to + * allow for correct alignment). + */ + + DEBUG4(60,"Checking 0x%x-0x%x, 0x%x-0x%x", + ptr, ptr+len, mptr->data, mptr->data+mptr->r_size); + + if( (ptr < mptr->data) || ((ptr+len) > (mptr->data+mptr->r_size)) ) + { + DEBUG4(0,"pointer not within region 0x%x-0x%x, 0x%x-0x%x", + ptr, ptr+len, mptr->data, mptr->data+mptr->r_size); + + malloc_errno = M_CODE_OUTOF_BOUNDS; + malloc_warning(func); + } + + return; +} + diff --git a/dmake/dbug/malloc/mlc_chn.c b/dmake/dbug/malloc/mlc_chn.c new file mode 100644 index 000000000000..3f24333611f9 --- /dev/null +++ b/dmake/dbug/malloc/mlc_chn.c @@ -0,0 +1,188 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include <stdio.h> +#include <fcntl.h> +#include "malloc.h" + +/* + * Function: malloc_chain_check() + * + * Purpose: to verify malloc chain is intact + * + * Arguments: todo - 0 - just check and return status + * 1 - call malloc_warn if error detected + * + * Returns: 0 - malloc chain intact & no overflows + * other - problems detected in malloc chain + * + * Narrative: + * + * Notes: If todo is non-zero the malloc_warn function, when called + * may not return (i.e. it may exit) + * + */ +#ifndef lint +static +char rcs_hdr[] = "$Id: mlc_chn.c,v 1.1.1.1 2000-09-22 15:33:26 hr Exp $"; +#endif + + +int +malloc_chain_check(todo) + int todo; +{ + char * func = "malloc_chain_check"; + int i; + extern char * malloc_data_start; + extern char * malloc_data_end; + extern struct mlist * malloc_end; + extern int malloc_errno; + extern struct mlist malloc_start; + struct mlist * oldptr; + struct mlist * ptr; + int rtn = 0; + + oldptr = &malloc_start; + for(ptr = malloc_start.next; ; ptr = ptr->next) + { + /* + * Since the malloc chain is a forward only chain, any + * pointer that we get should always be positioned in + * memory following the previous pointer. If this is not + * so, we must have a corrupted chain. + */ + if( ptr ) + { + if(ptr < oldptr ) + { + malloc_errno = M_CODE_CHAIN_BROKE; + if( todo ) + { + malloc_fatal(func); + } + rtn++; + break; + } + oldptr = ptr; + } + else + { + if( oldptr != malloc_end ) + { + /* + * This should never happen. If it does, then + * we got a real problem. + */ + malloc_errno = M_CODE_NO_END; + if( todo ) + { + malloc_fatal(func); + } + rtn++; + } + break; + } + + /* + * verify that ptr is within the malloc region... + * since we started within the malloc chain this should never + * happen. + */ + + if( ((char *)ptr < malloc_data_start) || + ((char *)ptr > malloc_data_end) ) + { + malloc_errno = M_CODE_BAD_PTR; + if( todo ) + { + malloc_fatal(func); + } + rtn++; + break; + } + + /* + * verify magic flag is set + */ + + if( (ptr->flag&M_MAGIC) != M_MAGIC ) + { + malloc_errno = M_CODE_BAD_MAGIC; + if( todo ) + { + malloc_warning(func); + } + rtn++; + continue; + } + + /* + * verify segments are correctly linked together + */ + + if( (ptr->prev && (ptr->prev->next != ptr) ) || + (ptr->next && (ptr->next->prev != ptr) ) || + ((ptr->next == NULL) && (ptr->prev == NULL)) ) + { + malloc_errno = M_CODE_BAD_CONNECT; + if( todo ) + { + malloc_warning(func); + } + rtn++; + continue; + } + + /* + * If this segment is allocated + */ + + if( (ptr->flag & M_INUSE) != 0 ) + { + /* + * verify no overflow of data area + */ + + for(i=ptr->r_size; i < ptr->s.size; i++) + { + if( ptr->data[i] != M_FILL ) + { + malloc_errno = M_CODE_OVERRUN; + if( todo ) + { + malloc_warning(func); + } + rtn++; + break; + } + } + } + else /* it's not allocated so */ + { + /* + * verify no reuse of freed data blocks + */ + + for(i=0; i < ptr->s.size; i++) + { + if( ptr->data[i] != M_FREE_FILL ) + { + malloc_errno = M_CODE_REUSE; + if( todo ) + { + malloc_warning(func); + } + rtn++; + break; + } + } + } + + } /* for(... */ + + return(rtn); + +} /* malloc_chain_check(... */ diff --git a/dmake/dbug/malloc/patchlev b/dmake/dbug/malloc/patchlev new file mode 100644 index 000000000000..00750edc07d6 --- /dev/null +++ b/dmake/dbug/malloc/patchlev @@ -0,0 +1 @@ +3 diff --git a/dmake/dbug/malloc/realloc.c b/dmake/dbug/malloc/realloc.c new file mode 100644 index 000000000000..2801cd86212d --- /dev/null +++ b/dmake/dbug/malloc/realloc.c @@ -0,0 +1,180 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include <stdio.h> +#include "malloc.h" + +/* + * Function: realloc() + * + * Purpose: to re-allocate a data area. + * + * Arguments: cptr - pointer to area to reallocate + * size - size to change area to + * + * Returns: pointer to new area (may be same area) + * + * Narrative: verify pointer is within malloc region + * obtain mlist pointer from cptr + * verify magic number is correct + * verify inuse flag is set + * verify connection to adjoining segments is correct + * save requested size + * round-up size to appropriate boundry + * IF size is bigger than what is in this segment + * try to join next segment to this segment + * IF size is less than what is is this segment + * determine leftover amount of space + * ELSE + * allocate new segment of size bites + * IF allocation failed + * return NULL + * copy previous data to new segment + * free previous segment + * return new pointer + * split of extra space in this segment (if any) + * clear bytes beyound what they had before + * return pointer to data + */ +#ifndef lint +static +char rcs_hdr[] = "$Id: realloc.c,v 1.2 2006-07-25 10:09:48 rt Exp $"; +#endif + +char * +realloc(cptr,size) + char * cptr; + unsigned int size; +{ + void free(); + char * func = "realloc"; + int i; + char * malloc(); + extern int malloc_checking; + extern struct mlist * malloc_end; + extern int malloc_errno; + extern char * malloc_data_end; + extern char * malloc_data_start; + void malloc_join(); + void malloc_memset(); + void malloc_split(); + char * memcpy(); + char * new_cptr; + struct mlist * ptr; + int r_size; + + /* + * IF malloc chain checking is on, go do it. + */ + if( malloc_checking ) + { + (void) malloc_chain_check(1); + } + + /* + * verify that cptr is within the malloc region... + */ + if( cptr < malloc_data_start || cptr > malloc_data_end ) + { + malloc_errno = M_CODE_BAD_PTR; + malloc_warning(func); + return (NULL); + } + + /* + * convert pointer to mlist struct pointer. To do this we must + * move the pointer backwards the correct number of bytes... + */ + + ptr = (struct mlist *) (cptr - M_SIZE); + + if( (ptr->flag&M_MAGIC) != M_MAGIC ) + { + malloc_errno = M_CODE_BAD_MAGIC; + malloc_warning(func); + return(NULL); + } + + if( ! (ptr->flag & M_INUSE) ) + { + malloc_errno = M_CODE_NOT_INUSE ; + malloc_warning(func); + return(NULL); + } + + if( (ptr->prev && (ptr->prev->next != ptr) ) || + (ptr->next && (ptr->next->prev != ptr) ) || + ((ptr->next == NULL) && (ptr->prev == NULL)) ) + { + malloc_errno = M_CODE_BAD_CONNECT; + malloc_warning(func); + return(NULL); + } + + r_size = ++size; + + M_ROUNDUP(size); + + if( size > ptr->s.size ) + { + malloc_join(ptr,ptr->next,1,1); + } + + if( size > ptr->s.size ) + { + /* + * else we can't combine it, so lets allocate a new chunk, + * copy the data and free the old chunk... + */ + new_cptr = malloc(size); + + if( new_cptr == (char *) 0) + { + return(new_cptr); + } + + if( r_size < ptr->r_size ) + { + i = r_size; + } + else + { + i = ptr->r_size; + } + (void)memcpy(new_cptr,ptr->data,i); + free(cptr); + return(new_cptr); + + } /* else... */ + + /* + * save amount of real data in new segment (this will be used in the + * memset later) and then save requested size of this segment. + */ + + if( ptr->r_size < r_size ) + { + i = ptr->r_size; + } + else + { + i = r_size; + } + + ptr->r_size = r_size; + + /* + * split off extra free space at end of this segment, if possible... + */ + + malloc_split(ptr); + + malloc_memset( ptr->data+i, M_FILL, (int) (ptr->s.size - i)); + + return(ptr->data); + +} /* realloc(... */ + + diff --git a/dmake/dbug/malloc/string.c b/dmake/dbug/malloc/string.c new file mode 100644 index 000000000000..7b92bf07ad1b --- /dev/null +++ b/dmake/dbug/malloc/string.c @@ -0,0 +1,533 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include "malloc.h" + +#ifndef lint +static +char rcs_hdr[] = "$Id: string.c,v 1.2 2006-07-25 10:10:03 rt Exp $"; +#endif + +int malloc_checking = 0; + +char * +strcat(str1,str2) + register char * str1; + register char * str2; +{ + char * rtn; + int len; + + /* + * check pointers agains malloc region. The malloc* functions + * will properly handle the case where a pointer does not + * point into malloc space. + */ + malloc_checking = 1; + + len = strlen(str2); + malloc_check_str("strcat", str2); + + len += strlen(str1) + 1; + malloc_checking = 0; + + malloc_check_data("strcat", str1, len); + + rtn = str1; + + while( *str1 ) + { + str1++; + } + + while( (*str1 = *str2) != '\0' ) + { + str1++; + str2++; + } + + return(rtn); +} + +char * +strdup(str1) + register char * str1; +{ + char * malloc(); + char * rtn; + register char * str2; + + malloc_check_str("strdup", str1); + + rtn = str2 = malloc((unsigned)strlen(str1)+1); + + if( rtn != (char *) 0) + { + while( (*str2 = *str1) != '\0' ) + { + str1++; + str2++; + } + } + + return(rtn); +} + +char * +strncat(str1,str2,len) + register char * str1; + register char * str2; + register int len; +{ + int len1; + int len2; + char * rtn; + + malloc_check_strn("strncat", str2, len); + + malloc_checking = 1; + + len2 = strlen(str2) + 1; + len1 = strlen(str1); + + malloc_checking = 0; + + + if( (len+1) < len2 ) + { + len1 += len + 1; + } + else + { + len1 += len2; + } + malloc_check_data("strncat", str1, len1); + + rtn = str1; + + while( *str1 ) + { + str1++; + } + + while( len-- && ((*str1++ = *str2++) != '\0') ) + { + } + + if( ! len ) + { + *str1 = '\0'; + } + + return(rtn); +} + +int +strcmp(str1,str2) + register char * str1; + register char * str2; +{ + malloc_check_str("strcmp", str1); + malloc_check_str("strcmp", str2); + + while( *str1 && (*str1 == *str2) ) + { + str1++; + str2++; + } + + + /* + * in order to deal with the case of a negative last char of either + * string when the other string has a null + */ + if( (*str2 == '\0') && (*str1 == '\0') ) + { + return(0); + } + else if( *str2 == '\0' ) + { + return(1); + } + else if( *str1 == '\0' ) + { + return(-1); + } + + return( *str1 - *str2 ); +} + +int +strncmp(str1,str2,len) + register char * str1; + register char * str2; + register int len; +{ + malloc_check_strn("strncmp", str1, len); + malloc_check_strn("strncmp", str2, len); + + while( --len >= 0 && *str1 && (*str1 == *str2) ) + { + str1++; + str2++; + } + + if( len < 0 ) + { + return(0); + } + /* + * in order to deal with the case of a negative last char of either + * string when the other string has a null + */ + if( (*str2 == '\0') && (*str1 == '\0') ) + { + return(0); + } + else if( *str2 == '\0' ) + { + return(1); + } + else if( *str1 == '\0' ) + { + return(-1); + } + + return( *str1 - *str2 ); +} + +char * +strcpy(str1,str2) + register char * str1; + register char * str2; +{ + char * rtn; + int len; + + malloc_checking = 1; + len = strlen(str2) + 1; + malloc_checking = 0; + + malloc_check_data("strcpy", str1, len); + malloc_check_data("strcpy", str2, len); + + rtn = str1; + + while( (*str1++ = *str2++) != '\0') + { + } + + return(rtn); +} + +char * +strncpy(str1,str2,len) + register char * str1; + register char * str2; + register int len; +{ + extern int malloc_checking; + char * rtn; + + malloc_check_data("strncpy", str1, len); + malloc_check_strn("strncpy", str2, len); + + rtn = str1; + + while((len-- > 0) && (*str1++ = *str2++) != '\0') + { + } + while( (len-- > 0) ) + { + *str1++ = '\0'; + } + + return(rtn); +} + +int +strlen(str1) + register char * str1; +{ + register char * s; + + if(! malloc_checking ) + { + malloc_check_str("strlen", str1); + } + + for( s = str1; *s; s++) + { + } + + return( s - str1 ); +} + +char * +strchr(str1,c) + register char * str1; + register int c; +{ + malloc_check_str("strchr", str1); + + while( *str1 && (*str1 != (char) c) ) + { + str1++; + } + + if(*str1 != (char) c) + { + str1 = (char *) 0; + } + + return(str1); +} + +char * +strrchr(str1,c) + register char * str1; + register int c; +{ + register char * rtn = (char *) 0; + + malloc_check_str("strrchr", str1); + + while( *str1 ) + { + if(*str1 == (char) c ) + { + rtn = str1; + } + str1++; + } + + if( *str1 == (char) c) + { + rtn = str1; + } + + return(rtn); +} + +char * +index(str1,c) + char * str1; + char c; +{ + return( strchr(str1,c) ); +} + +char * +rindex(str1,c) + char * str1; + char c; +{ + return( strrchr(str1,c) ); +} + +char * +strpbrk(str1,str2) + register char * str1; + register char * str2; +{ + register char * tmp; + + malloc_check_str("strpbrk", str1); + malloc_check_str("strpbrk", str2); + + while(*str1) + { + for( tmp=str2; *tmp && *tmp != *str1; tmp++) + { + } + if( *tmp ) + { + break; + } + str1++; + } + + if( ! *str1 ) + { + str1 = (char *) 0; + } + + return(str1); +} + +int +strspn(str1,str2) + register char * str1; + register char * str2; +{ + register char * tmp; + char * orig = str1; + + malloc_check_str("strspn", str1); + malloc_check_str("strspn", str2); + + while(*str1) + { + for( tmp=str2; *tmp && *tmp != *str1; tmp++) + { + } + if(! *tmp ) + { + break; + } + str1++; + } + + return( (int) (str1 - orig) ); +} + +int +strcspn(str1,str2) + register char * str1; + register char * str2; +{ + register char * tmp; + char * orig = str1; + + malloc_check_str("strcspn", str1); + malloc_check_str("strcspn", str2); + + while(*str1) + { + for( tmp=str2; *tmp && *tmp != *str1; tmp++) + { + } + if( *tmp ) + { + break; + } + str1++; + } + + return( (int) (str1 - orig) ); +} + +/* + * strtok() source taken from that posted to comp.lang.c by Chris Torek + * in Jan 1990. + */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Get next token from string s (NULL on 2nd, 3rd, etc. calls), + * where tokens are nonempty strings separated by runs of + * chars from delim. Writes NULs into s to end tokens. delim need not + * remain constant from call to call. + * + * Modified by cpc: changed variable names to conform with naming + * conventions used in rest of code. Added malloc pointer + * check calls. + */ +char * +strtok(str1, str2) + char * str1; + char * str2; +{ + static char * last; + char * strtoken(); + + if( str1 ) + { + malloc_check_str("strtok", str1); + last = str1; + } + malloc_check_str("strtok", str2); + + return (strtoken(&last, str2, 1)); +} + + +/* + * Get next token from string *stringp, where tokens are (possibly empty) + * strings separated by characters from delim. Tokens are separated + * by exactly one delimiter iff the skip parameter is false; otherwise + * they are separated by runs of characters from delim, because we + * skip over any initial `delim' characters. + * + * Writes NULs into the string at *stringp to end tokens. + * delim will usually, but need not, remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strtoken returns NULL. + */ +char * +strtoken(stringp, delim, skip) + register char **stringp; + register char *delim; + int skip; +{ + register char *s; + register char *spanp; + register int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + + if (skip) { + /* + * Skip (span) leading delimiters (s += strspn(s, delim)). + */ + cont: + c = *s; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) { + s++; + goto cont; + } + } + if (c == 0) { /* no token found */ + *stringp = NULL; + return (NULL); + } + } + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + diff --git a/dmake/dbug/malloc/testmem.c b/dmake/dbug/malloc/testmem.c new file mode 100644 index 000000000000..46fba912f8c0 --- /dev/null +++ b/dmake/dbug/malloc/testmem.c @@ -0,0 +1,646 @@ +/* + * This stuff is all stolen (with permission, since it was in the public + * domain) from Henry Spencer's string and memory library. Thanks Henry. + */ + +/* + * Test program for string(3) routines. + * + * Note that at least one Bell Labs implementation of the string + * routines flunks a couple of these tests -- the ones which test + * behavior on "negative" characters. + */ + +#include <stdio.h> +#include <string.h> + +char * index(); +char * rindex(); + +#define STREQ(a, b) (strcmp((a), (b)) == 0) + +char *it = "<UNSET>"; /* Routine name for message routines. */ +int waserror = 0; /* For exit status. */ + +char uctest[] = "\004\203"; /* For testing signedness of chars. */ +int charsigned; /* Result. */ + +/* + - check - complain if condition is not true + */ +void +check(thing, number) +int thing; +int number; /* Test number for error message. */ +{ + if (!thing) { + printf("%s flunked test %d\n", it, number); + waserror = 1; + } +} + +/* + - equal - complain if first two args don't strcmp as equal + */ +void +equal(a, b, number) +char *a; +char *b; +int number; /* Test number for error message. */ +{ + check(a != NULL && b != NULL && STREQ(a, b), number); +} + +char one[50]; +char two[50]; + +#ifdef UNIXERR +#define ERR 1 +#endif +#ifdef BERKERR +#define ERR 1 +#endif +#ifdef ERR +int f; +extern char *sys_errlist[]; +extern int sys_nerr; +extern int errno; +#endif + +/* ARGSUSED */ +main(argc, argv) +int argc; +char *argv[]; +{ + /* + * First, establish whether chars are signed. + */ + if (uctest[0] < uctest[1]) + charsigned = 0; + else + charsigned = 1; + + /* + * Then, do the rest of the work. Split into two functions because + * some compilers get unhappy about a single immense function. + */ + first(); + second(); + + exit((waserror) ? 1 : 0); +} + +first() +{ + /* + * Test strcmp first because we use it to test other things. + */ + it = "strcmp"; + check(strcmp("", "") == 0, 1); /* Trivial case. */ + check(strcmp("a", "a") == 0, 2); /* Identity. */ + check(strcmp("abc", "abc") == 0, 3); /* Multicharacter. */ + check(strcmp("abc", "abcd") < 0, 4); /* Length mismatches. */ + check(strcmp("abcd", "abc") > 0, 5); + check(strcmp("abcd", "abce") < 0, 6); /* Honest miscompares. */ + check(strcmp("abce", "abcd") > 0, 7); + check(strcmp("a\203", "a") > 0, 8); /* Tricky if char signed. */ + if (charsigned) /* Sign-bit comparison. */ + check(strcmp("a\203", "a\003") < 0, 9); + else + check(strcmp("a\203", "a\003") > 0, 9); + check(strcmp("a", "a\203") < 0, 10); /* Tricky if char signed. */ + + /* + * Test strcpy next because we need it to set up other tests. + */ + it = "strcpy"; + check(strcpy(one, "abcd") == one, 1); /* Returned value. */ + equal(one, "abcd", 2); /* Basic test. */ + + (void) strcpy(one, "x"); + equal(one, "x", 3); /* Writeover. */ + equal(one+2, "cd", 4); /* Wrote too much? */ + + (void) strcpy(two, "hi there"); + (void) strcpy(one, two); + equal(one, "hi there", 5); /* Basic test encore. */ + equal(two, "hi there", 6); /* Stomped on source? */ + + (void) strcpy(one, ""); + equal(one, "", 7); /* Boundary condition. */ + + /* + * strcat + */ + it = "strcat"; + (void) strcpy(one, "ijk"); + check(strcat(one, "lmn") == one, 1); /* Returned value. */ + equal(one, "ijklmn", 2); /* Basic test. */ + + (void) strcpy(one, "x"); + (void) strcat(one, "yz"); + equal(one, "xyz", 3); /* Writeover. */ + equal(one+4, "mn", 4); /* Wrote too much? */ + + (void) strcpy(one, "gh"); + (void) strcpy(two, "ef"); + (void) strcat(one, two); + equal(one, "ghef", 5); /* Basic test encore. */ + equal(two, "ef", 6); /* Stomped on source? */ + + (void) strcpy(one, ""); + (void) strcat(one, ""); + equal(one, "", 7); /* Boundary conditions. */ + (void) strcpy(one, "ab"); + (void) strcat(one, ""); + equal(one, "ab", 8); + (void) strcpy(one, ""); + (void) strcat(one, "cd"); + equal(one, "cd", 9); + + /* + * strncat - first test it as strcat, with big counts, then + * test the count mechanism. + */ + it = "strncat"; + (void) strcpy(one, "ijk"); + check(strncat(one, "lmn", 99) == one, 1); /* Returned value. */ + equal(one, "ijklmn", 2); /* Basic test. */ + + (void) strcpy(one, "x"); + (void) strncat(one, "yz", 99); + equal(one, "xyz", 3); /* Writeover. */ + equal(one+4, "mn", 4); /* Wrote too much? */ + + (void) strcpy(one, "gh"); + (void) strcpy(two, "ef"); + (void) strncat(one, two, 99); + equal(one, "ghef", 5); /* Basic test encore. */ + equal(two, "ef", 6); /* Stomped on source? */ + + (void) strcpy(one, ""); + (void) strncat(one, "", 99); + equal(one, "", 7); /* Boundary conditions. */ + (void) strcpy(one, "ab"); + (void) strncat(one, "", 99); + equal(one, "ab", 8); + (void) strcpy(one, ""); + (void) strncat(one, "cd", 99); + equal(one, "cd", 9); + + (void) strcpy(one, "ab"); + (void) strncat(one, "cdef", 2); + equal(one, "abcd", 10); /* Count-limited. */ + + (void) strncat(one, "gh", 0); + equal(one, "abcd", 11); /* Zero count. */ + + (void) strncat(one, "gh", 2); + equal(one, "abcdgh", 12); /* Count and length equal. */ + + /* + * strncmp - first test as strcmp with big counts, then test + * count code. + */ + it = "strncmp"; + check(strncmp("", "", 99) == 0, 1); /* Trivial case. */ + check(strncmp("a", "a", 99) == 0, 2); /* Identity. */ + check(strncmp("abc", "abc", 99) == 0, 3); /* Multicharacter. */ + check(strncmp("abc", "abcd", 99) < 0, 4); /* Length unequal. */ + check(strncmp("abcd", "abc", 99) > 0, 5); + check(strncmp("abcd", "abce", 99) < 0, 6); /* Honestly unequal. */ + check(strncmp("abce", "abcd", 99) > 0, 7); + check(strncmp("a\203", "a", 2) > 0, 8); /* Tricky if '\203' < 0 */ + if (charsigned) /* Sign-bit comparison. */ + check(strncmp("a\203", "a\003", 2) < 0, 9); + else + check(strncmp("a\203", "a\003", 2) > 0, 9); + check(strncmp("abce", "abcd", 3) == 0, 10); /* Count limited. */ + check(strncmp("abce", "abc", 3) == 0, 11); /* Count == length. */ + check(strncmp("abcd", "abce", 4) < 0, 12); /* Nudging limit. */ + check(strncmp("abc", "def", 0) == 0, 13); /* Zero count. */ + + /* + * strncpy - testing is a bit different because of odd semantics + */ + it = "strncpy"; + check(strncpy(one, "abc", 4) == one, 1); /* Returned value. */ + equal(one, "abc", 2); /* Did the copy go right? */ + + (void) strcpy(one, "abcdefgh"); + (void) strncpy(one, "xyz", 2); + equal(one, "xycdefgh", 3); /* Copy cut by count. */ + + (void) strcpy(one, "abcdefgh"); + (void) strncpy(one, "xyz", 3); /* Copy cut just before NUL. */ + equal(one, "xyzdefgh", 4); + + (void) strcpy(one, "abcdefgh"); + (void) strncpy(one, "xyz", 4); /* Copy just includes NUL. */ + equal(one, "xyz", 5); + equal(one+4, "efgh", 6); /* Wrote too much? */ + + (void) strcpy(one, "abcdefgh"); + (void) strncpy(one, "xyz", 5); /* Copy includes padding. */ + equal(one, "xyz", 7); + equal(one+4, "", 8); + equal(one+5, "fgh", 9); + + (void) strcpy(one, "abc"); + (void) strncpy(one, "xyz", 0); /* Zero-length copy. */ + equal(one, "abc", 10); + + (void) strncpy(one, "", 2); /* Zero-length source. */ + equal(one, "", 11); + equal(one+1, "", 12); + equal(one+2, "c", 13); + + (void) strcpy(one, "hi there"); + (void) strncpy(two, one, 9); + equal(two, "hi there", 14); /* Just paranoia. */ + equal(one, "hi there", 15); /* Stomped on source? */ + + /* + * strlen + */ + it = "strlen"; + check(strlen("") == 0, 1); /* Empty. */ + check(strlen("a") == 1, 2); /* Single char. */ + check(strlen("abcd") == 4, 3); /* Multiple chars. */ + + /* + * strchr + */ + it = "strchr"; + check(strchr("abcd", 'z') == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(strchr(one, 'c') == one+2, 2); /* Basic test. */ + check(strchr(one, 'd') == one+3, 3); /* End of string. */ + check(strchr(one, 'a') == one, 4); /* Beginning. */ + check(strchr(one, '\0') == one+4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(strchr(one, 'b') == one+1, 6); /* Finding first. */ + (void) strcpy(one, ""); + check(strchr(one, 'b') == NULL, 7); /* Empty string. */ + check(strchr(one, '\0') == one, 8); /* NUL in empty string. */ + + /* + * index - just like strchr + */ + it = "index"; + check(index("abcd", 'z') == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(index(one, 'c') == one+2, 2); /* Basic test. */ + check(index(one, 'd') == one+3, 3); /* End of string. */ + check(index(one, 'a') == one, 4); /* Beginning. */ + check(index(one, '\0') == one+4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(index(one, 'b') == one+1, 6); /* Finding first. */ + (void) strcpy(one, ""); + check(index(one, 'b') == NULL, 7); /* Empty string. */ + check(index(one, '\0') == one, 8); /* NUL in empty string. */ + + /* + * strrchr + */ + it = "strrchr"; + check(strrchr("abcd", 'z') == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(strrchr(one, 'c') == one+2, 2); /* Basic test. */ + check(strrchr(one, 'd') == one+3, 3); /* End of string. */ + check(strrchr(one, 'a') == one, 4); /* Beginning. */ + check(strrchr(one, '\0') == one+4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(strrchr(one, 'b') == one+3, 6); /* Finding last. */ + (void) strcpy(one, ""); + check(strrchr(one, 'b') == NULL, 7); /* Empty string. */ + check(strrchr(one, '\0') == one, 8); /* NUL in empty string. */ + + /* + * rindex - just like strrchr + */ + it = "rindex"; + check(rindex("abcd", 'z') == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(rindex(one, 'c') == one+2, 2); /* Basic test. */ + check(rindex(one, 'd') == one+3, 3); /* End of string. */ + check(rindex(one, 'a') == one, 4); /* Beginning. */ + check(rindex(one, '\0') == one+4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(rindex(one, 'b') == one+3, 6); /* Finding last. */ + (void) strcpy(one, ""); + check(rindex(one, 'b') == NULL, 7); /* Empty string. */ + check(rindex(one, '\0') == one, 8); /* NUL in empty string. */ +} + +second() +{ + /* + * strpbrk - somewhat like strchr + */ + it = "strpbrk"; + check(strpbrk("abcd", "z") == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(strpbrk(one, "c") == one+2, 2); /* Basic test. */ + check(strpbrk(one, "d") == one+3, 3); /* End of string. */ + check(strpbrk(one, "a") == one, 4); /* Beginning. */ + check(strpbrk(one, "") == NULL, 5); /* Empty search list. */ + check(strpbrk(one, "cb") == one+1, 6); /* Multiple search. */ + (void) strcpy(one, "abcabdea"); + check(strpbrk(one, "b") == one+1, 7); /* Finding first. */ + check(strpbrk(one, "cb") == one+1, 8); /* With multiple search. */ + check(strpbrk(one, "db") == one+1, 9); /* Another variant. */ + (void) strcpy(one, ""); + check(strpbrk(one, "bc") == NULL, 10); /* Empty string. */ + check(strpbrk(one, "") == NULL, 11); /* Both strings empty. */ + +#if 0 + /* + * strstr - somewhat like strchr + */ + it = "strstr"; + check(strstr("abcd", "z") == NULL, 1); /* Not found. */ + check(strstr("abcd", "abx") == NULL, 2); /* Dead end. */ + (void) strcpy(one, "abcd"); + check(strstr(one, "c") == one+2, 3); /* Basic test. */ + check(strstr(one, "bc") == one+1, 4); /* Multichar. */ + check(strstr(one, "d") == one+3, 5); /* End of string. */ + check(strstr(one, "cd") == one+2, 6); /* Tail of string. */ + check(strstr(one, "abc") == one, 7); /* Beginning. */ + check(strstr(one, "abcd") == one, 8); /* Exact match. */ + check(strstr(one, "abcde") == NULL, 9); /* Too long. */ + check(strstr(one, "de") == NULL, 10); /* Past end. */ + check(strstr(one, "") == one+4, 11); /* Finding empty. */ + (void) strcpy(one, "ababa"); + check(strstr(one, "ba") == one+1, 12); /* Finding first. */ + (void) strcpy(one, ""); + check(strstr(one, "b") == NULL, 13); /* Empty string. */ + check(strstr(one, "") == one, 14); /* Empty in empty string. */ + (void) strcpy(one, "bcbca"); + check(strstr(one, "bca") == one+2, 15); /* False start. */ + (void) strcpy(one, "bbbcabbca"); + check(strstr(one, "bbca") == one+1, 16); /* With overlap. */ +#endif + + /* + * strspn + */ + it = "strspn"; + check(strspn("abcba", "abc") == 5, 1); /* Whole string. */ + check(strspn("abcba", "ab") == 2, 2); /* Partial. */ + check(strspn("abc", "qx") == 0, 3); /* None. */ + check(strspn("", "ab") == 0, 4); /* Null string. */ + check(strspn("abc", "") == 0, 5); /* Null search list. */ + + /* + * strcspn + */ + it = "strcspn"; + check(strcspn("abcba", "qx") == 5, 1); /* Whole string. */ + check(strcspn("abcba", "cx") == 2, 2); /* Partial. */ + check(strcspn("abc", "abc") == 0, 3); /* None. */ + check(strcspn("", "ab") == 0, 4); /* Null string. */ + check(strcspn("abc", "") == 3, 5); /* Null search list. */ + + /* + * strtok - the hard one + */ + it = "strtok"; + (void) strcpy(one, "first, second, third"); + equal(strtok(one, ", "), "first", 1); /* Basic test. */ + equal(one, "first", 2); + equal(strtok((char *)NULL, ", "), "second", 3); + equal(strtok((char *)NULL, ", "), "third", 4); + check(strtok((char *)NULL, ", ") == NULL, 5); + (void) strcpy(one, ", first, "); + equal(strtok(one, ", "), "first", 6); /* Extra delims, 1 tok. */ + check(strtok((char *)NULL, ", ") == NULL, 7); + (void) strcpy(one, "1a, 1b; 2a, 2b"); + equal(strtok(one, ", "), "1a", 8); /* Changing delim lists. */ + equal(strtok((char *)NULL, "; "), "1b", 9); + equal(strtok((char *)NULL, ", "), "2a", 10); + (void) strcpy(two, "x-y"); + equal(strtok(two, "-"), "x", 11); /* New string before done. */ + equal(strtok((char *)NULL, "-"), "y", 12); + check(strtok((char *)NULL, "-") == NULL, 13); + (void) strcpy(one, "a,b, c,, ,d"); + equal(strtok(one, ", "), "a", 14); /* Different separators. */ + equal(strtok((char *)NULL, ", "), "b", 15); + equal(strtok((char *)NULL, " ,"), "c", 16); /* Permute list too. */ + equal(strtok((char *)NULL, " ,"), "d", 17); + check(strtok((char *)NULL, ", ") == NULL, 18); + check(strtok((char *)NULL, ", ") == NULL, 19); /* Persistence. */ + (void) strcpy(one, ", "); + check(strtok(one, ", ") == NULL, 20); /* No tokens. */ + (void) strcpy(one, ""); + check(strtok(one, ", ") == NULL, 21); /* Empty string. */ + (void) strcpy(one, "abc"); + equal(strtok(one, ", "), "abc", 22); /* No delimiters. */ + check(strtok((char *)NULL, ", ") == NULL, 23); + (void) strcpy(one, "abc"); + equal(strtok(one, ""), "abc", 24); /* Empty delimiter list. */ + check(strtok((char *)NULL, "") == NULL, 25); + (void) strcpy(one, "abcdefgh"); + (void) strcpy(one, "a,b,c"); + equal(strtok(one, ","), "a", 26); /* Basics again... */ + equal(strtok((char *)NULL, ","), "b", 27); + equal(strtok((char *)NULL, ","), "c", 28); + check(strtok((char *)NULL, ",") == NULL, 29); + equal(one+6, "gh", 30); /* Stomped past end? */ + equal(one, "a", 31); /* Stomped old tokens? */ + equal(one+2, "b", 32); + equal(one+4, "c", 33); + + /* + * memcmp + */ + it = "memcmp"; + check(memcmp("a", "a", 1) == 0, 1); /* Identity. */ + check(memcmp("abc", "abc", 3) == 0, 2); /* Multicharacter. */ + check(memcmp("abcd", "abce", 4) < 0, 3); /* Honestly unequal. */ + check(memcmp("abce", "abcd", 4) > 0, 4); + check(memcmp("alph", "beta", 4) < 0, 5); + if (charsigned) /* Sign-bit comparison. */ + check(memcmp("a\203", "a\003", 2) < 0, 6); + else + check(memcmp("a\203", "a\003", 2) > 0, 6); + check(memcmp("abce", "abcd", 3) == 0, 7); /* Count limited. */ + check(memcmp("abc", "def", 0) == 0, 8); /* Zero count. */ + + /* + * memchr + */ + it = "memchr"; + check(memchr("abcd", 'z', 4) == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(memchr(one, 'c', 4) == one+2, 2); /* Basic test. */ + check(memchr(one, 'd', 4) == one+3, 3); /* End of string. */ + check(memchr(one, 'a', 4) == one, 4); /* Beginning. */ + check(memchr(one, '\0', 5) == one+4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(memchr(one, 'b', 5) == one+1, 6); /* Finding first. */ + check(memchr(one, 'b', 0) == NULL, 7); /* Zero count. */ + check(memchr(one, 'a', 1) == one, 8); /* Singleton case. */ + (void) strcpy(one, "a\203b"); + check(memchr(one, 0203, 3) == one+1, 9); /* Unsignedness. */ + + /* + * memcpy + * + * Note that X3J11 says memcpy must work regardless of overlap. + * The SVID says it might fail. + */ + it = "memcpy"; + check(memcpy(one, "abc", 4) == one, 1); /* Returned value. */ + equal(one, "abc", 2); /* Did the copy go right? */ + + (void) strcpy(one, "abcdefgh"); + (void) memcpy(one+1, "xyz", 2); + equal(one, "axydefgh", 3); /* Basic test. */ + + (void) strcpy(one, "abc"); + (void) memcpy(one, "xyz", 0); + equal(one, "abc", 4); /* Zero-length copy. */ + + (void) strcpy(one, "hi there"); + (void) strcpy(two, "foo"); + (void) memcpy(two, one, 9); + equal(two, "hi there", 5); /* Just paranoia. */ + equal(one, "hi there", 6); /* Stomped on source? */ + + (void) strcpy(one, "abcdefgh"); + (void) memcpy(one+1, one, 9); + equal(one, "aabcdefgh", 7); /* Overlap, right-to-left. */ + + (void) strcpy(one, "abcdefgh"); + (void) memcpy(one+1, one+2, 7); + equal(one, "acdefgh", 8); /* Overlap, left-to-right. */ + + (void) strcpy(one, "abcdefgh"); + (void) memcpy(one, one, 9); + equal(one, "abcdefgh", 9); /* 100% overlap. */ + + /* + * memccpy - first test like memcpy, then the search part + * + * The SVID, the only place where memccpy is mentioned, says + * overlap might fail, so we don't try it. Besides, it's hard + * to see the rationale for a non-left-to-right memccpy. + */ + it = "memccpy"; + check(memccpy(one, "abc", 'q', 4) == NULL, 1); /* Returned value. */ + equal(one, "abc", 2); /* Did the copy go right? */ + + (void) strcpy(one, "abcdefgh"); + (void) memccpy(one+1, "xyz", 'q', 2); + equal(one, "axydefgh", 3); /* Basic test. */ + + (void) strcpy(one, "abc"); + (void) memccpy(one, "xyz", 'q', 0); + equal(one, "abc", 4); /* Zero-length copy. */ + + (void) strcpy(one, "hi there"); + (void) strcpy(two, "foo"); + (void) memccpy(two, one, 'q', 9); + equal(two, "hi there", 5); /* Just paranoia. */ + equal(one, "hi there", 6); /* Stomped on source? */ + + (void) strcpy(one, "abcdefgh"); + (void) strcpy(two, "horsefeathers"); + check(memccpy(two, one, 'f', 9) == two+6, 7); /* Returned value. */ + equal(one, "abcdefgh", 8); /* Source intact? */ + equal(two, "abcdefeathers", 9); /* Copy correct? */ + + (void) strcpy(one, "abcd"); + (void) strcpy(two, "bumblebee"); + check(memccpy(two, one, 'a', 4) == two+1, 10); /* First char. */ + equal(two, "aumblebee", 11); + check(memccpy(two, one, 'd', 4) == two+4, 12); /* Last char. */ + equal(two, "abcdlebee", 13); + (void) strcpy(one, "xyz"); + check(memccpy(two, one, 'x', 1) == two+1, 14); /* Singleton. */ + equal(two, "xbcdlebee", 15); + + /* + * memset + */ + it = "memset"; + (void) strcpy(one, "abcdefgh"); + check(memset(one+1, 'x', 3) == one+1, 1); /* Return value. */ + equal(one, "axxxefgh", 2); /* Basic test. */ + + (void) memset(one+2, 'y', 0); + equal(one, "axxxefgh", 3); /* Zero-length set. */ + + (void) memset(one+5, 0, 1); + equal(one, "axxxe", 4); /* Zero fill. */ + equal(one+6, "gh", 5); /* And the leftover. */ + + (void) memset(one+2, 010045, 1); + equal(one, "ax\045xe", 6); /* Unsigned char convert. */ + + /* + * bcopy - much like memcpy + * + * Berklix manual is silent about overlap, so don't test it. + */ + it = "bcopy"; + (void) bcopy("abc", one, 4); + equal(one, "abc", 1); /* Simple copy. */ + + (void) strcpy(one, "abcdefgh"); + (void) bcopy("xyz", one+1, 2); + equal(one, "axydefgh", 2); /* Basic test. */ + + (void) strcpy(one, "abc"); + (void) bcopy("xyz", one, 0); + equal(one, "abc", 3); /* Zero-length copy. */ + + (void) strcpy(one, "hi there"); + (void) strcpy(two, "foo"); + (void) bcopy(one, two, 9); + equal(two, "hi there", 4); /* Just paranoia. */ + equal(one, "hi there", 5); /* Stomped on source? */ + + /* + * bzero + */ + it = "bzero"; + (void) strcpy(one, "abcdef"); + bzero(one+2, 2); + equal(one, "ab", 1); /* Basic test. */ + equal(one+3, "", 2); + equal(one+4, "ef", 3); + + (void) strcpy(one, "abcdef"); + bzero(one+2, 0); + equal(one, "abcdef", 4); /* Zero-length copy. */ + + /* + * bcmp - somewhat like memcmp + */ + it = "bcmp"; + check(bcmp("a", "a", 1) == 0, 1); /* Identity. */ + check(bcmp("abc", "abc", 3) == 0, 2); /* Multicharacter. */ + check(bcmp("abcd", "abce", 4) != 0, 3); /* Honestly unequal. */ + check(bcmp("abce", "abcd", 4) != 0, 4); + check(bcmp("alph", "beta", 4) != 0, 5); + check(bcmp("abce", "abcd", 3) == 0, 6); /* Count limited. */ + check(bcmp("abc", "def", 0) == 0, 8); /* Zero count. */ + +#ifdef ERR + /* + * strerror - VERY system-dependent + */ + it = "strerror"; + f = open("/", 1); /* Should always fail. */ + check(f < 0 && errno > 0 && errno < sys_nerr, 1); + equal(strerror(errno), sys_errlist[errno], 2); +#ifdef UNIXERR + equal(strerror(errno), "Is a directory", 3); +#endif +#ifdef BERKERR + equal(strerror(errno), "Permission denied", 3); +#endif +#endif +} diff --git a/dmake/dbug/malloc/testmlc.c b/dmake/dbug/malloc/testmlc.c new file mode 100644 index 000000000000..16e11736cc18 --- /dev/null +++ b/dmake/dbug/malloc/testmlc.c @@ -0,0 +1,176 @@ +/* NOT copyright by SoftQuad Inc. -- msb, 1988 */ +#ifndef lint +static char *SQ_SccsId = "@(#)mtest3.c 1.2 88/08/25"; +#endif +#include <stdio.h> +/* +** looptest.c -- intensive allocator tester +** +** Usage: looptest +** +** History: +** 4-Feb-1987 rtech!daveb +*/ + +# ifdef SYS5 +# define random rand +# else +# include <sys/vadvise.h> +# endif + +# include <stdio.h> +# include <signal.h> +# include <setjmp.h> + +# define MAXITER 1000000 /* main loop iterations */ +# define MAXOBJS 1000 /* objects in pool */ +# define BIGOBJ 90000 /* max size of a big object */ +# define TINYOBJ 80 /* max size of a small object */ +# define BIGMOD 100 /* 1 in BIGMOD is a BIGOBJ */ +# define STATMOD 10000 /* interation interval for status */ + +main( argc, argv ) +int argc; +char **argv; +{ + register int **objs; /* array of objects */ + register int *sizes; /* array of object sizes */ + register int n; /* iteration counter */ + register int i; /* object index */ + register int size; /* object size */ + register int r; /* random number */ + + int objmax; /* max size this iteration */ + int cnt; /* number of allocated objects */ + int nm = 0; /* number of mallocs */ + int nre = 0; /* number of reallocs */ + int nal; /* number of allocated objects */ + int nfre; /* number of free list objects */ + long alm; /* memory in allocated objects */ + long frem; /* memory in free list */ + long startsize; /* size at loop start */ + long endsize; /* size at loop exit */ + long maxiter = 0; /* real max # iterations */ + + extern char end; /* memory before heap */ + char *calloc(); + char *malloc(); + char *sbrk(); + long atol(); + +# ifndef SYS5 + /* your milage may vary... */ + vadvise( VA_ANOM ); +# endif + + if (argc > 1) + maxiter = atol (argv[1]); + if (maxiter <= 0) + maxiter = MAXITER; + + printf("MAXITER %d MAXOBJS %d ", maxiter, MAXOBJS ); + printf("BIGOBJ %d, TINYOBJ %d, nbig/ntiny 1/%d\n", + BIGOBJ, TINYOBJ, BIGMOD ); + fflush( stdout ); + + if( NULL == (objs = (int **)calloc( MAXOBJS, sizeof( *objs ) ) ) ) + { + fprintf(stderr, "Can't allocate memory for objs array\n"); + exit(1); + } + + if( NULL == ( sizes = (int *)calloc( MAXOBJS, sizeof( *sizes ) ) ) ) + { + fprintf(stderr, "Can't allocate memory for sizes array\n"); + exit(1); + } + + /* as per recent discussion on net.lang.c, calloc does not + ** necessarily fill in NULL pointers... + */ + for( i = 0; i < MAXOBJS; i++ ) + objs[ i ] = NULL; + + startsize = sbrk(0) - &end; + printf( "Memory use at start: %d bytes\n", startsize ); + fflush(stdout); + + printf("Starting the test...\n"); + fflush(stdout); + for( n = 0; n < maxiter ; n++ ) + { + if( !(n % STATMOD) ) + { + printf("%d iterations\n", n); + fflush(stdout); + } + + /* determine object of interst and it's size */ + + r = random(); + objmax = ( r % BIGMOD ) ? TINYOBJ : BIGOBJ; + size = r % objmax; + i = r % (MAXOBJS - 1); + + /* either replace the object of get a new one */ + + if( objs[ i ] == NULL ) + { + objs[ i ] = (int *)malloc( size ); + nm++; + } + else + { + /* don't keep bigger objects around */ + if( size > sizes[ i ] ) + { + objs[ i ] = (int *)realloc( objs[ i ], size ); + nre++; + } + else + { + free( objs[ i ] ); + objs[ i ] = (int *)malloc( size ); + nm++; + } + } + + sizes[ i ] = size; + if( objs[ i ] == NULL ) + { + printf("\nCouldn't allocate %d byte object!\n", + size ); + break; + } + } /* for() */ + + printf( "\n" ); + cnt = 0; + for( i = 0; i < MAXOBJS; i++ ) + if( objs[ i ] ) + cnt++; + + printf( "Did %d iterations, %d objects, %d mallocs, %d reallocs\n", + n, cnt, nm, nre ); + printf( "Memory use at end: %d bytes\n", sbrk(0) - &end ); + fflush( stdout ); + + /* free all the objects */ + for( i = 0; i < MAXOBJS; i++ ) + if( objs[ i ] != NULL ) + free( objs[ i ] ); + + endsize = sbrk(0) - &end; + printf( "Memory use after free: %d bytes\n", endsize ); + fflush( stdout ); + + if( startsize != endsize ) + printf("startsize %d != endsize %d\n", startsize, endsize ); + + free( objs ); + free( sizes ); + + malloc_dump(2); + exit( 0 ); +} + diff --git a/dmake/dbug/malloc/tostring.c b/dmake/dbug/malloc/tostring.c new file mode 100644 index 000000000000..e3bc9990271d --- /dev/null +++ b/dmake/dbug/malloc/tostring.c @@ -0,0 +1,132 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +#include "tostring.h" + +/* + * Function: tostring() + * + * Purpose: to convert an integer to an ascii display string + * + * Arguments: buf - place to put the + * val - integer to convert + * len - length of output field (0 if just enough to hold data) + * base - base for number conversion (only works for base <= 16) + * fill - fill char when len > # digits + * + * Returns: length of string + * + * Narrative: IF fill character is non-blank + * Determine base + * If base is HEX + * add "0x" to begining of string + * IF base is OCTAL + * add "0" to begining of string + * + * While value is greater than zero + * use val % base as index into xlation str to get cur char + * divide val by base + * + * Determine fill-in length + * + * Fill in fill chars + * + * Copy in number + * + * + * Mod History: + * 90/01/24 cpcahil Initial revision. + */ + +#ifndef lint +static +char rcs_hdr[] = "$Id: tostring.c,v 1.2 2006-07-25 10:10:17 rt Exp $"; +#endif + +#define T_LEN 10 + +int +tostring(buf,val,len,base,fill) + int base; + char * buf; + char fill; + int len; + int val; + +{ + char * bufstart = buf; + int i = T_LEN; + char * xbuf = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char tbuf[T_LEN]; + + /* + * if we are filling with non-blanks, make sure the + * proper start string is added + */ + if( fill != ' ' ) + { + switch(base) + { + case B_HEX: + *(buf++) = '0'; + *(buf++) = 'x'; + if( len ) + { + len -= 2; + } + break; + case B_OCTAL: + *(buf++) = fill; + if( len ) + { + len--; + } + break; + default: + break; + } + } + + while( val > 0 ) + { + tbuf[--i] = xbuf[val % base]; + val = val / base; + } + + if( len ) + { + len -= (T_LEN - i); + + if( len > 0 ) + { + while(len-- > 0) + { + *(buf++) = fill; + } + } + else + { + /* + * string is too long so we must truncate + * off some characters. We do this the easiest + * way by just incrementing i. This means the + * most significant digits are lost. + */ + while( len++ < 0 ) + { + i++; + } + } + } + + while( i < T_LEN ) + { + *(buf++) = tbuf[i++]; + } + + return( (int) (buf - bufstart) ); + +} /* tostring(... */ + diff --git a/dmake/dbug/malloc/tostring.h b/dmake/dbug/malloc/tostring.h new file mode 100644 index 000000000000..ccde36db4173 --- /dev/null +++ b/dmake/dbug/malloc/tostring.h @@ -0,0 +1,13 @@ +/* + * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). + * You may copy, distribute, and use this software as long as this + * copyright statement is not removed. + */ +/* + * $Id: tostring.h,v 1.2 2006-07-25 10:10:32 rt Exp $ + */ +#define B_BIN 2 +#define B_DEC 10 +#define B_HEX 16 +#define B_OCTAL 8 + diff --git a/dmake/dbug/readme b/dmake/dbug/readme new file mode 100644 index 000000000000..15efc00a7edc --- /dev/null +++ b/dmake/dbug/readme @@ -0,0 +1,13 @@ +This directory contains two public domain debugging packages. + + 1. Fred Fishes DEBUG macros. + 2. Connor P. Cahills malloc library. + +Descriptions of both can be found in their respective sub-directories. dbug +for the DEBUG macros and malloc for the malloc library. I have left the +malloc distribution intact as it comes from the net except for the changes +noted in the _changes file. + +I thank the authors for making them available for others to use. + +-dennis |