dbug.c 68.3 KB
Newer Older
unknown's avatar
unknown committed
1
/******************************************************************************
unknown's avatar
unknown committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *                                                                            *
 *                                 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     *
24
 *      product  and  explicitly disclaims any implied warranties of mer-     *
unknown's avatar
unknown committed
25 26
 *      chantability or fitness for any particular purpose.                   *
 *                                                                            *
unknown's avatar
unknown committed
27 28 29 30 31 32
 ******************************************************************************
 */

/*
 *  FILE
 *
unknown's avatar
unknown committed
33
 *      dbug.c   runtime support routines for dbug package
unknown's avatar
unknown committed
34 35 36
 *
 *  SCCS
 *
unknown's avatar
unknown committed
37
 *      @(#)dbug.c      1.25    7/25/89
unknown's avatar
unknown committed
38 39 40
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
41 42 43 44
 *      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.
unknown's avatar
unknown committed
45
 *
unknown's avatar
unknown committed
46 47 48
 *      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.
unknown's avatar
unknown committed
49 50 51
 *
 *  AUTHOR(S)
 *
unknown's avatar
unknown committed
52 53 54
 *      Fred Fish               (base code)
 *      Enhanced Software Technologies, Tempe, AZ
 *      asuvax!mcdphx!estinc!fnf
unknown's avatar
unknown committed
55
 *
unknown's avatar
unknown committed
56 57
 *      Binayak Banerjee        (profiling enhancements)
 *      seismo!bpa!sjuvax!bbanerje
unknown's avatar
unknown committed
58
 *
unknown's avatar
unknown committed
59
 *      Michael Widenius:
60 61 62 63 64 65 66 67 68 69 70 71 72
 *        DBUG_DUMP       - To dump a block of memory.
 *        PUSH_FLAG "O"   - To be used insted of "o" if we
 *                          want flushing after each write
 *        PUSH_FLAG "A"   - as 'O', but we will append to the out file instead
 *                          of creating a new one.
 *        Check of malloc on entry/exit (option "S")
 *
 *      Sergei Golubchik:
 *        DBUG_EXECUTE_IF
 *        incremental mode (-#+t:-d,info ...)
 *        DBUG_SET, _db_explain_
 *        thread-local settings
 *        negative lists (-#-d,info => everything but "info")
unknown's avatar
unknown committed
73
 *
74 75 76 77
 *        function/ syntax
 *        (the logic is - think of a call stack as of a path.
 *        "function" means only this function, "function/" means the hierarchy.
 *        in the future, filters like function1/function2 could be supported.
78
 *        following this logic glob(7) wildcards are supported.)
79
 *
unknown's avatar
unknown committed
80 81
 */

82 83 84 85
/*
  We can't have SAFE_MUTEX defined here as this will cause recursion
  in pthread_mutex_lock
*/
86

87
#include <my_global.h>
88
#undef SAFE_MUTEX
unknown's avatar
unknown committed
89 90
#include <m_string.h>
#include <errno.h>
91 92 93 94 95 96 97

#ifdef HAVE_FNMATCH_H
#include <fnmatch.h>
#else
#define fnmatch(A,B,C) strcmp(A,B)
#endif

unknown's avatar
unknown committed
98 99 100 101
#if defined(MSDOS) || defined(__WIN__)
#include <process.h>
#endif

102 103 104
#ifndef DBUG_OFF


unknown's avatar
unknown committed
105
/*
unknown's avatar
unknown committed
106
 *            Manifest constants which may be "tuned" if desired.
unknown's avatar
unknown committed
107 108
 */

unknown's avatar
unknown committed
109 110 111
#define PRINTBUF              1024    /* Print buffer size */
#define INDENT                2       /* Indentation per trace level */
#define MAXDEPTH              200     /* Maximum trace depth default */
unknown's avatar
unknown committed
112 113

/*
unknown's avatar
unknown committed
114 115 116
 *      The following flags are used to determine which
 *      capabilities the user has enabled with the settings
 *      push macro.
117 118 119
 *
 *      TRACE_ON is also used in _db_stack_frame_->level
 *      (until we add flags to _db_stack_frame_, increasing it by 4 bytes)
unknown's avatar
unknown committed
120 121
 */

122 123 124 125 126 127 128 129 130 131 132 133
#define DEBUG_ON        (1 <<  1)  /* Debug enabled */
#define FILE_ON         (1 <<  2)  /* File name print enabled */
#define LINE_ON         (1 <<  3)  /* Line number print enabled */
#define DEPTH_ON        (1 <<  4)  /* Function nest level print enabled */
#define PROCESS_ON      (1 <<  5)  /* Process name print enabled */
#define NUMBER_ON       (1 <<  6)  /* Number each line of output */
#define PROFILE_ON      (1 <<  7)  /* Print out profiling code */
#define PID_ON          (1 <<  8)  /* Identify each line with process id */
#define TIMESTAMP_ON    (1 <<  9)  /* timestamp every line of output */
#define SANITY_CHECK_ON (1 << 10)  /* Check safemalloc on DBUG_ENTER */
#define FLUSH_ON_WRITE  (1 << 11)  /* Flush on every write */
#define OPEN_APPEND     (1 << 12)  /* Open for append      */
134
#define TRACE_ON        ((uint)1 << 31)  /* Trace enabled. MUST be the highest bit!*/
unknown's avatar
unknown committed
135 136 137 138

#define TRACING (cs->stack->flags & TRACE_ON)
#define DEBUGGING (cs->stack->flags & DEBUG_ON)
#define PROFILING (cs->stack->flags & PROFILE_ON)
unknown's avatar
unknown committed
139 140

/*
unknown's avatar
unknown committed
141
 *      Typedefs to make things more obvious.
unknown's avatar
unknown committed
142 143
 */

144
#define BOOLEAN my_bool
unknown's avatar
unknown committed
145 146

/*
unknown's avatar
unknown committed
147
 *      Make it easy to change storage classes if necessary.
unknown's avatar
unknown committed
148 149
 */

unknown's avatar
unknown committed
150 151 152 153
#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 */
unknown's avatar
unknown committed
154 155 156 157 158 159

/*
 * The default file for profiling.  Could also add another flag
 * (G?) which allowed the user to specify this.
 *
 * If the automatic variables get allocated on the stack in
160
 * reverse order from their declarations, then define AUTOS_REVERSE to 1.
unknown's avatar
unknown committed
161 162 163 164 165 166 167
 * This is used by the code that keeps track of stack usage.  For
 * forward allocation, the difference in the dbug frame pointers
 * represents stack used by the callee function.  For reverse allocation,
 * the difference represents stack used by the caller function.
 *
 */

unknown's avatar
unknown committed
168 169 170 171
#define PROF_FILE       "dbugmon.out"
#define PROF_EFMT       "E\t%ld\t%s\n"
#define PROF_SFMT       "S\t%lx\t%lx\t%s\n"
#define PROF_XFMT       "X\t%ld\t%s\n"
unknown's avatar
unknown committed
172

unknown's avatar
unknown committed
173
#ifdef M_I386           /* predefined by xenix 386 compiler */
unknown's avatar
unknown committed
174
#define AUTOS_REVERSE 1
175 176
#else
#define AUTOS_REVERSE 0
unknown's avatar
unknown committed
177 178 179
#endif

/*
unknown's avatar
unknown committed
180
 *      Externally supplied functions.
unknown's avatar
unknown committed
181 182 183
 */

#ifndef HAVE_PERROR
unknown's avatar
unknown committed
184
static void perror();          /* Fake system/library error print routine */
unknown's avatar
unknown committed
185 186
#endif

187
#ifdef SAFEMALLOC
unknown's avatar
unknown committed
188
IMPORT int _sanity(const char *file,uint line); /* safemalloc sanity checker */
189 190 191
#else
#define _sanity(X,Y) (1)
#endif
unknown's avatar
unknown committed
192 193

/*
unknown's avatar
unknown committed
194 195 196
 *      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.
unknown's avatar
unknown committed
197 198 199 200
 */

struct link {
    struct link *next_link;   /* Pointer to the next link */
201 202
    char   flags;
    char   str[1];            /* Pointer to link's contents */
unknown's avatar
unknown committed
203 204
};

205 206 207 208 209 210 211
/* flags for struct link and return flags of InList */
#define SUBDIR          1 /* this MUST be 1 */
#define INCLUDE         2
#define EXCLUDE         4
/* this is not a struct link flag, but only a return flags of InList */
#define MATCHED     65536
#define NOT_MATCHED     0
212

unknown's avatar
unknown committed
213
/*
unknown's avatar
unknown committed
214 215 216 217 218 219 220
 *      Debugging settings 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 settings and the
 *      stack is pushed by adding a new settings to the head of the
 *      list or popped by removing the first link.
 *
 *      Note: if out_file is NULL, the other fields are not initialized at all!
unknown's avatar
unknown committed
221 222
 */

unknown's avatar
unknown committed
223
struct settings {
224 225
  uint flags;                   /* Current settings flags               */
  uint maxdepth;                /* Current maximum trace depth          */
226
  uint delay;                   /* Delay after each output line         */
227
  uint sub_level;               /* Sub this from code_state->level      */
228 229 230 231 232 233 234 235
  FILE *out_file;               /* Current output stream                */
  FILE *prof_file;              /* Current profiling stream             */
  char name[FN_REFLEN];         /* Name of output file                  */
  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 settings *next;        /* Next settings in the list            */
unknown's avatar
unknown committed
236 237
};

unknown's avatar
unknown committed
238
#define is_shared(S, V) ((S)->next && (S)->next->V == (S)->V)
unknown's avatar
unknown committed
239 240

/*
unknown's avatar
unknown committed
241
 *      Local variables not seen by user.
unknown's avatar
unknown committed
242 243 244
 */


unknown's avatar
unknown committed
245 246
static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */
static struct settings init_settings;
247
static const char *db_process= 0;/* Pointer to process name; argv[0] */
248
my_bool _dbug_on_= TRUE;	 /* FALSE if no debugging at all */
unknown's avatar
unknown committed
249

unknown's avatar
unknown committed
250 251
typedef struct _db_code_state_ {
  const char *process;          /* Pointer to process name; usually argv[0] */
252 253 254 255 256 257 258
  const char *func;             /* Name of current user function            */
  const char *file;             /* Name of current user file                */
  struct _db_stack_frame_ *framep; /* Pointer to current frame              */
  struct settings *stack;       /* debugging settings                       */
  const char *jmpfunc;          /* Remember current function for setjmp     */
  const char *jmpfile;          /* Remember current file for setjmp         */
  int lineno;                   /* Current debugger output line number      */
259
  uint level;                   /* Current function nesting level           */
260
  int jmplevel;                 /* Remember nesting level at setjmp()       */
unknown's avatar
unknown committed
261 262

/*
unknown's avatar
unknown committed
263 264 265 266
 *      The following 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.
unknown's avatar
unknown committed
267
 *
unknown's avatar
unknown committed
268 269
 *      These variables are currently used only by _db_pargs_() and
 *      _db_doprnt_().
unknown's avatar
unknown committed
270 271
 */

unknown's avatar
unknown committed
272 273 274
  uint u_line;                  /* User source code line number */
  int  locked;                  /* If locked with _db_lock_file_ */
  const char *u_keyword;        /* Keyword for current macro */
unknown's avatar
unknown committed
275 276
} CODE_STATE;

unknown's avatar
unknown committed
277 278 279 280
/*
  The test below is so we could call functions with DBUG_ENTER before
  my_thread_init().
*/
281 282
#define get_code_state_if_not_set_or_return if (!cs && !((cs=code_state()))) return
#define get_code_state_or_return if (!((cs=code_state()))) return
unknown's avatar
unknown committed
283 284

        /* Handling lists */
285 286 287
#define ListAdd(A,B,C) ListAddDel(A,B,C,INCLUDE)
#define ListDel(A,B,C) ListAddDel(A,B,C,EXCLUDE)
static struct link *ListAddDel(struct link *, const char *, const char *, int);
unknown's avatar
unknown committed
288
static struct link *ListCopy(struct link *);
289
static int InList(struct link *linkp,const char *cp);
unknown's avatar
unknown committed
290
static uint ListFlags(struct link *linkp);
unknown's avatar
unknown committed
291 292 293 294 295 296 297
static void FreeList(struct link *linkp);

        /* OpenClose debug output stream */
