Linux Perf Users
 help / color / mirror / Atom feed
* [PATCH 0/2] perf cs-etm: Fix bogus branch samples
@ 2026-07-13 18:00 Leo Yan
  2026-07-13 18:00 ` [PATCH 1/2] perf cs-etm: Avoid bogus branch samples before async exceptions Leo Yan
  2026-07-13 18:00 ` [PATCH 2/2] perf cs-etm: Centralize branch sample checks Leo Yan
  0 siblings, 2 replies; 4+ messages in thread
From: Leo Yan @ 2026-07-13 18:00 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Mike Leach, Suzuki K Poulose
  Cc: Arnaldo Carvalho de Melo, coresight, linux-arm-kernel,
	linux-perf-users, linux-kernel, Leo Yan

This series fixes the bogus branch samples in the CoreSight ETM trace.

When an interrupt occurs immediately after an untaken branch, the
decoder incorrectly synthesizes a branch sample for that branch.
The first patch fixes this issue.

The second patch consolidate the branch check so make sure the flush
flow to apply the same check to avoid generate bogus samples.

The series has been verified on Orion6 board.

Signed-off-by: Leo Yan <leo.yan@arm.com>
---
Leo Yan (2):
      perf cs-etm: Avoid bogus branch samples before async exceptions
      perf cs-etm: Centralize branch sample checks

 tools/perf/util/cs-etm.c | 44 ++++++++++++++++++++------------------------
 1 file changed, 20 insertions(+), 24 deletions(-)
---
base-commit: ef3af1df4f3372bd8ad47619452a283048b3bc8d
change-id: 20260713-perf_cs_etm_fix_non_taken-5b4d7f73f41e

Best regards,
-- 
Leo Yan <leo.yan@arm.com>


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

* [PATCH 1/2] perf cs-etm: Avoid bogus branch samples before async exceptions
  2026-07-13 18:00 [PATCH 0/2] perf cs-etm: Fix bogus branch samples Leo Yan
