[DTrace-devel] [PATCH] Add support for built-in variable errno
Kris Van Hees
kris.van.hees at oracle.com
Sat Apr 24 18:57:04 PDT 2021
On Fri, Apr 23, 2021 at 12:50:12PM -0400, eugene.loh at oracle.com wrote:
> From: Eugene Loh <eugene.loh at oracle.com>
>
> The built-in variable errno should be:
>
> *) the errno for failed system calls for syscall:::return probes
>
> *) 0 otherwise
>
> This behavior is slightly different from the errno in C library functions
> (where errno is not reset by successful system calls) and from the
> behavior described in the DTrace Guide (which allows errno to be non-0
> for non-syscall probes).
The behaviour of errno in the C library is not relevant here because this is
the D variable 'errno' which has no relation with the C library variable of the
same name. As far as the DTrace guide goes... the description there is not
as accurate as I would like because the reference implementation (DTrace on
Oracle Solaris) is very OS specific and Linux does not have any mechanism to
provide the exact same functionality.
> Introduce an mst->syserrno, and have the provider trampolines set it to 0.
Let's name it mst->errno for consistency with the errno variable itself.
> Then have syscall:::return probes check arg0, setting mst->syserrno=-arg0
> if the value would be within bounds. Finally, have dt_get_bvar() retrieve
> errno from mst->syserrno.
>
> Update tests. The only particularly meaningful test for errno is
> tst.errno2.d, which will not yet work since it still needs, for example,
> string support. Add a tst.errno3.sh test for the new errno support.
>
> Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
> ---
> bpf/get_bvar.c | 2 +
> libdtrace/dt_cg.c | 2 +
> libdtrace/dt_dctx.h | 2 +
> libdtrace/dt_prov_syscall.c | 17 +++++++
> test/unittest/builtinvar/tst.errno.d | 1 -
> test/unittest/builtinvar/tst.errno1.d | 1 -
> test/unittest/builtinvar/tst.errno3.r | 1 +
> test/unittest/builtinvar/tst.errno3.sh | 56 ++++++++++++++++++++++++
> test/unittest/variables/bvar/tst.errno.d | 3 +-
> 9 files changed, 81 insertions(+), 4 deletions(-)
> create mode 100644 test/unittest/builtinvar/tst.errno3.r
> create mode 100755 test/unittest/builtinvar/tst.errno3.sh
>
> diff --git a/bpf/get_bvar.c b/bpf/get_bvar.c
> index 7e7b25a3..971209ab 100644
> --- a/bpf/get_bvar.c
> +++ b/bpf/get_bvar.c
> @@ -106,6 +106,8 @@ noinline uint64_t dt_get_bvar(dt_dctx_t *dctx, uint32_t id)
>
> return val >> 32;
> }
> + case DIF_VAR_ERRNO:
> + return mst->syserrno;
> case DIF_VAR_CURCPU: {
> uint32_t key = 0;
> void *val = bpf_map_lookup_elem(&cpuinfo, &key);
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index 047cb7ba..54c50d01 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -114,6 +114,7 @@ dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act)
> * // mov %r7, %r0
> * dctx.mst = rc; // stdw [%fp + DCTX_FP(DCTX_MST)], %r7
> * dctx.mst->prid = PRID; // stw [%r7 + DMST_PRID], PRID
> + * dctx.mst->syserrno = 0; // stw [%r7 + DMST_ERRNO], 0
> */
> emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_FP, DCTX_FP(DCTX_MST), 0));
> dt_cg_xsetx(dlp, mem, DT_LBL_NONE, BPF_REG_1, mem->di_id);
> @@ -124,6 +125,7 @@ dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act)
> emit(dlp, BPF_MOV_REG(BPF_REG_7, BPF_REG_0));
> emit(dlp, BPF_STORE(BPF_DW, BPF_REG_FP, DCTX_FP(DCTX_MST), BPF_REG_7));
> emite(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_7, DMST_PRID, -1), prid);
> + emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_7, DMST_ERRNO, 0));
>
> /*
> * buf = rc + roundup(sizeof(dt_mstate_t), 8);
> diff --git a/libdtrace/dt_dctx.h b/libdtrace/dt_dctx.h
> index 0873602c..8318ea00 100644
> --- a/libdtrace/dt_dctx.h
> +++ b/libdtrace/dt_dctx.h
> @@ -22,6 +22,7 @@ typedef struct dt_mstate {
> uint32_t prid; /* Probe ID */
> uint32_t clid; /* Clause ID (unique per probe) */
> uint32_t tag; /* Tag (for future use) */
> + int32_t syserrno; /* syscall errno */
> uint64_t fault; /* DTrace fault flags */
> uint64_t tstamp; /* cached timestamp value */
> #if 0
> @@ -62,6 +63,7 @@ typedef struct dt_dctx {
> #define DMST_PRID offsetof(dt_mstate_t, prid)
> #define DMST_CLID offsetof(dt_mstate_t, clid)
> #define DMST_TAG offsetof(dt_mstate_t, tag)
> +#define DMST_ERRNO offsetof(dt_mstate_t, syserrno)
> #define DMST_FAULT offsetof(dt_mstate_t, fault)
> #define DMST_TSTAMP offsetof(dt_mstate_t, tstamp)
> #define DMST_REGS offsetof(dt_mstate_t, regs)
> diff --git a/libdtrace/dt_prov_syscall.c b/libdtrace/dt_prov_syscall.c
> index 65b624a9..43fe44c0 100644
> --- a/libdtrace/dt_prov_syscall.c
> +++ b/libdtrace/dt_prov_syscall.c
> @@ -188,6 +188,23 @@ static void trampoline(dt_pcb_t *pcb)
> for ( ; i < ARRAY_SIZE(((dt_mstate_t *)0)->argv); i++)
> emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(i), 0));
>
> + /*
> + * For return probes, store the errno. That is, examine arg0.
> + * If it is >=0 or <=-2048, ignore it. Otherwise, store -arg0
> + * in dctx->mst->syserrno.
> + */
> + if (strcmp(pcb->pcb_probe->desc->prb, "return") == 0) {
> + uint_t lbl_errno_done = dt_irlist_label(dlp);
> +
> + emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_7, DMST_ARG(0)));
> + emit(dlp, BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_errno_done));
> + emit(dlp, BPF_BRANCH_IMM(BPF_JSLE, BPF_REG_0, -2048, lbl_errno_done));
> + emit(dlp, BPF_NEG_REG(BPF_REG_0));
> + emit(dlp, BPF_STORE(BPF_W, BPF_REG_7, DMST_ERRNO, BPF_REG_0));
> + emitl(dlp, lbl_errno_done,
> + BPF_NOP());
> + }
> +
> dt_cg_tramp_epilogue(pcb);
> }
>
> diff --git a/test/unittest/builtinvar/tst.errno.d b/test/unittest/builtinvar/tst.errno.d
> index 58b71bc3..877b8f84 100644
> --- a/test/unittest/builtinvar/tst.errno.d
> +++ b/test/unittest/builtinvar/tst.errno.d
> @@ -4,7 +4,6 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> diff --git a/test/unittest/builtinvar/tst.errno1.d b/test/unittest/builtinvar/tst.errno1.d
> index dcac23ae..c9bd35c2 100644
> --- a/test/unittest/builtinvar/tst.errno1.d
> +++ b/test/unittest/builtinvar/tst.errno1.d
> @@ -4,7 +4,6 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> diff --git a/test/unittest/builtinvar/tst.errno3.r b/test/unittest/builtinvar/tst.errno3.r
> new file mode 100644
> index 00000000..708ecc26
> --- /dev/null
> +++ b/test/unittest/builtinvar/tst.errno3.r
> @@ -0,0 +1 @@
> + 0 0 0 0 0 0 0 0 0 2 0 9 0 2 0 9 0 0 0 0 0 0 0 0
> diff --git a/test/unittest/builtinvar/tst.errno3.sh b/test/unittest/builtinvar/tst.errno3.sh
> new file mode 100755
> index 00000000..40215f31
> --- /dev/null
> +++ b/test/unittest/builtinvar/tst.errno3.sh
> @@ -0,0 +1,56 @@
> +#!/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.
> +#
> +
> +dtrace=$1
> +CC=/usr/bin/gcc
> +
> +DIRNAME="$tmpdir/builtinvar-errno3.$$.$RANDOM"
> +mkdir -p $DIRNAME
> +cd $DIRNAME
> +
> +cat << EOF > main.c
> +#include <stdio.h>
> +#include <errno.h>
> +#include <sys/types.h> /* open() */
> +#include <sys/stat.h> /* open() */
> +#include <fcntl.h> /* open() */
> +#include <unistd.h> /* close() */
> +void foo(char *s) {
> + int fd = open(s, O_WRONLY);
> + close(fd);
> +}
> +int main(int c, char **v) {
> + foo("/dev/null");
> + foo("/dev/null");
> + foo("/no/such/path/exists");
> + foo("/no/such/path/exists");
> + foo("/dev/null");
> + foo("/dev/null");
> + return 0;
> +}
> +EOF
> +
> +$CC main.c
> +if [ $? -ne 0 ]; then
> + echo compilation failed
> + exit 1
> +fi
> +
> +$dtrace $dt_flags -qn '
> + syscall::open:,
> + syscall::openat:,
> + syscall::close:
> + /pid == $target/
> + { printf(" %d", errno); }
> +' -c ./a.out
> +if [ $? -ne 0 ]; then
> + echo DTrace failed
> + exit 1
> +fi
> +
> +exit 0
> diff --git a/test/unittest/variables/bvar/tst.errno.d b/test/unittest/variables/bvar/tst.errno.d
> index f7a08071..4a866dff 100644
> --- a/test/unittest/variables/bvar/tst.errno.d
> +++ b/test/unittest/variables/bvar/tst.errno.d
> @@ -1,10 +1,9 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
> + * 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.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION: The 'errno' variable can be accessed and is not -1.
> --
> 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