[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