[DTrace-devel] [PATCH v4] Implement TLS variables

Kris Van Hees kris.van.hees at oracle.com
Tue Nov 30 06:32:17 UTC 2021


Thread-local storage (TLS) variables are a form of dynamic variables in
DTrace.  They are implemented using a global BPF hash map, indexed using
a key value that is derived from the task ID and the variable ID (with
some special magic to handle the idle task that has TID 0 on all CPUs).

Access to the TLS variables is handled through a pre-compiled BPF
function : dt_get_tvar.  It returns the address of the storage location
for the given variable in the current task.  This is used for both load
and store operations.

The dvars BPF map contains an element at key 0 that contains a value of
all zeros.  This is used to initialize new TLS variables.

One test (variables/tvar/err.limited_space.d) is marked XFAIL because
there is no support for reporting drops just yet.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 bpf/Build                                     |  2 +-
 bpf/get_tvar.c                                | 45 +++++++--
 bpf/map_tvar.c                                | 14 ---
 bpf/set_tvar.c                                | 18 ----
 libdtrace/dt_bpf.c                            | 52 +++++-----
 libdtrace/dt_bpf.h                            |  1 +
 libdtrace/dt_cc.c                             |  3 +
 libdtrace/dt_cg.c                             | 97 ++++++++++++++++---
 libdtrace/dt_dis.c                            | 27 +-----
 libdtrace/dt_dlibs.c                          |  5 +-
 libdtrace/dt_ident.c                          | 13 ++-
 libdtrace/dt_impl.h                           |  2 +-
 libdtrace/dt_open.c                           |  5 +-
 libdtrace/dt_subr.c                           | 10 --
 .../tvar/err.D_OP_INCOMPAT.default_int.d      | 22 +++++
 .../variables/tvar/err.limited_space.d        | 35 +++++++
 .../variables/tvar/err.limited_space.r        |  6 ++
 .../variables/tvar/tst.alignment-array.r      |  2 +
 .../variables/tvar/tst.alignment-array.sh     | 52 ++++++++++
 .../variables/tvar/tst.alignment-char.r       |  2 +
 .../variables/tvar/tst.alignment-char.sh      | 52 ++++++++++
 .../variables/tvar/tst.alignment-int.r        |  2 +
 .../variables/tvar/tst.alignment-int.sh       | 52 ++++++++++
 .../variables/tvar/tst.alignment-long.r       |  2 +
 .../variables/tvar/tst.alignment-long.sh      | 52 ++++++++++
 .../variables/tvar/tst.alignment-ptr.r        |  2 +
 .../variables/tvar/tst.alignment-ptr.sh       | 52 ++++++++++
 .../variables/tvar/tst.alignment-short.r      |  2 +
 .../variables/tvar/tst.alignment-short.sh     | 52 ++++++++++
 .../variables/tvar/tst.alignment-struct-2.r   |  2 +
 .../variables/tvar/tst.alignment-struct-2.sh  | 52 ++++++++++
 .../variables/tvar/tst.alignment-struct.r     |  2 +
 .../variables/tvar/tst.alignment-struct.sh    | 52 ++++++++++
 .../unittest/variables/tvar/tst.default_int.d | 21 ++++
 test/unittest/variables/tvar/tst.init.d       | 22 +++++
 .../variables/tvar/tst.load_before_store.d    | 19 ++++
 .../variables/tvar/tst.post_inc_lvar.d        | 19 ++++
 .../variables/tvar/tst.post_inc_lvar.r        |  5 +
 .../variables/tvar/tst.pre_inc_lvar.d         | 19 ++++
 .../variables/tvar/tst.pre_inc_lvar.r         |  5 +
 test/unittest/variables/tvar/tst.store-char.d | 28 ++++++
 test/unittest/variables/tvar/tst.store-char.r |  1 +
 .../variables/tvar/tst.store_zero_deletes.d   | 36 +++++++
 .../variables/tvar/tst.store_zero_deletes.r   |  1 +
 test/unittest/variables/tvar/tst.str-size.d   | 31 ++++++
 test/unittest/variables/tvar/tst.str-size.r   |  1 +
 test/unittest/variables/tvar/tst.struct.d     | 29 ++++++
 test/unittest/variables/tvar/tst.struct.r     |  5 +
 48 files changed, 907 insertions(+), 124 deletions(-)
 delete mode 100644 bpf/map_tvar.c
 delete mode 100644 bpf/set_tvar.c
 create mode 100644 test/unittest/variables/tvar/err.D_OP_INCOMPAT.default_int.d
 create mode 100644 test/unittest/variables/tvar/err.limited_space.d
 create mode 100644 test/unittest/variables/tvar/err.limited_space.r
 create mode 100644 test/unittest/variables/tvar/tst.alignment-array.r
 create mode 100755 test/unittest/variables/tvar/tst.alignment-array.sh
 create mode 100644 test/unittest/variables/tvar/tst.alignment-char.r
 create mode 100755 test/unittest/variables/tvar/tst.alignment-char.sh
 create mode 100644 test/unittest/variables/tvar/tst.alignment-int.r
 create mode 100755 test/unittest/variables/tvar/tst.alignment-int.sh
 create mode 100644 test/unittest/variables/tvar/tst.alignment-long.r
 create mode 100755 test/unittest/variables/tvar/tst.alignment-long.sh
 create mode 100644 test/unittest/variables/tvar/tst.alignment-ptr.r
 create mode 100755 test/unittest/variables/tvar/tst.alignment-ptr.sh
 create mode 100644 test/unittest/variables/tvar/tst.alignment-short.r
 create mode 100755 test/unittest/variables/tvar/tst.alignment-short.sh
 create mode 100644 test/unittest/variables/tvar/tst.alignment-struct-2.r
 create mode 100755 test/unittest/variables/tvar/tst.alignment-struct-2.sh
 create mode 100644 test/unittest/variables/tvar/tst.alignment-struct.r
 create mode 100755 test/unittest/variables/tvar/tst.alignment-struct.sh
 create mode 100644 test/unittest/variables/tvar/tst.default_int.d
 create mode 100644 test/unittest/variables/tvar/tst.init.d
 create mode 100644 test/unittest/variables/tvar/tst.load_before_store.d
 create mode 100644 test/unittest/variables/tvar/tst.post_inc_lvar.d
 create mode 100644 test/unittest/variables/tvar/tst.post_inc_lvar.r
 create mode 100644 test/unittest/variables/tvar/tst.pre_inc_lvar.d
 create mode 100644 test/unittest/variables/tvar/tst.pre_inc_lvar.r
 create mode 100644 test/unittest/variables/tvar/tst.store-char.d
 create mode 100644 test/unittest/variables/tvar/tst.store-char.r
 create mode 100644 test/unittest/variables/tvar/tst.store_zero_deletes.d
 create mode 100644 test/unittest/variables/tvar/tst.store_zero_deletes.r
 create mode 100644 test/unittest/variables/tvar/tst.str-size.d
 create mode 100644 test/unittest/variables/tvar/tst.str-size.r
 create mode 100644 test/unittest/variables/tvar/tst.struct.d
 create mode 100644 test/unittest/variables/tvar/tst.struct.r

