forked from mysql/mysql-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdbug.c
2282 lines (2063 loc) · 60.8 KB
/
dbug.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/******************************************************************************
* *
* N O T I C E *
* *
* Copyright Abandoned, 1987, Fred Fish *
* *
* *
* This previously copyrighted work has been placed into the public *
* domain by the author and may be freely used for any purpose, *
* private or commercial. *
* *
* Because of the number of inquiries I was receiving about the use *
* of this product in commercially developed works I have decided to *
* simply make it public domain to further its unrestricted use. I *
* specifically would be most happy to see this material become a *
* part of the standard Unix distributions by AT&T and the Berkeley *
* Computer Science Research Group, and a standard part of the GNU *
* system from the Free Software Foundation. *
* *
* I would appreciate it, as a courtesy, if this notice is left in *
* all copies and derivative works. Thank you. *
* *
* The author makes no warranty of any kind with respect to this *
* product and explicitly disclaims any implied warranties of mer- *
* chantability or fitness for any particular purpose. *
* *
******************************************************************************
*/
/*
* FILE
*
* dbug.c runtime support routines for dbug package
*
* SCCS
*
* @(#)dbug.c 1.25 7/25/89
*
* DESCRIPTION
*
* These are the runtime support routines for the dbug package.
* The dbug package has two main components; the user include
* file containing various macro definitions, and the runtime
* support routines which are called from the macro expansions.
*
* Externally visible functions in the runtime support module
* use the naming convention pattern "_db_xx...xx_", thus
* they are unlikely to collide with user defined function names.
*
* AUTHOR(S)
*
* Fred Fish (base code)
* Enhanced Software Technologies, Tempe, AZ
* asuvax!mcdphx!estinc!fnf
*
* Binayak Banerjee (profiling enhancements)
* seismo!bpa!sjuvax!bbanerje
*
* Michael Widenius:
* DBUG_DUMP - To dump a block of memory.
* PUSH_FLAG "O" - To be used insted of "o" if we
* want flushing after each write
* PUSH_FLAG "A" - as 'O', but we will append to the out file instead
* of creating a new one.
* Check of malloc on entry/exit (option "S")
*
* Sergei Golubchik:
* DBUG_EXECUTE_IF
* incremental mode (-#+t:-d,info ...)
* DBUG_SET, _db_explain_
* thread-local settings
* negative lists (-#-d,info => everything but "info")
*
* function/ syntax
* (the logic is - think of a call stack as of a path.
* "function" means only this function, "function/" means the hierarchy.
* in the future, filters like function1/function2 could be supported.
* following this logic glob(7) wildcards are supported.)
*
*/
/*
We can't have SAFE_MUTEX defined here as this will cause recursion
in pthread_mutex_lock
*/
#undef SAFE_MUTEX
#include <my_global.h>
#include <m_string.h>
#include <errno.h>
#ifdef HAVE_FNMATCH_H
#include <fnmatch.h>
#else
#define fnmatch(A,B,C) strcmp(A,B)
#endif
#if defined(__WIN__)
#include <process.h>
#endif
#ifndef DBUG_OFF
/*
* Manifest constants which may be "tuned" if desired.
*/
#define PRINTBUF 1024 /* Print buffer size */
#define INDENT 2 /* Indentation per trace level */
#define MAXDEPTH 200 /* Maximum trace depth default */
/*
* The following flags are used to determine which
* capabilities the user has enabled with the settings
* push macro.
*
* TRACE_ON is also used in _db_stack_frame_->level
* (until we add flags to _db_stack_frame_, increasing it by 4 bytes)
*/
#define DEBUG_ON (1 << 1) /* Debug enabled */
#define FILE_ON (1 << 2) /* File name print enabled */
#define LINE_ON (1 << 3) /* Line number print enabled */
#define DEPTH_ON (1 << 4) /* Function nest level print enabled */
#define PROCESS_ON (1 << 5) /* Process name print enabled */
#define NUMBER_ON (1 << 6) /* Number each line of output */
#define PROFILE_ON (1 << 7) /* Print out profiling code */
#define PID_ON (1 << 8) /* Identify each line with process id */
#define TIMESTAMP_ON (1 << 9) /* timestamp every line of output */
#define FLUSH_ON_WRITE (1 << 10) /* Flush on every write */
#define OPEN_APPEND (1 << 11) /* Open for append */
#define TRACE_ON ((uint)1 << 31) /* Trace enabled. MUST be the highest bit!*/
#define TRACING (cs->stack->flags & TRACE_ON)
#define DEBUGGING (cs->stack->flags & DEBUG_ON)
#define PROFILING (cs->stack->flags & PROFILE_ON)
/*
* Typedefs to make things more obvious.
*/
#define BOOLEAN my_bool
/*
* Make it easy to change storage classes if necessary.
*/
#define IMPORT extern /* Names defined externally */
#define EXPORT /* Allocated here, available globally */
#define AUTO auto /* Names to be allocated on stack */
#define REGISTER register /* Names to be placed in registers */
/*
* The default file for profiling. Could also add another flag
* (G?) which allowed the user to specify this.
*
* If the automatic variables get allocated on the stack in
* reverse order from their declarations, then define AUTOS_REVERSE to 1.
* This is used by the code that keeps track of stack usage. For
* forward allocation, the difference in the dbug frame pointers
* represents stack used by the callee function. For reverse allocation,
* the difference represents stack used by the caller function.
*
*/
#define PROF_FILE "dbugmon.out"
#define PROF_EFMT "E\t%ld\t%s\n"
#define PROF_SFMT "S\t%lx\t%lx\t%s\n"
#define PROF_XFMT "X\t%ld\t%s\n"
#ifdef M_I386 /* predefined by xenix 386 compiler */
#define AUTOS_REVERSE 1
#else
#define AUTOS_REVERSE 0
#endif
/*
* Externally supplied functions.
*/
#ifndef HAVE_PERROR
static void perror(); /* Fake system/library error print routine */
#endif
/*
* The user may specify a list of functions to trace or
* debug. These lists are kept in a linear linked list,
* a very simple implementation.
*/
struct link {
struct link *next_link; /* Pointer to the next link */
char flags;
char str[1]; /* Pointer to link's contents */
};
/* flags for struct link and return flags of InList */
#define SUBDIR 1 /* this MUST be 1 */
#define INCLUDE 2
#define EXCLUDE 4
/* this is not a struct link flag, but only a return flags of InList */
#define MATCHED 65536
#define NOT_MATCHED 0
/*
* Debugging settings can be pushed or popped off of a
* stack which is implemented as a linked list. Note
* that the head of the list is the current settings and the
* stack is pushed by adding a new settings to the head of the
* list or popped by removing the first link.
*
* Note: if out_file is NULL, the other fields are not initialized at all!
*/
struct settings {
uint flags; /* Current settings flags */
uint maxdepth; /* Current maximum trace depth */
uint delay; /* Delay after each output line */
uint sub_level; /* Sub this from code_state->level */
FILE *out_file; /* Current output stream */
FILE *prof_file; /* Current profiling stream */
char name[FN_REFLEN]; /* Name of output file */
struct link *functions; /* List of functions */
struct link *p_functions; /* List of profiled functions */
struct link *keywords; /* List of debug keywords */
struct link *processes; /* List of process names */
struct settings *next; /* Next settings in the list */
};
#define is_shared(S, V) ((S)->next && (S)->next->V == (S)->V)
/*
* Local variables not seen by user.
*/
static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */
static struct settings init_settings;
static const char *db_process= 0;/* Pointer to process name; argv[0] */
my_bool _dbug_on_= TRUE; /* FALSE if no debugging at all */
typedef struct _db_code_state_ {
const char *process; /* Pointer to process name; usually argv[0] */
const char *func; /* Name of current user function */
const char *file; /* Name of current user file */
struct _db_stack_frame_ *framep; /* Pointer to current frame */
struct settings *stack; /* debugging settings */
const char *jmpfunc; /* Remember current function for setjmp */
const char *jmpfile; /* Remember current file for setjmp */
int lineno; /* Current debugger output line number */
uint level; /* Current function nesting level */
int jmplevel; /* Remember nesting level at setjmp() */
/*
* The following variables are used to hold the state information
* between the call to _db_pargs_() and _db_doprnt_(), during
* expansion of the DBUG_PRINT macro. This is the only macro
* that currently uses these variables.
*
* These variables are currently used only by _db_pargs_() and
* _db_doprnt_().
*/
uint u_line; /* User source code line number */
int locked; /* If locked with _db_lock_file_ */
const char *u_keyword; /* Keyword for current macro */
} CODE_STATE;
/*
The test below is so we could call functions with DBUG_ENTER before
my_thread_init().
*/
#define get_code_state_if_not_set_or_return if (!cs && !((cs=code_state()))) return
#define get_code_state_or_return if (!((cs=code_state()))) return
/* Handling lists */
#define ListAdd(A,B,C) ListAddDel(A,B,C,INCLUDE)
#define ListDel(A,B,C) ListAddDel(A,B,C,EXCLUDE)
static struct link *ListAddDel(struct link *, const char *, const char *, int);
static struct link *ListCopy(struct link *);
static int InList(struct link *linkp,const char *cp);
static uint ListFlags(struct link *linkp);
static void FreeList(struct link *linkp);
/* OpenClose debug output stream */
static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int);
static void DBUGCloseFile(CODE_STATE *cs, FILE *fp);
/* Push current debug settings */
static void PushState(CODE_STATE *cs);
/* Free memory associated with debug state. */
static void FreeState (CODE_STATE *cs, struct settings *state, int free_state);
/* Test for tracing enabled */
static int DoTrace(CODE_STATE *cs);
/*
return values of DoTrace.
Can also be used as bitmask: ret & DO_TRACE
*/
#define DO_TRACE 1
#define DONT_TRACE 2
#define ENABLE_TRACE 3
#define DISABLE_TRACE 4
/* Test to see if file is writable */
#if defined(HAVE_ACCESS)
static BOOLEAN Writable(const char *pathname);
/* Change file owner and group */
static void ChangeOwner(CODE_STATE *cs, char *pathname);
/* Allocate memory for runtime support */
#endif
static void DoPrefix(CODE_STATE *cs, uint line);
static char *DbugMalloc(size_t size);
static const char *BaseName(const char *pathname);
static void Indent(CODE_STATE *cs, int indent);
static void DbugFlush(CODE_STATE *);
static void DbugExit(const char *why);
static const char *DbugStrTok(const char *s);
static void DbugVfprintf(FILE *stream, const char* format, va_list args);
/*
* Miscellaneous printf format strings.
*/
#define ERR_MISSING_RETURN "missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
#define ERR_OPEN "%s: can't open debug output stream \"%s\": "
#define ERR_CLOSE "%s: can't close debug file: "
#define ERR_ABORT "%s: debugger aborting because %s\n"
/*
* Macros and defines for testing file accessibility under UNIX and MSDOS.
*/
#undef EXISTS
#if !defined(HAVE_ACCESS)
#define EXISTS(pathname) (FALSE) /* Assume no existance */
#define Writable(name) (TRUE)
#else
#define EXISTS(pathname) (access(pathname, F_OK) == 0)
#define WRITABLE(pathname) (access(pathname, W_OK) == 0)
#endif
/*
** Macros to allow dbugging with threads
*/
#include <my_pthread.h>
static pthread_mutex_t THR_LOCK_dbug;
static CODE_STATE *code_state(void)
{
CODE_STATE *cs, **cs_ptr;
/*
_dbug_on_ is reset if we don't plan to use any debug commands at all and
we want to run on maximum speed
*/
if (!_dbug_on_)
return 0;
if (!init_done)
{
init_done=TRUE;
pthread_mutex_init(&THR_LOCK_dbug, NULL);
bzero(&init_settings, sizeof(init_settings));
init_settings.out_file=stderr;
init_settings.flags=OPEN_APPEND;
}
if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug()))
return 0; /* Thread not initialised */
if (!(cs= *cs_ptr))
{
cs=(CODE_STATE*) DbugMalloc(sizeof(*cs));
bzero((uchar*) cs,sizeof(*cs));
cs->process= db_process ? db_process : "dbug";
cs->func="?func";
cs->file="?file";
cs->stack=&init_settings;
*cs_ptr= cs;
}
return cs;
}
/*
* Translate some calls among different systems.
*/
#ifdef HAVE_SLEEP
/* sleep() wants seconds */
#define Delay(A) sleep(((uint) A)/10)
#else
#define Delay(A) (0)
#endif
/*
* FUNCTION
*
* _db_process_ give the name to the current process/thread
*
* SYNOPSIS
*
* VOID _process_(name)
* char *name;
*
*/
void _db_process_(const char *name)
{
CODE_STATE *cs;
if (!db_process)
db_process= name;
get_code_state_or_return;
cs->process= name;
}
/*
* FUNCTION
*
* DbugParse parse control string and set current debugger settings
*
* DESCRIPTION
*
* Given pointer to a debug control string in "control",
* parses the control string, and sets
* up a current debug settings.
*
* The debug control string is a sequence of colon separated fields
* as follows:
*
* [+]<field_1>:<field_2>:...:<field_N>
*
* Each field consists of a mandatory flag character followed by
* an optional "," and comma separated list of modifiers:
*
* [sign]flag[,modifier,modifier,...,modifier]
*
* See the manual for the list of supported signs, flags, and modifiers
*
* For convenience, any leading "-#" is stripped off.
*
* RETURN
* 1 - a list of functions ("f" flag) was possibly changed
* 0 - a list of functions was not changed
*/
int DbugParse(CODE_STATE *cs, const char *control)
{
const char *end;
int rel, f_used=0;
struct settings *stack;
stack= cs->stack;
if (control[0] == '-' && control[1] == '#')
control+=2;
rel= control[0] == '+' || control[0] == '-';
if ((!rel || (!stack->out_file && !stack->next)))
{
/* Free memory associated with the state before resetting its members */
FreeState(cs, stack, 0);
stack->flags= 0;
stack->delay= 0;
stack->maxdepth= 0;
stack->sub_level= 0;
stack->out_file= stderr;
stack->prof_file= NULL;
stack->functions= NULL;
stack->p_functions= NULL;
stack->keywords= NULL;
stack->processes= NULL;
}
else if (!stack->out_file)
{
stack->flags= stack->next->flags;
stack->delay= stack->next->delay;
stack->maxdepth= stack->next->maxdepth;
stack->sub_level= stack->next->sub_level;
strcpy(stack->name, stack->next->name);
stack->prof_file= stack->next->prof_file;
if (stack->next == &init_settings)
{
/*
Never share with the global parent - it can change under your feet.
Reset out_file to stderr to prevent sharing of trace files between
global and session settings.
*/
stack->out_file= stderr;
stack->functions= ListCopy(init_settings.functions);
stack->p_functions= ListCopy(init_settings.p_functions);
stack->keywords= ListCopy(init_settings.keywords);
stack->processes= ListCopy(init_settings.processes);
}
else
{
stack->out_file= stack->next->out_file;
stack->functions= stack->next->functions;
stack->p_functions= stack->next->p_functions;
stack->keywords= stack->next->keywords;
stack->processes= stack->next->processes;
}
}
end= DbugStrTok(control);
while (control < end)
{
int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0;
if (sign) control++;
c= *control++;
if (*control == ',') control++;
/* XXX when adding new cases here, don't forget _db_explain_ ! */
switch (c) {
case 'd':
if (sign < 0 && control == end)
{
if (!is_shared(stack, keywords))
FreeList(stack->keywords);
stack->keywords=NULL;
stack->flags &= ~DEBUG_ON;
break;
}
if (rel && is_shared(stack, keywords))
stack->keywords= ListCopy(stack->keywords);
if (sign < 0)
{
if (DEBUGGING)
stack->keywords= ListDel(stack->keywords, control, end);
break;
}
stack->keywords= ListAdd(stack->keywords, control, end);
stack->flags |= DEBUG_ON;
break;
case 'D':
stack->delay= atoi(control);
break;
case 'f':
f_used= 1;
if (sign < 0 && control == end)
{
if (!is_shared(stack,functions))
FreeList(stack->functions);
stack->functions=NULL;
break;
}
if (rel && is_shared(stack,functions))
stack->functions= ListCopy(stack->functions);
if (sign < 0)
stack->functions= ListDel(stack->functions, control, end);
else
stack->functions= ListAdd(stack->functions, control, end);
break;
case 'F':
if (sign < 0)
stack->flags &= ~FILE_ON;
else
stack->flags |= FILE_ON;
break;
case 'i':
if (sign < 0)
stack->flags &= ~PID_ON;
else
stack->flags |= PID_ON;
break;
case 'L':
if (sign < 0)
stack->flags &= ~LINE_ON;
else
stack->flags |= LINE_ON;
break;
case 'n':
if (sign < 0)
stack->flags &= ~DEPTH_ON;
else
stack->flags |= DEPTH_ON;
break;
case 'N':
if (sign < 0)
stack->flags &= ~NUMBER_ON;
else
stack->flags |= NUMBER_ON;
break;
case 'A':
case 'O':
stack->flags |= FLUSH_ON_WRITE;
/* fall through */
case 'a':
case 'o':
if (sign < 0)
{
if (!is_shared(stack, out_file))
DBUGCloseFile(cs, stack->out_file);
stack->flags &= ~FLUSH_ON_WRITE;
stack->out_file= stderr;
break;
}
if (c == 'a' || c == 'A')
stack->flags |= OPEN_APPEND;
else
stack->flags &= ~OPEN_APPEND;
if (control != end)
DBUGOpenFile(cs, control, end, stack->flags & OPEN_APPEND);
else
DBUGOpenFile(cs, "-",0,0);
break;
case 'p':
if (sign < 0 && control == end)
{
if (!is_shared(stack,processes))
FreeList(stack->processes);
stack->processes=NULL;
break;
}
if (rel && is_shared(stack, processes))
stack->processes= ListCopy(stack->processes);
if (sign < 0)
stack->processes= ListDel(stack->processes, control, end);
else
stack->processes= ListAdd(stack->processes, control, end);
break;
case 'P':
if (sign < 0)
stack->flags &= ~PROCESS_ON;
else
stack->flags |= PROCESS_ON;
break;
case 'r':
stack->sub_level= cs->level;
break;
case 't':
if (sign < 0)
{
if (control != end)
stack->maxdepth-= atoi(control);
else
stack->maxdepth= 0;
}
else
{
if (control != end)
stack->maxdepth+= atoi(control);
else
stack->maxdepth= MAXDEPTH;
}
if (stack->maxdepth > 0)
stack->flags |= TRACE_ON;
else
stack->flags &= ~TRACE_ON;
break;
case 'T':
if (sign < 0)
stack->flags &= ~TIMESTAMP_ON;
else
stack->flags |= TIMESTAMP_ON;
break;
}
if (!*end)
break;
control=end+1;
end= DbugStrTok(control);
}
return !rel || f_used;
}
#define framep_trace_flag(cs, frp) (frp ? \
frp->level & TRACE_ON : \
(ListFlags(cs->stack->functions) & INCLUDE) ? \
0 : (uint)TRACE_ON)
void FixTraceFlags_helper(CODE_STATE *cs, const char *func,
struct _db_stack_frame_ *framep)
{
if (framep->prev)
FixTraceFlags_helper(cs, framep->func, framep->prev);
cs->func= func;
cs->level= framep->level & ~TRACE_ON;
framep->level= cs->level | framep_trace_flag(cs, framep->prev);
/*
we don't set cs->framep correctly, even though DoTrace uses it.
It's ok, because cs->framep may only affect DO_TRACE/DONT_TRACE return
values, but we ignore them here anyway
*/
switch(DoTrace(cs)) {
case ENABLE_TRACE:
framep->level|= TRACE_ON;
break;
case DISABLE_TRACE:
framep->level&= ~TRACE_ON;
break;
}
}
#define fflags(cs) cs->stack->out_file ? ListFlags(cs->stack->functions) : TRACE_ON;
void FixTraceFlags(uint old_fflags, CODE_STATE *cs)
{
const char *func;
uint new_fflags, traceon, level;
struct _db_stack_frame_ *framep;
/*
first (a.k.a. safety) check:
if we haven't started tracing yet, no call stack at all - we're safe.
*/
framep=cs->framep;
if (framep == 0)
return;
/*
Ok, the tracing has started, call stack isn't empty.
second check: does the new list have a SUBDIR rule ?
*/
new_fflags=fflags(cs);
if (new_fflags & SUBDIR)
goto yuck;
/*
Ok, new list doesn't use SUBDIR.
third check: we do NOT need to re-scan if
neither old nor new lists used SUBDIR flag and if a default behavior
(whether an unlisted function is traced) hasn't changed.
Default behavior depends on whether there're INCLUDE elements in the list.
*/
if (!(old_fflags & SUBDIR) && !((new_fflags^old_fflags) & INCLUDE))
return;
/*
Ok, old list may've used SUBDIR, or defaults could've changed.
fourth check: are we inside a currently active SUBDIR rule ?
go up the call stack, if TRACE_ON flag ever changes its value - we are.
*/
for (traceon=framep->level; framep; framep=framep->prev)
if ((traceon ^ framep->level) & TRACE_ON)
goto yuck;
/*
Ok, TRACE_ON flag doesn't change in the call stack.
fifth check: but is the top-most value equal to a default one ?
*/
if (((traceon & TRACE_ON) != 0) == ((new_fflags & INCLUDE) == 0))
return;
yuck:
/*
Yuck! function list was changed, and one of the currently active rules
was possibly affected. For example, a tracing could've been enabled or
disabled for a function somewhere up the call stack.
To react correctly, we must go up the call stack all the way to
the top and re-match rules to set TRACE_ON bit correctly.
We must traverse the stack forwards, not backwards.
That's what a recursive helper is doing.
It'll destroy two CODE_STATE fields, save them now.
*/
func= cs->func;
level= cs->level;
FixTraceFlags_helper(cs, func, cs->framep);
/* now we only need to restore CODE_STATE fields, and we're done */
cs->func= func;
cs->level= level;
}
/*
* FUNCTION
*
* _db_set_ set current debugger settings
*
* SYNOPSIS
*
* VOID _db_set_(control)
* char *control;
*
* DESCRIPTION
*
* Given pointer to a debug control string in "control",
* parses the control string, and sets up a current debug
* settings. Pushes a new debug settings if the current is
* set to the initial debugger settings.
*
*/
void _db_set_(const char *control)
{
CODE_STATE *cs;
uint old_fflags;
get_code_state_or_return;
old_fflags=fflags(cs);
if (cs->stack == &init_settings)
PushState(cs);
if (DbugParse(cs, control))
FixTraceFlags(old_fflags, cs);
}
/*
* FUNCTION
*
* _db_push_ push current debugger settings and set up new one
*
* SYNOPSIS
*
* VOID _db_push_(control)
* char *control;
*
* DESCRIPTION
*
* Given pointer to a debug control string in "control", pushes
* the current debug settings, parses the control string, and sets
* up a new debug settings with DbugParse()
*
*/
void _db_push_(const char *control)
{
CODE_STATE *cs;
uint old_fflags;
get_code_state_or_return;
old_fflags=fflags(cs);
PushState(cs);
if (DbugParse(cs, control))
FixTraceFlags(old_fflags, cs);
}
/**
Returns TRUE if session-local settings have been set.
*/
int _db_is_pushed_()
{
CODE_STATE *cs= NULL;
get_code_state_or_return FALSE;
return (cs->stack != &init_settings);
}
/*
* FUNCTION
*
* _db_set_init_ set initial debugger settings
*
* SYNOPSIS
*
* VOID _db_set_init_(control)
* char *control;
*
* DESCRIPTION
* see _db_set_
*
*/
void _db_set_init_(const char *control)
{
CODE_STATE tmp_cs;
bzero((uchar*) &tmp_cs, sizeof(tmp_cs));
tmp_cs.stack= &init_settings;
tmp_cs.process= db_process ? db_process : "dbug";
DbugParse(&tmp_cs, control);
}
/*
* FUNCTION
*
* _db_pop_ pop the debug stack
*
* DESCRIPTION
*
* Pops the debug stack, returning the debug settings to its
* condition prior to the most recent _db_push_ invocation.
* Note that the pop will fail if it would remove the last
* valid settings from the stack. This prevents user errors
* in the push/pop sequence from screwing up the debugger.
* Maybe there should be some kind of warning printed if the
* user tries to pop too many states.
*
*/
void _db_pop_()
{
struct settings *discard;
uint old_fflags;
CODE_STATE *cs;
get_code_state_or_return;
discard= cs->stack;
if (discard != &init_settings)
{
old_fflags=fflags(cs);
cs->stack= discard->next;
FreeState(cs, discard, 1);
FixTraceFlags(old_fflags, cs);
}
}
/*
* FUNCTION
*
* _db_explain_ generates 'control' string for the current settings
*
* RETURN
* 0 - ok
* 1 - buffer too short, output truncated
*
*/
/* helper macros */
#define char_to_buf(C) do { \
*buf++=(C); \
if (buf >= end) goto overflow; \
} while (0)
#define str_to_buf(S) do { \
char_to_buf(','); \
buf=strnmov(buf, (S), len+1); \
if (buf >= end) goto overflow; \
} while (0)
#define list_to_buf(l, f) do { \
struct link *listp=(l); \
while (listp) \
{ \
if (listp->flags & (f)) \
{ \
str_to_buf(listp->str); \
if (listp->flags & SUBDIR) \
char_to_buf('/'); \
} \
listp=listp->next_link; \
} \
} while (0)
#define int_to_buf(i) do { \
char b[50]; \
int10_to_str((i), b, 10); \
str_to_buf(b); \
} while (0)
#define colon_to_buf do { \
if (buf != start) char_to_buf(':'); \
} while(0)
#define op_int_to_buf(C, val, def) do { \
if ((val) != (def)) \
{ \
colon_to_buf; \
char_to_buf((C)); \
int_to_buf(val); \
} \
} while (0)
#define op_intf_to_buf(C, val, def, cond) do { \
if ((cond)) \
{ \
colon_to_buf; \
char_to_buf((C)); \
if ((val) != (def)) int_to_buf(val); \
} \
} while (0)
#define op_str_to_buf(C, val, cond) do { \
if ((cond)) \
{ \
char *s=(val); \
colon_to_buf; \
char_to_buf((C)); \
if (*s) str_to_buf(s); \
} \
} while (0)
#define op_list_to_buf(C, val, cond) do { \
if ((cond)) \
{ \
int f=ListFlags(val); \
colon_to_buf; \
char_to_buf((C)); \
if (f & INCLUDE) \
list_to_buf(val, INCLUDE); \
if (f & EXCLUDE) \
{ \
colon_to_buf; \
char_to_buf('-'); \
char_to_buf((C)); \
list_to_buf(val, EXCLUDE); \
} \
} \
} while (0)
#define op_bool_to_buf(C, cond) do { \
if ((cond)) \
{ \
colon_to_buf; \
char_to_buf((C)); \
} \
} while (0)
int _db_explain_ (CODE_STATE *cs, char *buf, size_t len)
{
char *start=buf, *end=buf+len-4;
get_code_state_if_not_set_or_return *buf=0;