All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Aaron Tomlin" <atomlin@atomlin.com>
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [PATCH perf-tools-next v13 10/10] perf ftrace: Filter extraneous functions in function graph tracer
Date: Mon, 02 Nov 2026 22:11:32 +0000	[thread overview]
Message-ID: <sashiko-outbox-194261@kernel.org> (raw)
In-Reply-To: <20261102214054.911298-11-atomlin@atomlin.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] perf ftrace: unmatched IRQ return marker leaked in ftrace_process_fgraph_line()
- [Medium] perf ftrace: premature orphan_depth reset on nested exit in ftrace_process_fgraph_line()
- [Medium] perf ftrace: ancestor frames discarded on unwind in ftrace_check_target_caller()
--

commit b4d5fc80488b4a74cd5c5107f8098d74571146bb
Author: Aaron Tomlin <atomlin@atomlin.com>

perf ftrace: Filter extraneous functions in function graph tracer

This commit adds per-task target callgraph state tracking and return-address
caller validation in userspace for perf ftrace to suppress unrelated top-level
functions and detached orphan calls when filtering with -G / --graph-funcs.

> diff --git a/tools/perf/util/ftrace.c b/tools/perf/util/ftrace.c
> index c2cf4a20553e2..a919307920321 100644
> --- a/tools/perf/util/ftrace.c
> +++ b/tools/perf/util/ftrace.c

[ ... ]

> @@ -879,6 +1087,208 @@ static int print_line(struct perf_ftrace *ftrace, const char *line, FILE *out)

[ ... ]

