[DTrace-devel] [PATCH 1/5] Work in Progress: Add support for string comparisons
eugene.loh at oracle.com
eugene.loh at oracle.com
Wed Aug 25 19:21:34 PDT 2021
From: Eugene Loh <eugene.loh at oracle.com>
The strcmp.S needs to be cleaned up a little to push less pressure
on the BPF verifier. Meanwhile, tst.str_comparison.d will fail.
Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
---
bpf/Build | 1 +
bpf/strcmp.S | 91 ++++++++++++++++++++
libdtrace/dt_cg.c | 32 ++++++-
libdtrace/dt_dlibs.c | 1 +
test/unittest/operators/tst.str_comparison.d | 45 ++++++++++
5 files changed, 166 insertions(+), 4 deletions(-)
create mode 100644 bpf/strcmp.S
create mode 100644 test/unittest/operators/tst.str_comparison.d
diff --git a/bpf/Build b/bpf/Build
index fe155c3a..287e6f6c 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 \
+ strcmp.S \
strnlen.c \
substr.S \
varint.c
diff --git a/bpf/strcmp.S b/bpf/strcmp.S
new file mode 100644
index 00000000..8c6d33a2
--- /dev/null
+++ b/bpf/strcmp.S
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * int dt_strcmp(char *s, char *t) {
+ * [%fp-8]=s
+ * [%fp-16]=t
+ *
+ * r6 = dt_vint2int(s);
+ * r7 = dt_vint_size(r6);
+ * r8 = dt_vint2int(t);
+ * r9 = dt_vint_size(r8);
+ *
+ * r1 = 0;
+ * r2 = min(r6, r8);
+ * Lloop:
+ * if (s[r7 + r1] > t[r9 + r1]) return +1;
+ * if (s[r7 + r1] < t[r9 + r1]) return -1;
+ * r1++;
+ * if (r1 < r6) goto Lloop;
+ * if (r6 > r8) return +1;
+ * if (r6 < r8) return -1;
+ * return 0;
+ * }
+ */
+ .text
+ .align 4
+ .global dt_strcmp
+dt_strcmp :
+ stxdw [%fp+-8], %r1 /* Spill s */
+ stxdw [%fp+-16], %r2 /* Spill t */
+
+ call dt_vint2int /* r6 = dt_vint2int(s) */
+ and %r0, 0xfffffff /* bound r6 */
+ lddw %r1, STRSZ
+ jle %r0, %r1, 1
+ mov %r0, %r1
+ mov %r6, %r0
+
+ mov %r1, %r6
+ call dt_vint_size /* r7 = dt_vint_size(r6) */
+ mov %r7, %r0
+
+ ldxdw %r1, [%fp+-16]
+ call dt_vint2int /* r8 = dt_vint2int(t) */
+ and %r0, 0xfffffff /* bound r8 */
+ lddw %r1, STRSZ
+ jle %r0, %r1, 1
+ mov %r0, %r1
+ mov %r8, %r0
+
+ mov %r1, %r8
+ call dt_vint_size /* r9 = dt_vint_size(r8) */
+ mov %r9, %r0
+
+ mov %r1, 0 /* r1 = 0 */
+ mov %r2, %r6 /* r2 = min(r6, r8) */
+ jle %r2, %r8, 1
+ mov %r2, %r8
+.Lloop:
+ ldxdw %r3, [%fp+-8] /* s[r7 + r1] */
+ mov %r0, %r7
+ add %r0, %r1
+ add %r3, %r0
+ ldxb %r3, [%r3+0]
+ lsh %r3, 56
+ ldxdw %r4, [%fp+-16] /* t[r9 + r1] */
+ mov %r0, %r9
+ add %r0, %r1
+ add %r4, %r0
+ ldxb %r4, [%r4+0]
+ lsh %r4, 56
+ jgt %r3, %r4, .Lmore /* if (s[r7 + r1] > t[r9 + r1]) return +1 */
+ jlt %r3, %r4, .Lless /* if (s[r7 + r1] < t[r9 + r1]) return -1 */
+ add %r1, 1 /* r1++ */
+ jlt %r1, %r6, .Lloop /* if (r1 < r6) goto Lloop */
+
+ jgt %r6, %r8, .Lmore /* if (r6 > r8) return +1 */
+ jlt %r6, %r8, .Lless /* if (r6 > r8) return -1 */
+
+ mov %r0, 0 /* return 0 */
+ exit
+
+.Lmore:
+ mov %r0, 1 /* return +1 */
+ exit
+.Lless:
+ mov %r0, -1 /* return -1 */
+ exit
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index a841272c..2c7bd178 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -2540,10 +2540,34 @@ dt_cg_compare_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, uint_t op)
dt_cg_node(dnp->dn_left, dlp, drp);
dt_cg_node(dnp->dn_right, dlp, drp);
- /* FIXME: No support for string comparison yet */
- if (dt_node_is_string(dnp->dn_left) || dt_node_is_string(dnp->dn_right))
- xyerror(D_UNKNOWN, "internal error -- no support for "
- "string comparison yet\n");
+ /* by now, we have checked and both args are strings or neither is */
+ if (dt_node_is_string(dnp->dn_left) ||
+ dt_node_is_string(dnp->dn_right)) {
+ dt_ident_t *idp;
+ uint_t Lnull = dt_irlist_label(dlp);
+
+ /* if either pointer is NULL, just compare pointers */
+ emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_left->dn_reg, 0, Lnull));
+ emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_right->dn_reg, 0, Lnull));
+
+ /* otherwise, replace pointers with relative string values */
+ if (dt_regset_xalloc_args(drp) == -1)
+ longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+ emit(dlp, BPF_MOV_REG(BPF_REG_1, dnp->dn_left->dn_reg));
+ emit(dlp, BPF_MOV_REG(BPF_REG_2, dnp->dn_right->dn_reg));
+ emit(dlp, BPF_MOV_IMM(BPF_REG_3, 64));
+ idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_strcmp");
+ assert(idp != NULL);
+ dt_regset_xalloc(drp, BPF_REG_0);
+ emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+ dt_regset_free_args(drp);
+ emit(dlp, BPF_MOV_REG(dnp->dn_left->dn_reg, BPF_REG_0));
+ dt_regset_free(drp, BPF_REG_0);
+ emit(dlp, BPF_MOV_IMM(dnp->dn_right->dn_reg, 0));
+
+ emitl(dlp, Lnull,
+ BPF_NOP());
+ }
emit(dlp, BPF_BRANCH_REG(op, dnp->dn_left->dn_reg, dnp->dn_right->dn_reg, lbl_true));
dt_regset_free(drp, dnp->dn_right->dn_reg);
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index ac5d5d0d..547fd10f 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_strcmp, DT_IDENT_SYMBOL),
DT_BPF_SYMBOL(dt_strnlen, DT_IDENT_SYMBOL),
DT_BPF_SYMBOL(dt_substr, DT_IDENT_SYMBOL),
/* BPF maps */
diff --git a/test/unittest/operators/tst.str_comparison.d b/test/unittest/operators/tst.str_comparison.d
new file mode 100644
index 00000000..88063847
--- /dev/null
+++ b/test/unittest/operators/tst.str_comparison.d
@@ -0,0 +1,45 @@
+/*
+ * 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: String comparisons work.
+ *
+ * SECTION: Operators
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+ nerrors = 0;
+ /* FIXME: add tests with strings of different lengths, both matching and not */
+ /* FIXME: add tests with strings in global variables to test verifier */
+ /* FIXME: add tests comparing NULL string pointers with NULL (NULL==NULL; strptr > NULL) */
+ nerrors += ("abc" <= "def" ? 0 : 1);
+ nerrors += ("abc" < "def" ? 0 : 1);
+ nerrors += ("abc" == "def" ? 1 : 0);
+ nerrors += ("abc" != "def" ? 0 : 1);
+ nerrors += ("abc" >= "def" ? 1 : 0);
+ nerrors += ("abc" > "def" ? 1 : 0);
+ nerrors += ("xyz" <= "def" ? 1 : 0);
+ nerrors += ("xyz" < "def" ? 1 : 0);
+ nerrors += ("xyz" == "def" ? 1 : 0);
+ nerrors += ("xyz" != "def" ? 0 : 1);
+ nerrors += ("xyz" >= "def" ? 0 : 1);
+ nerrors += ("xyz" > "def" ? 0 : 1);
+ nerrors += ("def" <= "def" ? 0 : 1);
+ nerrors += ("def" < "def" ? 1 : 0);
+ nerrors += ("def" == "def" ? 0 : 1);
+ nerrors += ("def" != "def" ? 1 : 0);
+ nerrors += ("def" >= "def" ? 0 : 1);
+ nerrors += ("def" > "def" ? 1 : 0);
+ exit(nerrors == 0 ? 0 : 1);
+}
+ERROR
+{
+ exit(1);
+}
--
2.18.4
More information about the DTrace-devel
mailing list