~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to dbug/dbug.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************************************
 
2
 *                                                                            *
 
3
 *                                 N O T I C E                                *
 
4
 *                                                                            *
 
5
 *                    Copyright Abandoned, 1987, Fred Fish                    *
 
6
 *                                                                            *
 
7
 *                                                                            *
 
8
 *      This previously copyrighted work has been placed into the  public     *
 
9
 *      domain  by  the  author  and  may be freely used for any purpose,     *
 
10
 *      private or commercial.                                                *
 
11
 *                                                                            *
 
12
 *      Because of the number of inquiries I was receiving about the  use     *
 
13
 *      of this product in commercially developed works I have decided to     *
 
14
 *      simply make it public domain to further its unrestricted use.   I     *
 
15
 *      specifically  would  be  most happy to see this material become a     *
 
16
 *      part of the standard Unix distributions by AT&T and the  Berkeley     *
 
17
 *      Computer  Science  Research Group, and a standard part of the GNU     *
 
18
 *      system from the Free Software Foundation.                             *
 
19
 *                                                                            *
 
20
 *      I would appreciate it, as a courtesy, if this notice is  left  in     *
 
21
 *      all copies and derivative works.  Thank you.                          *
 
22
 *                                                                            *
 
23
 *      The author makes no warranty of any kind  with  respect  to  this     *
 
24
 *      product  and  explicitly disclaims any implied warranties of mer-     *
 
25
 *      chantability or fitness for any particular purpose.                   *
 
26
 *                                                                            *
 
27
 ******************************************************************************
 
28
 */
 
29
 
 
30
/*
 
31
 *  FILE
 
32
 *
 
33
 *      dbug.c   runtime support routines for dbug package
 
34
 *
 
35
 *  SCCS
 
36
 *
 
37
 *      @(#)dbug.c      1.25    7/25/89
 
38
 *
 
39
 *  DESCRIPTION
 
40
 *
 
41
 *      These are the runtime support routines for the dbug package.
 
42
 *      The dbug package has two main components; the user include
 
43
 *      file containing various macro definitions, and the runtime
 
44
 *      support routines which are called from the macro expansions.
 
45
 *
 
46
 *      Externally visible functions in the runtime support module
 
47
 *      use the naming convention pattern "_db_xx...xx_", thus
 
48
 *      they are unlikely to collide with user defined function names.
 
49
 *
 
50
 *  AUTHOR(S)
 
51
 *
 
52
 *      Fred Fish               (base code)
 
53
 *      Enhanced Software Technologies, Tempe, AZ
 
54
 *      asuvax!mcdphx!estinc!fnf
 
55
 *
 
56
 *      Binayak Banerjee        (profiling enhancements)
 
57
 *      seismo!bpa!sjuvax!bbanerje
 
58
 *
 
59
 *      Michael Widenius:
 
60
 *      DBUG_DUMP       - To dump a block of memory.
 
61
 *      PUSH_FLAG "O"   - To be used insted of "o" if we
 
62
 *                        want flushing after each write
 
63
 *      PUSH_FLAG "A"   - as 'O', but we will append to the out file instead
 
64
 *                        of creating a new one.
 
65
 *      Check of malloc on entry/exit (option "S")
 
66
 *
 
67
 *      DBUG_EXECUTE_IF
 
68
 *      incremental mode (-#+t:-d,info ...)
 
69
 *      DBUG_SET, _db_explain_
 
70
 *      thread-local settings
 
71
 *
 
72
 */
 
73
 
 
74
 
 
75
#include <my_global.h>
 
76
#include <m_string.h>
 
77
#include <errno.h>
 
78
#if defined(MSDOS) || defined(__WIN__)
 
79
#include <process.h>
 
80
#endif
 
81
 
 
82
 
 
83
#ifndef DBUG_OFF
 
84
 
 
85
 
 
86
/*
 
87
 *            Manifest constants which may be "tuned" if desired.
 
88
 */
 
89
 
 
90
#define PRINTBUF              1024    /* Print buffer size */
 
91
#define INDENT                2       /* Indentation per trace level */
 
92
#define MAXDEPTH              200     /* Maximum trace depth default */
 
93
 
 
94
/*
 
95
 *      The following flags are used to determine which
 
96
 *      capabilities the user has enabled with the settings
 
97
 *      push macro.
 
98
 */
 
99
 
 
100
#define TRACE_ON        000001  /* Trace enabled */
 
101
#define DEBUG_ON        000002  /* Debug enabled */
 
102
#define FILE_ON         000004  /* File name print enabled */
 
103
#define LINE_ON         000010  /* Line number print enabled */
 
104
#define DEPTH_ON        000020  /* Function nest level print enabled */
 
105
#define PROCESS_ON      000040  /* Process name print enabled */
 
106
#define NUMBER_ON       000100  /* Number each line of output */
 
107
#define PROFILE_ON      000200  /* Print out profiling code */
 
108
#define PID_ON          000400  /* Identify each line with process id */
 
109
#define TIMESTAMP_ON    001000  /* timestamp every line of output */
 
110
#define SANITY_CHECK_ON 002000  /* Check safemalloc on DBUG_ENTER */
 
111
#define FLUSH_ON_WRITE  004000  /* Flush on every write */
 
112
#define OPEN_APPEND     010000  /* Open for append      */
 
113
 
 
114
#define TRACING (cs->stack->flags & TRACE_ON)
 
115
#define DEBUGGING (cs->stack->flags & DEBUG_ON)
 
116
#define PROFILING (cs->stack->flags & PROFILE_ON)
 
117
 
 
118
/*
 
119
 *      Typedefs to make things more obvious.
 
120
 */
 
121
 
 
122
#ifndef __WIN__
 
123
typedef int BOOLEAN;
 
124
#else
 
125
#define BOOLEAN BOOL
 
126
#endif
 
127
 
 
128
/*
 
129
 *      Make it easy to change storage classes if necessary.
 
130
 */
 
131
 
 
132
#define IMPORT extern           /* Names defined externally */
 
133
#define EXPORT                  /* Allocated here, available globally */
 
134
#define AUTO auto               /* Names to be allocated on stack */
 
135
#define REGISTER register       /* Names to be placed in registers */
 
136
 
 
137
/*
 
138
 * The default file for profiling.  Could also add another flag
 
139
 * (G?) which allowed the user to specify this.
 
140
 *
 
141
 * If the automatic variables get allocated on the stack in
 
142
 * reverse order from their declarations, then define AUTOS_REVERSE.
 
143
 * This is used by the code that keeps track of stack usage.  For
 
144
 * forward allocation, the difference in the dbug frame pointers
 
145
 * represents stack used by the callee function.  For reverse allocation,
 
146
 * the difference represents stack used by the caller function.
 
147
 *
 
148
 */
 
149
 
 
150
#define PROF_FILE       "dbugmon.out"
 
151
#define PROF_EFMT       "E\t%ld\t%s\n"
 
152
#define PROF_SFMT       "S\t%lx\t%lx\t%s\n"
 
153
#define PROF_XFMT       "X\t%ld\t%s\n"
 
154
 
 
155
#ifdef M_I386           /* predefined by xenix 386 compiler */
 
156
#define AUTOS_REVERSE 1
 
157
#endif
 
158
 
 
159
/*
 
160
 *      Externally supplied functions.
 
161
 */
 
162
 
 
163
#ifndef HAVE_PERROR
 
164
static void perror();          /* Fake system/library error print routine */
 
165
#endif
 
166
 
 
167
IMPORT int _sanity(const char *file,uint line); /* safemalloc sanity checker */
 
168
 
 
169
/*
 
170
 *      The user may specify a list of functions to trace or
 
171
 *      debug.  These lists are kept in a linear linked list,
 
172
 *      a very simple implementation.
 
173
 */
 
174
 
 
175
struct link {
 
176
    struct link *next_link;   /* Pointer to the next link */
 
177
    char   str[1];        /* Pointer to link's contents */
 
178
};
 
179
 
 
180
/*
 
181
 *      Debugging settings can be pushed or popped off of a
 
182
 *      stack which is implemented as a linked list.  Note
 
183
 *      that the head of the list is the current settings and the
 
184
 *      stack is pushed by adding a new settings to the head of the
 
185
 *      list or popped by removing the first link.
 
186
 *
 
187
 *      Note: if out_file is NULL, the other fields are not initialized at all!
 
188
 */
 
189
 
 
190
struct settings {
 
191
  int flags;                    /* Current settings flags */
 
192
  int maxdepth;                 /* Current maximum trace depth */
 
193
  uint delay;                   /* Delay after each output line */
 
194
  int sub_level;                /* Sub this from code_state->level */
 
195
  FILE *out_file;               /* Current output stream */
 
196
  FILE *prof_file;              /* Current profiling stream */
 
197
  char name[FN_REFLEN];         /* Name of output file */
 
198
  struct link *functions;       /* List of functions */
 
199
  struct link *p_functions;     /* List of profiled functions */
 
200
  struct link *keywords;        /* List of debug keywords */
 
201
  struct link *processes;       /* List of process names */
 
202
  struct settings *next;        /* Next settings in the list */
 
203
};
 
204
 
 
205
#define is_shared(S, V) ((S)->next && (S)->next->V == (S)->V)
 
206
 
 
207
/*
 
208
 *      Local variables not seen by user.
 
209
 */
 
210
 
 
211
 
 
212
static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */
 
213
static struct settings init_settings;
 
214
static const char *db_process= 0;/* Pointer to process name; argv[0] */
 
215
 
 
216
typedef struct _db_code_state_ {
 
217
  const char *process;          /* Pointer to process name; usually argv[0] */
 
218
  const char *func;             /* Name of current user function */
 
219
  const char *file;             /* Name of current user file */
 
220
  char **framep;                /* Pointer to current frame */
 
221
  struct settings *stack;       /* debugging settings */
 
222
  const char *jmpfunc;          /* Remember current function for setjmp */
 
223
  const char *jmpfile;          /* Remember current file for setjmp */
 
224
  int lineno;                   /* Current debugger output line number */
 
225
  int level;                    /* Current function nesting level */
 
226
  int jmplevel;                 /* Remember nesting level at setjmp() */
 
227
 
 
228
/*
 
229
 *      The following variables are used to hold the state information
 
230
 *      between the call to _db_pargs_() and _db_doprnt_(), during
 
231
 *      expansion of the DBUG_PRINT macro.  This is the only macro
 
232
 *      that currently uses these variables.
 
233
 *
 
234
 *      These variables are currently used only by _db_pargs_() and
 
235
 *      _db_doprnt_().
 
236
 */
 
237
 
 
238
  uint u_line;                  /* User source code line number */
 
239
  int  locked;                  /* If locked with _db_lock_file_ */
 
240
  const char *u_keyword;        /* Keyword for current macro */
 
241
} CODE_STATE;
 
242
 
 
243
/*
 
244
  The test below is so we could call functions with DBUG_ENTER before
 
245
  my_thread_init().
 
246
*/
 
247
#define get_code_state_or_return if (!cs && !((cs=code_state()))) return
 
248
 
 
249
        /* Handling lists */
 
250
static struct link *ListAdd(struct link *, const char *, const char *);
 
251
static struct link *ListDel(struct link *, const char *, const char *);
 
252
static struct link *ListCopy(struct link *);
 
253
static void FreeList(struct link *linkp);
 
254
 
 
255
        /* OpenClose debug output stream */
 
256
static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int);
 
257
static void DBUGCloseFile(CODE_STATE *cs, FILE *fp);
 
258
        /* Push current debug settings */
 
259
static void PushState(CODE_STATE *cs);
 
260
        /* Free memory associated with debug state. */
 
261
static void FreeState (CODE_STATE *cs, struct settings *state, int free_state);
 
262
        /* Test for tracing enabled */
 
