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