[DTrace-devel] [PATCH v2 5/6] Fix disassembly for global and local variables

eugene.loh at oracle.com eugene.loh at oracle.com
Thu Apr 1 14:49:10 PDT 2021


From: Eugene Loh <eugene.loh at oracle.com>

Recent patches have changed how global and local variables are
accessed.  This means that the disassembler, which relies on
recognizing BPF instructions to add annotations, must be updated.

Add a "struct dtrace_difv" field dtdv_offset so that variables
can be looked up by offset rather than ID.

Rename dt_dis_varname() to dt_dis_varname_id().  Add a by-offset
lookup function dt_dis_varname_off().  Eliminate dt_dis_lvarname().
Replace calls to dt_dis_lvarname() with a new dt_dis_varname()
that prints the variable name if any can be found.  Note that we
do not need to check "store_imm" instructions for a variable name,
but we must check op2imm instructions (for by-reference accesses).

Add a test, checking global and local scalars and structs.

Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
---
 include/dtrace/dif.h                 |   1 +
 libdtrace/dt_as.c                    |   1 +
 libdtrace/dt_dis.c                   | 152 ++++++++++++++++++---------
 test/unittest/dif/tst.DisVarNames.r  |  23 ++++
 test/unittest/dif/tst.DisVarNames.sh |  53 ++++++++++
 5 files changed, 180 insertions(+), 50 deletions(-)
 create mode 100644 test/unittest/dif/tst.DisVarNames.r
 create mode 100755 test/unittest/dif/tst.DisVarNames.sh

