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