> +static int ftrace_check_target_caller(struct perf_ftrace *ftrace,
> +				      struct ftrace_task_state *ts,
> +				      const char *func_start,
> +				      const char *entry_name,
> +				      bool is_graph_target,
> +				      int leading_spaces,
> +				      bool has_open_brace,
> +				      bool has_semicolon,
> +				      bool has_retaddr,
> +				      int caller_idx,
> +				      const char *blank_prefix,
> +				      FILE *out,
> +				      bool *is_target_root)
> +{
> +	if (!ftrace_has_graph_funcs(ftrace) || !ts->in_target ||
> +	    ts->depth <= ts->target_root_depth)
> +		return 0;
> +
> +	if (has_retaddr && caller_idx >= 0 && caller_idx < ts->target_root_depth) {
> +		ftrace_unwind_to_depth(ts, caller_idx + 1, blank_prefix, out);
> +		if (is_graph_target) {
> +			ftrace_reset_to_target_root(ts, entry_name, ts->depth,
> +						    leading_spaces);
> +			*is_target_root = true;
> +			return 0;
> +		}
> +		reset_target_root_state(ts);
> +		if (has_open_brace && !has_semicolon &&
> +		    push_nontarget_frame(ts, func_start, leading_spaces) < 0)
> +			return -ENOMEM;
> +		return 1;
> +	}
> +
> +	if (leading_spaces <= ts->target_root_indent) {
> +		ftrace_unwind_to_depth(ts, 0, blank_prefix, out);
> +		if (is_graph_target) {
> +			ftrace_reset_to_target_root(ts, entry_name, 0,
> +						    leading_spaces);
> +			*is_target_root = true;
> +			return 0;
> +		}
> +		reset_target_root_state(ts);\n> +		if (has_open_brace && !has_semicolon &&
> +		    push_nontarget_frame(ts, func_start, leading_spaces) < 0)
> +			return -ENOMEM;
> +		return 1;
> +	}

[Severity: Medium]
When recovering from a dropped target root exit in
ftrace_check_target_caller() because leading_spaces <= ts->target_root_indent
(with caller_idx < 0, such as when funcgraph-retaddr is unavailable or the
call goes through an untraced wrapper), does calling
ftrace_unwind_to_depth(ts, 0, blank_prefix, out) discard active non-target
ancestor frames below ts->target_root_depth whose indentation is strictly less
than leading_spaces?

If an outer ancestor was pushed onto ts->stack before the target root entered,
unwinding all the way to 0 pops that ancestor frame as well. If that ancestor
subsequently calls another function or re-enters a target function, caller_idx
lookup will fail and suppress inline frame synthesis (--inline) when resolving
inlines in ftrace_process_fgraph_line():

	if (caller_idx < 0)
		num_inlines = 0;

Should this unwind to ts->target_root_depth first and then pop only non-target
ancestor frames with indent >= leading_spaces, matching the exit recovery path
in ftrace_process_fgraph_line()?

> +
> +	if (has_retaddr) {
> +		if (caller_idx < 0) {
> +			if (ts->pending_irq_ent[0] != '\0') {
> +				ts->pending_irq_ent[0] = '\0';
> +				ts->suppress_irq_ret = true;
> +			}
> +			if (has_open_brace && !has_semicolon) {
> +				ts->orphan_depth = 1;
> +				ts->orphan_base_indent = leading_spaces;
> +				ts->orphan_seq = ts->seq;
> +			}
> +			return 1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * ftrace_process_fgraph_line - Process and output a function graph trace line
>   * @ftrace: Perf ftrace context
> @@ -910,16 +1320,54 @@ int ftrace_process_fgraph_line(struct perf_ftrace *ftrace, const char *line, FIL

[ ... ]

>  	if ((!has_filter || ftrace->graph_thresh) && !ftrace->use_inline)
>  		return print_line(ftrace, line, out);
>  
> +	if (strstr(line, "==========>") != NULL ||
> +	    strstr(line, "<==========") != NULL) {
> +		bool is_irq_ent = (strstr(line, "==========>") != NULL);
> +
> +		if (has_filter && parse_graph_cpu(ftrace, line, &cpu)) {
> +			ts = get_cpu_state(ftrace, cpu);
> +			if (ts && ts->in_target && ts->orphan_depth == 0) {
> +				if (is_irq_ent) {
> +					if (ts->pending_irq_ent[0] != '\0')
> +						print_line(ftrace, ts->pending_irq_ent, out);
> +					strlcpy(ts->pending_irq_ent, line,
> +						sizeof(ts->pending_irq_ent));
> +					ts->suppress_irq_ret = false;
> +				} else {
> +					if (ts->pending_irq_ent[0] != '\0') {
> +						print_line(ftrace, ts->pending_irq_ent, out);
> +						ts->pending_irq_ent[0] = '\0';
> +					}
> +					if (ts->suppress_irq_ret)
> +						ts->suppress_irq_ret = false;
> +					else
> +						print_line(ftrace, line, out);
> +				}
> +			} else if (ts && !ts->in_target) {
> +				ts->pending_irq_ent[0] = '\0';
> +				ts->suppress_irq_ret = false;
> +			}
> +		} else if (!has_filter) {
> +			print_line(ftrace, line, out);
> +		}
> +		return 0;
> +	}

[ ... ]

> @@ -950,30 +1398,31 @@ int ftrace_process_fgraph_line(struct perf_ftrace *ftrace, const char *line, FIL
>  
>  		extract_exit_func_name(func_start, exit_name, sizeof(exit_name));
>  
> -		top_indent = -1;
> -		for (i = ts->depth - 1; i >= 0; i--) {
> -			if (!ts->stack[i].inlined) {
> -				top_indent = ts->stack[i].indent;
> -				break;
> +		if (ts->orphan_depth > 0) {
> +			if (has_filter && ts->in_target &&
> +			    (leading_spaces <= ts->target_root_indent ||
> +			     (ts->orphan_base_indent > 0 &&
> +			      leading_spaces < ts->orphan_base_indent))) {
> +				ts->orphan_depth = 0;
> +				ts->orphan_base_indent = 0;
> +				ts->orphan_seq = 0;
> +			} else {
> +				if (ts->orphan_base_indent > 0 &&
> +				    leading_spaces == ts->orphan_base_indent)
> +					ts->orphan_depth = 0;
> +				else
> +					ts->orphan_depth--;
> +				if (ts->orphan_depth == 0) {
> +					ts->orphan_base_indent = 0;
> +					ts->orphan_seq = 0;
> +				}
> +				return 0;
>  			}
>  		}

[Severity: Medium]
Can a dropped child entry inside an orphan subtree cause ts->orphan_depth to
reach 0 prematurely in ftrace_process_fgraph_line()?

When an orphan root frame enters at indentation I, ftrace_check_target_caller()
sets ts->orphan_depth = 1 and ts->orphan_base_indent = I. If a nested child
inside that orphan subtree at indentation > I has its entry event dropped by
the ring buffer, ts->orphan_depth remains 1.

When that nested child exits with leading_spaces > ts->orphan_base_indent, the
else branch decrements ts->orphan_depth from 1 to 0 and clears
ts->orphan_base_indent = 0, even though leading_spaces > ts->orphan_base_indent
indicates execution is still deeper than the outermost orphan frame. Could
subsequent calls or exits within the still-active outer orphan frame then leak
into the output or pop active target frames?

[ ... ]

> @@ -1083,9 +1551,166 @@ int ftrace_process_fgraph_line(struct perf_ftrace *ftrace, const char *line, FIL

[ ... ]

> +		if (ts->orphan_depth > 0) {
> +			if (has_open_brace && !has_semicolon) {
> +				ts->orphan_depth++;
> +				ts->orphan_seq = ts->seq;
> +			}
> +			return 0;
> +		}
> +	}
> +
> +	ts->suppress_irq_ret = false;

[Severity: Medium]
If an interrupt section contains more than one top-level orphan call between
==========> and <========== (for example, when an untraced interrupt entry
sequentially invokes irq_enter_rcu(), the interrupt handler, and
irq_exit_rcu()), will this unconditional reset of ts->suppress_irq_ret = false
in ftrace_process_fgraph_line() leak a dangling <========== marker?

On the first orphan call after ==========>, ftrace_check_target_caller() sees
ts->pending_irq_ent[0] != '\0', clears ts->pending_irq_ent[0] = '\0', and sets
ts->suppress_irq_ret = true.

Once that first orphan call finishes (either immediately if it is a leaf call,
or when its closing brace returns ts->orphan_depth to 0), the entry of the
second top-level orphan call reaches:

	ts->suppress_irq_ret = false;

before calling ftrace_check_target_caller(). Because ts->pending_irq_ent[0] is
already '\0', ftrace_check_target_caller() suppresses the second orphan call
without setting ts->suppress_irq_ret back to true, causing the subsequent
<========== line to be printed without a matching opening marker.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102214054.911298-1-atomlin@atomlin.com?part=10

      reply	other threads:[~2026-11-02 22:11 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02 21:40 [PATCH perf-tools-next v13 00/10] perf ftrace: Support inlined functions and display enhancements in function graph tracer Aaron Tomlin
2026-11-02 21:40 ` [PATCH perf-tools-next v13 01/10] perf ftrace: Handle signals, non-blocking reads and pager exits in __cmd_ftrace() Aaron Tomlin
2026-11-02 21:46   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 02/10] perf ftrace: Optimise __cmd_ftrace() stack frame with strbuf streaming Aaron Tomlin
2026-11-02 21:51   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 03/10] perf ftrace: Support filtering return address comments Aaron Tomlin
2026-11-02 21:46   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 04/10] perf ftrace: Add basic task call stack management Aaron Tomlin
2026-11-02 21:52   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 05/10] perf ftrace: Support context switch tracking in function graph tracer Aaron Tomlin
2026-11-02 21:55   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 06/10] perf ftrace: Support display of inlined functions " Aaron Tomlin
2026-11-02 22:05   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 07/10] perf symbol: Introduce vmlinux_path__find() helper Aaron Tomlin
2026-11-02 21:53   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 08/10] perf test: Add test for ftrace inline and task stack tracking Aaron Tomlin
2026-11-02 21:50   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 09/10] perf ftrace: Support omitting execution duration in function graph tracer Aaron Tomlin
2026-11-02 21:54   ` sashiko-bot
2026-11-02 21:40 ` [PATCH perf-tools-next v13 10/10] perf ftrace: Filter extraneous functions " Aaron Tomlin
2026-11-02 22:11   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-194261@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=atomlin@atomlin.com \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.