static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int);
static void DBUGCloseFile(CODE_STATE *cs, FILE *fp);
        /* Push current debug settings */
static void PushState(CODE_STATE *cs);
298
	/* Free memory associated with debug state. */
299
static void FreeState (CODE_STATE *cs, struct settings *state, int free_state);
unknown's avatar
unknown committed
300
        /* Test for tracing enabled */
301
static int DoTrace(CODE_STATE *cs);
302 303 304 305 306 307 308 309
/*
  return values of DoTrace.
  Can also be used as bitmask: ret & DO_TRACE
*/
#define DO_TRACE        1
#define DONT_TRACE      2
#define ENABLE_TRACE    3
#define DISABLE_TRACE   4
unknown's avatar
unknown committed
310 311

        /* Test to see if file is writable */
312
#if defined(HAVE_ACCESS) && !defined(MSDOS)
313
static BOOLEAN Writable(const char *pathname);
unknown's avatar
unknown committed
314 315 316
        /* Change file owner and group */
static void ChangeOwner(CODE_STATE *cs, char *pathname);
        /* Allocate memory for runtime support */
unknown's avatar
unknown committed
317
#endif
unknown's avatar
unknown committed
318 319 320

static void DoPrefix(CODE_STATE *cs, uint line);

321
static char *DbugMalloc(size_t size);
unknown's avatar
unknown committed
322 323
static const char *BaseName(const char *pathname);
static void Indent(CODE_STATE *cs, int indent);
unknown's avatar
unknown committed
324
static void DbugFlush(CODE_STATE *);
unknown's avatar
unknown committed
325
static void DbugExit(const char *why);
unknown's avatar
unknown committed
326 327 328 329 330 331 332 333 334 335
static const char *DbugStrTok(const char *s);

#ifndef THREAD
        /* Open profile output stream */
static FILE *OpenProfile(CODE_STATE *cs, const char *name);
        /* Profile if asked for it */
static BOOLEAN DoProfile(CODE_STATE *);
        /* Return current user time (ms) */
static unsigned long Clock(void);
#endif
unknown's avatar
unknown committed
336 337

/*
unknown's avatar
unknown committed
338
 *      Miscellaneous printf format strings.
unknown's avatar
unknown committed
339 340 341 342 343 344 345 346 347
 */

#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\": "

/*
unknown's avatar
unknown committed
348
 *      Macros and defines for testing file accessibility under UNIX and MSDOS.
unknown's avatar
unknown committed
349 350
 */

unknown's avatar
unknown committed
351
#undef EXISTS
unknown's avatar
unknown committed
352
#if !defined(HAVE_ACCESS) || defined(MSDOS)
unknown's avatar
unknown committed
353
#define EXISTS(pathname) (FALSE)        /* Assume no existance */
unknown's avatar
unknown committed
354 355
#define Writable(name) (TRUE)
#else
unknown's avatar
unknown committed
356 357
#define EXISTS(pathname)         (access(pathname, F_OK) == 0)
#define WRITABLE(pathname)       (access(pathname, W_OK) == 0)
unknown's avatar
unknown committed
358 359
#endif
#ifndef MSDOS
unknown's avatar
unknown committed
360
#define ChangeOwner(cs,name)
unknown's avatar
unknown committed
361 362
#endif

363

unknown's avatar
unknown committed
364 365 366 367 368 369
/*
** Macros to allow dbugging with threads
*/

#ifdef THREAD
#include <my_pthread.h>
370
static pthread_mutex_t THR_LOCK_dbug;
unknown's avatar
unknown committed
371 372 373

static CODE_STATE *code_state(void)
{
374
  CODE_STATE *cs, **cs_ptr;
unknown's avatar
unknown committed
375

376 377 378 379 380 381 382
  /*
    _dbug_on_ is reset if we don't plan to use any debug commands at all and
    we want to run on maximum speed
   */
  if (!_dbug_on_)
    return 0;

unknown's avatar
unknown committed
383 384
  if (!init_done)
  {
385
    init_done=TRUE;
unknown's avatar
unknown committed
386 387 388 389 390 391
    pthread_mutex_init(&THR_LOCK_dbug,MY_MUTEX_INIT_FAST);
    bzero(&init_settings, sizeof(init_settings));
    init_settings.out_file=stderr;
    init_settings.flags=OPEN_APPEND;
  }

392 393 394
  if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug()))
    return 0;                                   /* Thread not initialised */
  if (!(cs= *cs_ptr))
unknown's avatar
unknown committed
395
  {
396 397 398 399 400 401
    cs=(CODE_STATE*) DbugMalloc(sizeof(*cs));
    bzero((uchar*) cs,sizeof(*cs));
    cs->process= db_process ? db_process : "dbug";
    cs->func="?func";
    cs->file="?file";
    cs->stack=&init_settings;
402
    *cs_ptr= cs;
unknown's avatar
unknown committed
403
  }
unknown's avatar
unknown committed
404
  return cs;
unknown's avatar
unknown committed
405 406 407 408
}

#else /* !THREAD */

unknown's avatar
unknown committed
409 410
static CODE_STATE static_code_state=
{
unknown's avatar
unknown committed
411 412
  "dbug", "?func", "?file", NULL, &init_settings,
  NullS, NullS, 0,0,0,0,0,NullS
unknown's avatar
unknown committed
413
};
unknown's avatar
unknown committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428

static CODE_STATE *code_state(void)
{
  if (!init_done)
  {
    bzero(&init_settings, sizeof(init_settings));
    init_settings.out_file=stderr;
    init_settings.flags=OPEN_APPEND;
    init_done=TRUE;
  }
  return &static_code_state;
}

#define pthread_mutex_lock(A) {}
#define pthread_mutex_unlock(A) {}
unknown's avatar
unknown committed
429 430
#endif

unknown's avatar
unknown committed
431 432 433 434 435 436 437 438 439 440
/*
 *      Translate some calls among different systems.
 */

#ifdef HAVE_SLEEP
/* sleep() wants seconds */
#define Delay(A) sleep(((uint) A)/10)
#else
#define Delay(A) (0)
#endif
unknown's avatar
unknown committed
441 442 443 444

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
445
 *      _db_process_       give the name to the current process/thread
unknown's avatar
unknown committed
446 447 448
 *
 *  SYNOPSIS
 *
449
 *      VOID _process_(name)
unknown's avatar
unknown committed
450
 *      char *name;
unknown's avatar
unknown committed
451
 *
unknown's avatar
unknown committed
452 453 454 455
 */

void _db_process_(const char *name)
{
456
  CODE_STATE *cs;
unknown's avatar
unknown committed
457 458 459

  if (!db_process)
    db_process= name;
460

unknown's avatar
unknown committed
461 462 463 464 465 466
  get_code_state_or_return;
  cs->process= name;
}

/*
 *  FUNCTION
unknown's avatar
unknown committed
467
 *
unknown's avatar
unknown committed
468
 *      DbugParse  parse control string and set current debugger settings
unknown's avatar
unknown committed
469
 *
unknown's avatar
unknown committed
470
 *  DESCRIPTION
unknown's avatar
unknown committed
471
 *
unknown's avatar
unknown committed
472 473 474
 *      Given pointer to a debug control string in "control",
 *      parses the control string, and sets
 *      up a current debug settings.
unknown's avatar
unknown committed
475
 *
unknown's avatar
unknown committed
476 477
 *      The debug control string is a sequence of colon separated fields
 *      as follows:
unknown's avatar
unknown committed
478
 *
unknown's avatar
unknown committed
479
 *              [+]<field_1>:<field_2>:...:<field_N>
unknown's avatar
unknown committed
480
 *
unknown's avatar
unknown committed
481 482
 *      Each field consists of a mandatory flag character followed by
 *      an optional "," and comma separated list of modifiers:
unknown's avatar
unknown committed
483
 *
unknown's avatar
unknown committed
484
 *              [sign]flag[,modifier,modifier,...,modifier]
unknown's avatar
unknown committed
485
 *
unknown's avatar
unknown committed
486
 *      See the manual for the list of supported signs, flags, and modifiers
unknown's avatar
unknown committed
487
 *
unknown's avatar
unknown committed
488
 *      For convenience, any leading "-#" is stripped off.
unknown's avatar
unknown committed
489
 *
490 491 492
 *  RETURN
 *      1 - a list of functions ("f" flag) was possibly changed
 *      0 - a list of functions was not changed
unknown's avatar
unknown committed
493 494
 */

