[PATCH 04/14] perf cs-etm: Use per-CPU queues for per-thread mode
James Clark
james.clark at linaro.org
Tue Aug 11 08:30:07 PDT 2026
This fixes two problems, firstly per-thread mode didn't support multiple
threads well or at all because we always use queue 0 and assumed it was
for a single thread. However, Perf attaches to all child threads of one
process or allows multiple threads to be specified on the commandline.
This meant that trace before a context packet could use the wrong MMAP
for decoding, and threads running concurrently wouldn't be interleaved
properly.
The second problem is that we'd put all HW_IDs into queue 0, so if a
thread runs on multiple CPUs then it would receive multiple HW_IDs and
trigger the assert that unformatted trace should only have 1 decoder
per-CPU:
$ perf record --per-thread -e cs_etm//u -- taskset --cpu-list 0 \
taskset --cpu-list 1 true
$ perf script
perf: util/cs-etm.c:3381: cs_etm__create_queue_decoders: Assertion `decoders == 1' failed.
Fixes: 9182f04a85b2 ("perf cs-etm: Pass unformatted flag to decoder")
Signed-off-by: James Clark <james.clark at linaro.org>
---
tools/perf/util/cs-etm.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index d423f1cb408f..28f5ce63ba80 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -295,9 +295,6 @@ static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq,
static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int cpu)
{
- if (etm->per_thread_decoding)
- return etm->queues.queue_array[0].priv;
-
if (cpu < 0 || cpu >= (int)etm->queues.nr_queues)
return NULL;
@@ -2160,6 +2157,7 @@ static void cs_etm__flush_all_stack(struct cs_etm_queue *etmq)
*/
static int cs_etm__get_data_block(struct cs_etm_queue *etmq)
{
+ struct cs_etm_auxtrace *etm = etmq->etm;
int ret;
/* The current block is not finished */
@@ -2188,6 +2186,27 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq)
*/
cs_etm__flush_all_stack(etmq);
+ /*
+ * Per-thread mode still uses a queue for each CPU, but that CPU can run
+ * different threads. When the TID from the AUX record on a CPU changes,
+ * re-initialize the thread using the AUX record/buffer fragment TID so
+ * we can start decoding even if the context ID packet was cropped or
+ * they're disabled.
+ */
+ if (etm->per_thread_decoding) {
+ struct cs_etm_traceid_queue *tidq = cs_etm__etmq_get_traceid_queue(etmq,
+ CS_ETM_PER_THREAD_TRACEID);
+
+ if (thread__tid(tidq->decode_thread) != etmq->buffer->tid) {
+ thread__zput(tidq->frontend_thread);
+ thread__zput(tidq->decode_thread);
+ tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host,
+ -1, etmq->buffer->tid);
+ tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host,
+ -1, etmq->buffer->tid);
+ }
+ }
+
return 1;
}
@@ -3245,7 +3264,7 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o
if (aux_offset >= auxtrace_event->offset &&
aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) {
- struct cs_etm_queue *etmq = cs_etm__get_queue(etm, auxtrace_event->cpu);
+ struct cs_etm_queue *etmq = cs_etm__get_queue(etm, sample->cpu);
if (!etmq)
return -EINVAL;
--
2.43.0
More information about the linux-arm-kernel
mailing list