[DTrace-devel] [PATCH 3/3] Add support for strjoin() function

Kris Van Hees kris.van.hees at oracle.com
Wed Sep 1 23:55:08 PDT 2021


Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 bpf/Build                                     |   1 +
 bpf/strjoin.S                                 |  55 ++++++
 libdtrace/dt_cg.c                             | 179 +++++++++++++++++-
 libdtrace/dt_dlibs.c                          |   1 +
 test/unittest/dif/strjoin.d                   |   6 +-
 .../funcs/strjoin/tst.strjoin-nested.d        |  34 ++++
 .../funcs/strjoin/tst.strjoin-nested.r        |   1 +
 .../funcs/{ => strjoin}/tst.strjoin.d         |   1 -
 .../funcs/{ => strjoin}/tst.strjoin.r         |   0
 9 files changed, 270 insertions(+), 8 deletions(-)
 create mode 100644 bpf/strjoin.S
 create mode 100644 test/unittest/funcs/strjoin/tst.strjoin-nested.d
 create mode 100644 test/unittest/funcs/strjoin/tst.strjoin-nested.r
 rename test/unittest/funcs/{ => strjoin}/tst.strjoin.d (95%)
 rename test/unittest/funcs/{ => strjoin}/tst.strjoin.r (100%)

diff --git a/bpf/Build b/bpf/Build
index e7682bc7..d7dbbd5a 100644
--- a/bpf/Build
+++ b/bpf/Build
@@ -26,6 +26,7 @@ bpf_dlib_SOURCES = \
 	get_bvar.c \
 	get_tvar.c set_tvar.c \
 	probe_error.c \
+	strjoin.S \
 	strlen.c
 
 bpf-check: $(objdir)/include/.dir.stamp
diff --git a/bpf/strjoin.S b/bpf/strjoin.S
new file mode 100644
index 00000000..2a19f2eb
--- /dev/null
+++ b/bpf/strjoin.S
@@ -0,0 +1,55 @@
+// 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_str	45
+
+/*
+ * void dt_strjoin(char *dst, const char *s1, const char *s2)
+ */
+	.text
+	.align	4
+	.global	dt_strjoin
+	.type	dt_strjoin, @function
+dt_strjoin:
+	mov	%r9, %r1
+	mov	%r7, %r2
+	mov	%r8, %r3
+
+	mov	%r1, %r7
+	call	dt_strlen
+	mov	%r6, %r0
+	add	%r7, DT_STRLEN_BYTES
+
+	mov	%r1, %r8
+	call	dt_strlen
+	add	%r6, %r0
+	add	%r8, DT_STRLEN_BYTES
+
+	mov	%r1, %r6
+	mov	%r2, %r9
+	call	dt_strlen_store
+	add	%r9, DT_STRLEN_BYTES
+
+	lddw	%r6, STRSZ
+	and	%r6, 0xffffffff
+
+	mov	%r1, %r9
+	mov	%r2, %r6
+	mov	%r3, %r7
+	call	BPF_FUNC_probe_read_str
+	jslt	%r0, 0, .L1
+	jslt	%r0, 1, .L2
+	add	%r9, %r0
+	sub	%r9, 1
+.L2:
+	mov	%r1, %r9
+	mov	%r2, %r6
+	mov	%r3, %r8
+	call	BPF_FUNC_probe_read_str
+.L1:
+	exit
+	.size	dt_strjoin, .-dt_strjoin
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index d8ffa479..738905fe 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -753,20 +753,31 @@ dt_cg_strlen(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src)
 	size_t		size = yypcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE];
 	uint_t		lbl_ok = dt_irlist_label(dlp);
 
+	TRACE_REGSET("        strlen:Begin");
+
 	assert(idp != NULL);
 	if (dt_regset_xalloc_args(drp) == -1)
 		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
 
-	emit(dlp,  BPF_MOV_REG(BPF_REG_1, src));
-	dt_regset_xalloc(drp, BPF_REG_0);
+	if (src != BPF_REG_1)
+		emit(dlp,  BPF_MOV_REG(BPF_REG_1, src));
+	if (dst != BPF_REG_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_JLE, BPF_REG_0, size, lbl_ok));
 	emit(dlp,  BPF_MOV_IMM(BPF_REG_0, size));
