Linux Perf Users
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Serhei Makarov" <serhei@serhei.io>
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [PATCH v2 1/2] perf inject: Support piped data with --convert-callchain
Date: Thu, 12 Nov 2026 20:44:27 +0000	[thread overview]
Message-ID: <sashiko-outbox-211811@kernel.org> (raw)
In-Reply-To: <20261112201833.78630-1-serhei@serhei.io>

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [Medium] perf inject: unstripped pipe attrs in cmd_inject() for
  file-to-pipe mode
- [Medium] perf inject: attr/sample mismatch for non-DWARF events in
  piped mode
- [Medium] perf inject: unchecked attr.size write in
  perf_event__repipe_attr()
- [Medium] perf inject: missing ordered_events flush in
  perf_event__repipe_oe_synth()
--

commit c6a36707bb63847953234da6d70009330a9ffc22
Author: Serhei Makarov <serhei@serhei.io>

perf inject: Support piped data with --convert-callchain

This commit enables piped input and output with --convert-callchain by
stripping DWARF user register and stack sample attributes in
perf_event__repipe_attr() and deferring the DWARF callchain check to
perf_event__convert_sample_callchain().

> diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
> index ce7562fc788c2..9fd02a6f5849f 100644
> --- a/tools/perf/builtin-inject.c
> +++ b/tools/perf/builtin-inject.c
> @@ -229,6 +229,7 @@ static int perf_event__repipe_attr(const struct perf_tool *tool,
>  				   union perf_event *event,
>  				   struct evlist **pevlist)
>  {
> +	union perf_event *event0;
>  	struct perf_inject *inject = container_of(tool, struct perf_inject,
>  						  tool);
>  	struct perf_event_attr attr;
> @@ -258,6 +259,25 @@ static int perf_event__repipe_attr(const struct perf_tool *tool,
>  		goto out;
>  	}
>  
> +	if (inject->convert_callchain) {
> +		/* Copy event to repipe with corrected final attributes,
> +		   without confusing downstream users of pevlist: */
> +		event0 = event;
> +		event = (void *)inject->event_copy;
> +		if (event == NULL) {
> +			inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE);
> +			if (!inject->event_copy)
> +				return -ENOMEM;
> +			event = (void *)inject->event_copy;
> +		}
> +		memcpy(event, event0, event0->header.size);
> +
> +		event->attr.attr.sample_type &= ~(PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER);
> +		event->attr.attr.sample_regs_user = 0;
> +		event->attr.attr.sample_stack_user = 0;
> +		event->attr.attr.exclude_callchain_user = 0;
> +	}

[Severity: Medium]
Can clearing sample_regs_user and sample_stack_user here clobber the
trailing event ID array when event->attr.attr.size is smaller than
PERF_ATTR_SIZE_VER3?

A PERF_RECORD_HEADER_ATTR record places its u64 ID array immediately after
attr_size bytes (event->attr.attr.size ?: PERF_ATTR_SIZE_VER0), so writing
to sample_regs_user and sample_stack_user without checking attr_size
overwrites the event IDs in the repiped record for VER0, VER1, or VER2
attribute headers.

[ ... ]

