[DTrace-devel] [PATCH 2/2] Add support for substr() subroutine

Kris Van Hees kris.van.hees at oracle.com
Thu Sep 2 08:34:09 PDT 2021


Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 bpf/Build                                     |  1 +
 bpf/substr.S                                  | 98 +++++++++++++++++++
 libdtrace/dt_bpf.c                            |  3 +-
 libdtrace/dt_cg.c                             | 59 ++++++++++-
 libdtrace/dt_dlibs.c                          |  2 +-
 .../err.D_PROTO_ARG.substr_non_scalar_arg2.d  | 18 ++++
 .../err.D_PROTO_ARG.substr_non_scalar_arg2.r  |  4 +
 .../err.D_PROTO_ARG.substr_non_scalar_arg3.d  | 18 ++++
 .../err.D_PROTO_ARG.substr_non_scalar_arg3.r  |  4 +
 .../err.D_PROTO_ARG.substr_non_string_arg1.d  | 18 ++++
 .../err.D_PROTO_ARG.substr_non_string_arg1.r  |  4 +
 .../err.D_PROTO_LEN.substr_missing_arg.d      | 18 ++++
 .../err.D_PROTO_LEN.substr_missing_arg.r      |  2 +
 .../err.D_PROTO_LEN.substr_too_few_args.d     | 18 ++++
 .../err.D_PROTO_LEN.substr_too_few_args.r     |  2 +
 .../err.D_PROTO_LEN.substr_too_many_args.d    | 18 ++++
 .../err.D_PROTO_LEN.substr_too_many_args.r    |  2 +
 .../funcs/substr/err.substr_null_arg1.d       | 25 +++++
 .../funcs/substr/err.substr_null_arg1.r       |  3 +
 .../{dif => funcs/substr}/substr2arg.d        |  1 -
 test/unittest/funcs/substr/substr2arg.r       |  0
 .../{dif => funcs/substr}/substr3arg.d        |  1 -
 test/unittest/funcs/substr/substr3arg.r       |  0
 .../funcs/substr/tst.substr-large-idx.d       | 22 +++++
 .../funcs/substr/tst.substr-large-idx.r       |  1 +
 .../substr/tst.substr-multi-const-cnt-neg.d   | 30 ++++++
 .../substr/tst.substr-multi-const-cnt-neg.r   | 11 +++
 .../substr/tst.substr-multi-const-cnt-pos.d   | 31 ++++++
 .../substr/tst.substr-multi-const-cnt-pos.r   | 12 +++
 .../substr/tst.substr-multi-const-idx-neg.d   | 30 ++++++
 .../substr/tst.substr-multi-const-idx-neg.r   | 11 +++
 .../substr/tst.substr-multi-const-idx-pos.d   | 31 ++++++
 .../substr/tst.substr-multi-const-idx-pos.r   | 12 +++
 .../substr/tst.substr-multi-var-cnt-neg.d     | 40 ++++++++
 .../substr/tst.substr-multi-var-cnt-neg.r     | 11 +++
 .../substr/tst.substr-multi-var-cnt-pos.d     | 42 ++++++++
 .../substr/tst.substr-multi-var-cnt-pos.r     | 12 +++
 .../substr/tst.substr-multi-var-idx-neg.d     | 40 ++++++++
 .../substr/tst.substr-multi-var-idx-neg.r     | 11 +++
 .../substr/tst.substr-multi-var-idx-pos.d     | 42 ++++++++
 .../substr/tst.substr-multi-var-idx-pos.r     | 12 +++
 .../funcs/substr/tst.substr-strjoin.d         | 33 +++++++
 .../funcs/substr/tst.substr-strjoin.r         |  1 +
 test/unittest/funcs/{ => substr}/tst.substr.d |  0
 test/unittest/funcs/{ => substr}/tst.substr.r |  0
 .../funcs/{ => substr}/tst.substrminate.d     |  0
 .../funcs/{ => substr}/tst.substrminate.r     |  0
 47 files changed, 748 insertions(+), 6 deletions(-)
 create mode 100644 bpf/substr.S
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.d
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.r
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.d
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.r
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.d
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.r
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.d
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.r
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.d
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.r
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.d
 create mode 100644 test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.r
 create mode 100644 test/unittest/funcs/substr/err.substr_null_arg1.d
 create mode 100644 test/unittest/funcs/substr/err.substr_null_arg1.r
 rename test/unittest/{dif => funcs/substr}/substr2arg.d (71%)
 create mode 100644 test/unittest/funcs/substr/substr2arg.r
 rename test/unittest/{dif => funcs/substr}/substr3arg.d (72%)
 create mode 100644 test/unittest/funcs/substr/substr3arg.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-large-idx.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-large-idx.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-const-cnt-neg.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-const-cnt-neg.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-const-cnt-pos.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-const-cnt-pos.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-const-idx-neg.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-const-idx-neg.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-const-idx-pos.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-const-idx-pos.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-var-cnt-neg.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-var-cnt-neg.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-var-cnt-pos.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-var-cnt-pos.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-var-idx-neg.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-var-idx-neg.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-var-idx-pos.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-multi-var-idx-pos.r
 create mode 100644 test/unittest/funcs/substr/tst.substr-strjoin.d
 create mode 100644 test/unittest/funcs/substr/tst.substr-strjoin.r
 rename test/unittest/funcs/{ => substr}/tst.substr.d (100%)
 rename test/unittest/funcs/{ => substr}/tst.substr.r (100%)
 rename test/unittest/funcs/{ => substr}/tst.substrminate.d (100%)
 rename test/unittest/funcs/{ => substr}/tst.substrminate.r (100%)

