From: Leo Yan leo.yan@arm.com
The CoreSight driver emits PERF_RECORD_AUX_OUTPUT_HW_ID via perf_report_aux_output_id(). If the perf ring buffer is full, perf_report_aux_output_id() can fail to emit the record and return silently.
When at least one HW_ID record is present in the file, perf skips the legacy metadata-only trace ID mapping path. Therefore any raw AUX queue whose HW_ID record was dropped can be left with an empty traceid_list, and cs_etm__create_queue_decoders() will not create a decoder for that queue.
Fix it by ignoring all HW_IDs for unformatted queues and always create one decoder per queue unconditionally. Trace ID is unused so we can hard code it to 1 (0 is a special value used in 'pending_timestamp_chan_id' to indicate not pending). We also need put the same trace ID in the CPU metadata because the relationship between CPU metadata and traceid_list is bidirectional. Moving cs_etm__metadata_set_trace_id() into the mapping function solves this and simplifies existing code. cs_etm__insert_trace_id_node() now does two things, so rename it to a more generic cs_etm__map_trace_id().
Signed-off-by: Leo Yan leo.yan@arm.com Signed-off-by: James Clark james.clark@linaro.org --- tools/perf/util/cs-etm.c | 85 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 27 deletions(-)
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 78ae8443ac96..c0152549e3e2 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -243,8 +243,12 @@ enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq) return etmq->etm->pid_fmt; }
-static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq, - u8 trace_chan_id, u64 *cpu_metadata) +/* + * Add a traceid_list entry that maps a trace ID to a CPU metadata for this etmq + * and update the trace ID in the cpu_metadata. + */ +static int cs_etm__map_trace_id(struct cs_etm_queue *etmq, u8 trace_chan_id, + u64 *cpu_metadata) { /* Get an RB node for this CPU */ struct int_node *inode = intlist__findnew(etmq->traceid_list, trace_chan_id); @@ -289,8 +293,7 @@ static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq,
/* Not one we've seen before, associate the traceID with the metadata pointer */ inode->priv = cpu_metadata; - - return 0; + return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_metadata); }
static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int cpu) @@ -314,12 +317,11 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id return -EINVAL;
/* - * If the queue is unformatted then only save one mapping in the - * queue associated with that CPU so only one decoder is made. + * Ignore HW_IDs for unformatted, a decoder will be made + * unconditionally later. */ if (etmq->format == UNFORMATTED) - return cs_etm__insert_trace_id_node(etmq, trace_chan_id, - cpu_metadata); + return 0;
/* * Otherwise, version 0 trace IDs are global so save them into every @@ -332,8 +334,7 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id if (!etmq || etmq->format == UNFORMATTED) continue;
- ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id, - cpu_metadata); + ret = cs_etm__map_trace_id(etmq, trace_chan_id, cpu_metadata); if (ret) return ret; } @@ -344,7 +345,6 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id static int cs_etm__process_trace_id_v0(struct cs_etm_auxtrace *etm, int cpu, u64 hw_id) { - int err; u64 *cpu_data; u8 trace_chan_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id);
@@ -352,22 +352,13 @@ static int cs_etm__process_trace_id_v0(struct cs_etm_auxtrace *etm, int cpu, if (cpu_data == NULL) return -EINVAL;
- err = cs_etm__map_trace_id_v0(etm, trace_chan_id, cpu_data); - if (err) - return err; - - /* - * if we are picking up the association from the packet, need to plug - * the correct trace ID into the metadata for setting up decoders later. - */ - return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_data); + return cs_etm__map_trace_id_v0(etm, trace_chan_id, cpu_data); }
static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, u64 hw_id) { struct cs_etm_queue *etmq = cs_etm__get_queue(etm, cpu); - int ret; u64 *cpu_data; u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id); u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); @@ -375,6 +366,13 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, if (!etmq) return -EINVAL;
+ /* + * Ignore HW_IDs for unformatted, a decoder will be made + * unconditionally later. + */ + if (etmq->format == UNFORMATTED) + return 0; + /* * Check sink id hasn't changed in per-cpu mode. In per-thread mode, * let it pass for now until an actual overlapping trace ID is hit. In @@ -419,13 +417,41 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, if (!cpu_data) return -EINVAL;
- ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data); - if (ret) - return ret; + return cs_etm__map_trace_id(etmq, trace_id, cpu_data); +}
- ret = cs_etm__metadata_set_trace_id(trace_id, cpu_data); - if (ret) - return ret; +static int cs_etm__map_unformatted_trace_ids(struct cs_etm_auxtrace *etm) +{ + struct auxtrace_queues *queues = &etm->queues; + + for (unsigned int i = 0; i < queues->nr_queues; i++) { + struct auxtrace_queue *queue = &queues->queue_array[i]; + struct cs_etm_queue *etmq = queue->priv; + u64 *cpu_data; + int ret; + + if (list_empty(&queue->head) || !etmq || etmq->format != UNFORMATTED) + continue; + + if (!intlist__empty(etmq->traceid_list)) { + pr_err("CS_ETM: Unformatted queue should't have trace ID\n"); + return -EINVAL; + } + + if (etm->per_thread_decoding) + cpu_data = etm->metadata[0]; + else + cpu_data = get_cpu_data(etm, queue->cpu); + + if (!cpu_data) { + pr_err("CS_ETM: Unformatted queue missing CPU metadata\n"); + return -EINVAL; + } + + ret = cs_etm__map_trace_id(etmq, 1, cpu_data); + if (ret) + return ret; + }
return 0; } @@ -3757,6 +3783,11 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, goto err_free_queues; }
+ /* Create IDs for TRBE sinks so that a decoder is made for each one. */ + err = cs_etm__map_unformatted_trace_ids(etm); + if (err) + goto err_free_queues; + err = cs_etm__create_decoders(etm); if (err) goto err_free_queues;