dbug.c 59.9 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
 *      Michael Widenius:
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
 *        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")
 *
 *        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.
 *        following this logic glob(7) wildcards are supported.)
unknown's avatar
unknown committed
76
 *
unknown's avatar
unknown committed
77 78
 */

79 80 81 82
/*
  We can't have SAFE_MUTEX defined here as this will cause recursion
  in pthread_mutex_lock
*/
83

84
#include <my_global.h>
85
#undef SAFE_MUTEX
unknown's avatar
unknown committed
86 87
#include <m_string.h>
#include <errno.h>
88

Sergei Golubchik's avatar
Sergei Golubchik committed
89 90
#ifndef DBUG_OFF

91 92 93 94 95 96
#ifdef HAVE_FNMATCH_H
#include <fnmatch.h>
#else
#define fnmatch(A,B,C) strcmp(A,B)
#endif

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

/*
unknown's avatar
unknown committed
102
 *            Manifest constants which may be "tuned" if desired.
unknown's avatar
unknown committed
103 104
 */

unknown's avatar
unknown committed
105 106 107
#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
108 109

/*
unknown's avatar
unknown committed
110 111 112
 *      The following flags are used to determine which
 *      capabilities the user has enabled with the settings
 *      push macro.
113 114 115
 *
 *      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
116 117
 */

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

#define TRACING (cs->stack->flags & TRACE_ON)
#define DEBUGGING (cs->stack->flags & DEBUG_ON)
unknown's avatar
unknown committed
133 134

/*
unknown's avatar
unknown committed
135
 *      Typedefs to make things more obvious.
unknown's avatar
unknown committed
136 137
 */

138
#define BOOLEAN my_bool
unknown's avatar
unknown committed
139 140

/*
unknown's avatar
unknown committed
141
 *      Externally supplied functions.
unknown's avatar
unknown committed
142 143 144
 */

#ifndef HAVE_PERROR
Sergei Golubchik's avatar
Sergei Golubchik committed
145 146 147 148 149 150
static void perror(char *s)
{
  if (s && *s != '\0')
    (void) fprintf(stderr, "%s: ", s);
  (void) fprintf(stderr, "<unknown system error>\n");
}
unknown's avatar
unknown committed
151 152 153
#endif

/*
unknown's avatar
unknown committed
154 155 156
 *      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
157 158 159 160
 */

struct link {
    struct link *next_link;   /* Pointer to the next link */
161 162
    char   flags;
    char   str[1];            /* Pointer to link's contents */
unknown's avatar
unknown committed
163 164
};

165 166 167 168 169 170 171 172
/* 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

173 174 175 176 177 178 179 180 181 182 183 184 185 186
/*
 * Debugging settings can be shared between threads.
 * But FILE* streams cannot normally be shared - what if
 * one thread closes a stream, while another thread still uses it?
 * As a workaround, we have shared FILE pointers with reference counters
 */
typedef struct {
  FILE *file;
  uint used;
} sFILE;

sFILE shared_stdout = { 0, 1 << 30 }, *sstdout = &shared_stdout;
sFILE shared_stderr = { 0, 1 << 30 }, *sstderr = &shared_stderr;

unknown's avatar
unknown committed
187
/*
unknown's avatar
unknown committed
188 189 190 191 192 193 194
 *      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
195 196
 */

unknown's avatar
unknown committed
197
struct settings {
198 199 200 201
  uint flags;                   /* Current settings flags               */
  uint maxdepth;                /* Current maximum trace depth          */
  uint delay;                   /* Delay after each output line         */
  uint sub_level;               /* Sub this from code_state->level      */
202
  sFILE *out_file;              /* Current output stream                */
203 204 205 206 207
  char name[FN_REFLEN];         /* Name of output file                  */
  struct link *functions;       /* List of 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
208 209
};

unknown's avatar
unknown committed
210
#define is_shared(S, V) ((S)->next && (S)->next->V == (S)->V)
unknown's avatar
unknown committed
211 212

/*
unknown's avatar
unknown committed
213
 *      Local variables not seen by user.
unknown's avatar
unknown committed
214 215 216
 */


unknown's avatar
unknown committed
217 218
static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */
static struct settings init_settings;
219
static const char *db_process= 0;/* Pointer to process name; argv[0] */
220
my_bool _dbug_on_= TRUE;	 /* FALSE if no debugging at all */
unknown's avatar
unknown committed
221

unknown's avatar
unknown committed
222 223
typedef struct _db_code_state_ {
  const char *process;          /* Pointer to process name; usually argv[0] */
224 225 226 227 228 229 230 231 232
  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      */
  uint level;                   /* Current function nesting level           */
  int jmplevel;                 /* Remember nesting level at setjmp()       */
unknown's avatar
unknown committed
233 234

/*
unknown's avatar
unknown committed
235 236 237 238
 *      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
239
 *
unknown's avatar
unknown committed
240 241
 *      These variables are currently used only by _db_pargs_() and
 *      _db_doprnt_().
unknown's avatar
unknown committed
242 243
 */

unknown's avatar
unknown committed
244 245 246
  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
247 248
} CODE_STATE;

unknown's avatar
unknown committed
249 250 251 252
/*
  The test below is so we could call functions with DBUG_ENTER before
  my_thread_init().
*/
253 254
#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
255 256

        /* Handling lists */
257 258 259
#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
260
static struct link *ListCopy(struct link *);
Monty's avatar
Monty committed
261
static int InList(struct link *linkp,const char *cp,int exact_match);
262
static uint ListFlags(struct link *linkp);
unknown's avatar
unknown committed
263 264 265 266
static void FreeList(struct link *linkp);

        /* OpenClose debug output stream */
static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int);
267
static void DBUGCloseFile(CODE_STATE *cs, sFILE *new_value);
unknown's avatar
unknown committed
268 269
        /* Push current debug settings */
static void PushState(CODE_STATE *cs);
270
	/* Free memory associated with debug state. */
271
static void FreeState (CODE_STATE *cs, int free_state);
unknown's avatar
unknown committed
272
        /* Test for tracing enabled */
273
static int DoTrace(CODE_STATE *cs);
274 275 276 277 278
static int default_my_dbug_sanity(void);

int (*dbug_sanity)(void)= default_my_dbug_sanity;


279 280 281 282 283 284 285 286
/*
  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
287 288

        /* Test to see if file is writable */
289
#if defined(HAVE_ACCESS)
290
static BOOLEAN Writable(const char *pathname);
unknown's avatar
unknown committed
291
#endif
unknown's avatar
unknown committed
292 293 294

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

295
static char *DbugMalloc(size_t size);
unknown's avatar
unknown committed
296 297
static const char *BaseName(const char *pathname);
static void Indent(CODE_STATE *cs, int indent);
298
static void DbugFlush(CODE_STATE *);
unknown's avatar
unknown committed
299
static void DbugExit(const char *why);
unknown's avatar
unknown committed
300
static const char *DbugStrTok(const char *s);
301
static void DbugVfprintf(FILE *stream, const char* format, va_list args);
unknown's avatar
unknown committed
302

unknown's avatar
unknown committed
303
/*
unknown's avatar
unknown committed
304
 *      Miscellaneous printf format strings.
unknown's avatar
unknown committed
305 306
 */

307
#define ERR_MISSING_RETURN "missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
unknown's avatar
unknown committed
308 309 310 311 312
#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"

/*
unknown's avatar
unknown committed
313
 *      Macros and defines for testing file accessibility under UNIX and MSDOS.
unknown's avatar
unknown committed
314 315
 */

unknown's avatar
unknown committed
316
#undef EXISTS
317
#if !defined(HAVE_ACCESS)
luz.paz's avatar
luz.paz committed
318
#define EXISTS(pathname) (FALSE)        /* Assume no existence */
unknown's avatar
unknown committed
319 320
#define Writable(name) (TRUE)
#else
unknown's avatar
unknown committed
321 322
#define EXISTS(pathname)         (access(pathname, F_OK) == 0)
#define WRITABLE(pathname)       (access(pathname, W_OK) == 0)
unknown's avatar
unknown committed
323 324 325 326 327 328 329
#endif

/*
** Macros to allow dbugging with threads
*/

#include <my_pthread.h>
330
static pthread_mutex_t THR_LOCK_dbug;
unknown's avatar
unknown committed
331 332 333

