mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-16 07:24:39 +08:00
20d9c478b0
Add functionatlity to setup trace queues so that traces associated with CoreSight auxtrace events found in the perf.data file can be classified properly. The decoder and memory callback associated with each queue are then used to decode the traces that have been assigned to that queue. Co-authored-by: Tor Jeremiassen <tor@ti.com> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org> Acked-by: Jiri Olsa <jolsa@redhat.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Kim Phillips <kim.phillips@arm.com> Cc: Mike Leach <mike.leach@arm.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Suzuki Poulouse <suzuki.poulose@arm.com> Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1516211539-5166-8-git-send-email-mathieu.poirier@linaro.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
702 lines
17 KiB
C
702 lines
17 KiB
C
/*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*
|
|
* Copyright(C) 2015-2018 Linaro Limited.
|
|
*
|
|
* Author: Tor Jeremiassen <tor@ti.com>
|
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/err.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/log2.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "auxtrace.h"
|
|
#include "color.h"
|
|
#include "cs-etm.h"
|
|
#include "cs-etm-decoder/cs-etm-decoder.h"
|
|
#include "debug.h"
|
|
#include "evlist.h"
|
|
#include "intlist.h"
|
|
#include "machine.h"
|
|
#include "map.h"
|
|
#include "perf.h"
|
|
#include "thread.h"
|
|
#include "thread_map.h"
|
|
#include "thread-stack.h"
|
|
#include "util.h"
|
|
|
|
#define MAX_TIMESTAMP (~0ULL)
|
|
|
|
struct cs_etm_auxtrace {
|
|
struct auxtrace auxtrace;
|
|
struct auxtrace_queues queues;
|
|
struct auxtrace_heap heap;
|
|
struct itrace_synth_opts synth_opts;
|
|
struct perf_session *session;
|
|
struct machine *machine;
|
|
struct thread *unknown_thread;
|
|
|
|
u8 timeless_decoding;
|
|
u8 snapshot_mode;
|
|
u8 data_queued;
|
|
u8 sample_branches;
|
|
|
|
int num_cpu;
|
|
u32 auxtrace_type;
|
|
u64 branches_sample_type;
|
|
u64 branches_id;
|
|
u64 **metadata;
|
|
u64 kernel_start;
|
|
unsigned int pmu_type;
|
|
};
|
|
|
|
struct cs_etm_queue {
|
|
struct cs_etm_auxtrace *etm;
|
|
struct thread *thread;
|
|
struct cs_etm_decoder *decoder;
|
|
struct auxtrace_buffer *buffer;
|
|
const struct cs_etm_state *state;
|
|
union perf_event *event_buf;
|
|
unsigned int queue_nr;
|
|
pid_t pid, tid;
|
|
int cpu;
|
|
u64 time;
|
|
u64 timestamp;
|
|
u64 offset;
|
|
};
|
|
|
|
static void cs_etm__packet_dump(const char *pkt_string)
|
|
{
|
|
const char *color = PERF_COLOR_BLUE;
|
|
int len = strlen(pkt_string);
|
|
|
|
if (len && (pkt_string[len-1] == '\n'))
|
|
color_fprintf(stdout, color, " %s", pkt_string);
|
|
else
|
|
color_fprintf(stdout, color, " %s\n", pkt_string);
|
|
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void cs_etm__dump_event(struct cs_etm_auxtrace *etm,
|
|
struct auxtrace_buffer *buffer)
|
|
{
|
|
int i, ret;
|
|
const char *color = PERF_COLOR_BLUE;
|
|
struct cs_etm_decoder_params d_params;
|
|
struct cs_etm_trace_params *t_params;
|
|
struct cs_etm_decoder *decoder;
|
|
size_t buffer_used = 0;
|
|
|
|
fprintf(stdout, "\n");
|
|
color_fprintf(stdout, color,
|
|
". ... CoreSight ETM Trace data: size %zu bytes\n",
|
|
buffer->size);
|
|
|
|
/* Use metadata to fill in trace parameters for trace decoder */
|
|
t_params = zalloc(sizeof(*t_params) * etm->num_cpu);
|
|
for (i = 0; i < etm->num_cpu; i++) {
|
|
t_params[i].protocol = CS_ETM_PROTO_ETMV4i;
|
|
t_params[i].etmv4.reg_idr0 = etm->metadata[i][CS_ETMV4_TRCIDR0];
|
|
t_params[i].etmv4.reg_idr1 = etm->metadata[i][CS_ETMV4_TRCIDR1];
|
|
t_params[i].etmv4.reg_idr2 = etm->metadata[i][CS_ETMV4_TRCIDR2];
|
|
t_params[i].etmv4.reg_idr8 = etm->metadata[i][CS_ETMV4_TRCIDR8];
|
|
t_params[i].etmv4.reg_configr =
|
|
etm->metadata[i][CS_ETMV4_TRCCONFIGR];
|
|
t_params[i].etmv4.reg_traceidr =
|
|
etm->metadata[i][CS_ETMV4_TRCTRACEIDR];
|
|
}
|
|
|
|
/* Set decoder parameters to simply print the trace packets */
|
|
d_params.packet_printer = cs_etm__packet_dump;
|
|
d_params.operation = CS_ETM_OPERATION_PRINT;
|
|
d_params.formatted = true;
|
|
d_params.fsyncs = false;
|
|
d_params.hsyncs = false;
|
|
d_params.frame_aligned = true;
|
|
|
|
decoder = cs_etm_decoder__new(etm->num_cpu, &d_params, t_params);
|
|
|
|
zfree(&t_params);
|
|
|
|
if (!decoder)
|
|
return;
|
|
do {
|
|
size_t consumed;
|
|
|
|
ret = cs_etm_decoder__process_data_block(
|
|
decoder, buffer->offset,
|
|
&((u8 *)buffer->data)[buffer_used],
|
|
buffer->size - buffer_used, &consumed);
|
|
if (ret)
|
|
break;
|
|
|
|
buffer_used += consumed;
|
|
} while (buffer_used < buffer->size);
|
|
|
|
cs_etm_decoder__free(decoder);
|
|
}
|
|
|
|
static int cs_etm__flush_events(struct perf_session *session,
|
|
struct perf_tool *tool)
|
|
{
|
|
(void) session;
|
|
(void) tool;
|
|
return 0;
|
|
}
|
|
|
|
static void cs_etm__free_queue(void *priv)
|
|
{
|
|
struct cs_etm_queue *etmq = priv;
|
|
|
|
free(etmq);
|
|
}
|
|
|
|
static void cs_etm__free_events(struct perf_session *session)
|
|
{
|
|
unsigned int i;
|
|
struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
|
|
struct cs_etm_auxtrace,
|
|
auxtrace);
|
|
struct auxtrace_queues *queues = &aux->queues;
|
|
|
|
for (i = 0; i < queues->nr_queues; i++) {
|
|
cs_etm__free_queue(queues->queue_array[i].priv);
|
|
queues->queue_array[i].priv = NULL;
|
|
}
|
|
|
|
auxtrace_queues__free(queues);
|
|
}
|
|
|
|
static void cs_etm__free(struct perf_session *session)
|
|
{
|
|
int i;
|
|
struct int_node *inode, *tmp;
|
|
struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
|
|
struct cs_etm_auxtrace,
|
|
auxtrace);
|
|
cs_etm__free_events(session);
|
|
session->auxtrace = NULL;
|
|
|
|
/* First remove all traceID/CPU# nodes for the RB tree */
|
|
intlist__for_each_entry_safe(inode, tmp, traceid_list)
|
|
intlist__remove(traceid_list, inode);
|
|
/* Then the RB tree itself */
|
|
intlist__delete(traceid_list);
|
|
|
|
for (i = 0; i < aux->num_cpu; i++)
|
|
zfree(&aux->metadata[i]);
|
|
|
|
zfree(&aux->metadata);
|
|
zfree(&aux);
|
|
}
|
|
|
|
static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u64 address,
|
|
size_t size, u8 *buffer)
|
|
{
|
|
u8 cpumode;
|
|
u64 offset;
|
|
int len;
|
|
struct thread *thread;
|
|
struct machine *machine;
|
|
struct addr_location al;
|
|
|
|
if (!etmq)
|
|
return -1;
|
|
|
|
machine = etmq->etm->machine;
|
|
if (address >= etmq->etm->kernel_start)
|
|
cpumode = PERF_RECORD_MISC_KERNEL;
|
|
else
|
|
cpumode = PERF_RECORD_MISC_USER;
|
|
|
|
thread = etmq->thread;
|
|
if (!thread) {
|
|
if (cpumode != PERF_RECORD_MISC_KERNEL)
|
|
return -EINVAL;
|
|
thread = etmq->etm->unknown_thread;
|
|
}
|
|
|
|
thread__find_addr_map(thread, cpumode, MAP__FUNCTION, address, &al);
|
|
|
|
if (!al.map || !al.map->dso)
|
|
return 0;
|
|
|
|
if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR &&
|
|
dso__data_status_seen(al.map->dso, DSO_DATA_STATUS_SEEN_ITRACE))
|
|
return 0;
|
|
|
|
offset = al.map->map_ip(al.map, address);
|
|
|
|
map__load(al.map);
|
|
|
|
len = dso__data_read_offset(al.map->dso, machine, offset, buffer, size);
|
|
|
|
if (len <= 0)
|
|
return 0;
|
|
|
|
return len;
|
|
}
|
|
|
|
static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm,
|
|
unsigned int queue_nr)
|
|
{
|
|
int i;
|
|
struct cs_etm_decoder_params d_params;
|
|
struct cs_etm_trace_params *t_params;
|
|
struct cs_etm_queue *etmq;
|
|
|
|
etmq = zalloc(sizeof(*etmq));
|
|
if (!etmq)
|
|
return NULL;
|
|
|
|
etmq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
|
|
if (!etmq->event_buf)
|
|
goto out_free;
|
|
|
|
etmq->etm = etm;
|
|
etmq->queue_nr = queue_nr;
|
|
etmq->pid = -1;
|
|
etmq->tid = -1;
|
|
etmq->cpu = -1;
|
|
|
|
/* Use metadata to fill in trace parameters for trace decoder */
|
|
t_params = zalloc(sizeof(*t_params) * etm->num_cpu);
|
|
|
|
if (!t_params)
|
|
goto out_free;
|
|
|
|
for (i = 0; i < etm->num_cpu; i++) {
|
|
t_params[i].protocol = CS_ETM_PROTO_ETMV4i;
|
|
t_params[i].etmv4.reg_idr0 = etm->metadata[i][CS_ETMV4_TRCIDR0];
|
|
t_params[i].etmv4.reg_idr1 = etm->metadata[i][CS_ETMV4_TRCIDR1];
|
|
t_params[i].etmv4.reg_idr2 = etm->metadata[i][CS_ETMV4_TRCIDR2];
|
|
t_params[i].etmv4.reg_idr8 = etm->metadata[i][CS_ETMV4_TRCIDR8];
|
|
t_params[i].etmv4.reg_configr =
|
|
etm->metadata[i][CS_ETMV4_TRCCONFIGR];
|
|
t_params[i].etmv4.reg_traceidr =
|
|
etm->metadata[i][CS_ETMV4_TRCTRACEIDR];
|
|
}
|
|
|
|
/* Set decoder parameters to simply print the trace packets */
|
|
d_params.packet_printer = cs_etm__packet_dump;
|
|
d_params.operation = CS_ETM_OPERATION_DECODE;
|
|
d_params.formatted = true;
|
|
d_params.fsyncs = false;
|
|
d_params.hsyncs = false;
|
|
d_params.frame_aligned = true;
|
|
d_params.data = etmq;
|
|
|
|
etmq->decoder = cs_etm_decoder__new(etm->num_cpu, &d_params, t_params);
|
|
|
|
zfree(&t_params);
|
|
|
|
if (!etmq->decoder)
|
|
goto out_free;
|
|
|
|
/*
|
|
* Register a function to handle all memory accesses required by
|
|
* the trace decoder library.
|
|
*/
|
|
if (cs_etm_decoder__add_mem_access_cb(etmq->decoder,
|
|
0x0L, ((u64) -1L),
|
|
cs_etm__mem_access))
|
|
goto out_free_decoder;
|
|
|
|
etmq->offset = 0;
|
|
|
|
return etmq;
|
|
|
|
out_free_decoder:
|
|
cs_etm_decoder__free(etmq->decoder);
|
|
out_free:
|
|
zfree(&etmq->event_buf);
|
|
free(etmq);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
|
|
struct auxtrace_queue *queue,
|
|
unsigned int queue_nr)
|
|
{
|
|
struct cs_etm_queue *etmq = queue->priv;
|
|
|
|
if (list_empty(&queue->head) || etmq)
|
|
return 0;
|
|
|
|
etmq = cs_etm__alloc_queue(etm, queue_nr);
|
|
|
|
if (!etmq)
|
|
return -ENOMEM;
|
|
|
|
queue->priv = etmq;
|
|
|
|
if (queue->cpu != -1)
|
|
etmq->cpu = queue->cpu;
|
|
|
|
etmq->tid = queue->tid;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cs_etm__setup_queues(struct cs_etm_auxtrace *etm)
|
|
{
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
for (i = 0; i < etm->queues.nr_queues; i++) {
|
|
ret = cs_etm__setup_queue(etm, &etm->queues.queue_array[i], i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cs_etm__update_queues(struct cs_etm_auxtrace *etm)
|
|
{
|
|
if (etm->queues.new_data) {
|
|
etm->queues.new_data = false;
|
|
return cs_etm__setup_queues(etm);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cs_etm__process_event(struct perf_session *session,
|
|
union perf_event *event,
|
|
struct perf_sample *sample,
|
|
struct perf_tool *tool)
|
|
{
|
|
int err = 0;
|
|
u64 timestamp;
|
|
struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
|
|
struct cs_etm_auxtrace,
|
|
auxtrace);
|
|
|
|
/* Keep compiler happy */
|
|
(void)event;
|
|
|
|
if (dump_trace)
|
|
return 0;
|
|
|
|
if (!tool->ordered_events) {
|
|
pr_err("CoreSight ETM Trace requires ordered events\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!etm->timeless_decoding)
|
|
return -EINVAL;
|
|
|
|
if (sample->time && (sample->time != (u64) -1))
|
|
timestamp = sample->time;
|
|
else
|
|
timestamp = 0;
|
|
|
|
if (timestamp || etm->timeless_decoding) {
|
|
err = cs_etm__update_queues(etm);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cs_etm__process_auxtrace_event(struct perf_session *session,
|
|
union perf_event *event,
|
|
struct perf_tool *tool __maybe_unused)
|
|
{
|
|
struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
|
|
struct cs_etm_auxtrace,
|
|
auxtrace);
|
|
if (!etm->data_queued) {
|
|
struct auxtrace_buffer *buffer;
|
|
off_t data_offset;
|
|
int fd = perf_data__fd(session->data);
|
|
bool is_pipe = perf_data__is_pipe(session->data);
|
|
int err;
|
|
|
|
if (is_pipe)
|
|
data_offset = 0;
|
|
else {
|
|
data_offset = lseek(fd, 0, SEEK_CUR);
|
|
if (data_offset == -1)
|
|
return -errno;
|
|
}
|
|
|
|
err = auxtrace_queues__add_event(&etm->queues, session,
|
|
event, data_offset, &buffer);
|
|
if (err)
|
|
return err;
|
|
|
|
if (dump_trace)
|
|
if (auxtrace_buffer__get_data(buffer, fd)) {
|
|
cs_etm__dump_event(etm, buffer);
|
|
auxtrace_buffer__put_data(buffer);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool cs_etm__is_timeless_decoding(struct cs_etm_auxtrace *etm)
|
|
{
|
|
struct perf_evsel *evsel;
|
|
struct perf_evlist *evlist = etm->session->evlist;
|
|
bool timeless_decoding = true;
|
|
|
|
/*
|
|
* Circle through the list of event and complain if we find one
|
|
* with the time bit set.
|
|
*/
|
|
evlist__for_each_entry(evlist, evsel) {
|
|
if ((evsel->attr.sample_type & PERF_SAMPLE_TIME))
|
|
timeless_decoding = false;
|
|
}
|
|
|
|
return timeless_decoding;
|
|
}
|
|
|
|
static const char * const cs_etm_global_header_fmts[] = {
|
|
[CS_HEADER_VERSION_0] = " Header version %llx\n",
|
|
[CS_PMU_TYPE_CPUS] = " PMU type/num cpus %llx\n",
|
|
[CS_ETM_SNAPSHOT] = " Snapshot %llx\n",
|
|
};
|
|
|
|
static const char * const cs_etm_priv_fmts[] = {
|
|
[CS_ETM_MAGIC] = " Magic number %llx\n",
|
|
[CS_ETM_CPU] = " CPU %lld\n",
|
|
[CS_ETM_ETMCR] = " ETMCR %llx\n",
|
|
[CS_ETM_ETMTRACEIDR] = " ETMTRACEIDR %llx\n",
|
|
[CS_ETM_ETMCCER] = " ETMCCER %llx\n",
|
|
[CS_ETM_ETMIDR] = " ETMIDR %llx\n",
|
|
};
|
|
|
|
static const char * const cs_etmv4_priv_fmts[] = {
|
|
[CS_ETM_MAGIC] = " Magic number %llx\n",
|
|
[CS_ETM_CPU] = " CPU %lld\n",
|
|
[CS_ETMV4_TRCCONFIGR] = " TRCCONFIGR %llx\n",
|
|
[CS_ETMV4_TRCTRACEIDR] = " TRCTRACEIDR %llx\n",
|
|
[CS_ETMV4_TRCIDR0] = " TRCIDR0 %llx\n",
|
|
[CS_ETMV4_TRCIDR1] = " TRCIDR1 %llx\n",
|
|
[CS_ETMV4_TRCIDR2] = " TRCIDR2 %llx\n",
|
|
[CS_ETMV4_TRCIDR8] = " TRCIDR8 %llx\n",
|
|
[CS_ETMV4_TRCAUTHSTATUS] = " TRCAUTHSTATUS %llx\n",
|
|
};
|
|
|
|
static void cs_etm__print_auxtrace_info(u64 *val, int num)
|
|
{
|
|
int i, j, cpu = 0;
|
|
|
|
for (i = 0; i < CS_HEADER_VERSION_0_MAX; i++)
|
|
fprintf(stdout, cs_etm_global_header_fmts[i], val[i]);
|
|
|
|
for (i = CS_HEADER_VERSION_0_MAX; cpu < num; cpu++) {
|
|
if (val[i] == __perf_cs_etmv3_magic)
|
|
for (j = 0; j < CS_ETM_PRIV_MAX; j++, i++)
|
|
fprintf(stdout, cs_etm_priv_fmts[j], val[i]);
|
|
else if (val[i] == __perf_cs_etmv4_magic)
|
|
for (j = 0; j < CS_ETMV4_PRIV_MAX; j++, i++)
|
|
fprintf(stdout, cs_etmv4_priv_fmts[j], val[i]);
|
|
else
|
|
/* failure.. return */
|
|
return;
|
|
}
|
|
}
|
|
|
|
int cs_etm__process_auxtrace_info(union perf_event *event,
|
|
struct perf_session *session)
|
|
{
|
|
struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
|
|
struct cs_etm_auxtrace *etm = NULL;
|
|
struct int_node *inode;
|
|
unsigned int pmu_type;
|
|
int event_header_size = sizeof(struct perf_event_header);
|
|
int info_header_size;
|
|
int total_size = auxtrace_info->header.size;
|
|
int priv_size = 0;
|
|
int num_cpu;
|
|
int err = 0, idx = -1;
|
|
int i, j, k;
|
|
u64 *ptr, *hdr = NULL;
|
|
u64 **metadata = NULL;
|
|
|
|
/*
|
|
* sizeof(auxtrace_info_event::type) +
|
|
* sizeof(auxtrace_info_event::reserved) == 8
|
|
*/
|
|
info_header_size = 8;
|
|
|
|
if (total_size < (event_header_size + info_header_size))
|
|
return -EINVAL;
|
|
|
|
priv_size = total_size - event_header_size - info_header_size;
|
|
|
|
/* First the global part */
|
|
ptr = (u64 *) auxtrace_info->priv;
|
|
|
|
/* Look for version '0' of the header */
|
|
if (ptr[0] != 0)
|
|
return -EINVAL;
|
|
|
|
hdr = zalloc(sizeof(*hdr) * CS_HEADER_VERSION_0_MAX);
|
|
if (!hdr)
|
|
return -ENOMEM;
|
|
|
|
/* Extract header information - see cs-etm.h for format */
|
|
for (i = 0; i < CS_HEADER_VERSION_0_MAX; i++)
|
|
hdr[i] = ptr[i];
|
|
num_cpu = hdr[CS_PMU_TYPE_CPUS] & 0xffffffff;
|
|
pmu_type = (unsigned int) ((hdr[CS_PMU_TYPE_CPUS] >> 32) &
|
|
0xffffffff);
|
|
|
|
/*
|
|
* Create an RB tree for traceID-CPU# tuple. Since the conversion has
|
|
* to be made for each packet that gets decoded, optimizing access in
|
|
* anything other than a sequential array is worth doing.
|
|
*/
|
|
traceid_list = intlist__new(NULL);
|
|
if (!traceid_list) {
|
|
err = -ENOMEM;
|
|
goto err_free_hdr;
|
|
}
|
|
|
|
metadata = zalloc(sizeof(*metadata) * num_cpu);
|
|
if (!metadata) {
|
|
err = -ENOMEM;
|
|
goto err_free_traceid_list;
|
|
}
|
|
|
|
/*
|
|
* The metadata is stored in the auxtrace_info section and encodes
|
|
* the configuration of the ARM embedded trace macrocell which is
|
|
* required by the trace decoder to properly decode the trace due
|
|
* to its highly compressed nature.
|
|
*/
|
|
for (j = 0; j < num_cpu; j++) {
|
|
if (ptr[i] == __perf_cs_etmv3_magic) {
|
|
metadata[j] = zalloc(sizeof(*metadata[j]) *
|
|
CS_ETM_PRIV_MAX);
|
|
if (!metadata[j]) {
|
|
err = -ENOMEM;
|
|
goto err_free_metadata;
|
|
}
|
|
for (k = 0; k < CS_ETM_PRIV_MAX; k++)
|
|
metadata[j][k] = ptr[i + k];
|
|
|
|
/* The traceID is our handle */
|
|
idx = metadata[j][CS_ETM_ETMTRACEIDR];
|
|
i += CS_ETM_PRIV_MAX;
|
|
} else if (ptr[i] == __perf_cs_etmv4_magic) {
|
|
metadata[j] = zalloc(sizeof(*metadata[j]) *
|
|
CS_ETMV4_PRIV_MAX);
|
|
if (!metadata[j]) {
|
|
err = -ENOMEM;
|
|
goto err_free_metadata;
|
|
}
|
|
for (k = 0; k < CS_ETMV4_PRIV_MAX; k++)
|
|
metadata[j][k] = ptr[i + k];
|
|
|
|
/* The traceID is our handle */
|
|
idx = metadata[j][CS_ETMV4_TRCTRACEIDR];
|
|
i += CS_ETMV4_PRIV_MAX;
|
|
}
|
|
|
|
/* Get an RB node for this CPU */
|
|
inode = intlist__findnew(traceid_list, idx);
|
|
|
|
/* Something went wrong, no need to continue */
|
|
if (!inode) {
|
|
err = PTR_ERR(inode);
|
|
goto err_free_metadata;
|
|
}
|
|
|
|
/*
|
|
* The node for that CPU should not be taken.
|
|
* Back out if that's the case.
|
|
*/
|
|
if (inode->priv) {
|
|
err = -EINVAL;
|
|
goto err_free_metadata;
|
|
}
|
|
/* All good, associate the traceID with the CPU# */
|
|
inode->priv = &metadata[j][CS_ETM_CPU];
|
|
}
|
|
|
|
/*
|
|
* Each of CS_HEADER_VERSION_0_MAX, CS_ETM_PRIV_MAX and
|
|
* CS_ETMV4_PRIV_MAX mark how many double words are in the
|
|
* global metadata, and each cpu's metadata respectively.
|
|
* The following tests if the correct number of double words was
|
|
* present in the auxtrace info section.
|
|
*/
|
|
if (i * 8 != priv_size) {
|
|
err = -EINVAL;
|
|
goto err_free_metadata;
|
|
}
|
|
|
|
etm = zalloc(sizeof(*etm));
|
|
|
|
if (!etm) {
|
|
err = -ENOMEM;
|
|
goto err_free_metadata;
|
|
}
|
|
|
|
err = auxtrace_queues__init(&etm->queues);
|
|
if (err)
|
|
goto err_free_etm;
|
|
|
|
etm->session = session;
|
|
etm->machine = &session->machines.host;
|
|
|
|
etm->num_cpu = num_cpu;
|
|
etm->pmu_type = pmu_type;
|
|
etm->snapshot_mode = (hdr[CS_ETM_SNAPSHOT] != 0);
|
|
etm->metadata = metadata;
|
|
etm->auxtrace_type = auxtrace_info->type;
|
|
etm->timeless_decoding = cs_etm__is_timeless_decoding(etm);
|
|
|
|
etm->auxtrace.process_event = cs_etm__process_event;
|
|
etm->auxtrace.process_auxtrace_event = cs_etm__process_auxtrace_event;
|
|
etm->auxtrace.flush_events = cs_etm__flush_events;
|
|
etm->auxtrace.free_events = cs_etm__free_events;
|
|
etm->auxtrace.free = cs_etm__free;
|
|
session->auxtrace = &etm->auxtrace;
|
|
|
|
if (dump_trace) {
|
|
cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu);
|
|
return 0;
|
|
}
|
|
|
|
err = auxtrace_queues__process_index(&etm->queues, session);
|
|
if (err)
|
|
goto err_free_queues;
|
|
|
|
etm->data_queued = etm->queues.populated;
|
|
|
|
return 0;
|
|
|
|
err_free_queues:
|
|
auxtrace_queues__free(&etm->queues);
|
|
session->auxtrace = NULL;
|
|
err_free_etm:
|
|
zfree(&etm);
|
|
err_free_metadata:
|
|
/* No need to check @metadata[j], free(NULL) is supported */
|
|
for (j = 0; j < num_cpu; j++)
|
|
free(metadata[j]);
|
|
zfree(&metadata);
|
|
err_free_traceid_list:
|
|
intlist__delete(traceid_list);
|
|
err_free_hdr:
|
|
zfree(&hdr);
|
|
|
|
return -EINVAL;
|
|
}
|