dbug.c 59.7 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
 *        DBUG_DUMP       - To dump a block of memory.
fluesvamp's avatar
fluesvamp committed
58
 *        PUSH_FLAG "O"   - To be used instead of "o" if we
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
 *                          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
  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                       */
  int lineno;                   /* Current debugger output line number      */
  uint level;                   /* Current function nesting level           */
unknown's avatar
unknown committed
230 231

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

unknown's avatar
unknown committed
241 242 243
  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
244 245
} CODE_STATE;

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

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

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

int (*dbug_sanity)(void)= default_my_dbug_sanity;


276 277 278 279 280 281 282 283
/*
  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
284 285

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

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

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

unknown's avatar
unknown committed
300
/*
unknown's avatar
unknown committed
301
 *      Miscellaneous printf format strings.
unknown's avatar
unknown committed
302 303
 */

304
#define ERR_MISSING_RETURN "missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
unknown's avatar
unknown committed
305 306 307 308 309
#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
310
 *      Macros and defines for testing file accessibility under UNIX and MSDOS.
unknown's avatar
unknown committed
311 312
 */

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

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

#include <my_pthread.h>
Eugene Kosov's avatar
Eugene Kosov committed
327 328 329 330
/*
** Protects writing to all file descriptors, init_settings.keywords
** pointer and it's pointee - a linked list with keywords.
*/
331
static pthread_mutex_t THR_LOCK_dbug;
unknown's avatar
unknown committed
332

Eugene Kosov's avatar
Eugene Kosov committed
333 334 335 336
static void LockMutex(CODE_STATE *cs)
{
  if (!cs->locked)
    pthread_mutex_lock(&THR_LOCK_dbug);
337
  cs->locked++;
Eugene Kosov's avatar
Eugene Kosov committed
338 339 340
}
static void UnlockMutex(CODE_STATE *cs)
{
341 342 343
  --cs->locked;
  assert(cs->locked >= 0);
  if (cs->locked == 0)
Eugene Kosov's avatar
Eugene Kosov committed
344 345
    pthread_mutex_unlock(&THR_LOCK_dbug);
}
Eugene Kosov's avatar
Eugene Kosov committed
346 347 348 349 350 351 352 353 354 355
static void LockIfInitSettings(CODE_STATE *cs)
{
  if (cs->stack == &init_settings)
    LockMutex(cs);
}
static void UnlockIfInitSettings(CODE_STATE *cs)
{
  if (cs->stack == &init_settings)
    UnlockMutex(cs);
}
Eugene Kosov's avatar
Eugene Kosov committed
356

unknown's avatar
unknown committed
357 358
static CODE_STATE *code_state(void)
{
359 360 361 362 363 364 365 366
  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
367 368 369

  if (!init_done)
  {
370
    init_done=TRUE;
371 372
    sstdout->file= stdout;
    sstderr->file= stderr;
Sergei Golubchik's avatar
Sergei Golubchik committed
373
    pthread_mutex_init(&THR_LOCK_dbug, NULL);
unknown's avatar
unknown committed
374
    bzero(&init_settings, sizeof(init_settings));
375
    init_settings.out_file= sstderr;
unknown's avatar
unknown committed
376 377 378
    init_settings.flags=OPEN_APPEND;
  }

379 380 381
  if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug()))
    return 0;                                   /* Thread not initialised */
  if (!(cs= *cs_ptr))
unknown's avatar
unknown committed
382
  {
383 384 385
    cs=(CODE_STATE*) DbugMalloc(sizeof(*cs));
    bzero((uchar*) cs,sizeof(*cs));
    cs->process= db_process ? db_process : "dbug";
386 387
    cs->func= "?func";
    cs->file= "?file";
388 389
    cs->stack=&init_settings;
    *cs_ptr= cs;
unknown's avatar
unknown committed
390
  }
unknown's avatar
unknown committed
391
  return cs;
unknown's avatar
unknown committed
392 393
}

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
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
415 416 417 418 419 420 421 422 423 424
/*
 *      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
425 426 427 428

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
429
 *      _db_process_       give the name to the current process/thread
unknown's avatar
unknown committed
430 431 432
 *
 *  SYNOPSIS
 *
433
 *      VOID _process_(name)
unknown's avatar
unknown committed
434
 *      char *name;
unknown's avatar
unknown committed
435
 *
unknown's avatar
unknown committed
436 437 438 439
 */

void _db_process_(const char *name)
{
440
  CODE_STATE *cs;
unknown's avatar
unknown committed
441 442 443

  if (!db_process)
    db_process= name;
444

unknown's avatar
unknown committed
445 446 447 448 449 450
  get_code_state_or_return;
  cs->process= name;
}

/*
 *  FUNCTION
unknown's avatar
unknown committed
451
 *
452
 *      DbugParse  parse control string and set current debugger settings
unknown's avatar
unknown committed
453
 *
unknown's avatar
unknown committed
454
 *  DESCRIPTION
unknown's avatar
unknown committed
455
 *
unknown's avatar
unknown committed
456 457 458
 *      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
459
 *
unknown's avatar
unknown committed
460 461
 *      The debug control string is a sequence of colon separated fields
 *      as follows:
unknown's avatar
unknown committed
462
 *
unknown's avatar
unknown committed
463
 *              [+]<field_1>:<field_2>:...:<field_N>
unknown's avatar
unknown committed
464
 *
unknown's avatar
unknown committed
465 466
 *      Each field consists of a mandatory flag character followed by
 *      an optional "," and comma separated list of modifiers:
unknown's avatar
unknown committed
467
 *
unknown's avatar
unknown committed
468
 *              [sign]flag[,modifier,modifier,...,modifier]
unknown's avatar
unknown committed
469
 *
unknown's avatar
unknown committed
470
 *      See the manual for the list of supported signs, flags, and modifiers
unknown's avatar
unknown committed
471
 *
unknown's avatar
unknown committed
472
 *      For convenience, any leading "-#" is stripped off.
unknown's avatar
unknown committed
473
 *
474 475 476
 *  RETURN
 *      1 - a list of functions ("f" flag) was possibly changed
 *      0 - a list of functions was not changed
unknown's avatar
unknown committed
477 478
 */

