[DTrace-devel] [PATCH v2 01/02] ERROR probe implementation

Kris Van Hees kris.van.hees at oracle.com
Thu Mar 18 21:53:37 PDT 2021


The ERROR probe is special because it is not associated with a system
event.  Instead, it is a 'fake' probe that is reported whenever there
a fault occurs during probe execution.  This can be implemented in
BPF by including the ERROR probe clauses in every probe program that
needs them.

Whenever a fault occurs in one of our BPF programs, a call is made to
dt_probe_error() to record data associated with the fault, and the
clause is terminated with a non-zero return value.  The data items
that can be set are:

	- offset in the clause at which the fault occurred
	- fault type
	- value

When a clause ends with a non-zero return value, execution is directed
to the ERROR probe clauses that are included in each probe program that
needs them.  These clauses are compiled as regular probe clauses, but
they are processed separately to resolve the EPID and PRID identifiers
according to the ERROR probe rather than the probe that the clauses are
included for.

(Special thanks to Eugene Loh for his review comments - various fixes
 he implemented got integrated into this patch.)

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
---
 bpf/Build                                     |   3 +-
 bpf/get_bvar.c                                |  27 +-
 bpf/probe_error.c                             |  40 +++
 bpf/probe_error.h                             |  14 ++
 libdtrace/dt_as.c                             |  48 ++++
 libdtrace/dt_bpf.c                            |  82 +++++-
 libdtrace/dt_cc.c                             |  90 +++++--
 libdtrace/dt_cg.c                             | 233 +++++++++++++-----
 libdtrace/dt_cg.h                             |   1 +
 libdtrace/dt_consume.c                        |   9 +
 libdtrace/dt_dlibs.c                          |   3 +
 libdtrace/dt_dof.c                            |   5 +-
 libdtrace/dt_handle.c                         |  21 +-
 libdtrace/dt_impl.h                           |   8 +-
 libdtrace/dt_map.c                            |  12 +-
 libdtrace/dt_pcb.h                            |   2 +-
 libdtrace/dt_probe.c                          |  79 +++++-
 libdtrace/dt_prov_dtrace.c                    |  26 +-
 libdtrace/dt_work.c                           |  14 +-
 libdtrace/dtrace.h                            |   4 +-
 test/unittest/builtinvar/tst.arg0.d           |   7 +-
 test/unittest/builtinvar/tst.arg0clause.d     |   6 +-
 test/unittest/builtinvar/tst.arg1.d           |   7 +-
 test/unittest/builtinvar/tst.arg1to8.d        |   7 +-
 test/unittest/builtinvar/tst.arg1to8clause.d  |   7 +-
 test/unittest/builtinvar/tst.caller.d         |   8 +-
 test/unittest/builtinvar/tst.caller1.d        |   8 +-
 test/unittest/builtinvar/tst.epid.d           |   7 +-
 test/unittest/builtinvar/tst.epid1.d          |   7 +-
 test/unittest/builtinvar/tst.errno.d          |   8 +-
 test/unittest/builtinvar/tst.errno1.d         |   8 +-
 test/unittest/builtinvar/tst.errno2.d         |   7 +-
 test/unittest/builtinvar/tst.execname.d       |   7 +-
 test/unittest/builtinvar/tst.hpriority.d      |   9 +-
 test/unittest/builtinvar/tst.id.d             |   9 +-
 test/unittest/builtinvar/tst.id1.d            |   9 +-
 test/unittest/builtinvar/tst.ipl.d            |  10 +-
 test/unittest/builtinvar/tst.ipl1.d           |  10 +-
 test/unittest/builtinvar/tst.lwpsinfo.d       |   9 +-
 test/unittest/builtinvar/tst.lwpsinfo1.d      |   9 +-
 test/unittest/builtinvar/tst.pid.d            |   9 +-
 test/unittest/builtinvar/tst.pid1.d           |   9 +-
 .../builtinvar/tst.psinfo-bug21974606.d       |   7 +-
 .../builtinvar/tst.psinfo-bug21984854.d       |   2 +-
 .../builtinvar/tst.psinfo-bug22561297.d       |   4 +-
 test/unittest/builtinvar/tst.psinfo.d         |   4 +-
 test/unittest/builtinvar/tst.psinfo1.d        |   9 +-
 test/unittest/builtinvar/tst.tid.d            |   9 +-
 test/unittest/builtinvar/tst.tid1.d           |   9 +-
 test/unittest/builtinvar/tst.timestamp.d      |   7 +-
 test/unittest/builtinvar/tst.vtimestamp.d     |   8 +-
 test/unittest/builtinvar/tst.vtimestamp2.d    |   7 +-
 test/unittest/error/tst.DTRACEFLT_BADADDR.d   |   5 +-
 .../tst.DTRACEFLT_BADADDR.null_ptr_field.d    |  27 ++
 .../tst.DTRACEFLT_BADADDR.null_ptr_field.r    |   3 +
 test/unittest/error/tst.DTRACEFLT_BADADDR.r   |   4 +-
 test/unittest/error/tst.DTRACEFLT_BADADDR2.r  |   2 +-
 .../error/tst.clause_scope-begin-ended.d      |  34 +++
 .../error/tst.clause_scope-begin-ended.r      |   5 +
 test/unittest/error/tst.clause_scope-begin.d  |  38 +++
 test/unittest/error/tst.clause_scope-begin.r  |   5 +
 .../unittest/error/tst.clause_scope-regular.d |  32 +++
 .../unittest/error/tst.clause_scope-regular.r |   5 +
 .../error/tst.clause_scope-regular.r.p        |   3 +
 test/unittest/error/tst.error.d               |   9 +-
 test/unittest/error/tst.error.r               |   2 +-
 test/unittest/error/tst.errorend.d            |   9 +-
 test/unittest/error/tst.errorend.r            |   2 +-
 test/unittest/printa/tst.walltimestamp.sh     |   7 +-
 69 files changed, 923 insertions(+), 233 deletions(-)
 create mode 100644 bpf/probe_error.c
 create mode 100644 bpf/probe_error.h
 create mode 100644 test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.d
 create mode 100644 test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.r
 create mode 100644 test/unittest/error/tst.clause_scope-begin-ended.d
 create mode 100644 test/unittest/error/tst.clause_scope-begin-ended.r
 create mode 100644 test/unittest/error/tst.clause_scope-begin.d
 create mode 100644 test/unittest/error/tst.clause_scope-begin.r
 create mode 100644 test/unittest/error/tst.clause_scope-regular.d
 create mode 100644 test/unittest/error/tst.clause_scope-regular.r
 create mode 100755 test/unittest/error/tst.clause_scope-regular.r.p

diff --git a/bpf/Build b/bpf/Build
index e9f2b1e2..36cf1fa0 100644
--- a/bpf/Build
+++ b/bpf/Build
@@ -1,5 +1,5 @@
 # Oracle Linux DTrace.
-# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2020, 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.
 
@@ -26,6 +26,7 @@ bpf_dlib_SOURCES = \
 	get_bvar.c \
 	get_gvar.c set_gvar.c \
 	get_tvar.c set_tvar.c \
+	probe_error.c \
 	memcpy.c strnlen.c
 
 bpf-check: $(objdir)/include/.dir.stamp
diff --git a/bpf/get_bvar.c b/bpf/get_bvar.c
index 174626f9..7e7b25a3 100644
--- a/bpf/get_bvar.c
+++ b/bpf/get_bvar.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
  */
 #include <linux/bpf.h>
 #include <stddef.h>
@@ -8,9 +8,12 @@
 #include <bpf-helpers.h>
 #include <dtrace/conf.h>
 #include <dtrace/dif_defines.h>
+#include <dtrace/faults_defines.h>
 #include <dt_dctx.h>
 #include <dt_state.h>
 
+#include "probe_error.h"
+
 #ifndef noinline
 # define noinline	__attribute__((noinline))
 #endif
@@ -18,8 +21,16 @@
 extern struct bpf_map_def cpuinfo;
 extern struct bpf_map_def state;
 
-noinline uint64_t dt_get_bvar(dt_mstate_t *mst, uint32_t id)
+#define error(dctx, fault, illval) \
+	({ \
+		dt_probe_error((dctx), -1, (fault), (illval)); \
+		-1; \
+	})
+
+noinline uint64_t dt_get_bvar(dt_dctx_t *dctx, uint32_t id)
 {
+	dt_mstate_t	*mst = dctx->mst;
+
 	switch (id) {
 	case DIF_VAR_CURTHREAD:
 		return bpf_get_current_task();
@@ -75,13 +86,13 @@ noinline uint64_t dt_get_bvar(dt_mstate_t *mst, uint32_t id)
 		/* Chase pointers val = current->real_parent->tgid. */
 		ptr = bpf_get_current_task();
 		if (ptr == 0)
-			return -1;
+			return error(dctx, DTRACEFLT_BADADDR, ptr);
 		if (bpf_probe_read((void *)&ptr, 8,
 		    (const void *)(ptr + *parent_off)))
-			return -1;
+			return error(dctx, DTRACEFLT_BADADDR, ptr + *parent_off);
 		if (bpf_probe_read((void *)&val, 4,
 		    (const void *)(ptr + *tgid_off)))
-			return -1;
+			return error(dctx, DTRACEFLT_BADADDR, ptr + *tgid_off);
 
 		return (uint64_t)val;
 	}
@@ -106,10 +117,6 @@ noinline uint64_t dt_get_bvar(dt_mstate_t *mst, uint32_t id)
 	}
 	default:
 		/* Not implemented yet. */
-#if 1
-		return (uint64_t)-1;
-#else
-		return (uint64_t)id;
-#endif
+		return error(dctx, DTRACEFLT_ILLOP, 0);
 	}
 }