@ 2026-07-13 18:00 ` Leo Yan
  2026-07-13 18:09   ` sashiko-bot
  2026-07-13 18:00 ` [PATCH 2/2] perf cs-etm: Centralize branch sample checks Leo Yan
  1 sibling, 1 reply; 4+ messages in thread
From: Leo Yan @ 2026-07-13 18:00 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Mike Leach, Suzuki K Poulose
  Cc: Arnaldo Carvalho de Melo, coresight, linux-arm-kernel,
	linux-perf-users, linux-kernel, Leo Yan

Consider the following code:

  00000000004000f4 <test_branch>:
  4000f4:       eb02003f        cmp     x1, x2
  4000f8:       54000109        b.ls    400118 <taken_path>

  00000000004000fc <fallthrough>:
  4000fc:       d282f2c2        mov     x2, #0x1796                     // #6038
  ...

  0000000000400118 <taken_path>:
  ...

When execution reaches 0x4000f8, the b.ls condition evaluates false, and
execution falls through to 0x4000fc. If an asynchronous exception (for
example, an interrupt) is taken before the next instruction executes,
cs_etm__exception() currently forces the previous range packet's
last_instr_taken_branch flag to true - in the case above, perf
incorrectly synthesizes a branch sample for the untaken branch, even
though execution actually falls through.

The trace log contains the following bogus branch sample at 0x4000f8:

 perf script --itrace=b -F comm,ip,insn,disasm,sym

 b_ls_not_taken_            400108 fallthrough insn: 61 ff ff 54                b.ne #0x4000f4
 b_ls_not_taken_            400108 fallthrough insn: 61 ff ff 54                b.ne #0x4000f4
 b_ls_not_taken_            4000f8 test_branch insn: 09 01 00 54                b.ls #0x400118
 b_ls_not_taken_  ffff800080010c80 vectors insn: 03 00 00 14            b #0xffff800080010c8c
 b_ls_not_taken_  ffff800080010ca4 vectors insn: 41 02 00 14            b #0xffff8000800115a8

The special fixup is only needed for SVC. For SVC exception entry,
cs_etm__set_sample_flags() has already identified the previous range as
a syscall branch, but last_instr_taken_branch is not set for the SVC
instruction. Restrict to force last_instr_taken_branch to true for a
system call only.

After:

 b_ls_not_taken_            400108 fallthrough insn: 61 ff ff 54                b.ne #0x4000f4
 b_ls_not_taken_            400108 fallthrough insn: 61 ff ff 54                b.ne #0x4000f4
 b_ls_not_taken_  ffff800080010c80 vectors insn: 03 00 00 14            b #0xffff800080010c8c
 b_ls_not_taken_  ffff800080010ca4 vectors insn: 41 02 00 14            b #0xffff8000800115a8

Fixes: 7100b12cf474 ("perf cs-etm: Generate branch sample for exception packet")
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
 tools/perf/util/cs-etm.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 95e3ec1171acc8442d8539a72a26a1a5a53a2f37..05e698f666bbe07af9caeda1a673f22d7196817a 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -1999,17 +1999,20 @@ static int cs_etm__context(struct cs_etm_queue *etmq,
 static int cs_etm__exception(struct cs_etm_traceid_queue *tidq)
 {
 	/*
-	 * When the exception packet is inserted, whether the last instruction
-	 * in previous range packet is taken branch or not, we need to force
-	 * to set 'prev_packet->last_instr_taken_branch' to true.  This ensures
-	 * to generate branch sample for the instruction range before the
-	 * exception is trapped to kernel or before the exception returning.
+	 * cs_etm__set_sample_flags() has already copied exception flags to
+	 * the previous range packet. Do not mark every exception boundary as a
+	 * taken branch: an async exception can arrive after an untaken branch,
+	 * and forcing the flag would synthesize a bogus branch sample.
 	 *
-	 * The exception packet includes the dummy address values, so don't
-	 * swap PACKET with PREV_PACKET.  This keeps PREV_PACKET to be useful
-	 * for generating instruction and branch samples.
+	 * Keep the fixup only for SVC. The decoder reports the range ending in
+	 * SVC without last_instr_taken_branch set, but perf represents SVC
+	 * exception entry as a syscall branch and needs the flag to emit that
+	 * branch sample.
 	 */
-	if (tidq->prev_packet->sample_type == CS_ETM_RANGE)
+	if (tidq->prev_packet->sample_type == CS_ETM_RANGE &&
+	    tidq->prev_packet->flags == (PERF_IP_FLAG_BRANCH |
+					 PERF_IP_FLAG_CALL |
+					 PERF_IP_FLAG_SYSCALLRET))
 		tidq->prev_packet->last_instr_taken_branch = true;
 
 	return 0;

-- 
2.34.1


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

* [PATCH 2/2] perf cs-etm: Centralize branch sample checks
  2026-07-13 18:00 [PATCH 0/2] perf cs-etm: Fix bogus branch samples Leo Yan
  2026-07-13 18:00 ` [PATCH 1/2] perf cs-etm: Avoid bogus branch samples before async exceptions Leo Yan
