|
@@ -0,0 +1,213 @@
|
|
|
+/*
|
|
|
+ * 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 "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 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)
|
|
|
+{
|
|
|
+ struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
|
|
|
+ struct cs_etm_auxtrace,
|
|
|
+ auxtrace);
|
|
|
+ cs_etm__free_events(session);
|
|
|
+ session->auxtrace = NULL;
|
|
|
+
|
|
|
+ zfree(&aux);
|
|
|
+}
|
|
|
+
|
|
|
+static int cs_etm__process_event(struct perf_session *session,
|
|
|
+ union perf_event *event,
|
|
|
+ struct perf_sample *sample,
|
|
|
+ struct perf_tool *tool)
|
|
|
+{
|
|
|
+ (void) session;
|
|
|
+ (void) event;
|
|
|
+ (void) sample;
|
|
|
+ (void) tool;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int cs_etm__process_auxtrace_event(struct perf_session *session,
|
|
|
+ union perf_event *event,
|
|
|
+ struct perf_tool *tool)
|
|
|
+{
|
|
|
+ (void) session;
|
|
|
+ (void) event;
|
|
|
+ (void) tool;
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+ int event_header_size = sizeof(struct perf_event_header);
|
|
|
+ int info_header_size;
|
|
|
+ int total_size = auxtrace_info->header.size;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
+
|
|
|
+ etm = zalloc(sizeof(*etm));
|
|
|
+
|
|
|
+ if (!etm)
|
|
|
+ err = -ENOMEM;
|
|
|
+
|
|
|
+ err = auxtrace_queues__init(&etm->queues);
|
|
|
+ if (err)
|
|
|
+ goto err_free_etm;
|
|
|
+
|
|
|
+ etm->session = session;
|
|
|
+ etm->machine = &session->machines.host;
|
|
|
+
|
|
|
+ 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)
|
|
|
+ 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);
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|