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