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