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 */