static CODE_STATE *code_state(void)
{
334 335 336 337 338 339 340 341
  CODE_STATE *cs, **cs_ptr;

  /*
    _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
342 343 344

  if (!init_done)
  {
345
    init_done=TRUE;
346 347
    sstdout->file= stdout;
    sstderr->file= stderr;
Sergei Golubchik's avatar
Sergei Golubchik committed
348
    pthread_mutex_init(&THR_LOCK_dbug, NULL);
unknown's avatar
unknown committed
349
    bzero(&init_settings, sizeof(init_settings));
350
    init_settings.out_file= sstderr;
unknown's avatar
unknown committed
351 352 353
    init_settings.flags=OPEN_APPEND;
  }

354 355 356
  if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug()))
    return 0;                                   /* Thread not initialised */
  if (!(cs= *cs_ptr))
unknown's avatar
unknown committed
357
  {
358 359 360
    cs=(CODE_STATE*) DbugMalloc(sizeof(*cs));
    bzero((uchar*) cs,sizeof(*cs));
    cs->process= db_process ? db_process : "dbug";
361 362
    cs->func= "?func";
    cs->file= "?file";
363 364
    cs->stack=&init_settings;
    *cs_ptr= cs;
unknown's avatar
unknown committed
365
  }
unknown's avatar
unknown committed
366
  return cs;
unknown's avatar
unknown committed
367 368
}

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
void
dbug_swap_code_state(void **code_state_store)
{
  CODE_STATE *cs, **cs_ptr;

  if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug()))
    return;
  cs= *cs_ptr;
  *cs_ptr= *code_state_store;
  *code_state_store= cs;
}

void dbug_free_code_state(void **code_state_store)
{
  if (*code_state_store)
  {
    free(*code_state_store);
    *code_state_store= NULL;
  }
}

unknown's avatar
unknown committed
390 391 392 393 394 395 396 397 398 399
/*
 *      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
400 401 402 403

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
404
 *      _db_process_       give the name to the current process/thread
unknown's avatar
unknown committed
405 406 407
 *
 *  SYNOPSIS
 *
408
 *      VOID _process_(name)
unknown's avatar
unknown committed
409
 *      char *name;
unknown's avatar
unknown committed
410
 *
unknown's avatar
unknown committed
411 412 413 414
 */

void _db_process_(const char *name)
{
415
  CODE_STATE *cs;
unknown's avatar
unknown committed
416 417 418

  if (!db_process)
    db_process= name;
419

unknown's avatar
unknown committed
420 421 422 423 424 425
  get_code_state_or_return;
  cs->process= name;
}

/*
 *  FUNCTION
unknown's avatar
unknown committed
426
 *
427
 *      DbugParse  parse control string and set current debugger settings
unknown's avatar
unknown committed
428
 *
unknown's avatar
unknown committed
429
 *  DESCRIPTION
unknown's avatar
unknown committed
430
 *
unknown's avatar
unknown committed
431 432 433
 *      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
434
 *
unknown's avatar
unknown committed
435 436
 *      The debug control string is a sequence of colon separated fields
 *      as follows:
unknown's avatar
unknown committed
437
 *
unknown's avatar
unknown committed
438
 *              [+]<field_1>:<field_2>:...:<field_N>
unknown's avatar
unknown committed
439
 *
unknown's avatar
unknown committed
440 441
 *      Each field consists of a mandatory flag character followed by
 *      an optional "," and comma separated list of modifiers:
unknown's avatar
unknown committed
442
 *
unknown's avatar
unknown committed
443
 *              [sign]flag[,modifier,modifier,...,modifier]
unknown's avatar
unknown committed
444
 *
unknown's avatar
unknown committed
445
 *      See the manual for the list of supported signs, flags, and modifiers
unknown's avatar
unknown committed
446
 *
unknown's avatar
unknown committed
447
 *      For convenience, any leading "-#" is stripped off.
unknown's avatar
unknown committed
448
 *
449 450 451
 *  RETURN
 *      1 - a list of functions ("f" flag) was possibly changed
 *      0 - a list of functions was not changed
unknown's avatar
unknown committed
452 453
 */

Sergei Golubchik's avatar
Sergei Golubchik committed
454
static int DbugParse(CODE_STATE *cs, const char *control)
unknown's avatar
unknown committed
455
{
unknown's avatar
unknown committed
456
  const char *end;
457
  int rel, f_used=0;
458
  struct settings *stack;
459
  int org_cs_locked;
unknown's avatar
unknown committed
460

461
  stack= cs->stack;
unknown's avatar
unknown committed
462

463 464 465
  if (!(org_cs_locked= cs->locked))
  {
    pthread_mutex_lock(&THR_LOCK_dbug);
Monty's avatar
Monty committed
466
    cs->locked= 1;
467 468
  }

unknown's avatar
unknown committed
469 470
  if (control[0] == '-' && control[1] == '#')
    control+=2;
Sergei Golubchik's avatar
Sergei Golubchik committed
471

unknown's avatar
unknown committed
472
  rel= control[0] == '+' || control[0] == '-';
473
  if ((!rel || (!stack->out_file && !stack->next)))
unknown's avatar
unknown committed
474
  {
475
    FreeState(cs, 0);
476 477 478 479
    stack->flags= 0;
    stack->delay= 0;
    stack->maxdepth= 0;
    stack->sub_level= 0;
480
    stack->out_file= sstderr;
481 482 483
    stack->functions= NULL;
    stack->keywords= NULL;
    stack->processes= NULL;
unknown's avatar
unknown committed
484
  }
485
  else if (!stack->out_file)
unknown's avatar
unknown committed
486
  {
487 488 489 490 491
    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);
492 493
    stack->out_file= stack->next->out_file;
    stack->out_file->used++;
494
    if (stack->next == &init_settings)
unknown's avatar
unknown committed
495
    {
496
      /* never share with the global parent - it can change under your feet */
497 498 499
      stack->functions= ListCopy(init_settings.functions);
      stack->keywords= ListCopy(init_settings.keywords);
      stack->processes= ListCopy(init_settings.processes);
unknown's avatar
unknown committed
500 501 502
    }
    else
    {
503 504 505
      stack->functions= stack->next->functions;
      stack->keywords= stack->next->keywords;
      stack->processes= stack->next->processes;
unknown's avatar
unknown committed
506
    }
unknown's avatar
unknown committed
507 508
  }

unknown's avatar
unknown committed
509
  end= DbugStrTok(control);
510
  while (control < end)
