aboutsummaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/Build2
-rw-r--r--tools/perf/util/auxtrace.c10
-rw-r--r--tools/perf/util/auxtrace.h7
-rw-r--r--tools/perf/util/build-id.c2
-rw-r--r--tools/perf/util/build-id.h1
-rw-r--r--tools/perf/util/cpumap.c30
-rw-r--r--tools/perf/util/cpumap.h32
-rw-r--r--tools/perf/util/cs-etm-decoder/Build7
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c91
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.c503
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.h118
-rw-r--r--tools/perf/util/cs-etm.c1533
-rw-r--r--tools/perf/util/cs-etm.h84
-rw-r--r--tools/perf/util/evlist.c26
-rw-r--r--tools/perf/util/evlist.h3
-rw-r--r--tools/perf/util/evsel.c43
-rw-r--r--tools/perf/util/evsel.h8
-rw-r--r--tools/perf/util/machine.c35
-rw-r--r--tools/perf/util/parse-events.c67
-rw-r--r--tools/perf/util/parse-events.h3
-rw-r--r--tools/perf/util/parse-events.l12
-rw-r--r--tools/perf/util/parse-events.y11
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c2
-rw-r--r--tools/perf/util/session.c40
-rw-r--r--tools/perf/util/symbol-minimal.c2
-rw-r--r--tools/perf/util/symbol.c3
26 files changed, 2579 insertions, 96 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 591b3fe3ed49..a8d806503a45 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -84,6 +84,8 @@ libperf-$(CONFIG_AUXTRACE) += auxtrace.o
libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/
libperf-$(CONFIG_AUXTRACE) += intel-pt.o
libperf-$(CONFIG_AUXTRACE) += intel-bts.o
+libperf-$(CONFIG_AUXTRACE) += cs-etm.o
+libperf-$(CONFIG_AUXTRACE) += cs-etm-decoder/
libperf-y += parse-branch-options.o
libperf-y += parse-regs-options.o
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 7f10430af39c..67551225764e 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -49,6 +49,7 @@
#include "intel-pt.h"
#include "intel-bts.h"
+#include "cs-etm.h"
int auxtrace_mmap__mmap(struct auxtrace_mmap *mm,
struct auxtrace_mmap_params *mp,
@@ -478,10 +479,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
heap_array[last].ordinal);
}
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+ struct perf_evlist *evlist)
{
if (itr)
- return itr->info_priv_size(itr);
+ return itr->info_priv_size(itr, evlist);
return 0;
}
@@ -852,7 +854,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
int err;
pr_debug2("Synthesizing auxtrace information\n");
- priv_size = auxtrace_record__info_priv_size(itr);
+ priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
if (!ev)
return -ENOMEM;
@@ -891,6 +893,8 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
return intel_pt_process_auxtrace_info(event, session);
case PERF_AUXTRACE_INTEL_BTS:
return intel_bts_process_auxtrace_info(event, session);
+ case PERF_AUXTRACE_CS_ETM:
+ return cs_etm__process_auxtrace_info(event, session);
case PERF_AUXTRACE_UNKNOWN:
default:
return -EINVAL;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index b86f90db1352..adb53e7bcabf 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -41,6 +41,7 @@ enum auxtrace_type {
PERF_AUXTRACE_UNKNOWN,
PERF_AUXTRACE_INTEL_PT,
PERF_AUXTRACE_INTEL_BTS,
+ PERF_AUXTRACE_CS_ETM,
};
enum itrace_period_type {
@@ -293,7 +294,8 @@ struct auxtrace_record {
int (*recording_options)(struct auxtrace_record *itr,
struct perf_evlist *evlist,
struct record_opts *opts);
- size_t (*info_priv_size)(struct auxtrace_record *itr);
+ size_t (*info_priv_size)(struct auxtrace_record *itr,
+ struct perf_evlist *evlist);
int (*info_fill)(struct auxtrace_record *itr,
struct perf_session *session,
struct auxtrace_info_event *auxtrace_info,
@@ -429,7 +431,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
int auxtrace_record__options(struct auxtrace_record *itr,
struct perf_evlist *evlist,
struct record_opts *opts);
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+ struct perf_evlist *evlist);
int auxtrace_record__info_fill(struct auxtrace_record *itr,
struct perf_session *session,
struct auxtrace_info_event *auxtrace_info,
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 6a7e273a514a..52d320e922e3 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -145,7 +145,7 @@ static int asnprintf(char **strp, size_t size, const char *fmt, ...)
return ret;
}
-static char *build_id__filename(const char *sbuild_id, char *bf, size_t size)
+char *build_id__filename(const char *sbuild_id, char *bf, size_t size)
{
char *tmp = bf;
int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 27a14a8a945b..eb2c2b6e1dab 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -11,6 +11,7 @@
extern struct perf_tool build_id__mark_dso_hit_ops;
struct dso;
+char *build_id__filename(const char *sbuild_id, char *bf, size_t size);
int build_id__sprintf(const u8 *build_id, int len, char *bf);
int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id);
int filename__sprintf_build_id(const char *pathname, char *sbuild_id);
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 10af1e7524fb..6523e1a8eea5 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -7,6 +7,10 @@
#include <stdlib.h>
#include "asm/bug.h"
+static int max_cpu_num;
+static int max_node_num;
+static int *cpunode_map;
+
static struct cpu_map *cpu_map__default_new(void)
{
struct cpu_map *cpus;
@@ -435,6 +439,32 @@ out:
pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
}
+int cpu__max_node(void)
+{
+ if (unlikely(!max_node_num))
+ set_max_node_num();
+
+ return max_node_num;
+}
+
+int cpu__max_cpu(void)
+{
+ if (unlikely(!max_cpu_num))
+ set_max_cpu_num();
+
+ return max_cpu_num;
+}
+
+int cpu__get_node(int cpu)
+{
+ if (unlikely(cpunode_map == NULL)) {
+ pr_debug("cpu_map not initialized\n");
+ return -1;
+ }
+
+ return cpunode_map[cpu];
+}
+
static int init_cpunode_map(void)
{
int i;
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 85f7772457fa..d6184ba929b6 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -56,37 +56,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
return map ? map->map[0] == -1 : true;
}
-int max_cpu_num;
-int max_node_num;
-int *cpunode_map;
-
int cpu__setup_cpunode_map(void);
-static inline int cpu__max_node(void)
-{
- if (unlikely(!max_node_num))
- pr_debug("cpu_map not initialized\n");
-
- return max_node_num;
-}
-
-static inline int cpu__max_cpu(void)
-{
- if (unlikely(!max_cpu_num))
- pr_debug("cpu_map not initialized\n");
-
- return max_cpu_num;
-}
-
-static inline int cpu__get_node(int cpu)
-{
- if (unlikely(cpunode_map == NULL)) {
- pr_debug("cpu_map not initialized\n");
- return -1;
- }
-
- return cpunode_map[cpu];
-}
+int cpu__max_node(void);
+int cpu__max_cpu(void);
+int cpu__get_node(int cpu);
int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
int (*f)(struct cpu_map *map, int cpu, void *data),
diff --git a/tools/perf/util/cs-etm-decoder/Build b/tools/perf/util/cs-etm-decoder/Build
new file mode 100644
index 000000000000..d4896fec940c
--- /dev/null
+++ b/tools/perf/util/cs-etm-decoder/Build
@@ -0,0 +1,7 @@
+ifeq ($(CSTRACE_PATH),)
+libperf-$(CONFIG_AUXTRACE) += cs-etm-decoder-stub.o
+else
+CFLAGS_cs-etm-decoder.o += -I$(CSTRACE_PATH)/include
+libperf-$(CONFIG_AUXTRACE) += cs-etm-decoder.o
+endif
+
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c
new file mode 100644
index 000000000000..38f2b756fe10
--- /dev/null
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Tor Jeremiassen <tor.jeremiassen@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU GEneral Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+
+#include "cs-etm-decoder.h"
+#include "../util.h"
+
+
+struct cs_etm_decoder
+{
+ void *state;
+ int dummy;
+};
+
+int cs_etm_decoder__flush(struct cs_etm_decoder *decoder)
+{
+ (void) decoder;
+ return -1;
+}
+
+int cs_etm_decoder__add_bin_file(struct cs_etm_decoder *decoder, uint64_t offset, uint64_t address, uint64_t len, const char *fname)
+{
+ (void) decoder;
+ (void) offset;
+ (void) address;
+ (void) len;
+ (void) fname;
+ return -1;
+}
+
+const struct cs_etm_state *cs_etm_decoder__process_data_block(struct cs_etm_decoder *decoder,
+ uint64_t indx,
+ const uint8_t *buf,
+ size_t len,
+ size_t *consumed)
+{
+ (void) decoder;
+ (void) indx;
+ (void) buf;
+ (void) len;
+ (void) consumed;
+ return NULL;
+}
+
+int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *decoder, uint64_t address, uint64_t len, cs_etm_mem_cb_type cb_func)
+{
+ (void) decoder;
+ (void) address;
+ (void) len;
+ (void) cb_func;
+ return -1;
+}
+
+int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder,
+ struct cs_etm_packet *packet)
+{
+ (void) decoder;
+ (void) packet;
+ return -1;
+}
+
+struct cs_etm_decoder *cs_etm_decoder__new(uint32_t num_cpu, struct cs_etm_decoder_params *d_params, struct cs_etm_trace_params t_params[])
+{
+ (void) num_cpu;
+ (void) d_params;
+ (void) t_params;
+ return NULL;
+}
+
+
+void cs_etm_decoder__free(struct cs_etm_decoder *decoder)
+{
+ (void) decoder;
+ return;
+}
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
new file mode 100644
index 000000000000..c6f23d63590d
--- /dev/null
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
@@ -0,0 +1,503 @@
+/*
+ *
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Tor Jeremiassen <tor.jeremiassen@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU GEneral Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <stdlib.h>
+
+#include "../cs-etm.h"
+#include "cs-etm-decoder.h"
+#include "../util.h"
+#include "../util/intlist.h"
+
+#include "c_api/opencsd_c_api.h"
+#include "ocsd_if_types.h"
+#include "etmv4/trc_pkt_types_etmv4.h"
+
+#define MAX_BUFFER 1024
+
+
+
+struct cs_etm_decoder
+{
+ struct cs_etm_state state;
+ dcd_tree_handle_t dcd_tree;
+ void (*packet_printer)(const char *);
+ cs_etm_mem_cb_type mem_access;
+ ocsd_datapath_resp_t prev_return;
+ size_t prev_processed;
+ bool trace_on;
+ bool discontinuity;
+ struct cs_etm_packet packet_buffer[MAX_BUFFER];
+ uint32_t packet_count;
+ uint32_t head;
+ uint32_t tail;
+ uint32_t end_tail;
+};
+
+static uint32_t cs_etm_decoder__mem_access(const void *context,
+ const ocsd_vaddr_t address,
+ const ocsd_mem_space_acc_t mem_space,
+ const uint32_t req_size,
+ uint8_t *buffer)
+{
+ struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
+ (void) mem_space;
+
+ return decoder->mem_access(decoder->state.data,address,req_size,buffer);
+}
+
+static int cs_etm_decoder__gen_etmv4_config(struct cs_etm_trace_params *params,
+ ocsd_etmv4_cfg *config)
+{
+ config->reg_configr = params->reg_configr;
+ config->reg_traceidr = params->reg_traceidr;
+ config->reg_idr0 = params->reg_idr0;
+ config->reg_idr1 = params->reg_idr1;
+ config->reg_idr2 = params->reg_idr2;
+ config->reg_idr8 = params->reg_idr8;
+
+ config->reg_idr9 = 0;
+ config->reg_idr10 = 0;
+ config->reg_idr11 = 0;
+ config->reg_idr12 = 0;
+ config->reg_idr13 = 0;
+ config->arch_ver = ARCH_V8;
+ config->core_prof = profile_CortexA;
+
+ return 0;
+}
+
+static int cs_etm_decoder__flush_packet(struct cs_etm_decoder *decoder)
+{
+ int err = 0;
+
+ if (decoder == NULL) return -1;
+
+ if (decoder->packet_count >= 31) return -1;
+
+ if (decoder->tail != decoder->end_tail) {
+ decoder->tail = (decoder->tail + 1) & (MAX_BUFFER - 1);
+ decoder->packet_count++;
+ }
+
+ return err;
+}
+
+int cs_etm_decoder__flush(struct cs_etm_decoder *decoder)
+{
+ return cs_etm_decoder__flush_packet(decoder);
+}
+
+static int cs_etm_decoder__buffer_packet(struct cs_etm_decoder *decoder,
+ const ocsd_generic_trace_elem *elem,
+ const uint8_t trace_chan_id,
+ enum cs_etm_sample_type sample_type)
+{
+ int err = 0;
+ uint32_t et = 0;
+ struct int_node *inode = NULL;
+
+ if (decoder == NULL) return -1;
+
+ if (decoder->packet_count >= 31) return -1;
+
+ err = cs_etm_decoder__flush_packet(decoder);
+
+ if (err) return err;
+
+ et = decoder->end_tail;
+ /* Search the RB tree for the cpu associated with this traceID */
+ inode = intlist__find(traceid_list, trace_chan_id);
+ if (!inode)
+ return PTR_ERR(inode);
+
+ decoder->packet_buffer[et].sample_type = sample_type;
+ decoder->packet_buffer[et].start_addr = elem->st_addr;
+ decoder->packet_buffer[et].end_addr = elem->en_addr;
+ decoder->packet_buffer[et].exc = false;
+ decoder->packet_buffer[et].exc_ret = false;
+ decoder->packet_buffer[et].cpu = *((int*)inode->priv);
+
+ et = (et + 1) & (MAX_BUFFER - 1);
+
+ decoder->end_tail = et;
+
+ return err;
+}
+
+static int cs_etm_decoder__mark_exception(struct cs_etm_decoder *decoder)
+{
+ int err = 0;
+
+ if (decoder == NULL) return -1;
+
+ decoder->packet_buffer[decoder->end_tail].exc = true;
+
+ return err;
+}
+
+static int cs_etm_decoder__mark_exception_return(struct cs_etm_decoder *decoder)
+{
+ int err = 0;
+
+ if (decoder == NULL) return -1;
+
+ decoder->packet_buffer[decoder->end_tail].exc_ret = true;
+
+ return err;
+}
+
+static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
+ const void *context,
+ const ocsd_trc_index_t indx,
+ const uint8_t trace_chan_id,
+ const ocsd_generic_trace_elem *elem)
+{
+ ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
+ struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
+
+ (void) indx;
+ (void) trace_chan_id;
+
+ switch (elem->elem_type) {
+ case OCSD_GEN_TRC_ELEM_UNKNOWN:
+ break;
+ case OCSD_GEN_TRC_ELEM_NO_SYNC:
+ decoder->trace_on = false;
+ break;
+ case OCSD_GEN_TRC_ELEM_TRACE_ON:
+ decoder->trace_on = true;
+ break;
+ //case OCSD_GEN_TRC_ELEM_TRACE_OVERFLOW:
+ //decoder->trace_on = false;
+ //decoder->discontinuity = true;
+ //break;
+ case OCSD_GEN_TRC_ELEM_INSTR_RANGE:
+ cs_etm_decoder__buffer_packet(decoder,elem,
+ trace_chan_id, CS_ETM_RANGE);
+ resp = OCSD_RESP_WAIT;
+ break;
+ case OCSD_GEN_TRC_ELEM_EXCEPTION:
+ cs_etm_decoder__mark_exception(decoder);
+ break;
+ case OCSD_GEN_TRC_ELEM_EXCEPTION_RET:
+ cs_etm_decoder__mark_exception_return(decoder);
+ break;
+ case OCSD_GEN_TRC_ELEM_PE_CONTEXT:
+ case OCSD_GEN_TRC_ELEM_EO_TRACE:
+ case OCSD_GEN_TRC_ELEM_ADDR_NACC:
+ case OCSD_GEN_TRC_ELEM_TIMESTAMP:
+ case OCSD_GEN_TRC_ELEM_CYCLE_COUNT:
+ //case OCSD_GEN_TRC_ELEM_TS_WITH_CC:
+ case OCSD_GEN_TRC_ELEM_EVENT:
+ default:
+ break;
+ }
+
+ decoder->state.err = 0;
+
+ return resp;
+}
+
+static ocsd_datapath_resp_t cs_etm_decoder__etmv4i_packet_printer(
+ const void *context,
+ const ocsd_datapath_op_t op,
+ const ocsd_trc_index_t indx,
+ const ocsd_etmv4_i_pkt *pkt)
+{
+ const size_t PACKET_STR_LEN = 1024;
+ ocsd_datapath_resp_t ret = OCSD_RESP_CONT;
+ char packet_str[PACKET_STR_LEN];
+ size_t offset;
+ struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
+
+ sprintf(packet_str,"%ld: ", (long int) indx);
+ offset = strlen(packet_str);
+
+ switch(op) {
+ case OCSD_OP_DATA:
+ if (ocsd_pkt_str(OCSD_PROTOCOL_ETMV4I,
+ (void *)pkt,
+ packet_str+offset,
+ PACKET_STR_LEN-offset) != OCSD_OK)
+ ret = OCSD_RESP_FATAL_INVALID_PARAM;
+ break;
+ case OCSD_OP_EOT:
+ sprintf(packet_str,"**** END OF TRACE ****\n");
+ break;
+ case OCSD_OP_FLUSH:
+ case OCSD_OP_RESET:
+ default:
+ break;
+ }
+
+ decoder->packet_printer(packet_str);
+
+ return ret;
+}
+
+static int cs_etm_decoder__create_etmv4i_packet_printer(struct cs_etm_decoder_params *d_params, struct cs_etm_trace_params *t_params,
+
+ struct cs_etm_decoder *decoder)
+{
+ ocsd_etmv4_cfg trace_config;
+ int ret = 0;
+
+ if (d_params->packet_printer == NULL)
+ return -1;
+
+ ret = cs_etm_decoder__gen_etmv4_config(t_params,&trace_config);
+
+ if (ret != 0)
+ return -1;
+
+ decoder->packet_printer = d_params->packet_printer;
+
+ ret = ocsd_dt_create_etmv4i_pkt_proc(decoder->dcd_tree,
+ &trace_config,
+ cs_etm_decoder__etmv4i_packet_printer,
+ decoder);
+
+ return ret;
+}
+
+static int cs_etm_decoder__create_etmv4i_packet_decoder(struct cs_etm_decoder_params *d_params, struct cs_etm_trace_params *t_params,
+ struct cs_etm_decoder *decoder)
+{
+ ocsd_etmv4_cfg trace_config;
+ int ret = 0;
+ decoder->packet_printer = d_params->packet_printer;
+
+ ret = cs_etm_decoder__gen_etmv4_config(t_params,&trace_config);
+
+ if (ret != 0)
+ return -1;
+
+ ret = ocsd_dt_create_etmv4i_decoder(decoder->dcd_tree,&trace_config);
+
+ if (ret != OCSD_OK)
+ return -1;
+
+ ret = ocsd_dt_set_gen_elem_outfn(decoder->dcd_tree,
+ cs_etm_decoder__gen_trace_elem_printer, decoder);
+ return ret;
+}
+
+int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *decoder, uint64_t address, uint64_t len, cs_etm_mem_cb_type cb_func)
+{
+ int err;
+
+ decoder->mem_access = cb_func;
+ err = ocsd_dt_add_callback_mem_acc(decoder->dcd_tree,
+ address,
+ address+len-1,
+ OCSD_MEM_SPACE_ANY,
+ cs_etm_decoder__mem_access,
+ decoder);
+ return err;
+}
+
+
+int cs_etm_decoder__add_bin_file(struct cs_etm_decoder *decoder, uint64_t offset, uint64_t address, uint64_t len, const char *fname)
+{
+ int err = 0;
+ file_mem_region_t region;
+
+ (void) len;
+ if (NULL == decoder)
+ return -1;
+
+ if (NULL == decoder->dcd_tree)
+ return -1;
+
+ region.file_offset = offset;
+ region.start_address = address;
+ region.region_size = len;
+ err = ocsd_dt_add_binfile_region_mem_acc(decoder->dcd_tree,
+ &region,
+ 1,
+ OCSD_MEM_SPACE_ANY,
+ fname);
+
+ return err;
+}
+
+const struct cs_etm_state *cs_etm_decoder__process_data_block(struct cs_etm_decoder *decoder,
+ uint64_t indx,
+ const uint8_t *buf,
+ size_t len,
+ size_t *consumed)
+{
+ int ret = 0;
+ ocsd_datapath_resp_t dp_ret = decoder->prev_return;
+ size_t processed = 0;
+
+ if (decoder->packet_count > 0) {
+ decoder->state.err = ret;
+ *consumed = processed;
+ return &(decoder->state);
+ }
+
+ while ((processed < len) && (0 == ret)) {
+
+ if (OCSD_DATA_RESP_IS_CONT(dp_ret)) {
+ uint32_t count;
+ dp_ret = ocsd_dt_process_data(decoder->dcd_tree,
+ OCSD_OP_DATA,
+ indx+processed,
+ len - processed,
+ &buf[processed],
+ &count);
+ processed += count;
+
+ } else if (OCSD_DATA_RESP_IS_WAIT(dp_ret)) {
+ dp_ret = ocsd_dt_process_data(decoder->dcd_tree,
+ OCSD_OP_FLUSH,
+ 0,
+ 0,
+ NULL,
+ NULL);
+ break;
+ } else {
+ ret = -1;
+ }
+ }
+ if (OCSD_DATA_RESP_IS_WAIT(dp_ret)) {
+ if (OCSD_DATA_RESP_IS_CONT(decoder->prev_return)) {
+ decoder->prev_processed = processed;
+ }
+ processed = 0;
+ } else if (OCSD_DATA_RESP_IS_WAIT(decoder->prev_return)) {
+ processed = decoder->prev_processed;
+ decoder->prev_processed = 0;
+ }
+ *consumed = processed;
+ decoder->prev_return = dp_ret;
+ decoder->state.err = ret;
+ return &(decoder->state);
+}
+
+int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder,
+ struct cs_etm_packet *packet)
+{
+ if (decoder->packet_count == 0) return -1;
+
+ if (packet == NULL) return -1;
+
+ *packet = decoder->packet_buffer[decoder->head];
+
+ decoder->head = (decoder->head + 1) & (MAX_BUFFER - 1);
+
+ decoder->packet_count--;
+
+ return 0;
+}
+
+static void cs_etm_decoder__clear_buffer(struct cs_etm_decoder *decoder)
+{
+ unsigned i;
+
+ decoder->head = 0;
+ decoder->tail = 0;
+ decoder->end_tail = 0;
+ decoder->packet_count = 0;
+ for (i = 0; i < MAX_BUFFER; i++) {
+ decoder->packet_buffer[i].start_addr = 0xdeadbeefdeadbeefUL;
+ decoder->packet_buffer[i].end_addr = 0xdeadbeefdeadbeefUL;
+ decoder->packet_buffer[i].exc = false;
+ decoder->packet_buffer[i].exc_ret = false;
+ decoder->packet_buffer[i].cpu = INT_MIN;
+ }
+}
+
+struct cs_etm_decoder *cs_etm_decoder__new(uint32_t num_cpu, struct cs_etm_decoder_params *d_params, struct cs_etm_trace_params t_params[])
+{
+ struct cs_etm_decoder *decoder;
+ ocsd_dcd_tree_src_t format;
+ uint32_t flags;
+ int ret;
+ size_t i;
+
+ if ((t_params == NULL) || (d_params == 0)) {
+ return NULL;
+ }
+
+ decoder = zalloc(sizeof(struct cs_etm_decoder));
+
+ if (decoder == NULL) {
+ return NULL;
+ }
+
+ decoder->state.data = d_params->data;
+ decoder->prev_return = OCSD_RESP_CONT;
+ cs_etm_decoder__clear_buffer(decoder);
+ format = (d_params->formatted ? OCSD_TRC_SRC_FRAME_FORMATTED :
+ OCSD_TRC_SRC_SINGLE);
+ flags = 0;
+ flags |= (d_params->fsyncs ? OCSD_DFRMTR_HAS_FSYNCS : 0);
+ flags |= (d_params->hsyncs ? OCSD_DFRMTR_HAS_HSYNCS : 0);
+ flags |= (d_params->frame_aligned ? OCSD_DFRMTR_FRAME_MEM_ALIGN : 0);
+
+ /* Create decode tree for the data source */
+ decoder->dcd_tree = ocsd_create_dcd_tree(format,flags);
+
+ if (decoder->dcd_tree == 0) {
+ goto err_free_decoder;
+ }
+
+ for (i = 0; i < num_cpu; ++i) {
+ switch (t_params[i].protocol)
+ {
+ case CS_ETM_PROTO_ETMV4i:
+ if (d_params->operation == CS_ETM_OPERATION_PRINT) {
+ ret = cs_etm_decoder__create_etmv4i_packet_printer(d_params,&t_params[i],decoder);
+ } else if (d_params->operation == CS_ETM_OPERATION_DECODE) {
+ ret = cs_etm_decoder__create_etmv4i_packet_decoder(d_params,&t_params[i],decoder);
+ } else {
+ ret = -CS_ETM_ERR_PARAM;
+ }
+ if (ret != 0) {
+ goto err_free_decoder_tree;
+ }
+ break;
+ default:
+ goto err_free_decoder_tree;
+ break;
+ }
+ }
+
+
+ return decoder;
+
+err_free_decoder_tree:
+ ocsd_destroy_dcd_tree(decoder->dcd_tree);
+err_free_decoder:
+ free(decoder);
+ return NULL;
+}
+
+
+void cs_etm_decoder__free(struct cs_etm_decoder *decoder)
+{
+ if (decoder == NULL) return;
+
+ ocsd_destroy_dcd_tree(decoder->dcd_tree);
+ decoder->dcd_tree = NULL;
+
+ free(decoder);
+}
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
new file mode 100644
index 000000000000..38c5ae84ed07
--- /dev/null
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Tor Jeremiassen <tor.jeremiassen@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU GEneral Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDE__CS_ETM_DECODER_H__
+#define INCLUDE__CS_ETM_DECODER_H__
+
+#include <linux/types.h>
+#include <stdio.h>
+
+struct cs_etm_decoder;
+
+struct cs_etm_buffer {
+ const unsigned char *buf;
+ size_t len;
+ uint64_t offset;
+ //bool consecutive;
+ uint64_t ref_timestamp;
+ //uint64_t trace_nr;
+};
+
+enum cs_etm_sample_type {
+ CS_ETM_RANGE = 1 << 0,
+};
+
+struct cs_etm_state {
+ int err;
+ void *data;
+ unsigned isa;
+ uint64_t start;
+ uint64_t end;
+ uint64_t timestamp;
+};
+
+struct cs_etm_packet {
+ enum cs_etm_sample_type sample_type;
+ uint64_t start_addr;
+ uint64_t end_addr;
+ bool exc;
+ bool exc_ret;
+ int cpu;
+};
+
+
+struct cs_etm_queue;
+typedef uint32_t (*cs_etm_mem_cb_type)(struct cs_etm_queue *, uint64_t, size_t, uint8_t *);
+
+struct cs_etm_trace_params {
+ void *etmv4i_packet_handler;
+ uint32_t reg_idr0;
+ uint32_t reg_idr1;
+ uint32_t reg_idr2;
+ uint32_t reg_idr8;
+ uint32_t reg_configr;
+ uint32_t reg_traceidr;
+ int protocol;
+};
+
+struct cs_etm_decoder_params {
+ int operation;
+ void (*packet_printer)(const char *);
+ cs_etm_mem_cb_type mem_acc_cb;
+ bool formatted;
+ bool fsyncs;
+ bool hsyncs;
+ bool frame_aligned;
+ void *data;
+};
+
+enum {
+ CS_ETM_PROTO_ETMV3 = 1,
+ CS_ETM_PROTO_ETMV4i,
+ CS_ETM_PROTO_ETMV4d,
+};
+
+enum {
+ CS_ETM_OPERATION_PRINT = 1,
+ CS_ETM_OPERATION_DECODE,
+};
+
+enum {
+ CS_ETM_ERR_NOMEM = 1,
+ CS_ETM_ERR_NODATA,
+ CS_ETM_ERR_PARAM,
+};
+
+
+struct cs_etm_decoder *cs_etm_decoder__new(uint32_t num_cpu, struct cs_etm_decoder_params *,struct cs_etm_trace_params []);
+
+int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *, uint64_t, uint64_t, cs_etm_mem_cb_type);
+
+int cs_etm_decoder__flush(struct cs_etm_decoder *);
+void cs_etm_decoder__free(struct cs_etm_decoder *);
+int cs_etm_decoder__get_packet(struct cs_etm_decoder *, struct cs_etm_packet *);
+
+int cs_etm_decoder__add_bin_file(struct cs_etm_decoder *, uint64_t, uint64_t, uint64_t, const char *);
+
+const struct cs_etm_state *cs_etm_decoder__process_data_block(struct cs_etm_decoder *,
+ uint64_t,
+ const uint8_t *,
+ size_t,
+ size_t *);
+
+#endif /* INCLUDE__CS_ETM_DECODER_H__ */
+
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
new file mode 100644
index 000000000000..ca93257a6cb7
--- /dev/null
+++ b/tools/perf/util/cs-etm.c
@@ -0,0 +1,1533 @@
+/*
+ * Copyright(C) 2016 Linaro Limited. All rights reserved.
+ * Author: Tor Jeremiassen <tor.jeremiassen@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/log2.h>
+
+#include "perf.h"
+#include "thread_map.h"
+#include "thread.h"
+#include "thread-stack.h"
+#include "callchain.h"
+#include "auxtrace.h"
+#include "evlist.h"
+#include "machine.h"
+#include "util.h"
+#include "util/intlist.h"
+#include "color.h"
+#include "cs-etm.h"
+#include "cs-etm-decoder/cs-etm-decoder.h"
+#include "debug.h"
+
+#include <stdlib.h>
+
+#define KiB(x) ((x) * 1024)
+#define MiB(x) ((x) * 1024 * 1024)
+#define MAX_TIMESTAMP (~0ULL)
+
+struct cs_etm_auxtrace {
+ struct auxtrace auxtrace;
+ struct auxtrace_queues queues;
+ struct auxtrace_heap heap;
+ u64 **metadata;
+ u32 auxtrace_type;
+ struct perf_session *session;
+ struct machine *machine;
+ struct perf_evsel *switch_evsel;
+ struct thread *unknown_thread;
+ uint32_t num_cpu;
+ bool timeless_decoding;
+ bool sampling_mode;
+ bool snapshot_mode;
+ bool data_queued;
+ bool sync_switch;
+ bool synth_needs_swap;
+ int have_sched_switch;
+
+ bool sample_instructions;
+ u64 instructions_sample_type;
+ u64 instructions_sample_period;
+ u64 instructions_id;
+ struct itrace_synth_opts synth_opts;
+ unsigned pmu_type;
+};
+
+struct cs_etm_queue {
+ struct cs_etm_auxtrace *etm;
+ unsigned queue_nr;
+ struct auxtrace_buffer *buffer;
+ const struct cs_etm_state *state;
+ struct ip_callchain *chain;
+ union perf_event *event_buf;
+ bool on_heap;
+ bool step_through_buffers;
+ bool use_buffer_pid_tid;
+ pid_t pid, tid;
+ int cpu;
+ struct thread *thread;
+ u64 time;
+ u64 timestamp;
+ bool stop;
+ struct cs_etm_decoder *decoder;
+ u64 offset;
+ bool eot;
+ bool kernel_mapped;
+};
+
+static int cs_etm__get_trace(struct cs_etm_buffer *buff, struct cs_etm_queue *etmq);
+static int cs_etm__update_queues(struct cs_etm_auxtrace *);
+static int cs_etm__process_queues(struct cs_etm_auxtrace *, u64);
+static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *, pid_t, u64);
+static uint32_t cs_etm__mem_access(struct cs_etm_queue *, uint64_t , size_t , uint8_t *);
+
+static void cs_etm__packet_dump(const char *pkt_string)
+{
+ const char *color = PERF_COLOR_BLUE;
+
+ 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)
+{
+ 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;
+ size_t i;
+
+ fprintf(stdout,"\n");
+ color_fprintf(stdout, color,
+ ". ... CoreSight ETM Trace data: size %zu bytes\n",
+ buffer->size);
+
+ t_params = zalloc(sizeof(struct cs_etm_trace_params) * etm->num_cpu);
+ for (i = 0; i < etm->num_cpu; ++i) {
+ t_params[i].protocol = CS_ETM_PROTO_ETMV4i;
+ t_params[i].reg_idr0 = etm->metadata[i][CS_ETMV4_TRCIDR0];
+ t_params[i].reg_idr1 = etm->metadata[i][CS_ETMV4_TRCIDR1];
+ t_params[i].reg_idr2 = etm->metadata[i][CS_ETMV4_TRCIDR2];
+ t_params[i].reg_idr8 = etm->metadata[i][CS_ETMV4_TRCIDR8];
+ t_params[i].reg_configr = etm->metadata[i][CS_ETMV4_TRCCONFIGR];
+ t_params[i].reg_traceidr = etm->metadata[i][CS_ETMV4_TRCTRACEIDR];
+ //[CS_ETMV4_TRCAUTHSTATUS] = " TRCAUTHSTATUS %"PRIx64"\n",
+ }
+ 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 == NULL) {
+ return;
+ }
+ do {
+ size_t consumed;
+ cs_etm_decoder__process_data_block(decoder,buffer->offset,&(((uint8_t *)buffer->data)[buffer_used]),buffer->size - buffer_used, &consumed);
+ 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){
+ struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
+ struct cs_etm_auxtrace,
+ auxtrace);
+
+ int ret;
+
+ if (dump_trace)
+ return 0;
+
+ if (!tool->ordered_events)
+ return -EINVAL;
+
+ ret = cs_etm__update_queues(etm);
+
+ if (ret < 0)
+ return ret;
+
+ if (etm->timeless_decoding)
+ return cs_etm__process_timeless_queues(etm,-1,MAX_TIMESTAMP - 1);
+
+ return cs_etm__process_queues(etm, MAX_TIMESTAMP);
+}
+
+static void cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm,
+ struct auxtrace_queue *queue)
+{
+ struct cs_etm_queue *etmq = queue->priv;
+
+ if ((queue->tid == -1) || (etm->have_sched_switch)) {
+ etmq->tid = machine__get_current_tid(etm->machine, etmq->cpu);
+ thread__zput(etmq->thread);
+ }
+
+ if ((!etmq->thread) && (etmq->tid != -1)) {
+ etmq->thread = machine__find_thread(etm->machine,-1,etmq->tid);
+ }
+
+ if (etmq->thread) {
+ etmq->pid = etmq->thread->pid_;
+ if (queue->cpu == -1) {
+ etmq->cpu = etmq->thread->cpu;
+ }
+ }
+}
+
+static void cs_etm__free_queue(void *priv)
+{
+ struct cs_etm_queue *etmq = priv;
+
+ if (!etmq)
+ return;
+
+ thread__zput(etmq->thread);
+ cs_etm_decoder__free(etmq->decoder);
+ zfree(&etmq->event_buf);
+ zfree(&etmq->chain);
+ free(etmq);
+}
+
+static void cs_etm__free_events(struct perf_session *session)
+{
+ struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
+ struct cs_etm_auxtrace,
+ auxtrace);
+
+ struct auxtrace_queues *queues = &(aux->queues);
+
+ unsigned i;
+
+ for (i = 0; i < queues->nr_queues; ++i) {
+ cs_etm__free_queue(queues->queue_array[i].priv);
+ queues->queue_array[i].priv = 0;
+ }
+
+ auxtrace_queues__free(queues);
+
+}
+
+static void cs_etm__free(struct perf_session *session)
+{
+
+ size_t i;
+ struct int_node *inode, *tmp;
+ struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
+ struct cs_etm_auxtrace,
+ auxtrace);
+ auxtrace_heap__free(&aux->heap);
+ cs_etm__free_events(session);
+ session->auxtrace = NULL;
+
+ /* First remove all traceID/CPU# nodes from the RB tree */
+ intlist__for_each_safe(inode, tmp, traceid_list)
+ intlist__remove(traceid_list, inode);
+ /* Then the RB tree itself */
+ intlist__delete(traceid_list);
+
+ //thread__delete(aux->unknown_thread);
+ for (i = 0; i < aux->num_cpu; ++i) {
+ zfree(&aux->metadata[i]);
+ }
+ zfree(&aux->metadata);
+ free(aux);
+}
+
+static void cs_etm__use_buffer_pid_tid(struct cs_etm_queue *etmq,
+ struct auxtrace_queue *queue,
+ struct auxtrace_buffer *buffer)
+{
+ if ((queue->cpu == -1) && (buffer->cpu != -1)) {
+ etmq->cpu = buffer->cpu;
+ }
+
+ etmq->pid = buffer->pid;
+ etmq->tid = buffer->tid;
+
+ thread__zput(etmq->thread);
+
+ if (etmq->tid != -1) {
+ if (etmq->pid != -1) {
+ etmq->thread = machine__findnew_thread(etmq->etm->machine,
+ etmq->pid,
+ etmq->tid);
+ } else {
+ etmq->thread = machine__findnew_thread(etmq->etm->machine,
+ -1,
+ etmq->tid);
+ }
+ }
+}
+
+
+static int cs_etm__get_trace(struct cs_etm_buffer *buff, struct cs_etm_queue *etmq)
+{
+ struct auxtrace_buffer *aux_buffer = etmq->buffer;
+ struct auxtrace_buffer *old_buffer = aux_buffer;
+ struct auxtrace_queue *queue;
+
+ if (etmq->stop) {
+ buff->len = 0;
+ return 0;
+ }
+
+ queue = &etmq->etm->queues.queue_array[etmq->queue_nr];
+
+ aux_buffer = auxtrace_buffer__next(queue,aux_buffer);
+
+ if (!aux_buffer) {
+ if (old_buffer) {
+ auxtrace_buffer__drop_data(old_buffer);
+ }
+ buff->len = 0;
+ return 0;
+ }
+
+ etmq->buffer = aux_buffer;
+
+ if (!aux_buffer->data) {
+ int fd = perf_data_file__fd(etmq->etm->session->file);
+
+ aux_buffer->data = auxtrace_buffer__get_data(aux_buffer, fd);
+ if (!aux_buffer->data)
+ return -ENOMEM;
+ }
+
+ if (old_buffer)
+ auxtrace_buffer__drop_data(old_buffer);
+
+ if (aux_buffer->use_data) {
+ buff->offset = aux_buffer->offset;
+ buff->len = aux_buffer->use_size;
+ buff->buf = aux_buffer->use_data;
+ } else {
+ buff->offset = aux_buffer->offset;
+ buff->len = aux_buffer->size;
+ buff->buf = aux_buffer->data;
+ }
+ /*
+ buff->offset = 0;
+ buff->len = sizeof(cstrace);
+ buff->buf = cstrace;
+ */
+
+ buff->ref_timestamp = aux_buffer->reference;
+
+ if (etmq->use_buffer_pid_tid &&
+ ((etmq->pid != aux_buffer->pid) ||
+ (etmq->tid != aux_buffer->tid))) {
+ cs_etm__use_buffer_pid_tid(etmq,queue,aux_buffer);
+ }
+
+ if (etmq->step_through_buffers)
+ etmq->stop = true;
+
+ return buff->len;
+}
+
+static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm,
+ unsigned int queue_nr)
+{
+ struct cs_etm_decoder_params d_params;
+ struct cs_etm_trace_params *t_params;
+ struct cs_etm_queue *etmq;
+ size_t i;
+
+ etmq = zalloc(sizeof(struct cs_etm_queue));
+ if (!etmq)
+ return NULL;
+
+ if (etm->synth_opts.callchain) {
+ size_t sz = sizeof(struct ip_callchain);
+
+ sz += etm->synth_opts.callchain_sz * sizeof(u64);
+ etmq->chain = zalloc(sz);
+ if (!etmq->chain)
+ goto out_free;
+ } else {
+ etmq->chain = 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;
+ etmq->stop = false;
+ etmq->kernel_mapped = false;
+
+ t_params = zalloc(sizeof(struct cs_etm_trace_params)*etm->num_cpu);
+
+ for (i = 0; i < etm->num_cpu; ++i) {
+ t_params[i].reg_idr0 = etm->metadata[i][CS_ETMV4_TRCIDR0];
+ t_params[i].reg_idr1 = etm->metadata[i][CS_ETMV4_TRCIDR1];
+ t_params[i].reg_idr2 = etm->metadata[i][CS_ETMV4_TRCIDR2];
+ t_params[i].reg_idr8 = etm->metadata[i][CS_ETMV4_TRCIDR8];
+ t_params[i].reg_configr = etm->metadata[i][CS_ETMV4_TRCCONFIGR];
+ t_params[i].reg_traceidr = etm->metadata[i][CS_ETMV4_TRCTRACEIDR];
+ t_params[i].protocol = CS_ETM_PROTO_ETMV4i;
+ }
+ 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;
+
+ etmq->offset = 0;
+ etmq->eot = false;
+
+ return etmq;
+
+out_free:
+ zfree(&etmq->event_buf);
+ zfree(&etmq->chain);
+ 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)))
+ return 0;
+
+ if (etmq == NULL) {
+ etmq = cs_etm__alloc_queue(etm,queue_nr);
+
+ if (etmq == NULL) {
+ return -ENOMEM;
+ }
+
+ queue->priv = etmq;
+
+ if (queue->cpu != -1) {
+ etmq->cpu = queue->cpu;
+ }
+
+ etmq->tid = queue->tid;
+
+ if (etm->sampling_mode) {
+ if (etm->timeless_decoding)
+ etmq->step_through_buffers = true;
+ if (etm->timeless_decoding || !etm->have_sched_switch)
+ etmq->use_buffer_pid_tid = true;
+ }
+ }
+
+ if (!etmq->on_heap &&
+ (!etm->sync_switch)) {
+ const struct cs_etm_state *state;
+ int ret = 0;
+
+ if (etm->timeless_decoding)
+ return ret;
+
+ //cs_etm__log("queue %u getting timestamp\n",queue_nr);
+ //cs_etm__log("queue %u decoding cpu %d pid %d tid %d\n",
+ //queue_nr, etmq->cpu, etmq->pid, etmq->tid);
+ (void) state;
+ return ret;
+ /*
+ while (1) {
+ state = cs_etm_decoder__decode(etmq->decoder);
+ if (state->err) {
+ if (state->err == CS_ETM_ERR_NODATA) {
+ //cs_etm__log("queue %u has no timestamp\n",
+ //queue_nr);
+ return 0;
+ }
+ continue;
+ }
+ if (state->timestamp)
+ break;
+ }
+
+ etmq->timestamp = state->timestamp;
+ //cs_etm__log("queue %u timestamp 0x%"PRIx64 "\n",
+ //queue_nr, etmq->timestamp);
+ etmq->state = state;
+ etmq->have_sample = true;
+ //cs_etm__sample_flags(etmq);
+ ret = auxtrace_heap__add(&etm->heap, queue_nr, etmq->timestamp);
+ if (ret)
+ return ret;
+ etmq->on_heap = true;
+ */
+ }
+
+ 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;
+}
+
+#if 0
+struct cs_etm_cache_entry {
+ struct auxtrace_cache_entry entry;
+ uint64_t icount;
+ uint64_t bcount;
+};
+
+static size_t cs_etm__cache_divisor(void)
+{
+ static size_t d = 64;
+
+ return d;
+}
+
+static size_t cs_etm__cache_size(struct dso *dso,
+ struct machine *machine)
+{
+ off_t size;
+
+ size = dso__data_size(dso,machine);
+ size /= cs_etm__cache_divisor();
+
+ if (size < 1000)
+ return 10;
+
+ if (size > (1 << 21))
+ return 21;
+
+ return 32 - __builtin_clz(size);
+}
+
+static struct auxtrace_cache *cs_etm__cache(struct dso *dso,
+ struct machine *machine)
+{
+ struct auxtrace_cache *c;
+ size_t bits;
+
+ if (dso->auxtrace_cache)
+ return dso->auxtrace_cache;
+
+ bits = cs_etm__cache_size(dso,machine);
+
+ c = auxtrace_cache__new(bits, sizeof(struct cs_etm_cache_entry), 200);
+
+ dso->auxtrace_cache = c;
+
+ return c;
+}
+
+static int cs_etm__cache_add(struct dso *dso, struct machine *machine,
+ uint64_t offset, uint64_t icount, uint64_t bcount)
+{
+ struct auxtrace_cache *c = cs_etm__cache(dso, machine);
+ struct cs_etm_cache_entry *e;
+ int err;
+
+ if (!c)
+ return -ENOMEM;
+
+ e = auxtrace_cache__alloc_entry(c);
+ if (!e)
+ return -ENOMEM;
+
+ e->icount = icount;
+ e->bcount = bcount;
+
+ err = auxtrace_cache__add(c, offset, &e->entry);
+
+ if (err)
+ auxtrace_cache__free_entry(c, e);
+
+ return err;
+}
+
+static struct cs_etm_cache_entry *cs_etm__cache_lookup(struct dso *dso,
+ struct machine *machine,
+ uint64_t offset)
+{
+ struct auxtrace_cache *c = cs_etm__cache(dso, machine);
+
+ if (!c)
+ return NULL;
+
+ return auxtrace_cache__lookup(dso->auxtrace_cache, offset);
+}
+#endif
+
+static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
+ struct cs_etm_packet *packet)
+{
+ int ret = 0;
+ struct cs_etm_auxtrace *etm = etmq->etm;
+ union perf_event *event = etmq->event_buf;
+ struct perf_sample sample = {.ip = 0,};
+ uint64_t start_addr = packet->start_addr;
+ uint64_t end_addr = packet->end_addr;
+
+ event->sample.header.type = PERF_RECORD_SAMPLE;
+ event->sample.header.misc = PERF_RECORD_MISC_USER;
+ event->sample.header.size = sizeof(struct perf_event_header);
+
+
+ sample.ip = start_addr;
+ sample.pid = etmq->pid;
+ sample.tid = etmq->tid;
+ sample.addr = end_addr;
+ sample.id = etmq->etm->instructions_id;
+ sample.stream_id = etmq->etm->instructions_id;
+ sample.period = (end_addr - start_addr) >> 2;
+ sample.cpu = packet->cpu;
+ sample.flags = 0; // etmq->flags;
+ sample.insn_len = 1; // etmq->insn_len;
+
+ //etmq->last_insn_cnt = etmq->state->tot_insn_cnt;
+
+#if 0
+ {
+ struct addr_location al;
+ uint64_t offset;
+ struct thread *thread;
+ struct machine *machine = etmq->etm->machine;
+ uint8_t cpumode;
+ struct cs_etm_cache_entry *e;
+ uint8_t buf[256];
+ size_t bufsz;
+
+ thread = etmq->thread;
+
+ if (!thread) {
+ thread = etmq->etm->unknown_thread;
+ }
+
+ if (start_addr > 0xffffffc000000000UL) {
+ cpumode = PERF_RECORD_MISC_KERNEL;
+ } else {
+ cpumode = PERF_RECORD_MISC_USER;
+ }
+
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, start_addr,&al);
+ if (!al.map || !al.map->dso) {
+ goto endTest;
+ }
+ if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR &&
+ dso__data_status_seen(al.map->dso,DSO_DATA_STATUS_SEEN_ITRACE)) {
+ goto endTest;
+ }
+
+ offset = al.map->map_ip(al.map,start_addr);
+
+
+ e = cs_etm__cache_lookup(al.map->dso, machine, offset);
+
+ if (e) {
+ (void) e;
+ } else {
+ int len;
+ map__load(al.map, machine->symbol_filter);
+
+ bufsz = sizeof(buf);
+ len = dso__data_read_offset(al.map->dso, machine,
+ offset, buf, bufsz);
+
+ if (len <= 0) {
+ goto endTest;
+ }
+
+ cs_etm__cache_add(al.map->dso, machine, offset, (end_addr - start_addr) >> 2, end_addr - start_addr);
+
+ }
+endTest:
+ (void) offset;
+ }
+#endif
+
+ ret = perf_session__deliver_synth_event(etm->session,event, &sample);
+
+ if (ret) {
+ pr_err("CS ETM Trace: failed to deliver instruction event, error %d\n", ret);
+
+ }
+ return ret;
+}
+
+struct cs_etm_synth {
+ struct perf_tool dummy_tool;
+ struct perf_session *session;
+};
+
+
+static int cs_etm__event_synth(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine)
+{
+ struct cs_etm_synth *cs_etm_synth =
+ container_of(tool, struct cs_etm_synth, dummy_tool);
+
+ (void) sample;
+ (void) machine;
+
+ return perf_session__deliver_synth_event(cs_etm_synth->session, event, NULL);
+
+}
+
+
+static int cs_etm__synth_event(struct perf_session *session,
+ struct perf_event_attr *attr, u64 id)
+{
+ struct cs_etm_synth cs_etm_synth;
+
+ memset(&cs_etm_synth, 0, sizeof(struct cs_etm_synth));
+ cs_etm_synth.session = session;
+
+ return perf_event__synthesize_attr(&cs_etm_synth.dummy_tool, attr, 1,
+ &id, cs_etm__event_synth);
+}
+
+static int cs_etm__synth_events(struct cs_etm_auxtrace *etm,
+ struct perf_session *session)
+{
+ struct perf_evlist *evlist = session->evlist;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr;
+ bool found = false;
+ u64 id;
+ int err;
+
+ evlist__for_each(evlist, evsel) {
+
+ if (evsel->attr.type == etm->pmu_type) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ pr_debug("There are no selected events with Core Sight Trace data\n");
+ return 0;
+ }
+
+ memset(&attr, 0, sizeof(struct perf_event_attr));
+ attr.size = sizeof(struct perf_event_attr);
+ attr.type = PERF_TYPE_HARDWARE;
+ attr.sample_type = evsel->attr.sample_type & PERF_SAMPLE_MASK;
+ attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID |
+ PERF_SAMPLE_PERIOD;
+ if (etm->timeless_decoding)
+ attr.sample_type &= ~(u64)PERF_SAMPLE_TIME;
+ else
+ attr.sample_type |= PERF_SAMPLE_TIME;
+
+ attr.exclude_user = evsel->attr.exclude_user;
+ attr.exclude_kernel = evsel->attr.exclude_kernel;
+ attr.exclude_hv = evsel->attr.exclude_hv;
+ attr.exclude_host = evsel->attr.exclude_host;
+ attr.exclude_guest = evsel->attr.exclude_guest;
+ attr.sample_id_all = evsel->attr.sample_id_all;
+ attr.read_format = evsel->attr.read_format;
+
+ id = evsel->id[0] + 1000000000;
+
+ if (!id)
+ id = 1;
+
+ if (etm->synth_opts.instructions) {
+ attr.config = PERF_COUNT_HW_INSTRUCTIONS;
+ attr.sample_period = etm->synth_opts.period;
+ etm->instructions_sample_period = attr.sample_period;
+ err = cs_etm__synth_event(session, &attr, id);
+
+ if (err) {
+ pr_err("%s: failed to synthesize 'instructions' event type\n",
+ __func__);
+ return err;
+ }
+ etm->sample_instructions = true;
+ etm->instructions_sample_type = attr.sample_type;
+ etm->instructions_id = id;
+ id += 1;
+ }
+
+ etm->synth_needs_swap = evsel->needs_swap;
+ return 0;
+}
+
+static int cs_etm__sample(struct cs_etm_queue *etmq)
+{
+ //const struct cs_etm_state *state = etmq->state;
+ struct cs_etm_packet packet;
+ //struct cs_etm_auxtrace *etm = etmq->etm;
+ int err;
+
+ err = cs_etm_decoder__get_packet(etmq->decoder,&packet);
+ // if there is no sample, it returns err = -1, no real error
+
+ if (!err && packet.sample_type & CS_ETM_RANGE) {
+ err = cs_etm__synth_instruction_sample(etmq,&packet);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int cs_etm__run_decoder(struct cs_etm_queue *etmq, u64 *timestamp)
+{
+ struct cs_etm_buffer buffer;
+ size_t buffer_used;
+ int err = 0;
+
+ /* Go through each buffer in the queue and decode them one by one */
+more:
+ buffer_used = 0;
+ memset(&buffer, 0, sizeof(buffer));
+ err = cs_etm__get_trace(&buffer,etmq);
+ if (err <= 0)
+ return err;
+
+ do {
+ size_t processed = 0;
+ etmq->state = cs_etm_decoder__process_data_block(etmq->decoder,
+ etmq->offset,
+ &buffer.buf[buffer_used],
+ buffer.len-buffer_used,
+ &processed);
+ err = etmq->state->err;
+ etmq->offset += processed;
+ buffer_used += processed;
+ if (!err)
+ cs_etm__sample(etmq);
+ } while (!etmq->eot && (buffer.len > buffer_used));
+goto more;
+
+ (void) timestamp;
+
+ return err;
+}
+
+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_queues(struct cs_etm_auxtrace *etm, u64 timestamp)
+{
+ unsigned int queue_nr;
+ u64 ts;
+ int ret;
+
+ while (1) {
+ struct auxtrace_queue *queue;
+ struct cs_etm_queue *etmq;
+
+ if (!etm->heap.heap_cnt)
+ return 0;
+
+ if (etm->heap.heap_array[0].ordinal >= timestamp)
+ return 0;
+
+ queue_nr = etm->heap.heap_array[0].queue_nr;
+ queue = &etm->queues.queue_array[queue_nr];
+ etmq = queue->priv;
+
+ //cs_etm__log("queue %u processing 0x%" PRIx64 " to 0x%" PRIx64 "\n",
+ //queue_nr, etm->heap.heap_array[0].ordinal,
+ //timestamp);
+
+ auxtrace_heap__pop(&etm->heap);
+
+ if (etm->heap.heap_cnt) {
+ ts = etm->heap.heap_array[0].ordinal + 1;
+ if (ts > timestamp)
+ ts = timestamp;
+ } else {
+ ts = timestamp;
+ }
+
+ cs_etm__set_pid_tid_cpu(etm, queue);
+
+ ret = cs_etm__run_decoder(etmq, &ts);
+
+ if (ret < 0) {
+ auxtrace_heap__add(&etm->heap, queue_nr, ts);
+ return ret;
+ }
+
+ if (!ret) {
+ ret = auxtrace_heap__add(&etm->heap, queue_nr, ts);
+ if (ret < 0)
+ return ret;
+ } else {
+ etmq->on_heap = false;
+ }
+ }
+ return 0;
+}
+
+static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
+ pid_t tid,
+ u64 time_)
+{
+ struct auxtrace_queues *queues = &etm->queues;
+ unsigned int i;
+ u64 ts = 0;
+
+ for (i = 0; i < queues->nr_queues; ++i) {
+ struct auxtrace_queue *queue = &(etm->queues.queue_array[i]);
+ struct cs_etm_queue *etmq = queue->priv;
+
+ if (etmq && ((tid == -1) || (etmq->tid == tid))) {
+ etmq->time = time_;
+ cs_etm__set_pid_tid_cpu(etm, queue);
+ cs_etm__run_decoder(etmq,&ts);
+
+ }
+ }
+ return 0;
+}
+
+static struct cs_etm_queue *cs_etm__cpu_to_etmq(struct cs_etm_auxtrace *etm,
+ int cpu)
+{
+ unsigned q,j;
+
+ if (etm->queues.nr_queues == 0)
+ return NULL;
+
+ if (cpu < 0)
+ q = 0;
+ else if ((unsigned) cpu >= etm->queues.nr_queues)
+ q = etm->queues.nr_queues - 1;
+ else
+ q = cpu;
+
+ if (etm->queues.queue_array[q].cpu == cpu)
+ return etm->queues.queue_array[q].priv;
+
+ for (j = 0; q > 0; j++) {
+ if (etm->queues.queue_array[--q].cpu == cpu)
+ return etm->queues.queue_array[q].priv;
+ }
+
+ for (; j < etm->queues.nr_queues; j++) {
+ if (etm->queues.queue_array[j].cpu == cpu)
+ return etm->queues.queue_array[j].priv;
+
+ }
+
+ return NULL;
+}
+
+static uint32_t cs_etm__mem_access(struct cs_etm_queue *etmq, uint64_t address, size_t size, uint8_t *buffer)
+{
+ struct addr_location al;
+ uint64_t offset;
+ struct thread *thread;
+ struct machine *machine;
+ uint8_t cpumode;
+ int len;
+
+ if (etmq == NULL)
+ return -1;
+
+ machine = etmq->etm->machine;
+ thread = etmq->thread;
+ if (address > 0xffffffc000000000UL) {
+ cpumode = PERF_RECORD_MISC_KERNEL;
+ } else {
+ cpumode = PERF_RECORD_MISC_USER;
+ }
+
+ 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, machine->symbol_filter);
+
+ len = dso__data_read_offset(al.map->dso, machine,
+ offset, buffer, size);
+
+ if (len <= 0) {
+ return 0;
+ }
+
+ return len;
+}
+
+static bool check_need_swap(int file_endian)
+{
+ const int data = 1;
+ u8 *check = (u8 *)&data;
+ int host_endian;
+
+ if (check[0] == 1)
+ host_endian = ELFDATA2LSB;
+ else
+ host_endian = ELFDATA2MSB;
+
+ return host_endian != file_endian;
+}
+
+static int cs_etm__read_elf_info(const char *fname, uint64_t *foffset, uint64_t *fstart, uint64_t *fsize)
+{
+ FILE *fp;
+ u8 e_ident[EI_NIDENT];
+ int ret = -1;
+ bool need_swap = false;
+ size_t buf_size;
+ void *buf;
+ int i;
+
+ fp = fopen(fname, "r");
+ if (fp == NULL)
+ return -1;
+
+ if (fread(e_ident, sizeof(e_ident), 1, fp) != 1)
+ goto out;
+
+ if (memcmp(e_ident, ELFMAG, SELFMAG) ||
+ e_ident[EI_VERSION] != EV_CURRENT)
+ goto out;
+
+ need_swap = check_need_swap(e_ident[EI_DATA]);
+
+ /* for simplicity */
+ fseek(fp, 0, SEEK_SET);
+
+ if (e_ident[EI_CLASS] == ELFCLASS32) {
+ Elf32_Ehdr ehdr;
+ Elf32_Phdr *phdr;
+
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+ goto out;
+
+ if (need_swap) {
+ ehdr.e_phoff = bswap_32(ehdr.e_phoff);
+ ehdr.e_phentsize = bswap_16(ehdr.e_phentsize);
+ ehdr.e_phnum = bswap_16(ehdr.e_phnum);
+ }
+
+ buf_size = ehdr.e_phentsize * ehdr.e_phnum;
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ goto out;
+
+ fseek(fp, ehdr.e_phoff, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) {
+
+ if (need_swap) {
+ phdr->p_type = bswap_32(phdr->p_type);
+ phdr->p_offset = bswap_32(phdr->p_offset);
+ phdr->p_filesz = bswap_32(phdr->p_filesz);
+ }
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ *foffset = phdr->p_offset;
+ *fstart = phdr->p_vaddr;
+ *fsize = phdr->p_filesz;
+ ret = 0;
+ break;
+ }
+ } else {
+ Elf64_Ehdr ehdr;
+ Elf64_Phdr *phdr;
+
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+ goto out;
+
+ if (need_swap) {
+ ehdr.e_phoff = bswap_64(ehdr.e_phoff);
+ ehdr.e_phentsize = bswap_16(ehdr.e_phentsize);
+ ehdr.e_phnum = bswap_16(ehdr.e_phnum);
+ }
+
+ buf_size = ehdr.e_phentsize * ehdr.e_phnum;
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ goto out;
+
+ fseek(fp, ehdr.e_phoff, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) {
+
+ if (need_swap) {
+ phdr->p_type = bswap_32(phdr->p_type);
+ phdr->p_offset = bswap_64(phdr->p_offset);
+ phdr->p_filesz = bswap_64(phdr->p_filesz);
+ }
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ *foffset = phdr->p_offset;
+ *fstart = phdr->p_vaddr;
+ *fsize = phdr->p_filesz;
+ ret = 0;
+ break;
+ }
+ }
+out_free:
+ free(buf);
+out:
+ fclose(fp);
+ return ret;
+}
+
+static int cs_etm__process_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool)
+{
+ struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
+ struct cs_etm_auxtrace,
+ auxtrace);
+
+ u64 timestamp;
+ int err = 0;
+
+ if (dump_trace)
+ return 0;
+
+ if (!tool->ordered_events) {
+ pr_err("CoreSight ETM Trace requires ordered events\n");
+ 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;
+
+ }
+
+ if (event->header.type == PERF_RECORD_MMAP2) {
+ struct dso *dso;
+ int cpu;
+ struct cs_etm_queue *etmq;
+
+ cpu = sample->cpu;
+
+ etmq = cs_etm__cpu_to_etmq(etm,cpu);
+
+ if (!etmq) {
+ return -1;
+ }
+
+ dso = dsos__find(&(etm->machine->dsos),event->mmap2.filename,false);
+ if (NULL != dso) {
+ err = cs_etm_decoder__add_mem_access_cb(
+ etmq->decoder,
+ event->mmap2.start,
+ event->mmap2.len,
+ cs_etm__mem_access);
+ }
+
+ if ((symbol_conf.vmlinux_name != NULL) && (!etmq->kernel_mapped)) {
+ uint64_t foffset;
+ uint64_t fstart;
+ uint64_t fsize;
+
+ err = cs_etm__read_elf_info(symbol_conf.vmlinux_name,
+ &foffset,&fstart,&fsize);
+
+ if (!err) {
+ cs_etm_decoder__add_bin_file(
+ etmq->decoder,
+ foffset,
+ fstart,
+ fsize & ~0x1ULL,
+ symbol_conf.vmlinux_name);
+
+ etmq->kernel_mapped = true;
+ }
+ }
+
+ }
+
+ if (etm->timeless_decoding) {
+ if (event->header.type == PERF_RECORD_EXIT) {
+ err = cs_etm__process_timeless_queues(etm,
+ event->fork.tid,
+ sample->time);
+ }
+ } else if (timestamp) {
+ err = cs_etm__process_queues(etm, timestamp);
+ }
+
+ //cs_etm__log("event %s (%u): cpu %d time%"PRIu64" tsc %#"PRIx64"\n",
+ //perf_event__name(event->header.type), event->header.type,
+ //sample->cpu, sample->time, timestamp);
+ return err;
+}
+
+static int cs_etm__process_auxtrace_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_tool *tool)
+{
+ struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
+ struct cs_etm_auxtrace,
+ auxtrace);
+
+ (void) tool;
+
+ if (!etm->data_queued) {
+ struct auxtrace_buffer *buffer;
+ off_t data_offset;
+ int fd = perf_data_file__fd(session->file);
+ bool is_pipe = perf_data_file__is_pipe(session->file);
+ 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 const char * const cs_etm_global_header_fmts[] = {
+ [CS_HEADER_VERSION_0] = " Header version %"PRIx64"\n",
+ [CS_PMU_TYPE_CPUS] = " PMU type/num cpus %"PRIx64"\n",
+ [CS_ETM_SNAPSHOT] = " Snapshot %"PRIx64"\n",
+};
+
+static const char * const cs_etm_priv_fmts[] = {
+ [CS_ETM_MAGIC] = " Magic number %"PRIx64"\n",
+ [CS_ETM_CPU] = " CPU %"PRIx64"\n",
+ [CS_ETM_ETMCR] = " ETMCR %"PRIx64"\n",
+ [CS_ETM_ETMTRACEIDR] = " ETMTRACEIDR %"PRIx64"\n",
+ [CS_ETM_ETMCCER] = " ETMCCER %"PRIx64"\n",
+ [CS_ETM_ETMIDR] = " ETMIDR %"PRIx64"\n",
+};
+
+static const char * const cs_etmv4_priv_fmts[] = {
+ [CS_ETM_MAGIC] = " Magic number %"PRIx64"\n",
+ [CS_ETM_CPU] = " CPU %"PRIx64"\n",
+ [CS_ETMV4_TRCCONFIGR] = " TRCCONFIGR %"PRIx64"\n",
+ [CS_ETMV4_TRCTRACEIDR] = " TRCTRACEIDR %"PRIx64"\n",
+ [CS_ETMV4_TRCIDR0] = " TRCIDR0 %"PRIx64"\n",
+ [CS_ETMV4_TRCIDR1] = " TRCIDR1 %"PRIx64"\n",
+ [CS_ETMV4_TRCIDR2] = " TRCIDR2 %"PRIx64"\n",
+ [CS_ETMV4_TRCIDR8] = " TRCIDR8 %"PRIx64"\n",
+ [CS_ETMV4_TRCAUTHSTATUS] = " TRCAUTHSTATUS %"PRIx64"\n",
+};
+
+static void cs_etm__print_auxtrace_info(u64 *val, size_t num)
+{
+ unsigned i,j,cpu;
+
+ for (i = 0, cpu = 0; 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);
+ size_t event_header_size = sizeof(struct perf_event_header);
+ size_t info_header_size = 8;
+ size_t total_size = auxtrace_info->header.size;
+ size_t priv_size = 0;
+ size_t num_cpu;
+ struct cs_etm_auxtrace *etm = 0;
+ int err = 0, idx = -1;
+ u64 *ptr;
+ u64 *hdr = NULL;
+ u64 **metadata = NULL;
+ size_t i,j,k;
+ unsigned pmu_type;
+ struct int_node *inode;
+
+ /*
+ * 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;
+ if (ptr[0] == 0) {
+ hdr = zalloc(sizeof(u64 *) * CS_HEADER_VERSION_0_MAX);
+ if (hdr == NULL) {
+ return -EINVAL;
+ }
+ 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) ((hdr[CS_PMU_TYPE_CPUS] >> 32) & 0xffffffff);
+ } else {
+ return -EINVAL;
+ }
+
+ /*
+ * 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)
+ return -ENOMEM;
+
+ metadata = zalloc(sizeof(u64 *) * num_cpu);
+ if (!metadata) {
+ err = -ENOMEM;
+ goto err_free_traceid_list;
+ }
+
+ if (metadata == NULL) {
+ return -EINVAL;
+ }
+
+ for (j = 0; j < num_cpu; ++j) {
+ if (ptr[i] == __perf_cs_etmv3_magic) {
+ metadata[j] = zalloc(sizeof(u64)*CS_ETM_PRIV_MAX);
+ if (metadata == NULL)
+ return -EINVAL;
+ 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_ETMIDR];
+ i += CS_ETM_PRIV_MAX;
+ } else if (ptr[i] == __perf_cs_etmv4_magic) {
+ metadata[j] = zalloc(sizeof(u64)*CS_ETMV4_PRIV_MAX);
+ if (metadata == NULL)
+ return -EINVAL;
+ 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 have been taken already.
+ * Backout 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];
+
+ }
+
+ if (i*8 != priv_size)
+ return -EINVAL;
+
+ if (dump_trace)
+ cs_etm__print_auxtrace_info(auxtrace_info->priv,num_cpu);
+
+ etm = zalloc(sizeof(struct cs_etm_auxtrace));
+
+ etm->num_cpu = num_cpu;
+ etm->pmu_type = pmu_type;
+ etm->snapshot_mode = (hdr[CS_ETM_SNAPSHOT] != 0);
+
+ if (!etm)
+ return -ENOMEM;
+
+
+ err = auxtrace_queues__init(&etm->queues);
+ if (err)
+ goto err_free;
+
+ etm->unknown_thread = thread__new(999999999,999999999);
+ if (etm->unknown_thread == NULL) {
+ err = -ENOMEM;
+ goto err_free_queues;
+ }
+ err = thread__set_comm(etm->unknown_thread, "unknown", 0);
+ if (err) {
+ goto err_delete_thread;
+ }
+
+ if (thread__init_map_groups(etm->unknown_thread,
+ etm->machine)) {
+ err = -ENOMEM;
+ goto err_delete_thread;
+ }
+
+ etm->timeless_decoding = true;
+ etm->sampling_mode = false;
+ etm->metadata = metadata;
+ etm->session = session;
+ etm->machine = &session->machines.host;
+ etm->auxtrace_type = auxtrace_info->type;
+
+ 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;
+
+ if (session->itrace_synth_opts && session->itrace_synth_opts->set) {
+ etm->synth_opts = *session->itrace_synth_opts;
+ } else {
+ itrace_synth_opts__set_default(&etm->synth_opts);
+ }
+ etm->synth_opts.branches = false;
+ etm->synth_opts.callchain = false;
+ etm->synth_opts.calls = false;
+ etm->synth_opts.returns = false;
+
+ err = cs_etm__synth_events(etm, session);
+ if (err)
+ goto err_delete_thread;
+
+ err = auxtrace_queues__process_index(&etm->queues, session);
+ if (err)
+ goto err_delete_thread;
+
+ etm->data_queued = etm->queues.populated;
+
+ return 0;
+
+err_delete_thread:
+ thread__delete(etm->unknown_thread);
+err_free_queues:
+ auxtrace_queues__free(&etm->queues);
+ session->auxtrace = NULL;
+err_free:
+ free(etm);
+err_free_metadata:
+ /* No need to check @metadata[j], free(NULL) is supported */
+ for (j = 0; j < num_cpu; ++j)
+ free(metadata[j]);
+ free(metadata);
+err_free_traceid_list:
+ intlist__delete(traceid_list);
+
+ return err;
+}
diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
new file mode 100644
index 000000000000..ec6ff78f1905
--- /dev/null
+++ b/tools/perf/util/cs-etm.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDE__UTIL_PERF_CS_ETM_H__
+#define INCLUDE__UTIL_PERF_CS_ETM_H__
+
+#include "util/event.h"
+#include "util/intlist.h"
+#include "util/session.h"
+
+/* Versionning header in case things need tro change in the future. That way
+ * decoding of old snapshot is still possible.
+ */
+enum {
+ /* Starting with 0x0 */
+ CS_HEADER_VERSION_0,
+ /* PMU->type (32 bit), total # of CPUs (32 bit) */
+ CS_PMU_TYPE_CPUS,
+ CS_ETM_SNAPSHOT,
+ CS_HEADER_VERSION_0_MAX,
+};
+
+/* Beginning of header common to both ETMv3 and V4 */
+enum {
+ CS_ETM_MAGIC,
+ CS_ETM_CPU,
+};
+
+/* ETMv3/PTM metadata */
+enum {
+ /* Dynamic, configurable parameters */
+ CS_ETM_ETMCR = CS_ETM_CPU + 1,
+ CS_ETM_ETMTRACEIDR,
+ /* RO, taken from sysFS */
+ CS_ETM_ETMCCER,
+ CS_ETM_ETMIDR,
+ CS_ETM_PRIV_MAX,
+};
+
+/* ETMv4 metadata */
+enum {
+ /* Dynamic, configurable parameters */
+ CS_ETMV4_TRCCONFIGR = CS_ETM_CPU + 1,
+ CS_ETMV4_TRCTRACEIDR,
+ /* RO, taken from sysFS */
+ CS_ETMV4_TRCIDR0,
+ CS_ETMV4_TRCIDR1,
+ CS_ETMV4_TRCIDR2,
+ CS_ETMV4_TRCIDR8,
+ CS_ETMV4_TRCAUTHSTATUS,
+ CS_ETMV4_PRIV_MAX,
+};
+
+/* RB tree for quick conversion between traceID and CPUs */
+struct intlist *traceid_list;
+
+#define KiB(x) ((x) * 1024)
+#define MiB(x) ((x) * 1024 * 1024)
+
+#define CS_ETM_HEADER_SIZE (CS_HEADER_VERSION_0_MAX * sizeof(u64))
+
+static const u64 __perf_cs_etmv3_magic = 0x3030303030303030ULL;
+static const u64 __perf_cs_etmv4_magic = 0x4040404040404040ULL;
+#define CS_ETMV3_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
+#define CS_ETMV4_PRIV_SIZE (CS_ETMV4_PRIV_MAX * sizeof(u64))
+
+int cs_etm__process_auxtrace_info(union perf_event *event,
+ struct perf_session *session);
+
+#endif
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index b4b96120fc3b..b856cf0393ea 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -1247,6 +1247,30 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **e
return err;
}
+int perf_evlist__apply_drv_configs(struct perf_evlist *evlist,
+ struct perf_evsel **err_evsel,
+ struct perf_evsel_config_term **err_term)
+{
+ struct perf_evsel *evsel;
+ int err = 0;
+ const int ncpus = cpu_map__nr(evlist->cpus),
+ nthreads = thread_map__nr(evlist->threads);
+
+ evlist__for_each(evlist, evsel) {
+ if (list_empty(&evsel->drv_config_terms))
+ continue;
+
+ err = perf_evsel__apply_drv_configs(evsel, ncpus,
+ nthreads, err_term);
+ if (err) {
+ *err_evsel = evsel;
+ break;
+ }
+ }
+
+ return err;
+}
+
int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
{
struct perf_evsel *evsel;
@@ -1486,7 +1510,7 @@ int perf_evlist__open(struct perf_evlist *evlist)
perf_evlist__update_id_pos(evlist);
evlist__for_each(evlist, evsel) {
- err = perf_evsel__open(evsel, evlist->cpus, evlist->threads);
+ err = perf_evsel__open(evsel, evsel->cpus, evsel->threads);
if (err < 0)
goto out_err;
}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index a459fe71b452..ae5c1eb1d08c 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -163,6 +163,9 @@ void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,
struct thread_map *threads);
int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target);
int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel);
+int perf_evlist__apply_drv_configs(struct perf_evlist *evlist,
+ struct perf_evsel **err_evsel,
+ struct perf_evsel_config_term **term);
void __perf_evlist__set_leader(struct list_head *list);
void perf_evlist__set_leader(struct perf_evlist *evlist);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index f0bd4825f95a..4f2dc807471e 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -211,6 +211,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
evsel->bpf_fd = -1;
INIT_LIST_HEAD(&evsel->node);
INIT_LIST_HEAD(&evsel->config_terms);
+ INIT_LIST_HEAD(&evsel->drv_config_terms);
perf_evsel__object.init(evsel);
evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
perf_evsel__calc_id_pos(evsel);
@@ -981,6 +982,27 @@ int perf_evsel__append_filter(struct perf_evsel *evsel,
return -1;
}
+int perf_evsel__apply_drv_configs(struct perf_evsel *evsel,
+ int ncpus, int nthreads,
+ struct perf_evsel_config_term **err_term)
+{
+ int err = 0;
+ struct perf_evsel_config_term *term;
+
+ list_for_each_entry(term, &evsel->drv_config_terms, list) {
+ err = perf_evsel__run_ioctl(evsel, ncpus, nthreads,
+ PERF_EVENT_IOC_SET_DRV_CONFIGS,
+ (void *)term->val.drv_cfg);
+
+ if (err) {
+ *err_term = term;
+ break;
+ }
+ }
+
+ return err;
+}
+
int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
{
return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
@@ -988,6 +1010,16 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
0);
}
+int perf_evsel__disable(struct perf_evsel *evsel)
+{
+ int nthreads = thread_map__nr(evsel->threads);
+ int ncpus = cpu_map__nr(evsel->cpus);
+
+ return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
+ PERF_EVENT_IOC_DISABLE,
+ 0);
+}
+
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
{
if (ncpus == 0 || nthreads == 0)
@@ -1033,6 +1065,16 @@ static void perf_evsel__free_config_terms(struct perf_evsel *evsel)
}
}
+static void perf_evsel__free_drv_config_terms(struct perf_evsel *evsel)
+{
+ struct perf_evsel_config_term *term, *h;
+
+ list_for_each_entry_safe(term, h, &evsel->drv_config_terms, list) {
+ list_del(&term->list);
+ free(term);
+ }
+}
+
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
{
int cpu, thread;
@@ -1054,6 +1096,7 @@ void perf_evsel__exit(struct perf_evsel *evsel)
perf_evsel__free_fd(evsel);
perf_evsel__free_id(evsel);
perf_evsel__free_config_terms(evsel);
+ perf_evsel__free_drv_config_terms(evsel);
close_cgroup(evsel->cgrp);
cpu_map__put(evsel->cpus);
cpu_map__put(evsel->own_cpus);
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 0e49bd742c63..b649143ac16b 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -44,6 +44,7 @@ enum {
PERF_EVSEL__CONFIG_TERM_CALLGRAPH,
PERF_EVSEL__CONFIG_TERM_STACK_USER,
PERF_EVSEL__CONFIG_TERM_INHERIT,
+ PERF_EVSEL__CONFIG_TERM_DRV_CFG,
PERF_EVSEL__CONFIG_TERM_MAX,
};
@@ -55,6 +56,7 @@ struct perf_evsel_config_term {
u64 freq;
bool time;
char *callgraph;
+ char *drv_cfg;
u64 stack_user;
bool inherit;
} val;
@@ -75,6 +77,7 @@ struct perf_evsel_config_term {
* PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all
* is used there is an id sample appended to non-sample events
* @priv: And what is in its containing unnamed union are tool specific
+ * @drv_config_terms: List of configurables sent directly to the PMU driver
*/
struct perf_evsel {
struct list_head node;
@@ -123,6 +126,7 @@ struct perf_evsel {
char *group_name;
bool cmdline_group_boundary;
struct list_head config_terms;
+ struct list_head drv_config_terms;
int bpf_fd;
};
@@ -227,7 +231,11 @@ int perf_evsel__append_filter(struct perf_evsel *evsel,
const char *op, const char *filter);
int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
const char *filter);
+int perf_evsel__apply_drv_configs(struct perf_evsel *evsel,
+ int ncpus, int nthreads,
+ struct perf_evsel_config_term **err_term);
int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads);
+int perf_evsel__disable(struct perf_evsel *evsel);
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
struct cpu_map *cpus);
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 8b303ff20289..888640ffada5 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1,3 +1,4 @@
+#include "build-id.h"
#include "callchain.h"
#include "debug.h"
#include "event.h"
@@ -685,8 +686,16 @@ static struct dso *machine__get_kernel(struct machine *machine)
DSO_TYPE_GUEST_KERNEL);
}
- if (kernel != NULL && (!kernel->has_build_id))
- dso__read_running_kernel_build_id(kernel, machine);
+ if (kernel != NULL && (!kernel->has_build_id)) {
+ if (symbol_conf.vmlinux_name != NULL) {
+ filename__read_build_id(symbol_conf.vmlinux_name,
+ kernel->build_id,
+ sizeof(kernel->build_id));
+ kernel->has_build_id = 1;
+ } else {
+ dso__read_running_kernel_build_id(kernel, machine);
+ }
+ }
return kernel;
}
@@ -700,8 +709,19 @@ static void machine__get_kallsyms_filename(struct machine *machine, char *buf,
{
if (machine__is_default_guest(machine))
scnprintf(buf, bufsz, "%s", symbol_conf.default_guest_kallsyms);
- else
- scnprintf(buf, bufsz, "%s/proc/kallsyms", machine->root_dir);
+ else {
+ if (symbol_conf.vmlinux_name != 0) {
+ unsigned char build_id[BUILD_ID_SIZE];
+ char build_id_hex[SBUILD_ID_SIZE];
+ filename__read_build_id(symbol_conf.vmlinux_name,
+ build_id,
+ sizeof(build_id));
+ build_id__sprintf(build_id,sizeof(build_id), build_id_hex);
+ build_id__filename((char *)build_id_hex,buf,bufsz);
+ } else {
+ scnprintf(buf, bufsz, "%s/proc/kallsyms", machine->root_dir);
+ }
+ }
}
const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
@@ -710,7 +730,7 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
* Returns the name of the start symbol in *symbol_name. Pass in NULL as
* symbol_name if it's not that important.
*/
-static u64 machine__get_running_kernel_start(struct machine *machine,
+static u64 machine__get_kallsyms_kernel_start(struct machine *machine,
const char **symbol_name)
{
char filename[PATH_MAX];
@@ -738,7 +758,7 @@ static u64 machine__get_running_kernel_start(struct machine *machine,
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
{
enum map_type type;
- u64 start = machine__get_running_kernel_start(machine, NULL);
+ u64 start = machine__get_kallsyms_kernel_start(machine, NULL);
for (type = 0; type < MAP__NR_TYPES; ++type) {
struct kmap *kmap;
@@ -1083,7 +1103,8 @@ int machine__create_kernel_maps(struct machine *machine)
{
struct dso *kernel = machine__get_kernel(machine);
const char *name;
- u64 addr = machine__get_running_kernel_start(machine, &name);
+ u64 addr = machine__get_kallsyms_kernel_start(machine, &name);
+
if (!addr)
return -1;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index e81dfb2e239c..6e825dbaddea 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -285,7 +285,8 @@ static struct perf_evsel *
__add_event(struct list_head *list, int *idx,
struct perf_event_attr *attr,
char *name, struct cpu_map *cpus,
- struct list_head *config_terms)
+ struct list_head *config_terms,
+ struct list_head *drv_config_terms)
{
struct perf_evsel *evsel;
@@ -305,6 +306,9 @@ __add_event(struct list_head *list, int *idx,
if (config_terms)
list_splice(config_terms, &evsel->config_terms);
+ if (drv_config_terms)
+ list_splice(drv_config_terms, &evsel->drv_config_terms);
+
list_add_tail(&evsel->node, list);
return evsel;
}
@@ -313,7 +317,8 @@ static int add_event(struct list_head *list, int *idx,
struct perf_event_attr *attr, char *name,
struct list_head *config_terms)
{
- return __add_event(list, idx, attr, name, NULL, config_terms) ? 0 : -ENOMEM;
+ return __add_event(list, idx, attr, name,
+ NULL, config_terms, NULL) ? 0 : -ENOMEM;
}
static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
@@ -824,7 +829,8 @@ static int config_term_pmu(struct perf_event_attr *attr,
struct parse_events_term *term,
struct parse_events_error *err)
{
- if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER)
+ if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER ||
+ term->type_term == PARSE_EVENTS__TERM_TYPE_DRV_CFG)
/*
* Always succeed for sysfs terms, as we dont know
* at this point what type they need to have.
@@ -870,10 +876,7 @@ static int config_attr(struct perf_event_attr *attr,
return 0;
}
-static int get_config_terms(struct list_head *head_config,
- struct list_head *head_terms __maybe_unused)
-{
-#define ADD_CONFIG_TERM(__type, __name, __val) \
+#define ADD_CONFIG_TERM(__type, __name, __val, __head_terms) \
do { \
struct perf_evsel_config_term *__t; \
\
@@ -884,33 +887,43 @@ do { \
INIT_LIST_HEAD(&__t->list); \
__t->type = PERF_EVSEL__CONFIG_TERM_ ## __type; \
__t->val.__name = __val; \
- list_add_tail(&__t->list, head_terms); \
+ list_add_tail(&__t->list, __head_terms); \
} while (0)
+static int get_config_terms(struct list_head *head_config,
+ struct list_head *head_terms __maybe_unused)
+{
struct parse_events_term *term;
list_for_each_entry(term, head_config, list) {
switch (term->type_term) {
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
- ADD_CONFIG_TERM(PERIOD, period, term->val.num);
+ ADD_CONFIG_TERM(PERIOD, period,
+ term->val.num, head_terms);
break;
case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
- ADD_CONFIG_TERM(FREQ, freq, term->val.num);
+ ADD_CONFIG_TERM(FREQ, freq,
+ term->val.num, head_terms);
break;
case PARSE_EVENTS__TERM_TYPE_TIME:
- ADD_CONFIG_TERM(TIME, time, term->val.num);
+ ADD_CONFIG_TERM(TIME, time,
+ term->val.num, head_terms);
break;
case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
- ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str);
+ ADD_CONFIG_TERM(CALLGRAPH, callgraph,
+ term->val.str, head_terms);
break;
case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
- ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num);
+ ADD_CONFIG_TERM(STACK_USER, stack_user,
+ term->val.num, head_terms);
break;
case PARSE_EVENTS__TERM_TYPE_INHERIT:
- ADD_CONFIG_TERM(INHERIT, inherit, term->val.num ? 1 : 0);
+ ADD_CONFIG_TERM(INHERIT, inherit,
+ term->val.num ? 1 : 0, head_terms);
break;
case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
- ADD_CONFIG_TERM(INHERIT, inherit, term->val.num ? 0 : 1);
+ ADD_CONFIG_TERM(INHERIT, inherit,
+ term->val.num ? 0 : 1, head_terms);
break;
default:
break;
@@ -920,6 +933,21 @@ do { \
return 0;
}
+static int get_drv_config_terms(struct list_head *head_config,
+ struct list_head *head_terms)
+{
+ struct parse_events_term *term;
+
+ list_for_each_entry(term, head_config, list) {
+ if (term->type_term != PARSE_EVENTS__TERM_TYPE_DRV_CFG)
+ continue;
+
+ ADD_CONFIG_TERM(DRV_CFG, drv_cfg, term->val.str, head_terms);
+ }
+
+ return 0;
+}
+
int parse_events_add_tracepoint(struct list_head *list, int *idx,
char *sys, char *event,
struct parse_events_error *err,
@@ -990,6 +1018,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data,
struct perf_pmu *pmu;
struct perf_evsel *evsel;
LIST_HEAD(config_terms);
+ LIST_HEAD(drv_config_terms);
pmu = perf_pmu__find(name);
if (!pmu)
@@ -1004,7 +1033,8 @@ int parse_events_add_pmu(struct parse_events_evlist *data,
if (!head_config) {
attr.type = pmu->type;
- evsel = __add_event(list, &data->idx, &attr, NULL, pmu->cpus, NULL);
+ evsel = __add_event(list, &data->idx, &attr,
+ NULL, pmu->cpus, NULL, NULL);
return evsel ? 0 : -ENOMEM;
}
@@ -1021,12 +1051,15 @@ int parse_events_add_pmu(struct parse_events_evlist *data,
if (get_config_terms(head_config, &config_terms))
return -ENOMEM;
+ if (get_drv_config_terms(head_config, &drv_config_terms))
+ return -ENOMEM;
+
if (perf_pmu__config(pmu, &attr, head_config, data->error))
return -EINVAL;
evsel = __add_event(list, &data->idx, &attr,
pmu_event_name(head_config), pmu->cpus,
- &config_terms);
+ &config_terms, &drv_config_terms);
if (evsel) {
evsel->unit = info.unit;
evsel->scale = info.scale;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index f1a6db107241..09c3ee2df45c 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -68,7 +68,8 @@ enum {
PARSE_EVENTS__TERM_TYPE_CALLGRAPH,
PARSE_EVENTS__TERM_TYPE_STACKSIZE,
PARSE_EVENTS__TERM_TYPE_NOINHERIT,
- PARSE_EVENTS__TERM_TYPE_INHERIT
+ PARSE_EVENTS__TERM_TYPE_INHERIT,
+ PARSE_EVENTS__TERM_TYPE_DRV_CFG,
};
struct parse_events_term {
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 58c5831ffd5c..de260ed0dd54 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -53,6 +53,16 @@ static int str(yyscan_t scanner, int token)
return token;
}
+static int drv_str(yyscan_t scanner, int token)
+{
+ YYSTYPE *yylval = parse_events_get_lval(scanner);
+ char *text = parse_events_get_text(scanner);
+
+ /* Strip off the '@' */
+ yylval->str = strdup(text + 1);
+ return token;
+}
+
#define REWIND(__alloc) \
do { \
YYSTYPE *__yylval = parse_events_get_lval(yyscanner); \
@@ -123,6 +133,7 @@ num_hex 0x[a-fA-F0-9]+
num_raw_hex [a-fA-F0-9]+
name [a-zA-Z_*?][a-zA-Z0-9_*?.]*
name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.]*
+drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)?
/* If you add a modifier you need to update check_modifier() */
modifier_event [ukhpPGHSDI]+
modifier_bp [rwx]{1,3}
@@ -196,6 +207,7 @@ no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
, { return ','; }
"/" { BEGIN(INITIAL); return '/'; }
{name_minus} { return str(yyscanner, PE_NAME); }
+@{drv_cfg_term} { return drv_str(yyscanner, PE_DRV_CFG_TERM); }
}
<mem>{
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index ad379968d4c1..d35c10275ba4 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -48,6 +48,7 @@ static inc_group_count(struct list_head *list,
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
%token PE_ERROR
%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
+%token PE_DRV_CFG_TERM
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM_HW
%type <num> PE_VALUE_SYM_SW
@@ -62,6 +63,7 @@ static inc_group_count(struct list_head *list,
%type <str> PE_MODIFIER_BP
%type <str> PE_EVENT_NAME
%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
+%type <str> PE_DRV_CFG_TERM
%type <num> value_sym
%type <head> event_config
%type <term> event_term
@@ -573,6 +575,15 @@ PE_TERM
ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, &@1, NULL));
$$ = term;
}
+|
+PE_DRV_CFG_TERM
+{
+ struct parse_events_term *term;
+
+ ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_DRV_CFG,
+ $1, $1, &@1, NULL));
+ $$ = term;
+}
sep_dc: ':' |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index a8e825fca42a..df49c0035170 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -806,6 +806,8 @@ static void python_process_general_event(struct perf_sample *sample,
PyInt_FromLong(sample->cpu));
pydict_set_item_string_decref(dict_sample, "ip",
PyLong_FromUnsignedLongLong(sample->ip));
+ pydict_set_item_string_decref(dict_sample, "addr",
+ PyLong_FromUnsignedLongLong(sample->addr));
pydict_set_item_string_decref(dict_sample, "time",
PyLong_FromUnsignedLongLong(sample->time));
pydict_set_item_string_decref(dict_sample, "period",
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 0ae4f73dc8eb..4596496f6c0f 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -234,14 +234,6 @@ static int process_event_stub(struct perf_tool *tool __maybe_unused,
return 0;
}
-static int process_build_id_stub(struct perf_tool *tool __maybe_unused,
- union perf_event *event __maybe_unused,
- struct perf_session *session __maybe_unused)
-{
- dump_printf(": unhandled!\n");
- return 0;
-}
-
static int process_finished_round_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct ordered_events *oe __maybe_unused)
@@ -254,23 +246,6 @@ static int process_finished_round(struct perf_tool *tool,
union perf_event *event,
struct ordered_events *oe);
-static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
- union perf_event *event __maybe_unused,
- struct perf_session *perf_session
- __maybe_unused)
-{
- dump_printf(": unhandled!\n");
- return 0;
-}
-
-static int process_event_auxtrace_info_stub(struct perf_tool *tool __maybe_unused,
- union perf_event *event __maybe_unused,
- struct perf_session *session __maybe_unused)
-{
- dump_printf(": unhandled!\n");
- return 0;
-}
-
static int skipn(int fd, off_t n)
{
char buf[4096];
@@ -297,10 +272,9 @@ static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused,
return event->auxtrace.size;
}
-static
-int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused,
- union perf_event *event __maybe_unused,
- struct perf_session *session __maybe_unused)
+static int process_event_op2_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *session __maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
@@ -341,7 +315,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
if (tool->tracing_data == NULL)
tool->tracing_data = process_event_synth_tracing_data_stub;
if (tool->build_id == NULL)
- tool->build_id = process_build_id_stub;
+ tool->build_id = process_event_op2_stub;
if (tool->finished_round == NULL) {
if (tool->ordered_events)
tool->finished_round = process_finished_round;
@@ -349,13 +323,13 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->finished_round = process_finished_round_stub;
}
if (tool->id_index == NULL)
- tool->id_index = process_id_index_stub;
+ tool->id_index = process_event_op2_stub;
if (tool->auxtrace_info == NULL)
- tool->auxtrace_info = process_event_auxtrace_info_stub;
+ tool->auxtrace_info = process_event_op2_stub;
if (tool->auxtrace == NULL)
tool->auxtrace = process_event_auxtrace_stub;
if (tool->auxtrace_error == NULL)
- tool->auxtrace_error = process_event_auxtrace_error_stub;
+ tool->auxtrace_error = process_event_op2_stub;
}
static void swap_sample_id_all(union perf_event *event, void *data)
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index 48906333a858..9be16712ce74 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -344,7 +344,7 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
if (ret >= 0)
dso->is_64_bit = ret;
- if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) {
+ if ((!dso->has_build_id) && (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0)) {
dso__set_build_id(dso, build_id);
}
return 0;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 415be561fad3..237830873c71 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1468,7 +1468,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
* Read the build id if possible. This is required for
* DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work
*/
- if (filename__read_build_id(dso->name, build_id, BUILD_ID_SIZE) > 0)
+ if ((!dso->has_build_id) &&
+ (filename__read_build_id(dso->name, build_id, BUILD_ID_SIZE) > 0))
dso__set_build_id(dso, build_id);
/*