[RFC PATCH 5/5] perf cs-etm: split decode by aux records.

From: James Clark
Date: Tue Feb 09 2021 - 05:18:31 EST


The trace data between aux records is not continuous, so the decoder
must be reset between each record to ensure that parsing happens
correctly and without any early exits.

Signed-off-by: James Clark <james.clark@xxxxxxx>
---
tools/perf/util/cs-etm.c | 108 ++++++++++++++++++++++++---------------
1 file changed, 66 insertions(+), 42 deletions(-)

diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 0aaa1f6d2822..b0f464a50e2f 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -95,6 +95,7 @@ struct cs_etm_queue {
int aux_record_list_len;
int aux_record_list_idx;
struct perf_record_aux *aux_record_list;
+ bool timestamp_found;
};

/* RB tree for quick conversion between traceID and metadata pointers */
@@ -788,6 +789,9 @@ static int cs_etm__seach_first_timestamp(struct cs_etm_queue *etmq,

etmq->aux_record_list[etmq->aux_record_list_len++] = *aux_record;

+ if (etmq->timestamp_found)
+ return 0;
+
/*
* We are under a CPU-wide trace scenario. As such we need to know
* when the code that generated the traces started to execute so that
@@ -796,56 +800,60 @@ static int cs_etm__seach_first_timestamp(struct cs_etm_queue *etmq,
* timestamp. The timestamp is then added to the auxtrace min heap
* in order to know what nibble (of all the etmqs) to decode first.
*/
- while (1) {
- /*
- * Fetch an aux_buffer from this etmq. Bail if no more
- * blocks or an error has been encountered.
- */
- ret = cs_etm__get_data_block(etmq);
- if (ret <= 0)
- return ret;
-
- /*
- * Run decoder on the trace block. The decoder will stop when
- * encountering a timestamp, a full packet queue or the end of
- * trace for that block.
- */
- ret = cs_etm__decode_data_block(etmq);
+ /*
+ * Fetch an aux_buffer from this etmq. Bail if no more
+ * blocks or an error has been encountered.
+ */
+ if (etmq->aux_record_list[etmq->aux_record_list_idx].aux_size <= 0) {
+ etmq->aux_record_list_idx++;
+ ret = cs_etm_decoder__reset(etmq->decoder);
if (ret)
return ret;
+ }
+ ret = cs_etm__get_data_block(etmq);
+ if (ret <= 0)
+ return ret;

- /*
- * Function cs_etm_decoder__do_{hard|soft}_timestamp() does all
- * the timestamp calculation for us.
- */
- timestamp = cs_etm__etmq_get_timestamp(etmq, &trace_chan_id);
+ /*
+ * Run decoder on the trace block. The decoder will stop when
+ * encountering a timestamp, a full packet queue or the end of
+ * trace for that block.
+ */
+ ret = cs_etm__decode_data_block(etmq);
+ if (ret)
+ return ret;

- /* We found a timestamp, no need to continue. */
- if (timestamp)
- break;
+ /*
+ * Function cs_etm_decoder__do_{hard|soft}_timestamp() does all
+ * the timestamp calculation for us.
+ */
+ timestamp = cs_etm__etmq_get_timestamp(etmq, &trace_chan_id);

+ /* We found a timestamp, no need to continue. */
+ if (timestamp) {
/*
- * We didn't find a timestamp so empty all the traceid packet
- * queues before looking for another timestamp packet, either
- * in the current data block or a new one. Packets that were
- * just decoded are useless since no timestamp has been
- * associated with them. As such simply discard them.
+ * We have a timestamp. Add it to the min heap to reflect when
+ * instructions conveyed by the range packets of this traceID queue
+ * started to execute. Once the same has been done for all the traceID
+ * queues of each etmq, redenring and decoding can start in
+ * chronological order.
+ *
+ * Note that packets decoded above are still in the traceID's packet
+ * queue and will be processed in cs_etm__process_queues().
*/
- cs_etm__clear_all_packet_queues(etmq);
+ etmq->timestamp_found = true;
+ cs_queue_nr = TO_CS_QUEUE_NR(etmq->queue_nr, trace_chan_id);
+ return auxtrace_heap__add(&etmq->etm->heap, cs_queue_nr, timestamp);
}
-
/*
- * We have a timestamp. Add it to the min heap to reflect when
- * instructions conveyed by the range packets of this traceID queue
- * started to execute. Once the same has been done for all the traceID
- * queues of each etmq, redenring and decoding can start in
- * chronological order.
- *
- * Note that packets decoded above are still in the traceID's packet
- * queue and will be processed in cs_etm__process_queues().
+ * We didn't find a timestamp so empty all the traceid packet
+ * queues before looking for another timestamp packet, either
+ * in the current data block or a new one. Packets that were
+ * just decoded are useless since no timestamp has been
+ * associated with them. As such simply discard them.
*/
- cs_queue_nr = TO_CS_QUEUE_NR(etmq->queue_nr, trace_chan_id);
- return auxtrace_heap__add(&etmq->etm->heap, cs_queue_nr, timestamp);
+ cs_etm__clear_all_packet_queues(etmq);
+ return 0;
}

static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
@@ -2012,6 +2020,15 @@ static int cs_etm__decode_data_block(struct cs_etm_queue *etmq)
{
int ret = 0;
size_t processed = 0;
+ u64 decode_size;
+
+ if (etmq->aux_record_list_idx >= etmq->aux_record_list_len ||
+ etmq->aux_record_list[etmq->aux_record_list_idx].aux_size > etmq->buf_len) {
+ // Assume that aux records always equally divide up the aux buffer
+ // so aux_size should never exceed the remaining buffer to decode.
+ ret = -1;
+ goto out;
+ }

/*
* Packets are decoded and added to the decoder's packet queue
@@ -2020,10 +2037,11 @@ static int cs_etm__decode_data_block(struct cs_etm_queue *etmq)
* operations that stop processing are a timestamp packet or a full
* decoder buffer queue.
*/
+ decode_size = etmq->aux_record_list[etmq->aux_record_list_idx].aux_size;
ret = cs_etm_decoder__process_data_block(etmq->decoder,
etmq->offset,
&etmq->buf[etmq->buf_used],
- etmq->buf_len,
+ decode_size,
&processed);
if (ret)
goto out;
@@ -2031,7 +2049,7 @@ static int cs_etm__decode_data_block(struct cs_etm_queue *etmq)
etmq->offset += processed;
etmq->buf_used += processed;
etmq->buf_len -= processed;
-
+ etmq->aux_record_list[etmq->aux_record_list_idx].aux_size -= processed;
out:
return ret;
}
@@ -2247,6 +2265,12 @@ static int cs_etm__process_queues(struct cs_etm_auxtrace *etm)
* if need be.
*/
refetch:
+ if (etmq->aux_record_list[etmq->aux_record_list_idx].aux_size <= 0) {
+ etmq->aux_record_list_idx++;
+ ret = cs_etm_decoder__reset(etmq->decoder);
+ if (ret)
+ return ret;
+ }
ret = cs_etm__get_data_block(etmq);
if (ret < 0)
goto out;
--
2.28.0