unknown's avatar
unknown committed
495
int DbugParse(CODE_STATE *cs, const char *control)
unknown's avatar
unknown committed
496
{
unknown's avatar
unknown committed
497
  const char *end;
498
  int rel, f_used=0;
499
  struct settings *stack;
500
  int org_cs_locked;
unknown's avatar
unknown committed
501

502
  stack= cs->stack;
unknown's avatar
unknown committed
503

504 505 506 507 508 509
  if (!(org_cs_locked= cs->locked))
  {
    cs->locked= 1;
    pthread_mutex_lock(&THR_LOCK_dbug);
  }

unknown's avatar
unknown committed
510 511 512
  if (control[0] == '-' && control[1] == '#')
    control+=2;
  rel= control[0] == '+' || control[0] == '-';
513
  if ((!rel || (!stack->out_file && !stack->next)))
unknown's avatar
unknown committed
514
  {
515 516 517
    /* If overwriting previous state, be sure to free old to avoid leak. */
    if (stack->out_file)
      FreeState(cs, stack, 0);
518 519 520 521 522 523 524 525 526 527
    stack->flags= 0;
    stack->delay= 0;
    stack->maxdepth= 0;
    stack->sub_level= 0;
    stack->out_file= stderr;
    stack->prof_file= NULL;
    stack->functions= NULL;
    stack->p_functions= NULL;
    stack->keywords= NULL;
    stack->processes= NULL;
unknown's avatar
unknown committed
528
  }
529
  else if (!stack->out_file)
unknown's avatar
unknown committed
530
  {
531 532 533 534 535 536 537 538
    stack->flags= stack->next->flags;
    stack->delay= stack->next->delay;
    stack->maxdepth= stack->next->maxdepth;
    stack->sub_level= stack->next->sub_level;
    strcpy(stack->name, stack->next->name);
    stack->out_file= stack->next->out_file;
    stack->prof_file= stack->next->prof_file;
    if (stack->next == &init_settings)
unknown's avatar
unknown committed
539 540
    {
      /* never share with the global parent - it can change under your feet */
541 542 543 544
      stack->functions= ListCopy(init_settings.functions);
      stack->p_functions= ListCopy(init_settings.p_functions);
      stack->keywords= ListCopy(init_settings.keywords);
      stack->processes= ListCopy(init_settings.processes);
unknown's avatar
unknown committed
545 546 547
    }
    else
    {
548 549 550 551
      stack->functions= stack->next->functions;
      stack->p_functions= stack->next->p_functions;
      stack->keywords= stack->next->keywords;
      stack->processes= stack->next->processes;
unknown's avatar
unknown committed
552
    }
unknown's avatar
unknown committed
553 554
  }

unknown's avatar
unknown committed
555
  end= DbugStrTok(control);
556
  while (control < end)
unknown's avatar
unknown committed
557 558
  {
    int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0;
559 560
    if (sign)
      control++;
unknown's avatar
unknown committed
561
    c= *control++;
562 563
    if (*control == ',')
      control++;
unknown's avatar
unknown committed
564
    /* XXX when adding new cases here, don't forget _db_explain_ ! */
unknown's avatar
unknown committed
565
    switch (c) {
unknown's avatar
unknown committed
566
    case 'd':
unknown's avatar
unknown committed
567 568
      if (sign < 0 && control == end)
      {
569 570 571 572
        if (!is_shared(stack, keywords))
          FreeList(stack->keywords);
        stack->keywords=NULL;
        stack->flags &= ~DEBUG_ON;
unknown's avatar
unknown committed
573
        break;
unknown's avatar
unknown committed
574
      }
575 576
      if (rel && is_shared(stack, keywords))
        stack->keywords= ListCopy(stack->keywords);
unknown's avatar
unknown committed
577 578 579
      if (sign < 0)
      {
        if (DEBUGGING)
580
          stack->keywords= ListDel(stack->keywords, control, end);
581
        break;
unknown's avatar
unknown committed
582
      }
583 584
      stack->keywords= ListAdd(stack->keywords, control, end);
      stack->flags |= DEBUG_ON;
unknown's avatar
unknown committed
585 586
      break;
    case 'D':
587
      stack->delay= atoi(control);
unknown's avatar
unknown committed
588 589
      break;
    case 'f':
590
      f_used= 1;
unknown's avatar
unknown committed
591 592
      if (sign < 0 && control == end)
      {
593 594 595
        if (!is_shared(stack,functions))
          FreeList(stack->functions);
        stack->functions=NULL;
unknown's avatar
unknown committed
596
        break;
unknown's avatar
unknown committed
597
      }
598 599
      if (rel && is_shared(stack,functions))
        stack->functions= ListCopy(stack->functions);
unknown's avatar
unknown committed
600
      if (sign < 0)
601
        stack->functions= ListDel(stack->functions, control, end);
unknown's avatar
unknown committed
602
      else
603
        stack->functions= ListAdd(stack->functions, control, end);
unknown's avatar
unknown committed
604 605
      break;
    case 'F':
unknown's avatar
unknown committed
606
      if (sign < 0)
607
        stack->flags &= ~FILE_ON;
unknown's avatar
unknown committed
608
      else
609
        stack->flags |= FILE_ON;
unknown's avatar
unknown committed
610 611
      break;
    case 'i':
unknown's avatar
unknown committed
612
      if (sign < 0)
613
        stack->flags &= ~PID_ON;
unknown's avatar
unknown committed
614
      else
615
        stack->flags |= PID_ON;
unknown's avatar
unknown committed
616 617 618
      break;
#ifndef THREAD
    case 'g':
unknown's avatar
unknown committed
619
      if (OpenProfile(cs, PROF_FILE))
unknown's avatar
unknown committed
620
      {
621 622
        stack->flags |= PROFILE_ON;
        stack->p_functions= ListAdd(stack->p_functions, control, end);
unknown's avatar
unknown committed
623 624 625 626
      }
      break;
#endif
    case 'L':
unknown's avatar
unknown committed
627
      if (sign < 0)
628
        stack->flags &= ~LINE_ON;
unknown's avatar
unknown committed
629
      else
630
        stack->flags |= LINE_ON;
unknown's avatar
unknown committed
631 632
      break;
    case 'n':
unknown's avatar
unknown committed
633
      if (sign < 0)
634
        stack->flags &= ~DEPTH_ON;
unknown's avatar
unknown committed
635
      else
636
        stack->flags |= DEPTH_ON;
unknown's avatar
unknown committed
637 638
      break;
    case 'N':
unknown's avatar
unknown committed
639
      if (sign < 0)
640
        stack->flags &= ~NUMBER_ON;
unknown's avatar
unknown committed
641
      else
642
        stack->flags |= NUMBER_ON;
unknown's avatar
unknown committed
643 644 645
      break;
    case 'A':
    case 'O':
646
      stack->flags |= FLUSH_ON_WRITE;
unknown's avatar
unknown committed
647
      /* fall through */
unknown's avatar
unknown committed
648 649
    case 'a':
    case 'o':
unknown's avatar
unknown committed
650 651
      if (sign < 0)
      {
652 653 654 655
        if (!is_shared(stack, out_file))
          DBUGCloseFile(cs, stack->out_file);
        stack->flags &= ~FLUSH_ON_WRITE;
        stack->out_file= stderr;
unknown's avatar
unknown committed
656
        break;
unknown's avatar
unknown committed
657
      }
unknown's avatar
unknown committed
658
      if (c == 'a' || c == 'A')
659
        stack->flags |= OPEN_APPEND;
unknown's avatar
unknown committed
660
      else
661
        stack->flags &= ~OPEN_APPEND;
unknown's avatar
unknown committed
662
      if (control != end)
663
        DBUGOpenFile(cs, control, end, stack->flags & OPEN_APPEND);
unknown's avatar
unknown committed
664 665
      else
        DBUGOpenFile(cs, "-",0,0);
unknown's avatar
unknown committed
666 667
      break;
    case 'p':
unknown's avatar
unknown committed
668 669
      if (sign < 0 && control == end)
      {
670 671 672
        if (!is_shared(stack,processes))
          FreeList(stack->processes);
        stack->processes=NULL;
unknown's avatar
unknown committed
673
        break;
unknown's avatar
unknown committed
674
      }
675 676
      if (rel && is_shared(stack, processes))
        stack->processes= ListCopy(stack->processes);
unknown's avatar
unknown committed
677
      if (sign < 0)
678
        stack->processes= ListDel(stack->processes, control, end);
unknown's avatar
unknown committed
679
      else
680
        stack->processes= ListAdd(stack->processes, control, end);
unknown's avatar
unknown committed
681 682
      break;
    case 'P':
unknown's avatar
unknown committed
683
      if (sign < 0)
684
        stack->flags &= ~PROCESS_ON;
unknown's avatar
unknown committed
685
      else
686
        stack->flags |= PROCESS_ON;
unknown's avatar
unknown committed
687 688
      break;
    case 'r':
689
      stack->sub_level= cs->level;
unknown's avatar
unknown committed
690 691
      break;
    case 't':
unknown's avatar
unknown committed
692 693 694
      if (sign < 0)
      {
        if (control != end)
695
          stack->maxdepth-= atoi(control);
unknown's avatar
unknown committed
696
        else
697
          stack->maxdepth= 0;
unknown's avatar
unknown committed
698 699 700 701
      }
      else
      {
        if (control != end)
702
          stack->maxdepth+= atoi(control);
unknown's avatar
unknown committed
703
        else
704
          stack->maxdepth= MAXDEPTH;
unknown's avatar
unknown committed
705
      }
706 707
      if (stack->maxdepth > 0)
        stack->flags |= TRACE_ON;
unknown's avatar
unknown committed
708
      else
709
        stack->flags &= ~TRACE_ON;
unknown's avatar
unknown committed
710 711 712
      break;
    case 'T':
      if (sign < 0)
713
        stack->flags &= ~TIMESTAMP_ON;
unknown's avatar
unknown committed
714
      else
715
        stack->flags |= TIMESTAMP_ON;
unknown's avatar
unknown committed
716 717
      break;
    case 'S':
unknown's avatar
unknown committed
718
      if (sign < 0)
719
        stack->flags &= ~SANITY_CHECK_ON;
unknown's avatar
unknown committed
720
      else
721
        stack->flags |= SANITY_CHECK_ON;
unknown's avatar
unknown committed
722 723
      break;
    }
unknown's avatar
unknown committed
724 725 726 727
    if (!*end)
      break;
    control=end+1;
    end= DbugStrTok(control);
unknown's avatar
unknown committed
728
  }
729 730 731 732 733 734 735
  if (!org_cs_locked)
  {
    pthread_mutex_unlock(&THR_LOCK_dbug);
    cs->locked= 0;
  }
  return !rel || f_used;}
  
unknown's avatar
unknown committed
736

737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
#define framep_trace_flag(cs, frp) (frp ?                                    \
                                     frp->level & TRACE_ON :                 \
                              (ListFlags(cs->stack->functions) & INCLUDE) ?  \
                                       0 : (uint)TRACE_ON)

void FixTraceFlags_helper(CODE_STATE *cs, const char *func,
                          struct _db_stack_frame_ *framep)
{
  if (framep->prev)
    FixTraceFlags_helper(cs, framep->func, framep->prev);

  cs->func= func;
  cs->level= framep->level & ~TRACE_ON;
  framep->level= cs->level | framep_trace_flag(cs, framep->prev);
  /*
    we don't set cs->framep correctly, even though DoTrace uses it.
    It's ok, because cs->framep may only affect DO_TRACE/DONT_TRACE return
    values, but we ignore them here anyway
  */
756
  switch(DoTrace(cs)) {
757 758 759 760 761 762 763 764 765 766 767
  case ENABLE_TRACE:
    framep->level|= TRACE_ON;
    break;
  case DISABLE_TRACE:
    framep->level&= ~TRACE_ON;
    break;
  }
}

#define fflags(cs) cs->stack->out_file ? ListFlags(cs->stack->functions) : TRACE_ON;

unknown's avatar
unknown committed
768
void FixTraceFlags(uint old_fflags, CODE_STATE *cs)
769 770
{
  const char *func;
unknown's avatar
unknown committed
771
  uint new_fflags, traceon, level;
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
  struct _db_stack_frame_ *framep;

  /*
    first (a.k.a. safety) check:
    if we haven't started tracing yet, no call stack at all - we're safe.
  */
  framep=cs->framep;
  if (framep == 0)
    return;

  /*
    Ok, the tracing has started, call stack isn't empty.

    second check: does the new list have a SUBDIR rule ?
  */
  new_fflags=fflags(cs);
  if (new_fflags & SUBDIR)
    goto yuck;

  /*
    Ok, new list doesn't use SUBDIR.

    third check: we do NOT need to re-scan if
    neither old nor new lists used SUBDIR flag and if a default behavior
    (whether an unlisted function is traced) hasn't changed.
    Default behavior depends on whether there're INCLUDE elements in the list.
  */
  if (!(old_fflags & SUBDIR) && !((new_fflags^old_fflags) & INCLUDE))
    return;

  /*
    Ok, old list may've used SUBDIR, or defaults could've changed.

    fourth check: are we inside a currently active SUBDIR rule ?
    go up the call stack, if TRACE_ON flag ever changes its value - we are.
  */
  for (traceon=framep->level; framep; framep=framep->prev)
    if ((traceon ^ framep->level) & TRACE_ON)
      goto yuck;

  /*
    Ok, TRACE_ON flag doesn't change in the call stack.

    fifth check: but is the top-most value equal to a default one ?
  */
  if (((traceon & TRACE_ON) != 0) == ((new_fflags & INCLUDE) == 0))
    return;

yuck:
  /*
    Yuck! function list was changed, and one of the currently active rules
    was possibly affected. For example, a tracing could've been enabled or
    disabled for a function somewhere up the call stack.
    To react correctly, we must go up the call stack all the way to
    the top and re-match rules to set TRACE_ON bit correctly.

    We must traverse the stack forwards, not backwards.
    That's what a recursive helper is doing.
    It'll destroy two CODE_STATE fields, save them now.
  */
  func= cs->func;
  level= cs->level;
  FixTraceFlags_helper(cs, func, cs->framep);
  /* now we only need to restore CODE_STATE fields, and we're done */
  cs->func= func;
  cs->level= level;
}

/*
 *  FUNCTION
 *
 *      _db_set_       set current debugger settings
 *
 *  SYNOPSIS
 *
 *      VOID _db_set_(control)
 *      char *control;
 *
 *  DESCRIPTION
 *
 *      Given pointer to a debug control string in "control",
 *      parses the control string, and sets
 *      up a current debug settings.
 *
 */

void _db_set_(const char *control)
{
  CODE_STATE *cs;
unknown's avatar
unknown committed
861
  uint old_fflags;
862 863
  get_code_state_or_return;
  old_fflags=fflags(cs);
unknown's avatar
unknown committed
864
  if (DbugParse(cs, control))
865 866
    FixTraceFlags(old_fflags, cs);
}
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881

/*
 *  FUNCTION
 *
 *      _db_push_       push current debugger settings 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 settings, parses the control string, and sets
882
 *      up a new debug settings
883 884 885 886 887
 *
 */

void _db_push_(const char *control)
{
888
  CODE_STATE *cs;
unknown's avatar
unknown committed
889
  uint old_fflags;
890
  get_code_state_or_return;
891
  old_fflags=fflags(cs);
892
  PushState(cs);
unknown's avatar
unknown committed
893
  if (DbugParse(cs, control))
894
    FixTraceFlags(old_fflags, cs);
895 896
}

897 898 899 900 901 902 903 904 905 906 907 908
/**
  Returns TRUE if session-local settings have been set.
*/

int _db_is_pushed_()
{
  CODE_STATE *cs= NULL;
  get_code_state_or_return FALSE;
  return (cs->stack != &init_settings);
}


909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
/*
 *  FUNCTION
 *
 *      _db_set_init_       set initial debugger settings
 *
 *  SYNOPSIS
 *
 *      VOID _db_set_init_(control)
 *      char *control;
 *
 *  DESCRIPTION
 *      see _db_set_
 *
 */

void _db_set_init_(const char *control)
{
926
  CODE_STATE tmp_cs;
927
  bzero((uchar*) &tmp_cs, sizeof(tmp_cs));
928
  tmp_cs.stack= &init_settings;
929
  DbugParse(&tmp_cs, control);
930 931
}

