[DTrace-devel] [PATCH] Add annotations for dctx->X and mst->X members
eugene.loh at oracle.com
eugene.loh at oracle.com
Tue Mar 29 22:24:02 UTC 2022
From: Eugene Loh <eugene.loh at oracle.com>
WIP
This is a version of Kris's recent patch but it allows certain
intervening instructions to appear in the expected instruction
stream in order to capture the bulk of the mst->X cases we will
see once the alloca() patch series has been applied.
I can clean this patch up if there is general buy-in to the idea.
---
libdtrace/dt_dis.c | 198 ++++++++++++++++++++++-
test/unittest/disasm/tst.ann-dctx-mst.r | 11 ++
test/unittest/disasm/tst.ann-dctx-mst.sh | 33 ++++
3 files changed, 237 insertions(+), 5 deletions(-)
create mode 100644 test/unittest/disasm/tst.ann-dctx-mst.r
create mode 100755 test/unittest/disasm/tst.ann-dctx-mst.sh
diff --git a/libdtrace/dt_dis.c b/libdtrace/dt_dis.c
index 792cc99e..1871d421 100644
--- a/libdtrace/dt_dis.c
+++ b/libdtrace/dt_dis.c
@@ -82,7 +82,185 @@ dt_dis_varname_off(const dtrace_difo_t *dp, uint_t off, uint_t scope, uint_t add
}
/*
- * Check if we are loading from the gvar, lvar, or strtab BPF map.
+ * Check if we are accessing the DTrace context.
+ *
+ * The sequence of instructions we are looking for is:
+ *
+ * insn code dst src offset imm
+ * -1: ld reg %fp DT_STK_DCTX 00000000
+ * 0: ld dst reg DCTX_*VARS 00000000
+ *
+ * where reg can be the same or different as dst.
+ */
+static int
+dt_dis_refname_dctx(const dtrace_difo_t *dp, const struct bpf_insn *in,
+ uint_t addr, int n, FILE *fp)
+{
+ static const char *dctx_member[DCTX_SIZE] = {
+ [0 ... DCTX_SIZE - 1] = NULL,
+ [DCTX_CTX] = "ctx",
+ [DCTX_ACT] = "act",
+ [DCTX_MST] = "mst",
+ [DCTX_BUF] = "buf",
+ [DCTX_MEM] = "mem",
+ [DCTX_STRTAB] = "strtab",
+ [DCTX_AGG] = "agg",
+ [DCTX_GVARS] = "gvars",
+ [DCTX_LVARS] = "lvars",
+ };
+ const char *s = dctx_member[in->off];
+
+ if (in[0].code != (BPF_LDX | BPF_MEM | BPF_DW) ||
+ in[0].imm != 0 ||
+ addr < 1 ||
+ in[-1].code != (BPF_LDX | BPF_MEM | BPF_DW) ||
+ in[-1].src_reg != BPF_REG_FP ||
+ in[-1].dst_reg != in->src_reg ||
+ in[-1].off != DT_STK_DCTX ||
+ in[-1].imm != 0 ||
+ in[0].off < 0 || in[0].off >= DCTX_SIZE ||
+ s == NULL)
+ return 0;
+
+ fprintf(fp, "%*s! dctx->%s\n", DT_DIS_PAD(n), "", s);
+ return 1;
+}
+
+/*
+ * Check if we are accessing the DTrace machine state.
+ *
+ * The sequence of instructions we are looking for is:
+ * code dst src offset imm
+ *
+ * ld mst %fp DT_STK_DCTX 00000000
+ *
+ * ld mst mst DCTX_MST 00000000
+ *
+ * ld reg mst DMST_* 00000000 -or-
+ * st mst reg DMST_* 00000000 -or-
+ * st mst 0 DMST_* imm
+ * where reg can be the same or different as mst.
+ *
+ * This sequence of instructions can be interrupted by
+ * any number of
+ * ld reg xxx xxxxxx 00000000
+ * st xxx xxx xxxxxx xxxxxxxx
+ * so long as reg!=mst.
+ */
+static int
+dt_dis_refname_mst(const dtrace_difo_t *dp, const struct bpf_insn *in,
+ uint_t addr, int n, FILE *fp)
+{
+ static const char *mst_members[sizeof(dt_mstate_t)] =
+ {
+ [0 ... sizeof(dt_mstate_t) - 1] = NULL,
+ [DMST_EPID] = "epid",
+ [DMST_PRID] = "prid",
+ [DMST_CLID] = "clid",
+ [DMST_TAG] = "tag",
+ [DMST_ERRNO] = "syscall_errno",
+ [DMST_SCRATCH_TOP] = "scratch_top",
+ [DMST_SCRATCH_NEXT] = "scratch_next",
+ [DMST_ERRNO] = "syscall_errno",
+ [DMST_SCALARIZER] = "scalarizer",
+ [DMST_FAULT] = "fault",
+ [DMST_TSTAMP] = "tstamp",
+ [DMST_REGS] = "regs",
+ [DMST_ARG(0)] = "arg0",
+ [DMST_ARG(1)] = "arg1",
+ [DMST_ARG(2)] = "arg2",
+ [DMST_ARG(3)] = "arg3",
+ [DMST_ARG(4)] = "arg4",
+ [DMST_ARG(5)] = "arg5",
+ [DMST_ARG(6)] = "arg6",
+ [DMST_ARG(7)] = "arg7",
+ [DMST_ARG(8)] = "arg8",
+ [DMST_ARG(9)] = "arg9",
+ };
+ int i, j, mst, off;
+
+ /* examine the current instruction and get mst and off */
+ if (BPF_CLASS(in[0].code) == BPF_LDX &&
+ BPF_MODE(in[0].code) == BPF_MEM &&
+ in[0].imm == 0) {
+ mst = in[0].src_reg;
+ off = in[0].off;
+ }
+ else if (BPF_CLASS(in[0].code) == BPF_STX &&
+ BPF_MODE(in[0].code) == BPF_MEM &&
+ in[0].imm == 0) {
+ mst = in[0].dst_reg;
+ off = in[0].off;
+ }
+ else if (BPF_CLASS(in[0].code) == BPF_ST &&
+ BPF_MODE(in[0].code) == BPF_MEM &&
+ in[0].src_reg == 0) {
+ mst = in[0].dst_reg;
+ off = in[0].off;
+ }
+ else
+ return 0;
+
+ /*
+ * Walk backwards through recent instructions.
+ * Naively, we are checking in[-1] and in[-2]. In practice,
+ * there may be some intervening instructions we can ignore.
+ * in[-i] is the actual instruction we are checking and in[-j]
+ * is the "ideal" index.
+ */
+ j = 1;
+ for (i = 1; i <= addr; i++) {
+ const char *str;
+
+ /* skip any stores */
+ if (BPF_CLASS(in[-i].code) == BPF_ST ||
+ BPF_CLASS(in[-i].code) == BPF_STX)
+ continue;
+
+ /* skip any loads to regs other than mst */
+ if (BPF_CLASS(in[-i].code) == BPF_LD ||
+ BPF_CLASS(in[-i].code) == BPF_LDX)
+ if (in[-i].dst_reg != mst)
+ continue;
+
+ /* look for the expected instruction */
+ if (j == 1) {
+ if (in[-i].code != (BPF_LDX | BPF_MEM | BPF_DW) ||
+ in[-i].dst_reg != mst ||
+ in[-i].src_reg != mst ||
+ in[-i].off != DCTX_MST ||
+ in[-i].imm != 0)
+ return 0;
+ /* advance to the next expected instruction */
+ j = 2;
+ continue;
+ }
+ if (j == 2) {
+ if (in[-i].code != (BPF_LDX | BPF_MEM | BPF_DW) ||
+ in[-i].dst_reg != mst ||
+ in[-i].src_reg != BPF_REG_FP ||
+ in[-i].off != DT_STK_DCTX ||
+ in[-i].imm != 0)
+ return 0;
+
+ if (off < 0 || off >= sizeof(dt_mstate_t))
+ return 0;
+
+ str = mst_members[off];
+ if (str != NULL)
+ fprintf(fp, "%*s! mst->%s", DT_DIS_PAD(n), "", str);
+
+ return 1;
+ }
+
+ }
+
+ return 0;
+}
+
+/*
+ * Check if we are accessing the gvars, lvars, or strtab BPF map, or the DTrace
+ * machine state.
*
* If we are loading a gvar or lvar, we want to report the variable name.
* If we are loading a string constant, we want to report its value.
@@ -116,6 +294,10 @@ dt_dis_refname(const dtrace_difo_t *dp, const struct bpf_insn *in, uint_t addr,
int dst, scope = -1, var_offset = -1;
const char *str;
+ /* check if this is a machine state (mst) field */
+ if (dt_dis_refname_mst(dp, in, addr, n, fp))
+ goto out;
+
/* make sure in[-2] and in[-1] exist */
if (addr < 2)
goto out;
@@ -259,9 +441,13 @@ dt_dis_load(const dtrace_difo_t *dp, const char *name, uint_t addr,
in->dst_reg == -(in->off - DT_STK_SPILL_BASE) / DT_STK_SLOT_SZ) {
fprintf(fp, "%*s! restore %s\n", DT_DIS_PAD(n), "",
reg(in->dst_reg));
- } else
- dt_dis_refname(dp, in, addr, n, fp);
+ return 0;
+ }
+
+ if (dt_dis_refname_dctx(dp, in, addr, n, fp))
+ return 0;
+ dt_dis_refname(dp, in, addr, n, fp);
return 0;
}
@@ -303,9 +489,10 @@ dt_dis_store(const dtrace_difo_t *dp, const char *name, uint_t addr,
in->src_reg == -(in->off - DT_STK_SPILL_BASE) / DT_STK_SLOT_SZ) {
fprintf(fp, "%*s! spill %s\n", DT_DIS_PAD(n), "",
reg(in->src_reg));
- } else
- dt_dis_refname(dp, in, addr, n, fp);
+ return 0;
+ }
+ dt_dis_refname(dp, in, addr, n, fp);
return 0;
}
@@ -319,6 +506,7 @@ dt_dis_store_imm(const dtrace_difo_t *dp, const char *name, uint_t addr,
n = fprintf(fp, "%-4s [%s%+d], %d", name, reg(in->dst_reg), in->off,
in->imm);
+ dt_dis_refname_mst(dp, in, addr, n, fp);
if (rname)
fprintf(fp, "%*s! = %s\n", DT_DIS_PAD(n), "", rname);
else
diff --git a/test/unittest/disasm/tst.ann-dctx-mst.r b/test/unittest/disasm/tst.ann-dctx-mst.r
new file mode 100644
index 00000000..da19f010
--- /dev/null
+++ b/test/unittest/disasm/tst.ann-dctx-mst.r
@@ -0,0 +1,11 @@
+79 X X 0018 00000000 lddw %rX, [%rX+24] ! dctx->buf
+79 X X 0000 00000000 lddw %rX, [%rX+0] ! dctx->ctx
+79 X X 0040 00000000 lddw %rX, [%rX+64] ! dctx->gvars
+79 X X 0048 00000000 lddw %rX, [%rX+72] ! dctx->lvars
+79 X X 0010 00000000 lddw %rX, [%rX+16] ! dctx->mst
+62 X X 0008 00000001 stw [%rX+8], 1 ! mst->clid ! = CLID
+62 X X 0000 00000003 stw [%rX+0], 3 ! mst->epid ! = EPID
+7a X X 0028 00000000 stdw [%rX+40], 0 ! mst->fault
+7b X X 0020 00000000 stdw [%rX+32], %rX ! mst->scalarizer
+79 X X 0014 00000000 lddw %rX, [%rX+20] ! mst->scratch_next
+7a X X 0030 00000000 stdw [%rX+48], 0 ! mst->tstamp
diff --git a/test/unittest/disasm/tst.ann-dctx-mst.sh b/test/unittest/disasm/tst.ann-dctx-mst.sh
new file mode 100755
index 00000000..e1c5214a
--- /dev/null
+++ b/test/unittest/disasm/tst.ann-dctx-mst.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#/
+
+dtrace=$1
+
+$dtrace $dt_flags -xdisasm=8 -qSn '
+BEGIN
+{
+ gvar = arg0;
+ self->tvar = 22;
+ this->lvar = 333;
+ x = alloca(8);
+ exit(0);
+}
+
+ERROR
+{
+ exit(1);
+}' 2>&1 | awk '/^NAME/ {
+ exit(0);
+ }
+
+ /! (dctx|mst)->/ {
+ sub(/^[^:]+: /, "");
+ sub(/ [0-9a] [0-9a] /, " X X ");
+ gsub(/%r[0-9]/, "%rX");
+ print;
+ }' | sort -u -k10
--
2.18.4
More information about the DTrace-devel
mailing list