[DTrace-devel] [PATCH REVIEW 4/4] consume, cg: implement speculations
Kris Van Hees
kris.van.hees at oracle.com
Wed Oct 13 07:49:45 PDT 2021
Some comments...
First off, in naming I think things are getting a little confusing... What
you seem to describe in naming is that we have a collection of speculations,
where each speculation (identified by id) is represented in a htab with a
dt_spec_bufs_head and it contains a list of dt_spec_bufs elements. That
seems to implement a structure where one or more speculation buffers are
linked together under a speculation buffers head, and that forms the data
to (hopefully) be committed at a later time for a specific speculation.
In DTrace documentation and the legacy code a speculation and a speculation
buffer are essentially the same thing. The speculation buffer holds all the
data for a specific speculation.
Translating that into the new structure, I'd say that we have a speculation
buffer (represented by your dt_spec_bufs_head) that is identified by an id
(speculation id). And it contains probe data chunks.
So maybe rename:
dt_spec_bufs_head -> dt_spec_buf
dt_spec_bufs -> dt_spec_buf_data
and functions should probabyl be renamed to reflect this a bit better also.
Further comments are assuming this kind of renaming...
On Thu, Sep 09, 2021 at 12:13:23PM +0100, Nick Alcock wrote:
> This works by tracking live speculations in a specs map containing
> dt_bpf_specs_t, each of which tracks the number of buffers written and
> whether a commit/discard has been called on it. (bpf/speculation.c,
> which finds free speculations, is limited to 16 speculations and needs
> adjusting when BPF loops work). Successful speculate()s record the ID
> of the active speculation in the clause header. When a commit happens,
> a COMMIT/DISCARD record is written.
>
> At consume time, buffers with active speculate()s are hived off into a
> list of dt_spec_bufs_t under a dt_spec_bufs_head_t (when peeking, this
> is only done on the first peek, identified via the CONSUME_PEEK_START
> peekflag); when a commit/discard hits for a given speculation, the efunc
> invocation etc is suppressed if need be (discards can be followed by
> data-recording actions, in which case the efunc is not suppressed), and
> the specs map entry for this speculation is sucked into the
> dt_spec_bufs_head and the head chained into the dt_spec_bufs_draining
> list. This list is traversed on every consume, and any new spec bufs
> committed (as if just received from the ring buffer) or discarded
> appropriately.
>
> Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
> ---
> bpf/Build | 3 +
> bpf/speculation.c | 63 +++
> bpf/speculation_set_drainable.c | 43 ++
> bpf/speculation_speculate.c | 47 ++
> libdtrace/dt_bpf.c | 22 +
> libdtrace/dt_bpf.h | 2 +
> libdtrace/dt_bpf_maps.h | 9 +
> libdtrace/dt_cc.c | 3 +
> libdtrace/dt_cg.c | 182 +++++--
> libdtrace/dt_consume.c | 513 ++++++++++++++++--
> libdtrace/dt_dlibs.c | 5 +
> libdtrace/dt_errtags.h | 2 +-
> libdtrace/dt_impl.h | 32 ++
> libdtrace/dt_open.c | 19 +-
> libdtrace/dt_peb.h | 1 +
> libdtrace/dtrace.h | 13 +-
> .../speculation/err.CommitWithInvalid.d | 40 ++
> .../speculation/err.CommitWithInvalid.r | 3 +
> .../err.D_ACT_SPEC.SpeculateWithCopyOut.d | 2 +-
> .../err.D_ACT_SPEC.SpeculateWithCopyOutStr.d | 2 +-
> .../err.D_COMM_COMM.CommitAftCommit.d | 61 ---
> .../err.D_COMM_COMM.CommitAftCommit.r | 2 -
> .../err.D_COMM_COMM.DisjointCommit.d | 81 ---
> .../err.D_COMM_COMM.DisjointCommit.r | 2 -
> ...tAftDiscard.d => err.DiscardWithInvalid.d} | 26 +-
> .../speculation/err.DiscardWithInvalid.r | 3 +
> .../speculation/err.SpecSizeVariations1.d | 59 --
> .../speculation/err.SpecSizeVariations1.r | 2 -
> .../speculation/err.SpecSizeVariations2.d | 59 --
> .../speculation/err.SpecSizeVariations2.r | 2 -
> .../speculation/tst.CommitAfterDiscard.d | 9 +-
> .../speculation/tst.CommitAfterDiscard.r | 2 +-
> .../speculation/tst.CommitCommitCommit.d | 58 ++
> .../speculation/tst.CommitCommitCommit.r | 5 +
> .../speculation/tst.CommitDiscard4x.d | 99 ++++
> .../speculation/tst.CommitDiscard4x.r | 6 +
> .../speculation/tst.CommitWithInactive.d | 38 ++
> .../speculation/tst.CommitWithInactive.r | 1 +
> .../unittest/speculation/tst.CommitWithZero.d | 11 +-
> .../unittest/speculation/tst.CommitWithZero.r | 4 -
> .../speculation/tst.DataRecAftDiscard.d | 5 +-
> .../speculation/tst.DiscardAftCommit.d | 12 +-
> .../speculation/tst.DiscardAftCommit.r | 2 +-
> .../speculation/tst.DiscardAftDataRec.d | 3 +-
> .../speculation/tst.DiscardAftDiscard.d | 7 +-
> ...AftDiscard.d => tst.DiscardWithInactive.d} | 26 +-
> .../speculation/tst.DiscardWithInactive.r | 1 +
> .../speculation/tst.DiscardWithZero.d | 12 +-
> .../speculation/tst.DiscardWithZero.r | 4 -
> .../unittest/speculation/tst.ExitAftDiscard.d | 1 -
> test/unittest/speculation/tst.NoSpecBuffer.d | 6 +-
> test/unittest/speculation/tst.NoSpecBuffer.r | 2 -
> test/unittest/speculation/tst.SingleCPU.d | 69 +++
> test/unittest/speculation/tst.SingleCPU.r | 17 +
> .../speculation/tst.SpecSizeVariations3.d | 59 --
> .../speculation/tst.SpecSizeVariations3.r | 3 -
> .../speculation/tst.SpecSizeVariations4.d | 16 +-
> .../speculation/tst.SpecSizeVariations4.r | 2 +-
> .../speculation/tst.SpecSizeVariations5.d | 12 +-
> .../speculation/tst.SpecSizeVariations5.r | 8 +-
> .../speculation/tst.SpeculationCommit.d | 5 +-
> ...mmit.d => tst.SpeculationCommitNotQuiet.d} | 11 +-
> .../tst.SpeculationCommitNotQuiet.r | 10 +
> .../speculation/tst.SpeculationDefault.d | 31 ++
> .../speculation/tst.SpeculationDefault.r | 5 +
> .../tst.SpeculationDefaultCommit.d | 38 ++
> .../tst.SpeculationDefaultCommit.r | 6 +
> .../tst.SpeculationDefaultDiscard.d | 37 ++
> .../tst.SpeculationDefaultDiscard.r | 5 +
> .../speculation/tst.SpeculationDiscard.d | 7 +-
> .../speculation/tst.SpeculationDiscard.r | 2 +-
> ...ard.d => tst.SpeculationDiscardNotQuiet.d} | 10 +-
> .../tst.SpeculationDiscardNotQuiet.r | 8 +
> test/unittest/speculation/tst.SpeculationID.d | 1 -
> .../speculation/tst.SpeculationWithZero.d | 6 +-
> .../speculation/tst.SpeculationWithZero.r | 3 -
> .../unittest/speculation/tst.TwoSpecBuffers.d | 8 +-
> .../unittest/speculation/tst.TwoSpecBuffers.r | 4 +-
> test/unittest/speculation/tst.negcommit.d | 1 -
> test/unittest/speculation/tst.negcommit.r | 2 +-
> test/unittest/speculation/tst.zerosize.d | 1 +
> 81 files changed, 1447 insertions(+), 557 deletions(-)
> create mode 100644 bpf/speculation.c
> create mode 100644 bpf/speculation_set_drainable.c
> create mode 100644 bpf/speculation_speculate.c
> create mode 100644 test/unittest/speculation/err.CommitWithInvalid.d
> create mode 100644 test/unittest/speculation/err.CommitWithInvalid.r
> delete mode 100644 test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.d
> delete mode 100644 test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.r
> delete mode 100644 test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.d
> delete mode 100644 test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.r
> copy test/unittest/speculation/{tst.ExitAftDiscard.d => err.DiscardWithInvalid.d} (51%)
> create mode 100644 test/unittest/speculation/err.DiscardWithInvalid.r
> delete mode 100644 test/unittest/speculation/err.SpecSizeVariations1.d
> delete mode 100644 test/unittest/speculation/err.SpecSizeVariations1.r
> delete mode 100644 test/unittest/speculation/err.SpecSizeVariations2.d
> delete mode 100644 test/unittest/speculation/err.SpecSizeVariations2.r
> create mode 100644 test/unittest/speculation/tst.CommitCommitCommit.d
> create mode 100644 test/unittest/speculation/tst.CommitCommitCommit.r
> create mode 100644 test/unittest/speculation/tst.CommitDiscard4x.d
> create mode 100644 test/unittest/speculation/tst.CommitDiscard4x.r
> create mode 100644 test/unittest/speculation/tst.CommitWithInactive.d
> create mode 100644 test/unittest/speculation/tst.CommitWithInactive.r
> copy test/unittest/speculation/{tst.ExitAftDiscard.d => tst.DiscardWithInactive.d} (51%)
> create mode 100644 test/unittest/speculation/tst.DiscardWithInactive.r
> create mode 100644 test/unittest/speculation/tst.SingleCPU.d
> create mode 100644 test/unittest/speculation/tst.SingleCPU.r
> delete mode 100644 test/unittest/speculation/tst.SpecSizeVariations3.d
> delete mode 100644 test/unittest/speculation/tst.SpecSizeVariations3.r
> copy test/unittest/speculation/{tst.SpeculationCommit.d => tst.SpeculationCommitNotQuiet.d} (78%)
> create mode 100644 test/unittest/speculation/tst.SpeculationCommitNotQuiet.r
> create mode 100644 test/unittest/speculation/tst.SpeculationDefault.d
> create mode 100644 test/unittest/speculation/tst.SpeculationDefault.r
> create mode 100644 test/unittest/speculation/tst.SpeculationDefaultCommit.d
> create mode 100644 test/unittest/speculation/tst.SpeculationDefaultCommit.r
> create mode 100644 test/unittest/speculation/tst.SpeculationDefaultDiscard.d
> create mode 100644 test/unittest/speculation/tst.SpeculationDefaultDiscard.r
> copy test/unittest/speculation/{tst.SpeculationDiscard.d => tst.SpeculationDiscardNotQuiet.d} (71%)
> create mode 100644 test/unittest/speculation/tst.SpeculationDiscardNotQuiet.r
>
> This is the majority of the work: it could possibly be split into two
> pieces (speculations cg+consume, peek_only revamp) but they are
> entwined enough that it felt too annoying to do that.
>
> diff --git a/bpf/Build b/bpf/Build
> index e08a28b67771..46d90733bbda 100644
> --- a/bpf/Build
> +++ b/bpf/Build
> @@ -26,6 +26,9 @@ bpf_dlib_SOURCES = \
> get_bvar.c \
> get_tvar.c set_tvar.c \
> probe_error.c \
> + speculation.c \
> + speculation_speculate.c \
> + speculation_set_drainable.c \
Just throw all of these in the same speculation.c source file.
> strnlen.c \
> varint.c
>
> diff --git a/bpf/speculation.c b/bpf/speculation.c
> new file mode 100644
> index 000000000000..d42152d8075d
> --- /dev/null
> +++ b/bpf/speculation.c
> @@ -0,0 +1,63 @@
> +// 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_bpf_maps.h>
> +
> +#ifndef noinline
> +# define noinline __attribute__((noinline))
> +#endif
> +
> +extern struct bpf_map_def specs;
> +extern uint64_t NSPEC;
> +
> +noinline uint32_t dt_speculation(void)
> +{
> + uint32_t id;
> + dt_bpf_specs_t zero;
> +
> + __builtin_memset(&zero, 0, sizeof (dt_bpf_specs_t));
> +
> +#if 1 /* Loops are broken in BPF right now */
> +#define SEARCH(n) \
> + do { \
> + if (n > (uint64_t) &NSPEC) \
> + return 0; \
> + id = (n); \
> + if (bpf_map_update_elem(&specs, &id, &zero, \
> + BPF_NOEXIST) == 0) \
> + return id; \
> + } while (0);
> +
> + SEARCH(1);
> + SEARCH(2);
> + SEARCH(3);
> + SEARCH(4);
> + SEARCH(5);
> + SEARCH(6);
> + SEARCH(7);
> + SEARCH(8);
> + SEARCH(9);
> + SEARCH(10);
> + SEARCH(11);
> + SEARCH(12);
> + SEARCH(13);
> + SEARCH(14);
> + SEARCH(15);
> + SEARCH(16);
> +#else
> + /*
> + * Waiting on a verifier that can handle loops
> + */
> + for (id = 1; id <= (uint64_t) &NSPEC; id++) {
> + if (bpf_map_update_elem(&specs, &id, &zero,
> + BPF_NOEXIST) == 0)
> + return id;
> + }
> +#endif
> +
> + return 0;
> +}
> diff --git a/bpf/speculation_set_drainable.c b/bpf/speculation_set_drainable.c
> new file mode 100644
> index 000000000000..55bb5ce45d14
> --- /dev/null
> +++ b/bpf/speculation_set_drainable.c
> @@ -0,0 +1,43 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
> + */
> +#include <linux/bpf.h>
> +#include <stddef.h>
> +#include <stdint.h>
> +#include <bpf-helpers.h>
> +#include <dt_bpf_maps.h>
> +#include <dtrace/faults_defines.h>
> +#include <asm/errno.h>
> +#include <stdatomic.h>
> +
> +#include "probe_error.h"
> +
> +#ifndef noinline
> +# define noinline __attribute__((noinline))
> +#endif
> +
> +extern struct bpf_map_def specs;
> +extern uint64_t NSPEC;
> +
> +/* Returns -1 on error. */
> +
> +noinline int32_t
> +dt_speculation_set_drainable(dt_dctx_t *dctx, uint32_t id)
> +{
> + dt_bpf_specs_t *spec;
> +
> + /*
> + * Arguably redundant to BPF_EXIST, but that doesn't seem to return
> + * -EEXIST as one might hope.
> + */
> + if ((spec = bpf_map_lookup_elem(&specs, &id)) == NULL) {
> + if (id <= (uint64_t) &NSPEC)
> + return 0;
> + dt_probe_error(dctx, -1, DTRACEFLT_ILLOP, 0);
> + return -1;
> + }
> + spec->draining = 1;
> +
> + return 0;
> +}
> diff --git a/bpf/speculation_speculate.c b/bpf/speculation_speculate.c
> new file mode 100644
> index 000000000000..2cb8feefef79
> --- /dev/null
> +++ b/bpf/speculation_speculate.c
> @@ -0,0 +1,47 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
> + */
> +#include <linux/bpf.h>
> +#include <stddef.h>
> +#include <stdint.h>
> +#include <bpf-helpers.h>
> +#include <dt_bpf_maps.h>
> +#include <dtrace/faults_defines.h>
> +#include <asm/errno.h>
> +#include <stdatomic.h>
> +
> +#include "probe_error.h"
> +
> +#ifndef noinline
> +# define noinline __attribute__((noinline))
> +#endif
> +
> +extern struct bpf_map_def specs;
> +
> +/*
> + * We consider a speculation ID usable only if it exists in the speculation map
> + * (indicating that speculation() has returned it) with a a zero read value
> + * (indicating that neither commit() nor discard() have been called for it).
> + * We bump the written value by one to indicate that another speculative buffer
> + * is (or will soon be, once this clause terminates and its epilogue runs)
> + * available for draining.
> + */
> +noinline int32_t
> +dt_speculation_speculate(uint32_t id)
> +{
> + dt_bpf_specs_t *spec;
> +
> + if ((spec = bpf_map_lookup_elem(&specs, &id)) == NULL)
> + return -1;
> +
> + /*
> + * Spec already being drained: do not continue to emit new
> + * data into it.
> + */
> + if (spec->draining)
> + return -1;
> +
> + __atomic_add_fetch(&spec->written, 1, __ATOMIC_RELAXED);
> + return 0;
> +}
As commented above, the above three files can be combined.
> diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
> index 8195cd07aac3..bd5e55acd9e2 100644
> --- a/libdtrace/dt_bpf.c
> +++ b/libdtrace/dt_bpf.c
> @@ -71,6 +71,20 @@ int dt_bpf_map_lookup(int fd, const void *key, void *val)
> return bpf(BPF_MAP_LOOKUP_ELEM, &attr);
> }
>
> +/*
> + * Delete the given key from the map referenced by the given fd.
> + */
> +int dt_bpf_map_delete(int fd, const void *key)
> +{
> + union bpf_attr attr;
> +
> + memset(&attr, 0, sizeof(attr));
> + attr.map_fd = fd;
> + attr.key = (uint64_t)(unsigned long)key;
> +
> + return bpf(BPF_MAP_DELETE_ELEM, &attr);
> +}
> +
> /*
> * Store the (key, value) pair in the map referenced by the given fd.
> */
> @@ -170,6 +184,9 @@ populate_probes_map(dtrace_hdl_t *dtp, int fd)
> * element (key 0). Every aggregation is stored with two copies
> * of its data to provide a lockless latch-based mechanism for
> * atomic reading and writing.
> + * - specs: Map associating speculation IDs with a dt_bpf_specs_t struct
> + * giving the number of buffers speculated into for this
> + * speculation, and the number drained by userspace.
> * - buffers: Perf event output buffer map, associating a perf event output
> * buffer with each CPU. The map is indexed by CPU id.
> * - cpuinfo: CPU information map, associating a cpuinfo_t structure with
> @@ -264,6 +281,11 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
> return -1; /* dt_errno is set for us */
> }
>
> + if (create_gmap(dtp, "specs", BPF_MAP_TYPE_HASH,
> + sizeof(uint32_t), sizeof(dt_bpf_specs_t),
> + dtp->dt_options[DTRACEOPT_NSPEC]) == -1)
> + return -1; /* dt_errno is set for us */
> +
> if (create_gmap(dtp, "buffers", BPF_MAP_TYPE_PERF_EVENT_ARRAY,
> sizeof(uint32_t), sizeof(uint32_t),
> dtp->dt_conf.num_online_cpus) == -1)
> diff --git a/libdtrace/dt_bpf.h b/libdtrace/dt_bpf.h
> index 2882520fd701..287178c11d9b 100644
> --- a/libdtrace/dt_bpf.h
> +++ b/libdtrace/dt_bpf.h
> @@ -26,6 +26,7 @@ extern "C" {
> #define DT_CONST_STRSZ 6
> #define DT_CONST_STKSIZ 7
> #define DT_CONST_BOOTTM 8
> +#define DT_CONST_NSPEC 9
>
> extern int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
> int group_fd, unsigned long flags);
> @@ -34,6 +35,7 @@ extern int bpf(enum bpf_cmd cmd, union bpf_attr *attr);
> extern int dt_bpf_gmap_create(struct dtrace_hdl *);
> extern int dt_bpf_map_lookup(int fd, const void *key, void *val);
> extern int dt_bpf_map_update(int fd, const void *key, const void *val);
> +extern int dt_bpf_map_delete(int fd, const void *key);
> extern int dt_bpf_load_progs(struct dtrace_hdl *, uint_t);
>
> #ifdef __cplusplus
> diff --git a/libdtrace/dt_bpf_maps.h b/libdtrace/dt_bpf_maps.h
> index aa088ce8ee80..e33748b7aa50 100644
> --- a/libdtrace/dt_bpf_maps.h
> +++ b/libdtrace/dt_bpf_maps.h
> @@ -12,6 +12,8 @@
> extern "C" {
> #endif
>
> +#include <stddef.h>
> +
> typedef struct dt_bpf_probe dt_bpf_probe_t;
> struct dt_bpf_probe {
> size_t prv; /* probeprov string offset in strtab */
> @@ -20,6 +22,13 @@ struct dt_bpf_probe {
> size_t prb; /* probename string offset in strtab */
> };
>
> +typedef struct dt_bpf_specs dt_bpf_specs_t;
> +struct dt_bpf_specs {
> + uint64_t written; /* number of spec buffers written */
> + uint32_t draining; /* 1 if userspace has been asked to
> + * drain this buffer */
> +};
> +
> #ifdef __cplusplus
> }
> #endif
> diff --git a/libdtrace/dt_cc.c b/libdtrace/dt_cc.c
> index 97c7d5e2a0a1..a25f354815dc 100644
> --- a/libdtrace/dt_cc.c
> +++ b/libdtrace/dt_cc.c
> @@ -2352,6 +2352,9 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
> nrp->dofr_data =
> dtp->dt_options[DTRACEOPT_STRSIZE];
> continue;
> + case DT_CONST_NSPEC:
> + nrp->dofr_data = dtp->dt_options[DTRACEOPT_NSPEC];
> + continue;
> case DT_CONST_STKSIZ:
> nrp->dofr_data = sizeof(uint64_t)
> * dtp->dt_options[DTRACEOPT_MAXFRAMES];
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index b94f4485f295..43ed2351d51c 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -488,8 +488,9 @@ dt_cg_tramp_error(dt_pcb_t *pcb)
> *
> * 1. Store the base pointer to the output data buffer in %r9.
> * 2. Initialize the machine state (dctx->mst).
> - * 3. Store the epid and tag at [%r9 + 0] and [%r9 + 4] respectively.
> - * 4. Evaluate the predicate expression and return if false.
> + * 3. Store the epid at [%r9 + 0].
> + * 4. Store 0 to indicate no active speculation at [%r9 + 4].
> + * 5. Evaluate the predicate expression and return if false.
> *
> * The dt_program() function will always return 0.
> */
> @@ -539,11 +540,11 @@ dt_cg_prologue(dt_pcb_t *pcb, dt_node_t *pred)
> emite(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, 0, -1), epid);
>
> /*
> - * dctx->mst->tag = 0; // stw [%r0 + DMST_TAG], 0
> + * Set the speculation ID field to zero to indicate no active
> + * speculation.
> * *((uint32_t *)&buf[4]) = 0;
> * // stw [%r9 + 4], 0
> */
> - emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_0, DMST_TAG, 0));
> emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, 4, 0));
>
> /*
> @@ -587,8 +588,10 @@ dt_cg_epilogue(dt_pcb_t *pcb)
> * Output the buffer if:
> * - data-recording action, or
> * - default action (no clause specified)
> + * - committing or discarding a speculation
> */
> - if (pcb->pcb_stmt->dtsd_clauseflags & DT_CLSFLAG_DATAREC) {
> + if (pcb->pcb_stmt->dtsd_clauseflags & DT_CLSFLAG_DATAREC ||
> + pcb->pcb_stmt->dtsd_clauseflags & DT_CLSFLAG_COMMIT_DISCARD) {
> dt_ident_t *buffers = dt_dlib_get_map(pcb->pcb_hdl, "buffers");
>
> assert(buffers != NULL);
> @@ -923,14 +926,16 @@ dt_cg_clsflags(dt_pcb_t *pcb, dtrace_actkind_t kind, const dt_node_t *dnp)
> *cfp |= DT_CLSFLAG_DESTRUCT;
>
> if (kind == DTRACEACT_COMMIT) {
> - if (*cfp & DT_CLSFLAG_COMMIT)
> - dnerror(dnp, D_COMM_COMM,
> - "commit( ) may not follow commit( )\n");
> if (*cfp & (DT_CLSFLAG_DATAREC | DT_CLSFLAG_AGGREGATION))
> dnerror(dnp, D_COMM_DREC, "commit( ) may "
> "not follow data-recording action(s)\n");
>
> - *cfp |= DT_CLSFLAG_COMMIT;
> + *cfp |= DT_CLSFLAG_COMMIT | DT_CLSFLAG_COMMIT_DISCARD;
> + return;
> + }
> +
> + if (kind == DTRACEACT_DISCARD) {
> + *cfp |= DT_CLSFLAG_COMMIT_DISCARD;
> return;
> }
>
> @@ -987,8 +992,7 @@ dt_cg_clsflags(dt_pcb_t *pcb, dtrace_actkind_t kind, const dt_node_t *dnp)
> dnerror(dnp, D_DREC_COMM,
> "data-recording actions may not follow commit( )\n");
>
> - if (!(*cfp & DT_CLSFLAG_SPECULATE))
> - *cfp |= DT_CLSFLAG_DATAREC;
> + *cfp |= DT_CLSFLAG_DATAREC;
> }
>
> static void
> @@ -1037,6 +1041,50 @@ dt_cg_act_clear(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> dt_cg_store_val(pcb, anp, DTRACEACT_LIBACT, NULL, DT_ACT_CLEAR);
> }
>
> +/*
> + * Mark a speculation as committed/discarded and ready for draining.
> + *
> + * The speculation ID must be in IDREG (true for both commit and discard).
> + *
> + * If the speculation does not exist, nothing will be done: the consumer has to
> + * detect this itself if the speculation is inactive (out-of-range values
> + * fault and do not write a commit/discard record).
> + */
> +static void
> +dt_cg_spec_set_drainable(dt_pcb_t *pcb, dt_node_t *dnp, int idreg)
> +{
> + dt_regset_t *drp = pcb->pcb_regs;
> + dt_irlist_t *dlp = &pcb->pcb_ir;
> + dt_ident_t *idp;
> + uint_t lbl_ok = dt_irlist_label(dlp);
> +
> + idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_speculation_set_drainable");
> + assert(idp != NULL);
> +
> + /*
> + * spec = dt_speculation_set_drainable(ctx, id);
> + * // call dt_speculation_commit_discard
> + * // (%r1 ... %r5 clobbered)
> + * if (rc == 0) // jeq %r0, 0, lbl_ok
> + * goto ok;
> + * return rc; // mov %dn_reg, %r0
> + * ok:
> + */
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
> + emit(dlp, BPF_MOV_REG(BPF_REG_2, idreg));
> + emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
> + emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_0, 0, lbl_ok));
> + emit(dlp, BPF_RETURN());
> + emitl(dlp, lbl_ok,
> + BPF_NOP());
> + dt_regset_free(drp, BPF_REG_0);
> + dt_regset_free_args(drp);
> +}
> +
> /*
> * Signal that the speculation with the given id should be committed to the
> * tracing output.
> @@ -1047,16 +1095,12 @@ dt_cg_act_clear(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> static void
> dt_cg_act_commit(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> {
> - dt_irlist_t *dlp = &pcb->pcb_ir;
> - uint_t off;
> -
> - dt_cg_node(dnp->dn_args, &pcb->pcb_ir, pcb->pcb_regs);
> -
> - off = dt_rec_add(pcb->pcb_hdl, dt_cg_fill_gap, DTRACEACT_COMMIT,
> - sizeof(uint64_t), sizeof(uint64_t), NULL,
> - DT_ACT_COMMIT);
> + dt_regset_t *drp = pcb->pcb_regs;
>
> - emit(dlp, BPF_STORE(BPF_DW, BPF_REG_9, off, BPF_REG_0)); /* FIXME */
> + dt_cg_node(dnp->dn_args, &pcb->pcb_ir, drp);
> + dt_cg_spec_set_drainable(pcb, dnp, dnp->dn_args->dn_reg);
> + dt_regset_free(drp, dnp->dn_args->dn_reg);
> + dt_cg_store_val(pcb, dnp->dn_args, DTRACEACT_COMMIT, NULL, DT_ACT_COMMIT);
> }
>
> static void
> @@ -1094,16 +1138,12 @@ dt_cg_act_denormalize(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> static void
> dt_cg_act_discard(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> {
> - dt_irlist_t *dlp = &pcb->pcb_ir;
> - uint_t off;
> -
> - dt_cg_node(dnp->dn_args, &pcb->pcb_ir, pcb->pcb_regs);
> -
> - off = dt_rec_add(pcb->pcb_hdl, dt_cg_fill_gap, DTRACEACT_DISCARD,
> - sizeof(uint64_t), sizeof(uint64_t), NULL,
> - DT_ACT_DISCARD);
> + dt_regset_t *drp = pcb->pcb_regs;
>
> - emit(dlp, BPF_STORE(BPF_DW, BPF_REG_9, off, BPF_REG_0)); /* FIXME */
> + dt_cg_node(dnp->dn_args, &pcb->pcb_ir, drp);
> + dt_cg_spec_set_drainable(pcb, dnp, dnp->dn_args->dn_reg);
> + dt_regset_free(drp, dnp->dn_args->dn_reg);
> + dt_cg_store_val(pcb, dnp->dn_args, DTRACEACT_DISCARD, NULL, DT_ACT_DISCARD);
> }
>
> /*
> @@ -1407,22 +1447,48 @@ dt_cg_act_setopt(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> * Signal that subsequent tracing output in the current clause should be kept
> * back pending a commit() or discard() for the speculation with the given id.
> *
> - * Stores:
> - * integer (4 bytes) -- speculation ID
> + * Updates the specid in the output buffer header, rather than emitting a new
> + * record into it.
> */
> static void
> dt_cg_act_speculate(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> {
> dt_irlist_t *dlp = &pcb->pcb_ir;
> - uint_t off;
> + dt_regset_t *drp = pcb->pcb_regs;
> + dt_ident_t *idp;
> + uint_t lbl_exit = pcb->pcb_exitlbl;
>
> - dt_cg_node(dnp->dn_args, &pcb->pcb_ir, pcb->pcb_regs);
> + idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_speculation_speculate");
> + assert(idp != NULL);
> +
> + dt_cg_node(dnp->dn_args, dlp, drp);
> + dnp->dn_reg = dnp->dn_args->dn_reg;
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
>
> - off = dt_rec_add(pcb->pcb_hdl, dt_cg_fill_gap, DTRACEACT_SPECULATE,
> - sizeof(uint64_t), sizeof(uint64_t), NULL,
> - DT_ACT_SPECULATE);
> + /*
> + * rc = dt_speculation_speculate(spec);
> + * // lddw %r1, %dn_reg
> + * // call dt_speculation_speculate
> + * // (%r1 ... %r5 clobbered)
> + * // (%r0 = error return)
> + * if (rc != 0) // jne %r0, 0, lbl_exit
> + * goto exit;
> + * *((uint32_t *)&buf[4]) = spec; // mov [%r9 + 4], %dn_reg
> + * exit: // nop
> + */
>
> - emit(dlp, BPF_STORE(BPF_DW, BPF_REG_9, off, BPF_REG_0)); /* FIXME */
> + emit(dlp, BPF_STORE(BPF_W, BPF_REG_FP, DT_STK_SPILL(0),
> + dnp->dn_reg));
> + emit(dlp, BPF_MOV_REG(BPF_REG_1, dnp->dn_reg));
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
> + dt_regset_free_args(drp);
> + emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, 0, lbl_exit));
> + emit(dlp, BPF_STORE(BPF_W, BPF_REG_9, 4, dnp->dn_reg));
> + dt_regset_free(drp, BPF_REG_0);
> + dt_regset_free(drp, dnp->dn_reg);
> }
>
> static void
> @@ -3011,12 +3077,39 @@ dt_cg_array_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> emit(dlp, BPF_ALU64_REG((dnp->dn_flags & DT_NF_SIGNED) ? BPF_ARSH : BPF_RSH, dnp->dn_reg, n));
> }
>
> +/*
> + * Get and return a new speculation ID. These are unallocated entries in the
> + * specs map, obtained by calling dt_speculation(). Return zero if none is
> + * available. TODO: add a drop in this case?
> + */
> static void
> dt_cg_subr_speculation(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> {
> + dt_ident_t *idp;
> +
> + idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_speculation");
> + assert(idp != NULL);
> +
> TRACE_REGSET(" subr-speculation:Begin");
> - dnp->dn_reg = dt_regset_alloc(drp);
> - emit(dlp, BPF_MOV_IMM(dnp->dn_reg, 0));
> + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> +
> + /*
> + * spec = dt_speculation();
> + * // call dt_speculation
> + * // (%r1 ... %r5 clobbered)
> + * // (%r0 = speculation ID, or 0)
> + * return rc; // mov %dn_reg, %r0
> + */
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
> + emit(dlp, BPF_MOV_REG(dnp->dn_reg, BPF_REG_0));
> + dt_regset_free(drp, BPF_REG_0);
> + dt_regset_free_args(drp);
> TRACE_REGSET(" subr-speculation:End ");
> }
>
> @@ -4887,6 +4980,9 @@ dt_cg(dt_pcb_t *pcb, dt_node_t *dnp)
> dxp->dx_ident->di_id = dt_regset_alloc(pcb->pcb_regs);
> dt_cg_node(dnp, &pcb->pcb_ir, pcb->pcb_regs);
> } else if (dnp->dn_kind == DT_NODE_CLAUSE) {
> + int nonspec_acts = 0;
> + int speculative = 0;
> +
> dt_cg_prologue(pcb, dnp->dn_pred);
>
> for (act = dnp->dn_acts; act != NULL; act = act->dn_list) {
> @@ -4902,6 +4998,11 @@ dt_cg(dt_pcb_t *pcb, dt_node_t *dnp)
> actdp->fun(pcb, act->dn_expr,
> actdp->kind);
> }
> +
> + if (actdp->kind == DTRACEACT_SPECULATE)
> + speculative = 1;
> + else
> + nonspec_acts++;
> break;
> }
> case DT_NODE_AGG:
> @@ -4926,7 +5027,8 @@ dt_cg(dt_pcb_t *pcb, dt_node_t *dnp)
> "statement\n", dnp->dn_kind);
> }
> }
> - if (dnp->dn_acts == NULL)
> + if (dnp->dn_acts == NULL ||
> + (speculative && nonspec_acts == 0))
> pcb->pcb_stmt->dtsd_clauseflags |= DT_CLSFLAG_DATAREC;
>
> dt_cg_epilogue(pcb);
> diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c
> index 5075254cb399..8296a0051065 100644
> --- a/libdtrace/dt_consume.c
> +++ b/libdtrace/dt_consume.c
> @@ -364,6 +364,37 @@ dt_stddev(uint64_t *data, uint64_t normal)
> return dt_sqrt_128(diff);
> }
>
> +static uint32_t
> +dt_spec_bufs_hval(const dt_spec_bufs_head_t *head)
> +{
> + uint32_t g = 0, hval = 0;
> + uint32_t *p = (uint32_t *) &head->dtsh_id;
> +
> + while ((uintptr_t) p < (((uintptr_t) &head->dtsh_id) +
> + sizeof(head->dtsh_id))) {
> + hval = (hval << 4) + *p++;
> + g = hval & 0xf0000000;
> + if (g != 0) {
> + hval ^= (g >> 24);
> + hval ^= g;
> + }
> + }
> +
> + return hval;
> +}
> +
> +static int
> +dt_spec_bufs_cmp(const dt_spec_bufs_head_t *p,
> + const dt_spec_bufs_head_t *q)
> +{
> + if (p->dtsh_id == q->dtsh_id)
> + return 0;
> + return 1;
> +}
> +
> +DEFINE_HE_STD_LINK_FUNCS(dt_spec_bufs, dt_spec_bufs_head_t, dtsh_he);
> +DEFINE_HTAB_STD_OPS(dt_spec_bufs);
> +
> static int
> dt_flowindent(dtrace_hdl_t *dtp, dtrace_probedata_t *data, dtrace_epid_t last,
> dtrace_epid_t next)
> @@ -1935,31 +1966,217 @@ dt_print_trace(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
> return dt_print_rawbytes(dtp, fp, data, rec->dtrd_size);
> }
>
> +/*
> + * The lifecycle of speculation buffers is as follows:
> + *
> + * - They are created upon speculation() as entries in the specs map mapping
> + * from the speculation ID to a dt_bpf_specs_t entry with read, written,
> + * and draining values all zero.
> + *
> + * - speculate() verifies the existence of the requested speculation entry in
> + * the specs map, and that draining has not been set in it, and atomically
> + * bumps the written value.
> + *
> + * - Speculations drain from the perf ring buffer into dt_spec_bufs, one
> + * dt_spec_buf per ring-buffer of data fetched from the kernel with a nonzero
> + * specid (one speculated clause); these are chained into dt_spec_buf_heads,
> + * one per speculation ID, and these are chained into dtp->dt_spec_bufs
> + * instead of being consumed.
> + *
> + * - commit / discard set the specs map entry's drained value to 1, which
> + * indicates that it is drainable by userspace and prevents further
> + * speculate()s, and record a single entry in the output buffer with the
> + * committed/discarded ID attached
> + *
> + * - Non-speculated buffers (with a zero specid) are scanned by
> + * dt_consume_one_buffer for commit / discard; the first found for a given
> + * live speculation triggers draining, chaining the spec buf head into
> + * dtp->dt_spec_bufs_draining. dt_spec_bufs_draining contains a counter of
> + * the number of buffers drained ("read", mirroring "written" in the specs
> + * map).
> + *
> + * - dt_spec_bufs_draining is traversed on every buffer consume and any
> + * speculations that are committing have their speculation buffers processed
> + * as if they had just been received (but keeping their original CPU number);
> + * both entries that are committing and those that are discarding are then
> + * removed, leaving the buffer head behind to be filled out by future
> + * ring-buffer fetches, and the number removed recorded by incrementing
> + * dtsh_draining_t.read. Buffers for which read >= written are removed from
> + * dt_spec_bufs_draining and have their buffer heads removed from
> + * dt_spec_bufs and their ID removed from the specs map, freeing them for
> + * recycling by future calls to speculation().
> + */
> +
> +/*
> + * Member of the dtsh_draining_list.
> + */
> +typedef struct dtsh_draining {
> + dt_list_t dtsd_list;
> + dt_spec_bufs_head_t *dtsd_dtsh;
> + uint64_t dtsd_read;
> +} dtsh_draining_t;
> +
> +static dt_spec_bufs_head_t *
> +dt_create_spec_buf_head(dtrace_hdl_t *dtp, int32_t spec)
This name is odd in comparison with the earlier dt_spec_bufs_* stuff. Per my
comments before, maybe name this dt_spec_buf_create().
> +{
> + dt_spec_bufs_head_t *dtsh;
> +
> + dtsh = dt_zalloc(dtp, sizeof(struct dt_spec_bufs_head));
> + if (!dtsh)
> + goto oom;
> + dtsh->dtsh_id = spec;
> +
> + if (dt_htab_insert(dtp->dt_specs_byid, dtsh) < 0)
> + goto oom;
> + dt_list_append(&dtp->dt_spec_bufs, dtsh);
> + return dtsh;
> +oom:
> + dt_free(dtp, dtsh);
> + dt_set_errno(dtp, EDT_NOMEM);
> + return NULL;
> +}
> +
> +static dt_spec_bufs_t *
> +dt_create_spec_buf(dtrace_hdl_t *dtp, dt_spec_bufs_head_t *dtsh,
> + dtrace_epid_t epid, unsigned int cpu,
> + dtrace_datadesc_t *datadesc, char *data,
> + uint32_t size)
Maybe dt_spec_buf_add_data() ? Or just t_spec_buf_add()
> +{
> + dt_spec_bufs_t *dtsb;
> +
> + dtsb = dt_zalloc(dtp, sizeof(struct dt_spec_bufs));
> + if (!dtsb)
> + goto oom;
> +
> + dtsb->dtsb_cpu = cpu;
> + dtsb->dtsb_size = size;
> + dtsb->dtsb_data = dt_alloc(dtp, size);
> + if (!dtsb->dtsb_data)
> + goto oom;
> +
> + dtsh->dtsh_size += size;
> + memcpy(dtsb->dtsb_data, data, size);
> +
> + dt_list_append(&dtsh->dtsh_dtsb_list, dtsb);
> + return dtsb;
> +
> +oom:
> + dt_free(dtp, dtsb);
> + dt_set_errno(dtp, EDT_NOMEM);
> + return NULL;
> +}
> +
> +/*
> + * Remove a speculation's buffers and possibly the speculation head and
> + * the record of it in the BPF specs map (which frees the ID for reuse).
> + *
> + * Note: if the speculation is being committed, it will also be interned on the
> + * dtp->dt_spec_bufs_draining list. Such speculations must be removed via
> + * dt_gc_speculations, which ultimately calls this function.
> + */
> +static void
> +dt_destroy_spec_bufs(dtrace_hdl_t *dtp, dt_spec_bufs_head_t *dtsh,
> + int remove_specid)
dt_spec_buf_destroy()
> +{
> + dt_spec_bufs_t *dtsb;
> +
> + while ((dtsb = dt_list_next(&dtsh->dtsh_dtsb_list)) != NULL) {
> +
> + dt_list_delete(&dtsh->dtsh_dtsb_list, dtsb);
> + dt_free(dtp, dtsb->dtsb_data);
> + dt_free(dtp, dtsb);
> + }
> +
> + dtsh->dtsh_size = 0;
> +
> + if (remove_specid) {
> + dt_ident_t *idp = dt_dlib_get_map(dtp, "specs");
> +
> + if (idp)
> + dt_bpf_map_delete(idp->di_id, &dtsh->dtsh_id);
> + dt_list_delete(&dtp->dt_spec_bufs, dtsh);
> + dt_free(dtp, dtsh);
> + }
> +}
> +
> +/*
> + * Peeking flags (values for the peekflag parameter for functions that have
> + * one).
> + *
> + * These let you process a single buffer more than once. The first call
> + * should pass CONSUME_PEEK_START: this suppresses deletion of consumed records.
> + * Subsequent calls should pass CONSUME_PEEK; this does as CONSUME_PEEK_START
> + * does, but also suppresses hiving off of speculative buffers (because they
> + * have already been hived off under CONSUME_PEEK_START). The final call should
> + * pass CONSUME_PEEK_FINISH; this does a normal buffer consumption (with
> + * deletion) except that speculative hiving-off is again suppressd.
> + *
> + * These are not bit-flags: pass only one.
> + */
> +#define CONSUME_PEEK_START 0x1
> +#define CONSUME_PEEK 0x2
> +#define CONSUME_PEEK_FINISH 0x3
> +
> static dtrace_workstatus_t
> dt_consume_one_buffer(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
> dtrace_probedata_t *pdat, dtrace_consume_probe_f *efunc,
> dtrace_consume_rec_f *rfunc, int flow, int quiet,
> - dtrace_epid_t *last, void *arg);
> + int peekflags, dtrace_epid_t *last, int committing,
> + void *arg);
> +
> +/*
> + * Commit one speculation.
> + */
> +static dtrace_workstatus_t
> +dt_commit_one_spec(dtrace_hdl_t *dtp, FILE *fp, dt_spec_bufs_head_t *dtsh,
> + dtrace_probedata_t *pdat, dtrace_consume_probe_f *efunc,
> + dtrace_consume_rec_f *rfunc, int flow, int quiet,
> + int peekflags, dtrace_epid_t *last, void *arg)
> +{
> + dt_spec_bufs_t *dtsb;
> +
> + for (dtsb = dt_list_next(&dtsh->dtsh_dtsb_list);
> + dtsb != NULL; dtsb = dt_list_next(dtsb)) {
> + dtrace_workstatus_t ret;
> + dtrace_probedata_t specpdat;
> +
> + memcpy(&specpdat, pdat, sizeof(dtrace_probedata_t));
> + specpdat.dtpda_cpu = dtsb->dtsb_cpu;
> + ret = dt_consume_one_buffer(dtp, fp, dtsb->dtsb_data,
> + dtsb->dtsb_size, &specpdat,
> + efunc, rfunc, flow, quiet,
> + peekflags, last, 1, arg);
> + if (ret != DTRACE_WORKSTATUS_OKAY)
> + return ret;
> + }
> +
> + return DTRACE_WORKSTATUS_OKAY;
> +}
>
> static dtrace_workstatus_t
> dt_consume_one_buffer(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
> dtrace_probedata_t *pdat, dtrace_consume_probe_f *efunc,
> dtrace_consume_rec_f *rfunc, int flow, int quiet,
> - dtrace_epid_t *last, void *arg)
> + int peekflags, dtrace_epid_t *last, int committing,
> + void *arg)
> {
> dtrace_epid_t epid;
> + dt_spec_bufs_head_t tmpl;
> + dt_spec_bufs_head_t *dtsh;
> + int specid;
> int i;
> int rval;
> + dtrace_workstatus_t ret;
> + int commit_discard_seen, only_commit_discards;
> + int data_recording = 1;
>
>
> epid = ((uint32_t *)data)[0];
> -#ifdef FIXME
> - tag = ((uint32_t *)data)[1]; /* for future use */
> -#endif
> + specid = ((uint32_t *)data)[1];
>
> /*
> * Fill in the epid and address of the epid in the buffer. We need to
> - * pass this to the efunc.
> + * pass this to the efunc and possibly to create speculations.
> */
> pdat->dtpda_epid = epid;
> pdat->dtpda_data = data;
> @@ -1978,30 +2195,115 @@ dt_consume_one_buffer(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
> return DTRACE_WORKSTATUS_ERROR;
> }
>
> - if (flow)
> - dt_flowindent(dtp, pdat, *last, DTRACE_EPIDNONE);
> + /*
> + * If speculating (and not peeking), hive this buffer off into a
> + * suitable spec buf, creating the spec buf head if need be. No need to
> + * do anything else with this buffer until a commit or discard is seen
> + * for it (in some other, non-speculated buffer).
> + */
>
> - rval = (*efunc)(pdat, arg);
> + if (!committing && specid != 0) {
> + size_t cursz = 0;
>
> - if (flow) {
> - if (pdat->dtpda_flow == DTRACEFLOW_ENTRY)
> - pdat->dtpda_indent += 2;
> - }
> + /*
> + * If peeking, we dealt with this buffer earlier: don't
> + * re-speculate it again.
> + */
> + if (peekflags == CONSUME_PEEK ||
> + peekflags == CONSUME_PEEK_FINISH)
> + return DTRACE_WORKSTATUS_OKAY;
> +
> + tmpl.dtsh_id = specid;
> + dtsh = dt_htab_lookup(dtp->dt_specs_byid, &tmpl);
> +
> + /*
> + * Discard when over the specsize.
> + *
> + * TODO: add a drop when OOM or > specsize -- and also on OOM in
> + * any of the consuming functions.
> + */
> +
> + if (dtsh)
> + cursz = dtsh->dtsh_size;
> +
> + if (cursz + size > dtp->dt_options[DTRACEOPT_SPECSIZE])
> + return DTRACE_WORKSTATUS_OKAY;
> +
> + if (!dtsh) {
> + if ((dtsh = dt_create_spec_buf_head(dtp, specid)) == NULL)
> + return -1;
> + }
> +
> + if (dt_create_spec_buf(dtp, dtsh, epid, pdat->dtpda_cpu,
> + pdat->dtpda_ddesc, data, size) == NULL)
> + return -1;
>
> - switch (rval) {
> - case DTRACE_CONSUME_NEXT:
> return DTRACE_WORKSTATUS_OKAY;
> - case DTRACE_CONSUME_DONE:
> - return DTRACE_WORKSTATUS_DONE;
> - case DTRACE_CONSUME_ABORT:
> - return dt_set_errno(dtp, EDT_DIRABORT);
> - case DTRACE_CONSUME_THIS:
> - break;
> - default:
> - return dt_set_errno(dtp, EDT_BADRVAL);
> }
>
> /*
> + * First, scan for commit/discard. Track whether we have seen discards,
> + * and whether we have seen anything else, to determine whether this
> + * clause should be considered data-recording from the user's
> + * perspective. (A clause with only a discard is not data-recording.
> + * Commits cannot share a clause with data-recording actions at all: see
> + * dt_cg_clsflags.)
> + *
> + * Do nothing of this if committing speculated buffers, since speculated
> + * buffers cannot contain commits or discards.
> + */
> + commit_discard_seen = 0;
> + only_commit_discards = 1;
> + for (i = 0; !committing && i < pdat->dtpda_ddesc->dtdd_nrecs; i++) {
> + dtrace_recdesc_t *rec;
> + dtrace_actkind_t act;
> +
> + rec = &pdat->dtpda_ddesc->dtdd_recs[i];
> + act = rec->dtrd_action;
> +
> + if (act == DTRACEACT_COMMIT || act == DTRACEACT_DISCARD) {
> + commit_discard_seen = 1;
> + } else
> + only_commit_discards = 0;
> + }
> +
> + /*
> + * If this clause is a commit or discard, and no other actions have been
> + * seen, this is not a data-recording clause, and we should not call the
> + * efunc or rfunc at all.
> + */
> +
> + if (commit_discard_seen && only_commit_discards)
> + data_recording = 0;
> +
> + if (data_recording) {
> + if (flow)
> + dt_flowindent(dtp, pdat, *last, DTRACE_EPIDNONE);
> +
> + rval = (*efunc)(pdat, arg);
> +
> + if (flow) {
> + if (pdat->dtpda_flow == DTRACEFLOW_ENTRY)
> + pdat->dtpda_indent += 2;
> + }
> +
> + switch (rval) {
> + case DTRACE_CONSUME_NEXT:
> + return DTRACE_WORKSTATUS_OKAY;
> + case DTRACE_CONSUME_DONE:
> + return DTRACE_WORKSTATUS_DONE;
> + case DTRACE_CONSUME_ABORT:
> + return dt_set_errno(dtp, EDT_DIRABORT);
> + case DTRACE_CONSUME_THIS:
> + break;
> + default:
> + return dt_set_errno(dtp, EDT_BADRVAL);
> + }
> + }
> +
> + /*
> + * Now scan for data-recording actions.
> + *
> * FIXME: This code is temporary.
> */
> for (i = 0; i < pdat->dtpda_ddesc->dtdd_nrecs; i++) {
> @@ -2037,6 +2339,49 @@ dt_consume_one_buffer(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
> }
> }
>
> + if (act == DTRACEACT_COMMIT || act == DTRACEACT_DISCARD) {
> + /*
> + * Committing or discarding. If this is the first
> + * commit/discard we've seen for this speculation,
> + * arrange to drain it until enough CPUs have passed by
> + * that all must have been drained. Out-of-range IDs
> + * are automatically ignored by the code below, since
> + * they will have no dtsh entries.
> + */
> +
> + assert(specid == 0);
> +
> + tmpl.dtsh_id = *(uint32_t *)recdata;
> + dtsh = dt_htab_lookup(dtp->dt_specs_byid, &tmpl);
> +
> + /*
> + * Speculation exists and is not already being drained.
> + */
> + if (dtsh && !dtsh->dtsh_spec.draining) {
> + dtsh_draining_t *dtsd;
> + dt_bpf_specs_t spec;
> + dt_ident_t *idp = dt_dlib_get_map(dtp, "specs");
> + int rval;
> +
> + rval = dt_bpf_map_lookup(idp->di_id, &tmpl.dtsh_id, &spec);
> + if (rval != 0)
> + return dt_set_errno(dtp, -rval);
> +
> + assert(spec.draining);
> + dtsd = dt_zalloc(dtp, sizeof(dtsh_draining_t));
> + if (!dtsd)
> + return dt_set_errno(dtp, EDT_NOMEM);
> + dtsd->dtsd_dtsh = dtsh;
> + dtsh->dtsh_committing = (act == DTRACEACT_COMMIT);
> + memcpy(&dtsh->dtsh_spec, &spec,
> + sizeof(dt_bpf_specs_t));
> + dt_list_append(&dtp->dt_spec_bufs_draining, dtsd);
> + }
> + continue;
> + }
> +
> + assert(data_recording);
> +
> rval = (*rfunc)(pdat, rec, arg);
>
> if (rval == DTRACE_CONSUME_NEXT)
> @@ -2122,9 +2467,49 @@ dt_consume_one_buffer(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
> * that we're done processing this EPID. The return value is ignored in
> * this case. XXX should we respect at least DTRACE_CONSUME_ABORT?
> */
> - (*rfunc)(pdat, NULL, arg);
> + if (data_recording) {
> + (*rfunc)(pdat, NULL, arg);
>
> - *last = epid;
> + *last = epid;
> + }
> +
> + /*
> + * If we're not committing a speculative buffer already, commit any spec
> + * buffers that are marked as committing and draining and have any
> + * content to drain.
> + */
> + if (!committing) {
> + dtsh_draining_t *dtsd, *next;
> +
> + for (dtsd = dt_list_next(&dtp->dt_spec_bufs_draining);
> + dtsd != NULL; dtsd = next) {
> +
> + dtsh = dtsd->dtsd_dtsh;
> + dtsd->dtsd_read += dt_list_length(&dtsd->dtsd_dtsh->dtsh_dtsb_list);
> +
> + if (dtsh->dtsh_committing) {
> + if ((ret = dt_commit_one_spec(dtp, fp, dtsh,
> + pdat, efunc, rfunc,
> + flow, quiet, peekflags,
> + last, arg)) !=
> + DTRACE_WORKSTATUS_OKAY) {
> + dt_destroy_spec_bufs(dtp, dtsh, 0);
> + return ret;
> + }
> + }
> +
> + next = dt_list_next(dtsd);
> +
> + if (dtsd->dtsd_read < dtsd->dtsd_dtsh->dtsh_spec.written)
> + dt_destroy_spec_bufs(dtp, dtsh, 0);
> + else {
> + dt_destroy_spec_bufs(dtp, dtsh, 1);
> + dt_htab_delete(dtp->dt_specs_byid, dtsd->dtsd_dtsh);
> + dt_list_delete(&dtp->dt_spec_bufs_draining, dtsd);
> + dt_free(dtp, dtsd);
> + }
> + }
> + }
>
> return DTRACE_WORKSTATUS_OKAY;
> }
> @@ -2132,7 +2517,7 @@ dt_consume_one_buffer(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
> static dtrace_workstatus_t
> dt_consume_one(dtrace_hdl_t *dtp, FILE *fp, char *buf,
> dtrace_probedata_t *pdat, dtrace_consume_probe_f *efunc,
> - dtrace_consume_rec_f *rfunc, int flow, int quiet,
> + dtrace_consume_rec_f *rfunc, int flow, int quiet, int peekflags,
> dtrace_epid_t *last, void *arg)
> {
> char *data = buf;
> @@ -2151,7 +2536,7 @@ dt_consume_one(dtrace_hdl_t *dtp, FILE *fp, char *buf,
> * uint32_t size;
> * uint32_t pad;
> * uint32_t epid;
> - * uint32_t tag;
> + * uint32_t specid;
> * uint64_t data[n];
> * }
> * and 'data' points to the 'size' member at this point.
> @@ -2170,7 +2555,8 @@ dt_consume_one(dtrace_hdl_t *dtp, FILE *fp, char *buf,
> size -= sizeof(uint32_t);
>
> return dt_consume_one_buffer(dtp, fp, data, size, pdat, efunc,
> - rfunc, flow, quiet, last, arg);
> + rfunc, flow, quiet, peekflags,
> + last, 0, arg);
> } else if (hdr->type == PERF_RECORD_LOST) {
> #ifdef FIXME
> uint64_t lost;
> @@ -2195,7 +2581,7 @@ dt_consume_one(dtrace_hdl_t *dtp, FILE *fp, char *buf,
> int
> dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, dt_peb_t *peb,
> dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc,
> - int peek_only, void *arg)
> + int peekflags, void *arg)
> {
> struct perf_event_mmap_page *rb_page = (void *)peb->base;
> struct perf_event_header *hdr;
> @@ -2214,6 +2600,11 @@ dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, dt_peb_t *peb,
>
> /*
> * Clear the probe data, and fill in data independent fields.
> + *
> + * Initializing more fields here (or anywhere above
> + * dt_consume_one_buffer) may require the addition of new fields to
> + * dt_spec_bufs, if you want the original value to be preserved acorss
across - I make those typos all the time myself :)
> + * speculate/commit.
> */
> memset(&pdat, 0, sizeof(pdat));
> pdat.dtpda_handle = dtp;
> @@ -2226,7 +2617,12 @@ dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, dt_peb_t *peb,
> base = peb->base + pebset->page_size;
>
> do {
> - head = ring_buffer_read_head(rb_page);
> + if (peekflags == CONSUME_PEEK || peekflags == CONSUME_PEEK_FINISH)
> + head = peb->last_head;
> + else {
> + head = ring_buffer_read_head(rb_page);
> + peb->last_head = head;
> + }
> tail = rb_page->data_tail;
>
> if (head == tail)
> @@ -2262,7 +2658,8 @@ dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, dt_peb_t *peb,
> }
>
> rval = dt_consume_one(dtp, fp, event, &pdat, efunc,
> - rfunc, flow, quiet, &last, arg);
> + rfunc, flow, quiet, peekflags,
> + &last, arg);
> if (rval == DTRACE_WORKSTATUS_DONE)
> return DTRACE_WORKSTATUS_OKAY;
> if (rval != DTRACE_WORKSTATUS_OKAY)
> @@ -2271,9 +2668,9 @@ dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, dt_peb_t *peb,
> tail += hdr->size;
> } while (tail != head);
>
> - if (!peek_only)
> + if (peekflags == 0 || peekflags == CONSUME_PEEK_FINISH)
> ring_buffer_write_tail(rb_page, tail);
> - } while (!peek_only);
> + } while (peekflags == 0);
>
> return DTRACE_WORKSTATUS_OKAY;
> }
> @@ -2420,7 +2817,8 @@ dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, struct epoll_event *events,
> dtp->dt_errarg = &begin;
>
> rval = dt_consume_cpu(dtp, fp, bpeb, dt_consume_begin_probe,
> - dt_consume_begin_record, 1, &begin);
> + dt_consume_begin_record, CONSUME_PEEK_START,
> + &begin);
>
> dtp->dt_errhdlr = begin.dtbgn_errhdlr;
> dtp->dt_errarg = begin.dtbgn_errarg;
> @@ -2454,7 +2852,8 @@ dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, struct epoll_event *events,
> dtp->dt_errarg = &begin;
>
> rval = dt_consume_cpu(dtp, fp, bpeb, dt_consume_begin_probe,
> - dt_consume_begin_record, 0, &begin);
> + dt_consume_begin_record, CONSUME_PEEK_FINISH,
> + &begin);
>
> dtp->dt_errhdlr = begin.dtbgn_errhdlr;
> dtp->dt_errarg = begin.dtbgn_errarg;
> @@ -2514,12 +2913,41 @@ dt_consume_proc_exits(dtrace_hdl_t *dtp)
> pthread_mutex_unlock(&dph->dph_lock);
> }
>
> +
> +int
> +dt_consume_init(dtrace_hdl_t *dtp)
> +{
> + dtp->dt_specs_byid = dt_htab_create(dtp, &dt_spec_bufs_htab_ops);
> +
> + if (!dtp->dt_specs_byid)
> + return dt_set_errno(dtp, EDT_NOMEM);
> + return 0;
> +}
> +
> +void
> +dt_consume_fini(dtrace_hdl_t *dtp)
> +{
> + dt_spec_bufs_head_t *dtsh;
> + dtsh_draining_t *dtsd;
> +
> + while ((dtsd = dt_list_next(&dtp->dt_spec_bufs_draining)) != NULL) {
> + dt_list_delete(&dtp->dt_spec_bufs_draining, dtsd);
> + dt_free(dtp, dtsd);
> + }
> +
> + while ((dtsh = dt_list_next(&dtp->dt_spec_bufs)) != NULL)
> + dt_destroy_spec_bufs(dtp, dtsh, 1);
> +
> + dt_htab_destroy(dtp, dtp->dt_specs_byid);
> +}
> +
> dtrace_workstatus_t
> dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf,
> dtrace_consume_rec_f *rf, void *arg)
> {
> dtrace_optval_t timeout = dtp->dt_options[DTRACEOPT_SWITCHRATE];
> struct epoll_event events[dtp->dt_conf.num_online_cpus];
> + int drained = 0;
> int i, cnt;
> dtrace_workstatus_t rval;
>
> @@ -2585,6 +3013,7 @@ dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf,
> * by one. If tracing has stopped, skip the CPU on which the END probe
> * executed because we want to process that one last.
> */
> +drain:
> for (i = 0; i < cnt; i++) {
> dt_peb_t *peb = events[i].data.ptr;
>
> @@ -2598,6 +3027,20 @@ dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf,
> return rval;
> }
>
> + /*
> + * If a commit or discard has come in, loop twice, because if it was a
> + * commit the commit probably wasn't the first in the CPU list, and it is
> + * quite likely that an earlier CPU contains some of the speculative
> + * content we want to commit. Circle round and process it once more to
> + * pick this up. This means users don't find themselves with committed
> + * speculative content routinely split between one consume loop and the
> + * next.
> + */
> + if (!drained && dt_list_next(&dtp->dt_spec_bufs_draining) != NULL) {
> + drained = 1;
> + goto drain;
> + }
> +
> /*
> * If tracing has not been stopped, we are done here.
> */
> diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
> index 04becfa6a016..fa85cd446036 100644
> --- a/libdtrace/dt_dlibs.c
> +++ b/libdtrace/dt_dlibs.c
> @@ -59,6 +59,9 @@ static const dt_ident_t dt_bpf_symbols[] = {
> DT_BPF_SYMBOL(dt_get_string, DT_IDENT_SYMBOL),
> DT_BPF_SYMBOL(dt_get_tvar, DT_IDENT_SYMBOL),
> DT_BPF_SYMBOL(dt_set_tvar, DT_IDENT_SYMBOL),
> + DT_BPF_SYMBOL(dt_speculation, DT_IDENT_SYMBOL),
> + DT_BPF_SYMBOL(dt_speculation_speculate, DT_IDENT_SYMBOL),
> + DT_BPF_SYMBOL(dt_speculation_set_drainable, DT_IDENT_SYMBOL),
> DT_BPF_SYMBOL(dt_strnlen, DT_IDENT_SYMBOL),
> /* BPF maps */
> DT_BPF_SYMBOL(aggs, DT_IDENT_PTR),
> @@ -68,6 +71,7 @@ static const dt_ident_t dt_bpf_symbols[] = {
> DT_BPF_SYMBOL(lvars, DT_IDENT_PTR),
> DT_BPF_SYMBOL(mem, DT_IDENT_PTR),
> DT_BPF_SYMBOL(probes, DT_IDENT_PTR),
> + DT_BPF_SYMBOL(specs, DT_IDENT_PTR),
> DT_BPF_SYMBOL(state, DT_IDENT_PTR),
> DT_BPF_SYMBOL(strtab, DT_IDENT_PTR),
> DT_BPF_SYMBOL(tvars, DT_IDENT_PTR),
> @@ -80,6 +84,7 @@ static const dt_ident_t dt_bpf_symbols[] = {
> DT_BPF_SYMBOL_ID(STRSZ, DT_IDENT_SCALAR, DT_CONST_STRSZ),
> DT_BPF_SYMBOL_ID(STKSIZ, DT_IDENT_SCALAR, DT_CONST_STKSIZ),
> DT_BPF_SYMBOL_ID(BOOTTM, DT_IDENT_SCALAR, DT_CONST_BOOTTM),
> + DT_BPF_SYMBOL_ID(NSPEC, DT_IDENT_SCALAR, DT_CONST_NSPEC),
> /* End-of-list marker */
> { NULL, }
> };
> diff --git a/libdtrace/dt_errtags.h b/libdtrace/dt_errtags.h
> index f5b3bab72959..83257a148d0f 100644
> --- a/libdtrace/dt_errtags.h
> +++ b/libdtrace/dt_errtags.h
> @@ -148,7 +148,7 @@ typedef enum {
> D_DECL_PROTO_VOID, /* void must be sole parameter */
> D_DECL_PROTO_NAME, /* void parameter may not have a name */
> D_DECL_PROTO_FORM, /* parameter name has no formal */
> - D_COMM_COMM, /* commit() after commit() */
> + D_OBSOLETE1, /* (was commit() after commit()) */
> D_COMM_DREC, /* commit() after data action */
> D_SPEC_SPEC, /* speculate() after speculate() */
> D_SPEC_COMM, /* speculate() after commit() */
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index bd8d9943c6fa..fb8cc0447a6c 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -31,6 +31,7 @@
> extern "C" {
> #endif
>
> +#include <dt_bpf_maps.h>
> #include <dt_parser.h>
> #include <dt_regset.h>
> #include <dt_strtab.h>
> @@ -225,6 +226,31 @@ typedef struct dt_lib_depend {
> dt_list_t dtld_dependents; /* linked-list of lib dependents */
> } dt_lib_depend_t;
>
> +typedef struct dt_spec_bufs {
> + dt_list_t dtsb_list; /* linked-list forward/back pointers */
> + unsigned int dtsb_cpu; /* cpu for data */
> + char *dtsb_data; /* data for later processing */
> + uint32_t dtsb_size; /* size of data */
> +} dt_spec_bufs_t;
> +
> +typedef struct dt_spec_bufs_head {
> + dt_list_t dtsh_list; /* list of dt_spec_bufs_heads */
> + int32_t dtsh_id; /* speculation ID */
> + size_t dtsh_size; /* size of all buffers in this spec */
> + int dtsh_committing; /* when draining, nonzero if commit */
> + dt_bpf_specs_t dtsh_spec; /* bpf-side specs record for this spec
> + (buffer read/write counts). */
> + dt_list_t dtsh_dtsb_list; /* list of dt_spec_bufs */
> + struct dt_hentry dtsh_he; /* htab links */
> +} dt_spec_bufs_head_t;
> +
> +/*
> + * This will be raised much higher in future: right now it is nailed low
> + * because the search-for-free-speculation code is unrolled rather than being a
> + * proper loop, due to limitations in the BPF verifier.
> + */
> +#define DT_MAX_NSPECS 16 /* sanity upper bound on speculations */
> +
> typedef uint32_t dt_version_t; /* encoded version (see below) */
>
> struct dtrace_hdl {
> @@ -365,6 +391,9 @@ struct dtrace_hdl {
> hrtime_t dt_laststatus; /* last status */
> hrtime_t dt_lastswitch; /* last switch of buffer data */
> hrtime_t dt_lastagg; /* last snapshot of aggregation data */
> + dt_list_t dt_spec_bufs; /* List of spec bufs */
> + dt_list_t dt_spec_bufs_draining; /* List of spec bufs being drained */
> + dt_htab_t *dt_specs_byid;/* spec ID -> list of dt_spec_bufs_head_t */
> char *dt_sprintf_buf; /* buffer for dtrace_sprintf() */
> int dt_sprintf_buflen; /* length of dtrace_sprintf() buffer */
> pthread_mutex_t dt_sprintf_lock; /* lock for dtrace_sprintf() buffer */
> @@ -729,6 +758,9 @@ 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 int dt_consume_init(dtrace_hdl_t *);
> +extern void dt_consume_fini(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 *);
> diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
> index 867a61a30cc5..d3908c0be05a 100644
> --- a/libdtrace/dt_open.c
> +++ b/libdtrace/dt_open.c
> @@ -790,7 +790,16 @@ dt_vopen(int version, int flags, int *errp,
> */
> dtp->dt_options[DTRACEOPT_STRSIZE] = 256;
>
> - /* Set the default value of maxframes. */
> + /*
> + * Set the default speculation size and number of simultaneously active
> + * speculations.
> + */
> + dtp->dt_options[DTRACEOPT_SPECSIZE] = 1024 * 1024 * 4;
> + dtp->dt_options[DTRACEOPT_NSPEC] = 1;
> +
> + /*
> + * Set the default value of maxframes.
> + */
> fd = fopen("/proc/sys/kernel/perf_event_max_stack", "r");
> assert(fd);
> if (fscanf(fd, "%lu", &dtp->dt_options[DTRACEOPT_MAXFRAMES]) != 1)
> @@ -1108,6 +1117,12 @@ dt_vopen(int version, int flags, int *errp,
> */
> dt_dlib_init(dtp);
>
> + /*
> + * Initialize consume handling, e.g. storage of uncommitted speculations.
> + */
> + if (dt_consume_init(dtp) < 0)
> + return set_open_errno(dtp, errp, dtp->dt_errno);
> +
> /*
> * Initialize the collection of probes that is made available by the
> * known providers.
> @@ -1196,6 +1211,8 @@ dtrace_close(dtrace_hdl_t *dtp)
>
> dt_free(dtp, dtp->dt_xlatormap);
>
> + dt_consume_fini(dtp);
> +
> for (idp = dtp->dt_externs; idp != NULL; idp = ndp) {
> ndp = idp->di_next;
> dt_ident_destroy(idp);
> diff --git a/libdtrace/dt_peb.h b/libdtrace/dt_peb.h
> index 961db93c3837..ab327ec775a2 100644
> --- a/libdtrace/dt_peb.h
> +++ b/libdtrace/dt_peb.h
> @@ -26,6 +26,7 @@ typedef struct dt_peb {
> int fd; /* fd of perf output buffer */
> char *base; /* address of buffer */
> char *endp; /* address of end of buffer */
> + uint64_t last_head; /* last known head, for peeking */
> } dt_peb_t;
>
> /*
> diff --git a/libdtrace/dtrace.h b/libdtrace/dtrace.h
> index 036876facbc0..a05cc4a9f21b 100644
> --- a/libdtrace/dtrace.h
> +++ b/libdtrace/dtrace.h
> @@ -145,12 +145,13 @@ typedef struct dtrace_stmtdesc {
> } dtrace_stmtdesc_t;
>
> /* dtsd clause flags */
> -#define DT_CLSFLAG_DATAREC 1 /* data-recording */
> -#define DT_CLSFLAG_SPECULATE 2 /* speculate */
> -#define DT_CLSFLAG_COMMIT 4 /* commit */
> -#define DT_CLSFLAG_EXIT 8 /* exit */
> -#define DT_CLSFLAG_DESTRUCT 16 /* destructive */
> -#define DT_CLSFLAG_AGGREGATION 32 /* aggregation */
> +#define DT_CLSFLAG_DATAREC 1 /* data-recording */
> +#define DT_CLSFLAG_SPECULATE 2 /* speculate */
> +#define DT_CLSFLAG_COMMIT 4 /* commit */
> +#define DT_CLSFLAG_COMMIT_DISCARD 8 /* commit/discard */
> +#define DT_CLSFLAG_EXIT 16 /* exit */
> +#define DT_CLSFLAG_DESTRUCT 32 /* destructive */
> +#define DT_CLSFLAG_AGGREGATION 64 /* aggregation */
>
> typedef int dtrace_stmt_f(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
> dtrace_stmtdesc_t *sdp, void *data);
> diff --git a/test/unittest/speculation/err.CommitWithInvalid.d b/test/unittest/speculation/err.CommitWithInvalid.d
> new file mode 100644
> index 000000000000..7dc094d4a21e
> --- /dev/null
> +++ b/test/unittest/speculation/err.CommitWithInvalid.d
> @@ -0,0 +1,40 @@
> +/*
> + * Oracle Linux DTrace.
> + * 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.
> + */
> +
> +/*
> + * ASSERTION: When commit() is called with an out-of-range buffer number,
> + * a fault is raised.
> + *
> + * SECTION: Speculative Tracing/Committing a Speculation
> + */
> +#pragma D option quiet
> +#pragma D option nspec=8
> +
> +BEGIN
> +{
> + self->i = 0;
> +}
> +
> +BEGIN
> +{
> + commit(1024);
> +}
> +
> +BEGIN
> +{
> + trace("This should not be seen");
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/speculation/err.CommitWithInvalid.r b/test/unittest/speculation/err.CommitWithInvalid.r
> new file mode 100644
> index 000000000000..a9fd33ad9d30
> --- /dev/null
> +++ b/test/unittest/speculation/err.CommitWithInvalid.r
> @@ -0,0 +1,3 @@
> +
> +-- @@stderr --
> +dtrace: error on enabled probe ID 4 (ID 1: dtrace:::BEGIN): illegal operation in action #2
> diff --git a/test/unittest/speculation/err.D_ACT_SPEC.SpeculateWithCopyOut.d b/test/unittest/speculation/err.D_ACT_SPEC.SpeculateWithCopyOut.d
> index aae3e4ff84c1..908e9b9c703f 100644
> --- a/test/unittest/speculation/err.D_ACT_SPEC.SpeculateWithCopyOut.d
> +++ b/test/unittest/speculation/err.D_ACT_SPEC.SpeculateWithCopyOut.d
> @@ -5,7 +5,7 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
> +/* @@skip: dtv2, no copyout yet */
> /*
> * ASSERTION: Destructive actions may never be speculative.
> *
> diff --git a/test/unittest/speculation/err.D_ACT_SPEC.SpeculateWithCopyOutStr.d b/test/unittest/speculation/err.D_ACT_SPEC.SpeculateWithCopyOutStr.d
> index c75d15067c8c..1b23ec3bcbe3 100644
> --- a/test/unittest/speculation/err.D_ACT_SPEC.SpeculateWithCopyOutStr.d
> +++ b/test/unittest/speculation/err.D_ACT_SPEC.SpeculateWithCopyOutStr.d
> @@ -5,7 +5,7 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
> +/* @@skip: dtv2, no copyoutstr yet */
> /*
> * ASSERTION: Destructive actions may never be speculative.
> *
> diff --git a/test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.d b/test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.d
> deleted file mode 100644
> index 4b20022ccc19..000000000000
> --- a/test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.d
> +++ /dev/null
> @@ -1,61 +0,0 @@
> -/*
> - * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2020, 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 clause cannot contain multiple commit() calls to same buffer.
> - *
> - * SECTION: Speculative Tracing/Committing a Speculation;
> - * Options and Tunables/cleanrate
> - */
> -#pragma D option quiet
> -#pragma D option cleanrate=3000hz
> -
> -BEGIN
> -{
> - self->i = 0;
> - var1 = 0;
> -}
> -
> -profile:::tick-1sec
> -/!var1/
> -{
> - var1 = speculation();
> - printf("Speculation ID: %d\n", var1);
> -}
> -
> -profile:::tick-1sec
> -/var1/
> -{
> - speculate(var1);
> - printf("Speculating on id: %d\n", var1);
> - self->i++;
> -}
> -
> -profile:::tick-1sec
> -/(!self->i)/
> -{
> -}
> -
> -profile:::tick-1sec
> -/(self->i)/
> -{
> - commit(var1);
> - commit(var1);
> - exit(0);
> -}
> -
> -END
> -{
> - printf("Succesfully commited both buffers");
> - exit(0);
> -}
> -
> -ERROR
> -{
> - exit(0);
> -}
> diff --git a/test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.r b/test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.r
> deleted file mode 100644
> index a687cd0b6d34..000000000000
> --- a/test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.r
> +++ /dev/null
> @@ -1,2 +0,0 @@
> --- @@stderr --
> -dtrace: failed to compile script test/unittest/speculation/err.D_COMM_COMM.CommitAftCommit.d: [D_COMM_COMM] line 48: commit( ) may not follow commit( )
> diff --git a/test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.d b/test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.d
> deleted file mode 100644
> index 24c7ae7d6231..000000000000
> --- a/test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.d
> +++ /dev/null
> @@ -1,81 +0,0 @@
> -/*
> - * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2020, 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 clause cannot contain multiple commit() calls to disjoint buffers.
> - *
> - * SECTION: Speculative Tracing/Committing a Speculation;
> - * Options and Tunables/cleanrate
> - */
> -#pragma D option quiet
> -#pragma D option cleanrate=3000hz
> -
> -BEGIN
> -{
> - self->i = 0;
> - self->j = 0;
> - self->commit = 0;
> - var1 = 0;
> - var2 = 0;
> -}
> -
> -BEGIN
> -{
> - var1 = speculation();
> - printf("Speculation ID: %d\n", var1);
> -}
> -
> -BEGIN
> -{
> - var2 = speculation();
> - printf("Speculation ID: %d\n", var2);
> -}
> -
> -BEGIN
> -/var1/
> -{
> - speculate(var1);
> - printf("Speculating on id: %d\n", var1);
> - self->i++;
> -}
> -
> -BEGIN
> -/var2/
> -{
> - speculate(var2);
> - printf("Speculating on id: %d", var2);
> - self->j++;
> -
> -}
> -
> -BEGIN
> -/(self->i) && (self->j)/
> -{
> - commit(var1);
> - commit(var2);
> - self->commit++;
> -}
> -
> -BEGIN
> -/self->commit/
> -{
> - printf("Succesfully commited both buffers");
> - exit(0);
> -}
> -
> -BEGIN
> -/!self->commit/
> -{
> - printf("Couldnt commit both buffers");
> - exit(1);
> -}
> -
> -ERROR
> -{
> - exit(1);
> -}
> diff --git a/test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.r b/test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.r
> deleted file mode 100644
> index ae18ddf58396..000000000000
> --- a/test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.r
> +++ /dev/null
> @@ -1,2 +0,0 @@
> --- @@stderr --
> -dtrace: failed to compile script test/unittest/speculation/err.D_COMM_COMM.DisjointCommit.d: [D_COMM_COMM] line 60: commit( ) may not follow commit( )
> diff --git a/test/unittest/speculation/tst.ExitAftDiscard.d b/test/unittest/speculation/err.DiscardWithInvalid.d
> similarity index 51%
> copy from test/unittest/speculation/tst.ExitAftDiscard.d
> copy to test/unittest/speculation/err.DiscardWithInvalid.d
> index 758c1554a0af..15ad11908646 100644
> --- a/test/unittest/speculation/tst.ExitAftDiscard.d
> +++ b/test/unittest/speculation/err.DiscardWithInvalid.d
> @@ -1,36 +1,40 @@
> /*
> * 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: Using exit after discard should work fine.
> + * ASSERTION: When discard() is called with an out-of-range buffer number,
> + * a fault is raised.
> *
> * SECTION: Speculative Tracing/Discarding a Speculation
> - *
> */
> #pragma D option quiet
> +#pragma D option nspec=8
>
> BEGIN
> {
> self->i = 0;
> - self->spec = speculation();
> }
>
> BEGIN
> -/self->spec/
> {
> - speculate(self->spec);
> - self->i++;
> - printf("self->i: %d\n", self->i);
> + discard(1024);
> +}
> +
> +BEGIN
> +{
> + trace("This should not be seen");
> }
>
> BEGIN
> -/self->i/
> {
> - discard(self->spec);
> exit(0);
> }
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/speculation/err.DiscardWithInvalid.r b/test/unittest/speculation/err.DiscardWithInvalid.r
> new file mode 100644
> index 000000000000..a9fd33ad9d30
> --- /dev/null
> +++ b/test/unittest/speculation/err.DiscardWithInvalid.r
> @@ -0,0 +1,3 @@
> +
> +-- @@stderr --
> +dtrace: error on enabled probe ID 4 (ID 1: dtrace:::BEGIN): illegal operation in action #2
> diff --git a/test/unittest/speculation/err.SpecSizeVariations1.d b/test/unittest/speculation/err.SpecSizeVariations1.d
> deleted file mode 100644
> index 05fb2cf632e9..000000000000
> --- a/test/unittest/speculation/err.SpecSizeVariations1.d
> +++ /dev/null
> @@ -1,59 +0,0 @@
> -/*
> - * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, 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:
> - * Verify the behavior of variations in specsize.
> - *
> - * SECTION: Speculative Tracing/Options and Tuning;
> - * Options and Tunables/specsize
> - *
> - */
> -
> -#pragma D option quiet
> -#pragma D option specsize=0
> -
> -BEGIN
> -{
> - self->speculateFlag = 0;
> - self->commitFlag = 0;
> - self->spec = speculation();
> - printf("Speculative buffer ID: %d\n", self->spec);
> -}
> -
> -BEGIN
> -{
> - speculate(self->spec);
> - printf("Lots of data\n");
> - printf("Has to be crammed into this buffer\n");
> - printf("Until it overflows\n");
> - printf("And causes flops\n");
> - self->speculateFlag++;
> -
> -}
> -
> -BEGIN
> -/1 <= self->speculateFlag/
> -{
> - commit(self->spec);
> - self->commitFlag++;
> -}
> -
> -BEGIN
> -/1 <= self->commitFlag/
> -{
> - printf("Statement was executed\n");
> - exit(0);
> -}
> -
> -BEGIN
> -/1 > self->commitFlag/
> -{
> - printf("Statement wasn't executed\n");
> - exit(1);
> -}
> diff --git a/test/unittest/speculation/err.SpecSizeVariations1.r b/test/unittest/speculation/err.SpecSizeVariations1.r
> deleted file mode 100644
> index daca8b5083ff..000000000000
> --- a/test/unittest/speculation/err.SpecSizeVariations1.r
> +++ /dev/null
> @@ -1,2 +0,0 @@
> --- @@stderr --
> -dtrace: could not enable tracing: Enabling exceeds size of buffer
> diff --git a/test/unittest/speculation/err.SpecSizeVariations2.d b/test/unittest/speculation/err.SpecSizeVariations2.d
> deleted file mode 100644
> index 9685d2400e58..000000000000
> --- a/test/unittest/speculation/err.SpecSizeVariations2.d
> +++ /dev/null
> @@ -1,59 +0,0 @@
> -/*
> - * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, 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:
> - * Verify the behavior of variations in specsize.
> - *
> - * SECTION: Speculative Tracing/Options and Tuning;
> - * Options and Tunables/specsize
> - *
> - */
> -
> -#pragma D option quiet
> -#pragma D option specsize=7
> -
> -BEGIN
> -{
> - self->speculateFlag = 0;
> - self->commitFlag = 0;
> - self->spec = speculation();
> - printf("Speculative buffer ID: %d\n", self->spec);
> -}
> -
> -BEGIN
> -{
> - speculate(self->spec);
> - printf("Lots of data\n");
> - printf("Has to be crammed into this buffer\n");
> - printf("Until it overflows\n");
> - printf("And causes flops\n");
> - self->speculateFlag++;
> -
> -}
> -
> -BEGIN
> -/1 <= self->speculateFlag/
> -{
> - commit(self->spec);
> - self->commitFlag++;
> -}
> -
> -BEGIN
> -/1 <= self->commitFlag/
> -{
> - printf("Statement was executed\n");
> - exit(0);
> -}
> -
> -BEGIN
> -/1 > self->commitFlag/
> -{
> - printf("Statement wasn't executed\n");
> - exit(1);
> -}
> diff --git a/test/unittest/speculation/err.SpecSizeVariations2.r b/test/unittest/speculation/err.SpecSizeVariations2.r
> deleted file mode 100644
> index daca8b5083ff..000000000000
> --- a/test/unittest/speculation/err.SpecSizeVariations2.r
> +++ /dev/null
> @@ -1,2 +0,0 @@
> --- @@stderr --
> -dtrace: could not enable tracing: Enabling exceeds size of buffer
> diff --git a/test/unittest/speculation/tst.CommitAfterDiscard.d b/test/unittest/speculation/tst.CommitAfterDiscard.d
> index b07222179e16..49456cdd4521 100644
> --- a/test/unittest/speculation/tst.CommitAfterDiscard.d
> +++ b/test/unittest/speculation/tst.CommitAfterDiscard.d
> @@ -4,19 +4,16 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> * Call to commit() on a buffer after it has been discarded is silently
> * ignored.
> *
> - * SECTION: Speculative Tracing/Committing a Speculation;
> - * Options and Tunables/cleanrate
> + * SECTION: Speculative Tracing/Committing a Speculation
> *
> */
> #pragma D option quiet
> -#pragma D option cleanrate=3000hz
>
> BEGIN
> {
> @@ -48,7 +45,7 @@ BEGIN
> BEGIN
> /self->commit/
> {
> - printf("Commited a discarded buffer\n");
> + printf("Committed a discarded buffer\n");
> exit(0);
> }
>
> @@ -56,7 +53,7 @@ BEGIN
> BEGIN
> /!self->commit/
> {
> - printf("Couldnt commit a discarded buffer\n");
> + printf("Couldn't commit a discarded buffer\n");
> exit(1);
> }
>
> diff --git a/test/unittest/speculation/tst.CommitAfterDiscard.r b/test/unittest/speculation/tst.CommitAfterDiscard.r
> index 3674e24e5bc4..8ae7a38ec525 100644
> --- a/test/unittest/speculation/tst.CommitAfterDiscard.r
> +++ b/test/unittest/speculation/tst.CommitAfterDiscard.r
> @@ -1,3 +1,3 @@
> Speculation ID: 1
> -Commited a discarded buffer
> +Committed a discarded buffer
>
> diff --git a/test/unittest/speculation/tst.CommitCommitCommit.d b/test/unittest/speculation/tst.CommitCommitCommit.d
> new file mode 100644
> index 000000000000..e91d0d05db22
> --- /dev/null
> +++ b/test/unittest/speculation/tst.CommitCommitCommit.d
> @@ -0,0 +1,58 @@
> +/*
> + * 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 clause can have multiple commits.
> + *
> + * SECTION: Speculative Tracing/Committing a Speculation
> + *
> + */
> +#pragma D option quiet
> +#pragma D option nspec=3
> +
> +BEGIN
> +{
> + i = speculation();
> + j = speculation();
> + k = speculation();
> + printf("Speculation IDs: %d %d %d\n", i, j, k);
> +}
> +
> +BEGIN
> +{
> + speculate(i);
> + printf("Speculating on id: %d\n", i);
> +}
> +
> +BEGIN
> +{
> + speculate(j);
> + printf("Speculating on id: %d\n", j);
> +}
> +
> +BEGIN
> +{
> + speculate(k);
> + printf("Speculating on id: %d\n", k);
> +}
> +
> +BEGIN
> +{
> + commit(k);
> + commit(j);
> + commit(i);
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/speculation/tst.CommitCommitCommit.r b/test/unittest/speculation/tst.CommitCommitCommit.r
> new file mode 100644
> index 000000000000..bce7ae388a20
> --- /dev/null
> +++ b/test/unittest/speculation/tst.CommitCommitCommit.r
> @@ -0,0 +1,5 @@
> +Speculation IDs: 1 2 3
> +Speculating on id: 3
> +Speculating on id: 2
> +Speculating on id: 1
> +
> diff --git a/test/unittest/speculation/tst.CommitDiscard4x.d b/test/unittest/speculation/tst.CommitDiscard4x.d
> new file mode 100644
> index 000000000000..3a1f5b04f907
> --- /dev/null
> +++ b/test/unittest/speculation/tst.CommitDiscard4x.d
> @@ -0,0 +1,99 @@
> +/*
> + * 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 clause can have multiple commits and discards.
> + *
> + * SECTION: Speculative Tracing/Committing a Speculation
> + *
> + */
> +#pragma D option quiet
> +#pragma D option nspec=8
> +
> +BEGIN
> +{
> + a = speculation();
> + b = speculation();
> + c = speculation();
> + d = speculation();
> + e = speculation();
> + f = speculation();
> + g = speculation();
> + h = speculation();
> + printf("Speculation IDs: %d %d %d %d %d %d %d %d\n",
> + a, b, c, d, e, f, g, h);
> +}
> +
> +BEGIN
> +{
> + speculate(a);
> + printf("Speculating on id: %d\n", a);
> +}
> +
> +BEGIN
> +{
> + speculate(b);
> + printf("Speculating on id: %d\n", b);
> +}
> +
> +BEGIN
> +{
> + speculate(c);
> + printf("Speculating on id: %d\n", c);
> +}
> +
> +BEGIN
> +{
> + speculate(d);
> + printf("Speculating on id: %d\n", d);
> +}
> +
> +BEGIN
> +{
> + speculate(e);
> + printf("Speculating on id: %d\n", e);
> +}
> +
> +BEGIN
> +{
> + speculate(f);
> + printf("Speculating on id: %d\n", f);
> +}
> +
> +BEGIN
> +{
> + speculate(g);
> + printf("Speculating on id: %d\n", g);
> +}
> +
> +BEGIN
> +{
> + speculate(h);
> + printf("Speculating on id: %d\n", h);
> +}
> +
> +BEGIN
> +{
> + commit(h);
> + discard(g);
> + commit(f);
> + discard(e);
> + commit(d);
> + discard(c);
> + commit(b);
> + discard(a);
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/speculation/tst.CommitDiscard4x.r b/test/unittest/speculation/tst.CommitDiscard4x.r
> new file mode 100644
> index 000000000000..b6bbe9d26851
> --- /dev/null
> +++ b/test/unittest/speculation/tst.CommitDiscard4x.r
> @@ -0,0 +1,6 @@
> +Speculation IDs: 1 2 3 4 5 6 7 8
> +Speculating on id: 8
> +Speculating on id: 6
> +Speculating on id: 4
> +Speculating on id: 2
> +
> diff --git a/test/unittest/speculation/tst.CommitWithInactive.d b/test/unittest/speculation/tst.CommitWithInactive.d
> new file mode 100644
> index 000000000000..17bfe52ba7cf
> --- /dev/null
> +++ b/test/unittest/speculation/tst.CommitWithInactive.d
> @@ -0,0 +1,38 @@
> +/*
> + * Oracle Linux DTrace.
> + * 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.
> + */
> +
> +/*
> + * ASSERTION: When commit() is called with an inactive buffer number,
> + * it is ignored.
> + *
> + * SECTION: Speculative Tracing/Comitting a Speculation
> + */
> +#pragma D option quiet
> +BEGIN
> +{
> + self->i = 0;
> +}
> +
> +BEGIN
> +{
> + commit(1);
> +}
> +
> +BEGIN
> +{
> + trace("This should be seen");
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/speculation/tst.CommitWithInactive.r b/test/unittest/speculation/tst.CommitWithInactive.r
> new file mode 100644
> index 000000000000..5c6e69bdcfe1
> --- /dev/null
> +++ b/test/unittest/speculation/tst.CommitWithInactive.r
> @@ -0,0 +1 @@
> +This should be seen
> diff --git a/test/unittest/speculation/tst.CommitWithZero.d b/test/unittest/speculation/tst.CommitWithZero.d
> index dbc6dc06e8b8..9246d1de64d4 100644
> --- a/test/unittest/speculation/tst.CommitWithZero.d
> +++ b/test/unittest/speculation/tst.CommitWithZero.d
> @@ -4,30 +4,23 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION: An Id of zero though invalid may be passed to speculate(),
> * commit() and discard() without any ill effects.
> *
> - * SECTION: Speculative Tracing/Creating a Speculation;
> - * Options and Tunables/cleanrate
> + * SECTION: Speculative Tracing/Creating a Speculation
> */
> #pragma D option quiet
> -#pragma D option cleanrate=4000hz
>
> BEGIN
> {
> self->commitFlag = 0;
> - self->var1 = speculation();
> - printf("Speculative buffer ID: %d\n", self->var1);
> - self->spec = speculation();
> - printf("Speculative buffer ID: %d\n", self->spec);
> }
>
> BEGIN
> {
> - commit(self->spec);
> + commit(0);
> self->commitFlag++;
> }
>
> diff --git a/test/unittest/speculation/tst.CommitWithZero.r b/test/unittest/speculation/tst.CommitWithZero.r
> index 507400da55a4..7534ce772b29 100644
> --- a/test/unittest/speculation/tst.CommitWithZero.r
> +++ b/test/unittest/speculation/tst.CommitWithZero.r
> @@ -1,6 +1,2 @@
> -Speculative buffer ID: 1
> -Speculative buffer ID: 0
> commit(), self->commitFlag = 1
>
> --- @@stderr --
> -dtrace: 1 failed speculation (no speculative buffer available)
> diff --git a/test/unittest/speculation/tst.DataRecAftDiscard.d b/test/unittest/speculation/tst.DataRecAftDiscard.d
> index e262604e5910..23601df39ff9 100644
> --- a/test/unittest/speculation/tst.DataRecAftDiscard.d
> +++ b/test/unittest/speculation/tst.DataRecAftDiscard.d
> @@ -4,17 +4,14 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> * Data recording actions may follow discard.
> *
> - * SECTION: Speculative Tracing/Discarding a Speculation;
> - * Options and Tunables/cleanrate
> + * SECTION: Speculative Tracing/Discarding a Speculation
> */
> #pragma D option quiet
> -#pragma D option cleanrate=2000hz
>
> BEGIN
> {
> diff --git a/test/unittest/speculation/tst.DiscardAftCommit.d b/test/unittest/speculation/tst.DiscardAftCommit.d
> index ca4a0645c00f..7b7e0e6896c1 100644
> --- a/test/unittest/speculation/tst.DiscardAftCommit.d
> +++ b/test/unittest/speculation/tst.DiscardAftCommit.d
> @@ -4,18 +4,16 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> - * Can call discard() on a buffer after it has been commited.
> + * Can call discard() on a buffer after it has been committed.
> *
> - * SECTION: Speculative Tracing/Discarding a Speculation;
> - * Options and Tunables/cleanrate
> + * SECTION: Speculative Tracing/Discarding a Speculation
> *
> */
> +
> #pragma D option quiet
> -#pragma D option cleanrate=3000hz
>
> BEGIN
> {
> @@ -47,7 +45,7 @@ BEGIN
> BEGIN
> /self->discard/
> {
> - printf("Discarded a commited buffer\n");
> + printf("Discarded a committed buffer\n");
> exit(0);
> }
>
> @@ -55,7 +53,7 @@ BEGIN
> BEGIN
> /!self->discard/
> {
> - printf("Couldnt discard a commited buffer\n");
> + printf("Couldn't discard a committed buffer\n");
> exit(1);
> }
>
> diff --git a/test/unittest/speculation/tst.DiscardAftCommit.r b/test/unittest/speculation/tst.DiscardAftCommit.r
> index 3f960663fd8c..004727149781 100644
> --- a/test/unittest/speculation/tst.DiscardAftCommit.r
> +++ b/test/unittest/speculation/tst.DiscardAftCommit.r
> @@ -1,5 +1,5 @@
> Speculation ID: 1
> This statement and the following are speculative!!
> Speculating on id: 1
> -Discarded a commited buffer
> +Discarded a committed buffer
>
> diff --git a/test/unittest/speculation/tst.DiscardAftDataRec.d b/test/unittest/speculation/tst.DiscardAftDataRec.d
> index 3d7df87ef838..740016648c11 100644
> --- a/test/unittest/speculation/tst.DiscardAftDataRec.d
> +++ b/test/unittest/speculation/tst.DiscardAftDataRec.d
> @@ -4,11 +4,10 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> - * Discard may not follow data recording actions.
> + * Discard may follow data recording actions.
> *
> * SECTION: Speculative Tracing/Discarding a Speculation
> *
> diff --git a/test/unittest/speculation/tst.DiscardAftDiscard.d b/test/unittest/speculation/tst.DiscardAftDiscard.d
> index d6231e2cca5c..1f5ae2b9740c 100644
> --- a/test/unittest/speculation/tst.DiscardAftDiscard.d
> +++ b/test/unittest/speculation/tst.DiscardAftDiscard.d
> @@ -4,18 +4,15 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> * Can call discard on an already discarded buffer.
> *
> - * SECTION: Speculative Tracing/Discarding a Speculation;
> - * Options and Tunables/cleanrate
> + * SECTION: Speculative Tracing/Discarding a Speculation
> *
> */
> #pragma D option quiet
> -#pragma D option cleanrate=3000hz
>
> BEGIN
> {
> @@ -55,7 +52,7 @@ BEGIN
> BEGIN
> /(!self->discard2) || (!self->discard1)/
> {
> - printf("Couldnt discard a discarded buffer\n");
> + printf("Couldn't discard a discarded buffer\n");
> exit(1);
> }
>
> diff --git a/test/unittest/speculation/tst.ExitAftDiscard.d b/test/unittest/speculation/tst.DiscardWithInactive.d
> similarity index 51%
> copy from test/unittest/speculation/tst.ExitAftDiscard.d
> copy to test/unittest/speculation/tst.DiscardWithInactive.d
> index 758c1554a0af..4228244bfa3d 100644
> --- a/test/unittest/speculation/tst.ExitAftDiscard.d
> +++ b/test/unittest/speculation/tst.DiscardWithInactive.d
> @@ -1,36 +1,38 @@
> /*
> * 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: Using exit after discard should work fine.
> + * ASSERTION: When discard() is called with an inactive buffer number,
> + * it is ignored.
> *
> * SECTION: Speculative Tracing/Discarding a Speculation
> - *
> */
> #pragma D option quiet
> -
> BEGIN
> {
> self->i = 0;
> - self->spec = speculation();
> }
>
> BEGIN
> -/self->spec/
> {
> - speculate(self->spec);
> - self->i++;
> - printf("self->i: %d\n", self->i);
> + discard(1);
> +}
> +
> +BEGIN
> +{
> + trace("This should be seen");
> }
>
> BEGIN
> -/self->i/
> {
> - discard(self->spec);
> exit(0);
> }
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/speculation/tst.DiscardWithInactive.r b/test/unittest/speculation/tst.DiscardWithInactive.r
> new file mode 100644
> index 000000000000..5c6e69bdcfe1
> --- /dev/null
> +++ b/test/unittest/speculation/tst.DiscardWithInactive.r
> @@ -0,0 +1 @@
> +This should be seen
> diff --git a/test/unittest/speculation/tst.DiscardWithZero.d b/test/unittest/speculation/tst.DiscardWithZero.d
> index 8ff2d43d9fe5..98c1f1bfde8e 100644
> --- a/test/unittest/speculation/tst.DiscardWithZero.d
> +++ b/test/unittest/speculation/tst.DiscardWithZero.d
> @@ -4,31 +4,23 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION: An Id of zero though invalid may be passed to speculate(),
> * commit() and discard() without any ill effects.
> *
> - * SECTION: Speculative Tracing/Creating a Speculation;
> - * Options and Tunables/cleanrate
> + * SECTION: Speculative Tracing/Creating a Speculation
> */
> #pragma D option quiet
> -#pragma D option cleanrate=4000hz
>
> BEGIN
> {
> self->discardFlag = 0;
> - self->var1 = speculation();
> - printf("Speculative buffer ID: %d\n", self->var1);
> - self->spec = speculation();
> - printf("Speculative buffer ID: %d\n", self->spec);
> }
>
> BEGIN
> -/0 == self->spec/
> {
> - discard(self->spec);
> + discard(0);
> self->discardFlag++;
> }
>
> diff --git a/test/unittest/speculation/tst.DiscardWithZero.r b/test/unittest/speculation/tst.DiscardWithZero.r
> index e4a7cb7f9528..f356261e2522 100644
> --- a/test/unittest/speculation/tst.DiscardWithZero.r
> +++ b/test/unittest/speculation/tst.DiscardWithZero.r
> @@ -1,6 +1,2 @@
> -Speculative buffer ID: 1
> -Speculative buffer ID: 0
> discard(), self->discardFlag = 1
>
> --- @@stderr --
> -dtrace: 1 failed speculation (no speculative buffer available)
> diff --git a/test/unittest/speculation/tst.ExitAftDiscard.d b/test/unittest/speculation/tst.ExitAftDiscard.d
> index 758c1554a0af..cbe32a572ee4 100644
> --- a/test/unittest/speculation/tst.ExitAftDiscard.d
> +++ b/test/unittest/speculation/tst.ExitAftDiscard.d
> @@ -4,7 +4,6 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION: Using exit after discard should work fine.
> diff --git a/test/unittest/speculation/tst.NoSpecBuffer.d b/test/unittest/speculation/tst.NoSpecBuffer.d
> index a3a2107c7132..da138e364f30 100644
> --- a/test/unittest/speculation/tst.NoSpecBuffer.d
> +++ b/test/unittest/speculation/tst.NoSpecBuffer.d
> @@ -5,18 +5,18 @@
> * http://oss.oracle.com/licenses/upl.
> */
>
> -/* @@xfail: dtv2 */
> /* @@trigger: none */
>
> /*
> * ASSERTION:
> - * The number of speculative buffers defaults to one. If no speculative buffer
> - * is available when speculation is called, an ID of zero is returned.
> + * If no speculative buffer is available when speculation is called,
> + * an ID of zero is returned.
> *
> * SECTION: Speculative Tracing/Creating a Speculation
> *
> */
> #pragma D option quiet
> +#pragma D option nspec=1
>
> BEGIN
> {
> diff --git a/test/unittest/speculation/tst.NoSpecBuffer.r b/test/unittest/speculation/tst.NoSpecBuffer.r
> index 8d6f9d2c8946..88d21d7ca4f7 100644
> --- a/test/unittest/speculation/tst.NoSpecBuffer.r
> +++ b/test/unittest/speculation/tst.NoSpecBuffer.r
> @@ -1,5 +1,3 @@
> Speculative buffer ID: 1
> Speculative buffer ID: 0
> i: 2 self->spec: 0
> --- @@stderr --
> -dtrace: 1 failed speculation (no speculative buffer available)
> diff --git a/test/unittest/speculation/tst.SingleCPU.d b/test/unittest/speculation/tst.SingleCPU.d
> new file mode 100644
> index 000000000000..a1cb4612a81b
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SingleCPU.d
> @@ -0,0 +1,69 @@
> +/*
> + * 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: Verify many speculations (single-CPU case).
> + *
> + * SECTION: Speculative Tracing
> + */
> +
> +#pragma D option quiet
> +
> +/* This test should take only 10s, but is taking much longer than the 10s
> + one would expect (48s here). Boosting timeout temporarily. */
> +/* @@timeout: 120 */
> +
> +BEGIN
> +{
> + n = 0;
> +}
> +
> +/*
> + * Each tick, n is incremented. Which clause is used rotates modulo 4.
> + * 0: get a specid
> + * 1: speculate some output
> + * 2: speculate some other output
> + * 3: commit (sometimes) or discard (usually)
> + */
> +
> +tick-10ms
> +/ (n & 3) == 0 /
> +{
> + i = speculation();
> +}
> +
> +tick-10ms
> +/ (n & 3) == 1 /
> +{
> + speculate(i);
> + printf("%4d %4d", n, i);
> +}
> +
> +tick-10ms
> +/ (n & 3) == 2 /
> +{
> + speculate(i);
> + printf("%4d hello world\n", n);
> +}
> +
> +tick-10ms
> +/ (n & 3) == 3 && (n & 63) == 3 /
> +{
> + commit(i);
> +}
> +
> +tick-10ms
> +/ (n & 3) == 3 && (n & 63) != 3 /
> +{
> + discard(i);
> +}
> +
> +tick-10ms
> +/ n++ >= 1000 /
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/speculation/tst.SingleCPU.r b/test/unittest/speculation/tst.SingleCPU.r
> new file mode 100644
> index 000000000000..3659925d10b0
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SingleCPU.r
> @@ -0,0 +1,17 @@
> + 1 1 2 hello world
> + 65 1 66 hello world
> + 129 1 130 hello world
> + 193 1 194 hello world
> + 257 1 258 hello world
> + 321 1 322 hello world
> + 385 1 386 hello world
> + 449 1 450 hello world
> + 513 1 514 hello world
> + 577 1 578 hello world
> + 641 1 642 hello world
> + 705 1 706 hello world
> + 769 1 770 hello world
> + 833 1 834 hello world
> + 897 1 898 hello world
> + 961 1 962 hello world
> +
> diff --git a/test/unittest/speculation/tst.SpecSizeVariations3.d b/test/unittest/speculation/tst.SpecSizeVariations3.d
> deleted file mode 100644
> index 9ba2d1ebf9d9..000000000000
> --- a/test/unittest/speculation/tst.SpecSizeVariations3.d
> +++ /dev/null
> @@ -1,59 +0,0 @@
> -/*
> - * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, 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:
> - * Verify the behavior of speculations with changes in specsize.
> - *
> - * SECTION: Speculative Tracing/Options and Tuning;
> - * Options and Tunables/specsize
> - *
> - */
> -
> -#pragma D option quiet
> -#pragma D option specsize=8
> -
> -BEGIN
> -{
> - self->speculateFlag = 0;
> - self->commitFlag = 0;
> - self->spec = speculation();
> - printf("Speculative buffer ID: %d\n", self->spec);
> -}
> -
> -BEGIN
> -{
> - speculate(self->spec);
> - printf("Lots of data\n");
> - printf("Has to be crammed into this buffer\n");
> - printf("Until it overflows\n");
> - printf("And causes flops\n");
> - self->speculateFlag++;
> -
> -}
> -
> -BEGIN
> -/1 <= self->speculateFlag/
> -{
> - commit(self->spec);
> - self->commitFlag++;
> -}
> -
> -BEGIN
> -/1 <= self->commitFlag/
> -{
> - printf("Statement was executed\n");
> - exit(1);
> -}
> -
> -BEGIN
> -/1 > self->commitFlag/
> -{
> - printf("Statement wasn't executed\n");
> - exit(0);
> -}
> diff --git a/test/unittest/speculation/tst.SpecSizeVariations3.r b/test/unittest/speculation/tst.SpecSizeVariations3.r
> deleted file mode 100644
> index 41ccfc14c78c..000000000000
> --- a/test/unittest/speculation/tst.SpecSizeVariations3.r
> +++ /dev/null
> @@ -1,3 +0,0 @@
> -Speculative buffer ID: 1
> -Statement wasn't executed
> -
> diff --git a/test/unittest/speculation/tst.SpecSizeVariations4.d b/test/unittest/speculation/tst.SpecSizeVariations4.d
> index 342ea316a219..17c6eab6118c 100644
> --- a/test/unittest/speculation/tst.SpecSizeVariations4.d
> +++ b/test/unittest/speculation/tst.SpecSizeVariations4.d
> @@ -4,7 +4,6 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> @@ -18,8 +17,11 @@
> #pragma D option quiet
> #pragma D option specsize=39
>
> +long long x;
> +
> BEGIN
> {
> + x = 123456789;
> self->speculateFlag = 0;
> self->commitFlag = 0;
> self->spec = speculation();
> @@ -29,10 +31,10 @@ BEGIN
> BEGIN
> {
> speculate(self->spec);
> - printf("Lots of data\n");
> - printf("Has to be crammed into this buffer\n");
> - printf("Until it overflows\n");
> - printf("And causes flops\n");
> + printf("%lld: Lots of data\n", x);
> + printf("%lld: Has to be crammed into this buffer\n", x);
> + printf("%lld: Until it overflows\n", x);
> + printf("%lld: And causes flops\n", x);
> self->speculateFlag++;
>
> }
> @@ -48,12 +50,12 @@ BEGIN
> /1 <= self->commitFlag/
> {
> printf("Statement was executed\n");
> - exit(1);
> + exit(0);
> }
>
> BEGIN
> /1 > self->commitFlag/
> {
> printf("Statement wasn't executed\n");
> - exit(0);
> + exit(1);
> }
> diff --git a/test/unittest/speculation/tst.SpecSizeVariations4.r b/test/unittest/speculation/tst.SpecSizeVariations4.r
> index 41ccfc14c78c..719e2441d27d 100644
> --- a/test/unittest/speculation/tst.SpecSizeVariations4.r
> +++ b/test/unittest/speculation/tst.SpecSizeVariations4.r
> @@ -1,3 +1,3 @@
> Speculative buffer ID: 1
> -Statement wasn't executed
> +Statement was executed
>
> diff --git a/test/unittest/speculation/tst.SpecSizeVariations5.d b/test/unittest/speculation/tst.SpecSizeVariations5.d
> index 4e90fbb1feca..cfbd885a5cdd 100644
> --- a/test/unittest/speculation/tst.SpecSizeVariations5.d
> +++ b/test/unittest/speculation/tst.SpecSizeVariations5.d
> @@ -4,7 +4,6 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> @@ -18,8 +17,11 @@
> #pragma D option quiet
> #pragma D option specsize=40
>
> +long long x;
> +
> BEGIN
> {
> + x = 123456789;
> self->speculateFlag = 0;
> self->commitFlag = 0;
> self->spec = speculation();
> @@ -29,10 +31,10 @@ BEGIN
> BEGIN
> {
> speculate(self->spec);
> - printf("Lots of data\n");
> - printf("Has to be crammed into this buffer\n");
> - printf("Until it overflows\n");
> - printf("And causes flops\n");
> + printf("%lld: Lots of data\n", x);
> + printf("%lld: Has to be crammed into this buffer\n", x);
> + printf("%lld: Until it overflows\n", x);
> + printf("%lld: And causes flops\n", x);
> self->speculateFlag++;
>
> }
> diff --git a/test/unittest/speculation/tst.SpecSizeVariations5.r b/test/unittest/speculation/tst.SpecSizeVariations5.r
> index da44024d5ffe..d09013a2ae19 100644
> --- a/test/unittest/speculation/tst.SpecSizeVariations5.r
> +++ b/test/unittest/speculation/tst.SpecSizeVariations5.r
> @@ -1,7 +1,7 @@
> Speculative buffer ID: 1
> -Lots of data
> -Has to be crammed into this buffer
> -Until it overflows
> -And causes flops
> +123456789: Lots of data
> +123456789: Has to be crammed into this buffer
> +123456789: Until it overflows
> +123456789: And causes flops
> Statement was executed
>
> diff --git a/test/unittest/speculation/tst.SpeculationCommit.d b/test/unittest/speculation/tst.SpeculationCommit.d
> index 554f5339db11..957042367b08 100644
> --- a/test/unittest/speculation/tst.SpeculationCommit.d
> +++ b/test/unittest/speculation/tst.SpeculationCommit.d
> @@ -4,18 +4,15 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION: Test the normal behavior of speculate() and commit().
> *
> * SECTION: Speculative Tracing/Committing a Speculation;
> - * Actions and Subroutines/speculation();
> - * Options and Tunables/cleanrate
> + * Actions and Subroutines/speculation()
> *
> */
> #pragma D option quiet
> -#pragma D option cleanrate=2000hz
>
> BEGIN
> {
> diff --git a/test/unittest/speculation/tst.SpeculationCommit.d b/test/unittest/speculation/tst.SpeculationCommitNotQuiet.d
> similarity index 78%
> copy from test/unittest/speculation/tst.SpeculationCommit.d
> copy to test/unittest/speculation/tst.SpeculationCommitNotQuiet.d
> index 554f5339db11..322eefeac799 100644
> --- a/test/unittest/speculation/tst.SpeculationCommit.d
> +++ b/test/unittest/speculation/tst.SpeculationCommitNotQuiet.d
> @@ -1,21 +1,18 @@
> /*
> * 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 */
>
> /*
> - * ASSERTION: Test the normal behavior of speculate() and commit().
> + * ASSERTION: Test the normal behavior of speculate() and commit() with
> + * quiet mode turned off.
> *
> * SECTION: Speculative Tracing/Committing a Speculation;
> - * Actions and Subroutines/speculation();
> - * Options and Tunables/cleanrate
> + * Actions and Subroutines/speculation()
> *
> */
> -#pragma D option quiet
> -#pragma D option cleanrate=2000hz
>
> BEGIN
> {
> diff --git a/test/unittest/speculation/tst.SpeculationCommitNotQuiet.r b/test/unittest/speculation/tst.SpeculationCommitNotQuiet.r
> new file mode 100644
> index 000000000000..635f849a4df1
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SpeculationCommitNotQuiet.r
> @@ -0,0 +1,10 @@
> + FUNCTION:NAME
> + :BEGIN Speculation ID: 1
> +
> + :BEGIN Called speculate on id: 1
> +
> + :BEGIN Succesfully tested buffer commit
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/speculation/tst.SpeculationCommitNotQuiet.d' matched 5 probes
> +
> diff --git a/test/unittest/speculation/tst.SpeculationDefault.d b/test/unittest/speculation/tst.SpeculationDefault.d
> new file mode 100644
> index 000000000000..aeeeb64d9956
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SpeculationDefault.d
> @@ -0,0 +1,31 @@
> +/*
> + * Oracle Linux DTrace.
> + * 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.
> + */
> +
> +/*
> + * ASSERTION: Test the normal behavior of a speculative default clause
> + * with quiet mode turned off.
> + *
> + * SECTION: Actions and Subroutines/speculation()
> + *
> + */
> +
> +BEGIN
> +{
> + self->spec = speculation();
> +}
> +
> +BEGIN
> +/self->spec/
> +{
> + speculate(self->spec);
> +}
> +
> +BEGIN
> +/self->spec/
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/speculation/tst.SpeculationDefault.r b/test/unittest/speculation/tst.SpeculationDefault.r
> new file mode 100644
> index 000000000000..b39c2480397c
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SpeculationDefault.r
> @@ -0,0 +1,5 @@
> + FUNCTION:NAME
> + :BEGIN
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/speculation/tst.SpeculationDefault.d' matched 3 probes
> diff --git a/test/unittest/speculation/tst.SpeculationDefaultCommit.d b/test/unittest/speculation/tst.SpeculationDefaultCommit.d
> new file mode 100644
> index 000000000000..6a8e20434c93
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SpeculationDefaultCommit.d
> @@ -0,0 +1,38 @@
> +/*
> + * Oracle Linux DTrace.
> + * 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.
> + */
> +
> +/*
> + * ASSERTION: Test the normal behavior when committing a speculative
> + * default clause with quiet mode turned off.
> + *
> + * SECTION: Speculative Tracing/Committing a Speculation;
> + * Actions and Subroutines/speculation()
> + *
> + */
> +
> +BEGIN
> +{
> + self->spec = speculation();
> +}
> +
> +BEGIN
> +/self->spec/
> +{
> + speculate(self->spec);
> +}
> +
> +BEGIN
> +/self->spec/
> +{
> + commit(self->spec);
> +}
> +
> +BEGIN
> +/self->spec/
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/speculation/tst.SpeculationDefaultCommit.r b/test/unittest/speculation/tst.SpeculationDefaultCommit.r
> new file mode 100644
> index 000000000000..8d09be3ad3e4
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SpeculationDefaultCommit.r
> @@ -0,0 +1,6 @@
> + FUNCTION:NAME
> + :BEGIN
> + :BEGIN
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/speculation/tst.SpeculationDefaultCommit.d' matched 4 probes
> diff --git a/test/unittest/speculation/tst.SpeculationDefaultDiscard.d b/test/unittest/speculation/tst.SpeculationDefaultDiscard.d
> new file mode 100644
> index 000000000000..3292ca982638
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SpeculationDefaultDiscard.d
> @@ -0,0 +1,37 @@
> +/*
> + * Oracle Linux DTrace.
> + * 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.
> + */
> +
> +/*
> + * ASSERTION: Test the normal behavior when discarding a speculative
> + * default clause with quiet mode turned off.
> + *
> + * SECTION: Speculative Tracing/Discarding a Speculation;
> + * Actions and Subroutines/speculation()
> + */
> +
> +BEGIN
> +{
> + self->spec = speculation();
> +}
> +
> +BEGIN
> +/self->spec/
> +{
> + speculate(self->spec);
> +}
> +
> +BEGIN
> +/self->spec/
> +{
> + discard(self->spec);
> +}
> +
> +BEGIN
> +/self->spec/
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/speculation/tst.SpeculationDefaultDiscard.r b/test/unittest/speculation/tst.SpeculationDefaultDiscard.r
> new file mode 100644
> index 000000000000..156d6b6ecba8
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SpeculationDefaultDiscard.r
> @@ -0,0 +1,5 @@
> + FUNCTION:NAME
> + :BEGIN
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/speculation/tst.SpeculationDefaultDiscard.d' matched 4 probes
> diff --git a/test/unittest/speculation/tst.SpeculationDiscard.d b/test/unittest/speculation/tst.SpeculationDiscard.d
> index 8803324a81bb..132cc13beedb 100644
> --- a/test/unittest/speculation/tst.SpeculationDiscard.d
> +++ b/test/unittest/speculation/tst.SpeculationDiscard.d
> @@ -4,17 +4,14 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION: Test the normal behavior of speculate() and discard().
> *
> - * SECTION: Speculative Tracing/Discarding a Speculation;
> - * Options and Tunables/cleanrate
> + * SECTION: Speculative Tracing/Discarding a Speculation
> *
> */
> #pragma D option quiet
> -#pragma D option cleanrate=2000hz
>
> BEGIN
> {
> @@ -42,7 +39,7 @@ BEGIN
> BEGIN
> /(1 == self->discard)/
> {
> - printf("Succesfully tested buffer discard\n");
> + printf("Successfully tested buffer discard\n");
> exit(0);
> }
>
> diff --git a/test/unittest/speculation/tst.SpeculationDiscard.r b/test/unittest/speculation/tst.SpeculationDiscard.r
> index 8b960a0dbd18..80e8b50fadc1 100644
> --- a/test/unittest/speculation/tst.SpeculationDiscard.r
> +++ b/test/unittest/speculation/tst.SpeculationDiscard.r
> @@ -1,3 +1,3 @@
> Speculation ID: 1
> -Succesfully tested buffer discard
> +Successfully tested buffer discard
>
> diff --git a/test/unittest/speculation/tst.SpeculationDiscard.d b/test/unittest/speculation/tst.SpeculationDiscardNotQuiet.d
> similarity index 71%
> copy from test/unittest/speculation/tst.SpeculationDiscard.d
> copy to test/unittest/speculation/tst.SpeculationDiscardNotQuiet.d
> index 8803324a81bb..23013c6e5ff1 100644
> --- a/test/unittest/speculation/tst.SpeculationDiscard.d
> +++ b/test/unittest/speculation/tst.SpeculationDiscardNotQuiet.d
> @@ -1,20 +1,16 @@
> /*
> * 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 */
>
> /*
> * ASSERTION: Test the normal behavior of speculate() and discard().
> *
> - * SECTION: Speculative Tracing/Discarding a Speculation;
> - * Options and Tunables/cleanrate
> + * SECTION: Speculative Tracing/Discarding a Speculation
> *
> */
> -#pragma D option quiet
> -#pragma D option cleanrate=2000hz
>
> BEGIN
> {
> @@ -42,7 +38,7 @@ BEGIN
> BEGIN
> /(1 == self->discard)/
> {
> - printf("Succesfully tested buffer discard\n");
> + printf("Successfully tested buffer discard\n");
> exit(0);
> }
>
> diff --git a/test/unittest/speculation/tst.SpeculationDiscardNotQuiet.r b/test/unittest/speculation/tst.SpeculationDiscardNotQuiet.r
> new file mode 100644
> index 000000000000..be17cec0cec9
> --- /dev/null
> +++ b/test/unittest/speculation/tst.SpeculationDiscardNotQuiet.r
> @@ -0,0 +1,8 @@
> + FUNCTION:NAME
> + :BEGIN Speculation ID: 1
> +
> + :BEGIN Successfully tested buffer discard
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/speculation/tst.SpeculationDiscardNotQuiet.d' matched 5 probes
> +
> diff --git a/test/unittest/speculation/tst.SpeculationID.d b/test/unittest/speculation/tst.SpeculationID.d
> index 21ef5ac64b55..f55d5fda44f5 100644
> --- a/test/unittest/speculation/tst.SpeculationID.d
> +++ b/test/unittest/speculation/tst.SpeculationID.d
> @@ -4,7 +4,6 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> diff --git a/test/unittest/speculation/tst.SpeculationWithZero.d b/test/unittest/speculation/tst.SpeculationWithZero.d
> index 959b734a9b75..04991b40f5a2 100644
> --- a/test/unittest/speculation/tst.SpeculationWithZero.d
> +++ b/test/unittest/speculation/tst.SpeculationWithZero.d
> @@ -4,7 +4,6 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> @@ -19,14 +18,11 @@
> BEGIN
> {
> self->speculateFlag = 0;
> - self->spec = speculation();
> - self->spec = speculation();
> - printf("Speculative buffer ID: %d\n", self->spec);
> }
>
> BEGIN
> {
> - speculate(self->spec);
> + speculate(0);
> self->speculateFlag++;
> }
>
> diff --git a/test/unittest/speculation/tst.SpeculationWithZero.r b/test/unittest/speculation/tst.SpeculationWithZero.r
> index 9165c854920c..35952e0a25e9 100644
> --- a/test/unittest/speculation/tst.SpeculationWithZero.r
> +++ b/test/unittest/speculation/tst.SpeculationWithZero.r
> @@ -1,5 +1,2 @@
> -Speculative buffer ID: 0
> Statement wasn't executed
>
> --- @@stderr --
> -dtrace: 1 failed speculation (no speculative buffer available)
> diff --git a/test/unittest/speculation/tst.TwoSpecBuffers.d b/test/unittest/speculation/tst.TwoSpecBuffers.d
> index 2df996927c15..25e73b0662c7 100644
> --- a/test/unittest/speculation/tst.TwoSpecBuffers.d
> +++ b/test/unittest/speculation/tst.TwoSpecBuffers.d
> @@ -4,12 +4,11 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> /*
> * ASSERTION:
> - * Increasing the value of nspec to two should will increase the number of
> - * speculative buffers to two.
> + * Increasing the value of nspec to two should set the number of
> + * speculative buffers to two: getting a third should fail.
> *
> * SECTION: Speculative Tracing/Options and Tuning;
> * Options and Tunables/nspec
> @@ -17,7 +16,6 @@
> */
>
> #pragma D option quiet
> -#pragma D option cleanrate=3000hz
> #pragma D option nspec=2
>
> BEGIN
> @@ -40,7 +38,7 @@ BEGIN
> BEGIN
> /var1 && var2 && (!var3)/
> {
> - printf("Succesfully got two speculative buffers");
> + printf("Successfully got two speculative buffers");
> exit(0);
> }
>
> diff --git a/test/unittest/speculation/tst.TwoSpecBuffers.r b/test/unittest/speculation/tst.TwoSpecBuffers.r
> index 3a35b4673ee4..0c6bd3d47fff 100644
> --- a/test/unittest/speculation/tst.TwoSpecBuffers.r
> +++ b/test/unittest/speculation/tst.TwoSpecBuffers.r
> @@ -1,6 +1,4 @@
> Speculation ID: 1
> Speculation ID: 2
> Speculation ID: 0
> -Succesfully got two speculative buffers
> --- @@stderr --
> -dtrace: 1 failed speculation (no speculative buffer available)
> +Successfully got two speculative buffers
> diff --git a/test/unittest/speculation/tst.negcommit.d b/test/unittest/speculation/tst.negcommit.d
> index cafb0a149bc7..5a680a84f866 100644
> --- a/test/unittest/speculation/tst.negcommit.d
> +++ b/test/unittest/speculation/tst.negcommit.d
> @@ -4,7 +4,6 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> -/* @@xfail: dtv2 */
>
> BEGIN
> {
> diff --git a/test/unittest/speculation/tst.negcommit.r b/test/unittest/speculation/tst.negcommit.r
> index 716afcb52d70..6bb75d1462ba 100644
> --- a/test/unittest/speculation/tst.negcommit.r
> +++ b/test/unittest/speculation/tst.negcommit.r
> @@ -3,4 +3,4 @@
>
> -- @@stderr --
> dtrace: script 'test/unittest/speculation/tst.negcommit.d' matched 2 probes
> -dtrace: error on enabled probe ID 1 (ID 1: dtrace:::BEGIN): illegal operation in action #1
> +dtrace: error on enabled probe ID 2 (ID 1: dtrace:::BEGIN): illegal operation in action #1
> diff --git a/test/unittest/speculation/tst.zerosize.d b/test/unittest/speculation/tst.zerosize.d
> index 74d1ace9e1e3..03d3b8497aa9 100644
> --- a/test/unittest/speculation/tst.zerosize.d
> +++ b/test/unittest/speculation/tst.zerosize.d
> @@ -4,6 +4,7 @@
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> +
> /* @@xfail: dtv2 */
>
> #pragma D option destructive
> --
> 2.33.0.256.gb827f06fa9
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
More information about the DTrace-devel
mailing list