[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