Linux Documentation
 help / color / mirror / Atom feed
From: James Clark <james.clark@linaro.org>
To: Amir Ayupov <aaupov@meta.com>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>,
	Mike Leach <mike.leach@arm.com>, Leo Yan <leo.yan@arm.com>,
	Arnaldo Carvalho de Melo <acme@kernel.org>,
	Namhyung Kim <namhyung@kernel.org>, Jiri Olsa <jolsa@kernel.org>,
	Ian Rogers <irogers@google.com>, Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Paschalis Mpeis <Paschalis.Mpeis@arm.com>,
	coresight@lists.linaro.org, linux-perf-users@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Arnaldo Carvalho de Melo <acme@redhat.com>,
	linux-doc@vger.kernel.org
Subject: Re: [PATCH v3 01/19] perf cs-etm: Queue context packets for frontend
Date: Thu, 4 Jun 2026 13:29:35 +0100	[thread overview]
Message-ID: <a6e8e27d-31b5-4b67-affd-97e20aae49bf@linaro.org> (raw)
In-Reply-To: <CAMOD+7+xeh-RNQJUUpGoV6gU3swOcpGX82n2FSNiiewZ2R3vKQ@mail.gmail.com>



On 03/06/2026 9:14 pm, Amir Ayupov wrote:
> Hi James,
> 
> I tested the v2 patch series and it looks good. Compared to my
> stamping pid/tid on each packet, there was a minor difference in 2/39
> tested perf data files: the number of brstack samples differs by one,
> however, there was no loss of binary profile. The resulting BOLT
> profile converted from the perf script output was identical, so I'm OK
> with v2 patch as-is.
> 
> -Amir
> 

Thanks for testing. Seems like Leo might have an explanation for the 
branch stack difference on the other thread. If you want to share the 
files I can look into it, otherwise I'm ok with ignoring it too.

James

