[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