diff --git a/bpf/Build b/bpf/Build
index 5c1bc235..2b17704b 100644
--- a/bpf/Build
+++ b/bpf/Build
@@ -24,7 +24,7 @@ bpf_dlib_SRCDEPS = $(objdir)/include/.dir.stamp
 bpf_dlib_SOURCES = \
 	agg_lqbin.c agg_qbin.c \
 	get_bvar.c \
-	get_tvar.c set_tvar.c \
+	get_tvar.c \
 	index.S \
 	lltostr.S \
 	probe_error.c \
diff --git a/bpf/get_tvar.c b/bpf/get_tvar.c
index 6c8c1d2a..0bc88d40 100644
--- a/bpf/get_tvar.c
+++ b/bpf/get_tvar.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
  */
 #include <linux/bpf.h>
 #include <stdint.h>
@@ -10,12 +10,45 @@
 # define noinline	__attribute__((noinline))
 #endif
 
-extern struct bpf_map_def tvars;
+extern struct bpf_map_def dvars;
+extern uint64_t NCPUS;
 
-noinline uint64_t dt_get_tvar(uint32_t id)
+noinline void *dt_get_tvar(uint32_t id, uint64_t not_zero)
 {
-	uint64_t	*val;
+	uint64_t	key;
+	uint64_t	dflt_key = 0;
+	void		*val;
 
-	val = bpf_map_lookup_elem(&tvars, &id);
-	return val ? *val : 0;
+	key = bpf_get_current_pid_tgid();
+	key &= 0x00000000ffffffffUL;
+	if (key == 0)
+		key = bpf_get_smp_processor_id();
+	else
+		key += (uint32_t)(uint64_t)&NCPUS;
+
+	key++;
+	key = (key << 32) | id;
+
+	if (!not_zero) {
+		bpf_map_delete_elem(&dvars, &key);
+		return 0;
+	}
+
+	val = bpf_map_lookup_elem(&dvars, &key);
+	if (val != 0)
+		return val;
+
+	/* Not found - create it with the default value. */
+	val = bpf_map_lookup_elem(&dvars, &dflt_key);
+	if (val == 0)
+		return 0;
+
+	if (bpf_map_update_elem(&dvars, &key, val, BPF_ANY) < 0)
+		return 0;
+
+	val = bpf_map_lookup_elem(&dvars, &key);
+	if (val != 0)
+		return val;
+
+	return 0;
 }
diff --git a/bpf/map_tvar.c b/bpf/map_tvar.c
deleted file mode 100644
index b7ab637a..00000000
--- a/bpf/map_tvar.c
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
- */
-#include <linux/bpf.h>
-#include <stdint.h>
-#include <bpf-helpers.h>
-
-struct bpf_map_def SEC("maps") tvars = {
-	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(uint32_t),
-	.value_size = sizeof(uint64_t),
-	.max_entries = 16,
-};
diff --git a/bpf/set_tvar.c b/bpf/set_tvar.c
deleted file mode 100644
index d7c48665..00000000
--- a/bpf/set_tvar.c
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
- */
-#include <linux/bpf.h>
-#include <stdint.h>
-#include <bpf-helpers.h>
-
-#ifndef noinline
-# define noinline	__attribute__((noinline))
-#endif
-
-extern struct bpf_map_def tvars;
-
-noinline void dt_set_tvar(uint32_t id, uint64_t val)
-{
-	bpf_map_update_elem(&tvars, &id, &val, BPF_ANY);
-}
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index fc1e5e8f..3475f158 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -98,7 +98,7 @@ int dt_bpf_map_update(int fd, const void *key, const void *val)
 	attr.map_fd = fd;
 	attr.key = (uint64_t)(unsigned long)key;
 	attr.value = (uint64_t)(unsigned long)val;
-	attr.flags = 0;
+	attr.flags = BPF_ANY;
 
 	return bpf(BPF_MAP_UPDATE_ELEM, &attr);
 }
@@ -213,36 +213,22 @@ populate_probes_map(dtrace_hdl_t *dtp, int fd)
  *		use.
  * - gvars:	Global variables map.  This is a global map with a singleton
  *		element (key 0) addressed by variable offset.
+ * - dvars:	Dynamic variables map.  This is a global hash map indexed with
+ *		a unique numeric identifier for each variable per thread.
  * - lvars:	Local variables map.  This is a per-CPU map with a singleton
  *		element (key 0) addressed by variable offset.