> 
> 
> On Wed, Jun 3, 2026 at 3:17 AM James Clark <james.clark@linaro.org> wrote:
>>
>>>
>> PE_CONTEXT elements update the context ID and exception level, but the
>> decoder may still have prior packets cached for frontend processing.
>> Updating the context immediately in the decoder backend can make those
>> cached packets get consumed with the wrong thread or EL state.
>>
>> Add a CS_ETM_CONTEXT packet carrying the TID and EL to the frontend,
>> this keeps context changes ordered with the rest of the packet stream
>> and avoids mismatches when synthesizing samples from cached packets.
>>
>> Separate the memory access function into one for the frontend and one
>> for decoding. The frontend also needs memory access to attach the
>> instruction to samples. Because the frontend does memory access for
>> both previous and current packets, change all the frontend memory access
>> function signatures to take both a tidq and packet. But backend always
>> uses the current backend EL and thread from the tidq.
>>
>> Treat context packets as a boundary for branch sample generation and
>> remove tidq->prev_packet_thread because it's not possible to branch to a
>> different thread, so only tracking the current thread is required for
>> sample generation.
>>
>> Fixes: e573e978fb12 ("perf cs-etm: Inject capabilitity for CoreSight traces")
>> Reported-by: Amir Ayupov <aaupov@meta.com>
>> Closes: https://lore.kernel.org/linux-perf-users/20260515021135.1729028-1-aaupov@meta.com/
>> Co-authored-by: James Clark <james.clark@linaro.org>
>> Signed-off-by: Leo Yan <leo.yan@arm.com>
>> Signed-off-by: James Clark <james.clark@linaro.org>
>> ---
>>   tools/perf/util/cs-etm-decoder/cs-etm-decoder.c |  21 ++-
>>   tools/perf/util/cs-etm.c                        | 234 ++++++++++++++----------
>>   tools/perf/util/cs-etm.h                        |   8 +-
>>   3 files changed, 162 insertions(+), 101 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 dee3020ceaa9..26940f1f1b0b 100644
>> --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
>> +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
>> @@ -402,6 +402,8 @@ cs_etm_decoder__buffer_packet(struct cs_etm_queue *etmq,
>>          packet_queue->packet_buffer[et].flags = 0;
>>          packet_queue->packet_buffer[et].exception_number = UINT32_MAX;
>>          packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id;
>> +       packet_queue->packet_buffer[et].el = ocsd_EL_unknown;
>> +       packet_queue->packet_buffer[et].tid = -1;
>>
>>          if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1)
>>                  return OCSD_RESP_WAIT;
>> @@ -449,6 +451,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq,
>>          packet->last_instr_type = elem->last_i_type;
>>          packet->last_instr_subtype = elem->last_i_subtype;
>>          packet->last_instr_cond = elem->last_instr_cond;
>> +       packet->el = elem->context.exception_level;
>>
>>          if (elem->last_i_type == OCSD_INSTR_BR || elem->last_i_type == OCSD_INSTR_BR_INDIRECT)
>>                  packet->last_instr_taken_branch = elem->last_instr_exec;
>> @@ -525,7 +528,9 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
>>                          const ocsd_generic_trace_elem *elem,
>>                          const uint8_t trace_chan_id)
>>   {
>> +       struct cs_etm_packet *packet;
>>          pid_t tid = -1;
>> +       int ret;
>>
>>          /*
>>           * Process the PE_CONTEXT packets if we have a valid contextID or VMID.
>> @@ -546,12 +551,18 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
>>                  break;
>>          }
>>
>> -       if (cs_etm__etmq_set_tid_el(etmq, tid, trace_chan_id,
>> -                                   elem->context.exception_level))
>> +       if (cs_etm__etmq_update_decode_context(etmq, trace_chan_id,
>> +                               elem->context.exception_level, tid))
>>                  return OCSD_RESP_FATAL_SYS_ERR;
>>
>> -       if (tid == -1)
>> -               return OCSD_RESP_CONT;
>> +       ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id,
>> +                                           CS_ETM_CONTEXT);
>> +       if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT)
>> +               return ret;
>> +
>> +       packet = &packet_queue->packet_buffer[packet_queue->tail];
>> +       packet->tid = tid;
>> +       packet->el = elem->context.exception_level;
>>
>>          /*
>>           * A timestamp is generated after a PE_CONTEXT element so make sure
>> @@ -559,7 +570,7 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
>>           */
>>          cs_etm_decoder__reset_timestamp(packet_queue);
>>
>> -       return OCSD_RESP_CONT;
>> +       return ret;
>>   }
>>
>>   static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
>> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
>> index 40c6ddfa8c8d..ce570913669c 100644
>> --- a/tools/perf/util/cs-etm.c
>> +++ b/tools/perf/util/cs-etm.c
>> @@ -85,15 +85,22 @@ struct cs_etm_traceid_queue {
>>          u64 period_instructions;
>>          size_t last_branch_pos;
>>          union perf_event *event_buf;
>> -       struct thread *thread;
>> -       struct thread *prev_packet_thread;
>> -       ocsd_ex_level prev_packet_el;
>> -       ocsd_ex_level el;
>>          struct branch_stack *last_branch;
>>          struct branch_stack *last_branch_rb;
>>          struct cs_etm_packet *prev_packet;
>>          struct cs_etm_packet *packet;
>>          struct cs_etm_packet_queue packet_queue;
>> +
>> +       struct thread *decode_thread;
>> +       ocsd_ex_level decode_el;
>> +
>> +       /*
>> +        * The frontend accesses the EL from '[prev_]packet' because it needs
>> +        * previous EL for branch and current EL for instruction samples. It's
>> +        * not possible to change thread in a single branch sample so no need to
>> +        * store or access the thread through the packet.
>> +        */
>> +       struct thread *frontend_thread;
>>   };
>>
>>   enum cs_etm_format {
>> @@ -614,10 +621,11 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
>>
>>          queue = &etmq->etm->queues.queue_array[etmq->queue_nr];
>>          tidq->trace_chan_id = trace_chan_id;
>> -       tidq->el = tidq->prev_packet_el = ocsd_EL_unknown;
>> -       tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1,
>> +       tidq->decode_el = ocsd_EL_unknown;
>> +       tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host, -1,
>> +                                              queue->tid);
>> +       tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1,
>>                                                 queue->tid);
>> -       tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host);
>>
>>          tidq->packet = zalloc(sizeof(struct cs_etm_packet));
>>          if (!tidq->packet)
>> @@ -750,21 +758,10 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm,
>>                  /*
>>                   * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for
>>                   * the next incoming packet.
>> -                *
>> -                * Threads and exception levels are also tracked for both the
>> -                * previous and current packets. This is because the previous
>> -                * packet is used for the 'from' IP for branch samples, so the
>> -                * thread at that time must also be assigned to that sample.
>> -                * Across discontinuity packets the thread can change, so by
>> -                * tracking the thread for the previous packet the branch sample
>> -                * will have the correct info.
>>                   */
>>                  tmp = tidq->packet;
>>                  tidq->packet = tidq->prev_packet;
>>                  tidq->prev_packet = tmp;
>> -               tidq->prev_packet_el = tidq->el;
>> -               thread__put(tidq->prev_packet_thread);
>> -               tidq->prev_packet_thread = thread__get(tidq->thread);
>>          }
>>   }
>>
>> @@ -937,8 +934,8 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
>>
>>                  /* Free this traceid_queue from the array */
>>                  tidq = etmq->traceid_queues[idx];
>> -               thread__zput(tidq->thread);
>> -               thread__zput(tidq->prev_packet_thread);
>> +               thread__zput(tidq->frontend_thread);
>> +               thread__zput(tidq->decode_thread);
>>                  zfree(&tidq->event_buf);
>>                  zfree(&tidq->last_branch);
>>                  zfree(&tidq->last_branch_rb);
>> @@ -1083,47 +1080,43 @@ static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address,
>>          }
>>   }
>>
>> -static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
>> -                             u64 address, size_t size, u8 *buffer,
>> -                             const ocsd_mem_space_acc_t mem_space)
>> +static u32 __cs_etm__mem_access(struct cs_etm_queue *etmq,
>> +                               u64 address, size_t size, u8 *buffer,
>> +                               const ocsd_mem_space_acc_t mem_space,
>> +                               ocsd_ex_level el, struct thread *thread)
>>   {
>>          u8  cpumode;
>>          u64 offset;
>>          int len;
>>          struct addr_location al;
>>          struct dso *dso;
>> -       struct cs_etm_traceid_queue *tidq;
>>          int ret = 0;
>>
>>          if (!etmq)
>>                  return 0;
>>
>>          addr_location__init(&al);
>> -       tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
>> -       if (!tidq)
>> -               goto out;
>>
>>          /*
>> -        * We've already tracked EL along side the PID in cs_etm__set_thread()
>> -        * so double check that it matches what OpenCSD thinks as well. It
>> -        * doesn't distinguish between EL0 and EL1 for this mem access callback
>> -        * so we had to do the extra tracking. Skip validation if it's any of
>> -        * the 'any' values.
>> +        * We track EL for the frontend and the backend when receiving context
>> +        * and range packets. OpenCSD doesn't distinguish between EL0 and EL1
>> +        * for this mem access callback so we had to do the extra tracking. Skip
>> +        * validation if it's any of the 'any' values.
>>           */
>>          if (!(mem_space == OCSD_MEM_SPACE_ANY ||
>>                mem_space == OCSD_MEM_SPACE_N || mem_space == OCSD_MEM_SPACE_S)) {
>>                  if (mem_space & OCSD_MEM_SPACE_EL1N) {
>>                          /* Includes both non secure EL1 and EL0 */
>> -                       assert(tidq->el == ocsd_EL1 || tidq->el == ocsd_EL0);
>> +                       assert(el == ocsd_EL1 || el == ocsd_EL0);
>>                  } else if (mem_space & OCSD_MEM_SPACE_EL2)
>> -                       assert(tidq->el == ocsd_EL2);
>> +                       assert(el == ocsd_EL2);
>>                  else if (mem_space & OCSD_MEM_SPACE_EL3)
>> -                       assert(tidq->el == ocsd_EL3);
>> +                       assert(el == ocsd_EL3);
>>          }
>>
>> -       cpumode = cs_etm__cpu_mode(etmq, address, tidq->el);
>> +       cpumode = cs_etm__cpu_mode(etmq, address, el);
>>
>> -       if (!thread__find_map(tidq->thread, cpumode, address, &al))
>> +       if (!thread__find_map(thread, cpumode, address, &al))
>>                  goto out;
>>
>>          dso = map__dso(al.map);
>> @@ -1138,7 +1131,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
>>
>>          map__load(al.map);
>>
>> -       len = dso__data_read_offset(dso, maps__machine(thread__maps(tidq->thread)),
>> +       len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)),
>>                                      offset, buffer, size);
>>
>>          if (len <= 0) {
>> @@ -1158,6 +1151,30 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
>>          return ret;
>>   }
>>
>> +static u32 cs_etm__frontend_mem_access(struct cs_etm_queue *etmq,
>> +                                      struct cs_etm_traceid_queue *tidq,
>> +                                      struct cs_etm_packet *packet,
>> +                                      u64 address, size_t size, u8 *buffer)
>> +{
>> +       return __cs_etm__mem_access(etmq, address, size, buffer, 0, packet->el,
>> +                                   tidq->frontend_thread);
>> +}
>> +
>> +static u32 cs_etm__decoder_mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
>> +                                     u64 address, size_t size, u8 *buffer,
>> +                                     const ocsd_mem_space_acc_t mem_space)
>> +{
>> +       struct cs_etm_traceid_queue *tidq;
>> +
>> +       tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
>> +       if (!tidq)
>> +               return 0;
>> +
>> +       return __cs_etm__mem_access(etmq, address, size, buffer,
>> +                                   mem_space, tidq->decode_el,
>> +                                   tidq->decode_thread);
>> +}
>> +
>>   static struct cs_etm_queue *cs_etm__alloc_queue(void)
>>   {
>>          struct cs_etm_queue *etmq = zalloc(sizeof(*etmq));
>> @@ -1333,12 +1350,13 @@ void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq)
>>   }
>>
>>   static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq,
>> -                                        u8 trace_chan_id, u64 addr)
>> +                                        struct cs_etm_traceid_queue *tidq,
>> +                                        struct cs_etm_packet *packet, u64 addr)
>>   {
>>          u8 instrBytes[2];
>>
>> -       cs_etm__mem_access(etmq, trace_chan_id, addr, ARRAY_SIZE(instrBytes),
>> -                          instrBytes, 0);
>> +       cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
>> +                                   ARRAY_SIZE(instrBytes), instrBytes);
>>          /*
>>           * T32 instruction size is indicated by bits[15:11] of the first
>>           * 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111
>> @@ -1371,16 +1389,16 @@ u64 cs_etm__last_executed_instr(const struct cs_etm_packet *packet)
>>   }
>>
>>   static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
>> -                                    u64 trace_chan_id,
>> -                                    const struct cs_etm_packet *packet,
>> +                                    struct cs_etm_traceid_queue *tidq,
>> +                                    struct cs_etm_packet *packet,
>>                                       u64 offset)
>>   {
>>          if (packet->isa == CS_ETM_ISA_T32) {
>>                  u64 addr = packet->start_addr;
>>
>>                  while (offset) {
>> -                       addr += cs_etm__t32_instr_size(etmq,
>> -                                                      trace_chan_id, addr);
>> +                       addr += cs_etm__t32_instr_size(etmq, tidq, packet,
>> +                                                      addr);
>>                          offset--;
>>                  }
>>                  return addr;
>> @@ -1490,34 +1508,51 @@ cs_etm__get_trace(struct cs_etm_queue *etmq)
>>          return etmq->buf_len;
>>   }
>>
>> -static void cs_etm__set_thread(struct cs_etm_queue *etmq,
>> -                              struct cs_etm_traceid_queue *tidq, pid_t tid,
>> -                              ocsd_ex_level el)
>> +/*
>> + * Convert a raw thread number to a thread struct and assign it to **thread.
>> + */
>> +static int cs_etm__etmq_update_thread(struct cs_etm_queue *etmq,
>> +                                     ocsd_ex_level el, pid_t tid,
>> +                                     struct thread **thread)
>>   {
>>          struct machine *machine = cs_etm__get_machine(etmq, el);
>>
>> +       if (!machine || !*thread)
>> +               return -EINVAL;
>> +
>>          if (tid != -1) {
>> -               thread__zput(tidq->thread);
>> -               tidq->thread = machine__find_thread(machine, -1, tid);
>> +               thread__zput(*thread);
>> +               *thread = machine__find_thread(machine, -1, tid);
>>          }
>>
>>          /* Couldn't find a known thread */
>> -       if (!tidq->thread)
>> -               tidq->thread = machine__idle_thread(machine);
>> +       if (!*thread)
>> +               *thread = machine__idle_thread(machine);
>>
>> -       tidq->el = el;
>> +       return 0;
>>   }
>>
>> -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid,
>> -                           u8 trace_chan_id, ocsd_ex_level el)
>> +/*
>> + * Set the thread and EL of the decode context which is ahead in time of the
>> + * frontend context.
>> + */
>> +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq,
>> +                                      u8 trace_chan_id,
>> +                                      ocsd_ex_level el, pid_t tid)
>>   {
>>          struct cs_etm_traceid_queue *tidq;
>> +       int ret;
>>
>>          tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
>>          if (!tidq)
>>                  return -EINVAL;
>>
>> -       cs_etm__set_thread(etmq, tidq, tid, el);
>> +       ret = cs_etm__etmq_update_thread(etmq, el, tid,
>> +                                        &tidq->decode_thread);
>> +       if (ret)
>> +               return ret;
>> +
>> +       tidq->decode_el = el;
>>          return 0;
>>   }
>>
>> @@ -1527,8 +1562,8 @@ bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq)
>>   }
>>
>>   static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
>> -                             u64 trace_chan_id,
>> -                             const struct cs_etm_packet *packet,
>> +                             struct cs_etm_traceid_queue *tidq,
>> +                             struct cs_etm_packet *packet,
>>                                struct perf_sample *sample)
>>   {
>>          /*
>> @@ -1545,14 +1580,14 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
>>           * cs_etm__t32_instr_size().
>>           */
>>          if (packet->isa == CS_ETM_ISA_T32)
>> -               sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id,
>> +               sample->insn_len = cs_etm__t32_instr_size(etmq, tidq, packet,
>>                                                            sample->ip);
>>          /* Otherwise, A64 and A32 instruction size are always 32-bit. */
>>          else
>>                  sample->insn_len = 4;
>>
>> -       cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len,
>> -                          (void *)sample->insn, 0);
>> +       cs_etm__frontend_mem_access(etmq, tidq, packet, sample->ip,
>> +                                   sample->insn_len, (void *)sample->insn);
>>   }
>>
>>   u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp)
>> @@ -1579,6 +1614,7 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq,
>>
>>   static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
>>                                              struct cs_etm_traceid_queue *tidq,
>> +                                           struct cs_etm_packet *packet,
>>                                              u64 addr, u64 period)
>>   {
>>          int ret = 0;
>> @@ -1588,15 +1624,15 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
>>
>>          perf_sample__init(&sample, /*all=*/true);
>>          event->sample.header.type = PERF_RECORD_SAMPLE;
>> -       event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el);
>> +       event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, packet->el);
>>          event->sample.header.size = sizeof(struct perf_event_header);
>>
>>          /* Set time field based on etm auxtrace config. */
>>          sample.time = cs_etm__resolve_sample_time(etmq, tidq);
>>
>>          sample.ip = addr;
>> -       sample.pid = thread__pid(tidq->thread);
>> -       sample.tid = thread__tid(tidq->thread);
>> +       sample.pid = thread__pid(tidq->frontend_thread);
>> +       sample.tid = thread__tid(tidq->frontend_thread);
>>          sample.id = etmq->etm->instructions_id;
>>          sample.stream_id = etmq->etm->instructions_id;
>>          sample.period = period;
>> @@ -1604,7 +1640,7 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
>>          sample.flags = tidq->prev_packet->flags;
>>          sample.cpumode = event->sample.header.misc;
>>
>> -       cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample);
>> +       cs_etm__copy_insn(etmq, tidq, tidq->packet, &sample);
>>
>>          if (etm->synth_opts.last_branch)
>>                  sample.branch_stack = tidq->last_branch;
>> @@ -1649,15 +1685,15 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
>>
>>          event->sample.header.type = PERF_RECORD_SAMPLE;
>>          event->sample.header.misc = cs_etm__cpu_mode(etmq, ip,
>> -                                                    tidq->prev_packet_el);
>> +                                                    tidq->prev_packet->el);
>>          event->sample.header.size = sizeof(struct perf_event_header);
>>
>>          /* Set time field based on etm auxtrace config. */
>>          sample.time = cs_etm__resolve_sample_time(etmq, tidq);
>>
>>          sample.ip = ip;
>> -       sample.pid = thread__pid(tidq->prev_packet_thread);
>> -       sample.tid = thread__tid(tidq->prev_packet_thread);
>> +       sample.pid = thread__pid(tidq->frontend_thread);
>> +       sample.tid = thread__tid(tidq->frontend_thread);
>>          sample.addr = cs_etm__first_executed_instr(tidq->packet);
>>          sample.id = etmq->etm->branches_id;
>>          sample.stream_id = etmq->etm->branches_id;
>> @@ -1666,8 +1702,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
>>          sample.flags = tidq->prev_packet->flags;
>>          sample.cpumode = event->sample.header.misc;
>>
>> -       cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet,
>> -                         &sample);
>> +       cs_etm__copy_insn(etmq, tidq, tidq->prev_packet, &sample);
>>
>>          /*
>>           * perf report cannot handle events without a branch stack
>> @@ -1788,7 +1823,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
>>   {
>>          struct cs_etm_auxtrace *etm = etmq->etm;
>>          int ret;
>> -       u8 trace_chan_id = tidq->trace_chan_id;
>>          u64 instrs_prev;
>>
>>          /* Get instructions remainder from previous packet */
>> @@ -1874,10 +1908,10 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
>>                           * been executed, but PC has not advanced to next
>>                           * instruction)
>>                           */
>> -                       addr = cs_etm__instr_addr(etmq, trace_chan_id,
>> -                                                 tidq->packet, offset - 1);
>> +                       addr = cs_etm__instr_addr(etmq, tidq, tidq->packet,
>> +                                                 offset - 1);
>>                          ret = cs_etm__synth_instruction_sample(
>> -                               etmq, tidq, addr,
>> +                               etmq, tidq, tidq->packet, addr,
>>                                  etm->instructions_sample_period);
>>                          if (ret)
>>                                  return ret;
>> @@ -1959,7 +1993,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
>>                  addr = cs_etm__last_executed_instr(tidq->prev_packet);
>>
>>                  err = cs_etm__synth_instruction_sample(
>> -                       etmq, tidq, addr,
>> +                       etmq, tidq, tidq->prev_packet, addr,
>>                          tidq->period_instructions);
>>                  if (err)
>>                          return err;
>> @@ -2014,7 +2048,7 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq,
>>                  addr = cs_etm__last_executed_instr(tidq->prev_packet);
>>
>>                  err = cs_etm__synth_instruction_sample(
>> -                       etmq, tidq, addr,
>> +                       etmq, tidq, tidq->prev_packet, addr,
>>                          tidq->period_instructions);
>>                  if (err)
>>                          return err;
>> @@ -2051,9 +2085,9 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq)
>>          return etmq->buf_len;
>>   }
>>
>> -static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>> -                                struct cs_etm_packet *packet,
>> -                                u64 end_addr)
>> +static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
>> +                                struct cs_etm_traceid_queue *tidq,
>> +                                struct cs_etm_packet *packet, u64 end_addr)
>>   {
>>          /* Initialise to keep compiler happy */
>>          u16 instr16 = 0;
>> @@ -2075,8 +2109,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>>                   * so below only read 2 bytes as instruction size for T32.
>>                   */
>>                  addr = end_addr - 2;
>> -               cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr16),
>> -                                  (u8 *)&instr16, 0);
>> +               cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
>> +                                           sizeof(instr16), (u8 *)&instr16);
>>                  if ((instr16 & 0xFF00) == 0xDF00)
>>                          return true;
>>
>> @@ -2091,8 +2125,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>>                   * +---------+---------+-------------------------+
>>                   */
>>                  addr = end_addr - 4;
>> -               cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32),
>> -                                  (u8 *)&instr32, 0);
>> +               cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
>> +                                           sizeof(instr32), (u8 *)&instr32);
>>                  if ((instr32 & 0x0F000000) == 0x0F000000 &&
>>                      (instr32 & 0xF0000000) != 0xF0000000)
>>                          return true;
>> @@ -2108,8 +2142,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>>                   * +-----------------------+---------+-----------+
>>                   */
>>                  addr = end_addr - 4;
>> -               cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32),
>> -                                  (u8 *)&instr32, 0);
>> +               cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
>> +                                           sizeof(instr32), (u8 *)&instr32);
>>                  if ((instr32 & 0xFFE0001F) == 0xd4000001)
>>                          return true;
>>
>> @@ -2125,7 +2159,6 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>>   static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
>>                                 struct cs_etm_traceid_queue *tidq, u64 magic)
>>   {
>> -       u8 trace_chan_id = tidq->trace_chan_id;
>>          struct cs_etm_packet *packet = tidq->packet;
>>          struct cs_etm_packet *prev_packet = tidq->prev_packet;
>>
>> @@ -2140,7 +2173,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
>>           */
>>          if (magic == __perf_cs_etmv4_magic) {
>>                  if (packet->exception_number == CS_ETMV4_EXC_CALL &&
>> -                   cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
>> +                   cs_etm__is_svc_instr(etmq, tidq, prev_packet,
>>                                           prev_packet->end_addr))
>>                          return true;
>>          }
>> @@ -2178,7 +2211,6 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
>>                                        struct cs_etm_traceid_queue *tidq,
>>                                        u64 magic)
>>   {
>> -       u8 trace_chan_id = tidq->trace_chan_id;
>>          struct cs_etm_packet *packet = tidq->packet;
>>          struct cs_etm_packet *prev_packet = tidq->prev_packet;
>>
>> @@ -2204,7 +2236,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, trace_chan_id, prev_packet,
>> +                   !cs_etm__is_svc_instr(etmq, tidq, prev_packet,
>>                                            prev_packet->end_addr))
>>                          return true;
>>
>> @@ -2228,7 +2260,6 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
>>   {
>>          struct cs_etm_packet *packet = tidq->packet;
>>          struct cs_etm_packet *prev_packet = tidq->prev_packet;
>> -       u8 trace_chan_id = tidq->trace_chan_id;
>>          u64 magic;
>>          int ret;
>>
>> @@ -2309,11 +2340,11 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
>>                  if (prev_packet->flags == (PERF_IP_FLAG_BRANCH |
>>                                             PERF_IP_FLAG_RETURN |
>>                                             PERF_IP_FLAG_INTERRUPT) &&
>> -                   cs_etm__is_svc_instr(etmq, trace_chan_id,
>> -                                        packet, packet->start_addr))
>> +                   cs_etm__is_svc_instr(etmq, tidq, packet, packet->start_addr)) {
>>                          prev_packet->flags = PERF_IP_FLAG_BRANCH |
>>                                               PERF_IP_FLAG_RETURN |
>>                                               PERF_IP_FLAG_SYSCALLRET;
>> +               }
>>                  break;
>>          case CS_ETM_DISCONTINUITY:
>>                  /*
>> @@ -2394,6 +2425,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
>>                                               PERF_IP_FLAG_RETURN |
>>                                               PERF_IP_FLAG_INTERRUPT;
>>                  break;
>> +       case CS_ETM_CONTEXT:
>>          case CS_ETM_EMPTY:
>>          default:
>>                  break;
>> @@ -2469,6 +2501,19 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq,
>>                           */
>>                          cs_etm__sample(etmq, tidq);
>>                          break;
>> +               case CS_ETM_CONTEXT:
>> +                       /*
>> +                        * Update context but don't swap packet. Keep the
>> +                        * previous one for branch source address info, if
>> +                        * tracing the kernel the context packet will be emitted
>> +                        * between two ranges.
>> +                        */
>> +                       ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el,
>> +                                                        tidq->packet->tid,
>> +                                                        &tidq->frontend_thread);
>> +                       if (ret)
>> +                               goto out;
>> +                       break;
>>                  case CS_ETM_EXCEPTION:
>>                  case CS_ETM_EXCEPTION_RET:
>>                          /*
>> @@ -2497,6 +2542,7 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq,
>>                  }
>>          }
>>
>> +out:
>>          return ret;
>>   }
>>
>> @@ -2620,7 +2666,7 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
>>                          if (!tidq)
>>                                  continue;
>>
>> -                       if (tid == -1 || thread__tid(tidq->thread) == tid)
>> +                       if (tid == -1 || thread__tid(tidq->frontend_thread) == tid)
>>                                  cs_etm__run_per_thread_timeless_decoder(etmq);
>>                  } else
>>                          cs_etm__run_per_cpu_timeless_decoder(etmq);
>> @@ -3328,7 +3374,7 @@ static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq)
>>           */
>>          if (cs_etm_decoder__add_mem_access_cb(etmq->decoder,
>>                                                0x0L, ((u64) -1L),
>> -                                             cs_etm__mem_access))
>> +                                             cs_etm__decoder_mem_access))
>>                  goto out_free_decoder;
>>
>>          zfree(&t_params);
>> diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
>> index aa9bb4a32eca..b81099c2b301 100644
>> --- a/tools/perf/util/cs-etm.h
>> +++ b/tools/perf/util/cs-etm.h
>> @@ -158,6 +158,7 @@ enum cs_etm_sample_type {
>>          CS_ETM_DISCONTINUITY,
>>          CS_ETM_EXCEPTION,
>>          CS_ETM_EXCEPTION_RET,
>> +       CS_ETM_CONTEXT,
>>   };
>>
>>   enum cs_etm_isa {
>> @@ -184,6 +185,8 @@ struct cs_etm_packet {
>>          u8 last_instr_size;
>>          u8 trace_chan_id;
>>          int cpu;
>> +       int el;
>> +       pid_t tid;
>>   };
>>
>>   #define CS_ETM_PACKET_MAX_BUFFER 1024
>> @@ -259,8 +262,9 @@ enum cs_etm_pid_fmt {
>>   #include <opencsd/ocsd_if_types.h>
>>   int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu);
>>   enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq);
>> -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid,
>> -                           u8 trace_chan_id, ocsd_ex_level el);
>> +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq,
>> +                                      u8 trace_chan_id, ocsd_ex_level el,
>> +                                      pid_t tid);
>>   bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq);
>>   void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq,
>>                                                u8 trace_chan_id);
>>
>> --
>> 2.34.1
>>


  reply	other threads:[~2026-06-04 12:29 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-03 10:17 [PATCH v3 00/19] perf cs-etm: Queue context packets for frontend James Clark
2026-06-03 10:17 ` [PATCH v3 01/19] " James Clark
2026-06-03 20:14   ` Amir Ayupov
2026-06-04 12:29     ` James Clark [this message]
2026-06-03 10:17 ` [PATCH v3 02/19] perf test: Add workload-ctl option James Clark
2026-06-03 10:17 ` [PATCH v3 03/19] perf test: Add a workload that forces context switches James Clark
2026-06-03 10:17 ` [PATCH v3 04/19] perf test cs-etm: Test process attribution James Clark
2026-06-03 10:17 ` [PATCH v3 05/19] perf test: Add deterministic workload James Clark
2026-06-03 10:17 ` [PATCH v3 06/19] perf test cs-etm: Replace unroll loop thread with deterministic decode test James Clark
2026-06-03 10:17 ` [PATCH v3 07/19] perf test cs-etm: Remove asm_pure_loop test James Clark
2026-06-03 10:17 ` [PATCH v3 08/19] perf test cs-etm: Replace memcpy test with raw dump stress test James Clark
2026-06-03 10:17 ` [PATCH v3 09/19] perf test: Add named_threads workload James Clark
2026-06-03 10:17 ` [PATCH v3 10/19] perf test cs-etm: Test decoding for concurrent threads test James Clark
2026-06-03 10:17 ` [PATCH v3 11/19] perf test cs-etm: Remove duplicate branch tests James Clark
2026-06-03 10:17 ` [PATCH v3 12/19] perf test cs-etm: Skip if not root James Clark
2026-06-03 10:17 ` [PATCH v3 13/19] perf test cs-etm: Reduce snapshot size James Clark
2026-06-03 10:17 ` [PATCH v3 14/19] perf test cs-etm: Speed up basic test James Clark
2026-06-03 10:17 ` [PATCH v3 15/19] perf test cs-etm: Remove unused Coresight workloads James Clark
2026-06-03 10:17 ` [PATCH v3 16/19] perf test cs-etm: Make disassembly test use kcore James Clark
2026-06-03 10:17 ` [PATCH v3 17/19] perf test cs-etm: Add all branch instructions to test James Clark
2026-06-03 10:17 ` [PATCH v3 18/19] perf test cs-etm: Speed up disassembly test James Clark
2026-06-03 10:17 ` [PATCH v3 19/19] perf test cs-etm: Move existing tests to coresight folder James Clark

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=a6e8e27d-31b5-4b67-affd-97e20aae49bf@linaro.org \
    --to=james.clark@linaro.org \
    --cc=Paschalis.Mpeis@arm.com \
    --cc=aaupov@meta.com \
    --cc=acme@kernel.org \
    --cc=acme@redhat.com \
    --cc=corbet@lwn.net \
    --cc=coresight@lists.linaro.org \
    --cc=irogers@google.com \
    --cc=jolsa@kernel.org \
    --cc=leo.yan@arm.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mike.leach@arm.com \
    --cc=namhyung@kernel.org \
    --cc=skhan@linuxfoundation.org \
    --cc=suzuki.poulose@arm.com \
    /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