unknown's avatar
unknown committed
511 512 513 514
  {
    int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0;
    if (sign) control++;
    c= *control++;
515 516
    if (*control == ',')
      control++;
unknown's avatar
unknown committed
517
    /* XXX when adding new cases here, don't forget _db_explain_ ! */
unknown's avatar
unknown committed
518
    switch (c) {
unknown's avatar
unknown committed
519
    case 'd':
unknown's avatar
unknown committed
520 521
      if (sign < 0 && control == end)
      {
522 523 524 525
        if (!is_shared(stack, keywords))
          FreeList(stack->keywords);
        stack->keywords=NULL;
        stack->flags &= ~DEBUG_ON;
unknown's avatar
unknown committed
526
        break;
unknown's avatar
unknown committed
527
      }
528 529
      if (rel && is_shared(stack, keywords))
        stack->keywords= ListCopy(stack->keywords);
unknown's avatar
unknown committed
530 531 532
      if (sign < 0)
      {
        if (DEBUGGING)
533
          stack->keywords= ListDel(stack->keywords, control, end);
534
        break;
unknown's avatar
unknown committed
535
      }
536 537
      stack->keywords= ListAdd(stack->keywords, control, end);
      stack->flags |= DEBUG_ON;
unknown's avatar
unknown committed
538 539
      break;
    case 'D':
540
      stack->delay= atoi(control);
unknown's avatar
unknown committed
541 542
      break;
    case 'f':
543
      f_used= 1;
unknown's avatar
unknown committed
544 545
      if (sign < 0 && control == end)
      {
546 547 548
        if (!is_shared(stack,functions))
          FreeList(stack->functions);
        stack->functions=NULL;
unknown's avatar
unknown committed
549
        break;
unknown's avatar
unknown committed
550
      }
551 552
      if (rel && is_shared(stack,functions))
        stack->functions= ListCopy(stack->functions);
unknown's avatar
unknown committed
553
      if (sign < 0)
554
        stack->functions= ListDel(stack->functions, control, end);
unknown's avatar
unknown committed
555
      else
556
        stack->functions= ListAdd(stack->functions, control, end);
unknown's avatar
unknown committed
557 558
      break;
    case 'F':
unknown's avatar
unknown committed
559
      if (sign < 0)
560
        stack->flags &= ~FILE_ON;
unknown's avatar
unknown committed
561
      else
562
        stack->flags |= FILE_ON;
unknown's avatar
unknown committed
563 564
      break;
    case 'i':
unknown's avatar
unknown committed
565
      if (sign < 0)
566
        stack->flags &= ~PID_ON;
unknown's avatar
unknown committed
567
      else
568
        stack->flags |= PID_ON;
unknown's avatar
unknown committed
569 570
      break;
    case 'L':
unknown's avatar
unknown committed
571
      if (sign < 0)
572
        stack->flags &= ~LINE_ON;
unknown's avatar
unknown committed
573
      else
574
        stack->flags |= LINE_ON;
unknown's avatar
unknown committed
575 576
      break;
    case 'n':
unknown's avatar
unknown committed
577
      if (sign < 0)
578
        stack->flags &= ~DEPTH_ON;
unknown's avatar
unknown committed
579
      else
580
        stack->flags |= DEPTH_ON;
unknown's avatar
unknown committed
581 582
      break;
    case 'N':
unknown's avatar
unknown committed
583
      if (sign < 0)
584
        stack->flags &= ~NUMBER_ON;
unknown's avatar
unknown committed
585
      else
586
        stack->flags |= NUMBER_ON;
unknown's avatar
unknown committed
587 588 589
      break;
    case 'A':
    case 'O':
590
      stack->flags |= FLUSH_ON_WRITE;
unknown's avatar
unknown committed
591
      /* fall through */
unknown's avatar
unknown committed
592 593
    case 'a':
    case 'o':
unknown's avatar
unknown committed
594 595
      if (sign < 0)
      {
596
        DBUGCloseFile(cs, sstderr);
597
        stack->flags &= ~FLUSH_ON_WRITE;
unknown's avatar
unknown committed
598
        break;
unknown's avatar
unknown committed
599
      }
unknown's avatar
unknown committed
600
      if (c == 'a' || c == 'A')
601
        stack->flags |= OPEN_APPEND;
unknown's avatar
unknown committed
602
      else
603
        stack->flags &= ~OPEN_APPEND;
unknown's avatar
unknown committed
604
      if (control != end)
605
        DBUGOpenFile(cs, control, end, stack->flags & OPEN_APPEND);
unknown's avatar
unknown committed
606 607
      else
        DBUGOpenFile(cs, "-",0,0);
unknown's avatar
unknown committed
608 609
      break;
    case 'p':
unknown's avatar
unknown committed
610 611
      if (sign < 0 && control == end)
      {
612 613 614
        if (!is_shared(stack,processes))
          FreeList(stack->processes);
        stack->processes=NULL;
unknown's avatar
unknown committed
615
        break;
unknown's avatar
unknown committed
616
      }
617 618
      if (rel && is_shared(stack, processes))
        stack->processes= ListCopy(stack->processes);
unknown's avatar
unknown committed
619
      if (sign < 0)
620
        stack->processes= ListDel(stack->processes, control, end);
unknown's avatar
unknown committed
621
      else
622
        stack->processes= ListAdd(stack->processes, control, end);
unknown's avatar
unknown committed
623 624
      break;
    case 'P':
unknown's avatar
unknown committed
625
      if (sign < 0)
626
        stack->flags &= ~PROCESS_ON;
unknown's avatar
unknown committed
627
      else
628
        stack->flags |= PROCESS_ON;
unknown's avatar
unknown committed
629 630
      break;
    case 'r':
631
      stack->sub_level= cs->level;
unknown's avatar
unknown committed
632 633
      break;
    case 't':
unknown's avatar
unknown committed
634 635 636
      if (sign < 0)
      {
        if (control != end)
637
          stack->maxdepth-= atoi(control);
unknown's avatar
unknown committed
638
        else
639
          stack->maxdepth= 0;
unknown's avatar
unknown committed
640 641 642 643
      }
      else
      {
        if (control != end)
644
          stack->maxdepth+= atoi(control);
unknown's avatar
unknown committed
645
        else
646
          stack->maxdepth= MAXDEPTH;
unknown's avatar
unknown committed
647
      }
648 649
      if (stack->maxdepth > 0)
        stack->flags |= TRACE_ON;
unknown's avatar
unknown committed
650
      else
651
        stack->flags &= ~TRACE_ON;
unknown's avatar
unknown committed
652 653 654
      break;
    case 'T':
      if (sign < 0)
655
        stack->flags &= ~TIMESTAMP_ON;
unknown's avatar
unknown committed
656
      else
657
        stack->flags |= TIMESTAMP_ON;
unknown's avatar
unknown committed
658
      break;
Sergei Golubchik's avatar
Sergei Golubchik committed
659 660 661 662 663 664
    case 'S':
      if (sign < 0)
        stack->flags &= ~SANITY_CHECK_ON;
      else
        stack->flags |= SANITY_CHECK_ON;
      break;
unknown's avatar
unknown committed
665
    }
unknown's avatar
unknown committed
666 667 668 669
    if (!*end)
      break;
    control=end+1;
    end= DbugStrTok(control);
unknown's avatar
unknown committed
670
  }
671 672 673
  if (!org_cs_locked)
  {
    cs->locked= 0;
Monty's avatar
Monty committed
674
    pthread_mutex_unlock(&THR_LOCK_dbug);
675
  }
676
  return !rel || f_used;
unknown's avatar
unknown committed
677
}
678
  
679 680 681 682 683
#define framep_trace_flag(cs, frp) (frp ?                                    \
                                     frp->level & TRACE_ON :                 \
                              (ListFlags(cs->stack->functions) & INCLUDE) ?  \
                                       0 : (uint)TRACE_ON)

