[DTrace-devel] [PATCH v5] Add support for strtok() subroutine
Kris Van Hees
kris.van.hees at oracle.com
Mon Dec 6 21:51:17 UTC 2021
On Mon, Dec 06, 2021 at 04:39:29PM -0500, eugene.loh--- via DTrace-devel wrote:
> From: Eugene Loh <eugene.loh at oracle.com>
>
> Internal state holds a copy of the string being parsed, along with an
> offset where in the string the next strtok(NULL, ...) call should start.
> This state cannot overlap the scratch memory used for stack() calls.
>
> Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
... I will add it to dev since we're about to cut a new errata release.
Thanks!
> ---
> bpf/Build | 1 +
> bpf/strtok.S | 498 ++++++++++++++++++
> libdtrace/dt_bpf.c | 18 +-
> libdtrace/dt_cg.c | 107 +++-
> libdtrace/dt_dctx.h | 6 +
> libdtrace/dt_dlibs.c | 1 +
> test/stress/fbtsafety/tst.shortstr.d | 1 -
> .../strtok/err.D_PROTO_ARG.strtokbaddel.d | 14 +
> .../strtok/err.D_PROTO_ARG.strtokbaddel.r | 4 +
> .../strtok/err.D_PROTO_ARG.strtokbadstr.d | 14 +
> .../strtok/err.D_PROTO_ARG.strtokbadstr.r | 4 +
> .../strtok/err.D_PROTO_LEN.strtoktoofew.d | 14 +
> .../strtok/err.D_PROTO_LEN.strtoktoofew.r | 2 +
> .../strtok/err.D_PROTO_LEN.strtoktoomany.d | 14 +
> .../strtok/err.D_PROTO_LEN.strtoktoomany.r | 2 +
> test/unittest/funcs/{ => strtok}/tst.strtok.d | 0
> test/unittest/funcs/{ => strtok}/tst.strtok.r | 0
> test/unittest/funcs/strtok/tst.strtok2.d | 39 ++
> test/unittest/funcs/strtok/tst.strtok2.r | 9 +
> test/unittest/funcs/strtok/tst.strtok_long.d | 35 ++
> test/unittest/funcs/strtok/tst.strtok_long.r | 5 +
> .../funcs/{ => strtok}/tst.strtok_null.d | 3 +-
> test/unittest/funcs/strtok/tst.strtok_null.r | 6 +
> .../funcs/strtok/tst.strtok_nulldel.d | 17 +
> .../funcs/strtok/tst.strtok_nulldel.r | 6 +
> .../funcs/strtok/tst.strtok_nullstr.d | 17 +
> .../funcs/strtok/tst.strtok_nullstr.r | 6 +
> .../funcs/strtok/tst.strtok_nullstr2.d | 26 +
> .../funcs/strtok/tst.strtok_nullstr2.r | 7 +
> test/unittest/funcs/strtok/tst.strtok_regs.d | 21 +
> test/unittest/funcs/strtok/tst.strtok_regs.r | 1 +
> test/unittest/funcs/tst.strtok_null.r | 6 -
> 32 files changed, 885 insertions(+), 19 deletions(-)
> create mode 100644 bpf/strtok.S
> create mode 100644 test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.d
> create mode 100644 test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.r
> create mode 100644 test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.d
> create mode 100644 test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.r
> create mode 100644 test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.d
> create mode 100644 test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.r
> create mode 100644 test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.d
> create mode 100644 test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.r
> rename test/unittest/funcs/{ => strtok}/tst.strtok.d (100%)
> rename test/unittest/funcs/{ => strtok}/tst.strtok.r (100%)
> create mode 100644 test/unittest/funcs/strtok/tst.strtok2.d
> create mode 100644 test/unittest/funcs/strtok/tst.strtok2.r
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_long.d
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_long.r
> rename test/unittest/funcs/{ => strtok}/tst.strtok_null.d (79%)
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_null.r
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_nulldel.d
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_nulldel.r
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_nullstr.d
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_nullstr.r
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_nullstr2.d
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_nullstr2.r
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_regs.d
> create mode 100644 test/unittest/funcs/strtok/tst.strtok_regs.r
> delete mode 100644 test/unittest/funcs/tst.strtok_null.r
>
> diff --git a/bpf/Build b/bpf/Build
> index 8d4fb0fa..062381b8 100644
> --- a/bpf/Build
> +++ b/bpf/Build
> @@ -37,6 +37,7 @@ bpf_dlib_SOURCES = \
> strjoin.S \
> strlen.c \
> strrchr.S \
> + strtok.S \
> substr.S
>
> bpf-check: $(objdir)/include/.dir.stamp
> diff --git a/bpf/strtok.S b/bpf/strtok.S
> new file mode 100644
> index 00000000..9b2563e0
> --- /dev/null
> +++ b/bpf/strtok.S
> @@ -0,0 +1,498 @@
> +// 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 BPF_FUNC_probe_read_str 45
> +
> + .text
> +
> +/*
> + * The algorithm stores information about the delimiters in a table for
> + * 256 ASCII chars, each table entry a single bit indicating whether the
> + * char is a delimiter. This version stores the 256-bit (32-byte) table
> + * on the stack as four, 8-byte bins. The bit for char c is bit c&63 in
> + * bin c>>6. The bins are stored at:
> + * bin 0 [TAB+- 8]
> + * bin 1 [TAB+-16]
> + * bin 2 [TAB+-24]
> + * bin 3 [TAB+-32]
> + * where TAB will be dt_strtok()'s FP.
> + */
> +
> +/* ================================================== */
> +
> +/*
> + * // Initialize the delimiter table (place a nonzero bit for each delimiter found).
> + * // Note that:
> + * // - we expect the delimiter string to be NULL-terminated
> + * // - the NULL byte is always going to be set in the delimiter table
> + * // - we also pass a "length" argument in to help the BPF verifier
> + * void dt_strtok_init_table(uint64_t *tab, char *del, uint64_t len)
> + * {
> + * idx = 0
> + * Linit_table:
> + * if (idx >= len) return
> + * chr = del[idx]
> + * bit = (1 << (chr & 63))
> + * bin = chr >> 6
> + * tab[bin] |= bit
> + * if (chr == '\0') return
> + * idx++
> + * goto Linit_table
> + * }
> + */
> + .align 4
> + .global dt_strtok_init_table
> + .type dt_strtok_init_table, @function
> +dt_strtok_init_table:
> +#define TAB %r1
> +#define DEL %r2
> +#define LEN %r3
> +#define IDX %r4
> +#define CHR %r5
> +#define BIN %r6
> +#define BIT %r7
> +#define TMP %r8
> +#define USE %r9
> + mov IDX, 0 /* idx = 0 */
> +
> +.Linit_table:
> + jlt IDX, LEN, 1 /* if (idx >= len) return */
> + exit
> +
> + mov CHR, DEL
> + add CHR, IDX
> + ldxb CHR, [CHR+0] /* chr = del[idx] */
> + and CHR, 0xff
> +
> + mov BIT, 1 /* bit = (1 << (chr & 63)) */
> + mov TMP, CHR
> + and TMP, 63
> + lsh BIT, TMP
> +
> + mov BIN, CHR /* bin = chr >> 6 */
> + rsh BIN, 6
> +
> + /* tab[bin] |= bit */
> + /*
> + * Since the table is on the stack and we cannot use variable
> + * offsets into the stack, we iterate over all four bins:
> + * tab[- 8] |= (0 == bin ? 1 : 0) * bit
> + * tab[-16] |= (1 == bin ? 1 : 0) * bit
> + * tab[-24] |= (2 == bin ? 1 : 0) * bit
> + * tab[-32] |= (3 == bin ? 1 : 0) * bit
> + *
> + * Further, since the BPF verifier is easily stymied by conditional
> + * branching, we compute the conditionalized operators with:
> + * // try == bin try != bin
> + * use = (try ^ bin) // use == 0 use > 0
> + * use-- // use < 0 use >= 0
> + * use >>= 63 // use == 1 use == 0
> + * tab[off] |= use * bit
> + */
> +.macro macro_init try off
> + mov USE, BIN
> + xor USE, \try
> + sub USE, 1
> + rsh USE, 63
> + mul USE, BIT
> +
> + ldxdw TMP, [TAB+\off]
> + or TMP, USE
> + stxdw [TAB+\off], TMP
> +.endm
> + macro_init 0,-8
> + macro_init 1,-16
> + macro_init 2,-24
> + macro_init 3,-32
> +
> + jne CHR, 0, 1 /* if (chr == '\0') return */
> + exit
> +
> + add IDX, 1 /* idx++ */
> +
> + ja .Linit_table /* goto Linit_table */
> +#undef TAB
> +#undef DEL
> +#undef LEN
> +#undef IDX
> +#undef CHR
> +#undef BIN
> +#undef BIT
> +#undef TMP
> +#undef USE
> + .size dt_strtok_init_table, .-dt_strtok_init_table
> +
> +/* ================================================== */
> +
> +/*
> + * // Look up each byte, replacing it by 1 or 0 using the table.
> + * void dt_strtok_lookup(uint64_t *tab, char *str, uint64_t len)
> + * {
> + * idx = 0
> + * Llookup:
> + * if (idx >= len) return
> + * ptr = &str[idx]
> + * bin = *ptr >> 6
> + * val = tab[bin]
> + * val >>= *ptr & 63
> + * val &= 1
> + * *ptr = val
> + * idx++
> + * goto Llookup
> + * }
> + */
> + .align 4
> + .global dt_strtok_lookup
> + .type dt_strtok_lookup, @function
> +dt_strtok_lookup:
> +
> +#define VAL %r0
> +#define TAB %r1
> +#define STR %r2
> +#define LEN %r3
> +#define BIN %r4
> +#define IDX %r5
> +#define PTR %r6
> +#define TMP %r7
> +#define USE %r8
> +
> + mov IDX, 0 /* idx = 0 */
> +
> +.Llookup:
> + jlt IDX, LEN, 1 /* if (idx >= len) return */
> + exit
> +
> + mov PTR, STR
> + add PTR, IDX /* ptr = &str[idx] */
> +
> + ldxb BIN, [PTR+0]
> + and BIN, 255
> + rsh BIN, 6 /* bin = *ptr >> 6 */
> +
> + /* val = tab[bin] */
> + /*
> + * The tab[bin] lookup is tricky for the same reasons as
> + * described for dt_strtok_init_table(). This time, we use
> + * val = 0
> + * val += (0 == bin ? 1 : 0) * tab[- 8]
> + * val += (1 == bin ? 1 : 0) * tab[-16]
> + * val += (2 == bin ? 1 : 0) * tab[-24]
> + * val += (3 == bin ? 1 : 0) * tab[-32]
> + * with each conditionalized operator replaced by
> + * // try == bin try != bin
> + * use = (try ^ bin) // use == 0 use > 0
> + * use-- // use < 0 use >= 0
> + * use >>= 63 // use == 1 use == 0
> + * val += use * tab[off]
> + */
> + mov VAL, 0 /* val = 0 */
> +.macro macro_look try off
> + mov USE, \try
> + xor USE, BIN /* use = try ^ bin */
> + sub USE, 1 /* use-- */
> + rsh USE, 63 /* use >>= 63 */
> + ldxdw TMP, [TAB+\off]
> + mul TMP, USE
> + add VAL, TMP /* val += use * tab[try] */
> +.endm
> + macro_look 0,-8
> + macro_look 1,-16
> + macro_look 2,-24
> + macro_look 3,-32
> +
> + ldxb TMP, [PTR+0]
> + and TMP, 63
> + rsh VAL, TMP /* val >>= *ptr & 63 */
> +
> + and VAL, 1 /* val &= 1 */
> +
> + stxb [PTR+0], VAL /* *ptr = val */
> +
> + add IDX, 1 /* idx++ */
> +
> + ja .Llookup /* goto Llookup */
> +#undef VAL
> +#undef TAB
> +#undef STR
> +#undef LEN
> +#undef BIN
> +#undef IDX
> +#undef PTR
> +#undef TMP
> +#undef USE
> + .size dt_strtok_lookup, .-dt_strtok_lookup
> +
> +/* ================================================== */
> +
> +/*
> + * // Each byte is 1 or 0, flip its value.
> + * // Do 8 bytes at a time; len must be a multiple of 8.
> + * void dt_strtok_flip(char *str, uint64_t len)
> + * {
> + * msk = 0x0101010101010101
> + * idx = 0
> + * Lflip:
> + * if (idx >= len) return
> + * ptr = (uint64_t*)&str[idx]
> + * val = *ptr
> + * val ^= msk
> + * *ptr = val
> + * idx += 8
> + * goto Lflip
> + * }
> + */
> + .align 4
> + .global dt_strtok_flip
> + .type dt_strtok_flip, @function
> +dt_strtok_flip:
> +
> +#define STR %r1
> +#define LEN %r2
> +#define MSK %r3
> +#define IDX %r4
> +#define PTR %r5
> +#define VAL %r6
> +
> + lddw MSK, 0x0101010101010101 /* msk = 0x0101010101010101 */
> + mov IDX, 0 /* idx = 0 */
> +
> +.Lflip:
> + jlt IDX, LEN, 1 /* if (idx >= siz) return */
> + exit
> +
> + mov PTR, STR
> + add PTR, IDX /* ptr = (uint64_t*)&str[idx] */
> +
> + ldxdw VAL, [PTR+0] /* val = *ptr */
> + xor VAL, MSK /* val ^= msk */
> + stxdw [PTR+0], VAL /* *ptr = val */
> +
> + add IDX, 8 /* idx += 8 */
> +
> + ja .Lflip /* goto Lflip */
> +#undef STR
> +#undef LEN
> +#undef MSK
> +#undef IDX
> +#undef PTR
> +#undef VAL
> + .size dt_strtok_flip, .-dt_strtok_flip
> +
> +/* ================================================== */
> +
> +/*
> + * // str has an 8-byte prefix giving the offset to start at.
> + * // So the actual string starts at str+8+*((uint64_t*)str).
> + * void dt_strtok(char *dst, char *str, const char *del, char *tmp)
> + * {
> + * // discard delimiter length prefix; we look for the NULL terminator anyhow
> + * del += DT_STRLEN_BYTES
> + *
> + * // len = roundup(STRSZ + 1, 8)
> + * len = ((STRSZ + 1) + 7) & -8
> + *
> + * // zero the delimiter table (do here so we see the stack usage)
> + * *((uint64_t *)(*fp + -8)) = 0
> + * *((uint64_t *)(*fp + -16)) = 0
> + * *((uint64_t *)(*fp + -24)) = 0
> + * *((uint64_t *)(*fp + -32)) = 0
> + *
> + * // initialize the delimiter table
> + * // Note:
> + * // - we provide "len" as a hint to the BPF verifier
> + * // - we copy "del" into "tmp" to guarantee "len" room
> + * bpf_probe_read_str(tmp, STRSZ + 1, del)
> + * dt_strtok_init_table(fp, tmp, len)
> + *
> + * // copy str into tmp
> + * end = bpf_probe_read_str(tmp, STRSZ + 1, str+8+*((uint64_t*)str))
> + * end--
> + *
> + * // check each byte against delimiter table
> + * dt_strtok_lookup(fp, tmp, len)
> + *
> + * // find first non-delimiting char
> + * bgn = bpf_probe_read_str(dst, STRSZ + 1, tmp)
> + * if (bgn s<= 0) goto Lnull
> + * bgn--
> + *
> + * // make sure there is at least one char
> + * if (bgn >= end) goto Lnull
> + *
> + * // flip each byte
> + * dt_strtok_flip(tmp, len)
> + *
> + * // find actual length
> + * len = bpf_probe_read_str(dst, STRSZ + 1 - bgn, tmp + bgn)
> + * if (len s<= 0) goto Lnull
> + * len--
> + *
> + * // adjust bgn to account for strtok offset
> + * bgn += *((uint64_t*)str)
> + *
> + * // copy str + bgn to destination
> + * dt_strlen_store(len, dst)
> + * bpf_probe_read(dst + DT_STRLEN_BYTES, len, str + 8 + bgn)
> + * dst[DT_STRLEN_BYTES + len] = '\0'
> + *
> + * // update the 8-byte prefix (strtok offset)
> + * bgn += len
> + * *((uint64_t*)str) = bgn
> + *
> + * return dst
> + *
> + * Lnull:
> + * // advance the strtok offset to the end and return 0
> + * *((uint64_t*)str) = end
> + * return 0
> + * }
> + */
> +
> + .align 4
> + .global dt_strtok
> + .type dt_strtok, @function
> +dt_strtok:
> +/* the stack has the delimiter table starting at %fp+-32 */
> +#define DST_X [%fp+-40]
> +#define END_X [%fp+-48]
> +#define STR %r6
> +#define DEL %r7
> +#define LEN %r8
> +#define TMP %r9
> +
> + /* stash variables */
> + stxdw DST_X, %r1
> + mov STR, %r2
> + mov DEL, %r3
> + mov TMP, %r4
> +
> + /* discard delimiter length prefix; we look for the NULL terminator anyhow */
> + add DEL, DT_STRLEN_BYTES /* del += DT_STRLEN_BYTES */
> +
> + /* len = roundup(STRSZ + 1, 8) */
> + lddw LEN, STRSZ
> + add LEN, 8
> + and LEN, -8 /* len = ((STRSZ + 1) + 7) & -8 */
> +
> + /* zero the delimiter table (do here so we see the stack usage) */
> + mov %r1, 0
> + stxdw [%fp+- 8], %r1 /* *((uint64_t *)(*fp + -8)) = 0 */
> + stxdw [%fp+-16], %r1 /* *((uint64_t *)(*fp + -16)) = 0 */
> + stxdw [%fp+-24], %r1 /* *((uint64_t *)(*fp + -24)) = 0 */
> + stxdw [%fp+-32], %r1 /* *((uint64_t *)(*fp + -32)) = 0 */
> +
> + /* initialize the delimiter table */
> + mov %r1, TMP
> + lddw %r2, STRSZ
> + add %r2, 1
> + mov %r3, DEL
> + call BPF_FUNC_probe_read_str /* bpf_probe_read_str(tmp, STRSZ + 1, del) */
> +#undef DEL
> + mov %r1, %fp
> + mov %r2, TMP
> + mov %r3, LEN
> + call dt_strtok_init_table /* dt_strtok_init_table(fp, tmp, len) */
> +
> + /* copy str into tmp */
> + mov %r1, TMP
> + lddw %r2, STRSZ
> + add %r2, 1
> + mov %r3, STR
> + add %r3, 8
> + ldxdw %r4, [STR+0]
> + jsge %r4, 0, 1
> + mov %r4, 0
> + add %r3, %r4
> + call BPF_FUNC_probe_read_str /* end = bpf_probe_read_str(tmp, STRSZ + 1, str+8+*((uint64_t*)str)) */
> + sub %r0, 1 /* end-- */
> + stxdw END_X, %r0
> +
> + /* check each byte against delimiter table */
> + mov %r1, %fp
> + mov %r2, TMP
> + mov %r3, LEN
> + call dt_strtok_lookup /* dt_strtok_lookup(fp, tmp, len) */
> +
> + /* find first non-delimiting char */
> +#define BGN %r7
> + ldxdw %r1, DST_X
> + lddw %r2, STRSZ
> + add %r2, 1
> + mov %r3, TMP
> + call BPF_FUNC_probe_read_str /* bgn = bpf_probe_read_str(dst, STRSZ + 1, tmp) */
> + mov BGN, %r0
> + jsle BGN, 0, .Lnull /* if (bgn s<= 0) goto Lnull */
> + sub BGN, 1 /* bgn-- */
> +
> + /* make sure there is at least one char */
> + ldxdw %r1, END_X
> + jge BGN, %r1, .Lnull /* if (bgn >= end) goto Lnull */
> +
> + /* flip each byte */
> + mov %r1, TMP
> + mov %r2, LEN
> + call dt_strtok_flip /* dt_strtok_flip(tmp, len) */
> +
> + /* find actual length */
> + ldxdw %r1, DST_X
> + lddw %r2, STRSZ
> + add %r2, 1
> + sub %r2, BGN
> + mov %r3, TMP
> + add %r3, BGN
> + call BPF_FUNC_probe_read_str /* len = bpf_probe_read_str(dst, STRSZ + 1 - bgn, tmp + bgn) */
> + mov LEN, %r0
> + jsle LEN, 0, .Lnull /* if (len s<= 0) goto Lnull */
> + sub LEN, 1 /* len-- */
> +
> + /* adjust bgn to account for strtok offset */
> + ldxdw %r1, [STR+0]
> + add BGN, %r1 /* bgn += *((uint64_t*)str) */
> +
> + /* (help the BPF verifier) */
> + jsge BGN, 0, 1
> + mov BGN, 0
> +
> + /* copy str + bgn to destination */
> + mov %r1, LEN
> + ldxdw %r2, DST_X
> + call dt_strlen_store /* dt_strlen_store(len, dst) */
> +
> + ldxdw %r1, DST_X
> + add %r1, DT_STRLEN_BYTES
> + mov %r2, LEN
> + mov %r3, STR
> + add %r3, 8
> + add %r3, BGN
> + call BPF_FUNC_probe_read /* bpf_probe_read(dst + DT_STRLEN_BYTES, len, str + 8 + bgn) */
> +
> + mov %r1, 0
> + ldxdw %r2, DST_X
> + add %r2, LEN
> + stxb [%r2+DT_STRLEN_BYTES], %r1
> + /* dst[DT_STRLEN_BYTES + len] = '\0' */
> +
> + /* update the 8-byte prefix (strtok offset) */
> + add BGN, LEN /* bgn += len */
> + stxdw [STR+0], BGN /* *((uint64_t*)str) = bgn */
> +
> + ldxdw %r0, DST_X /* return dst */
> + exit
> +
> + /* advance the strtok offset to the end and return 0 */
> +.Lnull:
> + ldxdw %r0, END_X
> + stxdw [STR+0], %r0 /* *((uint64_t*)str) = end */
> + mov %r0, 0 /* return 0 */
> + exit
> +#undef STR
> +#undef TMP
> +#undef LEN
> +#undef BGN
> +#undef DST_X
> + .size dt_strtok, .-dt_strtok
> diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
> index a85fe5af..ccfd7f09 100644
> --- a/libdtrace/dt_bpf.c
> +++ b/libdtrace/dt_bpf.c
> @@ -292,20 +292,20 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
> * - maximum trace buffer record size, rounded up to the nearest
> * multiple of 8
> * - the greater of:
> - * + the maximum stack trace size
> - * + DT_TSTRING_SLOTS times the maximum string size (plus
> - * space for length and terminating '\0') rounded up to
> - * the nearest multiple of 8),
> - * plus the maximum string size plus space for '\0' (to
> - * accomodate the BPF verifier)
> + * - the maximum stack trace size
> + * - DT_TSTRING_SLOTS times the maximum space needed to
> + * store a string
> + * - size of the internal strtok() state
> + * - 8 bytes for the offset index
> + * - the maximum string size
> + * - a byte for the terminating NULL char
> */
> memsz = roundup(sizeof(dt_mstate_t), 8) +
> 8 +
> roundup(dtp->dt_maxreclen, 8) +
> MAX(sizeof(uint64_t) * dtp->dt_options[DTRACEOPT_MAXFRAMES],
> - DT_TSTRING_SLOTS * strdatasz +
> - dtp->dt_options[DTRACEOPT_STRSIZE] + 1
> - );
> + DT_TSTRING_SLOTS * strdatasz) +
> + sizeof(uint64_t) + dtp->dt_options[DTRACEOPT_STRSIZE] + 1;
> if (create_gmap(dtp, "mem", BPF_MAP_TYPE_PERCPU_ARRAY,
> sizeof(uint32_t), memsz, 1) == -1)
> return -1; /* dt_errno is set for us */
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index b01d3a76..48a61b8f 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -154,6 +154,12 @@ dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act)
> emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, roundup(dtp->dt_maxreclen, 8)));
> emit(dlp, BPF_STORE(BPF_DW, BPF_REG_FP, DCTX_FP(DCTX_MEM), BPF_REG_0));
>
> + /*
> + * Store -1 to the strtok internal-state offset to indicate
> + * that strtok internal state is not yet initialized.
> + */
> + emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_0, DMEM_STRTOK, -1));
> +
> /*
> * Store pointer to BPF map "name" in the DTrace context field "fld" at
> * "offset".
> @@ -3872,6 +3878,105 @@ dt_cg_subr_strstr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> TRACE_REGSET(" subr-strstr:End ");
> }
>
> +static void
> +dt_cg_subr_strtok(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> +{
> + dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> + dt_node_t *str = dnp->dn_args;
> + dt_node_t *del = str->dn_list;
> + dt_ident_t *idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_strtok");
> + uint64_t off;
> + int reg;
> +
> + assert(idp != NULL);
> +
> + TRACE_REGSET(" subr-strtok:Begin");
> +
> + reg = dt_regset_alloc(drp);
> + if (reg == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> + emit(dlp, BPF_LOAD(BPF_DW, reg, BPF_REG_FP, DT_STK_DCTX));
> + emit(dlp, BPF_LOAD(BPF_DW, reg, reg, DCTX_MEM));
> +
> + /*
> + * Start with a static check for a NULL string.
> + * That is, strtok(NULL, foo) has a special meaning:
> + * continue parsing the previous string. In contrast,
> + * strtok(str, foo) (even if str==NULL) means to parse str.
> + */
> + if (str->dn_op != DT_TOK_INT || str->dn_value != 0) {
> + /* string is present: copy it to internal state */
> + dt_cg_node(str, dlp, drp);
> + dt_cg_check_notnull(dlp, drp, str->dn_reg);
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> + /* the 8-byte prefix is the offset, which we initialize to 0 */
> + emit(dlp, BPF_MOV_REG(BPF_REG_1, reg));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DMEM_STRTOK));
> + emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_1, 0, 0));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8));
> + emit(dlp, BPF_MOV_IMM(BPF_REG_2, dtp->dt_options[DTRACEOPT_STRSIZE] + 1));
> + emit(dlp, BPF_MOV_REG(BPF_REG_3, str->dn_reg));
> + dt_regset_free(drp, str->dn_reg);
> + if (str->dn_tstring)
> + dt_cg_tstring_free(yypcb, str);
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, DT_STRLEN_BYTES));
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read_str));
> + dt_regset_free(drp, BPF_REG_0);
> + dt_regset_free_args(drp);
> + } else {
> + /* NULL string: error if internal state is uninitialized */
> + emit(dlp, BPF_MOV_REG(BPF_REG_0, reg));
> + emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_0, DMEM_STRTOK));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1));
> + dt_cg_check_notnull(dlp, drp, BPF_REG_0);
> + }
> +
> + /* get delimiters */
> + dt_cg_node(del, dlp, drp);
> + dt_cg_check_notnull(dlp, drp, del->dn_reg);
> +
> + /* allocate temporary string for result */
> + 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_MOV_REG(dnp->dn_reg, reg));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, dnp->dn_tstring->dn_value));
> +
> + /* allocate temporary string for internal purposes */
> + off = dt_cg_tstring_xalloc(yypcb);
> +
> + /* function call */
> + 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, reg));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, DMEM_STRTOK));
> + emit(dlp, BPF_MOV_REG(BPF_REG_3, del->dn_reg));
> + dt_regset_free(drp, del->dn_reg);
> + if (del->dn_tstring)
> + dt_cg_tstring_free(yypcb, del);
> +
> + emit(dlp, BPF_MOV_REG(BPF_REG_4, reg));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, off));
> +
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
> + dt_regset_free_args(drp);
> + dt_cg_tstring_xfree(yypcb, off);
> + emit(dlp, BPF_MOV_REG(dnp->dn_reg, BPF_REG_0));
> + dt_regset_free(drp, BPF_REG_0);
> +
> + dt_regset_free(drp, reg);
> +
> + TRACE_REGSET(" subr-strtok:End ");
> +}
> +
> static void
> dt_cg_subr_substr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> {
> @@ -3986,7 +4091,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
> [DIF_SUBR_STRCHR] = &dt_cg_subr_strchr,
> [DIF_SUBR_STRRCHR] = &dt_cg_subr_strrchr,
> [DIF_SUBR_STRSTR] = &dt_cg_subr_strstr,
> - [DIF_SUBR_STRTOK] = NULL,
> + [DIF_SUBR_STRTOK] = &dt_cg_subr_strtok,
> [DIF_SUBR_SUBSTR] = &dt_cg_subr_substr,
> [DIF_SUBR_INDEX] = &dt_cg_subr_index,
> [DIF_SUBR_RINDEX] = &dt_cg_subr_rindex,
> diff --git a/libdtrace/dt_dctx.h b/libdtrace/dt_dctx.h
> index d19ea0cc..945f228e 100644
> --- a/libdtrace/dt_dctx.h
> +++ b/libdtrace/dt_dctx.h
> @@ -55,6 +55,12 @@ typedef struct dt_dctx {
> #define DCTX_LVARS offsetof(dt_dctx_t, lvars)
> #define DCTX_SIZE ((int16_t)sizeof(dt_dctx_t))
>
> +/*
> + * Macro to determine the offset from mem to the strtok internal state.
> + */
> +#define DMEM_STRTOK MAX(sizeof(uint64_t) * dtp->dt_options[DTRACEOPT_MAXFRAMES], \
> + DT_TSTRING_SLOTS * roundup(DT_STRLEN_BYTES + dtp->dt_options[DTRACEOPT_STRSIZE] + 1, 8))
> +
> /*
> * Macro to determine the (negative) offset from the frame pointer (%fp) for
> * the given offset in dt_dctx_t.
> diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
> index 10965eb5..a15b8269 100644
> --- a/libdtrace/dt_dlibs.c
> +++ b/libdtrace/dt_dlibs.c
> @@ -66,6 +66,7 @@ static const dt_ident_t dt_bpf_symbols[] = {
> DT_BPF_SYMBOL(dt_strcmp, DT_IDENT_SYMBOL),
> DT_BPF_SYMBOL(dt_strjoin, DT_IDENT_SYMBOL),
> DT_BPF_SYMBOL(dt_strrchr, DT_IDENT_SYMBOL),
> + DT_BPF_SYMBOL(dt_strtok, DT_IDENT_SYMBOL),
> DT_BPF_SYMBOL(dt_substr, DT_IDENT_SYMBOL),
> DT_BPF_SYMBOL(dt_speculation, DT_IDENT_SYMBOL),
> DT_BPF_SYMBOL(dt_speculation_speculate, DT_IDENT_SYMBOL),
> diff --git a/test/stress/fbtsafety/tst.shortstr.d b/test/stress/fbtsafety/tst.shortstr.d
> index 714d4833..dbf71b07 100644
> --- a/test/stress/fbtsafety/tst.shortstr.d
> +++ b/test/stress/fbtsafety/tst.shortstr.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
> #pragma D option strsize=16
> diff --git a/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.d b/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.d
> new file mode 100644
> index 00000000..da693577
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.d
> @@ -0,0 +1,14 @@
> +/*
> + * 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
> +
> +BEGIN
> +{
> + strtok("abc", 1234);
> + exit(0);
> +}
> diff --git a/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.r b/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.r
> new file mode 100644
> index 00000000..7704e007
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.r
> @@ -0,0 +1,4 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbaddel.d: [D_PROTO_ARG] line 12: strtok( ) argument #2 is incompatible with prototype:
> + prototype: char *
> + argument: int
> diff --git a/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.d b/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.d
> new file mode 100644
> index 00000000..30b3919b
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.d
> @@ -0,0 +1,14 @@
> +/*
> + * 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
> +
> +BEGIN
> +{
> + strtok(1234, "abc");
> + exit(0);
> +}
> diff --git a/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.r b/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.r
> new file mode 100644
> index 00000000..29d11628
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.r
> @@ -0,0 +1,4 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/funcs/strtok/err.D_PROTO_ARG.strtokbadstr.d: [D_PROTO_ARG] line 12: strtok( ) argument #1 is incompatible with prototype:
> + prototype: char *
> + argument: int
> diff --git a/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.d b/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.d
> new file mode 100644
> index 00000000..146b4e6f
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.d
> @@ -0,0 +1,14 @@
> +/*
> + * 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
> +
> +BEGIN
> +{
> + strtok("abc");
> + exit(0);
> +}
> diff --git a/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.r b/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.r
> new file mode 100644
> index 00000000..b1b87366
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoofew.d: [D_PROTO_LEN] line 12: strtok( ) prototype mismatch: 1 arg passed, 2 expected
> diff --git a/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.d b/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.d
> new file mode 100644
> index 00000000..242ea73d
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.d
> @@ -0,0 +1,14 @@
> +/*
> + * 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
> +
> +BEGIN
> +{
> + strtok("abc", "def", "ghi");
> + exit(0);
> +}
> diff --git a/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.r b/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.r
> new file mode 100644
> index 00000000..64e72d90
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/funcs/strtok/err.D_PROTO_LEN.strtoktoomany.d: [D_PROTO_LEN] line 12: strtok( ) prototype mismatch: 3 args passed, 2 expected
> diff --git a/test/unittest/funcs/tst.strtok.d b/test/unittest/funcs/strtok/tst.strtok.d
> similarity index 100%
> rename from test/unittest/funcs/tst.strtok.d
> rename to test/unittest/funcs/strtok/tst.strtok.d
> diff --git a/test/unittest/funcs/tst.strtok.r b/test/unittest/funcs/strtok/tst.strtok.r
> similarity index 100%
> rename from test/unittest/funcs/tst.strtok.r
> rename to test/unittest/funcs/strtok/tst.strtok.r
> diff --git a/test/unittest/funcs/strtok/tst.strtok2.d b/test/unittest/funcs/strtok/tst.strtok2.d
> new file mode 100644
> index 00000000..14f34043
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok2.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
> +
> +BEGIN
> +{
> + x = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
> + y = "abcABC";
> + printf("%s\n", strtok(x, y));
> + y = "MNO";
> + printf("%s\n", strtok(NULL, y));
> + printf("%s\n", strtok(NULL, y));
> + y = "0123456789";
> + printf("%s\n", strtok(NULL, y));
> + printf("%s\n", strtok(NULL, y) == NULL ? "got expected NULL" : "ERROR: non-NULL");
> +
> + x = "";
> + y = "";
> + printf("%s\n", strtok(x, y) == NULL ? "got expected NULL" : "ERROR: non-NULL");
> +
> + x = "foo";
> + printf("%s\n", strtok(x, y));
> +
> + x = "";
> + y = "foo";
> + printf("%s\n", strtok(x, y) == NULL ? "got expected NULL" : "ERROR: non-NULL");
> +
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/funcs/strtok/tst.strtok2.r b/test/unittest/funcs/strtok/tst.strtok2.r
> new file mode 100644
> index 00000000..c58ea336
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok2.r
> @@ -0,0 +1,9 @@
> +defghijklmnopqrstuvwxyz
> +ABCDEFGHIJKL
> +PQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKL
> +MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
> +got expected NULL
> +got expected NULL
> +foo
> +got expected NULL
> +
> diff --git a/test/unittest/funcs/strtok/tst.strtok_long.d b/test/unittest/funcs/strtok/tst.strtok_long.d
> new file mode 100644
> index 00000000..1fbe415d
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_long.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.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + /* 256-char string ending in "XYZ" */
> + x = "_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________XYZ";
> +
> + /* check whether the last char of a long string is seen */
> + y = "a";
> + printf("%s\n", strtok(x, y));
> +
> + /* check whether the last char of a long delimiter string is seen */
> + z = "zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA";
> + printf("%s\n", strtok(z, x));
> +
> + /* check that strtok internal state can handle a large offset */
> + y = "Z";
> + printf("%s\n", strtok(x, y));
> + y = "a";
> + printf("%s\n", strtok(NULL, y));
> +
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/funcs/strtok/tst.strtok_long.r b/test/unittest/funcs/strtok/tst.strtok_long.r
> new file mode 100644
> index 00000000..a752554e
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_long.r
> @@ -0,0 +1,5 @@
> +_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________XYZ
> +zyxwvutsrqponmlkjihgfedcba
> +_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________XY
> +Z
> +
> diff --git a/test/unittest/funcs/tst.strtok_null.d b/test/unittest/funcs/strtok/tst.strtok_null.d
> similarity index 79%
> rename from test/unittest/funcs/tst.strtok_null.d
> rename to test/unittest/funcs/strtok/tst.strtok_null.d
> index 45301a78..1d536d60 100644
> --- a/test/unittest/funcs/tst.strtok_null.d
> +++ b/test/unittest/funcs/strtok/tst.strtok_null.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 */
>
> /*
> * Test that a strtok(NULL, ...) without first calling strtok(string, ...)
> diff --git a/test/unittest/funcs/strtok/tst.strtok_null.r b/test/unittest/funcs/strtok/tst.strtok_null.r
> new file mode 100644
> index 00000000..4002d6ef
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_null.r
> @@ -0,0 +1,6 @@
> + FUNCTION:NAME
> + :ERROR
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/funcs/strtok/tst.strtok_null.d' matched 2 probes
> +dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
> diff --git a/test/unittest/funcs/strtok/tst.strtok_nulldel.d b/test/unittest/funcs/strtok/tst.strtok_nulldel.d
> new file mode 100644
> index 00000000..4f6d27f5
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_nulldel.d
> @@ -0,0 +1,17 @@
> +/*
> + * 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.
> + */
> +
> +BEGIN
> +{
> + strtok("abc", NULL);
> + exit(1);
> +}
> +
> +ERROR
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/funcs/strtok/tst.strtok_nulldel.r b/test/unittest/funcs/strtok/tst.strtok_nulldel.r
> new file mode 100644
> index 00000000..2798a1c6
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_nulldel.r
> @@ -0,0 +1,6 @@
> + FUNCTION:NAME
> + :ERROR
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/funcs/strtok/tst.strtok_nulldel.d' matched 2 probes
> +dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
> diff --git a/test/unittest/funcs/strtok/tst.strtok_nullstr.d b/test/unittest/funcs/strtok/tst.strtok_nullstr.d
> new file mode 100644
> index 00000000..74562714
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_nullstr.d
> @@ -0,0 +1,17 @@
> +/*
> + * 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.
> + */
> +
> +BEGIN
> +{
> + strtok(strstr("abc", "def"), "");
> + exit(1);
> +}
> +
> +ERROR
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/funcs/strtok/tst.strtok_nullstr.r b/test/unittest/funcs/strtok/tst.strtok_nullstr.r
> new file mode 100644
> index 00000000..dca5173b
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_nullstr.r
> @@ -0,0 +1,6 @@
> + FUNCTION:NAME
> + :ERROR
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/funcs/strtok/tst.strtok_nullstr.d' matched 2 probes
> +dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
> diff --git a/test/unittest/funcs/strtok/tst.strtok_nullstr2.d b/test/unittest/funcs/strtok/tst.strtok_nullstr2.d
> new file mode 100644
> index 00000000..2ac38c31
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_nullstr2.d
> @@ -0,0 +1,26 @@
> +/*
> + * 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.
> + */
> +
> +BEGIN
> +{
> + trace(strtok("abc", "def"));
> +}
> +
> +BEGIN
> +{
> + trace(strtok(NULL, "def"));
> +}
> +
> +BEGIN
> +{
> + exit(1);
> +}
> +
> +ERROR
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/funcs/strtok/tst.strtok_nullstr2.r b/test/unittest/funcs/strtok/tst.strtok_nullstr2.r
> new file mode 100644
> index 00000000..6cbf5bd3
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_nullstr2.r
> @@ -0,0 +1,7 @@
> + FUNCTION:NAME
> + :BEGIN abc
> + :ERROR
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/funcs/strtok/tst.strtok_nullstr2.d' matched 4 probes
> +dtrace: error on enabled probe ID 4 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #2
> diff --git a/test/unittest/funcs/strtok/tst.strtok_regs.d b/test/unittest/funcs/strtok/tst.strtok_regs.d
> new file mode 100644
> index 00000000..58ad4b62
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_regs.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.
> + */
> +
> +/* @@xfail: reg spill-fill problems */
> +
> +#pragma D option quiet
> +
> +BEGIN {
> + A = "000";
> + B = "111";
> + C = "222";
> + D = "333";
> + E = "444";
> + F = "555";
> + trace(strjoin(A, strjoin(B, strjoin(C, strjoin(D, strtok(E, F))))));
> + exit(0);
> +}
> diff --git a/test/unittest/funcs/strtok/tst.strtok_regs.r b/test/unittest/funcs/strtok/tst.strtok_regs.r
> new file mode 100644
> index 00000000..07e764be
> --- /dev/null
> +++ b/test/unittest/funcs/strtok/tst.strtok_regs.r
> @@ -0,0 +1 @@
> +000111222333444
> diff --git a/test/unittest/funcs/tst.strtok_null.r b/test/unittest/funcs/tst.strtok_null.r
> deleted file mode 100644
> index a1f82f91..00000000
> --- a/test/unittest/funcs/tst.strtok_null.r
> +++ /dev/null
> @@ -1,6 +0,0 @@
> - FUNCTION:NAME
> - :ERROR
> -
> --- @@stderr --
> -dtrace: script 'test/unittest/funcs/tst.strtok_null.d' matched 2 probes
> -dtrace: error on enabled probe ID 1 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1 at DIF offset 40
> --
> 2.18.4
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
More information about the DTrace-devel
mailing list