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@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;