[DTrace-devel] [PATCH v2 12/14] Add support for NULL strings
Kris Van Hees
kris.van.hees at oracle.com
Wed May 24 00:20:12 UTC 2023
On Wed, May 03, 2023 at 02:24:33AM -0400, eugene.loh--- via DTrace-devel wrote:
> From: Eugene Loh <eugene.loh at oracle.com>
>
> In C, strings are (char*), and it is possible to have a NULL pointer.
>
> In D, strings have fixed length strsize. There may still be NULL
> pointers -- say, as inputs to strtok() or as outputs from unsuccessful
> strchr() (and other) functions.
>
> Such NULL pointers are not a problem for storing strings as dynamic
> variables (thread-local variables or associative arrays), since storing
> 0 clears a dynamic element and reading a cleared element means 0.
>
> Static (global and local) variables are a different story. Each
> string has strsize bytes allocated for it, and every possible string
> value is legal: bytes up to the first NUL are part of the string
> and subsequent bytes are ignored. There is no value that represents
> a NULL string.
>
> Specifically, there is an important semantic difference between NULL
> and empty strings. The former are NULL pointers, while the latter are
> strings that simply start with the NUL terminating char.
>
> Add code to handle store and load of NULL-pointer strings to and from
> global and local variables.
>
> Specifically, define a byte string DT_NULL_STRING whose first byte is
> 0x00 but with extra nonzero bytes to distinguish between empty and NULL
> strings. An empty string stored as a static variable will have its
> first bytes all zero. A NULL string will have its first bytes be
> DT_NULL_STRING. That is, both will have initial byte 0, and then we
> have to go further to distinguish the two cases.
>
> We require strsize >= sizeof(DT_NULL_STRING), which is reasonable.
>
> Note that comparisons between NULL and empty strings should work the
> same way as between NULL strings and any other non-NULL strings. Note
> that Solaris and legacy DTrace on Linux incorrectly treated comparisons
> between NULL and empty strings as between equal values.
>
> Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
> ---
> libdtrace/dt_cg.c | 127 ++++++++++++++++--
> libdtrace/dt_impl.h | 23 ++++
> libdtrace/dt_options.c | 5 +
> .../unittest/codegen/err.deref_string-assoc.d | 30 +++++
> .../unittest/codegen/err.deref_string-assoc.r | 3 +
> .../codegen/err.deref_string-assoc.r.p | 6 +
> test/unittest/codegen/err.deref_string-gvar.d | 30 +++++
> test/unittest/codegen/err.deref_string-gvar.r | 3 +
> .../codegen/err.deref_string-gvar.r.p | 6 +
> test/unittest/codegen/err.deref_string-lvar.d | 30 +++++
> test/unittest/codegen/err.deref_string-lvar.r | 3 +
> .../codegen/err.deref_string-lvar.r.p | 6 +
> test/unittest/codegen/err.deref_string-tvar.d | 30 +++++
> test/unittest/codegen/err.deref_string-tvar.r | 3 +
> .../codegen/err.deref_string-tvar.r.p | 6 +
> .../codegen/tst.str_runtime_NULL-assoc.d | 32 +++++
> .../codegen/tst.str_runtime_NULL-assoc.r | 2 +
> .../codegen/tst.str_runtime_NULL-gvar.d | 78 +++++++++++
> .../codegen/tst.str_runtime_NULL-gvar.r | 2 +
> .../codegen/tst.str_runtime_NULL-lvar.d | 32 +++++
> .../codegen/tst.str_runtime_NULL-lvar.r | 2 +
> .../codegen/tst.str_runtime_NULL-tvar.d | 32 +++++
> .../codegen/tst.str_runtime_NULL-tvar.r | 2 +
> .../tst.str_comparison-basic-with-NULL.d | 30 ++++-
> test/unittest/options/err.strsize-min.d | 13 ++
> test/unittest/options/err.strsize-min.r | 2 +
> test/unittest/options/tst.strsize-min.d | 13 ++
> test/unittest/options/tst.strsize-min.r | 5 +
> test/unittest/vars/tst.nullassign.d | 21 ++-
> 29 files changed, 555 insertions(+), 22 deletions(-)
> create mode 100644 test/unittest/codegen/err.deref_string-assoc.d
> create mode 100644 test/unittest/codegen/err.deref_string-assoc.r
> create mode 100755 test/unittest/codegen/err.deref_string-assoc.r.p
> create mode 100644 test/unittest/codegen/err.deref_string-gvar.d
> create mode 100644 test/unittest/codegen/err.deref_string-gvar.r
> create mode 100755 test/unittest/codegen/err.deref_string-gvar.r.p
> create mode 100644 test/unittest/codegen/err.deref_string-lvar.d
> create mode 100644 test/unittest/codegen/err.deref_string-lvar.r
> create mode 100755 test/unittest/codegen/err.deref_string-lvar.r.p
> create mode 100644 test/unittest/codegen/err.deref_string-tvar.d
> create mode 100644 test/unittest/codegen/err.deref_string-tvar.r
> create mode 100755 test/unittest/codegen/err.deref_string-tvar.r.p
> create mode 100644 test/unittest/codegen/tst.str_runtime_NULL-assoc.d
> create mode 100644 test/unittest/codegen/tst.str_runtime_NULL-assoc.r
> create mode 100644 test/unittest/codegen/tst.str_runtime_NULL-gvar.d
> create mode 100644 test/unittest/codegen/tst.str_runtime_NULL-gvar.r
> create mode 100644 test/unittest/codegen/tst.str_runtime_NULL-lvar.d
> create mode 100644 test/unittest/codegen/tst.str_runtime_NULL-lvar.r
> create mode 100644 test/unittest/codegen/tst.str_runtime_NULL-tvar.d
> create mode 100644 test/unittest/codegen/tst.str_runtime_NULL-tvar.r
> create mode 100644 test/unittest/options/err.strsize-min.d
> create mode 100644 test/unittest/options/err.strsize-min.r
> create mode 100644 test/unittest/options/tst.strsize-min.d
> create mode 100644 test/unittest/options/tst.strsize-min.r
>
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index 6c0d8cb6..f5e86ee8 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -2630,7 +2630,32 @@ dt_cg_load_var(dt_node_t *dst, dt_irlist_t *dlp, dt_regset_t *drp)
> emit(dlp, BPF_LOAD(BPF_DW, dst->dn_reg, dst->dn_reg, DCTX_GVARS));
>
> /* load the variable value or address */
> - if (dst->dn_flags & DT_NF_REF) {
> + if (dt_node_is_string(dst)) {
> + /*
> + * Strings are a special case of by-reference. If we have
> + * a NULL string, we want to set the pointer to 0.
> + */
> + size_t size = sizeof(DT_NULL_STRING);
> + int reg;
> + uint_t L1 = dt_irlist_label(dlp);
> + uint_t L2 = dt_irlist_label(dlp);
> +
> + assert(dst->dn_flags & DT_NF_REF);
> + assert(size > 0 && size <= 8 &&
> + (size & (size - 1)) == 0);
> +
> + if ((reg = dt_regset_alloc(drp)) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> + emit(dlp, BPF_LOAD(ldstw[size], reg, dst->dn_reg, idp->di_offset));
> + emit(dlp, BPF_BRANCH_IMM(BPF_JNE, reg, DT_NULL_STRING, L1));
> + emit(dlp, BPF_MOV_IMM(dst->dn_reg, 0));
> + emit(dlp, BPF_JUMP(L2));
> + emitl(dlp, L1,
> + BPF_ALU64_IMM(BPF_ADD, dst->dn_reg, idp->di_offset));
> + emitl(dlp, L2,
> + BPF_NOP());
> + dt_regset_free(drp, reg);
> + } else if (dst->dn_flags & DT_NF_REF) {
> assert(!(dst->dn_flags & DT_NF_ALLOCA));
> emit(dlp, BPF_ALU64_IMM(BPF_ADD, dst->dn_reg, idp->di_offset));
> } else {
> @@ -3265,11 +3290,64 @@ dt_cg_store_var(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
> else
> emit(dlp, BPF_LOAD(BPF_DW, reg, reg, DCTX_GVARS));
>
> - /* store by value or by reference */
> - if (dnp->dn_flags & DT_NF_REF) {
> - size_t srcsz;
> + /* Store */
> + if (dt_node_is_string(dnp) && dt_node_is_integer(dnp->dn_right)) {
> + /* Store a compile-time NULL pointer (string = NULL) */
> + /*
> + * The parser has already ensured the rhs is 0. In
> + * dt_cook_op2() for DT_TOK_ASGN, it checks dt_node_is_argcompat().
> + * Since rhs is not string compatible (string or char*), we move on
> + * to dt_node_is_ptrcompat(lp, rp, NULL, NULL). There we see a test
> + * if (rp_is_int && (rp->dn_kind != DT_NODE_INT || rp->dn_value != 0))
> + * return 0; // fail if rp is an integer that is not 0 constant
> + */
> +
> + size = sizeof(DT_NULL_STRING);
> +
> + assert(dt_node_is_string(dnp) == dt_node_is_string(dnp->dn_left));
> + assert(idp->di_size == dt_node_type_size(dnp->dn_left));
> + assert(dt_node_is_integer(dnp->dn_right) == (dnp->dn_right->dn_kind == DT_NODE_INT));
> + assert(dnp->dn_right->dn_value == 0);
> + assert(size > 0 && size <= 8 &&
> + (size & (size - 1)) == 0);
>
> emit(dlp, BPF_ALU64_IMM(BPF_ADD, reg, idp->di_offset));
> + emit(dlp, BPF_STORE_IMM(ldstw[size], reg, 0, DT_NULL_STRING));
> +
> + /*
> + * Since strings are passed by value, we need to force
> + * the value of the assignment to be the destination
> + * address.
> + */
> + dt_regset_free(drp, dnp->dn_reg);
> + dnp->dn_reg = reg;
> + } else if (dt_node_is_string(dnp)) {
> + /* General store to string */
> + uint_t Lnull = 0, Ldone = 0;
> + size_t srcsz;
> +
> + assert(dt_node_is_string(dnp) == dt_node_is_string(dnp->dn_left));
> + assert(idp->di_size == dt_node_type_size(dnp->dn_left));
> + assert(dnp->dn_reg == dnp->dn_right->dn_reg);
> +
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, reg, idp->di_offset));
> +
> + /* Check if we are storing a NULL */
> + if (!(dnp->dn_right->dn_flags & DT_NF_ALLOCA)) {
> + Lnull = dt_irlist_label(dlp);
> + Ldone = dt_irlist_label(dlp);
> +
> + emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_reg, 0, Lnull));
> + }
> +
> + /* Normal store to string */
> +
> + /* Start by zeroing out the first bytes */
> + size = sizeof(DT_NULL_STRING);
> + assert(size > 0 && size <= 8 &&
> + (size & (size - 1)) == 0);
> +
> + emit(dlp, BPF_STORE_IMM(ldstw[size], reg, 0, 0));
>
> /*
> * Determine the amount of data to be copied. It is
> @@ -3280,19 +3358,50 @@ dt_cg_store_var(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
> size = MIN(srcsz, idp->di_size);
>
> dt_cg_check_ptr_arg(dlp, drp, dnp->dn_right, NULL);
> +
> dt_cg_memcpy(dlp, drp, reg, dnp->dn_reg, size);
>
> + if (!(dnp->dn_right->dn_flags & DT_NF_ALLOCA)) {
> + emit(dlp, BPF_JUMP(Ldone));
> +
> + /* Store a null pointer */
> + emitl(dlp, Lnull,
> + BPF_NOP());
> + size = sizeof(DT_NULL_STRING);
> + assert(size > 0 && size <= 8 && (size & (size - 1)) == 0);
> + emit(dlp, BPF_STORE_IMM(ldstw[size], reg, 0, DT_NULL_STRING));
> + emitl(dlp, Ldone,
> + BPF_NOP());
> + }
> +
> /*
> * Since strings are passed by value, we need to force
> * the value of the assignment to be the destination
> * address.
> */
> - if (dt_node_is_string(dnp)) {
> - dt_regset_free(drp, dnp->dn_reg);
> - dnp->dn_reg = reg;
> - } else
> - dt_regset_free(drp, reg);
> +
> + dt_regset_free(drp, dnp->dn_reg);
> + dnp->dn_reg = reg;
> + } else if (dnp->dn_flags & DT_NF_REF) {
> + /* Store by reference (copy) */
> + size_t srcsz;
> +
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, reg, idp->di_offset));
> +
> + /*
> + * Determine the amount of data to be copied. It is
> + * the lesser of the size of the identifier and the
> + * size of the data being copied in.
> + */
> + srcsz = dt_node_type_size(dnp->dn_right);
> + size = MIN(srcsz, idp->di_size);
> +
> + dt_cg_check_ptr_arg(dlp, drp, dnp->dn_right, NULL);
> + dt_cg_memcpy(dlp, drp, reg, dnp->dn_reg, size);
> +
> + dt_regset_free(drp, reg);
> } else {
> + /* Store by value */
> size = idp->di_size;
>
> assert(size > 0 && size <= 8 &&
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index e240cef0..b5b4020c 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -58,6 +58,29 @@ extern "C" {
> # define __stringify(x) __stringify_(x)
> #endif
>
> +/*
> + * Note that string = "" is an empty string: its first byte is 0x00.
> + * To store a NULL string -- basically, (char*)0 -- make the first byte 0x00.
> + * Then, to distinguish between empty and NULL strings, follow the initial
> + * byte either with 0s (empty string) or a special set of nonzero bytes.
> + * That is, to designate a NULL string, make the initial bytes DT_NULL_STRING,
> + * where:
> + * - the initial byte is 0x00
> + * - subsequent bytes are nonzero
> + * - the width is at least 2 bytes
> + * (that is, there is at least one nonzero byte)
> + * - the width is at most 4 bytes
> + * (so that DT_NULL_STRING can be used as an IMM)
> + * - the highest bit is 0
> + * (so that if DT_NULL_STRING is an IMM, it won't get sign extended)
> + * Finally, note that strsize must be large enough to hold DT_NULL_STRING.
> + */
> +#ifdef _BIG_ENDIAN
> +#define DT_NULL_STRING ((uint16_t)0x007f)
> +#else
> +#define DT_NULL_STRING ((uint16_t)0x7f00)
> +#endif
> +
> struct dt_module; /* see below */
> struct dt_pfdict; /* see <dt_printf.h> */
> struct dt_arg; /* see below */
> diff --git a/libdtrace/dt_options.c b/libdtrace/dt_options.c
> index d25b2ac9..6d2dd52e 100644
> --- a/libdtrace/dt_options.c
> +++ b/libdtrace/dt_options.c
> @@ -878,6 +878,11 @@ dt_opt_strsize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
> if (dt_opt_size(dtp, arg, option) != 0)
> return -1; /* dt_errno is set for us */
>
> + if (dtp->dt_options[option] < sizeof(DT_NULL_STRING)) {
> + dtp->dt_options[option] = val;
> + return dt_set_errno(dtp, EDT_BADOPTVAL);
> + }
> +
> if (dtp->dt_options[option] > UINT_MAX) {
> dtp->dt_options[option] = val;
> return dt_set_errno(dtp, EOVERFLOW);
> diff --git a/test/unittest/codegen/err.deref_string-assoc.d b/test/unittest/codegen/err.deref_string-assoc.d
> new file mode 100644
> index 00000000..04acf1b5
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-assoc.d
> @@ -0,0 +1,30 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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
> +{
> + s[1234] = "ABCDEFG";
> + trace(s[1234][1]);
> +}
> +
> +BEGIN
> +{
> + s[1234] = NULL;
> + trace(s[1234][1]);
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/codegen/err.deref_string-assoc.r b/test/unittest/codegen/err.deref_string-assoc.r
> new file mode 100644
> index 00000000..08277992
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-assoc.r
> @@ -0,0 +1,3 @@
> +66
> +-- @@stderr --
> +dtrace: error on enabled probe ID 4 (ID 1: dtrace:::BEGIN): invalid address (1) in action #2 at BPF pc NNN
> diff --git a/test/unittest/codegen/err.deref_string-assoc.r.p b/test/unittest/codegen/err.deref_string-assoc.r.p
> new file mode 100755
> index 00000000..68ebc99b
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-assoc.r.p
> @@ -0,0 +1,6 @@
> +#!/usr/bin/sed -f
> +
> +# runtest.sh looks for "0x" to filter out pointer values.
> +# Strip the 0x so that the illegal address will not be filtered out;
> +# we want the address to be checked.
> +s/0x//
> diff --git a/test/unittest/codegen/err.deref_string-gvar.d b/test/unittest/codegen/err.deref_string-gvar.d
> new file mode 100644
> index 00000000..e2beeaed
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-gvar.d
> @@ -0,0 +1,30 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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
> +{
> + s = "ABCDEFG";
> + trace(s[1]);
> +}
> +
> +BEGIN
> +{
> + s = NULL;
> + trace(s[1]);
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/codegen/err.deref_string-gvar.r b/test/unittest/codegen/err.deref_string-gvar.r
> new file mode 100644
> index 00000000..08277992
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-gvar.r
> @@ -0,0 +1,3 @@
> +66
> +-- @@stderr --
> +dtrace: error on enabled probe ID 4 (ID 1: dtrace:::BEGIN): invalid address (1) in action #2 at BPF pc NNN
> diff --git a/test/unittest/codegen/err.deref_string-gvar.r.p b/test/unittest/codegen/err.deref_string-gvar.r.p
> new file mode 100755
> index 00000000..68ebc99b
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-gvar.r.p
> @@ -0,0 +1,6 @@
> +#!/usr/bin/sed -f
> +
> +# runtest.sh looks for "0x" to filter out pointer values.
> +# Strip the 0x so that the illegal address will not be filtered out;
> +# we want the address to be checked.
> +s/0x//
> diff --git a/test/unittest/codegen/err.deref_string-lvar.d b/test/unittest/codegen/err.deref_string-lvar.d
> new file mode 100644
> index 00000000..36525065
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-lvar.d
> @@ -0,0 +1,30 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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
> +{
> + this->s = "ABCDEFG";
> + trace(this->s[1]);
> +}
> +
> +BEGIN
> +{
> + this->s = NULL;
> + trace(this->s[1]);
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/codegen/err.deref_string-lvar.r b/test/unittest/codegen/err.deref_string-lvar.r
> new file mode 100644
> index 00000000..08277992
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-lvar.r
> @@ -0,0 +1,3 @@
> +66
> +-- @@stderr --
> +dtrace: error on enabled probe ID 4 (ID 1: dtrace:::BEGIN): invalid address (1) in action #2 at BPF pc NNN
> diff --git a/test/unittest/codegen/err.deref_string-lvar.r.p b/test/unittest/codegen/err.deref_string-lvar.r.p
> new file mode 100755
> index 00000000..68ebc99b
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-lvar.r.p
> @@ -0,0 +1,6 @@
> +#!/usr/bin/sed -f
> +
> +# runtest.sh looks for "0x" to filter out pointer values.
> +# Strip the 0x so that the illegal address will not be filtered out;
> +# we want the address to be checked.
> +s/0x//
> diff --git a/test/unittest/codegen/err.deref_string-tvar.d b/test/unittest/codegen/err.deref_string-tvar.d
> new file mode 100644
> index 00000000..d7df8bc1
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-tvar.d
> @@ -0,0 +1,30 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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
> +{
> + self->s = "ABCDEFG";
> + trace(self->s[1]);
> +}
> +
> +BEGIN
> +{
> + self->s = NULL;
> + trace(self->s[1]);
> +}
> +
> +BEGIN
> +{
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/codegen/err.deref_string-tvar.r b/test/unittest/codegen/err.deref_string-tvar.r
> new file mode 100644
> index 00000000..08277992
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-tvar.r
> @@ -0,0 +1,3 @@
> +66
> +-- @@stderr --
> +dtrace: error on enabled probe ID 4 (ID 1: dtrace:::BEGIN): invalid address (1) in action #2 at BPF pc NNN
> diff --git a/test/unittest/codegen/err.deref_string-tvar.r.p b/test/unittest/codegen/err.deref_string-tvar.r.p
> new file mode 100755
> index 00000000..68ebc99b
> --- /dev/null
> +++ b/test/unittest/codegen/err.deref_string-tvar.r.p
> @@ -0,0 +1,6 @@
> +#!/usr/bin/sed -f
> +
> +# runtest.sh looks for "0x" to filter out pointer values.
> +# Strip the 0x so that the illegal address will not be filtered out;
> +# we want the address to be checked.
> +s/0x//
> diff --git a/test/unittest/codegen/tst.str_runtime_NULL-assoc.d b/test/unittest/codegen/tst.str_runtime_NULL-assoc.d
> new file mode 100644
> index 00000000..5d7668c0
> --- /dev/null
> +++ b/test/unittest/codegen/tst.str_runtime_NULL-assoc.d
> @@ -0,0 +1,32 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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
> +{
> + a1[1234] = strchr("abcdefghij", 'Z');
> + a2[1234] = strstr("ABCDEFGHIJ", "a");
> + a3[1234] = a1[1234];
> +}
> +
> +BEGIN
> +/a1[1234] != a2[1234] || a1[1234] != a2[1234] || a1[1234] != a3[1234] || a1[1234] != NULL/
> +{
> + printf("FAIL\n");
> +}
> +
> +BEGIN
> +{
> + printf("done\n");
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/codegen/tst.str_runtime_NULL-assoc.r b/test/unittest/codegen/tst.str_runtime_NULL-assoc.r
> new file mode 100644
> index 00000000..3669a236
> --- /dev/null
> +++ b/test/unittest/codegen/tst.str_runtime_NULL-assoc.r
> @@ -0,0 +1,2 @@
> +done
> +
> diff --git a/test/unittest/codegen/tst.str_runtime_NULL-gvar.d b/test/unittest/codegen/tst.str_runtime_NULL-gvar.d
> new file mode 100644
> index 00000000..439bcc13
> --- /dev/null
> +++ b/test/unittest/codegen/tst.str_runtime_NULL-gvar.d
> @@ -0,0 +1,78 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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.
> + */
> +
> +/*
> + * Confirm that runtime-NULL strings can be handled.
> + */
> +
> +#pragma D option quiet
> +
> +/* set some global variables to run-time NULL */
> +BEGIN
> +{
> + g1 = strchr("abcdefghij", 'Z');
> + g2 = strstr("ABCDEFGHIJ", "a");
> + g3 = g1;
> +}
> +
> +/* set some (clause-)local variables to run-time NULL */
> +BEGIN
> +{
> + this->l1 = strchr("abcdefghij", 'Z');
> + this->l2 = strstr("ABCDEFGHIJ", "a");
> + this->l3 = this->l1;
> +}
> +
> +/* set some thread-local variables to run-time NULL */
> +BEGIN
> +{
> + self->t1 = strchr("abcdefghij", 'Z');
> + self->t2 = strstr("ABCDEFGHIJ", "a");
> + self->t3 = self->t1;
> +}
> +
> +/* set some associative-array variables to run-time NULL */
> +BEGIN
> +{
> + a1[1234] = strchr("abcdefghij", 'Z');
> + a2[1234] = strstr("ABCDEFGHIJ", "a");
> + a3[1234] = a1[1234];
> +}
> +
> +/* compare some variables */
> +BEGIN
> +/g1 != g2 || this->l1 != this->l2 || self->t1 != self->t2 || a1[1234] != a2[1234]/
> +{
> + printf("FAIL A\n");
> +}
> +
> +/* compare more variables */
> +BEGIN
> +/g1 != g3 || this->l1 != this->l3 || self->t1 != self->t3 || a1[1234] != a3[1234]/
> +{
> + printf("FAIL B\n");
> +}
> +
> +/* compare some variables to compile-time NULL */
> +BEGIN
> +/g1 != NULL || this->l1 != NULL || self->t1 != NULL || a1[1234] != NULL/
> +{
> + printf("FAIL C\n");
> +}
> +
> +/* exit */
> +BEGIN
> +{
> + printf("done\n");
> + exit(0);
> +}
> +
> +/* handle any unexpected errors */
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/codegen/tst.str_runtime_NULL-gvar.r b/test/unittest/codegen/tst.str_runtime_NULL-gvar.r
> new file mode 100644
> index 00000000..3669a236
> --- /dev/null
> +++ b/test/unittest/codegen/tst.str_runtime_NULL-gvar.r
> @@ -0,0 +1,2 @@
> +done
> +
> diff --git a/test/unittest/codegen/tst.str_runtime_NULL-lvar.d b/test/unittest/codegen/tst.str_runtime_NULL-lvar.d
> new file mode 100644
> index 00000000..76feea80
> --- /dev/null
> +++ b/test/unittest/codegen/tst.str_runtime_NULL-lvar.d
> @@ -0,0 +1,32 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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
> +{
> + this->l1 = strchr("abcdefghij", 'Z');
> + this->l2 = strstr("ABCDEFGHIJ", "a");
> + this->l3 = this->l1;
> +}
> +
> +BEGIN
> +/this->l1 != this->l2 || this->l1 != this->l2 || this->l1 != this->l3 || this->l1 != NULL/
> +{
> + printf("FAIL\n");
> +}
> +
> +BEGIN
> +{
> + printf("done\n");
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/codegen/tst.str_runtime_NULL-lvar.r b/test/unittest/codegen/tst.str_runtime_NULL-lvar.r
> new file mode 100644
> index 00000000..3669a236
> --- /dev/null
> +++ b/test/unittest/codegen/tst.str_runtime_NULL-lvar.r
> @@ -0,0 +1,2 @@
> +done
> +
> diff --git a/test/unittest/codegen/tst.str_runtime_NULL-tvar.d b/test/unittest/codegen/tst.str_runtime_NULL-tvar.d
> new file mode 100644
> index 00000000..e8aa14af
> --- /dev/null
> +++ b/test/unittest/codegen/tst.str_runtime_NULL-tvar.d
> @@ -0,0 +1,32 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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
> +{
> + self->t1 = strchr("abcdefghij", 'Z');
> + self->t2 = strstr("ABCDEFGHIJ", "a");
> + self->t3 = self->t1;
> +}
> +
> +BEGIN
> +/self->t1 != self->t2 || self->t1 != self->t2 || self->t1 != self->t3 || self->t1 != NULL/
> +{
> + printf("FAIL\n");
> +}
> +
> +BEGIN
> +{
> + printf("done\n");
> + exit(0);
> +}
> +
> +ERROR
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/codegen/tst.str_runtime_NULL-tvar.r b/test/unittest/codegen/tst.str_runtime_NULL-tvar.r
> new file mode 100644
> index 00000000..3669a236
> --- /dev/null
> +++ b/test/unittest/codegen/tst.str_runtime_NULL-tvar.r
> @@ -0,0 +1,2 @@
> +done
> +
> diff --git a/test/unittest/operators/tst.str_comparison-basic-with-NULL.d b/test/unittest/operators/tst.str_comparison-basic-with-NULL.d
> index 64bb0579..81f16eb6 100644
> --- a/test/unittest/operators/tst.str_comparison-basic-with-NULL.d
> +++ b/test/unittest/operators/tst.str_comparison-basic-with-NULL.d
> @@ -1,10 +1,9 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2021, 2023, 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: No support for NULL strings yet */
>
> /*
> * ASSERTION: String comparisons work.
> @@ -22,6 +21,8 @@ BEGIN
> s2 = "jklmnopqr";
> s3 = "stuvwxyz!";
>
> + /* Compare normal strings, where lhs < rhs */
> +
> nerrors += (s1 <= s2 ? 0 : 1);
> nerrors += (s1 < s2 ? 0 : 1);
> nerrors += (s1 == s2 ? 1 : 0);
> @@ -29,6 +30,8 @@ BEGIN
> nerrors += (s1 >= s2 ? 1 : 0);
> nerrors += (s1 > s2 ? 1 : 0);
>
> + /* Compare normal strings, where lhs == rhs */
> +
> nerrors += (s2 <= s2 ? 0 : 1);
> nerrors += (s2 < s2 ? 1 : 0);
> nerrors += (s2 == s2 ? 0 : 1);
> @@ -36,6 +39,8 @@ BEGIN
> nerrors += (s2 >= s2 ? 0 : 1);
> nerrors += (s2 > s2 ? 1 : 0);
>
> + /* Compare normal strings, where lhs > rhs */
> +
> nerrors += (s3 <= s2 ? 1 : 0);
> nerrors += (s3 < s2 ? 1 : 0);
> nerrors += (s3 == s2 ? 1 : 0);
> @@ -43,6 +48,8 @@ BEGIN
> nerrors += (s3 >= s2 ? 0 : 1);
> nerrors += (s3 > s2 ? 0 : 1);
>
> + /* Compare strings, where one is NULL */
> +
> s2 = NULL;
> nerrors += (s3 <= s2 ? 1 : 0);
> nerrors += (s3 < s2 ? 1 : 0);
> @@ -58,6 +65,25 @@ BEGIN
> nerrors += (s2 >= s3 ? 1 : 0);
> nerrors += (s2 > s3 ? 1 : 0);
>
> + /* Compare NULL and empty strings */
> +
> + s3 = "";
> + nerrors += (s3 <= s2 ? 1 : 0);
> + nerrors += (s3 < s2 ? 1 : 0);
> + nerrors += (s3 == s2 ? 1 : 0);
> + nerrors += (s3 != s2 ? 0 : 1);
> + nerrors += (s3 >= s2 ? 0 : 0);
> + nerrors += (s3 > s2 ? 0 : 0);
> +
> + nerrors += (s2 <= s3 ? 0 : 1);
> + nerrors += (s2 < s3 ? 0 : 1);
> + nerrors += (s2 == s3 ? 1 : 0);
> + nerrors += (s2 != s3 ? 0 : 1);
> + nerrors += (s2 >= s3 ? 1 : 0);
> + nerrors += (s2 > s3 ? 1 : 0);
> +
> + /* Compare two NULL strings */
> +
> s3 = NULL;
> nerrors += (s2 <= s3 ? 0 : 1);
> nerrors += (s2 < s3 ? 1 : 0);
> diff --git a/test/unittest/options/err.strsize-min.d b/test/unittest/options/err.strsize-min.d
> new file mode 100644
> index 00000000..a2aeca0f
> --- /dev/null
> +++ b/test/unittest/options/err.strsize-min.d
> @@ -0,0 +1,13 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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.
> + */
> +
> +/* @@runtest-opts: -xstrsize=1 */
> +
> +BEGIN
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/options/err.strsize-min.r b/test/unittest/options/err.strsize-min.r
> new file mode 100644
> index 00000000..00d527e0
> --- /dev/null
> +++ b/test/unittest/options/err.strsize-min.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to set -x strsize: Invalid value for specified option
> diff --git a/test/unittest/options/tst.strsize-min.d b/test/unittest/options/tst.strsize-min.d
> new file mode 100644
> index 00000000..9fd902b1
> --- /dev/null
> +++ b/test/unittest/options/tst.strsize-min.d
> @@ -0,0 +1,13 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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.
> + */
> +
> +/* @@runtest-opts: -xstrsize=2 */
> +
> +BEGIN
> +{
> + exit(0);
> +}
> diff --git a/test/unittest/options/tst.strsize-min.r b/test/unittest/options/tst.strsize-min.r
> new file mode 100644
> index 00000000..1a72a90f
> --- /dev/null
> +++ b/test/unittest/options/tst.strsize-min.r
> @@ -0,0 +1,5 @@
> + FUNCTION:NAME
> + :BEGIN
> +
> +-- @@stderr --
> +dtrace: script 'test/unittest/options/tst.strsize-min.d' matched 1 probe
> diff --git a/test/unittest/vars/tst.nullassign.d b/test/unittest/vars/tst.nullassign.d
> index 1de59a43..44ccc30e 100644
> --- a/test/unittest/vars/tst.nullassign.d
> +++ b/test/unittest/vars/tst.nullassign.d
> @@ -1,25 +1,24 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2006, 2023, 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
>
> BEGIN
> {
> - die = "Die";
> - tap = ", SystemTap, ";
> - the = "The";
> + lv = "Live";
> + dt = ", DTrace, ";
> + ps = "Prosper";
> }
>
> BEGIN
> {
> - phrase = strjoin(die, tap);
> - phrase = strjoin(phrase, die);
> - expected = "Die, SystemTap, Die";
> + phrase = strjoin(lv, dt);
> + phrase = strjoin(phrase, lv);
> + expected = "Live, DTrace, Live";
> }
>
> BEGIN
> @@ -31,13 +30,13 @@ BEGIN
>
> BEGIN
> {
> - this->phrase = strjoin(the, tap);
> + this->phrase = strjoin(ps, dt);
> }
>
> BEGIN
> {
> - this->phrase = strjoin(this->phrase, the);
> - expected = "The, SystemTap, The";
> + this->phrase = strjoin(this->phrase, ps);
> + expected = "Prosper, DTrace, Prosper";
> }
>
> BEGIN
> --
> 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