Re: [PATCH v2 2/2] cg: transition [u]stack() from action to subroutine

Eugene Loh <[email protected]>
Newsgroups dev.linux.lists.dtrace
Message-ID <[email protected]>
Reviewed-by: Eugene Loh <[email protected]>

On 9/29/25 15:47, Kris Van Hees wrote:
> In order to allow [u]stack() to be used in expressions, they must be
> subroutines.  An exception is added to allow them to retain their
> original behaviour as data recording actions as well.
>
> The implementation change requires a new internal type to be defined
> (dt_stack) to hold stack trace data.
>
> This patch does not allow [u]stack() to be used in assignments to
> variables just yet, but it supports all previous uses of [u]stack().
> Additional functionality will be added on top of this.
>
> Signed-off-by: Kris Van Hees <[email protected]>
> ---
>   include/dtrace/dif_defines.h                  |   6 +-
>   libdtrace/dt_cg.c                             | 350 +++++++++++-------
>   libdtrace/dt_dctx.h                           |   4 +-
>   libdtrace/dt_open.c                           |  21 +-
>   libdtrace/dt_options.c                        |  44 ++-
>   libdtrace/dt_printf.c                         |   3 +-
>   test/unittest/funcs/tst.subr.d                |  13 +-
>   .../printa/err.D_PRINTF_ARG_TYPE.stack.r      |   2 +-
>   .../printa/err.D_PRINTF_ARG_TYPE.ustack.r     |   2 +-
>   9 files changed, 289 insertions(+), 156 deletions(-)
>
> diff --git a/include/dtrace/dif_defines.h b/include/dtrace/dif_defines.h
> index 9f6e3b55..a18614d2 100644
> --- a/include/dtrace/dif_defines.h
> +++ b/include/dtrace/dif_defines.h
> @@ -2,7 +2,7 @@
>    * Licensed under the Universal Permissive License v 1.0 as shown at
>    * http://oss.oracle.com/licenses/upl.
>    *
> - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
>    */
>   
>   /*
> @@ -210,8 +210,10 @@
>   #define DIF_SUBR_INET_NTOA6		43
>   #define DIF_SUBR_D_PATH			44
>   #define DIF_SUBR_LINK_NTOP		45
> +#define DIF_SUBR_STACK			46
> +#define DIF_SUBR_USTACK			47
>   
> -#define DIF_SUBR_MAX			45
> +#define DIF_SUBR_MAX			47
>   
>   typedef uint32_t	dif_instr_t;
>   
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index 51473667..0299ccb1 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -2723,7 +2723,8 @@ dt_cg_stack_arg(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_actkind_t kind)
>    * The "kind" argument is either DTRACEACT_STACK or DTRACEACT_USTACK.
>    */
>   static int
> -dt_cg_act_stack_sub(dt_pcb_t *pcb, dt_node_t *dnp, int reg, int off, dtrace_actkind_t kind)
> +dt_cg_act_stack_sub(dt_pcb_t *pcb, dt_node_t *dnp, int reg, int off,
> +		    dtrace_actkind_t kind)
>   {
>   	dtrace_hdl_t	*dtp = pcb->pcb_hdl;
>   	dt_irlist_t	*dlp = &pcb->pcb_ir;
> @@ -2743,12 +2744,9 @@ dt_cg_act_stack_sub(dt_pcb_t *pcb, dt_node_t *dnp, int reg, int off, dtrace_actk
>   
>   	/* Handle alignment and reserve space in the output buffer. */
>   	if (reg >= 0) {
> -		uint_t	nextoff;
> -		nextoff = (off + (align - 1)) & ~(align - 1);
> -		if (off < nextoff)
> -			emit(dlp,  BPF_ALU64_IMM(BPF_ADD, reg, nextoff - off));
> -		off = nextoff + prefsz + stacksize;
> +		off = ALIGN(off, align);
>   	} else {
> +		reg = BPF_REG_9;
>   		off = dt_rec_add(dtp, dt_cg_fill_gap, kind,
>   				 prefsz + stacksize, align, NULL, arg);
>   	}
> @@ -2762,11 +2760,7 @@ dt_cg_act_stack_sub(dt_pcb_t *pcb, dt_node_t *dnp, int reg, int off, dtrace_actk
>   		dt_regset_free_args(drp);
>   		/* mov32 %r0, %r0 effectively masks the lower 32 bits. */
>   		emit(dlp,  BPF_MOV32_REG(BPF_REG_0, BPF_REG_0));
> -
> -		if (reg >= 0)
> -			emit(dlp,  BPF_STORE(BPF_DW, reg, 0, BPF_REG_0));
> -		else
> -			emit(dlp,  BPF_STORE(BPF_DW, BPF_REG_9, off, BPF_REG_0));
> +		emit(dlp,  BPF_STORE(BPF_DW, reg, off, BPF_REG_0));
>   		dt_regset_free(drp, BPF_REG_0);
>   	}
>   
> @@ -2774,14 +2768,8 @@ dt_cg_act_stack_sub(dt_pcb_t *pcb, dt_node_t *dnp, int reg, int off, dtrace_actk
>   	if (dt_regset_xalloc_args(drp) == -1)
>   		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
>   	dt_cg_access_dctx(BPF_REG_1, dlp, drp, DCTX_CTX);
> -	if (reg >= 0) {
> -		emit(dlp,  BPF_MOV_REG(BPF_REG_2, reg));
> -		if (prefsz)
> -			emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, prefsz));
> -	} else {
> -		emit(dlp,  BPF_MOV_REG(BPF_REG_2, BPF_REG_9));
> -		emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, off + prefsz));
> -	}
> +	emit(dlp,  BPF_MOV_REG(BPF_REG_2, reg));
> +	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, off + prefsz));
>   	emit(dlp,  BPF_MOV_IMM(BPF_REG_3, stacksize));
>   	if (kind == DTRACEACT_USTACK)
>   		emit(dlp,  BPF_MOV_IMM(BPF_REG_4, BPF_F_USER_STACK));
> @@ -2798,11 +2786,7 @@ dt_cg_act_stack_sub(dt_pcb_t *pcb, dt_node_t *dnp, int reg, int off, dtrace_actk
>   	emitl(dlp, lbl_valid,
>   		   BPF_NOP());
>   
> -	/* Finish. */
> -	if (reg >= 0)
> -		emit(dlp,  BPF_ALU64_IMM(BPF_ADD, reg, prefsz + stacksize));
> -
> -	return off;
> +	return prefsz + stacksize;
>   }
>   
>   static void
> @@ -3909,33 +3893,42 @@ dt_cg_arglist(dt_ident_t *idp, dt_node_t *args, dt_irlist_t *dlp,
>   			longjmp(yypcb->pcb_jmpbuf, EDT_NOTUPREG);
>   
>   		/*
> -		 * Handle actions that can be used as agg keys.
> -		 * If we are not an aggregation, dt_cg_node(dnp, ...)
> -		 * will alert us there is a problem.
> +		 * Some function calls (actions or subroutines) need special
> +		 * handling.
>   		 */
> -		if (idp->di_kind == DT_IDENT_AGG &&
> -		    dnp->dn_kind == DT_NODE_FUNC &&
> -		    dnp->dn_ident != NULL)
> -			switch (dnp->dn_ident->di_id) {
> -			case DT_ACT_STACK:
> -			case DT_ACT_USTACK:
> -				/* If this is a stack()-like function, we can handle it later. */
> -				dt_cg_push_stack(BPF_REG_FP, dlp, drp);
> -				continue;
> -			case DT_ACT_JSTACK:
> -				dnerror(dnp, D_UNKNOWN, "jstack() is not implemented (yet)\n");
> -				/* FIXME: Needs implementation */
> -			case DT_ACT_SYM:
> -			case DT_ACT_MOD:
> -			case DT_ACT_UADDR:
> -			case DT_ACT_UMOD:
> -			case DT_ACT_USYM:
> -				/* Otherwise, use the action's arg, not its "return value". */
> -				dt_cg_node(dnp->dn_args, dlp, drp);
> -				dt_cg_push_stack(dnp->dn_args->dn_reg, dlp, drp);
> -				dt_regset_free(drp, dnp->dn_args->dn_reg);
> -				continue;
> +		if (dnp->dn_kind == DT_NODE_FUNC) {
> +			dt_ident_t	*fidp = dnp->dn_ident;
> +
> +			assert(fidp != NULL);
> +
> +			/*
> +			 * For subroutines, stack() and ustack() don't need to
> +			 * be evaluated until we fill in the data of the tuple.
> +			 */
> +			if (fidp->di_kind == DT_IDENT_FUNC) {
> +				if (fidp->di_id == DIF_SUBR_STACK ||
> +				    fidp->di_id == DIF_SUBR_USTACK) {
> +					dt_cg_push_stack(BPF_REG_FP, dlp, drp);
> +					continue;
> +				}
> +			} else if (fidp->di_kind == DT_IDENT_ACTFUNC) {
> +				switch (fidp->di_id) {
> +				case DT_ACT_JSTACK:
> +					dnerror(dnp, D_UNKNOWN, "jstack() is not implemented (yet)\n");
> +					/* FIXME: Needs implementation */
> +				case DT_ACT_SYM:
> +				case DT_ACT_MOD:
> +				case DT_ACT_UADDR:
> +				case DT_ACT_UMOD:
> +				case DT_ACT_USYM:
> +					/* Otherwise, use the action's arg, not its "return value". */
> +					dt_cg_node(dnp->dn_args, dlp, drp);
> +					dt_cg_push_stack(dnp->dn_args->dn_reg, dlp, drp);
> +					dt_regset_free(drp, dnp->dn_args->dn_reg);
> +					continue;
> +				}
>   			}
> +		}
>   
>   		/* Push the component (pointer or value) onto the tuple stack. */
>   		dt_cg_node(dnp, dlp, drp);
> @@ -3972,7 +3965,6 @@ empty_args:
>   	dt_regset_free(drp, BPF_REG_0);
>   
>   	/* Reserve space for a uint32_t value at the beginning of the tuple. */
> -	emit(dlp, BPF_ALU64_IMM(BPF_ADD, treg, sizeof(uint32_t)));
>   	tuplesize = sizeof(uint32_t);
>   
>   	if ((areg = dt_regset_alloc(drp)) == -1)
> @@ -3986,53 +3978,62 @@ empty_args:
>   	for (dnp = args, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
>   		dtrace_diftype_t	t;
>   		size_t			size;
> -		uint_t			nextoff;
>   		int			is_symmod = 0;
>   
> -		if (dnp->dn_kind == DT_NODE_FUNC &&
> -		    dnp->dn_ident != NULL)
> -			switch (dnp->dn_ident->di_id) {
> -			case DT_ACT_STACK:
> -				tuplesize = dt_cg_act_stack_sub(yypcb, dnp, treg, tuplesize, DTRACEACT_STACK);
> -				continue;
> -			case DT_ACT_USTACK:
> -				tuplesize = dt_cg_act_stack_sub(yypcb, dnp, treg, tuplesize, DTRACEACT_USTACK);
> -				continue;
> -			case DT_ACT_UADDR:
> -			case DT_ACT_USYM:
> -			case DT_ACT_UMOD:
> -				nextoff = (tuplesize + (8 - 1)) & ~(8 - 1);
> -				if (tuplesize < nextoff)
> -					emit(dlp,  BPF_ALU64_IMM(BPF_ADD, treg, nextoff - tuplesize));
> -
> -				/* Preface the value with the user process pid. */
> -				if (dt_regset_xalloc_args(drp) == -1)
> -					longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> -				dt_regset_xalloc(drp, BPF_REG_0);
> -				emit(dlp, BPF_CALL_HELPER(BPF_FUNC_get_current_pid_tgid));
> -				dt_regset_free_args(drp);
> -				emit(dlp, BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32));
> -				emit(dlp, BPF_STORE(BPF_DW, treg, 0, BPF_REG_0));
> -				dt_regset_free(drp, BPF_REG_0);
> -
> -				/* Then store the value. */
> -				dt_regset_xalloc(drp, BPF_REG_0);
> -				emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, areg, -i * DT_STK_SLOT_SZ));
> -				emit(dlp,  BPF_STORE(BPF_DW, treg, 8, BPF_REG_0));
> -				dt_regset_free(drp, BPF_REG_0);
> -
> -				emit(dlp,  BPF_ALU64_IMM(BPF_ADD, treg, 16));
> -				tuplesize = nextoff + 16;
> -
> -				continue;
> -			case DT_ACT_SYM:
> -			case DT_ACT_MOD:
> -				is_symmod = 1;
> -				size = 8;
> -				dt_regset_xalloc(drp, BPF_REG_0);
> -				emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, areg, -i * DT_STK_SLOT_SZ));
> -				break;
> +		if (dnp->dn_kind == DT_NODE_FUNC) {
> +			dt_ident_t	*fidp = dnp->dn_ident;
> +
> +			if (fidp->di_kind == DT_IDENT_FUNC) {
> +				switch (fidp->di_id) {
> +				case DIF_SUBR_STACK:
> +					tuplesize += dt_cg_act_stack_sub(
> +							yypcb, dnp, treg,
> +							tuplesize,
> +							DTRACEACT_STACK);
> +					continue;
> +				case DIF_SUBR_USTACK:
> +					tuplesize += dt_cg_act_stack_sub(
> +							yypcb, dnp, treg,
> +							tuplesize,
> +							DTRACEACT_USTACK);
> +					continue;
> +				}
> +			} else if (fidp->di_kind == DT_IDENT_ACTFUNC) {
> +				switch (fidp->di_id) {
> +				case DT_ACT_UADDR:
> +				case DT_ACT_USYM:
> +				case DT_ACT_UMOD:
> +					tuplesize = ALIGN(tuplesize, 8);
> +
> +					/* Preface the value with the user process pid. */
> +					if (dt_regset_xalloc_args(drp) == -1)
> +						longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +					dt_regset_xalloc(drp, BPF_REG_0);
> +					emit(dlp, BPF_CALL_HELPER(BPF_FUNC_get_current_pid_tgid));
> +					dt_regset_free_args(drp);
> +					emit(dlp, BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32));
> +					emit(dlp, BPF_STORE(BPF_DW, treg, tuplesize, BPF_REG_0));
> +					dt_regset_free(drp, BPF_REG_0);
> +
> +					/* Then store the value. */
> +					dt_regset_xalloc(drp, BPF_REG_0);
> +					emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, areg, -i * DT_STK_SLOT_SZ));
> +					emit(dlp,  BPF_STORE(BPF_DW, treg, tuplesize + 8, BPF_REG_0));
> +					dt_regset_free(drp, BPF_REG_0);
> +
> +					tuplesize += 16;
> +
> +					continue;
> +				case DT_ACT_SYM:
> +				case DT_ACT_MOD:
> +					is_symmod = 1;
> +					size = 8;
> +					dt_regset_xalloc(drp, BPF_REG_0);
> +					emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, areg, -i * DT_STK_SLOT_SZ));
> +					break;
> +				}
>   			}
> +		}
>   
>   		if (!is_symmod) {
>   			dt_node_diftype(dtp, dnp, &t);
> @@ -4052,15 +4053,12 @@ empty_args:
>   		}
>   
>   		if (dt_node_is_scalar(dnp) || dt_node_is_float(dnp) || is_symmod) {
> -			nextoff = (tuplesize + (size - 1)) & ~(size - 1);
> -			if (tuplesize < nextoff)
> -				emit(dlp,  BPF_ALU64_IMM(BPF_ADD, treg, nextoff - tuplesize));
> +			tuplesize = ALIGN(tuplesize, size);
>   
> -			emit(dlp,  BPF_STOREX(size, treg, 0, BPF_REG_0));
> +			emit(dlp,  BPF_STOREX(size, treg, tuplesize, BPF_REG_0));
>   			dt_regset_free(drp, BPF_REG_0);
>   
> -			emit(dlp,  BPF_ALU64_IMM(BPF_ADD, treg, size));
> -			tuplesize = nextoff + size;
> +			tuplesize += size;
>   		} else if (dt_node_is_string(dnp)) {
>   			uint_t	lbl_valid = dt_irlist_label(dlp);
>   
> @@ -4068,6 +4066,7 @@ empty_args:
>   				longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
>   
>   			emit(dlp,  BPF_MOV_REG(BPF_REG_1, treg));
> +			emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, tuplesize));
>   			emit(dlp,  BPF_MOV_IMM(BPF_REG_2, size + 1));
>   			emit(dlp,  BPF_MOV_REG(BPF_REG_3, BPF_REG_0));
>   			dt_cg_tstring_free(yypcb, dnp);
> @@ -4081,7 +4080,7 @@ empty_args:
>   					  DT_ISIMM, 128 + i);
>   
>   			emitl(dlp, lbl_valid,
> -				   BPF_ALU64_IMM(BPF_ADD, treg, size + 1));
> +				   BPF_NOP());
>   			tuplesize += size + 1;
>   		} else if (t.dtdt_flags & DIF_TF_BYREF) {
>   			uint_t	lbl_valid = dt_irlist_label(dlp);
> @@ -4090,6 +4089,7 @@ empty_args:
>   				longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
>   
>   			emit(dlp,  BPF_MOV_REG(BPF_REG_1, treg));
> +			emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, tuplesize));
>   			emit(dlp,  BPF_MOV_IMM(BPF_REG_2, size));
>   			emit(dlp,  BPF_MOV_REG(BPF_REG_3, BPF_REG_0));
>   			dt_regset_free(drp, BPF_REG_0);
> @@ -4102,7 +4102,7 @@ empty_args:
>   					  DT_ISIMM, 0);
>   
>   			emitl(dlp, lbl_valid,
> -				   BPF_ALU64_IMM(BPF_ADD, treg, size));
> +				   BPF_NOP());
>   			tuplesize += size;
>   		} else
>   			assert(0);	/* We shouldn't be able to get here. */
> @@ -4128,10 +4128,10 @@ empty_args:
>   		dt_regset_xalloc(drp, BPF_REG_0);
>   		emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
>   		dt_regset_free_args(drp);
> -		emit(dlp,  BPF_STORE(BPF_DW, treg, 0, BPF_REG_0));
> +		emit(dlp,  BPF_STORE(BPF_DW, treg, tuplesize, BPF_REG_0));
>   		dt_regset_free(drp, BPF_REG_0);
>   	} else
> -		emit(dlp,  BPF_STORE_IMM(BPF_DW, treg, 0, 0));
> +		emit(dlp,  BPF_STORE_IMM(BPF_DW, treg, tuplesize, 0));
>   
>   	/* Account for the optional TLS key (or 0). */
>   	tuplesize += sizeof(uint64_t);
> @@ -6324,6 +6324,42 @@ dt_cg_subr_strchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>   	TRACE_REGSET("    subr-strchr:End  ");
>   }
>   
> +static void
> +dt_cg_subr_stack_common(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
> +			dtrace_actkind_t kind)
> +{
> +	dtrace_hdl_t	*dtp = yypcb->pcb_hdl;
> +
> +	if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
> +		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> +	/*
> +	 * Collect the stack in the static buffer (because it is an invariant
> +	 * for a probe firing), and return a pointer to it.
> +	 */
> +	dt_cg_access_dctx(dnp->dn_reg, dlp, drp, DCTX_MEM);
> +	emit(dlp, BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, DMEM_STACK(dtp)));
> +
> +	/* Call the stack helper function to collect the stack. */
> +	dt_cg_act_stack_sub(yypcb, dnp, dnp->dn_reg, 0, kind);
> +}
> +
> +static void
> +dt_cg_subr_stack(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> +{
> +	TRACE_REGSET("    subr-stack:Begin");
> +	dt_cg_subr_stack_common(dnp, dlp, drp, DTRACEACT_STACK);
> +	TRACE_REGSET("    subr-stack:End  ");
> +}
> +
> +static void
> +dt_cg_subr_ustack(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> +{
> +	TRACE_REGSET("    subr-ustack:Begin");
> +	dt_cg_subr_stack_common(dnp, dlp, drp, DTRACEACT_USTACK);
> +	TRACE_REGSET("    subr-ustack:End  ");
> +}
> +
>   static void
>   dt_cg_subr_strrchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>   {
> @@ -6953,6 +6989,8 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
>   	[DIF_SUBR_INET_NTOA6]		= &dt_cg_subr_inet_ntoa6,
>   	[DIF_SUBR_D_PATH]		= &dt_cg_subr_d_path,
>   	[DIF_SUBR_LINK_NTOP]		= &dt_cg_subr_link_ntop,
> +	[DIF_SUBR_STACK]		= &dt_cg_subr_stack,
> +	[DIF_SUBR_USTACK]		= &dt_cg_subr_ustack,
>   };
>   
>   static void
> @@ -8692,43 +8730,50 @@ dt_cg_agg(dt_pcb_t *pcb, dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>   			uint64_t arg = 0;
>   			dt_ident_t *idp = knp->dn_ident;
>   
> -			if (knp->dn_kind == DT_NODE_FUNC && idp != NULL &&
> -			    idp->di_kind == DT_IDENT_ACTFUNC) {
> -				size = 16;
> +			if (knp->dn_kind == DT_NODE_FUNC && idp != NULL) {
>   				alignment = 8;
> -				switch (idp->di_id) {
> -				case DT_ACT_USTACK:
> -					arg = dt_cg_stack_arg(dtp, knp, DTRACEACT_USTACK);
> -					kind = DTRACEACT_USTACK;
> -					size = 8 + 8 * DTRACE_STACK_NFRAMES(arg);
> -					break;
> -				case DT_ACT_JSTACK:
> -					kind = DTRACEACT_JSTACK;
> -					break;
> -				case DT_ACT_USYM:
> -					kind = DTRACEACT_USYM;
> -					break;
> -				case DT_ACT_UMOD:
> -					kind = DTRACEACT_UMOD;
> -					break;
> -				case DT_ACT_UADDR:
> -					kind = DTRACEACT_UADDR;
> -					break;
> -				case DT_ACT_STACK:
> -					arg = dt_cg_stack_arg(dtp, knp, DTRACEACT_STACK);
> -					kind = DTRACEACT_STACK;
> -					size = 8 * arg;
> -					break;
> -				case DT_ACT_SYM:
> -					kind = DTRACEACT_SYM;
> -					size = 8;
> -					break;
> -				case DT_ACT_MOD:
> -					kind = DTRACEACT_MOD;
> -					size = 8;
> -					break;
> +
> +				if (idp->di_kind == DT_IDENT_FUNC) {
> +					switch (idp->di_id) {
> +					case DIF_SUBR_USTACK:
> +						arg = dt_cg_stack_arg(dtp, knp, DTRACEACT_USTACK);
> +						kind = DTRACEACT_USTACK;
> +						size = 8 + 8 * DTRACE_STACK_NFRAMES(arg);
> +						goto add_rec;
> +					case DIF_SUBR_STACK:
> +						arg = dt_cg_stack_arg(dtp, knp, DTRACEACT_STACK);
> +						kind = DTRACEACT_STACK;
> +						size = 8 * arg;
> +						goto add_rec;
> +					}
> +				} else if (idp->di_kind == DT_IDENT_ACTFUNC) {
> +					size = 16;
> +					switch (idp->di_id) {
> +					case DT_ACT_JSTACK:
> +						kind = DTRACEACT_JSTACK;
> +						goto add_rec;
> +					case DT_ACT_USYM:
> +						kind = DTRACEACT_USYM;
> +						goto add_rec;
> +					case DT_ACT_UMOD:
> +						kind = DTRACEACT_UMOD;
> +						goto add_rec;
> +					case DT_ACT_UADDR:
> +						kind = DTRACEACT_UADDR;
> +						goto add_rec;
> +					case DT_ACT_SYM:
> +						kind = DTRACEACT_SYM;
> +						size = 8;
> +						goto add_rec;
> +					case DT_ACT_MOD:
> +						kind = DTRACEACT_MOD;
> +						size = 8;
> +						goto add_rec;
> +					}
>   				}
> -			} else if (dt_node_is_string(knp)) {
> +			}
> +
> +			if (dt_node_is_string(knp)) {
>   				size = dtp->dt_options[DTRACEOPT_STRSIZE] + 1;
>   				alignment = 1;
>   			} else {
> @@ -8739,6 +8784,7 @@ dt_cg_agg(dt_pcb_t *pcb, dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>   				alignment = size;
>   			}
>   
> +add_rec:
>   			dt_aggid_rec_add(dtp, aid->di_id, kind, size, alignment, arg);
>   		}
>   	}
> @@ -8822,10 +8868,34 @@ dt_cg(dt_pcb_t *pcb, dt_node_t *dnp)
>   				if (enp->dn_kind == DT_NODE_AGG)
>   					dt_cg_agg(pcb, enp, &pcb->pcb_ir,
>   						  pcb->pcb_regs);
> -				else
> +				else {
> +					/*
> +					 * Special case: [u]stack() in statement
> +					 * context is treated as data generating
> +					 * action.
> +					 */
> +					if (enp->dn_kind == DT_NODE_FUNC) {
> +						uint_t	id;
> +
> +						id = enp->dn_ident->di_id;
> +						if (id == DIF_SUBR_STACK)
> +							id = DTRACEACT_STACK;
> +						else if (id == DIF_SUBR_USTACK)
> +							id = DTRACEACT_USTACK;
> +						else
> +							id = 0;
> +
> +						if (id) {
> +							dt_cg_act_stack(pcb, enp, id);
> +							pcb->pcb_stmt->dtsd_clauseflags |= DT_CLSFLAG_DATAREC;
> +							goto done;
> +						}
> +					}
>   					dt_cg_node(enp, &pcb->pcb_ir,
>   						   pcb->pcb_regs);
> +				}
>   
> +done:
>   				if (enp->dn_reg != -1) {
>   					dt_regset_free(pcb->pcb_regs,
>   						       enp->dn_reg);
> diff --git a/libdtrace/dt_dctx.h b/libdtrace/dt_dctx.h
> index 9ff6cca5..b50a1395 100644
> --- a/libdtrace/dt_dctx.h
> +++ b/libdtrace/dt_dctx.h
> @@ -1,6 +1,6 @@
>   /*
>    * Oracle Linux DTrace.
> - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2020, 2025, 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.
>    */
> @@ -128,7 +128,7 @@ typedef struct dt_dctx {
>    *	    completed.
>    */
>   #define DMEM_STACK_SZ(dtp) \
> -		(sizeof(uint64_t) * (dtp)->dt_options[DTRACEOPT_MAXFRAMES])
> +		(sizeof(uint64_t) * (dtp)->dt_options[DTRACEOPT_MAXFRAMES] + 1)
>   #define DMEM_TSTR_SZ(dtp) \
>   		(DT_TSTRING_SLOTS * DT_TSTRING_SIZE(dtp))
>   #define DMEM_STRTOK_SZ(dtp) \
> diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
> index 509f263d..ed0b14a1 100644
> --- a/libdtrace/dt_open.c
> +++ b/libdtrace/dt_open.c
> @@ -175,7 +175,7 @@ static const dt_ident_t _dtrace_globals[] = {
>   { "ipl", DT_IDENT_SCALAR, 0, DIF_VAR_IPL, DT_ATTR_STABCMN, DT_VERS_1_0,
>   	&dt_idops_type, "uint_t" },
>   { "jstack", DT_IDENT_ACTFUNC, 0, DT_ACT_JSTACK, DT_ATTR_STABCMN, DT_VERS_1_0,
> -	&dt_idops_func, "stack([uint32_t], [uint32_t])" },
> +	&dt_idops_func, "dt_stack([uint32_t], [uint32_t])" },
>   { "link_ntop", DT_IDENT_FUNC, 0, DIF_SUBR_LINK_NTOP, DT_ATTR_STABCMN,
>   	DT_VERS_1_5, &dt_idops_func, "string(int, void *)" },
>   { "llquantize", DT_IDENT_AGGFUNC, 0, DT_AGG_LLQUANTIZE,
> @@ -273,8 +273,8 @@ static const dt_ident_t _dtrace_globals[] = {
>   { "speculation", DT_IDENT_FUNC, 0, DIF_SUBR_SPECULATION,
>   	DT_ATTR_STABCMN, DT_VERS_1_0,
>   	&dt_idops_func, "int()" },
> -{ "stack", DT_IDENT_ACTFUNC, 0, DT_ACT_STACK, DT_ATTR_STABCMN, DT_VERS_1_0,
> -	&dt_idops_func, "stack([uint32_t])" },
> +{ "stack", DT_IDENT_FUNC, DT_IDFLG_DPTR, DIF_SUBR_STACK, DT_ATTR_STABCMN,
> +	DT_VERS_1_0, &dt_idops_func, "dt_stack([uint32_t])" },
>   { "stackdepth", DT_IDENT_SCALAR, 0, DIF_VAR_STACKDEPTH,
>   	DT_ATTR_STABCMN, DT_VERS_1_0,
>   	&dt_idops_type, "uint32_t" },
> @@ -328,8 +328,8 @@ static const dt_ident_t _dtrace_globals[] = {
>   	DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" },
>   { "uregs", DT_IDENT_ARRAY, 0, DIF_VAR_UREGS, DT_ATTR_STABCMN, DT_VERS_1_0,
>   	&dt_idops_regs, NULL },
> -{ "ustack", DT_IDENT_ACTFUNC, 0, DT_ACT_USTACK, DT_ATTR_STABCMN, DT_VERS_1_0,
> -	&dt_idops_func, "stack([uint32_t], [uint32_t])" },
> +{ "ustack", DT_IDENT_FUNC, DT_IDFLG_DPTR, DIF_SUBR_USTACK, DT_ATTR_STABCMN,
> +	DT_VERS_1_0, &dt_idops_func, "dt_stack([uint32_t], [uint32_t])" },
>   { "ustackdepth", DT_IDENT_SCALAR, 0, DIF_VAR_USTACKDEPTH,
>   	DT_ATTR_STABCMN, DT_VERS_1_2,
>   	&dt_idops_type, "uint32_t" },
> @@ -1054,8 +1054,17 @@ dt_vopen(int version, int flags, int *errp,
>   	dtp->dt_type_dyn = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT,
>   	    "<DYN>", ctf_lookup_by_name(dmp->dm_ctfp, "void"));
>   
> +	/*
> +	 * The stack type is added as a typedef of uint64_t[MAXFRAMES].  The
> +	 * final value of MAXFRAMES may be adjusted with the "stackframes"
> +	 * option.
> +	 */
> +	ctr.ctr_contents = ctf_lookup_by_name(dmp->dm_ctfp, "uint64_t");
> +	ctr.ctr_index = ctf_lookup_by_name(dmp->dm_ctfp, "long");
> +	ctr.ctr_nelems = _dtrace_stackframes;
> +
>   	dtp->dt_type_stack = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT,
> -	    "stack", ctf_lookup_by_name(dmp->dm_ctfp, "void"));
> +		"dt_stack", ctf_add_array(dmp->dm_ctfp, CTF_ADD_ROOT, &ctr));
>   
>   	dtp->dt_type_symaddr = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT,
>   	    "_symaddr", ctf_lookup_by_name(dmp->dm_ctfp, "void"));
> diff --git a/libdtrace/dt_options.c b/libdtrace/dt_options.c
> index 25e79979..7508f044 100644
> --- a/libdtrace/dt_options.c
> +++ b/libdtrace/dt_options.c
> @@ -1,6 +1,6 @@
>   /*
>    * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, 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.
>    */
> @@ -912,6 +912,46 @@ dt_opt_rate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
>   	return 0;
>   }
>   
> +/*
> + * When setting the maxframes option, set the option in the dt_options array
> + * using dt_opt_runtime() as usual, and then update the definition of the CTF
> + * type for "_stack" to be an array of the corresponding size.
> + * If any errors occur, reset dt_options[option] to its previous value.
> + */
> +static int
> +dt_opt_maxframes(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
> +{
> +	dtrace_optval_t val = dtp->dt_options[option];
> +	ctf_file_t *fp = DT_STACK_CTFP(dtp);
> +	ctf_id_t type = ctf_type_resolve(fp, DT_STACK_TYPE(dtp));
> +	ctf_arinfo_t r;
> +
> +	if (dt_opt_runtime(dtp, arg, option) != 0)
> +		return -1; /* dt_errno is set for us */
> +
> +	if (dtp->dt_options[option] > UINT_MAX) {
> +		dtp->dt_options[option] = val;
> +		return dt_set_errno(dtp, EOVERFLOW);
> +	}
> +
> +	if (ctf_array_info(fp, type, &r) == CTF_ERR) {
> +		dtp->dt_options[option] = val;
> +		dtp->dt_ctferr = ctf_errno(fp);
> +		return dt_set_errno(dtp, EDT_CTF);
> +	}
> +
> +	r.ctr_nelems = (uint_t)dtp->dt_options[option];
> +
> +	if (ctf_set_array(fp, type, &r) == CTF_ERR ||
> +	    ctf_update(fp) == CTF_ERR) {
> +		dtp->dt_options[option] = val;
> +		dtp->dt_ctferr = ctf_errno(fp);
> +		return dt_set_errno(dtp, EDT_CTF);
> +	}
> +
> +	return 0;
> +}
> +
>   /*
>    * When setting the strsize option, set the option in the dt_options array
>    * using dt_opt_size() as usual, and then update the definition of the CTF
> @@ -1157,7 +1197,7 @@ static const dt_option_t _dtrace_rtoptions[] = {
>   	{ "jstackframes", dt_opt_runtime, DTRACEOPT_JSTACKFRAMES },
>   	{ "jstackstrsize", dt_opt_size, DTRACEOPT_JSTACKSTRSIZE },
>   	{ "lockmem", dt_opt_lockmem, DTRACEOPT_LOCKMEM },
> -	{ "maxframes", dt_opt_runtime, DTRACEOPT_MAXFRAMES },
> +	{ "maxframes", dt_opt_maxframes, DTRACEOPT_MAXFRAMES },
>   	{ "nusdtprobes", dt_opt_runtime, DTRACEOPT_NUSDTPROBES },
>   	{ "nspec", dt_opt_runtime, DTRACEOPT_NSPEC },
>   	{ "pcapsize", dt_opt_pcapsize, DTRACEOPT_PCAPSIZE },
> diff --git a/libdtrace/dt_printf.c b/libdtrace/dt_printf.c
> index 0d7c0bb4..a3e15397 100644
> --- a/libdtrace/dt_printf.c
> +++ b/libdtrace/dt_printf.c
> @@ -24,7 +24,8 @@
>   static int
>   pfcheck_addr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
>   {
> -	return dt_node_is_pointer(dnp) || dt_node_is_integer(dnp);
> +	return (dt_node_is_pointer(dnp) && !dt_node_is_stack(dnp)) ||
> +	       dt_node_is_integer(dnp);
>   }
>   
>   /*ARGSUSED*/
> diff --git a/test/unittest/funcs/tst.subr.d b/test/unittest/funcs/tst.subr.d
> index 9f7203c0..d97a0780 100644
> --- a/test/unittest/funcs/tst.subr.d
> +++ b/test/unittest/funcs/tst.subr.d
> @@ -1,6 +1,6 @@
>   /*
>    * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, 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.
>    */
> @@ -36,6 +36,15 @@
>   	/*DSTYLED*/			\
>   	}
>   
> +#define STKFUNC(x)			\
> +	BEGIN				\
> +	/*DSTYLED*/			\
> +	{				\
> +		subr++;			\
> +		@stk[x] = sum(1);	\
> +	/*DSTYLED*/			\
> +	}
> +
>   #define NUM_UNIMPLEMENTED 7
>   
>   INTFUNC(rand())
> @@ -89,6 +98,8 @@ STRFUNC(inet_ntoa6((in6_addr_t *)alloca(sizeof(in6_addr_t))))
>   /* Not implemented yet.
>      STRFUNC(d_path(&(curthread->fs->root)))
>      STRFUNC(link_ntop(ARPHRD_ETHER, (void *)alloca(sizeof(ipaddr_t)))) */
> +STKFUNC(stack(5))
> +STKFUNC(ustack(5))
>   
>   BEGIN
>   /subr == DIF_SUBR_MAX + 1 - NUM_UNIMPLEMENTED/
> diff --git a/test/unittest/printa/err.D_PRINTF_ARG_TYPE.stack.r b/test/unittest/printa/err.D_PRINTF_ARG_TYPE.stack.r
> index 50e884a0..95ac80da 100644
> --- a/test/unittest/printa/err.D_PRINTF_ARG_TYPE.stack.r
> +++ b/test/unittest/printa/err.D_PRINTF_ARG_TYPE.stack.r
> @@ -2,4 +2,4 @@
>   dtrace: failed to compile script test/unittest/printa/err.D_PRINTF_ARG_TYPE.stack.d: [D_PRINTF_ARG_TYPE] line 12: printa( ) argument #2 is incompatible with conversion #1 prototype:
>   	conversion: %p
>   	 prototype: pointer or integer
> -	  argument: stack
> +	  argument: dt_stack
> diff --git a/test/unittest/printa/err.D_PRINTF_ARG_TYPE.ustack.r b/test/unittest/printa/err.D_PRINTF_ARG_TYPE.ustack.r
> index fc9e1512..5df10376 100644
> --- a/test/unittest/printa/err.D_PRINTF_ARG_TYPE.ustack.r
> +++ b/test/unittest/printa/err.D_PRINTF_ARG_TYPE.ustack.r
> @@ -2,4 +2,4 @@
>   dtrace: failed to compile script test/unittest/printa/err.D_PRINTF_ARG_TYPE.ustack.d: [D_PRINTF_ARG_TYPE] line 12: printa( ) argument #2 is incompatible with conversion #1 prototype:
>   	conversion: %p
>   	 prototype: pointer or integer
> -	  argument: stack
> +	  argument: dt_stack
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.