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

Kris Van Hees kris.van.hees at oracle.com
Wed Dec 1 07:28:05 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                                |  63 +++++++++-
 bpf/map_tvar.c                                |  14 ---
 bpf/set_tvar.c                                |  18 ---
 libdtrace/dt_bpf.c                            |  51 ++++----
 libdtrace/dt_bpf.h                            |   1 +
 libdtrace/dt_cc.c                             |   3 +
 libdtrace/dt_cg.c                             | 118 ++++++++++++++----
 libdtrace/dt_dis.c                            |  25 +---
 libdtrace/dt_dlibs.c                          |   5 +-
 libdtrace/dt_ident.c                          |  13 +-
 libdtrace/dt_impl.h                           |   1 +
 libdtrace/dt_open.c                           |   3 +
 .../tvar/err.D_OP_INCOMPAT.default_int.d      |  22 ++++
 .../variables/tvar/err.limited_space.d        |  34 +++++
 .../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   |  35 ++++++
 .../variables/tvar/tst.store_zero_deletes.r   |   1 +
 .../variables/tvar/tst.store_zero_nop.d       |  33 +++++
 .../variables/tvar/tst.store_zero_nop.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     |  27 ++++
 48 files changed, 960 insertions(+), 119 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.store_zero_nop.d
 create mode 100644 test/unittest/variables/tvar/tst.store_zero_nop.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

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..87e6a79d 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,63 @@
 # 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 store, uint64_t nval)
 {
-	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 we are going to store a zero-value, it is a request to delete the
+	 * TLS variable.
+	 */
+	if (store && !nval) {
+		bpf_map_delete_elem(&dvars, &key);
+		return 0;
+	}
+
+	/*
+	 * For load or non-zero store, we return the address of the value if
+	 * there is one.
+	 */
+	val = bpf_map_lookup_elem(&dvars, &key);
+	if (val != 0)
+		return val;
+
+	/*
+	 * If we are performing a load (not a store), and no var was found,
+	 * we are done.
+	 */
+	if (!store)
+		return 0;
+
+	/*
+	 * Not found and we are storing a non-zero value: create the variable
+	 * 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..29504442 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) + 1;
 
 	/* 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,19 @@ 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];
+
+		fd = create_gmap(dtp, "dvars", BPF_MAP_TYPE_HASH,
+				 sizeof(uint64_t), dtp->dt_maxtlslen, dvarc);
+		if (fd == -1)
+			return -1;	/* dt_errno is set for us */
+
+		/* Initialize the default value (key = 0). */
+		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..f72234b5 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -2027,7 +2027,7 @@ dt_cg_load_var(dt_node_t *dst, dt_irlist_t *dlp, dt_regset_t *drp)
 
 	idp->di_flags |= DT_IDFLG_DIFR;
 
-	/* global and local variables (not thread-local or built-in) */
+	/* global and local variables */
 	if ((idp->di_flags & DT_IDFLG_LOCAL) ||
 	    (!(idp->di_flags & DT_IDFLG_TLS) &&
 	     idp->di_id >= DIF_VAR_OTHER_UBASE)) {
@@ -2056,21 +2056,59 @@ dt_cg_load_var(dt_node_t *dst, dt_irlist_t *dlp, dt_regset_t *drp)
 		return;
 	}
 
-	/* otherwise, handle thread-local and built-in variables */
+	/* thread-local variables */
+	if (idp->di_flags & DT_IDFLG_TLS) {	/* TLS var */
+		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);
+
+		emit(dlp,  BPF_MOV_IMM(BPF_REG_1, varid));
+		emit(dlp,  BPF_MOV_IMM(BPF_REG_2, 0));
+		emit(dlp,  BPF_MOV_IMM(BPF_REG_3, 0));
+		dt_regset_xalloc(drp, BPF_REG_0);
+		emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+		dt_regset_free_args(drp);
+
+		emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, 0, lbl_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;
+	}
+
+	/* 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));
-		idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_tvar");
-	} else if (idp->di_id < DIF_VAR_OTHER_UBASE) {	/* built-in var */
-		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);
+	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));
 	dt_regset_xalloc(drp, BPF_REG_0);
+	idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_get_bvar");
+	assert(idp != NULL);
 	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
 	dt_regset_free_args(drp);
 
@@ -2078,6 +2116,7 @@ dt_cg_load_var(dt_node_t *dst, dt_irlist_t *dlp, dt_regset_t *drp)
 
 	if ((dst->dn_reg = dt_regset_alloc(drp)) == -1)
 		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
 	emit(dlp,  BPF_MOV_REG(dst->dn_reg, BPF_REG_0));
 	dt_regset_free(drp, BPF_REG_0);
 }
@@ -2300,15 +2339,14 @@ static void
 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	varid, lbl_done;
+	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 +2389,55 @@ 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_IMM(BPF_REG_2, 1));
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, 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);
+	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);
+		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..db8989a3 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
+		 * We know that the previous two instruction exist and assigns
 		 * the variable id to %r1 (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 3ac38b42..d9b6c747 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -293,6 +293,7 @@ struct dtrace_hdl {
 	dt_strtab_t *dt_ccstab;	/* global string table (during compilation) */
 	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 acdfe46e..40708d82 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.
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..8e8e5931
--- /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 thread-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..f8fd0378
--- /dev/null
+++ b/test/unittest/variables/tvar/err.limited_space.d
@@ -0,0 +1,34 @@
+/*
+ * 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 dynvarsize=15
+
+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..692f391a
--- /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 thread-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..9b901075
--- /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 thread-local variable evaluates to
+ *            the old value of the 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..0dbbf7f5
--- /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 a thread-local variable evaluates to
+ *            the new value of the 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..42aa70d5
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.store_zero_deletes.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.
+ */
+
+/*
+ * 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 dynvarsize=15
+
+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.store_zero_nop.d b/test/unittest/variables/tvar/tst.store_zero_nop.d
new file mode 100644
index 00000000..976bf082
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.store_zero_nop.d
@@ -0,0 +1,33 @@
+/*
+ * 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 new TLS variable does not allocate space.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+#pragma D option quiet
+#pragma D option dynvarsize=5
+
+BEGIN
+{
+	self->a = 1;
+	self->b = 0;
+	self->c = 0;
+	self->d = 0;
+	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_nop.r b/test/unittest/variables/tvar/tst.store_zero_nop.r
new file mode 100644
index 00000000..83b33d23
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.store_zero_nop.r
@@ -0,0 +1 @@
+1000
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..b4ab6ea2
--- /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=4
+
+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..3b7586fa
--- /dev/null
+++ b/test/unittest/variables/tvar/tst.struct.d
@@ -0,0 +1,27 @@
+/*
+ * 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 and can
+ *	      be assigned to.
+ *
+ * SECTION: Variables/Thread-Local Variables
+ */
+
+self struct task_struct tsk;
+
+BEGIN
+{
+	self->tsk = *curthread;
+
+	exit(self->tsk.pid == pid ? 0 : 1);
+}
+
+ERROR
+{
+	exit(1);
+}
-- 
2.33.0




More information about the DTrace-devel mailing list