unknown's avatar
unknown committed
932 933 934
/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
935
 *      _db_pop_    pop the debug stack
unknown's avatar
unknown committed
936 937 938
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
939 940 941 942 943 944 945
 *      Pops the debug stack, returning the debug settings to its
 *      condition prior to the most recent _db_push_ invocation.
 *      Note that the pop will fail if it would remove the last
 *      valid settings 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.
unknown's avatar
unknown committed
946 947 948
 *
 */

unknown's avatar
unknown committed
949
void _db_pop_()
unknown's avatar
unknown committed
950
{
unknown's avatar
unknown committed
951
  struct settings *discard;
unknown's avatar
unknown committed
952
  uint old_fflags;
953
  CODE_STATE *cs;
unknown's avatar
unknown committed
954 955 956 957 958

  get_code_state_or_return;

  discard= cs->stack;
  if (discard->next != NULL)
unknown's avatar
unknown committed
959
  {
960
    old_fflags=fflags(cs);
unknown's avatar
unknown committed
961
    cs->stack= discard->next;
962
    FreeState(cs, discard, 1);
963
    FixTraceFlags(old_fflags, cs);
unknown's avatar
unknown committed
964 965 966
  }
}

unknown's avatar
unknown committed
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
/*
 *  FUNCTION
 *
 *      _db_explain_    generates 'control' string for the current settings
 *
 *  RETURN
 *      0 - ok
 *      1  - buffer too short, output truncated
 *
 */

/* helper macros */
#define char_to_buf(C)    do {                  \
        *buf++=(C);                             \
        if (buf >= end) goto overflow;          \
      } while (0)
#define str_to_buf(S)    do {                   \
        char_to_buf(',');                       \
985
        buf=strnmov(buf, (S), (uint) (end-buf)); \
unknown's avatar
unknown committed
986 987
        if (buf >= end) goto overflow;          \
      } while (0)
988
#define list_to_buf(l, f)  do {                 \
unknown's avatar
unknown committed
989 990 991
        struct link *listp=(l);                 \
        while (listp)                           \
        {                                       \
992
          if (listp->flags & (f))               \
993
          {                                     \
994
            str_to_buf(listp->str);             \
995 996 997
            if (listp->flags & SUBDIR)          \
              char_to_buf('/');                 \
          }                                     \
unknown's avatar
unknown committed
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
          listp=listp->next_link;               \
        }                                       \
      } while (0)
#define int_to_buf(i)  do {                     \
        char b[50];                             \
        int10_to_str((i), b, 10);               \
        str_to_buf(b);                          \
      } while (0)
#define colon_to_buf   do {                     \
        if (buf != start) char_to_buf(':');     \
      } while(0)
#define op_int_to_buf(C, val, def) do {         \
        if ((val) != (def))                     \
        {                                       \
          colon_to_buf;                         \
          char_to_buf((C));                     \
          int_to_buf(val);                      \
        }                                       \
      } while (0)
#define op_intf_to_buf(C, val, def, cond) do {  \
        if ((cond))                             \
        {                                       \
          colon_to_buf;                         \
          char_to_buf((C));                     \
          if ((val) != (def)) int_to_buf(val);  \
        }                                       \
      } while (0)
#define op_str_to_buf(C, val, cond) do {        \
        if ((cond))                             \
        {                                       \
          char *s=(val);                        \
          colon_to_buf;                         \
          char_to_buf((C));                     \
          if (*s) str_to_buf(s);                \
        }                                       \
      } while (0)
#define op_list_to_buf(C, val, cond) do {       \
        if ((cond))                             \
        {                                       \
1037
          int f=ListFlags(val);                 \
unknown's avatar
unknown committed
1038 1039
          colon_to_buf;                         \
          char_to_buf((C));                     \
1040 1041 1042 1043 1044 1045 1046 1047 1048
          if (f & INCLUDE)                      \
            list_to_buf(val, INCLUDE);          \
          if (f & EXCLUDE)                      \
          {                                     \
            colon_to_buf;                       \
            char_to_buf('-');                   \
            char_to_buf((C));                   \
            list_to_buf(val, EXCLUDE);          \
          }                                     \
unknown's avatar
unknown committed
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
        }                                       \
      } while (0)
#define op_bool_to_buf(C, cond) do {            \
        if ((cond))                             \
        {                                       \
          colon_to_buf;                         \
          char_to_buf((C));                     \
        }                                       \
      } while (0)

1059
int _db_explain_ (CODE_STATE *cs, char *buf, size_t len)
unknown's avatar
unknown committed
1060 1061 1062
{
  char *start=buf, *end=buf+len-4;

1063
  get_code_state_if_not_set_or_return *buf=0;
unknown's avatar
unknown committed
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081

  op_list_to_buf('d', cs->stack->keywords, DEBUGGING);
  op_int_to_buf ('D', cs->stack->delay, 0);
  op_list_to_buf('f', cs->stack->functions, cs->stack->functions);
  op_bool_to_buf('F', cs->stack->flags & FILE_ON);
  op_bool_to_buf('i', cs->stack->flags & PID_ON);
  op_list_to_buf('g', cs->stack->p_functions, PROFILING);
  op_bool_to_buf('L', cs->stack->flags & LINE_ON);
  op_bool_to_buf('n', cs->stack->flags & DEPTH_ON);
  op_bool_to_buf('N', cs->stack->flags & NUMBER_ON);
  op_str_to_buf(
    ((cs->stack->flags & FLUSH_ON_WRITE ? 0 : 32) |
     (cs->stack->flags & OPEN_APPEND ? 'A' : 'O')),
    cs->stack->name, cs->stack->out_file != stderr);
  op_list_to_buf('p', cs->stack->processes, cs->stack->processes);
  op_bool_to_buf('P', cs->stack->flags & PROCESS_ON);
  op_bool_to_buf('r', cs->stack->sub_level != 0);
  op_intf_to_buf('t', cs->stack->maxdepth, MAXDEPTH, TRACING);
unknown's avatar
unknown committed
1082
  op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON);
unknown's avatar
unknown committed
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
  op_bool_to_buf('S', cs->stack->flags & SANITY_CHECK_ON);

  *buf= '\0';
  return 0;

overflow:
  *end++= '.';
  *end++= '.';
  *end++= '.';
  *end=   '\0';
  return 1;
}

#undef char_to_buf
#undef str_to_buf
#undef list_to_buf
#undef int_to_buf
#undef colon_to_buf
#undef op_int_to_buf
#undef op_intf_to_buf
#undef op_str_to_buf
#undef op_list_to_buf
#undef op_bool_to_buf
unknown's avatar
unknown committed
1106 1107 1108 1109

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1110 1111 1112 1113 1114 1115
 *      _db_explain_init_       explain initial debugger settings
 *
 *  DESCRIPTION
 *      see _db_explain_
 */

1116
int _db_explain_init_(char *buf, size_t len)
unknown's avatar
unknown committed
1117 1118
{
  CODE_STATE cs;
1119
  bzero((uchar*) &cs,sizeof(cs));
unknown's avatar
unknown committed
1120 1121 1122 1123 1124 1125 1126 1127
  cs.stack=&init_settings;
  return _db_explain_(&cs, buf, len);
}

/*
 *  FUNCTION
 *
 *      _db_enter_    process entry point to user function
unknown's avatar
unknown committed
1128 1129 1130
 *
 *  SYNOPSIS
 *
1131
 *      VOID _db_enter_(_func_, _file_, _line_, _stack_frame_)
unknown's avatar
unknown committed
1132 1133 1134
 *      char *_func_;           points to current function name
 *      char *_file_;           points to current file name
 *      int _line_;             called from source line number
1135
 *      struct _db_stack_frame_ allocated on the caller's stack
unknown's avatar
unknown committed
1136 1137 1138
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1139 1140 1141 1142 1143 1144 1145 1146 1147
 *      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.
unknown's avatar
unknown committed
1148
 *
unknown's avatar
unknown committed
1149 1150
 *      Also prints a trace line if tracing is enabled and
 *      increments the current function nesting depth.
unknown's avatar
unknown committed
1151
 *
unknown's avatar
unknown committed
1152 1153 1154
 *      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.
unknown's avatar
unknown committed
1155 1156 1157
 *
 */

unknown's avatar
unknown committed
1158
void _db_enter_(const char *_func_, const char *_file_,
1159
                uint _line_, struct _db_stack_frame_ *_stack_frame_)
unknown's avatar
unknown committed
1160
{
1161 1162 1163 1164
  int save_errno;
  CODE_STATE *cs;
  if (!((cs=code_state())))
  {
1165 1166
    _stack_frame_->level= 0; /* Set to avoid valgrind warnings if dbug is enabled later */
    _stack_frame_->prev= 0;
1167 1168 1169
    return;
  }
  save_errno= errno;
unknown's avatar
unknown committed
1170

1171 1172
  _stack_frame_->func= cs->func;
  _stack_frame_->file= cs->file;
unknown's avatar
unknown committed
1173 1174
  cs->func=  _func_;
  cs->file=  _file_;
1175 1176 1177
  _stack_frame_->prev= cs->framep;
  _stack_frame_->level= ++cs->level | framep_trace_flag(cs, cs->framep);
  cs->framep= _stack_frame_;
unknown's avatar
unknown committed
1178
#ifndef THREAD
unknown's avatar
unknown committed
1179 1180 1181
  if (DoProfile(cs))
  {
    long stackused;
1182
    if (cs->framep->prev == NULL)
unknown's avatar
unknown committed
1183 1184
      stackused= 0;
    else
unknown's avatar
unknown committed
1185
    {
1186
      stackused= (char*)(cs->framep->prev) - (char*)(cs->framep);
unknown's avatar
unknown committed
1187 1188 1189 1190
      stackused= stackused > 0 ? stackused : -stackused;
    }
    (void) fprintf(cs->stack->prof_file, PROF_EFMT , Clock(), cs->func);
    (void) fprintf(cs->stack->prof_file, PROF_SFMT, (ulong) cs->framep, stackused,
1191
                   AUTOS_REVERSE ? _stack_frame_->func : cs->func);
unknown's avatar
unknown committed
1192 1193
    (void) fflush(cs->stack->prof_file);
  }
unknown's avatar
unknown committed
1194
#endif
1195
  switch (DoTrace(cs)) {
1196 1197 1198 1199 1200
  case ENABLE_TRACE:
    cs->framep->level|= TRACE_ON;
    if (!TRACING) break;
    /* fall through */
  case DO_TRACE:
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
    if ((cs->stack->flags & SANITY_CHECK_ON) && _sanity(_file_,_line_))
      cs->stack->flags &= ~SANITY_CHECK_ON;
    if (TRACING)
    {
      if (!cs->locked)
        pthread_mutex_lock(&THR_LOCK_dbug);
      DoPrefix(cs, _line_);
      Indent(cs, cs->level);
      (void) fprintf(cs->stack->out_file, ">%s\n", cs->func);
      DbugFlush(cs);                       /* This does a unlock */
    }
1212 1213 1214 1215 1216 1217
    break;
  case DISABLE_TRACE:
    cs->framep->level&= ~TRACE_ON;
    /* fall through */
  case DONT_TRACE:
    break;
unknown's avatar
unknown committed
1218 1219
  }
  errno=save_errno;
unknown's avatar
unknown committed
1220 1221 1222 1223 1224
}

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1225
 *      _db_return_    process exit from user function
unknown's avatar
unknown committed
1226 1227 1228
 *
 *  SYNOPSIS
 *
1229
 *      VOID _db_return_(_line_, _stack_frame_)
unknown's avatar
unknown committed
1230
 *      int _line_;             current source line number
1231
 *      struct _db_stack_frame_ allocated on the caller's stack
unknown's avatar
unknown committed
1232 1233 1234
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1235 1236 1237 1238
 *      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.
unknown's avatar
unknown committed
1239 1240 1241
 *
 */

