~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to dbug/dbug.c

  • Committer: Jay Pipes
  • Date: 2008-07-18 19:46:13 UTC
  • mto: This revision was merged to the branch mainline in revision 182.
  • Revision ID: jay@mysql.com-20080718194613-8316rg4hc7bt2i2v
Final removal of DBUG library and cleanup of Makefiles

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
 
#endif