[DTrace-devel] [PATCH 5/6 v3] Implement associative array support
Kris Van Hees
kris.van.hees at oracle.com
Fri Mar 11 16:19:51 UTC 2022
v4 in the works due to a issue with register spilling
On Fri, Mar 11, 2022 at 01:26:30AM -0500, Kris Van Hees via DTrace-devel wrote:
> Associative arrays (global or TLS) are variables that are indexed with
> a tuple (one or more values). The underlying storage is provided by
> the dvar (dynamic variables) BPF hash map. Since dvar elements are
> indexed using a unique 64-bit ID, and given that it provides storage
> for both regular TLS variables and associate array elements, the
> algorithms to calculate the ID are designed to provide orthogonal
> value ranges.
>
> TLS variable: dvar key has high order bit 0
> Associative array element: dvar key has high order bit 1
>
> The dvar key for an associative array element is determined based on the
> tuple content values. A new BPF hash map (tuples) associates the
> concatenation of the tuple values as key with the address of the hash
> map element (which is guaranteed to be unique). Since BPF maps are
> allocated in kernel space, these addresses will always have high order
> bit 1.
>
> To ensure uniqueness between global associate array elements and TLS
> associate array elements, and to ensure uniqueness between elements
> with the same tuple index in different arrays, the key in the tuples
> map is determined based on a rewritten tuple:
>
> [ variable ID, original tuple values, TLS variable key or 0 ]
>
> While variable IDs are not unique between variable kinds, the final
> component in the rewritten tuple is the TLS variable key (for TLS
> associative arrays - never 0) or 0 (for global associative arrays).
>
> Various new tests are added to exercise the new functionality.
>
> Running out of dynamic variable space is not being reported as a drop
> yet due to lack of drop counter support. It generates an error
> instead, and therefore the test for the drop reporting remains XFAIL.
>
> Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
> ---
> bpf/get_dvar.c | 74 ++-
> libdtrace/dt_bpf.c | 19 +-
> libdtrace/dt_bpf.h | 3 +-
> libdtrace/dt_cc.c | 5 +-
> libdtrace/dt_cg.c | 466 ++++++++++++------
> libdtrace/dt_dctx.h | 18 +-
> libdtrace/dt_dlibs.c | 2 +
> libdtrace/dt_ident.c | 9 +-
> libdtrace/dt_impl.h | 1 +
> test/unittest/assocs/err.limited_space.d | 34 ++
> test/unittest/assocs/err.limited_space.r | 6 +
> test/unittest/assocs/err.tupoflow.d | 3 +-
> test/unittest/assocs/err.tupoflow.r | 2 +-
> test/unittest/assocs/tst.clean-tuple.d | 29 ++
> test/unittest/assocs/tst.cpyarray.d | 3 +-
> test/unittest/assocs/tst.gvar-postdec.d | 22 +
> test/unittest/assocs/tst.gvar-postinc.d | 22 +
> test/unittest/assocs/tst.gvar-predec.d | 22 +
> test/unittest/assocs/tst.gvar-preinc.d | 22 +
> test/unittest/assocs/tst.init-str.d | 29 ++
> test/unittest/assocs/tst.init.d | 21 +
> test/unittest/assocs/tst.misc.d | 3 +-
> test/unittest/assocs/tst.nested.d | 29 ++
> test/unittest/assocs/tst.nested.r | 2 +
> test/unittest/assocs/tst.orthogonality.d | 3 +-
> test/unittest/assocs/tst.store_zero_deletes.d | 35 ++
> test/unittest/assocs/tst.store_zero_deletes.r | 1 +
> test/unittest/assocs/tst.tvar-postdec.d | 22 +
> test/unittest/assocs/tst.tvar-postinc.d | 22 +
> test/unittest/assocs/tst.tvar-predec.d | 22 +
> test/unittest/assocs/tst.tvar-preinc.d | 22 +
> 31 files changed, 797 insertions(+), 176 deletions(-)
> create mode 100644 test/unittest/assocs/err.limited_space.d
> create mode 100644 test/unittest/assocs/err.limited_space.r
> create mode 100644 test/unittest/assocs/tst.clean-tuple.d
> create mode 100644 test/unittest/assocs/tst.gvar-postdec.d
> create mode 100644 test/unittest/assocs/tst.gvar-postinc.d
> create mode 100644 test/unittest/assocs/tst.gvar-predec.d
> create mode 100644 test/unittest/assocs/tst.gvar-preinc.d
> create mode 100644 test/unittest/assocs/tst.init-str.d
> create mode 100644 test/unittest/assocs/tst.init.d
> create mode 100644 test/unittest/assocs/tst.nested.d
> create mode 100644 test/unittest/assocs/tst.nested.r
> create mode 100644 test/unittest/assocs/tst.store_zero_deletes.d
> create mode 100644 test/unittest/assocs/tst.store_zero_deletes.r
> create mode 100644 test/unittest/assocs/tst.tvar-postdec.d
> create mode 100644 test/unittest/assocs/tst.tvar-postinc.d
> create mode 100644 test/unittest/assocs/tst.tvar-predec.d
> create mode 100644 test/unittest/assocs/tst.tvar-preinc.d
>
> diff --git a/bpf/get_dvar.c b/bpf/get_dvar.c
> index 14f21783..25ce37f4 100644
> --- a/bpf/get_dvar.c
> +++ b/bpf/get_dvar.c
> @@ -11,8 +11,24 @@
> #endif
>
> extern struct bpf_map_def dvars;
> +extern struct bpf_map_def tuples;
> extern uint64_t NCPUS;
>
> +/*
> + * Dynamic variables are identified using a unique 64-bit key. Three diferent
> + * categories of dynamic variables are supported in DTrace:
> + *
> + * 1. Thread-local storage (TLS) variables:
> + * dvar key = TLS key (highest bit = 0)
> + * 2. Global associative array elements:
> + * dvar key = &tuples[var id, tuple, (uint64_t)0] (highest bit = 1)
> + * 2. TLS associative array elements:
> + * dvar key = &tuples[var id, tuple, TLS key] (highest bit = 1)
> + *
> + * Given that the TLS key can never be 0, uniqueness of the dvar key is
> + * guaranteed in this scheme.
> + */
> +
> noinline uint64_t dt_tlskey(uint32_t id)
> {
> uint64_t key;
> @@ -25,7 +41,7 @@ noinline uint64_t dt_tlskey(uint32_t id)
> key += (uint32_t)(uint64_t)&NCPUS;
>
> key++;
> - key = (key << 32) | id;
> + key = ((key & 0x7fffffff) << 32) | id;
>
> return key;
> }
> @@ -81,3 +97,59 @@ noinline void *dt_get_tvar(uint32_t id, uint64_t store, uint64_t nval)
> {
> return dt_get_dvar(dt_tlskey(id), store, nval);
> }
> +
> +noinline void *dt_get_assoc(uint32_t id, const char *tuple,
> + uint64_t store, uint64_t nval)
> +{
> + uint64_t *valp;
> + uint64_t val;
> + uint64_t dflt_val = 0;
> +
> + /*
> + * Place the variable ID at the beginning of the tuple.
> + */
> + *(uint32_t *)tuple = id;
> +
> + /*
> + * Look for the tuple in the tuples map.
> + */
> + valp = bpf_map_lookup_elem(&tuples, tuple);
> + if (valp == 0) {
> + /*
> + * Not found. If we are not storing a value (i.e. performing a
> + * load), return the default value (0). If we are trying to
> + * delete an associative array element, we don't have to do
> + * anything because it does not exist anyway.
> + */
> + if (!store || !nval)
> + return 0;
> +
> + /*
> + * Create the tuple and use the address of the value as the
> + * actual value.
> + */
> + if (bpf_map_update_elem(&tuples, tuple, &dflt_val, BPF_ANY) < 0)
> + return 0;
> + valp = bpf_map_lookup_elem(&tuples, tuple);
> + if (valp == 0)
> + return 0;
> + *valp = (uint64_t)valp;
> + if (bpf_map_update_elem(&tuples, tuple, valp, BPF_ANY) < 0)
> + return 0;
> +
> + val = *valp;
> + } else {
> + /*
> + * Record the value (used as key into the dvars map), and if we
> + * are storing a zero-value (deleting the element), delete the
> + * tuple. The associated dynamic variable will be delete by
> + * the dt_get_dvar() call.
> + */
> + val = *valp;
> +
> + if (store && !nval)
> + bpf_map_delete_elem(&tuples, tuple);
> + }
> +
> + return dt_get_dvar(val, store, nval);
> +}
> diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
> index 159c4722..ade09b15 100644
> --- a/libdtrace/dt_bpf.c
> +++ b/libdtrace/dt_bpf.c
> @@ -224,6 +224,11 @@ populate_probes_map(dtrace_hdl_t *dtp, int fd)
> * tracing session.
> * - lvars: Local variables map. This is a per-CPU map with a singleton
> * element (key 0) addressed by variable offset.
> + * - tuples: Tuple-to-id map. This is a global hash map indexed with a
> + * tuple. The value associated with the tuple key is an id that
> + * is used to index the dvars map. The key size is determined as
> + * the largest tuple used across all programs in the tracing
> + * session.
> */
> int
> dt_bpf_gmap_create(dtrace_hdl_t *dtp)
> @@ -249,9 +254,10 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
> /* Determine sizes for global, local, and TLS maps. */
> gvarsz = P2ROUNDUP(dt_idhash_datasize(dtp->dt_globals), 8);
> lvarsz = P2ROUNDUP(dtp->dt_maxlvaralloc, 8);
> +
> if (dtp->dt_maxdvarsize)
> - dvarc = (dtp->dt_options[DTRACEOPT_DYNVARSIZE] /
> - dtp->dt_maxdvarsize) + 1;
> + dvarc = dtp->dt_options[DTRACEOPT_DYNVARSIZE] /
> + dtp->dt_maxdvarsize;
>
> /* Create global maps as long as there are no errors. */
> dtp->dt_stmap_fd = create_gmap(dtp, "state", BPF_MAP_TYPE_ARRAY,
> @@ -356,8 +362,10 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
> int fd;
> char dflt[dtp->dt_maxdvarsize];
>
> + /* Allocate one extra element for the default value. */
> fd = create_gmap(dtp, "dvars", BPF_MAP_TYPE_HASH,
> - sizeof(uint64_t), dtp->dt_maxdvarsize, dvarc);
> + sizeof(uint64_t), dtp->dt_maxdvarsize,
> + dvarc + 1);
> if (fd == -1)
> return -1; /* dt_errno is set for us */
>
> @@ -366,6 +374,11 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
> dt_bpf_map_update(fd, &key, &dflt);
> }
>
> + if (dtp->dt_maxtuplesize > 0 &&
> + create_gmap(dtp, "tuples", BPF_MAP_TYPE_HASH,
> + dtp->dt_maxtuplesize, sizeof(uint64_t), dvarc) == -1)
> + return -1; /* dt_errno is set for us */
> +
> /* Populate the 'cpuinfo' map. */
> dt_bpf_map_update(ci_mapfd, &key, dtp->dt_conf.cpus);
>
> diff --git a/libdtrace/dt_bpf.h b/libdtrace/dt_bpf.h
> index 5b9bae80..237546e8 100644
> --- a/libdtrace/dt_bpf.h
> +++ b/libdtrace/dt_bpf.h
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2020, 2022, 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.
> */
> @@ -28,6 +28,7 @@ extern "C" {
> #define DT_CONST_BOOTTM 8
> #define DT_CONST_NSPEC 9
> #define DT_CONST_NCPUS 10
> +#define DT_CONST_TUPSZ 11
>
> extern int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
> int group_fd, unsigned long flags);
> diff --git a/libdtrace/dt_cc.c b/libdtrace/dt_cc.c
> index ce824eb5..49901710 100644
> --- a/libdtrace/dt_cc.c
> +++ b/libdtrace/dt_cc.c
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2008, 2022, 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.
> */
> @@ -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_TUPSZ:
> + nrp->dofr_data = DMEM_TUPLE_SZ(dtp);
> + continue;
> case DT_CONST_NSPEC:
> nrp->dofr_data = dtp->dt_options[DTRACEOPT_NSPEC];
> continue;
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index 1cb06ef7..7ccf63c4 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -2325,6 +2325,225 @@ dt_cg_store(dt_node_t *src, dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *dst)
> }
> }
>
> +/*
> + * Generate code for a typecast or for argument promotion from the type of the
> + * actual to the type of the formal. We need to generate code for casts when
> + * a scalar type is being narrowed or changing signed-ness. We first shift the
> + * desired bits high (losing excess bits if narrowing) and then shift them down
> + * using logical shift (unsigned result) or arithmetic shift (signed result).
> + */
> +static void
> +dt_cg_typecast(const dt_node_t *src, const dt_node_t *dst,
> + dt_irlist_t *dlp, dt_regset_t *drp)
> +{
> + size_t srcsize;
> + size_t dstsize;
> + int n;
> +
> + /* If the destination type is '@' (any type) we need not cast. */
> + if (dst->dn_ctfp == NULL && dst->dn_type == CTF_ERR)
> + return;
> +
> + srcsize = dt_node_type_size(src);
> + dstsize = dt_node_type_size(dst);
> +
> + if (dstsize < srcsize)
> + n = sizeof(uint64_t) * NBBY - dstsize * NBBY;
> + else
> + n = sizeof(uint64_t) * NBBY - srcsize * NBBY;
> +
> + if (dt_node_is_scalar(dst) && n != 0 && (dstsize < srcsize ||
> + (src->dn_flags & DT_NF_SIGNED) ^ (dst->dn_flags & DT_NF_SIGNED))) {
> + emit(dlp, BPF_MOV_REG(dst->dn_reg, src->dn_reg));
> + emit(dlp, BPF_ALU64_IMM(BPF_LSH, dst->dn_reg, n));
> + emit(dlp, BPF_ALU64_IMM((dst->dn_flags & DT_NF_SIGNED) ? BPF_ARSH : BPF_RSH, dst->dn_reg, n));
> + }
> +}
> +
> +/*
> + * Generate code to push the specified argument list on to the tuple stack.
> + * We use this routine for handling the index tuple for associative arrays.
> + * We must first generate code for all subexpressions because any subexpression
> + * could itself require the use of the tuple assembly area and we only provide
> + * one.
> + *
> + * Since the number of tuple components is unknown, we do not want to waste
> + * registers on storing the subexpression values. So, instead, we store the
> + * subexpression values on the stack.
> + *
> + * Once code for all subexpressions has been generated, we assemble the tuple.
> + *
> + * Note that we leave space at the beginning of the tuple for a uint32_t value,
> + * and at the end space for a uint64_t value.
> + */
> +static void
> +dt_cg_arglist(dt_ident_t *idp, dt_node_t *args, dt_irlist_t *dlp,
> + dt_regset_t *drp)
> +{
> + dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> + const dt_idsig_t *isp = idp->di_data;
> + dt_node_t *dnp;
> + dt_ident_t *tupsz = dt_dlib_get_var(dtp, "TUPSZ");
> + int i;
> + int treg, areg;
> + uint_t tuplesize = sizeof(uint32_t);
> + size_t strsize = dtp->dt_options[DTRACEOPT_STRSIZE];
> +
> + TRACE_REGSET(" arglist: Begin");
> +
> + for (dnp = args, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
> + dt_cg_node(dnp, dlp, drp);
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_FP, DT_STK_SP));
> + emit(dlp, BPF_STORE(BPF_DW, BPF_REG_0, 0, dnp->dn_reg));
> + dt_regset_free(drp, dnp->dn_reg);
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, -DT_STK_SLOT_SZ));
> + emit(dlp, BPF_STORE(BPF_DW, BPF_REG_FP, DT_STK_SP, BPF_REG_0));
> + dt_regset_free(drp, BPF_REG_0);
> + }
> +
> + if (i > dtp->dt_conf.dtc_diftupregs)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOTUPREG);
> +
> + TRACE_REGSET(" arglist: Stack");
> +
> + if ((treg = dt_regset_alloc(drp)) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + emit(dlp, BPF_LOAD(BPF_DW, treg, BPF_REG_FP, DT_STK_DCTX));
> + emit(dlp, BPF_LOAD(BPF_DW, treg, treg, DCTX_MEM));
> +
> + /*
> + * We need to clear the tuple assembly area in case the previous tuple
> + * was larger than the one we will construct, because otherwise we end
> + * up with trailing garbage.
> + */
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + emit(dlp, BPF_MOV_REG(BPF_REG_1, treg));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DMEM_TUPLE(dtp)));
> + emite(dlp, BPF_MOV_IMM(BPF_REG_2, -1), tupsz);
> + emit(dlp, BPF_MOV_REG(BPF_REG_3, BPF_REG_1));
> + emite(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -1), tupsz);
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read));
> + dt_regset_free_args(drp);
> + dt_regset_free(drp, BPF_REG_0);
> +
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, treg, DMEM_TUPLE(dtp) + sizeof(uint32_t)));
> +
> + if ((areg = dt_regset_alloc(drp)) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + for (dnp = args, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
> + dtrace_diftype_t t;
> + size_t size;
> +
> + dt_node_diftype(yypcb->pcb_hdl, dnp, &t);
> + size = t.dtdt_size;
> +
> + /* Append the value to the tuple assembly area. */
> + if (t.dtdt_size == 0)
> + continue;
> +
> + emit(dlp, BPF_LOAD(BPF_DW, areg, BPF_REG_FP, DT_STK_SP));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, areg, DT_STK_SLOT_SZ));
> + emit(dlp, BPF_STORE(BPF_DW, BPF_REG_FP, DT_STK_SP, areg));
> + emit(dlp, BPF_LOAD(BPF_DW, areg, areg, 0));
> +
> + isp->dis_args[i].dn_reg = areg;
> + dt_cg_typecast(dnp, &isp->dis_args[i], dlp, drp);
> + isp->dis_args[i].dn_reg = -1;
> +
> + if (dt_node_is_scalar(dnp) || dt_node_is_float(dnp)) {
> + assert(size > 0 && size <= 8 &&
> + (size & (size - 1)) == 0);
> +
> + emit(dlp, BPF_STORE(ldstw[size], treg, 0, areg));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, treg, size));
> +
> + tuplesize += size;
> + } else if (dt_node_is_string(dnp)) {
> + uint_t lbl_valid = dt_irlist_label(dlp);
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + emit(dlp, BPF_MOV_REG(BPF_REG_1, treg));
> + emit(dlp, BPF_MOV_IMM(BPF_REG_2, strsize + 1));
> + emit(dlp, BPF_MOV_REG(BPF_REG_3, areg));
> + dt_cg_tstring_free(yypcb, dnp);
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read_str));
> + dt_regset_free_args(drp);
> + emit(dlp, BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_valid));
> + dt_cg_probe_error(yypcb, -1, DTRACEFLT_BADADDR, 0);
> + emitl(dlp, lbl_valid,
> + BPF_ALU64_REG(BPF_ADD, treg, BPF_REG_0));
> + dt_regset_free(drp, BPF_REG_0);
> +
> + tuplesize += size + 1;
> + } else if (t.dtdt_flags & DIF_TF_BYREF) {
> + uint_t lbl_valid = dt_irlist_label(dlp);
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + emit(dlp, BPF_MOV_REG(BPF_REG_1, treg));
> + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size));
> + emit(dlp, BPF_MOV_REG(BPF_REG_3, areg));
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read));
> + dt_regset_free_args(drp);
> + emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_0, 0, lbl_valid));
> + dt_cg_probe_error(yypcb, -1, DTRACEFLT_BADADDR, 0);
> + emitl(dlp, lbl_valid,
> + BPF_ALU64_IMM(BPF_ADD, treg, size));
> + dt_regset_free(drp, BPF_REG_0);
> +
> + tuplesize += size;
> + } else
> + assert(0); /* We shouldn't be able to get here. */
> + }
> +
> + dt_regset_free(drp, areg);
> +
> + TRACE_REGSET(" arglist: Tuple");
> +
> + /* Add room for an optional TLS key (or 0). */
> + tuplesize += sizeof(uint64_t);
> +
> + if (idp->di_flags & DT_IDFLG_TLS) {
> + dt_ident_t *idp = dt_dlib_get_func(dtp, "dt_tlskey");
> + uint_t varid = idp->di_id - DIF_VAR_OTHER_UBASE;
> +
> + assert(idp != NULL);
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + emit(dlp, BPF_MOV_IMM(BPF_REG_1, varid));
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
> + dt_regset_free_args(drp);
> + emit(dlp, BPF_STORE(BPF_DW, treg, 0, BPF_REG_0));
> + dt_regset_free(drp, BPF_REG_0);
> + } else
> + emit(dlp, BPF_STORE_IMM(BPF_DW, treg, 0, 0));
> +
> + if (tuplesize > dtp->dt_maxtuplesize)
> + dtp->dt_maxtuplesize = tuplesize;
> +
> + emit(dlp, BPF_LOAD(BPF_DW, treg, BPF_REG_FP, DT_STK_DCTX));
> + emit(dlp, BPF_LOAD(BPF_DW, treg, treg, DCTX_MEM));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, treg, DMEM_TUPLE(dtp)));
> +
> + args->dn_reg = treg;
> +
> + TRACE_REGSET(" arglist: End ");
> +}
> +
> /*
> * dnp = node of the assignment
> * dn_left = identifier node for the destination (idp = identifier)
> @@ -2340,6 +2559,63 @@ dt_cg_store_var(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
>
> idp->di_flags |= DT_IDFLG_DIFW;
>
> + /* Associative (global or TLS) array */
> + if (idp->di_kind == DT_IDENT_ARRAY) {
> + dt_cg_arglist(idp, dnp->dn_left->dn_args, dlp, drp);
> +
> + varid = idp->di_id - DIF_VAR_OTHER_UBASE;
> + size = idp->di_size;
> + idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_assoc");
> + assert(idp != NULL);
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + emit(dlp, BPF_MOV_IMM(BPF_REG_1, varid));
> + emit(dlp, BPF_MOV_REG(BPF_REG_2, dnp->dn_left->dn_args->dn_reg));
> + dt_regset_free(drp, dnp->dn_left->dn_args->dn_reg);
> + emit(dlp, BPF_MOV_IMM(BPF_REG_3, 1));
> + emit(dlp, BPF_MOV_REG(BPF_REG_4, dnp->dn_reg));
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
> + dt_regset_free_args(drp);
> + lbl_done = dt_irlist_label(dlp);
> + emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_reg, 0, lbl_done));
> +
> + if ((reg = dt_regset_alloc(drp)) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + emit(dlp, BPF_MOV_REG(reg, BPF_REG_0));
> + dt_regset_free(drp, BPF_REG_0);
> +
> + dt_cg_check_notnull(dlp, drp, reg);
> +
> + if (dnp->dn_flags & DT_NF_REF) {
> + size_t srcsz;
> +
> + /*
> + * Determine the amount of data to be copied. It is
> + * the lesser of the size of the identifier and the
> + * size of the data being copied in.
> + */
> + srcsz = dt_node_type_size(dnp->dn_right);
> + size = MIN(srcsz, size);
> +
> + dt_cg_memcpy(dlp, drp, reg, dnp->dn_reg, size);
> + } else {
> + assert(size > 0 && size <= 8 &&
> + (size & (size - 1)) == 0);
> +
> + emit(dlp, BPF_STORE(ldstw[size], reg, 0, dnp->dn_reg));
> + }
> +
> + dt_regset_free(drp, reg);
> +
> + emitl(dlp, lbl_done,
> + BPF_NOP());
> + return;
> + }
> +
> /* global and local variables (that is, not thread-local) */
> if (!(idp->di_flags & DT_IDFLG_TLS)) {
> if ((reg = dt_regset_alloc(drp)) == -1)
> @@ -2431,103 +2707,6 @@ dt_cg_store_var(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
> BPF_NOP());
> }
>
> -/*
> - * Generate code for a typecast or for argument promotion from the type of the
> - * actual to the type of the formal. We need to generate code for casts when
> - * a scalar type is being narrowed or changing signed-ness. We first shift the
> - * desired bits high (losing excess bits if narrowing) and then shift them down
> - * using logical shift (unsigned result) or arithmetic shift (signed result).
> - */
> -static void
> -dt_cg_typecast(const dt_node_t *src, const dt_node_t *dst,
> - dt_irlist_t *dlp, dt_regset_t *drp)
> -{
> - size_t srcsize;
> - size_t dstsize;
> - int n;
> -
> - /* If the destination type is '@' (any type) we need not cast. */
> - if (dst->dn_ctfp == NULL && dst->dn_type == CTF_ERR)
> - return;
> -
> - srcsize = dt_node_type_size(src);
> - dstsize = dt_node_type_size(dst);
> -
> - if (dstsize < srcsize)
> - n = sizeof(uint64_t) * NBBY - dstsize * NBBY;
> - else
> - n = sizeof(uint64_t) * NBBY - srcsize * NBBY;
> -
> - if (dt_node_is_scalar(dst) && n != 0 && (dstsize < srcsize ||
> - (src->dn_flags & DT_NF_SIGNED) ^ (dst->dn_flags & DT_NF_SIGNED))) {
> - emit(dlp, BPF_MOV_REG(dst->dn_reg, src->dn_reg));
> - emit(dlp, BPF_ALU64_IMM(BPF_LSH, dst->dn_reg, n));
> - emit(dlp, BPF_ALU64_IMM((dst->dn_flags & DT_NF_SIGNED) ? BPF_ARSH : BPF_RSH, dst->dn_reg, n));
> - }
> -}
> -
> -/*
> - * Generate code to push the specified argument list on to the tuple stack.
> - * We use this routine for handling subroutine calls and associative arrays.
> - * We must first generate code for all subexpressions before loading the stack
> - * because any subexpression could itself require the use of the tuple stack.
> - * This holds a number of registers equal to the number of arguments, but this
> - * is not a huge problem because the number of arguments can't exceed the
> - * number of tuple register stack elements anyway. At most one extra register
> - * is required (either by dt_cg_typecast() or for dtdt_size, below). This
> - * implies that a DIF implementation should offer a number of general purpose
> - * registers at least one greater than the number of tuple registers.
> - */
> -static void
> -dt_cg_arglist(dt_ident_t *idp, dt_node_t *args,
> - dt_irlist_t *dlp, dt_regset_t *drp)
> -{
> - const dt_idsig_t *isp = idp->di_data;
> - dt_node_t *dnp;
> - int i = 0;
> -
> - for (dnp = args; dnp != NULL; dnp = dnp->dn_list)
> - dt_cg_node(dnp, dlp, drp);
> -
> - for (dnp = args; dnp != NULL; dnp = dnp->dn_list, i++) {
> - dtrace_diftype_t t;
> - uint_t op;
> - int reg;
> -
> - dt_node_diftype(yypcb->pcb_hdl, dnp, &t);
> -
> - isp->dis_args[i].dn_reg = dnp->dn_reg; /* re-use register */
> - dt_cg_typecast(dnp, &isp->dis_args[i], dlp, drp);
> - isp->dis_args[i].dn_reg = -1;
> -
> - if (t.dtdt_flags & DIF_TF_BYREF)
> - op = DIF_OP_PUSHTR;
> - else
> - op = DIF_OP_PUSHTV;
> -
> - if (t.dtdt_size != 0) {
> - if ((reg = dt_regset_alloc(drp)) == -1)
> - longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> - dt_cg_setx(dlp, reg, t.dtdt_size);
> - } else
> - reg = DIF_REG_R0;
> -
> -#if 0
> - instr = DIF_INSTR_PUSHTS(op, t.dtdt_kind, reg, dnp->dn_reg);
> - dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
> -#else
> - emit(dlp, BPF_CALL_FUNC(op));
> -#endif
> - dt_regset_free(drp, dnp->dn_reg);
> -
> - if (reg != DIF_REG_R0)
> - dt_regset_free(drp, reg);
> - }
> -
> - if (i > yypcb->pcb_hdl->dt_conf.dtc_diftupregs)
> - longjmp(yypcb->pcb_jmpbuf, EDT_NOTUPREG);
> -}
> -
> static void
> dt_cg_arithmetic_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
> uint_t op)
> @@ -3144,9 +3323,6 @@ dt_cg_asgn_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> if (dnp->dn_left->dn_kind == DT_NODE_VAR) {
> idp = dt_ident_resolve(dnp->dn_left->dn_ident);
>
> - if (idp->di_kind == DT_IDENT_ARRAY)
> - dt_cg_arglist(idp, dnp->dn_left->dn_args, dlp, drp);
> -
> dt_cg_store_var(dnp, dlp, drp, idp);
>
> /*
> @@ -3174,75 +3350,59 @@ dt_cg_asgn_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> static void
> dt_cg_assoc_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> {
> - ssize_t base;
> + dt_ident_t *idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_assoc");
> + uint_t varid;
>
> + TRACE_REGSET(" assoc_op: Begin");
> +
> + assert(idp != NULL);
> assert(dnp->dn_kind == DT_NODE_VAR);
> assert(!(dnp->dn_ident->di_flags & DT_IDFLG_LOCAL));
> assert(dnp->dn_args != NULL);
>
> + dnp->dn_ident->di_flags |= DT_IDFLG_DIFR;
> +
> dt_cg_arglist(dnp->dn_ident, dnp->dn_args, dlp, drp);
>
> if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
> longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
>
> - if (dnp->dn_ident->di_flags & DT_IDFLG_TLS)
> - base = 0x2000;
> - else
> - base = 0x3000;
> + varid = dnp->dn_ident->di_id - DIF_VAR_OTHER_UBASE;
>
> - dnp->dn_ident->di_flags |= DT_IDFLG_DIFR;
> - emit(dlp, BPF_LOAD(BPF_DW, dnp->dn_reg, BPF_REG_FP, base + dnp->dn_ident->di_id));
> + emit(dlp, BPF_MOV_IMM(BPF_REG_1, varid));
> + emit(dlp, BPF_MOV_REG(BPF_REG_2, dnp->dn_args->dn_reg));
> + dt_regset_free(drp, dnp->dn_args->dn_reg);
> + emit(dlp, BPF_MOV_IMM(BPF_REG_3, 0));
> + emit(dlp, BPF_MOV_IMM(BPF_REG_4, 0));
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
> + dt_regset_free_args(drp);
>
> - /*
> - * If the associative array is a pass-by-reference type, then we are
> - * loading its value as a pointer to either load or store through it.
> - * The array element in question may not have been faulted in yet, in
> - * which case DIF_OP_LD*AA will return zero. We append an epilogue
> - * of instructions similar to the following:
> - *
> - * ld?aa id, %r1 ! base ld?aa instruction above
> - * tst %r1 ! start of epilogue
> - * +--- bne label
> - * | setx size, %r1
> - * | allocs %r1, %r1
> - * | st?aa id, %r1
> - * | ld?aa id, %r1
> - * v
> - * label: < rest of code >
> - *
> - * The idea is that we allocs a zero-filled chunk of scratch space and
> - * do a DIF_OP_ST*AA to fault in and initialize the array element, and
> - * then reload it to get the faulted-in address of the new variable
> - * storage. This isn't cheap, but pass-by-ref associative array values
> - * are (thus far) uncommon and the allocs cost only occurs once. If
> - * this path becomes important to DTrace users, we can improve things
> - * by adding a new DIF opcode to fault in associative array elements.
> - */
> if (dnp->dn_flags & DT_NF_REF) {
> -#ifdef FIXME
> - uint_t stvop = op == DIF_OP_LDTAA ? DIF_OP_STTAA : DIF_OP_STGAA;
> - uint_t label = dt_irlist_label(dlp);
> -
> - emit(dlp, BPF_BRANCH_IMM(BPF_JNE, dnp->dn_reg, 0, label));
> -
> - dt_cg_setx(dlp, dnp->dn_reg, dt_node_type_size(dnp));
> - instr = DIF_INSTR_ALLOCS(dnp->dn_reg, dnp->dn_reg);
> - dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
> -
> - dnp->dn_ident->di_flags |= DT_IDFLG_DIFW;
> - instr = DIF_INSTR_STV(stvop, dnp->dn_ident->di_id, dnp->dn_reg);
> - dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
> -
> - instr = DIF_INSTR_LDV(op, dnp->dn_ident->di_id, dnp->dn_reg);
> - dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
> + emit(dlp, BPF_MOV_REG(dnp->dn_reg, BPF_REG_0));
> + dt_regset_free(drp, BPF_REG_0);
> + } else {
> + size_t size = dt_node_type_size(dnp);
> + uint_t lbl_notnull = dt_irlist_label(dlp);
> + uint_t lbl_done = dt_irlist_label(dlp);
> +
> + assert(size > 0 && size <= 8 &&
> + (size & (size - 1)) == 0);
> +
> + emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, 0, lbl_notnull));
> + emit(dlp, BPF_MOV_IMM(dnp->dn_reg, 0));
> + emit(dlp, BPF_JUMP(lbl_done));
> + emitl(dlp, lbl_notnull,
> + BPF_LOAD(ldstw[size], dnp->dn_reg, BPF_REG_0, 0));
> + dt_regset_free(drp, BPF_REG_0);
>
> - emitl(dlp, label,
> + emitl(dlp, lbl_done,
> BPF_NOP());
> -#else
> - xyerror(D_UNKNOWN, "internal error -- no support for "
> - "associative arrays yet\n");
> -#endif
> }
> +
> + TRACE_REGSET(" assoc_op: End ");
> }
>
> static void
> diff --git a/libdtrace/dt_dctx.h b/libdtrace/dt_dctx.h
> index d45720d7..dc2f8e58 100644
> --- a/libdtrace/dt_dctx.h
> +++ b/libdtrace/dt_dctx.h
> @@ -72,9 +72,13 @@ typedef struct dt_dctx {
> * +----------------+----------------+
> * 0 -> | Stack : tstring | \
> * | trace (shared) storage | |
> - * | storage : | > DMEM_SIZE
> + * | storage : | |
> * +----------------+----------------+ |
> - * DMEM_STRTOK -> | strtok() internal state | /
> + * DMEM_STRTOK -> | strtok() internal state | > DMEM_SIZE
> + * +---------------------------------+ |
> + * DMEM_TUPLE -> | tuple assembly area | |
> + * +---------------------------------+ |
> + * DMEM_TUPLE_DFLT -> | default empty tuple | /
> * +---------------------------------+
> */
>
> @@ -88,17 +92,23 @@ typedef struct dt_dctx {
> P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 1, 8))
> #define DMEM_STRTOK_SZ(dtp) \
> (sizeof(uint64_t) + (dtp)->dt_options[DTRACEOPT_STRSIZE] + 1)
> +#define DMEM_TUPLE_SZ(dtp) \
> + ((dtp)->dt_maxtuplesize)
>
> /*
> - * Macro to determine the offset from mem to the strtok internal state.
> + * Macros to determine the offset of the components of dctx->mem.
> */
> #define DMEM_STRTOK(dtp) \
> MAX(DMEM_STACK_SZ(dtp), DMEM_TSTR_SZ(dtp))
> +#define DMEM_TUPLE(dtp) \
> + (DMEM_STRTOK(dtp) + DMEM_STRTOK_SZ(dtp))
> +#define DMEM_TUPLE_DFLT(dtp) \
> + (DMEM_TUPLE(dtp) + DMEM_TUPLE_SZ(dtp))
>
> /*
> * Macro to determine the total size of the mem area.
> */
> -#define DMEM_SIZE(dtp) (DMEM_STRTOK(dtp) + DMEM_STRTOK_SZ(dtp))
> +#define DMEM_SIZE(dtp) (DMEM_TUPLE_DFLT(dtp) + DMEM_TUPLE_SZ(dtp))
>
> /*
> * The stack layout for BPF programs that are generated as trampolines for
> diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
> index ebed0509..329a951a 100644
> --- a/libdtrace/dt_dlibs.c
> +++ b/libdtrace/dt_dlibs.c
> @@ -67,6 +67,7 @@ static const dt_ident_t dt_bpf_symbols[] = {
> DT_BPF_SYMBOL(specs, DT_IDENT_PTR),
> DT_BPF_SYMBOL(state, DT_IDENT_PTR),
> DT_BPF_SYMBOL(strtab, DT_IDENT_PTR),
> + DT_BPF_SYMBOL(tuples, DT_IDENT_PTR),
>
> /* BPF internal identifiers */
> DT_BPF_SYMBOL_ID(EPID, DT_IDENT_SCALAR, DT_CONST_EPID),
> @@ -79,6 +80,7 @@ static const dt_ident_t dt_bpf_symbols[] = {
> DT_BPF_SYMBOL_ID(BOOTTM, DT_IDENT_SCALAR, DT_CONST_BOOTTM),
> DT_BPF_SYMBOL_ID(NSPEC, DT_IDENT_SCALAR, DT_CONST_NSPEC),
> DT_BPF_SYMBOL_ID(NCPUS, DT_IDENT_SCALAR, DT_CONST_NCPUS),
> + DT_BPF_SYMBOL_ID(TUPSZ, DT_IDENT_SCALAR, DT_CONST_TUPSZ),
>
> /* End-of-list marker */
> { NULL, }
> diff --git a/libdtrace/dt_ident.c b/libdtrace/dt_ident.c
> index 6d8a8efa..098f1fcb 100644
> --- a/libdtrace/dt_ident.c
> +++ b/libdtrace/dt_ident.c
> @@ -1016,13 +1016,14 @@ dt_ident_set_storage(dt_ident_t *idp, uint_t alignment, uint_t size)
> idp->di_offset = (dhp->dh_nextoff + (alignment - 1)) &
> ~(alignment - 1);
> dhp->dh_nextoff = idp->di_offset + size;
> - } else {
> + } else
> idp->di_offset = 0;
> - if (size > dtp->dt_maxdvarsize)
> - dtp->dt_maxdvarsize = size;
> - }
>
> idp->di_size = size;
> +
> + if ((idp->di_kind == DT_IDENT_ARRAY ||
> + (idp->di_flags & DT_IDFLG_TLS)) && (size > dtp->dt_maxdvarsize))
> + dtp->dt_maxdvarsize = size;
> }
>
> void
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index f738ebeb..747815ac 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -293,6 +293,7 @@ struct dtrace_hdl {
> uint_t dt_strlen; /* global string table (runtime) size */
> uint_t dt_maxreclen; /* largest record size across programs */
> uint_t dt_maxdvarsize; /* largest dynamic variable across programs */
> + uint_t dt_maxtuplesize; /* largest tuple across programs */
> uint_t dt_maxlvaralloc; /* largest lvar alloc across pcbs */
> dt_tstring_t *dt_tstrings; /* temporary string slots */
> dt_list_t dt_modlist; /* linked list of dt_module_t's */
> diff --git a/test/unittest/assocs/err.limited_space.d b/test/unittest/assocs/err.limited_space.d
> new file mode 100644
> index 00000000..024dd42a
> --- /dev/null
> +++ b/test/unittest/assocs/err.limited_space.d
> @@ -0,0 +1,34 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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 - no drops yet */
> +
> +/*
> + * ASSERTION: Trying to store more associative array elements than we have
> + * space for will trigger a dynamic variable drop.
> + *
> + * SECTION: Variables/Thread-Local Variables
> + */
> +
> +#pragma D option dynvarsize=15
> +
> +BEGIN
> +{
> + a[1] = 1;
> + a[2] = 2;
> + a[3] = 3;
> + a[4] = 4;
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/assocs/err.limited_space.r b/test/unittest/assocs/err.limited_space.r
> new file mode 100644
> index 00000000..41a7c38a
> --- /dev/null
> +++ b/test/unittest/assocs/err.limited_space.r
> @@ -0,0 +1,6 @@
> + FUNCTION:NAME
> + :BEGIN
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/variables/tvar/err.limited_space.d' matched 3 probes
> +dtrace: 1 dynamic variable drop
> diff --git a/test/unittest/assocs/err.tupoflow.d b/test/unittest/assocs/err.tupoflow.d
> index 25e0ddcd..5ca1753b 100644
> --- a/test/unittest/assocs/err.tupoflow.d
> +++ b/test/unittest/assocs/err.tupoflow.d
> @@ -1,10 +1,9 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2006, 2022, 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:
> diff --git a/test/unittest/assocs/err.tupoflow.r b/test/unittest/assocs/err.tupoflow.r
> index 4421bae1..ba73a4d2 100644
> --- a/test/unittest/assocs/err.tupoflow.r
> +++ b/test/unittest/assocs/err.tupoflow.r
> @@ -1,2 +1,2 @@
> -- @@stderr --
> -dtrace: failed to compile script test/unittest/assocs/err.tupoflow.d: Insufficient registers to generate code
> +dtrace: failed to compile script test/unittest/assocs/err.tupoflow.d: Insufficient tuple registers to generate code
> diff --git a/test/unittest/assocs/tst.clean-tuple.d b/test/unittest/assocs/tst.clean-tuple.d
> new file mode 100644
> index 00000000..a5b20204
> --- /dev/null
> +++ b/test/unittest/assocs/tst.clean-tuple.d
> @@ -0,0 +1,29 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Tuple assembly does not contain garbage from previous uses
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN {
> + x["foo"] = 1234;
> + x["long key her"] = 56789;
> +
> + trace(x["foo"]);
> + trace(x["long key her"]);
> +
> + exit(x["foo"] != 1234 || x["long key her"] != 56789);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/assocs/tst.cpyarray.d b/test/unittest/assocs/tst.cpyarray.d
> index 9e4871bc..367428c6 100644
> --- a/test/unittest/assocs/tst.cpyarray.d
> +++ b/test/unittest/assocs/tst.cpyarray.d
> @@ -1,10 +1,9 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2006, 2022, 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:
> diff --git a/test/unittest/assocs/tst.gvar-postdec.d b/test/unittest/assocs/tst.gvar-postdec.d
> new file mode 100644
> index 00000000..17ed5edb
> --- /dev/null
> +++ b/test/unittest/assocs/tst.gvar-postdec.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Post-decrement should work for global associative arrays.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + old = a["a"] = 42;
> + val = a["a"]--;
> +
> + exit(val != old || a["a"] != old - 1);
> +}
> diff --git a/test/unittest/assocs/tst.gvar-postinc.d b/test/unittest/assocs/tst.gvar-postinc.d
> new file mode 100644
> index 00000000..69fb1047
> --- /dev/null
> +++ b/test/unittest/assocs/tst.gvar-postinc.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Post-increment should work for global associative arrays.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + old = a["a"] = 42;
> + val = a["a"]++;
> +
> + exit(val != old || a["a"] != old + 1);
> +}
> diff --git a/test/unittest/assocs/tst.gvar-predec.d b/test/unittest/assocs/tst.gvar-predec.d
> new file mode 100644
> index 00000000..36b6917d
> --- /dev/null
> +++ b/test/unittest/assocs/tst.gvar-predec.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Pre-decrement should work for global associative arrays.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + old = a["a"] = 42;
> + val = --a["a"];
> +
> + exit(val != old - 1);
> +}
> diff --git a/test/unittest/assocs/tst.gvar-preinc.d b/test/unittest/assocs/tst.gvar-preinc.d
> new file mode 100644
> index 00000000..ad0d8b9d
> --- /dev/null
> +++ b/test/unittest/assocs/tst.gvar-preinc.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Pre-increment should work for global associative arrays.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + old = a["a"] = 42;
> + val = ++a["a"];
> +
> + exit(val != old + 1);
> +}
> diff --git a/test/unittest/assocs/tst.init-str.d b/test/unittest/assocs/tst.init-str.d
> new file mode 100644
> index 00000000..2894fd68
> --- /dev/null
> +++ b/test/unittest/assocs/tst.init-str.d
> @@ -0,0 +1,29 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Default value of unassigned elements in asssociative string
> + * arrays is 0. This will cause an 'invalid address 0x0' fault
> + * upon dereferencing.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +string s[int];
> +
> +BEGIN
> +{
> + trace(s[1]);
> + exit(1);
> +}
> +
> +ERROR
> +{
> + exit(arg4 != 1 || arg5 != 0);
> +}
> diff --git a/test/unittest/assocs/tst.init.d b/test/unittest/assocs/tst.init.d
> new file mode 100644
> index 00000000..567898b6
> --- /dev/null
> +++ b/test/unittest/assocs/tst.init.d
> @@ -0,0 +1,21 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Default value of unassigned elements in asssociative arrays is 0
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + a["a"] = 42;
> +
> + exit(a["b"] != 0);
> +}
> diff --git a/test/unittest/assocs/tst.misc.d b/test/unittest/assocs/tst.misc.d
> index eec35719..ed5498a5 100644
> --- a/test/unittest/assocs/tst.misc.d
> +++ b/test/unittest/assocs/tst.misc.d
> @@ -1,10 +1,9 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2006, 2022, 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:
> diff --git a/test/unittest/assocs/tst.nested.d b/test/unittest/assocs/tst.nested.d
> new file mode 100644
> index 00000000..0e491460
> --- /dev/null
> +++ b/test/unittest/assocs/tst.nested.d
> @@ -0,0 +1,29 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Nested tuple assembly works correctly.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + a[1, 2, 3] = 1234;
> + a[3, 2, 3] = 4321;
> + a[3, 2, 1] = 2;
> + printf("%d\n", a[1, a[3, 2, 1], 3]);
> +
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/assocs/tst.nested.r b/test/unittest/assocs/tst.nested.r
> new file mode 100644
> index 00000000..2286d4f0
> --- /dev/null
> +++ b/test/unittest/assocs/tst.nested.r
> @@ -0,0 +1,2 @@
> +1234
> +
> diff --git a/test/unittest/assocs/tst.orthogonality.d b/test/unittest/assocs/tst.orthogonality.d
> index 0ae50d08..b87211ea 100644
> --- a/test/unittest/assocs/tst.orthogonality.d
> +++ b/test/unittest/assocs/tst.orthogonality.d
> @@ -1,10 +1,9 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2006, 2022, 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 */
>
> /*
> * This test confirms the orthogonality of associative arrays and thread-local
> diff --git a/test/unittest/assocs/tst.store_zero_deletes.d b/test/unittest/assocs/tst.store_zero_deletes.d
> new file mode 100644
> index 00000000..2db9c6c0
> --- /dev/null
> +++ b/test/unittest/assocs/tst.store_zero_deletes.d
> @@ -0,0 +1,35 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Storing 0 into an associate array element removes it from
> + * storage, making room for another element.
> + *
> + * SECTION: Variables/Thread-Local Variables
> + */
> +
> +#pragma D option quiet
> +#pragma D option dynvarsize=15
> +
> +BEGIN
> +{
> + a[1] = 1;
> + a[2] = 2;
> + a[3] = 3;
> + a[1] = 0;
> + a[4] = 4;
> + trace(a[1]);
> + trace(a[2]);
> + trace(a[3]);
> + trace(a[4]);
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/assocs/tst.store_zero_deletes.r b/test/unittest/assocs/tst.store_zero_deletes.r
> new file mode 100644
> index 00000000..0f6f3a7d
> --- /dev/null
> +++ b/test/unittest/assocs/tst.store_zero_deletes.r
> @@ -0,0 +1 @@
> +0234
> diff --git a/test/unittest/assocs/tst.tvar-postdec.d b/test/unittest/assocs/tst.tvar-postdec.d
> new file mode 100644
> index 00000000..a8f6bf7f
> --- /dev/null
> +++ b/test/unittest/assocs/tst.tvar-postdec.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Post-decrement should work for TLS associative arrays.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + old = self->a["a"] = 42;
> + val = self->a["a"]--;
> +
> + exit(val != old || self->a["a"] != old - 1);
> +}
> diff --git a/test/unittest/assocs/tst.tvar-postinc.d b/test/unittest/assocs/tst.tvar-postinc.d
> new file mode 100644
> index 00000000..e9a50763
> --- /dev/null
> +++ b/test/unittest/assocs/tst.tvar-postinc.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Post-increment should work for TLS associative arrays.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + old = self->a["a"] = 42;
> + val = self->a["a"]++;
> +
> + exit(val != old || self->a["a"] != old + 1);
> +}
> diff --git a/test/unittest/assocs/tst.tvar-predec.d b/test/unittest/assocs/tst.tvar-predec.d
> new file mode 100644
> index 00000000..8b85b990
> --- /dev/null
> +++ b/test/unittest/assocs/tst.tvar-predec.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Pre-decrement should work for TLS associative arrays.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + old = self->a["a"] = 42;
> + val = --self->a["a"];
> +
> + exit(val != old - 1);
> +}
> diff --git a/test/unittest/assocs/tst.tvar-preinc.d b/test/unittest/assocs/tst.tvar-preinc.d
> new file mode 100644
> index 00000000..ddea5609
> --- /dev/null
> +++ b/test/unittest/assocs/tst.tvar-preinc.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2022, 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: Pre-increment should work for TLS associative arrays.
> + *
> + * SECTION: Variables/Associative Arrays
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + old = self->a["a"] = 42;
> + val = ++self->a["a"];
> +
> + exit(val != old + 1);
> +}
> --
> 2.34.1
>
>
> _______________________________________________
> 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