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