diff --git a/bpf/Build b/bpf/Build
index d7dbbd5a..5ea497a3 100644
--- a/bpf/Build
+++ b/bpf/Build
@@ -27,6 +27,7 @@ bpf_dlib_SOURCES = \
 	get_tvar.c set_tvar.c \
 	probe_error.c \
 	strjoin.S \
+	substr.S \
 	strlen.c
 
 bpf-check: $(objdir)/include/.dir.stamp
diff --git a/bpf/substr.S b/bpf/substr.S
new file mode 100644
index 00000000..9e032441
--- /dev/null
+++ b/bpf/substr.S
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#define DT_STRLEN_BYTES		2
+
+#define BPF_FUNC_probe_read	4
+
+/*
+ * void dt_substr(char *dst, const char *src, int32_t idx, int32_t cnt,
+ *		  uint64_t argc)
+ */
+	.text
+	.align	4
+	.global	dt_substr
+dt_substr :
+	stxdw	[%fp+-8], %r1		/* Spill dst */
+	mov	%r9, %r1
+	stxdw	[%fp+-16], %r2		/* Spill src */
+	lsh	%r3, 32			/* Sign-extend idx */
+	arsh	%r3, 32
+	mov	%r6, %r3		/* %r6 = idx */
+
+	lddw	%r8, STRSZ		/* %r8 = STRSZ (temporary) */
+	jgt	%r5, 2, .Lhave_cnt
+	mov	%r4, %r8		/* cnt = STRSZ */
+.Lhave_cnt:
+	lsh	%r4, 32			/* Sign-extend cnt */
+	arsh	%r4, 32
+	mov	%r7, %r4		/* %r7 = cnt */
+
+	/*
+	 * Get the source string length and validate it.  If the length is 0,
+	 * the result is the empty string.  If the length is greater than the
+	 * maximum string length (STRSZ), cap it at that value.
+	 */
+	ldxdw	%r1, [%fp+-16]
+	call	dt_strlen		/* len = dt_strlen(src) */
+	jeq	%r0, 0, .Lempty
+	mov	%r1, %r8		/* %r1 = STRSZ */
+	jle	%r0, %r8, .Llen_ok
+	mov	%r0, %r8		/* len = STRSZ */
+.Llen_ok:
+	mov	%r8, %r0		/* %r8 = len */
+
+	jsge	%r6, 0, .Ladjust_cnt
+
+.Lidx_neg:
+	add	%r6, %r8		/* idx += len */
+	jsge	%r6, 0, .Ladjust_cnt
+	mov	%r0, 0
+	sub32	%r0, %r6		/* neg messes up the verifier */
+	jsle	%r7, %r0, .Ladjust_cnt
+	add32	%r7, %r6		/* cnt += idx */
+	mov	%r6, 0			/* idx = 0 */
+
+.Ladjust_cnt:
+	jsge	%r6, %r1, .Lempty
+	jsge	%r6, %r8, .Lempty
+	jslt	%r6, 0, .Lempty
+	jsge	%r7, 0, .Lcnt_pos
+	mov	%r0, %r8
+	sub32	%r0, %r6
+	add32	%r7, %r0		/* cnt += len - idx */
+	lsh	%r7, 32
+	arsh	%r7, 32
+	jsle	%r7, 0, .Lempty
+.Lcnt_pos:
+	sub	%r1, %r6
+	jsge	%r1, %r7, .Lcopy
+	mov	%r7, %r1		/* cnt = STRSZ - idx */
+
+.Lcopy:
+	ldxdw	%r8, [%fp+-16]
+	add	%r8, DT_STRLEN_BYTES
+	add	%r8, %r6		/* %r8 = src + DT_STRLEN_BYTES + idx */
+
+	mov	%r1, %r7
+	ldxdw	%r2, [%fp+-8]
+	mov	%r9, %r2		/* %r9 = dst */
+	call	dt_strlen_store		/* plen = dt_strlen_store(cnt, dst) */
+	add	%r9, DT_STRLEN_BYTES	/* %r9 = dst + DT_STRLEN_BYTES */
+
+	mov	%r1, %r9
+	mov	%r2, %r7
+	mov	%r3, %r8
+	call	BPF_FUNC_probe_read
+
+	add	%r9, %r7
+	stb	[%r9+0],0
+	exit
+
+.Lempty:
+	/* Store the empty string in the destination. */
+	stb	[%r9+0], 0
+	stb	[%r9+1], 0
+	exit
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index b6e44a6e..58de3b4a 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -297,8 +297,7 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
 	 * value that is used to store the strtab.
 	 */
 	dtp->dt_strlen = dt_strtab_size(dtp->dt_ccstab);
