In per-thread mode all AUX fragments for a thread are decoded by a single decoder, even if the task migrates between CPUs. If independent fragments come from different CPUs, the trace stream may not be contiguous for that decoder context.
Retain the CPU from each AUX sample in its queued fragment. Compare the preceding and fetched buffer CPUs in cs_etm__get_trace() and request a reset when either CPU is unknown or their CPU IDs differ. Indexed files can omit PERF_SAMPLE_CPU and hardware-ID records while remaining decodable through TID matching and the legacy metadata mapping. An unknown CPU therefore cannot establish trace continuity.
On 32-bit hosts, queuing a large AUX fragment can split it into several buffers while returning only the final piece. Save the queue tail before queuing and tag every newly added piece with the AUX sample CPU. Do this after queuing to preserve CPU filtering based on the original AUXTRACE record. Per-CPU fragments already match the sample CPU, so use the same assignment in both modes.
Skip CPU comparisons for internally split continuation buffers, which belong to the same AUX fragment. Keep AUX flags on the final piece so a collision applies after the whole fragment.
Check CPU metadata even for empty fragments. Preserve any reset request until the next nonempty block is decoded.
Signed-off-by: Leo Yan leo.yan@arm.com --- tools/perf/util/cs-etm.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 6f95730fe3b94ae3f534eee505132a2efbc4ff96..8f04d7ceddd9d1f7ff22af234b19400efe27ba2b 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -1486,6 +1486,16 @@ cs_etm__get_trace(struct cs_etm_queue *etmq) (old_buffer->aux_flags & PERF_AUX_FLAG_COLLISION))) etmq->reset_decoder = true;
+ /* + * Unknown or different CPUs cannot establish per-thread continuity. + * Internally split buffers still belong to the same AUX fragment. + */ + if (etmq->etm->per_thread_decoding && old_buffer && + !aux_buffer->consecutive && + (old_buffer->cpu.cpu == -1 || aux_buffer->cpu.cpu == -1 || + old_buffer->cpu.cpu != aux_buffer->cpu.cpu)) + etmq->reset_decoder = true; + return 0; }
@@ -3251,10 +3261,15 @@ 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 auxtrace_buffer *pos; + struct list_head *head;
if (!etmq) return -EINVAL;
+ head = &etm->queues.queue_array[etmq->queue_nr].head; + pos = list_last_entry_or_null(head, struct auxtrace_buffer, list); + /* * If this AUX event was inside this buffer somewhere, create a new auxtrace event * based on the sizes of the aux event, and queue that fragment. @@ -3272,9 +3287,22 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o if (err) return err;
- if (buffer) + if (buffer) { buffer->aux_flags = aux_event->flags;
+ /* + * Preserve queue-time CPU filtering and tag every split + * piece added after the previous tail, up to the returned + * buffer, for migration detection. + */ + pos = list_prepare_entry(pos, head, list); + list_for_each_entry_continue(pos, head, list) { + pos->cpu.cpu = sample->cpu; + if (pos == buffer) + break; + } + } + format = (aux_event->flags & PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW) ? UNFORMATTED : FORMATTED; if (etmq->format != UNSET && format != etmq->format) {