-	emitl(dlp, lbl_ok,
-		   BPF_MOV_REG(dst, BPF_REG_0));
-	dt_regset_free(drp, BPF_REG_0);
+
+	if (dst != BPF_REG_0) {
+		emitl(dlp, lbl_ok,
+			   BPF_MOV_REG(dst, BPF_REG_0));
+		dt_regset_free(drp, BPF_REG_0);
+	} else
+		emitl(dlp, lbl_ok,
+			   BPF_NOP());
+
+	TRACE_REGSET("        strlen:End  ");
 }
 
 static void
@@ -3088,6 +3099,162 @@ dt_cg_subr_strlen(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	TRACE_REGSET("    subr-strlen:End  ");
 }
 
+static void
+dt_cg_subr_strjoin(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
+{
+#if 1
+	dt_node_t	*s1 = dnp->dn_args;
+	dt_node_t	*s2 = s1->dn_list;
+	dt_ident_t	*idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_strjoin");
+
+	assert(idp != NULL);
+
+	TRACE_REGSET("    subr-strjoin:Begin");
+
+	dt_cg_node(s1, dlp, drp);
+	dt_cg_check_notnull(dlp, drp, s1->dn_reg);
+	dt_cg_node(s2, dlp, drp);
+	dt_cg_check_notnull(dlp, drp, s2->dn_reg);
+
+	/*
+	 * We can re-use the register from s1 for our result.  The result needs
+	 * be be a temporary string, so we need to allocate one.
+	 */
+	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, s1->dn_reg));
+	dt_regset_free(drp, s1->dn_reg);
+	if (s1->dn_tstring)
+		dt_cg_tstring_free(s1->dn_tstring->dn_value);
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, s2->dn_reg));
+	dt_regset_free(drp, s2->dn_reg);
+	if (s2->dn_tstring)
+		dt_cg_tstring_free(s2->dn_tstring->dn_value);
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+
+	TRACE_REGSET("    subr-strjoin:End  ");
+#else
+	dt_node_t	*s1 = dnp->dn_args;
+	dt_node_t	*s2 = s1->dn_list;
+	dt_ident_t	*idp;
+	int		reg;
+	size_t		size = yypcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE];
+	uint_t		lbl_copy_ok = dt_irlist_label(dlp);
+	uint_t		lbl_append_ok = dt_irlist_label(dlp);
+
+	TRACE_REGSET("    subr-strjoin:Begin");
+
+	dt_cg_node(s1, dlp, drp);
+	dt_cg_check_notnull(dlp, drp, s1->dn_reg);
+	dt_cg_node(s2, dlp, drp);
+	dt_cg_check_notnull(dlp, drp, s2->dn_reg);
+
+	reg = dt_regset_alloc(drp);
+	if (reg == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	/*
+	 * Calculate the combined string length.  Get the length of each
+	 * string, advance the string pointer to the character data, and add
+	 * the string length together.
+	 */
+	dt_cg_strlen(dlp, drp, reg, s1->dn_reg);
+	emit(dlp,  BPF_ALU64_REG(BPF_ADD, s1->dn_reg, reg));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	dt_cg_strlen(dlp, drp, BPF_REG_0, s2->dn_reg);
+	emit(dlp,  BPF_ALU64_REG(BPF_ADD, s2->dn_reg, BPF_REG_0));
+	emit(dlp,  BPF_ALU64_REG(BPF_ADD, reg, BPF_REG_0));
+	dt_regset_free(drp, BPF_REG_0);
+
+	/*
+	 * We can re-use the register from s1 for our result.
+	 */
+	dnp->dn_reg = s1->dn_reg;
+	dt_node_tstring(dnp, dt_cg_tstring_alloc());
+
+	/*
+	 * Store the combined length as a variable-length integer in the
+	 * temporary string, and advance past the prefix.
+	 */
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	emit(dlp,  BPF_MOV_REG(BPF_REG_1, reg));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_2, BPF_REG_FP, DT_STK_DCTX));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_2, BPF_REG_2, DCTX_MEM));
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, dnp->dn_tstring->dn_value));
+	emit(dlp,  BPF_MOV_REG(reg, BPF_REG_2));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_int2vint");
+	assert(idp != NULL);
+	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+	dt_regset_free_args(drp);
+	emit(dlp,  BPF_ALU64_REG(BPF_ADD, reg, BPF_REG_0));
+	dt_regset_free(drp, BPF_REG_0);
+
+	/*
+	 * Copy the first string.
+	 */
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	emit(dlp,  BPF_MOV_REG(BPF_REG_1, reg));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, size));
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, s1->dn_reg));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read_str));
+	dt_regset_free_args(drp);
+	emit(dlp,  BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_copy_ok));
+	dt_cg_probe_error(yypcb, -1, DTRACEFLT_BADADDR, 0);
+	emitl(dlp, lbl_copy_ok,
+		   BPF_ALU64_REG(BPF_ADD, reg, BPF_REG_0));
+	emit(dlp,  BPF_ALU64_IMM(BPF_SUB, reg, 1));
+	dt_regset_free(drp, BPF_REG_0);
+	
+	/*
+	 * Append the second string.
+	 */
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	emit(dlp,  BPF_MOV_REG(BPF_REG_1, reg));
+	dt_regset_free(drp, reg);
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, size));
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, s2->dn_reg));
+	dt_regset_free(drp, s2->dn_reg);
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read_str));
+	dt_regset_free_args(drp);
+	emit(dlp,  BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_append_ok));
+	dt_cg_probe_error(yypcb, -1, DTRACEFLT_BADADDR, 0);
+	dt_regset_free(drp, BPF_REG_0);
+
+	/*
+	 * Put a pointer to the temporary string in our result register.
+	 */
+	emitl(dlp, lbl_append_ok,
+		   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));
+
+	TRACE_REGSET("    subr-strjoin:End  ");
+#endif
+}
+
 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] = {
@@ -3114,7 +3281,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
 	[DIF_SUBR_GETMAJOR]		= NULL,
 	[DIF_SUBR_GETMINOR]		= NULL,
 	[DIF_SUBR_DDI_PATHNAME]		= NULL,
-	[DIF_SUBR_STRJOIN]		= NULL,
+	[DIF_SUBR_STRJOIN]		= dt_cg_subr_strjoin,
 	[DIF_SUBR_LLTOSTR]		= NULL,
 	[DIF_SUBR_BASENAME]		= NULL,
 	[DIF_SUBR_DIRNAME]		= NULL,
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index 04becfa6..771b7fe0 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -59,6 +59,7 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	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_strjoin, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_strnlen, DT_IDENT_SYMBOL),
 	/* BPF maps */
 	DT_BPF_SYMBOL(aggs, DT_IDENT_PTR),
