Linux Perf Users
 help / color / mirror / Atom feed
From: Amir Ayupov <aaupov@fb.com>
To: <linux-perf-users@vger.kernel.org>, <coresight@lists.linaro.org>,
	<linux-arm-kernel@lists.infradead.org>,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	James Clark <james.clark@linaro.org>, Leo Yan <leo.yan@arm.com>,
	Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@redhat.com>,
	Arnaldo Carvalho de Melo <acme@kernel.org>,
	Namhyung Kim <namhyung@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Alexander Shishkin <alexander.shishkin@linux.intel.com>,
	Jiri Olsa <jolsa@kernel.org>, Ian Rogers <irogers@google.com>,
	Adrian Hunter <adrian.hunter@intel.com>,
	John Garry <john.g.garry@oracle.com>,
	Will Deacon <will@kernel.org>
Cc: <linux-doc@vger.kernel.org>, Mike Leach <mike.leach@arm.com>,
	Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Swapnil Sapkal <swapnil.sapkal@amd.com>
Subject: [PATCH 8/9] perf cs-etm: Consume branch history when attaching it to a sample
Date: Mon, 3 Aug 2026 02:06:39 -0700	[thread overview]
Message-ID: <20260803090640.2412336-8-aaupov@fb.com> (raw)
In-Reply-To: <20260803090640.2412336-1-aaupov@fb.com>

--itrace=L attaches whatever the thread stack holds when a sample is
processed. With a duty cycled trace most samples fire while the trace is
off. Those samples have nothing newly decoded, but the thread stack still
holds the previous window, so they were being given branches that ran an
arbitrary amount of time earlier as if they immediately preceded the
sample.

The reconstruction is discarded on a trace discontinuity, but that does
not help here: the discontinuity that ends a gap is only decoded once
trace resumes, which is after the samples in the gap have been processed.

A trace window belongs to exactly one sample. With AUX pause and resume
the sample is what stops the trace, so the pairing is one to one by
construction. Take the branch history when attaching it instead of
copying it, and a later sample with nothing newly decoded then finds an
empty branch stack, which dlfilter-nonempty-brstack.so removes.

This is not a small correction. On a 12 s single-threaded capture with
pause period 100003 and resume period 8350251, of 335291 samples that
previously received branch history only 3371 were backed by trace decoded
for that sample; the other 331920 repeated an earlier window. The
proportion follows the ETM duty cycle, so it holds for any low duty cycle
configuration.

Note that thread_stack__br_sample(), used by lowercase --itrace=l, still
copies, so synthesised instruction samples keep the overlapping branch
stacks they have today.

Signed-off-by: Amir Ayupov <aaupov@fb.com>
---
 tools/perf/util/cs-etm.c       | 17 ++++++++---------
 tools/perf/util/thread-stack.c | 17 +++++++++++++++++
 tools/perf/util/thread-stack.h |  1 +
 3 files changed, 26 insertions(+), 9 deletions(-)

diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 048ff97caa936..a3498a0a96a05 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -3123,19 +3123,18 @@ static int cs_etm__process_sample(struct cs_etm_auxtrace *etm,
 		return -ENOMEM;
 
 	/*
-	 * The thread stack is emptied when the decoder reports a
-	 * discontinuity and when a queue runs out of trace, so branches from
-	 * before either of those are never reported.
-	 *
-	 * Note that a sample landing in a gap between two trace windows is
-	 * not covered by that: the discontinuity that ends the gap has not
-	 * been decoded at this point, so the preceding window is still in
-	 * the thread stack and gets attached. Filtering those out needs a
-	 * per-window end time that the decoder does not currently expose.
+	 * Take the branch history rather than copying it. The trace window
+	 * belongs to the sample that ends it, so once it has been attached a
+	 * later sample with nothing newly decoded finds an empty stack rather
+	 * than being given an earlier window's branches. That is the common
+	 * case whenever the trace is duty cycled, by AUX pause/resume or by
+	 * ETM strobing.
 	 */
 	thread_stack__br_sample_late(thread, sample->cpu, etm->br_stack,
 				     etm->br_stack_sz, sample->ip,
 				     machine__kernel_start(machine));
+	thread_stack__br_stack_consume(thread, sample->cpu);
+
 	if (etm->br_stack->nr)
 		sample->branch_stack = etm->br_stack;
 
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 51eaedb47bb1d..374a291aa48fe 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -614,6 +614,23 @@ void thread_stack__sample_late(struct thread *thread, int cpu,
 	}
 }
 
