| /* |
| * 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 *)˙ |
| 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) |
|