4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
31 * Implements the dbug_routine class.
32 * This code is derived from the public domain DBUG
33 * package written by Fred Fish.
36 #pragma ident "%Z%%M% %I% %E% SMI"
48 #include <sys/types.h>
54 /* forward references */
55 static int listparse(register char *ctlp
, flist_object_t
*head
);
56 static boolean
inlist(flist_object_t
*flist_object_p
, const char *cp
);
57 static boolean
dotrace(dbug_state_object_t
*dbug_state_object_p
,
58 const char *func
, const char *process
);
59 static void indent(register dbug_state_object_t
*dbug_state_object_p
,
61 static void doprefix(dbug_state_object_t
*dbug_state_object_p
, int line
,
62 long lineno
, const char *file
, const char *process
);
63 static FILE *openfile(char *name
);
64 static boolean
writable(char *pathname
);
65 static void changeowner(char *pathname
);
66 static int delayarg(int value
);
67 static void delay(uint_t xx
);
68 static ulong_t
getclock();
69 static char *mystrtok(char *s1
, char *s2
);
72 /* initialize static members of class */
76 dbug_state_object_t
*sd_push
= NULL
;
78 /* this structure defines thread specific data */
79 typedef struct thread_data
{
81 unsigned long td_stackinit
; /* Begining of stack. */
83 int td_line
; /* Current line number. */
84 char td_keyword
[64]; /* Current keyword. */
85 dbug_object_t
*td_first
; /* Current routine. */
92 thread_data_t mdt_data
;
95 * format of control string
96 * command[:command:...]
99 * debugging on 'd' d[,<keyword>[,...]]
100 * delay value 'D' D[,<delay value>]
101 * function list 'f' f[,<function name>[,...]]
102 * print filename 'F' F
104 * print line number 'L' L
105 * print call depth 'n' n
106 * number each line 'N' N
107 * output file 'o' o[,<filename>
108 * process name list 'p' p[,<process name>[,...]]
109 * print proc name 'P' P
110 * reset indentation 'r' r
111 * print runtime 'R' R
112 * print thread info 'T' T
114 * print stack depth 's' s
122 * Constructor for the dbug_routine class.
124 * line - line number where object was created.
125 * file - file name object was created in.
126 * function- routine name object was created in.
132 dbug_object_create(int line
, const char *file
, const char *function
)
134 dbug_object_t
*dbug_object_p
;
135 dbug_state_object_t
*dbug_state_object_p
;
140 thread_data_t
*tdp
= NULL
;
144 if (thr_keycreate(&mdt_key
, dbug_thread_exit
) != 0)
148 GET_THREAD_DATA_PTR(&tdp
);
150 tdp
= (thread_data_t
*)calloc(sizeof (*tdp
), 1);
153 thr_setspecific(mdt_key
, tdp
);
155 tdp
->td_keyword
[0] = '\0';
156 tdp
->td_first
= NULL
;
159 GET_THREAD_DATA_PTR(&tdp
);
162 dbug_object_p
= (dbug_object_t
*)calloc(sizeof (dbug_object_t
), 1);
164 if (dbug_object_p
== NULL
)
167 /* save the function name */
169 strcpy(dbug_object_p
->d_func
, function
);
171 strcpy(dbug_object_p
->d_func
, "unknown");
173 /* save the base of the file name */
175 cptr
= strrchr(file
, '/');
177 strcpy(dbug_object_p
->d_file
, file
);
179 strcpy(dbug_object_p
->d_file
, cptr
++);
181 strcpy(dbug_object_p
->d_file
, "unknown");
183 /* Chain this onto our list of them */
184 dbug_object_p
->d_prev
= tdp
->td_first
;
185 tdp
->td_first
= dbug_object_p
;
187 /* set the default routine exit point line number to zero */
188 dbug_object_p
->d_leaveline
= 0;
190 /* If debugging is off, then all done */
191 if (NOT
db_debugon())
194 /* if the active state is null initialize it */
196 db_push("d,:f,:F:i:L:n:N:o,cfsd_debug.out:p,:P:r:R:T:t:s");
198 /* get a pointer to the active state */
199 dbug_state_object_p
= sd_push
;
203 * Get the new stack depth.
204 * There a two problems associated with this.
205 * One is because c++ allows declarations anywhere inside of
206 * a routine. So it is difficult to position the dbug_enter()
207 * macro after all declarations and still be useful.
208 * Two is that the dbug_enter() macro should be before all
209 * other automatic objects so that its destructor gets called
210 * last as the routine is returning.
211 * The solution is to advise placing the dbug_enter() macro at
212 * the start of the routine and specifying that that stack
213 * values apply upto but not including the current routine.
215 stacksize
= (ulong_t
)this;
217 stacksize
= tdp
->td_stackinit
- stacksize
;
219 stacksize
= stacksize
- tdp
->td_stackinit
;
222 /* record the new nesting level */
223 dbug_state_object_p
->s_level
++;
225 /* if producing a trace of function calls */
226 if (dotrace(dbug_state_object_p
, dbug_object_p
->d_func
, sd_process
)) {
227 doprefix(dbug_state_object_p
, line
, sd_lineno
++,
228 dbug_object_p
->d_file
, sd_process
);
229 indent(dbug_state_object_p
, dbug_state_object_p
->s_level
);
230 if (dbug_state_object_p
->sf_stack
)
231 fprintf(dbug_state_object_p
->s_out_file
, ">%s %ld\n",
232 dbug_object_p
->d_func
, stacksize
);
234 fprintf(dbug_state_object_p
->s_out_file
, ">%s\n",
235 dbug_object_p
->d_func
);
236 fflush(dbug_state_object_p
->s_out_file
);
237 delay(dbug_state_object_p
->s_delay
);
240 /* if a new thread */
241 if (created
&& dbug_state_object_p
->sf_thread
) {
242 doprefix(dbug_state_object_p
, line
, sd_lineno
++,
243 dbug_object_p
->d_file
, sd_process
);
244 indent(dbug_state_object_p
, dbug_state_object_p
->s_level
);
245 fprintf(dbug_state_object_p
->s_out_file
, "thread created\n");
246 fflush(dbug_state_object_p
->s_out_file
);
247 delay(dbug_state_object_p
->s_delay
);
251 UNLOCK_THREAD_DATA();
256 * dbug_object_destroy
259 * Destructor for the dbug_routine class.
260 * Unchains this object from the list.
267 dbug_object_destroy(char *function_name
, int line
)
269 dbug_object_t
*dbug_object_p
;
270 dbug_state_object_t
*dbug_state_object_p
;
274 GET_THREAD_DATA_PTR(&tdp
);
276 /* unchain from the list of objects */
277 dbug_object_p
= tdp
->td_first
;
278 tdp
->td_first
= dbug_object_p
->d_prev
;
280 /* If debugging is off, then nothing else to do */
281 if (NOT
db_debugon())
284 dbug_object_p
->d_leaveline
= line
;
286 /* get a pointer to the active state */
287 dbug_state_object_p
= sd_push
;
290 * Make sure the last one created is being deleted.
291 * This will not be the case if there are multiple dbug_routine
292 * objects per routine or if one is created outside of a routine.
294 if (strcmp(function_name
, dbug_object_p
->d_func
)) {
295 doprefix(dbug_state_object_p
, dbug_object_p
->d_leaveline
,
296 sd_lineno
++, dbug_object_p
->d_file
, sd_process
);
297 indent(dbug_state_object_p
, dbug_state_object_p
->s_level
);
298 fprintf(dbug_state_object_p
->s_out_file
,
299 "<expected %s, actual %s, ERROR: "
300 "dbug_enter/dbug_leave out of sequence.\n",
301 dbug_object_p
->d_func
, function_name
);
302 fflush(dbug_state_object_p
->s_out_file
);
303 /* delay(dbug_state_object_p->s_delay); */
306 /* if producing a trace of function calls */
307 if (dotrace(dbug_state_object_p
, dbug_object_p
->d_func
, sd_process
)) {
308 doprefix(dbug_state_object_p
, dbug_object_p
->d_leaveline
,
309 sd_lineno
++, dbug_object_p
->d_file
, sd_process
);
310 indent(dbug_state_object_p
, dbug_state_object_p
->s_level
);
311 fprintf(dbug_state_object_p
->s_out_file
, "<%s\n",
312 dbug_object_p
->d_func
);
313 fflush(dbug_state_object_p
->s_out_file
);
315 delay(dbug_state_object_p
->s_delay
);
320 /* record the new nesting level */
321 dbug_state_object_p
->s_level
--;
325 UNLOCK_THREAD_DATA();
333 * Test a keyword to determine if it is in the currently active
334 * keyword list. As with the function list, a keyword is accepted
335 * if the list is null, otherwise it must match one of the list
336 * members. When debugging is not on, no keywords are accepted.
337 * After the maximum trace level is exceeded, no keywords are
338 * accepted (this behavior subject to change). Additionally,
339 * the current function and process must be accepted based on
340 * their respective lists.
342 * keyword - the keyword to test
344 * Returns 1 if keyword accepted, 0 otherwise.
350 db_keyword(dbug_object_t
*dbug_object_p
, const char *keyword
)
352 dbug_state_object_t
*dbug_state_object_p
;
355 /* return FALSE if not debugging */
356 if (NOT
db_debugon())
361 /* return FALSE if not debugging */
362 if (NOT
db_debugon())
365 /* get a pointer to the active state */
366 dbug_state_object_p
= sd_push
;
368 if (dbug_state_object_p
->sf_debug
) { /* is this test necessary ? */
369 if (inlist(dbug_state_object_p
->s_functions
,
370 dbug_object_p
->d_func
)) {
371 if (inlist(dbug_state_object_p
->s_processes
,
373 if (inlist(dbug_state_object_p
->s_keywords
,
383 UNLOCK_THREAD_DATA();
392 * Saves arguments for subsequent usage by db_printf.
394 * line - the line number the db_print occurs on
395 * keyword - determines whether or not to really print anything
402 db_pargs(dbug_object_t
*dbug_object_p
, int line
, char *keyword
)
406 /* return if no debugging yet */
407 if (NOT
db_debugon())
410 GET_THREAD_DATA_PTR(&tdp
);
414 strcpy(tdp
->td_keyword
, keyword
);
416 tdp
->td_keyword
[0] = '\0';
422 return (fileno(sd_push
->s_out_file
));
430 * Outputs the specified message if the keyword specified
431 * by db_pargs() has been selected. The line number specified
432 * by db_pargs() is also used as the line number the db_printf()
433 * occurs on. The format string should NOT include a terminating
434 * newline as one is supplied automatically.
436 * format - printf style printing control string
437 * ... - additional arguments required by the control string
444 db_printf(char *keyword
, char *format
, ...)
446 dbug_object_t
*dbug_object_p
;
448 dbug_state_object_t
*dbug_state_object_p
= sd_push
;
451 dbug_object_p
= db_get_dbug_object_p();
452 /* return if no debugging yet */
453 if (NOT
db_debugon())
456 GET_THREAD_DATA_PTR(&tdp
);
458 /* return if keyword not selected */
459 if (NOT
db_keyword(dbug_object_p
, tdp
->td_keyword
))
464 /* get a pointer to the active state */
466 va_start(args
, format
);
468 doprefix(dbug_state_object_p
, tdp
->td_line
, sd_lineno
++,
469 dbug_object_p
->d_file
, sd_process
);
470 if (dbug_state_object_p
->sf_trace
)
471 indent(dbug_state_object_p
, dbug_state_object_p
->s_level
+1);
473 fprintf(dbug_state_object_p
->s_out_file
, "%s: ",
474 dbug_object_p
->d_func
);
475 if (tdp
->td_keyword
[0])
476 fprintf(dbug_state_object_p
->s_out_file
, "%s: ",
478 vfprintf(dbug_state_object_p
->s_out_file
, format
, args
);
479 fprintf(dbug_state_object_p
->s_out_file
, "\n");
480 fflush(dbug_state_object_p
->s_out_file
);
481 delay(dbug_state_object_p
->s_delay
);
485 UNLOCK_THREAD_DATA();
493 * Prints out a trace of the call stack.
495 * line - the line number where this call was made
496 * keyword - keyword to test against
502 db_traceprint(int line
, const char *keyword
)
504 dbug_object_t
*dbug_object_p
;
506 /* return if no debugging yet */
507 if (NOT
db_debugon())
510 if ((dbug_object_p
= db_get_dbug_object_p()) == NULL
)
513 /* If the specified keyword is enabled */
514 if (db_keyword(dbug_object_p
, keyword
)) {
515 /* perform setup for using db_printf */
516 db_pargs(dbug_object_p
, line
, NULL
);
518 /* Output a header message */
519 db_printf(NULL
, "Stack Trace");
521 /* walk the stack of dbug_routine objects */
522 for (pdr
= dbug_object_p
; pdr
!= NULL
; pdr
= pdr
->d_prev
) {
523 /* output the routine name */
524 db_printf(NULL
, " %s() (%s)", pdr
->d_func
,
535 * Called when an assert fails.
536 * Prints out a stack trace and aborts.
538 * line line number assert occurred at
539 * msgp string form of assert code that failed
545 db_assert(dbug_object_t
*dbug_object_p
, int line
, const char *msgp
)
547 if (NOT
db_debugon())
549 db_pargs(dbug_object_p
, line
, NULL
);
550 db_printf(NULL
, "Assertion Failed %s:%s():%d \"%s\"",
551 dbug_object_p
->d_file
, dbug_object_p
->d_func
, line
, msgp
);
552 db_traceprint(line
, NULL
);
561 * Called when an precond fails.
562 * Prints out a stack trace and aborts.
564 * line line number precond occurred at
565 * msgp string form of precond code that failed
571 db_precond(dbug_object_t
*dbug_object_p
, int line
, const char *msgp
)
573 if (NOT
db_debugon())
575 db_pargs(dbug_object_p
, line
, NULL
);
576 db_printf(NULL
, "Precondition Failed %s:%s():%d \"%s\"",
577 dbug_object_p
->d_file
, dbug_object_p
->d_func
, line
, msgp
);
578 db_traceprint(line
, NULL
);
587 * Push current debugger state and set up a new one.
588 * Returns NULL if no errors, an error string if there
591 * format of control string
592 * command[:command:...]
595 * debugging on 'd' d[,<keyword>[,...]]
596 * delay value 'D' D[,<delay value>]
597 * function list 'f' f[,<function name>[,...]]
598 * print filename 'F' F
600 * print line number 'L' L
601 * print call depth 'n' n
602 * number each line 'N' N
603 * output file 'o' o[,<filename>
604 * process name list 'p' p[,<process name>[,...]]
605 * print proc name 'P' P
606 * reset indentation 'r' r
607 * print runtime 'R' R
608 * print thread info 'T' T
610 * print stack depth 's' s
613 db_push(const char *control
)
615 char *dupcontrol
= NULL
;
616 dbug_state_object_t
*dbug_state_object_p
;
617 flist_object_t
*flist_object_p
;
625 /* error if the control string is NULL */
626 if (control
== NULL
) {
627 strcpy(res
, "mdbug: control string is NULL");
631 /* turn debugging flag off */
634 /* get the level from the old state if it exists */
638 level
= sd_push
->s_level
;
640 /* Create a new state */
641 dbug_state_object_p
= dbug_state_create(level
);
642 if (dbug_state_object_p
== NULL
) {
643 strcpy(res
, "mdbug: out of memory, dbug_state_create");
647 /* add it to our list of states and make it the current one */
648 dbug_state_object_p
->s_next
= sd_push
;
649 sd_push
= dbug_state_object_p
;
651 /* Strip off -# if in the control string */
652 if ((*control
== '-') && (*(control
+1) == '#'))
655 /* make a copy of the control string so we can modify it with strtok */
656 dupcontrol
= strdup(control
);
657 if (dupcontrol
== NULL
) {
658 strcpy(res
, "mdbug: out of memory, strdup");
662 /* parse the control string */
663 for (scan
= mystrtok(dupcontrol
, ":");
665 scan
= mystrtok(NULL
, ":")) {
667 case 'd': /* debugging on */
669 dbug_state_object_p
->sf_debug
= TRUE
;
670 if (*scan
++ == ',') {
671 retval
= listparse(scan
,
672 dbug_state_object_p
->s_keywords
);
675 "mdbug: -d too many keywords");
681 case 'D': /* specify delay value */
682 dbug_state_object_p
->s_delay
= 0;
683 if (*scan
++ == ',') {
684 flist_object_p
= flist_create();
685 retval
= listparse(scan
, flist_object_p
);
688 "mdbug: -D too many delays");
691 if (flist_object_p
->f_count
> 0) {
692 dbug_state_object_p
->s_delay
=
694 (char *)fl_top(flist_object_p
)));
696 flist_destroy(flist_object_p
);
700 case 'f': /* list of functions to watch */
701 if (*scan
++ == ',') {
702 retval
= listparse(scan
,
703 dbug_state_object_p
->s_functions
);
706 "mdbug: -f too many functions");
712 case 'F': /* print file name with dbug output */
713 dbug_state_object_p
->sf_file
= TRUE
;
716 case 'i': /* print pid with dbug output */
717 dbug_state_object_p
->sf_pid
= TRUE
;
720 case 'L': /* print line nums with dbug output */
721 dbug_state_object_p
->sf_line
= TRUE
;
724 case 'n': /* print function call depth */
725 dbug_state_object_p
->sf_depth
= TRUE
;
728 case 'N': /* number each line of dbug output */
729 dbug_state_object_p
->sf_number
= TRUE
;
732 case 'o': /* specifies output file for dbug */
733 if (*scan
++ == ',') {
734 flist_object_p
= flist_create();
735 retval
= listparse(scan
, flist_object_p
);
738 "mdbug: -o too many output files");
742 if (flist_object_p
->f_count
> 0) {
743 dbug_state_object_p
->s_out_file
=
745 fl_top(flist_object_p
));
746 if (dbug_state_object_p
->s_out_file
!=
748 dbug_state_object_p
->sf_didopen
751 dbug_state_object_p
->s_out_file
=
753 flist_destroy(flist_object_p
);
755 dbug_state_object_p
->s_out_file
=
757 if (dbug_state_object_p
->s_out_file
== NULL
) {
759 "mdbug: -o cannot open output file");
764 case 'p': /* debug specified processes */
765 if (*scan
++ == ',') {
766 retval
= listparse(scan
,
767 dbug_state_object_p
->s_processes
);
770 "mdbug: -p too many processes");
776 case 'P': /* print process name on dbug output */
777 dbug_state_object_p
->sf_process
= TRUE
;
780 case 'r': /* reset indentation to zero */
781 dbug_state_object_p
->s_level
= 0;
784 case 's': /* print stack depth on enter */
785 dbug_state_object_p
->sf_stack
= TRUE
;
788 case 'R': /* print time prog has been running */
789 dbug_state_object_p
->sf_time
= TRUE
;
790 time(&dbug_state_object_p
->s_starttime
);
793 case 'T': /* print thread information */
794 dbug_state_object_p
->sf_thread
= TRUE
;
797 case 't': /* print trace of functions called */
798 dbug_state_object_p
->sf_trace
= TRUE
;
799 dbug_state_object_p
->s_maxdepth
= MAXDEPTH
;
800 if (*scan
++ == ',') {
801 flist_object_p
= flist_create();
802 retval
= listparse(scan
, flist_object_p
);
805 "mdbug: -t too many traces");
808 if (flist_object_p
->f_count
> 0) {
809 dbug_state_object_p
->s_maxdepth
=
811 fl_top(flist_object_p
));
813 flist_destroy(flist_object_p
);
820 /* free up the dupped control string */
823 UNLOCK_THREAD_DATA();
834 * Pop the debug stack.
839 dbug_state_object_t
*dbug_state_object_p
;
843 /* return if no debugging yet */
847 /* get and remove the top item from the list */
848 dbug_state_object_p
= sd_push
;
849 sd_push
= dbug_state_object_p
->s_next
;
851 /* Delete the item. */
852 dbug_state_destroy(dbug_state_object_p
);
854 /* get the current top of the stack */
855 dbug_state_object_p
= sd_push
;
856 if (dbug_state_object_p
) {
857 /* See if debugging is turned on */
858 if (dbug_state_object_p
->sf_debug
)
865 UNLOCK_THREAD_DATA();
873 * Specifies the name of the process.
874 * Only the pointer is saved, the string is not copied.
881 db_process(const char *namep
)
885 strcpy(sd_process
, namep
);
888 GET_THREAD_DATA_PTR(&tdp
);
889 tdp
->td_stackinit
= (ulong_t
)this;
898 * parse list of modifiers in debug control string
900 * Given pointer to a comma separated list of strings in "cltp",
901 * parses the list, building a list and returning a pointer to it.
902 * The original comma separated list is destroyed in the process of
903 * building the linked list, thus it had better be a duplicate
904 * if it is important.
906 * This routine is only called from db_push.
907 * Returns 0 for success, -1 for failure.
910 listparse(register char *ctlp
, flist_object_t
*head
)
915 /* scan the string until end */
916 while (*ctlp
!= '\0') {
917 /* See if no more room on the list */
918 if (fl_space(head
) == 0)
921 /* save the begining of this section */
924 /* loop until the end of the token is found */
925 while ((*ctlp
!= '\0') && (*ctlp
!= ','))
928 /* add a string terminator if necessary, for strdup */
932 /* make a copy of the string */
933 item
= strdup(start
);
937 /* add it to the list */
949 * Tests the string pointed to by "cp" to determine if it is in
950 * the list pointed to by "flist_object_p". Linkp points to the first
951 * link in the list. If flist_object_p is empty then the string is treated
952 * as if it is in the list (I.E all strings are in the null list).
953 * This may seem rather strange at first but leads to the desired
954 * operation if no list is given. The net effect is that all
955 * strings will be accepted when there is no list, and when there
956 * is a list, only those strings in the list will be accepted.
959 inlist(flist_object_t
*flist_object_p
, const char *cp
)
961 register boolean accept
;
964 if ((flist_object_p
== NULL
) || (flist_object_p
->f_count
== 0) ||
970 /* walk the list of items */
971 for (item
= (char *)fl_top(flist_object_p
);
973 item
= (char *)fl_next(flist_object_p
)) {
975 if (strcmp(item
, cp
) == 0) {
990 * Checks to see if tracing is enabled based on whether the
991 * user has specified tracing, the maximum trace depth has
992 * not yet been reached, the current function is selected,
993 * and the current process is selected. Returns TRUE if
994 * tracing is enabled, FALSE otherwise.
997 dotrace(dbug_state_object_t
*dbug_state_object_p
, const char *func
,
1003 if (dbug_state_object_p
->sf_trace
) {
1004 if (dbug_state_object_p
->s_level
<=
1005 dbug_state_object_p
->s_maxdepth
) {
1006 if (inlist(dbug_state_object_p
->s_functions
, func
)) {
1007 if (inlist(dbug_state_object_p
->s_processes
,
1023 * Indent a line to the given level. Note that this is
1024 * a simple minded but portable implementation.
1025 * There are better ways.
1027 * Also, the indent must be scaled by the compile time option
1028 * of character positions per nesting level.
1031 indent(register dbug_state_object_t
*dbug_state_object_p
, int indent
)
1034 char buffer
[PRINTBUF
];
1038 (count
< (indent
- INDENT
)) && (count
< (PRINTBUF
- 1));
1040 if ((count
% INDENT
) == 0)
1041 buffer
[count
] = '|';
1043 buffer
[count
] = ' ';
1046 buffer
[count
] = '\0';
1047 fprintf(dbug_state_object_p
->s_out_file
, buffer
);
1048 fflush(dbug_state_object_p
->s_out_file
);
1056 * Print prefix common to all debugger output lines, prior to
1057 * doing indentation if necessary. Print such information as
1058 * current process name, current source file name and line number,
1059 * and current function nesting depth.
1062 doprefix(dbug_state_object_t
*dbug_state_object_p
, int line
, long lineno
,
1063 const char *file
, const char *process
)
1066 if (dbug_state_object_p
->sf_pid
)
1067 fprintf(dbug_state_object_p
->s_out_file
, "%5d: ",
1071 if (dbug_state_object_p
->sf_thread
)
1072 fprintf(dbug_state_object_p
->s_out_file
, "%5ld: ",
1075 if (dbug_state_object_p
->sf_number
)
1076 fprintf(dbug_state_object_p
->s_out_file
, "%5ld: ", lineno
);
1078 if (dbug_state_object_p
->sf_process
&& process
)
1079 fprintf(dbug_state_object_p
->s_out_file
, "%s: ", process
);
1081 if (dbug_state_object_p
->sf_file
)
1082 fprintf(dbug_state_object_p
->s_out_file
, "%14s: ", file
);
1084 if (dbug_state_object_p
->sf_line
)
1085 fprintf(dbug_state_object_p
->s_out_file
, "%5d: ", line
);
1087 if (dbug_state_object_p
->sf_depth
)
1088 fprintf(dbug_state_object_p
->s_out_file
, "%4d: ",
1089 dbug_state_object_p
->s_level
);
1091 fflush(dbug_state_object_p
->s_out_file
);
1099 * Given name of a new file (or NULL for stdout) opens the file
1100 * and sets the output stream to the new file.
1103 openfile(char *name
)
1111 if (NOT
writable(name
))
1114 /* see if the file already exists */
1115 if (file_exists(name
))
1121 fp
= fopen(name
, "a+");
1126 * If the file is newly created, give it away to the user
1127 * that started the program.
1140 * Because the debugger might be linked in with a program that
1141 * runs with the set-uid-bit (suid) set, we have to be careful
1142 * about opening a user named file for debug output. This consists
1143 * of checking the file for write access with the real user id,
1144 * or checking the directory where the file will be created.
1146 * Returns TRUE if the user would normally be allowed write or
1147 * create access to the named file. Returns FALSE otherwise.
1150 writable(char *pathname
)
1156 boolean granted
= FALSE
;
1157 if (file_exists(pathname
)) {
1158 if (file_writable(pathname
)) {
1162 lastslash
= strrchr(pathname
, '/');
1163 if (lastslash
!= NULL
) {
1168 if (file_writable(pathname
)) {
1171 if (lastslash
!= NULL
) {
1186 * For unix systems, change the owner of the newly created debug
1187 * file to the real owner. This is strictly for the benefit of
1188 * programs that are running with the set-user-id bit set.
1190 * Note that at this point, the fact that pathname represents
1191 * a newly created file has already been established. If the
1192 * program that the debugger is linked to is not running with
1193 * the suid bit set, then this operation is redundant (but
1197 changeowner(char *pathname
)
1200 chown(pathname
, getuid(), getgid());
1209 * Converts delay argument, given in tenths of a second, to the
1210 * appropriate numerical argument used by the system to delay
1211 * that that many tenths of a second. For example, on the
1212 * amiga, there is a system call "Delay()" which takes an
1213 * argument in ticks (50 per second). On unix, the sleep
1214 * command takes seconds. Thus a value of "10", for one
1215 * second of delay, gets converted to 50 on the amiga, and 1
1216 * on unix. Other systems will need to use a timing loop.
1221 unsigned int delayarg
= 0;
1224 delayarg
= value
/ 10; /* Delay is in seconds for sleep () */
1234 * Implements the delay function.
1236 * A dummy delay stub for systems that do not support delays.
1237 * With a little work, this can be turned into a timing loop.
1250 msleep((ulong_t
)xx
);
1259 * Returns the time in milliseconds used by this process
1264 #include <sys/param.h>
1267 #include <sys/time.h>
1268 #include <sys/resource.h>
1276 getrusage(RUSAGE_SELF
, &ru
);
1277 return ((ru
.ru_utime
.tv_sec
* 1000) + (ru
.ru_utime
.tv_usec
/ 1000));
1298 return (clock() * 10);
1307 * A version of strtok for those systems without it
1310 mystrtok(char *s1
, char *s2
)
1312 static char *end
= NULL
;
1313 register char *rtnval
;
1319 rtnval
= mystrtok((char *)NULL
, s2
);
1320 } else if (end
!= NULL
) {
1323 while ((*end
!= *s2
) && (*end
!= '\0')) {
1341 * Called when a thread exits.
1343 * data pointer to thread specific data
1348 dbug_thread_exit(void *data
)
1350 dbug_state_object_t
*dbug_state_object_p
;
1354 /* If debugging is off, then nothing else to do */
1355 if (NOT
db_debugon())
1358 /* get a pointer to the active state */
1359 dbug_state_object_p
= sd_push
;
1361 if (dbug_state_object_p
->sf_thread
) {
1362 doprefix(dbug_state_object_p
, 0, sd_lineno
++, "unknown",
1364 indent(dbug_state_object_p
, dbug_state_object_p
->s_level
);
1365 fprintf(dbug_state_object_p
->s_out_file
, "thread destroyed\n");
1366 fflush(dbug_state_object_p
->s_out_file
);
1367 delay(dbug_state_object_p
->s_delay
);
1371 FREE_THREAD_DATA(data
);
1372 UNLOCK_THREAD_DATA();
1380 * Causes the process to exit immediatly with a core dump.
1388 dbug_state_object_t
*dbug_state_object_p
= sd_push
;
1389 fflush(dbug_state_object_p
->s_out_file
);
1391 kill(getpid(), SIGABRT
);
1392 (void) signal(SIGABRT
, SIG_DFL
);
1393 (void) sigrelse(SIGABRT
);
1402 * Constructor for the dbug_state class.
1404 * The current level in the call stack.
1408 dbug_state_object_t
*
1409 dbug_state_create(int level
)
1411 dbug_state_object_t
*dbug_state_object_p
;
1413 dbug_state_object_p
=
1414 (dbug_state_object_t
*)calloc(sizeof (dbug_state_object_t
), 1);
1416 if (dbug_state_object_p
== NULL
)
1419 dbug_state_object_p
->sf_trace
= 0;
1420 dbug_state_object_p
->sf_debug
= 0;
1421 dbug_state_object_p
->sf_file
= 0;
1422 dbug_state_object_p
->sf_line
= 0;
1423 dbug_state_object_p
->sf_depth
= 0;
1424 dbug_state_object_p
->sf_process
= 0;
1425 dbug_state_object_p
->sf_number
= 0;
1426 dbug_state_object_p
->sf_pid
= 0;
1427 dbug_state_object_p
->sf_stack
= 0;
1428 dbug_state_object_p
->sf_time
= 0;
1429 dbug_state_object_p
->sf_didopen
= 0;
1430 dbug_state_object_p
->sf_thread
= 0;
1431 dbug_state_object_p
->s_maxdepth
= MAXDEPTH
;
1432 dbug_state_object_p
->s_delay
= 0;
1433 dbug_state_object_p
->s_level
= level
;
1434 dbug_state_object_p
->s_starttime
= 0;
1435 dbug_state_object_p
->s_out_file
= stderr
;
1436 dbug_state_object_p
->s_next
= NULL
;
1437 return (dbug_state_object_p
);
1442 * dbug_state_destroy
1445 * Destructor for the dbug_state class.
1451 dbug_state_destroy(dbug_state_object_t
*dbug_state_object_p
)
1453 if (dbug_state_object_p
->sf_didopen
)
1454 fclose(dbug_state_object_p
->s_out_file
);
1455 free(dbug_state_object_p
);
1463 * Returns 1 if debugging is currently enabled, 0 otherwise.
1471 db_debugon(dbug_object_p
)
1472 dbug_object_t
*dbug_object_p
;
1477 file_exists(const char *pathname
)
1479 return (access(pathname
, F_OK
) == 0);
1482 file_writable(const char *pathname
)
1484 return (access(pathname
, W_OK
) == 0);
1487 db_get_dbug_object_p()
1491 GET_THREAD_DATA_PTR(&tdp
);
1492 return (tdp
->td_first
);
1494 #endif /* DBUG_OFF */