[DTrace-devel] [PATCH] Add annotations for dctx->X and mst->X members
Kris Van Hees
kris.van.hees at oracle.com
Wed Mar 30 17:11:59 UTC 2022
Let's hold off on this entire annotation thing for dctx and mst members for
now. I agree we can do better in general with providing annotations and not
require strict instruction sequence matching.
Something to tackle in the near future hopefully.
On Tue, Mar 29, 2022 at 06:24:02PM -0400, eugene.loh--- via DTrace-devel wrote:
> 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
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
More information about the DTrace-devel
mailing list