Mercurial > illumos > onarm
comparison usr/src/cmd/fs.d/cachefs/mdbug/dbug.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c9caec207d52 |
---|---|
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 "@(#)dbug.c 1.8 05/09/21 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 */ |