[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