263
static BOOLEAN DoTrace(CODE_STATE *cs);
 
264
 
 
265
        /* Test to see if file is writable */
 
266
#if !(!defined(HAVE_ACCESS) || defined(MSDOS))
 
267
static BOOLEAN Writable(const char *pathname);
 
268
        /* Change file owner and group */
 
269
static void ChangeOwner(CODE_STATE *cs, char *pathname);
 
270
        /* Allocate memory for runtime support */
 
271
#endif
 
272
 
 
273
static void DoPrefix(CODE_STATE *cs, uint line);
 
274
 
 
275
static char *DbugMalloc(size_t size);
 
276
static const char *BaseName(const char *pathname);
 
277
static void Indent(CODE_STATE *cs, int indent);
 
278
static BOOLEAN InList(struct link *linkp,const char *cp);
 
279
static void dbug_flush(CODE_STATE *);
 
280
static void DbugExit(const char *why);
 
281
static const char *DbugStrTok(const char *s);
 
282
 
 
283
#ifndef THREAD
 
284
        /* Open profile output stream */
 
285
static FILE *OpenProfile(CODE_STATE *cs, const char *name);
 
286
        /* Profile if asked for it */
 
287
static BOOLEAN DoProfile(CODE_STATE *);
 
288
        /* Return current user time (ms) */
 
289
static unsigned long Clock(void);
 
290
#endif
 
291
 
 
292
/*
 
293
 *      Miscellaneous printf format strings.
 
294
 */
 
295
 
 
296
#define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
 
297
#define ERR_OPEN "%s: can't open debug output stream \"%s\": "
 
298
#define ERR_CLOSE "%s: can't close debug file: "
 
299
#define ERR_ABORT "%s: debugger aborting because %s\n"
 
300
#define ERR_CHOWN "%s: can't change owner/group of \"%s\": "
 
301
 
 
302
/*
 
303
 *      Macros and defines for testing file accessibility under UNIX and MSDOS.
 
304
 */
 
305
 
 
306
#undef EXISTS
 
307
#if !defined(HAVE_ACCESS) || defined(MSDOS)
 
308
#define EXISTS(pathname) (FALSE)        /* Assume no existance */
 
309
#define Writable(name) (TRUE)
 
310
#else
 
311
#define EXISTS(pathname)         (access(pathname, F_OK) == 0)
 
312
#define WRITABLE(pathname)       (access(pathname, W_OK) == 0)
 
313
#endif
 
314
#ifndef MSDOS
 
315
#define ChangeOwner(cs,name)
 
316
#endif
 
317
 
 
318
 
 
319
/*
 
320
** Macros to allow dbugging with threads
 
321
*/
 
322
 
 
323
#ifdef THREAD
 
324
#include <my_pthread.h>
 
325
pthread_mutex_t THR_LOCK_dbug;
 
326
 
 
327
static CODE_STATE *code_state(void)
 
328
{
 
329
  CODE_STATE *cs=0;
 
330
  struct st_my_thread_var *tmp;
 
331
 
 
332
  if (!init_done)
 
333
  {
 
334
    pthread_mutex_init(&THR_LOCK_dbug,MY_MUTEX_INIT_FAST);
 
335
    bzero(&init_settings, sizeof(init_settings));
 
336
    init_settings.out_file=stderr;
 
337
    init_settings.flags=OPEN_APPEND;
 
338
    init_done=TRUE;
 
339
  }
 
340
 
 
341
  if ((tmp=my_thread_var))
 
342
  {
 
343
    if (!(cs=(CODE_STATE *) tmp->dbug))
 
344
    {
 
345
      cs=(CODE_STATE*) DbugMalloc(sizeof(*cs));
 
346
      bzero((uchar*) cs,sizeof(*cs));
 
347
      cs->process= db_process ? db_process : "dbug";
 
348
      cs->func="?func";
 
349
      cs->file="?file";
 
350
      cs->stack=&init_settings;
 
351
      tmp->dbug= (void*) cs;
 
352
    }
 
353
  }
 
354
  return cs;
 
355
}
 
356
 
 
357
#else /* !THREAD */
 
358
 
 
359
static CODE_STATE static_code_state=
 
360
{
 
361
  "dbug", "?func", "?file", NULL, &init_settings,
 
362
  NullS, NullS, 0,0,0,0,0,NullS
 
363
};
 
364
 
 
365
static CODE_STATE *code_state(void)
 
366
{
 
367
  if (!init_done)
 
368
  {
 
369
    bzero(&init_settings, sizeof(init_settings));
 
370
    init_settings.out_file=stderr;
 
371
    init_settings.flags=OPEN_APPEND;
 
372
    init_done=TRUE;
 
373
  }
 
374
  return &static_code_state;
 
375
}
 
376
 
 
377
#define pthread_mutex_lock(A) {}
 
378
#define pthread_mutex_unlock(A) {}
 
379
#endif
 
380
 
 
381
/*
 
382
 *      Translate some calls among different systems.
 
383
 */
 
384
 
 
385
#ifdef HAVE_SLEEP
 
386
/* sleep() wants seconds */
 
387
#define Delay(A) sleep(((uint) A)/10)
 
388
#else
 
389
#define Delay(A) (0)
 
390
#endif
 
391
 
 
392
/*
 
393
 *  FUNCTION
 
394
 *
 
395
 *      _db_process_       give the name to the current process/thread
 
396
 *
 
397
 *  SYNOPSIS
 
398
 *
 
399
 *      VOID _process_(name)
 
400
 *      char *name;
 
401
 *
 
402
 */
 
403
 
 
404
void _db_process_(const char *name)
 
405
{
 
406
  CODE_STATE *cs=0;
 
407
 
 
408
  if (!db_process)
 
409
    db_process= name;
 
410
  
 
411
  get_code_state_or_return;
 
412
  cs->process= name;
 
413
}
 
414
 
 
415
 
 
416
/*
 
417
 *  FUNCTION
 
418
 *
 
419
 *      DbugParse       parse control string and set current debugger setting
 
420
 *
 
421
 *  DESCRIPTION
 
422
 *
 
423
 *      Given pointer to a debug control string in "control",
 
424
 *      parses the control string, and sets
 
425
 *      up a current debug settings.
 
426
 *
 
427
 *      The debug control string is a sequence of colon separated fields
 
428
 *      as follows:
 
429
 *
 
430
 *              [+]<field_1>:<field_2>:...:<field_N>
 
431
 *
 
432
 *      Each field consists of a mandatory flag character followed by
 
433
 *      an optional "," and comma separated list of modifiers:
 
434
 *
 
435
 *              [sign]flag[,modifier,modifier,...,modifier]
 
436
 *
 
437
 *      See the manual for the list of supported signs, flags, and modifiers
 
438
 *
 
439
 *      For convenience, any leading "-#" is stripped off.
 
440
 *
 
441
 */
 
442
 
 
443
static void DbugParse(CODE_STATE *cs, const char *control)
 
444
{
 
445
  const char *end;
 
446
  int rel=0;
 
447
  struct settings *stack;
 
448
 
 
449
  get_code_state_or_return;
 
450
  stack= cs->stack;
 
451
 
 
452
  if (control[0] == '-' && control[1] == '#')
 
453
    control+=2;
 
454
 
 
455
  rel= control[0] == '+' || control[0] == '-';
 
456
  if ((!rel || (!stack->out_file && !stack->next)))
 
457
  {
 
458
    stack->flags= 0;
 
459
    stack->delay= 0;
 
460
    stack->maxdepth= 0;
 
461
    stack->sub_level= 0;
 
462
    stack->out_file= stderr;
 
463
    stack->prof_file= NULL;
 
464
    stack->functions= NULL;
 
465
    stack->p_functions= NULL;
 
466
    stack->keywords= NULL;
 
467
    stack->processes= NULL;
 
468
  }
 
469
  else if (!stack->out_file)
 
470
  {
 
471
    stack->flags= stack->next->flags;
 
472
    stack->delay= stack->next->delay;
 
473
    stack->maxdepth= stack->next->maxdepth;
 
474
    stack->sub_level= stack->next->sub_level;
 
475
    strcpy(stack->name, stack->next->name);
 
476
    stack->out_file= stack->next->out_file;
 
477
    stack->prof_file= stack->next->prof_file;
 
478
    if (stack->next == &init_settings)
 
479
    {
 
480
      /* never share with the global parent - it can change under your feet */
 
481
      stack->functions= ListCopy(init_settings.functions);
 
482
      stack->p_functions= ListCopy(init_settings.p_functions);
 
483
      stack->keywords= ListCopy(init_settings.keywords);
 
484
      stack->processes= ListCopy(init_settings.processes);
 
485
    }
 
486
    else
 
487
    {
 
488
      stack->functions= stack->next->functions;
 
489
      stack->p_functions= stack->next->p_functions;
 
490
      stack->keywords= stack->next->keywords;
 
491
      stack->processes= stack->next->processes;
 
492
    }
 
493
  }
 
494
 
 
495
  end= DbugStrTok(control);
 
496
  while (1)
 
497
  {
 
498
    int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0;
 
499
    if (sign) control++;
 
500
    if (!rel) sign=0;
 
501
    c= *control++;
 
502
    if (*control == ',') control++;
 
503
    /* XXX when adding new cases here, don't forget _db_explain_ ! */
 
504
    switch (c) {
 
505
    case 'd':
 
506
      if (sign < 0 && control == end)
 
507
      {
 
508
        if (!is_shared(stack, keywords))
 
509
          FreeList(stack->keywords);
 
510
        stack->keywords=NULL;
 
511
        stack->flags &= ~DEBUG_ON;
 
512
        break;
 
513
      }
 
514
      if (rel && is_shared(stack, keywords))
 
515
        stack->keywords= ListCopy(stack->keywords);
 
516
      if (sign < 0)
 
517
      {
 
518
        if (DEBUGGING)
 
519
          stack->keywords= ListDel(stack->keywords, control, end);
 
520
      break;
 
521
      }
 
522
      stack->keywords= ListAdd(stack->keywords, control, end);
 
523
      stack->flags |= DEBUG_ON;
 
524
      break;
 
525
    case 'D':
 
526
      stack->delay= atoi(control);
 
527
      break;
 
528
    case 'f':
 
529
      if (sign < 0 && control == end)
 
530
      {
 
531
        if (!is_shared(stack,functions))
 
532
          FreeList(stack->functions);
 
533
        stack->functions=NULL;
 
534
        break;
 
535
      }
 
536
      if (rel && is_shared(stack,functions))
 
537
        stack->functions= ListCopy(stack->functions);
 
538
      if (sign < 0)
 
539
        stack->functions= ListDel(stack->functions, control, end);
 
540
      else
 
541
        stack->functions= ListAdd(stack->functions, control, end);
 
542
      break;
 
543
    case 'F':
 
544
      if (sign < 0)
 
545
        stack->flags &= ~FILE_ON;
 
546
      else
 
547
        stack->flags |= FILE_ON;
 
548
      break;
 
549
    case 'i':
 
550
      if (sign < 0)
 
551
        stack->flags &= ~PID_ON;
 
552
      else
 
553
        stack->flags |= PID_ON;
 
554
      break;
 
555
#ifndef THREAD
 
556
    case 'g':
 
557
      if (OpenProfile(cs, PROF_FILE))
 
558
      {
 
559
        stack->flags |= PROFILE_ON;
 
560
        stack->p_functions= ListAdd(stack->p_functions, control, end);
 
561
      }
 
562
      break;
 
563
#endif
 
564
    case 'L':
 
565
      if (sign < 0)
 
566
        stack->flags &= ~LINE_ON;
 
567
      else
 
568
        stack->flags |= LINE_ON;
 
569
      break;
 
570
    case 'n':
 
571
      if (sign < 0)
 
572
        stack->flags &= ~DEPTH_ON;
 
573
      else
 
574
        stack->flags |= DEPTH_ON;
 
575
      break;
 
576
    case 'N':
 
577
      if (sign < 0)
 
578
        stack->flags &= ~NUMBER_ON;
 
579
      else
 
580
        stack->flags |= NUMBER_ON;
 
581
      break;
 
582
    case 'A':
 
583
    case 'O':
 
584
      stack->flags |= FLUSH_ON_WRITE;
 
585
      /* fall through */
 
586
    case 'a':
 
587
    case 'o':
 
588
      if (sign < 0)
 
589
      {
 
590
        if (!is_shared(stack, out_file))
 
591
          DBUGCloseFile(cs, stack->out_file);
 
592
        stack->flags &= ~FLUSH_ON_WRITE;
 
593
        stack->out_file= stderr;
 
594
        break;
 
595
      }
 
596
      if (c == 'a' || c == 'A')
 
597
        stack->flags |= OPEN_APPEND;
 
598
      else
 
599
        stack->flags &= ~OPEN_APPEND;
 
600
      if (control != end)
 
601
        DBUGOpenFile(cs, control, end, stack->flags & OPEN_APPEND);
 
602
      else
 
603
        DBUGOpenFile(cs, "-",0,0);
 
604
      break;
 
605
    case 'p':
 
606
      if (sign < 0 && control == end)
 
607
      {
 
608
        if (!is_shared(stack,processes))
 
609
          FreeList(stack->processes);
 
610
        stack->processes=NULL;
 
611
        break;
 
612
      }
 
613
      if (rel && is_shared(stack, processes))
 
614
        stack->processes= ListCopy(stack->processes);
 
615
      if (sign < 0)
 
616
        stack->processes= ListDel(stack->processes, control, end);
 
617
      else
 
618
        stack->processes= ListAdd(stack->processes, control, end);
 
619
      break;
 
620
    case 'P':
 
621
      if (sign < 0)
 
622
        stack->flags &= ~PROCESS_ON;
 
623
      else
 
624
        stack->flags |= PROCESS_ON;
 
625
      break;
 
626
    case 'r':
 
627
      stack->sub_level= cs->level;
 
628
      break;
 
629
    case 't':
 
630
      if (sign < 0)
 
631
      {
 
632
        if (control != end)
 
633
          stack->maxdepth-= atoi(control);
 
634
        else
 
635
          stack->maxdepth= 0;
 
636
      }
 
637
      else
 
638
      {
 
639
        if (control != end)
 
640
          stack->maxdepth+= atoi(control);
 
641
        else
 
642
          stack->maxdepth= MAXDEPTH;
 
643
      }
 
644
      if (stack->maxdepth > 0)
 
645
        stack->flags |= TRACE_ON;
 
646
      else
 
647
        stack->flags &= ~TRACE_ON;
 
648
      break;
 
649
    case 'T':
 
650
      if (sign < 0)
 
651
        stack->flags &= ~TIMESTAMP_ON;
 
652
      else
 
653
        stack->flags |= TIMESTAMP_ON;
 
654
      break;
 
655
    case 'S':
 
656
      if (sign < 0)
 
657
        stack->flags &= ~SANITY_CHECK_ON;
 
658
      else
 
659
        stack->flags |= SANITY_CHECK_ON;
 
660
      break;
 
661
    }
 
662
    if (!*end)
 
663
      break;
 
664
    control=end+1;
 
665
    end= DbugStrTok(control);
 
666
  }
 
667
}
 
668
 
 
669
 
 
670
/*
 
671
 *  FUNCTION
 
672
 *
 
673
 *      _db_set_       set current debugger settings
 
674
 *
 
675
 *  SYNOPSIS
 
676
 *
 
677
 *      VOID _db_set_(control)
 
678
 *      char *control;
 
679
 *
 
680
 *  DESCRIPTION
 
681
 *
 
682
 *      Given pointer to a debug control string in "control",
 
683
 *      parses the control string, and sets up a current debug
 
684
 *      settings. Pushes a new debug settings if the current is
 
685
 *      set to the initial debugger settings.
 
686
 */
 
687
 
 
688
void _db_set_(CODE_STATE *cs, const char *control)
 
689
{
 
690
  get_code_state_or_return;
 
691
 
 
692
  if (cs->stack == &init_settings)
 
693
    PushState(cs);
 
694
 
 
695
  DbugParse(cs, control);
 
696
}
 
697
 
 
698
 
 
699
/*
 
700
 *  FUNCTION
 
701
 *
 
702
 *      _db_push_       push current debugger settings and set up new one
 
703
 *
 
704
 *  SYNOPSIS
 
705
 *
 
706
 *      VOID _db_push_(control)
 
707
 *      char *control;
 
708
 *
 
709
 *  DESCRIPTION
 
710
 *
 
711
 *      Given pointer to a debug control string in "control", pushes
 
712
 *      the current debug settings, parses the control string, and sets
 
713
 *      up a new debug settings with DbugParse()
 
714
 *
 
715
 */
 
716
 
 
717
void _db_push_(const char *control)
 
718
{
 
719
  CODE_STATE *cs=0;
 
720
  get_code_state_or_return;
 
721
  PushState(cs);
 
722
  DbugParse(cs, control);
 
723
}
 
724
 
 
725
/*
 
726
 *  FUNCTION
 
727
 *
 
728
 *      _db_set_init_       set initial debugger settings
 
729
 *
 
730
 *  SYNOPSIS
 
731
 *
 
732
 *      VOID _db_set_init_(control)
 
733
 *      char *control;
 
734
 *
 
735
 *  DESCRIPTION
 
736
 *      see _db_set_
 
737
 *
 
738
 */
 
739
 
 
740
void _db_set_init_(const char *control)
 
741
{
 
742
  CODE_STATE tmp_cs;
 
743
  bzero((uchar*) &tmp_cs, sizeof(tmp_cs));
 
744
  tmp_cs.stack= &init_settings;
 
745
  DbugParse(&tmp_cs, control);
 
746
}
 
747
 
 
748
/*
 
749
 *  FUNCTION
 
750
 *
 
751
 *      _db_pop_    pop the debug stack
 
752
 *
 
753
 *  DESCRIPTION
 
754
 *
 
755
 *      Pops the debug stack, returning the debug settings to its
 
756
 *      condition prior to the most recent _db_push_ invocation.
 
757
 *      Note that the pop will fail if it would remove the last
 
758
 *      valid settings from the stack.  This prevents user errors
 
759
 *      in the push/pop sequence from screwing up the debugger.
 
760
 *      Maybe there should be some kind of warning printed if the
 
761
 *      user tries to pop too many states.
 
762
 *
 
763
 */
 
764
 
 
765
void _db_pop_()
 
766
{
 
767
  struct settings *discard;
 
768
  CODE_STATE *cs=0;
 
769
 
 
770
  get_code_state_or_return;
 
771
 
 
772
  discard= cs->stack;
 
773
  if (discard->next != NULL)
 
774
  {
 
775
    cs->stack= discard->next;
 
776
    FreeState(cs, discard, 1);
 
777
  }
 
778
}
 
779
 
 
780
/*
 
781
 *  FUNCTION
 
782
 *
 
783
 *      _db_explain_    generates 'control' string for the current settings
 
784
 *
 
785
 *  RETURN
 
786
 *      0 - ok
 
787
 *      1  - buffer too short, output truncated
 
788
 *
 
789
 */
 
790
 
 
791
/* helper macros */
 
792
#define char_to_buf(C)    do {                  \
 
793
        *buf++=(C);                             \
 
794
        if (buf >= end) goto overflow;          \
 
795
      } while (0)
 
796
#define str_to_buf(S)    do {                   \
 
797
        char_to_buf(',');                       \
 
798
        buf=strnmov(buf, (S), len+1);           \
 
799
        if (buf >= end) goto overflow;          \
 
800
      } while (0)
 
801
#define list_to_buf(l)  do {                    \
 
802
        struct link *listp=(l);                 \
 
803
        while (listp)                           \
 
804
        {                                       \
 
805
          str_to_buf(listp->str);               \
 
806
          listp=listp->next_link;               \
 
807
        }                                       \
 
808
      } while (0)
 
809
#define int_to_buf(i)  do {                     \
 
810
        char b[50];                             \
 
811
        int10_to_str((i), b, 10);               \
 
812
        str_to_buf(b);                          \
 
813
      } while (0)
 
814
#define colon_to_buf   do {                     \
 
815
        if (buf != start) char_to_buf(':');     \
 
816
      } while(0)
 
817
#define op_int_to_buf(C, val, def) do {         \
 
818
        if ((val) != (def))                     \
 
819
        {                                       \
 
820
          colon_to_buf;                         \
 
821
          char_to_buf((C));                     \
 
822
          int_to_buf(val);                      \
 
823
        }                                       \
 
824
      } while (0)
 
825
#define op_intf_to_buf(C, val, def, cond) do {  \
 
826
        if ((cond))                             \
 
827
        {                                       \
 
828
          colon_to_buf;                         \
 
829
          char_to_buf((C));                     \
 
830
          if ((val) != (def)) int_to_buf(val);  \
 
831
        }                                       \
 
832
      } while (0)
 
833
#define op_str_to_buf(C, val, cond) do {        \
 
834
        if ((cond))                             \
 
835
        {                                       \
 
836
          char *s=(val);                        \
 
837
          colon_to_buf;                         \
 
838
          char_to_buf((C));                     \
 
839
          if (*s) str_to_buf(s);                \
 
840
        }                                       \
 
841
      } while (0)
 
842
#define op_list_to_buf(C, val, cond) do {       \
 
843
        if ((cond))                             \
 
844
        {                                       \
 
845
          colon_to_buf;                         \
 
846
          char_to_buf((C));                     \
 
847
          list_to_buf(val);                     \
 
848
        }                                       \
 
849
      } while (0)
 
850
#define op_bool_to_buf(C, cond) do {            \
 
851
        if ((cond))                             \
 
852
        {                                       \
 
853
          colon_to_buf;                         \
 
854
          char_to_buf((C));                     \
 
855
        }                                       \
 
856
      } while (0)
 
857
 
 
858
int _db_explain_ (CODE_STATE *cs, char *buf, size_t len)
 
859
{
 
860
  char *start=buf, *end=buf+len-4;
 
861
 
 
862
  get_code_state_or_return *buf=0;
 
863
 
 
864
  op_list_to_buf('d', cs->stack->keywords, DEBUGGING);
 
865
  op_int_to_buf ('D', cs->stack->delay, 0);
 
866
  op_list_to_buf('f', cs->stack->functions, cs->stack->functions);
 
867
  op_bool_to_buf('F', cs->stack->flags & FILE_ON);
 
868
  op_bool_to_buf('i', cs->stack->flags & PID_ON);
 
869
  op_list_to_buf('g', cs->stack->p_functions, PROFILING);
 
870
  op_bool_to_buf('L', cs->stack->flags & LINE_ON);
 
871
  op_bool_to_buf('n', cs->stack->flags & DEPTH_ON);
 
872
  op_bool_to_buf('N', cs->stack->flags & NUMBER_ON);
 
873
  op_str_to_buf(
 
874
    ((cs->stack->flags & FLUSH_ON_WRITE ? 0 : 32) |
 
875
     (cs->stack->flags & OPEN_APPEND ? 'A' : 'O')),
 
876
    cs->stack->name, cs->stack->out_file != stderr);
 
877
  op_list_to_buf('p', cs->stack->processes, cs->stack->processes);
 
878
  op_bool_to_buf('P', cs->stack->flags & PROCESS_ON);
 
879
  op_bool_to_buf('r', cs->stack->sub_level != 0);
 
880
  op_intf_to_buf('t', cs->stack->maxdepth, MAXDEPTH, TRACING);
 
881
  op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON);
 
882
  op_bool_to_buf('S', cs->stack->flags & SANITY_CHECK_ON);
 
883
 
 
884
  *buf= '\0';
 
885
  return 0;
 
886
 
 
887
overflow:
 
888
  *end++= '.';
 
889
  *end++= '.';
 
890
  *end++= '.';
 
891
  *end=   '\0';
 
892
  return 1;
 
893
}
 
894
 
 
895
#undef char_to_buf
 
896
#undef str_to_buf
 
897
#undef list_to_buf
 
898
#undef int_to_buf
 
899
#undef colon_to_buf
 
900
#undef op_int_to_buf
 
901
#undef op_intf_to_buf
 
902
#undef op_str_to_buf
 
903
#undef op_list_to_buf
 
904
#undef op_bool_to_buf
 
905
 
 
906
/*
 
907
 *  FUNCTION
 
908
 *
 
909
 *      _db_explain_init_       explain initial debugger settings
 
910
 *
 
911
 *  DESCRIPTION
 
912
 *      see _db_explain_
 
913
 */
 
914
 
 
915
int _db_explain_init_(char *buf, size_t len)
 
916
{
 
917
  CODE_STATE cs;
 
918
  bzero((uchar*) &cs,sizeof(cs));
 
919
  cs.stack=&init_settings;
 
920
  return _db_explain_(&cs, buf, len);
 
921
}
 
922
 
 
923
/*
 
924
 *  FUNCTION
 
925
 *
 
926
 *      _db_enter_    process entry point to user function
 
927
 *
 
928
 *  SYNOPSIS
 
929
 *
 
930
 *      VOID _db_enter_(_func_, _file_, _line_,
 
931
 *                       _sfunc_, _sfile_, _slevel_, _sframep_)
 
932
 *      char *_func_;           points to current function name
 
933
 *      char *_file_;           points to current file name
 
934
 *      int _line_;             called from source line number
 
935
 *      char **_sfunc_;         save previous _func_
 
936
 *      char **_sfile_;         save previous _file_
 
937
 *      int *_slevel_;          save previous nesting level
 
938
 *      char ***_sframep_;      save previous frame pointer
 
939
 *
 
940
 *  DESCRIPTION
 
941
 *
 
942
 *      Called at the beginning of each user function to tell
 
943
 *      the debugger that a new function has been entered.
 
944
 *      Note that the pointers to the previous user function
 
945
 *      name and previous user file name are stored on the
 
946
 *      caller's stack (this is why the ENTER macro must be
 
947
 *      the first "executable" code in a function, since it
 
948
 *      allocates these storage locations).  The previous nesting
 
949
 *      level is also stored on the callers stack for internal
 
950
 *      self consistency checks.
 
951
 *
 
952
 *      Also prints a trace line if tracing is enabled and
 
953
 *      increments the current function nesting depth.
 
954
 *
 
955
 *      Note that this mechanism allows the debugger to know
 
956
 *      what the current user function is at all times, without
 
957
 *      maintaining an internal stack for the function names.
 
958
 *
 
959
 */
 
960
 
 
961
void _db_enter_(const char *_func_, const char *_file_,
 
962
                uint _line_, const char **_sfunc_, const char **_sfile_,
 
963
                uint *_slevel_, char ***_sframep_ __attribute__((unused)))
 
964
{
 
965
  int save_errno=errno;
 
966
  CODE_STATE *cs=0;
 
967
  get_code_state_or_return;
 
968
 
 
969
  *_sfunc_= cs->func;
 
970
  *_sfile_= cs->file;
 
971
  cs->func=  _func_;
 
972
  cs->file=  _file_;
 
973
  *_slevel_=  ++cs->level;
 
974
#ifndef THREAD
 
975
  *_sframep_= cs->framep;
 
976
  cs->framep= (char **) _sframep_;
 
977
  if (DoProfile(cs))
 
978
  {
 
979
    long stackused;
 
980
    if (*cs->framep == NULL)
 
981
      stackused= 0;
 
982
    else
 
983
    {
 
984
      stackused= ((long)(*cs->framep)) - ((long)(cs->framep));
 
985
      stackused= stackused > 0 ? stackused : -stackused;
 
986
    }
 
987
    (void) fprintf(cs->stack->prof_file, PROF_EFMT , Clock(), cs->func);
 
988
#ifdef AUTOS_REVERSE
 
989
    (void) fprintf(cs->stack->prof_file, PROF_SFMT, cs->framep, stackused, *_sfunc_);
 
990
#else
 
991
    (void) fprintf(cs->stack->prof_file, PROF_SFMT, (ulong) cs->framep, stackused,
 
992
                    cs->func);
 
993
#endif
 
994
    (void) fflush(cs->stack->prof_file);
 
995
  }
 
996
#endif
 
997
  if (DoTrace(cs))
 
998
  {
 
999
    if (!cs->locked)
 
1000
      pthread_mutex_lock(&THR_LOCK_dbug);
 
1001
    DoPrefix(cs, _line_);
 
1002
    Indent(cs, cs->level);
 
1003
    (void) fprintf(cs->stack->out_file, ">%s\n", cs->func);
 
1004
    dbug_flush(cs);                       /* This does a unlock */
 
1005
  }
 
1006
  errno=save_errno;
 
1007
}
 
1008
 
 
1009
/*
 
1010
 *  FUNCTION
 
1011
 *
 
1012
 *      _db_return_    process exit from user function
 
1013
 *
 
1014
 *  SYNOPSIS
 
1015
 *
 
1016
 *      VOID _db_return_(_line_, _sfunc_, _sfile_, _slevel_)
 
1017
 *      int _line_;             current source line number
 
1018
 *      char **_sfunc_;         where previous _func_ is to be retrieved
 
1019
 *      char **_sfile_;         where previous _file_ is to be retrieved
 
1020
 *      int *_slevel_;          where previous level was stashed
 
1021
 *
 
1022
 *  DESCRIPTION
 
1023
 *
 
1024
 *      Called just before user function executes an explicit or implicit
 
1025
 *      return.  Prints a trace line if trace is enabled, decrements
 
1026
 *      the current nesting level, and restores the current function and
 
1027
 *      file names from the defunct function's stack.
 
1028
 *
 
1029
 */
 
1030
 
 
1031
/* helper macro */
 
1032
void _db_return_(uint _line_, const char **_sfunc_,
 
1033
                 const char **_sfile_, uint *_slevel_)
 
1034
{
 
1035
  int save_errno=errno;
 
1036
  CODE_STATE *cs=0;
 
1037
  get_code_state_or_return;
 
1038
 
 
1039
  if (cs->level != (int) *_slevel_)
 
1040
  {
 
1041
    if (!cs->locked)
 
1042
      pthread_mutex_lock(&THR_LOCK_dbug);
 
1043
    (void) fprintf(cs->stack->out_file, ERR_MISSING_RETURN, cs->process,
 
1044
                   cs->func);
 
1045
    dbug_flush(cs);
 
1046
  }
 
1047
  else
 
1048
  {
 
1049
#ifndef THREAD
 
1050
    if (DoProfile(cs))
 
1051
      (void) fprintf(cs->stack->prof_file, PROF_XFMT, Clock(), cs->func);
 
1052
#endif
 
1053
    if (DoTrace(cs))
 
1054
    {
 
1055
      if (!cs->locked)
 
1056
        pthread_mutex_lock(&THR_LOCK_dbug);
 
1057
      DoPrefix(cs, _line_);
 
1058
      Indent(cs, cs->level);
 
1059
      (void) fprintf(cs->stack->out_file, "<%s\n", cs->func);
 
1060
      dbug_flush(cs);
 
1061
    }
 
1062
  }
 
1063
  cs->level= *_slevel_-1;
 
1064
  cs->func= *_sfunc_;
 
1065
  cs->file= *_sfile_;
 
1066
#ifndef THREAD
 
1067
  if (cs->framep != NULL)
 
1068
    cs->framep= (char **) *cs->framep;
 
1069
#endif
 
1070
  errno=save_errno;
 
1071
}
 
1072
 
 
1073
 
 
1074
/*
 
1075
 *  FUNCTION
 
1076
 *
 
1077
 *      _db_pargs_    log arguments for subsequent use by _db_doprnt_()
 
1078
 *
 
1079
 *  SYNOPSIS
 
1080
 *
 
1081
 *      VOID _db_pargs_(_line_, keyword)
 
1082
 *      int _line_;
 
1083
 *      char *keyword;
 
1084
 *
 
1085
 *  DESCRIPTION
 
1086
 *
 
1087
 *      The new universal printing macro DBUG_PRINT, which replaces
 
1088
 *      all forms of the DBUG_N macros, needs two calls to runtime
 
1089
 *      support routines.  The first, this function, remembers arguments
 
1090
 *      that are used by the subsequent call to _db_doprnt_().
 
1091
 *
 
1092
 */
 
1093
 
 
1094
void _db_pargs_(uint _line_, const char *keyword)
 
1095
{
 
1096
  CODE_STATE *cs=0;
 
1097
  get_code_state_or_return;
 
1098
  cs->u_line= _line_;
 
1099
  cs->u_keyword= keyword;
 
1100
}
 
1101
 
 
1102
 
 
1103
/*
 
1104
 *  FUNCTION
 
1105
 *
 
1106
 *      _db_doprnt_    handle print of debug lines
 
1107
 *
 
1108
 *  SYNOPSIS
 
1109
 *
 
1110
 *      VOID _db_doprnt_(format, va_alist)
 
1111
 *      char *format;
 
1112
 *      va_dcl;
 
1113
 *
 
1114
 *  DESCRIPTION
 
1115
 *
 
1116
 *      When invoked via one of the DBUG macros, tests the current keyword
 
1117
 *      set by calling _db_pargs_() to see if that macro has been selected
 
1118
 *      for processing via the debugger control string, and if so, handles
 
1119
 *      printing of the arguments via the format string.  The line number
 
1120
 *      of the DBUG macro in the source is found in u_line.
 
1121
 *
 
1122
 *      Note that the format string SHOULD NOT include a terminating
 
1123
 *      newline, this is supplied automatically.
 
1124
 *
 
1125
 */
 
1126
 
 
1127
#include <stdarg.h>
 
1128
 
 
1129
void _db_doprnt_(const char *format,...)
 
1130
{
 
1131
  va_list args;
 
1132
 
 
1133
  CODE_STATE *cs=0;
 
1134
  get_code_state_or_return;
 
1135
 
 
1136
  va_start(args,format);
 
1137
 
 
1138
  if (_db_keyword_(cs, cs->u_keyword))
 
1139
  {
 
1140
    int save_errno=errno;
 
1141
    if (!cs->locked)
 
1142
      pthread_mutex_lock(&THR_LOCK_dbug);
 
1143
    DoPrefix(cs, cs->u_line);
 
1144
    if (TRACING)
 
1145
      Indent(cs, cs->level + 1);
 
1146
    else
 
1147
      (void) fprintf(cs->stack->out_file, "%s: ", cs->func);
 
1148
    (void) fprintf(cs->stack->out_file, "%s: ", cs->u_keyword);
 
1149
    (void) vfprintf(cs->stack->out_file, format, args);
 
1150
    (void) fputc('\n',cs->stack->out_file);
 
1151
    dbug_flush(cs);
 
1152
    errno=save_errno;
 
1153
  }
 
1154
  va_end(args);
 
1155
}
 
1156
 
 
1157
 
 
1158
/*
 
1159
 *  FUNCTION
 
1160
 *
 
1161
 *            _db_dump_    dump a string in hex
 
1162
 *
 
1163
 *  SYNOPSIS
 
1164
 *
 
1165
 *            void _db_dump_(_line_,keyword,memory,length)
 
1166
 *            int _line_;               current source line number
 
1167
 *            char *keyword;
 
1168
 *            char *memory;             Memory to print
 
1169
 *            int length;               Bytes to print
 
1170
 *
 
1171
 *  DESCRIPTION
 
1172
 *  Dump N characters in a binary array.
 
1173
 *  Is used to examine corrputed memory or arrays.
 
1174
 */
 
1175
 
 
1176
void _db_dump_(uint _line_, const char *keyword,
 
1177
               const unsigned char *memory, size_t length)
 
1178
{
 
1179
  int pos;
 
1180
  char dbuff[90];
 
1181
 
 
1182
  CODE_STATE *cs=0;
 
1183
  get_code_state_or_return;
 
1184
 
 
1185
  if (_db_keyword_(cs, keyword))
 
1186
  {
 
1187
    if (!cs->locked)
 
1188
      pthread_mutex_lock(&THR_LOCK_dbug);
 
1189
    DoPrefix(cs, _line_);
 
1190
    if (TRACING)
 
1191
    {
 
1192
      Indent(cs, cs->level + 1);
 
1193
      pos= min(max(cs->level-cs->stack->sub_level,0)*INDENT,80);
 
1194
    }
 
1195
    else
 
1196
    {
 
1197
      fprintf(cs->stack->out_file, "%s: ", cs->func);
 
1198
    }
 
1199
    sprintf(dbuff,"%s: Memory: 0x%lx  Bytes: (%ld)\n",
 
1200
            keyword, (ulong) memory, (long) length);
 
1201
    (void) fputs(dbuff,cs->stack->out_file);
 
1202
 
 
1203
    pos=0;
 
1204
    while (length-- > 0)
 
1205
    {
 
1206
      uint tmp= *((unsigned char*) memory++);
 
1207
      if ((pos+=3) >= 80)
 
1208
      {
 
1209
        fputc('\n',cs->stack->out_file);
 
1210
        pos=3;
 
1211
      }
 
1212
      fputc(_dig_vec_upper[((tmp >> 4) & 15)], cs->stack->out_file);
 
1213
      fputc(_dig_vec_upper[tmp & 15], cs->stack->out_file);
 
1214
      fputc(' ',cs->stack->out_file);
 
1215
    }
 
1216
    (void) fputc('\n',cs->stack->out_file);
 
1217
    dbug_flush(cs);
 
1218
  }
 
1219
}
 
1220
 
 
1221
 
 
1222
/*
 
1223
 *  FUNCTION
 
1224
 *
 
1225
 *      ListAdd    add to the list modifiers from debug control string
 
1226
 *
 
1227
 *  SYNOPSIS
 
1228
 *
 
1229
 *      static struct link *ListAdd(listp, ctlp, end)
 
1230
 *      struct link *listp;
 
1231
 *      char *ctlp;
 
1232
 *      char *end;
 
1233
 *
 
1234
 *  DESCRIPTION
 
1235
 *
 
1236
 *      Given pointer to a comma separated list of strings in "cltp",
 
1237
 *      parses the list, and adds it to listp, returning a pointer
 
1238
 *      to the new list
 
1239
 *
 
1240
 *      Note that since each link is added at the head of the list,
 
1241
 *      the final list will be in "reverse order", which is not
 
1242
 *      significant for our usage here.
 
1243
 *
 
1244
 */
 
1245
 
 
1246
static struct link *ListAdd(struct link *head,
 
1247
                             const char *ctlp, const char *end)
 
1248
{
 
1249
  const char *start;
 
1250
  struct link *new_malloc;
 
1251
  int len;
 
1252
 
 
1253
  while (ctlp < end)
 
1254
  {
 
1255
    start= ctlp;
 
1256
    while (ctlp < end && *ctlp != ',')
 
1257
      ctlp++;
 
1258
    len=ctlp-start;
 
1259
    new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len);
 
1260
    memcpy(new_malloc->str, start, len);
 
1261
    new_malloc->str[len]=0;
 
1262
    new_malloc->next_link= head;
 
1263
    head= new_malloc;
 
1264
    ctlp++;
 
1265
  }
 
1266
  return head;
 
1267
}
 
1268
 
 
1269
/*
 
1270
 *  FUNCTION
 
1271
 *
 
1272
 *      ListDel    remove from the list modifiers in debug control string
 
1273
 *
 
1274
 *  SYNOPSIS
 
1275
 *
 
1276
 *      static struct link *ListDel(listp, ctlp, end)
 
1277
 *      struct link *listp;
 
1278
 *      char *ctlp;
 
1279
 *      char *end;
 
1280
 *
 
1281
 *  DESCRIPTION
 
1282
 *
 
1283
 *      Given pointer to a comma separated list of strings in "cltp",
 
1284
 *      parses the list, and removes these strings from the listp,
 
1285
 *      returning a pointer to the new list.
 
1286
 *
 
1287
 */
 
1288
 
 
1289
static struct link *ListDel(struct link *head,
 
1290
                             const char *ctlp, const char *end)
 
1291
{
 
1292
  const char *start;
 
1293
  struct link **cur;
 
1294
  int len;
 
1295
 
 
1296
  while (ctlp < end)
 
1297
  {
 
1298
    start= ctlp;
 
1299
    while (ctlp < end && *ctlp != ',')
 
1300
      ctlp++;
 
1301
    len=ctlp-start;
 
1302
    cur=&head;
 
1303
    do
 
1304
    {
 
1305
      while (*cur && !strncmp((*cur)->str, start, len))
 
1306
      {
 
1307
        struct link *delme=*cur;
 
1308
        *cur=(*cur)->next_link;
 
1309
        free((void*) delme);
 
1310
      }
 
1311
    } while (*cur && *(cur=&((*cur)->next_link)));
 
1312
  }
 
1313
  return head;
 
1314
}
 
1315
 
 
1316
/*
 
1317
 *  FUNCTION
 
1318
 *
 
1319
 *      ListCopy    make a copy of the list
 
1320
 *
 
1321
 *  SYNOPSIS
 
1322
 *
 
1323
 *      static struct link *ListCopy(orig)
 
1324
 *      struct link *orig;
 
1325
 *
 
1326
 *  DESCRIPTION
 
1327
 *
 
1328
 *      Given pointer to list, which contains a copy of every element from
 
1329
 *      the original list.
 
1330
 *
 
1331
 *      the orig pointer can be NULL
 
1332
 *
 
1333
 *      Note that since each link is added at the head of the list,
 
1334
 *      the final list will be in "reverse order", which is not
 
1335
 *      significant for our usage here.
 
1336
 *
 
1337
 */
 
1338
 
 
1339
static struct link *ListCopy(struct link *orig)
 
1340
{
 
1341
  struct link *new_malloc;
 
1342
  struct link *head;
 
1343
  int len;
 
1344
 
 
1345
  head= NULL;
 
1346
  while (orig != NULL)
 
1347
  {
 
1348
    len= strlen(orig->str);
 
1349
    new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len);
 
1350
    memcpy(new_malloc->str, orig->str, len);
 
1351
    new_malloc->str[len]= 0;
 
1352
    new_malloc->next_link= head;
 
1353
    head= new_malloc;
 
1354
    orig= orig->next_link;
 
1355
  }
 
1356
  return head;
 
1357
}
 
1358
 
 
1359
/*
 
1360
 *  FUNCTION
 
1361
 *
 
1362
 *      InList    test a given string for member of a given list
 
1363
 *
 
1364
 *  SYNOPSIS
 
1365
 *
 
1366
 *      static BOOLEAN InList(linkp, cp)
 
1367
 *      struct link *linkp;
 
1368
 *      char *cp;
 
1369
 *
 
1370
 *  DESCRIPTION
 
1371
 *
 
1372
 *      Tests the string pointed to by "cp" to determine if it is in
 
1373
 *      the list pointed to by "linkp".  Linkp points to the first
 
1374
 *      link in the list.  If linkp is NULL then the string is treated
 
1375
 *      as if it is in the list (I.E all strings are in the null list).
 
1376
 *      This may seem rather strange at first but leads to the desired
 
1377
 *      operation if no list is given.  The net effect is that all
 
1378
 *      strings will be accepted when there is no list, and when there
 
1379
 *      is a list, only those strings in the list will be accepted.
 
1380
 *
 
1381
 */
 
1382
 
 
1383
static BOOLEAN InList(struct link *linkp, const char *cp)
 
1384
{
 
1385
  REGISTER struct link *scan;
 
1386
  REGISTER BOOLEAN result;
 
1387
 
 
1388
  if (linkp == NULL)
 
1389
    result= TRUE;
 
1390
  else
 
1391
  {
 
1392
    result= FALSE;
 
1393
    for (scan= linkp; scan != NULL; scan= scan->next_link)
 
1394
    {
 
1395
      if (!strcmp(scan->str, cp))
 
1396
      {
 
1397
        result= TRUE;
 
1398
        break;
 
1399
      }
 
1400
    }
 
1401
  }
 
1402
  return result;
 
1403
}
 
1404
 
 
1405
 
 
1406
/*
 
1407
 *  FUNCTION
 
1408
 *
 
1409
 *      PushState    push current settings onto stack and set up new one
 
1410
 *
 
1411
 *  SYNOPSIS
 
1412
 *
 
1413
 *      static VOID PushState()
 
1414
 *
 
1415
 *  DESCRIPTION
 
1416
 *
 
1417
 *      Pushes the current settings on the settings stack, and creates
 
1418
 *      a new settings. The new settings is NOT initialized
 
1419
 *
 
1420
 *      The settings stack is a linked list of settings, with the new
 
1421
 *      settings added at the head.  This allows the stack to grow
 
1422
 *      to the limits of memory if necessary.
 
1423
 *
 
1424
 */
 
1425
 
 
1426
static void PushState(CODE_STATE *cs)
 
1427
{
 
1428
  struct settings *new_malloc;
 
1429
 
 
1430
  new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings));
 
1431
  new_malloc->next= cs->stack;
 
1432
  new_malloc->out_file= NULL;
 
1433
  cs->stack= new_malloc;
 
1434
}
 
1435
 
 
1436
/*
 
1437
 *  FUNCTION
 
1438
 *
 
1439
 *      FreeState    Free memory associated with a struct state.
 
1440
 *
 
1441
 *  SYNOPSIS
 
1442
 *
 
1443
 *      static void FreeState (state)
 
1444
 *      struct state *state;
 
1445
 *      int free_state;
 
1446
 *
 
1447
 *  DESCRIPTION
 
1448
 *
 
1449
 *      Deallocates the memory allocated for various information in a
 
1450
 *      state. If free_state is set, also free 'state'
 
1451
 *
 
1452
 */
 
1453
static void FreeState(CODE_STATE *cs, struct settings *state, int free_state)
 
1454
{
 
1455
  if (!is_shared(state, keywords))
 
1456
    FreeList(state->keywords);
 
1457
  if (!is_shared(state, functions))
 
1458
    FreeList(state->functions);
 
1459
  if (!is_shared(state, processes))
 
1460
    FreeList(state->processes);
 
1461
  if (!is_shared(state, p_functions))
 
1462
    FreeList(state->p_functions);
 
1463
  if (!is_shared(state, out_file))
 
1464
    DBUGCloseFile(cs, state->out_file);
 
1465
  (void) fflush(cs->stack->out_file);
 
1466
  if (state->prof_file)
 
1467
    DBUGCloseFile(cs, state->prof_file);
 
1468
  if (free_state)
 
1469
    free((void*) state);
 
1470
}
 
1471
 
 
1472
 
 
1473
/*
 
1474
 *  FUNCTION
 
1475
 *
 
1476
 *      _db_end_    End debugging, freeing state stack memory.
 
1477
 *
 
1478
 *  SYNOPSIS
 
1479
 *
 
1480
 *      static VOID _db_end_ ()
 
1481
 *
 
1482
 *  DESCRIPTION
 
1483
 *
 
1484
 *      Ends debugging, de-allocating the memory allocated to the
 
1485
 *      state stack.
 
1486
 *
 
1487
 *      To be called at the very end of the program.
 
1488
 *
 
1489
 */
 
1490
void _db_end_()
 
1491
{
 
1492
  struct settings *discard;
 
1493
  static struct settings tmp;
 
1494
  CODE_STATE *cs=0;
 
1495
 
 
1496
  get_code_state_or_return;
 
1497
 
 
1498
  while ((discard= cs->stack))
 
1499
  {
 
1500
    if (discard == &init_settings)
 
1501
      break;
 
1502
    cs->stack= discard->next;
 
1503
    FreeState(cs, discard, 1);
 
1504
  }
 
1505
  tmp= init_settings;
 
1506
 
 
1507
  /* Use mutex lock to make it less likely anyone access out_file */
 
1508
  pthread_mutex_lock(&THR_LOCK_dbug);
 
1509
  init_settings.flags=    OPEN_APPEND;
 
1510
  init_settings.out_file= stderr;
 
1511
  init_settings.prof_file= stderr;
 
1512
  init_settings.maxdepth= 0;
 
1513
  init_settings.delay= 0;
 
1514
  init_settings.sub_level= 0;
 
1515
  init_settings.functions= 0;
 
1516
  init_settings.p_functions= 0;
 
1517
  init_settings.keywords= 0;
 
1518
  init_settings.processes= 0;
 
1519
  pthread_mutex_unlock(&THR_LOCK_dbug);
 
1520
  FreeState(cs, &tmp, 0);
 
1521
}
 
1522
 
 
1523
 
 
1524
/*
 
1525
 *  FUNCTION
 
1526
 *
 
1527
 *      DoTrace    check to see if tracing is current enabled
 
1528
 *
 
1529
 *  SYNOPSIS
 
1530
 *
 
1531
 *      static BOOLEAN DoTrace(stack)
 
1532
 *
 
1533
 *  DESCRIPTION
 
1534
 *
 
1535
 *      Checks to see if tracing is enabled based on whether the
 
1536
 *      user has specified tracing, the maximum trace depth has
 
1537
 *      not yet been reached, the current function is selected,
 
1538
 *      and the current process is selected.  Returns TRUE if
 
1539
 *      tracing is enabled, FALSE otherwise.
 
1540
 *
 
1541
 */
 
1542
 
 
1543
static BOOLEAN DoTrace(CODE_STATE *cs)
 
1544
{
 
1545
  return (TRACING && cs->level <= cs->stack->maxdepth &&
 
1546
          InList(cs->stack->functions, cs->func) &&
 
1547
          InList(cs->stack->processes, cs->process));
 
1548
}
 
1549
 
 
1550
 
 
1551
/*
 
1552
 *  FUNCTION
 
1553
 *
 
1554
 *      DoProfile    check to see if profiling is current enabled
 
1555
 *
 
1556
 *  SYNOPSIS
 
1557
 *
 
1558
 *      static BOOLEAN DoProfile()
 
1559
 *
 
1560
 *  DESCRIPTION
 
1561
 *
 
1562
 *      Checks to see if profiling is enabled based on whether the
 
1563
 *      user has specified profiling, the maximum trace depth has
 
1564
 *      not yet been reached, the current function is selected,
 
1565
 *      and the current process is selected.  Returns TRUE if
 
1566
 *      profiling is enabled, FALSE otherwise.
 
1567
 *
 
1568
 */
 
1569
 
 
1570
#ifndef THREAD
 
1571
static BOOLEAN DoProfile(CODE_STATE *cs)
 
1572
{
 
1573
  return PROFILING &&
 
1574
         cs->level <= cs->stack->maxdepth &&
 
1575
         InList(cs->stack->p_functions, cs->func) &&
 
1576
         InList(cs->stack->processes, cs->process);
 
1577
}
 
1578
#endif
 
1579
 
 
1580
FILE *_db_fp_(void)
 
1581
{
 
1582
  CODE_STATE *cs=0;
 
1583
  get_code_state_or_return NULL;
 
1584
  return cs->stack->out_file;
 
1585
}
 
1586
 
 
1587
 
 
1588
/*
 
1589
 *  FUNCTION
 
1590
 *
 
1591
 *      _db_strict_keyword_     test keyword for member of keyword list
 
1592
 *
 
1593
 *  SYNOPSIS
 
1594
 *
 
1595
 *      BOOLEAN _db_strict_keyword_(keyword)
 
1596
 *      char *keyword;
 
1597
 *
 
1598
 *  DESCRIPTION
 
1599
 *
 
1600
 *      Similar to _db_keyword_, but keyword is NOT accepted if keyword list
 
1601
 *      is empty. Used in DBUG_EXECUTE_IF() - for actions that must not be
 
1602
 *      executed by default.
 
1603
 *
 
1604
 *      Returns TRUE if keyword accepted, FALSE otherwise.
 
1605
 *
 
1606
 */
 
1607
 
 
1608
BOOLEAN _db_strict_keyword_(const char *keyword)
 
1609
{
 
1610
  CODE_STATE *cs=0;
 
1611
  get_code_state_or_return FALSE;
 
1612
  if (!DEBUGGING || cs->stack->keywords == NULL)
 
1613
    return FALSE;
 
1614
  return _db_keyword_(cs, keyword);
 
1615
}
 
1616
 
 
1617
/*
 
1618
 *  FUNCTION
 
1619
 *
 
1620
 *      _db_keyword_    test keyword for member of keyword list
 
1621
 *
 
1622
 *  SYNOPSIS
 
1623
 *
 
1624
 *      BOOLEAN _db_keyword_(keyword)
 
1625
 *      char *keyword;
 
1626
 *
 
1627
 *  DESCRIPTION
 
1628
 *
 
1629
 *      Test a keyword to determine if it is in the currently active
 
1630
 *      keyword list.  As with the function list, a keyword is accepted
 
1631
 *      if the list is null, otherwise it must match one of the list
 
1632
 *      members.  When debugging is not on, no keywords are accepted.
 
1633
 *      After the maximum trace level is exceeded, no keywords are
 
1634
 *      accepted (this behavior subject to change).  Additionally,
 
1635
 *      the current function and process must be accepted based on
 
1636
 *      their respective lists.
 
1637
 *
 
1638
 *      Returns TRUE if keyword accepted, FALSE otherwise.
 
1639
 *
 
1640
 */
 
1641
 
 
1642
BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword)
 
1643
{
 
1644
  get_code_state_or_return FALSE;
 
1645
 
 
1646
  return (DEBUGGING &&
 
1647
          (!TRACING || cs->level <= cs->stack->maxdepth) &&
 
1648
          InList(cs->stack->functions, cs->func) &&
 
1649
          InList(cs->stack->keywords, keyword) &&
 
1650
          InList(cs->stack->processes, cs->process));
 
1651
}
 
1652
 
 
1653
/*
 
1654
 *  FUNCTION
 
1655
 *
 
1656
 *      Indent    indent a line to the given indentation level
 
1657
 *
 
1658
 *  SYNOPSIS
 
1659
 *
 
1660
 *      static VOID Indent(indent)
 
1661
 *      int indent;
 
1662
 *
 
1663
 *  DESCRIPTION
 
1664
 *
 
1665
 *      Indent a line to the given level.  Note that this is
 
1666
 *      a simple minded but portable implementation.
 
1667
 *      There are better ways.
 
1668
 *
 
1669
 *      Also, the indent must be scaled by the compile time option
 
1670
 *      of character positions per nesting level.
 
1671
 *
 
1672
 */
 
1673
 
 
1674
static void Indent(CODE_STATE *cs, int indent)
 
1675
{
 
1676
  REGISTER int count;
 
1677
 
 
1678
  indent= max(indent-1-cs->stack->sub_level,0)*INDENT;
 
1679
  for (count= 0; count < indent ; count++)
 
1680
  {
 
1681
    if ((count % INDENT) == 0)
 
1682
      fputc('|',cs->stack->out_file);
 
1683
    else
 
1684
      fputc(' ',cs->stack->out_file);
 
1685
  }
 
1686
}
 
1687
 
 
1688
 
 
1689
/*
 
1690
 *  FUNCTION
 
1691
 *
 
1692
 *      FreeList    free all memory associated with a linked list
 
1693
 *
 
1694
 *  SYNOPSIS
 
1695
 *
 
1696
 *      static VOID FreeList(linkp)
 
1697
 *      struct link *linkp;
 
1698
 *
 
1699
 *  DESCRIPTION
 
1700
 *
 
1701
 *      Given pointer to the head of a linked list, frees all
 
1702
 *      memory held by the list and the members of the list.
 
1703
 *
 
1704
 */
 
1705
 
 
1706
static void FreeList(struct link *linkp)
 
1707
{
 
1708
  REGISTER struct link *old;
 
1709
 
 
1710
  while (linkp != NULL)
 
1711
  {
 
1712
    old= linkp;
 
1713
    linkp= linkp->next_link;
 
1714
    free((void*) old);
 
1715
  }
 
1716
}
 
1717
 
 
1718
 
 
1719
/*
 
1720
 *  FUNCTION
 
1721
 *
 
1722
 *      DoPrefix    print debugger line prefix prior to indentation
 
1723
 *
 
1724
 *  SYNOPSIS
 
1725
 *
 
1726
 *      static VOID DoPrefix(_line_)
 
1727
 *      int _line_;
 
1728
 *
 
1729
 *  DESCRIPTION
 
1730
 *
 
1731
 *      Print prefix common to all debugger output lines, prior to
 
1732
 *      doing indentation if necessary.  Print such information as
 
1733
 *      current process name, current source file name and line number,
 
1734
 *      and current function nesting depth.
 
1735
 *
 
1736
 */
 
1737
 
 
1738
static void DoPrefix(CODE_STATE *cs, uint _line_)
 
