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