Sergei Golubchik's avatar
Sergei Golubchik committed
684 685
static void FixTraceFlags_helper(CODE_STATE *cs, const char *func,
                                 struct _db_stack_frame_ *framep)
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
{
  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
  */
  switch(DoTrace(cs)) {
  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;

Sergei Golubchik's avatar
Sergei Golubchik committed
710
static void FixTraceFlags(uint old_fflags, CODE_STATE *cs)
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
{
  const char *func;
  uint new_fflags, traceon, level;
  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;
}
781

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
/*
 *  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. Pushes a new debug settings if the current is
 *      set to the initial debugger settings.
798
 *
799 800
 */

801
void _db_set_(const char *control)
802
{
803 804
  CODE_STATE *cs;
  uint old_fflags;
805
  get_code_state_or_return;
806
  old_fflags=fflags(cs);
807 808
  if (cs->stack == &init_settings)
    PushState(cs);
809 810
  if (DbugParse(cs, control))
    FixTraceFlags(old_fflags, cs);
811 812
}

813 814 815 816 817 818 819 820 821 822 823 824 825 826
/*
 *  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
827
 *      up a new debug settings
828 829 830 831 832
 *
 */

void _db_push_(const char *control)
{
833 834
  CODE_STATE *cs;
  uint old_fflags;
835
  get_code_state_or_return;
836
  old_fflags=fflags(cs);
837
  PushState(cs);
838 839 840 841
  if (DbugParse(cs, control))
    FixTraceFlags(old_fflags, cs);
}

Sergei Golubchik's avatar
Sergei Golubchik committed
842

843 844 845 846 847 848 849 850 851
/**
  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);
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
}

/*
 *  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)
{
871
  CODE_STATE tmp_cs;
872
  bzero((uchar*) &tmp_cs, sizeof(tmp_cs));
873
  tmp_cs.stack= &init_settings;
874
  tmp_cs.process= db_process ? db_process : "dbug";
875
  DbugParse(&tmp_cs, control);
876 877
}

unknown's avatar
unknown committed
878 879 880
/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
881
 *      _db_pop_    pop the debug stack
unknown's avatar
unknown committed
882 883 884
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
885 886 887 888 889 890 891
 *      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
892 893 894
 *
 */

unknown's avatar
unknown committed
895
void _db_pop_()
unknown's avatar
unknown committed
896
{
897 898
  uint old_fflags;
  CODE_STATE *cs;
unknown's avatar
unknown committed
899 900 901

  get_code_state_or_return;

902
  if (cs->stack != &init_settings)
unknown's avatar
unknown committed
903
  {
904
    old_fflags=fflags(cs);
905
    FreeState(cs, 1);
906
    FixTraceFlags(old_fflags, cs);
unknown's avatar
unknown committed
907 908 909
  }
}

unknown's avatar
unknown committed
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
/*
 *  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(',');                       \
928
        buf=strnmov(buf, (S), (uint) (end-buf)); \
unknown's avatar
unknown committed
929 930
        if (buf >= end) goto overflow;          \
      } while (0)
931
#define list_to_buf(l, f)  do {                 \
unknown's avatar
unknown committed
932 933 934
        struct link *listp=(l);                 \
        while (listp)                           \
        {                                       \
935 936 937 938 939 940
          if (listp->flags & (f))               \
          {                                     \
            str_to_buf(listp->str);             \
            if (listp->flags & SUBDIR)          \
              char_to_buf('/');                 \
          }                                     \
unknown's avatar
unknown committed
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979
          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))                             \
        {                                       \
980
          int f=ListFlags(val);                 \
unknown's avatar
unknown committed
981 982
          colon_to_buf;                         \
          char_to_buf((C));                     \
983 984 985 986 987 988 989 990 991
          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
992 993 994 995 996 997 998 999 1000 1001
        }                                       \
      } while (0)
#define op_bool_to_buf(C, cond) do {            \
        if ((cond))                             \
        {                                       \
          colon_to_buf;                         \
          char_to_buf((C));                     \
        }                                       \
      } while (0)

1002
int _db_explain_ (CODE_STATE *cs, char *buf, size_t len)
unknown's avatar
unknown committed
1003 1004 1005
{
  char *start=buf, *end=buf+len-4;

1006
  get_code_state_if_not_set_or_return *buf=0;
unknown's avatar
unknown committed
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018

  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_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')),
1019
    cs->stack->name, cs->stack->out_file != sstderr);
unknown's avatar
unknown committed
1020 1021 1022 1023
  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
1024
  op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON);
Sergei Golubchik's avatar
Sergei Golubchik committed
1025
  op_bool_to_buf('S', cs->stack->flags & SANITY_CHECK_ON);
unknown's avatar
unknown committed
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047

  *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
1048 1049 1050 1051

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1052 1053 1054 1055 1056 1057
 *      _db_explain_init_       explain initial debugger settings
 *
 *  DESCRIPTION
 *      see _db_explain_
 */

1058
int _db_explain_init_(char *buf, size_t len)
unknown's avatar
unknown committed
1059 1060
{
  CODE_STATE cs;
1061
  bzero((uchar*) &cs,sizeof(cs));
unknown's avatar
unknown committed
1062 1063 1064 1065 1066 1067 1068 1069
  cs.stack=&init_settings;
  return _db_explain_(&cs, buf, len);
}

/*
 *  FUNCTION
 *
 *      _db_enter_    process entry point to user function
unknown's avatar
unknown committed
1070 1071 1072
 *
 *  SYNOPSIS
 *
1073
 *      VOID _db_enter_(_func_, _file_, _line_, _stack_frame_)
unknown's avatar
unknown committed
1074 1075 1076
 *      char *_func_;           points to current function name
 *      char *_file_;           points to current file name
 *      int _line_;             called from source line number
1077
 *      struct _db_stack_frame_ allocated on the caller's stack
unknown's avatar
unknown committed
1078 1079 1080
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1081 1082 1083 1084 1085 1086 1087 1088 1089
 *      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
1090
 *
unknown's avatar
unknown committed
1091 1092
 *      Also prints a trace line if tracing is enabled and
 *      increments the current function nesting depth.
unknown's avatar
unknown committed
1093
 *
unknown's avatar
unknown committed
1094 1095 1096
 *      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
1097 1098 1099
 *
 */

unknown's avatar
unknown committed
1100
void _db_enter_(const char *_func_, const char *_file_,
1101
                uint _line_, struct _db_stack_frame_ *_stack_frame_)
unknown's avatar
unknown committed
1102
{
Monty's avatar
Monty committed
1103
  int save_errno, org_cs_locked;
1104 1105 1106 1107 1108 1109 1110 1111
  CODE_STATE *cs;
  if (!((cs=code_state())))
  {
    _stack_frame_->level= 0; /* Set to avoid valgrind warnings if dbug is enabled later */
    _stack_frame_->prev= 0;
    return;
  }
  save_errno= errno;
unknown's avatar
unknown committed
1112

1113
  _stack_frame_->line= -1;
1114 1115
  _stack_frame_->func= cs->func;
  _stack_frame_->file= cs->file;
unknown's avatar
unknown committed
1116 1117
  cs->func=  _func_;
  cs->file=  _file_;
1118 1119 1120
  _stack_frame_->prev= cs->framep;
  _stack_frame_->level= ++cs->level | framep_trace_flag(cs, cs->framep);
  cs->framep= _stack_frame_;
1121

1122 1123 1124 1125 1126 1127
  switch (DoTrace(cs)) {
  case ENABLE_TRACE:
    cs->framep->level|= TRACE_ON;
    if (!TRACING) break;
    /* fall through */
  case DO_TRACE:
1128
    if ((cs->stack->flags & SANITY_CHECK_ON) && (*dbug_sanity)())
Sergei Golubchik's avatar
Sergei Golubchik committed
1129
      cs->stack->flags &= ~SANITY_CHECK_ON;
1130 1131
    if (TRACING)
    {
Monty's avatar
Monty committed
1132 1133
      if (!(org_cs_locked= cs->locked))
      {
1134
        pthread_mutex_lock(&THR_LOCK_dbug);
Monty's avatar
Monty committed
1135 1136
        cs->locked= 1;
      }
1137 1138
      DoPrefix(cs, _line_);
      Indent(cs, cs->level);
1139
      (void) fprintf(cs->stack->out_file->file, ">%s\n", cs->func);
1140
      DbugFlush(cs);                       /* This does a unlock */
Monty's avatar
Monty committed
1141 1142 1143 1144 1145
      if (!org_cs_locked)
      {
        cs->locked= 0;
        pthread_mutex_unlock(&THR_LOCK_dbug);
      }
1146 1147 1148 1149 1150 1151 1152 1153
    }
    break;
  case DISABLE_TRACE:
    cs->framep->level&= ~TRACE_ON;
    /* fall through */
  case DONT_TRACE:
    break;
  }
unknown's avatar
unknown committed
1154
  errno=save_errno;
unknown's avatar
unknown committed
1155 1156 1157 1158 1159
}

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1160
 *      _db_return_    process exit from user function
unknown's avatar
unknown committed
1161 1162 1163
 *
 *  SYNOPSIS
 *
1164
 *      VOID _db_return_(_line_, _stack_frame_)
unknown's avatar
unknown committed
1165
 *      int _line_;             current source line number
1166
 *      struct _db_stack_frame_ allocated on the caller's stack
unknown's avatar
unknown committed
1167 1168 1169
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1170 1171 1172 1173
 *      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
1174 1175 1176
 *
 */

1177
void _db_return_(struct _db_stack_frame_ *_stack_frame_)
unknown's avatar
unknown committed
1178
{
unknown's avatar
unknown committed
1179
  int save_errno=errno;
1180 1181
  uint _slevel_= _stack_frame_->level & ~TRACE_ON;
  CODE_STATE *cs;
unknown's avatar
unknown committed
1182
  get_code_state_or_return;
unknown's avatar
unknown committed
1183

1184 1185 1186 1187
  if (_stack_frame_->line == 0)
    return;

  if (_stack_frame_->line == -1 || cs->framep != _stack_frame_)
unknown's avatar
unknown committed
1188
  {
1189 1190 1191
    char buf[512];
    my_snprintf(buf, sizeof(buf), ERR_MISSING_RETURN, cs->func);
    DbugExit(buf);
unknown's avatar
unknown committed
1192
  }
1193

1194 1195
  if (DoTrace(cs) & DO_TRACE)
  {
Monty's avatar
Monty committed
1196
    int org_cs_locked;
1197
    if ((cs->stack->flags & SANITY_CHECK_ON) && (*dbug_sanity)())
Sergei Golubchik's avatar
Sergei Golubchik committed
1198
      cs->stack->flags &= ~SANITY_CHECK_ON;
1199
    if (TRACING)
unknown's avatar
unknown committed
1200
    {
Monty's avatar
Monty committed
1201 1202
      if (!(org_cs_locked= cs->locked))
      {
1203
        pthread_mutex_lock(&THR_LOCK_dbug);
Monty's avatar
Monty committed
1204 1205
        cs->locked= 1;
      }
1206
      DoPrefix(cs, _stack_frame_->line);
1207
      Indent(cs, cs->level);
1208
      (void) fprintf(cs->stack->out_file->file, "<%s\n", cs->func);
1209
      DbugFlush(cs);
Monty's avatar
Monty committed
1210 1211 1212 1213 1214
      if (!org_cs_locked)
      {
        cs->locked= 0;
        pthread_mutex_unlock(&THR_LOCK_dbug);
      }
unknown's avatar
unknown committed
1215
    }
unknown's avatar
unknown committed
1216
  }
1217 1218 1219 1220 1221 1222 1223
  /*
    Check to not set level < 0. This can happen if DBUG was disabled when
    function was entered and enabled in function.
  */
  cs->level= _slevel_ != 0 ? _slevel_ - 1 : 0;
  cs->func= _stack_frame_->func;
  cs->file= _stack_frame_->file;
unknown's avatar
unknown committed
1224
  if (cs->framep != NULL)
1225
    cs->framep= cs->framep->prev;
unknown's avatar
unknown committed
1226
  errno=save_errno;
unknown's avatar
unknown committed
1227 1228 1229 1230 1231 1232
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1233
 *      _db_pargs_    log arguments for subsequent use by _db_doprnt_()
unknown's avatar
unknown committed
1234 1235 1236
 *
 *  SYNOPSIS
 *
1237
 *      int _db_pargs_(_line_, keyword)
unknown's avatar
unknown committed
1238 1239
 *      int _line_;
 *      char *keyword;
unknown's avatar
unknown committed
1240 1241 1242
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1243 1244 1245 1246
 *      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
1247 1248 1249
 *
 */

1250
int _db_pargs_(uint _line_, const char *keyword)
unknown's avatar
unknown committed
1251
{
1252
  CODE_STATE *cs;
1253
  get_code_state_or_return 0;
unknown's avatar
unknown committed
1254
  cs->u_line= _line_;
1255
  cs->u_keyword= keyword;
1256 1257

  return DEBUGGING && _db_keyword_(cs, cs->u_keyword, 0);
unknown's avatar
unknown committed
1258 1259 1260 1261 1262 1263
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1264
 *      _db_doprnt_    handle print of debug lines
unknown's avatar
unknown committed
1265 1266 1267
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1268 1269 1270
 *      VOID _db_doprnt_(format, va_alist)
 *      char *format;
 *      va_dcl;
unknown's avatar
unknown committed
1271 1272 1273
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1274 1275 1276 1277 1278
 *      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
1279
 *
unknown's avatar
unknown committed
1280 1281
 *      Note that the format string SHOULD NOT include a terminating
 *      newline, this is supplied automatically.
unknown's avatar
unknown committed
1282 1283 1284 1285 1286
 *
 */

#include <stdarg.h>

unknown's avatar
unknown committed
1287
void _db_doprnt_(const char *format,...)
unknown's avatar
unknown committed
1288 1289
{
  va_list args;
1290
  CODE_STATE *cs;
Monty's avatar
Monty committed
1291
  int save_errno, org_cs_locked;
1292

unknown's avatar
unknown committed
1293
  get_code_state_or_return;
unknown's avatar
unknown committed
1294 1295 1296

  va_start(args,format);

Monty's avatar
Monty committed
1297 1298
  if (!(org_cs_locked= cs->locked))
  {
1299
    pthread_mutex_lock(&THR_LOCK_dbug);
Monty's avatar
Monty committed
1300 1301
    cs->locked= 1;
  }
1302 1303 1304 1305 1306 1307 1308 1309 1310
  save_errno=errno;
  DoPrefix(cs, cs->u_line);
  if (TRACING)
    Indent(cs, cs->level + 1);
  else
    (void) fprintf(cs->stack->out_file->file, "%s: ", cs->func);
  (void) fprintf(cs->stack->out_file->file, "%s: ", cs->u_keyword);
  DbugVfprintf(cs->stack->out_file->file, format, args);
  DbugFlush(cs);
Monty's avatar
Monty committed
1311 1312 1313 1314 1315
  if (!org_cs_locked)
  {
    cs->locked= 0;
    pthread_mutex_unlock(&THR_LOCK_dbug);
  }
1316
  errno=save_errno;
1317

unknown's avatar
unknown committed
1318 1319 1320
  va_end(args);
}

1321
/*
1322
 * This function is intended as a
1323
 * vfprintf clone with consistent, platform independent output for 
1324 1325
 * problematic formats like %p, %zd and %lld.
 */
1326
static void DbugVfprintf(FILE *stream, const char* format, va_list args)
1327 1328
{
  char cvtbuf[1024];
1329
  (void) my_vsnprintf(cvtbuf, sizeof(cvtbuf), format, args);
1330 1331 1332
  (void) fprintf(stream, "%s\n", cvtbuf);
}

unknown's avatar
unknown committed
1333 1334 1335 1336

/*
 *  FUNCTION
 *
1337
 *            _db_dump_    dump a string in hex
unknown's avatar
unknown committed
1338 1339 1340
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1341 1342 1343 1344 1345
 *            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
1346 1347 1348
 *
 *  DESCRIPTION
 *  Dump N characters in a binary array.
1349
 *  Is used to examine corrupted memory or arrays.
unknown's avatar
unknown committed
1350 1351
 */

1352 1353
void _db_dump_(uint _line_, const char *keyword,
               const unsigned char *memory, size_t length)
unknown's avatar
unknown committed
1354
{
Monty's avatar
Monty committed
1355
  int pos, org_cs_locked;
1356
  CODE_STATE *cs;
unknown's avatar
unknown committed
1357 1358
  get_code_state_or_return;

Monty's avatar
Monty committed
1359 1360
  if (!(org_cs_locked= cs->locked))
  {
1361
    pthread_mutex_lock(&THR_LOCK_dbug);
Monty's avatar
Monty committed
1362 1363
    cs->locked= 1;
  }
1364
  if (_db_keyword_(cs, keyword, 0))
unknown's avatar
unknown committed
1365
  {
unknown's avatar
unknown committed
1366
    DoPrefix(cs, _line_);
unknown's avatar
unknown committed
1367 1368
    if (TRACING)
    {
unknown's avatar
unknown committed
1369
      Indent(cs, cs->level + 1);
1370
      pos= MY_MIN(MY_MAX(cs->level-cs->stack->sub_level,0)*INDENT,80);
unknown's avatar
unknown committed
1371 1372 1373
    }
    else
    {
1374
      fprintf(cs->stack->out_file->file, "%s: ", cs->func);
unknown's avatar
unknown committed
1375
    }
1376 1377
    (void) fprintf(cs->stack->out_file->file, "%s: Memory: %p  Bytes: (%ld)\n",
            keyword, memory, (long) length);
unknown's avatar
unknown committed
1378 1379 1380 1381 1382 1383 1384

    pos=0;
    while (length-- > 0)
    {
      uint tmp= *((unsigned char*) memory++);
      if ((pos+=3) >= 80)
      {
1385
        fputc('\n',cs->stack->out_file->file);
unknown's avatar
unknown committed
1386
        pos=3;
unknown's avatar
unknown committed
1387
      }
1388 1389 1390
      fputc(_dig_vec_upper[((tmp >> 4) & 15)], cs->stack->out_file->file);
      fputc(_dig_vec_upper[tmp & 15], cs->stack->out_file->file);
      fputc(' ',cs->stack->out_file->file);
unknown's avatar
unknown committed
1391
    }
1392
    (void) fputc('\n',cs->stack->out_file->file);
1393
    DbugFlush(cs);
unknown's avatar
unknown committed
1394
  }
Monty's avatar
Monty committed
1395 1396 1397
  if (!org_cs_locked)
  {
    cs->locked= 0;
1398
    pthread_mutex_unlock(&THR_LOCK_dbug);
Monty's avatar
Monty committed
1399
  }
unknown's avatar
unknown committed
1400 1401
}

unknown's avatar
unknown committed
1402 1403

/*
unknown's avatar
unknown committed
1404 1405
 *  FUNCTION
 *
1406
 *      ListAddDel    modify the list according to debug control string
unknown's avatar
unknown committed
1407 1408 1409 1410
 *
 *  DESCRIPTION
 *
 *      Given pointer to a comma separated list of strings in "cltp",
1411 1412
 *      parses the list, and modifies "listp", returning a pointer
 *      to the new list.
unknown's avatar
unknown committed
1413
 *
1414
 *      The mode of operation is defined by "todo" parameter.
unknown's avatar
unknown committed
1415
 *
1416 1417 1418 1419 1420
 *      If it is INCLUDE, elements (strings from "cltp") are added to the
 *      list, they will have INCLUDE flag set. If the list already contains
 *      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
1421
 *
1422 1423 1424
 *      If it is EXCLUDE, elements are added to the list with the EXCLUDE
 *      flag set. If the list already contains the string in question,
 *      it is removed, new element is not added.
unknown's avatar
unknown committed
1425 1426
 */

1427 1428
static struct link *ListAddDel(struct link *head, const char *ctlp,
                               const char *end, int todo)
unknown's avatar
unknown committed
1429
{
unknown's avatar
unknown committed
1430 1431
  const char *start;
  struct link **cur;
1432 1433
  size_t len;
  int subdir;
unknown's avatar
unknown committed
1434

1435 1436 1437
  ctlp--;
next:
  while (++ctlp < end)
unknown's avatar
unknown committed
1438 1439
  {
    start= ctlp;
1440
    subdir=0;
unknown's avatar
unknown committed
1441 1442
    while (ctlp < end && *ctlp != ',')
      ctlp++;
1443
    len= (int) (ctlp-start);
1444
    if (start[len-1] == '/')
unknown's avatar
unknown committed
1445
    {
1446 1447 1448 1449 1450 1451 1452
      len--;
      subdir=SUBDIR;
    }
    if (len == 0) continue;
    for (cur=&head; *cur; cur=&((*cur)->next_link))
    {
      if (!strncmp((*cur)->str, start, len))
unknown's avatar
unknown committed
1453
      {
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467
        if ((*cur)->flags & todo)  /* same action ? */
          (*cur)->flags|= subdir;  /* just merge the SUBDIR flag */
        else if (todo == EXCLUDE)
        {
          struct link *delme=*cur;
          *cur=(*cur)->next_link;
          free((void*) delme);
        }
        else
        {
          (*cur)->flags&=~(EXCLUDE & SUBDIR);
          (*cur)->flags|=INCLUDE | subdir;
        }
        goto next;
unknown's avatar
unknown committed
1468
      }
1469 1470 1471 1472 1473 1474
    }
    *cur= (struct link *) DbugMalloc(sizeof(struct link)+len);
    memcpy((*cur)->str, start, len);
    (*cur)->str[len]=0;
    (*cur)->flags=todo | subdir;
    (*cur)->next_link=0;
unknown's avatar
unknown committed
1475 1476 1477
  }
  return head;
}
unknown's avatar
unknown committed
1478

unknown's avatar
unknown committed
1479 1480 1481
/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1482
 *      ListCopy    make a copy of the list
unknown's avatar
unknown committed
1483 1484 1485
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1486 1487
 *      static struct link *ListCopy(orig)
 *      struct link *orig;
unknown's avatar
unknown committed
1488 1489 1490
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1491 1492
 *      Given pointer to list, which contains a copy of every element from
 *      the original list.
unknown's avatar
unknown committed
1493
 *
unknown's avatar
unknown committed
1494 1495 1496 1497 1498
 *      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
1499 1500 1501
 *
 */

unknown's avatar
unknown committed
1502
static struct link *ListCopy(struct link *orig)
unknown's avatar
unknown committed
1503
{
unknown's avatar
unknown committed
1504 1505
  struct link *new_malloc;
  struct link *head;
1506
  size_t len;
unknown's avatar
unknown committed
1507 1508 1509 1510 1511 1512 1513 1514

  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;
1515
    new_malloc->flags=orig->flags;
unknown's avatar
unknown committed
1516 1517 1518
    new_malloc->next_link= head;
    head= new_malloc;
    orig= orig->next_link;
unknown's avatar
unknown committed
1519
  }
unknown's avatar
unknown committed
1520
  return head;
unknown's avatar
unknown committed
1521 1522 1523 1524 1525
}

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1526
 *      InList    test a given string for member of a given list
unknown's avatar
unknown committed
1527 1528 1529
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1530 1531
 *      Tests the string pointed to by "cp" to determine if it is in
 *      the list pointed to by "linkp".  Linkp points to the first
1532 1533
 *      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
1534 1535 1536 1537
 *      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
1538
 *
1539 1540 1541
 *  RETURN
 *      combination of SUBDIR, INCLUDE, EXCLUDE, MATCHED flags
 *
unknown's avatar
unknown committed
1542 1543
 */

1544
static int InList(struct link *linkp, const char *cp, int exact_match)
unknown's avatar
unknown committed
1545
{
1546 1547
  int result;
  for (result=MATCHED; linkp != NULL; linkp= linkp->next_link)
unknown's avatar
unknown committed
1548
  {
1549
    if (!(exact_match ? strcmp(linkp->str,cp) : fnmatch(linkp->str, cp, 0)))
Monty's avatar
Monty committed
1550 1551 1552 1553
    {
      result= linkp->flags;
      break;
    }
1554 1555 1556 1557
    if (!(linkp->flags & EXCLUDE))
      result=NOT_MATCHED;
    if (linkp->flags & SUBDIR)
      result|=SUBDIR;
unknown's avatar
unknown committed
1558
  }
unknown's avatar
unknown committed
1559
  return result;
unknown's avatar
unknown committed
1560 1561
}

1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575
/*
 *  FUNCTION
 *
 *      ListFlags    returns aggregated list flags (ORed over all elements)
 *
 */

static uint ListFlags(struct link *linkp)
{
  uint f;
  for (f=0; linkp != NULL; linkp= linkp->next_link)
    f|= linkp->flags;
  return f;
}
unknown's avatar
unknown committed
1576 1577 1578 1579

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1580
 *      PushState    push current settings onto stack and set up new one
unknown's avatar
unknown committed
1581 1582 1583
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1584
 *      static VOID PushState()
unknown's avatar
unknown committed
1585 1586 1587
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1588 1589
 *      Pushes the current settings on the settings stack, and creates
 *      a new settings. The new settings is NOT initialized
unknown's avatar
unknown committed
1590
 *
unknown's avatar
unknown committed
1591 1592 1593
 *      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
1594 1595 1596
 *
 */

unknown's avatar
unknown committed
1597
static void PushState(CODE_STATE *cs)
unknown's avatar
unknown committed
1598
{
unknown's avatar
unknown committed
1599
  struct settings *new_malloc;
unknown's avatar
unknown committed
1600

unknown's avatar
unknown committed
1601
  new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings));
1602
  bzero(new_malloc, sizeof(*new_malloc));
unknown's avatar
unknown committed
1603 1604
  new_malloc->next= cs->stack;
  cs->stack= new_malloc;
unknown's avatar
unknown committed
1605 1606
}

1607 1608 1609 1610 1611 1612 1613 1614 1615
/*
 *  FUNCTION
 *
 *	FreeState    Free memory associated with a struct state.
 *
 *  SYNOPSIS
 *
 *	static void FreeState (state)
 *	struct state *state;
1616
 *      int free_state;
1617 1618 1619 1620
 *
 *  DESCRIPTION
 *
 *	Deallocates the memory allocated for various information in a
1621
 *	state. If free_state is set, also free 'state'
1622 1623
 *
 */
1624
static void FreeState(CODE_STATE *cs, int free_state)
1625
{
1626
  struct settings *state= cs->stack;
1627 1628 1629 1630 1631 1632
  if (!is_shared(state, keywords))
    FreeList(state->keywords);
  if (!is_shared(state, functions))
    FreeList(state->functions);
  if (!is_shared(state, processes))
    FreeList(state->processes);
1633

1634
  DBUGCloseFile(cs, NULL);
1635

1636
  if (free_state)
1637 1638 1639 1640 1641
  {
    struct settings *next= state->next;
    free(state);
    cs->stack= next;
  }
1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661
}


/*
 *  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.
 *
 */
1662
void _db_end_()
1663
{
Sergei Golubchik's avatar
Sergei Golubchik committed
1664
  CODE_STATE *cs, dummy_cs;
1665 1666 1667 1668 1669
  /*
    Set _dbug_on_ to be able to do full reset even when DEBUGGER_OFF was
    called after dbug was initialized
  */
  _dbug_on_= 1;
Sergei Golubchik's avatar
Sergei Golubchik committed
1670
  cs= code_state();
unknown's avatar
unknown committed
1671

Sergei Golubchik's avatar
Sergei Golubchik committed
1672
  if (cs)
Sergei Golubchik's avatar
Sergei Golubchik committed
1673
  {
1674 1675
    while (cs->stack && cs->stack != &init_settings)
      FreeState(cs, 1);
Sergei Golubchik's avatar
Sergei Golubchik committed
1676 1677 1678 1679 1680 1681 1682 1683 1684
  }
  else
  {
    cs= &dummy_cs;
    bzero(cs, sizeof(*cs));
  }

  cs->stack= &init_settings;
  FreeState(cs, 0);
1685
  pthread_mutex_destroy(&THR_LOCK_dbug);
1686
  init_done= 0;
Monty's avatar
Monty committed
1687
  _dbug_on_= 0;
1688 1689
}

unknown's avatar
unknown committed
1690 1691 1692 1693

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1694
 *      DoTrace    check to see if tracing is current enabled
unknown's avatar
unknown committed
1695 1696 1697
 *
 *  DESCRIPTION
 *
1698 1699 1700
 *      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
1701 1702 1703
 *
 */

1704
static int DoTrace(CODE_STATE *cs)
unknown's avatar
unknown committed
1705
{
Monty's avatar
Monty committed
1706 1707 1708
  int res= DONT_TRACE;
  if (!cs->locked)
    pthread_mutex_lock(&THR_LOCK_dbug);
1709
  if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) &&
1710
      InList(cs->stack->processes, cs->process, 0) & (MATCHED|INCLUDE))
Monty's avatar
Monty committed
1711
  {
1712
    switch(InList(cs->stack->functions, cs->func, 0)) {
Monty's avatar
Monty committed
1713 1714 1715 1716 1717 1718
    case INCLUDE|SUBDIR:
      res= ENABLE_TRACE;
      break;
    case INCLUDE:
      res= DO_TRACE;
      break;
1719 1720
    case MATCHED|SUBDIR:
    case NOT_MATCHED|SUBDIR:
Monty's avatar
Monty committed
1721 1722 1723
    case MATCHED:
      res= (framep_trace_flag(cs, cs->framep) ? DO_TRACE : DONT_TRACE);
      break;
1724
    case EXCLUDE:
Monty's avatar
Monty committed
1725 1726 1727 1728 1729 1730
    case NOT_MATCHED:
      res= DONT_TRACE;
      break;
    case EXCLUDE|SUBDIR:
      res= DISABLE_TRACE;
      break;
1731
    }
Monty's avatar
Monty committed
1732 1733 1734 1735
  }
  if (!cs->locked)
    pthread_mutex_unlock(&THR_LOCK_dbug);
  return res;
unknown's avatar
unknown committed
1736 1737
}

Monty's avatar
Monty committed
1738

unknown's avatar
unknown committed
1739 1740
FILE *_db_fp_(void)
{
1741
  CODE_STATE *cs;
unknown's avatar
unknown committed
1742
  get_code_state_or_return NULL;
1743
  return cs->stack->out_file->file;
unknown's avatar
unknown committed
1744 1745
}

unknown's avatar
unknown committed
1746 1747 1748
/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1749
 *      _db_keyword_    test keyword for member of keyword list
unknown's avatar
unknown committed
1750 1751 1752
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1753
 *      Test a keyword to determine if it is in the currently active
1754
 *      keyword list.  If strict=0, a keyword is accepted
unknown's avatar
unknown committed
1755 1756 1757 1758 1759 1760
 *      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
1761
 *
unknown's avatar
unknown committed
1762
 *      Returns TRUE if keyword accepted, FALSE otherwise.
unknown's avatar
unknown committed
1763 1764 1765
 *
 */

1766
BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict)
Alfranio Correia's avatar
Alfranio Correia committed
1767
{
1768
  int match= strict ? INCLUDE : INCLUDE|MATCHED;
Monty's avatar
Monty committed
1769
  int res;
1770
  get_code_state_if_not_set_or_return FALSE;
Alfranio Correia's avatar
Alfranio Correia committed
1771

Monty's avatar
Monty committed
1772 1773 1774 1775 1776 1777 1778 1779
  if (!(DEBUGGING && (DoTrace(cs) & DO_TRACE)))
    return 0;
  if (!cs->locked)
    pthread_mutex_lock(&THR_LOCK_dbug);
  res= (InList(cs->stack->keywords, keyword, strict) & match);
  if (!cs->locked)
    pthread_mutex_unlock(&THR_LOCK_dbug);
  return res != 0;
Alfranio Correia's avatar
Alfranio Correia committed
1780 1781
}

unknown's avatar
unknown committed
1782 1783 1784
/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1785
 *      Indent    indent a line to the given indentation level
unknown's avatar
unknown committed
1786 1787 1788
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1789 1790
 *      static VOID Indent(indent)
 *      int indent;
unknown's avatar
unknown committed
1791 1792 1793
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1794 1795 1796
 *      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
1797
 *
unknown's avatar
unknown committed
1798 1799
 *      Also, the indent must be scaled by the compile time option
 *      of character positions per nesting level.
unknown's avatar
unknown committed
1800 1801 1802
 *
 */

unknown's avatar
unknown committed
1803
static void Indent(CODE_STATE *cs, int indent)
unknown's avatar
unknown committed
1804
{
Sergei Golubchik's avatar
Sergei Golubchik committed
1805
  int count;
unknown's avatar
unknown committed
1806

1807
  indent= MY_MAX(indent-1-cs->stack->sub_level,0)*INDENT;
unknown's avatar
unknown committed
1808
  for (count= 0; count < indent ; count++)
unknown's avatar
unknown committed
1809 1810
  {
    if ((count % INDENT) == 0)
1811
      fputc('|',cs->stack->out_file->file);
unknown's avatar
unknown committed
1812
    else
1813
      fputc(' ',cs->stack->out_file->file);
unknown's avatar
unknown committed
1814 1815 1816 1817 1818 1819 1820
  }
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1821
 *      FreeList    free all memory associated with a linked list
unknown's avatar
unknown committed
1822 1823 1824
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1825 1826
 *      static VOID FreeList(linkp)
 *      struct link *linkp;
unknown's avatar
unknown committed
1827 1828 1829
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1830 1831
 *      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
1832 1833 1834
 *
 */

unknown's avatar
unknown committed
1835
static void FreeList(struct link *linkp)
unknown's avatar
unknown committed
1836
{
Sergei Golubchik's avatar
Sergei Golubchik committed
1837
  struct link *old;
unknown's avatar
unknown committed
1838

unknown's avatar
unknown committed
1839 1840 1841 1842
  while (linkp != NULL)
  {
    old= linkp;
    linkp= linkp->next_link;
1843
    free((void*) old);
unknown's avatar
unknown committed
1844 1845 1846 1847 1848 1849 1850
  }
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1851
 *      DoPrefix    print debugger line prefix prior to indentation
unknown's avatar
unknown committed
1852 1853 1854
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1855 1856
 *      static VOID DoPrefix(_line_)
 *      int _line_;
unknown's avatar
unknown committed
1857 1858 1859
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1860 1861 1862 1863
 *      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
1864 1865 1866
 *
 */

unknown's avatar
unknown committed
1867
static void DoPrefix(CODE_STATE *cs, uint _line_)
unknown's avatar
unknown committed
1868
{
unknown's avatar
unknown committed
1869 1870 1871
  cs->lineno++;
  if (cs->stack->flags & PID_ON)
  {
1872
    (void) fprintf(cs->stack->out_file->file, "%-7s: ", my_thread_name());
unknown's avatar
unknown committed
1873
  }
unknown's avatar
unknown committed
1874
  if (cs->stack->flags & NUMBER_ON)
1875
    (void) fprintf(cs->stack->out_file->file, "%5d: ", cs->lineno);
unknown's avatar
unknown committed
1876 1877
  if (cs->stack->flags & TIMESTAMP_ON)
  {
unknown's avatar
unknown committed
1878 1879 1880 1881 1882
#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);
1883
    (void) fprintf (cs->stack->out_file->file,
unknown's avatar
unknown committed
1884 1885 1886 1887 1888
                    /* "%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
1889 1890 1891 1892
    struct timeval tv;
    struct tm *tm_p;
    if (gettimeofday(&tv, NULL) != -1)
    {
unknown's avatar
unknown committed
1893
      if ((tm_p= localtime((const time_t *)&tv.tv_sec)))
unknown's avatar
unknown committed
1894
      {
1895
        (void) fprintf (cs->stack->out_file->file,
unknown's avatar
unknown committed
1896 1897 1898 1899 1900 1901 1902
                        /* "%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
1903
#endif
unknown's avatar
unknown committed
1904
  }
unknown's avatar
unknown committed
1905
  if (cs->stack->flags & PROCESS_ON)
1906
    (void) fprintf(cs->stack->out_file->file, "%s: ", cs->process);
unknown's avatar
unknown committed
1907
  if (cs->stack->flags & FILE_ON)
1908
    (void) fprintf(cs->stack->out_file->file, "%14s: ", BaseName(cs->file));
unknown's avatar
unknown committed
1909
  if (cs->stack->flags & LINE_ON)
1910
    (void) fprintf(cs->stack->out_file->file, "%5d: ", _line_);
unknown's avatar
unknown committed
1911
  if (cs->stack->flags & DEPTH_ON)
1912
    (void) fprintf(cs->stack->out_file->file, "%4d: ", cs->level);
unknown's avatar
unknown committed
1913 1914 1915 1916 1917 1918
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1919
 *      DBUGOpenFile    open new output stream for debugger output
unknown's avatar
unknown committed
1920 1921 1922
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1923 1924
 *      static VOID DBUGOpenFile(name)
 *      char *name;
unknown's avatar
unknown committed
1925 1926 1927
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1928 1929
 *      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
1930 1931 1932
 *
 */

unknown's avatar
unknown committed
1933 1934
static void DBUGOpenFile(CODE_STATE *cs,
                         const char *name,const char *end,int append)
unknown's avatar
unknown committed
1935
{
Sergei Golubchik's avatar
Sergei Golubchik committed
1936
  FILE *fp;
unknown's avatar
unknown committed
1937 1938 1939

  if (name != NULL)
  {
unknown's avatar
unknown committed
1940 1941
    if (end)
    {
1942
      size_t len=end-name;
unknown's avatar
unknown committed
1943 1944 1945 1946
      memcpy(cs->stack->name, name, len);
      cs->stack->name[len]=0;
    }
    else
1947
      strmov(cs->stack->name,name);
unknown's avatar
unknown committed
1948 1949
    name=cs->stack->name;
    if (strcmp(name, "-") == 0)
unknown's avatar
unknown committed
1950
    {
1951
      DBUGCloseFile(cs, sstdout);
unknown's avatar
unknown committed
1952 1953
      cs->stack->flags |= FLUSH_ON_WRITE;
      cs->stack->name[0]=0;
unknown's avatar
unknown committed
1954 1955 1956
    }
    else
    {
1957
      if (!Writable(name))
unknown's avatar
unknown committed
1958
      {
unknown's avatar
unknown committed
1959 1960 1961
        (void) fprintf(stderr, ERR_OPEN, cs->process, name);
        perror("");
        fflush(stderr);
unknown's avatar
unknown committed
1962 1963 1964
      }
      else
      {
1965
        if (!(fp= fopen(name, append ? "a+" : "w")))
unknown's avatar
unknown committed
1966 1967 1968 1969 1970 1971 1972
        {
          (void) fprintf(stderr, ERR_OPEN, cs->process, name);
          perror("");
          fflush(stderr);
        }
        else
        {
1973 1974 1975 1976
          sFILE *sfp= (sFILE *)DbugMalloc(sizeof(sFILE));
          sfp->file= fp;
          sfp->used= 1;
          DBUGCloseFile(cs, sfp);
unknown's avatar
unknown committed
1977
        }
unknown's avatar
unknown committed
1978 1979 1980 1981 1982 1983 1984 1985
      }
    }
  }
}

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1986
 *      DBUGCloseFile    close the debug output stream
unknown's avatar
unknown committed
1987 1988 1989
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1990 1991
 *      static VOID DBUGCloseFile(fp)
 *      FILE *fp;
unknown's avatar
unknown committed
1992 1993 1994
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1995 1996
 *      Closes the debug output stream unless it is standard output
 *      or standard error.
unknown's avatar
unknown committed
1997 1998 1999
 *
 */

2000
static void DBUGCloseFile(CODE_STATE *cs, sFILE *new_value)
unknown's avatar
unknown committed
2001
{
2002 2003 2004 2005 2006 2007 2008 2009
  sFILE *fp;
  if (!cs || !cs->stack || !cs->stack->out_file)
    return;
  if (!cs->locked)
    pthread_mutex_lock(&THR_LOCK_dbug);

  fp= cs->stack->out_file;
  if (--fp->used == 0)
unknown's avatar
unknown committed
2010
  {
2011 2012 2013 2014 2015 2016 2017 2018 2019
    if (fclose(fp->file) == EOF)
    {
      (void) fprintf(stderr, ERR_CLOSE, cs->process);
      perror("");
    }
    else
    {
      free(fp);
    }
unknown's avatar
unknown committed
2020
  }
2021 2022 2023
  cs->stack->out_file= new_value;
  if (!cs->locked)
    pthread_mutex_unlock(&THR_LOCK_dbug);
unknown's avatar
unknown committed
2024 2025 2026 2027 2028 2029
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2030
 *      DbugExit    print error message and exit
unknown's avatar
unknown committed
2031 2032 2033
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2034 2035
 *      static VOID DbugExit(why)
 *      char *why;
unknown's avatar
unknown committed
2036 2037 2038
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2039 2040 2041 2042
 *      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
2043 2044 2045
 *
 */

unknown's avatar
unknown committed
2046
static void DbugExit(const char *why)
unknown's avatar
unknown committed
2047
{
unknown's avatar
unknown committed
2048 2049 2050
  CODE_STATE *cs=code_state();
  (void) fprintf(stderr, ERR_ABORT, cs ? cs->process : "(null)", why);
  (void) fflush(stderr);
2051
  DBUG_ABORT();
unknown's avatar
unknown committed
2052 2053 2054 2055 2056 2057
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2058
 *      DbugMalloc    allocate memory for debugger runtime support
unknown's avatar
unknown committed
2059 2060 2061
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2062 2063
 *      static long *DbugMalloc(size)
 *      int size;
unknown's avatar
unknown committed
2064 2065 2066
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2067 2068 2069 2070 2071 2072
 *      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
2073 2074 2075
 *
 */

unknown's avatar
unknown committed
2076
static char *DbugMalloc(size_t size)
unknown's avatar
unknown committed
2077
{
Sergei Golubchik's avatar
Sergei Golubchik committed
2078
  char *new_malloc;
unknown's avatar
unknown committed
2079

2080
  if (!(new_malloc= (char*) malloc(size)))
unknown's avatar
unknown committed
2081 2082
    DbugExit("out of memory");
  return new_malloc;
unknown's avatar
unknown committed
2083 2084 2085 2086
}


/*
2087
 *     strtok lookalike - splits on ':', magically handles ::, :\ and :/
unknown's avatar
unknown committed
2088 2089
 */

unknown's avatar
unknown committed
2090
static const char *DbugStrTok(const char *s)
unknown's avatar
unknown committed
2091
{
2092 2093
  while (s[0] && (s[0] != ':' ||
                  (s[1] == '\\' || s[1] == '/' || (s[1] == ':' && s++))))
unknown's avatar
unknown committed
2094 2095
    s++;
  return s;
unknown's avatar
unknown committed
2096 2097 2098 2099 2100 2101
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2102
 *      BaseName    strip leading pathname components from name
unknown's avatar
unknown committed
2103 2104 2105
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2106 2107
 *      static char *BaseName(pathname)
 *      char *pathname;
unknown's avatar
unknown committed
2108 2109 2110
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2111 2112 2113
 *      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
2114 2115 2116
 *
 */

unknown's avatar
unknown committed
2117
static const char *BaseName(const char *pathname)
unknown's avatar
unknown committed
2118
{
Sergei Golubchik's avatar
Sergei Golubchik committed
2119
  const char *base;
unknown's avatar
unknown committed
2120

unknown's avatar
unknown committed
2121
  base= strrchr(pathname, FN_LIBCHAR);
unknown's avatar
unknown committed
2122
  if (base++ == NullS)
unknown's avatar
unknown committed
2123 2124
    base= pathname;
  return base;
unknown's avatar
unknown committed
2125 2126 2127 2128 2129 2130
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2131
 *      Writable    test to see if a pathname is writable/creatable
unknown's avatar
unknown committed
2132 2133 2134
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2135 2136
 *      static BOOLEAN Writable(pathname)
 *      char *pathname;
unknown's avatar
unknown committed
2137 2138 2139
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2140 2141 2142 2143 2144
 *      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
2145
 *
unknown's avatar
unknown committed
2146 2147
 *      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
2148 2149 2150 2151 2152 2153
 *
 */


#ifndef Writable

2154
static BOOLEAN Writable(const char *pathname)
unknown's avatar
unknown committed
2155
{
Sergei Golubchik's avatar
Sergei Golubchik committed
2156 2157
  BOOLEAN granted;
  char *lastslash;
unknown's avatar
unknown committed
2158

unknown's avatar
unknown committed
2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175
  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
2176
  }
unknown's avatar
unknown committed
2177
  return granted;
unknown's avatar
unknown committed
2178 2179 2180 2181
}
#endif

/*
Sergei Golubchik's avatar
Sergei Golubchik committed
2182
  flush dbug-stream, free mutex lock & wait delay
luz.paz's avatar
luz.paz committed
2183
  This is because some systems (MSDOS!!) doesn't flush fileheader
Sergei Golubchik's avatar
Sergei Golubchik committed
2184 2185
  and dbug-file isn't readable after a system crash !!
*/
unknown's avatar
unknown committed
2186

2187
static void DbugFlush(CODE_STATE *cs)
unknown's avatar
unknown committed
2188
{
unknown's avatar
unknown committed
2189
  if (cs->stack->flags & FLUSH_ON_WRITE)
unknown's avatar
unknown committed
2190
  {
2191
    (void) fflush(cs->stack->out_file->file);
2192 2193
    if (cs->stack->delay)
      (void) Delay(cs->stack->delay);
unknown's avatar
unknown committed
2194
  }
2195 2196 2197 2198 2199 2200 2201
} /* DbugFlush */


/* For debugging */

void _db_flush_()
{
unknown's avatar
unknown committed
2202
  CODE_STATE *cs;
2203
  get_code_state_or_return;
Monty's avatar
Monty committed
2204 2205 2206 2207 2208 2209
  if (DEBUGGING)
  {
    pthread_mutex_lock(&THR_LOCK_dbug);
    (void) fflush(cs->stack->out_file->file);
    pthread_mutex_unlock(&THR_LOCK_dbug);
  }
2210
}
unknown's avatar
unknown committed
2211

Monty's avatar
Monty committed
2212

2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231
#ifndef __WIN__
void _db_suicide_()
{
  int retval;
  sigset_t new_mask;
  sigfillset(&new_mask);

  fprintf(stderr, "SIGKILL myself\n");
  fflush(stderr);

  retval= kill(getpid(), SIGKILL);
  assert(retval == 0);
  retval= sigsuspend(&new_mask);
  fprintf(stderr, "sigsuspend returned %d errno %d \n", retval, errno);
  assert(FALSE); /* With full signal mask, we should never return here. */
}
#endif  /* ! __WIN__ */


unknown's avatar
unknown committed
2232
void _db_lock_file_()
unknown's avatar
unknown committed
2233
{
2234
  CODE_STATE *cs;
unknown's avatar
unknown committed
2235
  get_code_state_or_return;
unknown's avatar
unknown committed
2236
  pthread_mutex_lock(&THR_LOCK_dbug);
unknown's avatar
unknown committed
2237
  cs->locked=1;
unknown's avatar
unknown committed
2238 2239
}

unknown's avatar
unknown committed
2240
void _db_unlock_file_()
unknown's avatar
unknown committed
2241
{
2242
  CODE_STATE *cs;
unknown's avatar
unknown committed
2243 2244
  get_code_state_or_return;
  cs->locked=0;
unknown's avatar
unknown committed
2245 2246 2247
  pthread_mutex_unlock(&THR_LOCK_dbug);
}

Alfranio Correia's avatar
Alfranio Correia committed
2248 2249
const char* _db_get_func_(void)
{
2250
  CODE_STATE *cs;
Alfranio Correia's avatar
Alfranio Correia committed
2251 2252 2253 2254
  get_code_state_or_return NULL;
  return cs->func;
}

2255 2256 2257 2258 2259 2260

static int default_my_dbug_sanity(void)
{
  return 0;
}

2261 2262 2263 2264 2265 2266 2267 2268 2269 2270
#else

/*
 * Dummy function, workaround for build failure on a platform where linking
 * with an empty archive fails.
 */
int i_am_a_dummy_function() {
  return 0;
}

Sergei Golubchik's avatar
Sergei Golubchik committed
2271
#endif /* DBUG_OFF */
2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291

/*
  This function is called by default on DBUG_ASSERT() when compiled with
  DBUG_ASSERT_AS_PRINTF
*/

#ifdef DBUG_ASSERT_AS_PRINTF

static void default_my_dbug_assert_failed(const char *assert_expr,
                                          const char *file,
                                          unsigned long line)
{
  fprintf(stderr, "Warning: assertion failed: %s at %s line %lu\n",
          assert_expr, file, line);
}

void (*my_dbug_assert_failed)(const char *assert_expr, const char* file,
                              unsigned long line)= default_my_dbug_assert_failed;

#endif /* DBUG_ASSERT_AS_PRINTF */