[DTrace-devel] [PATCH] Add support for built-in variable errno

eugene.loh at oracle.com eugene.loh at oracle.com
Fri Apr 23 09:50:12 PDT 2021


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).

Introduce an mst->syserrno, and have the provider trampolines set it to 0.
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




More information about the DTrace-devel mailing list