diff --git a/test/unittest/dif/strjoin.d b/test/unittest/dif/strjoin.d
index e7834eb9..ee70dec8 100644
--- a/test/unittest/dif/strjoin.d
+++ b/test/unittest/dif/strjoin.d
@@ -1,6 +1,10 @@
-/* @@xfail: dtv2 */
 BEGIN
 {
 	trace(strjoin(probeprov, probename));
 	exit(0);
 }
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/funcs/strjoin/tst.strjoin-nested.d b/test/unittest/funcs/strjoin/tst.strjoin-nested.d
new file mode 100644
index 00000000..84ba459c
--- /dev/null
+++ b/test/unittest/funcs/strjoin/tst.strjoin-nested.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.
+ */
+
+/*
+ * ASSERTION: Nested strjoin() works
+ *
+ * SECTION: Actions and Subroutines/strjoin()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	trace(strjoin(
+		strjoin(
+			strjoin(probeprov, ":"),
+			strjoin(probemod, ":")
+		),
+		strjoin(
+			strjoin(probefunc, ":"),
+			probename
+		)
+	      ));
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/funcs/strjoin/tst.strjoin-nested.r b/test/unittest/funcs/strjoin/tst.strjoin-nested.r
new file mode 100644
index 00000000..748852fb
--- /dev/null
+++ b/test/unittest/funcs/strjoin/tst.strjoin-nested.r
@@ -0,0 +1 @@
+dtrace:::BEGIN
diff --git a/test/unittest/funcs/tst.strjoin.d b/test/unittest/funcs/strjoin/tst.strjoin.d
similarity index 95%
rename from test/unittest/funcs/tst.strjoin.d
rename to test/unittest/funcs/strjoin/tst.strjoin.d
index beec500c..6afd2ded 100644
--- a/test/unittest/funcs/tst.strjoin.d
+++ b/test/unittest/funcs/strjoin/tst.strjoin.d
@@ -4,7 +4,6 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
 
 #pragma D option quiet
 
diff --git a/test/unittest/funcs/tst.strjoin.r b/test/unittest/funcs/strjoin/tst.strjoin.r
similarity index 100%
rename from test/unittest/funcs/tst.strjoin.r
rename to test/unittest/funcs/strjoin/tst.strjoin.r
-- 
2.33.0




More information about the DTrace-devel mailing list