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

eugene.loh at oracle.com eugene.loh at oracle.com
Fri Mar 19 10:45:33 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().

Add code to recognize access of global and local variables from
the BPF instruction sequence:
      insn     code  dst  src    offset        imm
      ----     ----  ---  ---  -----------  --------
       -1:      ld   dst  %fp  DT_STK_DCTX  00000000
        0:      ld   dst  dst  DCTX_*VARS   00000000
followed by one of
       +1:      ld   dst  dst  var_offset   00000000
       +1:      st   dst  src  var_offset   00000000
       +1:      add  dst    0     0         var_offset
the last case being for access by-reference.

That is, if we have a load instruction, we see if it might be
"instruction 0".  If so, we check the preceding "instruction -1".
If we still match the pattern, we examine the following "instruction +1"
and extract the var_offset to look up the variable name.  Since the
instruction we are annotating is always a load, we can consolidate
all annotation of global and local arrays in the disassembler to
dt_dis_load().

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                   | 130 ++++++++++++++++-----------
 test/unittest/dif/tst.DisVarNames.r  |  23 +++++
 test/unittest/dif/tst.DisVarNames.sh |  42 +++++++++
 5 files changed, 145 insertions(+), 52 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..1a589bca 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,20 +63,18 @@ 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;
 		}
 	}
 
@@ -150,19 +148,68 @@ 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;
+	__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 = in->dst_reg;
+	int		scope;
 
 	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);
+	/*
+	 * 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
+	 *          -1:    ld   dst  %fp  DT_STK_DCTX  00000000
+	 *           0:    ld   dst  dst  DCTX_*VARS   00000000
+	 * followed by one of
+	 *          +1:    ld   dst  dst  var_offset   00000000
+	 *          +1:    st   dst  src  var_offset   00000000
+	 *          +1:    add  dst    0     0         var_offset
+	 * the last case being for access by-reference.
+	 */
+	if (in->off == DCTX_GVARS)
+		scope = DIFV_SCOPE_GLOBAL;
+	else if (in->off == DCTX_LVARS)
+		scope = DIFV_SCOPE_LOCAL;
 	else
-		fprintf(fp, "\n");
+		scope = -1;
+	if (in[ 0].code == ldcode &&
+	    in[ 0].src_reg == dst &&
+	    scope >= 0 &&
+	    in[ 0].imm == 0 &&
+	    in[-1].code == ldcode &&
+	    in[-1].dst_reg == dst &&
+	    in[-1].src_reg == BPF_REG_FP &&
+	    in[-1].off == DT_STK_DCTX &&
+	    in[-1].imm == 0 &&
+	    in[+1].dst_reg == dst) {
+		int var_offset = -1;
+		const char	*vname = NULL;
+
+		if (in[+1].code == ldcode &&
+		    in[+1].src_reg == dst &&
+		    in[+1].imm == 0)
+			var_offset = in[+1].off;
+		else if (in[+1].code == stcode &&
+		    in[+1].src_reg != dst &&
+		    in[+1].imm == 0)
+			var_offset = in[+1].off;
+		else if (in[+1].code == addcode &&
+		    in[+1].src_reg == 0 &&
+		    in[+1].off == 0)
+			var_offset = in[+1].imm;
+
+		if (var_offset >= 0)
+			vname = dt_dis_varname_off(dp, var_offset, scope, addr);
+		if (vname)
+			fprintf(fp, "%*s! %s%s", DT_DIS_PAD(n), "",
+			    scope == DIFV_SCOPE_LOCAL ? "this->" : "", vname);
+	}
+	fprintf(fp, "\n");
 
 	return 0;
 }
@@ -193,20 +240,9 @@ 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,
+	fprintf(fp, "%-4s [%s%+d], %s\n", 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");
-
 	return 0;
 }
 
@@ -215,24 +251,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 +278,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 +289,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 +682,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 +731,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..f2766716
--- /dev/null
+++ b/test/unittest/dif/tst.DisVarNames.r
@@ -0,0 +1,23 @@
+Z
+Y
+X
+A
+A
+A
+A
+B
+A
+C
+Z
+this->z
+this->y
+this->x
+C
+this->c
+this->b
+this->a
+this->b
+C
+this->x
+Y
+this->z
diff --git a/test/unittest/dif/tst.DisVarNames.sh b/test/unittest/dif/tst.DisVarNames.sh
new file mode 100755
index 00000000..ae13823e
--- /dev/null
+++ b/test/unittest/dif/tst.DisVarNames.sh
@@ -0,0 +1,42 @@
+#!/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
+tmpout=$tmpdir/dis-var-names.out
+
+$dtrace $dt_flags -Sen '
+struct foo {
+    int foo_i, foo_j, foo_k;
+} A, B, C;
+this struct foo a, b, c;
+BEGIN {
+    X = Y = Z = 1;
+    A.foo_i = A.foo_j = A.foo_k = 1;
+    B = A;
+    C = A;
+    this->x = this->y = this->z = Z;
+    this->a = this->b = this->c = C;
+    trace(this->b.foo_j);
+    trace(C.foo_k);
+    trace(this->x);
+    trace(Y);
+    trace(this->z);
+}' |& awk '
+/ 00000000    lddw %r.*A/ { print "A" }
+/ 00000000    lddw %r.*B/ { print "B" }
+/ 00000000    lddw %r.*C/ { print "C" }
+/ 00000000    lddw %r.*X/ { print "X" }
+/ 00000000    lddw %r.*Y/ { print "Y" }
+/ 00000000    lddw %r.*Z/ { print "Z" }
+/ 00000000    lddw %r.*this->a/ { print "this->a" }
+/ 00000000    lddw %r.*this->b/ { print "this->b" }
+/ 00000000    lddw %r.*this->c/ { print "this->c" }
+/ 00000000    lddw %r.*this->x/ { print "this->x" }
+/ 00000000    lddw %r.*this->y/ { print "this->y" }
+/ 00000000    lddw %r.*this->z/ { print "this->z" }'
-- 
2.18.4




More information about the DTrace-devel mailing list