diff --git a/include/dtrace/dif.h b/include/dtrace/dif.h
index ad4d2f3a..191aa73c 100644
--- a/include/dtrace/dif.h
+++ b/include/dtrace/dif.h
@@ -50,6 +50,7 @@ typedef struct dtrace_diftype {
 typedef struct dtrace_difv {
 	uint32_t dtdv_name;
 	uint32_t dtdv_id;
+	uint32_t dtdv_offset;
 	uint8_t dtdv_kind;
 	uint8_t dtdv_scope;
 	uint32_t dtdv_insn_from;
diff --git a/libdtrace/dt_as.c b/libdtrace/dt_as.c
index 786fc22c..f648f21a 100644
--- a/libdtrace/dt_as.c
+++ b/libdtrace/dt_as.c
@@ -92,6 +92,7 @@ dt_copyvar(dt_idhash_t *dhp, dt_ident_t *idp, dtrace_hdl_t *dtp)
 
 	dvp->dtdv_name = (uint_t)stroff;
 	dvp->dtdv_id = idp->di_id;
+	dvp->dtdv_offset = idp->di_offset;
 	dvp->dtdv_flags = 0;
 
 	switch (idp->di_kind) {
diff --git a/libdtrace/dt_dis.c b/libdtrace/dt_dis.c
index d4f4ad67..7b7c39f9 100644
--- a/libdtrace/dt_dis.c
+++ b/libdtrace/dt_dis.c
@@ -45,7 +45,7 @@ reg(int r)
 }
 
 static const char *
-dt_dis_varname(const dtrace_difo_t *dp, uint_t id, uint_t scope, uint_t addr)
+dt_dis_varname_id(const dtrace_difo_t *dp, uint_t id, uint_t scope, uint_t addr)
 {
 	const dtrace_difv_t *dvp = dp->dtdo_vartab;
 	uint_t i;
@@ -63,26 +63,98 @@ dt_dis_varname(const dtrace_difo_t *dp, uint_t id, uint_t scope, uint_t addr)
 	return NULL;
 }
 
-static char *
-dt_dis_lvarname(const dtrace_difo_t *dp, int reg, int var, uint_t addr,
-		char *buf, int len)
+static const char *
+dt_dis_varname_off(const dtrace_difo_t *dp, uint_t off, uint_t scope, uint_t addr)
 {
-	if (reg == BPF_REG_FP) {
-		var = -1; /* FIXME:  need to find variable offset */
-		if (var != -1) {
-			const char	*vname;
-
-			vname = dt_dis_varname(dp, var, DIFV_SCOPE_LOCAL, addr);
-			if (vname) {
-				snprintf(buf, len, "this->%s", vname);
-				return buf;
-			}
+	uint_t i;
+
+	for (i = 0; i < dp->dtdo_varlen; i++) {
+		const dtrace_difv_t *dvp = &dp->dtdo_vartab[i];
+		if (dvp->dtdv_offset == off && dvp->dtdv_scope == scope &&
+		    dvp->dtdv_insn_from <= addr && addr <= dvp->dtdv_insn_to) {
+			if (dvp->dtdv_name < dp->dtdo_strlen)
+				return dp->dtdo_strtab + dvp->dtdv_name;
+			break;
 		}
 	}
 
 	return NULL;
 }
 
+/*
+ * Check if we are loading either the gvar or lvar BPF map.  If so,
+ * we want to report the name of the variable it is looking up.
+ * The sequence of instructions we are looking for is:
+ *         insn   code  dst  src    offset        imm
+ *          -2:    ld   dst  %fp  DT_STK_DCTX  00000000
+ *          -1:    ld   dst  dst  DCTX_*VARS   00000000
+ *           0:    ld   dst  dst  var_offset   00000000
+ *           0:    st   dst  src  var_offset   00000000
+ *           0:    add  dst    0     0         var_offset
+ * where instruction 0 is the current instruction, which may be one
+ * of the three above cases.  The three cases represent:
+ *   - load by value
+ *   - store by value
+ *   - access by reference
+ */
+static void
+dt_dis_varname(const dtrace_difo_t *dp, const struct bpf_insn *in, uint_t addr,
+	       int n, FILE *fp)
+{
+	__u8		ldcode = BPF_LDX | BPF_MEM | BPF_DW;
+	__u8		stcode = BPF_STX | BPF_MEM | BPF_DW;
+	__u8		addcode = BPF_ALU64 | BPF_ADD | BPF_K;
+	int		dst, scope, var_offset = -1;
+	const char	*vname;
+
+	/* make sure in[-2] and in[-1] exist */
+	if (addr < 2)
+		goto out;
+
+	/* get dst and scope */
+	dst = in[-1].dst_reg;
+
+	if (in[-1].off == DCTX_GVARS)
+		scope = DIFV_SCOPE_GLOBAL;
+	else if (in[-1].off == DCTX_LVARS)
+		scope = DIFV_SCOPE_LOCAL;
+	else
+		goto out;
+
+	/* check preceding instructions */
+	if (in[-2].code != ldcode ||
+	    in[-2].dst_reg != dst ||
+	    in[-2].src_reg != BPF_REG_FP ||
+	    in[-2].off != DT_STK_DCTX ||
+	    in[-2].imm != 0 ||
+	    in[-1].code != ldcode ||
+	    in[-1].src_reg != dst ||
+	    in[-1].imm != 0)
+		goto out;
+
+	/* check the current instruction and read var_offset */
+	if (in->dst_reg != dst)
+		goto out;
+	if (in->code == ldcode && in->src_reg == dst && in->imm == 0)
+		var_offset = in->off;
+	else if (in->code == stcode && in->src_reg != dst && in->imm == 0)
+		var_offset = in->off;
+	else if (in->code == addcode && in->src_reg == 0 && in->off == 0)
+		var_offset = in->imm;
+	else
+		goto out;
+
+	/* print name */
+	vname = dt_dis_varname_off(dp, var_offset, scope, addr);
+	if (vname == NULL)
+		goto out;
+	fprintf(fp, "%*s! %s%s", DT_DIS_PAD(n), "",
+		scope == DIFV_SCOPE_LOCAL ? "this->" : "", vname);
+
+out:
+	fprintf(fp, "\n");
+}
+
 /*ARGSUSED*/
 static uint_t
 dt_dis_str(const dtrace_difo_t *dp, const char *name, uint_t addr,
@@ -115,7 +187,11 @@ static uint_t
 dt_dis_op2imm(const dtrace_difo_t *dp, const char *name, uint_t addr,
 	      const struct bpf_insn *in, const char *rname, FILE *fp)
 {
-	fprintf(fp, "%-4s %s, %d\n", name, reg(in->dst_reg), in->imm);
+	int		n;
+
+	n = fprintf(fp, "%-4s %s, %d", name, reg(in->dst_reg), in->imm);
+	dt_dis_varname(dp, in, addr, n, fp);
+
 	return 0;
 }
 
@@ -150,19 +226,12 @@ static uint_t
 dt_dis_load(const dtrace_difo_t *dp, const char *name, uint_t addr,
 	      const struct bpf_insn *in, const char *rname, FILE *fp)
 {
-	char		buf[256];
-	const char	*vname;
 	int		n;
 
 	n = fprintf(fp, "%-4s %s, [%s%+d]", name, reg(in->dst_reg),
 		    reg(in->src_reg), in->off);
 
-	vname = dt_dis_lvarname(dp, in->src_reg, in->off, addr, buf,
-				sizeof(buf));
-	if (vname)
-		fprintf(fp, "%*s! %s\n", DT_DIS_PAD(n), "", vname);
-	else
-		fprintf(fp, "\n");
+	dt_dis_varname(dp, in, addr, n, fp);
 
 	return 0;
 }
@@ -193,19 +262,12 @@ static uint_t
 dt_dis_store(const dtrace_difo_t *dp, const char *name, uint_t addr,
 	     const struct bpf_insn *in, const char *rname, FILE *fp)
 {
-	char		buf[256];
-	const char	*vname;
 	int		n;
 
 	n = fprintf(fp, "%-4s [%s%+d], %s", name, reg(in->dst_reg), in->off,
 		    reg(in->src_reg));
 
-	vname = dt_dis_lvarname(dp, in->dst_reg, in->off, addr, buf,
-				sizeof(buf));
-	if (vname)
-		fprintf(fp, "%*s! %s\n", DT_DIS_PAD(n), "", vname);
-	else
-		fprintf(fp, "\n");
+	dt_dis_varname(dp, in, addr, n, fp);
 
 	return 0;
 }
@@ -215,24 +277,14 @@ static uint_t
 dt_dis_store_imm(const dtrace_difo_t *dp, const char *name, uint_t addr,
 		 const struct bpf_insn *in, const char *rname, FILE *fp)
 {
-	char		buf[256];
-	const char	*vname;
 	int		n;
 
 	n = fprintf(fp, "%-4s [%s%+d], %d", name, reg(in->dst_reg), in->off,
 		    in->imm);
 
-	vname = dt_dis_lvarname(dp, in->dst_reg, in->off, addr, buf,
-				sizeof(buf));
-	if (vname) {
-		if (rname)
-			fprintf(fp, "%*s! %s = %s\n", DT_DIS_PAD(n), "",
-				vname, rname);
-		else
-			fprintf(fp, "%*s! %s\n", DT_DIS_PAD(n), "", vname);
-	} else if (rname) {
+	if (rname)
 		fprintf(fp, "%*s! = %s\n", DT_DIS_PAD(n), "", rname);
-	} else
+	else
 		fprintf(fp, "\n");
 
 	return 0;
@@ -252,7 +304,7 @@ dt_dis_bpf_args(const dtrace_difo_t *dp, const char *fn,
 		 */
 		in--;
 		snprintf(buf, len, "%s",
-			 dt_dis_varname(dp, in->imm, DIFV_SCOPE_GLOBAL, addr));
+			 dt_dis_varname_id(dp, in->imm, DIFV_SCOPE_GLOBAL, addr));
 		return buf;
 	} else if (strcmp(fn, "dt_get_tvar") == 0 ||
 		   strcmp(fn, "dt_set_tvar") == 0) {
@@ -263,7 +315,7 @@ dt_dis_bpf_args(const dtrace_difo_t *dp, const char *fn,
 		 */
 		in--;
 		snprintf(buf, len, "self->%s",
-			 dt_dis_varname(dp, in->imm + DIF_VAR_OTHER_UBASE,
+			 dt_dis_varname_id(dp, in->imm + DIF_VAR_OTHER_UBASE,
 					DIFV_SCOPE_THREAD, addr));
 		return buf;
 	} else if (strcmp(fn, "dt_get_string") == 0) {
@@ -656,8 +708,8 @@ dt_dis_difo(const dtrace_difo_t *dp, FILE *fp, const dt_ident_t *idp,
 	}
 
 	if (dp->dtdo_varlen != 0) {
-		fprintf(fp, "\n%-16s %-4s %-3s %-3s %-11s %-4s %s\n",
-			"NAME", "ID", "KND", "SCP", "RANGE", "FLAG", "TYPE");
+		fprintf(fp, "\n%-16s %-4s %-6s %-3s %-3s %-11s %-4s %s\n",
+			"NAME", "ID", "OFFSET", "KND", "SCP", "RANGE", "FLAG", "TYPE");
 	}
 
 	for (i = 0; i < dp->dtdo_varlen; i++) {
@@ -705,9 +757,9 @@ dt_dis_difo(const dtrace_difo_t *dp, FILE *fp, const dt_ident_t *idp,
 		if (v->dtdv_flags & DIFV_F_MOD)
 			strcat(flags, "/w");
 
-		fprintf(fp, "%-16s %-4x %-3s %-3s %-11s %-4s %s\n",
-			&dp->dtdo_strtab[v->dtdv_name], v->dtdv_id, kind,
-			scope, range, flags + 1,
+		fprintf(fp, "%-16s %-4x %-6x %-3s %-3s %-11s %-4s %s\n",
+			&dp->dtdo_strtab[v->dtdv_name], v->dtdv_id,
+			v->dtdv_offset, kind, scope, range, flags + 1,
 			dt_dis_typestr(&v->dtdv_type, type, sizeof(type)));
 	}
 
diff --git a/test/unittest/dif/tst.DisVarNames.r b/test/unittest/dif/tst.DisVarNames.r
new file mode 100644
index 00000000..d7fdef90
--- /dev/null
+++ b/test/unittest/dif/tst.DisVarNames.r
@@ -0,0 +1,23 @@
+CheckVariable_Z
+CheckVariable_Y
+CheckVariable_X
+CheckVariable_A
+CheckVariable_A
+CheckVariable_A
+CheckVariable_A
+CheckVariable_B
+CheckVariable_A
+CheckVariable_C
+CheckVariable_Z
+this->CheckVariable_z
+this->CheckVariable_y
+this->CheckVariable_x
+CheckVariable_C
+this->CheckVariable_c
+this->CheckVariable_b
+this->CheckVariable_a
+this->CheckVariable_b
+CheckVariable_C
+this->CheckVariable_x
+CheckVariable_Y
+this->CheckVariable_z
diff --git a/test/unittest/dif/tst.DisVarNames.sh b/test/unittest/dif/tst.DisVarNames.sh
new file mode 100755
index 00000000..c592f883
--- /dev/null
+++ b/test/unittest/dif/tst.DisVarNames.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, 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.
+
+# ASSERTION:  That dtrace -S recognizes global and local variable names.
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+struct foo {
+    int foo_i, foo_j, foo_k;
+} CheckVariable_A,
+  CheckVariable_B,
+  CheckVariable_C;
+
+this struct foo
+  CheckVariable_a,
+  CheckVariable_b,
+  CheckVariable_c;
+
+BEGIN {
+    CheckVariable_X =
+    CheckVariable_Y =
+    CheckVariable_Z = 1;
+
+    CheckVariable_A.foo_i =
+    CheckVariable_A.foo_j =
+    CheckVariable_A.foo_k = 1;
+
+    CheckVariable_B =
+    CheckVariable_A;
+
+    CheckVariable_C =
+    CheckVariable_A;
+
+    this->CheckVariable_x =
+    this->CheckVariable_y =
+    this->CheckVariable_z = CheckVariable_Z;
+
+    this->CheckVariable_a =
+    this->CheckVariable_b =
+    this->CheckVariable_c = CheckVariable_C;
+
+    trace(this->CheckVariable_b.foo_j);
+    trace(      CheckVariable_C.foo_k);
+    trace(this->CheckVariable_x);
+    trace(      CheckVariable_Y);
+    trace(this->CheckVariable_z);
+}' |& awk '/ ! CheckVariable_/ ||
+     / ! this->CheckVariable_/ { print $NF }'
-- 
2.18.4




More information about the DTrace-devel mailing list