Commit 21fe8dc1 authored by Mathieu Poirier's avatar Mathieu Poirier Committed by Arnaldo Carvalho de Melo

perf cs-etm: Add support for CPU-wide trace scenarios

Add support for CPU-wide trace scenarios by correlating range packets
with timestamp packets.  That way range packets received on different
ETMQ/traceID channels can be processed and synthesized in chronological
order.
Signed-off-by: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Tested-by: default avatarLeo Yan <leo.yan@linaro.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lkml.kernel.org/r/20190524173508.29044-18-mathieu.poirier@linaro.orgSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 675f302f
......@@ -90,12 +90,26 @@ struct cs_etm_queue {
};
static int cs_etm__update_queues(struct cs_etm_auxtrace *etm);
static int cs_etm__process_queues(struct cs_etm_auxtrace *etm);
static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
pid_t tid);
static int cs_etm__get_data_block(struct cs_etm_queue *etmq);
static int cs_etm__decode_data_block(struct cs_etm_queue *etmq);
/* PTMs ETMIDR [11:8] set to b0011 */
#define ETMIDR_PTM_VERSION 0x00000300
/*
* A struct auxtrace_heap_item only has a queue_nr and a timestamp to
* work with. One option is to modify to auxtrace_heap_XYZ() API or simply
* encode the etm queue number as the upper 16 bit and the channel as
* the lower 16 bit.
*/
#define TO_CS_QUEUE_NR(queue_nr, trace_id_chan) \
(queue_nr << 16 | trace_chan_id)
#define TO_QUEUE_NR(cs_queue_nr) (cs_queue_nr >> 16)
#define TO_TRACE_CHAN_ID(cs_queue_nr) (cs_queue_nr & 0x0000ffff)
static u32 cs_etm__get_v7_protocol_version(u32 etmidr)
{
etmidr &= ETMIDR_PTM_VERSION;
......@@ -147,6 +161,29 @@ void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq,
etmq->pending_timestamp = trace_chan_id;
}
static u64 cs_etm__etmq_get_timestamp(struct cs_etm_queue *etmq,
u8 *trace_chan_id)
{
struct cs_etm_packet_queue *packet_queue;
if (!etmq->pending_timestamp)
return 0;
if (trace_chan_id)
*trace_chan_id = etmq->pending_timestamp;
packet_queue = cs_etm__etmq_get_packet_queue(etmq,
etmq->pending_timestamp);
if (!packet_queue)
return 0;
/* Acknowledge pending status */
etmq->pending_timestamp = 0;
/* See function cs_etm_decoder__do_{hard|soft}_timestamp() */
return packet_queue->timestamp;
}
static void cs_etm__clear_packet_queue(struct cs_etm_packet_queue *queue)
{
int i;
......@@ -171,6 +208,20 @@ static void cs_etm__clear_packet_queue(struct cs_etm_packet_queue *queue)
}
}
static void cs_etm__clear_all_packet_queues(struct cs_etm_queue *etmq)
{
int idx;
struct int_node *inode;
struct cs_etm_traceid_queue *tidq;
struct intlist *traceid_queues_list = etmq->traceid_queues_list;
intlist__for_each_entry(inode, traceid_queues_list) {
idx = (int)(intptr_t)inode->priv;
tidq = etmq->traceid_queues[idx];
cs_etm__clear_packet_queue(&tidq->packet_queue);
}
}
static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
struct cs_etm_traceid_queue *tidq,
u8 trace_chan_id)
......@@ -458,15 +509,15 @@ static int cs_etm__flush_events(struct perf_session *session,
if (!tool->ordered_events)
return -EINVAL;
if (!etm->timeless_decoding)
return -EINVAL;
ret = cs_etm__update_queues(etm);
if (ret < 0)
return ret;
return cs_etm__process_timeless_queues(etm, -1);
if (etm->timeless_decoding)
return cs_etm__process_timeless_queues(etm, -1);
return cs_etm__process_queues(etm);
}
static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
......@@ -685,6 +736,9 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
unsigned int queue_nr)
{
int ret = 0;
unsigned int cs_queue_nr;
u8 trace_chan_id;
u64 timestamp;
struct cs_etm_queue *etmq = queue->priv;
if (list_empty(&queue->head) || etmq)
......@@ -702,6 +756,67 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
etmq->queue_nr = queue_nr;
etmq->offset = 0;
if (etm->timeless_decoding)
goto out;
/*
* 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
* it can be correlated with execution on other CPUs. So we get a
* handle on the beginning of traces and decode until we find a
* 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)
goto out;
/*
* 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)
goto out;
/*
* 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)
break;
/*
* 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_etm__clear_all_packet_queues(etmq);
}
/*
* 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_queue_nr = TO_CS_QUEUE_NR(queue_nr, trace_id_chan);
ret = auxtrace_heap__add(&etm->heap, cs_queue_nr, timestamp);
out:
return ret;
}
......@@ -1846,6 +1961,28 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq,
return ret;
}
static void cs_etm__clear_all_traceid_queues(struct cs_etm_queue *etmq)
{
int idx;
struct int_node *inode;
struct cs_etm_traceid_queue *tidq;
struct intlist *traceid_queues_list = etmq->traceid_queues_list;
intlist__for_each_entry(inode, traceid_queues_list) {
idx = (int)(intptr_t)inode->priv;
tidq = etmq->traceid_queues[idx];
/* Ignore return value */
cs_etm__process_traceid_queue(etmq, tidq);
/*
* Generate an instruction sample with the remaining
* branchstack entries.
*/
cs_etm__flush(etmq, tidq);
}
}
static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
{
int err = 0;
......@@ -1913,6 +2050,105 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
return 0;
}
static int cs_etm__process_queues(struct cs_etm_auxtrace *etm)
{
int ret = 0;
unsigned int cs_queue_nr, queue_nr;
u8 trace_chan_id;
u64 timestamp;
struct auxtrace_queue *queue;
struct cs_etm_queue *etmq;
struct cs_etm_traceid_queue *tidq;
while (1) {
if (!etm->heap.heap_cnt)
goto out;
/* Take the entry at the top of the min heap */
cs_queue_nr = etm->heap.heap_array[0].queue_nr;
queue_nr = TO_QUEUE_NR(cs_queue_nr);
trace_chan_id = TO_TRACE_CHAN_ID(cs_queue_nr);
queue = &etm->queues.queue_array[queue_nr];
etmq = queue->priv;
/*
* Remove the top entry from the heap since we are about
* to process it.
*/
auxtrace_heap__pop(&etm->heap);
tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
if (!tidq) {
/*
* No traceID queue has been allocated for this traceID,
* which means something somewhere went very wrong. No
* other choice than simply exit.
*/
ret = -EINVAL;
goto out;
}
/*
* Packets associated with this timestamp are already in
* the etmq's traceID queue, so process them.
*/
ret = cs_etm__process_traceid_queue(etmq, tidq);
if (ret < 0)
goto out;
/*
* Packets for this timestamp have been processed, time to
* move on to the next timestamp, fetching a new auxtrace_buffer
* if need be.
*/
refetch:
ret = cs_etm__get_data_block(etmq);
if (ret < 0)
goto out;
/*
* No more auxtrace_buffers to process in this etmq, simply
* move on to another entry in the auxtrace_heap.
*/
if (!ret)
continue;
ret = cs_etm__decode_data_block(etmq);
if (ret)
goto out;
timestamp = cs_etm__etmq_get_timestamp(etmq, &trace_chan_id);
if (!timestamp) {
/*
* Function cs_etm__decode_data_block() returns when
* there is no more traces to decode in the current
* auxtrace_buffer OR when a timestamp has been
* encountered on any of the traceID queues. Since we
* did not get a timestamp, there is no more traces to
* process in this auxtrace_buffer. As such empty and
* flush all traceID queues.
*/
cs_etm__clear_all_traceid_queues(etmq);
/* Fetch another auxtrace_buffer for this etmq */
goto refetch;
}
/*
* Add to the min heap the timestamp for packets that have
* just been decoded. They will be processed and synthesized
* during the next call to cs_etm__process_traceid_queue() for
* this queue/traceID.
*/
cs_queue_nr = TO_CS_QUEUE_NR(queue_nr, trace_chan_id);
ret = auxtrace_heap__add(&etm->heap, cs_queue_nr, timestamp);
}
out:
return ret;
}
static int cs_etm__process_itrace_start(struct cs_etm_auxtrace *etm,
union perf_event *event)
{
......@@ -1991,9 +2227,6 @@ static int cs_etm__process_event(struct perf_session *session,
return -EINVAL;
}
if (!etm->timeless_decoding)
return -EINVAL;
if (sample->time && (sample->time != (u64) -1))
timestamp = sample->time;
else
......@@ -2005,7 +2238,8 @@ static int cs_etm__process_event(struct perf_session *session,
return err;
}
if (event->header.type == PERF_RECORD_EXIT)
if (etm->timeless_decoding &&
event->header.type == PERF_RECORD_EXIT)
return cs_etm__process_timeless_queues(etm,
event->fork.tid);
......@@ -2014,6 +2248,10 @@ static int cs_etm__process_event(struct perf_session *session,
else if (event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)
return cs_etm__process_switch_cpu_wide(etm, event);
if (!etm->timeless_decoding &&
event->header.type == PERF_RECORD_AUX)
return cs_etm__process_queues(etm);
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment