1 /*
2 * CDDL HEADER START
3 *
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
7 * with the License.
8 *
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.
13 *
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]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /*
27 *
28 * dbug.c
29 *
30 * Purpose:
31 * Implements the dbug_routine class.
32 * This code is derived from the public domain DBUG
33 * package written by Fred Fish.
34 *
35 */
36 #pragma ident "%Z%%M% %I% %E% SMI"
37
38 #ifndef DBUG_OFF
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <stdarg.h>
45 #include <string.h>
46 #include <time.h>
47 #include <thread.h>
48 #include <sys/types.h>
49 #include <signal.h>
50 #include "flist.h"
51 #include "mdbug.h"
52 #include "priv.h"
53
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,
60 int indent);
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);
70 void doabort();
71
72 /* initialize static members of class */
73 int sd_on = 0;
74 char sd_process[128];
75 long sd_lineno = 0;
76 dbug_state_object_t *sd_push = NULL;
77
78 /* this structure defines thread specific data */
79 typedef struct thread_data {
80 #ifdef STACKINIT
81 unsigned long td_stackinit; /* Begining of stack. */
82 #endif
83 int td_line; /* Current line number. */
84 char td_keyword[64]; /* Current keyword. */
85 dbug_object_t *td_first; /* Current routine. */
86 } thread_data_t;
87 #ifdef _REENTRANT
88 mutex_t mdt_lock;
89 int mdt_once = 0;
90 thread_key_t mdt_key;
91 #else
92 thread_data_t mdt_data;
93 #endif
94 /*
95 * format of control string
96 * command[:command:...]
97 *
98 * commands
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
103 * print pid 'i' i
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
113 * print trace 't' t
114 * print stack depth 's' s
115 */
116
117 /*
118 *
119 * dbug_object_create
120 *
121 * Description:
122 * Constructor for the dbug_routine class.
123 * Arguments:
124 * line - line number where object was created.
125 * file - file name object was created in.
126 * function- routine name object was created in.
127 * Returns:
128 * Errors:
129 * Preconditions:
130 */
131 void
dbug_object_create(int line,const char * file,const char * function)132 dbug_object_create(int line, const char *file, const char *function)
133 {
134 dbug_object_t *dbug_object_p;
135 dbug_state_object_t *dbug_state_object_p;
136 ulong_t stacksize;
137 int created = 0;
138 char *cptr;
139
140 thread_data_t *tdp = NULL;
141 #ifdef _REENTRANT
142 LOCK_THREAD_DATA();
143 if (!mdt_once) {
144 if (thr_keycreate(&mdt_key, dbug_thread_exit) != 0)
145 doabort();
146 mdt_once++;
147 }
148 GET_THREAD_DATA_PTR(&tdp);
149 if (tdp == NULL) {
150 tdp = (thread_data_t *)calloc(sizeof (*tdp), 1);
151 if (tdp == NULL)
152 doabort();
153 thr_setspecific(mdt_key, tdp);
154 created = 1;
155 tdp->td_keyword[0] = '\0';
156 tdp->td_first = NULL;
157 }
158 #else
159 GET_THREAD_DATA_PTR(&tdp);
160 #endif
161
162 dbug_object_p = (dbug_object_t *)calloc(sizeof (dbug_object_t), 1);
163
164 if (dbug_object_p == NULL)
165 doabort();
166
167 /* save the function name */
168 if (function)
169 strcpy(dbug_object_p->d_func, function);
170 else
171 strcpy(dbug_object_p->d_func, "unknown");
172
173 /* save the base of the file name */
174 if (file) {
175 cptr = strrchr(file, '/');
176 if (cptr == NULL)
177 strcpy(dbug_object_p->d_file, file);
178 else
179 strcpy(dbug_object_p->d_file, cptr++);
180 } else
181 strcpy(dbug_object_p->d_file, "unknown");
182
183 /* Chain this onto our list of them */
184 dbug_object_p->d_prev = tdp->td_first;
185 tdp->td_first = dbug_object_p;
186
187 /* set the default routine exit point line number to zero */
188 dbug_object_p->d_leaveline = 0;
189
190 /* If debugging is off, then all done */
191 if (NOT db_debugon())
192 goto out;
193
194 /* if the active state is null initialize it */
195 if (sd_push == NULL)
196 db_push("d,:f,:F:i:L:n:N:o,cfsd_debug.out:p,:P:r:R:T:t:s");
197
198 /* get a pointer to the active state */
199 dbug_state_object_p = sd_push;
200
201 #ifdef STACKINIT
202 /*
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.
214 */
215 stacksize = (ulong_t)this;
216 if (GROWDOWN)
217 stacksize = tdp->td_stackinit - stacksize;
218 else
219 stacksize = stacksize - tdp->td_stackinit;
220 #endif
221
222 /* record the new nesting level */
223 dbug_state_object_p->s_level++;
224
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);
233 else
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);
238 }
239
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);
248 }
249
250 out:;
251 UNLOCK_THREAD_DATA();
252 }
253
254 /*
255 *
256 * dbug_object_destroy
257 *
258 * Description:
259 * Destructor for the dbug_routine class.
260 * Unchains this object from the list.
261 * Arguments:
262 * Returns:
263 * Errors:
264 * Preconditions:
265 */
266 void
dbug_object_destroy(char * function_name,int line)267 dbug_object_destroy(char *function_name, int line)
268 {
269 dbug_object_t *dbug_object_p;
270 dbug_state_object_t *dbug_state_object_p;
271 thread_data_t *tdp;
272
273 LOCK_THREAD_DATA();
274 GET_THREAD_DATA_PTR(&tdp);
275
276 /* unchain from the list of objects */
277 dbug_object_p = tdp->td_first;
278 tdp->td_first = dbug_object_p->d_prev;
279
280 /* If debugging is off, then nothing else to do */
281 if (NOT db_debugon())
282 goto out;
283
284 dbug_object_p->d_leaveline = line;
285
286 /* get a pointer to the active state */
287 dbug_state_object_p = sd_push;
288
289 /*
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.
293 */
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); */
304 }
305
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);
314 #if 0
315 delay(dbug_state_object_p->s_delay);
316 #endif
317 }
318
319
320 /* record the new nesting level */
321 dbug_state_object_p->s_level--;
322
323 out:;
324 free(dbug_object_p);
325 UNLOCK_THREAD_DATA();
326 }
327
328 /*
329 *
330 * db_keyword
331 *
332 * Description:
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.
341 * Arguments:
342 * keyword - the keyword to test
343 * Returns:
344 * Returns 1 if keyword accepted, 0 otherwise.
345 * Errors:
346 * Preconditions:
347 * precond(keyword)
348 */
349 int
db_keyword(dbug_object_t * dbug_object_p,const char * keyword)350 db_keyword(dbug_object_t *dbug_object_p, const char *keyword)
351 {
352 dbug_state_object_t *dbug_state_object_p;
353 int ret = 0;
354
355 /* return FALSE if not debugging */
356 if (NOT db_debugon())
357 return (0);
358
359 LOCK_THREAD_DATA();
360
361 /* return FALSE if not debugging */
362 if (NOT db_debugon())
363 goto out;
364
365 /* get a pointer to the active state */
366 dbug_state_object_p = sd_push;
367
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,
372 sd_process)) {
373 if (inlist(dbug_state_object_p->s_keywords,
374 keyword)) {
375 ret = 1;
376 goto out;
377 }
378 }
379 }
380 }
381
382 out:
383 UNLOCK_THREAD_DATA();
384 return (ret);
385 }
386
387 /*
388 *
389 * db_pargs
390 *
391 * Description:
392 * Saves arguments for subsequent usage by db_printf.
393 * Arguments:
394 * line - the line number the db_print occurs on
395 * keyword - determines whether or not to really print anything
396 * Returns:
397 * Errors:
398 * Preconditions:
399 * precond(keyword)
400 */
401 void
db_pargs(dbug_object_t * dbug_object_p,int line,char * keyword)402 db_pargs(dbug_object_t *dbug_object_p, int line, char *keyword)
403 {
404 thread_data_t *tdp;
405
406 /* return if no debugging yet */
407 if (NOT db_debugon())
408 return;
409
410 GET_THREAD_DATA_PTR(&tdp);
411
412 tdp->td_line = line;
413 if (keyword)
414 strcpy(tdp->td_keyword, keyword);
415 else
416 tdp->td_keyword[0] = '\0';
417 }
418
419 int
db_getfd()420 db_getfd()
421 {
422 return (fileno(sd_push->s_out_file));
423 }
424
425 /*
426 *
427 * db_printf
428 *
429 * Description:
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.
435 * Arguments:
436 * format - printf style printing control string
437 * ... - additional arguments required by the control string
438 * Returns:
439 * Errors:
440 * Preconditions:
441 * precond(format)
442 */
443 void
db_printf(char * keyword,char * format,...)444 db_printf(char *keyword, char *format, ...)
445 {
446 dbug_object_t *dbug_object_p;
447 thread_data_t *tdp;
448 dbug_state_object_t *dbug_state_object_p = sd_push;
449 va_list args;
450
451 dbug_object_p = db_get_dbug_object_p();
452 /* return if no debugging yet */
453 if (NOT db_debugon())
454 return;
455
456 GET_THREAD_DATA_PTR(&tdp);
457
458 /* return if keyword not selected */
459 if (NOT db_keyword(dbug_object_p, tdp->td_keyword))
460 return;
461
462 LOCK_THREAD_DATA();
463
464 /* get a pointer to the active state */
465
466 va_start(args, format);
467
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);
472 else
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: ",
477 tdp->td_keyword);
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);
482
483 va_end(args);
484
485 UNLOCK_THREAD_DATA();
486 }
487
488 /*
489 *
490 * db_traceprint
491 *
492 * Description:
493 * Prints out a trace of the call stack.
494 * Arguments:
495 * line - the line number where this call was made
496 * keyword - keyword to test against
497 * Returns:
498 * Errors:
499 * Preconditions:
500 */
501 void
db_traceprint(int line,const char * keyword)502 db_traceprint(int line, const char *keyword)
503 {
504 dbug_object_t *dbug_object_p;
505 dbug_object_t *pdr;
506 /* return if no debugging yet */
507 if (NOT db_debugon())
508 return;
509
510 if ((dbug_object_p = db_get_dbug_object_p()) == NULL)
511 doabort();
512
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);
517
518 /* Output a header message */
519 db_printf(NULL, "Stack Trace");
520
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,
525 pdr->d_file);
526 }
527 }
528 }
529
530 /*
531 *
532 * db_assert
533 *
534 * Description:
535 * Called when an assert fails.
536 * Prints out a stack trace and aborts.
537 * Arguments:
538 * line line number assert occurred at
539 * msgp string form of assert code that failed
540 * Returns:
541 * Preconditions:
542 * precond(msgp)
543 */
544 void
db_assert(dbug_object_t * dbug_object_p,int line,const char * msgp)545 db_assert(dbug_object_t *dbug_object_p, int line, const char *msgp)
546 {
547 if (NOT db_debugon())
548 db_push("-#:d");
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);
553 doabort();
554 }
555
556 /*
557 *
558 * db_precond
559 *
560 * Description:
561 * Called when an precond fails.
562 * Prints out a stack trace and aborts.
563 * Arguments:
564 * line line number precond occurred at
565 * msgp string form of precond code that failed
566 * Returns:
567 * Preconditions:
568 * precond(msgp)
569 */
570 void
db_precond(dbug_object_t * dbug_object_p,int line,const char * msgp)571 db_precond(dbug_object_t *dbug_object_p, int line, const char *msgp)
572 {
573 if (NOT db_debugon())
574 db_push("-#:d");
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);
579 doabort();
580 }
581
582 /*
583 *
584 * db_push
585 *
586 * Description:
587 * Push current debugger state and set up a new one.
588 * Returns NULL if no errors, an error string if there
589 * is an error.
590 *
591 * format of control string
592 * command[:command:...]
593 *
594 * commands
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
599 * print pid 'i' i
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
609 * print trace 't' t
610 * print stack depth 's' s
611 */
612 char *
db_push(const char * control)613 db_push(const char *control)
614 {
615 char *dupcontrol = NULL;
616 dbug_state_object_t *dbug_state_object_p;
617 flist_object_t *flist_object_p;
618 register char *scan;
619 int retval;
620 char res[100];
621 int level;
622
623 LOCK_THREAD_DATA();
624
625 /* error if the control string is NULL */
626 if (control == NULL) {
627 strcpy(res, "mdbug: control string is NULL");
628 goto out;
629 }
630
631 /* turn debugging flag off */
632 sd_on = FALSE;
633
634 /* get the level from the old state if it exists */
635 if (sd_push == NULL)
636 level = 0;
637 else
638 level = sd_push->s_level;
639
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");
644 goto out;
645 }
646
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;
650
651 /* Strip off -# if in the control string */
652 if ((*control == '-') && (*(control+1) == '#'))
653 control += 2;
654
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");
659 goto out;
660 }
661
662 /* parse the control string */
663 for (scan = mystrtok(dupcontrol, ":");
664 scan != NULL;
665 scan = mystrtok(NULL, ":")) {
666 switch (*scan++) {
667 case 'd': /* debugging on */
668 sd_on = TRUE;
669 dbug_state_object_p->sf_debug = TRUE;
670 if (*scan++ == ',') {
671 retval = listparse(scan,
672 dbug_state_object_p->s_keywords);
673 if (retval < 0) {
674 strcpy(res,
675 "mdbug: -d too many keywords");
676 goto out;
677 }
678 }
679 break;
680
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);
686 if (retval < 0) {
687 strcpy(res,
688 "mdbug: -D too many delays");
689 goto out;
690 }
691 if (flist_object_p->f_count > 0) {
692 dbug_state_object_p->s_delay =
693 delayarg(atoi(
694 (char *)fl_top(flist_object_p)));
695 }
696 flist_destroy(flist_object_p);
697 }
698 break;
699
700 case 'f': /* list of functions to watch */
701 if (*scan++ == ',') {
702 retval = listparse(scan,
703 dbug_state_object_p->s_functions);
704 if (retval < 0) {
705 strcpy(res,
706 "mdbug: -f too many functions");
707 goto out;
708 }
709 }
710 break;
711
712 case 'F': /* print file name with dbug output */
713 dbug_state_object_p->sf_file = TRUE;
714 break;
715
716 case 'i': /* print pid with dbug output */
717 dbug_state_object_p->sf_pid = TRUE;
718 break;
719
720 case 'L': /* print line nums with dbug output */
721 dbug_state_object_p->sf_line = TRUE;
722 break;
723
724 case 'n': /* print function call depth */
725 dbug_state_object_p->sf_depth = TRUE;
726 break;
727
728 case 'N': /* number each line of dbug output */
729 dbug_state_object_p->sf_number = TRUE;
730 break;
731
732 case 'o': /* specifies output file for dbug */
733 if (*scan++ == ',') {
734 flist_object_p = flist_create();
735 retval = listparse(scan, flist_object_p);
736 if (retval < 0) {
737 strcpy(res,
738 "mdbug: -o too many output files");
739 goto out;
740 }
741
742 if (flist_object_p->f_count > 0) {
743 dbug_state_object_p->s_out_file =
744 openfile((char *)
745 fl_top(flist_object_p));
746 if (dbug_state_object_p->s_out_file !=
747 NULL)
748 dbug_state_object_p->sf_didopen
749 = 1;
750 } else
751 dbug_state_object_p->s_out_file =
752 openfile(NULL);
753 flist_destroy(flist_object_p);
754 } else
755 dbug_state_object_p->s_out_file =
756 openfile(NULL);
757 if (dbug_state_object_p->s_out_file == NULL) {
758 strcpy(res,
759 "mdbug: -o cannot open output file");
760 goto out;
761 }
762 break;
763
764 case 'p': /* debug specified processes */
765 if (*scan++ == ',') {
766 retval = listparse(scan,
767 dbug_state_object_p->s_processes);
768 if (retval < 0) {
769 strcpy(res,
770 "mdbug: -p too many processes");
771 goto out;
772 }
773 }
774 break;
775
776 case 'P': /* print process name on dbug output */
777 dbug_state_object_p->sf_process = TRUE;
778 break;
779
780 case 'r': /* reset indentation to zero */
781 dbug_state_object_p->s_level = 0;
782 break;
783
784 case 's': /* print stack depth on enter */
785 dbug_state_object_p->sf_stack = TRUE;
786 break;
787
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);
791 break;
792
793 case 'T': /* print thread information */
794 dbug_state_object_p->sf_thread = TRUE;
795 break;
796
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);
803 if (retval < 0) {
804 strcpy(res,
805 "mdbug: -t too many traces");
806 goto out;
807 }
808 if (flist_object_p->f_count > 0) {
809 dbug_state_object_p->s_maxdepth =
810 atoi((char *)
811 fl_top(flist_object_p));
812 }
813 flist_destroy(flist_object_p);
814 }
815 break;
816 }
817 }
818
819 out:
820 /* free up the dupped control string */
821 free(dupcontrol);
822
823 UNLOCK_THREAD_DATA();
824
825 /* return result */
826 return (NULL);
827 }
828
829 /*
830 *
831 * db_pop
832 *
833 * Description:
834 * Pop the debug stack.
835 */
836 void
db_pop()837 db_pop()
838 {
839 dbug_state_object_t *dbug_state_object_p;
840
841 LOCK_THREAD_DATA();
842
843 /* return if no debugging yet */
844 if (sd_push == NULL)
845 goto out;
846
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;
850
851 /* Delete the item. */
852 dbug_state_destroy(dbug_state_object_p);
853
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)
859 sd_on = TRUE;
860 else
861 sd_on = FALSE;
862 }
863
864 out:;
865 UNLOCK_THREAD_DATA();
866 }
867
868 /*
869 *
870 * db_process
871 *
872 * Description:
873 * Specifies the name of the process.
874 * Only the pointer is saved, the string is not copied.
875 * Arguments:
876 * namep
877 * Returns:
878 * Preconditions:
879 */
880 void
db_process(const char * namep)881 db_process(const char *namep)
882 {
883 thread_data_t *tdp;
884
885 strcpy(sd_process, namep);
886
887 #ifdef STACKINIT
888 GET_THREAD_DATA_PTR(&tdp);
889 tdp->td_stackinit = (ulong_t)this;
890 #endif
891 }
892
893 /*
894 *
895 * listparse
896 *
897 * Description:
898 * parse list of modifiers in debug control string
899 *
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.
905 *
906 * This routine is only called from db_push.
907 * Returns 0 for success, -1 for failure.
908 */
909 static int
listparse(register char * ctlp,flist_object_t * head)910 listparse(register char *ctlp, flist_object_t *head)
911 {
912 char *start;
913 char *item;
914
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)
919 return (-1);
920
921 /* save the begining of this section */
922 start = ctlp;
923
924 /* loop until the end of the token is found */
925 while ((*ctlp != '\0') && (*ctlp != ','))
926 ctlp++;
927
928 /* add a string terminator if necessary, for strdup */
929 if (*ctlp == ',')
930 *ctlp++ = '\0';
931
932 /* make a copy of the string */
933 item = strdup(start);
934 if (item == NULL)
935 return (-1);
936
937 /* add it to the list */
938 fl_push(head, item);
939 }
940
941 return (0);
942 }
943
944 /*
945 *
946 * inlist
947 *
948 * Description:
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.
957 */
958 static boolean
inlist(flist_object_t * flist_object_p,const char * cp)959 inlist(flist_object_t *flist_object_p, const char *cp)
960 {
961 register boolean accept;
962 register char *item;
963
964 if ((flist_object_p == NULL) || (flist_object_p->f_count == 0) ||
965 (cp == NULL))
966 accept = TRUE;
967 else {
968 accept = FALSE;
969
970 /* walk the list of items */
971 for (item = (char *)fl_top(flist_object_p);
972 item != NULL;
973 item = (char *)fl_next(flist_object_p)) {
974 /* see if a match */
975 if (strcmp(item, cp) == 0) {
976 accept = TRUE;
977 break;
978 }
979 }
980 }
981
982 return (accept);
983 }
984
985 /*
986 *
987 * dotrace
988 *
989 * Description:
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.
995 */
996 static boolean
dotrace(dbug_state_object_t * dbug_state_object_p,const char * func,const char * process)997 dotrace(dbug_state_object_t *dbug_state_object_p, const char *func,
998 const char *process)
999 {
1000 boolean trace;
1001
1002 trace = FALSE;
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,
1008 process)) {
1009 trace = TRUE;
1010 }
1011 }
1012 }
1013 }
1014
1015 return (trace);
1016 }
1017
1018 /*
1019 *
1020 * indent
1021 *
1022 * Description:
1023 * Indent a line to the given level. Note that this is
1024 * a simple minded but portable implementation.
1025 * There are better ways.
1026 *
1027 * Also, the indent must be scaled by the compile time option
1028 * of character positions per nesting level.
1029 */
1030 static void
indent(register dbug_state_object_t * dbug_state_object_p,int indent)1031 indent(register dbug_state_object_t *dbug_state_object_p, int indent)
1032 {
1033 register int count;
1034 char buffer[PRINTBUF];
1035
1036 indent *= INDENT;
1037 for (count = 0;
1038 (count < (indent - INDENT)) && (count < (PRINTBUF - 1));
1039 count++) {
1040 if ((count % INDENT) == 0)
1041 buffer[count] = '|';
1042 else
1043 buffer[count] = ' ';
1044 }
1045
1046 buffer[count] = '\0';
1047 fprintf(dbug_state_object_p->s_out_file, buffer);
1048 fflush(dbug_state_object_p->s_out_file);
1049 }
1050
1051 /*
1052 *
1053 * doprefix
1054 *
1055 * Description:
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.
1060 */
1061 static void
doprefix(dbug_state_object_t * dbug_state_object_p,int line,long lineno,const char * file,const char * process)1062 doprefix(dbug_state_object_t *dbug_state_object_p, int line, long lineno,
1063 const char *file, const char *process)
1064 {
1065 #if DBUG_UNIX
1066 if (dbug_state_object_p->sf_pid)
1067 fprintf(dbug_state_object_p->s_out_file, "%5d: ",
1068 (int)getpid());
1069 #endif
1070
1071 if (dbug_state_object_p->sf_thread)
1072 fprintf(dbug_state_object_p->s_out_file, "%5ld: ",
1073 (long)thr_self());
1074
1075 if (dbug_state_object_p->sf_number)
1076 fprintf(dbug_state_object_p->s_out_file, "%5ld: ", lineno);
1077
1078 if (dbug_state_object_p->sf_process && process)
1079 fprintf(dbug_state_object_p->s_out_file, "%s: ", process);
1080
1081 if (dbug_state_object_p->sf_file)
1082 fprintf(dbug_state_object_p->s_out_file, "%14s: ", file);
1083
1084 if (dbug_state_object_p->sf_line)
1085 fprintf(dbug_state_object_p->s_out_file, "%5d: ", line);
1086
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);
1090
1091 fflush(dbug_state_object_p->s_out_file);
1092 }
1093
1094 /*
1095 *
1096 * openfile
1097 *
1098 * Description:
1099 * Given name of a new file (or NULL for stdout) opens the file
1100 * and sets the output stream to the new file.
1101 */
1102 static FILE *
openfile(char * name)1103 openfile(char *name)
1104 {
1105 FILE *fp;
1106 boolean newfile;
1107
1108 if (name == NULL)
1109 return (stdout);
1110
1111 if (NOT writable(name))
1112 return (NULL);
1113
1114 /* see if the file already exists */
1115 if (file_exists(name))
1116 newfile = FALSE;
1117 else
1118 newfile = TRUE;
1119
1120 /* open the file */
1121 fp = fopen(name, "a+");
1122 if (fp == NULL)
1123 return (NULL);
1124
1125 /*
1126 * If the file is newly created, give it away to the user
1127 * that started the program.
1128 */
1129 if (newfile) {
1130 changeowner(name);
1131 }
1132 return (fp);
1133 }
1134
1135 /*
1136 *
1137 * writable
1138 *
1139 * Description:
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.
1145 *
1146 * Returns TRUE if the user would normally be allowed write or
1147 * create access to the named file. Returns FALSE otherwise.
1148 */
1149 static boolean
writable(char * pathname)1150 writable(char *pathname)
1151 {
1152 #if DBUG_UNIX
1153
1154 char *lastslash;
1155
1156 boolean granted = FALSE;
1157 if (file_exists(pathname)) {
1158 if (file_writable(pathname)) {
1159 granted = TRUE;
1160 }
1161 } else {
1162 lastslash = strrchr(pathname, '/');
1163 if (lastslash != NULL) {
1164 *lastslash = '\0';
1165 } else {
1166 pathname = ".";
1167 }
1168 if (file_writable(pathname)) {
1169 granted = TRUE;
1170 }
1171 if (lastslash != NULL) {
1172 *lastslash = '/';
1173 }
1174 }
1175 return (granted);
1176 #else
1177 return (TRUE);
1178 #endif
1179 }
1180
1181 /*
1182 *
1183 * changeowner
1184 *
1185 * Description:
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.
1189 *
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
1194 * harmless).
1195 */
1196 static void
changeowner(char * pathname)1197 changeowner(char *pathname)
1198 {
1199 #if DBUG_UNIX
1200 chown(pathname, getuid(), getgid());
1201 #endif
1202 }
1203
1204 /*
1205 *
1206 * delayarg
1207 *
1208 * Description:
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.
1217 */
1218 static int
delayarg(int value)1219 delayarg(int value)
1220 {
1221 unsigned int delayarg = 0;
1222
1223 #if (unix || xenix)
1224 delayarg = value / 10; /* Delay is in seconds for sleep () */
1225 #endif
1226 return (delayarg);
1227 }
1228
1229 /*
1230 *
1231 * delay
1232 *
1233 * Description:
1234 * Implements the delay function.
1235 *
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.
1238 */
1239
1240 static void
delay(uint_t xx)1241 delay(uint_t xx)
1242 {
1243 #if (unix || xenix)
1244 sleep(xx);
1245 #endif
1246 #if amiga
1247 Delay(xx);
1248 #endif
1249 #ifdef __ZTC__
1250 msleep((ulong_t)xx);
1251 #endif
1252 }
1253
1254 /*
1255 *
1256 * getclock
1257 *
1258 * Description:
1259 * Returns the time in milliseconds used by this process
1260 * so far.
1261 */
1262 #if (unix || xenix)
1263
1264 #include <sys/param.h>
1265 #if BSD4_3 || sun
1266
1267 #include <sys/time.h>
1268 #include <sys/resource.h>
1269
1270 static ulong_t
getclock()1271 getclock()
1272 {
1273 #if 0
1274 struct rusage ru;
1275
1276 getrusage(RUSAGE_SELF, &ru);
1277 return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000));
1278 #else
1279 return (0);
1280 #endif
1281 }
1282
1283 #else
1284
1285 static ulong_t
getclock()1286 getclock()
1287 {
1288 return (0);
1289 }
1290
1291 #endif
1292 #endif /* unix */
1293
1294 #ifdef MSDOS
1295 static ulong_t
getclock()1296 getclock()
1297 {
1298 return (clock() * 10);
1299 }
1300 #endif
1301
1302 /*
1303 *
1304 * mystrtok
1305 *
1306 * Description:
1307 * A version of strtok for those systems without it
1308 */
1309 static char *
mystrtok(char * s1,char * s2)1310 mystrtok(char *s1, char *s2)
1311 {
1312 static char *end = NULL;
1313 register char *rtnval;
1314
1315 rtnval = NULL;
1316 if (s2 != NULL) {
1317 if (s1 != NULL) {
1318 end = s1;
1319 rtnval = mystrtok((char *)NULL, s2);
1320 } else if (end != NULL) {
1321 if (*end != '\0') {
1322 rtnval = end;
1323 while ((*end != *s2) && (*end != '\0')) {
1324 end++;
1325 }
1326 if (*end != '\0') {
1327 *end++ = '\0';
1328 }
1329 }
1330 }
1331 }
1332
1333 return (rtnval);
1334 }
1335
1336 /*
1337 *
1338 * dbug_thread_exit
1339 *
1340 * Description:
1341 * Called when a thread exits.
1342 * Arguments:
1343 * data pointer to thread specific data
1344 * Returns:
1345 * Preconditions:
1346 */
1347 void
dbug_thread_exit(void * data)1348 dbug_thread_exit(void *data)
1349 {
1350 dbug_state_object_t *dbug_state_object_p;
1351
1352 LOCK_THREAD_DATA();
1353
1354 /* If debugging is off, then nothing else to do */
1355 if (NOT db_debugon())
1356 goto out;
1357
1358 /* get a pointer to the active state */
1359 dbug_state_object_p = sd_push;
1360
1361 if (dbug_state_object_p->sf_thread) {
1362 doprefix(dbug_state_object_p, 0, sd_lineno++, "unknown",
1363 sd_process);
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);
1368 }
1369
1370 out:;
1371 FREE_THREAD_DATA(data);
1372 UNLOCK_THREAD_DATA();
1373 }
1374
1375 /*
1376 *
1377 * doabort
1378 *
1379 * Description:
1380 * Causes the process to exit immediatly with a core dump.
1381 * Arguments:
1382 * Returns:
1383 * Preconditions:
1384 */
1385 void
doabort()1386 doabort()
1387 {
1388 dbug_state_object_t *dbug_state_object_p = sd_push;
1389 fflush(dbug_state_object_p->s_out_file);
1390 for (;;) {
1391 kill(getpid(), SIGABRT);
1392 (void) signal(SIGABRT, SIG_DFL);
1393 (void) sigrelse(SIGABRT);
1394 }
1395 }
1396
1397 /*
1398 *
1399 * dbug_state_create
1400 *
1401 * Description:
1402 * Constructor for the dbug_state class.
1403 * Arguments:
1404 * The current level in the call stack.
1405 * Returns:
1406 * Preconditions:
1407 */
1408 dbug_state_object_t *
dbug_state_create(int level)1409 dbug_state_create(int level)
1410 {
1411 dbug_state_object_t *dbug_state_object_p;
1412
1413 dbug_state_object_p =
1414 (dbug_state_object_t *)calloc(sizeof (dbug_state_object_t), 1);
1415
1416 if (dbug_state_object_p == NULL)
1417 doabort();
1418
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);
1438 }
1439
1440 /*
1441 *
1442 * dbug_state_destroy
1443 *
1444 * Description:
1445 * Destructor for the dbug_state class.
1446 * Arguments:
1447 * Returns:
1448 * Preconditions:
1449 */
1450 void
dbug_state_destroy(dbug_state_object_t * dbug_state_object_p)1451 dbug_state_destroy(dbug_state_object_t *dbug_state_object_p)
1452 {
1453 if (dbug_state_object_p->sf_didopen)
1454 fclose(dbug_state_object_p->s_out_file);
1455 free(dbug_state_object_p);
1456 }
1457
1458 /*
1459 *
1460 * db_debugon
1461 *
1462 * Description:
1463 * Returns 1 if debugging is currently enabled, 0 otherwise.
1464 * Arguments:
1465 * Returns:
1466 * Errors:
1467 * Preconditions:
1468 */
1469
1470 int
db_debugon(dbug_object_p)1471 db_debugon(dbug_object_p)
1472 dbug_object_t *dbug_object_p;
1473 {
1474 return (sd_on);
1475 }
1476 boolean
file_exists(const char * pathname)1477 file_exists(const char *pathname)
1478 {
1479 return (access(pathname, F_OK) == 0);
1480 }
1481 boolean
file_writable(const char * pathname)1482 file_writable(const char *pathname)
1483 {
1484 return (access(pathname, W_OK) == 0);
1485 }
1486 dbug_object_t *
db_get_dbug_object_p()1487 db_get_dbug_object_p()
1488 {
1489 thread_data_t *tdp;
1490
1491 GET_THREAD_DATA_PTR(&tdp);
1492 return (tdp->td_first);
1493 }
1494 #endif /* DBUG_OFF */
1495