[PATCH v2 09/14] perf cs-etm: Classify exception calls using the exception packet

James Clark james.clark at linaro.org
Tue Sep 29 07:08:42 PDT 2026



On 23/09/2026 16:21, Leo Yan wrote:
> ETMv4 and ETE use the same exception number for SVC, HVC and SMC.
> Identify SVC by inspecting the instruction before the preferred return
> address in the exception packet.
> 
> Preserve the exception level and preferred return address from OpenCSD,
> and use them with the packet's ISA in cs_etm__is_syscall() and
> cs_etm__is_sync_exception(). This makes exception call classification
> independent of the preceding instruction range.
> 
> Assisted-by: Codex:gpt-6
> Signed-off-by: Leo Yan <leo.yan at arm.com>
> ---
>   tools/perf/util/cs-etm-decoder/cs-etm-decoder.c |  7 ++++++-
>   tools/perf/util/cs-etm.c                        | 10 +++-------
>   tools/perf/util/cs-etm.h                        |  1 +
>   3 files changed, 10 insertions(+), 8 deletions(-)
> 
> diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
> index 469aba5e596a791289c5bcece132aba7b0baf6c3..48e8c355a3e495cc30dcb4056c04a034d10ce860 100644
> --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
> +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
> @@ -511,7 +511,8 @@ cs_etm_decoder__buffer_exception(struct cs_etm_queue *etmq,
>   				 struct cs_etm_packet_queue *queue,
>   				 const ocsd_generic_trace_elem *elem,
>   				 const uint8_t trace_chan_id)
> -{	int ret = 0;
> +{
> +	int ret = 0;
>   	struct cs_etm_packet *packet;
>   
>   	ret = cs_etm_decoder__buffer_packet(etmq, queue, elem, trace_chan_id,
> @@ -521,6 +522,10 @@ cs_etm_decoder__buffer_exception(struct cs_etm_queue *etmq,
>   
>   	packet = &queue->packet_buffer[queue->tail];
>   	packet->exception_number = elem->exception_number;
> +	if (elem->context.el_valid)
> +		packet->el = elem->context.exception_level;
> +	if (elem->excep_ret_addr)
> +		packet->end_addr = elem->en_addr;
>   
>   	return ret;
>   }
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index c8a92b5dba6001329d68766d540b9b7ead90e9e6..a40b3b2fe2e6318154111d9b7ebd05bee53934f1 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -2019,7 +2019,7 @@ static int cs_etm__exception(struct cs_etm_traceid_queue *tidq)
>   	 * to generate branch sample for the instruction range before the
>   	 * exception is trapped to kernel or before the exception returning.
>   	 *
> -	 * The exception packet includes the dummy address values, so don't
> +	 * The exception packet does not describe an instruction range, so don't
>   	 * swap PACKET with PREV_PACKET.  This keeps PREV_PACKET to be useful
>   	 * for generating instruction and branch samples.
>   	 */
> @@ -2225,7 +2225,6 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
>   			       struct cs_etm_traceid_queue *tidq, u64 magic)
>   {
>   	struct cs_etm_packet *packet = tidq->packet;
> -	struct cs_etm_packet *prev_packet = tidq->prev_packet;
>   
>   	if (magic == __perf_cs_etmv3_magic)
>   		if (packet->exception_number == CS_ETMV3_EXC_SVC)
> @@ -2238,8 +2237,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
>   	 */
>   	if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic) {
>   		if (packet->exception_number == CS_ETMV4_EXC_CALL &&
> -		    cs_etm__is_svc_instr(etmq, tidq, prev_packet,
> -					 prev_packet->end_addr))
> +		    cs_etm__is_svc_instr(etmq, tidq, packet, packet->end_addr))
>   			return true;
>   	}
>   
> @@ -2277,7 +2275,6 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
>   				      u64 magic)
>   {
>   	struct cs_etm_packet *packet = tidq->packet;
> -	struct cs_etm_packet *prev_packet = tidq->prev_packet;
>   
>   	if (magic == __perf_cs_etmv3_magic)
>   		if (packet->exception_number == CS_ETMV3_EXC_SMC ||
> @@ -2301,8 +2298,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
>   		 * (SMC, HVC) are taken as sync exceptions.
>   		 */
>   		if (packet->exception_number == CS_ETMV4_EXC_CALL &&
> -		    !cs_etm__is_svc_instr(etmq, tidq, prev_packet,
> -					  prev_packet->end_addr))
> +		    !cs_etm__is_svc_instr(etmq, tidq, packet, packet->end_addr))
>   			return true;
>   
>   		/*
> diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
> index b81099c2b301c25f894cc9a50edfde87aceff49d..4d03f2a680b58aacd91c9f6c957fb2d38e7ac2ad 100644
> --- a/tools/perf/util/cs-etm.h
> +++ b/tools/perf/util/cs-etm.h
> @@ -174,6 +174,7 @@ struct cs_etm_packet {
>   	enum cs_etm_sample_type sample_type;
>   	enum cs_etm_isa isa;
>   	u64 start_addr;
> +	/* For exceptions: preferred return address, or CS_ETM_INVAL_ADDR. */
>   	u64 end_addr;

Minor nit, but you could use a union. I don't know how clear re-using 
"end_addr" id for exception packets is when reading the code.

        union {
            u64 end_addr; // Range packets
            u64 pref_ret_addr; // Exception packets
        };


>   	u32 instr_count;
>   	u32 last_instr_type;
> 




More information about the linux-arm-kernel mailing list