[DTrace-devel] [PATCH 2/2] Add support for substr() subroutine

Eugene Loh eugene.loh at oracle.com
Thu Sep 2 15:34:55 PDT 2021


For now, my main comment is that it would be helpful (for review but 
someday also for our users) if the semantics we're supporting would be 
described in the patch (commit msg or even better in the code, like in 
bpf/substr.S).  The exotic cases of idx and cnt do not seem to be 
described in the documentation (which is another problem).  High-level 
comments in bpf/substr.S saying what is intended would help someone 
understand whether the code is operating correctly.

Meanwhile, ...

On 9/2/21 11:34 AM, Kris Van Hees wrote:
> diff --git a/bpf/substr.S b/bpf/substr.S
> new file mode 100644
> index 00000000..9e032441
> --- /dev/null
> +++ b/bpf/substr.S
> @@ -0,0 +1,98 @@
> +// 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
> +
> +/*
> + * void dt_substr(char *dst, const char *src, int32_t idx, int32_t cnt,
> + *		  uint64_t argc)
> + */
> +	.text
> +	.align	4
> +	.global	dt_substr
> +dt_substr :
> +	stxdw	[%fp+-8], %r1		/* Spill dst */

AFAICT, it is unnecessary to spill dst to the stack.  It lives in %r9 
anyhow.

> +	mov	%r9, %r1
> +	stxdw	[%fp+-16], %r2		/* Spill src */
> +	lsh	%r3, 32			/* Sign-extend idx */
> +	arsh	%r3, 32
> +	mov	%r6, %r3		/* %r6 = idx */
> +
> +	lddw	%r8, STRSZ		/* %r8 = STRSZ (temporary) */
> +	jgt	%r5, 2, .Lhave_cnt
> +	mov	%r4, %r8		/* cnt = STRSZ */
> +.Lhave_cnt:

This "argc" interface strikes me as clumsy.  Just have 
dt_cg_subr_substr() figure cnt out if the user does not supply a value.  
That simplifies both it (the caller) and this code (the callee).

> +	lsh	%r4, 32			/* Sign-extend cnt */
> +	arsh	%r4, 32
> +	mov	%r7, %r4		/* %r7 = cnt */
> +
> +	/*
> +	 * Get the source string length and validate it.  If the length is 0,
> +	 * the result is the empty string.  If the length is greater than the
> +	 * maximum string length (STRSZ), cap it at that value.
> +	 */
> +	ldxdw	%r1, [%fp+-16]
> +	call	dt_strlen		/* len = dt_strlen(src) */
> +	jeq	%r0, 0, .Lempty
> +	mov	%r1, %r8		/* %r1 = STRSZ */

I'm confused why you need %r1=STRSZ (namely, why you need the first 
instruction in Ladjust_cnt), but I've seen crazier stuff with the BPF 
verifier so I cannot claim this is unneeded.  Does look odd, though.

> +	jle	%r0, %r8, .Llen_ok
> +	mov	%r0, %r8		/* len = STRSZ */
> +.Llen_ok:
> +	mov	%r8, %r0		/* %r8 = len */

You don't need the value to be also in %r0.  So how about replacing the 
above four lines with

         jgt     %r0, %r8, .Lfoobar
         mov     %r8, %r0
     .Lfoobar:

or even just
         jgt     %r0, %r8, 1
         mov     %r8, %r0


