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 /*
32 * FILE
33 *
34 * dbug.c runtime support routines for dbug package
35 *
36 * SCCS
37 *
38 * @(#)dbug.c 1.19 9/5/87
39 *
40 * DESCRIPTION
41 *
42 * These are the runtime support routines for the dbug package.
43 * The dbug package has two main components; the user include
44 * file containing various macro definitions, and the runtime
45 * support routines which are called from the macro expansions.
46 *
47 * Externally visible functions in the runtime support module
48 * use the naming convention pattern "_db_xx...xx_", thus
49 * they are unlikely to collide with user defined function names.
50 *
51 * AUTHOR(S)
52 *
53 * Fred Fish (base code)
54 * Enhanced Software Technologies, Tempe, AZ
55 * asuvax!mcdphx!estinc!fnf
56 *
57 * Binayak Banerjee (profiling enhancements)
58 * seismo!bpa!sjuvax!bbanerje
59 */
60
61
62 #include <stdio.h>
63
64 #ifndef NO_VARARGS
65 #include <stdarg.h> /* Use system supplied stdarg package */
66 #else
67 #include "vargs.h" /* Use our "fake" varargs (WAAAY deprecated) */
68 #endif
69
70 #if amiga
71 #define HZ (50) /* Probably in some header somewhere */
72 #endif
73
74 #ifdef M_XENIX /* Some xenix compilers predefine this */
75 #ifndef xenix
76 #define xenix 1
77 #endif
78 #endif
79
80 /*
81 * Manifest constants that should not require any changes.
82 */
83
84 #define FALSE 0 /* Boolean FALSE */
85 #define TRUE 1 /* Boolean TRUE */
86 #define EOS '\000' /* End Of String marker */
87
88 /*
89 * Manifest constants which may be "tuned" if desired.
90 */
91
92 #define PRINTBUF 1024 /* Print buffer size */
93 #define INDENT 4 /* Indentation per trace level */
94 #define MAXDEPTH 200 /* Maximum trace depth default */
95
96 /*
97 * The following flags are used to determine which
98 * capabilities the user has enabled with the state
99 * push macro.
100 */
101
102 #define TRACE_ON 000001 /* Trace enabled */
103 #define DEBUG_ON 000002 /* Debug enabled */
104 #define FILE_ON 000004 /* File name print enabled */
105 #define LINE_ON 000010 /* Line number print enabled */
106 #define DEPTH_ON 000020 /* Function nest level print enabled */
107 #define PROCESS_ON 000040 /* Process name print enabled */
108 #define NUMBER_ON 000100 /* Number each line of output */
109 #define PROFILE_ON 000200 /* Print out profiling code */
110 #define PID_ON 000400 /* Identify each line with process id */
111
112 #define TRACING (stack -> flags & TRACE_ON)
113 #define DEBUGGING (stack -> flags & DEBUG_ON)
114 #define PROFILING (stack -> flags & PROFILE_ON)
115 #define STREQ(a,b) (strcmp(a,b) == 0)
116
117 /*
118 * Typedefs to make things more obvious.
119 */
120
121 #define VOID void /* Can't use typedef for most compilers */
122 typedef int BOOLEAN;
123
124 /*
125 * Make it easy to change storage classes if necessary.
126 */
127
128 #define LOCAL static /* Names not needed by outside world */
129 #define IMPORT extern /* Names defined externally */
130 #define EXPORT /* Allocated here, available globally */
131 #define AUTO auto /* Names to be allocated on stack */
132 #define REGISTER register /* Names to be placed in registers */
133
134 /*
135 * The default file for profiling. Could also add another flag
136 * (G?) which allowed the user to specify this.
137 *
138 * If the automatic variables get allocated on the stack in
139 * reverse order from their declarations, then define AUTOS_REVERSE.
140 * This is used by the code that keeps track of stack usage. For
141 * forward allocation, the difference in the dbug frame pointers
142 * represents stack used by the callee function. For reverse allocation,
143 * the difference represents stack used by the caller function.
144 *
145 */
146
147 #define PROF_FILE "dbugmon.out"
148 #define PROF_EFMT "E\t%ld\t%s\n"
149 #define PROF_SFMT "S\t%lx\t%lx\t%s\n"
150 #define PROF_XFMT "X\t%ld\t%s\n"
151
152 #if M_I386 /* predefined by xenix 386 compiler */
153 #define AUTOS_REVERSE 1
154 #endif
155
156 /*
157 * Variables which are available externally but should only
158 * be accessed via the macro package facilities.
159 */
160
161 EXPORT FILE *_db_fp_ = stderr; /* Output stream, default stderr */
162 EXPORT FILE *_db_pfp_ = (FILE *)0; /* Profile stream, 'dbugmon.out' */
163 EXPORT char *_db_process_ = "dbug"; /* Pointer to process name; argv[0] */
164 EXPORT BOOLEAN _db_on_ = FALSE; /* TRUE if debugging currently on */
165 EXPORT BOOLEAN _db_pon_ = FALSE; /* TRUE if debugging currently on */
166
167 /*
168 * Externally supplied functions.
169 */
170
171 #if (unix || xenix) /* Only needed for unix */
172 IMPORT VOID perror (); /* Print system/library error */
173 IMPORT int chown (); /* Change owner of a file */
174 IMPORT int getgid (); /* Get real group id */
175 IMPORT int getuid (); /* Get real user id */
176 IMPORT int access (); /* Test file for access */
177 #else
178 #if !(amiga && LATTICE) && !defined(__TURBOC__)
179 LOCAL VOID perror (); /* Fake system/library error print routine */
180 #endif
181 #endif
182
183 #ifdef __TURBOC__
184 #include <stdio.h> /* get perror */
185 #endif
186
187 # if BSD4_3 || sun
188 IMPORT int getrusage ();
189 #endif
190
191 IMPORT int atoi (); /* Convert ascii to integer */
192 IMPORT VOID exit (); /* Terminate execution */
193 IMPORT int fclose (); /* Close a stream */
194 IMPORT FILE *fopen (); /* Open a stream */
195 IMPORT int fprintf (); /* Formatted print on file */
196 IMPORT int vfprintf (); /* Varargs form of fprintf */
197 IMPORT VOID free ();
198 IMPORT char *malloc (); /* Allocate memory */
199 IMPORT int strcmp (); /* Compare strings */
200 IMPORT char *strcpy (); /* Copy strings around */
201 IMPORT size_t strlen (); /* Find length of string */
202
203 #ifndef fflush /* This is sometimes a macro */
204 IMPORT int fflush (); /* Flush output for stream */
205 #endif
206
207
208 /*
209 * The user may specify a list of functions to trace or
210 * debug. These lists are kept in a linear linked list,
211 * a very simple implementation.
212 */
213
214 struct link {
215 char *string; /* Pointer to link's contents */
216 struct link *next_link; /* Pointer to the next link */
217 };
218
219
220 /*
221 * Debugging states can be pushed or popped off of a
222 * stack which is implemented as a linked list. Note
223 * that the head of the list is the current state and the
224 * stack is pushed by adding a new state to the head of the
225 * list or popped by removing the first link.
226 */
227
228 struct state {
229 int flags; /* Current state flags */
230 int maxdepth; /* Current maximum trace depth */
231 unsigned int delay; /* Delay after each output line */
232 int level; /* Current function nesting level */
233 FILE *out_file; /* Current output stream */
234 FILE *prof_file; /* Current profiling stream */
235 struct link *functions; /* List of functions */
236 struct link *p_functions; /* List of profiled functions */
237 struct link *keywords; /* List of debug keywords */
238 struct link *processes; /* List of process names */
239 struct state *next_state; /* Next state in the list */
240 };
241
242 LOCAL struct state *stack = NULL; /* Linked list of stacked states */
243
244 /*
245 * Local variables not seen by user.
246 */
247
248 LOCAL int lineno = 0; /* Current debugger output line number */
249 LOCAL char *func = "?func"; /* Name of current user function */
250 LOCAL char *file = "?file"; /* Name of current user file */
251 LOCAL BOOLEAN init_done = FALSE;/* Set to TRUE when initialization done */
252 LOCAL char **framep = NULL; /* Pointer to current frame */
253
254 #if (unix || xenix || amiga || defined(__TURBOC__))
255 LOCAL int jmplevel; /* Remember nesting level at setjmp () */
256 LOCAL char *jmpfunc; /* Remember current function for setjmp */
257 LOCAL char *jmpfile; /* Remember current file for setjmp */
258 #endif
259
260 LOCAL struct link *ListParse ();/* Parse a debug command string */
261 LOCAL char *StrDup (); /* Make a fresh copy of a string */
262 LOCAL VOID OpenFile (); /* Open debug output stream */
263 LOCAL VOID OpenProfile (); /* Open profile output stream */
264 LOCAL VOID CloseFile (); /* Close debug output stream */
265 LOCAL VOID PushState (); /* Push current debug state */
266 LOCAL VOID ChangeOwner (); /* Change file owner and group */
267 LOCAL BOOLEAN DoTrace (); /* Test for tracing enabled */
268 LOCAL BOOLEAN Writable (); /* Test to see if file is writable */
269 LOCAL unsigned long Clock (); /* Return current user time (ms) */
270 LOCAL long *DbugMalloc (); /* Allocate memory for runtime support */
271 LOCAL char *BaseName (); /* Remove leading pathname components */
272 LOCAL VOID DoPrefix (); /* Print debugger line prefix */
273 LOCAL VOID FreeList (); /* Free memory from linked list */
274 LOCAL VOID Indent (); /* Indent line to specified indent */
275
276 /* Supplied in Sys V runtime environ */
277 LOCAL char *strtok (); /* Break string into tokens */
278 LOCAL char *strrchr (); /* Find last occurance of char */
279
280 /*
281 * The following local variables are used to hold the state information
282 * between the call to _db_pargs_() and _db_doprnt_(), during
283 * expansion of the DBUG_PRINT macro. This is the only macro
284 * that currently uses these variables.
285 *
286 * These variables are currently used only by _db_pargs_() and
287 * _db_doprnt_().
288 */
289
290 LOCAL int u_line = 0; /* User source code line number */
291 LOCAL char *u_keyword = "?"; /* Keyword for current macro */
292
293 /*
294 * Miscellaneous printf format strings.
295 */
296
297 #define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
298 #define ERR_OPEN "%s: can't open debug output stream \"%s\": "
299 #define ERR_CLOSE "%s: can't close debug file: "
300 #define ERR_ABORT "%s: debugger aborting because %s\n"
301 #define ERR_CHOWN "%s: can't change owner/group of \"%s\": "
302
303 /*
304 * Macros and defines for testing file accessibility under UNIX.
305 */
306
307 #if (unix || xenix)
308 # define A_EXISTS 00 /* Test for file existance */
309 # define A_EXECUTE 01 /* Test for execute permission */
310 # define A_WRITE 02 /* Test for write access */
311 # define A_READ 03 /* Test for read access */
312 # define EXISTS(pathname) (access (pathname, A_EXISTS) == 0)
313 # define WRITABLE(pathname) (access (pathname, A_WRITE) == 0)
314 #else
315 # define EXISTS(pathname) (FALSE) /* Assume no existance */
316 #endif
317
318 /*
319 * Translate some calls among different systems.
320 */
321
322 #if (unix || xenix)
323 # define Delay sleep
324 IMPORT unsigned int sleep (); /* Pause for given number of seconds */
325 #endif
326
327 #if amiga
328 IMPORT int Delay (); /* Pause for given number of ticks */
329 #endif
330
331
332 /*
333 * FUNCTION
334 *
335 * _db_push_ push current debugger state and set up new one
336 *
337 * SYNOPSIS
338 *
339 * VOID _db_push_ (control)
340 * char *control;
341 *
342 * DESCRIPTION
343 *
344 * Given pointer to a debug control string in "control", pushes
345 * the current debug state, parses the control string, and sets
346 * up a new debug state.
347 *
348 * The only attribute of the new state inherited from the previous
349 * state is the current function nesting level. This can be
350 * overridden by using the "r" flag in the control string.
351 *
352 * The debug control string is a sequence of colon separated fields
353 * as follows:
354 *
355 * <field_1>:<field_2>:...:<field_N>
356 *
357 * Each field consists of a mandatory flag character followed by
358 * an optional "," and comma separated list of modifiers:
359 *
360 * flag[,modifier,modifier,...,modifier]
361 *
362 * The currently recognized flag characters are:
363 *
364 * d Enable output from DBUG_<N> macros for
365 * for the current state. May be followed
366 * by a list of keywords which selects output
367 * only for the DBUG macros with that keyword.
368 * A null list of keywords implies output for
369 * all macros.
370 *
371 * D Delay after each debugger output line.
372 * The argument is the number of tenths of seconds
373 * to delay, subject to machine capabilities.
374 * I.E. -#D,20 is delay two seconds.
375 *
376 * f Limit debugging and/or tracing, and profiling to the
377 * list of named functions. Note that a null list will
378 * disable all functions. The appropriate "d" or "t"
379 * flags must still be given, this flag only limits their
380 * actions if they are enabled.
381 *
382 * F Identify the source file name for each
383 * line of debug or trace output.
384 *
385 * i Identify the process with the pid for each line of
386 * debug or trace output.
387 *
388 * g Enable profiling. Create a file called 'dbugmon.out'
389 * containing information that can be used to profile
390 * the program. May be followed by a list of keywords
391 * that select profiling only for the functions in that
392 * list. A null list implies that all functions are
393 * considered.
394 *
395 * L Identify the source file line number for
396 * each line of debug or trace output.
397 *
398 * n Print the current function nesting depth for
399 * each line of debug or trace output.
400 *
401 * N Number each line of dbug output.
402 *
403 * o Redirect the debugger output stream to the
404 * specified file. The default output is stderr.
405 *
406 * p Limit debugger actions to specified processes.
407 * A process must be identified with the
408 * DBUG_PROCESS macro and match one in the list
409 * for debugger actions to occur.
410 *
411 * P Print the current process name for each
412 * line of debug or trace output.
413 *
414 * r When pushing a new state, do not inherit
415 * the previous state's function nesting level.
416 * Useful when the output is to start at the
417 * left margin.
418 *
419 * t Enable function call/exit trace lines.
420 * May be followed by a list (containing only
421 * one modifier) giving a numeric maximum
422 * trace level, beyond which no output will
423 * occur for either debugging or tracing
424 * macros. The default is a compile time
425 * option.
426 *
427 * Some examples of debug control strings which might appear
428 * on a shell command line (the "-#" is typically used to
429 * introduce a control string to an application program) are:
430 *
431 * -#d:t
432 * -#d:f,main,subr1:F:L:t,20
433 * -#d,input,output,files:n
434 *
435 * For convenience, any leading "-#" is stripped off.
436 *
437 */
438
439
_db_push_(control)440 VOID _db_push_ (control)
441 char *control;
442 {
443 REGISTER char *scan;
444 REGISTER struct link *temp;
445
446 if (control && *control == '-') {
447 if (*++control == '#') {
448 control++;
449 }
450 }
451 control = StrDup (control);
452 PushState ();
453 scan = strtok (control, ":");
454 for (; scan != NULL; scan = strtok ((char *)NULL, ":")) {
455 switch (*scan++) {
456 case 'd':
457 _db_on_ = TRUE;
458 stack -> flags |= DEBUG_ON;
459 if (*scan++ == ',') {
460 stack -> keywords = ListParse (scan);
461 }
462 break;
463 case 'D':
464 stack -> delay = 0;
465 if (*scan++ == ',') {
466 temp = ListParse (scan);
467 stack -> delay = DelayArg (atoi (temp -> string));
468 FreeList (temp);
469 }
470 break;
471 case 'f':
472 if (*scan++ == ',') {
473 stack -> functions = ListParse (scan);
474 }
475 break;
476 case 'F':
477 stack -> flags |= FILE_ON;
478 break;
479 case 'i':
480 stack -> flags |= PID_ON;
481 break;
482 case 'g':
483 _db_pon_ = TRUE;
484 OpenProfile(PROF_FILE);
485 stack -> flags |= PROFILE_ON;
486 if (*scan++ == ',') {
487 stack -> p_functions = ListParse (scan);
488 }
489 break;
490 case 'L':
491 stack -> flags |= LINE_ON;
492 break;
493 case 'n':
494 stack -> flags |= DEPTH_ON;
495 break;
496 case 'N':
497 stack -> flags |= NUMBER_ON;
498 break;
499 case 'o':
500 if (*scan++ == ',') {
501 temp = ListParse (scan);
502 OpenFile (temp -> string);
503 FreeList (temp);
504 } else {
505 OpenFile ("-");
506 }
507 break;
508 case 'p':
509 if (*scan++ == ',') {
510 stack -> processes = ListParse (scan);
511 }
512 break;
513 case 'P':
514 stack -> flags |= PROCESS_ON;
515 break;
516 case 'r':
517 stack -> level = 0;
518 break;
519 case 't':
520 stack -> flags |= TRACE_ON;
521 if (*scan++ == ',') {
522 temp = ListParse (scan);
523 stack -> maxdepth = atoi (temp -> string);
524 FreeList (temp);
525 }
526 break;
527 }
528 }
529 free (control);
530 }
531
532
533
534 /*
535 * FUNCTION
536 *
537 * _db_pop_ pop the debug stack
538 *
539 * DESCRIPTION
540 *
541 * Pops the debug stack, returning the debug state to its
542 * condition prior to the most recent _db_push_ invocation.
543 * Note that the pop will fail if it would remove the last
544 * valid state from the stack. This prevents user errors
545 * in the push/pop sequence from screwing up the debugger.
546 * Maybe there should be some kind of warning printed if the
547 * user tries to pop too many states.
548 *
549 */
550
_db_pop_()551 VOID _db_pop_ ()
552 {
553 REGISTER struct state *discard;
554
555 discard = stack;
556 if (discard != NULL && discard -> next_state != NULL) {
557 stack = discard -> next_state;
558 _db_fp_ = stack -> out_file;
559 _db_pfp_ = stack -> prof_file;
560 if (discard -> keywords != NULL) {
561 FreeList (discard -> keywords);
562 }
563 if (discard -> functions != NULL) {
564 FreeList (discard -> functions);
565 }
566 if (discard -> processes != NULL) {
567 FreeList (discard -> processes);
568 }
569 if (discard -> p_functions != NULL) {
570 FreeList (discard -> p_functions);
571 }
572 CloseFile (discard -> out_file);
573 CloseFile (discard -> prof_file);
574 free ((char *) discard);
575 }
576 }
577
578
579 /*
580 * FUNCTION
581 *
582 * _db_enter_ process entry point to user function
583 *
584 * SYNOPSIS
585 *
586 * VOID _db_enter_ (_func_, _file_, _line_,
587 * _sfunc_, _sfile_, _slevel_, _sframep_)
588 * char *_func_; points to current function name
589 * char *_file_; points to current file name
590 * int _line_; called from source line number
591 * char **_sfunc_; save previous _func_
592 * char **_sfile_; save previous _file_
593 * int *_slevel_; save previous nesting level
594 * char ***_sframep_; save previous frame pointer
595 *
596 * DESCRIPTION
597 *
598 * Called at the beginning of each user function to tell
599 * the debugger that a new function has been entered.
600 * Note that the pointers to the previous user function
601 * name and previous user file name are stored on the
602 * caller's stack (this is why the ENTER macro must be
603 * the first "executable" code in a function, since it
604 * allocates these storage locations). The previous nesting
605 * level is also stored on the callers stack for internal
606 * self consistency checks.
607 *
608 * Also prints a trace line if tracing is enabled and
609 * increments the current function nesting depth.
610 *
611 * Note that this mechanism allows the debugger to know
612 * what the current user function is at all times, without
613 * maintaining an internal stack for the function names.
614 *
615 */
616
_db_enter_(_func_,_file_,_line_,_sfunc_,_sfile_,_slevel_,_sframep_)617 VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_,
618 _sframep_)
619 char *_func_;
620 char *_file_;
621 int _line_;
622 char **_sfunc_;
623 char **_sfile_;
624 int *_slevel_;
625 char ***_sframep_;
626 {
627 long stackused;
628
629 if (!init_done) {
630 _db_push_ ("");
631 }
632 *_sfunc_ = func;
633 func = _func_;
634 *_sfile_ = file;
635 file = BaseName (_file_);
636 stack -> level++;
637 *_slevel_ = stack -> level;
638 *_sframep_ = framep;
639 framep = (char **) _sframep_;
640 if (DoProfile ()) {
641 if (*framep == NULL) {
642 stackused = 0;
643 } else {
644 stackused = ((long)(*framep)) - ((long)(framep));
645 stackused = stackused > 0 ? stackused : -stackused;
646 }
647 (VOID) fprintf (_db_pfp_, PROF_EFMT , Clock (), func);
648 #if AUTOS_REVERSE
649 (VOID) fprintf (_db_pfp_, PROF_SFMT, framep, stackused, *_sfunc_);
650 #else
651 (VOID) fprintf (_db_pfp_, PROF_SFMT, framep, stackused, func);
652 #endif
653 (VOID) fflush (_db_pfp_);
654 }
655 if (DoTrace ()) {
656 DoPrefix (_line_);
657 Indent (stack -> level);
658 (VOID) fprintf (_db_fp_, ">%s\n", func);
659 (VOID) fflush (_db_fp_);
660 (VOID) Delay (stack -> delay);
661 }
662 }
663
664
665 /*
666 * FUNCTION
667 *
668 * _db_return_ process exit from user function
669 *
670 * SYNOPSIS
671 *
672 * VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
673 * int _line_; current source line number
674 * char **_sfunc_; where previous _func_ is to be retrieved
675 * char **_sfile_; where previous _file_ is to be retrieved
676 * int *_slevel_; where previous level was stashed
677 *
678 * DESCRIPTION
679 *
680 * Called just before user function executes an explicit or implicit
681 * return. Prints a trace line if trace is enabled, decrements
682 * the current nesting level, and restores the current function and
683 * file names from the defunct function's stack.
684 *
685 */
686
_db_return_(_line_,_sfunc_,_sfile_,_slevel_)687 VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
688 int _line_;
689 char **_sfunc_;
690 char **_sfile_;
691 int *_slevel_;
692 {
693 if (!init_done) {
694 _db_push_ ("");
695 }
696 if (stack -> level != *_slevel_ && (TRACING || DEBUGGING || PROFILING)) {
697 (VOID) fprintf (_db_fp_, ERR_MISSING_RETURN, _db_process_, func);
698 } else if (DoProfile ()) {
699 (VOID) fprintf (_db_pfp_, PROF_XFMT, Clock(), func);
700 } else if (DoTrace ()) {
701 DoPrefix (_line_);
702 Indent (stack -> level);
703 (VOID) fprintf (_db_fp_, "<%s\n", func);
704 }
705 (VOID) fflush (_db_fp_);
706 (VOID) Delay (stack -> delay);
707 stack -> level = *_slevel_ - 1;
708 func = *_sfunc_;
709 file = *_sfile_;
710 if (framep != NULL) {
711 framep = (char **) *framep;
712 }
713 }
714
715
716 /*
717 * FUNCTION
718 *
719 * _db_pargs_ log arguments for subsequent use by _db_doprnt_()
720 *
721 * SYNOPSIS
722 *
723 * VOID _db_pargs_ (_line_, keyword)
724 * int _line_;
725 * char *keyword;
726 *
727 * DESCRIPTION
728 *
729 * The new universal printing macro DBUG_PRINT, which replaces
730 * all forms of the DBUG_N macros, needs two calls to runtime
731 * support routines. The first, this function, remembers arguments
732 * that are used by the subsequent call to _db_doprnt_().
733 *
734 */
735
_db_pargs_(_line_,keyword)736 VOID _db_pargs_ (_line_, keyword)
737 int _line_;
738 char *keyword;
739 {
740 u_line = _line_;
741 u_keyword = keyword;
742 }
743
744
745 /*
746 * FUNCTION
747 *
748 * _db_doprnt_ handle print of debug lines
749 *
750 * SYNOPSIS
751 *
752 * VOID _db_doprnt_ (format, va_alist)
753 * char *format;
754 * va_dcl;
755 *
756 * DESCRIPTION
757 *
758 * When invoked via one of the DBUG macros, tests the current keyword
759 * set by calling _db_pargs_() to see if that macro has been selected
760 * for processing via the debugger control string, and if so, handles
761 * printing of the arguments via the format string. The line number
762 * of the DBUG macro in the source is found in u_line.
763 *
764 * Note that the format string SHOULD NOT include a terminating
765 * newline, this is supplied automatically.
766 *
767 */
768
769 /*VARARGS1*/
770
771 #ifndef NO_VARARGS
772
_db_doprnt_(char * format,...)773 VOID _db_doprnt_ (char *format, ...)
774 {
775 va_list args;
776
777 va_start (args, format);
778 if (_db_keyword_ (u_keyword)) {
779 DoPrefix (u_line);
780 if (TRACING) {
781 Indent (stack -> level + 1);
782 } else {
783 (VOID) fprintf (_db_fp_, "%s: ", func);
784 }
785 (VOID) fprintf (_db_fp_, "%s: ", u_keyword);
786 (VOID) vfprintf (_db_fp_, format, args);
787 (VOID) fprintf (_db_fp_, "\n");
788 (VOID) fflush (_db_fp_);
789 (VOID) Delay (stack -> delay);
790 }
791 va_end (args);
792 }
793
794 #else
795
_db_doprnt_(format,va_alist)796 VOID _db_doprnt_ (format, va_alist)
797 char *format;
798 va_dcl
799 {
800 va_list args;
801
802 va_start (args);
803 if (_db_keyword_ (u_keyword)) {
804 DoPrefix (u_line);
805 if (TRACING) {
806 Indent (stack -> level + 1);
807 } else {
808 (VOID) fprintf (_db_fp_, "%s: ", func);
809 }
810 (VOID) fprintf (_db_fp_, "%s: ", u_keyword);
811 (VOID) vfprintf (_db_fp_, format, args);
812 (VOID) fprintf (_db_fp_, "\n");
813 (VOID) fflush (_db_fp_);
814 (VOID) Delay (stack -> delay);
815 }
816 va_end (args);
817 }
818
819 #endif
820
821
822 /*
823 * FUNCTION
824 *
825 * ListParse parse list of modifiers in debug control string
826 *
827 * SYNOPSIS
828 *
829 * LOCAL struct link *ListParse (ctlp)
830 * char *ctlp;
831 *
832 * DESCRIPTION
833 *
834 * Given pointer to a comma separated list of strings in "cltp",
835 * parses the list, building a list and returning a pointer to it.
836 * The original comma separated list is destroyed in the process of
837 * building the linked list, thus it had better be a duplicate
838 * if it is important.
839 *
840 * Note that since each link is added at the head of the list,
841 * the final list will be in "reverse order", which is not
842 * significant for our usage here.
843 *
844 */
845
ListParse(ctlp)846 LOCAL struct link *ListParse (ctlp)
847 char *ctlp;
848 {
849 REGISTER char *start;
850 REGISTER struct link *new;
851 REGISTER struct link *head;
852
853 head = NULL;
854 while (*ctlp != EOS) {
855 start = ctlp;
856 while (*ctlp != EOS && *ctlp != ',') {
857 ctlp++;
858 }
859 if (*ctlp == ',') {
860 *ctlp++ = EOS;
861 }
862 new = (struct link *) DbugMalloc (sizeof (struct link));
863 new -> string = StrDup (start);
864 new -> next_link = head;
865 head = new;
866 }
867 return (head);
868 }
869
870
871 /*
872 * FUNCTION
873 *
874 * InList test a given string for member of a given list
875 *
876 * SYNOPSIS
877 *
878 * LOCAL BOOLEAN InList (linkp, cp)
879 * struct link *linkp;
880 * char *cp;
881 *
882 * DESCRIPTION
883 *
884 * Tests the string pointed to by "cp" to determine if it is in
885 * the list pointed to by "linkp". Linkp points to the first
886 * link in the list. If linkp is NULL then the string is treated
887 * as if it is in the list (I.E all strings are in the null list).
888 * This may seem rather strange at first but leads to the desired
889 * operation if no list is given. The net effect is that all
890 * strings will be accepted when there is no list, and when there
891 * is a list, only those strings in the list will be accepted.
892 *
893 */
894
InList(linkp,cp)895 LOCAL BOOLEAN InList (linkp, cp)
896 struct link *linkp;
897 char *cp;
898 {
899 REGISTER struct link *scan;
900 REGISTER BOOLEAN accept;
901
902 if (linkp == NULL) {
903 accept = TRUE;
904 } else {
905 accept = FALSE;
906 for (scan = linkp; scan != NULL; scan = scan -> next_link) {
907 if (STREQ (scan -> string, cp)) {
908 accept = TRUE;
909 break;
910 }
911 }
912 }
913 return (accept);
914 }
915
916
917 /*
918 * FUNCTION
919 *
920 * PushState push current state onto stack and set up new one
921 *
922 * SYNOPSIS
923 *
924 * LOCAL VOID PushState ()
925 *
926 * DESCRIPTION
927 *
928 * Pushes the current state on the state stack, and initializes
929 * a new state. The only parameter inherited from the previous
930 * state is the function nesting level. This action can be
931 * inhibited if desired, via the "r" flag.
932 *
933 * The state stack is a linked list of states, with the new
934 * state added at the head. This allows the stack to grow
935 * to the limits of memory if necessary.
936 *
937 */
938
PushState()939 LOCAL VOID PushState ()
940 {
941 REGISTER struct state *new;
942
943 new = (struct state *) DbugMalloc (sizeof (struct state));
944 new -> flags = 0;
945 new -> delay = 0;
946 new -> maxdepth = MAXDEPTH;
947 if (stack != NULL) {
948 new -> level = stack -> level;
949 } else {
950 new -> level = 0;
951 }
952 new -> out_file = stderr;
953 new -> functions = NULL;
954 new -> p_functions = NULL;
955 new -> keywords = NULL;
956 new -> processes = NULL;
957 new -> next_state = stack;
958 stack = new;
959 init_done = TRUE;
960 }
961
962
963 /*
964 * FUNCTION
965 *
966 * DoTrace check to see if tracing is current enabled
967 *
968 * SYNOPSIS
969 *
970 * LOCAL BOOLEAN DoTrace ()
971 *
972 * DESCRIPTION
973 *
974 * Checks to see if tracing is enabled based on whether the
975 * user has specified tracing, the maximum trace depth has
976 * not yet been reached, the current function is selected,
977 * and the current process is selected. Returns TRUE if
978 * tracing is enabled, FALSE otherwise.
979 *
980 */
981
DoTrace()982 LOCAL BOOLEAN DoTrace ()
983 {
984 REGISTER BOOLEAN trace;
985
986 trace = FALSE;
987 if (TRACING) {
988 if (stack -> level <= stack -> maxdepth) {
989 if (InList (stack -> functions, func)) {
990 if (InList (stack -> processes, _db_process_)) {
991 trace = TRUE;
992 }
993 }
994 }
995 }
996 return (trace);
997 }
998
999
1000 /*
1001 * FUNCTION
1002 *
1003 * DoProfile check to see if profiling is current enabled
1004 *
1005 * SYNOPSIS
1006 *
1007 * LOCAL BOOLEAN DoProfile ()
1008 *
1009 * DESCRIPTION
1010 *
1011 * Checks to see if profiling is enabled based on whether the
1012 * user has specified profiling, the maximum trace depth has
1013 * not yet been reached, the current function is selected,
1014 * and the current process is selected. Returns TRUE if
1015 * profiling is enabled, FALSE otherwise.
1016 *
1017 */
1018
DoProfile()1019 LOCAL BOOLEAN DoProfile ()
1020 {
1021 REGISTER BOOLEAN profile;
1022
1023 profile = FALSE;
1024 if (PROFILING) {
1025 if (stack -> level <= stack -> maxdepth) {
1026 if (InList (stack -> p_functions, func)) {
1027 if (InList (stack -> processes, _db_process_)) {
1028 profile = TRUE;
1029 }
1030 }
1031 }
1032 }
1033 return (profile);
1034 }
1035
1036
1037 /*
1038 * FUNCTION
1039 *
1040 * _db_keyword_ test keyword for member of keyword list
1041 *
1042 * SYNOPSIS
1043 *
1044 * BOOLEAN _db_keyword_ (keyword)
1045 * char *keyword;
1046 *
1047 * DESCRIPTION
1048 *
1049 * Test a keyword to determine if it is in the currently active
1050 * keyword list. As with the function list, a keyword is accepted
1051 * if the list is null, otherwise it must match one of the list
1052 * members. When debugging is not on, no keywords are accepted.
1053 * After the maximum trace level is exceeded, no keywords are
1054 * accepted (this behavior subject to change). Additionally,
1055 * the current function and process must be accepted based on
1056 * their respective lists.
1057 *
1058 * Returns TRUE if keyword accepted, FALSE otherwise.
1059 *
1060 */
1061
_db_keyword_(keyword)1062 BOOLEAN _db_keyword_ (keyword)
1063 char *keyword;
1064 {
1065 REGISTER BOOLEAN accept;
1066
1067 if (!init_done) {
1068 _db_push_ ("");
1069 }
1070 accept = FALSE;
1071 if (DEBUGGING) {
1072 if (stack -> level <= stack -> maxdepth) {
1073 if (InList (stack -> functions, func)) {
1074 if (InList (stack -> keywords, keyword)) {
1075 if (InList (stack -> processes, _db_process_)) {
1076 accept = TRUE;
1077 }
1078 }
1079 }
1080 }
1081 }
1082 return (accept);
1083 }
1084
1085
1086 /*
1087 * FUNCTION
1088 *
1089 * Indent indent a line to the given indentation level
1090 *
1091 * SYNOPSIS
1092 *
1093 * LOCAL VOID Indent (indent)
1094 * int indent;
1095 *
1096 * DESCRIPTION
1097 *
1098 * Indent a line to the given level. Note that this is
1099 * a simple minded but portable implementation.
1100 * There are better ways.
1101 *
1102 * Also, the indent must be scaled by the compile time option
1103 * of character positions per nesting level.
1104 *
1105 */
1106
Indent(indent)1107 LOCAL VOID Indent (indent)
1108 int indent;
1109 {
1110 REGISTER int count;
1111 AUTO char buffer[PRINTBUF];
1112
1113 indent *= INDENT;
1114 for (count = 0; (count < (indent - INDENT)) && (count < (PRINTBUF - 1)); count++) {
1115 if ((count % INDENT) == 0) {
1116 buffer[count] = '|';
1117 } else {
1118 buffer[count] = ' ';
1119 }
1120 }
1121 buffer[count] = EOS;
1122 (VOID) fprintf (_db_fp_, buffer);
1123 (VOID) fflush (_db_fp_);
1124 }
1125
1126
1127 /*
1128 * FUNCTION
1129 *
1130 * FreeList free all memory associated with a linked list
1131 *
1132 * SYNOPSIS
1133 *
1134 * LOCAL VOID FreeList (linkp)
1135 * struct link *linkp;
1136 *
1137 * DESCRIPTION
1138 *
1139 * Given pointer to the head of a linked list, frees all
1140 * memory held by the list and the members of the list.
1141 *
1142 */
1143
FreeList(linkp)1144 LOCAL VOID FreeList (linkp)
1145 struct link *linkp;
1146 {
1147 REGISTER struct link *old;
1148
1149 while (linkp != NULL) {
1150 old = linkp;
1151 linkp = linkp -> next_link;
1152 if (old -> string != NULL) {
1153 free (old -> string);
1154 }
1155 free ((char *) old);
1156 }
1157 }
1158
1159
1160 /*
1161 * FUNCTION
1162 *
1163 * StrDup make a duplicate of a string in new memory
1164 *
1165 * SYNOPSIS
1166 *
1167 * LOCAL char *StrDup (string)
1168 * char *string;
1169 *
1170 * DESCRIPTION
1171 *
1172 * Given pointer to a string, allocates sufficient memory to make
1173 * a duplicate copy, and copies the string to the newly allocated
1174 * memory. Failure to allocated sufficient memory is immediately
1175 * fatal.
1176 *
1177 */
1178
1179
StrDup(string)1180 LOCAL char *StrDup (string)
1181 char *string;
1182 {
1183 REGISTER char *new;
1184
1185 new = (char *) DbugMalloc (strlen (string) + 1);
1186 (VOID) strcpy (new, string);
1187 return (new);
1188 }
1189
1190
1191 /*
1192 * FUNCTION
1193 *
1194 * DoPrefix print debugger line prefix prior to indentation
1195 *
1196 * SYNOPSIS
1197 *
1198 * LOCAL VOID DoPrefix (_line_)
1199 * int _line_;
1200 *
1201 * DESCRIPTION
1202 *
1203 * Print prefix common to all debugger output lines, prior to
1204 * doing indentation if necessary. Print such information as
1205 * current process name, current source file name and line number,
1206 * and current function nesting depth.
1207 *
1208 */
1209
1210
DoPrefix(_line_)1211 LOCAL VOID DoPrefix (_line_)
1212 int _line_;
1213 {
1214 #if (unix || xenix)
1215 extern int getpid ();
1216 #endif
1217
1218 lineno++;
1219 #if (unix || xenix)
1220 if (stack -> flags & PID_ON) {
1221 (VOID) fprintf (_db_fp_, "%5d: ", getpid ());
1222 }
1223 #endif
1224 if (stack -> flags & NUMBER_ON) {
1225 (VOID) fprintf (_db_fp_, "%5d: ", lineno);
1226 }
1227 if (stack -> flags & PROCESS_ON) {
1228 (VOID) fprintf (_db_fp_, "%s: ", _db_process_);
1229 }
1230 if (stack -> flags & FILE_ON) {
1231 (VOID) fprintf (_db_fp_, "%14s: ", file);
1232 }
1233 if (stack -> flags & LINE_ON) {
1234 (VOID) fprintf (_db_fp_, "%5d: ", _line_);
1235 }
1236 if (stack -> flags & DEPTH_ON) {
1237 (VOID) fprintf (_db_fp_, "%4d: ", stack -> level);
1238 }
1239 (VOID) fflush (_db_fp_);
1240 }
1241
1242
1243 /*
1244 * FUNCTION
1245 *
1246 * OpenFile open new output stream for debugger output
1247 *
1248 * SYNOPSIS
1249 *
1250 * LOCAL VOID OpenFile (name)
1251 * char *name;
1252 *
1253 * DESCRIPTION
1254 *
1255 * Given name of a new file (or "-" for stdout) opens the file
1256 * and sets the output stream to the new file.
1257 *
1258 */
1259
OpenFile(name)1260 LOCAL VOID OpenFile (name)
1261 char *name;
1262 {
1263 REGISTER FILE *fp;
1264 REGISTER BOOLEAN newfile;
1265
1266 if (name != NULL) {
1267 if (strcmp (name, "-") == 0) {
1268 _db_fp_ = stdout;
1269 stack -> out_file = _db_fp_;
1270 } else {
1271 if (!Writable (name)) {
1272 (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
1273 perror ("");
1274 (VOID) fflush (_db_fp_);
1275 (VOID) Delay (stack -> delay);
1276 } else {
1277 if (EXISTS (name)) {
1278 newfile = FALSE;
1279 } else {
1280 newfile = TRUE;
1281 }
1282 fp = fopen (name, "a");
1283 if (fp == NULL) {
1284 (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
1285 perror ("");
1286 (VOID) fflush (_db_fp_);
1287 (VOID) Delay (stack -> delay);
1288 } else {
1289 _db_fp_ = fp;
1290 stack -> out_file = fp;
1291 if (newfile) {
1292 ChangeOwner (name);
1293 }
1294 }
1295 }
1296 }
1297 }
1298 }
1299
1300
1301 /*
1302 * FUNCTION
1303 *
1304 * OpenProfile open new output stream for profiler output
1305 *
1306 * SYNOPSIS
1307 *
1308 * LOCAL VOID OpenProfile (name)
1309 * char *name;
1310 *
1311 * DESCRIPTION
1312 *
1313 * Given name of a new file, opens the file
1314 * and sets the profiler output stream to the new file.
1315 *
1316 * It is currently unclear whether the prefered behavior is
1317 * to truncate any existing file, or simply append to it.
1318 * The latter behavior would be desirable for collecting
1319 * accumulated runtime history over a number of separate
1320 * runs. It might take some changes to the analyzer program
1321 * though, and the notes that Binayak sent with the profiling
1322 * diffs indicated that append was the normal mode, but this
1323 * does not appear to agree with the actual code. I haven't
1324 * investigated at this time [fnf; 24-Jul-87].
1325 */
1326
OpenProfile(name)1327 LOCAL VOID OpenProfile (name)
1328 char *name;
1329 {
1330 REGISTER FILE *fp;
1331 REGISTER BOOLEAN newfile;
1332
1333 if (name != NULL) {
1334 if (!Writable (name)) {
1335 (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
1336 perror ("");
1337 (VOID) fflush (_db_fp_);
1338 (VOID) Delay (stack -> delay);
1339 } else {
1340 if (EXISTS (name)) {
1341 newfile = FALSE;
1342 } else {
1343 newfile = TRUE;
1344 }
1345 fp = fopen (name, "w");
1346 if (fp == NULL) {
1347 (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
1348 perror ("");
1349 (VOID) fflush (_db_fp_);
1350 (VOID) Delay (stack -> delay);
1351 } else {
1352 _db_pfp_ = fp;
1353 stack -> prof_file = fp;
1354 if (newfile) {
1355 ChangeOwner (name);
1356 }
1357 }
1358 }
1359 }
1360 }
1361
1362
1363 /*
1364 * FUNCTION
1365 *
1366 * CloseFile close the debug output stream
1367 *
1368 * SYNOPSIS
1369 *
1370 * LOCAL VOID CloseFile (fp)
1371 * FILE *fp;
1372 *
1373 * DESCRIPTION
1374 *
1375 * Closes the debug output stream unless it is standard output
1376 * or standard error.
1377 *
1378 */
1379
CloseFile(fp)1380 LOCAL VOID CloseFile (fp)
1381 FILE *fp;
1382 {
1383 if (fp != stderr && fp != stdout) {
1384 if (fclose (fp) == EOF) {
1385 (VOID) fprintf (stderr, ERR_CLOSE, _db_process_);
1386 perror ("");
1387 (VOID) fflush (stderr);
1388 (VOID) Delay (stack -> delay);
1389 }
1390 }
1391 }
1392
1393
1394 /*
1395 * FUNCTION
1396 *
1397 * DbugExit print error message and exit
1398 *
1399 * SYNOPSIS
1400 *
1401 * LOCAL VOID DbugExit (why)
1402 * char *why;
1403 *
1404 * DESCRIPTION
1405 *
1406 * Prints error message using current process name, the reason for
1407 * aborting (typically out of memory), and exits with status 1.
1408 * This should probably be changed to use a status code
1409 * defined in the user's debugger include file.
1410 *
1411 */
1412
DbugExit(why)1413 LOCAL VOID DbugExit (why)
1414 char *why;
1415 {
1416 (VOID) fprintf (stderr, ERR_ABORT, _db_process_, why);
1417 (VOID) fflush (stderr);
1418 (VOID) Delay (stack -> delay);
1419 exit (1);
1420 }
1421
1422
1423 /*
1424 * FUNCTION
1425 *
1426 * DbugMalloc allocate memory for debugger runtime support
1427 *
1428 * SYNOPSIS
1429 *
1430 * LOCAL long *DbugMalloc (size)
1431 * int size;
1432 *
1433 * DESCRIPTION
1434 *
1435 * Allocate more memory for debugger runtime support functions.
1436 * Failure to to allocate the requested number of bytes is
1437 * immediately fatal to the current process. This may be
1438 * rather unfriendly behavior. It might be better to simply
1439 * print a warning message, freeze the current debugger state,
1440 * and continue execution.
1441 *
1442 */
1443
DbugMalloc(size)1444 LOCAL long *DbugMalloc (size)
1445 int size;
1446 {
1447 register long *new;
1448
1449 new = (long *) malloc ((unsigned int) size);
1450 if (new == NULL) {
1451 DbugExit ("out of memory");
1452 }
1453 return (new);
1454 }
1455
1456
1457 /*
1458 * This function may be eliminated when strtok is available
1459 * in the runtime environment (missing from BSD4.1).
1460 */
1461
strtok(s1,s2)1462 LOCAL char *strtok (s1, s2)
1463 char *s1, *s2;
1464 {
1465 static char *end = NULL;
1466 REGISTER char *rtnval;
1467
1468 rtnval = NULL;
1469 if (s2 != NULL) {
1470 if (s1 != NULL) {
1471 end = s1;
1472 rtnval = strtok ((char *) NULL, s2);
1473 } else if (end != NULL) {
1474 if (*end != EOS) {
1475 rtnval = end;
1476 while (*end != *s2 && *end != EOS) {end++;}
1477 if (*end != EOS) {
1478 *end++ = EOS;
1479 }
1480 }
1481 }
1482 }
1483 return (rtnval);
1484 }
1485
1486
1487 /*
1488 * FUNCTION
1489 *
1490 * BaseName strip leading pathname components from name
1491 *
1492 * SYNOPSIS
1493 *
1494 * LOCAL char *BaseName (pathname)
1495 * char *pathname;
1496 *
1497 * DESCRIPTION
1498 *
1499 * Given pointer to a complete pathname, locates the base file
1500 * name at the end of the pathname and returns a pointer to
1501 * it.
1502 *
1503 */
1504
BaseName(pathname)1505 LOCAL char *BaseName (pathname)
1506 char *pathname;
1507 {
1508 register char *base;
1509
1510 base = strrchr (pathname, '/');
1511 if (base++ == NULL) {
1512 base = pathname;
1513 }
1514 return (base);
1515 }
1516
1517
1518 /*
1519 * FUNCTION
1520 *
1521 * Writable test to see if a pathname is writable/creatable
1522 *
1523 * SYNOPSIS
1524 *
1525 * LOCAL BOOLEAN Writable (pathname)
1526 * char *pathname;
1527 *
1528 * DESCRIPTION
1529 *
1530 * Because the debugger might be linked in with a program that
1531 * runs with the set-uid-bit (suid) set, we have to be careful
1532 * about opening a user named file for debug output. This consists
1533 * of checking the file for write access with the real user id,
1534 * or checking the directory where the file will be created.
1535 *
1536 * Returns TRUE if the user would normally be allowed write or
1537 * create access to the named file. Returns FALSE otherwise.
1538 *
1539 */
1540
Writable(pathname)1541 LOCAL BOOLEAN Writable (pathname)
1542 char *pathname;
1543 {
1544 REGISTER BOOLEAN granted;
1545 #if (unix || xenix)
1546 REGISTER char *lastslash;
1547 #endif
1548
1549 #if (!unix && !xenix)
1550 granted = TRUE;
1551 #else
1552 granted = FALSE;
1553 if (EXISTS (pathname)) {
1554 if (WRITABLE (pathname)) {
1555 granted = TRUE;
1556 }
1557 } else {
1558 lastslash = strrchr (pathname, '/');
1559 if (lastslash != NULL) {
1560 *lastslash = EOS;
1561 } else {
1562 pathname = ".";
1563 }
1564 if (WRITABLE (pathname)) {
1565 granted = TRUE;
1566 }
1567 if (lastslash != NULL) {
1568 *lastslash = '/';
1569 }
1570 }
1571 #endif
1572 return (granted);
1573 }
1574
1575
1576 /*
1577 * This function may be eliminated when strrchr is available
1578 * in the runtime environment (missing from BSD4.1).
1579 * Alternately, you can use rindex() on BSD systems.
1580 */
1581
strrchr(s,c)1582 LOCAL char *strrchr (s, c)
1583 char *s;
1584 char c;
1585 {
1586 REGISTER char *scan;
1587
1588 for (scan = s; *scan != EOS; scan++) {;}
1589 while (scan > s && *--scan != c) {;}
1590 if (*scan != c) {
1591 scan = NULL;
1592 }
1593 return (scan);
1594 }
1595
1596
1597 /*
1598 * FUNCTION
1599 *
1600 * ChangeOwner change owner to real user for suid programs
1601 *
1602 * SYNOPSIS
1603 *
1604 * LOCAL VOID ChangeOwner (pathname)
1605 *
1606 * DESCRIPTION
1607 *
1608 * For unix systems, change the owner of the newly created debug
1609 * file to the real owner. This is strictly for the benefit of
1610 * programs that are running with the set-user-id bit set.
1611 *
1612 * Note that at this point, the fact that pathname represents
1613 * a newly created file has already been established. If the
1614 * program that the debugger is linked to is not running with
1615 * the suid bit set, then this operation is redundant (but
1616 * harmless).
1617 *
1618 */
1619
ChangeOwner(pathname)1620 LOCAL VOID ChangeOwner (pathname)
1621 char *pathname;
1622 {
1623 #if (unix || xenix)
1624 if (chown (pathname, getuid (), getgid ()) == -1) {
1625 (VOID) fprintf (stderr, ERR_CHOWN, _db_process_, pathname);
1626 perror ("");
1627 (VOID) fflush (stderr);
1628 (VOID) Delay (stack -> delay);
1629 }
1630 #endif
1631 }
1632
1633
1634 /*
1635 * FUNCTION
1636 *
1637 * _db_setjmp_ save debugger environment
1638 *
1639 * SYNOPSIS
1640 *
1641 * VOID _db_setjmp_ ()
1642 *
1643 * DESCRIPTION
1644 *
1645 * Invoked as part of the user's DBUG_SETJMP macro to save
1646 * the debugger environment in parallel with saving the user's
1647 * environment.
1648 *
1649 */
1650
_db_setjmp_()1651 VOID _db_setjmp_ ()
1652 {
1653 jmplevel = stack -> level;
1654 jmpfunc = func;
1655 jmpfile = file;
1656 }
1657
1658
1659 /*
1660 * FUNCTION
1661 *
1662 * _db_longjmp_ restore previously saved debugger environment
1663 *
1664 * SYNOPSIS
1665 *
1666 * VOID _db_longjmp_ ()
1667 *
1668 * DESCRIPTION
1669 *
1670 * Invoked as part of the user's DBUG_LONGJMP macro to restore
1671 * the debugger environment in parallel with restoring the user's
1672 * previously saved environment.
1673 *
1674 */
1675
_db_longjmp_()1676 VOID _db_longjmp_ ()
1677 {
1678 stack -> level = jmplevel;
1679 if (jmpfunc) {
1680 func = jmpfunc;
1681 }
1682 if (jmpfile) {
1683 file = jmpfile;
1684 }
1685 }
1686
1687
1688 /*
1689 * FUNCTION
1690 *
1691 * DelayArg convert D flag argument to appropriate value
1692 *
1693 * SYNOPSIS
1694 *
1695 * LOCAL int DelayArg (value)
1696 * int value;
1697 *
1698 * DESCRIPTION
1699 *
1700 * Converts delay argument, given in tenths of a second, to the
1701 * appropriate numerical argument used by the system to delay
1702 * that that many tenths of a second. For example, on the
1703 * amiga, there is a system call "Delay()" which takes an
1704 * argument in ticks (50 per second). On unix, the sleep
1705 * command takes seconds. Thus a value of "10", for one
1706 * second of delay, gets converted to 50 on the amiga, and 1
1707 * on unix. Other systems will need to use a timing loop.
1708 *
1709 */
1710
DelayArg(value)1711 LOCAL int DelayArg (value)
1712 int value;
1713 {
1714 unsigned int delayarg = 0;
1715
1716 #if (unix || xenix)
1717 delayarg = value / 10; /* Delay is in seconds for sleep () */
1718 #endif
1719 #if amiga
1720 delayarg = (HZ * value) / 10; /* Delay in ticks for Delay () */
1721 #endif
1722 return (delayarg);
1723 }
1724
1725
1726 /*
1727 * A dummy delay stub for systems that do not support delays.
1728 * With a little work, this can be turned into a timing loop.
1729 */
1730
1731 #if (!unix && !xenix && !amiga)
Delay()1732 Delay ()
1733 {
1734 }
1735 #endif
1736
1737
1738 /*
1739 * FUNCTION
1740 *
1741 * perror perror simulation for systems that don't have it
1742 *
1743 * SYNOPSIS
1744 *
1745 * LOCAL VOID perror (s)
1746 * char *s;
1747 *
1748 * DESCRIPTION
1749 *
1750 * Perror produces a message on the standard error stream which
1751 * provides more information about the library or system error
1752 * just encountered. The argument string s is printed, followed
1753 * by a ':', a blank, and then a message and a newline.
1754 *
1755 * An undocumented feature of the unix perror is that if the string
1756 * 's' is a null string (NOT a NULL pointer!), then the ':' and
1757 * blank are not printed.
1758 *
1759 * This version just complains about an "unknown system error".
1760 *
1761 */
1762
1763 #if !unix && !xenix && !(amiga && LATTICE) && !defined(__TURBOC__)
1764
perror(s)1765 LOCAL VOID perror (s)
1766 char *s;
1767 {
1768 if (s && *s != EOS) {
1769 (VOID) fprintf (stderr, "%s: ", s);
1770 }
1771 (VOID) fprintf (stderr, "<unknown system error>\n");
1772 }
1773
1774 #endif /* !unix && !xenix && !(amiga && LATTICE) */
1775
1776 /*
1777 * Here we need the definitions of the clock routine. Add your
1778 * own for whatever system that you have.
1779 */
1780
1781 #if (unix || xenix)
1782
1783 /* # include <sys/param.h> */
1784 # if BSD4_3 || sun
1785
1786 /*
1787 * Definition of the Clock() routine for 4.3 BSD.
1788 */
1789
1790 #include <sys/time.h>
1791 #include <sys/resource.h>
1792
1793 /*
1794 * Returns the user time in milliseconds used by this process so
1795 * far.
1796 */
1797
Clock()1798 LOCAL unsigned long Clock ()
1799 {
1800 struct rusage ru;
1801
1802 (VOID) getrusage (RUSAGE_SELF, &ru);
1803 return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000));
1804 }
1805
1806 #else /* unix or xenix, but not 4.3 or sun */
1807
1808 /*
1809 #if 1
1810 #include <sys/types.h>
1811 #include <sys/param.h>
1812 #include <sys/times.h>
1813
1814 #ifndef HZ
1815 #define HZ 60
1816 #endif
1817
1818 LOCAL unsigned long Clock ()
1819 {
1820 struct tms now;
1821 long elapsed;
1822
1823 times(&now);
1824
1825 elapsed = now.tms_utime * 1000 * HZ;
1826 return(elapsed);
1827 }
1828
1829 #else
1830 LOCAL unsigned long Clock ()
1831 {
1832 return (0);
1833 }
1834 #endif
1835 */
1836
1837 # endif
1838
1839 #else /* not unix or xenix */
1840
1841 #if amiga
1842
1843 struct DateStamp { /* Yes, this is a hack, but doing it right */
1844 long ds_Days; /* is incredibly ugly without splitting this */
1845 long ds_Minute; /* off into a separate file */
1846 long ds_Tick;
1847 };
1848
1849 static int first_clock = TRUE;
1850 static struct DateStamp begin;
1851 static struct DateStamp elapsed;
1852
Clock()1853 LOCAL unsigned long Clock ()
1854 {
1855 register struct DateStamp *now;
1856 register unsigned long millisec = 0;
1857 extern VOID *AllocMem ();
1858
1859 now = (struct DateStamp *) AllocMem ((long) sizeof (struct DateStamp), 0L);
1860 if (now != NULL) {
1861 if (first_clock == TRUE) {
1862 first_clock = FALSE;
1863 (VOID) DateStamp (now);
1864 begin = *now;
1865 }
1866 (VOID) DateStamp (now);
1867 millisec = 24 * 3600 * (1000 / HZ) * (now -> ds_Days - begin.ds_Days);
1868 millisec += 60 * (1000 / HZ) * (now -> ds_Minute - begin.ds_Minute);
1869 millisec += (1000 / HZ) * (now -> ds_Tick - begin.ds_Tick);
1870 (VOID) FreeMem (now, (long) sizeof (struct DateStamp));
1871 }
1872 return (millisec);
1873 }
1874
1875 #else /* not amiga */
Clock()1876 LOCAL unsigned long Clock ()
1877 {
1878 return (0);
1879 }
1880 #endif /* amiga */
1881 #endif /* unix */
1882