unknown's avatar
unknown committed
1242
/* helper macro */
1243
void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_)
unknown's avatar
unknown committed
1244
{
unknown's avatar
unknown committed
1245
  int save_errno=errno;
1246
  uint _slevel_= _stack_frame_->level & ~TRACE_ON;
1247
  CODE_STATE *cs;
unknown's avatar
unknown committed
1248
  get_code_state_or_return;
unknown's avatar
unknown committed
1249

1250
  if (cs->level != _slevel_)
unknown's avatar
unknown committed
1251 1252 1253 1254 1255
  {
    if (!cs->locked)
      pthread_mutex_lock(&THR_LOCK_dbug);
    (void) fprintf(cs->stack->out_file, ERR_MISSING_RETURN, cs->process,
                   cs->func);
unknown's avatar
unknown committed
1256
    DbugFlush(cs);
unknown's avatar
unknown committed
1257 1258
  }
  else
unknown's avatar
unknown committed
1259 1260
  {
#ifndef THREAD
unknown's avatar
unknown committed
1261 1262
    if (DoProfile(cs))
      (void) fprintf(cs->stack->prof_file, PROF_XFMT, Clock(), cs->func);
unknown's avatar
unknown committed
1263
#endif
1264
    if (DoTrace(cs) & DO_TRACE)
unknown's avatar
unknown committed
1265
    {
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
      if ((cs->stack->flags & SANITY_CHECK_ON) &&
          _sanity(_stack_frame_->file,_line_))
        cs->stack->flags &= ~SANITY_CHECK_ON;
      if (TRACING)
      {
        if (!cs->locked)
          pthread_mutex_lock(&THR_LOCK_dbug);
        DoPrefix(cs, _line_);
        Indent(cs, cs->level);
        (void) fprintf(cs->stack->out_file, "<%s\n", cs->func);
        DbugFlush(cs);
      }
unknown's avatar
unknown committed
1278
    }
unknown's avatar
unknown committed
1279
  }
1280 1281 1282 1283
  /*
    Check to not set level < 0. This can happen if DBUG was disabled when
    function was entered and enabled in function.
  */
1284 1285 1286
  cs->level= _slevel_ != 0 ? _slevel_ - 1 : 0;
  cs->func= _stack_frame_->func;
  cs->file= _stack_frame_->file;
unknown's avatar
unknown committed
1287
  if (cs->framep != NULL)
1288
    cs->framep= cs->framep->prev;
unknown's avatar
unknown committed
1289
  errno=save_errno;
unknown's avatar
unknown committed
1290 1291 1292 1293 1294 1295
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1296
 *      _db_pargs_    log arguments for subsequent use by _db_doprnt_()
unknown's avatar
unknown committed
1297 1298 1299
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1300 1301 1302
 *      VOID _db_pargs_(_line_, keyword)
 *      int _line_;
 *      char *keyword;
unknown's avatar
unknown committed
1303 1304 1305
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1306 1307 1308 1309
 *      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_().
unknown's avatar
unknown committed
1310 1311 1312
 *
 */

unknown's avatar
unknown committed
1313
void _db_pargs_(uint _line_, const char *keyword)
unknown's avatar
unknown committed
1314
{
1315
  CODE_STATE *cs;
unknown's avatar
unknown committed
1316 1317
  get_code_state_or_return;
  cs->u_line= _line_;
1318
  cs->u_keyword= keyword;
unknown's avatar
unknown committed
1319 1320 1321 1322 1323 1324
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1325
 *      _db_doprnt_    handle print of debug lines
unknown's avatar
unknown committed
1326 1327 1328
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1329 1330 1331
 *      VOID _db_doprnt_(format, va_alist)
 *      char *format;
 *      va_dcl;
unknown's avatar
unknown committed
1332 1333 1334
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1335 1336 1337 1338 1339
 *      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.
unknown's avatar
unknown committed
1340
 *
unknown's avatar
unknown committed
1341 1342
 *      Note that the format string SHOULD NOT include a terminating
 *      newline, this is supplied automatically.
unknown's avatar
unknown committed
1343 1344 1345 1346 1347
 *
 */

#include <stdarg.h>

unknown's avatar
unknown committed
1348
void _db_doprnt_(const char *format,...)
unknown's avatar
unknown committed
1349 1350
{
  va_list args;
1351
  CODE_STATE *cs;
unknown's avatar
unknown committed
1352
  get_code_state_or_return;
unknown's avatar
unknown committed
1353 1354 1355

  va_start(args,format);

1356 1357
  if (!cs->locked)
    pthread_mutex_lock(&THR_LOCK_dbug);
1358
  if (_db_keyword_(cs, cs->u_keyword, 0))
unknown's avatar
unknown committed
1359
  {
unknown's avatar
unknown committed
1360
    int save_errno=errno;
unknown's avatar
unknown committed
1361 1362 1363 1364 1365 1366 1367 1368
    DoPrefix(cs, cs->u_line);
    if (TRACING)
      Indent(cs, cs->level + 1);
    else
      (void) fprintf(cs->stack->out_file, "%s: ", cs->func);
    (void) fprintf(cs->stack->out_file, "%s: ", cs->u_keyword);
    (void) vfprintf(cs->stack->out_file, format, args);
    (void) fputc('\n',cs->stack->out_file);
unknown's avatar
unknown committed
1369
    DbugFlush(cs);
unknown's avatar
unknown committed
1370 1371
    errno=save_errno;
  }
1372 1373 1374
  else if (!cs->locked)
    pthread_mutex_unlock(&THR_LOCK_dbug);

unknown's avatar
unknown committed
1375 1376 1377 1378 1379 1380 1381
  va_end(args);
}


/*
 *  FUNCTION
 *
1382
 *            _db_dump_    dump a string in hex
unknown's avatar
unknown committed
1383 1384 1385
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1386 1387 1388 1389 1390
 *            void _db_dump_(_line_,keyword,memory,length)
 *            int _line_;               current source line number
 *            char *keyword;
 *            char *memory;             Memory to print
 *            int length;               Bytes to print
unknown's avatar
unknown committed
1391 1392 1393 1394 1395 1396
 *
 *  DESCRIPTION
 *  Dump N characters in a binary array.
 *  Is used to examine corrputed memory or arrays.
 */

1397 1398
void _db_dump_(uint _line_, const char *keyword,
               const unsigned char *memory, size_t length)
unknown's avatar
unknown committed
1399 1400 1401
{
  int pos;
  char dbuff[90];
1402
  CODE_STATE *cs;
unknown's avatar
unknown committed
1403 1404
  get_code_state_or_return;

1405 1406
  if (!cs->locked)
    pthread_mutex_lock(&THR_LOCK_dbug);
1407
  if (_db_keyword_(cs, keyword, 0))
unknown's avatar
unknown committed
1408
  {
unknown's avatar
unknown committed
1409
    DoPrefix(cs, _line_);
unknown's avatar
unknown committed
1410 1411
    if (TRACING)
    {
unknown's avatar
unknown committed
1412 1413
      Indent(cs, cs->level + 1);
      pos= min(max(cs->level-cs->stack->sub_level,0)*INDENT,80);
unknown's avatar
unknown committed
1414 1415 1416
    }
    else
    {
unknown's avatar
unknown committed
1417
      fprintf(cs->stack->out_file, "%s: ", cs->func);
unknown's avatar
unknown committed
1418
    }
1419
    sprintf(dbuff,"%s: Memory: 0x%lx  Bytes: (%ld)\n",
1420
            keyword, (ulong) memory, (long) length);
unknown's avatar
unknown committed
1421
    (void) fputs(dbuff,cs->stack->out_file);
unknown's avatar
unknown committed
1422 1423 1424 1425 1426 1427 1428

    pos=0;
    while (length-- > 0)
    {
      uint tmp= *((unsigned char*) memory++);
      if ((pos+=3) >= 80)
      {
unknown's avatar
unknown committed
1429 1430
        fputc('\n',cs->stack->out_file);
        pos=3;
unknown's avatar
unknown committed
1431
      }
unknown's avatar
unknown committed
1432 1433 1434
      fputc(_dig_vec_upper[((tmp >> 4) & 15)], cs->stack->out_file);
      fputc(_dig_vec_upper[tmp & 15], cs->stack->out_file);
      fputc(' ',cs->stack->out_file);
unknown's avatar
unknown committed
1435
    }
unknown's avatar
unknown committed
1436
    (void) fputc('\n',cs->stack->out_file);
unknown's avatar
unknown committed
1437
    DbugFlush(cs);
unknown's avatar
unknown committed
1438
  }
1439 1440
  else if (!cs->locked)
    pthread_mutex_unlock(&THR_LOCK_dbug);
unknown's avatar
unknown committed
1441 1442
}

unknown's avatar
unknown committed
1443 1444

/*
unknown's avatar
unknown committed
1445 1446
 *  FUNCTION
 *
1447
 *      ListAddDel    modify the list according to debug control string
unknown's avatar
unknown committed
1448 1449 1450 1451
 *
 *  DESCRIPTION
 *
 *      Given pointer to a comma separated list of strings in "cltp",
1452 1453
 *      parses the list, and modifies "listp", returning a pointer
 *      to the new list.
unknown's avatar
unknown committed
1454
 *
1455
 *      The mode of operation is defined by "todo" parameter.
unknown's avatar
unknown committed
1456
 *
1457 1458
 *      If it is INCLUDE, elements (strings from "cltp") are added to the
 *      list, they will have INCLUDE flag set. If the list already contains
1459 1460 1461
 *      the string in question, new element is not added, but a flag of
 *      the existing element is adjusted (INCLUDE bit is set, EXCLUDE bit
 *      is removed).
unknown's avatar
unknown committed
1462
 *
1463
 *      If it is EXCLUDE, elements are added to the list with the EXCLUDE
1464 1465
 *      flag set. If the list already contains the string in question,
 *      it is removed, new element is not added.
unknown's avatar
unknown committed
1466 1467
 */

1468 1469
static struct link *ListAddDel(struct link *head, const char *ctlp,
                               const char *end, int todo)
unknown's avatar
unknown committed
1470
{
unknown's avatar
unknown committed
1471 1472
  const char *start;
  struct link **cur;
1473
  int len, subdir;
unknown's avatar
unknown committed
1474

1475 1476 1477
  ctlp--;
next:
  while (++ctlp < end)
unknown's avatar
unknown committed
1478 1479
  {
    start= ctlp;
1480
    subdir=0;
unknown's avatar
unknown committed
1481 1482 1483
    while (ctlp < end && *ctlp != ',')
      ctlp++;
    len=ctlp-start;
1484 1485 1486 1487 1488
    if (start[len-1] == '/')
    {
      len--;
      subdir=SUBDIR;
    }
1489 1490
    if (len == 0) continue;
    for (cur=&head; *cur; cur=&((*cur)->next_link))
unknown's avatar
unknown committed
1491
    {
1492
      if (!strncmp((*cur)->str, start, len))
unknown's avatar
unknown committed
1493
      {
1494 1495 1496
        if ((*cur)->flags & todo)  /* same action ? */
          (*cur)->flags|= subdir;  /* just merge the SUBDIR flag */
        else if (todo == EXCLUDE)
1497 1498 1499 1500 1501 1502 1503
        {
          struct link *delme=*cur;
          *cur=(*cur)->next_link;
          free((void*) delme);
        }
        else
        {
1504 1505
          (*cur)->flags&=~(EXCLUDE & SUBDIR);
          (*cur)->flags|=INCLUDE | subdir;
1506 1507
        }
        goto next;
unknown's avatar
unknown committed
1508
      }
1509 1510 1511 1512
    }
    *cur= (struct link *) DbugMalloc(sizeof(struct link)+len);
    memcpy((*cur)->str, start, len);
    (*cur)->str[len]=0;
1513
    (*cur)->flags=todo | subdir;
1514
    (*cur)->next_link=0;
unknown's avatar
unknown committed
1515 1516 1517
  }
  return head;
}
unknown's avatar
unknown committed
1518

unknown's avatar
unknown committed
1519 1520 1521
/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1522
 *      ListCopy    make a copy of the list
unknown's avatar
unknown committed
1523 1524 1525
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1526 1527
 *      static struct link *ListCopy(orig)
 *      struct link *orig;
unknown's avatar
unknown committed
1528 1529 1530
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1531 1532
 *      Given pointer to list, which contains a copy of every element from
 *      the original list.
unknown's avatar
unknown committed
1533
 *
unknown's avatar
unknown committed
1534 1535 1536 1537 1538
 *      the orig pointer can be NULL
 *
 *      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.
unknown's avatar
unknown committed
1539 1540 1541
 *
 */

unknown's avatar
unknown committed
1542
static struct link *ListCopy(struct link *orig)
unknown's avatar
unknown committed
1543
{
unknown's avatar
unknown committed
1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554
  struct link *new_malloc;
  struct link *head;
  int len;

  head= NULL;
  while (orig != NULL)
  {
    len= strlen(orig->str);
    new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len);
    memcpy(new_malloc->str, orig->str, len);
    new_malloc->str[len]= 0;
1555
    new_malloc->flags=orig->flags;
unknown's avatar
unknown committed
1556 1557 1558
    new_malloc->next_link= head;
    head= new_malloc;
    orig= orig->next_link;
unknown's avatar
unknown committed
1559
  }
unknown's avatar
unknown committed
1560
  return head;
unknown's avatar
unknown committed
1561 1562 1563 1564 1565
}

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1566
 *      InList    test a given string for member of a given list
unknown's avatar
unknown committed
1567 1568 1569
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1570 1571
 *      Tests the string pointed to by "cp" to determine if it is in
 *      the list pointed to by "linkp".  Linkp points to the first
1572 1573
 *      link in the list.  If linkp is NULL or contains only EXCLUDE
 *      elements then the string is treated as if it is in the list.
unknown's avatar
unknown committed
1574 1575 1576 1577
 *      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.
unknown's avatar
unknown committed
1578
 *
1579
 *  RETURN
1580
 *      combination of SUBDIR, INCLUDE, EXCLUDE, MATCHED flags
1581
 *
unknown's avatar
unknown committed
1582 1583
 */

1584
static int InList(struct link *linkp, const char *cp)
unknown's avatar
unknown committed
1585
{
1586
  int result;
unknown's avatar
unknown committed
1587

1588
  for (result=MATCHED; linkp != NULL; linkp= linkp->next_link)
unknown's avatar
unknown committed
1589
  {
1590
    if (!fnmatch(linkp->str, cp, 0))
1591
      return linkp->flags;
1592
    if (!(linkp->flags & EXCLUDE))
1593 1594 1595
      result=NOT_MATCHED;
    if (linkp->flags & SUBDIR)
      result|=SUBDIR;
unknown's avatar
unknown committed
1596
  }
unknown's avatar
unknown committed
1597
  return result;
unknown's avatar
unknown committed
1598 1599
}

1600 1601 1602 1603 1604 1605 1606
/*
 *  FUNCTION
 *
 *      ListFlags    returns aggregated list flags (ORed over all elements)
 *
 */

unknown's avatar
unknown committed
1607
static uint ListFlags(struct link *linkp)
1608
{
unknown's avatar
unknown committed
1609
  uint f;
1610 1611 1612 1613
  for (f=0; linkp != NULL; linkp= linkp->next_link)
    f|= linkp->flags;
  return f;
}
unknown's avatar
unknown committed
1614 1615 1616 1617

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1618
 *      PushState    push current settings onto stack and set up new one
unknown's avatar
unknown committed
1619 1620 1621
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1622
 *      static VOID PushState()
unknown's avatar
unknown committed
1623 1624 1625
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1626 1627
 *      Pushes the current settings on the settings stack, and creates
 *      a new settings. The new settings is NOT initialized
unknown's avatar
unknown committed
1628
 *
unknown's avatar
unknown committed
1629 1630 1631
 *      The settings stack is a linked list of settings, with the new
 *      settings added at the head.  This allows the stack to grow
 *      to the limits of memory if necessary.
unknown's avatar
unknown committed
1632 1633 1634
 *
 */

unknown's avatar
unknown committed
1635
static void PushState(CODE_STATE *cs)
unknown's avatar
unknown committed
1636
{
unknown's avatar
unknown committed
1637
  struct settings *new_malloc;
unknown's avatar
unknown committed
1638

unknown's avatar
unknown committed
1639 1640 1641 1642
  new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings));
  new_malloc->next= cs->stack;
  new_malloc->out_file= NULL;
  cs->stack= new_malloc;