diff --git a/bpf/probe_error.c b/bpf/probe_error.c
new file mode 100644
index 00000000..38d17885
--- /dev/null
+++ b/bpf/probe_error.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <linux/bpf.h>
+#include <stdint.h>
+#include <bpf-helpers.h>
+#include <dt_dctx.h>
+
+#ifndef noinline
+# define noinline	__attribute__((noinline))
+#endif
+
+extern int64_t dt_error(dt_dctx_t *dctx);
+
+/*
+ * DTrace ERROR probes provide 6 arguments:
+ *	arg0 = always NULL (used to be kernel consumer state pointer)
+ *      arg1 = EPID of probe that triggered the fault
+ *      arg2 = clause index of code that triggered the fault
+ *      arg3 = BPF offset in the clause that triggered the fault (or -1)
+ *      arg4 = fault type
+ *      arg5 = fault-specific value (usually address being accessed or 0)
+ */
+noinline void dt_probe_error(dt_dctx_t *dctx, uint64_t pc, uint64_t fault,
+			     uint64_t illval)
+{
+	dt_mstate_t	*mst = dctx->mst;
+
+	mst->argv[0] = 0;
+	mst->argv[1] = mst->epid;
+	mst->argv[2] = mst->clid;
+	mst->argv[3] = pc;
+	mst->argv[4] = fault;
+	mst->argv[5] = illval;
+
+	dt_error(dctx);
+
+	mst->fault = fault;
+}
diff --git a/bpf/probe_error.h b/bpf/probe_error.h
new file mode 100644
index 00000000..d9e5e985
--- /dev/null
+++ b/bpf/probe_error.h
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ */
+#ifndef BPF_PROBE_ERRROR_H
+#define BPF_PROBE_ERRROR_H
+
+#include <stdint.h>
+#include <dt_dctx.h>
+
+extern void dt_probe_error(dt_dctx_t *dctx, uint64_t pc, uint64_t fault,
+			   uint64_t illval);
+
+#endif /* BPF_PROBE_ERRROR_H */
diff --git a/libdtrace/dt_as.c b/libdtrace/dt_as.c
index 4ca2f420..ecf14feb 100644
--- a/libdtrace/dt_as.c
+++ b/libdtrace/dt_as.c
@@ -605,3 +605,51 @@ fail:
 
 	return dp;
 }
+
+dtrace_difo_t *
+dt_difo_copy(dtrace_hdl_t *dtp, const dtrace_difo_t *odp)
+{
+	dtrace_difo_t	*dp;
+
+	dp = dt_zalloc(dtp, sizeof(dtrace_difo_t));
+	if (dp == NULL)
+		goto no_mem;
+
+#define DIFO_COPY_DATA(dtp, odp, dp, len, ptr) \
+	do { \
+		if ((odp)->len > 0) { \
+			size_t	tsiz = sizeof(typeof((dp)->ptr[0])); \
+			\
+			(dp)->len = (odp)->len; \
+			(dp)->ptr = dt_calloc((dtp), (dp)->len, tsiz); \
+			if ((dp)->ptr == NULL) \
+				goto no_mem; \
+			\
+			memcpy((dp)->ptr, (odp)->ptr, (dp)->len * tsiz); \
+		} \
+	} while (0)
+
+	DIFO_COPY_DATA(dtp, odp, dp, dtdo_len, dtdo_buf);
+	DIFO_COPY_DATA(dtp, odp, dp, dtdo_strlen, dtdo_strtab);
+	DIFO_COPY_DATA(dtp, odp, dp, dtdo_varlen, dtdo_vartab);
+	DIFO_COPY_DATA(dtp, odp, dp, dtdo_brelen, dtdo_breltab);
+	DIFO_COPY_DATA(dtp, odp, dp, dtdo_krelen, dtdo_kreltab);
+	DIFO_COPY_DATA(dtp, odp, dp, dtdo_urelen, dtdo_ureltab);
+
+	dp->dtdo_ddesc = dt_datadesc_hold(odp->dtdo_ddesc);
+	dp->dtdo_flags = odp->dtdo_flags;
+
+	return dp;
+
+no_mem:
+	dt_free(dtp, dp->dtdo_buf);
+	dt_free(dtp, dp->dtdo_strtab);
+	dt_free(dtp, dp->dtdo_vartab);
+	dt_free(dtp, dp->dtdo_breltab);
+	dt_free(dtp, dp->dtdo_kreltab);
+	dt_free(dtp, dp->dtdo_ureltab);
+	dt_free(dtp, dp);
+
+	dt_set_errno(dtp, EDT_NOMEM);
+	return NULL;
+}
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index ba595f08..edc17e7a 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -274,19 +274,18 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
 }
 
 /*
- * Perform relocation processing on a program.
+ * Perform relocation processing for BPF maps in a program.
  */
 static void
