| /* |
| * 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; |
| } |