dbug.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:49k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /******************************************************************************
  2.  *       *
  3.  *    N O T I C E       *
  4.  *       *
  5.  *       Copyright Abandoned, 1987, Fred Fish       *
  6.  *       *
  7.  *       *
  8.  * This previously copyrighted work has been placed into the  public     *
  9.  * domain by  the  author  and  may be freely used for any purpose,     *
  10.  * private or commercial.       *
  11.  *       *
  12.  * Because of the number of inquiries I was receiving about the  use     *
  13.  * of this product in commercially developed works I have decided to     *
  14.  * simply make it public domain to further its unrestricted use. I     *
  15.  * specifically  would  be  most happy to see this material become a     *
  16.  * part of the standard Unix distributions by AT&T and the  Berkeley     *
  17.  * Computer  Science  Research Group, and a standard part of the GNU     *
  18.  * system from the Free Software Foundation.       *
  19.  *       *
  20.  * I would appreciate it, as a courtesy, if this notice is  left  in     *
  21.  * all copies and derivative works.  Thank you.       *
  22.  *       *
  23.  * The author makes no warranty of any kind  with respect  to  this     *
  24.  * product  and  explicitly disclaims any implied warranties of mer-     *
  25.  * chantability or fitness for any particular purpose.       *
  26.  *       *
  27.  ******************************************************************************
  28.  */
  29. /*
  30.  *  FILE
  31.  *
  32.  * dbug.c  runtime support routines for dbug package
  33.  *
  34.  *  SCCS
  35.  *
  36.  * @(#)dbug.c 1.25 7/25/89
  37.  *
  38.  *  DESCRIPTION
  39.  *
  40.  * These are the runtime support routines for the dbug package.
  41.  * The dbug package has two main components; the user include
  42.  * file containing various macro definitions, and the runtime
  43.  * support routines which are called from the macro expansions.
  44.  *
  45.  * Externally visible functions in the runtime support module
  46.  * use the naming convention pattern "_db_xx...xx_", thus
  47.  * they are unlikely to collide with user defined function names.
  48.  *
  49.  *  AUTHOR(S)
  50.  *
  51.  * Fred Fish (base code)
  52.  * Enhanced Software Technologies, Tempe, AZ
  53.  * asuvax!mcdphx!estinc!fnf
  54.  *
  55.  * Binayak Banerjee (profiling enhancements)
  56.  * seismo!bpa!sjuvax!bbanerje
  57.  *
  58.  * Michael Widenius:
  59.  * DBUG_DUMP - To dump a pice of memory.
  60.  * PUSH_FLAG "O" - To be used insted of "o" if we don't
  61.  *   want flushing (for slow systems)
  62.  * PUSH_FLAG "A" - as 'O', but we will append to the out file instead
  63.  *   of creating a new one.
  64.  * Check of malloc on entry/exit (option "S")
  65.  */
  66. #ifdef DBUG_OFF
  67. #undef DBUG_OFF
  68. #endif
  69. #include <global.h>
  70. #include <m_string.h>
  71. #include <errno.h>
  72. #if defined(MSDOS) || defined(__WIN__)
  73. #include <process.h>
  74. #endif
  75. #ifdef _DBUG_CONDITION_
  76. #define _DBUG_START_CONDITION_ "d:t"
  77. #else
  78. #define _DBUG_START_CONDITION_ ""
  79. #endif
  80. /*
  81.  *       Manifest constants that should not require any changes.
  82.  */
  83. #define EOS       '00'  /* End Of String marker */
  84. /*
  85.  *       Manifest constants which may be "tuned" if desired.
  86.  */
  87. #define PRINTBUF       1024    /* Print buffer size */
  88. #define INDENT       2       /* Indentation per trace level */
  89. #define MAXDEPTH       200     /* Maximum trace depth default */
  90. /*
  91.  * The following flags are used to determine which
  92.  * capabilities the user has enabled with the state
  93.  * push macro.
  94.  */
  95. #define TRACE_ON 000001 /* Trace enabled */
  96. #define DEBUG_ON 000002 /* Debug enabled */
  97. #define FILE_ON 000004 /* File name print enabled */
  98. #define LINE_ON 000010 /* Line number print enabled */
  99. #define DEPTH_ON 000020 /* Function nest level print enabled */
  100. #define PROCESS_ON 000040 /* Process name print enabled */
  101. #define NUMBER_ON 000100 /* Number each line of output */
  102. #define PROFILE_ON 000200 /* Print out profiling code */
  103. #define PID_ON 000400 /* Identify each line with process id */
  104. #define SANITY_CHECK_ON 001000 /* Check safemalloc on DBUG_ENTER */
  105. #define FLUSH_ON_WRITE 002000 /* Flush on every write */
  106. #define TRACING (stack -> flags & TRACE_ON)
  107. #define DEBUGGING (stack -> flags & DEBUG_ON)
  108. #define PROFILING (stack -> flags & PROFILE_ON)
  109. #define STREQ(a,b) (strcmp(a,b) == 0)
  110. /*
  111.  * Typedefs to make things more obvious.
  112.  */
  113. #ifndef __WIN__
  114. typedef int BOOLEAN;
  115. #else
  116. #define BOOLEAN BOOL
  117. #endif
  118. /*
  119.  * Make it easy to change storage classes if necessary.
  120.  */
  121. #define IMPORT extern /* Names defined externally */
  122. #define EXPORT /* Allocated here, available globally */
  123. #define AUTO auto /* Names to be allocated on stack */
  124. #define REGISTER register /* Names to be placed in registers */
  125. /*
  126.  * The default file for profiling.  Could also add another flag
  127.  * (G?) which allowed the user to specify this.
  128.  *
  129.  * If the automatic variables get allocated on the stack in
  130.  * reverse order from their declarations, then define AUTOS_REVERSE.
  131.  * This is used by the code that keeps track of stack usage.  For
  132.  * forward allocation, the difference in the dbug frame pointers
  133.  * represents stack used by the callee function.  For reverse allocation,
  134.  * the difference represents stack used by the caller function.
  135.  *
  136.  */
  137. #define PROF_FILE "dbugmon.out"
  138. #define PROF_EFMT "Et%ldt%sn"
  139. #define PROF_SFMT "St%lxt%lxt%sn"
  140. #define PROF_XFMT "Xt%ldt%sn"
  141. #ifdef M_I386 /* predefined by xenix 386 compiler */
  142. #define AUTOS_REVERSE 1
  143. #endif
  144. /*
  145.  * Variables which are available externally but should only
  146.  * be accessed via the macro package facilities.
  147.  */
  148. EXPORT FILE *_db_fp_ = (FILE *) 0; /* Output stream, default stderr */
  149. EXPORT char *_db_process_ = (char*) "dbug"; /* Pointer to process name; argv[0] */
  150. EXPORT FILE *_db_pfp_ = (FILE *)0; /* Profile stream, 'dbugmon.out' */
  151. EXPORT BOOLEAN _db_on_ = FALSE; /* TRUE if debugging currently on */
  152. EXPORT BOOLEAN _db_pon_ = FALSE; /* TRUE if profile currently on */
  153. EXPORT BOOLEAN _no_db_ = FALSE; /* TRUE if no debugging at all */
  154. /*
  155.  * Externally supplied functions.
  156.  */
  157. #ifndef HAVE_PERROR
  158. static void perror (); /* Fake system/library error print routine */
  159. #endif
  160. IMPORT int _sanity(const char *file,uint line);
  161. /*
  162.  * The user may specify a list of functions to trace or
  163.  * debug. These lists are kept in a linear linked list,
  164.  * a very simple implementation.
  165.  */
  166. struct link {
  167.     char *str;       /* Pointer to link's contents */
  168.     struct link *next_link;   /* Pointer to the next link */
  169. };
  170. /*
  171.  * Debugging states can be pushed or popped off of a
  172.  * stack which is implemented as a linked list.  Note
  173.  * that the head of the list is the current state and the
  174.  * stack is pushed by adding a new state to the head of the
  175.  * list or popped by removing the first link.
  176.  */
  177. struct state {
  178.   int flags; /* Current state flags */
  179.   int maxdepth; /* Current maximum trace depth */
  180.   uint delay; /* Delay after each output line */
  181.   int sub_level; /* Sub this from code_state->level */
  182.   FILE *out_file; /* Current output stream */
  183.   FILE *prof_file; /* Current profiling stream */
  184.   char name[FN_REFLEN]; /* Name of output file */
  185.   struct link *functions; /* List of functions */
  186.   struct link *p_functions; /* List of profiled functions */
  187.   struct link *keywords; /* List of debug keywords */
  188.   struct link *processes; /* List of process names */
  189.   struct state *next_state; /* Next state in the list */
  190. };
  191. /*
  192.  * Local variables not seen by user.
  193.  */
  194. static my_bool init_done = FALSE; /* Set to TRUE when initialization done */
  195. static struct state *stack=0;
  196. typedef struct st_code_state {
  197.   int lineno; /* Current debugger output line number */
  198.   int level; /* Current function nesting level */
  199.   const char *func; /* Name of current user function */
  200.   const char *file; /* Name of current user file */
  201.   char **framep; /* Pointer to current frame */
  202.   int jmplevel; /* Remember nesting level at setjmp () */
  203.   const char *jmpfunc; /* Remember current function for setjmp */
  204.   const char *jmpfile; /* Remember current file for setjmp */
  205. /*
  206.  * The following variables are used to hold the state information
  207.  * between the call to _db_pargs_() and _db_doprnt_(), during
  208.  * expansion of the DBUG_PRINT macro.  This is the only macro
  209.  * that currently uses these variables.
  210.  *
  211.  * These variables are currently used only by _db_pargs_() and
  212.  * _db_doprnt_().
  213.  */
  214.   uint u_line; /* User source code line number */
  215.   const char *u_keyword; /* Keyword for current macro */
  216.   int  locked; /* If locked with _db_lock_file */
  217. } CODE_STATE;
  218. /* Parse a debug command string */
  219. static struct link *ListParse(char *ctlp);
  220. /* Make a fresh copy of a string */
  221. static char *StrDup(const char *str);
  222. /* Open debug output stream */
  223. static void DBUGOpenFile(const char *name, int append);
  224. #ifndef THREAD
  225. /* Open profile output stream */
  226. static FILE *OpenProfile(const char *name);
  227. /* Profile if asked for it */
  228. static BOOLEAN DoProfile(void);
  229. #endif
  230. /* Return current user time (ms) */
  231. #ifndef THREAD
  232. static unsigned long Clock (void);
  233. #endif
  234. /* Close debug output stream */
  235. static void CloseFile(FILE *fp);
  236. /* Push current debug state */
  237. static void PushState(void);
  238. /* Test for tracing enabled */
  239. static BOOLEAN DoTrace(CODE_STATE *state);
  240. /* Test to see if file is writable */
  241. #if !(!defined(HAVE_ACCESS) || defined(MSDOS))
  242. static BOOLEAN Writable(char *pathname);
  243. /* Change file owner and group */
  244. static void ChangeOwner(char *pathname);
  245. /* Allocate memory for runtime support */
  246. #endif
  247. static char *DbugMalloc(int size);
  248. /* Remove leading pathname components */
  249. static char *BaseName(const char *pathname);
  250. static void DoPrefix(uint line);
  251. static void FreeList(struct link *linkp);
  252. static void Indent(int indent);
  253. static BOOLEAN InList(struct link *linkp,const char *cp);
  254. static void dbug_flush(CODE_STATE *);
  255. static void DbugExit(const char *why);
  256. static int DelayArg(int value);
  257. /* Supplied in Sys V runtime environ */
  258. /* Break string into tokens */
  259. static char *static_strtok(char *s1,pchar chr);
  260. /*
  261.  * Miscellaneous printf format strings.
  262.  */
  263. #define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function "%s"n"
  264. #define ERR_OPEN "%s: can't open debug output stream "%s": "
  265. #define ERR_CLOSE "%s: can't close debug file: "
  266. #define ERR_ABORT "%s: debugger aborting because %sn"
  267. #define ERR_CHOWN "%s: can't change owner/group of "%s": "
  268. /*
  269.  * Macros and defines for testing file accessibility under UNIX and MSDOS.
  270.  */
  271. #if !defined(HAVE_ACCESS) || defined(MSDOS)
  272. #define EXISTS(pathname) (FALSE) /* Assume no existance */
  273. #define Writable(name) (TRUE)
  274. #else
  275. #define EXISTS(pathname)  (access (pathname, F_OK) == 0)
  276. #define WRITABLE(pathname)  (access (pathname, W_OK) == 0)
  277. #endif
  278. #ifndef MSDOS
  279. #define ChangeOwner(name)
  280. #endif
  281. /*
  282.  * Translate some calls among different systems.
  283.  */
  284. #if defined(unix) || defined(xenix) || defined(VMS) || defined(__NetBSD__)
  285. # define Delay(A) sleep((uint) A)
  286. #elif defined(AMIGA)
  287. IMPORT int Delay (); /* Pause for given number of ticks */
  288. #else
  289. static int Delay(int ticks);
  290. #endif
  291. /*
  292. ** Macros to allow dbugging with threads
  293. */
  294. #ifdef THREAD
  295. #include <my_pthread.h>
  296. pthread_mutex_t THR_LOCK_dbug;
  297. static void init_dbug_state(void)
  298. {
  299.   pthread_mutex_init(&THR_LOCK_dbug,NULL);
  300. }
  301. static CODE_STATE *code_state(void)
  302. {
  303.   CODE_STATE *state=0;
  304.   struct st_my_thread_var *tmp=my_thread_var;
  305.   if (tmp)
  306.   {
  307.     if (!(state=(CODE_STATE *) tmp->dbug))
  308.     {
  309.       state=(CODE_STATE*) DbugMalloc(sizeof(*state));
  310.       bzero((char*) state,sizeof(*state));
  311.       state->func="?func";
  312.       state->file="?file";
  313.       tmp->dbug=(gptr) state;
  314.     }
  315.   }
  316.   return state;
  317. }
  318. #else /* !THREAD */
  319. #define init_dbug_state()
  320. #define code_state() (&static_code_state)
  321. #define pthread_mutex_lock(A) {}
  322. #define pthread_mutex_unlock(A) {}
  323. static CODE_STATE  static_code_state = { 0,0,"?func","?file",NULL,0,NULL,
  324.  NULL,0,"?",0};
  325. #endif
  326. /*
  327.  *  FUNCTION
  328.  *
  329.  * _db_push_ push current debugger state and set up new one
  330.  *
  331.  *  SYNOPSIS
  332.  *
  333.  * VOID _db_push_ (control)
  334.  * char *control;
  335.  *
  336.  *  DESCRIPTION
  337.  *
  338.  * Given pointer to a debug control string in "control", pushes
  339.  * the current debug state, parses the control string, and sets
  340.  * up a new debug state.
  341.  *
  342.  * The only attribute of the new state inherited from the previous
  343.  * state is the current function nesting level.  This can be
  344.  * overridden by using the "r" flag in the control string.
  345.  *
  346.  * The debug control string is a sequence of colon separated fields
  347.  * as follows:
  348.  *
  349.  * <field_1>:<field_2>:...:<field_N>
  350.  *
  351.  * Each field consists of a mandatory flag character followed by
  352.  * an optional "," and comma separated list of modifiers:
  353.  *
  354.  * flag[,modifier,modifier,...,modifier]
  355.  *
  356.  * The currently recognized flag characters are:
  357.  *
  358.  * d Enable output from DBUG_<N> macros for
  359.  * for the current state. May be followed
  360.  * by a list of keywords which selects output
  361.  * only for the DBUG macros with that keyword.
  362.  * A null list of keywords implies output for
  363.  * all macros.
  364.  *
  365.  * D Delay after each debugger output line.
  366.  * The argument is the number of tenths of seconds
  367.  * to delay, subject to machine capabilities.
  368.  * I.E.  -#D,20 is delay two seconds.
  369.  *
  370.  * f Limit debugging and/or tracing, and profiling to the
  371.  * list of named functions.  Note that a null list will
  372.  * disable all functions. The appropriate "d" or "t"
  373.  * flags must still be given, this flag only limits their
  374.  * actions if they are enabled.
  375.  *
  376.  * F Identify the source file name for each
  377.  * line of debug or trace output.
  378.  *
  379.  * i Identify the process with the pid for each line of
  380.  * debug or trace output.
  381.  *
  382.  * g Enable profiling.  Create a file called 'dbugmon.out'
  383.  * containing information that can be used to profile
  384.  * the program.  May be followed by a list of keywords
  385.  * that select profiling only for the functions in that
  386.  * list.  A null list implies that all functions are
  387.  * considered.
  388.  *
  389.  * L Identify the source file line number for
  390.  * each line of debug or trace output.
  391.  *
  392.  * n Print the current function nesting depth for
  393.  * each line of debug or trace output.
  394.  *
  395.  * N Number each line of dbug output.
  396.  *
  397.  * o Redirect the debugger output stream to the
  398.  * specified file.  The default output is stderr.
  399.  *
  400.  * O As O but the file is really flushed between each
  401.  * write. When neaded the file is closed and reopened
  402.  * between each write.
  403.  *
  404.  * p Limit debugger actions to specified processes.
  405.  * A process must be identified with the
  406.  * DBUG_PROCESS macro and match one in the list
  407.  * for debugger actions to occur.
  408.  *
  409.  * P Print the current process name for each
  410.  * line of debug or trace output.
  411.  *
  412.  * r When pushing a new state, do not inherit
  413.  * the previous state's function nesting level.
  414.  * Useful when the output is to start at the
  415.  * left margin.
  416.  *
  417.  * S Do function _sanity(_file_,_line_) at each
  418.  * debugged function until _sanity() returns
  419.  * something that differs from 0.
  420.  * (Moustly used with safemalloc)
  421.  *
  422.  * t Enable function call/exit trace lines.
  423.  * May be followed by a list (containing only
  424.  * one modifier) giving a numeric maximum
  425.  * trace level, beyond which no output will
  426.  * occur for either debugging or tracing
  427.  * macros.  The default is a compile time
  428.  * option.
  429.  *
  430.  * Some examples of debug control strings which might appear
  431.  * on a shell command line (the "-#" is typically used to
  432.  * introduce a control string to an application program) are:
  433.  *
  434.  * -#d:t
  435.  * -#d:f,main,subr1:F:L:t,20
  436.  * -#d,input,output,files:n
  437.  *
  438.  * For convenience, any leading "-#" is stripped off.
  439.  *
  440.  */
  441. void _db_push_ (control)
  442. const char *control;
  443. {
  444.   reg1 char *scan;
  445.   reg2 struct link *temp;
  446.   CODE_STATE *state;
  447.   char *new_str;
  448.   if (! _db_fp_)
  449.     _db_fp_= stderr; /* Output stream, default stderr */
  450.   if (control && *control == '-')
  451.   {
  452.     if (*++control == '#')
  453.       control++;
  454.   }
  455.   if (*control)
  456.     _no_db_=0; /* We are using dbug after all */
  457.   new_str = StrDup (control);
  458.   PushState ();
  459.   state=code_state();
  460.   scan = static_strtok (new_str, ':');
  461.   for (; scan != NULL; scan = static_strtok ((char *)NULL, ':')) {
  462.     switch (*scan++) {
  463.     case 'd':
  464.       _db_on_ = TRUE;
  465.       stack -> flags |= DEBUG_ON;
  466.       if (*scan++ == ',') {
  467. stack -> keywords = ListParse (scan);
  468.       }
  469.       break;
  470.     case 'D':
  471.       stack -> delay = 0;
  472.       if (*scan++ == ',') {
  473. temp = ListParse (scan);
  474. stack -> delay = DelayArg (atoi (temp -> str));
  475. FreeList (temp);
  476.       }
  477.       break;
  478.     case 'f':
  479.       if (*scan++ == ',') {
  480. stack -> functions = ListParse (scan);
  481.       }
  482.       break;
  483.     case 'F':
  484.       stack -> flags |= FILE_ON;
  485.       break;
  486.     case 'i':
  487.       stack -> flags |= PID_ON;
  488.       break;
  489. #ifndef THREAD
  490.     case 'g':
  491.       _db_pon_ = TRUE;
  492.       if (OpenProfile(PROF_FILE))
  493.       {
  494. stack -> flags |= PROFILE_ON;
  495. if (*scan++ == ',')
  496.   stack -> p_functions = ListParse (scan);
  497.       }
  498.       break;
  499. #endif
  500.     case 'L':
  501.       stack -> flags |= LINE_ON;
  502.       break;
  503.     case 'n':
  504.       stack -> flags |= DEPTH_ON;
  505.       break;
  506.     case 'N':
  507.       stack -> flags |= NUMBER_ON;
  508.       break;
  509.     case 'A':
  510.     case 'O':
  511.       stack -> flags |= FLUSH_ON_WRITE;
  512.     case 'a':
  513.     case 'o':
  514.       if (*scan++ == ',') {
  515. temp = ListParse (scan);
  516. DBUGOpenFile(temp -> str, (int) (scan[-2] == 'A' || scan[-2] == 'a'));
  517. FreeList (temp);
  518.       } else {
  519. DBUGOpenFile ("-",0);
  520.       }
  521.       break;
  522.     case 'p':
  523.       if (*scan++ == ',') {
  524. stack -> processes = ListParse (scan);
  525.       }
  526.       break;
  527.     case 'P':
  528.       stack -> flags |= PROCESS_ON;
  529.       break;
  530.     case 'r':
  531.       stack->sub_level= state->level;
  532.       break;
  533.     case 't':
  534.       stack -> flags |= TRACE_ON;
  535.       if (*scan++ == ',') {
  536. temp = ListParse (scan);
  537. stack -> maxdepth = atoi (temp -> str);
  538. FreeList (temp);
  539.       }
  540.       break;
  541.     case 'S':
  542.       stack -> flags |= SANITY_CHECK_ON;
  543.       break;
  544.     }
  545.   }
  546.   free (new_str);
  547. }
  548. /*
  549.  *  FUNCTION
  550.  *
  551.  * _db_pop_    pop the debug stack
  552.  *
  553.  *  DESCRIPTION
  554.  *
  555.  * Pops the debug stack, returning the debug state to its
  556.  * condition prior to the most recent _db_push_ invocation.
  557.  * Note that the pop will fail if it would remove the last
  558.  * valid state from the stack.  This prevents user errors
  559.  * in the push/pop sequence from screwing up the debugger.
  560.  * Maybe there should be some kind of warning printed if the
  561.  * user tries to pop too many states.
  562.  *
  563.  */
  564. void _db_pop_ ()
  565. {
  566.   reg1 struct state *discard;
  567.   discard = stack;
  568.   if (discard != NULL && discard -> next_state != NULL) {
  569.     stack = discard -> next_state;
  570.     _db_fp_ = stack -> out_file;
  571.     _db_pfp_ = stack -> prof_file;
  572.     if (discard -> keywords != NULL) {
  573.       FreeList (discard -> keywords);
  574.     }
  575.     if (discard -> functions != NULL) {
  576.       FreeList (discard -> functions);
  577.     }
  578.     if (discard -> processes != NULL) {
  579.       FreeList (discard -> processes);
  580.     }
  581.     if (discard -> p_functions != NULL) {
  582.       FreeList (discard -> p_functions);
  583.     }
  584.     CloseFile (discard -> out_file);
  585.     if (discard -> prof_file)
  586.       CloseFile (discard -> prof_file);
  587.     free ((char *) discard);
  588.     if (!(stack->flags & DEBUG_ON))
  589.       _db_on_=0;
  590.   }
  591.   else
  592.   {
  593.     _db_on_=0;
  594.   }
  595. }
  596. /*
  597.  *  FUNCTION
  598.  *
  599.  * _db_enter_    process entry point to user function
  600.  *
  601.  *  SYNOPSIS
  602.  *
  603.  * VOID _db_enter_ (_func_, _file_, _line_,
  604.  *  _sfunc_, _sfile_, _slevel_, _sframep_)
  605.  * char *_func_; points to current function name
  606.  * char *_file_; points to current file name
  607.  * int _line_; called from source line number
  608.  * char **_sfunc_; save previous _func_
  609.  * char **_sfile_; save previous _file_
  610.  * int *_slevel_; save previous nesting level
  611.  * char ***_sframep_; save previous frame pointer
  612.  *
  613.  *  DESCRIPTION
  614.  *
  615.  * Called at the beginning of each user function to tell
  616.  * the debugger that a new function has been entered.
  617.  * Note that the pointers to the previous user function
  618.  * name and previous user file name are stored on the
  619.  * caller's stack (this is why the ENTER macro must be
  620.  * the first "executable" code in a function, since it
  621.  * allocates these storage locations).  The previous nesting
  622.  * level is also stored on the callers stack for internal
  623.  * self consistency checks.
  624.  *
  625.  * Also prints a trace line if tracing is enabled and
  626.  * increments the current function nesting depth.
  627.  *
  628.  * Note that this mechanism allows the debugger to know
  629.  * what the current user function is at all times, without
  630.  * maintaining an internal stack for the function names.
  631.  *
  632.  */
  633. void _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_,
  634.  _sframep_)
  635. const char *_func_;
  636. const char *_file_;
  637. uint _line_;
  638. const char **_sfunc_;
  639. const char **_sfile_;
  640. uint *_slevel_;
  641. char ***_sframep_ __attribute__((unused));
  642. {
  643.   reg1 CODE_STATE *state;
  644.   if (!_no_db_)
  645.   {
  646.     int save_errno=errno;
  647.     if (!init_done)
  648.       _db_push_ (_DBUG_START_CONDITION_);
  649.     state=code_state();
  650.     *_sfunc_ = state->func;
  651.     *_sfile_ = state->file;
  652.     state->func =(char*)  _func_;
  653.     state->file = (char*) _file_; /* BaseName takes time !! */
  654.     *_slevel_ =  ++state->level;
  655. #ifndef THREAD
  656.     *_sframep_ = state->framep;
  657.     state->framep = (char **) _sframep_;
  658.     if (DoProfile ())
  659.     {
  660.       long stackused;
  661.       if (*state->framep == NULL) {
  662. stackused = 0;
  663.       } else {
  664. stackused = ((long)(*state->framep)) - ((long)(state->framep));
  665. stackused = stackused > 0 ? stackused : -stackused;
  666.       }
  667.       (void) fprintf (_db_pfp_, PROF_EFMT , Clock (), state->func);
  668. #ifdef AUTOS_REVERSE
  669.       (void) fprintf (_db_pfp_, PROF_SFMT, state->framep, stackused, *_sfunc_);
  670. #else
  671.       (void) fprintf (_db_pfp_, PROF_SFMT, (ulong) state->framep, stackused,
  672.       state->func);
  673. #endif
  674.       (void) fflush (_db_pfp_);
  675.     }
  676. #endif
  677.     if (DoTrace (state))
  678.     {
  679.       if (!state->locked)
  680. pthread_mutex_lock(&THR_LOCK_dbug);
  681.       DoPrefix (_line_);
  682.       Indent (state -> level);
  683.       (void) fprintf (_db_fp_, ">%sn", state->func);
  684.       dbug_flush (state); /* This does a unlock */
  685.     }
  686. #ifdef SAFEMALLOC
  687.     if (stack -> flags & SANITY_CHECK_ON)
  688.       if (_sanity(_file_,_line_)) /* Check of safemalloc */
  689. stack -> flags &= ~SANITY_CHECK_ON;
  690. #endif
  691.     errno=save_errno;
  692.   }
  693. }
  694. /*
  695.  *  FUNCTION
  696.  *
  697.  * _db_return_    process exit from user function
  698.  *
  699.  *  SYNOPSIS
  700.  *
  701.  * VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
  702.  * int _line_; current source line number
  703.  * char **_sfunc_; where previous _func_ is to be retrieved
  704.  * char **_sfile_; where previous _file_ is to be retrieved
  705.  * int *_slevel_; where previous level was stashed
  706.  *
  707.  *  DESCRIPTION
  708.  *
  709.  * Called just before user function executes an explicit or implicit
  710.  * return.  Prints a trace line if trace is enabled, decrements
  711.  * the current nesting level, and restores the current function and
  712.  * file names from the defunct function's stack.
  713.  *
  714.  */
  715. void _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
  716. uint _line_;
  717. const char **_sfunc_;
  718. const char **_sfile_;
  719. uint *_slevel_;
  720. {
  721.   CODE_STATE *state;
  722.   if (!_no_db_)
  723.   {
  724.     int save_errno=errno;
  725.     if (!init_done)
  726.       _db_push_ ("");
  727.     if (!(state=code_state()))
  728.       return; /* Only happens at end of program */
  729.     if (stack->flags & (TRACE_ON | DEBUG_ON | PROFILE_ON))
  730.     {
  731.       if (!state->locked)
  732. pthread_mutex_lock(&THR_LOCK_dbug);
  733.       if (state->level != (int) *_slevel_)
  734. (void) fprintf (_db_fp_, ERR_MISSING_RETURN, _db_process_,
  735. state->func);
  736.       else
  737.       {
  738. #ifdef SAFEMALLOC
  739. if (stack -> flags & SANITY_CHECK_ON)
  740.   if (_sanity(*_sfile_,_line_))
  741.     stack->flags &= ~SANITY_CHECK_ON;
  742. #endif
  743. #ifndef THREAD
  744. if (DoProfile ())
  745.   (void) fprintf (_db_pfp_, PROF_XFMT, Clock(), state->func);
  746. #endif
  747. if (DoTrace (state))
  748. {
  749.   DoPrefix (_line_);
  750.   Indent (state->level);
  751.   (void) fprintf (_db_fp_, "<%sn", state->func);
  752. }
  753.       }
  754.       dbug_flush(state);
  755.     }
  756.     state->level = *_slevel_-1;
  757.     state->func = *_sfunc_;
  758.     state->file = *_sfile_;
  759. #ifndef THREAD
  760.     if (state->framep != NULL)
  761.       state->framep = (char **) *state->framep;
  762. #endif
  763.     errno=save_errno;
  764.   }
  765. }
  766. /*
  767.  *  FUNCTION
  768.  *
  769.  * _db_pargs_    log arguments for subsequent use by _db_doprnt_()
  770.  *
  771.  *  SYNOPSIS
  772.  *
  773.  * VOID _db_pargs_ (_line_, keyword)
  774.  * int _line_;
  775.  * char *keyword;
  776.  *
  777.  *  DESCRIPTION
  778.  *
  779.  * The new universal printing macro DBUG_PRINT, which replaces
  780.  * all forms of the DBUG_N macros, needs two calls to runtime
  781.  * support routines.  The first, this function, remembers arguments
  782.  * that are used by the subsequent call to _db_doprnt_().
  783.  *
  784.  */
  785. void _db_pargs_ (_line_, keyword)
  786. uint _line_;
  787. const char *keyword;
  788. {
  789.   CODE_STATE *state=code_state();
  790.   state->u_line = _line_;
  791.   state->u_keyword = (char*) keyword;
  792. }
  793. /*
  794.  *  FUNCTION
  795.  *
  796.  * _db_doprnt_    handle print of debug lines
  797.  *
  798.  *  SYNOPSIS
  799.  *
  800.  * VOID _db_doprnt_ (format, va_alist)
  801.  * char *format;
  802.  * va_dcl;
  803.  *
  804.  *  DESCRIPTION
  805.  *
  806.  * When invoked via one of the DBUG macros, tests the current keyword
  807.  * set by calling _db_pargs_() to see if that macro has been selected
  808.  * for processing via the debugger control string, and if so, handles
  809.  * printing of the arguments via the format string.  The line number
  810.  * of the DBUG macro in the source is found in u_line.
  811.  *
  812.  * Note that the format string SHOULD NOT include a terminating
  813.  * newline, this is supplied automatically.
  814.  *
  815.  */
  816. #include <stdarg.h>
  817. void _db_doprnt_ (const char *format,...)
  818. {
  819.   va_list args;
  820.   CODE_STATE *state;
  821.   state=code_state();
  822.   va_start(args,format);
  823.   if (_db_keyword_ (state->u_keyword)) {
  824.     int save_errno=errno;
  825.     if (!state->locked)
  826.       pthread_mutex_lock(&THR_LOCK_dbug);
  827.     DoPrefix (state->u_line);
  828.     if (TRACING) {
  829.       Indent (state->level + 1);
  830.     } else {
  831.       (void) fprintf (_db_fp_, "%s: ", state->func);
  832.     }
  833.     (void) fprintf (_db_fp_, "%s: ", state->u_keyword);
  834.     (void) vfprintf (_db_fp_, format, args);
  835.     va_end(args);
  836.     (void) fputc('n',_db_fp_);
  837.     dbug_flush(state);
  838.     errno=save_errno;
  839.   }
  840.   va_end(args);
  841. }
  842. /*
  843.  *  FUNCTION
  844.  *
  845.  *       _db_dump_    dump a string until '' is found
  846.  *
  847.  *  SYNOPSIS
  848.  *
  849.  *       void _db_dump_ (_line_,keyword,memory,length)
  850.  *       int _line_; current source line number
  851.  *       char *keyword;
  852.  *       char *memory; Memory to print
  853.  *       int length; Bytes to print
  854.  *
  855.  *  DESCRIPTION
  856.  *  Dump N characters in a binary array.
  857.  *  Is used to examine corrputed memory or arrays.
  858.  */
  859. void _db_dump_(_line_,keyword,memory,length)
  860. uint _line_,length;
  861. const char *keyword;
  862. const char *memory;
  863. {
  864.   int pos;
  865.   char dbuff[90];
  866.   CODE_STATE *state;
  867.   state=code_state();
  868.   if (_db_keyword_ ((char*) keyword))
  869.   {
  870.     if (!state->locked)
  871.       pthread_mutex_lock(&THR_LOCK_dbug);
  872.     DoPrefix (_line_);
  873.     if (TRACING)
  874.     {
  875.       Indent (state->level + 1);
  876.       pos= min(max(state->level-stack->sub_level,0)*INDENT,80);
  877.     }
  878.     else
  879.     {
  880.       fprintf(_db_fp_, "%s: ", state->func);
  881.     }
  882.     sprintf(dbuff,"%s: Memory: %lx  Bytes: (%d)n",
  883.     keyword,(ulong) memory, length);
  884.     (void) fputs(dbuff,_db_fp_);
  885.     pos=0;
  886.     while (length-- > 0)
  887.     {
  888.       uint tmp= *((unsigned char*) memory++);
  889.       if ((pos+=3) >= 80)
  890.       {
  891. fputc('n',_db_fp_);
  892. pos=3;
  893.       }
  894.       fputc(_dig_vec[((tmp >> 4) & 15)], _db_fp_);
  895.       fputc(_dig_vec[tmp & 15], _db_fp_);
  896.       fputc(' ',_db_fp_);
  897.     }
  898.     (void) fputc('n',_db_fp_);
  899.     dbug_flush(state);
  900.   }
  901. }
  902. /*
  903.  *  FUNCTION
  904.  *
  905.  * ListParse    parse list of modifiers in debug control string
  906.  *
  907.  *  SYNOPSIS
  908.  *
  909.  * static struct link *ListParse (ctlp)
  910.  * char *ctlp;
  911.  *
  912.  *  DESCRIPTION
  913.  *
  914.  * Given pointer to a comma separated list of strings in "cltp",
  915.  * parses the list, building a list and returning a pointer to it.
  916.  * The original comma separated list is destroyed in the process of
  917.  * building the linked list, thus it had better be a duplicate
  918.  * if it is important.
  919.  *
  920.  * Note that since each link is added at the head of the list,
  921.  * the final list will be in "reverse order", which is not
  922.  * significant for our usage here.
  923.  *
  924.  */
  925. static struct link *ListParse (ctlp)
  926. char *ctlp;
  927. {
  928.   REGISTER char *start;
  929.   REGISTER struct link *new;
  930.   REGISTER struct link *head;
  931.   head = NULL;
  932.   while (*ctlp != EOS) {
  933.     start = ctlp;
  934.     while (*ctlp != EOS && *ctlp != ',') {
  935.       ctlp++;
  936.     }
  937.     if (*ctlp == ',') {
  938.       *ctlp++ = EOS;
  939.     }
  940.     new = (struct link *) DbugMalloc (sizeof (struct link));
  941.     new -> str = StrDup (start);
  942.     new -> next_link = head;
  943.     head = new;
  944.   }
  945.   return (head);
  946. }
  947. /*
  948.  *  FUNCTION
  949.  *
  950.  * InList   test a given string for member of a given list
  951.  *
  952.  *  SYNOPSIS
  953.  *
  954.  * static BOOLEAN InList (linkp, cp)
  955.  * struct link *linkp;
  956.  * char *cp;
  957.  *
  958.  *  DESCRIPTION
  959.  *
  960.  * Tests the string pointed to by "cp" to determine if it is in
  961.  * the list pointed to by "linkp".  Linkp points to the first
  962.  * link in the list.  If linkp is NULL then the string is treated
  963.  * as if it is in the list (I.E all strings are in the null list).
  964.  * This may seem rather strange at first but leads to the desired
  965.  * operation if no list is given. The net effect is that all
  966.  * strings will be accepted when there is no list, and when there
  967.  * is a list, only those strings in the list will be accepted.
  968.  *
  969.  */
  970. static BOOLEAN InList (linkp, cp)
  971. struct link *linkp;
  972. const char *cp;
  973. {
  974.   REGISTER struct link *scan;
  975.   REGISTER BOOLEAN result;
  976.   if (linkp == NULL) {
  977.     result = TRUE;
  978.   } else {
  979.     result = FALSE;
  980.     for (scan = linkp; scan != NULL; scan = scan -> next_link) {
  981.       if (STREQ (scan -> str, cp)) {
  982. result = TRUE;
  983. break;
  984.       }
  985.     }
  986.   }
  987.   return (result);
  988. }
  989. /*
  990.  *  FUNCTION
  991.  *
  992.  * PushState    push current state onto stack and set up new one
  993.  *
  994.  *  SYNOPSIS
  995.  *
  996.  * static VOID PushState ()
  997.  *
  998.  *  DESCRIPTION
  999.  *
  1000.  * Pushes the current state on the state stack, and initializes
  1001.  * a new state.  The only parameter inherited from the previous
  1002.  * state is the function nesting level.  This action can be
  1003.  * inhibited if desired, via the "r" flag.
  1004.  *
  1005.  * The state stack is a linked list of states, with the new
  1006.  * state added at the head.  This allows the stack to grow
  1007.  * to the limits of memory if necessary.
  1008.  *
  1009.  */
  1010. static void PushState ()
  1011. {
  1012.   REGISTER struct state *new;
  1013.   if (!init_done)
  1014.   {
  1015.     init_dbug_state();
  1016.     init_done=TRUE;
  1017.   }
  1018.   (void) code_state(); /* Alloc memory */
  1019.   new = (struct state *) DbugMalloc (sizeof (struct state));
  1020.   new -> flags = 0;
  1021.   new -> delay = 0;
  1022.   new -> maxdepth = MAXDEPTH;
  1023.   new -> sub_level=0;
  1024.   new -> out_file = stderr;
  1025.   new -> prof_file = (FILE*) 0;
  1026.   new -> functions = NULL;
  1027.   new -> p_functions = NULL;
  1028.   new -> keywords = NULL;
  1029.   new -> processes = NULL;
  1030.   new -> next_state = stack;
  1031.   stack=new;
  1032. }
  1033. /*
  1034.  *  FUNCTION
  1035.  *
  1036.  * DoTrace    check to see if tracing is current enabled
  1037.  *
  1038.  *  SYNOPSIS
  1039.  *
  1040.  * static BOOLEAN DoTrace (stack)
  1041.  *
  1042.  *  DESCRIPTION
  1043.  *
  1044.  * Checks to see if tracing is enabled based on whether the
  1045.  * user has specified tracing, the maximum trace depth has
  1046.  * not yet been reached, the current function is selected,
  1047.  * and the current process is selected.  Returns TRUE if
  1048.  * tracing is enabled, FALSE otherwise.
  1049.  *
  1050.  */
  1051. static BOOLEAN DoTrace (CODE_STATE *state)
  1052. {
  1053.   reg2 BOOLEAN trace=FALSE;
  1054.   if (TRACING &&
  1055.       state->level <= stack -> maxdepth &&
  1056.       InList (stack -> functions, state->func) &&
  1057.       InList (stack -> processes, _db_process_))
  1058.       trace = TRUE;
  1059.   return (trace);
  1060. }
  1061. /*
  1062.  *  FUNCTION
  1063.  *
  1064.  * DoProfile    check to see if profiling is current enabled
  1065.  *
  1066.  *  SYNOPSIS
  1067.  *
  1068.  * static BOOLEAN DoProfile ()
  1069.  *
  1070.  *  DESCRIPTION
  1071.  *
  1072.  * Checks to see if profiling is enabled based on whether the
  1073.  * user has specified profiling, the maximum trace depth has
  1074.  * not yet been reached, the current function is selected,
  1075.  * and the current process is selected.  Returns TRUE if
  1076.  * profiling is enabled, FALSE otherwise.
  1077.  *
  1078.  */
  1079. #ifndef THREAD
  1080. static BOOLEAN DoProfile ()
  1081. {
  1082.   REGISTER BOOLEAN profile;
  1083.   CODE_STATE *state;
  1084.   state=code_state();
  1085.   profile = FALSE;
  1086.   if (PROFILING &&
  1087.       state->level <= stack -> maxdepth &&
  1088.       InList (stack -> p_functions, state->func) &&
  1089.       InList (stack -> processes, _db_process_))
  1090.     profile = TRUE;
  1091.   return (profile);
  1092. }
  1093. #endif
  1094. /*
  1095.  *  FUNCTION
  1096.  *
  1097.  * _db_keyword_ test keyword for member of keyword list
  1098.  *
  1099.  *  SYNOPSIS
  1100.  *
  1101.  * BOOLEAN _db_keyword_ (keyword)
  1102.  * char *keyword;
  1103.  *
  1104.  *  DESCRIPTION
  1105.  *
  1106.  * Test a keyword to determine if it is in the currently active
  1107.  * keyword list.  As with the function list, a keyword is accepted
  1108.  * if the list is null, otherwise it must match one of the list
  1109.  * members.  When debugging is not on, no keywords are accepted.
  1110.  * After the maximum trace level is exceeded, no keywords are
  1111.  * accepted (this behavior subject to change).  Additionally,
  1112.  * the current function and process must be accepted based on
  1113.  * their respective lists.
  1114.  *
  1115.  * Returns TRUE if keyword accepted, FALSE otherwise.
  1116.  *
  1117.  */
  1118. BOOLEAN _db_keyword_ (keyword)
  1119. const char *keyword;
  1120. {
  1121.   REGISTER BOOLEAN result;
  1122.   CODE_STATE *state;
  1123.   if (!init_done)
  1124.     _db_push_ ("");
  1125.   state=code_state();
  1126.   result = FALSE;
  1127.   if (DEBUGGING &&
  1128.       state->level <= stack -> maxdepth &&
  1129.       InList (stack -> functions, state->func) &&
  1130.       InList (stack -> keywords, keyword) &&
  1131.       InList (stack -> processes, _db_process_))
  1132.     result = TRUE;
  1133.   return (result);
  1134. }
  1135. /*
  1136.  *  FUNCTION
  1137.  *
  1138.  * Indent   indent a line to the given indentation level
  1139.  *
  1140.  *  SYNOPSIS
  1141.  *
  1142.  * static VOID Indent (indent)
  1143.  * int indent;
  1144.  *
  1145.  *  DESCRIPTION
  1146.  *
  1147.  * Indent a line to the given level.  Note that this is
  1148.  * a simple minded but portable implementation.
  1149.  * There are better ways.
  1150.  *
  1151.  * Also, the indent must be scaled by the compile time option
  1152.  * of character positions per nesting level.
  1153.  *
  1154.  */
  1155. static void Indent (indent)
  1156. int indent;
  1157. {
  1158.   REGISTER int count;
  1159.   indent= max(indent-1-stack->sub_level,0)*INDENT;
  1160.   for (count = 0; count < indent ; count++)
  1161.   {
  1162.     if ((count % INDENT) == 0)
  1163.       fputc('|',_db_fp_);
  1164.     else
  1165.       fputc(' ',_db_fp_);
  1166.   }
  1167. }
  1168. /*
  1169.  *  FUNCTION
  1170.  *
  1171.  * FreeList    free all memory associated with a linked list
  1172.  *
  1173.  *  SYNOPSIS
  1174.  *
  1175.  * static VOID FreeList (linkp)
  1176.  * struct link *linkp;
  1177.  *
  1178.  *  DESCRIPTION
  1179.  *
  1180.  * Given pointer to the head of a linked list, frees all
  1181.  * memory held by the list and the members of the list.
  1182.  *
  1183.  */
  1184. static void FreeList (linkp)
  1185. struct link *linkp;
  1186. {
  1187.   REGISTER struct link *old;
  1188.   while (linkp != NULL) {
  1189.     old = linkp;
  1190.     linkp = linkp -> next_link;
  1191.     if (old -> str != NULL) {
  1192.       free (old -> str);
  1193.     }
  1194.     free ((char *) old);
  1195.   }
  1196. }
  1197. /*
  1198.  *  FUNCTION
  1199.  *
  1200.  * StrDup  make a duplicate of a string in new memory
  1201.  *
  1202.  *  SYNOPSIS
  1203.  *
  1204.  * static char *StrDup (my_string)
  1205.  * char *string;
  1206.  *
  1207.  *  DESCRIPTION
  1208.  *
  1209.  * Given pointer to a string, allocates sufficient memory to make
  1210.  * a duplicate copy, and copies the string to the newly allocated
  1211.  * memory.  Failure to allocated sufficient memory is immediately
  1212.  * fatal.
  1213.  *
  1214.  */
  1215. static char *StrDup (str)
  1216. const char *str;
  1217. {
  1218.     reg1 char *new;
  1219.     new = DbugMalloc ((int) strlen (str) + 1);
  1220.     (void) strcpy (new, str);
  1221.     return (new);
  1222. }
  1223. /*
  1224.  *  FUNCTION
  1225.  *
  1226.  * DoPrefix    print debugger line prefix prior to indentation
  1227.  *
  1228.  *  SYNOPSIS
  1229.  *
  1230.  * static VOID DoPrefix (_line_)
  1231.  * int _line_;
  1232.  *
  1233.  *  DESCRIPTION
  1234.  *
  1235.  * Print prefix common to all debugger output lines, prior to
  1236.  * doing indentation if necessary.  Print such information as
  1237.  * current process name, current source file name and line number,
  1238.  * and current function nesting depth.
  1239.  *
  1240.  */
  1241. static void DoPrefix (_line_)
  1242. uint _line_;
  1243. {
  1244.   CODE_STATE *state;
  1245.   state=code_state();
  1246.   state->lineno++;
  1247.   if (stack -> flags & PID_ON) {
  1248. #ifdef THREAD
  1249.     (void) fprintf (_db_fp_, "%-7s: ", my_thread_name());
  1250. #else
  1251.     (void) fprintf (_db_fp_, "%5d: ", getpid ());
  1252. #endif
  1253.   }
  1254.   if (stack -> flags & NUMBER_ON) {
  1255.     (void) fprintf (_db_fp_, "%5d: ", state->lineno);
  1256.   }
  1257.   if (stack -> flags & PROCESS_ON) {
  1258.     (void) fprintf (_db_fp_, "%s: ", _db_process_);
  1259.   }
  1260.   if (stack -> flags & FILE_ON) {
  1261.     (void) fprintf (_db_fp_, "%14s: ", BaseName(state->file));
  1262.   }
  1263.   if (stack -> flags & LINE_ON) {
  1264.     (void) fprintf (_db_fp_, "%5d: ", _line_);
  1265.   }
  1266.   if (stack -> flags & DEPTH_ON) {
  1267.     (void) fprintf (_db_fp_, "%4d: ", state->level);
  1268.   }
  1269. }
  1270. /*
  1271.  *  FUNCTION
  1272.  *
  1273.  * DBUGOpenFile open new output stream for debugger output
  1274.  *
  1275.  *  SYNOPSIS
  1276.  *
  1277.  * static VOID DBUGOpenFile (name)
  1278.  * char *name;
  1279.  *
  1280.  *  DESCRIPTION
  1281.  *
  1282.  * Given name of a new file (or "-" for stdout) opens the file
  1283.  * and sets the output stream to the new file.
  1284.  *
  1285.  */
  1286. static void DBUGOpenFile (const char *name,int append)
  1287. {
  1288.   REGISTER FILE *fp;
  1289.   REGISTER BOOLEAN newfile;
  1290.   if (name != NULL)
  1291.   {
  1292.     strmov(stack->name,name);
  1293.     if (strcmp (name, "-") == 0)
  1294.     {
  1295.       _db_fp_ = stdout;
  1296.       stack -> out_file = _db_fp_;
  1297.       stack -> flags |= FLUSH_ON_WRITE;
  1298.     }
  1299.     else
  1300.     {
  1301.       if (!Writable(name))
  1302.       {
  1303. (void) fprintf (stderr, ERR_OPEN, _db_process_, name);
  1304. perror ("");
  1305. fflush(stderr);
  1306.       }
  1307.       else
  1308.       {
  1309. newfile= !EXISTS (name);
  1310. if (!(fp = fopen(name, append ? "a+" : "w")))
  1311. {
  1312.   (void) fprintf (stderr, ERR_OPEN, _db_process_, name);
  1313.   perror ("");
  1314.   fflush(stderr);
  1315. }
  1316. else
  1317. {
  1318.   _db_fp_ = fp;
  1319.   stack -> out_file = fp;
  1320.   if (newfile) {
  1321.     ChangeOwner (name);
  1322.   }
  1323. }
  1324.       }
  1325.     }
  1326.   }
  1327. }
  1328. /*
  1329.  *  FUNCTION
  1330.  *
  1331.  * OpenProfile    open new output stream for profiler output
  1332.  *
  1333.  *  SYNOPSIS
  1334.  *
  1335.  * static FILE *OpenProfile (name)
  1336.  * char *name;
  1337.  *
  1338.  *  DESCRIPTION
  1339.  *
  1340.  * Given name of a new file, opens the file
  1341.  * and sets the profiler output stream to the new file.
  1342.  *
  1343.  * It is currently unclear whether the prefered behavior is
  1344.  * to truncate any existing file, or simply append to it.
  1345.  * The latter behavior would be desirable for collecting
  1346.  * accumulated runtime history over a number of separate
  1347.  * runs.  It might take some changes to the analyzer program
  1348.  * though, and the notes that Binayak sent with the profiling
  1349.  * diffs indicated that append was the normal mode, but this
  1350.  * does not appear to agree with the actual code. I haven't
  1351.  * investigated at this time [fnf; 24-Jul-87].
  1352.  */
  1353. #ifndef THREAD
  1354. static FILE *OpenProfile (const char *name)
  1355. {
  1356.   REGISTER FILE *fp;
  1357.   REGISTER BOOLEAN newfile;
  1358.   fp=0;
  1359.   if (!Writable (name))
  1360.   {
  1361.     (void) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1362.     perror ("");
  1363.     dbug_flush(0);
  1364.     (void) Delay (stack -> delay);
  1365.   }
  1366.   else
  1367.   {
  1368.     newfile= !EXISTS (name);
  1369.     if (!(fp = fopen (name, "w")))
  1370.     {
  1371.       (void) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1372.       perror ("");
  1373.       dbug_flush(0);
  1374.     }
  1375.     else
  1376.     {
  1377.       _db_pfp_ = fp;
  1378.       stack -> prof_file = fp;
  1379.       if (newfile)
  1380.       {
  1381. ChangeOwner (name);
  1382.       }
  1383.     }
  1384.   }
  1385.   return fp;
  1386. }
  1387. #endif
  1388. /*
  1389.  *  FUNCTION
  1390.  *
  1391.  * CloseFile    close the debug output stream
  1392.  *
  1393.  *  SYNOPSIS
  1394.  *
  1395.  * static VOID CloseFile (fp)
  1396.  * FILE *fp;
  1397.  *
  1398.  *  DESCRIPTION
  1399.  *
  1400.  * Closes the debug output stream unless it is standard output
  1401.  * or standard error.
  1402.  *
  1403.  */
  1404. static void CloseFile (fp)
  1405. FILE *fp;
  1406. {
  1407.   if (fp != stderr && fp != stdout) {
  1408.     if (fclose (fp) == EOF) {
  1409.       pthread_mutex_lock(&THR_LOCK_dbug);
  1410.       (void) fprintf (_db_fp_, ERR_CLOSE, _db_process_);
  1411.       perror ("");
  1412.       dbug_flush(0);
  1413.     }
  1414.   }
  1415. }
  1416. /*
  1417.  *  FUNCTION
  1418.  *
  1419.  * DbugExit    print error message and exit
  1420.  *
  1421.  *  SYNOPSIS
  1422.  *
  1423.  * static VOID DbugExit (why)
  1424.  * char *why;
  1425.  *
  1426.  *  DESCRIPTION
  1427.  *
  1428.  * Prints error message using current process name, the reason for
  1429.  * aborting (typically out of memory), and exits with status 1.
  1430.  * This should probably be changed to use a status code
  1431.  * defined in the user's debugger include file.
  1432.  *
  1433.  */
  1434. static void DbugExit (const char *why)
  1435. {
  1436.   (void) fprintf (stderr, ERR_ABORT, _db_process_, why);
  1437.   (void) fflush (stderr);
  1438.   exit (1);
  1439. }
  1440. /*
  1441.  *  FUNCTION
  1442.  *
  1443.  * DbugMalloc    allocate memory for debugger runtime support
  1444.  *
  1445.  *  SYNOPSIS
  1446.  *
  1447.  * static long *DbugMalloc (size)
  1448.  * int size;
  1449.  *
  1450.  *  DESCRIPTION
  1451.  *
  1452.  * Allocate more memory for debugger runtime support functions.
  1453.  * Failure to to allocate the requested number of bytes is
  1454.  * immediately fatal to the current process.  This may be
  1455.  * rather unfriendly behavior.  It might be better to simply
  1456.  * print a warning message, freeze the current debugger state,
  1457.  * and continue execution.
  1458.  *
  1459.  */
  1460. static char *DbugMalloc (size)
  1461. int size;
  1462. {
  1463.     register char *new;
  1464.     if (!(new = malloc ((unsigned int) size)))
  1465.       DbugExit ("out of memory");
  1466.     return (new);
  1467. }
  1468. /*
  1469.  * As strtok but two separators in a row are changed to one
  1470.  * separator (to allow directory-paths in dos).
  1471.  */
  1472. static char *static_strtok (s1, separator)
  1473. char *s1;
  1474. pchar separator;
  1475. {
  1476.   static char *end = NULL;
  1477.   reg1 char *rtnval,*cpy;
  1478.   rtnval = NULL;
  1479.   if (s1 != NULL)
  1480.     end = s1;
  1481.   if (end != NULL && *end != EOS)
  1482.   {
  1483.     rtnval=cpy=end;
  1484.     do
  1485.     {
  1486.       if ((*cpy++ = *end++) == separator)
  1487.       {
  1488. if (*end != separator)
  1489. {
  1490.   cpy--; /* Point at separator */
  1491.   break;
  1492. }
  1493. end++; /* Two separators in a row, skipp one */
  1494.       }
  1495.     } while (*end != EOS);
  1496.     *cpy=EOS; /* Replace last separator */
  1497.   }
  1498.   return (rtnval);
  1499. }
  1500. /*
  1501.  *  FUNCTION
  1502.  *
  1503.  * BaseName    strip leading pathname components from name
  1504.  *
  1505.  *  SYNOPSIS
  1506.  *
  1507.  * static char *BaseName (pathname)
  1508.  * char *pathname;
  1509.  *
  1510.  *  DESCRIPTION
  1511.  *
  1512.  * Given pointer to a complete pathname, locates the base file
  1513.  * name at the end of the pathname and returns a pointer to
  1514.  * it.
  1515.  *
  1516.  */
  1517. static char *BaseName (const char *pathname)
  1518. {
  1519.   register const char *base;
  1520.   base = strrchr (pathname, FN_LIBCHAR);
  1521.   if (base++ == NullS)
  1522.     base = pathname;
  1523.   return ((char*) base);
  1524. }
  1525. /*
  1526.  *  FUNCTION
  1527.  *
  1528.  * Writable    test to see if a pathname is writable/creatable
  1529.  *
  1530.  *  SYNOPSIS
  1531.  *
  1532.  * static BOOLEAN Writable (pathname)
  1533.  * char *pathname;
  1534.  *
  1535.  *  DESCRIPTION
  1536.  *
  1537.  * Because the debugger might be linked in with a program that
  1538.  * runs with the set-uid-bit (suid) set, we have to be careful
  1539.  * about opening a user named file for debug output.  This consists
  1540.  * of checking the file for write access with the real user id,
  1541.  * or checking the directory where the file will be created.
  1542.  *
  1543.  * Returns TRUE if the user would normally be allowed write or
  1544.  * create access to the named file.  Returns FALSE otherwise.
  1545.  *
  1546.  */
  1547. #ifndef Writable
  1548. static BOOLEAN Writable (pathname)
  1549. char *pathname;
  1550. {
  1551.   REGISTER BOOLEAN granted;
  1552.   REGISTER char *lastslash;
  1553.   granted = FALSE;
  1554.   if (EXISTS (pathname)) {
  1555.     if (WRITABLE (pathname)) {
  1556.       granted = TRUE;
  1557.     }
  1558.   } else {
  1559.     lastslash = strrchr (pathname, '/');
  1560.     if (lastslash != NULL) {
  1561.       *lastslash = EOS;
  1562.     } else {
  1563.       pathname = ".";
  1564.     }
  1565.     if (WRITABLE (pathname)) {
  1566.       granted = TRUE;
  1567.     }
  1568.     if (lastslash != NULL) {
  1569.       *lastslash = '/';
  1570.     }
  1571.   }
  1572.   return (granted);
  1573. }
  1574. #endif
  1575. /*
  1576.  *  FUNCTION
  1577.  *
  1578.  * ChangeOwner    change owner to real user for suid programs
  1579.  *
  1580.  *  SYNOPSIS
  1581.  *
  1582.  * static VOID ChangeOwner (pathname)
  1583.  *
  1584.  *  DESCRIPTION
  1585.  *
  1586.  * For unix systems, change the owner of the newly created debug
  1587.  * file to the real owner.  This is strictly for the benefit of
  1588.  * programs that are running with the set-user-id bit set.
  1589.  *
  1590.  * Note that at this point, the fact that pathname represents
  1591.  * a newly created file has already been established.  If the
  1592.  * program that the debugger is linked to is not running with
  1593.  * the suid bit set, then this operation is redundant (but
  1594.  * harmless).
  1595.  *
  1596.  */
  1597. #ifndef ChangeOwner
  1598. static void ChangeOwner (pathname)
  1599. char *pathname;
  1600. {
  1601.   if (chown (pathname, getuid (), getgid ()) == -1)
  1602.   {
  1603.     (void) fprintf (stderr, ERR_CHOWN, _db_process_, pathname);
  1604.     perror ("");
  1605.     (void) fflush (stderr);
  1606.   }
  1607. }
  1608. #endif
  1609. /*
  1610.  *  FUNCTION
  1611.  *
  1612.  * _db_setjmp_    save debugger environment
  1613.  *
  1614.  *  SYNOPSIS
  1615.  *
  1616.  * VOID _db_setjmp_ ()
  1617.  *
  1618.  *  DESCRIPTION
  1619.  *
  1620.  * Invoked as part of the user's DBUG_SETJMP macro to save
  1621.  * the debugger environment in parallel with saving the user's
  1622.  * environment.
  1623.  *
  1624.  */
  1625. #ifdef HAVE_LONGJMP
  1626. EXPORT void _db_setjmp_ ()
  1627. {
  1628.   CODE_STATE *state;
  1629.   state=code_state();
  1630.   state->jmplevel = state->level;
  1631.   state->jmpfunc = state->func;
  1632.   state->jmpfile = state->file;
  1633. }
  1634. /*
  1635.  *  FUNCTION
  1636.  *
  1637.  * _db_longjmp_ restore previously saved debugger environment
  1638.  *
  1639.  *  SYNOPSIS
  1640.  *
  1641.  * VOID _db_longjmp_ ()
  1642.  *
  1643.  *  DESCRIPTION
  1644.  *
  1645.  * Invoked as part of the user's DBUG_LONGJMP macro to restore
  1646.  * the debugger environment in parallel with restoring the user's
  1647.  * previously saved environment.
  1648.  *
  1649.  */
  1650. EXPORT void _db_longjmp_ ()
  1651. {
  1652.   CODE_STATE *state;
  1653.   state=code_state();
  1654.   state->level = state->jmplevel;
  1655.   if (state->jmpfunc) {
  1656.     state->func = state->jmpfunc;
  1657.   }
  1658.   if (state->jmpfile) {
  1659.     state->file = state->jmpfile;
  1660.   }
  1661. }
  1662. #endif
  1663. /*
  1664.  *  FUNCTION
  1665.  *
  1666.  * DelayArg   convert D flag argument to appropriate value
  1667.  *
  1668.  *  SYNOPSIS
  1669.  *
  1670.  * static int DelayArg (value)
  1671.  * int value;
  1672.  *
  1673.  *  DESCRIPTION
  1674.  *
  1675.  * Converts delay argument, given in tenths of a second, to the
  1676.  * appropriate numerical argument used by the system to delay
  1677.  * that that many tenths of a second.  For example, on the
  1678.  * amiga, there is a system call "Delay()" which takes an
  1679.  * argument in ticks (50 per second).  On unix, the sleep
  1680.  * command takes seconds. Thus a value of "10", for one
  1681.  * second of delay, gets converted to 50 on the amiga, and 1
  1682.  * on unix.  Other systems will need to use a timing loop.
  1683.  *
  1684.  */
  1685. #ifdef AMIGA
  1686. #define HZ (50)       /* Probably in some header somewhere */
  1687. #endif
  1688. static int DelayArg (value)
  1689. int value;
  1690. {
  1691.   uint delayarg = 0;
  1692. #if (unix || xenix)
  1693.   delayarg = value / 10; /* Delay is in seconds for sleep () */
  1694. #endif
  1695. #ifdef AMIGA
  1696.   delayarg = (HZ * value) / 10; /* Delay in ticks for Delay () */
  1697. #endif
  1698.   return (delayarg);
  1699. }
  1700. /*
  1701.  * A dummy delay stub for systems that do not support delays.
  1702.  * With a little work, this can be turned into a timing loop.
  1703.  */
  1704. #if ! defined(Delay) && ! defined(AMIGA)
  1705. static int Delay (ticks)
  1706. int ticks;
  1707. {
  1708.   return ticks;
  1709. }
  1710. #endif
  1711. /*
  1712.  *  FUNCTION
  1713.  *
  1714.  * perror   perror simulation for systems that don't have it
  1715.  *
  1716.  *  SYNOPSIS
  1717.  *
  1718.  * static VOID perror (s)
  1719.  * char *s;
  1720.  *
  1721.  *  DESCRIPTION
  1722.  *
  1723.  * Perror produces a message on the standard error stream which
  1724.  * provides more information about the library or system error
  1725.  * just encountered.  The argument string s is printed, followed
  1726.  * by a ':', a blank, and then a message and a newline.
  1727.  *
  1728.  * An undocumented feature of the unix perror is that if the string
  1729.  * 's' is a null string (NOT a NULL pointer!), then the ':' and
  1730.  * blank are not printed.
  1731.  *
  1732.  * This version just complains about an "unknown system error".
  1733.  *
  1734.  */
  1735. #ifndef HAVE_PERROR
  1736. static void perror (s)
  1737. char *s;
  1738. {
  1739.   if (s && *s != EOS) {
  1740.     (void) fprintf (stderr, "%s: ", s);
  1741.   }
  1742.   (void) fprintf (stderr, "<unknown system error>n");
  1743. }
  1744. #endif /* HAVE_PERROR */
  1745. /* flush dbug-stream, free mutex lock & wait delay */
  1746. /* This is because some systems (MSDOS!!) dosn't flush fileheader */
  1747. /* and dbug-file isn't readable after a system crash !! */
  1748. static void dbug_flush(CODE_STATE *state)
  1749. {
  1750. #ifndef THREAD
  1751.   if (stack->flags & FLUSH_ON_WRITE)
  1752. #endif
  1753.   {
  1754. #if defined(MSDOS) || defined(__WIN__)
  1755.     if (_db_fp_ != stdout && _db_fp_ != stderr)
  1756.     {
  1757.       if (!(freopen(stack->name,"a",_db_fp_)))
  1758.       {
  1759. (void) fprintf(stderr, ERR_OPEN, _db_process_);
  1760. fflush(stderr);
  1761. _db_fp_ = stdout;
  1762. stack -> out_file = _db_fp_;
  1763. stack -> flags|=FLUSH_ON_WRITE;
  1764.       }
  1765.     }
  1766.     else
  1767. #endif
  1768.     {
  1769.       (void) fflush (_db_fp_);
  1770.       if (stack->delay)
  1771. (void) Delay (stack->delay);
  1772.     }
  1773.   }
  1774.   if (!state || !state->locked)
  1775.     pthread_mutex_unlock(&THR_LOCK_dbug);
  1776. } /* dbug_flush */
  1777. void _db_lock_file()
  1778. {
  1779.   CODE_STATE *state;
  1780.   state=code_state();
  1781.   pthread_mutex_lock(&THR_LOCK_dbug);
  1782.   state->locked=1;
  1783. }
  1784. void _db_unlock_file()
  1785. {
  1786.   CODE_STATE *state;
  1787.   state=code_state();
  1788.   state->locked=0;
  1789.   pthread_mutex_unlock(&THR_LOCK_dbug);
  1790. }
  1791. /*
  1792.  * Here we need the definitions of the clock routine.  Add your
  1793.  * own for whatever system that you have.
  1794.  */
  1795. #ifdef HAVE_GETRUSAGE
  1796. #include <sys/param.h>
  1797. #include <sys/resource.h>
  1798. /* extern int     getrusage(int, struct rusage *); */
  1799. /*
  1800.  * Returns the user time in milliseconds used by this process so
  1801.  * far.
  1802.  */
  1803. static unsigned long Clock ()
  1804. {
  1805.     struct rusage ru;
  1806.     (void) getrusage (RUSAGE_SELF, &ru);
  1807.     return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000));
  1808. }
  1809. #else
  1810. #if defined(MSDOS) || defined(__WIN__)
  1811. static ulong Clock()
  1812. {
  1813.   return clock()*(1000/CLOCKS_PER_SEC);
  1814. }
  1815. #else
  1816. #ifdef amiga
  1817. struct DateStamp { /* Yes, this is a hack, but doing it right */
  1818. long ds_Days; /* is incredibly ugly without splitting this */
  1819. long ds_Minute; /* off into a separate file */
  1820. long ds_Tick;
  1821. };
  1822. static int first_clock = TRUE;
  1823. static struct DateStamp begin;
  1824. static struct DateStamp elapsed;
  1825. static unsigned long Clock ()
  1826. {
  1827.     register struct DateStamp *now;
  1828.     register unsigned long millisec = 0;
  1829.     extern VOID *AllocMem ();
  1830.     now = (struct DateStamp *) AllocMem ((long) sizeof (struct DateStamp), 0L);
  1831.     if (now != NULL) {
  1832. if (first_clock == TRUE) {
  1833.     first_clock = FALSE;
  1834.     (void) DateStamp (now);
  1835.     begin = *now;
  1836. }
  1837. (void) DateStamp (now);
  1838. millisec = 24 * 3600 * (1000 / HZ) * (now -> ds_Days - begin.ds_Days);
  1839. millisec += 60 * (1000 / HZ) * (now -> ds_Minute - begin.ds_Minute);
  1840. millisec += (1000 / HZ) * (now -> ds_Tick - begin.ds_Tick);
  1841. (void) FreeMem (now, (long) sizeof (struct DateStamp));
  1842.     }
  1843.     return (millisec);
  1844. }
  1845. #else
  1846. #ifndef THREAD
  1847. static unsigned long Clock ()
  1848. {
  1849.     return (0);
  1850. }
  1851. #endif
  1852. #endif /* amiga */
  1853. #endif /* MSDOS || __WIN__ */
  1854. #endif /* RUSAGE */
  1855. #ifdef NO_VARARGS
  1856. /*
  1857.  * Fake vfprintf for systems that don't support it.  If this
  1858.  * doesn't work, you are probably SOL...
  1859.  */
  1860. static int vfprintf (stream, format, ap)
  1861. FILE *stream;
  1862. char *format;
  1863. va_list ap;
  1864. {
  1865.     int rtnval;
  1866.     ARGS_DCL;
  1867.     ARG0 =  va_arg (ap, ARGS_TYPE);
  1868.     ARG1 =  va_arg (ap, ARGS_TYPE);
  1869.     ARG2 =  va_arg (ap, ARGS_TYPE);
  1870.     ARG3 =  va_arg (ap, ARGS_TYPE);
  1871.     ARG4 =  va_arg (ap, ARGS_TYPE);
  1872.     ARG5 =  va_arg (ap, ARGS_TYPE);
  1873.     ARG6 =  va_arg (ap, ARGS_TYPE);
  1874.     ARG7 =  va_arg (ap, ARGS_TYPE);
  1875.     ARG8 =  va_arg (ap, ARGS_TYPE);
  1876.     ARG9 =  va_arg (ap, ARGS_TYPE);
  1877.     rtnval = fprintf (stream, format, ARGS_LIST);
  1878.     return (rtnval);
  1879. }
  1880. #endif /* NO_VARARGS */