-dt_bpf_reloc_prog(dtrace_hdl_t *dtp, const dt_probe_t *prp,
-		  const dtrace_difo_t *dp)
+dt_bpf_reloc_prog(dtrace_hdl_t *dtp, const dtrace_difo_t *dp)
 {
 	int			len = dp->dtdo_brelen;
 	const dof_relodesc_t	*rp = dp->dtdo_breltab;
+	struct bpf_insn		*text = dp->dtdo_buf;
 
 	for (; len != 0; len--, rp++) {
 		char		*name = &dp->dtdo_strtab[rp->dofr_name];
 		dt_ident_t	*idp = dt_idhash_lookup(dtp->dt_bpfsyms, name);
-		struct bpf_insn	*text = dp->dtdo_buf;
 		int		ioff = rp->dofr_offset /
 					sizeof(struct bpf_insn);
 		uint32_t	val = 0;
@@ -326,15 +325,12 @@ dt_bpf_load_prog(dtrace_hdl_t *dtp, const dt_probe_t *prp,
 	char				*p, *q;
 
 	/*
-	 * Check whether there are any probe-specific relocations to be
-	 * performed.  If so, we need to modify the executable code.  This can
-	 * be done in-place since program loading is serialized.
-	 *
-	 * Relocations that are probe independent were already done at an
-	 * earlier time so we can ignore those.
+	 * Check whether there are any BPF specific relocations that may need to
+	 * be performed.  If so, we need to modify the executable code.  This
+	 * can be done in-place since program loading is serialized.
 	 */
 	if (dp->dtdo_brelen)
-		dt_bpf_reloc_prog(dtp, prp, dp);
+		dt_bpf_reloc_prog(dtp, dp);
 
 	memset(&attr, 0, sizeof(struct bpf_load_program_attr));
 
@@ -390,17 +386,77 @@ dt_bpf_load_prog(dtrace_hdl_t *dtp, const dt_probe_t *prp,
 	return -1;
 }
 
+/*
+ * Perform relocation processing on the ERROR probe program.
+ */
+static void
+dt_bpf_reloc_error_prog(dtrace_hdl_t *dtp, dtrace_difo_t *dp)
+{
+	int			len = dp->dtdo_brelen;
+	const dof_relodesc_t	*rp = dp->dtdo_breltab;
+	dof_relodesc_t		*nrp = dp->dtdo_breltab;
+	struct bpf_insn		*text = dp->dtdo_buf;
+
+	for (; len != 0; len--, rp++) {
+		char		*name = &dp->dtdo_strtab[rp->dofr_name];
+		dt_ident_t	*idp = dt_idhash_lookup(dtp->dt_bpfsyms, name);
+		int		ioff = rp->dofr_offset /
+					sizeof(struct bpf_insn);
+
+		/*
+		 * We need to turns "call dt_error" into a NOP.  We also remove
+		 * the relocation record because it is obsolete.
+		 */
+		if (idp != NULL && idp->di_kind == DT_IDENT_SYMBOL &&
+		    strcmp(idp->di_name, "dt_error") == 0) {
+			text[ioff] = BPF_NOP();
+			continue;
+		}
+
+		if (nrp != rp)
+			*nrp = *rp;
+
+		nrp++;
+	}
+
+	dp->dtdo_brelen -= rp - nrp;
+}
+
 int
 dt_bpf_load_progs(dtrace_hdl_t *dtp, uint_t cflags)
 {
 	dt_probe_t	*prp;
+	dtrace_difo_t	*dp;
+	dt_ident_t	*idp = dt_dlib_get_func(dtp, "dt_error");
+
+	assert(idp != NULL);
+
+	/*
+	 * First construct the ERROR probe program (to be included in probe
+	 * programs that may trigger a fault).
+	 *
+	 * After constructing the program, we need to patch up any calls to
+	 * dt_error because DTrace cannot handle faults in ERROR itself.
+	 */
+	dp = dt_program_construct(dtp, dtp->dt_error, cflags, idp);
+	if (dp == NULL)
+		return -1;
 
+	idp->di_flags |= DT_IDFLG_CGREG;	/* mark it as inline-ready */
+	dt_bpf_reloc_error_prog(dtp, dp);
+
+	/*
+	 * Now construct all the other programs.
+	 */
 	for (prp = dt_list_next(&dtp->dt_enablings); prp != NULL;
 	     prp = dt_list_next(prp)) {
-		dtrace_difo_t	*dp;
 		int		fd, rc;
 
-		dp = dt_program_construct(dtp, prp, cflags);
+		/* Already done. */
+		if (prp == dtp->dt_error)
+			continue;
+
+		dp = dt_program_construct(dtp, prp, cflags, NULL);
 		if (dp == NULL)
 			return -1;
 
diff --git a/libdtrace/dt_cc.c b/libdtrace/dt_cc.c
index 0ee1c381..211b5c67 100644
--- a/libdtrace/dt_cc.c
+++ b/libdtrace/dt_cc.c
@@ -1585,6 +1585,12 @@ dt_clause_create(dtrace_hdl_t *dtp, dtrace_difo_t *dp)
 	dp->dtdo_ddesc = yypcb->pcb_ddesc;
 	yypcb->pcb_ddesc = NULL;
 
+	/*
+	 * Special case: ERROR probe default clause.
+	 */
+	if (yypcb->pcb_cflags & DTRACE_C_EPROBE)
+		dp->dtdo_ddesc->dtdd_uarg = DT_ECB_ERROR;
+
 	/*
 	 * Generate a symbol name.
 	 */
@@ -2205,7 +2211,7 @@ out:
 }
 
 static dtrace_difo_t *
-dt_construct(dtrace_hdl_t *dtp, dt_probe_t *prp, uint_t cflags)
+dt_construct(dtrace_hdl_t *dtp, dt_probe_t *prp, uint_t cflags, dt_ident_t *idp)
 {
 	dt_pcb_t	pcb;
 	dt_node_t	*tnp;
@@ -2266,6 +2272,12 @@ dt_construct(dtrace_hdl_t *dtp, dt_probe_t *prp, uint_t cflags)
 	dt_cg(yypcb, tnp);
 	dp = dt_as(yypcb);
 
+	/*
+	 * If we were called with an identifier, assign the DIFO to it.
+	 */
+	if (idp != NULL)
+		dt_ident_set_data(idp, dp);
+
 out:
 	if (dtp->dt_cdefs_fd != -1 &&
 	    (ftruncate(dtp->dt_cdefs_fd, 0) == -1 ||
@@ -2287,32 +2299,41 @@ out:
 
 static int
 dt_link_layout(dtrace_hdl_t *dtp, const dtrace_difo_t *dp, uint_t *pcp,
-	       uint_t *rcp, uint_t *vcp)
+	       uint_t *rcp, uint_t *vcp, dt_ident_t *idp)
 {
 	uint_t			pc = *pcp;
 	uint_t			len = dp->dtdo_brelen;
 	const dof_relodesc_t	*rp = dp->dtdo_breltab;
+	int			no_deps = 0;
+
+	if (idp != NULL) {
+		idp->di_flags |= DT_IDFLG_REF;
+		if (idp->di_flags & DT_IDFLG_CGREG)
+			no_deps = 1;
+	}
 
 	(*pcp) += dp->dtdo_len;
 	(*rcp) += len;
 	(*vcp) += dp->dtdo_varlen;
+
+	if (no_deps)
+		return pc;
+
 	for (; len != 0; len--, rp++) {
 		char            *name = &dp->dtdo_strtab[rp->dofr_name];
-		dt_ident_t      *idp = dt_dlib_get_func(dtp, name);
 		dtrace_difo_t   *rdp;
 		int		ipc;
 
+		idp = dt_dlib_get_func(dtp, name);
 		if (idp == NULL ||			/* not found */
 		    idp->di_kind != DT_IDENT_SYMBOL ||	/* not external sym */
 		    idp->di_flags & DT_IDFLG_REF)       /* already seen */
 			continue;
 
-		idp->di_flags |= DT_IDFLG_REF;
-
 		rdp = dt_dlib_get_func_difo(dtp, idp);
 		if (rdp == NULL)
 			return -1;
-		ipc = dt_link_layout(dtp, rdp, pcp, rcp, vcp);
+		ipc = dt_link_layout(dtp, rdp, pcp, rcp, vcp, idp);
 		if (ipc == -1)
 			return -1;
 		idp->di_id = ipc;
@@ -2323,7 +2344,7 @@ dt_link_layout(dtrace_hdl_t *dtp, const dtrace_difo_t *dp, uint_t *pcp,
 
 static int
 dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
-		  const dtrace_difo_t *sdp, dt_strtab_t *stab,
+		  dt_ident_t *idp, const dtrace_difo_t *sdp, dt_strtab_t *stab,
 		  uint_t *pcp, uint_t *rcp, uint_t *vcp, dtrace_epid_t epid,
 		  uint_t clid)
 {
@@ -2336,6 +2357,13 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
 	uint_t			len = sdp->dtdo_brelen;
 	const dof_relodesc_t	*rp = sdp->dtdo_breltab;
 	dof_relodesc_t		*nrp = &dp->dtdo_breltab[rc];
+	int			no_deps = 0;
+
+	if (idp != NULL) {
+		idp->di_flags |= DT_IDFLG_REF;
+		if (idp->di_flags & DT_IDFLG_CGREG)
+			no_deps = 1;
+	}
 
 	/*
 	 * Make sure there is enough room in the destination instruction buffer
@@ -2396,37 +2424,45 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
 	nrp = &dp->dtdo_breltab[rc];
 	for (; len != 0; len--, rp++, nrp++) {
 		const char	*name = &sdp->dtdo_strtab[rp->dofr_name];
-		dt_ident_t	*idp = dt_dlib_get_sym(dtp, name);
 		dtrace_difo_t	*rdp;
 		dtrace_epid_t	nepid;
 		int		ipc;
 
+		idp = dt_dlib_get_sym(dtp, name);
 		if (idp == NULL)			/* not found */
 			continue;
 
 		switch (idp->di_kind) {
 		case DT_IDENT_SCALAR:			/* constant */
+			if (no_deps) {
+				nrp->dofr_type = R_BPF_NONE;
+				continue;
+			}
+
 			switch (idp->di_id) {
 			case DT_CONST_EPID:
 				nrp->dofr_data = epid;
-				break;
+				continue;
 			case DT_CONST_PRID:
 				nrp->dofr_data = prp->desc->id;
-				break;
+				continue;
 			case DT_CONST_CLID:
 				nrp->dofr_data = clid;
-				break;
+				continue;
 			case DT_CONST_ARGC:
 				nrp->dofr_data = 0;	/* FIXME */
-				break;
+				continue;
 			}
 
-			break;
+			continue;
 		case DT_IDENT_SYMBOL:			/* BPF function */
-			if (idp->di_flags & DT_IDFLG_REF)
+			if (no_deps) {
+				nrp->dofr_data = rp->dofr_data + pc;
 				continue;
+			}
 
-			idp->di_flags |= DT_IDFLG_REF;
+			if (idp->di_flags & DT_IDFLG_REF)
+				continue;
 
 			rdp = dt_dlib_get_func_difo(dtp, idp);
 			if (rdp == NULL)
@@ -2437,7 +2473,7 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
 				clid++;
 			} else
 				nepid = 0;
-			ipc = dt_link_construct(dtp, prp, dp, rdp, stab,
+			ipc = dt_link_construct(dtp, prp, dp, idp, rdp, stab,
 						pcp, rcp, vcp, nepid, clid);
 			if (ipc == -1)
 				return -1;
@@ -2445,7 +2481,7 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
 			idp->di_id = ipc;
 			nrp->dofr_data = idp->di_id;	/* set value */
 
-			break;
+			continue;
 		default:
 			continue;
 		}
@@ -2497,7 +2533,8 @@ dt_link_resolve(dtrace_hdl_t *dtp, dtrace_difo_t *dp)
 }
 
 static int
-dt_link(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp)
+dt_link(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
+	dt_ident_t *idp)
 {
 	uint_t		insc = 0;
 	uint_t		relc = 0;
@@ -2510,7 +2547,7 @@ dt_link(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp)
 	 * Determine the layout of the final (linked) DIFO, and calculate the
 	 * total instruction, relocation record, and variable table counts.
 	 */
-	rc = dt_link_layout(dtp, dp, &insc, &relc, &varc);
+	rc = dt_link_layout(dtp, dp, &insc, &relc, &varc, idp);
 	dt_dlib_reset(dtp, B_TRUE);
 	if (rc == -1)
 		goto fail;
@@ -2548,8 +2585,8 @@ dt_link(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp)
 	if (stab == NULL)
 		goto nomem;
 
-	rc = dt_link_construct(dtp, prp, fdp, dp, stab, &insc, &relc, &varc,
-			       0, 0);
+	rc = dt_link_construct(dtp, prp, fdp, idp, dp, stab, &insc, &relc,
+			       &varc, 0, 0);
 	dt_dlib_reset(dtp, B_FALSE);
 	if (rc == -1)
 		goto fail;
@@ -2609,26 +2646,27 @@ nomem:
 }
 
 dtrace_difo_t *
-dt_program_construct(dtrace_hdl_t *dtp, dt_probe_t *prp, uint_t cflags)
+dt_program_construct(dtrace_hdl_t *dtp, dt_probe_t *prp, uint_t cflags,
+		     dt_ident_t *idp)
 {
 	dtrace_difo_t *dp;
 
 	assert(prp != NULL);
 
-	dp = dt_construct(dtp, prp, cflags);
+	dp = dt_construct(dtp, prp, cflags, idp);
 	if (dp == NULL)
 		return NULL;
 
 	if (cflags & DTRACE_C_DIFV && DT_DISASM(dtp, 2))
-		dt_dis_difo(dp, stderr, NULL);
+		dt_dis_difo(dp, stderr, idp);
 
-	if (dt_link(dtp, prp, dp) != 0) {
+	if (dt_link(dtp, prp, dp, idp) != 0) {
 		dt_difo_free(dtp, dp);
 		return NULL;
 	}
 
 	if (cflags & DTRACE_C_DIFV && DT_DISASM(dtp, 3))
-		dt_dis_difo(dp, stderr, NULL);
+		dt_dis_difo(dp, stderr, idp);
 
 	return dp;
 }
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 858eb5a5..63726793 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -172,16 +172,20 @@ dt_cg_call_clause(dtrace_hdl_t *dtp, dt_ident_t *idp, dt_clause_arg_t *arg)
 	dt_irlist_t	*dlp = arg->dlp;
 
 	/*
-	 *	if (*dctx.act != act)	// ldw %r0, [%fp + DCTX_FP(DCTX_ACT)]
+	 *	if (*dctx.act != act)	// ldw %r0, [%fp +
+	 *				//	     DCTX_FP(DCTX_ACT)]
 	 *		goto exit;	// ldw %r0, [%r0 + 0]
 	 *				// jne %r0, act, lbl_exit
-	 *	dt_clause(dctx);	// mov %r1, %fp
-	 *				// add %r1, DCTX_FP(0)
-	 *				// call dt_program
 	 */
 	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_FP, DCTX_FP(DCTX_ACT)));
 	emit(dlp,  BPF_LOAD(BPF_W, BPF_REG_0, BPF_REG_0, 0));
 	emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, arg->act, arg->lbl_exit));
+
+	/*
+	 *	dt_clause(dctx);	// mov %r1, %fp
+	 *				// add %r1, DCTX_FP(0)
+	 *				// call dt_program
+	 */
 	emit(dlp,  BPF_MOV_REG(BPF_REG_1, BPF_REG_FP));
 	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DCTX_FP(0)));
 	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
@@ -190,12 +194,12 @@ dt_cg_call_clause(dtrace_hdl_t *dtp, dt_ident_t *idp, dt_clause_arg_t *arg)
 }
 
 static void
-dt_cg_tramp_call_clauses(dt_pcb_t *pcb, dt_activity_t act)
+dt_cg_tramp_call_clauses(dt_pcb_t *pcb, dt_probe_t *prp, dt_activity_t act)
 {
 	dt_irlist_t	*dlp = &pcb->pcb_ir;
 	dt_clause_arg_t	arg = { dlp, act, pcb->pcb_exitlbl };
 
-	dt_probe_clause_iter(pcb->pcb_hdl, pcb->pcb_probe,
+	dt_probe_clause_iter(pcb->pcb_hdl, prp,
 			     (dt_clause_f *)dt_cg_call_clause, &arg);
 }
 
@@ -218,7 +222,7 @@ dt_cg_tramp_return(dt_pcb_t *pcb)
 void
 dt_cg_tramp_epilogue(dt_pcb_t *pcb)
 {
-	dt_cg_tramp_call_clauses(pcb, DT_ACTIVITY_ACTIVE);
+	dt_cg_tramp_call_clauses(pcb, pcb->pcb_probe, DT_ACTIVITY_ACTIVE);
 	dt_cg_tramp_return(pcb);
 }
 
@@ -227,7 +231,7 @@ dt_cg_tramp_epilogue_advance(dt_pcb_t *pcb, dt_activity_t act)
 {
 	dt_irlist_t	*dlp = &pcb->pcb_ir;
 
-	dt_cg_tramp_call_clauses(pcb, act);
+	dt_cg_tramp_call_clauses(pcb, pcb->pcb_probe, act);
 
 	/*
 	 *	(*dctx.act)++;		// lddw %r0, [%fp + DCTX_FP(DCTX_ACT)]
@@ -241,6 +245,54 @@ dt_cg_tramp_epilogue_advance(dt_pcb_t *pcb, dt_activity_t act)
 	dt_cg_tramp_return(pcb);
 }
 
+static int
+dt_cg_tramp_error_call_clause(dtrace_hdl_t *dtp, dt_ident_t *idp,
+			      dt_irlist_t *dlp)
+{
+	/*
+	 *	dt_error_#(dctx);	// mov %r1, %r9
+	 *				// call dt_error_#
+	 */
+	emit(dlp,  BPF_MOV_REG(BPF_REG_1, BPF_REG_9));
+	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+
+	return 0;
+}
+
+/*
+ * Generate the trampoline BPF program for the ERROR probe.
+ *
+ * The trampoline BPF program for the ERROR probe is implemented as a function
+ * that calls the ERROR probe clauses one by one.  It must satisfy the
+ * signature:
+ *
+ *	int dt_error(dt_dctx_t *dctx)
+ */
+void
+dt_cg_tramp_error(dt_pcb_t *pcb)
+{
+	dtrace_hdl_t	*dtp = pcb->pcb_hdl;
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+
+	/*
+	 * int dt_error(dt_dctx_t *dctx)
+	 *				//     (%r1 = pointer to dt_dctx_t)
+	 * {
+	 *	int	rc;
+	 *				//     (%r9 = reserved reg for dctx)
+	 *				// mov %r9, %r1
+	 */
+	TRACE_REGSET("Trampoline: Begin");
+	emit(dlp, BPF_MOV_REG(BPF_REG_9, BPF_REG_1));
+
+	dt_probe_clause_iter(dtp, dtp->dt_error,
+			     (dt_clause_f *)dt_cg_tramp_error_call_clause, dlp);
+
+	emit(dlp, BPF_MOV_IMM(BPF_REG_0, 0));
+	emit(dlp, BPF_RETURN());
+	TRACE_REGSET("Trampoline: Begin");
+}
+
 /*
  * Generate the function prologue.
  *
@@ -339,7 +391,7 @@ dt_cg_prologue(dt_pcb_t *pcb, dt_node_t *pred)
 
 /*
  * Generate the function epilogue:
- *	4. If a fault was flagged, return 0.
+ *	4. If a fault was flagged, return the fault code.
  *	5. Submit the buffer to the perf event output buffer for the current
  *	   cpu.
  *	6. Return 0
@@ -363,19 +415,8 @@ dt_cg_epilogue(dt_pcb_t *pcb)
 		assert(buffers != NULL);
 
 		/*
-		 *	rc = dctx->mst->fault;	// lddw %r0, [%fp + DT_STK_DCTX]
-		 *				// lddw %r0, [%r0 + DCTX_MST]
-		 *				// lddw %r0, [%r0 + DMST_FAULT]
-		 *	if (rc != 0)
-		 *	    goto exit;		// jne %r0, 0, pcb->pcb_exitlbl
-		 */
-		emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_FP, DT_STK_DCTX));
-		emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_0, DCTX_MST));
-		emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_0, DMST_FAULT));
-		emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, 0, pcb->pcb_exitlbl));
-
-		/*
-		 *	bpf_perf_event_output(dctx->ctx, &buffers, BPF_F_CURRENT_CPU,
+		 *	bpf_perf_event_output(dctx->ctx, &buffers,
+		 *			      BPF_F_CURRENT_CPU,
 		 *			      buf - 4, bufoff + 4);
 		 *				// lddw %r1, [%fp + DT_STK_DCTX]
 		 *				// lddw %r1, [%r1 + DCTX_CTX]
@@ -384,7 +425,7 @@ dt_cg_epilogue(dt_pcb_t *pcb)
 		 *				// mov %r4, %r9
 		 *				// add %r4, -4
 		 *				// mov %r5, pcb->pcb_bufoff
-		 *				// add %r4, 4
+		 *				// add %r5, 4
 		 *				// call bpf_perf_event_output
 		 *
 		 */
