Linux Perf Users
 help / color / mirror / Atom feed
* [PATCH v2 1/2] perf inject: Support piped data with --convert-callchain
@ 2026-11-12 20:18 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:44 ` [PATCH v2 1/2] perf inject: Support piped data with --convert-callchain sashiko-bot
  0 siblings, 2 replies; 4+ messages in thread
From: Serhei Makarov @ 2026-11-12 20:18 UTC (permalink / raw)
  To: acme, irogers, namhyung, james.clark
  Cc: jolsa, adrian.hunter, peterz, mingo, linux-kernel,
	linux-perf-users, serhei

When profiling applications without framepointers or non-framepointer
distros, it's helpful to pipe stack sample data directly to perf
inject --convert-callchain, e.g.:

perf record -F 999 --call-graph dwarf -o - -- myapp \
  | perf inject --convert-callchain -i - -o perf.data

This piped setup doesn't store stack samples on disk, removing a major
performance bottleneck for DWARF stack unwinding as noted in [1].

To support piped output, write a modified copy of the event attributes
in perf_event__repipe_attr. This gets around the inability to modify
event attribute records in-place after they are written to the pipe.

[1]: https://rwmj.wordpress.com/2023/02/14/frame-pointers-vs-dwarf-my-verdict/
> The first most obvious thing is that even with the smallest stack
> data collection, DWARF’s perf.data is over 10 times larger, and it
> balloons even larger once you start to collect more reasonable stack
> sizes. For a single minute of data collection, collecting 10s of
> gigabytes of data is not very practical even on high end machines, and
> continuous performance analysis would be impossible at these data
> rates.

Signed-off-by: Serhei Makarov <serhei@serhei.io>

---

v2 changes)
 * Rebased onto perf-tools-next/perf-tools-next which includes the
 existing size-calculation fix (cf Ian Rogers, commit daac18e7c42c).
---
 tools/perf/builtin-inject.c | 68 +++++++++++++++++++++++++------------
 1 file changed, 46 insertions(+), 22 deletions(-)

diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index ce7562fc788c..9fd02a6f5849 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;
+	}
+
 	if (!inject->itrace_synth_opts.set) {
 		ret = perf_event__repipe_synth(tool, event);
 		goto out;
@@ -519,6 +539,18 @@ static int perf_event__repipe_sample(const struct perf_tool *tool,
 	return perf_event__repipe_synth(tool, event);
 }
 
+static bool evsel__has_dwarf_callchain(struct evsel *evsel)
+{
+	struct perf_event_attr *attr = &evsel->core.attr;
+	const u64 dwarf_callchain_flags =
+		PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_CALLCHAIN;
+
+	if (!attr->exclude_callchain_user)
+		return false;
+
+	return (attr->sample_type & dwarf_callchain_flags) == dwarf_callchain_flags;
+}
+
 static int perf_event__convert_sample_callchain(const struct perf_tool *tool,
 						union perf_event *event,
 						struct perf_sample *sample,
@@ -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);
+	cursor = get_tls_callchain_cursor();
+
 	if (event_copy == NULL) {
 		inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE);
 		if (!inject->event_copy)
@@ -2650,18 +2686,6 @@ static int __cmd_inject(struct perf_inject *inject)
 	return ret;
 }
 
-static bool evsel__has_dwarf_callchain(struct evsel *evsel)
-{
-	struct perf_event_attr *attr = &evsel->core.attr;
-	const u64 dwarf_callchain_flags =
-		PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_CALLCHAIN;
-
-	if (!attr->exclude_callchain_user)
-		return false;
-
-	return (attr->sample_type & dwarf_callchain_flags) == dwarf_callchain_flags;
-}
-
 int cmd_inject(int argc, const char **argv)
 {
 	struct perf_inject inject = {
@@ -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;
-		}
-
-		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;
+				}
 			}
 		}
 
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v2 2/2] perf test: Test piped data in inject-callchain.sh
  2026-11-12 20:18 [PATCH v2 1/2] perf inject: Support piped data with --convert-callchain Serhei Makarov
@ 2026-11-12 20:18 ` Serhei Makarov
  2026-11-12 20:28   ` sashiko-bot
  2026-11-12 20:44 ` [PATCH v2 1/2] perf inject: Support piped data with --convert-callchain sashiko-bot
  1 sibling, 1 reply; 4+ messages in thread
From: Serhei Makarov @ 2026-11-12 20:18 UTC (permalink / raw)
  To: acme, irogers, namhyung, james.clark
  Cc: jolsa, adrian.hunter, peterz, mingo, linux-kernel,
	linux-perf-users, serhei

Expand the test to verify --convert-callchain on piped data.

Also, check that it's now safe to run 'perf inject --convert-callchain'
on events without DWARF callchain data. In the prior version of the
code, the up-front evsel check (not possible to do with piping) was
guarding against a segfault in the prior version of the code.

