~drizzle-trunk/drizzle/development

1 by brian
clean slate
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