| /* |
| * 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 <mdb/mdb_modapi.h> |
| #include <mdb/mdb_macalias.h> |
| #include <mdb/mdb_fmt.h> |
| #include <mdb/mdb_err.h> |
| #include <mdb/mdb_help.h> |
| #include <mdb/mdb.h> |
| #include <regex.h> |
| |
| 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%<u>%-70s%</u>\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%<b>NAME%</b>\n %s - %s\n\n", |
| idcp->idc_name, idcp->idc_descr); |
| |
| mdb_printf("%<b>SYNOPSIS%</b>\n "); |
| if (usage[0] == '?') { |
| mdb_printf("[ %<u>addr%</u> ] "); |
| usage++; |
| } else if (usage[0] == ':') { |
| mdb_printf("%<u>addr%</u> "); |
| usage++; |
| } |
| |
| mdb_printf("%s%s %s\n\n", prefix, idcp->idc_name, usage); |
| |
| if (idcp->idc_help != NULL) { |
| mdb_printf("%<b>DESCRIPTION%</b>\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("%<b>ATTRIBUTES%</b>\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); |
| } |