> +
> +	jsge	%r6, 0, .Ladjust_cnt
> +
> +.Lidx_neg:
> +	add	%r6, %r8		/* idx += len */
> +	jsge	%r6, 0, .Ladjust_cnt
> +	mov	%r0, 0
> +	sub32	%r0, %r6		/* neg messes up the verifier */
> +	jsle	%r7, %r0, .Ladjust_cnt
> +	add32	%r7, %r6		/* cnt += idx */
> +	mov	%r6, 0			/* idx = 0 */
> +
> +.Ladjust_cnt:
> +	jsge	%r6, %r1, .Lempty
> +	jsge	%r6, %r8, .Lempty
> +	jslt	%r6, 0, .Lempty
> +	jsge	%r7, 0, .Lcnt_pos
> +	mov	%r0, %r8
> +	sub32	%r0, %r6
> +	add32	%r7, %r0		/* cnt += len - idx */
> +	lsh	%r7, 32
> +	arsh	%r7, 32
> +	jsle	%r7, 0, .Lempty
> +.Lcnt_pos:
> +	sub	%r1, %r6
> +	jsge	%r1, %r7, .Lcopy
> +	mov	%r7, %r1		/* cnt = STRSZ - idx */
> +
> +.Lcopy:
> +	ldxdw	%r8, [%fp+-16]
> +	add	%r8, DT_STRLEN_BYTES
> +	add	%r8, %r6		/* %r8 = src + DT_STRLEN_BYTES + idx */
> +
> +	mov	%r1, %r7
> +	ldxdw	%r2, [%fp+-8]
> +	mov	%r9, %r2		/* %r9 = dst */
> +	call	dt_strlen_store		/* plen = dt_strlen_store(cnt, dst) */
> +	add	%r9, DT_STRLEN_BYTES	/* %r9 = dst + DT_STRLEN_BYTES */
> +
> +	mov	%r1, %r9
> +	mov	%r2, %r7
> +	mov	%r3, %r8
> +	call	BPF_FUNC_probe_read
> +
> +	add	%r9, %r7
> +	stb	[%r9+0],0
> +	exit
> +
> +.Lempty:
> +	/* Store the empty string in the destination. */
> +	stb	[%r9+0], 0
> +	stb	[%r9+1], 0
> +	exit

I guess .Lempty is okay.  It might be cleaner to use dt_strlen_store(), 
but I suppose all that is going away soon enough.

Do you also want a terminating NULL?


> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index b48b9f7b..749324e4 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -3149,6 +3149,63 @@ dt_cg_subr_strjoin(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>   	TRACE_REGSET("    subr-strjoin:End  ");
>   }
>   
> +static void
> +dt_cg_subr_substr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> +{
> +	dt_node_t	*str = dnp->dn_args;
> +	dt_node_t	*idx = str->dn_list;
> +	dt_node_t	*cnt = idx->dn_list;
> +	dt_ident_t	*idp;
> +
> +	TRACE_REGSET("    subr-substr:Begin");
> +
> +	dt_cg_node(str, dlp, drp);
> +	dt_cg_check_notnull(dlp, drp, str->dn_reg);
> +	dt_cg_node(idx, dlp, drp);
> +	if (cnt != NULL)
> +		dt_cg_node(cnt, dlp, drp);
> +
> +	/*
> +	 * Allocate the result register and associate it with a temporary
> +	 * string slot.
> +	 */
> +	dnp->dn_reg = dt_regset_alloc(drp);
> +	if (dnp->dn_reg == -1)
> +		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +	dt_node_tstring(dnp, dt_cg_tstring_alloc());
> +
> +        emit(dlp,  BPF_LOAD(BPF_DW, dnp->dn_reg, BPF_REG_FP, DT_STK_DCTX));
> +        emit(dlp,  BPF_LOAD(BPF_DW, dnp->dn_reg, dnp->dn_reg, DCTX_MEM));
> +        emit(dlp,  BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, dnp->dn_tstring->dn_value));

Tabs instead of spaces?

> +
> +	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, str->dn_reg));
> +	dt_regset_free(drp, str->dn_reg);
> +	if (str->dn_tstring)
> +		dt_cg_tstring_free(str->dn_tstring->dn_value);
> +	emit(dlp, BPF_MOV_REG(BPF_REG_3, idx->dn_reg));
> +	dt_regset_free(drp, idx->dn_reg);
> +	if (cnt != NULL) {
> +		emit(dlp, BPF_MOV_REG(BPF_REG_4, cnt->dn_reg));
> +		dt_regset_free(drp, cnt->dn_reg);
> +		emit(dlp, BPF_MOV_IMM(BPF_REG_5, 3));
> +	} else {
> +		emit(dlp, BPF_MOV_IMM(BPF_REG_4, 0));
> +		emit(dlp, BPF_MOV_IMM(BPF_REG_5, 2));
> +	}

Again, drop the %r5 stuff.  Just, for cnt==NULL, use 
dt_options[DTRACEOPT_STRSIZE] instead of 0.

> +	dt_regset_xalloc(drp, BPF_REG_0);
> +	idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_substr");
> +	assert(idp != NULL);
> +	emite(dlp,  BPF_CALL_FUNC(idp->di_id), idp);
> +	dt_regset_free_args(drp);
> +	dt_regset_free(drp, BPF_REG_0);
> +
> +	TRACE_REGSET("    subr-substr:End  ");
> +}



More information about the DTrace-devel mailing list