> @@ -526,7 +558,7 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool,
>  {
>  	struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
>  	struct evsel *evsel = sample->evsel;
> -	struct callchain_cursor *cursor = get_tls_callchain_cursor();
> +	struct callchain_cursor *cursor;
>  	union perf_event *event_copy = (void *)inject->event_copy;
>  	struct callchain_cursor_node *node;
>  	struct thread *thread;
> @@ -535,6 +567,10 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool,
>  	u64 i, k;
>  	int ret;
>  
> +	if (!evsel__has_dwarf_callchain(evsel))
> +		return perf_event__repipe(tool, event, sample, machine);

[Severity: Medium]
Does repiping non-DWARF samples unchanged leave their payload out of sync
with the stripped perf_event_attr headers in piped mode?

For piped input, cmd_inject() skips the upfront evsel__has_dwarf_callchain()
validation, while both perf_event__repipe_attr() and __cmd_inject()
unconditionally clear PERF_SAMPLE_REGS_USER and PERF_SAMPLE_STACK_USER on
every event's attributes.  If a piped event has PERF_SAMPLE_REGS_USER or
PERF_SAMPLE_STACK_USER without a DWARF callchain,
perf_event__convert_sample_callchain() writes the original sample payload
containing user registers or stack data while the output attributes claim
those fields are absent, causing downstream sample parsing to fail.

[ ... ]

> @@ -2956,15 +2980,15 @@ int cmd_inject(int argc, const char **argv)
>  	if (inject.convert_callchain) {
>  		struct evsel *evsel;
>  
> -		if (inject.output.is_pipe || inject.session->data->is_pipe) {
> -			pr_err("--convert-callchain cannot work with pipe\n");
> -			goto out_delete;
> -		}

[Severity: Medium]
When reading from an on-disk perf.data file and writing to a pipe (such as
perf inject -i perf.data --convert-callchain -o -), does cmd_inject() emit
unstripped PERF_RECORD_HEADER_ATTR records into the output pipe?

For non-pipe input (!data.is_pipe), perf_event__repipe_attr() is never
called; instead, cmd_inject() synthesizes the pipe's attribute headers
directly from inject.session->evlist:

tools/perf/builtin-inject.c:cmd_inject() {
    ...
		if (!data.is_pipe) {
			if (inject.aslr)
				aslr_tool__strip_evlist(tool, inject.session->evlist);

			ret = perf_event__synthesize_for_pipe(&inject.tool,
							      inject.session,
							      &inject.output,
							      perf_event__repipe);
    ...
}

Because inject.session->evlist still has PERF_SAMPLE_REGS_USER and
PERF_SAMPLE_STACK_USER set at that point while
perf_event__convert_sample_callchain() strips those fields from every
sample, the output pipe's attribute headers do not match its sample records
and downstream consumers fail with -EFAULT.

> -
> -		evlist__for_each_entry(inject.session->evlist, evsel) {
> -			if (!evsel__has_dwarf_callchain(evsel) && !evsel__is_dummy_event(evsel)) {
> -				pr_err("--convert-callchain requires DWARF call graph.\n");
> -				goto out_delete;
> +		/* For on-disk data, check evlist up-front.
> +		   For piped data, evlist is not available yet;
> +		   check in perf_event__convert_sample_callchain. */
> +		if (!inject.session->data->is_pipe) {
> +			evlist__for_each_entry(inject.session->evlist, evsel) {
> +				if (!evsel__has_dwarf_callchain(evsel) && !evsel__is_dummy_event(evsel)) {
> +					pr_err("--convert-callchain requires DWARF call graph.\n");
> +					goto out_delete;
> +				}
>  			}
>  		}

[Severity: Medium]
With piped input enabled for --convert-callchain, does leaving
inject.tool.finished_round set to perf_event__repipe_oe_synth() keep every
raw DWARF sample buffered in memory until EOF?

In __cmd_inject(), inject->convert_callchain enables
inject->tool.ordered_events, and __perf_session__process_pipe_events() turns
on copy_on_queue so every queued sample is duplicated onto the heap.  Because
perf_event__repipe_oe_synth() writes PERF_RECORD_FINISHED_ROUND directly to
the output without calling perf_event__process_finished_round() to flush
session->ordered_events, raw DWARF stack samples accumulate on the heap for
the entire session and all PERF_RECORD_FINISHED_ROUND events are emitted
ahead of the timestamped samples.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261112201833.78630-1-serhei@serhei.io?part=1

      parent reply	other threads:[~2026-11-12 20:44 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 20:18 [PATCH v2 1/2] perf inject: Support piped data with --convert-callchain Serhei Makarov
2026-11-12 20:18 ` [PATCH v2 2/2] perf test: Test piped data in inject-callchain.sh Serhei Makarov
2026-11-12 20:28   ` sashiko-bot
2026-11-12 20:44 ` 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-211811@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=serhei@serhei.io \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox