mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-11 23:48:08 +09:00
This patch adds the required mechanic to quickly lookup the CPU number
associated with a traceID. That way the CPU that executed the code
conveyed by a decoded packet can be identified, without having to
do unecessary translations.
Using this new functionality the "cs-trace-disasm.py" script is
enhanced to output the file and CPU number the code has been
executed on:
FILE: /lib/aarch64-linux-gnu/ld-2.21.so CPU: 3
7fab57fd80: 910003e0 mov x0, sp
7fab57fd84: 94000d53 bl 7fab5832d0 <free@plt+0x3790>
FILE: /lib/aarch64-linux-gnu/ld-2.21.so CPU: 3
7fab5832d0: d11203ff sub sp, sp, #0x480
FILE: /lib/aarch64-linux-gnu/ld-2.21.so CPU: 3
7fab5832d4: a9ba7bfd stp x29, x30, [sp,#-96]!
7fab5832d8: 910003fd mov x29, sp
7fab5832dc: a90363f7 stp x23, x24, [sp,#48]
7fab5832e0: 9101e3b7 add x23, x29, #0x78
7fab5832e4: a90573fb stp x27, x28, [sp,#80]
7fab5832e8: a90153f3 stp x19, x20, [sp,#16]
7fab5832ec: aa0003fb mov x27, x0
7fab5832f0: 910a82e1 add x1, x23, #0x2a0
7fab5832f4: a9025bf5 stp x21, x22, [sp,#32]
7fab5832f8: a9046bf9 stp x25, x26, [sp,#64]
7fab5832fc: 910102e0 add x0, x23, #0x40
7fab583300: f800841f str xzr, [x0],#8
7fab583304: eb01001f cmp x0, x1
7fab583308: 54ffffc1 b.ne 7fab583300 <free@plt+0x37c0>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
504 lines
16 KiB
C
504 lines
16 KiB
C
/*
|
|
*
|
|
* 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,
|
|
®ion,
|
|
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);
|
|
}
|