[DTrace-devel] [PATCH 8/8] Add support for lltostr() subroutine
eugene.loh at oracle.com
eugene.loh at oracle.com
Wed Sep 29 08:13:41 PDT 2021
From: Eugene Loh <eugene.loh at oracle.com>
Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
---
bpf/Build | 1 +
bpf/lltostr.S | 167 ++++++++++++++++++++++++
libdtrace/dt_cg.c | 37 +++++-
libdtrace/dt_dlibs.c | 1 +
test/unittest/dif/lltostr.d | 1 -
test/unittest/funcs/tst.lltostr-short.d | 39 ++++++
test/unittest/funcs/tst.lltostr-short.r | 26 ++++
test/unittest/funcs/tst.lltostr.d | 7 +-
test/unittest/funcs/tst.lltostr.r | 4 +
9 files changed, 279 insertions(+), 4 deletions(-)
create mode 100644 bpf/lltostr.S
create mode 100644 test/unittest/funcs/tst.lltostr-short.d
create mode 100644 test/unittest/funcs/tst.lltostr-short.r
diff --git a/bpf/Build b/bpf/Build
index 0b39764f..606c9b8f 100644
--- a/bpf/Build
+++ b/bpf/Build
@@ -26,6 +26,7 @@ bpf_dlib_SOURCES = \
get_bvar.c \
get_tvar.c set_tvar.c \
index.S \
+ lltostr.S \
probe_error.c \
rindex.S \
strchr.S \
diff --git a/bpf/lltostr.S b/bpf/lltostr.S
new file mode 100644
index 00000000..6b600264
--- /dev/null
+++ b/bpf/lltostr.S
@@ -0,0 +1,167 @@
+// 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
+#define MAXCHARS 19
+
+ .text
+/*
+ * int dt_lltostr(char *STR, uint64_t VAL)
+ * {
+ * // start writing at the end (IDX==1 is the last char)
+ * IDX = 1
+ *
+ * // compute sign bit
+ * SGN = VAL >> 63
+ *
+ * // replace VAL with its absolute value
+ * VAL *= (1 - 2 * SGN)
+ *
+ * // prewrite a '0' in case VAL==0
+ * [%fp + -1] = '0'
+ *
+ * Lloop:
+ * if (VAL == 0) goto Ldone
+ *
+ * r5 = VAL / 10
+ * r3 = itoa(VAL % 10)
+ *
+ * [%fp + -IDX] = r3
+ *
+ * VAL = r5
+ * IDX++
+ *
+ * jump Lloop
+ *
+ * Ldone:
+ *
+ * // back IDX up one char (unless no chars were written in loop)
+ * if (IDX > 1) IDX--
+ *
+ * // if (SGN), account for sign char
+ * IDX += SGN
+ *
+ * LEN = min(IDX, STRSZ)
+ *
+ * dt_strlen_store(LEN, STR)
+ * STR += DT_STRLEN_BYTES
+ *
+ * // copy to output string
+ * bpf_probe_read(STR, LEN, %fp + -IDX)
+ *
+ * // if (SGN), we included a garbage sign char; fix it
+ * if (SGN) STR[0] = '-'
+ *
+ * // add terminating NULL byte
+ * if (LEN < STRSZ) STR[LEN] = '\0'
+ * }
+ */
+ .align 4
+ .global dt_lltostr
+dt_lltostr:
+
+/*
+ * assign certain variables to registers:
+ * - STR is the output string
+ * - VAL is the value we are trying to convert
+ * - IDX is the index of the output char we are writing
+ * (1-based, starting from the end)
+ * - SGN is the sign bit of the input value (1: neg; 0: otherwise)
+ * registers r0-r5 are used as temporary variables
+ * (including of course for function calls)
+ */
+#define STR %r6
+#define VAL %r7
+#define IDX %r8
+#define SGN %r9
+
+ mov STR, %r1
+ mov VAL, %r2
+
+ mov IDX, 1 /* IDX = 1 */
+
+ mov SGN, VAL
+ rsh SGN, 63 /* SGN = VAL >> 63 */
+
+ mov %r5, 1
+ sub %r5, SGN
+ sub %r5, SGN
+ mul VAL, %r5 /* VAL *= (1 - 2 * SGN) */
+
+ mov %r5, '0'
+ stxb [%fp+-1], %r5 /* prewrite a '0' in case VAL==0 */
+
+.Lloop:
+ jeq VAL, 0, .Ldone /* if (VAL == 0) goto Ldone */
+ jgt IDX, MAXCHARS, .Ldone /* help the BPF verifier */
+
+ mov %r5, VAL
+ div %r5, 10 /* r5 = VAL / 10 */
+
+ mov %r4, 10
+ mul %r4, %r5
+ mov %r3, VAL
+ sub %r3, %r4
+ add %r3, '0' /* r3 = itoa(VAL % 10) */
+
+ mov %r2, %fp
+ mov %r1, IDX
+ mul %r1, -1
+ add %r2, %r1
+ stxb [%r2+0], %r3 /* [%fp + -IDX] = r3 */
+
+ mov VAL, %r5 /* VAL = r5 */
+ add IDX, 1 /* IDX++ */
+
+ ja .Lloop
+
+.Ldone:
+
+/* at this point VAL==0, so use its register for string length */
+#undef VAL
+#define LEN %r7
+
+ jle IDX, 1, 1 /* if (IDX > 1) IDX-- */
+ sub IDX, 1
+
+ jge IDX, 1, 1 /* help the BPF verifier out */
+ mov IDX, 1
+ jle IDX, MAXCHARS, 1
+ mov IDX, MAXCHARS
+ and SGN, 1
+
+ add IDX, SGN /* IDX += SGN */
+
+ lddw %r1, STRSZ
+ mov LEN, IDX
+ jle LEN, %r1, 1
+ mov LEN, %r1 /* LEN = min(IDX, STRSZ) */
+
+ mov %r1, LEN
+ mov %r2, STR
+ call dt_strlen_store /* dt_strlen_store(LEN, STR) */
+
+ add STR, DT_STRLEN_BYTES /* STR += DT_STRLEN_BYTES */
+
+ mov %r1, STR
+ mov %r2, LEN
+ mov %r3, %fp
+ mov %r4, 0
+ sub %r4, IDX
+ add %r3, %r4
+ call BPF_FUNC_probe_read /* bpf_probe_read(STR, LEN, %fp + -IDX) */
+
+ mov %r5, '-'
+ jeq SGN, 0, 1
+ stxb [STR+0], %r5 /* if (SGN) STR[0] = '-' */
+
+ lddw %r5, STRSZ
+ jlt LEN, %r5, 1
+ exit
+ add STR, LEN
+ mov %r5, 0
+ stxb [STR+0], %r5 /* if (LEN < STRSZ) STR[LEN] = '\0' */
+ exit
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 6aac64ae..80b8566e 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -3231,6 +3231,41 @@ dt_cg_subr_index(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
TRACE_REGSET(" subr-index:End ");
}
+static void
+dt_cg_subr_lltostr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
+{
+ dt_ident_t *idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_lltostr");
+ dt_node_t *val = dnp->dn_args;
+
+ assert(idp != NULL);
+
+ TRACE_REGSET(" subr-lltostr:Begin");
+ dt_cg_node(val, dlp, drp);
+
+ if (dt_regset_xalloc_args(drp) == -1)
+ longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+ dnp->dn_reg = dt_regset_alloc(drp);
+ if (dnp->dn_reg == -1)
+ longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+ dt_cg_tstring_alloc(yypcb, dnp);
+
+ 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));
+ emit(dlp, BPF_MOV_REG(BPF_REG_1, dnp->dn_reg));
+
+ emit(dlp, BPF_MOV_REG(BPF_REG_2, val->dn_reg));
+ dt_regset_free(drp, val->dn_reg);
+
+ 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-lltostr:End ");
+}
+
static void
dt_cg_subr_rindex(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
{
@@ -3689,7 +3724,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
[DIF_SUBR_GETMINOR] = NULL,
[DIF_SUBR_DDI_PATHNAME] = NULL,
[DIF_SUBR_STRJOIN] = dt_cg_subr_strjoin,
- [DIF_SUBR_LLTOSTR] = NULL,
+ [DIF_SUBR_LLTOSTR] = &dt_cg_subr_lltostr,
[DIF_SUBR_BASENAME] = NULL,
[DIF_SUBR_DIRNAME] = NULL,
[DIF_SUBR_CLEANPATH] = NULL,
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index 0e3ebb85..7d10898f 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -60,6 +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_index, DT_IDENT_SYMBOL),
+ DT_BPF_SYMBOL(dt_lltostr, DT_IDENT_SYMBOL),
DT_BPF_SYMBOL(dt_rindex, DT_IDENT_SYMBOL),
DT_BPF_SYMBOL(dt_strchr, DT_IDENT_SYMBOL),
DT_BPF_SYMBOL(dt_strcmp, DT_IDENT_SYMBOL),
diff --git a/test/unittest/dif/lltostr.d b/test/unittest/dif/lltostr.d
index fe7b3b9e..6aa19d94 100644
--- a/test/unittest/dif/lltostr.d
+++ b/test/unittest/dif/lltostr.d
@@ -1,4 +1,3 @@
-/* @@xfail: dtv2 */
BEGIN
{
exit(lltostr(12345678) == "12345678" ? 0 : 1);
diff --git a/test/unittest/funcs/tst.lltostr-short.d b/test/unittest/funcs/tst.lltostr-short.d
new file mode 100644
index 00000000..96904566
--- /dev/null
+++ b/test/unittest/funcs/tst.lltostr-short.d
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma D option quiet
+#pragma D option strsize=7
+
+BEGIN
+{
+ printf("%s\n", lltostr(0));
+ printf("%s\n", lltostr(1));
+ printf("%s\n", lltostr(12));
+ printf("%s\n", lltostr(123));
+ printf("%s\n", lltostr(1234));
+ printf("%s\n", lltostr(12345));
+ printf("%s\n", lltostr(123456));
+ printf("%s\n", lltostr(1234567));
+ printf("%s\n", lltostr(12345678));
+ printf("%s\n", lltostr(123456789));
+ printf("%s\n", lltostr(1234567890));
+ printf("%s\n", lltostr(-1));
+ printf("%s\n", lltostr(-12));
+ printf("%s\n", lltostr(-123));
+ printf("%s\n", lltostr(-1234));
+ printf("%s\n", lltostr(-12345));
+ printf("%s\n", lltostr(-123456));
+ printf("%s\n", lltostr(-1234567));
+ printf("%s\n", lltostr(-12345678));
+ printf("%s\n", lltostr(-123456789));
+ printf("%s\n", lltostr(-1234567890));
+ printf("%s\n", lltostr(9999999));
+ printf("%s\n", lltostr(10000000));
+ printf("%s\n", lltostr(-999999));
+ printf("%s\n", lltostr(-1000000));
+ exit(0);
+}
diff --git a/test/unittest/funcs/tst.lltostr-short.r b/test/unittest/funcs/tst.lltostr-short.r
new file mode 100644
index 00000000..a70a84f5
--- /dev/null
+++ b/test/unittest/funcs/tst.lltostr-short.r
@@ -0,0 +1,26 @@
+0
+1
+12
+123
+1234
+12345
+123456
+1234567
+1234567
+1234567
+1234567
+-1
+-12
+-123
+-1234
+-12345
+-123456
+-123456
+-123456
+-123456
+-123456
+9999999
+1000000
+-999999
+-100000
+
diff --git a/test/unittest/funcs/tst.lltostr.d b/test/unittest/funcs/tst.lltostr.d
index 2415906b..0563778b 100644
--- a/test/unittest/funcs/tst.lltostr.d
+++ b/test/unittest/funcs/tst.lltostr.d
@@ -1,10 +1,9 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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 */
#pragma D option quiet
@@ -17,5 +16,9 @@ BEGIN
printf("%s\n", lltostr(-123456789));
printf("%s\n", lltostr(1LL << 62));
printf("%s\n", lltostr(-(1LL << 62)));
+ printf("%s\n", lltostr(0x7ffffffffffffffe));
+ printf("%s\n", lltostr(0x7fffffffffffffff));
+ printf("%s\n", lltostr(0x8000000000000000));
+ printf("%s\n", lltostr(0x8000000000000001));
exit(0);
}
diff --git a/test/unittest/funcs/tst.lltostr.r b/test/unittest/funcs/tst.lltostr.r
index e007c648..05480536 100644
--- a/test/unittest/funcs/tst.lltostr.r
+++ b/test/unittest/funcs/tst.lltostr.r
@@ -5,4 +5,8 @@
-123456789
4611686018427387904
-4611686018427387904
+9223372036854775806
+9223372036854775807
+-9223372036854775808
+-9223372036854775807
--
2.18.4
More information about the DTrace-devel
mailing list