Sergei Golubchik's avatar
Sergei Golubchik committed
479
static int DbugParse(CODE_STATE *cs, const char *control)
unknown's avatar
unknown committed
480
{
unknown's avatar
unknown committed
481
  const char *end;
482
  int rel, f_used=0;
483
  struct settings *stack;
unknown's avatar
unknown committed
484

485
  stack= cs->stack;
unknown's avatar
unknown committed
486

unknown's avatar
unknown committed
487 488
  if (control[0] == '-' && control[1] == '#')
    control+=2;
Sergei Golubchik's avatar
Sergei Golubchik committed
489

unknown's avatar
unknown committed
490
  rel= control[0] == '+' || control[0] == '-';
491
  if ((!rel || (!stack->out_file && !stack->next)))
unknown's avatar
unknown committed
492
  {
493
    LockIfInitSettings(cs);
494
    FreeState(cs, 0);
495 496 497 498
    stack->flags= 0;
    stack->delay= 0;
    stack->maxdepth= 0;
    stack->sub_level= 0;
499
    stack->out_file= sstderr;
500 501 502
    stack->functions= NULL;
    stack->keywords= NULL;
    stack->processes= NULL;
503
    UnlockIfInitSettings(cs);
unknown's avatar
unknown committed
504
  }
505
  else if (!stack->out_file)
unknown's avatar
unknown committed
506
  {
507 508 509 510 511
    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);
512 513
    stack->out_file= stack->next->out_file;
    stack->out_file->used++;
514
    if (stack->next == &init_settings)
unknown's avatar
unknown committed
515
    {
516
      /* never share with the global parent - it can change under your feet */
517
      stack->functions= ListCopy(init_settings.functions);
518
      LockMutex(cs);
519
      stack->keywords= ListCopy(init_settings.keywords);
520
      UnlockMutex(cs);
521
      stack->processes= ListCopy(init_settings.processes);
unknown's avatar
unknown committed
522 523 524
    }
    else
    {
525 526 527
      stack->functions= stack->next->functions;
      stack->keywords= stack->next->keywords;
      stack->processes= stack->next->processes;
unknown's avatar
unknown committed
528
    }
unknown's avatar
unknown committed
529 530
  }

unknown's avatar
unknown committed
531
  end= DbugStrTok(control);
532
  while (control < end)
