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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Copyright 2020 Joyent, Inc.
  28  */
  29 
  30 /*
  31  * Support for ::set dcmd.  The +/-o option processing code is provided in a
  32  * stand-alone function so it can be used by the command-line option processing
  33  * code in mdb_main.c.  This facility provides an easy way for us to add more
  34  * configurable options without having to add a new dcmd each time.
  35  */
  36 
  37 #include <mdb/mdb_target.h>
  38 #include <mdb/mdb_modapi.h>
  39 #include <mdb/mdb_string.h>
  40 #include <mdb/mdb_debug.h>
  41 #include <mdb/mdb.h>
  42 
  43 /*ARGSUSED*/
  44 static int
  45 opt_set_mflags(int enable, uint_t bits, const char *arg)
  46 {
  47         mdb.m_flags = (mdb.m_flags & ~bits) | (bits & -enable);
  48         return (1);
  49 }
  50 
  51 /*ARGSUSED*/
  52 static int
  53 opt_set_tflags(int enable, uint_t bits, const char *arg)
  54 {
  55         mdb.m_tgtflags = (mdb.m_tgtflags & ~bits) | (bits & -enable);
  56         return (1);
  57 }
  58 
  59 static int
  60 opt_pager(int enable, uint_t bits, const char *arg)
  61 {
  62         if (enable)
  63                 mdb_iob_setflags(mdb.m_out, MDB_IOB_PGENABLE);
  64         else
  65                 mdb_iob_clrflags(mdb.m_out, MDB_IOB_PGENABLE);
  66 
  67         return (opt_set_mflags(enable, bits, arg));
  68 }
  69 
  70 static int
  71 opt_adb(int enable, uint_t bits, const char *arg)
  72 {
  73         if (enable)
  74                 (void) mdb_set_prompt("");
  75         else if (mdb.m_promptlen == 0)
  76                 (void) mdb_set_prompt("> ");
  77 
  78         (void) opt_pager(1 - enable, MDB_FL_PAGER, arg);
  79         return (opt_set_mflags(enable, bits, arg));
  80 }
  81 
  82 /*ARGSUSED*/
  83 static int
  84 opt_armemlim(int enable, uint_t bits, const char *arg)
  85 {
  86         if (strisnum(arg)) {
  87                 mdb.m_armemlim = strtoi(arg);
  88                 return (1);
  89         }
  90         if (strcmp(arg, "none") == 0) {
  91                 mdb.m_armemlim = MDB_ARR_NOLIMIT;
  92                 return (1);
  93         }
  94         return (0);
  95 }
  96 
  97 /*ARGSUSED*/
  98 static int
  99 opt_arstrlim(int enable, uint_t bits, const char *arg)
 100 {
 101         if (strisnum(arg)) {
 102                 mdb.m_arstrlim = strtoi(arg);
 103                 return (1);
 104         }
 105         if (strcmp(arg, "none") == 0) {
 106                 mdb.m_arstrlim = MDB_ARR_NOLIMIT;
 107                 return (1);
 108         }
 109         return (0);
 110 }
 111 
 112 /*ARGSUSED*/
 113 static int
 114 opt_exec_mode(int enable, uint_t bits, const char *arg)
 115 {
 116         if (strcmp(arg, "ask") == 0) {
 117                 mdb.m_execmode = MDB_EM_ASK;
 118                 return (1);
 119         } else if (strcmp(arg, "stop") == 0) {
 120                 mdb.m_execmode = MDB_EM_STOP;
 121                 return (1);
 122         } else if (strcmp(arg, "follow") == 0) {
 123                 mdb.m_execmode = MDB_EM_FOLLOW;
 124                 return (1);
 125         }
 126         return (0);
 127 }
 128 
 129 /*ARGSUSED*/
 130 static int
 131 opt_fork_mode(int enable, uint_t bits, const char *arg)
 132 {
 133         if (strcmp(arg, "ask") == 0) {
 134                 mdb.m_forkmode = MDB_FM_ASK;
 135                 return (1);
 136         } else if (strcmp(arg, "parent") == 0) {
 137                 mdb.m_forkmode = MDB_FM_PARENT;
 138                 return (1);
 139         } else if (strcmp(arg, "child") == 0) {
 140                 mdb.m_forkmode = MDB_FM_CHILD;
 141                 return (1);
 142         }
 143         return (0);
 144 }
 145 
 146 /*ARGSUSED*/
 147 static int
 148 opt_set_term(int enable, uint_t bits, const char *arg)
 149 {
 150         mdb.m_termtype = strdup(arg);
 151         mdb.m_flags &= ~MDB_FL_TERMGUESS;
 152 
 153         return (1);
 154 }
 155 
 156 int
 157 mdb_set_options(const char *s, int enable)
 158 {
 159         static const struct opdesc {
 160                 const char *opt_name;
 161                 int (*opt_func)(int, uint_t, const char *);
 162                 uint_t opt_bits;
 163         } opdtab[] = {
 164                 { "adb", opt_adb, MDB_FL_REPLAST | MDB_FL_NOMODS | MDB_FL_ADB },
 165                 { "array_mem_limit", opt_armemlim, 0 },
 166                 { "array_str_limit", opt_arstrlim, 0 },
 167                 { "follow_exec_mode", opt_exec_mode, 0 },
 168                 { "follow_fork_mode", opt_fork_mode, 0 },
 169                 { "pager", opt_pager, MDB_FL_PAGER },
 170                 { "term", opt_set_term, 0 },
 171 
 172                 { "autowrap", opt_set_mflags, MDB_FL_AUTOWRAP },
 173                 { "ignoreeof", opt_set_mflags, MDB_FL_IGNEOF },
 174                 { "repeatlast", opt_set_mflags, MDB_FL_REPLAST },
 175                 { "latest", opt_set_mflags, MDB_FL_LATEST },
 176                 { "noctf", opt_set_mflags, MDB_FL_NOCTF },
 177                 { "nomods", opt_set_mflags, MDB_FL_NOMODS },
 178                 { "showlmid", opt_set_mflags, MDB_FL_SHOWLMID },
 179                 { "lmraw", opt_set_mflags, MDB_FL_LMRAW },
 180                 { "stop_on_bpt_nosym", opt_set_mflags, MDB_FL_BPTNOSYMSTOP },
 181                 { "write_readback", opt_set_mflags, MDB_FL_READBACK },
 182 
 183                 { "allow_io_access", opt_set_tflags, MDB_TGT_F_ALLOWIO },
 184                 { "nostop", opt_set_tflags, MDB_TGT_F_NOSTOP },
 185                 { NULL, NULL, 0 }
 186         };
 187 
 188         const struct opdesc *opp;
 189         char *buf = strdup(s);
 190         char *opt, *arg;
 191         int status = 1;
 192 
 193         for (opt = strtok(buf, ","); opt != NULL; opt = strtok(NULL, ",")) {
 194                 if ((arg = strchr(opt, '=')) != NULL)
 195                         *arg++ = '\0';
 196 
 197                 for (opp = opdtab; opp->opt_name != NULL; opp++) {
 198                         if (strcmp(opt, opp->opt_name) == 0) {
 199                                 if (opp->opt_bits != 0 && arg != NULL) {
 200                                         mdb_warn("option does not accept an "
 201                                             "argument -- %s\n", opt);
 202                                         status = 0;
 203                                 } else if (opp->opt_bits == 0 && arg == NULL) {
 204                                         mdb_warn("option requires an argument "
 205                                             "-- %s\n", opt);
 206                                         status = 0;
 207                                 } else if (opp->opt_func(enable != 0,
 208                                     opp->opt_bits, arg) == 0) {
 209                                         mdb_warn("invalid argument for option "
 210                                             "%s -- %s\n", opt, arg);
 211                                         status = 0;
 212                                 }
 213                                 break;
 214                         }
 215                 }
 216 
 217                 if (opp->opt_name == NULL) {
 218                         mdb_warn("invalid debugger option -- %s\n", opt);
 219                         status = 0;
 220                 }
 221         }
 222 
 223         mdb_free(buf, strlen(s) + 1);
 224         return (status);
 225 }
 226 
 227 static void
 228 print_path(const char **path, int indent)
 229 {
 230         if (path != NULL && *path != NULL) {
 231                 for (mdb_printf("%s\n", *path++); *path != NULL; path++)
 232                         mdb_printf("%*s%s\n", indent, " ", *path);
 233         }
 234         mdb_printf("\n");
 235 }
 236 
 237 #define LABEL_INDENT    26
 238 
 239 static void
 240 print_properties(void)
 241 {
 242         int tflags = mdb_tgt_getflags(mdb.m_target);
 243         uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_AUTOWRAP;
 244 
 245         mdb_iob_clrflags(mdb.m_out, MDB_IOB_AUTOWRAP);
 246         mdb_printf("\n  macro path: ");
 247         print_path(mdb.m_ipath, 14);
 248         mdb_printf(" module path: ");
 249         print_path(mdb.m_lpath, 14);
 250         mdb_iob_setflags(mdb.m_out, oflags);
 251 
 252         mdb_printf("%*s %lr (%s)\n", LABEL_INDENT, "symbol matching distance:",
 253             mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
 254 
 255         mdb_printf("%*s ", LABEL_INDENT, "array member print limit:");
 256         if (mdb.m_armemlim != MDB_ARR_NOLIMIT)
 257                 mdb_printf("%u\n", mdb.m_armemlim);
 258         else
 259                 mdb_printf("none\n");
 260 
 261         mdb_printf(" array string print limit: ");
 262         if (mdb.m_arstrlim != MDB_ARR_NOLIMIT)
 263                 mdb_printf("%u\n", mdb.m_arstrlim);
 264         else
 265                 mdb_printf("none\n");
 266 
 267         mdb_printf("%*s \"%s\"\n", LABEL_INDENT, "command prompt:",
 268             mdb.m_prompt);
 269 
 270         mdb_printf("%*s ", LABEL_INDENT, "debugger options:");
 271         (void) mdb_inc_indent(LABEL_INDENT + 1);
 272 
 273         /*
 274          * The ::set output implicitly relies on "autowrap" being enabled, so
 275          * we enable it for the duration of the command.
 276          */
 277         oflags = mdb.m_flags;
 278         mdb_iob_set_autowrap(mdb.m_out);
 279 
 280         mdb_printf("follow_exec_mode=");
 281         switch (mdb.m_execmode) {
 282         case MDB_EM_ASK:
 283                 mdb_printf("ask");
 284                 break;
 285         case MDB_EM_STOP:
 286                 mdb_printf("stop");
 287                 break;
 288         case MDB_EM_FOLLOW:
 289                 mdb_printf("follow");
 290                 break;
 291         }
 292 
 293 #define COMMAFLAG(name) { mdb_printf(", "); mdb_printf(name); }
 294 
 295         COMMAFLAG("follow_fork_mode");
 296         switch (mdb.m_forkmode) {
 297         case MDB_FM_ASK:
 298                 mdb_printf("ask");
 299                 break;
 300         case MDB_FM_PARENT:
 301                 mdb_printf("parent");
 302                 break;
 303         case MDB_FM_CHILD:
 304                 mdb_printf("child");
 305                 break;
 306         }
 307 
 308         if (mdb.m_flags & MDB_FL_ADB)
 309                 COMMAFLAG("adb");
 310         if (oflags & MDB_FL_AUTOWRAP)
 311                 COMMAFLAG("autowrap");
 312         if (mdb.m_flags & MDB_FL_IGNEOF)
 313                 COMMAFLAG("ignoreeof");
 314         if (mdb.m_flags & MDB_FL_LMRAW)
 315                 COMMAFLAG("lmraw");
 316         if (mdb.m_flags & MDB_FL_PAGER)
 317                 COMMAFLAG("pager");
 318         if (mdb.m_flags & MDB_FL_REPLAST)
 319                 COMMAFLAG("repeatlast");
 320         if (mdb.m_flags & MDB_FL_SHOWLMID)
 321                 COMMAFLAG("showlmid");
 322         if (mdb.m_flags & MDB_FL_BPTNOSYMSTOP)
 323                 COMMAFLAG("stop_on_bpt_nosym");
 324         if (mdb.m_flags & MDB_FL_READBACK)
 325                 COMMAFLAG("write_readback");
 326         mdb_printf("\n");
 327         (void) mdb_dec_indent(LABEL_INDENT + 1);
 328 
 329         mdb_printf("%*s ", LABEL_INDENT, "target options:");
 330         (void) mdb_inc_indent(LABEL_INDENT + 1);
 331 
 332         if (tflags & MDB_TGT_F_RDWR)
 333                 mdb_printf("read-write");
 334         else
 335                 mdb_printf("read-only");
 336         if (tflags & MDB_TGT_F_ALLOWIO)
 337                 COMMAFLAG("allow-io-access");
 338         if (tflags & MDB_TGT_F_FORCE)
 339                 COMMAFLAG("force-attach");
 340         if (tflags & MDB_TGT_F_PRELOAD)
 341                 COMMAFLAG("preload-syms");
 342         if (tflags & MDB_TGT_F_NOLOAD)
 343                 COMMAFLAG("no-load-objs");
 344         if (tflags & MDB_TGT_F_NOSTOP)
 345                 COMMAFLAG("no-stop");
 346         mdb_printf("\n");
 347         (void) mdb_dec_indent(LABEL_INDENT + 1);
 348 
 349         mdb.m_flags = oflags;
 350 }
 351 
 352 /*ARGSUSED*/
 353 int
 354 cmd_set(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 355 {
 356         const char *opt_I = NULL, *opt_L = NULL, *opt_P = NULL, *opt_o = NULL;
 357         const char *opt_plus_o = NULL, *opt_D = NULL;
 358         uint_t opt_w = FALSE, opt_plus_w = FALSE, opt_W = FALSE;
 359         uint_t opt_plus_W = FALSE, opt_F = FALSE;
 360         uintptr_t opt_s = (uintptr_t)(long)-1;
 361 
 362         int tflags = 0;
 363         int i;
 364 
 365         if (flags & DCMD_ADDRSPEC)
 366                 return (DCMD_USAGE);
 367 
 368         /*
 369          * If no options are specified, print out the current set of target
 370          * and debugger properties that can be modified with ::set.
 371          */
 372         if (argc == 0) {
 373                 print_properties();
 374                 return (DCMD_OK);
 375         }
 376 
 377         while ((i = mdb_getopts(argc, argv,
 378             'F', MDB_OPT_SETBITS, TRUE, &opt_F,
 379             'I', MDB_OPT_STR, &opt_I,
 380             'L', MDB_OPT_STR, &opt_L,
 381             'P', MDB_OPT_STR, &opt_P,
 382             'o', MDB_OPT_STR, &opt_o,
 383             's', MDB_OPT_UINTPTR, &opt_s,
 384             'w', MDB_OPT_SETBITS, TRUE, &opt_w,
 385             'W', MDB_OPT_SETBITS, TRUE, &opt_W,
 386             'D', MDB_OPT_STR, &opt_D, NULL)) != argc) {
 387                 uint_t n = 1;
 388 
 389                 argv += i; /* skip past args we processed */
 390                 argc -= i; /* adjust argc */
 391 
 392                 if (argv[0].a_type != MDB_TYPE_STRING)
 393                         return (DCMD_USAGE);
 394 
 395                 if (strcmp(argv->a_un.a_str, "+W") == 0)
 396                         opt_plus_W = TRUE;
 397                 else if (strcmp(argv->a_un.a_str, "+w") == 0)
 398                         opt_plus_w = TRUE;
 399                 else if (strcmp(argv->a_un.a_str, "+o") == 0 &&
 400                     argc >= 2 && argv[1].a_type == MDB_TYPE_STRING) {
 401                         opt_plus_o = argv[1].a_un.a_str;
 402                         n = 2;
 403                 } else
 404                         return (DCMD_USAGE);
 405 
 406                 /* remove the flag and possible argument */
 407                 argv += n;
 408                 argc -= n;
 409         }
 410 
 411         if ((opt_w && opt_plus_w) || (opt_W && opt_plus_W))
 412                 return (DCMD_USAGE);
 413 
 414         /*
 415          * Handle -w, -/+W and -F first: as these options modify the target,
 416          * they are the only ::set changes that can potentially fail.  We'll
 417          * use these flags to modify a copy of the target's t_flags, which we'll
 418          * then pass to the target's setflags op.  This allows the target to
 419          * detect newly-set and newly-cleared flags by comparing the passed
 420          * value to the current t_flags.
 421          */
 422         tflags = mdb_tgt_getflags(mdb.m_target);
 423 
 424         if (opt_w)
 425                 tflags |= MDB_TGT_F_RDWR;
 426         if (opt_plus_w)
 427                 tflags &= ~MDB_TGT_F_RDWR;
 428         if (opt_W)
 429                 tflags |= MDB_TGT_F_ALLOWIO;
 430         if (opt_plus_W)
 431                 tflags &= ~MDB_TGT_F_ALLOWIO;
 432         if (opt_F)
 433                 tflags |= MDB_TGT_F_FORCE;
 434 
 435         if (tflags != mdb_tgt_getflags(mdb.m_target) &&
 436             mdb_tgt_setflags(mdb.m_target, tflags) == -1)
 437                 return (DCMD_ERR);
 438 
 439         /*
 440          * Now handle everything that either can't fail or we don't care if
 441          * it does.  Note that we handle +/-o first in case another option
 442          * overrides a change made implicity by a +/-o argument (e.g. -P).
 443          */
 444         if (opt_o != NULL)
 445                 (void) mdb_set_options(opt_o, TRUE);
 446         if (opt_plus_o != NULL)
 447                 (void) mdb_set_options(opt_plus_o, FALSE);
 448         if (opt_I != NULL) {
 449 #ifdef _KMDB
 450                 mdb_warn("macro path cannot be set under kmdb\n");
 451 #else
 452                 mdb_set_ipath(opt_I);
 453 #endif
 454         }
 455         if (opt_L != NULL)
 456                 mdb_set_lpath(opt_L);
 457         if (opt_P != NULL)
 458                 (void) mdb_set_prompt(opt_P);
 459         if (opt_s != (uintptr_t)-1)
 460                 mdb.m_symdist = (size_t)opt_s;
 461         if (opt_D != NULL && (i = mdb_dstr2mode(opt_D)) != MDB_DBG_HELP)
 462                 mdb_dmode((uint_t)i);
 463 
 464         return (DCMD_OK);
 465 }