[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