unknown's avatar
unknown committed
1643 1644
}

1645 1646 1647 1648 1649 1650 1651 1652 1653
/*
 *  FUNCTION
 *
 *	FreeState    Free memory associated with a struct state.
 *
 *  SYNOPSIS
 *
 *	static void FreeState (state)
 *	struct state *state;
1654
 *      int free_state;
1655 1656 1657 1658
 *
 *  DESCRIPTION
 *
 *	Deallocates the memory allocated for various information in a
1659
 *	state. If free_state is set, also free 'state'
1660 1661
 *
 */
1662
static void FreeState(CODE_STATE *cs, struct settings *state, int free_state)
1663
{
1664 1665 1666 1667 1668 1669 1670 1671
  if (!is_shared(state, keywords))
    FreeList(state->keywords);
  if (!is_shared(state, functions))
    FreeList(state->functions);
  if (!is_shared(state, processes))
    FreeList(state->processes);
  if (!is_shared(state, p_functions))
    FreeList(state->p_functions);
1672 1673
  if (!is_shared(state, out_file) &&
      state->out_file != stderr && state->out_file != stdout)
1674
    DBUGCloseFile(cs, state->out_file);
1675
  (void) fflush(cs->stack->out_file);
1676 1677
  if (state->prof_file &&
      state->prof_file != stderr && state->prof_file != stdout)
1678
    DBUGCloseFile(cs, state->prof_file);
1679
  if (free_state)
1680
    free((void*) state);
1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700
}


/*
 *  FUNCTION
 *
 *	_db_end_    End debugging, freeing state stack memory.
 *
 *  SYNOPSIS
 *
 *	static VOID _db_end_ ()
 *
 *  DESCRIPTION
 *
 *	Ends debugging, de-allocating the memory allocated to the
 *	state stack.
 *
 *	To be called at the very end of the program.
 *
 */
1701
void _db_end_()
1702
{
unknown's avatar
unknown committed
1703
  struct settings *discard;
1704
  static struct settings tmp;
1705 1706 1707 1708 1709 1710
  CODE_STATE *cs;
  /*
    Set _dbug_on_ to be able to do full reset even when DEBUGGER_OFF was
    called after dbug was initialized
  */
  _dbug_on_= 1;
unknown's avatar
unknown committed
1711 1712
  get_code_state_or_return;

1713 1714 1715
  while ((discard= cs->stack))
  {
    if (discard == &init_settings)
unknown's avatar
unknown committed
1716 1717
      break;
    cs->stack= discard->next;
1718
    FreeState(cs, discard, 1);
1719
  }
1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735
  tmp= init_settings;

  /* Use mutex lock to make it less likely anyone access out_file */
  pthread_mutex_lock(&THR_LOCK_dbug);
  init_settings.flags=    OPEN_APPEND;
  init_settings.out_file= stderr;
  init_settings.prof_file= stderr;
  init_settings.maxdepth= 0;
  init_settings.delay= 0;
  init_settings.sub_level= 0;
  init_settings.functions= 0;
  init_settings.p_functions= 0;
  init_settings.keywords= 0;
  init_settings.processes= 0;
  pthread_mutex_unlock(&THR_LOCK_dbug);
  FreeState(cs, &tmp, 0);
1736 1737
}

unknown's avatar
unknown committed
1738 1739 1740 1741

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1742
 *      DoTrace    check to see if tracing is current enabled
unknown's avatar
unknown committed
1743 1744 1745
 *
 *  DESCRIPTION
 *
1746 1747 1748
 *      Checks to see if dbug in this function is enabled based on
 *      whether the maximum trace depth has been reached, the current
 *      function is selected, and the current process is selected.
unknown's avatar
unknown committed
1749 1750 1751
 *
 */

1752
static int DoTrace(CODE_STATE *cs)
unknown's avatar
unknown committed
1753
{
1754 1755 1756 1757
  if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) &&
      InList(cs->stack->processes, cs->process) & (MATCHED|INCLUDE))
    switch(InList(cs->stack->functions, cs->func)) {
    case INCLUDE|SUBDIR:  return ENABLE_TRACE;
1758
    case INCLUDE:         return DO_TRACE;
1759 1760
    case MATCHED|SUBDIR:
    case NOT_MATCHED|SUBDIR:
1761
    case MATCHED:         return framep_trace_flag(cs, cs->framep) ?
1762 1763 1764 1765 1766 1767
                                           DO_TRACE : DONT_TRACE;
    case EXCLUDE:
    case NOT_MATCHED:     return DONT_TRACE;
    case EXCLUDE|SUBDIR:  return DISABLE_TRACE;
    }
  return DONT_TRACE;
unknown's avatar
unknown committed
1768 1769 1770 1771 1772 1773
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1774
 *      DoProfile    check to see if profiling is current enabled
unknown's avatar
unknown committed
1775 1776 1777
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1778
 *      static BOOLEAN DoProfile()
unknown's avatar
unknown committed
1779 1780 1781
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1782 1783 1784 1785 1786
 *      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.
unknown's avatar
unknown committed
1787 1788 1789 1790
 *
 */

#ifndef THREAD
unknown's avatar
unknown committed
1791
static BOOLEAN DoProfile(CODE_STATE *cs)
unknown's avatar
unknown committed
1792
{
unknown's avatar
unknown committed
1793 1794
  return PROFILING &&
         cs->level <= cs->stack->maxdepth &&
1795 1796
         InList(cs->stack->p_functions, cs->func) & (INCLUDE|MATCHED) &&
         InList(cs->stack->processes, cs->process) & (INCLUDE|MATCHED);
unknown's avatar
unknown committed
1797 1798 1799
}
#endif

unknown's avatar
unknown committed
1800 1801
FILE *_db_fp_(void)
{
1802
  CODE_STATE *cs;
unknown's avatar
unknown committed
1803 1804 1805 1806
  get_code_state_or_return NULL;
  return cs->stack->out_file;
}

unknown's avatar
unknown committed
1807 1808 1809
/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1810
 *      _db_keyword_    test keyword for member of keyword list
unknown's avatar
unknown committed
1811 1812 1813
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1814
 *      Test a keyword to determine if it is in the currently active
1815
 *      keyword list.  If strict=0, a keyword is accepted
unknown's avatar
unknown committed
1816 1817 1818 1819 1820 1821
 *      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.
unknown's avatar
unknown committed
1822
 *
unknown's avatar
unknown committed
1823
 *      Returns TRUE if keyword accepted, FALSE otherwise.
unknown's avatar
unknown committed
1824 1825 1826
 *
 */

1827
BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict)
unknown's avatar
unknown committed
1828
{
1829
  get_code_state_if_not_set_or_return FALSE;
1830
  strict=strict ? INCLUDE : INCLUDE|MATCHED;
unknown's avatar
unknown committed
1831

1832
  return DEBUGGING && DoTrace(cs) & DO_TRACE &&
1833
         InList(cs->stack->keywords, keyword) & strict;
unknown's avatar
unknown committed
1834 1835 1836 1837 1838
}

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1839
 *      Indent    indent a line to the given indentation level
unknown's avatar
unknown committed
1840 1841 1842
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1843 1844
 *      static VOID Indent(indent)
 *      int indent;
unknown's avatar
unknown committed
1845 1846 1847
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1848 1849 1850
 *      Indent a line to the given level.  Note that this is
 *      a simple minded but portable implementation.
 *      There are better ways.
unknown's avatar
unknown committed
1851
 *
unknown's avatar
unknown committed
1852 1853
 *      Also, the indent must be scaled by the compile time option
 *      of character positions per nesting level.
unknown's avatar
unknown committed
1854 1855 1856
 *
 */