@@ -411,6 +452,67 @@ dt_cg_epilogue(dt_pcb_t *pcb)
 	TRACE_REGSET("Epilogue: End  ");
 }
 
+/*
+ * Generate code for a fault condition.  A call is made to dt_probe_error() to
+ * set the fault information, and then a non-zero return from the function is
+ * performed.
+ */
+static void
+dt_cg_probe_error(dt_pcb_t *pcb, uint32_t off, uint32_t fault, uint64_t illval)
+{
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+	dt_regset_t	*drp = pcb->pcb_regs;
+	dt_ident_t	*idp = dt_dlib_get_func(yypcb->pcb_hdl,
+						"dt_probe_error");
+
+	assert(idp != NULL);
+
+	/*
+	 *	return dt_probe_error(
+	 *		dctx,		// lddw %r1, %fp, DT_STK_DCT
+	 *		off,		// mov %r2, off
+	 *		fault,		// mov %r3, fault
+	 *		illval);	// mov %r4, illval
+	 *				// call dt_probe_error
+	 *				// exit
+	 */
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, off));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_3, fault));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_4, illval));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+	emit(dlp,  BPF_RETURN());
+}
+
+/*
+ * Check whether mst->fault indicates a fault was triggered.  If so, abort the
+ * current clause by means of a straight jump to the exit labal.
+ */
+static void
+dt_cg_check_fault(dt_pcb_t *pcb)
+{
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+	dt_regset_t	*drp = pcb->pcb_regs;
+	int		reg = dt_regset_alloc(drp);
+	uint_t		lbl_ok = dt_irlist_label(dlp);
+
+	if (reg == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp,  BPF_LOAD(BPF_DW, reg, BPF_REG_FP, DT_STK_DCTX));
+	emit(dlp,  BPF_LOAD(BPF_DW, reg, reg, DCTX_MST));
+	emit(dlp,  BPF_LOAD(BPF_DW, reg, reg, DMST_FAULT));
+	emit(dlp,  BPF_BRANCH_IMM(BPF_JEQ, reg, 0, lbl_ok));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_0, 0));
+	emit(dlp,  BPF_RETURN());
+	emitl(dlp, lbl_ok,
+		   BPF_NOP());
+	dt_regset_free(drp, reg);
+}
 /*
  * Generate an instruction sequence to fill a gap in the output buffer with 0
  * values.  This is used to ensure that there are no uninitialized bytes in the
@@ -1348,44 +1450,34 @@ dt_cg_load_var(dt_node_t *dst, dt_irlist_t *dlp, dt_regset_t *drp)
 			emit(dlp, BPF_MOV_IMM(dst->dn_reg, 0));
 		else
 			emit(dlp, BPF_LOAD(BPF_DW, dst->dn_reg, BPF_REG_FP, DT_STK_LVAR(idp->di_id)));
-	} else if (idp->di_flags & DT_IDFLG_TLS) {	/* TLS var */
-		if (dt_regset_xalloc_args(drp) == -1)
-			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
-		emit(dlp,  BPF_MOV_IMM(BPF_REG_1, idp->di_id - DIF_VAR_OTHER_UBASE));
-		dt_regset_xalloc(drp, BPF_REG_0);
-		idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_tvar");
-		assert(idp != NULL);
-		emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
-		dt_regset_free_args(drp);
+		return;
+	}
 
-		if ((dst->dn_reg = dt_regset_alloc(drp)) == -1)
-			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
 
-		emit(dlp, BPF_MOV_REG(dst->dn_reg, BPF_REG_0));
-		dt_regset_free(drp, BPF_REG_0);
+	if (idp->di_flags & DT_IDFLG_TLS) {	/* TLS var */
+		emit(dlp, BPF_MOV_IMM(BPF_REG_1, idp->di_id - DIF_VAR_OTHER_UBASE));
+		idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_tvar");
+	} else if (idp->di_id < DIF_VAR_OTHER_UBASE) {	/* built-in var */
+		emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
+		emit(dlp, BPF_MOV_IMM(BPF_REG_2, idp->di_id));
+		idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_bvar");
 	} else {					/* global var */
-		if (dt_regset_xalloc_args(drp) == -1)
-			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
-		if (idp->di_id < DIF_VAR_OTHER_UBASE) {	/* built-in var */
-			emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
-			emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_1, DCTX_MST));
-			emit(dlp, BPF_MOV_IMM(BPF_REG_2, idp->di_id));
-			idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_bvar");
-		} else {
-			emit(dlp, BPF_MOV_IMM(BPF_REG_1, idp->di_id - DIF_VAR_OTHER_UBASE));
-			idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_gvar");
-		}
-		assert(idp != NULL);
-		dt_regset_xalloc(drp, BPF_REG_0);
-		emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
-		dt_regset_free_args(drp);
+		emit(dlp, BPF_MOV_IMM(BPF_REG_1, idp->di_id - DIF_VAR_OTHER_UBASE));
+		idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_gvar");
+	}
+	assert(idp != NULL);
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+	dt_regset_free_args(drp);
 
-		if ((dst->dn_reg = dt_regset_alloc(drp)) == -1)
-			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	dt_cg_check_fault(yypcb);
 
-		emit(dlp, BPF_MOV_REG(dst->dn_reg, BPF_REG_0));
-		dt_regset_free(drp, BPF_REG_0);
-	}
+	if ((dst->dn_reg = dt_regset_alloc(drp)) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp,  BPF_MOV_REG(dst->dn_reg, BPF_REG_0));
+	dt_regset_free(drp, BPF_REG_0);
 }
 
 static void
@@ -2717,7 +2809,8 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 		dnp->dn_reg = dnp->dn_child->dn_reg;
 
 		if (!(dnp->dn_flags & DT_NF_REF)) {
-			uint_t ubit;
+			uint_t	ubit;
+			uint_t	lbl_valid = dt_irlist_label(dlp);
 
 			/*
 			 * Save and restore DT_NF_USERLAND across dt_cg_load():
@@ -2728,6 +2821,13 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 			dnp->dn_flags |=
 			    (dnp->dn_child->dn_flags & DT_NF_USERLAND);
 
+			/* if NULL pointer, report BADARR */
+			emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, dnp->dn_reg, 0,
+						  lbl_valid));
+			dt_cg_probe_error(yypcb, -1, DTRACEFLT_BADADDR, 0);
+			emitl(dlp, lbl_valid,
+				   BPF_NOP());
+
 			/* FIXME: Does not handled signed or userland */
 			emit(dlp, BPF_LOAD(dt_cg_load(dnp, ctfp, dnp->dn_type), dnp->dn_reg, dnp->dn_reg, 0));
 
@@ -2811,14 +2911,26 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 		break;
 
 	case DT_TOK_PTR:
-	case DT_TOK_DOT:
+	case DT_TOK_DOT: {
+		uint_t	lbl_valid = dt_irlist_label(dlp);
+
 		assert(dnp->dn_right->dn_kind == DT_NODE_IDENT);
 		dt_cg_node(dnp->dn_left, dlp, drp);
 
 		/*
-		 * Ensure that the lvalue is not the NULL pointer.
+		 * If the lvalue is the NULL pointer, we must report a BADARR
+		 * fault.
+		 *
+		 *	if (left != 0)		// jne %lreg, 0, valid
+		 *		goto valid;
+		 *				//     (report BADADDR fault)
+		 * valid:
 		 */
-		emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_left->dn_reg, 0, yypcb->pcb_exitlbl));
+		emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, dnp->dn_left->dn_reg, 0,
+					  lbl_valid));
+		dt_cg_probe_error(yypcb, -1, DTRACEFLT_BADADDR, 0);
+		emitl(dlp, lbl_valid,
+			   BPF_NOP());
 
 		/*
 		 * If the left-hand side of PTR or DOT is a dynamic variable,
@@ -2900,6 +3012,7 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 
 		dnp->dn_reg = dnp->dn_left->dn_reg;
 		break;
+	}
 
 	case DT_TOK_STRING:
 		if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
diff --git a/libdtrace/dt_cg.h b/libdtrace/dt_cg.h
index bf8a3f1d..daeaa9a1 100644
--- a/libdtrace/dt_cg.h
+++ b/libdtrace/dt_cg.h
@@ -23,6 +23,7 @@ extern void dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act);
 extern void dt_cg_tramp_prologue(dt_pcb_t *pcb);
 extern void dt_cg_tramp_epilogue(dt_pcb_t *pcb);
 extern void dt_cg_tramp_epilogue_advance(dt_pcb_t *pcb, dt_activity_t act);
+extern void dt_cg_tramp_error(dt_pcb_t *pcb);
 
 #ifdef	__cplusplus
 }
diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c
index 1a562983..a034ee8e 100644
--- a/libdtrace/dt_consume.c
+++ b/libdtrace/dt_consume.c
@@ -1979,6 +1979,15 @@ dt_consume_one(dtrace_hdl_t *dtp, FILE *fp, char *buf,
 		if (rval != 0)
 			return dt_set_errno(dtp, EDT_BADEPID);
 
+		if (pdat->dtpda_ddesc->dtdd_uarg != DT_ECB_DEFAULT) {
+			rval = dt_handle(dtp, pdat);
+
+			if (rval == DTRACE_CONSUME_NEXT)
+				return DTRACE_WORKSTATUS_OKAY;
+			if (rval == DTRACE_CONSUME_ERROR)
+				return DTRACE_WORKSTATUS_ERROR;
+		}
+
 		if (flow)
 			dt_flowindent(dtp, pdat, *last, DTRACE_EPIDNONE);
 
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index b85c1962..8536704d 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -54,6 +54,7 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	/* BPF library (external) functions */
 	DT_BPF_SYMBOL(dt_agg_lqbin, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_agg_qbin, DT_IDENT_SYMBOL),