+/*
+ * Branch history belongs to the sample that ends the trace window, so a
+ * decoder that attaches it to an existing sample should take it rather than
+ * copy it. A later sample with no newly decoded trace then finds an empty
+ * branch stack instead of the previous window's branches.
+ */
+void thread_stack__br_stack_consume(struct thread *thread, int cpu)
+{
+	struct thread_stack *ts = thread__stack(thread, cpu);
+
+	if (!ts || !ts->br_stack_rb)
+		return;
+
+	ts->br_stack_pos = 0;
+	ts->br_stack_rb->nr = 0;
+}
+
 void thread_stack__br_sample(struct thread *thread, int cpu,
 			     struct branch_stack *dst, unsigned int sz)
 {
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
index b3cd09beb62f0..2aec292bd1bcb 100644
--- a/tools/perf/util/thread-stack.h
+++ b/tools/perf/util/thread-stack.h
@@ -88,6 +88,7 @@ void thread_stack__sample(struct thread *thread, int cpu, struct ip_callchain *c
 void thread_stack__sample_late(struct thread *thread, int cpu,
 			       struct ip_callchain *chain, size_t sz, u64 ip,
 			       u64 kernel_start);
+void thread_stack__br_stack_consume(struct thread *thread, int cpu);
 void thread_stack__br_sample(struct thread *thread, int cpu,
 			     struct branch_stack *dst, unsigned int sz);
 void thread_stack__br_sample_late(struct thread *thread, int cpu,
-- 
2.52.0


  parent reply	other threads:[~2026-08-03  9:07 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-03  9:06 [PATCH 1/9] perf header: Tolerate inconsistent HEADER_GROUP_DESC Amir Ayupov
2026-08-03  9:06 ` [PATCH 2/9] perf thread-stack: Report branch stack hw_idx as not available Amir Ayupov
2026-08-03  9:19   ` sashiko-bot
2026-08-03  9:06 ` [PATCH 3/9] perf thread-stack: Bound wrapped branch stack copy Amir Ayupov
2026-08-03  9:26   ` sashiko-bot
2026-08-03  9:06 ` [PATCH 4/9] perf dlfilter: Add non-empty branch stack filter Amir Ayupov
2026-08-03  9:06 ` [PATCH 5/9] perf cs-etm: Split up cs_etm__process_timestamped_queues() Amir Ayupov
2026-08-03  9:06 ` [PATCH 6/9] perf cs-etm: Add branch history to existing samples Amir Ayupov
2026-08-03  9:23   ` sashiko-bot
2026-08-03  9:06 ` [PATCH 7/9] perf test cs-etm: Test branch history on " Amir Ayupov
2026-08-03  9:21   ` sashiko-bot
2026-08-03  9:06 ` Amir Ayupov [this message]
2026-08-03  9:06 ` [PATCH 9/9] Documentation: coresight: Document context-sensitive PGO workflow Amir Ayupov

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=20260803090640.2412336-8-aaupov@fb.com \
    --to=aaupov@fb.com \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=corbet@lwn.net \
    --cc=coresight@lists.linaro.org \
    --cc=irogers@google.com \
    --cc=james.clark@linaro.org \
    --cc=john.g.garry@oracle.com \
    --cc=jolsa@kernel.org \
    --cc=leo.yan@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mike.leach@arm.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=peterz@infradead.org \
    --cc=skhan@linuxfoundation.org \
    --cc=suzuki.poulose@arm.com \
    --cc=swapnil.sapkal@amd.com \
    --cc=will@kernel.org \
    /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