~maria-captains/mariadb-native-client/trunk

« back to all changes in this revision

Viewing changes to libmysql/dbug.c

  • Committer: ghost
  • Date: 2011-10-10 11:01:17 UTC
  • Revision ID: ghost@work-20111010110117-a2zv9mgwavp0iw0a
Initial import

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