- *
- * FIXME: TLS variable storage is still being designed further so this is just
- *	  a temporary placeholder and will most likely be replaced by something
- *	  else.  If we stick to the legacy DTrace approach, we will need to
- *	  determine the maximum overall key size for TLS variables *and* the
- *	  maximum value size.  Based on these values, the legacy code would
- *	  take the memory size set aside for dynamic variables, and divide it by
- *	  the storage size needed for the largest dynamic variable (associative
- *	  array element or TLS variable).
- *
- * - tvars:	Thread-local (TLS) variables map, associating a 64-bit value
- *		with each thread-local variable.  The map is indexed by a value
- *		computed based on the thread-local variable id and execution
- *		thread information to ensure each thread has its own copy of a
- *		given thread-local variable.  The amount of TLS variable space
- *		to allocate for these dynamic variables is calculated based on
- *		the number of uniquely named TLS variables (next-to-be-assigned
- *		id minus the base id).
  */
 int
 dt_bpf_gmap_create(dtrace_hdl_t *dtp)
 {
-	int		stabsz, gvarsz, lvarsz, tvarc, aggsz, memsz;
+	int		stabsz, gvarsz, lvarsz, aggsz, memsz;
+	int		dvarc = 0;
 	int		ci_mapfd, st_mapfd, pr_mapfd;
 	uint32_t	key = 0;
 	size_t		strsize = dtp->dt_options[DTRACEOPT_STRSIZE];
 	uint8_t		*buf, *end;
 	char		*strtab;
+	size_t		strdatasz = P2ROUNDUP(DT_STRLEN_BYTES + strsize + 1, 8);
 
 	/* If we already created the global maps, return success. */
 	if (dt_gmap_done)
@@ -257,7 +243,9 @@ 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);
-	tvarc = dt_idhash_peekid(dtp->dt_tls) - DIF_VAR_OTHER_UBASE;
+	if (dtp->dt_maxtlslen)
+		dvarc = dtp->dt_options[DTRACEOPT_DYNVARSIZE] /
+			dtp->dt_maxtlslen;
 
 	/* Create global maps as long as there are no errors. */
 	dtp->dt_stmap_fd = create_gmap(dtp, "state", BPF_MAP_TYPE_ARRAY,
@@ -312,9 +300,7 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
 		8 +
 		roundup(dtp->dt_maxreclen, 8) +
 		MAX(sizeof(uint64_t) * dtp->dt_options[DTRACEOPT_MAXFRAMES],
-		    DT_TSTRING_SLOTS *
-			roundup(DT_STRLEN_BYTES +
-				dtp->dt_options[DTRACEOPT_STRSIZE] + 1, 8) +
+		    DT_TSTRING_SLOTS * strdatasz +
 		    dtp->dt_options[DTRACEOPT_STRSIZE] + 1
 		);
 	if (create_gmap(dtp, "mem", BPF_MAP_TYPE_PERCPU_ARRAY,
@@ -371,10 +357,20 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
 			sizeof(uint32_t), lvarsz, 1) == -1)
 		return -1;		/* dt_errno is set for us */
 
-	if (tvarc > 0 &&
-	    create_gmap(dtp, "tvars", BPF_MAP_TYPE_ARRAY,
-			sizeof(uint32_t), sizeof(uint64_t), tvarc) == -1)
-		return -1;		/* dt_errno is set for us */
+	if (dvarc > 0) {
+		int	fd;
+		char	dflt[dtp->dt_maxtlslen];
+
+		/* We allocate room for an additional default value. */
+		fd = create_gmap(dtp, "dvars", BPF_MAP_TYPE_HASH,
+				 sizeof(uint64_t), dtp->dt_maxtlslen,
+				 dvarc + 1);
+		if (fd == -1)
+			return -1;	/* dt_errno is set for us */
+
+		memset(dflt, 0, dtp->dt_maxtlslen);
+		dt_bpf_map_update(fd, &key, &dflt);
+	}
 
 	/* 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 287178c1..5b9bae80 100644
--- a/libdtrace/dt_bpf.h
+++ b/libdtrace/dt_bpf.h
@@ -27,6 +27,7 @@ extern "C" {
 #define DT_CONST_STKSIZ	7
 #define DT_CONST_BOOTTM	8
 #define DT_CONST_NSPEC	9
+#define DT_CONST_NCPUS	10
 
 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 a25f3548..ce824eb5 100644
--- a/libdtrace/dt_cc.c
+++ b/libdtrace/dt_cc.c
@@ -2355,6 +2355,9 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
 			case DT_CONST_NSPEC:
 				nrp->dofr_data = dtp->dt_options[DTRACEOPT_NSPEC];
 				continue;
+			case DT_CONST_NCPUS:
+				nrp->dofr_data = dtp->dt_conf.max_cpuid + 1;
+				continue;
 			case DT_CONST_STKSIZ:
 				nrp->dofr_data = sizeof(uint64_t)
 				    * dtp->dt_options[DTRACEOPT_MAXFRAMES];
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index c64fa78c..588943ee 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -2057,18 +2057,55 @@ dt_cg_load_var(dt_node_t *dst, dt_irlist_t *dlp, dt_regset_t *drp)
 	}
 
 	/* otherwise, handle thread-local and built-in variables */
-	if (dt_regset_xalloc_args(drp) == -1)
-		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
-
 	if (idp->di_flags & DT_IDFLG_TLS) {	/* TLS var */
-		emit(dlp, BPF_MOV_IMM(BPF_REG_1, idp->di_id - DIF_VAR_OTHER_UBASE));
+		uint_t	varid = idp->di_id - DIF_VAR_OTHER_UBASE;
+		uint_t	lbl_notnull = dt_irlist_label(dlp);
+		uint_t	lbl_done = dt_irlist_label(dlp);
+
 		idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_tvar");
+		assert(idp != NULL);
+
+		if ((dst->dn_reg = dt_regset_alloc(drp)) == -1)
+			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+		if (dt_regset_xalloc_args(drp) == -1)
+			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+		dt_regset_xalloc(drp, BPF_REG_0);
+		emit(dlp,  BPF_MOV_IMM(BPF_REG_1, varid));
+		emit(dlp,  BPF_MOV_IMM(BPF_REG_2, 1));
+		emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+		dt_regset_free_args(drp);
+
+		emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, 0, lbl_notnull));
+		emit(dlp,  BPF_MOV_IMM(dst->dn_reg, 0));
+		emit(dlp,  BPF_JUMP(lbl_done));
+		emitl(dlp, lbl_notnull,
+			   BPF_MOV_REG(dst->dn_reg, BPF_REG_0));
+		dt_regset_free(drp, BPF_REG_0);
+
+		/* load the variable value if not by ref */
+		if (!(dst->dn_flags & DT_NF_REF)) {
+			size_t	size = dt_node_type_size(dst);
+
+			assert(size > 0 && size <= 8 &&
+			       (size & (size - 1)) == 0);
+
+			emit(dlp, BPF_LOAD(ldstw[size], dst->dn_reg, dst->dn_reg, 0));
+		}
+
+		emitl(dlp, lbl_done,
+			   BPF_NOP());
+
+		return;
 	} else if (idp->di_id < DIF_VAR_OTHER_UBASE) {	/* built-in var */
+		if (dt_regset_xalloc_args(drp) == -1)
+			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
 		emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
 		emit(dlp, BPF_MOV_IMM(BPF_REG_2, idp->di_id));
 		idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_bvar");
 	} else
 		assert(0);