$ /opt/perf/bin/perf test -vv "DWARF callchain"
 89: perf inject to convert DWARF callchains to regular ones:
--- start ---
test child forked, pid 1952580
recording piped data without DWARF callchain
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.075 MB - ]
recording data with DWARF callchain
[ perf record: Woken up 33 times to write data ]
[ perf record: Captured and wrote 8.266 MB /tmp/perf-test.B1OMir (1025 samples) ]
recording piped data with DWARF callchain
[ perf record: Woken up 33 times to write data ]
[ perf record: Captured and wrote 8.283 MB - ]
check --convert-callchain without DWARF data
convert DWARF callchain using perf inject
convert piped DWARF callchain using unpiped perf inject
convert piped DWARF callchain using piped (input) perf inject
convert piped DWARF callchain using piped (input+output) perf inject
compare the (unpiped) both result excluding inlined functions
compare the (piped) various results excluding inlined functions
---- end(0) ----
 89: perf inject to convert DWARF callchains to regular ones         : Ok

Signed-off-by: Serhei Makarov <serhei@serhei.io>
---
 tools/perf/tests/shell/inject-callchain.sh | 63 +++++++++++++++++++---
 1 file changed, 57 insertions(+), 6 deletions(-)

diff --git a/tools/perf/tests/shell/inject-callchain.sh b/tools/perf/tests/shell/inject-callchain.sh
index a1cba8010f95..900459a8dc9e 100755
--- a/tools/perf/tests/shell/inject-callchain.sh
+++ b/tools/perf/tests/shell/inject-callchain.sh
@@ -23,23 +23,74 @@ trap_cleanup()
 	exit 1
 }
 
+trap_err()
+{
+    local SIG=$?
+    if (( $? == 139 )); then #SIGSEGV
+        err=1
+    fi
+    echo "Unexpected signal ${SIG} in ${FUNCNAME[1]}"
+    cleanup
+    exit ${err}
+}
+
 trap trap_cleanup EXIT TERM INT
+trap trap_err ERR
+
+echo "recording piped data without DWARF callchain"
+perf record -F 999 -o - -- perf test -w noploop | cat > ${TESTDATA}.nodwarf
 
 echo "recording data with DWARF callchain"
 perf record -F 999 --call-graph dwarf -o "${TESTDATA}" -- perf test -w noploop
 
+echo "recording piped data with DWARF callchain"
+perf record -F 999 --call-graph dwarf -o - -- perf test -w noploop | cat > ${TESTDATA}.piped
+
+echo "check --convert-callchain without DWARF data"
+# verify that running on non-DWARF data does not produce segfault
+perf inject -i "${TESTDATA}.nodwarf" --convert-callchain -o "${TESTDATA}.nodwarf.conv"
+cat "${TESTDATA}.nodwarf" | perf inject --convert-callchain -i - -o "${TESTDATA}.nodwarf.conv2"
+cat "${TESTDATA}.nodwarf" | perf inject --convert-callchain -i - -o - | cat > ${TESTDATA}.nodwarf.conv3
+
 echo "convert DWARF callchain using perf inject"
 perf inject -i "${TESTDATA}" --convert-callchain -o "${TESTDATA}.new"
 
+echo "convert piped DWARF callchain using unpiped perf inject"
+perf inject -i "${TESTDATA}.piped" --convert-callchain -o "${TESTDATA}.piped.conv"
+
+echo "convert piped DWARF callchain using piped (input) perf inject"
+cat "${TESTDATA}.piped" | perf inject --convert-callchain -i - -o "${TESTDATA}.pipein.conv"
+
+echo "convert piped DWARF callchain using piped (input+output) perf inject"
+cat "${TESTDATA}.piped" | perf inject --convert-callchain -i - -o - | cat > ${TESTDATA}.pipeout.conv
+
+# skip reporting/comparing .nodwarf vs .nodwarf.conv
+
 perf report -i "${TESTDATA}" --no-children -q --percent-limit=1 > ${TESTDATA}.out
 perf report -i "${TESTDATA}.new" --no-children -q --percent-limit=1 > ${TESTDATA}.new.out
 
