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