+
 	assert(idp != NULL);
 	dt_regset_xalloc(drp, BPF_REG_0);
 	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
@@ -2301,14 +2338,14 @@ dt_cg_store_var(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
 		dt_ident_t *idp)
 {
 	uint_t	varid;
+	uint_t	lbl_done = dt_irlist_label(dlp);
+	int	reg;
+	size_t	size;
 
 	idp->di_flags |= DT_IDFLG_DIFW;
 
 	/* global and local variables (that is, not thread-local) */
 	if (!(idp->di_flags & DT_IDFLG_TLS)) {
-		int	reg;
-		size_t	size;
-
 		if ((reg = dt_regset_alloc(drp)) == -1)
 			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
 
@@ -2351,23 +2388,53 @@ dt_cg_store_var(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
 
 	/* TLS var */
 	varid = idp->di_id - DIF_VAR_OTHER_UBASE;
-	idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_set_tvar");
+	size = idp->di_size;
+
+	idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_tvar");
 	assert(idp != NULL);
 
 	if (dt_regset_xalloc_args(drp) == -1)
 		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
 
-	/*
-	 * We assign the varid in the instruction preceding the call because
-	 * the disassembler expects this sequence in support for annotating
-	 * the disassembly with variables names.
-	 */
-	emit(dlp,  BPF_MOV_REG(BPF_REG_2, dnp->dn_reg));
 	emit(dlp,  BPF_MOV_IMM(BPF_REG_1, varid));
+	emit(dlp,  BPF_MOV_REG(BPF_REG_2, dnp->dn_reg));
 	dt_regset_xalloc(drp, BPF_REG_0);
 	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
-	dt_regset_free(drp, BPF_REG_0);
 	dt_regset_free_args(drp);
+	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);
+		if (dt_node_is_string(dnp))
+			srcsz += DT_STRLEN_BYTES;
+		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());
 }
 
 /*
diff --git a/libdtrace/dt_dis.c b/libdtrace/dt_dis.c
index 410d29fe..8c1c1f17 100644
--- a/libdtrace/dt_dis.c
+++ b/libdtrace/dt_dis.c
@@ -306,36 +306,17 @@ dt_dis_bpf_args(const dtrace_difo_t *dp, const char *fn,
 		snprintf(buf, len, "%s",
 			 dt_dis_varname_id(dp, in->imm, DIFV_SCOPE_GLOBAL, addr));
 		return buf;
-	} else if (strcmp(fn, "dt_get_tvar") == 0 ||
-		   strcmp(fn, "dt_set_tvar") == 0) {
+	} else if (strcmp(fn, "dt_get_tvar") == 0) {
 		/*
-		 * We know that the previous instruction exists and assigns
-		 * the variable id to %r1 (because we wrote the code generator
+		 * We know that the previous two instruction exist and assigns
+		 * the variable id to %r0 (because we wrote the code generator
 		 * to emit these instructions in this exact order.)
 		 */
-		in--;
+		in -= 2;
 		snprintf(buf, len, "self->%s",
 			 dt_dis_varname_id(dp, in->imm + DIF_VAR_OTHER_UBASE,
 					DIFV_SCOPE_THREAD, addr));
 		return buf;
-	} else if (strcmp(fn, "dt_get_string") == 0) {
-		const char	*s;
-		char		*se;
-
-		/*
-		 * We know that the previous instruction exists and assigns
-		 * the string offset to %r1 (because we wrote the code
-		 * generator to emit these instructions in this exact order.
-		 */
-		in--;
-		if (in->imm >= dp->dtdo_strlen)
-			return NULL;
-
-		s = dt_difo_getstr(dp, in->imm);
-		se = strchr2esc(s, strlen(s));
-		snprintf(buf, len, "\"%s\"n", se ? se : s);
-		free(se);
-		return buf;
 	}
 
 	return NULL;
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index 75c00e9e..acda81f2 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -56,9 +56,7 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	DT_BPF_SYMBOL(dt_agg_qbin, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_error, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_get_bvar, DT_IDENT_SYMBOL),
-	DT_BPF_SYMBOL(dt_get_string, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_get_tvar, DT_IDENT_SYMBOL),
-	DT_BPF_SYMBOL(dt_set_tvar, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_index, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_lltostr, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_rindex, DT_IDENT_SYMBOL),
@@ -75,6 +73,7 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	DT_BPF_SYMBOL(aggs, DT_IDENT_PTR),
 	DT_BPF_SYMBOL(buffers, DT_IDENT_PTR),
 	DT_BPF_SYMBOL(cpuinfo, DT_IDENT_PTR),
+	DT_BPF_SYMBOL(dvars, DT_IDENT_PTR),
 	DT_BPF_SYMBOL(gvars, DT_IDENT_PTR),
 	DT_BPF_SYMBOL(lvars, DT_IDENT_PTR),
 	DT_BPF_SYMBOL(mem, DT_IDENT_PTR),
@@ -82,7 +81,6 @@ 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(tvars, DT_IDENT_PTR),
 	/* BPF internal identifiers */
 	DT_BPF_SYMBOL_ID(EPID, DT_IDENT_SCALAR, DT_CONST_EPID),
 	DT_BPF_SYMBOL_ID(PRID, DT_IDENT_SCALAR, DT_CONST_PRID),
@@ -93,6 +91,7 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	DT_BPF_SYMBOL_ID(STKSIZ, DT_IDENT_SCALAR, DT_CONST_STKSIZ),
 	DT_BPF_SYMBOL_ID(BOOTTM, DT_IDENT_SCALAR, DT_CONST_BOOTTM),
 	DT_BPF_SYMBOL_ID(NSPEC, DT_IDENT_SCALAR, DT_CONST_NSPEC),
+	DT_BPF_SYMBOL_ID(NCPUS, DT_IDENT_SCALAR, DT_CONST_NCPUS),
 	/* End-of-list marker */
 	{ NULL, }
 };
diff --git a/libdtrace/dt_ident.c b/libdtrace/dt_ident.c
index 2d3d7813..96c086b2 100644
--- a/libdtrace/dt_ident.c
+++ b/libdtrace/dt_ident.c
@@ -1016,10 +1016,17 @@ dt_ident_set_storage(dt_ident_t *idp, uint_t alignment, uint_t size)
 	    idp->di_type == DT_STR_TYPE(dtp))
 		size += DT_STRLEN_BYTES;
 
