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