blob: fbd404a1076e537c99c6cc9178febb487e2b32de [file] [log] [blame]
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright (c) 2018 Joyent, Inc. All rights reserved.
* Copyright (c) 2013 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
* Copyright (c) 2015, 2017 by Delphix. All rights reserved.
* Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
*/
#include <sys/elf.h>
#include <sys/elf_SPARC.h>
#include <libproc.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <alloca.h>
#include <libctf.h>
#include <ctype.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_argvec.h>
#include <mdb/mdb_nv.h>
#include <mdb/mdb_fmt.h>
#include <mdb/mdb_target.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_conf.h>
#include <mdb/mdb_module.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_stdlib.h>
#include <mdb/mdb_lex.h>
#include <mdb/mdb_io_impl.h>
#include <mdb/mdb_help.h>
#include <mdb/mdb_disasm.h>
#include <mdb/mdb_frame.h>
#include <mdb/mdb_evset.h>
#include <mdb/mdb_print.h>
#include <mdb/mdb_nm.h>
#include <mdb/mdb_set.h>
#include <mdb/mdb_demangle.h>
#include <mdb/mdb_ctf.h>
#include <mdb/mdb_whatis.h>
#include <mdb/mdb_whatis_impl.h>
#include <mdb/mdb_macalias.h>
#include <mdb/mdb_tab.h>
#include <mdb/mdb_typedef.h>
#ifdef _KMDB
#include <kmdb/kmdb_kdi.h>
#endif
#include <mdb/mdb.h>
#ifdef __sparc
#define SETHI_MASK 0xc1c00000
#define SETHI_VALUE 0x01000000
#define IS_SETHI(machcode) (((machcode) & SETHI_MASK) == SETHI_VALUE)
#define OP(machcode) ((machcode) >> 30)
#define OP3(machcode) (((machcode) >> 19) & 0x3f)
#define RD(machcode) (((machcode) >> 25) & 0x1f)
#define RS1(machcode) (((machcode) >> 14) & 0x1f)
#define I(machcode) (((machcode) >> 13) & 0x01)
#define IMM13(machcode) ((machcode) & 0x1fff)
#define IMM22(machcode) ((machcode) & 0x3fffff)
#define OP_ARITH_MEM_MASK 0x2
#define OP_ARITH 0x2
#define OP_MEM 0x3
#define OP3_CC_MASK 0x10
#define OP3_COMPLEX_MASK 0x20
#define OP3_ADD 0x00
#define OP3_OR 0x02
#define OP3_XOR 0x03
#ifndef R_O7
#define R_O7 0xf
#endif
#endif /* __sparc */
static mdb_tgt_addr_t
write_uint8(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
{
uint8_t o, n = (uint8_t)ull;
if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
addr) == -1)
return (addr);
if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
return (addr);
if (rdback) {
if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
return (addr);
mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8x=%8T0x%x\n",
mdb_iob_getmargin(mdb.m_out), addr, o, n);
}
return (addr + sizeof (n));
}
static mdb_tgt_addr_t
write_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
{
uint16_t o, n = (uint16_t)ull;
if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
addr) == -1)
return (addr);
if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
return (addr);
if (rdback) {
if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
return (addr);
mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8hx=%8T0x%hx\n",
mdb_iob_getmargin(mdb.m_out), addr, o, n);
}
return (addr + sizeof (n));
}
static mdb_tgt_addr_t
write_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
{
uint32_t o, n = (uint32_t)ull;
if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
addr) == -1)
return (addr);
if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
return (addr);
if (rdback) {
if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
return (addr);
mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#16x=%8T0x%x\n",
mdb_iob_getmargin(mdb.m_out), addr, o, n);
}
return (addr + sizeof (n));
}
static mdb_tgt_addr_t
write_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
{
uint64_t o;
if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
addr) == -1)
return (addr);
if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
return (addr);
if (rdback) {
if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
return (addr);
mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#24llx=%8T0x%llx\n",
mdb_iob_getmargin(mdb.m_out), addr, o, n);
}
return (addr + sizeof (n));
}
/*
* Writes to objects of size 1, 2, 4, or 8 bytes. The function
* doesn't care if the object is a number or not (e.g. it could
* be a byte array, or a struct) as long as the size of the write
* is one of the aforementioned ones.
*/
static mdb_tgt_addr_t
write_var_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, size_t size,
uint_t rdback)
{
if (size < sizeof (uint64_t)) {
uint64_t max_num = 1ULL << (size * NBBY);
if (val >= max_num) {
uint64_t write_len = 0;
/* count bytes needed for val */
while (val != 0) {
write_len++;
val >>= NBBY;
}
mdb_warn("value too big for the length of the write: "
"supplied %llu bytes but maximum is %llu bytes\n",
(u_longlong_t)write_len, (u_longlong_t)size);
return (addr);
}
}
switch (size) {
case 1:
return (write_uint8(as, addr, val, rdback));
case 2:
return (write_uint16(as, addr, val, rdback));
case 4:
return (write_uint32(as, addr, val, rdback));
case 8:
return (write_uint64(as, addr, val, rdback));
default:
mdb_warn("writes of size %u are not supported\n ", size);
return (addr);
}
}
static mdb_tgt_addr_t
write_ctf_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
{
mdb_ctf_id_t mid;
size_t size;
ssize_t type_size;
int kind;
if (mdb_ctf_lookup_by_addr(addr, &mid) != 0) {
mdb_warn("no CTF data found at this address\n");
return (addr);
}
kind = mdb_ctf_type_kind(mid);
if (kind == CTF_ERR) {
mdb_warn("CTF data found but type kind could not be read");
return (addr);
}
if (kind == CTF_K_TYPEDEF) {
mdb_ctf_id_t temp_id;
if (mdb_ctf_type_resolve(mid, &temp_id) != 0) {
mdb_warn("failed to resolve type");
return (addr);
}
kind = mdb_ctf_type_kind(temp_id);
}
if (kind != CTF_K_INTEGER && kind != CTF_K_POINTER &&
kind != CTF_K_ENUM) {
mdb_warn("CTF type should be integer, pointer, or enum\n");
return (addr);
}
type_size = mdb_ctf_type_size(mid);
if (type_size < 0) {
mdb_warn("CTF data found but size could not be read");
return (addr);
}
size = type_size;
return (write_var_uint(as, addr, n, size, rdback));
}
static int
write_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr,
int argc, const mdb_arg_t *argv)
{
mdb_tgt_addr_t (*write_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
uint64_t, uint_t);
mdb_tgt_addr_t naddr;
uintmax_t value;
int rdback = mdb.m_flags & MDB_FL_READBACK;
size_t i;
if (argc == 1) {
mdb_warn("expected value to write following %c\n",
argv->a_un.a_char);
return (DCMD_ERR);
}
switch (argv->a_un.a_char) {
case 'v':
write_value = write_uint8;
break;
case 'w':
write_value = write_uint16;
break;
case 'z':
write_value = write_ctf_uint;
break;
case 'W':
write_value = write_uint32;
break;
case 'Z':
write_value = write_uint64;
break;
}
for (argv++, i = 1; i < argc; i++, argv++) {
if (argv->a_type == MDB_TYPE_CHAR) {
mdb_warn("expected immediate value instead of '%c'\n",
argv->a_un.a_char);
return (DCMD_ERR);
}
if (argv->a_type == MDB_TYPE_STRING) {
if (mdb_eval(argv->a_un.a_str) == -1) {
mdb_warn("failed to write \"%s\"",
argv->a_un.a_str);
return (DCMD_ERR);
}
value = mdb_nv_get_value(mdb.m_dot);
} else
value = argv->a_un.a_val;
mdb_nv_set_value(mdb.m_dot, addr);
if ((naddr = write_value(as, addr, value, rdback)) == addr) {
mdb_warn("failed to write %llr at address 0x%llx",
value, addr);
mdb.m_incr = 0;
return (DCMD_ERR);
}
mdb.m_incr = naddr - addr;
addr = naddr;
}
return (DCMD_OK);
}
static mdb_tgt_addr_t
match_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
{
uint16_t x, val = (uint16_t)v64, mask = (uint16_t)m64;
for (; mdb_tgt_aread(mdb.m_target, as, &x,
sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
if ((x & mask) == val) {
mdb_iob_printf(mdb.m_out, "%lla\n", addr);
break;
}
}
return (addr);
}
static mdb_tgt_addr_t
match_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
{
uint32_t x, val = (uint32_t)v64, mask = (uint32_t)m64;
for (; mdb_tgt_aread(mdb.m_target, as, &x,
sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
if ((x & mask) == val) {
mdb_iob_printf(mdb.m_out, "%lla\n", addr);
break;
}
}
return (addr);
}
static mdb_tgt_addr_t
match_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, uint64_t mask)
{
uint64_t x;
for (; mdb_tgt_aread(mdb.m_target, as, &x,
sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
if ((x & mask) == val) {
mdb_iob_printf(mdb.m_out, "%lla\n", addr);
break;
}
}
return (addr);
}
static int
match_arglist(mdb_tgt_as_t as, uint_t flags, mdb_tgt_addr_t addr,
int argc, const mdb_arg_t *argv)
{
mdb_tgt_addr_t (*match_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
uint64_t, uint64_t);
uint64_t args[2] = { 0, -1ULL }; /* [ value, mask ] */
size_t i;
if (argc < 2) {
mdb_warn("expected value following %c\n", argv->a_un.a_char);
return (DCMD_ERR);
}
if (argc > 3) {
mdb_warn("only value and mask may follow %c\n",
argv->a_un.a_char);
return (DCMD_ERR);
}
switch (argv->a_un.a_char) {
case 'l':
match_value = match_uint16;
break;
case 'L':
match_value = match_uint32;
break;
case 'M':
match_value = match_uint64;
break;
}
for (argv++, i = 1; i < argc; i++, argv++) {
if (argv->a_type == MDB_TYPE_CHAR) {
mdb_warn("expected immediate value instead of '%c'\n",
argv->a_un.a_char);
return (DCMD_ERR);
}
if (argv->a_type == MDB_TYPE_STRING) {
if (mdb_eval(argv->a_un.a_str) == -1) {
mdb_warn("failed to evaluate \"%s\"",
argv->a_un.a_str);
return (DCMD_ERR);
}
args[i - 1] = mdb_nv_get_value(mdb.m_dot);
} else
args[i - 1] = argv->a_un.a_val;
}
addr = match_value(as, addr, args[0], args[1]);
mdb_nv_set_value(mdb.m_dot, addr);
/*
* In adb(1), the match operators ignore any repeat count that has
* been applied to them. We emulate this undocumented property
* by returning DCMD_ABORT if our input is not a pipeline.
*/
return ((flags & DCMD_PIPE) ? DCMD_OK : DCMD_ABORT);
}
static int
argncmp(int argc, const mdb_arg_t *argv, const char *s)
{
for (; *s != '\0'; s++, argc--, argv++) {
if (argc == 0 || argv->a_type != MDB_TYPE_CHAR)
return (FALSE);
if (argv->a_un.a_char != *s)
return (FALSE);
}
return (TRUE);
}
static int
print_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint_t flags,
int argc, const mdb_arg_t *argv)
{
char buf[MDB_TGT_SYM_NAMLEN];
mdb_tgt_addr_t oaddr = addr;
mdb_tgt_addr_t naddr;
GElf_Sym sym;
size_t i, n;
if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
const char *fmt;
int is_dis;
/*
* This is nasty, but necessary for precise adb compatibility.
* Detect disassembly format by looking for "ai" or "ia":
*/
if (argncmp(argc, argv, "ai")) {
fmt = "%-#*lla\n";
is_dis = TRUE;
} else if (argncmp(argc, argv, "ia")) {
fmt = "%-#*lla";
is_dis = TRUE;
} else {
fmt = "%-#*lla%16T";
is_dis = FALSE;
}
/*
* If symbolic decoding is on, disassembly is off, and the
* address exactly matches a symbol, print the symbol name:
*/
if ((mdb.m_flags & MDB_FL_PSYM) && !is_dis &&
(as == MDB_TGT_AS_VIRT || as == MDB_TGT_AS_FILE) &&
mdb_tgt_lookup_by_addr(mdb.m_target, (uintptr_t)addr,
MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0)
mdb_iob_printf(mdb.m_out, "%s:\n", buf);
/*
* If this is a virtual address, cast it so that it reflects
* only the valid component of the address.
*/
if (as == MDB_TGT_AS_VIRT)
addr = (uintptr_t)addr;
mdb_iob_printf(mdb.m_out, fmt,
(uint_t)mdb_iob_getmargin(mdb.m_out), addr);
}
if (argc == 0) {
/*
* Yes, for you trivia buffs: if you use a format verb and give
* no format string, you get: X^"= "i ... note that in adb the
* the '=' verb once had 'z' as its default, but then 'z' was
* deleted (it was once an alias for 'i') and so =\n now calls
* scanform("z") and produces a 'bad modifier' message.
*/
static const mdb_arg_t def_argv[] = {
{ MDB_TYPE_CHAR, MDB_INIT_CHAR('X') },
{ MDB_TYPE_CHAR, MDB_INIT_CHAR('^') },
{ MDB_TYPE_STRING, MDB_INIT_STRING("= ") },
{ MDB_TYPE_CHAR, MDB_INIT_CHAR('i') }
};
argc = sizeof (def_argv) / sizeof (mdb_arg_t);
argv = def_argv;
}
mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
for (i = 0, n = 1; i < argc; i++, argv++) {
switch (argv->a_type) {
case MDB_TYPE_CHAR:
naddr = mdb_fmt_print(mdb.m_target, as, addr, n,
argv->a_un.a_char);
mdb.m_incr = naddr - addr;
addr = naddr;
n = 1;
break;
case MDB_TYPE_IMMEDIATE:
n = argv->a_un.a_val;
break;
case MDB_TYPE_STRING:
mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
n = 1;
break;
}
}
mdb.m_incr = addr - oaddr;
mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
return (DCMD_OK);
}
static int
print_common(mdb_tgt_as_t as, uint_t flags, int argc, const mdb_arg_t *argv)
{
mdb_tgt_addr_t addr = mdb_nv_get_value(mdb.m_dot);
if (argc != 0 && argv->a_type == MDB_TYPE_CHAR) {
if (strchr("vwzWZ", argv->a_un.a_char))
return (write_arglist(as, addr, argc, argv));
if (strchr("lLM", argv->a_un.a_char))
return (match_arglist(as, flags, addr, argc, argv));
}
return (print_arglist(as, addr, flags, argc, argv));
}
/*ARGSUSED*/
static int
cmd_print_core(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
{
return (print_common(MDB_TGT_AS_VIRT, flags, argc, argv));
}
#ifndef _KMDB
/*ARGSUSED*/
static int
cmd_print_object(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
{
return (print_common(MDB_TGT_AS_FILE, flags, argc, argv));
}
#endif
/*ARGSUSED*/
static int
cmd_print_phys(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
{
return (print_common(MDB_TGT_AS_PHYS, flags, argc, argv));
}
/*ARGSUSED*/
static int
cmd_print_value(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uintmax_t ndot, dot = mdb_get_dot();
const char *tgt_argv[1];
mdb_tgt_t *t;
size_t i, n;
if (argc == 0) {
mdb_warn("expected one or more format characters "
"following '='\n");
return (DCMD_ERR);
}
tgt_argv[0] = (const char *)&dot;
t = mdb_tgt_create(mdb_value_tgt_create, 0, 1, tgt_argv);
mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
for (i = 0, n = 1; i < argc; i++, argv++) {
switch (argv->a_type) {
case MDB_TYPE_CHAR:
ndot = mdb_fmt_print(t, MDB_TGT_AS_VIRT,
dot, n, argv->a_un.a_char);
if (argv->a_un.a_char == '+' ||
argv->a_un.a_char == '-')
dot = ndot;
n = 1;
break;
case MDB_TYPE_IMMEDIATE:
n = argv->a_un.a_val;
break;
case MDB_TYPE_STRING:
mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
n = 1;
break;
}
}
mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
mdb_nv_set_value(mdb.m_dot, dot);
mdb.m_incr = 0;
mdb_tgt_destroy(t);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_assign_variable(uintptr_t addr, uint_t flags,
int argc, const mdb_arg_t *argv)
{
uintmax_t dot = mdb_nv_get_value(mdb.m_dot);
const char *p;
mdb_var_t *v;
if (argc == 2) {
if (argv->a_type != MDB_TYPE_CHAR) {
mdb_warn("improper arguments following '>' operator\n");
return (DCMD_ERR);
}
switch (argv->a_un.a_char) {
case 'c':
addr = *((uchar_t *)&addr);
break;
case 's':
addr = *((ushort_t *)&addr);
break;
case 'i':
addr = *((uint_t *)&addr);
break;
case 'l':
addr = *((ulong_t *)&addr);
break;
default:
mdb_warn("%c is not a valid // modifier\n",
argv->a_un.a_char);
return (DCMD_ERR);
}
dot = addr;
argv++;
argc--;
}
if (argc != 1 || argv->a_type != MDB_TYPE_STRING) {
mdb_warn("expected single variable name following '>'\n");
return (DCMD_ERR);
}
if (strlen(argv->a_un.a_str) >= (size_t)MDB_NV_NAMELEN) {
mdb_warn("variable names may not exceed %d characters\n",
MDB_NV_NAMELEN - 1);
return (DCMD_ERR);
}
if ((p = strbadid(argv->a_un.a_str)) != NULL) {
mdb_warn("'%c' may not be used in a variable name\n", *p);
return (DCMD_ERR);
}
if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
(void) mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str, NULL, dot, 0);
else
mdb_nv_set_value(v, dot);
mdb.m_incr = 0;
return (DCMD_OK);
}
static int
print_soutype(const char *sou, uintptr_t addr, uint_t flags)
{
static const char *prefixes[] = { "struct ", "union " };
size_t namesz = 7 + strlen(sou) + 1;
char *name = mdb_alloc(namesz, UM_SLEEP | UM_GC);
mdb_ctf_id_t id;
int i;
for (i = 0; i < 2; i++) {
(void) mdb_snprintf(name, namesz, "%s%s", prefixes[i], sou);
if (mdb_ctf_lookup_by_name(name, &id) == 0) {
mdb_arg_t v;
int rv;
v.a_type = MDB_TYPE_STRING;
v.a_un.a_str = name;
rv = mdb_call_dcmd("print", addr, flags, 1, &v);
return (rv);
}
}
return (DCMD_ERR);
}
static int
print_type(const char *name, uintptr_t addr, uint_t flags)
{
mdb_ctf_id_t id;
char *sname;
size_t snamesz;
int rv;
if (!(flags & DCMD_ADDRSPEC)) {
addr = mdb_get_dot();
flags |= DCMD_ADDRSPEC;
}
if ((rv = print_soutype(name, addr, flags)) != DCMD_ERR)
return (rv);
snamesz = strlen(name) + 3;
sname = mdb_zalloc(snamesz, UM_SLEEP | UM_GC);
(void) mdb_snprintf(sname, snamesz, "%s_t", name);
if (mdb_ctf_lookup_by_name(sname, &id) == 0) {
mdb_arg_t v;
int rv;
v.a_type = MDB_TYPE_STRING;
v.a_un.a_str = sname;
rv = mdb_call_dcmd("print", addr, flags, 1, &v);
return (rv);
}
sname[snamesz - 2] = 's';
rv = print_soutype(sname, addr, flags);
return (rv);
}
static int
exec_alias(const char *fname, uintptr_t addr, uint_t flags)
{
const char *alias;
int rv;
if ((alias = mdb_macalias_lookup(fname)) == NULL)
return (DCMD_ERR);
if (flags & DCMD_ADDRSPEC) {
size_t sz = sizeof (uintptr_t) * 2 + strlen(alias) + 1;
char *addralias = mdb_alloc(sz, UM_SLEEP | UM_GC);
(void) mdb_snprintf(addralias, sz, "%p%s", addr, alias);
rv = mdb_eval(addralias);
} else {
rv = mdb_eval(alias);
}
return (rv == -1 ? DCMD_ABORT : DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_src_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
const char *fname;
mdb_io_t *fio;
int rv;
if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
fname = argv->a_un.a_str;
if (flags & DCMD_PIPE_OUT) {
mdb_warn("macro files cannot be used as input to a pipeline\n");
return (DCMD_ABORT);
}
if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
O_RDONLY, 0)) != NULL) {
mdb_frame_t *fp = mdb.m_frame;
int err;
mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
err = mdb_run();
ASSERT(fp == mdb.m_frame);
mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
yylineno = mdb_iob_lineno(mdb.m_in);
if (err == MDB_ERR_PAGER && mdb.m_fmark != fp)
longjmp(fp->f_pcb, err);
if (err == MDB_ERR_QUIT || err == MDB_ERR_ABORT ||
err == MDB_ERR_SIGINT || err == MDB_ERR_OUTPUT)
longjmp(fp->f_pcb, err);
return (DCMD_OK);
}
if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
(rv = print_type(fname, addr, flags)) != DCMD_ERR)
return (rv);
mdb_warn("failed to open %s (see ::help '$<')\n", fname);
return (DCMD_ABORT);
}
static int
cmd_exec_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
const char *fname;
mdb_io_t *fio;
int rv;
/*
* The syntax [expr[,count]]$< with no trailing macro file name is
* magic in that if count is zero, this command won't be called and
* the expression is thus a no-op. If count is non-zero, we get
* invoked with argc == 0, and this means abort the current macro.
* If our debugger stack depth is greater than one, we may be using
* $< from within a previous $<<, so in that case we set m_in to
* NULL to force this entire frame to be popped.
*/
if (argc == 0) {
if (mdb_iob_stack_size(&mdb.m_frame->f_istk) != 0) {
mdb_iob_destroy(mdb.m_in);
mdb.m_in = mdb_iob_stack_pop(&mdb.m_frame->f_istk);
} else if (mdb.m_depth > 1) {
mdb_iob_destroy(mdb.m_in);
mdb.m_in = NULL;
} else
mdb_warn("input stack is empty\n");
return (DCMD_OK);
}
if ((flags & (DCMD_PIPE | DCMD_PIPE_OUT)) || mdb.m_depth == 1)
return (cmd_src_file(addr, flags, argc, argv));
if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
fname = argv->a_un.a_str;
if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
O_RDONLY, 0)) != NULL) {
mdb_iob_destroy(mdb.m_in);
mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
return (DCMD_OK);
}
if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
(rv = print_type(fname, addr, flags)) != DCMD_ERR)
return (rv);
mdb_warn("failed to open %s (see ::help '$<')\n", fname);
return (DCMD_ABORT);
}
#ifndef _KMDB
/*ARGSUSED*/
static int
cmd_cat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int status = DCMD_OK;
char buf[BUFSIZ];
mdb_iob_t *iob;
mdb_io_t *fio;
if (flags & DCMD_ADDRSPEC)
return (DCMD_USAGE);
for (; argc-- != 0; argv++) {
if (argv->a_type != MDB_TYPE_STRING) {
mdb_warn("expected string argument\n");
status = DCMD_ERR;
continue;
}
if ((fio = mdb_fdio_create_path(NULL,
argv->a_un.a_str, O_RDONLY, 0)) == NULL) {
mdb_warn("failed to open %s", argv->a_un.a_str);
status = DCMD_ERR;
continue;
}
iob = mdb_iob_create(fio, MDB_IOB_RDONLY);
while (!(mdb_iob_getflags(iob) & (MDB_IOB_EOF | MDB_IOB_ERR))) {
ssize_t len = mdb_iob_read(iob, buf, sizeof (buf));
if (len > 0) {
if (mdb_iob_write(mdb.m_out, buf, len) < 0) {
if (errno != EPIPE)
mdb_warn("write failed");
status = DCMD_ERR;
break;
}
}
}
if (mdb_iob_err(iob))
mdb_warn("error while reading %s", mdb_iob_name(iob));
mdb_iob_destroy(iob);
}
return (status);
}
#endif
/*ARGSUSED*/
static int
cmd_grep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
if (mdb_eval(argv->a_un.a_str) == -1)
return (DCMD_ABORT);
if (mdb_get_dot() != 0)
mdb_printf("%lr\n", addr);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
if (mdb_eval(argv->a_un.a_str) == -1)
return (DCMD_ABORT);
mdb_printf("%llr\n", mdb_get_dot());
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_notsup(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
mdb_warn("command is not supported by current target\n");
return (DCMD_ERR);
}
/*ARGSUSED*/
static int
cmd_quit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
#ifdef _KMDB
uint_t opt_u = FALSE;
if (mdb_getopts(argc, argv,
'u', MDB_OPT_SETBITS, TRUE, &opt_u, NULL) != argc)
return (DCMD_USAGE);
if (opt_u) {
if (mdb.m_flags & MDB_FL_NOUNLOAD) {
warn("%s\n", mdb_strerror(EMDB_KNOUNLOAD));
return (DCMD_ERR);
}
kmdb_kdi_set_unload_request();
}
#endif
longjmp(mdb.m_frame->f_pcb, MDB_ERR_QUIT);
/*NOTREACHED*/
return (DCMD_ERR);
}
#ifdef _KMDB
static void
quit_help(void)
{
mdb_printf(
"-u unload the debugger (if not loaded at boot)\n");
}
#endif
/*ARGSUSED*/
static int
cmd_vars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t opt_nz = FALSE, opt_tag = FALSE, opt_prt = FALSE;
mdb_var_t *v;
if (mdb_getopts(argc, argv,
'n', MDB_OPT_SETBITS, TRUE, &opt_nz,
'p', MDB_OPT_SETBITS, TRUE, &opt_prt,
't', MDB_OPT_SETBITS, TRUE, &opt_tag, NULL) != argc)
return (DCMD_USAGE);
mdb_nv_rewind(&mdb.m_nv);
while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
if ((opt_tag == FALSE || (v->v_flags & MDB_NV_TAGGED)) &&
(opt_nz == FALSE || mdb_nv_get_value(v) != 0)) {
if (opt_prt) {
mdb_printf("%#llr>%s\n",
mdb_nv_get_value(v), mdb_nv_get_name(v));
} else {
mdb_printf("%s = %llr\n",
mdb_nv_get_name(v), mdb_nv_get_value(v));
}
}
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_nzvars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uintmax_t value;
mdb_var_t *v;
if (argc != 0)
return (DCMD_USAGE);
mdb_nv_rewind(&mdb.m_nv);
while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
if ((value = mdb_nv_get_value(v)) != 0)
mdb_printf("%s = %llr\n", mdb_nv_get_name(v), value);
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_radix(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 0)
return (DCMD_USAGE);
if (flags & DCMD_ADDRSPEC) {
if (addr < 2 || addr > 16) {
mdb_warn("expected radix from 2 to 16\n");
return (DCMD_ERR);
}
mdb.m_radix = (int)addr;
}
mdb_iob_printf(mdb.m_out, "radix = %d base ten\n", mdb.m_radix);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_symdist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 0)
return (DCMD_USAGE);
if (flags & DCMD_ADDRSPEC)
mdb.m_symdist = addr;
mdb_printf("symbol matching distance = %lr (%s)\n",
mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_pgwidth(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 0)
return (DCMD_USAGE);
if (flags & DCMD_ADDRSPEC)
mdb_iob_resize(mdb.m_out, mdb.m_out->iob_rows, addr);
mdb_printf("output page width = %lu\n", mdb.m_out->iob_cols);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_reopen(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 0)
return (DCMD_USAGE);
if (mdb_tgt_setflags(mdb.m_target, MDB_TGT_F_RDWR) == -1) {
mdb_warn("failed to re-open target for writing");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
print_xdata(void *ignored, const char *name, const char *desc, size_t nbytes)
{
mdb_printf("%-24s - %s (%lu bytes)\n", name, desc, (ulong_t)nbytes);
return (0);
}
/*ARGSUSED*/
static int
cmd_xdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 0 || (flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
(void) mdb_tgt_xdata_iter(mdb.m_target, print_xdata, NULL);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_unset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
mdb_var_t *v;
size_t i;
for (i = 0; i < argc; i++) {
if (argv[i].a_type != MDB_TYPE_STRING) {
mdb_warn("bad option: arg %lu is not a string\n",
(ulong_t)i + 1);
return (DCMD_USAGE);
}
}
for (i = 0; i < argc; i++, argv++) {
if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
mdb_warn("variable '%s' not defined\n",
argv->a_un.a_str);
else
mdb_nv_remove(&mdb.m_nv, v);
}
return (DCMD_OK);
}
#ifndef _KMDB
/*ARGSUSED*/
static int
cmd_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t opt_e = FALSE, opt_d = FALSE;
const char *filename = NULL;
int i;
i = mdb_getopts(argc, argv,
'd', MDB_OPT_SETBITS, TRUE, &opt_d,
'e', MDB_OPT_SETBITS, TRUE, &opt_e, NULL);
if ((i != argc && i != argc - 1) || (opt_d && opt_e) ||
(i != argc && argv[i].a_type != MDB_TYPE_STRING) ||
(i != argc && opt_d == TRUE) || (flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
if (mdb.m_depth != 1) {
mdb_warn("log may not be manipulated in this context\n");
return (DCMD_ABORT);
}
if (i != argc)
filename = argv[i].a_un.a_str;
/*
* If no arguments were specified, print the log file name (if any)
* and report whether the log is enabled or disabled.
*/
if (argc == 0) {
if (mdb.m_log) {
mdb_printf("%s: logging to \"%s\" is currently %s\n",
mdb.m_pname, IOP_NAME(mdb.m_log),
mdb.m_flags & MDB_FL_LOG ? "enabled" : "disabled");
} else
mdb_printf("%s: no log is active\n", mdb.m_pname);
return (DCMD_OK);
}
/*
* If the -d option was specified, pop the log i/o object off the
* i/o stack of stdin, stdout, and stderr.
*/
if (opt_d) {
if (mdb.m_flags & MDB_FL_LOG) {
(void) mdb_iob_pop_io(mdb.m_in);
(void) mdb_iob_pop_io(mdb.m_out);
(void) mdb_iob_pop_io(mdb.m_err);
mdb.m_flags &= ~MDB_FL_LOG;
} else
mdb_warn("logging is already disabled\n");
return (DCMD_OK);
}
/*
* The -e option is the default: (re-)enable logging by pushing
* the log i/o object on to stdin, stdout, and stderr. If we have
* a previous log file, we need to pop it and close it. If we have
* no new log file, push the previous one back on.
*/
if (filename != NULL) {
if (mdb.m_log != NULL) {
if (mdb.m_flags & MDB_FL_LOG) {
(void) mdb_iob_pop_io(mdb.m_in);
(void) mdb_iob_pop_io(mdb.m_out);
(void) mdb_iob_pop_io(mdb.m_err);
mdb.m_flags &= ~MDB_FL_LOG;
}
mdb_io_rele(mdb.m_log);
}
mdb.m_log = mdb_fdio_create_path(NULL, filename,
O_CREAT | O_APPEND | O_WRONLY, 0666);
if (mdb.m_log == NULL) {
mdb_warn("failed to open %s", filename);
return (DCMD_ERR);
}
}
if (mdb.m_log != NULL) {
mdb_iob_push_io(mdb.m_in, mdb_logio_create(mdb.m_log));
mdb_iob_push_io(mdb.m_out, mdb_logio_create(mdb.m_log));
mdb_iob_push_io(mdb.m_err, mdb_logio_create(mdb.m_log));
mdb_printf("%s: logging to \"%s\"\n", mdb.m_pname, filename);
mdb.m_log = mdb_io_hold(mdb.m_log);
mdb.m_flags |= MDB_FL_LOG;
return (DCMD_OK);
}
mdb_warn("no log file has been selected\n");
return (DCMD_ERR);
}
static int
cmd_old_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc == 0) {
mdb_arg_t arg = { MDB_TYPE_STRING, MDB_INIT_STRING("-d") };
return (cmd_log(addr, flags, 1, &arg));
}
return (cmd_log(addr, flags, argc, argv));
}
#endif
/*ARGSUSED*/
static int
cmd_load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int i, mode = MDB_MOD_LOCAL;
i = mdb_getopts(argc, argv,
#ifdef _KMDB
'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
#endif
'f', MDB_OPT_SETBITS, MDB_MOD_FORCE, &mode,
'g', MDB_OPT_SETBITS, MDB_MOD_GLOBAL, &mode,
's', MDB_OPT_SETBITS, MDB_MOD_SILENT, &mode,
NULL);
argc -= i;
argv += i;
if ((flags & DCMD_ADDRSPEC) || argc != 1 ||
argv->a_type != MDB_TYPE_STRING ||
strchr("+-", argv->a_un.a_str[0]) != NULL)
return (DCMD_USAGE);
if (mdb_module_load(argv->a_un.a_str, mode) < 0)
return (DCMD_ERR);
return (DCMD_OK);
}
static void
load_help(void)
{
mdb_printf(
#ifdef _KMDB
"-d defer load until next continue\n"
#endif
"-s load module silently\n");
}
/*ARGSUSED*/
static int
cmd_unload(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int mode = 0;
int i;
i = mdb_getopts(argc, argv,
#ifdef _KMDB
'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
#endif
NULL);
argc -= i;
argv += i;
if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
if (mdb_module_unload(argv->a_un.a_str, mode) == -1) {
mdb_warn("failed to unload %s", argv->a_un.a_str);
return (DCMD_ERR);
}
return (DCMD_OK);
}
#ifdef _KMDB
static void
unload_help(void)
{
mdb_printf(
"-d defer unload until next continue\n");
}
#endif
static int
cmd_dbmode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
return (DCMD_USAGE);
if (argc != 0) {
if (argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
if ((addr = mdb_dstr2mode(argv->a_un.a_str)) != MDB_DBG_HELP)
mdb_dmode(addr);
} else if (flags & DCMD_ADDRSPEC)
mdb_dmode(addr);
mdb_printf("debugging mode = 0x%04x\n", mdb.m_debug);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_version(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
#ifdef DEBUG
mdb_printf("\r%s (DEBUG)\n", mdb_conf_version());
#else
mdb_printf("\r%s\n", mdb_conf_version());
#endif
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_algol(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (mdb.m_flags & MDB_FL_ADB)
mdb_printf("No algol 68 here\n");
else
mdb_printf("No adb here\n");
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_obey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (mdb.m_flags & MDB_FL_ADB)
mdb_printf("CHAPTER 1\n");
else
mdb_printf("No Language H here\n");
return (DCMD_OK);
}
/*ARGSUSED*/
static int
print_global(void *data, const GElf_Sym *sym, const char *name,
const mdb_syminfo_t *sip, const char *obj)
{
uintptr_t value;
if (mdb_tgt_vread((mdb_tgt_t *)data, &value, sizeof (value),
(uintptr_t)sym->st_value) == sizeof (value))
mdb_printf("%s(%llr):\t%lr\n", name, sym->st_value, value);
else
mdb_printf("%s(%llr):\t?\n", name, sym->st_value);
return (0);
}
/*ARGSUSED*/
static int
cmd_globals(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 0)
return (DCMD_USAGE);
(void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
MDB_TGT_SYMTAB, MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_OBJECT |
MDB_TGT_TYPE_FUNC, print_global, mdb.m_target);
return (0);
}
/*ARGSUSED*/
static int
cmd_eval(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
if (mdb_eval(argv->a_un.a_str) == -1)
return (DCMD_ABORT);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
print_file(void *data, const GElf_Sym *sym, const char *name,
const mdb_syminfo_t *sip, const char *obj)
{
int i = *((int *)data);
mdb_printf("%d\t%s\n", i++, name);
*((int *)data) = i;
return (0);
}
/*ARGSUSED*/
static int
cmd_files(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int i = 1;
const char *obj = MDB_TGT_OBJ_EVERY;
if ((flags & DCMD_ADDRSPEC) || argc > 1)
return (DCMD_USAGE);
if (argc == 1) {
if (argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
obj = argv->a_un.a_str;
}
(void) mdb_tgt_symbol_iter(mdb.m_target, obj, MDB_TGT_SYMTAB,
MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FILE, print_file, &i);
return (DCMD_OK);
}
static const char *
map_name(const mdb_map_t *map, const char *name)
{
if (map->map_flags & MDB_TGT_MAP_HEAP)
return ("[ heap ]");
if (name != NULL && name[0] != 0)
return (name);
if (map->map_flags & MDB_TGT_MAP_SHMEM)
return ("[ shmem ]");
if (map->map_flags & MDB_TGT_MAP_STACK)
return ("[ stack ]");
if (map->map_flags & MDB_TGT_MAP_ANON)
return ("[ anon ]");
if (map->map_name != NULL)
return (map->map_name);
return ("[ unknown ]");
}
/*ARGSUSED*/
static int
print_map(void *ignored, const mdb_map_t *map, const char *name)
{
name = map_name(map, name);
mdb_printf("%?p %?p %?lx %s\n", map->map_base,
map->map_base + map->map_size, map->map_size, name);
return (0);
}
static int
cmd_mappings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
const mdb_map_t *m;
if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
return (DCMD_USAGE);
mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
"BASE", "LIMIT", "SIZE", "NAME");
if (flags & DCMD_ADDRSPEC) {
if ((m = mdb_tgt_addr_to_map(mdb.m_target, addr)) == NULL)
mdb_warn("failed to obtain mapping");
else
(void) print_map(NULL, m, NULL);
} else if (argc != 0) {
if (argv->a_type == MDB_TYPE_STRING)
m = mdb_tgt_name_to_map(mdb.m_target, argv->a_un.a_str);
else
m = mdb_tgt_addr_to_map(mdb.m_target, argv->a_un.a_val);
if (m == NULL)
mdb_warn("failed to obtain mapping");
else
(void) print_map(NULL, m, NULL);
} else if (mdb_tgt_mapping_iter(mdb.m_target, print_map, NULL) == -1)
mdb_warn("failed to iterate over mappings");
return (DCMD_OK);
}
static int
whatis_map_callback(void *wp, const mdb_map_t *map, const char *name)
{
mdb_whatis_t *w = wp;
uintptr_t cur;
name = map_name(map, name);
while (mdb_whatis_match(w, map->map_base, map->map_size, &cur))
mdb_whatis_report_address(w, cur, "in %s [%p,%p)\n",
name, map->map_base, map->map_base + map->map_size);
return (0);
}
/*ARGSUSED*/
int
whatis_run_mappings(mdb_whatis_t *w, void *ignored)
{
(void) mdb_tgt_mapping_iter(mdb.m_target, whatis_map_callback, w);
return (0);
}
/*ARGSUSED*/
static int
objects_printversion(void *ignored, const mdb_map_t *map, const char *name)
{
ctf_file_t *ctfp;
const char *version;
ctfp = mdb_tgt_name_to_ctf(mdb.m_target, name);
if (ctfp == NULL || (version = ctf_label_topmost(ctfp)) == NULL)
version = "Unknown";
mdb_printf("%-28s %s\n", name, version);
return (0);
}
/*ARGSUSED*/
static int
cmd_objects(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t opt_v = FALSE;
mdb_tgt_map_f *cb;
if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
return (DCMD_USAGE);
if (opt_v) {
cb = objects_printversion;
mdb_printf("%<u>%-28s %s%</u>\n", "NAME", "VERSION");
} else {
cb = print_map;
mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
"BASE", "LIMIT", "SIZE", "NAME");
}
if (mdb_tgt_object_iter(mdb.m_target, cb, NULL) == -1) {
mdb_warn("failed to iterate over objects");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
showrev_addversion(void *vers_nv, const mdb_map_t *ignored, const char *object)
{
ctf_file_t *ctfp;
const char *version = NULL;
char *objname;
objname = mdb_alloc(strlen(object) + 1, UM_SLEEP | UM_GC);
(void) strcpy(objname, object);
if ((ctfp = mdb_tgt_name_to_ctf(mdb.m_target, objname)) != NULL)
version = ctf_label_topmost(ctfp);
/*
* Not all objects have CTF and label data, so set version to "Unknown".
*/
if (version == NULL)
version = "Unknown";
(void) mdb_nv_insert(vers_nv, version, NULL, (uintptr_t)objname,
MDB_NV_OVERLOAD);
return (0);
}
static int
showrev_ispatch(const char *s)
{
if (s == NULL)
return (0);
if (*s == 'T')
s++; /* skip T for T-patch */
for (; *s != '\0'; s++) {
if ((*s < '0' || *s > '9') && *s != '-')
return (0);
}
return (1);
}
/*ARGSUSED*/
static int
showrev_printobject(mdb_var_t *v, void *ignored)
{
mdb_printf("%s ", MDB_NV_COOKIE(v));
return (0);
}
static int
showrev_printversion(mdb_var_t *v, void *showall)
{
const char *version = mdb_nv_get_name(v);
int patch;
patch = showrev_ispatch(version);
if (patch || (uintptr_t)showall) {
mdb_printf("%s: %s Objects: ",
(patch ? "Patch" : "Version"), version);
(void) mdb_inc_indent(2);
mdb_nv_defn_iter(v, showrev_printobject, NULL);
(void) mdb_dec_indent(2);
mdb_printf("\n");
}
return (0);
}
/*
* Display version information for each object in the system.
* Print information about patches only, unless showall is TRUE.
*/
static int
showrev_objectversions(int showall)
{
mdb_nv_t vers_nv;
(void) mdb_nv_create(&vers_nv, UM_SLEEP | UM_GC);
if (mdb_tgt_object_iter(mdb.m_target, showrev_addversion,
&vers_nv) == -1) {
mdb_warn("failed to iterate over objects");
return (DCMD_ERR);
}
mdb_nv_sort_iter(&vers_nv, showrev_printversion,
(void *)(uintptr_t)showall, UM_SLEEP | UM_GC);
return (DCMD_OK);
}
/*
* Display information similar to what showrev(1M) displays when invoked
* with no arguments.
*/
static int
showrev_sysinfo(void)
{
const char *s;
int rc;
struct utsname u;
if ((rc = mdb_tgt_uname(mdb.m_target, &u)) != -1) {
mdb_printf("Hostname: %s\n", u.nodename);
mdb_printf("Release: %s\n", u.release);
mdb_printf("Kernel architecture: %s\n", u.machine);
}
/*
* Match the order of the showrev(1M) output and put "Application
* architecture" before "Kernel version"
*/
if ((s = mdb_tgt_isa(mdb.m_target)) != NULL)
mdb_printf("Application architecture: %s\n", s);
if (rc != -1)
mdb_printf("Kernel version: %s %s %s %s\n",
u.sysname, u.release, u.machine, u.version);
if ((s = mdb_tgt_platform(mdb.m_target)) != NULL)
mdb_printf("Platform: %s\n", s);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_showrev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t opt_p = FALSE, opt_v = FALSE;
if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
'p', MDB_OPT_SETBITS, TRUE, &opt_p,
'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
return (DCMD_USAGE);
if (opt_p || opt_v)
return (showrev_objectversions(opt_v));
else
return (showrev_sysinfo());
}
#ifdef __sparc
static void
findsym_output(uintptr_t *symlist, uintptr_t value, uintptr_t location)
{
uintptr_t *symbolp;
for (symbolp = symlist; *symbolp; symbolp++)
if (value == *symbolp)
mdb_printf("found %a at %a\n", value, location);
}
/*ARGSUSED*/
static int
findsym_cb(void *data, const GElf_Sym *sym, const char *name,
const mdb_syminfo_t *sip, const char *obj)
{
uint32_t *text;
int len;
int i;
int j;
uint8_t rd;
uintptr_t value;
int32_t imm13;
uint8_t op;
uint8_t op3;
uintptr_t *symlist = data;
size_t size = sym->st_size;
/*
* if the size of the symbol is 0, then this symbol must be for an
* alternate entry point or just some global label. We will,
* therefore, get back to the text that follows this symbol in
* some other symbol
*/
if (size == 0)
return (0);
if (sym->st_shndx == SHN_UNDEF)
return (0);
text = alloca(size);
if (mdb_vread(text, size, sym->st_value) == -1) {
mdb_warn("failed to read text for %s", name);
return (0);
}
len = size / 4;
for (i = 0; i < len; i++) {
if (!IS_SETHI(text[i]))
continue;
rd = RD(text[i]);
value = IMM22(text[i]) << 10;
/*
* see if we already have a match with just the sethi
*/
findsym_output(symlist, value, sym->st_value + i * 4);
/*
* search from the sethi on until we hit a relevant instr
*/
for (j = i + 1; j < len; j++) {
if ((op = OP(text[j])) & OP_ARITH_MEM_MASK) {
op3 = OP3(text[j]);
if (RS1(text[j]) != rd)
goto instr_end;
/*
* This is a simple tool; we only deal
* with operations which take immediates
*/
if (I(text[j]) == 0)
goto instr_end;
/*
* sign extend the immediate value
*/
imm13 = IMM13(text[j]);
imm13 <<= 19;
imm13 >>= 19;
if (op == OP_ARITH) {
/* arithmetic operations */
if (op3 & OP3_COMPLEX_MASK)
goto instr_end;
switch (op3 & ~OP3_CC_MASK) {
case OP3_OR:
value |= imm13;
break;
case OP3_ADD:
value += imm13;
break;
case OP3_XOR:
value ^= imm13;
break;
default:
goto instr_end;
}
} else {
/* loads and stores */
/* op3 == OP_MEM */
value += imm13;
}
findsym_output(symlist, value,
sym->st_value + j * 4);
instr_end:
/*
* if we're clobbering rd, break
*/
if (RD(text[j]) == rd)
break;
} else if (IS_SETHI(text[j])) {
if (RD(text[j]) == rd)
break;
} else if (OP(text[j]) == 1) {
/*
* see if a call clobbers an %o or %g
*/
if (rd <= R_O7)
break;
}
}
}
return (0);
}
static int
cmd_findsym(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uintptr_t *symlist;
uint_t optg = FALSE;
uint_t type;
int len, i;
i = mdb_getopts(argc, argv, 'g', MDB_OPT_SETBITS, TRUE, &optg, NULL);
argc -= i;
argv += i;
len = argc + ((flags & DCMD_ADDRSPEC) ? 1 : 0) + 1;
if (len <= 1)
return (DCMD_USAGE);
/*
* Set up a NULL-terminated symbol list, and then iterate over the
* symbol table, scanning each function for references to these symbols.
*/
symlist = mdb_alloc(len * sizeof (uintptr_t), UM_SLEEP | UM_GC);
len = 0;
for (i = 0; i < argc; i++, argv++) {
const char *str = argv->a_un.a_str;
uintptr_t value;
GElf_Sym sym;
if (argv->a_type == MDB_TYPE_STRING) {
if (strchr("+-", str[0]) != NULL)
return (DCMD_USAGE);
else if (str[0] >= '0' && str[0] <= '9')
value = mdb_strtoull(str);
else if (mdb_lookup_by_name(str, &sym) != 0) {
mdb_warn("symbol '%s' not found", str);
return (DCMD_USAGE);
} else
value = sym.st_value;
} else
value = argv[i].a_un.a_val;
if (value != NULL)
symlist[len++] = value;
}
if (flags & DCMD_ADDRSPEC)
symlist[len++] = addr;
symlist[len] = NULL;
if (optg)
type = MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_FUNC;
else
type = MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FUNC;
if (mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
MDB_TGT_SYMTAB, type, findsym_cb, symlist) == -1) {
mdb_warn("failed to iterate over symbol table");
return (DCMD_ERR);
}
return (DCMD_OK);
}
#endif /* __sparc */
static int
dis_str2addr(const char *s, uintptr_t *addr)
{
GElf_Sym sym;
if (s[0] >= '0' && s[0] <= '9') {
*addr = (uintptr_t)mdb_strtoull(s);
return (0);
}
if (mdb_tgt_lookup_by_name(mdb.m_target,
MDB_TGT_OBJ_EVERY, s, &sym, NULL) == -1) {
mdb_warn("symbol '%s' not found\n", s);
return (-1);
}
*addr = (uintptr_t)sym.st_value;
return (0);
}
static int
cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
mdb_tgt_t *tgt = mdb.m_target;
mdb_disasm_t *dis = mdb.m_disasm;
uintptr_t oaddr, naddr;
mdb_tgt_as_t as;
mdb_tgt_status_t st;
char buf[BUFSIZ];
GElf_Sym sym;
int i;
uint_t opt_f = FALSE; /* File-mode off by default */
uint_t opt_w = FALSE; /* Window mode off by default */
uint_t opt_a = FALSE; /* Raw-address mode off by default */
uint_t opt_b = FALSE; /* Address & symbols off by default */
uintptr_t n = -1UL; /* Length of window in instructions */
uintptr_t eaddr = 0; /* Ending address; 0 if limited by n */
i = mdb_getopts(argc, argv,
'f', MDB_OPT_SETBITS, TRUE, &opt_f,
'w', MDB_OPT_SETBITS, TRUE, &opt_w,
'a', MDB_OPT_SETBITS, TRUE, &opt_a,
'b', MDB_OPT_SETBITS, TRUE, &opt_b,
'n', MDB_OPT_UINTPTR, &n, NULL);
/*
* Disgusting argument post-processing ... basically the idea is to get
* the target address into addr, which we do by using the specified
* expression value, looking up a string as a symbol name, or by
* using the address specified as dot.
*/
if (i != argc) {
if (argc != 0 && (argc - i) == 1) {
if (argv[i].a_type == MDB_TYPE_STRING) {
if (argv[i].a_un.a_str[0] == '-')
return (DCMD_USAGE);
if (dis_str2addr(argv[i].a_un.a_str, &addr))
return (DCMD_ERR);
} else
addr = argv[i].a_un.a_val;
} else
return (DCMD_USAGE);
}
/*
* If we're not in window mode yet, and some type of arguments were
* specified, see if the address corresponds nicely to a function.
* If not, turn on window mode; otherwise disassemble the function.
*/
if (opt_w == FALSE && (argc != i || (flags & DCMD_ADDRSPEC))) {
if (mdb_tgt_lookup_by_addr(tgt, addr,
MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0 &&
GELF_ST_TYPE(sym.st_info) == STT_FUNC) {
/*
* If the symbol has a size then set our end address to
* be the end of the function symbol we just located.
*/
if (sym.st_size != 0)
eaddr = addr + (uintptr_t)sym.st_size;
} else
opt_w = TRUE;
}
/*
* Window-mode doesn't make sense in a loop.
*/
if (flags & DCMD_LOOP)
opt_w = FALSE;
/*
* If -n was explicit, limit output to n instructions;
* otherwise set n to some reasonable default
*/
if (n != -1UL)
eaddr = 0;
else
n = 10;
/*
* If the state is IDLE (i.e. no address space), turn on -f.
*/
if (mdb_tgt_status(tgt, &st) == 0 && st.st_state == MDB_TGT_IDLE)
opt_f = TRUE;
if (opt_f)
as = MDB_TGT_AS_FILE;
else
as = MDB_TGT_AS_VIRT;
if (opt_w == FALSE) {
n++;
while ((eaddr == 0 && n-- != 0) || (addr < eaddr)) {
naddr = mdb_dis_ins2str(dis, tgt, as,
buf, sizeof (buf), addr);
if (naddr == addr)
return (DCMD_ERR);
if (opt_a)
mdb_printf("%-#32p%8T%s\n", addr, buf);
else if (opt_b)
mdb_printf("%-#?p %-#32a%8T%s\n",
addr, addr, buf);
else
mdb_printf("%-#32a%8T%s\n", addr, buf);
addr = naddr;
}
} else {
#ifdef __sparc
if (addr & 0x3) {
mdb_warn("address is not properly aligned\n");
return (DCMD_ERR);
}
#endif
for (oaddr = mdb_dis_previns(dis, tgt, as, addr, n);
oaddr < addr; oaddr = naddr) {
naddr = mdb_dis_ins2str(dis, tgt, as,
buf, sizeof (buf), oaddr);
if (naddr == oaddr)
return (DCMD_ERR);
if (opt_a)
mdb_printf("%-#32p%8T%s\n", oaddr, buf);
else if (opt_b)
mdb_printf("%-#?p %-#32a%8T%s\n",
oaddr, oaddr, buf);
else
mdb_printf("%-#32a%8T%s\n", oaddr, buf);
}
if ((naddr = mdb_dis_ins2str(dis, tgt, as,
buf, sizeof (buf), addr)) == addr)
return (DCMD_ERR);
mdb_printf("%<b>");
mdb_flush();
if (opt_a)
mdb_printf("%-#32p%8T%s%", addr, buf);
else if (opt_b)
mdb_printf("%-#?p %-#32a%8T%s", addr, addr, buf);
else
mdb_printf("%-#32a%8T%s%", addr, buf);
mdb_printf("%</b>\n");
for (addr = naddr; n-- != 0; addr = naddr) {
naddr = mdb_dis_ins2str(dis, tgt, as,
buf, sizeof (buf), addr);
if (naddr == addr)
return (DCMD_ERR);
if (opt_a)
mdb_printf("%-#32p%8T%s\n", addr, buf);
else if (opt_b)
mdb_printf("%-#?p %-#32a%8T%s\n",
addr, addr, buf);
else
mdb_printf("%-#32a%8T%s\n", addr, buf);
}
}
mdb_set_dot(addr);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
walk_step(uintptr_t addr, const void *data, void *private)
{
mdb_printf("%#lr\n", addr);
return (WALK_NEXT);
}
static int
cmd_walk(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int status;
if (argc < 1 || argc > 2 || argv[0].a_type != MDB_TYPE_STRING ||
argv[argc - 1].a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
if (argc > 1) {
const char *name = argv[1].a_un.a_str;
mdb_var_t *v = mdb_nv_lookup(&mdb.m_nv, name);
const char *p;
if (v != NULL && (v->v_flags & MDB_NV_RDONLY) != 0) {
mdb_warn("variable %s is read-only\n", name);
return (DCMD_ABORT);
}
if (v == NULL && (p = strbadid(name)) != NULL) {
mdb_warn("'%c' may not be used in a variable "
"name\n", *p);
return (DCMD_ABORT);
}
if (v == NULL && (v = mdb_nv_insert(&mdb.m_nv,
name, NULL, 0, 0)) == NULL)
return (DCMD_ERR);
/*
* If there already exists a vcb for this variable, we may be
* calling ::walk in a loop. We only create a vcb for this
* variable on the first invocation.
*/
if (mdb_vcb_find(v, mdb.m_frame) == NULL)
mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
}
if (flags & DCMD_ADDRSPEC)
status = mdb_pwalk(argv->a_un.a_str, walk_step, NULL, addr);
else
status = mdb_walk(argv->a_un.a_str, walk_step, NULL);
if (status == -1) {
mdb_warn("failed to perform walk");
return (DCMD_ERR);
}
return (DCMD_OK);
}
static int
cmd_walk_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
const mdb_arg_t *argv)
{
if (argc > 1)
return (1);
if (argc == 1) {
ASSERT(argv[0].a_type == MDB_TYPE_STRING);
return (mdb_tab_complete_walker(mcp, argv[0].a_un.a_str));
}
if (argc == 0 && flags & DCMD_TAB_SPACE)
return (mdb_tab_complete_walker(mcp, NULL));
return (1);
}
static ssize_t
mdb_partial_xread(void *buf, size_t nbytes, uintptr_t addr, void *arg)
{
ssize_t (*fp)(mdb_tgt_t *, const void *, size_t, uintptr_t) =
(ssize_t (*)(mdb_tgt_t *, const void *, size_t, uintptr_t))arg;
return (fp(mdb.m_target, buf, nbytes, addr));
}
/* ARGSUSED3 */
static ssize_t
mdb_partial_pread(void *buf, size_t nbytes, physaddr_t addr, void *arg)
{
return (mdb_tgt_pread(mdb.m_target, buf, nbytes, addr));
}
static int
cmd_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t dflags =
MDB_DUMP_ALIGN | MDB_DUMP_NEWDOT | MDB_DUMP_ASCII | MDB_DUMP_HEADER;
uint_t phys = FALSE;
uint_t file = FALSE;
uintptr_t group = 4;
uintptr_t length = 0;
uintptr_t width = 1;
mdb_tgt_status_t st;
int error;
if (mdb_getopts(argc, argv,
'e', MDB_OPT_SETBITS, MDB_DUMP_ENDIAN, &dflags,
'f', MDB_OPT_SETBITS, TRUE, &file,
'g', MDB_OPT_UINTPTR, &group,
'l', MDB_OPT_UINTPTR, &length,
'p', MDB_OPT_SETBITS, TRUE, &phys,
'q', MDB_OPT_CLRBITS, MDB_DUMP_ASCII, &dflags,
'r', MDB_OPT_SETBITS, MDB_DUMP_RELATIVE, &dflags,
's', MDB_OPT_SETBITS, MDB_DUMP_SQUISH, &dflags,
't', MDB_OPT_SETBITS, MDB_DUMP_TRIM, &dflags,
'u', MDB_OPT_CLRBITS, MDB_DUMP_ALIGN, &dflags,
'v', MDB_OPT_SETBITS, MDB_DUMP_PEDANT, &dflags,
'w', MDB_OPT_UINTPTR, &width, NULL) != argc)
return (DCMD_USAGE);
if ((phys && file) ||
(width == 0) || (width > 0x10) ||
(group == 0) || (group > 0x100) ||
(mdb.m_dcount > 1 && length > 0))
return (DCMD_USAGE);
if (length == 0)
length = mdb.m_dcount;
/*
* If neither -f nor -p were specified and the state is IDLE (i.e. no
* address space), turn on -p. This is so we can read large files.
*/
if (phys == FALSE && file == FALSE && mdb_tgt_status(mdb.m_target,
&st) == 0 && st.st_state == MDB_TGT_IDLE)
phys = TRUE;
dflags |= MDB_DUMP_GROUP(group) | MDB_DUMP_WIDTH(width);
if (phys)
error = mdb_dump64(mdb_get_dot(), length, dflags,
mdb_partial_pread, NULL);
else if (file)
error = mdb_dumpptr(addr, length, dflags,
mdb_partial_xread, (void *)mdb_tgt_fread);
else
error = mdb_dumpptr(addr, length, dflags,
mdb_partial_xread, (void *)mdb_tgt_vread);
return (((flags & DCMD_LOOP) || (error == -1)) ? DCMD_ABORT : DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_echo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (flags & DCMD_ADDRSPEC)
return (DCMD_USAGE);
for (; argc-- != 0; argv++) {
if (argv->a_type == MDB_TYPE_STRING)
mdb_printf("%s ", argv->a_un.a_str);
else
mdb_printf("%llr ", argv->a_un.a_val);
}
mdb_printf("\n");
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_head(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint64_t cnt = 10;
const char *c;
mdb_pipe_t p;
if (!flags & DCMD_PIPE)
return (DCMD_USAGE);
if (argc == 1 || argc == 2) {
const char *num;
if (argc == 1) {
if (argv[0].a_type != MDB_TYPE_STRING ||
*argv[0].a_un.a_str != '-')
return (DCMD_USAGE);
num = argv[0].a_un.a_str + 1;
} else {
if (argv[0].a_type != MDB_TYPE_STRING ||
strcmp(argv[0].a_un.a_str, "-n") != 0)
return (DCMD_USAGE);
num = argv[1].a_un.a_str;
}
for (cnt = 0, c = num; *c != '\0' && isdigit(*c); c++)
cnt = cnt * 10 + (*c - '0');
if (*c != '\0')
return (DCMD_USAGE);
} else if (argc != 0) {
return (DCMD_USAGE);
}
mdb_get_pipe(&p);
if (p.pipe_data == NULL)
return (DCMD_OK);
p.pipe_len = MIN(p.pipe_len, cnt);
if (flags & DCMD_PIPE_OUT) {
mdb_set_pipe(&p);
} else {
while (p.pipe_len-- > 0)
mdb_printf("%lx\n", *p.pipe_data++);
}
return (DCMD_OK);
}
static void
head_help(void)
{
mdb_printf(
"-n num\n or\n"
"-num pass only the first `num' elements in the pipe.\n"
"\n%<b>Note:%</b> `num' is a decimal number.\n");
}
static int
cmd_typeset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int add_tag = 0, del_tag = 0;
const char *p;
mdb_var_t *v;
if (argc == 0)
return (cmd_vars(addr, flags, argc, argv));
if (argv->a_type == MDB_TYPE_STRING && (argv->a_un.a_str[0] == '-' ||
argv->a_un.a_str[0] == '+')) {
if (argv->a_un.a_str[1] != 't')
return (DCMD_USAGE);
if (argv->a_un.a_str[0] == '-')
add_tag++;
else
del_tag++;
argc--;
argv++;
}
if (!(flags & DCMD_ADDRSPEC))
addr = 0; /* set variables to zero unless explicit addr given */
for (; argc-- != 0; argv++) {
if (argv->a_type != MDB_TYPE_STRING)
continue;
if (argv->a_un.a_str[0] == '-' || argv->a_un.a_str[0] == '+') {
mdb_warn("ignored bad option -- %s\n",
argv->a_un.a_str);
continue;
}
if ((p = strbadid(argv->a_un.a_str)) != NULL) {
mdb_warn("'%c' may not be used in a variable "
"name\n", *p);
return (DCMD_ERR);
}
if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL) {
v = mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str,
NULL, addr, 0);
} else if (flags & DCMD_ADDRSPEC)
mdb_nv_set_value(v, addr);
if (v != NULL) {
if (add_tag)
v->v_flags |= MDB_NV_TAGGED;
if (del_tag)
v->v_flags &= ~MDB_NV_TAGGED;
}
}
return (DCMD_OK);
}
#ifndef _KMDB
/*ARGSUSED*/
static int
cmd_context(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (argc != 0 || !(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
if (mdb_tgt_setcontext(mdb.m_target, (void *)addr) == 0)
return (DCMD_OK);
return (DCMD_ERR);
}
#endif
/*ARGSUSED*/
static int
cmd_prompt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
const char *p = "";
if (argc != 0) {
if (argc > 1 || argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
p = argv->a_un.a_str;
}
(void) mdb_set_prompt(p);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_term(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
mdb_printf("%s\n", mdb.m_termtype);
return (DCMD_OK);
}
/*ARGSUSED*/
static int
cmd_vtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
physaddr_t pa;
mdb_tgt_as_t as = MDB_TGT_AS_VIRT;
if (mdb_getopts(argc, argv, 'a', MDB_OPT_UINTPTR, (uintptr_t *)&as,
NULL) != argc)
return (DCMD_USAGE);
if (mdb_tgt_vtop(mdb.m_target, as, addr, &pa) == -1) {
mdb_warn("failed to get physical mapping");
return (DCMD_ERR);
}
if (flags & DCMD_PIPE_OUT)
mdb_printf("%llr\n", pa);
else
mdb_printf("virtual %lr mapped to physical %llr\n", addr, pa);
return (DCMD_OK);
}
#define EVENTS_OPT_A 0x1 /* ::events -a (show all events) */
#define EVENTS_OPT_V 0x2 /* ::events -v (verbose display) */
static const char *
event_action(const mdb_tgt_spec_desc_t *sp)
{
if (!(sp->spec_flags & MDB_TGT_SPEC_HIDDEN) && sp->spec_data != NULL)
return (sp->spec_data);
return ("-");
}
static void
print_evsep(void)
{
static const char dash20[] = "--------------------";
mdb_printf("----- - -- -- -- %s%s --%s\n", dash20, dash20, dash20);
}
/*ARGSUSED*/
static int
print_event(mdb_tgt_t *t, void *private, int vid, void *data)
{
uint_t opts = (uint_t)(uintptr_t)private;
mdb_tgt_spec_desc_t sp;
char s1[41], s2[22];
const char *s2str;
int visible;
(void) mdb_tgt_vespec_info(t, vid, &sp, s1, sizeof (s1));
visible = !(sp.spec_flags & (MDB_TGT_SPEC_HIDDEN|MDB_TGT_SPEC_DELETED));
if ((opts & EVENTS_OPT_A) || visible) {
int encoding = (!(sp.spec_flags & MDB_TGT_SPEC_DISABLED)) |
(!(sp.spec_flags & MDB_TGT_SPEC_MATCHED) << 1);
char ldelim = "<<(["[encoding];
char rdelim = ">>)]"[encoding];
char state = "0-+*!"[sp.spec_state];
char tflag = "T "[!(sp.spec_flags & MDB_TGT_SPEC_STICKY)];
char aflag = "d "[!(sp.spec_flags & MDB_TGT_SPEC_AUTODIS)];
if (sp.spec_flags & MDB_TGT_SPEC_TEMPORARY)
tflag = 't'; /* TEMP takes precedence over STICKY */
if (sp.spec_flags & MDB_TGT_SPEC_AUTODEL)
aflag = 'D'; /* AUTODEL takes precedence over AUTODIS */
if (sp.spec_flags & MDB_TGT_SPEC_AUTOSTOP)
aflag = 's'; /* AUTOSTOP takes precedence over both */
if (opts & EVENTS_OPT_V) {
if (sp.spec_state == MDB_TGT_SPEC_IDLE ||
sp.spec_state == MDB_TGT_SPEC_ERROR)
s2str = mdb_strerror(sp.spec_errno);
else
s2str = "-";
} else
s2str = event_action(&sp);
if (mdb_snprintf(s2, sizeof (s2), "%s", s2str) >= sizeof (s2))
(void) strabbr(s2, sizeof (s2));
if (vid > -10 && vid < 10)
mdb_printf("%c%2d %c", ldelim, vid, rdelim);
else
mdb_printf("%c%3d%c", ldelim, vid, rdelim);
mdb_printf(" %c %c%c %2u %2u %-40s %-21s\n",
state, tflag, aflag, sp.spec_hits, sp.spec_limit, s1, s2);
if (opts & EVENTS_OPT_V) {
mdb_printf("%-17s%s\n", "", event_action(&sp));
print_evsep();
}
}
return (0);
}
/*ARGSUSED*/
static int
cmd_events(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t opts = 0;
if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
'a', MDB_OPT_SETBITS, EVENTS_OPT_A, &opts,
'v', MDB_OPT_SETBITS, EVENTS_OPT_V, &opts, NULL) != argc)
return (DCMD_USAGE);
if (opts & EVENTS_OPT_V) {
mdb_printf(" ID S TA HT LM %-40s %-21s\n%-17s%s\n",
"Description", "Status", "", "Action");
} else {
mdb_printf(" ID S TA HT LM %-40s %-21s\n",
"Description", "Action");
}
print_evsep();
return (mdb_tgt_vespec_iter(mdb.m_target, print_event,
(void *)(uintptr_t)opts));
}
static int
tgt_status(const mdb_tgt_status_t *tsp)