All of lore.kernel.org
 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 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.