unknown's avatar
unknown committed
533 534 535 536
  {
    int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0;
    if (sign) control++;
    c= *control++;
537 538
    if (*control == ',')
      control++;
unknown's avatar
unknown committed
539
    /* XXX when adding new cases here, don't forget _db_explain_ ! */
unknown's avatar
unknown committed
540
    switch (c) {
unknown's avatar
unknown committed
541
    case 'd':
unknown's avatar
unknown committed
542 543
      if (sign < 0 && control == end)
      {
Eugene Kosov's avatar
Eugene Kosov committed
544
        LockIfInitSettings(cs);
545 546 547
        if (!is_shared(stack, keywords))
          FreeList(stack->keywords);
        stack->keywords=NULL;
Eugene Kosov's avatar
Eugene Kosov committed
548
        UnlockIfInitSettings(cs);
549
        stack->flags &= ~DEBUG_ON;
unknown's avatar
unknown committed
550
        break;
unknown's avatar
unknown committed
551
      }
Eugene Kosov's avatar
Eugene Kosov committed
552
      LockIfInitSettings(cs);
553 554
      if (rel && is_shared(stack, keywords))
        stack->keywords= ListCopy(stack->keywords);
Eugene Kosov's avatar
Eugene Kosov committed
555
      UnlockIfInitSettings(cs);
unknown's avatar
unknown committed
556 557 558
      if (sign < 0)
      {
        if (DEBUGGING)
Eugene Kosov's avatar
Eugene Kosov committed
559 560
        {
          LockIfInitSettings(cs);
561
          stack->keywords= ListDel(stack->keywords, control, end);
Eugene Kosov's avatar
Eugene Kosov committed
562 563
          UnlockIfInitSettings(cs);
        }
564
        break;
unknown's avatar
unknown committed
565
      }
Eugene Kosov's avatar
Eugene Kosov committed
566
      LockIfInitSettings(cs);
567
      stack->keywords= ListAdd(stack->keywords, control, end);
Eugene Kosov's avatar
Eugene Kosov committed
568
      UnlockIfInitSettings(cs);
569
      stack->flags |= DEBUG_ON;
unknown's avatar
unknown committed
570 571
      break;
    case 'D':
572
      stack->delay= atoi(control);
unknown's avatar
unknown committed
573 574
      break;
    case 'f':
575
      f_used= 1;
unknown's avatar
unknown committed
576 577
      if (sign < 0 && control == end)
      {
578 579 580
        if (!is_shared(stack,functions))
          FreeList(stack->functions);
        stack->functions=NULL;
unknown's avatar
unknown committed
581
        break;
unknown's avatar
unknown committed
582
      }
583 584
      if (rel && is_shared(stack,functions))
        stack->functions= ListCopy(stack->functions);
unknown's avatar
unknown committed
585
      if (sign < 0)
586
        stack->functions= ListDel(stack->functions, control, end);
unknown's avatar
unknown committed
587
      else
588
        stack->functions= ListAdd(stack->functions, control, end);
unknown's avatar
unknown committed
589 590
      break;
    case 'F':
unknown's avatar
unknown committed
591
      if (sign < 0)
592
        stack->flags &= ~FILE_ON;
unknown's avatar
unknown committed
593
      else
594
        stack->flags |= FILE_ON;
unknown's avatar
unknown committed
595 596
      break;
    case 'i':
unknown's avatar
unknown committed
597
      if (sign < 0)
598
        stack->flags &= ~PID_ON;
unknown's avatar
unknown committed
599
      else
600
        stack->flags |= PID_ON;
unknown's avatar
unknown committed
601 602
      break;
    case 'L':
unknown's avatar
unknown committed
603
      if (sign < 0)
604
        stack->flags &= ~LINE_ON;
unknown's avatar
unknown committed
605
      else
606
        stack->flags |= LINE_ON;
unknown's avatar
unknown committed
607 608
      break;
    case 'n':
unknown's avatar
unknown committed
609
      if (sign < 0)
610
        stack->flags &= ~DEPTH_ON;
unknown's avatar
unknown committed
611
      else
612
        stack->flags |= DEPTH_ON;
unknown's avatar
unknown committed
613 614
      break;
    case 'N':
unknown's avatar
unknown committed
615
      if (sign < 0)
616
        stack->flags &= ~NUMBER_ON;
unknown's avatar
unknown committed
617
      else
618
        stack->flags |= NUMBER_ON;
unknown's avatar
unknown committed
619 620 621
      break;
    case 'A':
    case 'O':
622
      stack->flags |= FLUSH_ON_WRITE;
unknown's avatar
unknown committed
623
      /* fall through */
unknown's avatar
unknown committed
624 625
    case 'a':
    case 'o':
unknown's avatar
unknown committed
626 627
      if (sign < 0)
      {
628
        DBUGCloseFile(cs, sstderr);
629
        stack->flags &= ~FLUSH_ON_WRITE;
unknown's avatar
unknown committed
630
        break;
unknown's avatar
unknown committed
631
      }
unknown's avatar
unknown committed
632
      if (c == 'a' || c == 'A')
633
        stack->flags |= OPEN_APPEND;
unknown's avatar
unknown committed
634
      else
635
        stack->flags &= ~OPEN_APPEND;
unknown's avatar
unknown committed
636
      if (control != end)
637
        DBUGOpenFile(cs, control, end, stack->flags & OPEN_APPEND);
unknown's avatar
unknown committed
638 639
      else
        DBUGOpenFile(cs, "-",0,0);
unknown's avatar
unknown committed
640 641
      break;
    case 'p':
unknown's avatar
unknown committed
642 643
      if (sign < 0 && control == end)
      {
644 645 646
        if (!is_shared(stack,processes))
          FreeList(stack->processes);
        stack->processes=NULL;
unknown's avatar
unknown committed
647
        break;
unknown's avatar
unknown committed
648
      }
649 650
      if (rel && is_shared(stack, processes))
        stack->processes= ListCopy(stack->processes);
unknown's avatar
unknown committed
651
      if (sign < 0)
652
        stack->processes= ListDel(stack->processes, control, end);
unknown's avatar
unknown committed
653
      else
654
        stack->processes= ListAdd(stack->processes, control, end);
unknown's avatar
unknown committed
655 656
      break;
    case 'P':
unknown's avatar
unknown committed
657
      if (sign < 0)
658
        stack->flags &= ~PROCESS_ON;
unknown's avatar
unknown committed
659
      else
660
        stack->flags |= PROCESS_ON;
unknown's avatar
unknown committed
661 662
      break;
    case 'r':
663
      stack->sub_level= cs->level;
unknown's avatar
unknown committed
664 665
      break;
    case 't':
unknown's avatar
unknown committed
666 667 668
      if (sign < 0)
      {
        if (control != end)
669
          stack->maxdepth-= atoi(control);
unknown's avatar
unknown committed
670
        else
671
          stack->maxdepth= 0;
unknown's avatar
unknown committed
672 673 674 675
      }
      else
      {
        if (control != end)
676
          stack->maxdepth+= atoi(control);
unknown's avatar
unknown committed
677
        else
678
          stack->maxdepth= MAXDEPTH;
unknown's avatar
unknown committed
679
      }
680 681
      if (stack->maxdepth > 0)
        stack->flags |= TRACE_ON;
unknown's avatar
unknown committed
682
      else
683
        stack->flags &= ~TRACE_ON;
unknown's avatar
unknown committed
684 685 686
      break;
    case 'T':
      if (sign < 0)
687
        stack->flags &= ~TIMESTAMP_ON;
unknown's avatar
unknown committed
688
      else
689
        stack->flags |= TIMESTAMP_ON;
unknown's avatar
unknown committed
690
      break;
Sergei Golubchik's avatar
Sergei Golubchik committed
691 692 693 694 695 696
    case 'S':
      if (sign < 0)
        stack->flags &= ~SANITY_CHECK_ON;
      else
        stack->flags |= SANITY_CHECK_ON;
      break;
unknown's avatar
unknown committed
697
    }
unknown's avatar
unknown committed
698 699 700 701
    if (!*end)
      break;
    control=end+1;
    end= DbugStrTok(control);
unknown's avatar
unknown committed
702
  }
703
  return !rel || f_used;
unknown's avatar
unknown committed
704
}
705
  
706 707 708 709 710
#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
711 712
static void FixTraceFlags_helper(CODE_STATE *cs, const char *func,
                                 struct _db_stack_frame_ *framep)
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
{
  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
737
static void FixTraceFlags(uint old_fflags, CODE_STATE *cs)
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 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
{
  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;
}
808

809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
/*
 *  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.
825
 *
826 827
 */

828
void _db_set_(const char *control)
829
{
830 831
  CODE_STATE *cs;
  uint old_fflags;
832
  get_code_state_or_return;
833
  old_fflags=fflags(cs);
834 835
  if (cs->stack == &init_settings)
    PushState(cs);
836 837
  if (DbugParse(cs, control))
    FixTraceFlags(old_fflags, cs);
838 839
}

840 841 842 843 844 845 846 847 848 849 850 851 852 853
/*
 *  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
854
 *      up a new debug settings
855 856 857 858 859
 *
 */

void _db_push_(const char *control)
{
860 861
  CODE_STATE *cs;
  uint old_fflags;
862
  get_code_state_or_return;
863
  old_fflags=fflags(cs);
864
  PushState(cs);
865 866 867 868
  if (DbugParse(cs, control))
    FixTraceFlags(old_fflags, cs);
}

Sergei Golubchik's avatar
Sergei Golubchik committed
869

870 871 872 873 874 875 876 877 878
/**
  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);
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
}

/*
 *  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)
{
898
  CODE_STATE tmp_cs;
899
  bzero((uchar*) &tmp_cs, sizeof(tmp_cs));
900
  tmp_cs.stack= &init_settings;
901
  tmp_cs.process= db_process ? db_process : "dbug";
902
  DbugParse(&tmp_cs, control);
903 904
}

unknown's avatar
unknown committed
905 906 907
/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
908
 *      _db_pop_    pop the debug stack
unknown's avatar
unknown committed
909 910 911
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
912 913 914 915 916 917 918
 *      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
919 920 921
 *
 */

unknown's avatar
unknown committed
922
void _db_pop_()
unknown's avatar
unknown committed
923
{
924 925
  uint old_fflags;
  CODE_STATE *cs;
unknown's avatar
unknown committed
926 927 928

  get_code_state_or_return;

929
  if (cs->stack != &init_settings)
unknown's avatar
unknown committed
930
  {
931
    old_fflags=fflags(cs);
932
    FreeState(cs, 1);
933
    FixTraceFlags(old_fflags, cs);
unknown's avatar
unknown committed
934 935 936
  }
}

unknown's avatar
unknown committed
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
/*
 *  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(',');                       \
955
        buf=strnmov(buf, (S), (uint) (end-buf)); \
unknown's avatar
unknown committed
956 957
        if (buf >= end) goto overflow;          \
      } while (0)
958
#define list_to_buf(l, f)  do {                 \
unknown's avatar
unknown committed
959 960 961
        struct link *listp=(l);                 \
        while (listp)                           \
        {                                       \
962 963 964 965 966 967
          if (listp->flags & (f))               \
          {                                     \
            str_to_buf(listp->str);             \
            if (listp->flags & SUBDIR)          \
              char_to_buf('/');                 \
          }                                     \
unknown's avatar
unknown committed
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
          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))                             \
        {                                       \
1007
          int f=ListFlags(val);                 \
unknown's avatar
unknown committed
1008 1009
          colon_to_buf;                         \
          char_to_buf((C));                     \
1010 1011 1012 1013 1014 1015 1016 1017 1018
          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
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
        }                                       \
      } while (0)
#define op_bool_to_buf(C, cond) do {            \
        if ((cond))                             \
        {                                       \
          colon_to_buf;                         \
          char_to_buf((C));                     \
        }                                       \
      } while (0)

1029
int _db_explain_ (CODE_STATE *cs, char *buf, size_t len)
unknown's avatar
unknown committed
1030 1031 1032
{
  char *start=buf, *end=buf+len-4;

1033
  get_code_state_if_not_set_or_return *buf=0;
unknown's avatar
unknown committed
1034

Eugene Kosov's avatar
Eugene Kosov committed
1035
  LockIfInitSettings(cs);
unknown's avatar
unknown committed
1036
  op_list_to_buf('d', cs->stack->keywords, DEBUGGING);
Eugene Kosov's avatar
Eugene Kosov committed
1037
  UnlockIfInitSettings(cs);
unknown's avatar
unknown committed
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
  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')),
1048
    cs->stack->name, cs->stack->out_file != sstderr);
unknown's avatar
unknown committed
1049 1050 1051 1052
  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
1053
  op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON);
Sergei Golubchik's avatar
Sergei Golubchik committed
1054
  op_bool_to_buf('S', cs->stack->flags & SANITY_CHECK_ON);
unknown's avatar
unknown committed
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076

  *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
1077 1078 1079 1080

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1081 1082 1083 1084 1085 1086
 *      _db_explain_init_       explain initial debugger settings
 *
 *  DESCRIPTION
 *      see _db_explain_
 */

1087
int _db_explain_init_(char *buf, size_t len)
unknown's avatar
unknown committed
1088 1089
{
  CODE_STATE cs;
1090
  bzero((uchar*) &cs,sizeof(cs));
unknown's avatar
unknown committed
1091 1092 1093 1094 1095 1096 1097 1098
  cs.stack=&init_settings;
  return _db_explain_(&cs, buf, len);
}

/*
 *  FUNCTION
 *
 *      _db_enter_    process entry point to user function
unknown's avatar
unknown committed
1099 1100 1101
 *
 *  SYNOPSIS
 *
1102
 *      VOID _db_enter_(_func_, _file_, _line_, _stack_frame_)
unknown's avatar
unknown committed
1103 1104 1105
 *      char *_func_;           points to current function name
 *      char *_file_;           points to current file name
 *      int _line_;             called from source line number
1106
 *      struct _db_stack_frame_ allocated on the caller's stack
unknown's avatar
unknown committed
1107 1108 1109
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1110 1111 1112 1113 1114 1115 1116 1117 1118
 *      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
1119
 *
unknown's avatar
unknown committed
1120 1121
 *      Also prints a trace line if tracing is enabled and
 *      increments the current function nesting depth.
unknown's avatar
unknown committed
1122
 *
unknown's avatar
unknown committed
1123 1124 1125
 *      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
1126 1127 1128
 *
 */

unknown's avatar
unknown committed
1129
void _db_enter_(const char *_func_, const char *_file_,
1130
                uint _line_, struct _db_stack_frame_ *_stack_frame_)
unknown's avatar
unknown committed
1131
{
1132 1133 1134 1135 1136 1137 1138
  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;
  }
unknown's avatar
unknown committed
1139

1140
  _stack_frame_->line= -1;
1141 1142
  _stack_frame_->func= cs->func;
  _stack_frame_->file= cs->file;
unknown's avatar
unknown committed
1143 1144
  cs->func=  _func_;
  cs->file=  _file_;
1145 1146 1147
  _stack_frame_->prev= cs->framep;
  _stack_frame_->level= ++cs->level | framep_trace_flag(cs, cs->framep);
  cs->framep= _stack_frame_;
1148

1149 1150 1151 1152 1153 1154
  switch (DoTrace(cs)) {
  case ENABLE_TRACE:
    cs->framep->level|= TRACE_ON;
    if (!TRACING) break;
    /* fall through */
  case DO_TRACE:
1155
    if ((cs->stack->flags & SANITY_CHECK_ON) && (*dbug_sanity)())
Sergei Golubchik's avatar
Sergei Golubchik committed
1156
      cs->stack->flags &= ~SANITY_CHECK_ON;
1157 1158
    if (TRACING)
    {
Eugene Kosov's avatar
Eugene Kosov committed
1159 1160
      int save_errno= errno;
      LockMutex(cs);
1161 1162
      DoPrefix(cs, _line_);
      Indent(cs, cs->level);
1163
      (void) fprintf(cs->stack->out_file->file, ">%s\n", cs->func);
Eugene Kosov's avatar
Eugene Kosov committed
1164 1165 1166
      UnlockMutex(cs);
      DbugFlush(cs);
      errno=save_errno;
1167 1168 1169 1170 1171 1172 1173 1174
    }
    break;
  case DISABLE_TRACE:
    cs->framep->level&= ~TRACE_ON;
    /* fall through */
  case DONT_TRACE:
    break;
  }
unknown's avatar
unknown committed
1175 1176 1177 1178 1179
}

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1180
 *      _db_return_    process exit from user function