-echo "compare the both result excluding inlined functions"
-if diff -u "${TESTDATA}.out" "${TESTDATA}.new.out" | grep "^- " | grep -qv "(inlined)"; then
-    echo "Found some differences"
-    diff -u "${TESTDATA}.out" "${TESTDATA}.new.out"
-    err=1
-fi
+compare () {
+    local OUT=$1
+    local NEWOUT=$2
+    if diff -u "${OUT}" "${NEWOUT}" | grep "^- " | grep -qv "(inlined)"; then
+        echo "Found some differences between ${OUT} and ${NEWOUT}"
+        diff -u "${OUT}" "${NEWOUT}"
+        err=1
+    fi
+}
+
+echo "compare the (unpiped) both result excluding inlined functions"
+compare "${TESTDATA}.out" "${TESTDATA}.new.out"
+
+perf report -i "${TESTDATA}.piped" --no-children -q --percent-limit=1 > ${TESTDATA}.piped.out
+perf report -i "${TESTDATA}.piped.conv" --no-children -q --percent-limit=1 > ${TESTDATA}.piped.conv.out
+perf report -i "${TESTDATA}.pipein.conv" --no-children -q --percent-limit=1 > ${TESTDATA}.pipein.conv.out
+perf report -i "${TESTDATA}.pipeout.conv" --no-children -q --percent-limit=1 > ${TESTDATA}.pipeout.conv.out
+
+echo "compare the (piped) various results excluding inlined functions"
+compare "${TESTDATA}.piped.out" "${TESTDATA}.piped.conv.out"
+compare "${TESTDATA}.piped.conv.out" "${TESTDATA}.pipein.conv.out"
+compare "${TESTDATA}.piped.conv.out" "${TESTDATA}.pipeout.conv.out"
 
 cleanup
 exit $err
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 2/2] perf test: Test piped data in inject-callchain.sh
  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
  0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-11-12 20:28 UTC (permalink / raw)
  To: Serhei Makarov; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] perf test: missing Signed-off-by trailer in commit message
- [Medium] perf test: trap_err() overwrites $? and exits 0 on command
  failure
- [Medium] perf test: pipelines ending in cat without pipefail mask perf
  crashes
--

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

> perf test: Test piped data in inject-callchain.sh
>
> Expand the test to verify --convert-callchain on piped data.
>
> Also, check that it's now safe to run 'perf inject --convert-callchain'
> on events without DWARF callchain data. In the prior version of the
> code, the up-front evsel check (not possible to do with piping) was
> guarding against a segfault in the prior version of the code.
>
> $ /opt/perf/bin/perf test -vv "DWARF callchain"
>  89: perf inject to convert DWARF callchains to regular ones:

[Severity: High]
This commit message is missing a Signed-off-by trailer.

> diff --git a/tools/perf/tests/shell/inject-callchain.sh b/tools/perf/tests/shell/inject-callchain.sh
> index a1cba8010f954..900459a8dc9e8 100755
> --- a/tools/perf/tests/shell/inject-callchain.sh
> +++ b/tools/perf/tests/shell/inject-callchain.sh
> @@ -23,23 +23,74 @@ trap_cleanup()
>  	exit 1
>  }
>  
> +trap_err()
> +{
> +    local SIG=$?
> +    if (( $? == 139 )); then #SIGSEGV
> +        err=1
> +    fi
> +    echo "Unexpected signal ${SIG} in ${FUNCNAME[1]}"
> +    cleanup
> +    exit ${err}
> +}

[Severity: Medium]
Does trap_err() exit with status 0 when a command fails or crashes?

Running "local SIG=$?" overwrites $? with the return status of the local
builtin (0), so "if (( $? == 139 ))" is always false.  Because err starts at
0 and is never updated for 139 or any other failure, "exit ${err}" causes the
test to pass silently when the trap triggers.

> +
>  trap trap_cleanup EXIT TERM INT
> +trap trap_err ERR
> +
> +echo "recording piped data without DWARF callchain"
> +perf record -F 999 -o - -- perf test -w noploop | cat > ${TESTDATA}.nodwarf
>  
>  echo "recording data with DWARF callchain"
>  perf record -F 999 --call-graph dwarf -o "${TESTDATA}" -- perf test -w noploop
>  
> +echo "recording piped data with DWARF callchain"
> +perf record -F 999 --call-graph dwarf -o - -- perf test -w noploop | cat > ${TESTDATA}.piped
> +
> +echo "check --convert-callchain without DWARF data"
> +# verify that running on non-DWARF data does not produce segfault
> +perf inject -i "${TESTDATA}.nodwarf" --convert-callchain -o "${TESTDATA}.nodwarf.conv"
> +cat "${TESTDATA}.nodwarf" | perf inject --convert-callchain -i - -o "${TESTDATA}.nodwarf.conv2"
> +cat "${TESTDATA}.nodwarf" | perf inject --convert-callchain -i - -o - | cat > ${TESTDATA}.nodwarf.conv3

[Severity: Medium]
Without "set -o pipefail", does piping perf record or perf inject into cat
mask crashes and non-zero exit statuses?  When an upstream command in these
pipelines segfaults, the pipeline still returns 0 from the trailing cat, and
because ${TESTDATA}.nodwarf.conv3 is never inspected afterwards, a crash in
that perf inject invocation goes undetected.

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

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 1/2] perf inject: Support piped data with --convert-callchain
  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:44 ` sashiko-bot
  1 sibling, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-11-12 20:44 UTC (permalink / raw)
  To: Serhei Makarov; +Cc: linux-perf-users

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

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-11-12 20:44 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH v2 1/2] perf inject: Support piped data with --convert-callchain sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox