/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. */ #include #include #include #include #include #include #include const char _mdb_help[] = "\nEach debugger command in %s is structured as follows:\n\n" " [ address [, count]] verb [ arguments ... ]\n" " ^ ^ ^ ^\n" " the start --+ | | +-- arguments are strings which can be\n" " address can be an | | quoted using \"\" or '' or\n" " expression | | expressions enclosed in $[ ]\n" " | |\n" " the repeat count --+ +--------- the verb is a name which begins\n" " is also an expression with either $, :, or ::. it can also\n" " be a format specifier (/ \\ ? or =)\n\n" "For information on debugger commands (dcmds) and walkers, type:\n\n" " ::help cmdname ... for more detailed information on a command\n" " ::dcmds ... for a list of dcmds and their descriptions\n" " ::walkers ... for a list of walkers and their descriptions\n" " ::dmods -l ... for a list of modules and their dcmds and walkers\n" " ::formats ... for a list of format characters for / \\ ? and =\n\n" "For information on command-line options, type:\n\n" " $ %s -? ... in your shell for a complete list of options\n\n"; /*ARGSUSED*/ static int print_dcmd(mdb_var_t *v, void *ignored) { const mdb_idcmd_t *idcp = mdb_nv_get_cookie(v); if (idcp->idc_descr != NULL) mdb_printf(" dcmd %-20s - %s\n", idcp->idc_name, idcp->idc_descr); return (0); } /*ARGSUSED*/ static int print_walk(mdb_var_t *v, void *ignored) { const mdb_iwalker_t *iwp = mdb_nv_get_cookie(v); if (iwp->iwlk_descr != NULL) mdb_printf(" walk %-20s - %s\n", iwp->iwlk_name, iwp->iwlk_descr); return (0); } /*ARGSUSED*/ static int print_dmod_long(mdb_var_t *v, void *ignored) { mdb_module_t *mod = mdb_nv_get_cookie(v); mdb_printf("\n%%-70s%\n", mod->mod_name); if (mod->mod_tgt_ctor != NULL) { mdb_printf(" ctor 0x%-18lx - target constructor\n", (ulong_t)mod->mod_tgt_ctor); } if (mod->mod_dis_ctor != NULL) { mdb_printf(" ctor 0x%-18lx - disassembler constructor\n", (ulong_t)mod->mod_dis_ctor); } mdb_nv_sort_iter(&mod->mod_dcmds, print_dcmd, NULL, UM_SLEEP | UM_GC); mdb_nv_sort_iter(&mod->mod_walkers, print_walk, NULL, UM_SLEEP | UM_GC); return (0); } /*ARGSUSED*/ static int print_dmod_short(mdb_var_t *v, void *ignored) { mdb_printf("%s\n", mdb_nv_get_name(v)); return (0); } /*ARGSUSED*/ int cmd_dmods(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int (*func)(mdb_var_t *, void *); uint_t opt_l = FALSE; mdb_var_t *v; int i; if (flags & DCMD_ADDRSPEC) return (DCMD_USAGE); i = mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, TRUE, &opt_l, NULL); func = opt_l ? print_dmod_long : print_dmod_short; if (i != argc) { if (argc - i != 1 || argv[i].a_type != MDB_TYPE_STRING) return (DCMD_USAGE); v = mdb_nv_lookup(&mdb.m_modules, argv[i].a_un.a_str); if (v == NULL) mdb_warn("%s module not loaded\n", argv[i].a_un.a_str); else (void) func(v, NULL); } else mdb_nv_sort_iter(&mdb.m_modules, func, NULL, UM_SLEEP | UM_GC); return (DCMD_OK); } #define FILTER_NAMEONLY 0x1 typedef struct filter_data { const char *pattern; int flags; #ifndef _KMDB regex_t reg; #endif } filter_data_t; static void filter_help(void) { mdb_printf("Options:\n" " -n Match only the name, not the description.\n" #ifdef _KMDB " pattern Substring to match against name/description." #else " pattern RE to match against name/description." #endif "\n"); } void cmd_dcmds_help(void) { mdb_printf( "List all of the dcmds that are currently available. If a pattern\n" "is provided then list only the commands that\n" #ifdef _KMDB "contain the provided substring." #else "match the provided regular expression." #endif "\n"); filter_help(); } void cmd_walkers_help(void) { mdb_printf( "List all of the walkers that are currently available. If a\n" "pattern is provided then list only the walkers that\n" #ifdef _KMDB "contain the provided substring." #else "match the provided regular expression." #endif "\n"); filter_help(); } static int print_wdesc(mdb_var_t *v, void *data) { filter_data_t *f = data; mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v)); const char *name = mdb_nv_get_name(v); boolean_t output = FALSE; if (name == NULL || iwp->iwlk_descr == NULL) return (0); if (f->pattern == NULL) { output = TRUE; } else { #ifdef _KMDB /* * kmdb doesn't have access to the reg* functions, so we fall * back to strstr. */ if (strstr(name, f->pattern) != NULL || (!(f->flags & FILTER_NAMEONLY) && strstr(iwp->iwlk_descr, f->pattern) != NULL)) output = TRUE; #else regmatch_t pmatch; if (regexec(&f->reg, name, 1, &pmatch, 0) == 0 || (!(f->flags & FILTER_NAMEONLY) && regexec(&f->reg, iwp->iwlk_descr, 1, &pmatch, 0) == 0)) output = TRUE; #endif } if (output) mdb_printf("%-24s - %s\n", name, iwp->iwlk_descr); return (0); } /*ARGSUSED*/ int cmd_walkers(uintptr_t addr __unused, uint_t flags, int argc, const mdb_arg_t *argv) { filter_data_t f; int i; #ifndef _KMDB int err; #endif if (flags & DCMD_ADDRSPEC) return (DCMD_USAGE); f.pattern = NULL; f.flags = 0; i = mdb_getopts(argc, argv, 'n', MDB_OPT_SETBITS, FILTER_NAMEONLY, &f.flags, NULL); argc -= i; argv += i; if (argc == 1) { if (argv->a_type != MDB_TYPE_STRING) return (DCMD_USAGE); f.pattern = argv->a_un.a_str; #ifndef _KMDB if ((err = regcomp(&f.reg, f.pattern, REG_EXTENDED)) != 0) { size_t nbytes; char *buf; nbytes = regerror(err, &f.reg, NULL, 0); buf = mdb_alloc(nbytes + 1, UM_SLEEP | UM_GC); (void) regerror(err, &f.reg, buf, nbytes); mdb_warn("%s\n", buf); return (DCMD_ERR); } #endif } else if (argc != 0) { return (DCMD_USAGE); } mdb_nv_sort_iter(&mdb.m_walkers, print_wdesc, &f, UM_SLEEP | UM_GC); return (DCMD_OK); } static int print_ddesc(mdb_var_t *v, void *data) { filter_data_t *f = data; mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v)); const char *name = mdb_nv_get_name(v); boolean_t output = FALSE; if (name == NULL || idcp->idc_descr == NULL) return (0); if (f->pattern == NULL) { output = TRUE; } else { #ifdef _KMDB /* * kmdb doesn't have access to the reg* functions, so we fall * back to strstr. */ if (strstr(name, f->pattern) != NULL || (!(f->flags & FILTER_NAMEONLY) && strstr(idcp->idc_descr, f->pattern) != NULL)) output = TRUE; #else regmatch_t pmatch; if (regexec(&f->reg, name, 1, &pmatch, 0) == 0 || (!(f->flags & FILTER_NAMEONLY) && regexec(&f->reg, idcp->idc_descr, 1, &pmatch, 0) == 0)) output = TRUE; #endif } if (output) mdb_printf("%-24s - %s\n", name, idcp->idc_descr); return (0); } /*ARGSUSED*/ int cmd_dcmds(uintptr_t addr __unused, uint_t flags, int argc, const mdb_arg_t *argv) { filter_data_t f; int i; #ifndef _KMDB int err; #endif if (flags & DCMD_ADDRSPEC) return (DCMD_USAGE); f.pattern = NULL; f.flags = 0; i = mdb_getopts(argc, argv, 'n', MDB_OPT_SETBITS, FILTER_NAMEONLY, &f.flags, NULL); argc -= i; argv += i; if (argc == 1) { if (argv->a_type != MDB_TYPE_STRING) return (DCMD_USAGE); f.pattern = argv->a_un.a_str; #ifndef _KMDB if ((err = regcomp(&f.reg, f.pattern, REG_EXTENDED)) != 0) { size_t nbytes; char *buf; nbytes = regerror(err, &f.reg, NULL, 0); buf = mdb_alloc(nbytes + 1, UM_SLEEP | UM_GC); (void) regerror(err, &f.reg, buf, nbytes); mdb_warn("%s\n", buf); return (DCMD_ERR); } #endif } else if (argc != 0) { return (DCMD_USAGE); } mdb_nv_sort_iter(&mdb.m_dcmds, print_ddesc, &f, UM_SLEEP | UM_GC); return (DCMD_OK); } /*ARGSUSED*/ int cmd_help(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { const char *prefix, *usage; const mdb_idcmd_t *idcp; if ((flags & DCMD_ADDRSPEC) || argc > 1) return (DCMD_USAGE); if (argc == 0) { mdb_printf(_mdb_help, mdb.m_pname, mdb.m_pname); return (DCMD_OK); } if (argv->a_type != MDB_TYPE_STRING) { warn("expected string argument\n"); return (DCMD_USAGE); } if (strncmp(argv->a_un.a_str, "::", 2) == 0) idcp = mdb_dcmd_lookup(argv->a_un.a_str + 2); else idcp = mdb_dcmd_lookup(argv->a_un.a_str); if (idcp == NULL) { mdb_warn("unknown command: %s\n", argv->a_un.a_str); return (DCMD_ERR); } prefix = strchr(":$=/\\?>", idcp->idc_name[0]) ? "" : "::"; usage = idcp->idc_usage ? idcp->idc_usage : ""; mdb_printf("\n%NAME%\n %s - %s\n\n", idcp->idc_name, idcp->idc_descr); mdb_printf("%SYNOPSIS%\n "); if (usage[0] == '?') { mdb_printf("[ %addr% ] "); usage++; } else if (usage[0] == ':') { mdb_printf("%addr% "); usage++; } mdb_printf("%s%s %s\n\n", prefix, idcp->idc_name, usage); if (idcp->idc_help != NULL) { mdb_printf("%DESCRIPTION%\n"); (void) mdb_inc_indent(2); idcp->idc_help(); (void) mdb_dec_indent(2); mdb_printf("\n"); } /* * For now, modules that are built-in mark their interfaces Evolving * (documented in mdb(1)) and modules that are loaded mark their * interfaces Unstable. In the future we could extend the dmod linkage * to include the module's intended stability and then show it here. */ mdb_printf("%ATTRIBUTES%\n\n"); mdb_printf(" Target: %s\n", mdb_tgt_name(mdb.m_target)); mdb_printf(" Module: %s\n", idcp->idc_modp->mod_name); mdb_printf(" Interface Stability: %s\n\n", (idcp->idc_descr != NULL && idcp->idc_modp->mod_hdl == NULL) ? "Evolving" : "Unstable"); return (DCMD_OK); } int cmd_help_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc, const mdb_arg_t *argv) { if (argc == 0 && !(flags & DCMD_TAB_SPACE)) return (0); if (argc > 1) return (0); if (argc == 0) return (mdb_tab_complete_dcmd(mcp, NULL)); else return (mdb_tab_complete_dcmd(mcp, argv[0].a_un.a_str)); } static int print_dcmd_def(mdb_var_t *v, void *private) { mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v)); int *ip = private; mdb_printf(" [%d] %s`%s\n", (*ip)++, idcp->idc_modp->mod_name, idcp->idc_name); return (0); } static int print_walker_def(mdb_var_t *v, void *private) { mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v)); int *ip = private; mdb_printf(" [%d] %s`%s\n", (*ip)++, iwp->iwlk_modp->mod_name, iwp->iwlk_name); return (0); } /*ARGSUSED*/ int cmd_which(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { const char defn_hdr[] = " > definition list:\n"; uint_t opt_v = FALSE; int i; i = mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL); for (; i < argc; i++) { const char *s = argv[i].a_un.a_str; int found = FALSE; mdb_iwalker_t *iwp; mdb_idcmd_t *idcp; const char *alias; if (argv->a_type != MDB_TYPE_STRING) continue; if (s[0] == '$' && s[1] == '<') s += 2; if ((idcp = mdb_dcmd_lookup(s)) != NULL) { mdb_var_t *v = idcp->idc_var; int i = 1; if (idcp->idc_modp != &mdb.m_rmod) { mdb_printf("%s is a dcmd from module %s\n", s, idcp->idc_modp->mod_name); } else mdb_printf("%s is a built-in dcmd\n", s); if (opt_v) { mdb_printf(defn_hdr); mdb_nv_defn_iter(v, print_dcmd_def, &i); } found = TRUE; } if ((iwp = mdb_walker_lookup(s)) != NULL) { mdb_var_t *v = iwp->iwlk_var; int i = 1; if (iwp->iwlk_modp != &mdb.m_rmod) { mdb_printf("%s is a walker from module %s\n", s, iwp->iwlk_modp->mod_name); } else mdb_printf("%s is a built-in walker\n", s); if (opt_v) { mdb_printf(defn_hdr); mdb_nv_defn_iter(v, print_walker_def, &i); } found = TRUE; } if ((alias = mdb_macalias_lookup(s)) != NULL) { mdb_printf("%s is a macro alias for '%s'\n", s, alias); found = TRUE; } if (!found) mdb_warn("%s not found\n", s); } return (DCMD_OK); }