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