[DTrace-devel] [PATCH] WIP: ERROR probe implementation
Kris Van Hees
kris.van.hees at oracle.com
Sat Jan 16 00:23:34 PST 2021
###
### This is a Work-In-Progress. The code should be near complete (so feel
### free to check it out and comment). The testsuite tests still need to
### be tweaked, and some follow-up patches are necessary to enable more of
### the error conditions that should trigger the ERROR probe.
###
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.
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
bpf/Build | 3 +-
bpf/get_bvar.c | 23 +++--
bpf/probe_error.c | 36 ++++++++
bpf/probe_error.h | 14 ++++
include/dtrace/difo_defines.h | 1 +
libdtrace/dt_as.c | 91 ++++++++++++++++++++
libdtrace/dt_bpf.c | 5 +-
libdtrace/dt_cc.c | 6 ++
libdtrace/dt_cg.c | 153 ++++++++++++++++++++++++++++++----
libdtrace/dt_consume.c | 9 ++
libdtrace/dt_handle.c | 12 +--
libdtrace/dt_impl.h | 4 +
libdtrace/dt_map.c | 12 ++-
libdtrace/dt_pcb.h | 3 +-
libdtrace/dt_probe.c | 125 +++++++++++++++++++++++++--
libdtrace/dt_prov_dtrace.c | 8 +-
libdtrace/dtrace.h | 4 +-
17 files changed, 459 insertions(+), 50 deletions(-)
create mode 100644 bpf/probe_error.c
create mode 100644 bpf/probe_error.h
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..8d941756 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,6 +21,12 @@
extern struct bpf_map_def cpuinfo;
extern struct bpf_map_def state;
+#define error(mst, fault, illval) \
+ ({ \
+ dt_probe_error((mst), -1, (fault), (illval)); \
+ -1; \
+ })
+
noinline uint64_t dt_get_bvar(dt_mstate_t *mst, uint32_t id)
{
switch (id) {
@@ -75,13 +84,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(mst, DTRACEFLT_BADADDR, ptr);
if (bpf_probe_read((void *)&ptr, 8,
(const void *)(ptr + *parent_off)))
- return -1;
+ return error(mst, DTRACEFLT_BADADDR, ptr + *parent_off);
if (bpf_probe_read((void *)&val, 4,
(const void *)(ptr + *tgid_off)))
- return -1;
+ return error(mst, DTRACEFLT_BADADDR, ptr + *tgid_off);
return (uint64_t)val;
}
@@ -106,10 +115,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(mst, DTRACEFLT_ILLOP, 0);
}
}
diff --git a/bpf/probe_error.c b/bpf/probe_error.c
new file mode 100644
index 00000000..f697b38f
--- /dev/null
+++ b/bpf/probe_error.c
@@ -0,0 +1,36 @@
+// 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
+
+/*
+ * 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 int64_t dt_probe_error(dt_mstate_t *mst, uint64_t pc, uint64_t fault,
+ uint64_t illval)
+{
+ 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;
+
+ mst->fault |= fault;
+
+ return fault;
+}
diff --git a/bpf/probe_error.h b/bpf/probe_error.h
new file mode 100644
index 00000000..8bd5ae29
--- /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 int64_t dt_probe_error(dt_mstate_t *mst, uint64_t pc, uint64_t fault,
+ uint64_t illval);
+
+#endif /* BPF_PROBE_ERRROR_H */
diff --git a/include/dtrace/difo_defines.h b/include/dtrace/difo_defines.h
index 1175a001..75e0b449 100644
--- a/include/dtrace/difo_defines.h
+++ b/include/dtrace/difo_defines.h
@@ -21,5 +21,6 @@ struct dtrace_difo;
* DIFO flags.
*/
#define DIFOFLG_DESTRUCTIVE 1 /* Uses destructive ops */
+#define DIFOFLG_ERROR 2 /* May trigger ERROR probe */
#endif /* _DTRACE_DIFO_DEFINES_H */
diff --git a/libdtrace/dt_as.c b/libdtrace/dt_as.c
index 4ca2f420..db65bdc7 100644
--- a/libdtrace/dt_as.c
+++ b/libdtrace/dt_as.c
@@ -258,6 +258,13 @@ dt_as(dt_pcb_t *pcb)
if (labels == NULL)
longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
+ /*
+ * Indicate whether this function contains code that may trigger the
+ * ERROR probe.
+ */
+ if (pcb->pcb_faultlbl > 0)
+ dp->dtdo_flags |= DIFOFLG_ERROR;
+
/*
* Make an initial pass through the instruction list, filling in the
* instruction buffer with valid instructions and skipping labeled nops.
@@ -605,3 +612,87 @@ 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;
+
+ /* Copy the executable code. */
+ dp->dtdo_len = odp->dtdo_len;
+ dp->dtdo_buf = dt_calloc(dtp, dp->dtdo_len, sizeof(struct bpf_insn));
+ if (dp->dtdo_buf == NULL)
+ goto no_mem;
+
+ memcpy(dp->dtdo_buf, odp->dtdo_buf,
+ dp->dtdo_len * sizeof(struct bpf_insn));
+
+ /* Copy the string table (if any). */
+ if (odp->dtdo_strlen > 0) {
+ dp->dtdo_strlen = odp->dtdo_strlen;
+ dp->dtdo_strtab = dt_zalloc(dtp, dp->dtdo_strlen);
+ if (dp->dtdo_strtab == NULL)
+ goto no_mem;
+
+ memcpy(dp->dtdo_strtab, odp->dtdo_strtab, dp->dtdo_strlen);
+ }
+
+ /* Copy the variable table (if any). */
+ if (odp->dtdo_varlen > 0) {
+ dp->dtdo_varlen = odp->dtdo_varlen;
+ dp->dtdo_vartab = dt_calloc(dtp, dp->dtdo_varlen,
+ sizeof(dtrace_difv_t));
+ if (dp->dtdo_vartab == NULL)
+ goto no_mem;
+
+ memcpy(dp->dtdo_vartab, odp->dtdo_vartab,
+ dp->dtdo_varlen * sizeof(dtrace_difv_t));
+ }
+
+ /* Copy the relocation tables (if any). */
+ if (odp->dtdo_brelen > 0) {
+ dp->dtdo_brelen = odp->dtdo_brelen;
+ dp->dtdo_breltab = dt_calloc(dtp, dp->dtdo_brelen,
+ sizeof(dof_relodesc_t));
+ if (dp->dtdo_breltab == NULL)
+ goto no_mem;
+
+ memcpy(dp->dtdo_breltab, odp->dtdo_breltab,
+ dp->dtdo_brelen * sizeof(dof_relodesc_t));
+ }
+
+ if (odp->dtdo_krelen > 0) {
+ dp->dtdo_krelen = odp->dtdo_krelen;
+ dp->dtdo_kreltab = dt_calloc(dtp, dp->dtdo_krelen,
+ sizeof(dof_relodesc_t));
+ if (dp->dtdo_kreltab == NULL)
+ goto no_mem;
+
+ memcpy(dp->dtdo_kreltab, odp->dtdo_kreltab,
+ dp->dtdo_krelen * sizeof(dof_relodesc_t));
+ }
+
+ if (odp->dtdo_urelen > 0) {
+ dp->dtdo_urelen = odp->dtdo_urelen;
+ dp->dtdo_ureltab = dt_calloc(dtp, dp->dtdo_urelen,
+ sizeof(dof_relodesc_t));
+ if (dp->dtdo_ureltab == NULL)
+ goto no_mem;
+
+ memcpy(dp->dtdo_ureltab, odp->dtdo_ureltab,
+ dp->dtdo_urelen * sizeof(dof_relodesc_t));
+ }
+
+ dp->dtdo_ddesc = dt_datadesc_hold(odp->dtdo_ddesc);
+ dp->dtdo_flags = odp->dtdo_flags;
+
+ return dp;
+
+no_mem:
+ dt_set_errno(dtp, EDT_NOMEM);
+ return NULL;
+}
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index 1705c990..06148789 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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.
*/
@@ -386,6 +386,9 @@ dt_bpf_load_progs(dtrace_hdl_t *dtp, uint_t cflags)
dtrace_difo_t *dp;
int fd, rc;
+ if (prp == dtp->dt_error)
+ continue;
+
dp = dt_program_construct(dtp, prp, cflags);
if (dp == NULL)
return -1;
diff --git a/libdtrace/dt_cc.c b/libdtrace/dt_cc.c
index 691c31f2..12f60601 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.
*/
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index f55fd6b6..70ba69f8 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -164,38 +164,75 @@ typedef struct {
dt_irlist_t *dlp;
dt_activity_t act;
uint_t lbl_exit;
+ int is_reg;
} dt_clause_arg_t;
static int
dt_cg_call_clause(dtrace_hdl_t *dtp, dt_ident_t *idp, dt_clause_arg_t *arg)
{
dt_irlist_t *dlp = arg->dlp;
+ dtrace_difo_t *dp = dt_dlib_get_func_difo(dtp, idp);
/*
- * 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
+ * Regular clause (not ERROR probe clause), so activity state checking
+ * must be done in case we should skip any furthe processing.
+ */
+ if (arg->is_reg) {
+ /*
+ * if (*dctx.act != act) // ldw %r0, [%fp +
+ * // DCTX_FP(DCTX_ACT)]
+ * goto exit; // ldw %r0, [%r0 + 0]
+ * // jne %r0, act, lbl_exit
+ */
+ 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));
+
+ /*
+ * If this identifier is the first one to possibly trigger the
+ * ERROR probe, we need to allocate a fault label.
+ */
+ if ((dp->dtdo_flags & DIFOFLG_ERROR) &&
+ yypcb->pcb_faultlbl == 0)
+ yypcb->pcb_faultlbl = dt_irlist_label(dlp);
+ }
+
+ /*
+ * rc = 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));
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);
+ /*
+ * Regular clause (not ERROR probe clause) so if the clause may trigger
+ * the ERROR probe, we must jump to the ERROR probe handling if a
+ * non-zero value is returned.
+ */
+ if (arg->is_reg && (dp->dtdo_flags & DIFOFLG_ERROR)) {
+ /*
+ * if (rc != 0) // jne %r0, 0, lbl_fault
+ * goto fault;
+ */
+ emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, 0,
+ yypcb->pcb_faultlbl));
+ }
+
return 0;
}
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,
+ int is_reg)
{
dt_irlist_t *dlp = &pcb->pcb_ir;
- dt_clause_arg_t arg = { dlp, act, pcb->pcb_exitlbl };
+ dt_clause_arg_t arg = { dlp, act, pcb->pcb_exitlbl, is_reg };
- 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 +255,20 @@ 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_irlist_t *dlp = &pcb->pcb_ir;
+
+ dt_cg_tramp_call_clauses(pcb, pcb->pcb_probe, DT_ACTIVITY_ACTIVE, 1);
+
+ /*
+ * If there are clauses that may trigger the ERROR probe, set the
+ * fault label and add calls to the ERROR probe clauses.
+ */
+ if (pcb->pcb_faultlbl > 0) {
+ emitl(dlp, pcb->pcb_faultlbl,
+ BPF_NOP());
+ dt_cg_tramp_call_clauses(pcb, pcb->pcb_hdl->dt_error, 0, 0);
+ }
+
dt_cg_tramp_return(pcb);
}
@@ -227,7 +277,17 @@ 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, 1);
+
+ /*
+ * If there are clauses that may trigger the ERROR probe, set the
+ * fault label and add calls to the ERROR probe clauses.
+ */
+ if (pcb->pcb_faultlbl > 0) {
+ emitl(dlp, pcb->pcb_faultlbl,
+ BPF_NOP());
+ dt_cg_tramp_call_clauses(pcb, pcb->pcb_hdl->dt_error, 0, 0);
+ }
/*
* (*dctx.act)++; // lddw %r0, [%fp + DCTX_FP(DCTX_ACT)]
@@ -368,13 +428,16 @@ dt_cg_epilogue(dt_pcb_t *pcb)
* if (rc != 0)
* goto exit; // jne %r0, 0, pcb->pcb_exitlbl
*/
+#if 0
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));
+#endif
/*
- * 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]
@@ -383,7 +446,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
*
*/
@@ -410,6 +473,47 @@ 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);
+
+ if (pcb->pcb_faultlbl == 0)
+ pcb->pcb_faultlbl = dt_irlist_label(dlp);
+
+ /*
+ * return dt_probe_error( // lddw %r1, %fp, DT_STK_DCTX
+ * dctx->mst, // lddw %r1, %r1, DCTX_MST
+ * 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_LOAD(BPF_DW, BPF_REG_1, BPF_REG_1, DCTX_MST));
+ 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());
+}
+
/*
* 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
@@ -2810,14 +2914,27 @@ 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));
+ lbl_valid = dt_irlist_label(dlp);
+ 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,
@@ -2899,6 +3016,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)
@@ -4252,6 +4370,7 @@ dt_cg(dt_pcb_t *pcb, dt_node_t *dnp)
dt_irlist_destroy(&pcb->pcb_ir);
dt_irlist_create(&pcb->pcb_ir);
pcb->pcb_exitlbl = dt_irlist_label(&pcb->pcb_ir);
+ pcb->pcb_faultlbl = 0;
pcb->pcb_bufoff = 0;
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_handle.c b/libdtrace/dt_handle.c
index 52ff27a8..758b6e10 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,11 +49,13 @@ 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));
+#if 0
stp = dt_list_next(&pgp->dp_stmts);
assert(stp != NULL);
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index bc996929..9e69f448 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 */
@@ -728,6 +730,7 @@ extern dtrace_difo_t *dt_program_construct(dtrace_hdl_t *dtp,
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);
@@ -735,6 +738,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..0813c089 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.
*/
@@ -44,6 +44,7 @@ typedef struct dt_pcb {
uint32_t pcb_bufoff; /* output buffer offset (for DFUNCs) */
dt_irlist_t pcb_ir; /* list of unrelocated IR instructions */
uint_t pcb_exitlbl; /* label for exit of program */
+ uint_t pcb_faultlbl; /* label for fault handling */
uint_t pcb_asvidx; /* assembler vartab index (see dt_as.c) */
ulong_t **pcb_asxrefs; /* assembler imported xlators (see dt_as.c) */
uint_t pcb_asxreflen; /* assembler xlator map length (see dt_as.c) */
diff --git a/libdtrace/dt_probe.c b/libdtrace/dt_probe.c
index 23ee49ad..ad3ffb4e 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,130 @@ 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)
+ * - all references to the EPID identifier in the DIFO are resolved
+ * in-place by assigning a new EPID for the clause in the ERROR probe
+ * - all references to the PRID identifier in the DIFO are resolved
+ * in-place with the probe id of the ERROR probe
+ * - EPID and PRID records are stripped from the relocation records
+ */
+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;
+ dof_relodesc_t *rp, *nrp;
+ dtrace_epid_t epid = dt_epid_add(dtp, dp->dtdo_ddesc,
+ dtp->dt_error->desc->id);;
+
+ /*
+ * Copy the DIFO.
+ */
+ ndp = dt_difo_copy(dtp, dp);
+ if (ndp == NULL)
+ goto no_mem;
+
+ /*
+ * Resolve EPID and PRID references.
+ */
+ len = ndp->dtdo_brelen;
+ rp = nrp = ndp->dtdo_breltab;
+ for (; len != 0; len--, rp++) {
+ const char *name = &ndp->dtdo_strtab[rp->dofr_name];
+ dt_ident_t *ridp = dt_dlib_get_sym(dtp, name);
+ uint32_t val = 0;
+
+ if (ridp != NULL && ridp->di_kind == DT_IDENT_SCALAR) {
+ if (ridp->di_id == DT_CONST_EPID)
+ val = epid;
+ else if (ridp->di_id == DT_CONST_PRID)
+ val = dtp->dt_error->desc->id;
+
+ if (val > 0) {
+ uint_t ioff = rp->dofr_offset /
+ sizeof(struct bpf_insn);
+
+ if (rp->dofr_type == R_BPF_64_64) {
+ ndp->dtdo_buf[ioff].imm = val;
+ ndp->dtdo_buf[ioff + 1].imm = 0;
+ } else if (rp->dofr_type == R_BPF_64_32)
+ ndp->dtdo_buf[ioff].imm = val;
+
+ continue;
+ }
+ }
+
+ if (nrp != rp)
+ *nrp = *rp;
+
+ nrp++;
+ }
+
+ ndp->dtdo_brelen -= rp - nrp;
+
+ /*
+ * 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 + 10);
+
+ /*
+ * 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..b9929c35 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;
}
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);
--
2.28.0
More information about the DTrace-devel
mailing list