-	idp->di_offset = (dhp->dh_nextoff + (alignment - 1)) & ~(alignment - 1);
-	idp->di_size = size;
+	if (!(idp->di_flags & DT_IDFLG_TLS)) {
+		idp->di_offset = (dhp->dh_nextoff + (alignment - 1)) &
+				 ~(alignment - 1);
+		dhp->dh_nextoff = idp->di_offset + size;
+	} else {
+		idp->di_offset = 0;
+		if (size > dtp->dt_maxtlslen)
+			dtp->dt_maxtlslen = size;
+	}
 
-	dhp->dh_nextoff = idp->di_offset + size;
+	idp->di_size = size;
 }
 
 void
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index b461d73d..d9b6c747 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -291,9 +291,9 @@ struct dtrace_hdl {
 	dt_idhash_t *dt_tls;	/* hash table of thread-local identifiers */
 	dt_idhash_t *dt_bpfsyms;/* hash table of BPF identifiers */
 	dt_strtab_t *dt_ccstab;	/* global string table (during compilation) */
-	char *dt_strtab;	/* global string table (runtime) */
 	uint_t dt_strlen;	/* global string table (runtime) size */
 	uint_t dt_maxreclen;	/* largest record size across programs */
+	uint_t dt_maxtlslen;	/* largest TLS variable 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/libdtrace/dt_open.c b/libdtrace/dt_open.c
index f83fb2f0..fc879a62 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -789,6 +789,9 @@ dt_vopen(int version, int flags, int *errp,
 	 */
 	dtp->dt_options[DTRACEOPT_STRSIZE] = 256;
 
+	/* Set the default dynamic variable space size. */
+	dtp->dt_options[DTRACEOPT_DYNVARSIZE] = 1024 * 1024 * 1;
+
 	/*
 	 * Set the default speculation size and number of simultaneously active
 	 * speculations.
@@ -879,7 +882,6 @@ dt_vopen(int version, int flags, int *errp,
 	    DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX);
 
 	dtp->dt_ccstab = dt_strtab_create(BUFSIZ);
-	dtp->dt_strtab = NULL;
 	dtp->dt_strlen = 0;
 
 	if (dtp->dt_macros == NULL || dtp->dt_aggs == NULL ||
@@ -1289,7 +1291,6 @@ dtrace_close(dtrace_hdl_t *dtp)
 	free(dtp->dt_module_path);
 	free(dtp->dt_kernpaths);
 	free(dtp->dt_provs);
-	free(dtp->dt_strtab);
 	free(dtp);
 
 	dt_debug_dump(0);
diff --git a/libdtrace/dt_subr.c b/libdtrace/dt_subr.c
index 2c8a4a22..5f8ddb8f 100644
--- a/libdtrace/dt_subr.c
+++ b/libdtrace/dt_subr.c
@@ -724,16 +724,6 @@ dt_difo_free(dtrace_hdl_t *dtp, dtrace_difo_t *dp)
 	if (dp == NULL)
 		return; /* simplify caller code */
 
-	/*
-	 * If we are destroying the DIFO that holds the final consolidated
-	 * string table, clear the dt_strtab and dt_strlen members in the
-	 * consumer handle.
-	 */
-	if (dtp->dt_strtab == dp->dtdo_strtab) {
-		dtp->dt_strtab = NULL;
-		dtp->dt_strlen = 0;
-	}
-
 	dt_free(dtp, dp->dtdo_buf);
 	dt_free(dtp, dp->dtdo_strtab);
 	dt_free(dtp, dp->dtdo_vartab);
diff --git a/test/unittest/variables/tvar/err.D_OP_INCOMPAT.default_int.d b/test/unittest/variables/tvar/err.D_OP_INCOMPAT.default_int.d
new file mode 100644
index 00000000..9b64106c
--- /dev/null
+++ b/test/unittest/variables/tvar/err.D_OP_INCOMPAT.default_int.d
@@ -0,0 +1,22 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: A thresd-local variable declared without a type is implicitly
+ *	      declared as an int.  Verify that assignment of a string value
+ *	      fails.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+self x;
+
+BEGIN
+{
+	self->x = execname;
+	exit(0);
+}
diff --git a/test/unittest/variables/tvar/err.limited_space.d b/test/unittest/variables/tvar/err.limited_space.d
new file mode 100644
index 00000000..7fb42934
--- /dev/null
+++ b/test/unittest/variables/tvar/err.limited_space.d
@@ -0,0 +1,35 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+/* @@xfail: dtv2 - no drops yet */
+
+/*
+ * ASSERTION: Trying to store more TLS variables than we have space for will
+ *	      trigger an error.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+#pragma D option strsize=5
+#pragma D option dynvarsize=31
+
+BEGIN
+{
+	self->a = 1;
+	self->b = 2;
+	self->c = 3;
+	self->d = 4;
+}
+
+BEGIN
+{
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/variables/tvar/err.limited_space.r b/test/unittest/variables/tvar/err.limited_space.r
new file mode 100644
index 00000000..41a7c38a
--- /dev/null
+++ b/test/unittest/variables/tvar/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/variables/tvar/tst.alignment-array.r b/test/unittest/variables/tvar/tst.alignment-array.r
new file mode 100644
index 00000000..d9646964
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-array.r
@@ -0,0 +1,2 @@
+NAME             OFFSET KND SCP FLAG TYPE
+var              0      scl tls r    D type (array) by ref (size 10)
diff --git a/test/unittest/variables/tvar/tst.alignment-array.sh b/test/unittest/variables/tvar/tst.alignment-array.sh
new file mode 100755
index 00000000..05abecd7
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-array.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+##
+#
+# ASSERTION: Thread-local variables of array type should be aligned at offset 0
+#
+# SECTION: Variables
+#
+##
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+self char dummy;
+self short var[5];
+
+BEGIN
+{
+	self->var[3] = 0x1234;
+	exit(0);
+}
+' 2>&1 | awk '
+BEGIN {
+	rc = 1;
+}
+
+/^NAME/ && /KND SCP/ {
+	printf "%-16s %-6s %-3s %-3s %-4s %s\n",
+	       "NAME", "OFFSET", "KND", "SCP","FLAG", "TYPE";
+	while (getline == 1 && NF > 0) {
+		$2 = $6 = "";
+		gsub(/ +/, " ");
+		printf "%-16s %-6s %-3s %-3s %-4s", $1, $2, $3, $4, $5;
+		$1 = $2 = $3 = $4 = $5 = "";
+		gsub(/ +/, " ");
+		print $0;
+		rc = 0;
+	}
+}
+
+END {
+	exit(rc);
+}
+'
+
+exit $?
diff --git a/test/unittest/variables/tvar/tst.alignment-char.r b/test/unittest/variables/tvar/tst.alignment-char.r
new file mode 100644
index 00000000..9a5312cb
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-char.r
@@ -0,0 +1,2 @@
+NAME             OFFSET KND SCP FLAG TYPE
+var              0      scl tls w    D type (integer) (size 1)
diff --git a/test/unittest/variables/tvar/tst.alignment-char.sh b/test/unittest/variables/tvar/tst.alignment-char.sh
new file mode 100755
index 00000000..d4284399
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-char.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+##
+#
+# ASSERTION: Thread-local variables of type char should be aligned at offset 0
+#
+# SECTION: Variables/Thread-Local Variables
+#
+##
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+self char dummy;
+self char var;
+
+BEGIN
+{
+	self->var = 0x12;
+	exit(0);
+}
+' 2>&1 | awk '
+BEGIN {
+	rc = 1;
+}
+
+/^NAME/ && /KND SCP/ {
+	printf "%-16s %-6s %-3s %-3s %-4s %s\n",
+	       "NAME", "OFFSET", "KND", "SCP","FLAG", "TYPE";
+	while (getline == 1 && NF > 0) {
+		$2 = $6 = "";
+		gsub(/ +/, " ");
+		printf "%-16s %-6s %-3s %-3s %-4s", $1, $2, $3, $4, $5;
+		$1 = $2 = $3 = $4 = $5 = "";
+		gsub(/ +/, " ");
+		print $0;
+		rc = 0;
+	}
+}
+
+END {
+	exit(rc);
+}
+'
+
+exit $?
diff --git a/test/unittest/variables/tvar/tst.alignment-int.r b/test/unittest/variables/tvar/tst.alignment-int.r
new file mode 100644
index 00000000..2835bb81
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-int.r
@@ -0,0 +1,2 @@
+NAME             OFFSET KND SCP FLAG TYPE
+var              0      scl tls w    D type (integer) (size 4)
diff --git a/test/unittest/variables/tvar/tst.alignment-int.sh b/test/unittest/variables/tvar/tst.alignment-int.sh
new file mode 100755
index 00000000..48947790
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-int.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+##
+#
+# ASSERTION: Thread-local variables of type int should be aligned at offset 0
+#
+# SECTION: Variables/Thread-Local Variables
+#
+##
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+self char dummy;
+self int var;
+
+BEGIN
+{
+	self->var = 0x12345678;
+	exit(0);
+}
+' 2>&1 | awk '
+BEGIN {
+	rc = 1;
+}
+
+/^NAME/ && /KND SCP/ {
+	printf "%-16s %-6s %-3s %-3s %-4s %s\n",
+	       "NAME", "OFFSET", "KND", "SCP","FLAG", "TYPE";
+	while (getline == 1 && NF > 0) {
+		$2 = $6 = "";
+		gsub(/ +/, " ");
+		printf "%-16s %-6s %-3s %-3s %-4s", $1, $2, $3, $4, $5;
+		$1 = $2 = $3 = $4 = $5 = "";
+		gsub(/ +/, " ");
+		print $0;
+		rc = 0;
+	}
+}
+
+END {
+	exit(rc);
+}
+'
+
+exit $?
diff --git a/test/unittest/variables/tvar/tst.alignment-long.r b/test/unittest/variables/tvar/tst.alignment-long.r
new file mode 100644
index 00000000..52592832
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-long.r
@@ -0,0 +1,2 @@
+NAME             OFFSET KND SCP FLAG TYPE
+var              0      scl tls w    D type (integer) (size 8)
diff --git a/test/unittest/variables/tvar/tst.alignment-long.sh b/test/unittest/variables/tvar/tst.alignment-long.sh
new file mode 100755
index 00000000..2cde5847
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-long.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+##
+#
+# ASSERTION: Thread-local variables of type long should be aligned at offset 0
+#
+# SECTION: Variables/Thread-Local Variables
+#
+##
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+self char dummy;
+self long var;
+
+BEGIN
+{
+	self->var = 0x1234567887654321ull;
+	exit(0);
+}
+' 2>&1 | awk '
+BEGIN {
+	rc = 1;
+}
+
+/^NAME/ && /KND SCP/ {
+	printf "%-16s %-6s %-3s %-3s %-4s %s\n",
+	       "NAME", "OFFSET", "KND", "SCP","FLAG", "TYPE";
+	while (getline == 1 && NF > 0) {
+		$2 = $6 = "";
+		gsub(/ +/, " ");
+		printf "%-16s %-6s %-3s %-3s %-4s", $1, $2, $3, $4, $5;
+		$1 = $2 = $3 = $4 = $5 = "";
+		gsub(/ +/, " ");
+		print $0;
+		rc = 0;
+	}
+}
+
+END {
+	exit(rc);
+}
+'
+
+exit $?
diff --git a/test/unittest/variables/tvar/tst.alignment-ptr.r b/test/unittest/variables/tvar/tst.alignment-ptr.r
new file mode 100644
index 00000000..a16cad9d
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-ptr.r
@@ -0,0 +1,2 @@
+NAME             OFFSET KND SCP FLAG TYPE
+var              0      scl tls w    D type (pointer) (size 8)
diff --git a/test/unittest/variables/tvar/tst.alignment-ptr.sh b/test/unittest/variables/tvar/tst.alignment-ptr.sh
new file mode 100755
index 00000000..ac179b3c
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-ptr.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+##
+#
+# ASSERTION: Thread-local pointer variables should be aligned at offset 0
+#
+# SECTION: Variables/Thread-Local Variables
+#
+##
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+self char dummy;
+self char *var;
+
+BEGIN
+{
+	self->var = 0;
+	exit(0);
+}
+' 2>&1 | awk '
+BEGIN {
+	rc = 1;
+}
+
+/^NAME/ && /KND SCP/ {
+	printf "%-16s %-6s %-3s %-3s %-4s %s\n",
+	       "NAME", "OFFSET", "KND", "SCP","FLAG", "TYPE";
+	while (getline == 1 && NF > 0) {
+		$2 = $6 = "";
+		gsub(/ +/, " ");
+		printf "%-16s %-6s %-3s %-3s %-4s", $1, $2, $3, $4, $5;
+		$1 = $2 = $3 = $4 = $5 = "";
+		gsub(/ +/, " ");
+		print $0;
+		rc = 0;
+	}
+}
+
+END {
+	exit(rc);
+}
+'
+
+exit $?
diff --git a/test/unittest/variables/tvar/tst.alignment-short.r b/test/unittest/variables/tvar/tst.alignment-short.r
new file mode 100644
index 00000000..f59ff061
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-short.r
@@ -0,0 +1,2 @@
+NAME             OFFSET KND SCP FLAG TYPE
+var              0      scl tls w    D type (integer) (size 2)
diff --git a/test/unittest/variables/tvar/tst.alignment-short.sh b/test/unittest/variables/tvar/tst.alignment-short.sh
new file mode 100755
index 00000000..db1cf1b2
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-short.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+##
+#
+# ASSERTION: Thread-local variables of type short should be aligned at offset 0
+#
+# SECTION: Variables/Thread-Local Variables
+#
+##
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+self char dummy;
+self short var;
+
+BEGIN
+{
+	self->var = 0x1234;
+	exit(0);
+}
+' 2>&1 | awk '
+BEGIN {
+	rc = 1;
+}
+
+/^NAME/ && /KND SCP/ {
+	printf "%-16s %-6s %-3s %-3s %-4s %s\n",
+	       "NAME", "OFFSET", "KND", "SCP","FLAG", "TYPE";
+	while (getline == 1 && NF > 0) {
+		$2 = $6 = "";
+		gsub(/ +/, " ");
+		printf "%-16s %-6s %-3s %-3s %-4s", $1, $2, $3, $4, $5;
+		$1 = $2 = $3 = $4 = $5 = "";
+		gsub(/ +/, " ");
+		print $0;
+		rc = 0;
+	}
+}
+
+END {
+	exit(rc);
+}
+'
+
+exit $?
diff --git a/test/unittest/variables/tvar/tst.alignment-struct-2.r b/test/unittest/variables/tvar/tst.alignment-struct-2.r
new file mode 100644
index 00000000..2d86343b
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-struct-2.r
@@ -0,0 +1,2 @@
+NAME             OFFSET KND SCP FLAG TYPE
+var              0      scl tls r    D type (struct) by ref (size 18)
diff --git a/test/unittest/variables/tvar/tst.alignment-struct-2.sh b/test/unittest/variables/tvar/tst.alignment-struct-2.sh
new file mode 100755
index 00000000..2f9d6dba
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-struct-2.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+##
+#
+# ASSERTION: Thread-local variables of type struct should be aligned at offset 0
+#
+# SECTION: Variables
+#
+##
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+self char dummy;
+self struct { int u; long v; short w; } var;
+
+BEGIN
+{
+	self->var.w = 0x1234;
+	exit(0);
+}
+' 2>&1 | awk '
+BEGIN {
+	rc = 1;
+}
+
+/^NAME/ && /KND SCP/ {
+	printf "%-16s %-6s %-3s %-3s %-4s %s\n",
+	       "NAME", "OFFSET", "KND", "SCP","FLAG", "TYPE";
+	while (getline == 1 && NF > 0) {
+		$2 = $6 = "";
+		gsub(/ +/, " ");
+		printf "%-16s %-6s %-3s %-3s %-4s", $1, $2, $3, $4, $5;
+		$1 = $2 = $3 = $4 = $5 = "";
+		gsub(/ +/, " ");
+		print $0;
+		rc = 0;
+	}
+}
+
+END {
+	exit(rc);
+}
+'
+
+exit $?
diff --git a/test/unittest/variables/tvar/tst.alignment-struct.r b/test/unittest/variables/tvar/tst.alignment-struct.r
new file mode 100644
index 00000000..b4aa69db
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-struct.r
@@ -0,0 +1,2 @@
+NAME             OFFSET KND SCP FLAG TYPE
+var              0      scl tls r    D type (struct) by ref (size 4)
diff --git a/test/unittest/variables/tvar/tst.alignment-struct.sh b/test/unittest/variables/tvar/tst.alignment-struct.sh
new file mode 100755
index 00000000..197edd8e
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.alignment-struct.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+##
+#
+# ASSERTION: Thread-local variables of type struct should be aligned at offset 0
+#
+# SECTION: Variables
+#
+##
+
+dtrace=$1
+
+$dtrace $dt_flags -Sen '
+self char dummy;
+self struct { short u, v; } var;
+
+BEGIN
+{
+	self->var.u = 0x1234;
+	exit(0);
+}
+' 2>&1 | awk '
+BEGIN {
+	rc = 1;
+}
+
+/^NAME/ && /KND SCP/ {
+	printf "%-16s %-6s %-3s %-3s %-4s %s\n",
+	       "NAME", "OFFSET", "KND", "SCP","FLAG", "TYPE";
+	while (getline == 1 && NF > 0) {
+		$2 = $6 = "";
+		gsub(/ +/, " ");
+		printf "%-16s %-6s %-3s %-3s %-4s", $1, $2, $3, $4, $5;
+		$1 = $2 = $3 = $4 = $5 = "";
+		gsub(/ +/, " ");
+		print $0;
+		rc = 0;
+	}
+}
+
+END {
+	exit(rc);
+}
+'
+
+exit $?
diff --git a/test/unittest/variables/tvar/tst.default_int.d b/test/unittest/variables/tvar/tst.default_int.d
new file mode 100644
index 00000000..83f42ea9
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.default_int.d
@@ -0,0 +1,21 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: A thresd-local variable declared without a type is implicitly
+ *	      declared as an int.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+self x;
+
+BEGIN
+{
+	x = 1;
+	exit(0);
+}
diff --git a/test/unittest/variables/tvar/tst.init.d b/test/unittest/variables/tvar/tst.init.d
new file mode 100644
index 00000000..3e31744d
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.init.d
@@ -0,0 +1,22 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Thread-local variables are initialized to zero.  This test will
+ *	      report failure when 'x' is not zero.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+#pragma D option quiet
+
+self int x;
+
+BEGIN
+{
+	exit(self->x);
+}
diff --git a/test/unittest/variables/tvar/tst.load_before_store.d b/test/unittest/variables/tvar/tst.load_before_store.d
new file mode 100644
index 00000000..68adbcfc
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.load_before_store.d
@@ -0,0 +1,19 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Test that a load-before-store pattern for a thread-local variable
+ *	      does not cause the script to fail.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+BEGIN
+{
+	++self->x;
+	exit(0);
+}
diff --git a/test/unittest/variables/tvar/tst.post_inc_lvar.d b/test/unittest/variables/tvar/tst.post_inc_lvar.d
new file mode 100644
index 00000000..da589768
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.post_inc_lvar.d
@@ -0,0 +1,19 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Test that post-increment on an local variable evaluates to the
+ *            old value of the local variable.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+BEGIN
+{
+	trace(self->x++);
+	exit(0);
+}
diff --git a/test/unittest/variables/tvar/tst.post_inc_lvar.r b/test/unittest/variables/tvar/tst.post_inc_lvar.r
new file mode 100644
index 00000000..c6d93fe4
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.post_inc_lvar.r
@@ -0,0 +1,5 @@
+                   FUNCTION:NAME
+                          :BEGIN                    0
+
+-- @@stderr --
+dtrace: script 'test/unittest/variables/tvar/tst.post_inc_lvar.d' matched 1 probe
diff --git a/test/unittest/variables/tvar/tst.pre_inc_lvar.d b/test/unittest/variables/tvar/tst.pre_inc_lvar.d
new file mode 100644
index 00000000..0e3b1e2e
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.pre_inc_lvar.d
@@ -0,0 +1,19 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Test that pre-increment on an local variable evaluates to the
+ *            new value of the local variable.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+BEGIN
+{
+	trace(++self->x);
+	exit(0);
+}
diff --git a/test/unittest/variables/tvar/tst.pre_inc_lvar.r b/test/unittest/variables/tvar/tst.pre_inc_lvar.r
new file mode 100644
index 00000000..f922c05a
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.pre_inc_lvar.r
@@ -0,0 +1,5 @@
+                   FUNCTION:NAME
+                          :BEGIN                    1
+
+-- @@stderr --
+dtrace: script 'test/unittest/variables/tvar/tst.pre_inc_lvar.d' matched 1 probe
diff --git a/test/unittest/variables/tvar/tst.store-char.d b/test/unittest/variables/tvar/tst.store-char.d
new file mode 100644
index 00000000..989bff94
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.store-char.d
@@ -0,0 +1,28 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Storing an integer to a 'char' thread-local variable does not
+ *	      store more than a single byte.
+ *
+ * SECTION: Variables/Scalar Variables
+ */
+
+#pragma D option quiet
+
+self char a, b;
+
+BEGIN
+{
+	self->b = 4;
+	self->a = 5;
+
+	trace(self->a);
+	trace(self->b);
+
+	exit(0);
+}
diff --git a/test/unittest/variables/tvar/tst.store-char.r b/test/unittest/variables/tvar/tst.store-char.r
new file mode 100644
index 00000000..fb1e7bc8
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.store-char.r
@@ -0,0 +1 @@
+54
diff --git a/test/unittest/variables/tvar/tst.store_zero_deletes.d b/test/unittest/variables/tvar/tst.store_zero_deletes.d
new file mode 100644
index 00000000..3f53af41
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.store_zero_deletes.d
@@ -0,0 +1,36 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Storing 0 into a TLS variable removes it from storage, making
+ *	      room for another variable.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+#pragma D option quiet
+#pragma D option strsize=5
+#pragma D option dynvarsize=31
+
+BEGIN
+{
+	self->a = 1;
+	self->b = 2;
+	self->c = 3;
+	self->a = 0;
+	self->d = 4;
+	trace(self->a);
+	trace(self->b);
+	trace(self->c);
+	trace(self->d);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/variables/tvar/tst.store_zero_deletes.r b/test/unittest/variables/tvar/tst.store_zero_deletes.r
new file mode 100644
index 00000000..0f6f3a7d
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.store_zero_deletes.r
@@ -0,0 +1 @@
+0234
diff --git a/test/unittest/variables/tvar/tst.str-size.d b/test/unittest/variables/tvar/tst.str-size.d
new file mode 100644
index 00000000..bd8aac13
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.str-size.d
@@ -0,0 +1,31 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Enough space is allocated for each string variable.
+ *
+ * SECTION: Variables/Scalar Variables
+ */
+
+#pragma D option quiet
+#pragma D option strsize=5
+
+BEGIN
+{
+	self->x = "abcd";
+	self->y = "abcd";
+	self->z = "abcd";
+	trace(self->x);
+	trace(self->y);
+	trace(self->z);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/variables/tvar/tst.str-size.r b/test/unittest/variables/tvar/tst.str-size.r
new file mode 100644
index 00000000..0f29a1fe
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.str-size.r
@@ -0,0 +1 @@
+abcdabcdabcd
diff --git a/test/unittest/variables/tvar/tst.struct.d b/test/unittest/variables/tvar/tst.struct.d
new file mode 100644
index 00000000..a7c880b5
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.struct.d
@@ -0,0 +1,29 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Thread-local variables can be declared with a struct type.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+struct foo {
+	int a;
+	int b;
+};
+self struct foo x;
+
+BEGIN
+{
+	self->x.a = 1;
+	self->x.b = 5;
+
+	trace(self->x.a);
+	trace(self->x.b);
+
+	exit(self->x.a == 1 && self->x.b == 5 ? 0 : 1);
+}
diff --git a/test/unittest/variables/tvar/tst.struct.r b/test/unittest/variables/tvar/tst.struct.r
new file mode 100644
index 00000000..367c2cd5
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.struct.r
@@ -0,0 +1,5 @@
+                   FUNCTION:NAME
+                          :BEGIN           1          5
+
+-- @@stderr --
+dtrace: script 'test/unittest/variables/tvar/tst.struct.d' matched 1 probe
-- 
2.33.0




More information about the DTrace-devel mailing list