xref: /titanic_44/usr/src/cmd/fs.d/cachefs/mdbug/dbug.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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  *
24  *			dbug.c
25  *
26  * Purpose:
27  *    Implements the dbug_routine class.
28  *    This code is derived from the public domain DBUG
29  *    package written by Fred Fish.
30  *
31  */
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 /* Copyright (c) 1994-1997 by Sun Microsystems, Inc. */
34 
35 #ifndef DBUG_OFF
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <stdarg.h>
42 #include <string.h>
43 #include <time.h>
44 #include <thread.h>
45 #include <sys/types.h>
46 #include <signal.h>
47 #include "flist.h"
48 #include "mdbug.h"
49 #include "priv.h"
50 
51 /* forward references */
52 static int listparse(register char *ctlp, flist_object_t *head);
53 static boolean inlist(flist_object_t *flist_object_p, const char *cp);
54 static boolean dotrace(dbug_state_object_t *dbug_state_object_p,
55     const char *func, const char *process);
56 static void indent(register dbug_state_object_t *dbug_state_object_p,
57     int indent);
58 static void doprefix(dbug_state_object_t *dbug_state_object_p, int line,
59     long lineno, const char *file, const char *process);
60 static FILE *openfile(char *name);
61 static boolean writable(char *pathname);
62 static void changeowner(char *pathname);
63 static int delayarg(int value);
64 static void delay(u_int xx);
65 static u_long getclock();
66 static char *mystrtok(char *s1, char *s2);
67 void doabort();
68 
69 /* initialize static members of class */
70 int	sd_on = 0;
71 char	sd_process[128];
72 long	sd_lineno = 0;
73 dbug_state_object_t *sd_push = NULL;
74 
75 /* this structure defines thread specific data */
76 typedef struct thread_data {
77 #ifdef STACKINIT
78 	unsigned long	 td_stackinit;		/* Begining of stack. */
79 #endif
80 	int		 td_line;		/* Current line number. */
81 	char		 td_keyword[64];	/* Current keyword. */
82 	dbug_object_t	*td_first;		/* Current routine. */
83 } thread_data_t;
84 #ifdef _REENTRANT
85 mutex_t		mdt_lock;
86 int		mdt_once = 0;
87 thread_key_t	mdt_key;
88 #else
89 thread_data_t	mdt_data;
90 #endif
91 /*
92  * format of control string
93  *   command[:command:...]
94  *
95  *   commands
96  *   debugging on	'd'  d[,<keyword>[,...]]
97  *   delay value	'D'  D[,<delay value>]
98  *   function list	'f'  f[,<function name>[,...]]
99  *   print filename	'F'  F
100  *   print pid		'i'  i
101  *   print line number	'L'  L
102  *   print call depth	'n'  n
103  *   number each line	'N'  N
104  *   output file	'o'  o[,<filename>
105  *   process name list	'p'  p[,<process name>[,...]]
106  *   print proc name	'P'  P
107  *   reset indentation	'r'  r
108  *   print runtime	'R'  R
109  *   print thread info	'T'  T
110  *   print trace	't'  t
111  *   print stack depth	's'  s
112  */
113 
114 /*
115  *
116  *		dbug_object_create
117  *
118  * Description:
119  *	Constructor for the dbug_routine class.
120  * Arguments:
121  *	line	- line number where object was created.
122  *	file	- file name object was created in.
123  *	function- routine name object was created in.
124  * Returns:
125  * Errors:
126  * Preconditions:
127  */
128 void
129 dbug_object_create(int line, const char *file, const char *function)
130 {
131 	dbug_object_t  *dbug_object_p;
132 	dbug_state_object_t *dbug_state_object_p;
133 	u_long stacksize;
134 	int created = 0;
135 	char *cptr;
136 
137 	thread_data_t *tdp = NULL;
138 #ifdef _REENTRANT
139 	LOCK_THREAD_DATA();
140 	if (!mdt_once) {
141 		if (thr_keycreate(&mdt_key, dbug_thread_exit) != 0)
142 			doabort();
143 		mdt_once++;
144 	}
145 	GET_THREAD_DATA_PTR(&tdp);
146 	if (tdp == NULL) {
147 		tdp = (thread_data_t *)calloc(sizeof (*tdp), 1);
148 		if (tdp == NULL)
149 			doabort();
150 		thr_setspecific(mdt_key, tdp);
151 		created = 1;
152 		tdp->td_keyword[0] = '\0';
153 		tdp->td_first = NULL;
154 	}
155 #else
156 	GET_THREAD_DATA_PTR(&tdp);
157 #endif
158 
159 	dbug_object_p = (dbug_object_t *)calloc(sizeof (dbug_object_t), 1);
160 
161 	if (dbug_object_p == NULL)
162 		doabort();
163 
164 	/* save the function name */
165 	if (function)
166 		strcpy(dbug_object_p->d_func, function);
167 	else
168 		strcpy(dbug_object_p->d_func, "unknown");
169 
170 	/* save the base of the file name */
171 	if (file) {
172 		cptr = strrchr(file, '/');
173 		if (cptr == NULL)
174 			strcpy(dbug_object_p->d_file, file);
175 		else
176 			strcpy(dbug_object_p->d_file, cptr++);
177 	} else
178 		strcpy(dbug_object_p->d_file, "unknown");
179 
180 	/* Chain this onto our list of them */
181 	dbug_object_p->d_prev = tdp->td_first;
182 	tdp->td_first = dbug_object_p;
183 
184 	/* set the default routine exit point line number to zero */
185 	dbug_object_p->d_leaveline = 0;
186 
187 	/* If debugging is off, then all done */
188 	if (NOT db_debugon())
189 		goto out;
190 
191 	/* if the active state is null initialize it */
192 	if (sd_push == NULL)
193 		db_push("d,:f,:F:i:L:n:N:o,cfsd_debug.out:p,:P:r:R:T:t:s");
194 
195 	/* get a pointer to the active state */
196 	dbug_state_object_p = sd_push;
197 
198 #ifdef STACKINIT
199 	/*
200 	 * Get the new stack depth.
201 	 * There a two problems associated with this.
202 	 * One is because c++ allows declarations anywhere inside of
203 	 * a routine.  So it is difficult to position the dbug_enter()
204 	 * macro after all declarations and still be useful.
205 	 * Two is that the dbug_enter() macro should be before all
206 	 * other automatic objects so that its destructor gets called
207 	 * last as the routine is returning.
208 	 * The solution is to advise placing the dbug_enter() macro at
209 	 * the start of the routine and specifying that that stack
210 	 * values apply upto but not including the current routine.
211 	 */
212 	stacksize = (u_long)this;
213 	if (GROWDOWN)
214 		stacksize = tdp->td_stackinit - stacksize;
215 	else
216 		stacksize = stacksize - tdp->td_stackinit;
217 #endif
218 
219 	/* record the new nesting level */
220 	dbug_state_object_p->s_level++;
221 
222 	/* if producing a trace of function calls */
223 	if (dotrace(dbug_state_object_p, dbug_object_p->d_func, sd_process)) {
224 		doprefix(dbug_state_object_p, line, sd_lineno++,
225 		    dbug_object_p->d_file, sd_process);
226 		indent(dbug_state_object_p, dbug_state_object_p->s_level);
227 		if (dbug_state_object_p->sf_stack)
228 			fprintf(dbug_state_object_p->s_out_file, ">%s   %ld\n",
229 			    dbug_object_p->d_func, stacksize);
230 		else
231 			fprintf(dbug_state_object_p->s_out_file, ">%s\n",
232 			    dbug_object_p->d_func);
233 		fflush(dbug_state_object_p->s_out_file);
234 		delay(dbug_state_object_p->s_delay);
235 	}
236 
237 	/* if a new thread */
238 	if (created && dbug_state_object_p->sf_thread) {
239 		doprefix(dbug_state_object_p, line, sd_lineno++,
240 		    dbug_object_p->d_file, sd_process);
241 		indent(dbug_state_object_p, dbug_state_object_p->s_level);
242 		fprintf(dbug_state_object_p->s_out_file, "thread created\n");
243 		fflush(dbug_state_object_p->s_out_file);
244 		delay(dbug_state_object_p->s_delay);
245 	}
246 
247 out:;
248 	UNLOCK_THREAD_DATA();
249 }
250 
251 /*
252  *
253  *		dbug_object_destroy
254  *
255  * Description:
256  *	Destructor for the dbug_routine class.
257  *	Unchains this object from the list.
258  * Arguments:
259  * Returns:
260  * Errors:
261  * Preconditions:
262  */
263 void
264 dbug_object_destroy(char *function_name, int line)
265 {
266 	dbug_object_t *dbug_object_p;
267 	dbug_state_object_t *dbug_state_object_p;
268 	thread_data_t *tdp;
269 
270 	LOCK_THREAD_DATA();
271 	GET_THREAD_DATA_PTR(&tdp);
272 
273 	/* unchain from the list of objects */
274 	dbug_object_p = tdp->td_first;
275 	tdp->td_first = dbug_object_p->d_prev;
276 
277 	/* If debugging is off, then nothing else to do */
278 	if (NOT db_debugon())
279 		goto out;
280 
281 	dbug_object_p->d_leaveline = line;
282 
283 	/* get a pointer to the active state */
284 	dbug_state_object_p = sd_push;
285 
286 	/*
287 	 * Make sure the last one created is being deleted.
288 	 * This will not be the case if there are multiple dbug_routine
289 	 * objects per routine or if one is created outside of a routine.
290 	 */
291 	if (strcmp(function_name, dbug_object_p->d_func)) {
292 		doprefix(dbug_state_object_p, dbug_object_p->d_leaveline,
293 		    sd_lineno++, dbug_object_p->d_file, sd_process);
294 		indent(dbug_state_object_p, dbug_state_object_p->s_level);
295 		fprintf(dbug_state_object_p->s_out_file,
296 		    "<expected %s, actual %s, ERROR: "
297 		    "dbug_enter/dbug_leave out of sequence.\n",
298 		    dbug_object_p->d_func, function_name);
299 		fflush(dbug_state_object_p->s_out_file);
300 		/* delay(dbug_state_object_p->s_delay); */
301 	}
302 
303 	/* if producing a trace of function calls */
304 	if (dotrace(dbug_state_object_p, dbug_object_p->d_func, sd_process)) {
305 		doprefix(dbug_state_object_p, dbug_object_p->d_leaveline,
306 		    sd_lineno++, dbug_object_p->d_file, sd_process);
307 		indent(dbug_state_object_p, dbug_state_object_p->s_level);
308 		fprintf(dbug_state_object_p->s_out_file, "<%s\n",
309 		    dbug_object_p->d_func);
310 		fflush(dbug_state_object_p->s_out_file);
311 #if 0
312 		delay(dbug_state_object_p->s_delay);
313 #endif
314 	}
315 
316 
317 	/* record the new nesting level */
318 	dbug_state_object_p->s_level--;
319 
320 out:;
321 	free(dbug_object_p);
322 	UNLOCK_THREAD_DATA();
323 }
324 
325 /*
326  *
327  *		db_keyword
328  *
329  * Description:
330  *	Test a keyword to determine if it is in the currently active
331  *	keyword list.  As with the function list, a keyword is accepted
332  *	if the list is null, otherwise it must match one of the list
333  *	members.  When debugging is not on, no keywords are accepted.
334  *	After the maximum trace level is exceeded, no keywords are
335  *	accepted (this behavior subject to change).  Additionally,
336  *	the current function and process must be accepted based on
337  *	their respective lists.
338  * Arguments:
339  *	keyword - the keyword to test
340  * Returns:
341  *	Returns 1 if keyword accepted, 0 otherwise.
342  * Errors:
343  * Preconditions:
344  *	precond(keyword)
345  */
346 int
347 db_keyword(dbug_object_t *dbug_object_p, const char *keyword)
348 {
349 	dbug_state_object_t *dbug_state_object_p;
350 	int ret = 0;
351 
352 	/* return FALSE if not debugging */
353 	if (NOT db_debugon())
354 		return (0);
355 
356 	LOCK_THREAD_DATA();
357 
358 	/* return FALSE if not debugging */
359 	if (NOT db_debugon())
360 		goto out;
361 
362 	/* get a pointer to the active state */
363 	dbug_state_object_p = sd_push;
364 
365 	if (dbug_state_object_p->sf_debug) {  /* is this test necessary ? */
366 		if (inlist(dbug_state_object_p->s_functions,
367 		    dbug_object_p->d_func)) {
368 			if (inlist(dbug_state_object_p->s_processes,
369 			    sd_process)) {
370 				if (inlist(dbug_state_object_p->s_keywords,
371 				    keyword)) {
372 					ret = 1;
373 					goto out;
374 				}
375 			}
376 		}
377 	}
378 
379 out:
380 	UNLOCK_THREAD_DATA();
381 	return (ret);
382 }
383 
384 /*
385  *
386  *		db_pargs
387  *
388  * Description:
389  *	Saves arguments for subsequent usage by db_printf.
390  * Arguments:
391  *	line    - the line number the db_print occurs on
392  *	keyword - determines whether or not to really print anything
393  * Returns:
394  * Errors:
395  * Preconditions:
396  *	precond(keyword)
397  */
398 void
399 db_pargs(dbug_object_t *dbug_object_p, int line, char *keyword)
400 {
401 	thread_data_t *tdp;
402 
403 	/* return if no debugging yet */
404 	if (NOT db_debugon())
405 		return;
406 
407 	GET_THREAD_DATA_PTR(&tdp);
408 
409 	tdp->td_line = line;
410 	if (keyword)
411 		strcpy(tdp->td_keyword, keyword);
412 	else
413 		tdp->td_keyword[0] = '\0';
414 }
415 
416 int
417 db_getfd()
418 {
419 	return (fileno(sd_push->s_out_file));
420 }
421 
422 /*
423  *
424  *		db_printf
425  *
426  * Description:
427  *	Outputs the specified message if the keyword specified
428  *	by db_pargs() has been selected.  The line number specified
429  *	by db_pargs() is also used as the line number the db_printf()
430  *	occurs on.  The format string should NOT include a terminating
431  *	newline as one is supplied automatically.
432  * Arguments:
433  *	format - printf style printing control string
434  *	...    - additional arguments required by the control string
435  * Returns:
436  * Errors:
437  * Preconditions:
438  *	precond(format)
439  */
440 void
441 db_printf(char *keyword, char *format, ...)
442 {
443 	dbug_object_t *dbug_object_p;
444 	thread_data_t *tdp;
445 	dbug_state_object_t *dbug_state_object_p = sd_push;
446 	va_list args;
447 
448 	dbug_object_p = db_get_dbug_object_p();
449 	/* return if no debugging yet */
450 	if (NOT db_debugon())
451 		return;
452 
453 	GET_THREAD_DATA_PTR(&tdp);
454 
455 	/* return if keyword not selected */
456 	if (NOT db_keyword(dbug_object_p, tdp->td_keyword))
457 		return;
458 
459 	LOCK_THREAD_DATA();
460 
461 	/* get a pointer to the active state */
462 
463 	va_start(args, format);
464 
465 	doprefix(dbug_state_object_p, tdp->td_line, sd_lineno++,
466 		dbug_object_p->d_file, sd_process);
467 	if (dbug_state_object_p->sf_trace)
468 		indent(dbug_state_object_p, dbug_state_object_p->s_level +1);
469 	else
470 		fprintf(dbug_state_object_p->s_out_file, "%s: ",
471 		    dbug_object_p->d_func);
472 	if (tdp->td_keyword[0])
473 		fprintf(dbug_state_object_p->s_out_file, "%s: ",
474 		    tdp->td_keyword);
475 	vfprintf(dbug_state_object_p->s_out_file, format, args);
476 	fprintf(dbug_state_object_p->s_out_file, "\n");
477 	fflush(dbug_state_object_p->s_out_file);
478 	delay(dbug_state_object_p->s_delay);
479 
480 	va_end(args);
481 
482 	UNLOCK_THREAD_DATA();
483 }
484 
485 /*
486  *
487  *		db_traceprint
488  *
489  * Description:
490  *	Prints out a trace of the call stack.
491  * Arguments:
492  *	line    - the line number where this call was made
493  *	keyword - keyword to test against
494  * Returns:
495  * Errors:
496  * Preconditions:
497  */
498 void
499 db_traceprint(int line, const char *keyword)
500 {
501 	dbug_object_t *dbug_object_p;
502 	dbug_object_t *pdr;
503 	/* return if no debugging yet */
504 	if (NOT db_debugon())
505 		return;
506 
507 	if ((dbug_object_p = db_get_dbug_object_p()) == NULL)
508 		doabort();
509 
510 	/* If the specified keyword is enabled */
511 	if (db_keyword(dbug_object_p, keyword)) {
512 		/* perform setup for using db_printf */
513 		db_pargs(dbug_object_p, line, NULL);
514 
515 		/* Output a header message */
516 		db_printf(NULL, "Stack Trace");
517 
518 		/* walk the stack of dbug_routine objects */
519 		for (pdr = dbug_object_p; pdr != NULL; pdr = pdr->d_prev) {
520 			/* output the routine name */
521 			db_printf(NULL, "  %s() (%s)", pdr->d_func,
522 			    pdr->d_file);
523 		}
524 	}
525 }
526 
527 /*
528  *
529  *			db_assert
530  *
531  * Description:
532  *	Called when an assert fails.
533  *	Prints out a stack trace and aborts.
534  * Arguments:
535  *	line	line number assert occurred at
536  *	msgp	string form of assert code that failed
537  * Returns:
538  * Preconditions:
539  *	precond(msgp)
540  */
541 void
542 db_assert(dbug_object_t *dbug_object_p, int line, const char *msgp)
543 {
544 	if (NOT db_debugon())
545 		db_push("-#:d");
546 	db_pargs(dbug_object_p, line, NULL);
547 	db_printf(NULL, "Assertion Failed %s:%s():%d \"%s\"",
548 	    dbug_object_p->d_file, dbug_object_p->d_func, line, msgp);
549 	db_traceprint(line, NULL);
550 	doabort();
551 }
552 
553 /*
554  *
555  *			db_precond
556  *
557  * Description:
558  *	Called when an precond fails.
559  *	Prints out a stack trace and aborts.
560  * Arguments:
561  *	line	line number precond occurred at
562  *	msgp	string form of precond code that failed
563  * Returns:
564  * Preconditions:
565  *	precond(msgp)
566  */
567 void
568 db_precond(dbug_object_t *dbug_object_p, int line, const char *msgp)
569 {
570 	if (NOT db_debugon())
571 		db_push("-#:d");
572 	db_pargs(dbug_object_p, line, NULL);
573 	db_printf(NULL, "Precondition Failed %s:%s():%d \"%s\"",
574 	    dbug_object_p->d_file, dbug_object_p->d_func, line, msgp);
575 	db_traceprint(line, NULL);
576 	doabort();
577 }
578 
579 /*
580  *
581  *		db_push
582  *
583  * Description:
584  *	Push current debugger state and set up a new one.
585  *	Returns NULL if no errors, an error string if there
586  *	is an error.
587  *
588  * format of control string
589  *   command[:command:...]
590  *
591  *   commands
592  *   debugging on	'd'  d[,<keyword>[,...]]
593  *   delay value	'D'  D[,<delay value>]
594  *   function list	'f'  f[,<function name>[,...]]
595  *   print filename	'F'  F
596  *   print pid		'i'  i
597  *   print line number	'L'  L
598  *   print call depth	'n'  n
599  *   number each line	'N'  N
600  *   output file	'o'  o[,<filename>
601  *   process name list	'p'  p[,<process name>[,...]]
602  *   print proc name	'P'  P
603  *   reset indentation	'r'  r
604  *   print runtime	'R'  R
605  *   print thread info	'T'  T
606  *   print trace	't'  t
607  *   print stack depth	's'  s
608  */
609 char *
610 db_push(const char *control)
611 {
612 	char *dupcontrol = NULL;
613 	dbug_state_object_t *dbug_state_object_p;
614 	flist_object_t *flist_object_p;
615 	register char *scan;
616 	int retval;
617 	char res[100];
618 	int level;
619 
620 	LOCK_THREAD_DATA();
621 
622 	/* error if the control string is NULL */
623 	if (control == NULL) {
624 		strcpy(res, "mdbug: control string is NULL");
625 		goto out;
626 	}
627 
628 	/* turn debugging flag off */
629 	sd_on = FALSE;
630 
631 	/* get the level from the old state if it exists */
632 	if (sd_push == NULL)
633 		level = 0;
634 	else
635 		level = sd_push->s_level;
636 
637 	/* Create a new state */
638 	dbug_state_object_p = dbug_state_create(level);
639 	if (dbug_state_object_p == NULL) {
640 		strcpy(res, "mdbug: out of memory, dbug_state_create");
641 		goto out;
642 	}
643 
644 	/* add it to our list of states and make it the current one */
645 	dbug_state_object_p->s_next = sd_push;
646 	sd_push = dbug_state_object_p;
647 
648 	/* Strip off -# if in the control string */
649 	if ((*control == '-') && (*(control+1) == '#'))
650 		control += 2;
651 
652 	/* make a copy of the control string so we can modify it with strtok */
653 	dupcontrol = strdup(control);
654 	if (dupcontrol == NULL) {
655 		strcpy(res, "mdbug: out of memory, strdup");
656 		goto out;
657 	}
658 
659 	/* parse the control string */
660 	for (scan = mystrtok(dupcontrol, ":");
661 	    scan != NULL;
662 	    scan = mystrtok(NULL, ":")) {
663 		switch (*scan++) {
664 		case 'd':			/* debugging on */
665 			sd_on = TRUE;
666 			dbug_state_object_p->sf_debug = TRUE;
667 			if (*scan++ == ',') {
668 				retval = listparse(scan,
669 				    dbug_state_object_p->s_keywords);
670 				if (retval < 0) {
671 					strcpy(res,
672 					    "mdbug: -d too many keywords");
673 					goto out;
674 				}
675 			}
676 			break;
677 
678 		case 'D': 			/* specify delay value */
679 			dbug_state_object_p->s_delay = 0;
680 			if (*scan++ == ',') {
681 				flist_object_p = flist_create();
682 				retval = listparse(scan, flist_object_p);
683 				if (retval < 0) {
684 					strcpy(res,
685 					    "mdbug: -D too many delays");
686 					goto out;
687 				}
688 				if (flist_object_p->f_count > 0) {
689 					dbug_state_object_p->s_delay =
690 					    delayarg(atoi(
691 					    (char *)fl_top(flist_object_p)));
692 				}
693 				flist_destroy(flist_object_p);
694 			}
695 			break;
696 
697 		case 'f': 			/* list of functions to watch */
698 			if (*scan++ == ',') {
699 				retval = listparse(scan,
700 				    dbug_state_object_p->s_functions);
701 				if (retval < 0) {
702 					strcpy(res,
703 					    "mdbug: -f too many functions");
704 					goto out;
705 				}
706 			}
707 			break;
708 
709 		case 'F': 		/* print file name with dbug output */
710 			dbug_state_object_p->sf_file = TRUE;
711 			break;
712 
713 		case 'i': 		/* print pid with dbug output */
714 			dbug_state_object_p->sf_pid = TRUE;
715 			break;
716 
717 		case 'L':		/* print line nums with dbug output */
718 			dbug_state_object_p->sf_line = TRUE;
719 			break;
720 
721 		case 'n': 		/* print function call depth */
722 			dbug_state_object_p->sf_depth = TRUE;
723 			break;
724 
725 		case 'N': 		/* number each line of dbug output */
726 			dbug_state_object_p->sf_number = TRUE;
727 			break;
728 
729 		case 'o': 		/* specifies output file for dbug */
730 			if (*scan++ == ',') {
731 				flist_object_p = flist_create();
732 				retval = listparse(scan, flist_object_p);
733 				if (retval < 0) {
734 					strcpy(res,
735 					    "mdbug: -o too many output files");
736 					goto out;
737 				}
738 
739 				if (flist_object_p->f_count > 0) {
740 					dbug_state_object_p->s_out_file =
741 					    openfile((char *)
742 					    fl_top(flist_object_p));
743 					if (dbug_state_object_p->s_out_file !=
744 					    NULL)
745 						dbug_state_object_p->sf_didopen
746 						    = 1;
747 				} else
748 					dbug_state_object_p->s_out_file =
749 					    openfile(NULL);
750 				flist_destroy(flist_object_p);
751 			} else
752 				dbug_state_object_p->s_out_file =
753 				    openfile(NULL);
754 			if (dbug_state_object_p->s_out_file == NULL) {
755 				strcpy(res,
756 				    "mdbug: -o cannot open output file");
757 				goto out;
758 			}
759 			break;
760 
761 		case 'p':			/* debug specified processes */
762 			if (*scan++ == ',') {
763 				retval = listparse(scan,
764 				    dbug_state_object_p->s_processes);
765 				if (retval < 0) {
766 					strcpy(res,
767 					    "mdbug: -p too many processes");
768 					goto out;
769 				}
770 			}
771 			break;
772 
773 		case 'P': 		/* print process name on dbug output */
774 			dbug_state_object_p->sf_process = TRUE;
775 			break;
776 
777 		case 'r': 			/* reset indentation to zero */
778 			dbug_state_object_p->s_level = 0;
779 			break;
780 
781 		case 's': 			/* print stack depth on enter */
782 			dbug_state_object_p->sf_stack = TRUE;
783 			break;
784 
785 		case 'R':		/* print time prog has been running */
786 			dbug_state_object_p->sf_time = TRUE;
787 			time(&dbug_state_object_p->s_starttime);
788 			break;
789 
790 		case 'T':		/* print thread information */
791 			dbug_state_object_p->sf_thread = TRUE;
792 			break;
793 
794 		case 't': 		/* print trace of functions called */
795 			dbug_state_object_p->sf_trace = TRUE;
796 			dbug_state_object_p->s_maxdepth = MAXDEPTH;
797 			if (*scan++ == ',') {
798 				flist_object_p = flist_create();
799 				retval = listparse(scan, flist_object_p);
800 				if (retval < 0) {
801 					strcpy(res,
802 					    "mdbug: -t too many traces");
803 					goto out;
804 				}
805 				if (flist_object_p->f_count > 0) {
806 					dbug_state_object_p->s_maxdepth =
807 					    atoi((char *)
808 					    fl_top(flist_object_p));
809 				}
810 				flist_destroy(flist_object_p);
811 			}
812 			break;
813 		}
814 	}
815 
816 out:
817 	/* free up the dupped control string */
818 	free(dupcontrol);
819 
820 	UNLOCK_THREAD_DATA();
821 
822 	/* return result */
823 	return (NULL);
824 }
825 
826 /*
827  *
828  *		db_pop
829  *
830  * Description:
831  *	Pop the debug stack.
832  */
833 void
834 db_pop()
835 {
836 	dbug_state_object_t *dbug_state_object_p;
837 
838 	LOCK_THREAD_DATA();
839 
840 	/* return if no debugging yet */
841 	if (sd_push == NULL)
842 		goto out;
843 
844 	/* get and remove the top item from the list */
845 	dbug_state_object_p = sd_push;
846 	sd_push = dbug_state_object_p->s_next;
847 
848 	/* Delete the item. */
849 	dbug_state_destroy(dbug_state_object_p);
850 
851 	/* get the current top of the stack */
852 	dbug_state_object_p = sd_push;
853 	if (dbug_state_object_p) {
854 		/* See if debugging is turned on */
855 		if (dbug_state_object_p->sf_debug)
856 			sd_on = TRUE;
857 		else
858 			sd_on = FALSE;
859 	}
860 
861 out:;
862 	UNLOCK_THREAD_DATA();
863 }
864 
865 /*
866  *
867  *			db_process
868  *
869  * Description:
870  *	Specifies the name of the process.
871  *	Only the pointer is saved, the string is not copied.
872  * Arguments:
873  *	namep
874  * Returns:
875  * Preconditions:
876  */
877 void
878 db_process(const char *namep)
879 {
880 	thread_data_t *tdp;
881 
882 	strcpy(sd_process, namep);
883 
884 #ifdef STACKINIT
885 	GET_THREAD_DATA_PTR(&tdp);
886 	tdp->td_stackinit = (u_long)this;
887 #endif
888 }
889 
890 /*
891  *
892  *			listparse
893  *
894  * Description:
895  *	parse list of modifiers in debug control string
896  *
897  *	Given pointer to a comma separated list of strings in "cltp",
898  *	parses the list, building a list and returning a pointer to it.
899  *	The original comma separated list is destroyed in the process of
900  *	building the linked list, thus it had better be a duplicate
901  *	if it is important.
902  *
903  *	This routine is only called from db_push.
904  *	Returns 0 for success, -1 for failure.
905  */
906 static int
907 listparse(register char *ctlp, flist_object_t *head)
908 {
909 	char *start;
910 	char *item;
911 
912 	/* scan the string until end */
913 	while (*ctlp != '\0') {
914 		/* See if no more room on the list */
915 		if (fl_space(head) == 0)
916 			return (-1);
917 
918 		/* save the begining of this section */
919 		start = ctlp;
920 
921 		/* loop until the end of the token is found */
922 		while ((*ctlp != '\0') && (*ctlp != ','))
923 			ctlp++;
924 
925 		/* add a string terminator if necessary, for strdup */
926 		if (*ctlp == ',')
927 			*ctlp++ = '\0';
928 
929 		/* make a copy of the string */
930 		item = strdup(start);
931 		if (item == NULL)
932 			return (-1);
933 
934 		/* add it to the list */
935 		fl_push(head, item);
936 	}
937 
938 	return (0);
939 }
940 
941 /*
942  *
943  *			inlist
944  *
945  * Description:
946  *	Tests the string pointed to by "cp" to determine if it is in
947  *	the list pointed to by "flist_object_p".  Linkp points to the first
948  *	link in the list.  If flist_object_p is empty then the string is treated
949  *	as if it is in the list (I.E all strings are in the null list).
950  *	This may seem rather strange at first but leads to the desired
951  *	operation if no list is given.  The net effect is that all
952  *	strings will be accepted when there is no list, and when there
953  *	is a list, only those strings in the list will be accepted.
954  */
955 static boolean
956 inlist(flist_object_t *flist_object_p, const char *cp)
957 {
958 	register boolean accept;
959 	register char *item;
960 
961 	if ((flist_object_p == NULL) || (flist_object_p->f_count == 0) ||
962 		(cp == NULL))
963 		accept = TRUE;
964 	else {
965 		accept = FALSE;
966 
967 		/* walk the list of items */
968 		for (item = (char *)fl_top(flist_object_p);
969 		    item != NULL;
970 		    item = (char *)fl_next(flist_object_p)) {
971 			/* see if a match */
972 			if (strcmp(item, cp) == 0) {
973 				accept = TRUE;
974 				break;
975 			}
976 		}
977 	}
978 
979 	return (accept);
980 }
981 
982 /*
983  *
984  *			dotrace
985  *
986  * Description:
987  *	Checks to see if tracing is enabled based on whether the
988  *	user has specified tracing, the maximum trace depth has
989  *	not yet been reached, the current function is selected,
990  *	and the current process is selected.  Returns TRUE if
991  *	tracing is enabled, FALSE otherwise.
992  */
993 static boolean
994 dotrace(dbug_state_object_t *dbug_state_object_p, const char *func,
995     const char *process)
996 {
997 	boolean trace;
998 
999 	trace = FALSE;
1000 	if (dbug_state_object_p->sf_trace) {
1001 		if (dbug_state_object_p->s_level <=
1002 		    dbug_state_object_p->s_maxdepth) {
1003 			if (inlist(dbug_state_object_p->s_functions, func)) {
1004 				if (inlist(dbug_state_object_p->s_processes,
1005 				    process)) {
1006 					trace = TRUE;
1007 				}
1008 			}
1009 		}
1010 	}
1011 
1012 	return (trace);
1013 }
1014 
1015 /*
1016  *
1017  *			indent
1018  *
1019  * Description:
1020  *	Indent a line to the given level.  Note that this is
1021  *	a simple minded but portable implementation.
1022  *	There are better ways.
1023  *
1024  *	Also, the indent must be scaled by the compile time option
1025  *	of character positions per nesting level.
1026  */
1027 static void
1028 indent(register dbug_state_object_t *dbug_state_object_p, int indent)
1029 {
1030 	register int count;
1031 	char buffer[PRINTBUF];
1032 
1033 	indent *= INDENT;
1034 	for (count = 0;
1035 	    (count < (indent - INDENT)) && (count < (PRINTBUF - 1));
1036 	    count++) {
1037 		if ((count % INDENT) == 0)
1038 			buffer[count] = '|';
1039 		else
1040 			buffer[count] = ' ';
1041 	}
1042 
1043 	buffer[count] = '\0';
1044 	fprintf(dbug_state_object_p->s_out_file, buffer);
1045 	fflush(dbug_state_object_p->s_out_file);
1046 }
1047 
1048 /*
1049  *
1050  *			doprefix
1051  *
1052  * Description:
1053  *	Print prefix common to all debugger output lines, prior to
1054  *	doing indentation if necessary.  Print such information as
1055  *	current process name, current source file name and line number,
1056  *	and current function nesting depth.
1057  */
1058 static void
1059 doprefix(dbug_state_object_t *dbug_state_object_p, int line, long lineno,
1060 	const char *file, const char *process)
1061 {
1062 #if DBUG_UNIX
1063 	if (dbug_state_object_p->sf_pid)
1064 		fprintf(dbug_state_object_p->s_out_file, "%5d: ", getpid());
1065 #endif
1066 
1067 	if (dbug_state_object_p->sf_thread)
1068 		fprintf(dbug_state_object_p->s_out_file, "%5ld: ", thr_self());
1069 
1070 	if (dbug_state_object_p->sf_number)
1071 		fprintf(dbug_state_object_p->s_out_file, "%5ld: ", lineno);
1072 
1073 	if (dbug_state_object_p->sf_process && process)
1074 		fprintf(dbug_state_object_p->s_out_file, "%s: ", process);
1075 
1076 	if (dbug_state_object_p->sf_file)
1077 		fprintf(dbug_state_object_p->s_out_file, "%14s: ", file);
1078 
1079 	if (dbug_state_object_p->sf_line)
1080 		fprintf(dbug_state_object_p->s_out_file, "%5d: ", line);
1081 
1082 	if (dbug_state_object_p->sf_depth)
1083 		fprintf(dbug_state_object_p->s_out_file, "%4d: ",
1084 		dbug_state_object_p->s_level);
1085 
1086 	fflush(dbug_state_object_p->s_out_file);
1087 }
1088 
1089 /*
1090  *
1091  *			openfile
1092  *
1093  * Description:
1094  *	Given name of a new file (or NULL for stdout) opens the file
1095  *	and sets the output stream to the new file.
1096  */
1097 static FILE *
1098 openfile(char *name)
1099 {
1100 	FILE *fp;
1101 	boolean newfile;
1102 
1103 	if (name == NULL)
1104 		return (stdout);
1105 
1106 	if (NOT writable(name))
1107 		return (NULL);
1108 
1109 	/* see if the file already exists */
1110 	if (file_exists(name))
1111 		newfile = FALSE;
1112 	else
1113 		newfile = TRUE;
1114 
1115 	/* open the file */
1116 	fp = fopen(name, "a+");
1117 	if (fp == NULL)
1118 		return (NULL);
1119 
1120 	/*
1121 	 * If the file is newly created, give it away to the user
1122 	 * that started the program.
1123 	 */
1124 	if (newfile) {
1125 		changeowner(name);
1126 	}
1127 	return (fp);
1128 }
1129 
1130 /*
1131  *
1132  *			writable
1133  *
1134  * Description:
1135  *	Because the debugger might be linked in with a program that
1136  *	runs with the set-uid-bit (suid) set, we have to be careful
1137  *	about opening a user named file for debug output.  This consists
1138  *	of checking the file for write access with the real user id,
1139  *	or checking the directory where the file will be created.
1140  *
1141  *	Returns TRUE if the user would normally be allowed write or
1142  *	create access to the named file.  Returns FALSE otherwise.
1143  */
1144 static boolean
1145 writable(char *pathname)
1146 {
1147 #if DBUG_UNIX
1148 
1149 	char *lastslash;
1150 
1151 	boolean granted = FALSE;
1152 	if (file_exists(pathname)) {
1153 		if (file_writable(pathname)) {
1154 			granted = TRUE;
1155 		}
1156 	} else {
1157 		lastslash = strrchr(pathname, '/');
1158 		if (lastslash != NULL) {
1159 			*lastslash = '\0';
1160 		} else {
1161 			pathname = ".";
1162 		}
1163 		if (file_writable(pathname)) {
1164 			granted = TRUE;
1165 		}
1166 		if (lastslash != NULL) {
1167 			*lastslash = '/';
1168 		}
1169 	}
1170 	return (granted);
1171 #else
1172 	return (TRUE);
1173 #endif
1174 }
1175 
1176 /*
1177  *
1178  *			changeowner
1179  *
1180  * Description:
1181  *	For unix systems, change the owner of the newly created debug
1182  *	file to the real owner.  This is strictly for the benefit of
1183  *	programs that are running with the set-user-id bit set.
1184  *
1185  *	Note that at this point, the fact that pathname represents
1186  *	a newly created file has already been established.  If the
1187  *	program that the debugger is linked to is not running with
1188  *	the suid bit set, then this operation is redundant (but
1189  *	harmless).
1190  */
1191 static void
1192 changeowner(char *pathname)
1193 {
1194 #if DBUG_UNIX
1195 	chown(pathname, getuid(), getgid());
1196 #endif
1197 }
1198 
1199 /*
1200  *
1201  *			delayarg
1202  *
1203  * Description:
1204  *	Converts delay argument, given in tenths of a second, to the
1205  *	appropriate numerical argument used by the system to delay
1206  *	that that many tenths of a second.  For example, on the
1207  *	amiga, there is a system call "Delay()" which takes an
1208  *	argument in ticks (50 per second).  On unix, the sleep
1209  *	command takes seconds.  Thus a value of "10", for one
1210  *	second of delay, gets converted to 50 on the amiga, and 1
1211  *	on unix.  Other systems will need to use a timing loop.
1212  */
1213 static int
1214 delayarg(int value)
1215 {
1216 	unsigned int delayarg = 0;
1217 
1218 #if (unix || xenix)
1219 	delayarg = value / 10;		/* Delay is in seconds for sleep () */
1220 #endif
1221 	return (delayarg);
1222 }
1223 
1224 /*
1225  *
1226  *			delay
1227  *
1228  * Description:
1229  *	Implements the delay function.
1230  *
1231  *	A dummy delay stub for systems that do not support delays.
1232  *	With a little work, this can be turned into a timing loop.
1233  */
1234 
1235 static void
1236 delay(u_int xx)
1237 {
1238 #if (unix || xenix)
1239 	sleep(xx);
1240 #endif
1241 #if amiga
1242 	Delay(xx);
1243 #endif
1244 #ifdef __ZTC__
1245 	msleep((u_long)xx);
1246 #endif
1247 }
1248 
1249 /*
1250  *
1251  *			getclock
1252  *
1253  * Description:
1254  *	Returns the time in milliseconds used by this process
1255  *	so far.
1256  */
1257 #if (unix || xenix)
1258 
1259 #include <sys/param.h>
1260 #if BSD4_3 || sun
1261 
1262 #include <sys/time.h>
1263 #include <sys/resource.h>
1264 
1265 static u_long
1266 getclock()
1267 {
1268 #if 0
1269 	struct rusage ru;
1270 
1271 	getrusage(RUSAGE_SELF, &ru);
1272 	return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000));
1273 #else
1274 	return (0);
1275 #endif
1276 }
1277 
1278 #else
1279 
1280 static u_long
1281 getclock()
1282 {
1283 	return (0);
1284 }
1285 
1286 #endif
1287 #endif	/* unix */
1288 
1289 #ifdef MSDOS
1290 static u_long
1291 getclock()
1292 {
1293 	return (clock() * 10);
1294 }
1295 #endif
1296 
1297 /*
1298  *
1299  *			mystrtok
1300  *
1301  * Description:
1302  *	A version of strtok for those systems without it
1303  */
1304 static char *
1305 mystrtok(char *s1, char *s2)
1306 {
1307 	static char *end = NULL;
1308 	register char *rtnval;
1309 
1310 	rtnval = NULL;
1311 	if (s2 != NULL) {
1312 		if (s1 != NULL) {
1313 			end = s1;
1314 			rtnval = mystrtok((char *) NULL, s2);
1315 		} else if (end != NULL) {
1316 			if (*end != '\0') {
1317 				rtnval = end;
1318 				while ((*end != *s2) && (*end != '\0')) {
1319 					end++;
1320 				}
1321 				if (*end != '\0') {
1322 					*end++ = '\0';
1323 				}
1324 			}
1325 		}
1326 	}
1327 
1328 	return (rtnval);
1329 }
1330 
1331 /*
1332  *
1333  *			dbug_thread_exit
1334  *
1335  * Description:
1336  *	Called when a thread exits.
1337  * Arguments:
1338  *	data	pointer to thread specific data
1339  * Returns:
1340  * Preconditions:
1341  */
1342 void
1343 dbug_thread_exit(void *data)
1344 {
1345 	dbug_state_object_t *dbug_state_object_p;
1346 
1347 	LOCK_THREAD_DATA();
1348 
1349 	/* If debugging is off, then nothing else to do */
1350 	if (NOT db_debugon())
1351 		goto out;
1352 
1353 	/* get a pointer to the active state */
1354 	dbug_state_object_p = sd_push;
1355 
1356 	if (dbug_state_object_p->sf_thread) {
1357 		doprefix(dbug_state_object_p, 0, sd_lineno++, "unknown",
1358 		    sd_process);
1359 		indent(dbug_state_object_p, dbug_state_object_p->s_level);
1360 		fprintf(dbug_state_object_p->s_out_file, "thread destroyed\n");
1361 		fflush(dbug_state_object_p->s_out_file);
1362 		delay(dbug_state_object_p->s_delay);
1363 	}
1364 
1365 out:;
1366 	FREE_THREAD_DATA(data);
1367 	UNLOCK_THREAD_DATA();
1368 }
1369 
1370 /*
1371  *
1372  *			doabort
1373  *
1374  * Description:
1375  *	Causes the process to exit immediatly with a core dump.
1376  * Arguments:
1377  * Returns:
1378  * Preconditions:
1379  */
1380 void
1381 doabort()
1382 {
1383 	dbug_state_object_t *dbug_state_object_p = sd_push;
1384 	fflush(dbug_state_object_p->s_out_file);
1385 	for (;;) {
1386 		kill(getpid(), SIGABRT);
1387 		(void) signal(SIGABRT, SIG_DFL);
1388 		(void) sigrelse(SIGABRT);
1389 	}
1390 }
1391 
1392 /*
1393  *
1394  *			dbug_state_create
1395  *
1396  * Description:
1397  *	Constructor for the dbug_state class.
1398  * Arguments:
1399  *	The current level in the call stack.
1400  * Returns:
1401  * Preconditions:
1402  */
1403 dbug_state_object_t *
1404 dbug_state_create(int level)
1405 {
1406 	dbug_state_object_t *dbug_state_object_p;
1407 
1408 	dbug_state_object_p =
1409 	    (dbug_state_object_t *)calloc(sizeof (dbug_state_object_t), 1);
1410 
1411 	if (dbug_state_object_p == NULL)
1412 		doabort();
1413 
1414 	dbug_state_object_p->sf_trace = 0;
1415 	dbug_state_object_p->sf_debug = 0;
1416 	dbug_state_object_p->sf_file = 0;
1417 	dbug_state_object_p->sf_line = 0;
1418 	dbug_state_object_p->sf_depth = 0;
1419 	dbug_state_object_p->sf_process = 0;
1420 	dbug_state_object_p->sf_number = 0;
1421 	dbug_state_object_p->sf_pid = 0;
1422 	dbug_state_object_p->sf_stack = 0;
1423 	dbug_state_object_p->sf_time = 0;
1424 	dbug_state_object_p->sf_didopen = 0;
1425 	dbug_state_object_p->sf_thread = 0;
1426 	dbug_state_object_p->s_maxdepth = MAXDEPTH;
1427 	dbug_state_object_p->s_delay = 0;
1428 	dbug_state_object_p->s_level = level;
1429 	dbug_state_object_p->s_starttime = 0;
1430 	dbug_state_object_p->s_out_file = stderr;
1431 	dbug_state_object_p->s_next = NULL;
1432 	return (dbug_state_object_p);
1433 }
1434 
1435 /*
1436  *
1437  *			dbug_state_destroy
1438  *
1439  * Description:
1440  *	Destructor for the dbug_state class.
1441  * Arguments:
1442  * Returns:
1443  * Preconditions:
1444  */
1445 void
1446 dbug_state_destroy(dbug_state_object_t *dbug_state_object_p)
1447 {
1448 	if (dbug_state_object_p->sf_didopen)
1449 		fclose(dbug_state_object_p->s_out_file);
1450 	free(dbug_state_object_p);
1451 }
1452 
1453 /*
1454  *
1455  *		db_debugon
1456  *
1457  * Description:
1458  *   Returns 1 if debugging is currently enabled, 0 otherwise.
1459  * Arguments:
1460  * Returns:
1461  * Errors:
1462  * Preconditions:
1463  */
1464 
1465 int
1466 db_debugon(dbug_object_p)
1467 dbug_object_t *dbug_object_p;
1468 {
1469 	return (sd_on);
1470 }
1471 boolean
1472 file_exists(const char *pathname)
1473 {
1474 	return (access(pathname, F_OK) == 0);
1475 }
1476 boolean
1477 file_writable(const char *pathname)
1478 {
1479 	return (access(pathname, W_OK) == 0);
1480 }
1481 dbug_object_t *
1482 db_get_dbug_object_p()
1483 {
1484 	thread_data_t *tdp;
1485 
1486 	GET_THREAD_DATA_PTR(&tdp);
1487 	return (tdp->td_first);
1488 }
1489 #endif /* DBUG_OFF */
1490