@ 2026-07-13 18:00 ` Leo Yan
  1 sibling, 0 replies; 4+ messages in thread
From: Leo Yan @ 2026-07-13 18:00 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Mike Leach, Suzuki K Poulose
  Cc: Arnaldo Carvalho de Melo, coresight, linux-arm-kernel,
	linux-perf-users, linux-kernel, Leo Yan

cs_etm__sample() validates whether the previous packet should generate
a branch sample before calling cs_etm__synth_branch_sample(), while
cs_etm__flush() generates branch samples unconditionally.

Move the eligibility check into cs_etm__synth_branch_sample() so that
both paths apply the same rule: generate branch samples only for
discontinuity packets or range packets ending in a taken branch.

Fixes: d603b4e9f9c3 ("perf cs-etm: Generate branch sample when receiving a CS_ETM_TRACE_ON packet")
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
 tools/perf/util/cs-etm.c | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 05e698f666bbe07af9caeda1a673f22d7196817a..dec50e2cc7b7857a04f631b3e0e08beb6e7e280a 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -1710,6 +1710,11 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
 		!(etm->branches_filter & tidq->prev_packet->flags))
 		return 0;
 
+	/* Generate branch sample only for tracing on or taken branches */
+	if (tidq->prev_packet->sample_type != CS_ETM_DISCONTINUITY &&
+	    !cs_etm__packet_has_taken_branch(tidq->prev_packet))
+		return 0;
+
 	perf_sample__init(&sample, /*all=*/true);
 	ip = cs_etm__last_executed_instr(tidq->prev_packet);
 
@@ -1946,21 +1951,9 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
 	}
 
 	if (etm->synth_opts.branches) {
-		bool generate_sample = false;
-
-		/* Generate sample for tracing on packet */
-		if (tidq->prev_packet->sample_type == CS_ETM_DISCONTINUITY)
-			generate_sample = true;
-
-		/* Generate sample for branch taken packet */
-		if (cs_etm__packet_has_taken_branch(tidq->prev_packet))
-			generate_sample = true;
-
-		if (generate_sample) {
-			ret = cs_etm__synth_branch_sample(etmq, tidq);
-			if (ret)
-				return ret;
-		}
+		ret = cs_etm__synth_branch_sample(etmq, tidq);
+		if (ret)
+			return ret;
 	}
 
 	cs_etm__packet_swap(etm, tidq);

-- 
2.34.1


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

* Re: [PATCH 1/2] perf cs-etm: Avoid bogus branch samples before async exceptions
  2026-07-13 18:00 ` [PATCH 1/2] perf cs-etm: Avoid bogus branch samples before async exceptions Leo Yan
@ 2026-07-13 18:09   ` sashiko-bot
  0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-07-13 18:09 UTC (permalink / raw)
  To: Leo Yan; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Restricting the `last_instr_taken_branch` fixup strictly to SVC instructions silently drops valid branch samples for all other exceptions, including asynchronous interrupts and other synchronous exceptions (SMC, HVC, aborts, breakpoints), breaking the control flow graph from user/kernel space into the exception handlers.
--

--- Patch [1]: [PATCH 1/2] perf cs-etm: Avoid bogus branch samples before async exceptions ---
commit d678304a350e693f048123702c01ddab5fbae8a5
Author: Leo Yan <leo.yan@arm.com>

perf cs-etm: Avoid bogus branch samples before async exceptions

This commit updates the CoreSight ETM decoder to avoid synthesizing bogus
branch samples before asynchronous exceptions. It does this by restricting
the fixup that forces the taken branch flag to true so that it only applies
to SVC (system call) exception entries.

> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 95e3ec1171acc..05e698f666bbe 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -1999,17 +1999,20 @@ static int cs_etm__context(struct cs_etm_queue *etmq,
>  static int cs_etm__exception(struct cs_etm_traceid_queue *tidq)
>  {
>  	/*
[ ... ]
>  	 */
> -	if (tidq->prev_packet->sample_type == CS_ETM_RANGE)
> +	if (tidq->prev_packet->sample_type == CS_ETM_RANGE &&
> +	    tidq->prev_packet->flags == (PERF_IP_FLAG_BRANCH |
> +					 PERF_IP_FLAG_CALL |
> +					 PERF_IP_FLAG_SYSCALLRET))
>  		tidq->prev_packet->last_instr_taken_branch = true;

[Severity: High]
Does this exact flag match inadvertently drop valid branch samples for other
synchronous exceptions like SMC, HVC, or data aborts?

These other exceptions use PERF_IP_FLAG_INTERRUPT and won't match the strict
mask introduced here. When tracing into hypervisors, secure monitors, or
exception handlers, the last_instr_taken_branch flag will remain false, which
prevents cs_etm__synth_branch_sample() from being called for the exception
entry.

Could this break the control flow graph for these traps by omitting their
entry branch samples?

>  
>  	return 0;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260713-perf_cs_etm_fix_non_taken-v1-0-4561607fc69f@arm.com?part=1

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

end of thread, other threads:[~2026-07-13 18:09 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 18:00 [PATCH 0/2] perf cs-etm: Fix bogus branch samples Leo Yan
2026-07-13 18:00 ` [PATCH 1/2] perf cs-etm: Avoid bogus branch samples before async exceptions Leo Yan
2026-07-13 18:09   ` sashiko-bot
2026-07-13 18:00 ` [PATCH 2/2] perf cs-etm: Centralize branch sample checks Leo Yan

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