unknown's avatar
unknown committed
1181 1182 1183
 *
 *  SYNOPSIS
 *
1184
 *      VOID _db_return_(_line_, _stack_frame_)
unknown's avatar
unknown committed
1185
 *      int _line_;             current source line number
1186
 *      struct _db_stack_frame_ allocated on the caller's stack
unknown's avatar
unknown committed
1187 1188 1189
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1190 1191 1192 1193
 *      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
1194 1195 1196
 *
 */

1197
void _db_return_(struct _db_stack_frame_ *_stack_frame_)
unknown's avatar
unknown committed
1198
{
1199 1200
  uint _slevel_= _stack_frame_->level & ~TRACE_ON;
  CODE_STATE *cs;
unknown's avatar
unknown committed
1201
  get_code_state_or_return;
unknown's avatar
unknown committed
1202

1203 1204 1205 1206
  if (_stack_frame_->line == 0)
    return;

  if (_stack_frame_->line == -1 || cs->framep != _stack_frame_)
unknown's avatar
unknown committed
1207
  {
1208 1209 1210
    char buf[512];
    my_snprintf(buf, sizeof(buf), ERR_MISSING_RETURN, cs->func);
    DbugExit(buf);
unknown's avatar
unknown committed
1211
  }
1212

1213 1214
  if (DoTrace(cs) & DO_TRACE)
  {
1215
    if ((cs->stack->flags & SANITY_CHECK_ON) && (*dbug_sanity)())
Sergei Golubchik's avatar
Sergei Golubchik committed
1216
      cs->stack->flags &= ~SANITY_CHECK_ON;
1217
    if (TRACING)
unknown's avatar
unknown committed
1218
    {
Eugene Kosov's avatar
Eugene Kosov committed
1219 1220
      int save_errno=errno;
      LockMutex(cs);
1221
      DoPrefix(cs, _stack_frame_->line);
1222
      Indent(cs, cs->level);
1223
      (void) fprintf(cs->stack->out_file->file, "<%s\n", cs->func);
Eugene Kosov's avatar
Eugene Kosov committed
1224
      UnlockMutex(cs);
1225
      DbugFlush(cs);
Eugene Kosov's avatar
Eugene Kosov committed
1226
      errno=save_errno;
unknown's avatar
unknown committed
1227
    }
unknown's avatar
unknown committed
1228
  }
1229 1230 1231 1232 1233 1234 1235
  /*
    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
1236
  if (cs->framep != NULL)
1237
    cs->framep= cs->framep->prev;
unknown's avatar
unknown committed
1238 1239 1240 1241 1242 1243
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1244
 *      _db_pargs_    log arguments for subsequent use by _db_doprnt_()
unknown's avatar
unknown committed
1245 1246 1247
 *
 *  SYNOPSIS
 *
1248
 *      int _db_pargs_(_line_, keyword)
unknown's avatar
unknown committed
1249 1250
 *      int _line_;
 *      char *keyword;
unknown's avatar
unknown committed
1251 1252 1253
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1254 1255 1256 1257
 *      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
1258 1259 1260
 *
 */

1261
int _db_pargs_(uint _line_, const char *keyword)
unknown's avatar
unknown committed
1262
{
1263
  CODE_STATE *cs;
1264
  get_code_state_or_return 0;
unknown's avatar
unknown committed
1265
  cs->u_line= _line_;
1266
  cs->u_keyword= keyword;
1267 1268

  return DEBUGGING && _db_keyword_(cs, cs->u_keyword, 0);
unknown's avatar
unknown committed
1269 1270 1271 1272 1273 1274
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1275
 *      _db_doprnt_    handle print of debug lines
unknown's avatar
unknown committed
1276 1277 1278
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1279 1280 1281
 *      VOID _db_doprnt_(format, va_alist)
 *      char *format;
 *      va_dcl;
unknown's avatar
unknown committed
1282 1283 1284
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1285 1286 1287 1288 1289
 *      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
1290
 *
unknown's avatar
unknown committed
1291 1292
 *      Note that the format string SHOULD NOT include a terminating
 *      newline, this is supplied automatically.
unknown's avatar
unknown committed
1293 1294 1295 1296 1297
 *
 */

#include <stdarg.h>

unknown's avatar
unknown committed
1298
void _db_doprnt_(const char *format,...)
unknown's avatar
unknown committed
1299 1300
{
  va_list args;
1301
  CODE_STATE *cs;
1302
  int save_errno;
1303

unknown's avatar
unknown committed
1304
  get_code_state_or_return;
unknown's avatar
unknown committed
1305 1306 1307

  va_start(args,format);

1308
  save_errno=errno;
1309
  LockMutex(cs);
1310 1311 1312 1313 1314 1315 1316
  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);
1317
  UnlockMutex(cs);
1318 1319
  DbugFlush(cs);
  errno=save_errno;
1320

unknown's avatar
unknown committed
1321 1322 1323
  va_end(args);
}

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

unknown's avatar
unknown committed
1336 1337 1338 1339

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

1355 1356
void _db_dump_(uint _line_, const char *keyword,
               const unsigned char *memory, size_t length)
unknown's avatar
unknown committed
1357 1358
{
  int pos;
1359
  CODE_STATE *cs;
unknown's avatar
unknown committed
1360 1361
  get_code_state_or_return;

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

    pos=0;
    while (length-- > 0)
    {
      uint tmp= *((unsigned char*) memory++);
      if ((pos+=3) >= 80)
      {
1384
        fputc('\n',cs->stack->out_file->file);
unknown's avatar
unknown committed
1385
        pos=3;
unknown's avatar
unknown committed
1386
      }
1387 1388 1389
      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
1390
    }
1391
    (void) fputc('\n',cs->stack->out_file->file);
Eugene Kosov's avatar
Eugene Kosov committed
1392
    UnlockMutex(cs);
1393
    DbugFlush(cs);
unknown's avatar
unknown committed
1394 1395 1396
  }
}

unknown's avatar
unknown committed
1397 1398

/*
unknown's avatar
unknown committed
1399 1400
 *  FUNCTION
 *
1401
 *      ListAddDel    modify the list according to debug control string
unknown's avatar
unknown committed
1402 1403 1404 1405
 *
 *  DESCRIPTION
 *
 *      Given pointer to a comma separated list of strings in "cltp",
1406 1407
 *      parses the list, and modifies "listp", returning a pointer
 *      to the new list.
unknown's avatar
unknown committed
1408
 *
1409
 *      The mode of operation is defined by "todo" parameter.
unknown's avatar
unknown committed
1410
 *
1411 1412 1413 1414 1415
 *      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
1416
 *
1417 1418 1419
 *      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
1420 1421
 */

1422 1423
static struct link *ListAddDel(struct link *head, const char *ctlp,
                               const char *end, int todo)
unknown's avatar
unknown committed
1424
{
unknown's avatar
unknown committed
1425 1426
  const char *start;
  struct link **cur;
1427 1428
  size_t len;
  int subdir;
unknown's avatar
unknown committed
1429

1430 1431 1432
  ctlp--;
next:
  while (++ctlp < end)
unknown's avatar
unknown committed
1433 1434
  {
    start= ctlp;
1435
    subdir=0;
unknown's avatar
unknown committed
1436 1437
    while (ctlp < end && *ctlp != ',')
      ctlp++;
1438
    len= (int) (ctlp-start);
1439
    if (start[len-1] == '/')
unknown's avatar
unknown committed
1440
    {
1441 1442 1443 1444 1445 1446 1447
      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
1448
      {
1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462
        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
1463
      }
1464 1465 1466 1467 1468 1469
    }
    *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
1470 1471 1472
  }
  return head;
}
unknown's avatar
unknown committed
1473

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

unknown's avatar
unknown committed
1497
static struct link *ListCopy(struct link *orig)
unknown's avatar
unknown committed
1498
{
unknown's avatar
unknown committed
1499 1500
  struct link *new_malloc;
  struct link *head;
1501
  size_t len;
unknown's avatar
unknown committed
1502 1503 1504 1505 1506 1507 1508 1509

  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;
1510
    new_malloc->flags=orig->flags;
unknown's avatar
unknown committed
1511 1512 1513
    new_malloc->next_link= head;
    head= new_malloc;
    orig= orig->next_link;
unknown's avatar
unknown committed
1514
  }
unknown's avatar
unknown committed
1515
  return head;
unknown's avatar
unknown committed
1516 1517 1518 1519 1520
}

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

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

1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
/*
 *  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
1571 1572 1573 1574

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

unknown's avatar
unknown committed
1592
static void PushState(CODE_STATE *cs)
unknown's avatar
unknown committed
1593
{
unknown's avatar
unknown committed
1594
  struct settings *new_malloc;
unknown's avatar
unknown committed
1595

unknown's avatar
unknown committed
1596
  new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings));
1597
  bzero(new_malloc, sizeof(*new_malloc));
unknown's avatar
unknown committed
1598 1599
  new_malloc->next= cs->stack;
  cs->stack= new_malloc;
unknown's avatar
unknown committed
1600 1601
}

1602 1603 1604 1605 1606 1607 1608 1609 1610
/*
 *  FUNCTION
 *
 *	FreeState    Free memory associated with a struct state.
 *
 *  SYNOPSIS
 *
 *	static void FreeState (state)
 *	struct state *state;
1611
 *      int free_state;
1612 1613 1614 1615
 *
 *  DESCRIPTION
 *
 *	Deallocates the memory allocated for various information in a
1616
 *	state. If free_state is set, also free 'state'
1617 1618
 *
 */
1619
static void FreeState(CODE_STATE *cs, int free_state)
1620
{
1621
  struct settings *state= cs->stack;
Eugene Kosov's avatar
Eugene Kosov committed
1622
  LockIfInitSettings(cs);
1623
  if (!is_shared(state, keywords))
1624
  {
1625
    FreeList(state->keywords);
1626 1627
    state->keywords= NULL;
  }
Eugene Kosov's avatar
Eugene Kosov committed
1628
  UnlockIfInitSettings(cs);
1629 1630 1631 1632
  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
  int res= DONT_TRACE;
1707
  if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) &&
1708
      InList(cs->stack->processes, cs->process, 0) & (MATCHED|INCLUDE))
Monty's avatar
Monty committed
1709
  {
1710
    switch(InList(cs->stack->functions, cs->func, 0)) {
Monty's avatar
Monty committed
1711 1712 1713 1714 1715 1716
    case INCLUDE|SUBDIR:
      res= ENABLE_TRACE;
      break;
    case INCLUDE:
      res= DO_TRACE;
      break;
1717 1718
    case MATCHED|SUBDIR:
    case NOT_MATCHED|SUBDIR:
Monty's avatar
Monty committed
1719 1720 1721
    case MATCHED:
      res= (framep_trace_flag(cs, cs->framep) ? DO_TRACE : DONT_TRACE);
      break;
1722
    case EXCLUDE:
Monty's avatar
Monty committed
1723 1724 1725 1726 1727 1728
    case NOT_MATCHED:
      res= DONT_TRACE;
      break;
    case EXCLUDE|SUBDIR:
      res= DISABLE_TRACE;
      break;
1729
    }
Monty's avatar
Monty committed
1730 1731
  }
  return res;
unknown's avatar
unknown committed
1732 1733
}

Monty's avatar
Monty committed
1734

unknown's avatar
unknown committed
1735 1736
FILE *_db_fp_(void)
{
1737
  CODE_STATE *cs;
unknown's avatar
unknown committed
1738
  get_code_state_or_return NULL;
1739
  return cs->stack->out_file->file;
unknown's avatar
unknown committed
1740 1741
}

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

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

Monty's avatar
Monty committed
1768 1769
  if (!(DEBUGGING && (DoTrace(cs) & DO_TRACE)))
    return 0;
Eugene Kosov's avatar
Eugene Kosov committed
1770
  LockIfInitSettings(cs);
Monty's avatar
Monty committed
1771
  res= (InList(cs->stack->keywords, keyword, strict) & match);
Eugene Kosov's avatar
Eugene Kosov committed
1772
  UnlockIfInitSettings(cs);
Monty's avatar
Monty committed
1773
  return res != 0;
Alfranio Correia's avatar
Alfranio Correia committed
1774 1775
}

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

unknown's avatar
unknown committed
1797
static void Indent(CODE_STATE *cs, int indent)
unknown's avatar
unknown committed
1798
{
Sergei Golubchik's avatar
Sergei Golubchik committed
1799
  int count;
unknown's avatar
unknown committed
1800

1801
  indent= MY_MAX(indent-1-cs->stack->sub_level,0)*INDENT;
unknown's avatar
unknown committed
1802
  for (count= 0; count < indent ; count++)
unknown's avatar
unknown committed
1803 1804
  {
    if ((count % INDENT) == 0)
1805
      fputc('|',cs->stack->out_file->file);
unknown's avatar
unknown committed
1806
    else
1807
      fputc(' ',cs->stack->out_file->file);
unknown's avatar
unknown committed
1808 1809 1810 1811 1812 1813 1814
  }
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1815
 *      FreeList    free all memory associated with a linked list
unknown's avatar
unknown committed
1816 1817 1818
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1819 1820
 *      static VOID FreeList(linkp)
 *      struct link *linkp;
unknown's avatar
unknown committed
1821 1822 1823
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1824 1825
 *      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
1826 1827 1828
 *
 */

unknown's avatar
unknown committed
1829
static void FreeList(struct link *linkp)
unknown's avatar
unknown committed
1830
{
Sergei Golubchik's avatar
Sergei Golubchik committed
1831
  struct link *old;
unknown's avatar
unknown committed
1832

unknown's avatar
unknown committed
1833 1834 1835 1836
  while (linkp != NULL)
  {
    old= linkp;
    linkp= linkp->next_link;
1837
    free((void*) old);
unknown's avatar
unknown committed
1838 1839 1840 1841 1842 1843 1844
  }
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1845
 *      DoPrefix    print debugger line prefix prior to indentation
unknown's avatar
unknown committed
1846 1847 1848
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1849 1850
 *      static VOID DoPrefix(_line_)
 *      int _line_;
unknown's avatar
unknown committed
1851 1852 1853
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1854 1855 1856 1857
 *      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
1858 1859 1860
 *
 */

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


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1913
 *      DBUGOpenFile    open new output stream for debugger output
unknown's avatar
unknown committed
1914 1915 1916
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1917 1918
 *      static VOID DBUGOpenFile(name)
 *      char *name;
unknown's avatar
unknown committed
1919 1920 1921
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1922 1923
 *      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
1924 1925 1926
 *
 */

unknown's avatar
unknown committed
1927 1928
static void DBUGOpenFile(CODE_STATE *cs,
                         const char *name,const char *end,int append)
unknown's avatar
unknown committed
1929
{
Sergei Golubchik's avatar
Sergei Golubchik committed
1930
  FILE *fp;
unknown's avatar
unknown committed
1931 1932 1933

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

/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
1980
 *      DBUGCloseFile    close the debug output stream
unknown's avatar
unknown committed
1981 1982 1983
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
1984 1985
 *      static VOID DBUGCloseFile(fp)
 *      FILE *fp;
unknown's avatar
unknown committed
1986 1987 1988
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
1989 1990
 *      Closes the debug output stream unless it is standard output
 *      or standard error.
unknown's avatar
unknown committed
1991 1992 1993
 *
 */

1994
static void DBUGCloseFile(CODE_STATE *cs, sFILE *new_value)
unknown's avatar
unknown committed
1995
{
1996
  sFILE *fp;
1997
  if (!cs || !cs->stack || !(fp= cs->stack->out_file))
1998 1999
    return;

2000
  if (fp != sstdout && fp != sstderr && --fp->used == 0)
unknown's avatar
unknown committed
2001
  {
2002 2003
    if (fclose(fp->file) == EOF)
    {
Eugene Kosov's avatar
Eugene Kosov committed
2004
      LockMutex(cs);
2005 2006
      (void) fprintf(stderr, ERR_CLOSE, cs->process);
      perror("");
Eugene Kosov's avatar
Eugene Kosov committed
2007
      UnlockMutex(cs);
2008 2009 2010 2011 2012
    }
    else
    {
      free(fp);
    }
unknown's avatar
unknown committed
2013
  }
2014
  cs->stack->out_file= new_value;
unknown's avatar
unknown committed
2015 2016 2017 2018 2019 2020
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2021
 *      DbugExit    print error message and exit
unknown's avatar
unknown committed
2022 2023 2024
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2025 2026
 *      static VOID DbugExit(why)
 *      char *why;
unknown's avatar
unknown committed
2027 2028 2029
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2030 2031 2032 2033
 *      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
2034 2035 2036
 *
 */

unknown's avatar
unknown committed
2037
static void DbugExit(const char *why)
unknown's avatar
unknown committed
2038
{
unknown's avatar
unknown committed
2039 2040 2041
  CODE_STATE *cs=code_state();
  (void) fprintf(stderr, ERR_ABORT, cs ? cs->process : "(null)", why);
  (void) fflush(stderr);
2042
  DBUG_ABORT();
unknown's avatar
unknown committed
2043 2044 2045 2046 2047 2048
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2049
 *      DbugMalloc    allocate memory for debugger runtime support
unknown's avatar
unknown committed
2050 2051 2052
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2053 2054
 *      static long *DbugMalloc(size)
 *      int size;
unknown's avatar
unknown committed
2055 2056 2057
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2058 2059 2060 2061 2062 2063
 *      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
2064 2065 2066
 *
 */

unknown's avatar
unknown committed
2067
static char *DbugMalloc(size_t size)
unknown's avatar
unknown committed
2068
{
Sergei Golubchik's avatar
Sergei Golubchik committed
2069
  char *new_malloc;
unknown's avatar
unknown committed
2070

2071
  if (!(new_malloc= (char*) malloc(size)))
unknown's avatar
unknown committed
2072 2073
    DbugExit("out of memory");
  return new_malloc;
unknown's avatar
unknown committed
2074 2075 2076 2077
}


/*
2078
 *     strtok lookalike - splits on ':', magically handles ::, :\ and :/
unknown's avatar
unknown committed
2079 2080
 */

unknown's avatar
unknown committed
2081
static const char *DbugStrTok(const char *s)
unknown's avatar
unknown committed
2082
{
2083 2084
  while (s[0] && (s[0] != ':' ||
                  (s[1] == '\\' || s[1] == '/' || (s[1] == ':' && s++))))
unknown's avatar
unknown committed
2085 2086
    s++;
  return s;
unknown's avatar
unknown committed
2087 2088 2089 2090 2091 2092
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2093
 *      BaseName    strip leading pathname components from name
unknown's avatar
unknown committed
2094 2095 2096
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2097 2098
 *      static char *BaseName(pathname)
 *      char *pathname;
unknown's avatar
unknown committed
2099 2100 2101
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2102 2103 2104
 *      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
2105 2106 2107
 *
 */

unknown's avatar
unknown committed
2108
static const char *BaseName(const char *pathname)
unknown's avatar
unknown committed
2109
{
Sergei Golubchik's avatar
Sergei Golubchik committed
2110
  const char *base;
unknown's avatar
unknown committed
2111

unknown's avatar
unknown committed
2112
  base= strrchr(pathname, FN_LIBCHAR);
unknown's avatar
unknown committed
2113
  if (base++ == NullS)
unknown's avatar
unknown committed
2114 2115
    base= pathname;
  return base;
unknown's avatar
unknown committed
2116 2117 2118 2119 2120 2121
}


/*
 *  FUNCTION
 *
unknown's avatar
unknown committed
2122
 *      Writable    test to see if a pathname is writable/creatable
unknown's avatar
unknown committed
2123 2124 2125
 *
 *  SYNOPSIS
 *
unknown's avatar
unknown committed
2126 2127
 *      static BOOLEAN Writable(pathname)
 *      char *pathname;
unknown's avatar
unknown committed
2128 2129 2130
 *
 *  DESCRIPTION
 *
unknown's avatar
unknown committed
2131 2132 2133 2134 2135
 *      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
2136
 *
unknown's avatar
unknown committed
2137 2138
 *      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
2139 2140 2141 2142 2143 2144
 *
 */


#ifndef Writable

2145
static BOOLEAN Writable(const char *pathname)
unknown's avatar
unknown committed
2146
{
Sergei Golubchik's avatar
Sergei Golubchik committed
2147 2148
  BOOLEAN granted;
  char *lastslash;
unknown's avatar
unknown committed
2149

unknown's avatar
unknown committed
2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166
  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
2167
  }
unknown's avatar
unknown committed
2168
  return granted;
unknown's avatar
unknown committed
2169 2170 2171 2172
}
#endif

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

2178
static void DbugFlush(CODE_STATE *cs)
unknown's avatar
unknown committed
2179
{
unknown's avatar
unknown committed
2180
  if (cs->stack->flags & FLUSH_ON_WRITE)
unknown's avatar
unknown committed
2181
  {
2182
    (void) fflush(cs->stack->out_file->file);
2183 2184
    if (cs->stack->delay)
      (void) Delay(cs->stack->delay);
unknown's avatar
unknown committed
2185
  }
2186 2187 2188 2189 2190 2191 2192
} /* DbugFlush */


/* For debugging */

void _db_flush_()
{
unknown's avatar
unknown committed
2193
  CODE_STATE *cs;
2194
  get_code_state_or_return;
Monty's avatar
Monty committed
2195 2196 2197 2198
  if (DEBUGGING)
  {
    (void) fflush(cs->stack->out_file->file);
  }
2199
}
unknown's avatar
unknown committed
2200

Monty's avatar
Monty committed
2201

2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220
#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
2221
void _db_lock_file_()
unknown's avatar
unknown committed
2222
{
2223
  CODE_STATE *cs;
unknown's avatar
unknown committed
2224
  get_code_state_or_return;
2225
  LockMutex(cs);
unknown's avatar
unknown committed
2226 2227
}

unknown's avatar
unknown committed
2228
void _db_unlock_file_()
unknown's avatar
unknown committed
2229
{
2230
  CODE_STATE *cs;
unknown's avatar
unknown committed
2231
  get_code_state_or_return;
2232
  UnlockMutex(cs);
unknown's avatar
unknown committed
2233 2234
}

Alfranio Correia's avatar
Alfranio Correia committed
2235 2236
const char* _db_get_func_(void)
{
2237
  CODE_STATE *cs;
Alfranio Correia's avatar
Alfranio Correia committed
2238 2239 2240 2241
  get_code_state_or_return NULL;
  return cs->func;
}

2242 2243 2244 2245 2246 2247

static int default_my_dbug_sanity(void)
{
  return 0;
}

2248 2249 2250 2251 2252
extern my_bool my_assert;
ATTRIBUTE_COLD
my_bool _db_my_assert(const char *file, int line, const char *msg)
{
  my_bool a = my_assert;
2253
  _db_flush_();
2254 2255 2256 2257
  if (!a)
    fprintf(stderr, "%s:%d: assert: %s\n", file, line, msg);
  return a;
}
2258 2259 2260 2261 2262 2263 2264 2265 2266 2267
#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
2268
#endif /* DBUG_OFF */
2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288

/*
  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 */