+	DT_BPF_SYMBOL(dt_error, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_get_bvar, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_get_gvar, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_get_string, DT_IDENT_SYMBOL),
@@ -394,6 +395,8 @@ get_symbols(dtrace_hdl_t *dtp, Elf *elf, int syms_idx, int strs_idx,
 			 * will get rid of these fake function symbols.
 			 */
 			idp = dt_dlib_get_map(dtp, name);
+			if (idp == NULL)
+				idp = dt_dlib_get_func(dtp, name);
 			if (idp == NULL) {
 				dt_dlib_error(dtp, D_IDENT_UNDEF,
 					      "undefined symbol %s in BPF dlib",
diff --git a/libdtrace/dt_dof.c b/libdtrace/dt_dof.c
index c78e38a3..06317d46 100644
--- a/libdtrace/dt_dof.c
+++ b/libdtrace/dt_dof.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -891,9 +891,6 @@ dtrace_getopt_dof(dtrace_hdl_t *dtp)
 void *
 dtrace_geterr_dof(dtrace_hdl_t *dtp)
 {
-	if (dtp->dt_errprog != NULL)
-		return dtrace_dof_create(dtp, dtp->dt_errprog, 0);
-
 	dt_set_errno(dtp, EDT_BADERROR);
 	return NULL;
 }
diff --git a/libdtrace/dt_handle.c b/libdtrace/dt_handle.c
index 52ff27a8..6968b052 100644
--- a/libdtrace/dt_handle.c
+++ b/libdtrace/dt_handle.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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.
  */
@@ -16,7 +16,6 @@
 #include <dt_impl.h>
 #include <dt_program.h>
 
-#ifdef FIXME
 static const char _dt_errprog[] =
 "dtrace:::ERROR"
 "{"
@@ -26,7 +25,6 @@ static const char _dt_errprog[] =
 "	trace(arg4);"
 "	trace(arg5);"
 "}";
-#endif
 
 int
 dtrace_handle_err(dtrace_hdl_t *dtp, dtrace_handle_err_f *hdlr, void *arg)
@@ -51,23 +49,18 @@ dtrace_handle_err(dtrace_hdl_t *dtp, dtrace_handle_err_f *hdlr, void *arg)
 	if (dtp->dt_options[DTRACEOPT_GRABANON] != DTRACEOPT_UNSET)
 		goto out;
 
-#if 0
-	if ((pgp = dtrace_program_strcompile(dtp, _dt_errprog,
-	    DTRACE_PROBESPEC_NAME, DTRACE_C_ZDEFS, 0, NULL)) == NULL)
+	pgp = dtrace_program_strcompile(dtp, _dt_errprog, DTRACE_PROBESPEC_NAME,
+					DTRACE_C_ZDEFS | DTRACE_C_EPROBE, 0,
+					NULL);
+	if (pgp == NULL)
 		return dt_set_errno(dtp, dtrace_errno(dtp));
 
-	stp = dt_list_next(&pgp->dp_stmts);
-	assert(stp != NULL);
-
-	edp = stp->ds_desc->dtsd_ecbdesc;
-	assert(edp != NULL);
-	edp->dted_uarg = DT_ECB_ERROR;
-#endif
+	if (dtrace_program_exec(dtp, pgp, NULL) == -1)
+		return -1;		/* errno already set */
 
 out:
 	dtp->dt_errhdlr = hdlr;
 	dtp->dt_errarg = arg;
-	dtp->dt_errprog = pgp;
 
 	return 0;
 }
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index e1aab88d..daa3642e 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -287,6 +287,8 @@ struct dtrace_hdl {
 	uint32_t dt_probes_sz;	/* size of array of probes */
 	uint32_t dt_probe_id;	/* next available probe id */
 
+	struct dt_probe *dt_error; /* ERROR probe */
+
 	dt_list_t dt_provlist;	/* linked list of dt_provider_t's */
 	struct dt_provider **dt_provs; /* hash table of dt_provider_t's */
 	uint_t dt_provbuckets;	/* number of provider hash buckets */
@@ -350,7 +352,6 @@ struct dtrace_hdl {
 	int dt_aggmap_fd;	/* file descriptor for the 'aggs' BPF map */
 	dtrace_handle_err_f *dt_errhdlr; /* error handler, if any */
 	void *dt_errarg;	/* error handler argument */
-	dtrace_prog_t *dt_errprog; /* error handler program, if any */
 	dtrace_handle_drop_f *dt_drophdlr; /* drop handler, if any */
 	void *dt_droparg;	/* drop handler argument */
 	dtrace_handle_proc_f *dt_prochdlr; /* proc handler, if any */
@@ -723,11 +724,13 @@ extern void *dt_compile(dtrace_hdl_t *dtp, int context,
 			dtrace_probespec_t pspec, void *arg, uint_t cflags,
 			int argc, char *const argv[], FILE *fp, const char *s);
 extern dtrace_difo_t *dt_program_construct(dtrace_hdl_t *dtp,
-					   struct dt_probe *prp, uint_t cflags);
+					   struct dt_probe *prp, uint_t cflags,
+					   dt_ident_t *idp);
 
 extern void dt_pragma(dt_node_t *);
 extern int dt_reduce(dtrace_hdl_t *, dt_version_t);
 extern dtrace_difo_t *dt_as(dt_pcb_t *);
+extern dtrace_difo_t *dt_difo_copy(dtrace_hdl_t *dtp, const dtrace_difo_t *odp);
 extern void dt_dis_program(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, FILE *fp);
 extern void dt_dis_difo(const dtrace_difo_t *dp, FILE *fp,
 			const dt_ident_t *idp);
@@ -736,6 +739,7 @@ extern int dt_aggregate_go(dtrace_hdl_t *);
 extern int dt_aggregate_init(dtrace_hdl_t *);
 extern void dt_aggregate_destroy(dtrace_hdl_t *);
 
+extern dtrace_datadesc_t *dt_datadesc_hold(dtrace_datadesc_t *ddp);
 extern void dt_datadesc_release(dtrace_hdl_t *, dtrace_datadesc_t *);
 extern dtrace_datadesc_t *dt_datadesc_create(dtrace_hdl_t *);
 extern int dt_datadesc_finalize(dtrace_hdl_t *, dtrace_datadesc_t *);
diff --git a/libdtrace/dt_map.c b/libdtrace/dt_map.c
index 6197d1d6..b7f067fb 100644
--- a/libdtrace/dt_map.c
+++ b/libdtrace/dt_map.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -15,10 +15,11 @@
 #include <dt_probe.h>
 #include <dt_printf.h>
 
-static void
+dtrace_datadesc_t *
 dt_datadesc_hold(dtrace_datadesc_t *ddp)
 {
 	ddp->dtdd_refcnt++;
+	return ddp;
 }
 
 void
@@ -49,9 +50,7 @@ dt_datadesc_create(dtrace_hdl_t *dtp)
 		return NULL;
 	}
 
-	dt_datadesc_hold(ddp);
-
-	return ddp;
+	return dt_datadesc_hold(ddp);
 }
 
 int
@@ -128,8 +127,7 @@ dt_epid_add(dtrace_hdl_t *dtp, dtrace_datadesc_t *ddp, dtrace_id_t prid)
 	if (dtp->dt_ddesc[epid] != NULL)
 		return epid;
 
-	dt_datadesc_hold(ddp);
-	dtp->dt_ddesc[epid] = ddp;
+	dtp->dt_ddesc[epid] = dt_datadesc_hold(ddp);
 	dtp->dt_pdesc[epid] = (dtrace_probedesc_t *)dtp->dt_probes[prid]->desc;
 
 	return epid;
diff --git a/libdtrace/dt_pcb.h b/libdtrace/dt_pcb.h
index cc39a0f3..28d824b3 100644
--- a/libdtrace/dt_pcb.h
+++ b/libdtrace/dt_pcb.h
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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.
  */
diff --git a/libdtrace/dt_probe.c b/libdtrace/dt_probe.c
index 23ee49ad..0190975e 100644
--- a/libdtrace/dt_probe.c
+++ b/libdtrace/dt_probe.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -22,6 +22,7 @@
 #include <dt_string.h>
 #include <dt_htab.h>
 #include <dt_list.h>
+#include <dt_bpf.h>
 
 typedef struct dt_probeclause {
 	dt_list_t	list;
@@ -1268,18 +1269,84 @@ dtrace_probe_iter(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp,
 	return dt_probe_iter(dtp, pdp, NULL, func, arg);
 }
 
+/*
+ * Create an ERROR-probe specific copy of a given clause.
+ *
+ * A modified copy of the clause is necessary because the ERROR probe may share
+ * some clauses with other probes, and yet it needs to be handled differently.
+ *
+ * The following modifications are made in the copy of the clause:
+ *
+ *	- it is named dt_error_N where N is taken from the original clause
+ *	  dt_clause_N (which also guarantees uniqueness)
+ */
+dt_ident_t *
+dt_probe_error_clause(dtrace_hdl_t *dtp, dt_ident_t *idp)
+{
+	char		*name;
+	int		len;
+	dtrace_difo_t	*dp = dt_dlib_get_func_difo(dtp, idp);
+	dt_ident_t	*nidp = NULL;
+	dtrace_difo_t	*ndp;
+
+	/*
+	 * Copy the DIFO.
+	 */
+	ndp = dt_difo_copy(dtp, dp);
+	if (ndp == NULL)
+		goto no_mem;
+
+	/*
+	 * Generate a new symbol name from the given identifier.
+	 */
+	len = strlen(idp->di_name);
+	name = dt_alloc(dtp, len);
+	if (name == NULL)
+		goto no_mem;
+
+	snprintf(name, len, "dt_error_%s", idp->di_name + strlen("dt_clause_"));
+
+	/*
+	 * Add the new symbol to the BPF identifier table and associate the
+	 * modified copy of the DIFO with the symbol.
+	 */
+	nidp = dt_dlib_add_func(dtp, name);
+	dt_free(dtp, name);
+	if (nidp == NULL)
+		goto no_mem;
+
+	dt_ident_set_data(nidp, ndp);
+
+	return nidp;
+
+no_mem:
+	if (ndp != NULL)
+		dt_difo_free(dtp, ndp);
+	if (nidp != NULL)
+		dt_ident_destroy(nidp);
+
+	dt_set_errno(dtp, EDT_NOMEM);
+	return NULL;
+}
+
 int
 dt_probe_add_clause(dtrace_hdl_t *dtp, dt_probe_t *prp, dt_ident_t *idp)
 {
 	dt_probeclause_t	*pcp;
 
 	pcp = dt_zalloc(dtp, sizeof(dt_probeclause_t));;
-	if (pcp == NULL) {
-		dt_set_errno(dtp, EDT_NOMEM);
-		return -1;
-	}
+	if (pcp == NULL)
+		return dt_set_errno(dtp, EDT_NOMEM);
+
+	if (prp == dtp->dt_error) {
+		pcp->clause = dt_probe_error_clause(dtp, idp);
+		if (pcp->clause == NULL) {
+			dt_free(dtp, pcp);
+			return 0;
+		}
+	} else
+		pcp->clause = idp;
 
-	pcp->clause = idp;
 	dt_list_append(&prp->clauses, pcp);
 
 	return 0;
diff --git a/libdtrace/dt_prov_dtrace.c b/libdtrace/dt_prov_dtrace.c
index b28f212d..fcd91400 100644
--- a/libdtrace/dt_prov_dtrace.c
+++ b/libdtrace/dt_prov_dtrace.c
@@ -48,13 +48,19 @@ static int populate(dtrace_hdl_t *dtp)
 		n++;
 		dt_list_append(&dtp->dt_enablings, prp);
 	}
+
 	prp = tp_probe_insert(dtp, prv, prvname, modname, funname, "END");
 	if (prp) {
 		n++;
 		dt_list_append(&dtp->dt_enablings, prp);
 	}
-	if (tp_probe_insert(dtp, prv, prvname, modname, funname, "ERROR"))
+
+	prp = tp_probe_insert(dtp, prv, prvname, modname, funname, "ERROR");
+	if (prp) {
 		n++;
+		dt_list_append(&dtp->dt_enablings, prp);
+		dtp->dt_error = prp;
+	}
 
 	return n;
 }
@@ -74,10 +80,20 @@ static void trampoline(dt_pcb_t *pcb)
 {
 	int		i;
 	dt_irlist_t	*dlp = &pcb->pcb_ir;
-	dt_activity_t	act;
+	dt_activity_t	act = DT_ACTIVITY_ACTIVE;
 	int		adv_act;
 	uint32_t	key = 0;
 
+	/*
+	 * The ERROR probe isn't really a trace event that a BPF program is
+	 * attached to.  Its entire trampoline program is provided by the code
+	 * generator.
+	 */
+	if (strcmp(pcb->pcb_probe->desc->prb, "ERROR") == 0) {
+		dt_cg_tramp_error(pcb);
+		return;
+	}
+
 	/*
 	 * The BEGIN probe should only run when the activity state is INACTIVE.
 	 * At the end of the trampoline (after executing any clauses), the
@@ -95,9 +111,6 @@ static void trampoline(dt_pcb_t *pcb)
 	 * When the END probe is triggered, we need to record the CPU it runs
 	 * on in state[DT_STATE_ENDEDON] to ensure that we know which trace
 	 * data buffer to process last.
-	 *
-	 * Any other probe requires the state to be ACTIVE, and does not change
-	 * the state.
 	 */
 	if (strcmp(pcb->pcb_probe->desc->prb, "BEGIN") == 0) {
 		act = DT_ACTIVITY_INACTIVE;
@@ -107,9 +120,6 @@ static void trampoline(dt_pcb_t *pcb)
 		act = DT_ACTIVITY_DRAINING;
 		adv_act = 1;
 		key = DT_STATE_ENDEDON;
-	} else {
-		act = DT_ACTIVITY_ACTIVE;
-		adv_act = 0;
 	}
 
 	dt_cg_tramp_prologue_act(pcb, act);
diff --git a/libdtrace/dt_work.c b/libdtrace/dt_work.c
index c9142114..7eb13da2 100644
--- a/libdtrace/dt_work.c
+++ b/libdtrace/dt_work.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -149,18 +149,6 @@ dtrace_go(dtrace_hdl_t *dtp, uint_t cflags)
 	if (dtp->dt_active)
 		return dt_set_errno(dtp, EINVAL);
 
-	/*
-	 * If a dtrace:::ERROR program and callback are registered, enable the
-	 * program before we start tracing.  If this fails for a vector open
-	 * with ENOTTY, we permit dtrace_go() to succeed so that vector clients
-	 * such as mdb's dtrace module can execute the rest of dtrace_go() even
-	 * though they do not provide support for the DTRACEIOC_ENABLE ioctl.
-	 */
-	if (dtp->dt_errprog != NULL &&
-	    dtrace_program_exec(dtp, dtp->dt_errprog, NULL) == -1 && (
-	    dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL))
-		return -1;		/* dt_errno has been set for us */
-
 	/*
 	 * Create the global BPF maps.  This is done only once regardless of
 	 * how many programs there are.
diff --git a/libdtrace/dtrace.h b/libdtrace/dtrace.h
index 4cf7c998..f9650e6e 100644
--- a/libdtrace/dtrace.h
+++ b/libdtrace/dtrace.h
@@ -101,7 +101,8 @@ typedef struct dtrace_proginfo {
 #define	DTRACE_C_DEFARG	0x0800	/* Use 0/"" as value for unspecified args */
 #define	DTRACE_C_NOLIBS	0x1000	/* Do not process D system libraries */
 #define	DTRACE_C_CTL	0x2000	/* Only process control directives */
-#define	DTRACE_C_MASK	0x3bff	/* mask of all valid flags to dtrace_*compile */
+#define	DTRACE_C_EPROBE	0x8000	/* Compiling default ERROR probe clause */
+#define	DTRACE_C_MASK	0xbbff	/* mask of all valid flags to dtrace_*compile */
 
 extern dtrace_prog_t *dtrace_program_strcompile(dtrace_hdl_t *dtp, const char *s,
     dtrace_probespec_t spec, uint_t cflags, int argc, char *const argv[]);
@@ -150,6 +151,7 @@ typedef struct dtrace_stmtdesc {
 #define DT_CLSFLAG_COMMIT	4	/* commit */
 #define DT_CLSFLAG_EXIT		8	/* exit */
 #define DT_CLSFLAG_DESTRUCT	16	/* destructive */
+#define DT_CLSFLAG_ERROR	32	/* may throw an error */
 
 typedef int dtrace_stmt_f(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
     dtrace_stmtdesc_t *sdp, void *data);
diff --git a/test/unittest/builtinvar/tst.arg0.d b/test/unittest/builtinvar/tst.arg0.d
index c7069da0..08a117ef 100644
--- a/test/unittest/builtinvar/tst.arg0.d
+++ b/test/unittest/builtinvar/tst.arg0.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -19,3 +19,8 @@ BEGIN
 	printf("The argument is %u\n", arg0);
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.arg0clause.d b/test/unittest/builtinvar/tst.arg0clause.d
index 6352b1ac..3bf6cfa2 100644
--- a/test/unittest/builtinvar/tst.arg0clause.d
+++ b/test/unittest/builtinvar/tst.arg0clause.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -25,3 +25,7 @@ syscall:::entry
 	printf("The argument is %u", arg0);
 	exit(0);
 }
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.arg1.d b/test/unittest/builtinvar/tst.arg1.d
index ee1673c4..5e6fceae 100644
--- a/test/unittest/builtinvar/tst.arg1.d
+++ b/test/unittest/builtinvar/tst.arg1.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -19,3 +19,8 @@ BEGIN
 	printf("The argument is %u\n", arg1);
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.arg1to8.d b/test/unittest/builtinvar/tst.arg1to8.d
index 54d1f6d2..b01b8010 100644
--- a/test/unittest/builtinvar/tst.arg1to8.d
+++ b/test/unittest/builtinvar/tst.arg1to8.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -20,3 +20,8 @@ BEGIN
 			arg3, arg4, arg5, arg6, arg7, arg8);
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.arg1to8clause.d b/test/unittest/builtinvar/tst.arg1to8clause.d
index 45adfc04..47ec5bc2 100644
--- a/test/unittest/builtinvar/tst.arg1to8clause.d
+++ b/test/unittest/builtinvar/tst.arg1to8clause.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -25,3 +25,8 @@ syscall:::entry
 		arg3, arg4, arg5, arg6, arg7, arg8);
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.caller.d b/test/unittest/builtinvar/tst.caller.d
index 313516a8..a6d867ea 100644
--- a/test/unittest/builtinvar/tst.caller.d
+++ b/test/unittest/builtinvar/tst.caller.d
@@ -1,9 +1,10 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
@@ -19,3 +20,8 @@ BEGIN
 	printf("The caller is %u\n", caller);
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.caller1.d b/test/unittest/builtinvar/tst.caller1.d
index eb6eada2..0f568190 100644
--- a/test/unittest/builtinvar/tst.caller1.d
+++ b/test/unittest/builtinvar/tst.caller1.d
@@ -1,9 +1,10 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
@@ -23,3 +24,8 @@ tick-10ms
 	printf("The caller is %u\n", caller);
 	exit (0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.epid.d b/test/unittest/builtinvar/tst.epid.d
index 6c2f7ee3..eae028e9 100644
--- a/test/unittest/builtinvar/tst.epid.d
+++ b/test/unittest/builtinvar/tst.epid.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -23,3 +23,8 @@ tick-10ms
 	printf("epid of this probe = %d\n", epid);
 	exit (0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.epid1.d b/test/unittest/builtinvar/tst.epid1.d
index eff8577a..4148c8c4 100644
--- a/test/unittest/builtinvar/tst.epid1.d
+++ b/test/unittest/builtinvar/tst.epid1.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -19,3 +19,8 @@ BEGIN
 	printf("epid of this probe = %d\n", epid);
 	exit (0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.errno.d b/test/unittest/builtinvar/tst.errno.d
index 1627ea30..58b71bc3 100644
--- a/test/unittest/builtinvar/tst.errno.d
+++ b/test/unittest/builtinvar/tst.errno.d
@@ -1,9 +1,10 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
@@ -24,3 +25,8 @@ tick-10ms
 	printf("the errno = %d\n", errno);
 	exit (0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.errno1.d b/test/unittest/builtinvar/tst.errno1.d
index c53ae0e7..dcac23ae 100644
--- a/test/unittest/builtinvar/tst.errno1.d
+++ b/test/unittest/builtinvar/tst.errno1.d
@@ -1,9 +1,10 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
@@ -20,3 +21,8 @@ BEGIN
 	printf("the errno = %d\n", errno);
 	exit (0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.errno2.d b/test/unittest/builtinvar/tst.errno2.d
index 1f41be33..44a8c785 100644
--- a/test/unittest/builtinvar/tst.errno2.d
+++ b/test/unittest/builtinvar/tst.errno2.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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.
  */
@@ -54,3 +54,8 @@ proc:::exit
 {
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.execname.d b/test/unittest/builtinvar/tst.execname.d
index aed9f335..0dee39e8 100644
--- a/test/unittest/builtinvar/tst.execname.d
+++ b/test/unittest/builtinvar/tst.execname.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -27,3 +27,8 @@ BEGIN
 	trace("execname didn't match");
 	exit(1);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.hpriority.d b/test/unittest/builtinvar/tst.hpriority.d
index b755acc1..f5adf3f5 100644
--- a/test/unittest/builtinvar/tst.hpriority.d
+++ b/test/unittest/builtinvar/tst.hpriority.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -18,5 +18,10 @@
 BEGIN
 {
 	printf("The high priority = %d\n", curlwpsinfo->pr_pri);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.id.d b/test/unittest/builtinvar/tst.id.d
index a0c4bd5f..f4187cdd 100644
--- a/test/unittest/builtinvar/tst.id.d
+++ b/test/unittest/builtinvar/tst.id.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -21,5 +21,10 @@ BEGIN
 tick-10ms
 {
 	printf("id of this probe = %d\n", id);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.id1.d b/test/unittest/builtinvar/tst.id1.d
index 8bc8ebc2..aef19210 100644
--- a/test/unittest/builtinvar/tst.id1.d
+++ b/test/unittest/builtinvar/tst.id1.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -17,5 +17,10 @@
 BEGIN
 {
 	printf("id of this probe = %d\n", id);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.ipl.d b/test/unittest/builtinvar/tst.ipl.d
index 65aca8ab..c2b9850f 100644
--- a/test/unittest/builtinvar/tst.ipl.d
+++ b/test/unittest/builtinvar/tst.ipl.d
@@ -1,9 +1,10 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
@@ -21,5 +22,10 @@ BEGIN
 tick-10ms
 {
 	printf("The interrupt priority level = %u\n", ipl);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.ipl1.d b/test/unittest/builtinvar/tst.ipl1.d
index 6a523371..1b489229 100644
--- a/test/unittest/builtinvar/tst.ipl1.d
+++ b/test/unittest/builtinvar/tst.ipl1.d
@@ -1,9 +1,10 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
@@ -17,5 +18,10 @@
 BEGIN
 {
 	printf("The interrupt priority level = %u\n", ipl);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.lwpsinfo.d b/test/unittest/builtinvar/tst.lwpsinfo.d
index 6bea6101..272ecdfe 100644
--- a/test/unittest/builtinvar/tst.lwpsinfo.d
+++ b/test/unittest/builtinvar/tst.lwpsinfo.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -31,5 +31,10 @@ tick-10ms
 	printf("The current lwp has priority %d\n", curlwpsinfo->pr_pri);
 	printf("The current lwp is named %s\n", curlwpsinfo->pr_name);
 	printf("The current lwp is on cpu %d\n", curlwpsinfo->pr_onpro);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.lwpsinfo1.d b/test/unittest/builtinvar/tst.lwpsinfo1.d
index a1cc5370..6011670d 100644
--- a/test/unittest/builtinvar/tst.lwpsinfo1.d
+++ b/test/unittest/builtinvar/tst.lwpsinfo1.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -27,5 +27,10 @@ BEGIN
 	printf("The current lwp has priority %d\n", curlwpsinfo->pr_pri);
 	printf("The current lwp is named %s\n", curlwpsinfo->pr_name);
 	printf("The current lwp is on cpu %d\n", curlwpsinfo->pr_onpro);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.pid.d b/test/unittest/builtinvar/tst.pid.d
index b54cf2d0..f233fd06 100644
--- a/test/unittest/builtinvar/tst.pid.d
+++ b/test/unittest/builtinvar/tst.pid.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -21,5 +21,10 @@ BEGIN
 tick-10ms
 {
 	printf("process id = %d \n", pid);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.pid1.d b/test/unittest/builtinvar/tst.pid1.d
index e86c1155..7775ca57 100644
--- a/test/unittest/builtinvar/tst.pid1.d
+++ b/test/unittest/builtinvar/tst.pid1.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -17,5 +17,10 @@
 BEGIN
 {
 	printf("process id = %d \n", pid);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.psinfo-bug21974606.d b/test/unittest/builtinvar/tst.psinfo-bug21974606.d
index 0e9aa19f..68d9503d 100644
--- a/test/unittest/builtinvar/tst.psinfo-bug21974606.d
+++ b/test/unittest/builtinvar/tst.psinfo-bug21974606.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
  */
@@ -29,3 +29,8 @@ exec-success
 	printf("Last char is [%c]\n", psargs[strlen(psargs) - 1]);
 	exit(psargs[strlen(psargs) - 1] == ' ' ? 1 : 0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.psinfo-bug21984854.d b/test/unittest/builtinvar/tst.psinfo-bug21984854.d
index e6e3acc1..8be02af0 100644
--- a/test/unittest/builtinvar/tst.psinfo-bug21984854.d
+++ b/test/unittest/builtinvar/tst.psinfo-bug21984854.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
diff --git a/test/unittest/builtinvar/tst.psinfo-bug22561297.d b/test/unittest/builtinvar/tst.psinfo-bug22561297.d
index e17c74ad..b9efd0ec 100644
--- a/test/unittest/builtinvar/tst.psinfo-bug22561297.d
+++ b/test/unittest/builtinvar/tst.psinfo-bug22561297.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -45,7 +45,7 @@ tick-10ms
 	printf("pool id = %d\n", curpsinfo->pr_poolid);
 	printf("zone id = %d\n", curpsinfo->pr_zoneid);
 	printf("contract = %d\n", curpsinfo->pr_contract);
-	exit (0);
+	exit(0);
 }
 
 ERROR
diff --git a/test/unittest/builtinvar/tst.psinfo.d b/test/unittest/builtinvar/tst.psinfo.d
index 9a9f718c..92f91de7 100644
--- a/test/unittest/builtinvar/tst.psinfo.d
+++ b/test/unittest/builtinvar/tst.psinfo.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -44,7 +44,7 @@ tick-10ms
 	printf("pool id = %d\n", curpsinfo->pr_poolid);
 	printf("zone id = %d\n", curpsinfo->pr_zoneid);
 	printf("contract = %d\n", curpsinfo->pr_contract);
-	exit (0);
+	exit(0);
 }
 
 ERROR
diff --git a/test/unittest/builtinvar/tst.psinfo1.d b/test/unittest/builtinvar/tst.psinfo1.d
index da770d80..9e6d5053 100644
--- a/test/unittest/builtinvar/tst.psinfo1.d
+++ b/test/unittest/builtinvar/tst.psinfo1.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -42,5 +42,10 @@ BEGIN
 	printf("pool id = %d\n", curpsinfo->pr_poolid);
 	printf("zone id = %d\n", curpsinfo->pr_zoneid);
 	printf("contract = %d\n", curpsinfo->pr_contract);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.tid.d b/test/unittest/builtinvar/tst.tid.d
index d8f35b28..e5af0321 100644
--- a/test/unittest/builtinvar/tst.tid.d
+++ b/test/unittest/builtinvar/tst.tid.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -21,5 +21,10 @@ BEGIN
 tick-10ms
 {
 	printf("Thread id = %d \n", tid);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.tid1.d b/test/unittest/builtinvar/tst.tid1.d
index 545a8025..9f4acb5b 100644
--- a/test/unittest/builtinvar/tst.tid1.d
+++ b/test/unittest/builtinvar/tst.tid1.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -17,5 +17,10 @@
 BEGIN
 {
 	printf("Thread id = %d \n", tid);
-	exit (0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
 }
diff --git a/test/unittest/builtinvar/tst.timestamp.d b/test/unittest/builtinvar/tst.timestamp.d
index b1f7103b..4d4126c6 100644
--- a/test/unittest/builtinvar/tst.timestamp.d
+++ b/test/unittest/builtinvar/tst.timestamp.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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.
  */
@@ -20,3 +20,8 @@ BEGIN
 	printf("The difftime = %d\n", timestamp - this->t);
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.vtimestamp.d b/test/unittest/builtinvar/tst.vtimestamp.d
index 3e6c8bb8..c9e2a112 100644
--- a/test/unittest/builtinvar/tst.vtimestamp.d
+++ b/test/unittest/builtinvar/tst.vtimestamp.d
@@ -1,9 +1,10 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
@@ -20,3 +21,8 @@ BEGIN
 	printf("The difftime = %d\n", vtimestamp - self->t);
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/builtinvar/tst.vtimestamp2.d b/test/unittest/builtinvar/tst.vtimestamp2.d
index b52324e8..087a11e3 100644
--- a/test/unittest/builtinvar/tst.vtimestamp2.d
+++ b/test/unittest/builtinvar/tst.vtimestamp2.d
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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.
  */
@@ -35,3 +35,8 @@ END
 		? -1
 		: (vtimestamp - self->vt) <= (timestamp - self->ts) ? 0 : 1);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/error/tst.DTRACEFLT_BADADDR.d b/test/unittest/error/tst.DTRACEFLT_BADADDR.d
index 84b79bf1..86a28f43 100644
--- a/test/unittest/error/tst.DTRACEFLT_BADADDR.d
+++ b/test/unittest/error/tst.DTRACEFLT_BADADDR.d
@@ -1,10 +1,9 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
@@ -19,7 +18,7 @@
 
 ERROR
 {
-	printf("The arguments are %u %u %u %u %u\n",
+	printf("The arguments are %u %u %d %u %u\n",
 		arg1, arg2, arg3, arg4, arg5);
 	printf("The value of arg4 should be %u\n", DTRACEFLT_BADADDR);
 	printf("The value of arg5 should be %u\n", NULL);
diff --git a/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.d b/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.d
new file mode 100644
index 00000000..f764e84c
--- /dev/null
+++ b/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.d
@@ -0,0 +1,27 @@
+/*
+ * 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: Test DTRACEFLT_BADADDR error reporting.
+ *
+ * SECTION: dtrace Provider
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	myepid = epid;
+	trace(((struct task_struct *)NULL)->pid);
+	exit(1);
+}
+
+ERROR
+{
+	exit(arg1 != myepid || arg2 != 1 || arg3 != -1 ||
+	     arg4 != DTRACEFLT_BADADDR || arg5 != 0);
+}
diff --git a/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.r b/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.r
new file mode 100644
index 00000000..ef424aed
--- /dev/null
+++ b/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.r
@@ -0,0 +1,3 @@
+
+-- @@stderr --
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/error/tst.DTRACEFLT_BADADDR.r b/test/unittest/error/tst.DTRACEFLT_BADADDR.r
index f323f843..dfb674cd 100644
--- a/test/unittest/error/tst.DTRACEFLT_BADADDR.r
+++ b/test/unittest/error/tst.DTRACEFLT_BADADDR.r
@@ -1,6 +1,6 @@
-The arguments are 2 1 16 1 0
+The arguments are 3 1 -1 1 0
 The value of arg4 should be 1
 The value of arg5 should be 0
 
 -- @@stderr --
-dtrace: error on enabled probe ID 2 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1 at DIF offset 16
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/error/tst.DTRACEFLT_BADADDR2.r b/test/unittest/error/tst.DTRACEFLT_BADADDR2.r
index 646233c9..3a0f2a63 100644
--- a/test/unittest/error/tst.DTRACEFLT_BADADDR2.r
+++ b/test/unittest/error/tst.DTRACEFLT_BADADDR2.r
@@ -3,4 +3,4 @@ The value of arg4 should be 1
 The value of arg5 should be 16384
 
 -- @@stderr --
-dtrace: error on enabled probe ID 2 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #2 at DIF offset 4
+dtrace: error on enabled probe ID 2 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #2
diff --git a/test/unittest/error/tst.clause_scope-begin-ended.d b/test/unittest/error/tst.clause_scope-begin-ended.d
new file mode 100644
index 00000000..29dd0aef
--- /dev/null
+++ b/test/unittest/error/tst.clause_scope-begin-ended.d
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+/* @@xfail: dtv2 */
+/*
+ * ASSERTION: A fault that triggers the ERROR probe terminates the execution of
+ *            the current clause, but other clauses for the same probe should
+ *            still be executed.  This tests the special case where the fault
+ *            occurs in the BEGIN probe and probing ends before we process the
+ *            probe data.
+ *
+ * SECTION: dtrace Provider
+ */
+
+#pragma D option quiet
+
+ERROR
+{
+	printf("Error fired\n");
+}
+
+BEGIN
+{
+	trace(((struct task_struct *)NULL)->pid);
+}
+
+BEGIN
+{
+	printf("Clause executed\n");
+	exit(0);
+}
diff --git a/test/unittest/error/tst.clause_scope-begin-ended.r b/test/unittest/error/tst.clause_scope-begin-ended.r
new file mode 100644
index 00000000..839d9004
--- /dev/null
+++ b/test/unittest/error/tst.clause_scope-begin-ended.r
@@ -0,0 +1,5 @@
+Error fired
+Clause executed
+
+-- @@stderr --
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/error/tst.clause_scope-begin.d b/test/unittest/error/tst.clause_scope-begin.d
new file mode 100644
index 00000000..c1195854
--- /dev/null
+++ b/test/unittest/error/tst.clause_scope-begin.d
@@ -0,0 +1,38 @@
+/*
+ * 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: A fault that triggers the ERROR probe terminates the execution of
+ *            the current clause, but other clauses for the same probe should
+ *            still be executed.  This tests the special case where the fault
+ *            occurs in the BEGIN probe and the probe data is processed before
+ *            probing ends.
+ *
+ * SECTION: dtrace Provider
+ */
+
+#pragma D option quiet
+
+ERROR
+{
+	printf("Error fired\n");
+}
+
+BEGIN
+{
+	trace(((struct task_struct *)NULL)->pid);
+}
+
+BEGIN
+{
+	printf("Clause executed\n");
+}
+
+tick-1s
+{
+	exit(0);
+}
diff --git a/test/unittest/error/tst.clause_scope-begin.r b/test/unittest/error/tst.clause_scope-begin.r
new file mode 100644
index 00000000..839d9004
--- /dev/null
+++ b/test/unittest/error/tst.clause_scope-begin.r
@@ -0,0 +1,5 @@
+Error fired
+Clause executed
+
+-- @@stderr --
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/error/tst.clause_scope-regular.d b/test/unittest/error/tst.clause_scope-regular.d
new file mode 100644
index 00000000..cda9f70a
--- /dev/null
+++ b/test/unittest/error/tst.clause_scope-regular.d
@@ -0,0 +1,32 @@
+/*
+ * 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: A fault that triggers the ERROR probe terminates the execution of
+ *            the current clause, but other clauses for the same probe should
+ *            still be executed.
+ *
+ * SECTION: dtrace Provider
+ */
+
+#pragma D option quiet
+
+ERROR
+{
+	printf("Error fired\n");
+}
+
+tick-10ms
+{
+	trace(((struct task_struct *)NULL)->pid);
+}
+
+tick-10ms
+{
+	printf("Clause executed\n");
+	exit(0);
+}
diff --git a/test/unittest/error/tst.clause_scope-regular.r b/test/unittest/error/tst.clause_scope-regular.r
new file mode 100644
index 00000000..ea15aeb0
--- /dev/null
+++ b/test/unittest/error/tst.clause_scope-regular.r
@@ -0,0 +1,5 @@
+Error fired
+Clause executed
+
+-- @@stderr --
+dtrace: error on enabled probe ID 3 (ID nnn: profile:::tick-10ms): invalid address ({ptr}) in action #1
diff --git a/test/unittest/error/tst.clause_scope-regular.r.p b/test/unittest/error/tst.clause_scope-regular.r.p
new file mode 100755
index 00000000..7659601b
--- /dev/null
+++ b/test/unittest/error/tst.clause_scope-regular.r.p
@@ -0,0 +1,3 @@
+#!/bin/sed -f
+# This report has a variable probe ID in it.
+s/ID [0-9][0-9]*: profile/ID nnn: profile/
diff --git a/test/unittest/error/tst.error.d b/test/unittest/error/tst.error.d
index 1d0b87c5..6cace8e3 100644
--- a/test/unittest/error/tst.error.d
+++ b/test/unittest/error/tst.error.d
@@ -1,20 +1,16 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
- *	To fire ERROR probe
+ * ASSERTION: Test ERROR probe firing.
  *
  * SECTION: dtrace Provider
- *
  */
 
-
 #pragma D option quiet
 
 ERROR
@@ -26,4 +22,5 @@ ERROR
 BEGIN
 {
 	*(char *)NULL;
+	exit(1);
 }
diff --git a/test/unittest/error/tst.error.r b/test/unittest/error/tst.error.r
index 190551ee..b2ceedd6 100644
--- a/test/unittest/error/tst.error.r
+++ b/test/unittest/error/tst.error.r
@@ -1,4 +1,4 @@
 Error fired
 
 -- @@stderr --
-dtrace: error on enabled probe ID 2 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1 at DIF offset 16
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/error/tst.errorend.d b/test/unittest/error/tst.errorend.d
index 07171752..8e22d393 100644
--- a/test/unittest/error/tst.errorend.d
+++ b/test/unittest/error/tst.errorend.d
@@ -1,20 +1,16 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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:
- *	Flow of ERROR probe
+ * ASSERTION: Ensure the END probe still fires after an ERROR was reported.
  *
  * SECTION: dtrace Provider
- *
  */
 
-
 #pragma D option quiet
 
 ERROR
@@ -31,4 +27,5 @@ END
 BEGIN
 {
 	*(char *)NULL;
+	exit(1);
 }
diff --git a/test/unittest/error/tst.errorend.r b/test/unittest/error/tst.errorend.r
index 7a4184e4..ea8f1c36 100644
--- a/test/unittest/error/tst.errorend.r
+++ b/test/unittest/error/tst.errorend.r
@@ -2,4 +2,4 @@ Error fired
 End fired after exit
 
 -- @@stderr --
-dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1 at DIF offset 16
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/printa/tst.walltimestamp.sh b/test/unittest/printa/tst.walltimestamp.sh
index f2d05b0c..aaa627bc 100755
--- a/test/unittest/printa/tst.walltimestamp.sh
+++ b/test/unittest/printa/tst.walltimestamp.sh
@@ -1,10 +1,11 @@
 #!/bin/bash
 #
 # Oracle Linux DTrace.
-# Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 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
 if [ $# != 1 ]; then
 	echo expected one argument: '<'dtrace-path'>'
 	exit 2
@@ -32,6 +33,10 @@ BEGIN
 
 	exit(0);
 }
+ERROR
+{
+	exit(1);
+}
 EOF
 
 if [ $? -ne 0 ]; then
-- 
2.28.0




More information about the DTrace-devel mailing list