-	stabsz = dtp->dt_strlen +
-		 ctf_type_size(DT_STR_CTFP(dtp), DT_STR_TYPE(dtp));
+	stabsz = dtp->dt_strlen + dtp->dt_options[DTRACEOPT_STRSIZE];
 	dtp->dt_strtab = dt_zalloc(dtp, stabsz);
 	if (dtp->dt_strtab == NULL)
 		return dt_set_errno(dtp, EDT_NOMEM);
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index b48b9f7b..749324e4 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -3149,6 +3149,63 @@ dt_cg_subr_strjoin(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	TRACE_REGSET("    subr-strjoin:End  ");
 }
 
+static void
+dt_cg_subr_substr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
+{
+	dt_node_t	*str = dnp->dn_args;
+	dt_node_t	*idx = str->dn_list;
+	dt_node_t	*cnt = idx->dn_list;
+	dt_ident_t	*idp;
+
+	TRACE_REGSET("    subr-substr:Begin");
+
+	dt_cg_node(str, dlp, drp);
+	dt_cg_check_notnull(dlp, drp, str->dn_reg);
+	dt_cg_node(idx, dlp, drp);
+	if (cnt != NULL)
+		dt_cg_node(cnt, dlp, drp);
+
+	/*
+	 * Allocate the result register and associate it with a temporary
+	 * string slot.
+	 */
+	dnp->dn_reg = dt_regset_alloc(drp);
+	if (dnp->dn_reg == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	dt_node_tstring(dnp, dt_cg_tstring_alloc());
+
+        emit(dlp,  BPF_LOAD(BPF_DW, dnp->dn_reg, BPF_REG_FP, DT_STK_DCTX));
+        emit(dlp,  BPF_LOAD(BPF_DW, dnp->dn_reg, dnp->dn_reg, DCTX_MEM));
+        emit(dlp,  BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, dnp->dn_tstring->dn_value));
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	emit(dlp, BPF_MOV_REG(BPF_REG_1, dnp->dn_reg));
+	emit(dlp, BPF_MOV_REG(BPF_REG_2, str->dn_reg));
+	dt_regset_free(drp, str->dn_reg);
+	if (str->dn_tstring)
+		dt_cg_tstring_free(str->dn_tstring->dn_value);
+	emit(dlp, BPF_MOV_REG(BPF_REG_3, idx->dn_reg));
+	dt_regset_free(drp, idx->dn_reg);
+	if (cnt != NULL) {
+		emit(dlp, BPF_MOV_REG(BPF_REG_4, cnt->dn_reg));
+		dt_regset_free(drp, cnt->dn_reg);
+		emit(dlp, BPF_MOV_IMM(BPF_REG_5, 3));
+	} else {
+		emit(dlp, BPF_MOV_IMM(BPF_REG_4, 0));
+		emit(dlp, BPF_MOV_IMM(BPF_REG_5, 2));
+	}
+	dt_regset_xalloc(drp, BPF_REG_0);
+	idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_substr");
+	assert(idp != NULL);
+	emite(dlp,  BPF_CALL_FUNC(idp->di_id), idp);
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+
+	TRACE_REGSET("    subr-substr:End  ");
+}
+
 typedef void dt_cg_subr_f(dt_node_t *, dt_irlist_t *, dt_regset_t *);
 
 static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
