xref: /titanic_44/usr/src/cmd/fs.d/cachefs/mdbug/dbug.c (revision f841f6ad96ea6675d6c6b35c749eaac601799fdf)
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
1286 getclock()
1287 {
1288 	return (0);
1289 }
1290 
1291 #endif
1292 #endif	/* unix */
1293 
1294 #ifdef MSDOS
1295 static ulong_t
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 *
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
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
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 *
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
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
1471 db_debugon(dbug_object_p)
1472 dbug_object_t *dbug_object_p;
1473 {
1474 	return (sd_on);
1475 }
1476 boolean
1477 file_exists(const char *pathname)
1478 {
1479 	return (access(pathname, F_OK) == 0);
1480 }
1481 boolean
1482 file_writable(const char *pathname)
1483 {
1484 	return (access(pathname, W_OK) == 0);
1485 }
1486 dbug_object_t *
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