perf tools: decoding capailitity for CoreSight traces
Added user space perf functionality for CoreSight trace decoding.
This commit is contained in:
parent
56139d02e2
commit
1265cc36fb
15 changed files with 2237 additions and 9 deletions
|
@ -77,6 +77,9 @@ include config/utilities.mak
|
|||
# Define NO_AUXTRACE if you do not want AUX area tracing support
|
||||
#
|
||||
# Define NO_LIBBPF if you do not want BPF support
|
||||
#
|
||||
# Define NO_CSTRACE if you do not want CoreSight trace decoding support
|
||||
#
|
||||
|
||||
# As per kernel Makefile, avoid funny character set dependencies
|
||||
unexport LC_ALL
|
||||
|
|
|
@ -92,7 +92,8 @@ static struct {
|
|||
|
||||
.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
|
||||
PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
|
||||
PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
|
||||
PERF_OUTPUT_EVNAME | PERF_OUTPUT_ADDR |
|
||||
PERF_OUTPUT_IP |
|
||||
PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
|
||||
PERF_OUTPUT_PERIOD,
|
||||
|
||||
|
|
|
@ -433,6 +433,24 @@ endif
|
|||
grep-libs = $(filter -l%,$(1))
|
||||
strip-libs = $(filter-out -l%,$(1))
|
||||
|
||||
ifdef CSTRACE_PATH
|
||||
ifeq (${IS_64_BIT}, 1)
|
||||
CSTRACE_LNX = linux64
|
||||
else
|
||||
CSTRACE_LNX = linux
|
||||
endif
|
||||
ifdef DEBUG
|
||||
LIBCSTRACE = -lcstraced_c_api -lcstraced
|
||||
CSTRACE_LIB_PATH = $(CSTRACE_PATH)/lib/$(CSTRACE_LNX)/dbg
|
||||
else
|
||||
LIBCSTRACE = -lcstrace_c_api -lcstrace
|
||||
CSTRACE_LIB_PATH = $(CSTRACE_PATH)/lib/$(CSTRACE_LNX)/rel
|
||||
endif
|
||||
$(call detected,CSTRACE)
|
||||
$(call detected_var,CSTRACE_PATH)
|
||||
EXTLIBS += -L$(CSTRACE_LIB_PATH) $(LIBCSTRACE) -lstdc++
|
||||
endif
|
||||
|
||||
ifdef NO_LIBPERL
|
||||
CFLAGS += -DNO_LIBPERL
|
||||
else
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -893,6 +894,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
|
|||
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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
7
tools/perf/util/cs-etm-decoder/Build
Normal file
7
tools/perf/util/cs-etm-decoder/Build
Normal file
|
@ -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
|
||||
|
91
tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c
Normal file
91
tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c
Normal file
|
@ -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;
|
||||
}
|
488
tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
Normal file
488
tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
Normal file
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
*
|
||||
* 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"
|
||||
|
||||
#include "c_api/rctdl_c_api.h"
|
||||
#include "rctdl_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;
|
||||
rctdl_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 rctdl_vaddr_t address,
|
||||
const rctdl_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,
|
||||
rctdl_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 rctdl_generic_trace_elem *elem, enum cs_etm_sample_type sample_type)
|
||||
{
|
||||
int err = 0;
|
||||
uint32_t et = 0;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
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 rctdl_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
|
||||
const void *context,
|
||||
const rctdl_trc_index_t indx,
|
||||
const uint8_t trace_chan_id,
|
||||
const rctdl_generic_trace_elem *elem)
|
||||
{
|
||||
rctdl_datapath_resp_t resp = RCTDL_RESP_CONT;
|
||||
struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
|
||||
|
||||
(void) indx;
|
||||
(void) trace_chan_id;
|
||||
|
||||
switch (elem->elem_type) {
|
||||
case RCTDL_GEN_TRC_ELEM_UNKNOWN:
|
||||
break;
|
||||
case RCTDL_GEN_TRC_ELEM_NO_SYNC:
|
||||
decoder->trace_on = false;
|
||||
break;
|
||||
case RCTDL_GEN_TRC_ELEM_TRACE_ON:
|
||||
decoder->trace_on = true;
|
||||
break;
|
||||
//case RCTDL_GEN_TRC_ELEM_TRACE_OVERFLOW:
|
||||
//decoder->trace_on = false;
|
||||
//decoder->discontinuity = true;
|
||||
//break;
|
||||
case RCTDL_GEN_TRC_ELEM_INSTR_RANGE:
|
||||
cs_etm_decoder__buffer_packet(decoder,elem, CS_ETM_RANGE);
|
||||
resp = RCTDL_RESP_WAIT;
|
||||
break;
|
||||
case RCTDL_GEN_TRC_ELEM_EXCEPTION:
|
||||
cs_etm_decoder__mark_exception(decoder);
|
||||
break;
|
||||
case RCTDL_GEN_TRC_ELEM_EXCEPTION_RET:
|
||||
cs_etm_decoder__mark_exception_return(decoder);
|
||||
break;
|
||||
case RCTDL_GEN_TRC_ELEM_PE_CONTEXT:
|
||||
case RCTDL_GEN_TRC_ELEM_EO_TRACE:
|
||||
case RCTDL_GEN_TRC_ELEM_ADDR_NACC:
|
||||
case RCTDL_GEN_TRC_ELEM_TIMESTAMP:
|
||||
case RCTDL_GEN_TRC_ELEM_CYCLE_COUNT:
|
||||
//case RCTDL_GEN_TRC_ELEM_TS_WITH_CC:
|
||||
case RCTDL_GEN_TRC_ELEM_EVENT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
decoder->state.err = 0;
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
static rctdl_datapath_resp_t cs_etm_decoder__etmv4i_packet_printer(
|
||||
const void *context,
|
||||
const rctdl_datapath_op_t op,
|
||||
const rctdl_trc_index_t indx,
|
||||
const rctdl_etmv4_i_pkt *pkt)
|
||||
{
|
||||
const size_t PACKET_STR_LEN = 1024;
|
||||
rctdl_datapath_resp_t ret = RCTDL_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 RCTDL_OP_DATA:
|
||||
if (rctdl_pkt_str(RCTDL_PROTOCOL_ETMV4I,
|
||||
(void *)pkt,
|
||||
packet_str+offset,
|
||||
PACKET_STR_LEN-offset) != RCTDL_OK)
|
||||
ret = RCTDL_RESP_FATAL_INVALID_PARAM;
|
||||
break;
|
||||
case RCTDL_OP_EOT:
|
||||
sprintf(packet_str,"**** END OF TRACE ****\n");
|
||||
break;
|
||||
case RCTDL_OP_FLUSH:
|
||||
case RCTDL_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)
|
||||
{
|
||||
rctdl_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 = rctdl_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)
|
||||
{
|
||||
rctdl_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 = rctdl_dt_create_etmv4i_decoder(decoder->dcd_tree,&trace_config);
|
||||
|
||||
if (ret != RCTDL_OK)
|
||||
return -1;
|
||||
|
||||
ret = rctdl_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 = rctdl_dt_add_callback_mem_acc(decoder->dcd_tree,
|
||||
address,
|
||||
address+len-1,
|
||||
RCTDL_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 = rctdl_dt_add_binfile_region_mem_acc(decoder->dcd_tree,
|
||||
®ion,
|
||||
1,
|
||||
RCTDL_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;
|
||||
rctdl_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 (RCTDL_DATA_RESP_IS_CONT(dp_ret)) {
|
||||
uint32_t count;
|
||||
dp_ret = rctdl_dt_process_data(decoder->dcd_tree,
|
||||
RCTDL_OP_DATA,
|
||||
indx+processed,
|
||||
len - processed,
|
||||
&buf[processed],
|
||||
&count);
|
||||
processed += count;
|
||||
|
||||
} else if (RCTDL_DATA_RESP_IS_WAIT(dp_ret)) {
|
||||
dp_ret = rctdl_dt_process_data(decoder->dcd_tree,
|
||||
RCTDL_OP_FLUSH,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL);
|
||||
break;
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
if (RCTDL_DATA_RESP_IS_WAIT(dp_ret)) {
|
||||
if (RCTDL_DATA_RESP_IS_CONT(decoder->prev_return)) {
|
||||
decoder->prev_processed = processed;
|
||||
}
|
||||
processed = 0;
|
||||
} else if (RCTDL_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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
rctdl_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 = RCTDL_RESP_CONT;
|
||||
cs_etm_decoder__clear_buffer(decoder);
|
||||
format = (d_params->formatted ? RCTDL_TRC_SRC_FRAME_FORMATTED :
|
||||
RCTDL_TRC_SRC_SINGLE);
|
||||
flags = 0;
|
||||
flags |= (d_params->fsyncs ? RCTDL_DFRMTR_HAS_FSYNCS : 0);
|
||||
flags |= (d_params->hsyncs ? RCTDL_DFRMTR_HAS_HSYNCS : 0);
|
||||
flags |= (d_params->frame_aligned ? RCTDL_DFRMTR_FRAME_MEM_ALIGN : 0);
|
||||
|
||||
/* Create decode tree for the data source */
|
||||
decoder->dcd_tree = rctdl_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:
|
||||
rctdl_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;
|
||||
|
||||
rctdl_destroy_dcd_tree(decoder->dcd_tree);
|
||||
decoder->dcd_tree = NULL;
|
||||
|
||||
free(decoder);
|
||||
}
|
117
tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
Normal file
117
tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
|
||||
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__ */
|
||||
|
1472
tools/perf/util/cs-etm.c
Normal file
1472
tools/perf/util/cs-etm.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -71,4 +71,7 @@ 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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Add table
Reference in a new issue