[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