1739
{
 
1740
  cs->lineno++;
 
1741
  if (cs->stack->flags & PID_ON)
 
1742
  {
 
1743
#ifdef THREAD
 
1744
    (void) fprintf(cs->stack->out_file, "%-7s: ", my_thread_name());
 
1745
#else
 
1746
    (void) fprintf(cs->stack->out_file, "%5d: ", (int) getpid());
 
1747
#endif
 
1748
  }
 
1749
  if (cs->stack->flags & NUMBER_ON)
 
1750
    (void) fprintf(cs->stack->out_file, "%5d: ", cs->lineno);
 
1751
  if (cs->stack->flags & TIMESTAMP_ON)
 
1752
  {
 
1753
#ifdef __WIN__
 
1754
    /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
 
1755
       in system ticks, 10 ms intervals. See my_getsystime.c for high res */
 
1756
    SYSTEMTIME loc_t;
 
1757
    GetLocalTime(&loc_t);
 
1758
    (void) fprintf (cs->stack->out_file,
 
1759
                    /* "%04d-%02d-%02d " */
 
1760
                    "%02d:%02d:%02d.%06d ",
 
1761
                    /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
 
1762
                    loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
 
1763
#else
 
1764
    struct timeval tv;
 
1765
    struct tm *tm_p;
 
1766
    if (gettimeofday(&tv, NULL) != -1)
 
1767
    {
 
1768
      if ((tm_p= localtime((const time_t *)&tv.tv_sec)))
 
1769
      {
 
1770
        (void) fprintf (cs->stack->out_file,
 
1771
                        /* "%04d-%02d-%02d " */
 
1772
                        "%02d:%02d:%02d.%06d ",
 
1773
                        /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
 
1774
                        tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
 
1775
                        (int) (tv.tv_usec));
 
1776
      }
 
1777
    }
 
1778
#endif
 
1779
  }
 
1780
  if (cs->stack->flags & PROCESS_ON)
 
1781
    (void) fprintf(cs->stack->out_file, "%s: ", cs->process);
 
1782
  if (cs->stack->flags & FILE_ON)
 
1783
    (void) fprintf(cs->stack->out_file, "%14s: ", BaseName(cs->file));
 
1784
  if (cs->stack->flags & LINE_ON)
 
1785
    (void) fprintf(cs->stack->out_file, "%5d: ", _line_);
 
1786
  if (cs->stack->flags & DEPTH_ON)
 
1787
    (void) fprintf(cs->stack->out_file, "%4d: ", cs->level);
 
1788
}
 
1789
 
 
1790
 
 
1791
/*
 
1792
 *  FUNCTION
 
1793
 *
 
1794
 *      DBUGOpenFile    open new output stream for debugger output
 
1795
 *
 
1796
 *  SYNOPSIS
 
1797
 *
 
1798
 *      static VOID DBUGOpenFile(name)
 
1799
 *      char *name;
 
1800
 *
 
1801
 *  DESCRIPTION
 
1802
 *
 
1803
 *      Given name of a new file (or "-" for stdout) opens the file
 
1804
 *      and sets the output stream to the new file.
 
1805
 *
 
1806
 */
 
1807
 
 
1808
static void DBUGOpenFile(CODE_STATE *cs,
 
1809
                         const char *name,const char *end,int append)
 
1810
{
 
1811
  REGISTER FILE *fp;
 
1812
  REGISTER BOOLEAN newfile;
 
1813
 
 
1814
  if (name != NULL)
 
1815
  {
 
1816
    if (end)
 
1817
    {
 
1818
      int len=end-name;
 
1819
      memcpy(cs->stack->name, name, len);
 
1820
      cs->stack->name[len]=0;
 
1821
    }
 
1822
    else
 
1823
    strmov(cs->stack->name,name);
 
1824
    name=cs->stack->name;
 
1825
    if (strcmp(name, "-") == 0)
 
1826
    {
 
1827
      cs->stack->out_file= stdout;
 
1828
      cs->stack->flags |= FLUSH_ON_WRITE;
 
1829
      cs->stack->name[0]=0;
 
1830
    }
 
1831
    else
 
1832
    {
 
1833
      if (!Writable(name))
 
1834
      {
 
1835
        (void) fprintf(stderr, ERR_OPEN, cs->process, name);
 
1836
        perror("");
 
1837
        fflush(stderr);
 
1838
      }
 
1839
      else
 
1840
      {
 
1841
        newfile= !EXISTS(name);
 
1842
        if (!(fp= fopen(name,
 
1843
#if defined(MSDOS) || defined(__WIN__)
 
1844
                append ? "a+c" : "wc"
 
1845
#else
 
1846
                append ? "a+" : "w"
 
1847
#endif
 
1848
                )))
 
1849
        {
 
1850
          (void) fprintf(stderr, ERR_OPEN, cs->process, name);
 
1851
          perror("");
 
1852
          fflush(stderr);
 
1853
        }
 
1854
        else
 
1855
        {
 
1856
          cs->stack->out_file= fp;
 
1857
          if (newfile)
 
1858
          {
 
1859
            ChangeOwner(cs, name);
 
1860
          }
 
1861
        }
 
1862
      }
 
1863
    }
 
1864
  }
 
1865
}
 
1866
 
 
1867
 
 
1868
/*
 
1869
 *  FUNCTION
 
1870
 *
 
1871
 *      OpenProfile    open new output stream for profiler output
 
1872
 *
 
1873
 *  SYNOPSIS
 
1874
 *
 
1875
 *      static FILE *OpenProfile(name)
 
1876
 *      char *name;
 
1877
 *
 
1878
 *  DESCRIPTION
 
1879
 *
 
1880
 *      Given name of a new file, opens the file
 
1881
 *      and sets the profiler output stream to the new file.
 
1882
 *
 
1883
 *      It is currently unclear whether the prefered behavior is
 
1884
 *      to truncate any existing file, or simply append to it.
 
1885
 *      The latter behavior would be desirable for collecting
 
1886
 *      accumulated runtime history over a number of separate
 
1887
 *      runs.  It might take some changes to the analyzer program
 
1888
 *      though, and the notes that Binayak sent with the profiling
 
1889
 *      diffs indicated that append was the normal mode, but this
 
1890
 *      does not appear to agree with the actual code. I haven't
 
1891
 *      investigated at this time [fnf; 24-Jul-87].
 
1892
 */
 
1893
 
 
1894
#ifndef THREAD
 
1895
static FILE *OpenProfile(CODE_STATE *cs, const char *name)
 
1896
{
 
1897
  REGISTER FILE *fp;
 
1898
  REGISTER BOOLEAN newfile;
 
1899
 
 
1900
  fp=0;
 
1901
  if (!Writable(name))
 
1902
  {
 
1903
    (void) fprintf(cs->stack->out_file, ERR_OPEN, cs->process, name);
 
1904
    perror("");
 
1905
    (void) Delay(cs->stack->delay);
 
1906
  }
 
1907
  else
 
1908
  {
 
1909
    newfile= !EXISTS(name);
 
1910
    if (!(fp= fopen(name, "w")))
 
1911
    {
 
1912
      (void) fprintf(cs->stack->out_file, ERR_OPEN, cs->process, name);
 
1913
      perror("");
 
1914
    }
 
1915
    else
 
1916
    {
 
1917
      cs->stack->prof_file= fp;
 
1918
      if (newfile)
 
1919
      {
 
1920
        ChangeOwner(cs, name);
 
1921
      }
 
1922
    }
 
1923
  }
 
1924
  return fp;
 
1925
}
 
1926
#endif
 
1927
 
 
1928
/*
 
1929
 *  FUNCTION
 
1930
 *
 
1931
 *      DBUGCloseFile    close the debug output stream
 
1932
 *
 
1933
 *  SYNOPSIS
 
1934
 *
 
1935
 *      static VOID DBUGCloseFile(fp)
 
1936
 *      FILE *fp;
 
1937
 *
 
1938
 *  DESCRIPTION
 
1939
 *
 
1940
 *      Closes the debug output stream unless it is standard output
 
1941
 *      or standard error.
 
1942
 *
 
1943
 */
 
1944
 
 
1945
static void DBUGCloseFile(CODE_STATE *cs, FILE *fp)
 
1946
{
 
1947
  if (fp != stderr && fp != stdout && fclose(fp) == EOF)
 
1948
  {
 
1949
    pthread_mutex_lock(&THR_LOCK_dbug);
 
1950
    (void) fprintf(cs->stack->out_file, ERR_CLOSE, cs->process);
 
1951
    perror("");
 
1952
    dbug_flush(cs);
 
1953
  }
 
1954
}
 
1955
 
 
1956
 
 
1957
/*
 
1958
 *  FUNCTION
 
1959
 *
 
1960
 *      DbugExit    print error message and exit
 
1961
 *
 
1962
 *  SYNOPSIS
 
1963
 *
 
1964
 *      static VOID DbugExit(why)
 
1965
 *      char *why;
 
1966
 *
 
1967
 *  DESCRIPTION
 
1968
 *
 
1969
 *      Prints error message using current process name, the reason for
 
1970
 *      aborting (typically out of memory), and exits with status 1.
 
1971
 *      This should probably be changed to use a status code
 
1972
 *      defined in the user's debugger include file.
 
1973
 *
 
1974
 */
 
1975
 
 
1976
static void DbugExit(const char *why)
 
1977
{
 
1978
  CODE_STATE *cs=code_state();
 
1979
  (void) fprintf(stderr, ERR_ABORT, cs ? cs->process : "(null)", why);
 
1980
  (void) fflush(stderr);
 
1981
  exit(1);
 
1982
}
 
1983
 
 
1984
 
 
1985
/*
 
1986
 *  FUNCTION
 
1987
 *
 
1988
 *      DbugMalloc    allocate memory for debugger runtime support
 
1989
 *
 
1990
 *  SYNOPSIS
 
1991
 *
 
1992
 *      static long *DbugMalloc(size)
 
1993
 *      int size;
 
1994
 *
 
1995
 *  DESCRIPTION
 
1996
 *
 
1997
 *      Allocate more memory for debugger runtime support functions.
 
1998
 *      Failure to to allocate the requested number of bytes is
 
1999
 *      immediately fatal to the current process.  This may be
 
2000
 *      rather unfriendly behavior.  It might be better to simply
 
2001
 *      print a warning message, freeze the current debugger cs,
 
2002
 *      and continue execution.
 
2003
 *
 
2004
 */
 
2005
 
 
2006
static char *DbugMalloc(size_t size)
 
2007
{
 
2008
  register char *new_malloc;
 
2009
 
 
2010
  if (!(new_malloc= (char*) malloc(size)))
 
2011
    DbugExit("out of memory");
 
2012
  return new_malloc;
 
2013
}
 
2014
 
 
2015
 
 
2016
/*
 
2017
 *     strtok lookalike - splits on ':', magically handles ::, :\ and :/
 
2018
 */
 
2019
 
 
2020
static const char *DbugStrTok(const char *s)
 
2021
{
 
2022
  while (s[0] && (s[0] != ':' ||
 
2023
                  (s[1] == '\\' || s[1] == '/' || (s[1] == ':' && s++))))
 
2024
    s++;
 
2025
  return s;
 
2026
}
 
2027
 
 
2028
 
 
2029
/*
 
2030
 *  FUNCTION
 
2031
 *
 
2032
 *      BaseName    strip leading pathname components from name
 
2033
 *
 
2034
 *  SYNOPSIS
 
2035
 *
 
2036
 *      static char *BaseName(pathname)
 
2037
 *      char *pathname;
 
2038
 *
 
2039
 *  DESCRIPTION
 
2040
 *
 
2041
 *      Given pointer to a complete pathname, locates the base file
 
2042
 *      name at the end of the pathname and returns a pointer to
 
2043
 *      it.
 
2044
 *
 
2045
 */
 
2046
 
 
2047
static const char *BaseName(const char *pathname)
 
2048
{
 
2049
  register const char *base;
 
2050
 
 
2051
  base= strrchr(pathname, FN_LIBCHAR);
 
2052
  if (base++ == NullS)
 
2053
    base= pathname;
 
2054
  return base;
 
2055
}
 
2056
 
 
2057
 
 
2058
/*
 
2059
 *  FUNCTION
 
2060
 *
 
2061
 *      Writable    test to see if a pathname is writable/creatable
 
2062
 *
 
2063
 *  SYNOPSIS
 
2064
 *
 
2065
 *      static BOOLEAN Writable(pathname)
 
2066
 *      char *pathname;
 
2067
 *
 
2068
 *  DESCRIPTION
 
2069
 *
 
2070
 *      Because the debugger might be linked in with a program that
 
2071
 *      runs with the set-uid-bit (suid) set, we have to be careful
 
2072
 *      about opening a user named file for debug output.  This consists
 
2073
 *      of checking the file for write access with the real user id,
 
2074
 *      or checking the directory where the file will be created.
 
2075
 *
 
2076
 *      Returns TRUE if the user would normally be allowed write or
 
2077
 *      create access to the named file.  Returns FALSE otherwise.
 
2078
 *
 
2079
 */
 
2080
 
 
2081
 
 
2082
#ifndef Writable
 
2083
 
 
2084
static BOOLEAN Writable(const char *pathname)
 
2085
{
 
2086
  REGISTER BOOLEAN granted;
 
2087
  REGISTER char *lastslash;
 
2088
 
 
2089
  granted= FALSE;
 
2090
  if (EXISTS(pathname))
 
2091
  {
 
2092
    if (WRITABLE(pathname))
 
2093
      granted= TRUE;
 
2094
  }
 
2095
  else
 
2096
  {
 
2097
    lastslash= strrchr(pathname, '/');
 
2098
    if (lastslash != NULL)
 
2099
      *lastslash= '\0';
 
2100
    else
 
2101
      pathname= ".";
 
2102
    if (WRITABLE(pathname))
 
2103
      granted= TRUE;
 
2104
    if (lastslash != NULL)
 
2105
      *lastslash= '/';
 
2106
  }
 
2107
  return granted;
 
2108
}
 
2109
#endif
 
2110
 
 
2111
 
 
2112
/*
 
2113
 *  FUNCTION
 
2114
 *
 
2115
 *      ChangeOwner    change owner to real user for suid programs
 
2116
 *
 
2117
 *  SYNOPSIS
 
2118
 *
 
2119
 *      static VOID ChangeOwner(pathname)
 
2120
 *
 
2121
 *  DESCRIPTION
 
2122
 *
 
2123
 *      For unix systems, change the owner of the newly created debug
 
2124
 *      file to the real owner.  This is strictly for the benefit of
 
2125
 *      programs that are running with the set-user-id bit set.
 
2126
 *
 
2127
 *      Note that at this point, the fact that pathname represents
 
2128
 *      a newly created file has already been established.  If the
 
2129
 *      program that the debugger is linked to is not running with
 
2130
 *      the suid bit set, then this operation is redundant (but
 
2131
 *      harmless).
 
2132
 *
 
2133
 */
 
2134
 
 
2135
#ifndef ChangeOwner
 
2136
static void ChangeOwner(CODE_STATE *cs, char *pathname)
 
2137
{
 
2138
  if (chown(pathname, getuid(), getgid()) == -1)
 
2139
  {
 
2140
    (void) fprintf(stderr, ERR_CHOWN, cs->process, pathname);
 
2141
    perror("");
 
2142
    (void) fflush(stderr);
 
2143
  }
 
2144
}
 
2145
#endif
 
2146
 
 
2147
 
 
2148
/*
 
2149
 *  FUNCTION
 
2150
 *
 
2151
 *      _db_setjmp_    save debugger environment
 
2152
 *
 
2153
 *  SYNOPSIS
 
2154
 *
 
2155
 *      VOID _db_setjmp_()
 
2156
 *
 
2157
 *  DESCRIPTION
 
2158
 *
 
2159
 *      Invoked as part of the user's DBUG_SETJMP macro to save
 
2160
 *      the debugger environment in parallel with saving the user's
 
2161
 *      environment.
 
2162
 *
 
2163
 */
 
2164
 
 
2165
#ifdef HAVE_LONGJMP
 
2166
 
 
2167
EXPORT void _db_setjmp_()
 
2168
{
 
2169
  CODE_STATE *cs=0;
 
2170
  get_code_state_or_return;
 
2171
 
 
2172
  cs->jmplevel= cs->level;
 
2173
  cs->jmpfunc= cs->func;
 
2174
  cs->jmpfile= cs->file;
 
2175
}
 
2176
 
 
2177
/*
 
2178
 *  FUNCTION
 
2179
 *
 
2180
 *      _db_longjmp_    restore previously saved debugger environment
 
2181
 *
 
2182
 *  SYNOPSIS
 
2183
 *
 
2184
 *      VOID _db_longjmp_()
 
2185
 *
 
2186
 *  DESCRIPTION
 
2187
 *
 
2188
 *      Invoked as part of the user's DBUG_LONGJMP macro to restore
 
2189
 *      the debugger environment in parallel with restoring the user's
 
2190
 *      previously saved environment.
 
2191
 *
 
2192
 */
 
2193
 
 
2194
EXPORT void _db_longjmp_()
 
2195
{
 
2196
  CODE_STATE *cs=0;
 
2197
  get_code_state_or_return;
 
2198
 
 
2199
  cs->level= cs->jmplevel;
 
2200
  if (cs->jmpfunc)
 
2201
    cs->func= cs->jmpfunc;
 
2202
  if (cs->jmpfile)
 
2203
    cs->file= cs->jmpfile;
 
2204
}
 
2205
#endif
 
2206
 
 
2207
/*
 
2208
 *  FUNCTION
 
2209
 *
 
2210
 *      perror    perror simulation for systems that don't have it
 
2211
 *
 
2212
 *  SYNOPSIS
 
2213
 *
 
2214
 *      static VOID perror(s)
 
2215
 *      char *s;
 
2216
 *
 
2217
 *  DESCRIPTION
 
2218
 *
 
2219
 *      Perror produces a message on the standard error stream which
 
2220
 *      provides more information about the library or system error
 
2221
 *      just encountered.  The argument string s is printed, followed
 
2222
 *      by a ':', a blank, and then a message and a newline.
 
2223
 *
 
2224
 *      An undocumented feature of the unix perror is that if the string
 
2225
 *      's' is a null string (NOT a NULL pointer!), then the ':' and
 
2226
 *      blank are not printed.
 
2227
 *
 
2228
 *      This version just complains about an "unknown system error".
 
2229
 *
 
2230
 */
 
2231
 
 
2232
#ifndef HAVE_PERROR
 
2233
static void perror(s)
 
2234
char *s;
 
2235
{
 
2236
  if (s && *s != '\0')
 
2237
    (void) fprintf(stderr, "%s: ", s);
 
2238
  (void) fprintf(stderr, "<unknown system error>\n");
 
2239
}
 
2240
#endif /* HAVE_PERROR */
 
2241
 
 
2242
 
 
2243
        /* flush dbug-stream, free mutex lock & wait delay */
 
2244
        /* This is because some systems (MSDOS!!) dosn't flush fileheader */
 
2245
        /* and dbug-file isn't readable after a system crash !! */
 
2246
 
 
2247
static void dbug_flush(CODE_STATE *cs)
 
2248
{
 
2249
#ifndef THREAD
 
2250
  if (cs->stack->flags & FLUSH_ON_WRITE)
 
2251
#endif
 
2252
  {
 
2253
    (void) fflush(cs->stack->out_file);
 
2254
    if (cs->stack->delay)
 
2255
      (void) Delay(cs->stack->delay);
 
2256
  }
 
2257
  if (!cs->locked)
 
2258
    pthread_mutex_unlock(&THR_LOCK_dbug);
 
2259
} /* dbug_flush */
 
2260
 
 
2261
 
 
2262
void _db_lock_file_()
 
2263
{
 
2264
  CODE_STATE *cs=0;
 
2265
  get_code_state_or_return;
 
2266
  pthread_mutex_lock(&THR_LOCK_dbug);
 
2267
  cs->locked=1;
 
2268
}
 
2269
 
 
2270
void _db_unlock_file_()
 
2271
{
 
2272
  CODE_STATE *cs=0;
 
2273
  get_code_state_or_return;
 
2274
  cs->locked=0;
 
2275
  pthread_mutex_unlock(&THR_LOCK_dbug);
 
2276
}
 
2277
 
 
2278
/*
 
2279
 * Here we need the definitions of the clock routine.  Add your
 
2280
 * own for whatever system that you have.
 
2281
 */
 
2282
 
 
2283
#ifndef THREAD
 
2284
#if defined(HAVE_GETRUSAGE)
 
2285
 
 
2286
#include <sys/param.h>
 
2287
#include <sys/resource.h>
 
2288
 
 
2289
/* extern int getrusage(int, struct rusage *); */
 
2290
 
 
2291
/*
 
2292
 * Returns the user time in milliseconds used by this process so
 
2293
 * far.
 
2294
 */
 
2295
 
 
2296
static unsigned long Clock()
 
2297
{
 
2298
    struct rusage ru;
 
2299
 
 
2300
    (void) getrusage(RUSAGE_SELF, &ru);
 
2301
    return ru.ru_utime.tv_sec*1000 + ru.ru_utime.tv_usec/1000;
 
2302
}
 
2303
 
 
2304
#elif defined(MSDOS) || defined(__WIN__)
 
2305
 
 
2306
static ulong Clock()
 
2307
{
 
2308
  return clock()*(1000/CLOCKS_PER_SEC);
 
2309
}
 
2310
#elif defined(amiga)
 
2311
 
 
2312
struct DateStamp {              /* Yes, this is a hack, but doing it right */
 
2313
        long ds_Days;           /* is incredibly ugly without splitting this */
 
2314
        long ds_Minute;         /* off into a separate file */
 
2315
        long ds_Tick;
 
2316
};
 
2317
 
 
2318
static int first_clock= TRUE;
 
2319
static struct DateStamp begin;
 
2320
static struct DateStamp elapsed;
 
2321
 
 
2322
static unsigned long Clock()
 
2323
{
 
2324
    register struct DateStamp *now;
 
2325
    register unsigned long millisec= 0;
 
2326
    extern VOID *AllocMem();
 
2327
 
 
2328
    now= (struct DateStamp *) AllocMem((long) sizeof(struct DateStamp), 0L);
 
2329
    if (now != NULL)
 
2330
    {
 
2331
        if (first_clock == TRUE)
 
2332
        {
 
2333
            first_clock= FALSE;
 
2334
            (void) DateStamp(now);
 
2335
            begin= *now;
 
2336
        }
 
2337
        (void) DateStamp(now);
 
2338
        millisec= 24 * 3600 * (1000 / HZ) * (now->ds_Days - begin.ds_Days);
 
2339
        millisec += 60 * (1000 / HZ) * (now->ds_Minute - begin.ds_Minute);
 
2340
        millisec += (1000 / HZ) * (now->ds_Tick - begin.ds_Tick);
 
2341
        (void) FreeMem(now, (long) sizeof(struct DateStamp));
 
2342
    }
 
2343
    return millisec;
 
2344
}
 
2345
#else
 
2346
static unsigned long Clock()
 
2347
{
 
2348
    return 0;
 
2349
}
 
2350
#endif /* RUSAGE */
 
2351
#endif /* THREADS */
 
2352
 
 
2353
#ifdef NO_VARARGS
 
2354
 
 
2355
/*
 
2356
 *      Fake vfprintf for systems that don't support it.  If this
 
2357
 *      doesn't work, you are probably SOL...
 
2358
 */
 
2359
 
 
2360
static int vfprintf(stream, format, ap)
 
2361
FILE *stream;
 
2362
char *format;
 
2363
va_list ap;
 
2364
{
 
2365
    int rtnval;
 
2366
    ARGS_DCL;
 
2367
 
 
2368
    ARG0=  va_arg(ap, ARGS_TYPE);
 
2369
    ARG1=  va_arg(ap, ARGS_TYPE);
 
2370
    ARG2=  va_arg(ap, ARGS_TYPE);
 
2371
    ARG3=  va_arg(ap, ARGS_TYPE);
 
2372
    ARG4=  va_arg(ap, ARGS_TYPE);
 
2373
    ARG5=  va_arg(ap, ARGS_TYPE);
 
2374
    ARG6=  va_arg(ap, ARGS_TYPE);
 
2375
    ARG7=  va_arg(ap, ARGS_TYPE);
 
2376
    ARG8=  va_arg(ap, ARGS_TYPE);
 
2377
    ARG9=  va_arg(ap, ARGS_TYPE);
 
2378
    rtnval= fprintf(stream, format, ARGS_LIST);
 
2379
    return rtnval;
 
2380
}
 
2381
 
 
2382
#endif  /* NO_VARARGS */
 
2383
 
 
2384
#else
 
2385
 
 
2386
/*
 
2387
 * Dummy function, workaround for MySQL bug#14420 related
 
2388
 * build failure on a platform where linking with an empty
 
2389
 * archive fails.
 
2390
 *
 
2391
 * This block can be removed as soon as a fix for bug#14420
 
2392
 * is implemented.
 
2393
 */
 
2394
int i_am_a_dummy_function() {
 
2395
       return 0;
 
2396
}
 
2397
 
 
2398
#endif