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 DBUG_OFF
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