unknown's avatar
unknown committed
1857
static void Indent(CODE_STATE *cs, int indent)
unknown's avatar
unknown committed
1858 1859 1860
{
  REGISTER int count;

unknown's avatar
unknown committed
1861 1862
  indent= max(indent-1-cs->stack->sub_level,0)*INDENT;
  for (count= 0; count < indent ; count++)
unknown's avatar
unknown committed
1863 1864
  {
    if ((count % INDENT) == 0)
unknown's avatar
unknown committed
1865
      fputc('|',cs->stack->out_file);
unknown's avatar
unknown committed
1866
    else
unknown's avatar
unknown committed
1867
      fputc(' ',cs->stack->out_file);
unknown's avatar
unknown committed
1868 1869 1870 1871 1872 1873 1874
  }
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1875
 *      FreeList    free all memory associated with a linked list
unknown's avatar
unknown committed
1876 1877 1878
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1879 1880
 *      static VOID FreeList(linkp)
 *      struct link *linkp;
unknown's avatar
unknown committed
1881 1882 1883
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1884 1885
 *      Given pointer to the head of a linked list, frees all
 *      memory held by the list and the members of the list.
unknown's avatar
unknown committed
1886 1887 1888
 *
 */

unknown's avatar
unknown committed
1889
static void FreeList(struct link *linkp)
unknown's avatar
unknown committed
1890 1891 1892
{
  REGISTER struct link *old;

unknown's avatar
unknown committed
1893 1894 1895 1896
  while (linkp != NULL)
  {
    old= linkp;
    linkp= linkp->next_link;
1897
    free((void*) old);
unknown's avatar
unknown committed
1898 1899 1900 1901 1902 1903 1904
  }
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1905
 *      DoPrefix    print debugger line prefix prior to indentation
unknown's avatar
unknown committed
1906 1907 1908
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1909 1910
 *      static VOID DoPrefix(_line_)
 *      int _line_;
unknown's avatar
unknown committed
1911 1912 1913
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1914 1915 1916 1917
 *      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.
unknown's avatar
unknown committed
1918 1919 1920
 *
 */

unknown's avatar
unknown committed
1921
static void DoPrefix(CODE_STATE *cs, uint _line_)
unknown's avatar
unknown committed
1922
{
unknown's avatar
unknown committed
1923 1924 1925
  cs->lineno++;
  if (cs->stack->flags & PID_ON)
  {
unknown's avatar
unknown committed
1926
#ifdef THREAD
unknown's avatar
unknown committed
1927
    (void) fprintf(cs->stack->out_file, "%-7s: ", my_thread_name());
unknown's avatar
unknown committed
1928
#else
unknown's avatar
unknown committed
1929
    (void) fprintf(cs->stack->out_file, "%5d: ", (int) getpid());
unknown's avatar
unknown committed
1930 1931
#endif
  }
unknown's avatar
unknown committed
1932 1933 1934 1935
  if (cs->stack->flags & NUMBER_ON)
    (void) fprintf(cs->stack->out_file, "%5d: ", cs->lineno);
  if (cs->stack->flags & TIMESTAMP_ON)
  {
unknown's avatar
unknown committed
1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946
#ifdef __WIN__
    /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
       in system ticks, 10 ms intervals. See my_getsystime.c for high res */
    SYSTEMTIME loc_t;
    GetLocalTime(&loc_t);
    (void) fprintf (cs->stack->out_file,
                    /* "%04d-%02d-%02d " */
                    "%02d:%02d:%02d.%06d ",
                    /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
                    loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
#else
unknown's avatar
unknown committed
1947 1948 1949 1950
    struct timeval tv;
    struct tm *tm_p;
    if (gettimeofday(&tv, NULL) != -1)
    {
unknown's avatar
unknown committed
1951
      if ((tm_p= localtime((const time_t *)&tv.tv_sec)))
unknown's avatar
unknown committed
1952 1953 1954 1955 1956 1957 1958 1959 1960
      {
        (void) fprintf (cs->stack->out_file,
                        /* "%04d-%02d-%02d " */
                        "%02d:%02d:%02d.%06d ",
                        /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
                        tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
                        (int) (tv.tv_usec));
      }
    }
unknown's avatar
unknown committed
1961
#endif
unknown's avatar
unknown committed
1962
  }
unknown's avatar
unknown committed
1963 1964 1965 1966 1967 1968 1969 1970
  if (cs->stack->flags & PROCESS_ON)
    (void) fprintf(cs->stack->out_file, "%s: ", cs->process);
  if (cs->stack->flags & FILE_ON)
    (void) fprintf(cs->stack->out_file, "%14s: ", BaseName(cs->file));
  if (cs->stack->flags & LINE_ON)
    (void) fprintf(cs->stack->out_file, "%5d: ", _line_);
  if (cs->stack->flags & DEPTH_ON)
    (void) fprintf(cs->stack->out_file, "%4d: ", cs->level);
unknown's avatar
unknown committed
1971 1972 1973 1974 1975 1976
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1977
 *      DBUGOpenFile    open new output stream for debugger output
unknown's avatar
unknown committed
1978 1979 1980
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1981 1982
 *      static VOID DBUGOpenFile(name)
 *      char *name;
unknown's avatar
unknown committed
1983 1984 1985
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1986 1987
 *      Given name of a new file (or "-" for stdout) opens the file
 *      and sets the output stream to the new file.
unknown's avatar
unknown committed
1988 1989 1990
 *
 */

unknown's avatar
unknown committed
1991 1992
static void DBUGOpenFile(CODE_STATE *cs,
                         const char *name,const char *end,int append)
unknown's avatar
unknown committed
1993 1994 1995 1996 1997 1998
{
  REGISTER FILE *fp;
  REGISTER BOOLEAN newfile;

  if (name != NULL)
  {
unknown's avatar
unknown committed
1999 2000 2001 2002 2003 2004 2005 2006 2007 2008
    if (end)
    {
      int len=end-name;
      memcpy(cs->stack->name, name, len);
      cs->stack->name[len]=0;
    }
    else
    strmov(cs->stack->name,name);
    name=cs->stack->name;
    if (strcmp(name, "-") == 0)
unknown's avatar
unknown committed
2009
    {
unknown's avatar
unknown committed
2010 2011 2012
      cs->stack->out_file= stdout;
      cs->stack->flags |= FLUSH_ON_WRITE;
      cs->stack->name[0]=0;
unknown's avatar
unknown committed
2013 2014 2015
    }
    else
    {
2016
      if (!Writable(name))
unknown's avatar
unknown committed
2017
      {
unknown's avatar
unknown committed
2018 2019 2020
        (void) fprintf(stderr, ERR_OPEN, cs->process, name);
        perror("");
        fflush(stderr);
unknown's avatar
unknown committed
2021 2022 2023
      }
      else
      {
unknown's avatar
unknown committed
2024
        newfile= !EXISTS(name);
2025
        if (!(fp= fopen(name, append ? "a+" : "w")))
unknown's avatar
unknown committed
2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038
        {
          (void) fprintf(stderr, ERR_OPEN, cs->process, name);
          perror("");
          fflush(stderr);
        }
        else
        {
          cs->stack->out_file= fp;
          if (newfile)
          {
            ChangeOwner(cs, name);
          }
        }
unknown's avatar
unknown committed
2039 2040 2041 2042 2043 2044 2045 2046 2047
      }
    }
  }
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2048
 *      OpenProfile    open new output stream for profiler output
unknown's avatar
unknown committed
2049 2050 2051
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2052 2053
 *      static FILE *OpenProfile(name)
 *      char *name;
unknown's avatar
unknown committed
2054 2055 2056
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068
 *      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].
unknown's avatar
unknown committed
2069 2070 2071
 */

#ifndef THREAD
unknown's avatar
unknown committed
2072
static FILE *OpenProfile(CODE_STATE *cs, const char *name)
unknown's avatar
unknown committed
2073 2074 2075 2076 2077
{
  REGISTER FILE *fp;
  REGISTER BOOLEAN newfile;

  fp=0;
unknown's avatar
unknown committed
2078
  if (!Writable(name))
unknown's avatar
unknown committed
2079
  {
unknown's avatar
unknown committed
2080 2081 2082
    (void) fprintf(cs->stack->out_file, ERR_OPEN, cs->process, name);
    perror("");
    (void) Delay(cs->stack->delay);
unknown's avatar
unknown committed
2083 2084 2085
  }
  else
  {
unknown's avatar
unknown committed
2086 2087
    newfile= !EXISTS(name);
    if (!(fp= fopen(name, "w")))
unknown's avatar
unknown committed
2088
    {
unknown's avatar
unknown committed
2089 2090
      (void) fprintf(cs->stack->out_file, ERR_OPEN, cs->process, name);
      perror("");
unknown's avatar
unknown committed
2091 2092 2093
    }
    else
    {
unknown's avatar
unknown committed
2094
      cs->stack->prof_file= fp;
unknown's avatar
unknown committed
2095 2096
      if (newfile)
      {
unknown's avatar
unknown committed
2097
        ChangeOwner(cs, name);
unknown's avatar
unknown committed
2098 2099 2100 2101 2102 2103 2104 2105 2106 2107
      }
    }
  }
  return fp;
}
#endif

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2108
 *      DBUGCloseFile    close the debug output stream
unknown's avatar
unknown committed
2109 2110 2111
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2112 2113
 *      static VOID DBUGCloseFile(fp)
 *      FILE *fp;
unknown's avatar
unknown committed
2114 2115 2116
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2117 2118
 *      Closes the debug output stream unless it is standard output
 *      or standard error.
unknown's avatar
unknown committed
2119 2120 2121
 *
 */

unknown's avatar
unknown committed
2122
static void DBUGCloseFile(CODE_STATE *cs, FILE *fp)
unknown's avatar
unknown committed
2123
{
unknown's avatar
unknown committed
2124 2125
  if (fp != stderr && fp != stdout && fclose(fp) == EOF)
  {
2126 2127
    if (!cs->locked)
      pthread_mutex_lock(&THR_LOCK_dbug);
unknown's avatar
unknown committed
2128 2129
    (void) fprintf(cs->stack->out_file, ERR_CLOSE, cs->process);
    perror("");
unknown's avatar
unknown committed
2130
    DbugFlush(cs);
unknown's avatar
unknown committed
2131 2132 2133 2134 2135 2136 2137
  }
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2138
 *      DbugExit    print error message and exit
unknown's avatar
unknown committed
2139 2140 2141
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2142 2143
 *      static VOID DbugExit(why)
 *      char *why;
unknown's avatar
unknown committed
2144 2145 2146
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2147 2148 2149 2150
 *      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.
unknown's avatar
unknown committed
2151 2152 2153
 *
 */

unknown's avatar
unknown committed
2154
static void DbugExit(const char *why)
unknown's avatar
unknown committed
2155
{
unknown's avatar
unknown committed
2156 2157 2158 2159
  CODE_STATE *cs=code_state();
  (void) fprintf(stderr, ERR_ABORT, cs ? cs->process : "(null)", why);
  (void) fflush(stderr);
  exit(1);
unknown's avatar
unknown committed
2160 2161 2162 2163 2164 2165
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2166
 *      DbugMalloc    allocate memory for debugger runtime support
unknown's avatar
unknown committed
2167 2168 2169
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2170 2171
 *      static long *DbugMalloc(size)
 *      int size;
unknown's avatar
unknown committed
2172 2173 2174
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2175 2176 2177 2178 2179 2180
 *      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 cs,
 *      and continue execution.
unknown's avatar
unknown committed
2181 2182 2183
 *
 */

unknown's avatar
unknown committed
2184
static char *DbugMalloc(size_t size)
unknown's avatar
unknown committed
2185
{
2186
  register char *new_malloc;
unknown's avatar
unknown committed
2187

2188
  if (!(new_malloc= (char*) malloc(size)))
unknown's avatar
unknown committed
2189 2190
    DbugExit("out of memory");
  return new_malloc;
unknown's avatar
unknown committed
2191 2192 2193 2194
}


/*
2195
 *     strtok lookalike - splits on ':', magically handles ::, :\ and :/
unknown's avatar
unknown committed
2196 2197
 */

unknown's avatar
unknown committed
2198
static const char *DbugStrTok(const char *s)
unknown's avatar
unknown committed
2199
{
2200 2201
  while (s[0] && (s[0] != ':' ||
                  (s[1] == '\\' || s[1] == '/' || (s[1] == ':' && s++))))
unknown's avatar
unknown committed
2202 2203
    s++;
  return s;
unknown's avatar
unknown committed
2204 2205 2206 2207 2208 2209
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2210
 *      BaseName    strip leading pathname components from name
unknown's avatar
unknown committed
2211 2212 2213
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2214 2215
 *      static char *BaseName(pathname)
 *      char *pathname;
unknown's avatar
unknown committed
2216 2217 2218
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2219 2220 2221
 *      Given pointer to a complete pathname, locates the base file
 *      name at the end of the pathname and returns a pointer to
 *      it.
unknown's avatar
unknown committed
2222 2223 2224
 *
 */

unknown's avatar
unknown committed
2225
static const char *BaseName(const char *pathname)
unknown's avatar
unknown committed
2226 2227 2228
{
  register const char *base;

unknown's avatar
unknown committed
2229
  base= strrchr(pathname, FN_LIBCHAR);
unknown's avatar
unknown committed
2230
  if (base++ == NullS)
unknown's avatar
unknown committed
2231 2232
    base= pathname;
  return base;
unknown's avatar
unknown committed
2233 2234 2235 2236 2237 2238
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2239
 *      Writable    test to see if a pathname is writable/creatable
unknown's avatar
unknown committed
2240 2241 2242
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2243 2244
 *      static BOOLEAN Writable(pathname)
 *      char *pathname;
unknown's avatar
unknown committed
2245 2246 2247
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2248 2249 2250 2251 2252
 *      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.
unknown's avatar
unknown committed
2253
 *
unknown's avatar
unknown committed
2254 2255
 *      Returns TRUE if the user would normally be allowed write or
 *      create access to the named file.  Returns FALSE otherwise.
unknown's avatar
unknown committed
2256 2257 2258 2259 2260 2261
 *
 */


#ifndef Writable

2262
static BOOLEAN Writable(const char *pathname)
unknown's avatar
unknown committed
2263 2264 2265 2266
{
  REGISTER BOOLEAN granted;
  REGISTER char *lastslash;

unknown's avatar
unknown committed
2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283
  granted= FALSE;
  if (EXISTS(pathname))
  {
    if (WRITABLE(pathname))
      granted= TRUE;
  }
  else
  {
    lastslash= strrchr(pathname, '/');
    if (lastslash != NULL)
      *lastslash= '\0';
    else
      pathname= ".";
    if (WRITABLE(pathname))
      granted= TRUE;
    if (lastslash != NULL)
      *lastslash= '/';
unknown's avatar
unknown committed
2284
  }
unknown's avatar
unknown committed
2285
  return granted;
unknown's avatar
unknown committed
2286 2287 2288 2289 2290 2291 2292
}
#endif


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2293
 *      ChangeOwner    change owner to real user for suid programs
unknown's avatar
unknown committed
2294 2295 2296
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2297
 *      static VOID ChangeOwner(pathname)
unknown's avatar
unknown committed
2298 2299 2300
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2301 2302 2303
 *      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.
unknown's avatar
unknown committed
2304
 *
unknown's avatar
unknown committed
2305 2306 2307 2308 2309
 *      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).
unknown's avatar
unknown committed
2310 2311 2312 2313
 *
 */

#ifndef ChangeOwner
unknown's avatar
unknown committed
2314
static void ChangeOwner(CODE_STATE *cs, char *pathname)
unknown's avatar
unknown committed
2315
{
unknown's avatar
unknown committed
2316
  if (chown(pathname, getuid(), getgid()) == -1)
unknown's avatar
unknown committed
2317
  {
unknown's avatar
unknown committed
2318 2319 2320
    (void) fprintf(stderr, ERR_CHOWN, cs->process, pathname);
    perror("");
    (void) fflush(stderr);
unknown's avatar
unknown committed
2321 2322 2323 2324 2325 2326 2327 2328
  }
}
#endif


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2329
 *      _db_setjmp_    save debugger environment
unknown's avatar
unknown committed
2330 2331 2332
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2333
 *      VOID _db_setjmp_()
unknown's avatar
unknown committed
2334 2335 2336
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2337 2338 2339
 *      Invoked as part of the user's DBUG_SETJMP macro to save
 *      the debugger environment in parallel with saving the user's
 *      environment.
unknown's avatar
unknown committed
2340 2341 2342 2343 2344
 *
 */

#ifdef HAVE_LONGJMP

unknown's avatar
unknown committed
2345
EXPORT void _db_setjmp_()
unknown's avatar
unknown committed
2346
{
2347
  CODE_STATE *cs;
unknown's avatar
unknown committed
2348
  get_code_state_or_return;
unknown's avatar
unknown committed
2349

unknown's avatar
unknown committed
2350 2351 2352
  cs->jmplevel= cs->level;
  cs->jmpfunc= cs->func;
  cs->jmpfile= cs->file;
unknown's avatar
unknown committed
2353 2354 2355 2356 2357
}

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2358
 *      _db_longjmp_    restore previously saved debugger environment
unknown's avatar
unknown committed
2359 2360 2361
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2362
 *      VOID _db_longjmp_()
unknown's avatar
unknown committed
2363 2364 2365
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2366 2367 2368
 *      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.
unknown's avatar
unknown committed
2369 2370 2371
 *
 */

unknown's avatar
unknown committed
2372
EXPORT void _db_longjmp_()
unknown's avatar
unknown committed
2373
{
2374
  CODE_STATE *cs;
unknown's avatar
unknown committed
2375 2376 2377 2378 2379 2380 2381
  get_code_state_or_return;

  cs->level= cs->jmplevel;
  if (cs->jmpfunc)
    cs->func= cs->jmpfunc;
  if (cs->jmpfile)
    cs->file= cs->jmpfile;
unknown's avatar
unknown committed
2382 2383 2384 2385 2386 2387
}
#endif

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2388
 *      perror    perror simulation for systems that don't have it
unknown's avatar
unknown committed
2389 2390 2391
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2392 2393
 *      static VOID perror(s)
 *      char *s;
unknown's avatar
unknown committed
2394 2395 2396
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2397 2398 2399 2400
 *      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.
unknown's avatar
unknown committed
2401
 *
unknown's avatar
unknown committed
2402 2403 2404
 *      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.
unknown's avatar
unknown committed
2405
 *
unknown's avatar
unknown committed
2406
 *      This version just complains about an "unknown system error".
unknown's avatar
unknown committed
2407 2408 2409 2410
 *
 */

#ifndef HAVE_PERROR
unknown's avatar
unknown committed
2411
static void perror(s)
unknown's avatar
unknown committed
2412 2413
char *s;
{
unknown's avatar
unknown committed
2414 2415 2416
  if (s && *s != '\0')
    (void) fprintf(stderr, "%s: ", s);
  (void) fprintf(stderr, "<unknown system error>\n");
unknown's avatar
unknown committed
2417 2418 2419 2420
}
#endif /* HAVE_PERROR */


unknown's avatar
unknown committed
2421 2422 2423
        /* flush dbug-stream, free mutex lock & wait delay */
        /* This is because some systems (MSDOS!!) dosn't flush fileheader */
        /* and dbug-file isn't readable after a system crash !! */
unknown's avatar
unknown committed
2424

unknown's avatar
unknown committed
2425
static void DbugFlush(CODE_STATE *cs)
unknown's avatar
unknown committed
2426
{
unknown's avatar
unknown committed
2427
  if (cs->stack->flags & FLUSH_ON_WRITE)
unknown's avatar
unknown committed
2428
  {
2429 2430 2431
    (void) fflush(cs->stack->out_file);
    if (cs->stack->delay)
      (void) Delay(cs->stack->delay);
unknown's avatar
unknown committed
2432
  }
unknown's avatar
unknown committed
2433
  if (!cs->locked)
unknown's avatar
unknown committed
2434
    pthread_mutex_unlock(&THR_LOCK_dbug);
unknown's avatar
unknown committed
2435
} /* DbugFlush */
unknown's avatar
unknown committed
2436 2437


unknown's avatar
unknown committed
2438 2439
/* For debugging */

unknown's avatar
unknown committed
2440
void _db_flush_()
unknown's avatar
unknown committed
2441 2442 2443 2444 2445 2446 2447
{
  CODE_STATE *cs;
  get_code_state_or_return;
  (void) fflush(cs->stack->out_file);
}


unknown's avatar
unknown committed
2448
void _db_lock_file_()
unknown's avatar
unknown committed
2449
{
2450
  CODE_STATE *cs;
unknown's avatar
unknown committed
2451
  get_code_state_or_return;
unknown's avatar
unknown committed
2452
  pthread_mutex_lock(&THR_LOCK_dbug);
unknown's avatar
unknown committed
2453
  cs->locked=1;
unknown's avatar
unknown committed
2454 2455
}

unknown's avatar
unknown committed
2456
void _db_unlock_file_()
unknown's avatar
unknown committed
2457
{
2458
  CODE_STATE *cs;
unknown's avatar
unknown committed
2459 2460
  get_code_state_or_return;
  cs->locked=0;
unknown's avatar
unknown committed
2461 2462 2463 2464 2465 2466 2467 2468
  pthread_mutex_unlock(&THR_LOCK_dbug);
}

/*
 * Here we need the definitions of the clock routine.  Add your
 * own for whatever system that you have.
 */

unknown's avatar
unknown committed
2469 2470
#ifndef THREAD
#if defined(HAVE_GETRUSAGE)
unknown's avatar
unknown committed
2471 2472 2473 2474

#include <sys/param.h>
#include <sys/resource.h>

unknown's avatar
unknown committed
2475
/* extern int getrusage(int, struct rusage *); */
unknown's avatar
unknown committed
2476 2477 2478 2479 2480 2481

/*
 * Returns the user time in milliseconds used by this process so
 * far.
 */

unknown's avatar
unknown committed
2482
static unsigned long Clock()
unknown's avatar
unknown committed
2483 2484 2485
{
    struct rusage ru;

unknown's avatar
unknown committed
2486 2487
    (void) getrusage(RUSAGE_SELF, &ru);
    return ru.ru_utime.tv_sec*1000 + ru.ru_utime.tv_usec/1000;
unknown's avatar
unknown committed
2488 2489
}

2490
#elif defined(MSDOS) || defined(__WIN__)
unknown's avatar
unknown committed
2491 2492 2493 2494 2495

static ulong Clock()
{
  return clock()*(1000/CLOCKS_PER_SEC);
}
unknown's avatar
unknown committed
2496
#elif defined(amiga)
unknown's avatar
unknown committed
2497

unknown's avatar
unknown committed
2498 2499 2500 2501
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;
unknown's avatar
unknown committed
2502 2503
};

unknown's avatar
unknown committed
2504
static int first_clock= TRUE;
unknown's avatar
unknown committed
2505 2506 2507
static struct DateStamp begin;
static struct DateStamp elapsed;

unknown's avatar
unknown committed
2508
static unsigned long Clock()
unknown's avatar
unknown committed
2509 2510
{
    register struct DateStamp *now;
unknown's avatar
unknown committed
2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527
    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));
unknown's avatar
unknown committed
2528
    }
unknown's avatar
unknown committed
2529
    return millisec;
unknown's avatar
unknown committed
2530 2531
}
#else
unknown's avatar
unknown committed
2532
static unsigned long Clock()
unknown's avatar
unknown committed
2533
{
unknown's avatar
unknown committed
2534
    return 0;
unknown's avatar
unknown committed
2535 2536
}
#endif /* RUSAGE */
unknown's avatar
unknown committed
2537
#endif /* THREADS */
unknown's avatar
unknown committed
2538 2539 2540 2541

#ifdef NO_VARARGS

/*
unknown's avatar
unknown committed
2542 2543
 *      Fake vfprintf for systems that don't support it.  If this
 *      doesn't work, you are probably SOL...
unknown's avatar
unknown committed
2544 2545
 */

unknown's avatar
unknown committed
2546
static int vfprintf(stream, format, ap)
unknown's avatar
unknown committed
2547 2548 2549 2550 2551 2552 2553
FILE *stream;
char *format;
va_list ap;
{
    int rtnval;
    ARGS_DCL;

unknown's avatar
unknown committed
2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565
    ARG0=  va_arg(ap, ARGS_TYPE);
    ARG1=  va_arg(ap, ARGS_TYPE);
    ARG2=  va_arg(ap, ARGS_TYPE);
    ARG3=  va_arg(ap, ARGS_TYPE);
    ARG4=  va_arg(ap, ARGS_TYPE);
    ARG5=  va_arg(ap, ARGS_TYPE);
    ARG6=  va_arg(ap, ARGS_TYPE);
    ARG7=  va_arg(ap, ARGS_TYPE);
    ARG8=  va_arg(ap, ARGS_TYPE);
    ARG9=  va_arg(ap, ARGS_TYPE);
    rtnval= fprintf(stream, format, ARGS_LIST);
    return rtnval;
unknown's avatar
unknown committed
2566 2567
}

unknown's avatar
unknown committed
2568
#endif  /* NO_VARARGS */
2569

2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583
#else

/*
 * Dummy function, workaround for MySQL bug#14420 related
 * build failure on a platform where linking with an empty
 * archive fails.
 *
 * This block can be removed as soon as a fix for bug#14420
 * is implemented.
 */
int i_am_a_dummy_function() {
       return 0;
}

2584
#endif