@@ -3184,7 +3241,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
 	[DIF_SUBR_STRRCHR]		= NULL,
 	[DIF_SUBR_STRSTR]		= NULL,
 	[DIF_SUBR_STRTOK]		= NULL,
-	[DIF_SUBR_SUBSTR]		= NULL,
+	[DIF_SUBR_SUBSTR]		= &dt_cg_subr_substr,
 	[DIF_SUBR_INDEX]		= NULL,
 	[DIF_SUBR_RINDEX]		= NULL,
 	[DIF_SUBR_HTONS]		= NULL,
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index 771b7fe0..d87ccb5f 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -60,7 +60,7 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	DT_BPF_SYMBOL(dt_get_tvar, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_set_tvar, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_strjoin, DT_IDENT_SYMBOL),
-	DT_BPF_SYMBOL(dt_strnlen, DT_IDENT_SYMBOL),
+	DT_BPF_SYMBOL(dt_substr, DT_IDENT_SYMBOL),
 	/* BPF maps */
 	DT_BPF_SYMBOL(aggs, DT_IDENT_PTR),
 	DT_BPF_SYMBOL(buffers, DT_IDENT_PTR),
diff --git a/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.d b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.d
new file mode 100644
index 00000000..bb2dc33a
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.d
@@ -0,0 +1,18 @@
+/*
+ * 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: The second argument to substr() should be a scalar.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+BEGIN
+{
+	trace(substr(probename, "a"));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.r b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.r
new file mode 100644
index 00000000..14a7e7f5
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.r
@@ -0,0 +1,4 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg2.d: [D_PROTO_ARG] line 16: substr( ) argument #2 is incompatible with prototype:
+	prototype: int
+	 argument: string
diff --git a/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.d b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.d
new file mode 100644
index 00000000..147ec362
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.d
@@ -0,0 +1,18 @@
+/*
+ * 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: The third argument to substr() should be a scalar.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+BEGIN
+{
+	trace(substr(probename, 1, "a"));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.r b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.r
new file mode 100644
index 00000000..102aef8e
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.r
@@ -0,0 +1,4 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_scalar_arg3.d: [D_PROTO_ARG] line 16: substr( ) argument #3 is incompatible with prototype:
+	prototype: int
+	 argument: string
diff --git a/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.d b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.d
new file mode 100644
index 00000000..bd9d1319
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.d
@@ -0,0 +1,18 @@
+/*
+ * 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: The first argument to substr() should be a string.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+BEGIN
+{
+	trace(substr(10, 1));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.r b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.r
new file mode 100644
index 00000000..85210c3c
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.r
@@ -0,0 +1,4 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/funcs/substr/err.D_PROTO_ARG.substr_non_string_arg1.d: [D_PROTO_ARG] line 16: substr( ) argument #1 is incompatible with prototype:
+	prototype: char *
+	 argument: int
diff --git a/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.d b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.d
new file mode 100644
index 00000000..c254e38a
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.d
@@ -0,0 +1,18 @@
+/*
+ * 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: substr() requires a string argument
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+BEGIN
+{
+	trace(substr());
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.r b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.r
new file mode 100644
index 00000000..b24cef0c
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/funcs/substr/err.D_PROTO_LEN.substr_missing_arg.d: [D_PROTO_LEN] line 16: substr( ) prototype mismatch: 0 args passed, at least 2 expected
diff --git a/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.d b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.d
new file mode 100644
index 00000000..2d6eccac
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.d
@@ -0,0 +1,18 @@
+/*
+ * 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: The substr() subroutine requires at least 2 arguments.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+BEGIN
+{
+	trace(substr(probename));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.r b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.r
new file mode 100644
index 00000000..5697f41f
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_few_args.d: [D_PROTO_LEN] line 16: substr( ) prototype mismatch: 1 arg passed, at least 2 expected
diff --git a/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.d b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.d
new file mode 100644
index 00000000..d69e93e6
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.d
@@ -0,0 +1,18 @@
+/*
+ * 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: The substr() subroutine accepts no more than three arguments.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+BEGIN
+{
+	trace(substr(probename, 1, 2, 3));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.r b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.r
new file mode 100644
index 00000000..1f82676f
--- /dev/null
+++ b/test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/funcs/substr/err.D_PROTO_LEN.substr_too_many_args.d: [D_PROTO_LEN] line 16: substr( ) prototype mismatch: 4 args passed, at most 3 expected
diff --git a/test/unittest/funcs/substr/err.substr_null_arg1.d b/test/unittest/funcs/substr/err.substr_null_arg1.d
new file mode 100644
index 00000000..50f9091e
--- /dev/null
+++ b/test/unittest/funcs/substr/err.substr_null_arg1.d
@@ -0,0 +1,25 @@
+/*
+ * 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: The first argument to substr() cannot be NULL.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	trace(substr(0, 1));
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/funcs/substr/err.substr_null_arg1.r b/test/unittest/funcs/substr/err.substr_null_arg1.r
new file mode 100644
index 00000000..ef424aed
--- /dev/null
+++ b/test/unittest/funcs/substr/err.substr_null_arg1.r
@@ -0,0 +1,3 @@
+
+-- @@stderr --
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/dif/substr2arg.d b/test/unittest/funcs/substr/substr2arg.d
similarity index 71%
rename from test/unittest/dif/substr2arg.d
rename to test/unittest/funcs/substr/substr2arg.d
index b29c601b..33670550 100644
--- a/test/unittest/dif/substr2arg.d
+++ b/test/unittest/funcs/substr/substr2arg.d
@@ -1,4 +1,3 @@
-/* @@xfail: dtv2 */
 BEGIN
 {
 	trace(substr(probename, 2));
diff --git a/test/unittest/funcs/substr/substr2arg.r b/test/unittest/funcs/substr/substr2arg.r
new file mode 100644
index 00000000..e69de29b
diff --git a/test/unittest/dif/substr3arg.d b/test/unittest/funcs/substr/substr3arg.d
similarity index 72%
rename from test/unittest/dif/substr3arg.d
rename to test/unittest/funcs/substr/substr3arg.d
index e2768b0a..8958acac 100644
--- a/test/unittest/dif/substr3arg.d
+++ b/test/unittest/funcs/substr/substr3arg.d
@@ -1,4 +1,3 @@
-/* @@xfail: dtv2 */
 BEGIN
 {
 	trace(substr(probename, 0, 2));
diff --git a/test/unittest/funcs/substr/substr3arg.r b/test/unittest/funcs/substr/substr3arg.r
new file mode 100644
index 00000000..e69de29b
diff --git a/test/unittest/funcs/substr/tst.substr-large-idx.d b/test/unittest/funcs/substr/tst.substr-large-idx.d
new file mode 100644
index 00000000..654a05e9
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-large-idx.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: The substr() subroutine supports index values well beyond the
+ *	      maximum string size.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+#pragma D option strsize=256
+
+BEGIN
+{
+	trace(substr("abcdefgh", 12345678));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-large-idx.r b/test/unittest/funcs/substr/tst.substr-large-idx.r
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-large-idx.r
@@ -0,0 +1 @@
+
diff --git a/test/unittest/funcs/substr/tst.substr-multi-const-cnt-neg.d b/test/unittest/funcs/substr/tst.substr-multi-const-cnt-neg.d
new file mode 100644
index 00000000..c6e1230f
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-const-cnt-neg.d
@@ -0,0 +1,30 @@
+/*
+ * 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: The substr() subroutine supports negative count values passed as
+ *	      constants.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	printf("\n% 3d % 3d %s", 2, -10, substr("abcdefghijklmnop", 2, -10));
+	printf("\n% 3d % 3d %s", 2,  -9, substr("abcdefghijklmnop", 2, -9));
+	printf("\n% 3d % 3d %s", 2,  -8, substr("abcdefghijklmnop", 2, -8));
+	printf("\n% 3d % 3d %s", 2,  -7, substr("abcdefghijklmnop", 2, -7));
+	printf("\n% 3d % 3d %s", 2,  -6, substr("abcdefghijklmnop", 2, -6));
+	printf("\n% 3d % 3d %s", 2,  -5, substr("abcdefghijklmnop", 2, -5));
+	printf("\n% 3d % 3d %s", 2,  -4, substr("abcdefghijklmnop", 2, -4));
+	printf("\n% 3d % 3d %s", 2,  -3, substr("abcdefghijklmnop", 2, -3));
+	printf("\n% 3d % 3d %s", 2,  -2, substr("abcdefghijklmnop", 2, -2));
+	printf("\n% 3d % 3d %s", 2,  -1, substr("abcdefghijklmnop", 2, -1));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-multi-const-cnt-neg.r b/test/unittest/funcs/substr/tst.substr-multi-const-cnt-neg.r
new file mode 100644
index 00000000..555db8e0
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-const-cnt-neg.r
@@ -0,0 +1,11 @@
+
+  2 -10 cdef
+  2  -9 cdefg
+  2  -8 cdefgh
+  2  -7 cdefghi
+  2  -6 cdefghij
+  2  -5 cdefghijk
+  2  -4 cdefghijkl
+  2  -3 cdefghijklm
+  2  -2 cdefghijklmn
+  2  -1 cdefghijklmno
diff --git a/test/unittest/funcs/substr/tst.substr-multi-const-cnt-pos.d b/test/unittest/funcs/substr/tst.substr-multi-const-cnt-pos.d
new file mode 100644
index 00000000..607df972
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-const-cnt-pos.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: The substr() subroutine supports positive count values passed as
+ *	      constants.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	printf("\n% 3d % 3d %s", 2,   0, substr("abcdefghijklmnop", 2, 0));
+	printf("\n% 3d % 3d %s", 2,   1, substr("abcdefghijklmnop", 2, 1));
+	printf("\n% 3d % 3d %s", 2,   2, substr("abcdefghijklmnop", 2, 2));
+	printf("\n% 3d % 3d %s", 2,   3, substr("abcdefghijklmnop", 2, 3));
+	printf("\n% 3d % 3d %s", 2,   4, substr("abcdefghijklmnop", 2, 4));
+	printf("\n% 3d % 3d %s", 2,   5, substr("abcdefghijklmnop", 2, 5));
+	printf("\n% 3d % 3d %s", 2,   6, substr("abcdefghijklmnop", 2, 6));
+	printf("\n% 3d % 3d %s", 2,   7, substr("abcdefghijklmnop", 2, 7));
+	printf("\n% 3d % 3d %s", 2,   8, substr("abcdefghijklmnop", 2, 8));
+	printf("\n% 3d % 3d %s", 2,   9, substr("abcdefghijklmnop", 2, 9));
+	printf("\n% 3d % 3d %s", 2,  10, substr("abcdefghijklmnop", 2, 10));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-multi-const-cnt-pos.r b/test/unittest/funcs/substr/tst.substr-multi-const-cnt-pos.r
new file mode 100644
index 00000000..88b87861
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-const-cnt-pos.r
@@ -0,0 +1,12 @@
+
+  2   0 
+  2   1 c
+  2   2 cd
+  2   3 cde
+  2   4 cdef
+  2   5 cdefg
+  2   6 cdefgh
+  2   7 cdefghi
+  2   8 cdefghij
+  2   9 cdefghijk
+  2  10 cdefghijkl
diff --git a/test/unittest/funcs/substr/tst.substr-multi-const-idx-neg.d b/test/unittest/funcs/substr/tst.substr-multi-const-idx-neg.d
new file mode 100644
index 00000000..d69af788
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-const-idx-neg.d
@@ -0,0 +1,30 @@
+/*
+ * 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: The substr() subroutine supports negative index values passed as
+ *	      constants.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	printf("\n% 3d % 3d %s", -10, 3, substr("abcdefghijklmnop", -10, 3));
+	printf("\n% 3d % 3d %s",  -9, 3, substr("abcdefghijklmnop", -9, 3));
+	printf("\n% 3d % 3d %s",  -8, 3, substr("abcdefghijklmnop", -8, 3));
+	printf("\n% 3d % 3d %s",  -7, 3, substr("abcdefghijklmnop", -7, 3));
+	printf("\n% 3d % 3d %s",  -6, 3, substr("abcdefghijklmnop", -6, 3));
+	printf("\n% 3d % 3d %s",  -5, 3, substr("abcdefghijklmnop", -5, 3));
+	printf("\n% 3d % 3d %s",  -4, 3, substr("abcdefghijklmnop", -4, 3));
+	printf("\n% 3d % 3d %s",  -3, 3, substr("abcdefghijklmnop", -3, 3));
+	printf("\n% 3d % 3d %s",  -2, 3, substr("abcdefghijklmnop", -2, 3));
+	printf("\n% 3d % 3d %s",  -1, 3, substr("abcdefghijklmnop", -1, 3));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-multi-const-idx-neg.r b/test/unittest/funcs/substr/tst.substr-multi-const-idx-neg.r
new file mode 100644
index 00000000..d8dc0dbd
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-const-idx-neg.r
@@ -0,0 +1,11 @@
+
+-10   3 ghi
+ -9   3 hij
+ -8   3 ijk
+ -7   3 jkl
+ -6   3 klm
+ -5   3 lmn
+ -4   3 mno
+ -3   3 nop
+ -2   3 op
+ -1   3 p
diff --git a/test/unittest/funcs/substr/tst.substr-multi-const-idx-pos.d b/test/unittest/funcs/substr/tst.substr-multi-const-idx-pos.d
new file mode 100644
index 00000000..558b747f
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-const-idx-pos.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: The substr() subroutine supports positive index values passed as
+ *	      constants.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	printf("\n% 3d % 3d %s",   0, 3, substr("abcdefghijklmnop", 0, 3));
+	printf("\n% 3d % 3d %s",   1, 3, substr("abcdefghijklmnop", 1, 3));
+	printf("\n% 3d % 3d %s",   2, 3, substr("abcdefghijklmnop", 2, 3));
+	printf("\n% 3d % 3d %s",   3, 3, substr("abcdefghijklmnop", 3, 3));
+	printf("\n% 3d % 3d %s",   4, 3, substr("abcdefghijklmnop", 4, 3));
+	printf("\n% 3d % 3d %s",   5, 3, substr("abcdefghijklmnop", 5, 3));
+	printf("\n% 3d % 3d %s",   6, 3, substr("abcdefghijklmnop", 6, 3));
+	printf("\n% 3d % 3d %s",   7, 3, substr("abcdefghijklmnop", 7, 3));
+	printf("\n% 3d % 3d %s",   8, 3, substr("abcdefghijklmnop", 8, 3));
+	printf("\n% 3d % 3d %s",   9, 3, substr("abcdefghijklmnop", 9, 3));
+	printf("\n% 3d % 3d %s",  10, 3, substr("abcdefghijklmnop", 10, 3));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-multi-const-idx-pos.r b/test/unittest/funcs/substr/tst.substr-multi-const-idx-pos.r
new file mode 100644
index 00000000..bf1f462f
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-const-idx-pos.r
@@ -0,0 +1,12 @@
+
+  0   3 abc
+  1   3 bcd
+  2   3 cde
+  3   3 def
+  4   3 efg
+  5   3 fgh
+  6   3 ghi
+  7   3 hij
+  8   3 ijk
+  9   3 jkl
+ 10   3 klm
diff --git a/test/unittest/funcs/substr/tst.substr-multi-var-cnt-neg.d b/test/unittest/funcs/substr/tst.substr-multi-var-cnt-neg.d
new file mode 100644
index 00000000..84b6efcb
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-var-cnt-neg.d
@@ -0,0 +1,40 @@
+/*
+ * 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: The substr() subroutine supports negative count values passed by
+ *	      variable.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	cnt = -10;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -9;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -8;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -7;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -6;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -5;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -4;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -3;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -2;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = -1;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-multi-var-cnt-neg.r b/test/unittest/funcs/substr/tst.substr-multi-var-cnt-neg.r
new file mode 100644
index 00000000..555db8e0
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-var-cnt-neg.r
@@ -0,0 +1,11 @@
+
+  2 -10 cdef
+  2  -9 cdefg
+  2  -8 cdefgh
+  2  -7 cdefghi
+  2  -6 cdefghij
+  2  -5 cdefghijk
+  2  -4 cdefghijkl
+  2  -3 cdefghijklm
+  2  -2 cdefghijklmn
+  2  -1 cdefghijklmno
diff --git a/test/unittest/funcs/substr/tst.substr-multi-var-cnt-pos.d b/test/unittest/funcs/substr/tst.substr-multi-var-cnt-pos.d
new file mode 100644
index 00000000..7bd234ad
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-var-cnt-pos.d
@@ -0,0 +1,42 @@
+/*
+ * 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: The substr() subroutine supports positive count values passed by
+ *	      variable.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	cnt = 0;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 1;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 2;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 3;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 4;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 5;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 6;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 7;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 8;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 9;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	cnt = 10;
+	printf("\n% 3d % 3d %s", 2, cnt, substr("abcdefghijklmnop", 2, cnt));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-multi-var-cnt-pos.r b/test/unittest/funcs/substr/tst.substr-multi-var-cnt-pos.r
new file mode 100644
index 00000000..88b87861
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-var-cnt-pos.r
@@ -0,0 +1,12 @@
+
+  2   0 
+  2   1 c
+  2   2 cd
+  2   3 cde
+  2   4 cdef
+  2   5 cdefg
+  2   6 cdefgh
+  2   7 cdefghi
+  2   8 cdefghij
+  2   9 cdefghijk
+  2  10 cdefghijkl
diff --git a/test/unittest/funcs/substr/tst.substr-multi-var-idx-neg.d b/test/unittest/funcs/substr/tst.substr-multi-var-idx-neg.d
new file mode 100644
index 00000000..232e2f5f
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-var-idx-neg.d
@@ -0,0 +1,40 @@
+/*
+ * 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: The substr() subroutine supports negative index values passed by
+ *	      variable.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	idx = -10;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -9;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -8;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -7;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -6;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -5;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -4;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -3;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -2;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = -1;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-multi-var-idx-neg.r b/test/unittest/funcs/substr/tst.substr-multi-var-idx-neg.r
new file mode 100644
index 00000000..d8dc0dbd
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-var-idx-neg.r
@@ -0,0 +1,11 @@
+
+-10   3 ghi
+ -9   3 hij
+ -8   3 ijk
+ -7   3 jkl
+ -6   3 klm
+ -5   3 lmn
+ -4   3 mno
+ -3   3 nop
+ -2   3 op
+ -1   3 p
diff --git a/test/unittest/funcs/substr/tst.substr-multi-var-idx-pos.d b/test/unittest/funcs/substr/tst.substr-multi-var-idx-pos.d
new file mode 100644
index 00000000..eb38999e
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-var-idx-pos.d
@@ -0,0 +1,42 @@
+/*
+ * 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: The substr() subroutine supports positive index values passed by
+ *	      variable.
+ *
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	idx = 0;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 1;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 2;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 3;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 4;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 5;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 6;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 7;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 8;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 9;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	idx = 10;
+	printf("\n% 3d % 3d %s", idx, 3, substr("abcdefghijklmnop", idx, 3));
+	exit(0);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-multi-var-idx-pos.r b/test/unittest/funcs/substr/tst.substr-multi-var-idx-pos.r
new file mode 100644
index 00000000..bf1f462f
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-multi-var-idx-pos.r
@@ -0,0 +1,12 @@
+
+  0   3 abc
+  1   3 bcd
+  2   3 cde
+  3   3 def
+  4   3 efg
+  5   3 fgh
+  6   3 ghi
+  7   3 hij
+  8   3 ijk
+  9   3 jkl
+ 10   3 klm
diff --git a/test/unittest/funcs/substr/tst.substr-strjoin.d b/test/unittest/funcs/substr/tst.substr-strjoin.d
new file mode 100644
index 00000000..f844d261
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-strjoin.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: Combining strjoin) and substr() works.
+ *
+ * SECTION: Actions and Subroutines/strjoin()
+ * SECTION: Actions and Subroutines/substr()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	trace(
+	    substr(
+		strjoin(
+		    strjoin(substr(probeprov, 0, 2), substr(probeprov, 2)),
+		    strjoin(substr(probename, 0, 3), substr(probename, 3))
+		), 6, 3
+	    )
+	);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/funcs/substr/tst.substr-strjoin.r b/test/unittest/funcs/substr/tst.substr-strjoin.r
new file mode 100644
index 00000000..f7836aea
--- /dev/null
+++ b/test/unittest/funcs/substr/tst.substr-strjoin.r
@@ -0,0 +1 @@
+BEG
diff --git a/test/unittest/funcs/tst.substr.d b/test/unittest/funcs/substr/tst.substr.d
similarity index 100%
rename from test/unittest/funcs/tst.substr.d
rename to test/unittest/funcs/substr/tst.substr.d
diff --git a/test/unittest/funcs/tst.substr.r b/test/unittest/funcs/substr/tst.substr.r
similarity index 100%
rename from test/unittest/funcs/tst.substr.r
rename to test/unittest/funcs/substr/tst.substr.r
diff --git a/test/unittest/funcs/tst.substrminate.d b/test/unittest/funcs/substr/tst.substrminate.d
similarity index 100%
rename from test/unittest/funcs/tst.substrminate.d
rename to test/unittest/funcs/substr/tst.substrminate.d
diff --git a/test/unittest/funcs/tst.substrminate.r b/test/unittest/funcs/substr/tst.substrminate.r
similarity index 100%
rename from test/unittest/funcs/tst.substrminate.r
rename to test/unittest/funcs/substr/tst.substrminate.r
-- 
2.33.0




More information about the DTrace-devel mailing list