summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsherrym <none@none>2005-07-14 17:18:02 -0700
committersherrym <none@none>2005-07-14 17:18:02 -0700
commit159eb9e1f62c284a23f12ecf781cdf352c9158f8 (patch)
tree62423ef26d29a27f51949ea18266c885db94c1ef
parent2bac1547bd2542defca8ad987edd71bbfc2edd14 (diff)
downloadillumos-joyent-159eb9e1f62c284a23f12ecf781cdf352c9158f8.tar.gz
6260772 mdb_ctf_func_info needs to be more sophisticated
6290893 Need to print out function arguments when they are available
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf.c29
-rw-r--r--usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c246
2 files changed, 267 insertions, 8 deletions
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
index 2de57f5978..82c304f7be 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
@@ -297,6 +297,7 @@ mdb_ctf_module_lookup(const char *name, mdb_ctf_id_t *p)
return (0);
}
+/*ARGSUSED*/
int
mdb_ctf_func_info(const GElf_Sym *symp, const mdb_syminfo_t *sip,
mdb_ctf_funcinfo_t *mfp)
@@ -304,20 +305,40 @@ mdb_ctf_func_info(const GElf_Sym *symp, const mdb_syminfo_t *sip,
ctf_file_t *fp = NULL;
ctf_funcinfo_t f;
mdb_tgt_t *t = mdb.m_target;
+ char name[MDB_SYM_NAMLEN];
+ const mdb_map_t *mp;
+ mdb_syminfo_t si;
+ int err;
- if (symp == NULL || sip == NULL || mfp == NULL)
+ if (symp == NULL || mfp == NULL)
return (set_errno(EINVAL));
- if ((fp = mdb_tgt_addr_to_ctf(t, symp->st_value)) == NULL)
+ /*
+ * In case the input symbol came from a merged or private symbol table,
+ * re-lookup the address as a symbol, and then perform a fully scoped
+ * lookup of that symbol name to get the mdb_syminfo_t for its CTF.
+ */
+ if ((fp = mdb_tgt_addr_to_ctf(t, symp->st_value)) == NULL ||
+ (mp = mdb_tgt_addr_to_map(t, symp->st_value)) == NULL ||
+ mdb_tgt_lookup_by_addr(t, symp->st_value, MDB_TGT_SYM_FUZZY,
+ name, sizeof (name), NULL, NULL) != 0)
+ return (-1); /* errno is set for us */
+
+ if (strchr(name, '`') != NULL)
+ err = mdb_tgt_lookup_by_scope(t, name, NULL, &si);
+ else
+ err = mdb_tgt_lookup_by_name(t, mp->map_name, name, NULL, &si);
+
+ if (err != 0)
return (-1); /* errno is set for us */
- if (ctf_func_info(fp, sip->sym_id, &f) == CTF_ERR)
+ if (ctf_func_info(fp, si.sym_id, &f) == CTF_ERR)
return (set_errno(ctf_to_errno(ctf_errno(fp))));
set_ctf_id(&mfp->mtf_return, fp, f.ctc_return);
mfp->mtf_argc = f.ctc_argc;
mfp->mtf_flags = f.ctc_flags;
- mfp->mtf_symidx = sip->sym_id;
+ mfp->mtf_symidx = si.sym_id;
return (0);
}
diff --git a/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c b/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
index 1cdf827f98..46083adc9c 100644
--- a/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
+++ b/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
@@ -37,6 +37,7 @@
#include <mdb/mdb_debug.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_amd64util.h>
+#include <mdb/mdb_ctf.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb.h>
@@ -139,6 +140,169 @@ mdb_amd64_printregs(const mdb_tgt_gregset_t *gregs)
kregs[KREG_ERR], (kregs[KREG_GS] & 0xffff), kregs[KREG_GSBASE]);
}
+
+
+/*
+ * Sun Studio 10 patch compiler and gcc 3.4.3 Sun branch implemented a
+ * "-save_args" option on amd64. When the option is specified, INTEGER
+ * type function arguments passed via registers will be saved on the stack
+ * immediately after %rbp, and will not be modified through out the life
+ * of the routine.
+ *
+ * +--------+
+ * %rbp --> | %rbp |
+ * +--------+
+ * -0x8(%rbp) | %rdi |
+ * +--------+
+ * -0x10(%rbp) | %rsi |
+ * +--------+
+ * -0x18(%rbp) | %rdx |
+ * +--------+
+ * -0x20(%rbp) | %rcx |
+ * +--------+
+ * -0x28(%rbp) | %r8 |
+ * +--------+
+ * -0x30(%rbp) | %r9 |
+ * +--------+
+ *
+ *
+ * For example, for the following function,
+ *
+ * void
+ * foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
+ * {
+ * ...
+ * }
+ *
+ * Disassembled code will look something like the following:
+ *
+ * pushq %rbp
+ * movq %rsp, %rbp
+ * subq $imm8, %rsp **
+ * movq %rdi, -0x8(%rbp)
+ * movq %rsi, -0x10(%rbp)
+ * movq %rdx, -0x18(%rbp)
+ * movq %rcx, -0x20(%rbp)
+ * movq %r8, -0x28(%rbp)
+ * movq %r9, -0x30(%rbp)
+ * ...
+ * or
+ * pushq %rbp
+ * movq %rsp, %rbp
+ * subq $imm8, %rsp **
+ * movq %r9, -0x30(%rbp)
+ * movq %r8, -0x28(%rbp)
+ * movq %rcx, -0x20(%rbp)
+ * movq %rdx, -0x18(%rbp)
+ * movq %rsi, -0x10(%rbp)
+ * movq %rdi, -0x8(%rbp)
+ * ...
+ *
+ * **: The space being reserved is in addition to what the current
+ * function prolog already reserves.
+ *
+ * If there are odd number of arguments to a function, additional space is
+ * reserved on the stack to maintain 16-byte alignment. For example,
+ *
+ * argc == 0: no argument saving.
+ * argc == 3: save 3, but space for 4 is reserved
+ * argc == 7: save 6.
+ */
+
+/*
+ * The longest instruction sequence in bytes before all 6 arguments are
+ * saved on the stack. This value depends on compiler implementation,
+ * therefore it should be examined periodically to guarantee accuracy.
+ */
+#define SEQ_LEN 80
+
+/*
+ * Size of the instruction sequence arrays. It should correspond to
+ * the maximum number of arguments passed via registers.
+ */
+#define INSTR_ARRAY_SIZE 6
+
+#define INSTR4(ins, off) \
+ (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
+ (ins[(off) + 3] << 24))
+
+/*
+ * Sun Studio 10 patch implementation saves %rdi first;
+ * GCC 3.4.3 Sun branch implementation saves them in reverse order.
+ */
+static const uint32_t save_instr[INSTR_ARRAY_SIZE] = {
+ 0xf87d8948, /* movq %rdi, -0x8(%rbp) */
+ 0xf0758948, /* movq %rsi, -0x10(%rbp) */
+ 0xe8558948, /* movq %rdx, -0x18(%rbp) */
+ 0xe04d8948, /* movq %rcx, -0x20(%rbp) */
+ 0xd845894c, /* movq %r8, -0x28(%rbp) */
+ 0xd04d894c /* movq %r9, -0x30(%rbp) */
+};
+
+static const uint32_t save_fp_instr[2] = {
+ 0xe5894855, /* pushq %rbp; movq %rsp,%rbp, encoding 1 */
+ 0xec8b4855 /* pushq %rbp; movq %rsp,%rbp, encoding 2 */
+};
+
+/*
+ * Look for the above instruction sequences as indicators for register
+ * arguments being available on the stack.
+ */
+static int
+is_argsaved(mdb_tgt_t *t, uintptr_t fstart, uint64_t size, uint_t argc,
+ int start_index)
+{
+ uint8_t ins[SEQ_LEN];
+ int i, j;
+ uint32_t n;
+
+ size = MIN(size, SEQ_LEN);
+ argc = MIN((start_index + argc), INSTR_ARRAY_SIZE);
+
+ if (mdb_tgt_vread(t, ins, size, fstart) != size)
+ return (0);
+
+ /*
+ * Make sure framepointer has been saved.
+ */
+ n = INSTR4(ins, 0);
+ for (i = 0; i < 2; i++) {
+ if (n == save_fp_instr[i])
+ break;
+ }
+
+ if (i >= 2)
+ return (0);
+
+ /*
+ * Compare against Sun Studio implementation
+ */
+ for (i = 8, j = start_index; i < size - 4; i++) {
+ n = INSTR4(ins, i);
+
+ if (n == save_instr[j]) {
+ i += 3;
+ if (++j >= argc)
+ return (1);
+ }
+ }
+
+ /*
+ * Compare against GCC implementation
+ */
+ for (i = 8, j = argc - 1; i < size - 4; i++) {
+ n = INSTR4(ins, i);
+
+ if (n == save_instr[j]) {
+ i += 3;
+ if (--j < start_index)
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
int
mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
mdb_tgt_stack_f *func, void *arg)
@@ -146,6 +310,10 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
mdb_tgt_gregset_t gregs;
kreg_t *kregs = &gregs.kregs[0];
int got_pc = (gsp->kregs[KREG_RIP] != 0);
+ uint_t argc, reg_argc;
+ long fr_argv[32];
+ int start_index; /* index to save_instr where to start comparison */
+ int i;
struct {
uintptr_t fr_savfp;
@@ -154,18 +322,77 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
uintptr_t fp = gsp->kregs[KREG_RBP];
uintptr_t pc = gsp->kregs[KREG_RIP];
+ uintptr_t curpc;
+
+ ssize_t size;
+
+ GElf_Sym s;
+ mdb_syminfo_t sip;
+ mdb_ctf_funcinfo_t mfp;
bcopy(gsp, &gregs, sizeof (gregs));
while (fp != 0) {
+ curpc = pc;
+
if (fp & (STACK_ALIGN - 1))
return (set_errno(EMDB_STKALIGN));
- bzero(&fr, sizeof (fr));
- (void) mdb_tgt_vread(t, &fr, sizeof (fr), fp);
+ if (mdb_tgt_vread(t, &fr, sizeof (fr), fp) != sizeof (fr))
+ return (-1); /* errno has been set for us */
+
+ if ((mdb_tgt_lookup_by_addr(t, pc, MDB_TGT_SYM_FUZZY,
+ NULL, 0, &s, &sip) == 0) &&
+ (mdb_ctf_func_info(&s, &sip, &mfp) == 0)) {
+ int return_type = mdb_ctf_type_kind(mfp.mtf_return);
+ argc = mfp.mtf_argc;
+ /*
+ * If the function returns a structure or union,
+ * %rdi contains the address in which to store the
+ * return value rather than for an argument.
+ */
+ if (return_type == CTF_K_STRUCT ||
+ return_type == CTF_K_UNION)
+ start_index = 1;
+ else
+ start_index = 0;
+ } else {
+ argc = 0;
+ }
- if (got_pc && func(arg, pc, 0, NULL, &gregs) != 0)
+ if (argc != 0 && is_argsaved(t, s.st_value, s.st_size,
+ argc, start_index)) {
+
+ /* Upto to 6 arguments are passed via registers */
+ reg_argc = MIN(6, mfp.mtf_argc);
+ size = reg_argc * sizeof (long);
+
+ if (mdb_tgt_vread(t, fr_argv, size, (fp - size))
+ != size)
+ return (-1); /* errno has been set for us */
+
+ /*
+ * Arrange the arguments in the right order for
+ * printing.
+ */
+ for (i = 0; i < (reg_argc >> 1); i++) {
+ long t = fr_argv[i];
+
+ fr_argv[i] = fr_argv[reg_argc - i - 1];
+ fr_argv[reg_argc - i - 1] = t;
+ }
+
+ if (argc > 6) {
+ size = (argc - 6) * sizeof (long);
+ if (mdb_tgt_vread(t, &fr_argv[6], size,
+ fp + sizeof (fr)) != size)
+ return (-1); /* errno has been set */
+ }
+ } else
+ argc = 0;
+
+ if (got_pc && func(arg, pc, argc, fr_argv, &gregs) != 0)
break;
kregs[KREG_RSP] = kregs[KREG_RBP];
@@ -173,6 +400,9 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
kregs[KREG_RBP] = fp = fr.fr_savfp;
kregs[KREG_RIP] = pc = fr.fr_savpc;
+ if (curpc == pc)
+ break;
+
got_pc = (pc != 0);
}
@@ -282,7 +512,15 @@ int
mdb_amd64_kvm_framev(void *arglim, uintptr_t pc, uint_t argc, const long *argv,
const mdb_tgt_gregset_t *gregs)
{
- argc = MIN(argc, (uintptr_t)arglim);
+ /*
+ * Historically adb limited stack trace argument display to a fixed-
+ * size number of arguments since no symbolic debugging info existed.
+ * On amd64 we can detect the true number of saved arguments so only
+ * respect an arglim of zero; otherwise display the entire argv[].
+ */
+ if (arglim == 0)
+ argc = 0;
+
mdb_printf("%0?lr %a(", gregs->kregs[KREG_RBP], pc);
if (argc != 0) {