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 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright (c) 2012, Joyent, Inc. All rights reserved. 26 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. 27 */ 28 29 #include <mdb/mdb_modapi.h> 30 #include <mdb/mdb_macalias.h> 31 #include <mdb/mdb_fmt.h> 32 #include <mdb/mdb_err.h> 33 #include <mdb/mdb_help.h> 34 #include <mdb/mdb.h> 35 #include <regex.h> 36 37 const char _mdb_help[] = 38 "\nEach debugger command in %s is structured as follows:\n\n" 39 " [ address [, count]] verb [ arguments ... ]\n" 40 " ^ ^ ^ ^\n" 41 " the start --+ | | +-- arguments are strings which can be\n" 42 " address can be an | | quoted using \"\" or '' or\n" 43 " expression | | expressions enclosed in $[ ]\n" 44 " | |\n" 45 " the repeat count --+ +--------- the verb is a name which begins\n" 46 " is also an expression with either $, :, or ::. it can also\n" 47 " be a format specifier (/ \\ ? or =)\n\n" 48 "For information on debugger commands (dcmds) and walkers, type:\n\n" 49 " ::help cmdname ... for more detailed information on a command\n" 50 " ::dcmds ... for a list of dcmds and their descriptions\n" 51 " ::walkers ... for a list of walkers and their descriptions\n" 52 " ::dmods -l ... for a list of modules and their dcmds and walkers\n" 53 " ::formats ... for a list of format characters for / \\ ? and =\n\n" 54 "For information on command-line options, type:\n\n" 55 " $ %s -? ... in your shell for a complete list of options\n\n"; 56 57 /*ARGSUSED*/ 58 static int 59 print_dcmd(mdb_var_t *v, void *ignored) 60 { 61 const mdb_idcmd_t *idcp = mdb_nv_get_cookie(v); 62 if (idcp->idc_descr != NULL) 63 mdb_printf(" dcmd %-20s - %s\n", 64 idcp->idc_name, idcp->idc_descr); 65 return (0); 66 } 67 68 /*ARGSUSED*/ 69 static int 70 print_walk(mdb_var_t *v, void *ignored) 71 { 72 const mdb_iwalker_t *iwp = mdb_nv_get_cookie(v); 73 if (iwp->iwlk_descr != NULL) 74 mdb_printf(" walk %-20s - %s\n", 75 iwp->iwlk_name, iwp->iwlk_descr); 76 return (0); 77 } 78 79 /*ARGSUSED*/ 80 static int 81 print_dmod_long(mdb_var_t *v, void *ignored) 82 { 83 mdb_module_t *mod = mdb_nv_get_cookie(v); 84 85 mdb_printf("\n%<u>%-70s%</u>\n", mod->mod_name); 86 87 if (mod->mod_tgt_ctor != NULL) { 88 mdb_printf(" ctor 0x%-18lx - target constructor\n", 89 (ulong_t)mod->mod_tgt_ctor); 90 } 91 92 if (mod->mod_dis_ctor != NULL) { 93 mdb_printf(" ctor 0x%-18lx - disassembler constructor\n", 94 (ulong_t)mod->mod_dis_ctor); 95 } 96 97 mdb_nv_sort_iter(&mod->mod_dcmds, print_dcmd, NULL, UM_SLEEP | UM_GC); 98 mdb_nv_sort_iter(&mod->mod_walkers, print_walk, NULL, UM_SLEEP | UM_GC); 99 100 return (0); 101 } 102 103 /*ARGSUSED*/ 104 static int 105 print_dmod_short(mdb_var_t *v, void *ignored) 106 { 107 mdb_printf("%s\n", mdb_nv_get_name(v)); 108 return (0); 109 } 110 111 /*ARGSUSED*/ 112 int 113 cmd_dmods(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 114 { 115 int (*func)(mdb_var_t *, void *); 116 uint_t opt_l = FALSE; 117 mdb_var_t *v; 118 int i; 119 120 if (flags & DCMD_ADDRSPEC) 121 return (DCMD_USAGE); 122 123 i = mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, TRUE, &opt_l, NULL); 124 func = opt_l ? print_dmod_long : print_dmod_short; 125 126 if (i != argc) { 127 if (argc - i != 1 || argv[i].a_type != MDB_TYPE_STRING) 128 return (DCMD_USAGE); 129 130 v = mdb_nv_lookup(&mdb.m_modules, argv[i].a_un.a_str); 131 132 if (v == NULL) 133 mdb_warn("%s module not loaded\n", argv[i].a_un.a_str); 134 else 135 (void) func(v, NULL); 136 137 } else 138 mdb_nv_sort_iter(&mdb.m_modules, func, NULL, UM_SLEEP | UM_GC); 139 140 return (DCMD_OK); 141 } 142 143 #define FILTER_NAMEONLY 0x1 144 145 typedef struct filter_data { 146 const char *pattern; 147 int flags; 148 #ifndef _KMDB 149 regex_t reg; 150 #endif 151 } filter_data_t; 152 153 static void 154 filter_help(void) 155 { 156 mdb_printf("Options:\n" 157 " -n Match only the name, not the description.\n" 158 #ifdef _KMDB 159 " pattern Substring to match against name/description." 160 #else 161 " pattern RE to match against name/description." 162 #endif 163 "\n"); 164 } 165 166 void 167 cmd_dcmds_help(void) 168 { 169 mdb_printf( 170 "List all of the dcmds that are currently available. If a pattern\n" 171 "is provided then list only the commands that\n" 172 #ifdef _KMDB 173 "contain the provided substring." 174 #else 175 "match the provided regular expression." 176 #endif 177 "\n"); 178 filter_help(); 179 } 180 181 void 182 cmd_walkers_help(void) 183 { 184 mdb_printf( 185 "List all of the walkers that are currently available. If a\n" 186 "pattern is provided then list only the walkers that\n" 187 #ifdef _KMDB 188 "contain the provided substring." 189 #else 190 "match the provided regular expression." 191 #endif 192 "\n"); 193 filter_help(); 194 } 195 196 static int 197 print_wdesc(mdb_var_t *v, void *data) 198 { 199 filter_data_t *f = data; 200 mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v)); 201 const char *name = mdb_nv_get_name(v); 202 boolean_t output = FALSE; 203 204 if (name == NULL || iwp->iwlk_descr == NULL) 205 return (0); 206 207 if (f->pattern == NULL) { 208 output = TRUE; 209 } else { 210 #ifdef _KMDB 211 /* 212 * kmdb doesn't have access to the reg* functions, so we fall 213 * back to strstr. 214 */ 215 if (strstr(name, f->pattern) != NULL || 216 (!(f->flags & FILTER_NAMEONLY) && 217 strstr(iwp->iwlk_descr, f->pattern) != NULL)) 218 output = TRUE; 219 #else 220 regmatch_t pmatch; 221 222 if (regexec(&f->reg, name, 1, &pmatch, 0) == 0 || 223 (!(f->flags & FILTER_NAMEONLY) && 224 regexec(&f->reg, iwp->iwlk_descr, 1, &pmatch, 0) == 0)) 225 output = TRUE; 226 #endif 227 228 } 229 230 if (output) 231 mdb_printf("%-24s - %s\n", name, iwp->iwlk_descr); 232 return (0); 233 } 234 235 /*ARGSUSED*/ 236 int 237 cmd_walkers(uintptr_t addr __unused, uint_t flags, int argc, 238 const mdb_arg_t *argv) 239 { 240 filter_data_t f; 241 int i; 242 #ifndef _KMDB 243 int err; 244 #endif 245 246 if (flags & DCMD_ADDRSPEC) 247 return (DCMD_USAGE); 248 249 f.pattern = NULL; 250 f.flags = 0; 251 252 i = mdb_getopts(argc, argv, 253 'n', MDB_OPT_SETBITS, FILTER_NAMEONLY, &f.flags, 254 NULL); 255 256 argc -= i; 257 argv += i; 258 259 if (argc == 1) { 260 if (argv->a_type != MDB_TYPE_STRING) 261 return (DCMD_USAGE); 262 f.pattern = argv->a_un.a_str; 263 264 #ifndef _KMDB 265 if ((err = regcomp(&f.reg, f.pattern, REG_EXTENDED)) != 0) { 266 size_t nbytes; 267 char *buf; 268 269 nbytes = regerror(err, &f.reg, NULL, 0); 270 buf = mdb_alloc(nbytes + 1, UM_SLEEP | UM_GC); 271 (void) regerror(err, &f.reg, buf, nbytes); 272 mdb_warn("%s\n", buf); 273 274 return (DCMD_ERR); 275 } 276 #endif 277 } else if (argc != 0) { 278 return (DCMD_USAGE); 279 } 280 281 mdb_nv_sort_iter(&mdb.m_walkers, print_wdesc, &f, UM_SLEEP | UM_GC); 282 return (DCMD_OK); 283 } 284 285 static int 286 print_ddesc(mdb_var_t *v, void *data) 287 { 288 filter_data_t *f = data; 289 mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v)); 290 const char *name = mdb_nv_get_name(v); 291 boolean_t output = FALSE; 292 293 if (name == NULL || idcp->idc_descr == NULL) 294 return (0); 295 296 if (f->pattern == NULL) { 297 output = TRUE; 298 } else { 299 #ifdef _KMDB 300 /* 301 * kmdb doesn't have access to the reg* functions, so we fall 302 * back to strstr. 303 */ 304 if (strstr(name, f->pattern) != NULL || 305 (!(f->flags & FILTER_NAMEONLY) && 306 strstr(idcp->idc_descr, f->pattern) != NULL)) 307 output = TRUE; 308 #else 309 regmatch_t pmatch; 310 311 if (regexec(&f->reg, name, 1, &pmatch, 0) == 0 || 312 (!(f->flags & FILTER_NAMEONLY) && 313 regexec(&f->reg, idcp->idc_descr, 1, &pmatch, 0) == 0)) 314 output = TRUE; 315 #endif 316 317 } 318 319 if (output) 320 mdb_printf("%-24s - %s\n", name, idcp->idc_descr); 321 return (0); 322 } 323 324 /*ARGSUSED*/ 325 int 326 cmd_dcmds(uintptr_t addr __unused, uint_t flags, int argc, 327 const mdb_arg_t *argv) 328 { 329 filter_data_t f; 330 int i; 331 #ifndef _KMDB 332 int err; 333 #endif 334 335 if (flags & DCMD_ADDRSPEC) 336 return (DCMD_USAGE); 337 338 f.pattern = NULL; 339 f.flags = 0; 340 341 i = mdb_getopts(argc, argv, 342 'n', MDB_OPT_SETBITS, FILTER_NAMEONLY, &f.flags, 343 NULL); 344 345 argc -= i; 346 argv += i; 347 348 if (argc == 1) { 349 if (argv->a_type != MDB_TYPE_STRING) 350 return (DCMD_USAGE); 351 f.pattern = argv->a_un.a_str; 352 353 #ifndef _KMDB 354 if ((err = regcomp(&f.reg, f.pattern, REG_EXTENDED)) != 0) { 355 size_t nbytes; 356 char *buf; 357 358 nbytes = regerror(err, &f.reg, NULL, 0); 359 buf = mdb_alloc(nbytes + 1, UM_SLEEP | UM_GC); 360 (void) regerror(err, &f.reg, buf, nbytes); 361 mdb_warn("%s\n", buf); 362 363 return (DCMD_ERR); 364 } 365 #endif 366 } else if (argc != 0) { 367 return (DCMD_USAGE); 368 } 369 370 mdb_nv_sort_iter(&mdb.m_dcmds, print_ddesc, &f, UM_SLEEP | UM_GC); 371 return (DCMD_OK); 372 } 373 374 /*ARGSUSED*/ 375 int 376 cmd_help(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 377 { 378 const char *prefix, *usage; 379 const mdb_idcmd_t *idcp; 380 381 if ((flags & DCMD_ADDRSPEC) || argc > 1) 382 return (DCMD_USAGE); 383 384 if (argc == 0) { 385 mdb_printf(_mdb_help, mdb.m_pname, mdb.m_pname); 386 return (DCMD_OK); 387 } 388 389 if (argv->a_type != MDB_TYPE_STRING) { 390 warn("expected string argument\n"); 391 return (DCMD_USAGE); 392 } 393 394 if (strncmp(argv->a_un.a_str, "::", 2) == 0) 395 idcp = mdb_dcmd_lookup(argv->a_un.a_str + 2); 396 else 397 idcp = mdb_dcmd_lookup(argv->a_un.a_str); 398 399 if (idcp == NULL) { 400 mdb_warn("unknown command: %s\n", argv->a_un.a_str); 401 return (DCMD_ERR); 402 } 403 404 prefix = strchr(":$=/\\?>", idcp->idc_name[0]) ? "" : "::"; 405 usage = idcp->idc_usage ? idcp->idc_usage : ""; 406 407 mdb_printf("\n%<b>NAME%</b>\n %s - %s\n\n", 408 idcp->idc_name, idcp->idc_descr); 409 410 mdb_printf("%<b>SYNOPSIS%</b>\n "); 411 if (usage[0] == '?') { 412 mdb_printf("[ %<u>addr%</u> ] "); 413 usage++; 414 } else if (usage[0] == ':') { 415 mdb_printf("%<u>addr%</u> "); 416 usage++; 417 } 418 419 mdb_printf("%s%s %s\n\n", prefix, idcp->idc_name, usage); 420 421 if (idcp->idc_help != NULL) { 422 mdb_printf("%<b>DESCRIPTION%</b>\n"); 423 (void) mdb_inc_indent(2); 424 idcp->idc_help(); 425 (void) mdb_dec_indent(2); 426 mdb_printf("\n"); 427 } 428 429 /* 430 * For now, modules that are built-in mark their interfaces Evolving 431 * (documented in mdb(1)) and modules that are loaded mark their 432 * interfaces Unstable. In the future we could extend the dmod linkage 433 * to include the module's intended stability and then show it here. 434 */ 435 mdb_printf("%<b>ATTRIBUTES%</b>\n\n"); 436 mdb_printf(" Target: %s\n", mdb_tgt_name(mdb.m_target)); 437 mdb_printf(" Module: %s\n", idcp->idc_modp->mod_name); 438 mdb_printf(" Interface Stability: %s\n\n", 439 (idcp->idc_descr != NULL && idcp->idc_modp->mod_hdl == NULL) ? 440 "Evolving" : "Unstable"); 441 442 return (DCMD_OK); 443 } 444 445 int 446 cmd_help_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc, 447 const mdb_arg_t *argv) 448 { 449 if (argc == 0 && !(flags & DCMD_TAB_SPACE)) 450 return (0); 451 452 if (argc > 1) 453 return (0); 454 455 if (argc == 0) 456 return (mdb_tab_complete_dcmd(mcp, NULL)); 457 else 458 return (mdb_tab_complete_dcmd(mcp, argv[0].a_un.a_str)); 459 } 460 461 462 static int 463 print_dcmd_def(mdb_var_t *v, void *private) 464 { 465 mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v)); 466 int *ip = private; 467 468 mdb_printf(" [%d] %s`%s\n", 469 (*ip)++, idcp->idc_modp->mod_name, idcp->idc_name); 470 471 return (0); 472 } 473 474 static int 475 print_walker_def(mdb_var_t *v, void *private) 476 { 477 mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v)); 478 int *ip = private; 479 480 mdb_printf(" [%d] %s`%s\n", 481 (*ip)++, iwp->iwlk_modp->mod_name, iwp->iwlk_name); 482 483 return (0); 484 } 485 486 /*ARGSUSED*/ 487 int 488 cmd_which(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 489 { 490 const char defn_hdr[] = " > definition list:\n"; 491 uint_t opt_v = FALSE; 492 int i; 493 494 i = mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL); 495 496 for (; i < argc; i++) { 497 const char *s = argv[i].a_un.a_str; 498 int found = FALSE; 499 mdb_iwalker_t *iwp; 500 mdb_idcmd_t *idcp; 501 const char *alias; 502 503 if (argv->a_type != MDB_TYPE_STRING) 504 continue; 505 506 if (s[0] == '$' && s[1] == '<') 507 s += 2; 508 509 if ((idcp = mdb_dcmd_lookup(s)) != NULL) { 510 mdb_var_t *v = idcp->idc_var; 511 int i = 1; 512 513 if (idcp->idc_modp != &mdb.m_rmod) { 514 mdb_printf("%s is a dcmd from module %s\n", 515 s, idcp->idc_modp->mod_name); 516 } else 517 mdb_printf("%s is a built-in dcmd\n", s); 518 519 if (opt_v) { 520 mdb_printf(defn_hdr); 521 mdb_nv_defn_iter(v, print_dcmd_def, &i); 522 } 523 found = TRUE; 524 } 525 526 if ((iwp = mdb_walker_lookup(s)) != NULL) { 527 mdb_var_t *v = iwp->iwlk_var; 528 int i = 1; 529 530 if (iwp->iwlk_modp != &mdb.m_rmod) { 531 mdb_printf("%s is a walker from module %s\n", 532 s, iwp->iwlk_modp->mod_name); 533 } else 534 mdb_printf("%s is a built-in walker\n", s); 535 536 if (opt_v) { 537 mdb_printf(defn_hdr); 538 mdb_nv_defn_iter(v, print_walker_def, &i); 539 } 540 found = TRUE; 541 } 542 543 if ((alias = mdb_macalias_lookup(s)) != NULL) { 544 mdb_printf("%s is a macro alias for '%s'\n", s, alias); 545 found = TRUE; 546 } 547 548 if (!found) 549 mdb_warn("%s not found\n", s); 550 } 551 552 return (DCMD_OK); 553 } 554