diff options
Diffstat (limited to 'decoder/tests/source')
-rw-r--r-- | decoder/tests/source/c_api_pkt_print_test.c | 1124 | ||||
-rw-r--r-- | decoder/tests/source/frame_demux_test.cpp | 524 | ||||
-rw-r--r-- | decoder/tests/source/mem_buff_demo.cpp | 421 | ||||
-rw-r--r-- | decoder/tests/source/trc_pkt_lister.cpp | 767 |
4 files changed, 2836 insertions, 0 deletions
diff --git a/decoder/tests/source/c_api_pkt_print_test.c b/decoder/tests/source/c_api_pkt_print_test.c new file mode 100644 index 0000000..b930e05 --- /dev/null +++ b/decoder/tests/source/c_api_pkt_print_test.c @@ -0,0 +1,1124 @@ +/* + * \file c_api_pkt_print_test.c + * \brief OpenCSD : C-API test program + * + * \copyright Copyright (c) 2016, ARM Limited. All Rights Reserved. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Example of using the library with the C-API. Used to validate that the C-API + * functions work. + * + * Simple test program to print packets from a single trace ID source stream. + * Hard coded configuration based on the Juno r1-1 test snapshot for ETMv4 and + * STM, TC2 test snapshot for ETMv3, PTM. + * + * The test source can be set from the command line, but will default to the + * ETMv4 trace for trace ID 0x10 on the juno r1-1 test snapshot. + * This example uses the updated C-API functionality from library version 0.4.0 onwards. + * Test values are hardcoded from the same values in the snapshots as we do not + * explicitly read the snapshot metadata in this example program. + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +/* include the C-API library header */ +#include "opencsd/c_api/opencsd_c_api.h" + +/* include the test external decoder factory and decoder types headers + - separate from the main library includes by definition as external decoder. +*/ +#include "ext_dcd_echo_test_fact.h" +#include "ext_dcd_echo_test.h" + +/* path to test snapshots, relative to tests/bin/<plat>/<dbg|rel> build output dir */ +#ifdef _WIN32 +const char *default_base_snapshot_path="..\\..\\..\\snapshots"; +const char *juno_snapshot = "\\juno_r1_1\\"; +const char *tc2_snapshot = "\\TC2\\"; +#else +const char *default_base_snapshot_path = "../../snapshots"; +const char *juno_snapshot = "/juno_r1_1/"; +const char *tc2_snapshot = "/TC2/"; +#endif +static const char *selected_snapshot; +static const char *usr_snapshot_path = 0; +#define MAX_TRACE_FILE_PATH_LEN 512 + +/* trace data and memory file dump names and values - taken from snapshot metadata */ +const char *trace_data_filename = "cstrace.bin"; +const char *stmtrace_data_filename = "cstraceitm.bin"; +const char *memory_dump_filename = "kernel_dump.bin"; +ocsd_vaddr_t mem_dump_address=0xFFFFFFC000081000; +const ocsd_vaddr_t mem_dump_address_tc2=0xC0008000; + +/* test variables - set by command line to feature test API */ +static int using_mem_acc_cb = 0; /* test the memory access callback function */ +static int use_region_file = 0; /* test multi region memory files */ +static int using_mem_acc_cb_id = 0; /* test the mem acc callback with trace ID parameter */ + +/* buffer to handle a packet string */ +#define PACKET_STR_LEN 1024 +static char packet_str[PACKET_STR_LEN]; + +/* decide if we decode & monitor, decode only or packet print */ +typedef enum _test_op { + TEST_PKT_PRINT, // process trace input into discrete packets and print. + TEST_PKT_DECODE, // process and decode trace packets, printing discrete packets and generic output. + TEST_PKT_DECODEONLY // process and decode trace packets, printing generic output packets only. +} test_op_t; + +// Default test operations +static test_op_t op = TEST_PKT_PRINT; // default operation is to packet print +static ocsd_trace_protocol_t test_protocol = OCSD_PROTOCOL_ETMV4I; // ETMV4 protocl +static uint8_t test_trc_id_override = 0x00; // no trace ID override. + +/* external decoder testing */ +static int test_extern_decoder = 0; /* test the external decoder infrastructure. */ +static ocsd_extern_dcd_fact_t *p_ext_fact; /* external decoder factory */ +#define EXT_DCD_NAME "ext_echo" + +/* raw packet printing test */ +static int frame_raw_unpacked = 0; +static int frame_raw_packed = 0; +static int test_printstr = 0; + +/* test the library printer API */ +static int test_lib_printers = 0; + +/* test the last error / error code api */ +static int test_error_api = 0; + +/* log statistics */ +static int stats = 0; + +/* Process command line options - choose the operation to use for the test. */ +static int process_cmd_line(int argc, char *argv[]) +{ + int idx = 1; + int len = 0; + + while(idx < argc) + { + if (strcmp(argv[idx], "-decode_only") == 0) + { + op = TEST_PKT_DECODEONLY; + } + else if (strcmp(argv[idx], "-decode") == 0) + { + op = TEST_PKT_DECODE; + } + else if (strcmp(argv[idx], "-id") == 0) + { + idx++; + if (idx < argc) + { + test_trc_id_override = (uint8_t)(strtoul(argv[idx], 0, 0)); + printf("ID override = 0x%02X\n", test_trc_id_override); + } + } + else if (strcmp(argv[idx], "-etmv3") == 0) + { + test_protocol = OCSD_PROTOCOL_ETMV3; + selected_snapshot = tc2_snapshot; + mem_dump_address = mem_dump_address_tc2; + } + else if (strcmp(argv[idx], "-ptm") == 0) + { + test_protocol = OCSD_PROTOCOL_PTM; + selected_snapshot = tc2_snapshot; + mem_dump_address = mem_dump_address_tc2; + } + else if (strcmp(argv[idx], "-stm") == 0) + { + test_protocol = OCSD_PROTOCOL_STM; + trace_data_filename = stmtrace_data_filename; + } + else if (strcmp(argv[idx], "-test_cb") == 0) + { + using_mem_acc_cb = 1; + use_region_file = 0; + } + else if (strcmp(argv[idx], "-test_cb_id") == 0) + { + using_mem_acc_cb = 1; + use_region_file = 0; + using_mem_acc_cb_id = 1; + } + else if (strcmp(argv[idx], "-test_region_file") == 0) + { + use_region_file = 1; + using_mem_acc_cb = 0; + } + else if (strcmp(argv[idx], "-extern") == 0) + { + test_extern_decoder = 1; + } + else if (strcmp(argv[idx], "-raw") == 0) + { + frame_raw_unpacked = 1; + } + else if (strcmp(argv[idx], "-stats") == 0) + { + stats = 1; + } + else if (strcmp(argv[idx], "-raw_packed") == 0) + { + frame_raw_packed = 1; + } + else if (strcmp(argv[idx], "-test_printstr") == 0) + { + test_printstr = 1; + } + else if (strcmp(argv[idx], "-test_libprint") == 0) + { + test_lib_printers = 1; + } + else if (strcmp(argv[idx], "-ss_path") == 0) + { + idx++; + if ((idx >= argc) || (strlen(argv[idx]) == 0)) + { + printf("-ss_path: Missing path parameter or zero length\n"); + return -1; + } + else + { + len = strlen(argv[idx]); + if (len > (MAX_TRACE_FILE_PATH_LEN - 32)) + { + printf("-ss_path: path too long\n"); + return -1; + } + usr_snapshot_path = argv[idx]; + } + + } + else if (strcmp(argv[idx], "-test_err_api") == 0) + { + test_error_api = 1; + } + else if(strcmp(argv[idx],"-help") == 0) + { + return -1; + } + else + printf("Ignored unknown argument %s\n", argv[idx]); + idx++; + } + return 0; +} + +static void print_cmd_line_help() +{ + printf("Usage:\n-etmv3|-stm|-ptm|-extern : choose protocol (one only, default etmv4)\n"); + printf("-id <ID> : decode source for id <ID> (default 0x10)\n"); + printf("-decode | -decode_only : full decode + trace packets / full decode packets only (default trace packets only)\n"); + printf("-raw / -raw_packed: print raw unpacked / packed data;\n"); + printf("-test_printstr | -test_libprint : ttest lib printstr callback | test lib based packet printers\n"); + printf("-test_region_file | -test_cb | -test_cb_id : mem accessor - test multi region file API | test callback API [with trcid] (default single memory file)\n\n"); + printf("-ss_path <path> : path from cwd to /snapshots/ directory. Test prog will append required test subdir\n"); +} + +/************************************************************************/ +/* Memory accessor functionality */ +/************************************************************************/ + +static FILE *dump_file = NULL; /* pointer to the file providing the opcode memory */ +static ocsd_mem_space_acc_t dump_file_mem_space = OCSD_MEM_SPACE_ANY; /* memory space used by the dump file */ +static long mem_file_size = 0; /* size of the memory file */ +static ocsd_vaddr_t mem_file_en_address = 0; /* end address last inclusive address in file. */ + +/* log the memacc output */ +/* #define LOG_MEMACC_CB */ + +/* decode memory access using a CallBack function +* tests CB API and add / remove mem acc API. +*/ +static uint32_t do_mem_acc_cb(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trc_id, const uint32_t reqBytes, uint8_t *byteBuffer) +{ + uint32_t read_bytes = 0; + size_t file_read_bytes; + + if(dump_file == NULL) + return 0; + + /* bitwise & the incoming mem space and supported mem space to confirm coverage */ + if(((uint8_t)mem_space & (uint8_t)dump_file_mem_space ) == 0) + return 0; + + /* calculate the bytes that can be read */ + if((address >= mem_dump_address) && (address <= mem_file_en_address)) + { + /* some bytes in our range */ + read_bytes = reqBytes; + + if((address + reqBytes - 1) > mem_file_en_address) + { + /* more than are available - just read the available */ + read_bytes = (uint32_t)(mem_file_en_address - (address - 1)); + } + } + + /* read some bytes if more than 0 to read. */ + if(read_bytes != 0) + { + fseek(dump_file,(long)(address-mem_dump_address),SEEK_SET); + file_read_bytes = fread(byteBuffer,sizeof(uint8_t),read_bytes,dump_file); + if(file_read_bytes < read_bytes) + read_bytes = file_read_bytes; + } +#ifdef LOG_MEMACC_CB + sprintf(packet_str, "mem_acc_cb(addr 0x%08llX, size %d, trcID 0x%02X)\n", address, reqBytes, trc_id); + ocsd_def_errlog_msgout(packet_str); +#endif + return read_bytes; +} + +static uint32_t mem_acc_cb(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer) +{ + return do_mem_acc_cb(p_context, address, mem_space, 0xff, reqBytes, byteBuffer); +} + +static uint32_t mem_acc_id_cb(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trc_id, const uint32_t reqBytes, uint8_t *byteBuffer) +{ + return do_mem_acc_cb(p_context, address, mem_space, trc_id, reqBytes, byteBuffer); +} + + +/* Create the memory accessor using the callback function and attach to decode tree */ +static ocsd_err_t create_mem_acc_cb(dcd_tree_handle_t dcd_tree_h, const char *mem_file_path) +{ + ocsd_err_t err = OCSD_OK; + dump_file = fopen(mem_file_path,"rb"); + if(dump_file != NULL) + { + fseek(dump_file,0,SEEK_END); + mem_file_size = ftell(dump_file); + mem_file_en_address = mem_dump_address + mem_file_size - 1; + + if (using_mem_acc_cb_id) + err = ocsd_dt_add_callback_trcid_mem_acc(dcd_tree_h, mem_dump_address, + mem_file_en_address, dump_file_mem_space, &mem_acc_id_cb, 0); + else + err = ocsd_dt_add_callback_mem_acc(dcd_tree_h, mem_dump_address, + mem_file_en_address, dump_file_mem_space, &mem_acc_cb, 0); + if(err != OCSD_OK) + { + fclose(dump_file); + dump_file = NULL; + } + } + else + err = OCSD_ERR_MEM_ACC_FILE_NOT_FOUND; + return err; +} + +/* remove the callback memory accessor from decode tree */ +static void destroy_mem_acc_cb(dcd_tree_handle_t dcd_tree_h) +{ + if(dump_file != NULL) + { + ocsd_dt_remove_mem_acc(dcd_tree_h,mem_dump_address,dump_file_mem_space); + fclose(dump_file); + dump_file = NULL; + } +} + +/* create and attach the memory accessor according to required test parameters */ +static ocsd_err_t create_test_memory_acc(dcd_tree_handle_t handle) +{ + ocsd_err_t ret = OCSD_OK; + char mem_file_path[MAX_TRACE_FILE_PATH_LEN]; + uint32_t i0adjust = 0x100; + int i = 0; + + /* region list to test multi region memory file API */ + ocsd_file_mem_region_t region_list[4]; + + /* path to the file containing the memory image traced - raw binary data in the snapshot */ + if(usr_snapshot_path != 0) + strcpy(mem_file_path,usr_snapshot_path); + else + strcpy(mem_file_path,default_base_snapshot_path); + strcat(mem_file_path,selected_snapshot); + strcat(mem_file_path,memory_dump_filename); + + /* + * decide how to handle the file - test the normal memory accessor (contiguous binary file), + * a callback accessor or a multi-region file (e.g. similar to using the code region in a .so) + * + * The same memory dump file is used in each case, we just present it differently + * to test the API functions. + */ + + /* memory access callback */ + if(using_mem_acc_cb) + { + ret = create_mem_acc_cb(handle,mem_file_path); + } + /* multi region file */ + else if(use_region_file) + { + + dump_file = fopen(mem_file_path,"rb"); + if(dump_file != NULL) + { + fseek(dump_file,0,SEEK_END); + mem_file_size = ftell(dump_file); + fclose(dump_file); + + /* populate the region list - split existing file into four regions */ + for(i = 0; i < 4; i++) + { + if(i != 0) + i0adjust = 0; + region_list[i].start_address = mem_dump_address + (i * mem_file_size/4) + i0adjust; + region_list[i].region_size = (mem_file_size/4) - i0adjust; + region_list[i].file_offset = (i * mem_file_size/4) + i0adjust; + } + + /* create a memory file accessor - full binary file */ + ret = ocsd_dt_add_binfile_region_mem_acc(handle,®ion_list[0],4,OCSD_MEM_SPACE_ANY,mem_file_path); + } + else + ret = OCSD_ERR_MEM_ACC_FILE_NOT_FOUND; + } + /* create a memory file accessor - simple contiguous full binary file */ + else + { + ret = ocsd_dt_add_binfile_mem_acc(handle,mem_dump_address,OCSD_MEM_SPACE_ANY,mem_file_path); + } + return ret; +} + +/************************************************************************/ +/** Packet printers */ +/************************************************************************/ + +/* +* Callback function to process the packets in the packet processor output stream - +* simply print them out in this case to the library message/error logger. +*/ +ocsd_datapath_resp_t packet_handler(void *context, const ocsd_datapath_op_t op, const ocsd_trc_index_t index_sop, const void *p_packet_in) +{ + ocsd_datapath_resp_t resp = OCSD_RESP_CONT; + int offset = 0; + + switch(op) + { + case OCSD_OP_DATA: + sprintf(packet_str,"Idx:%" OCSD_TRC_IDX_STR "; ", index_sop); + offset = strlen(packet_str); + + /* + * got a packet - convert to string and use the libraries' message output to print to file and stdoout + * Since the test always prints a single ID, we know the protocol type. + */ + if(ocsd_pkt_str(test_protocol,p_packet_in,packet_str+offset,PACKET_STR_LEN-offset) == OCSD_OK) + { + /* add in <CR> */ + if(strlen(packet_str) == PACKET_STR_LEN - 1) /* maximum length */ + packet_str[PACKET_STR_LEN-2] = '\n'; + else + strcat(packet_str,"\n"); + + /* print it using the library output logger. */ + ocsd_def_errlog_msgout(packet_str); + } + else + resp = OCSD_RESP_FATAL_INVALID_PARAM; /* mark fatal error */ + break; + + case OCSD_OP_EOT: + sprintf(packet_str,"**** END OF TRACE ****\n"); + ocsd_def_errlog_msgout(packet_str); + break; + + default: break; + } + + return resp; +} + +/* print an array of hex data - used by the packet monitor to print hex data from packet.*/ +static int print_data_array(const uint8_t *p_array, const int array_size, char *p_buffer, int buf_size) +{ + int chars_printed = 0; + int bytes_processed; + p_buffer[0] = 0; + + if(buf_size > 9) + { + /* set up the header */ + strcat(p_buffer,"[ "); + chars_printed+=2; + + for(bytes_processed = 0; bytes_processed < array_size; bytes_processed++) + { + sprintf(p_buffer+chars_printed,"0x%02X ", p_array[bytes_processed]); + chars_printed += 5; + if((chars_printed + 5) > buf_size) + break; + } + + strcat(p_buffer,"];"); + chars_printed+=2; + } + else if(buf_size >= 4) + { + sprintf(p_buffer,"[];"); + chars_printed+=3; + } + return chars_printed; +} + +/* +* Callback function to process packets and packet data from the monitor output of the +* packet processor. Again print them to the library error logger. +*/ +void packet_monitor( void *context, + const ocsd_datapath_op_t op, + const ocsd_trc_index_t index_sop, + const void *p_packet_in, + const uint32_t size, + const uint8_t *p_data) +{ + int offset = 0; + + switch(op) + { + default: break; + case OCSD_OP_DATA: + sprintf(packet_str,"Idx:%" OCSD_TRC_IDX_STR ";", index_sop); + offset = strlen(packet_str); + offset+= print_data_array(p_data,size,packet_str+offset,PACKET_STR_LEN-offset); + + /* got a packet - convert to string and use the libraries' message output to print to file and stdoout */ + if(ocsd_pkt_str(test_protocol,p_packet_in,packet_str+offset,PACKET_STR_LEN-offset) == OCSD_OK) + { + /* add in <CR> */ + if(strlen(packet_str) == PACKET_STR_LEN - 1) /* maximum length */ + packet_str[PACKET_STR_LEN-2] = '\n'; + else + strcat(packet_str,"\n"); + + /* print it using the library output logger. */ + ocsd_def_errlog_msgout(packet_str); + } + break; + + case OCSD_OP_EOT: + sprintf(packet_str,"**** END OF TRACE ****\n"); + ocsd_def_errlog_msgout(packet_str); + break; + } +} + + +/* +* printer for the generic trace elements when decoder output is being processed +*/ +ocsd_datapath_resp_t gen_trace_elem_print(const void *p_context, const ocsd_trc_index_t index_sop, const uint8_t trc_chan_id, const ocsd_generic_trace_elem *elem) +{ + ocsd_datapath_resp_t resp = OCSD_RESP_CONT; + int offset = 0; + + sprintf(packet_str,"Idx:%" OCSD_TRC_IDX_STR "; TrcID:0x%02X; ", index_sop, trc_chan_id); + offset = strlen(packet_str); + + if(ocsd_gen_elem_str(elem, packet_str+offset,PACKET_STR_LEN - offset) == OCSD_OK) + { + /* add in <CR> */ + if(strlen(packet_str) == PACKET_STR_LEN - 1) /* maximum length */ + packet_str[PACKET_STR_LEN-2] = '\n'; + else + strcat(packet_str,"\n"); + } + else + { + strcat(packet_str,"Unable to create element string\n"); + } + + /* print it using the library output logger. */ + ocsd_def_errlog_msgout(packet_str); + + return resp; +} + +/************************************************************************/ +/** decoder creation **/ + +/*** generic ***/ +static ocsd_err_t create_generic_decoder(dcd_tree_handle_t handle, const char *p_name, const void *p_cfg, const void *p_context) +{ + ocsd_err_t ret = OCSD_OK; + uint8_t CSID = 0; + + if(op == TEST_PKT_PRINT) /* test operation set to packet printing only */ + { + /* + * Create a packet processor on the decode tree for the configuration we have. + * We need to supply the configuration + */ + ret = ocsd_dt_create_decoder(handle,p_name,OCSD_CREATE_FLG_PACKET_PROC,p_cfg,&CSID); + if(ret == OCSD_OK) + { + /* Attach the packet handler to the output of the packet processor - referenced by CSID */ + if (test_lib_printers) + ret = ocsd_dt_set_pkt_protocol_printer(handle, CSID, 0); + else + ret = ocsd_dt_attach_packet_callback(handle,CSID, OCSD_C_API_CB_PKT_SINK,&packet_handler,p_context); + if(ret != OCSD_OK) + ocsd_dt_remove_decoder(handle,CSID); /* if the attach failed then destroy the decoder. */ + } + } + else + { + /* Full decode - need decoder, and memory dump */ + + /* create the packet decoder and packet processor pair from the supplied name */ + ret = ocsd_dt_create_decoder(handle,p_name,OCSD_CREATE_FLG_FULL_DECODER,p_cfg,&CSID); + if(ret == OCSD_OK) + { + if((op != TEST_PKT_DECODEONLY) && (ret == OCSD_OK)) + { + /* + * print the packets as well as the decode - use the packet processors monitor + * output this time, as the main output is attached to the packet decoder. + */ + if (test_lib_printers) + ret = ocsd_dt_set_pkt_protocol_printer(handle, CSID, 1); + else + ret = ocsd_dt_attach_packet_callback(handle,CSID,OCSD_C_API_CB_PKT_MON,packet_monitor,p_context); + } + + /* attach a memory accessor */ + if(ret == OCSD_OK) + ret = create_test_memory_acc(handle); + + /* if the attach failed then destroy the decoder. */ + if(ret != OCSD_OK) + ocsd_dt_remove_decoder(handle,CSID); + } + } + return ret; +} + +/*** ETMV4 specific settings ***/ +static ocsd_err_t create_decoder_etmv4(dcd_tree_handle_t dcd_tree_h) +{ + ocsd_etmv4_cfg trace_config; + + /* + * populate the ETMv4 configuration structure with + * hard coded values from snapshot .ini files. + */ + + trace_config.arch_ver = ARCH_V8; + trace_config.core_prof = profile_CortexA; + + trace_config.reg_configr = 0x000000C1; + trace_config.reg_traceidr = 0x00000010; /* this is the trace ID -> 0x10, change this to analyse other streams in snapshot.*/ + + if(test_trc_id_override != 0) + { + trace_config.reg_traceidr = (uint32_t)test_trc_id_override; + } + test_trc_id_override = trace_config.reg_traceidr; /* remember what ID we actually used */ + + trace_config.reg_idr0 = 0x28000EA1; + trace_config.reg_idr1 = 0x4100F403; + trace_config.reg_idr2 = 0x00000488; + trace_config.reg_idr8 = 0x0; + trace_config.reg_idr9 = 0x0; + trace_config.reg_idr10 = 0x0; + trace_config.reg_idr11 = 0x0; + trace_config.reg_idr12 = 0x0; + trace_config.reg_idr13 = 0x0; + + /* create an ETMV4 decoder - no context needed as we have a single stream to a single handler. */ + return create_generic_decoder(dcd_tree_h,OCSD_BUILTIN_DCD_ETMV4I,(void *)&trace_config,0); +} + +/*** ETMV3 specific settings ***/ +static ocsd_err_t create_decoder_etmv3(dcd_tree_handle_t dcd_tree_h) +{ + ocsd_etmv3_cfg trace_config_etmv3; + + /* + * populate the ETMv3 configuration structure with + * hard coded values from snapshot .ini files. + */ + + trace_config_etmv3.arch_ver = ARCH_V7; + trace_config_etmv3.core_prof = profile_CortexA; + trace_config_etmv3.reg_ccer = 0x344008F2; + trace_config_etmv3.reg_ctrl = 0x10001860; + trace_config_etmv3.reg_idr = 0x410CF250; + trace_config_etmv3.reg_trc_id = 0x010; + if(test_trc_id_override != 0) + { + trace_config_etmv3.reg_trc_id = (uint32_t)test_trc_id_override; + } + test_trc_id_override = trace_config_etmv3.reg_trc_id; /* remember what ID we actually used */ + + /* create an ETMV3 decoder - no context needed as we have a single stream to a single handler. */ + return create_generic_decoder(dcd_tree_h,OCSD_BUILTIN_DCD_ETMV3,(void *)&trace_config_etmv3,0); +} + +/*** PTM specific settings ***/ +static ocsd_err_t create_decoder_ptm(dcd_tree_handle_t dcd_tree_h) +{ + ocsd_ptm_cfg trace_config_ptm; + + /* + * populate the PTM configuration structure with + * hard coded values from snapshot .ini files. + */ + + trace_config_ptm.arch_ver = ARCH_V7; + trace_config_ptm.core_prof = profile_CortexA; + trace_config_ptm.reg_ccer = 0x34C01AC2; + trace_config_ptm.reg_ctrl = 0x10001000; + trace_config_ptm.reg_idr = 0x411CF312; + trace_config_ptm.reg_trc_id = 0x013; + if(test_trc_id_override != 0) + { + trace_config_ptm.reg_trc_id = (uint32_t)test_trc_id_override; + } + test_trc_id_override = trace_config_ptm.reg_trc_id; /* remember what ID we actually used */ + + /* create an PTM decoder - no context needed as we have a single stream to a single handler. */ + return create_generic_decoder(dcd_tree_h,OCSD_BUILTIN_DCD_PTM,(void *)&trace_config_ptm,0); + +} + +/*** STM specific settings ***/ +static ocsd_err_t create_decoder_stm(dcd_tree_handle_t dcd_tree_h) +{ + ocsd_stm_cfg trace_config_stm; + + /* + * populate the STM configuration structure with + * hard coded values from snapshot .ini files. + */ + #define STMTCSR_TRC_ID_MASK 0x007F0000 + #define STMTCSR_TRC_ID_SHIFT 16 + + trace_config_stm.reg_tcsr = 0x00A00005; + if(test_trc_id_override != 0) + { + trace_config_stm.reg_tcsr &= ~STMTCSR_TRC_ID_MASK; + trace_config_stm.reg_tcsr |= ((((uint32_t)test_trc_id_override) << STMTCSR_TRC_ID_SHIFT) & STMTCSR_TRC_ID_MASK); + } + trace_config_stm.reg_feat3r = 0x10000; /* channel default */ + trace_config_stm.reg_devid = 0xFF; /* master default */ + + /* not using hw event trace decode */ + trace_config_stm.reg_hwev_mast = 0; + trace_config_stm.reg_feat1r = 0; + trace_config_stm.hw_event = HwEvent_Unknown_Disabled; + + /* create a STM decoder - no context needed as we have a single stream to a single handler. */ + return create_generic_decoder(dcd_tree_h, OCSD_BUILTIN_DCD_STM, (void *)&trace_config_stm, 0); +} + +static ocsd_err_t create_decoder_extern(dcd_tree_handle_t dcd_tree_h) +{ + echo_dcd_cfg_t trace_cfg_ext; + + /* setup the custom configuration */ + trace_cfg_ext.cs_id = 0x010; + if (test_trc_id_override != 0) + { + trace_cfg_ext.cs_id = (uint32_t)test_trc_id_override; + } + test_trc_id_override = trace_cfg_ext.cs_id; + + /* create an external decoder - no context needed as we have a single stream to a single handler. */ + return create_generic_decoder(dcd_tree_h, EXT_DCD_NAME, (void *)&trace_cfg_ext, 0); +} + +static ocsd_err_t attach_raw_printers(dcd_tree_handle_t dcd_tree_h) +{ + ocsd_err_t err = OCSD_OK; + int flags = 0; + if (frame_raw_unpacked) + flags |= OCSD_DFRMTR_UNPACKED_RAW_OUT; + if (frame_raw_packed) + flags |= OCSD_DFRMTR_PACKED_RAW_OUT; + if (flags) + { + err = ocsd_dt_set_raw_frame_printer(dcd_tree_h, flags); + } + return err; +} + +static void print_output_str(const void *p_context, const char *psz_msg_str, const int str_len) +{ + printf("** CUST_PRNTSTR: %s", psz_msg_str); +} + +static ocsd_err_t test_printstr_cb(dcd_tree_handle_t dcd_tree_h) +{ + ocsd_err_t err = OCSD_OK; + if (test_printstr) + err = ocsd_def_errlog_set_strprint_cb(dcd_tree_h, 0, print_output_str); + return err; +} +/************************************************************************/ + +ocsd_err_t register_extern_decoder() +{ + ocsd_err_t err = OCSD_ERR_NO_PROTOCOL; + + p_ext_fact = ext_echo_get_dcd_fact(); + if (p_ext_fact) + { + err = ocsd_register_custom_decoder(EXT_DCD_NAME, p_ext_fact); + if (err == OCSD_OK) + test_protocol = p_ext_fact->protocol_id; + else + printf("External Decoder Registration: Failed to register decoder."); + } + else + printf("External Decoder Registration: Failed to get decoder factory."); + + return err; +} + +/* create a decoder according to options */ +static ocsd_err_t create_decoder(dcd_tree_handle_t dcd_tree_h) +{ + ocsd_err_t err = OCSD_OK; + + /* extended for the external decoder testing*/ + if (test_extern_decoder) + err = register_extern_decoder(); + if (err != OCSD_OK) + return err; + + switch(test_protocol) + { + case OCSD_PROTOCOL_ETMV4I: + err = create_decoder_etmv4(dcd_tree_h); + break; + + case OCSD_PROTOCOL_ETMV3: + err = create_decoder_etmv3(dcd_tree_h); + break; + + case OCSD_PROTOCOL_STM: + err = create_decoder_stm(dcd_tree_h); + break; + + case OCSD_PROTOCOL_PTM: + err = create_decoder_ptm(dcd_tree_h); + break; + + /* we only register a single external decoder in this test, + so it will always be assigned the first custom protocol ID */ + case OCSD_PROTOCOL_CUSTOM_0: + err = create_decoder_extern(dcd_tree_h); + break; + + default: + err = OCSD_ERR_NO_PROTOCOL; + break; + } + return err; +} + +#define INPUT_BLOCK_SIZE 1024 + +/* process buffer until done or error */ +ocsd_err_t process_data_block(dcd_tree_handle_t dcd_tree_h, int block_index, uint8_t *p_block, const int block_size) +{ + ocsd_err_t ret = OCSD_OK; + uint32_t bytes_done = 0; + ocsd_datapath_resp_t dp_ret = OCSD_RESP_CONT; + uint32_t bytes_this_time = 0; + + while((bytes_done < (uint32_t)block_size) && (ret == OCSD_OK)) + { + if(OCSD_DATA_RESP_IS_CONT(dp_ret)) + { + dp_ret = ocsd_dt_process_data(dcd_tree_h, + OCSD_OP_DATA, + block_index+bytes_done, + block_size-bytes_done, + ((uint8_t *)p_block)+bytes_done, + &bytes_this_time); + bytes_done += bytes_this_time; + } + else if(OCSD_DATA_RESP_IS_WAIT(dp_ret)) + { + dp_ret = ocsd_dt_process_data(dcd_tree_h, OCSD_OP_FLUSH,0,0,NULL,NULL); + } + else + ret = OCSD_ERR_DATA_DECODE_FATAL; /* data path responded with an error - stop processing */ + } + return ret; +} + +void print_statistics(dcd_tree_handle_t dcdtree_handle) +{ + ocsd_decode_stats_t *p_stats = 0; + ocsd_err_t err; + + sprintf(packet_str, "\nReading packet decoder statistics for ID:0x%02x...\n", test_trc_id_override); + ocsd_def_errlog_msgout(packet_str); + + err = ocsd_dt_get_decode_stats(dcdtree_handle, test_trc_id_override, &p_stats); + if (!err && p_stats) + { + sprintf(packet_str, "Total Bytes %ld; Unsynced Bytes: %ld\nBad Header Errors: %d; Bad sequence errors: %d\n", (long)p_stats->channel_total, + (long)p_stats->channel_unsynced, p_stats->bad_header_errs, p_stats->bad_sequence_errs); + ocsd_dt_reset_decode_stats(dcdtree_handle, test_trc_id_override); + } + else + { + sprintf(packet_str, "Not available for this ID.\n"); + } + ocsd_def_errlog_msgout(packet_str); +} + +int process_trace_data(FILE *pf) +{ + ocsd_err_t ret = OCSD_OK; + dcd_tree_handle_t dcdtree_handle = C_API_INVALID_TREE_HANDLE; + uint8_t data_buffer[INPUT_BLOCK_SIZE]; + ocsd_trc_index_t index = 0; + size_t data_read; + + + /* Create a decode tree for this source data. + source data is frame formatted, memory aligned from an ETR (no frame syncs) so create tree accordingly + */ + dcdtree_handle = ocsd_create_dcd_tree(OCSD_TRC_SRC_FRAME_FORMATTED, OCSD_DFRMTR_FRAME_MEM_ALIGN); + + if(dcdtree_handle != C_API_INVALID_TREE_HANDLE) + { + + ret = create_decoder(dcdtree_handle); + ocsd_tl_log_mapped_mem_ranges(dcdtree_handle); + + if (ret == OCSD_OK) + { + /* attach the generic trace element output callback */ + if (test_lib_printers) + ret = ocsd_dt_set_gen_elem_printer(dcdtree_handle); + else + ret = ocsd_dt_set_gen_elem_outfn(dcdtree_handle, gen_trace_elem_print, 0); + } + + + /* raw print and str print cb options tested in their init functions */ + if (ret == OCSD_OK) + ret = test_printstr_cb(dcdtree_handle); + + if (ret == OCSD_OK) + ret = attach_raw_printers(dcdtree_handle); + + + /* now push the trace data through the packet processor */ + while(!feof(pf) && (ret == OCSD_OK)) + { + /* read from file */ + data_read = fread(data_buffer,1,INPUT_BLOCK_SIZE,pf); + if(data_read > 0) + { + /* process a block of data - any packets from the trace stream + we have configured will appear at the callback + */ + ret = process_data_block(dcdtree_handle, + index, + data_buffer, + data_read); + index += data_read; + } + else if(ferror(pf)) + ret = OCSD_ERR_FILE_ERROR; + } + + /* no errors - let the data path know we are at end of trace */ + if(ret == OCSD_OK) + ocsd_dt_process_data(dcdtree_handle, OCSD_OP_EOT, 0,0,NULL,NULL); + + if (stats) { + print_statistics(dcdtree_handle); + } + /* shut down the mem acc CB if in use. */ + if(using_mem_acc_cb) + { + destroy_mem_acc_cb(dcdtree_handle); + } + + /* dispose of the decode tree - which will dispose of any packet processors we created + */ + ocsd_destroy_dcd_tree(dcdtree_handle); + } + else + { + printf("Failed to create trace decode tree\n"); + ret = OCSD_ERR_NOT_INIT; + } + return (int)ret; +} + +#define ERR_BUFFER_SIZE 256 +int test_err_api() +{ + dcd_tree_handle_t dcdtree_handle = C_API_INVALID_TREE_HANDLE; + ocsd_err_t ret = OCSD_OK, err_test; + ocsd_trc_index_t index = 0, err_index = 0; + uint8_t cs_id; + char err_buffer[ERR_BUFFER_SIZE]; + + /* Create a decode tree for this source data. + source data is frame formatted, memory aligned from an ETR (no frame syncs) so create tree accordingly + */ + dcdtree_handle = ocsd_create_dcd_tree(OCSD_TRC_SRC_SINGLE, OCSD_DFRMTR_FRAME_MEM_ALIGN); + + if (dcdtree_handle != C_API_INVALID_TREE_HANDLE) + { + + ret = create_decoder(dcdtree_handle); + if (ret == OCSD_OK) + { + /* attach the generic trace element output callback */ + if (test_lib_printers) + ret = ocsd_dt_set_gen_elem_printer(dcdtree_handle); + else + ret = ocsd_dt_set_gen_elem_outfn(dcdtree_handle, gen_trace_elem_print, 0); + } + + + /* raw print and str print cb options tested in their init functions */ + if (ret == OCSD_OK) + ret = test_printstr_cb(dcdtree_handle); + + if (ret == OCSD_OK) + ret = attach_raw_printers(dcdtree_handle); + + /* feed some duff data into a decoder to provoke an error! */ + uint8_t trace_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x60, 0x71 }; + + if (ret == OCSD_OK) + ret = process_data_block(dcdtree_handle, index, trace_data, sizeof(trace_data)); + + ocsd_err_str(ret, err_buffer, ERR_BUFFER_SIZE); + printf("testing error API for code %d: %s\n", ret, err_buffer); + err_test = ocsd_get_last_err(&err_index, &cs_id, err_buffer, ERR_BUFFER_SIZE); + printf("get last error:\ncode = 0x%02x; trace index %d; cs_id 0x%02x;\nstring: %s\n", err_test, err_index, cs_id, err_buffer); + + } + return ret; +} + +int main(int argc, char *argv[]) +{ + FILE *trace_data; + char trace_file_path[MAX_TRACE_FILE_PATH_LEN]; + int ret = 0, i, len; + char message[512]; + + /* default to juno */ + selected_snapshot = juno_snapshot; + + /* command line params */ + if(process_cmd_line(argc,argv) != 0) + { + print_cmd_line_help(); + return -2; + } + + /* trace data file path */ + if(usr_snapshot_path != 0) + strcpy(trace_file_path,usr_snapshot_path); + else + strcpy(trace_file_path,default_base_snapshot_path); + strcat(trace_file_path,selected_snapshot); + strcat(trace_file_path,trace_data_filename); + printf("opening %s trace data file\n",trace_file_path); + trace_data = fopen(trace_file_path,"rb"); + + if(trace_data != NULL) + { + /* set up the logging in the library - enable the error logger, with an output printer*/ + ret = ocsd_def_errlog_init(OCSD_ERR_SEV_INFO,1); + + /* set up the output - to file and stdout, set custom logfile name */ + if(ret == 0) + ret = ocsd_def_errlog_config_output(C_API_MSGLOGOUT_FLG_FILE | C_API_MSGLOGOUT_FLG_STDOUT, "c_api_test.log"); + + /* print sign-on message in log */ + sprintf(message, "C-API packet print test\nLibrary Version %s\n\n",ocsd_get_version_str()); + ocsd_def_errlog_msgout(message); + + /* print command line used */ + message[0] = 0; + len = 0; + for (i = 0; i < argc; i++) + { + len += strlen(argv[i]) + 1; + if (len < 512) + { + strcat(message, argv[i]); + strcat(message, " "); + } + } + if((len + 2) < 512) + strcat(message, "\n\n"); + ocsd_def_errlog_msgout(message); + + /* process the trace data */ + if (ret == 0) { + if (test_error_api) + ret = test_err_api(); + else + ret = process_trace_data(trace_data); + } + /* close the data file */ + fclose(trace_data); + } + else + { + printf("Unable to open file %s to process trace data\n", trace_file_path); + ret = -1; + } + return ret; +} +/* End of File simple_pkt_c_api.c */ diff --git a/decoder/tests/source/frame_demux_test.cpp b/decoder/tests/source/frame_demux_test.cpp new file mode 100644 index 0000000..69856cc --- /dev/null +++ b/decoder/tests/source/frame_demux_test.cpp @@ -0,0 +1,524 @@ +/* +* \file frame_demux_test.cpp +* \brief OpenCSD: Test the frame demux code for robustness with correct and invalid data. +* +* \copyright Copyright (c) 2022, ARM Limited. All Rights Reserved. +*/ + +/* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Runs sets of test data through the frame demuxer to ensure that it is robust for valid and + * invalid inputs + */ + +#include <cstdio> +#include <string> +#include <iostream> +#include <sstream> +#include <cstring> + +#include "opencsd.h" // the library + + /* Decode tree is the main decoder framework - contains the frame demuxer + and will have an output printer attached to the raw output */ +static DecodeTree* pDecoder = 0; +static const uint32_t base_cfg = OCSD_DFRMTR_FRAME_MEM_ALIGN | + OCSD_DFRMTR_PACKED_RAW_OUT | OCSD_DFRMTR_UNPACKED_RAW_OUT; +static ocsdDefaultErrorLogger err_log; +static ocsdMsgLogger logger; + +/* test data */ +#define ID_BYTE_ID(id) ((uint8_t)(id) << 1 | 0x01) +#define ID_BYTE_DATA(data) ((uint8_t)(data & 0xFE)) +#define FLAGS_BYTE(id0, id1, id2, id3, id4, id5, id6, id7) ((uint8_t) ( \ + ((id7 & 0x1) << 7) | ((id6 & 0x1) << 6) | ((id5 & 0x1) << 5) | ((id4 & 0x1) << 4) | \ + ((id3 & 0x1) << 3) | ((id2 & 0x1) << 2) | ((id1 & 0x1) << 1) | (id0 & 0x1) )) +#define HSYNC_BYTES() 0xff, 0x7f +#define FSYNC_BYTES() 0xff, 0xff, 0xff, 0x7f +#define DATASIZE(array) static const size_t array##_sz = sizeof(array) / sizeof(array[0]) + + +static const uint8_t buf_hsync_fsync[] = { + FSYNC_BYTES(), + ID_BYTE_ID(0x10), 0x01, ID_BYTE_DATA(0x2), 0x03, + HSYNC_BYTES(), ID_BYTE_ID(0x20), 0x4, ID_BYTE_DATA(0x5), 0x6, + ID_BYTE_DATA(0x7), 0x08, HSYNC_BYTES(), ID_BYTE_DATA(0x9), 0xA, + ID_BYTE_ID(0x10), 0x0B, ID_BYTE_DATA(0xC), + FLAGS_BYTE(0, 0, 0, 1, 1, 1, 1, 0), +}; +DATASIZE(buf_hsync_fsync); + +static const uint8_t buf_mem_align[] = { + ID_BYTE_ID(0x10), 0x01, ID_BYTE_DATA(0x02), 0x03, + ID_BYTE_DATA(0x04), 0x05, ID_BYTE_DATA(0x06), 0x07, + ID_BYTE_ID(0x20), 0x08, ID_BYTE_DATA(0x09), 0x0A, + ID_BYTE_DATA(0x0B), 0x0C, ID_BYTE_DATA(0x0D), + FLAGS_BYTE(0, 0, 0, 0, 0, 1, 1, 1), + ID_BYTE_DATA(0x0E), 0x0F, ID_BYTE_ID(0x30), 0x10, + ID_BYTE_DATA(0x11), 0x12, ID_BYTE_DATA(0x13), 0x14, + ID_BYTE_DATA(0x15), 0x16, ID_BYTE_ID(0x10), 0x17, + ID_BYTE_DATA(0x18), 0x19, ID_BYTE_DATA(0x20), + FLAGS_BYTE(0, 0, 1, 1, 1, 1, 0, 0), +}; +DATASIZE(buf_mem_align); + +static const uint8_t buf_mem_align_8id[] = { + ID_BYTE_ID(0x10), 0x01, ID_BYTE_DATA(0x02), 0x03, + ID_BYTE_DATA(0x04), 0x05, ID_BYTE_DATA(0x06), 0x07, + ID_BYTE_ID(0x20), 0x08, ID_BYTE_DATA(0x09), 0x0A, + ID_BYTE_DATA(0x0B), 0x0C, ID_BYTE_DATA(0x0D), + FLAGS_BYTE(0, 0, 0, 0, 0, 1, 1, 1), + // 8 IDs, all with prev flag + ID_BYTE_ID(0x01), 0x0E, ID_BYTE_ID(0x02), 0x0F, + ID_BYTE_ID(0x03), 0x10, ID_BYTE_ID(0x04), 0x11, + ID_BYTE_ID(0x05), 0x12, ID_BYTE_ID(0x06), 0x13, + ID_BYTE_ID(0x07), 0x14, ID_BYTE_DATA(0x50), + FLAGS_BYTE(1, 1, 1, 1, 1, 1, 1, 1), + ID_BYTE_DATA(0x15), 0x16, ID_BYTE_DATA(0x17), 0x18, + ID_BYTE_DATA(0x19), 0x1A, ID_BYTE_DATA(0x1B), 0x1C, + ID_BYTE_ID(0x20), 0x1D, ID_BYTE_DATA(0x1E), 0x1F, + ID_BYTE_DATA(0x20), 0x21, ID_BYTE_DATA(0x22), + FLAGS_BYTE(1, 1, 1, 1, 0, 0, 0, 0), +}; +DATASIZE(buf_mem_align_8id); + +static const uint8_t buf_mem_align_st_rst[] = { + FSYNC_BYTES(), FSYNC_BYTES(), FSYNC_BYTES(), FSYNC_BYTES(), + ID_BYTE_ID(0x10), 0x01, ID_BYTE_DATA(0x02), 0x03, + ID_BYTE_DATA(0x04), 0x05, ID_BYTE_DATA(0x06), 0x07, + ID_BYTE_ID(0x20), 0x08, ID_BYTE_DATA(0x09), 0x0A, + ID_BYTE_DATA(0x0B), 0x0C, ID_BYTE_DATA(0x0D), + FLAGS_BYTE(0, 0, 0, 0, 0, 1, 1, 1), + ID_BYTE_DATA(0x0E), 0x0F, ID_BYTE_ID(0x30), 0x10, + ID_BYTE_DATA(0x11), 0x12, ID_BYTE_DATA(0x13), 0x14, + ID_BYTE_DATA(0x15), 0x16, ID_BYTE_ID(0x10), 0x17, + ID_BYTE_DATA(0x18), 0x19, ID_BYTE_DATA(0x20), + FLAGS_BYTE(0, 0, 1, 1, 1, 1, 0, 0), +}; +DATASIZE(buf_mem_align_st_rst); + +static const uint8_t buf_mem_align_mid_rst[] = { + ID_BYTE_ID(0x10), 0x01, ID_BYTE_DATA(0x02), 0x03, + ID_BYTE_DATA(0x04), 0x05, ID_BYTE_DATA(0x06), 0x07, + ID_BYTE_ID(0x20), 0x08, ID_BYTE_DATA(0x09), 0x0A, + ID_BYTE_DATA(0x0B), 0x0C, ID_BYTE_DATA(0x0D), + FLAGS_BYTE(0, 0, 0, 0, 0, 1, 1, 1), + FSYNC_BYTES(), FSYNC_BYTES(), FSYNC_BYTES(), FSYNC_BYTES(), + ID_BYTE_DATA(0x0E), 0x0F, ID_BYTE_ID(0x30), 0x10, + ID_BYTE_DATA(0x11), 0x12, ID_BYTE_DATA(0x13), 0x14, + ID_BYTE_DATA(0x15), 0x16, ID_BYTE_ID(0x10), 0x17, + ID_BYTE_DATA(0x18), 0x19, ID_BYTE_DATA(0x20), + FLAGS_BYTE(0, 0, 1, 1, 1, 1, 0, 0), +}; +DATASIZE(buf_mem_align_mid_rst); + +static const uint8_t buf_mem_align_en_rst[] = { + ID_BYTE_ID(0x10), 0x01, ID_BYTE_DATA(0x02), 0x03, + ID_BYTE_DATA(0x04), 0x05, ID_BYTE_DATA(0x06), 0x07, + ID_BYTE_ID(0x20), 0x08, ID_BYTE_DATA(0x09), 0x0A, + ID_BYTE_DATA(0x0B), 0x0C, ID_BYTE_DATA(0x0D), + FLAGS_BYTE(0, 0, 0, 0, 0, 1, 1, 1), + ID_BYTE_DATA(0x0E), 0x0F, ID_BYTE_ID(0x30), 0x10, + ID_BYTE_DATA(0x11), 0x12, ID_BYTE_DATA(0x13), 0x14, + ID_BYTE_DATA(0x15), 0x16, ID_BYTE_ID(0x10), 0x17, + ID_BYTE_DATA(0x18), 0x19, ID_BYTE_DATA(0x20), + FLAGS_BYTE(0, 0, 1, 1, 1, 1, 0, 0), + FSYNC_BYTES(), FSYNC_BYTES(), FSYNC_BYTES(), FSYNC_BYTES(), +}; +DATASIZE(buf_mem_align_en_rst); + +static const uint8_t buf_bad_data[] = { +0xff, 0xff, 0xff, 0x7f, 0x30, 0xff, 0x53, 0x54, 0x4d, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0, 0x36, 0xff, 0xb1, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x2b, +0x36, 0x36, 0x3a, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, +0, 0x2c, 0, 0, 0, 0x32, 0x1, 0, +}; +DATASIZE(buf_bad_data); + +static ocsd_err_t initDecoder(int init_opts) +{ + pDecoder = DecodeTree::CreateDecodeTree(OCSD_TRC_SRC_FRAME_FORMATTED, init_opts); + if (!pDecoder) + return OCSD_ERR_MEM; + return OCSD_OK; +} + +static void destroyDecoder() +{ + delete pDecoder; + pDecoder = 0; +} + +static void printTestHeaderStr(const char* hdr_str) +{ + std::ostringstream oss; + + oss << "\n---------------------------------------------------------\n"; + oss << hdr_str; + oss << "\n---------------------------------------------------------\n"; + logger.LogMsg(oss.str()); +} + +static void printSubTestName(const int test_num, const char* name) +{ + std::ostringstream oss; + + oss << "\n..Sub Test " << test_num << " : " << name << "\n"; + logger.LogMsg(oss.str()); +} + +static ocsd_err_t setConfig(uint32_t flags) +{ + TraceFormatterFrameDecoder* pFmt = pDecoder->getFrameDeformatter(); + return pFmt->Configure(flags); + +} + +// fail and print on none RESP_CONT response. +static ocsd_datapath_resp_t checkDataPathValue(ocsd_datapath_resp_t resp, int& failed_count) +{ + if (resp == OCSD_RESP_CONT) + return resp; + + std::ostringstream oss; + oss << "\nTest Datapath error response: " << ocsdDataRespStr(resp).getStr() << "\n"; + logger.LogMsg(oss.str()); + failed_count++; + return resp; +} + +static void resetDecoder(int& failed) +{ + checkDataPathValue(pDecoder->TraceDataIn(OCSD_OP_RESET, 0, 0, 0, 0), failed); +} + + +static void checkInOutSizes(const char *test, size_t in, size_t out, int& failed) +{ + if (in != out) { + failed++; + std::ostringstream oss; + oss << test << " test failed - mismatch between processed and input sizes:"; + oss << " In=" << in << "; Out=" << out; + logger.LogMsg(oss.str()); + } +} + +static int checkResult(int failed) +{ + std::ostringstream oss; + oss << "\nTEST : " << ((failed) ? "FAIL" : "PASS") << "\n"; + logger.LogMsg(oss.str()); + return failed; +} +static int testDemuxInit() +{ + ocsd_err_t err; + std::ostringstream oss; + int failed = 0; + + printTestHeaderStr("Demux Init Tests - check bad input rejected"); + + // init with invalid no flags + oss.str(""); + oss << "\nCheck 0 flag error: "; + err = initDecoder(0); + if (err) { + err = err_log.GetLastError()->getErrorCode(); + } + if (err != OCSD_ERR_INVALID_PARAM_VAL) { + oss << "FAIL: expected error code not returned\n"; + failed++; + } + else + oss << "PASS\n"; + logger.LogMsg(oss.str()); + + // init with invalid unknown flags + oss.str(""); + oss << "\nCheck unknown flag error: "; + err = initDecoder(0x80 | OCSD_DFRMTR_FRAME_MEM_ALIGN); + if (err) { + err = err_log.GetLastError()->getErrorCode(); + } + if (err != OCSD_ERR_INVALID_PARAM_VAL) { + oss << "FAIL: expected error code not returned\n"; + failed++; + } + else + oss << "PASS\n"; + logger.LogMsg(oss.str()); + + // init with bad combo + oss.str(""); + oss << "\nCheck bad combination flag error: "; + err = initDecoder(OCSD_DFRMTR_FRAME_MEM_ALIGN | OCSD_DFRMTR_HAS_FSYNCS); + if (err) { + err = err_log.GetLastError()->getErrorCode(); + } + if (err != OCSD_ERR_INVALID_PARAM_VAL) { + oss << "FAIL: expected error code not returned\n"; + failed++; + } + else + oss << "PASS\n"; + logger.LogMsg(oss.str()); + + return failed; +} + +static int runDemuxBadDataTest() +{ + + int failed = 0; + uint32_t processed = 0; + std::ostringstream oss; + ocsd_datapath_resp_t resp; + + printTestHeaderStr("Demux Bad Data Test - arbitrary test data input"); + + setConfig(base_cfg | OCSD_DFRMTR_RESET_ON_4X_FSYNC); + + // reset the decoder. + resetDecoder(failed); + resp = checkDataPathValue(pDecoder->TraceDataIn(OCSD_OP_DATA, 0, buf_bad_data_sz, buf_bad_data, &processed), failed); + if ((resp == OCSD_RESP_FATAL_INVALID_DATA) && + (err_log.GetLastError()->getErrorCode() == OCSD_ERR_DFMTR_BAD_FHSYNC)) + { + failed--; // cancel the fail - we require that the error happens for bad input + oss << "Got correct error response for invalid input\n"; + } + else + { + oss << "Expected error code not returned\n"; + } + logger.LogMsg(oss.str()); + + setConfig(base_cfg); + return checkResult(failed); +} + +static int runHSyncFSyncTest() +{ + uint32_t cfg_flags = base_cfg; + uint32_t processed = 0, total = 0; + ocsd_trc_index_t index = 0; + int failed = 0; + ocsd_datapath_resp_t resp; + std::ostringstream oss; + + printTestHeaderStr("FSYNC & HSYNC tests: check hander code for TPIU captures works."); + + // set for hsync / fsync operation + cfg_flags &= ~OCSD_DFRMTR_FRAME_MEM_ALIGN; // clear mem align + cfg_flags |= OCSD_DFRMTR_HAS_HSYNCS | OCSD_DFRMTR_HAS_FSYNCS; + setConfig(cfg_flags); + + // straight frame test with fsync + hsync + printSubTestName(1, "HSyncFSync frame"); + resetDecoder(failed); + checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, index, buf_hsync_fsync_sz, buf_hsync_fsync, &processed), + failed); + checkInOutSizes("HSyncFSync frame", buf_hsync_fsync_sz, processed, failed); + + // test fsync broken across 2 input blocks + printSubTestName(2, "HSyncFSync split frame"); + resetDecoder(failed); + checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, index, 2, buf_hsync_fsync, &processed), + failed); + total += processed; + index += processed; + checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, index, buf_hsync_fsync_sz - processed, buf_hsync_fsync + processed, &processed), + failed); + total += processed; + checkInOutSizes("HSyncFSync split frame", buf_hsync_fsync_sz, total, failed); + + // check bad input data is rejected. + printSubTestName(3, "HSyncFSync bad input data"); + resetDecoder(failed); + resp = checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, index, buf_bad_data_sz, buf_bad_data, &processed), + failed); + if ((resp == OCSD_RESP_FATAL_INVALID_DATA) && + (err_log.GetLastError()->getErrorCode() == OCSD_ERR_DFMTR_BAD_FHSYNC)) + { + failed--; // cancel the fail - we require that the error happens for bad input + oss << "Got correct error response for invalid input\n"; + } + else + { + oss << "Expected error code not returned\n"; + } + logger.LogMsg(oss.str()); + + + setConfig(base_cfg); + return checkResult(failed); +} + +static int runMemAlignTest() +{ + uint32_t processed = 0; + int failed = 0; + + printTestHeaderStr("MemAligned Buffer tests: exercise the 16 byte frame buffer handler"); + + // default decoder set to mem align so just run the test. + + // straight frame pair + printSubTestName(1, "MemAlignFrame"); + resetDecoder(failed); + checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, 0, buf_mem_align_sz, buf_mem_align, &processed), + failed); + checkInOutSizes("MemAlignFrame", buf_mem_align_sz, processed, failed); + + // frame with 8 id test + printSubTestName(2, "MemAlignFrame-8-ID"); + resetDecoder(failed); + checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, 0, buf_mem_align_8id_sz, buf_mem_align_8id, &processed), + failed); + checkInOutSizes("MemAlignFrame-8-ID", buf_mem_align_8id_sz, processed, failed); + + // check reset FSYNC frame handling + setConfig(base_cfg | OCSD_DFRMTR_RESET_ON_4X_FSYNC); + printSubTestName(3, "MemAlignFrame-rst_st"); + resetDecoder(failed); + checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, 0, buf_mem_align_st_rst_sz, buf_mem_align_st_rst, &processed), + failed); + checkInOutSizes("MemAlignFrame-rst_st", buf_mem_align_st_rst_sz, processed, failed); + + printSubTestName(4, "MemAlignFrame-rst_mid"); + resetDecoder(failed); + checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, 0, buf_mem_align_mid_rst_sz, buf_mem_align_mid_rst, &processed), + failed); + checkInOutSizes("MemAlignFrame-rst_mid", buf_mem_align_mid_rst_sz, processed, failed); + + printSubTestName(5, "MemAlignFrame-rst_en"); + resetDecoder(failed); + checkDataPathValue( + pDecoder->TraceDataIn(OCSD_OP_DATA, 0, buf_mem_align_en_rst_sz, buf_mem_align_en_rst, &processed), + failed); + checkInOutSizes("MemAlignFrame-rst_en", buf_mem_align_en_rst_sz, processed, failed); + + setConfig(base_cfg); + return checkResult(failed); +} + +int main(int argc, char* argv[]) +{ + int failed = 0; + ocsd_err_t err; + std::ostringstream moss; + RawFramePrinter* framePrinter = 0; + + /* initialise logger */ + + static const int logOpts = ocsdMsgLogger::OUT_STDOUT | ocsdMsgLogger::OUT_FILE; + + logger.setLogOpts(logOpts); + logger.setLogFileName("frame_demux_test.ppl"); + moss << "---------------------------------------------------------\n"; + moss << "Trace Demux Frame Test - check CoreSight frame processing\n"; + moss << "---------------------------------------------------------\n\n"; + moss << "** Library Version : " << ocsdVersion::vers_str() << "\n\n"; + logger.LogMsg(moss.str()); + + /* initialise error logger */ + err_log.initErrorLogger(OCSD_ERR_SEV_INFO); + err_log.setOutputLogger(&logger); + DecodeTree::setAlternateErrorLogger(&err_log); + + /* run the init tests */ + failed += testDemuxInit(); + + /* create a decoder for the remainder of the tests */ + err = initDecoder(base_cfg); + moss.str(""); + moss << "Creating Decoder for active Demux testing\n"; + if (!err && pDecoder) { + err = pDecoder->addRawFramePrinter(&framePrinter, OCSD_DFRMTR_PACKED_RAW_OUT | OCSD_DFRMTR_UNPACKED_RAW_OUT); + if (err) + moss << "Failed to add Frame printer\n"; + } + if (err || !pDecoder) { + + moss << "Failed to initialise decoder for remainder of the tests\nSkipping active demux tests\n"; + failed++; + } + + /* remainder of the tests that need an active decoder */ + if (!err) { + try { + failed += runMemAlignTest(); + failed += runHSyncFSyncTest(); + failed += runDemuxBadDataTest(); + } + catch (ocsdError& err) { + moss.str(""); + moss << "*** TEST ERROR: Unhandled error from tests. Aborting test run ***\n"; + moss << err.getErrorString(err) << "\n"; + logger.LogMsg(moss.str()); + failed++; + } + } + + /* testing done */ + moss.str(""); + moss << "\n\n---------------------------------------------------------\n"; + moss << "Trace Demux Testing Complete\n"; + if (failed) + moss << "FAILED: recorded " << failed << " errors or failures.\n"; + else + moss << "PASSED ALL tests\n"; + moss << "\n\n---------------------------------------------------------\n"; + + logger.LogMsg(moss.str()); + + if (pDecoder) + destroyDecoder(); + + return failed ? -1 : 0; +} diff --git a/decoder/tests/source/mem_buff_demo.cpp b/decoder/tests/source/mem_buff_demo.cpp new file mode 100644 index 0000000..052870f --- /dev/null +++ b/decoder/tests/source/mem_buff_demo.cpp @@ -0,0 +1,421 @@ +/* +* \file mem_buff_demo.cpp +* \brief OpenCSD: using the library with memory buffers for data. +* +* \copyright Copyright (c) 2018, ARM Limited. All Rights Reserved. +*/ + +/* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Example showing techniques to drive library using only memory buffers as input data + * and image data, avoiding file i/o in main processing routines. (File I/O used to + * initially populate buffers but this can be replaced if data is generated by a client + * environment running live.) + */ + +#include <cstdio> +#include <string> +#include <iostream> +#include <sstream> +#include <cstring> + +#include "opencsd.h" // the library + +// uncomment below to use callback function for program memory image +// #define EXAMPLE_USE_MEM_CALLBACK + +/* Input trace buffer */ +static uint8_t *input_trace_data = 0; +static uint32_t input_trace_data_size = 0; + +/* program memory image for decode */ +static uint8_t *program_image_buffer = 0; // buffer for image data. +static uint32_t program_image_size = 0; // size of program image data. +static ocsd_vaddr_t program_image_address = 0; // load address on target of program image. + +/* a message logger to pass to the error logger / decoder. */ +static ocsdMsgLogger logger; + +/* logger callback function - print out error strings */ +class logCallback : public ocsdMsgLogStrOutI +{ +public: + logCallback() {}; + virtual ~logCallback() {}; + virtual void printOutStr(const std::string &outStr) + { + std::cout << outStr.c_str(); + } +}; +static logCallback logCB; + +/* Decode tree is the main decoder framework - contains the frame unpacker, + packet and trace stream decoders, plus memory image references */ +static DecodeTree *pDecoder = 0; + +/* an error logger - Decode tree registers all components with the error logger +so that errors can be correctly attributed and printed if required +*/ +static ocsdDefaultErrorLogger err_log; + +/* callbacks used by the library */ +#ifdef EXAMPLE_USE_MEM_CALLBACK +// program memory image callback definition +uint32_t mem_access_callback_fn(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer); +#endif + +// callback for the decoder output elements +class DecoderOutputProcessor : public ITrcGenElemIn +{ +public: + DecoderOutputProcessor() {}; + virtual ~DecoderOutputProcessor() {}; + + virtual ocsd_datapath_resp_t TraceElemIn(const ocsd_trc_index_t index_sop, + const uint8_t trc_chan_id, + const OcsdTraceElement &elem) + { + // must fully process or make a copy of data in here. + // element reference only valid for scope of call. + + // for the example program we will stringise and print - + // but this is a client program implmentation dependent. + std::string elemStr; + std::ostringstream oss; + oss << "Idx:" << index_sop << "; ID:" << std::hex << (uint32_t)trc_chan_id << "; "; + elem.toString(elemStr); + oss << elemStr << std::endl; + logger.LogMsg(oss.str()); + return OCSD_RESP_CONT; + } +}; +static DecoderOutputProcessor output; + +/* for test purposes we are initialising from files, but this could be generated test data as + part of a larger program and / or compiled in memory images. + + We have hardcoded in one of the snapshots supplied with the library + */ +static int initDataBuffers() +{ + FILE *fp; + std::string filename; + long size; + size_t bytes_read; + + /* the file names to create the data buffers */ +#ifdef _WIN32 + static const char *default_base_snapshot_path = "..\\..\\..\\snapshots"; + static const char *juno_snapshot = "\\juno_r1_1\\"; +#else + static const char *default_base_snapshot_path = "../../../snapshots"; + static const char *juno_snapshot = "/juno_r1_1/"; +#endif + + /* trace data and memory file dump names and values - taken from snapshot metadata */ + static const char *trace_data_filename = "cstrace.bin"; + static const char *memory_dump_filename = "kernel_dump.bin"; + static ocsd_vaddr_t mem_dump_address = 0xFFFFFFC000081000; + + /* load up the trace data */ + filename = default_base_snapshot_path; + filename += (std::string)juno_snapshot; + filename += (std::string)trace_data_filename; + + fp = fopen(filename.c_str(), "rb"); + if (!fp) + return OCSD_ERR_FILE_ERROR; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + input_trace_data_size = (uint32_t)size; + input_trace_data = new (std::nothrow) uint8_t[input_trace_data_size]; + if (!input_trace_data) { + fclose(fp); + return OCSD_ERR_MEM; + } + rewind(fp); + bytes_read = fread(input_trace_data, 1, input_trace_data_size, fp); + fclose(fp); + if (bytes_read < (size_t)input_trace_data_size) + return OCSD_ERR_FILE_ERROR; + + /* load up a memory image */ + filename = default_base_snapshot_path; + filename += (std::string)juno_snapshot; + filename += (std::string)memory_dump_filename; + + fp = fopen(filename.c_str(), "rb"); + if (!fp) + return OCSD_ERR_FILE_ERROR; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + program_image_size = (uint32_t)size; + program_image_buffer = new (std::nothrow) uint8_t[program_image_size]; + if (!program_image_buffer) { + fclose(fp); + return OCSD_ERR_MEM; + } + rewind(fp); + bytes_read = fread(program_image_buffer, 1, program_image_size, fp); + fclose(fp); + if (bytes_read < (size_t)program_image_size) + return OCSD_ERR_FILE_ERROR; + program_image_address = mem_dump_address; + return OCSD_OK; +} + +static ocsd_err_t createETMv4StreamDecoder() +{ + ocsd_etmv4_cfg trace_config; + ocsd_err_t err = OCSD_OK; + EtmV4Config *pCfg = 0; + + /* + * populate the ETMv4 configuration structure with + * hard coded values from snapshot .ini files. + */ + + trace_config.arch_ver = ARCH_V8; + trace_config.core_prof = profile_CortexA; + + trace_config.reg_configr = 0x000000C1; + trace_config.reg_traceidr = 0x00000010; /* this is the trace ID -> 0x10, change this to analyse other streams in snapshot.*/ + trace_config.reg_idr0 = 0x28000EA1; + trace_config.reg_idr1 = 0x4100F403; + trace_config.reg_idr2 = 0x00000488; + trace_config.reg_idr8 = 0x0; + trace_config.reg_idr9 = 0x0; + trace_config.reg_idr10 = 0x0; + trace_config.reg_idr11 = 0x0; + trace_config.reg_idr12 = 0x0; + trace_config.reg_idr13 = 0x0; + + pCfg = new (std::nothrow) EtmV4Config(&trace_config); + if (!pCfg) + return OCSD_ERR_MEM; + + err = pDecoder->createDecoder(OCSD_BUILTIN_DCD_ETMV4I, /* etm v4 decoder */ + OCSD_CREATE_FLG_FULL_DECODER, /* full trace decode */ + pCfg); + delete pCfg; + return err; +} + +/* Create the decode tree and add the error logger, stream decoder, memory image data to it. + Also register the output callback that processes the decoded trace packets. */ +static ocsd_err_t initialiseDecoder() +{ + ocsd_err_t ret = OCSD_OK; + + /* use the creation function to get the type of decoder we want + either OCSD_TRC_SRC_SINGLE : single trace source - not frame formatted + OCSD_TRC_SRC_FRAME_FORMATTED :multi source - CoreSight trace frame + and set the config flags for operation + OCSD_DFRMTR_FRAME_MEM_ALIGN: input data mem aligned -> no syncs + + For this test we create a decode that can unpack frames and is not expecting sync packets. + */ + pDecoder = DecodeTree::CreateDecodeTree(OCSD_TRC_SRC_FRAME_FORMATTED, OCSD_DFRMTR_FRAME_MEM_ALIGN); + if (!pDecoder) + return OCSD_ERR_MEM; + + /* set up decoder logging - the message logger for output, and the error logger for the library */ + logger.setLogOpts(ocsdMsgLogger::OUT_STR_CB); /* no IO from the logger, just a string callback. */ + logger.setStrOutFn(&logCB); /* set the callback - in this example it will go to stdio but this is up to the implementor. */ + + // for debugging - stdio and file +// logger.setLogOpts(ocsdMsgLogger::OUT_FILE | ocsdMsgLogger::OUT_STDOUT); + + err_log.initErrorLogger(OCSD_ERR_SEV_INFO); + err_log.setOutputLogger(&logger); /* pass the output logger to the error logger. */ + + pDecoder->setAlternateErrorLogger(&err_log); /* pass the error logger to the decoder, do not use the library version. */ + + /* now set up the elements that the decoder needs */ + + /* we will decode one of the streams in this example + create a Full decode ETMv4 stream decoder */ + ret = createETMv4StreamDecoder(); + if (ret != OCSD_OK) + return ret; + + /* as this has full decode we must supply a memory image. */ + + ret = pDecoder->createMemAccMapper(); // the mapper is needed to add code images to. + if (ret != OCSD_OK) + return ret; + +#ifdef EXAMPLE_USE_MEM_CALLBACK + // in this example we have a single buffer so we demonstrate how to use a callback. + // we are passing the buffer pointer as context as we only have one buffer, but this + // could be a structure that is a list of memory image buffers. Context is entirely + // client defined. + // Always use OCSD_MEM_SPACE_ANY unless there is a reason to restrict the image to a specific + // memory space. + pDecoder->addCallbackMemAcc(program_image_address, program_image_address + program_image_size-1, + OCSD_MEM_SPACE_ANY,mem_access_callback_fn, program_image_buffer); +#else + // or we can use the built in memory buffer interface - split our one buffer into two to + // demonstrate the addition of multiple regions + ocsd_vaddr_t block1_st, block2_st; + uint32_t block1_sz, block2_sz; + uint8_t *p_block1, *p_block2; + + // break our single buffer into 2 buffers for demo purposes + block1_sz = program_image_size / 2; + block1_sz &= ~0x3; // align + block2_sz = program_image_size - block1_sz; + block1_st = program_image_address; // loaded program memory start address of program + block2_st = program_image_address + block1_sz; + p_block1 = program_image_buffer; + p_block2 = program_image_buffer + block1_sz; + + /* how to add 2 "separate" buffers to the decoder */ + // Always use OCSD_MEM_SPACE_ANY unless there is a reason to restrict the image to a specific + // memory space. + ret = pDecoder->addBufferMemAcc(block1_st, OCSD_MEM_SPACE_ANY, p_block1, block1_sz); + if (ret != OCSD_OK) + return ret; + + ret = pDecoder->addBufferMemAcc(block2_st, OCSD_MEM_SPACE_ANY, p_block2, block2_sz); + if (ret != OCSD_OK) + return ret; +#endif + + /* finally we need to provide an output callback to recieve the decoded information */ + pDecoder->setGenTraceElemOutI(&output); + return ret; +} + +/* get rid of the objects we created */ +static void destroyDecoder() +{ + delete pDecoder; + delete [] input_trace_data; + delete [] program_image_buffer; +} + +#ifdef EXAMPLE_USE_MEM_CALLBACK +/* if we set up to use a callback to access memory image then this is what will be called. */ +/* In this case the client must do all the work in determining if the requested address is in the + memory area. */ +uint32_t mem_access_callback_fn(const void *p_context, const ocsd_vaddr_t address, + const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer) +{ + ocsd_vaddr_t buf_end_address = program_image_address + program_image_size - 1; + uint32_t read_bytes = reqBytes; + + /* context should be our memory image buffer - if not return 0 bytes read */ + if (p_context != program_image_buffer) + return 0; + + /* not concerned with memory spaces - assume all global */ + if ((address < program_image_address) || (address > buf_end_address)) + return 0; // requested address not in our buffer. + + // if requested bytes from address more than we have, only read to end of buffer + if ((address + reqBytes - 1) > buf_end_address) + read_bytes = (uint32_t)(buf_end_address - (address - 1)); + + // copy the requested data. + memcpy(byteBuffer, program_image_buffer + (address - program_image_address), read_bytes); + + return read_bytes; +} +#endif + +/* use the decoder to process the global trace data buffer */ +static ocsd_datapath_resp_t processTraceData(uint32_t *bytes_done) +{ + /* process in blocks of fixed size. */ + #define DATA_CHUNK_SIZE 2048 + + ocsd_datapath_resp_t resp = OCSD_RESP_CONT; + uint32_t block_size, buff_offset, bytes_to_do = input_trace_data_size, bytes_processed; + ocsd_trc_index_t index = 0; + + /* process the data in chunks, until either all done or + * error occurs. + */ + while ((resp == OCSD_RESP_CONT) && (bytes_to_do)) + { + /* size up a block of input data */ + block_size = (bytes_to_do >= DATA_CHUNK_SIZE) ? DATA_CHUNK_SIZE : bytes_to_do; + buff_offset = input_trace_data_size - bytes_to_do; + + /* push it through the decoder */ + resp = pDecoder->TraceDataIn(OCSD_OP_DATA, index, block_size, + input_trace_data + buff_offset, &bytes_processed); + + /* adjust counter per bytes processed */ + bytes_to_do -= bytes_processed; + index += bytes_processed; + } + + /* if all done then signal end of trace - flushes out any remaining data */ + if (!bytes_to_do) + resp = pDecoder->TraceDataIn(OCSD_OP_EOT, 0, 0, 0, 0); + + /* return amount processed */ + *bytes_done = input_trace_data_size - bytes_to_do; + return resp; +} + +/* main routine - init input data, decode, finish ... */ +int main(int argc, char* argv[]) +{ + int ret = OCSD_OK; + ocsd_datapath_resp_t retd; + char msg[256]; + uint32_t bytes_done; + + /* initialise all the data needed for decode */ + if ((ret = initDataBuffers()) != OCSD_OK) + { + logger.LogMsg("Failed to create trace data buffers\n"); + return ret; + } + /* initialise a decoder object */ + if ((ret = initialiseDecoder()) == OCSD_OK) + { + retd = processTraceData(&bytes_done); + if (!OCSD_DATA_RESP_IS_CONT(retd)) + { + ret = OCSD_ERR_DATA_DECODE_FATAL; + logger.LogMsg("Processing failed with data error\n"); + } + + /* get rid of the decoder and print a brief result. */ + destroyDecoder(); + sprintf(msg, "Processed %u bytes out of %u\n", bytes_done, input_trace_data_size); + logger.LogMsg(msg); + } + else + logger.LogMsg("Failed to create decoder for trace processing\n"); + return ret; +} diff --git a/decoder/tests/source/trc_pkt_lister.cpp b/decoder/tests/source/trc_pkt_lister.cpp new file mode 100644 index 0000000..9760351 --- /dev/null +++ b/decoder/tests/source/trc_pkt_lister.cpp @@ -0,0 +1,767 @@ +/* + * \file trc_pkt_lister.cpp + * \brief OpenCSD : Trace Packet Lister Test program + * + * \copyright Copyright (c) 2015, ARM Limited. All Rights Reserved. + */ + + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Test program / utility - list trace packets in supplied snapshot. */ + +#include <cstdio> +#include <string> +#include <iostream> +#include <sstream> +#include <cstring> + +#include "opencsd.h" // the library +#include "trace_snapshots.h" // the snapshot reading test library + +static bool process_cmd_line_opts( int argc, char* argv[]); +static void ListTracePackets(ocsdDefaultErrorLogger &err_logger, SnapShotReader &reader, const std::string &trace_buffer_name); +static bool process_cmd_line_logger_opts(int argc, char* argv[]); +static void log_cmd_line_opts(int argc, char* argv[]); + + // default path +#ifdef WIN32 +static std::string ss_path = ".\\"; +#else +static std::string ss_path = "./"; +#endif + +static std::string source_buffer_name = ""; // source name - used if more than one source +static bool all_source_ids = true; // output all IDs in source. +static std::vector<uint8_t> id_list; // output specific IDs in source + +static ocsdMsgLogger logger; +static int logOpts = ocsdMsgLogger::OUT_STDOUT | ocsdMsgLogger::OUT_FILE; +static std::string logfileName = "trc_pkt_lister.ppl"; +static bool outRawPacked = false; +static bool outRawUnpacked = false; +static bool ss_verbose = false; +static bool decode = false; +static bool no_undecoded_packets = false; +static bool pkt_mon = false; +static int test_waits = 0; +static bool dstream_format = false; +static bool tpiu_format = false; +static bool has_hsync = false; +static bool src_addr_n = false; +static bool stats = false; + +int main(int argc, char* argv[]) +{ + std::ostringstream moss; + + if(process_cmd_line_logger_opts(argc,argv)) + { + printf("Bad logger command line options\nProgram Exiting\n"); + return -2; + } + + logger.setLogOpts(logOpts); + logger.setLogFileName(logfileName.c_str()); + + moss << "Trace Packet Lister: CS Decode library testing\n"; + moss << "-----------------------------------------------\n\n"; + moss << "** Library Version : " << ocsdVersion::vers_str() << "\n\n"; + logger.LogMsg(moss.str()); + + log_cmd_line_opts(argc,argv); + + ocsdDefaultErrorLogger err_log; + err_log.initErrorLogger(OCSD_ERR_SEV_INFO); + err_log.setOutputLogger(&logger); + + if(!process_cmd_line_opts(argc, argv)) + return -1; + + moss.str(""); + moss << "Trace Packet Lister : reading snapshot from path " << ss_path << "\n"; + logger.LogMsg(moss.str()); + + SnapShotReader ss_reader; + ss_reader.setSnapshotDir(ss_path); + ss_reader.setErrorLogger(&err_log); + ss_reader.setVerboseOutput(ss_verbose); + + if(ss_reader.snapshotFound()) + { + if(ss_reader.readSnapShot()) + { + std::vector<std::string> sourceBuffList; + if(ss_reader.getSourceBufferNameList(sourceBuffList)) + { + bool bValidSourceName = false; + // check source name list + if(source_buffer_name.size() == 0) + { + // default to first in the list + source_buffer_name = sourceBuffList[0]; + bValidSourceName = true; + } + else + { + for(size_t i = 0; i < sourceBuffList.size(); i++) + { + if(sourceBuffList[i] == source_buffer_name) + { + bValidSourceName = true; + break; + } + } + } + + if(bValidSourceName) + { + std::ostringstream oss; + oss << "Using " << source_buffer_name << " as trace source\n"; + logger.LogMsg(oss.str()); + ListTracePackets(err_log,ss_reader,source_buffer_name); + } + else + { + std::ostringstream oss; + oss << "Trace Packet Lister : Trace source name " << source_buffer_name << " not found\n"; + logger.LogMsg(oss.str()); + oss.str(""); + oss << "Valid source names are:-\n"; + for(size_t i = 0; i < sourceBuffList.size(); i++) + { + oss << sourceBuffList[i] << "\n"; + } + logger.LogMsg(oss.str()); + } + + } + else + logger.LogMsg("Trace Packet Lister : No trace source buffer names found\n"); + } + else + logger.LogMsg("Trace Packet Lister : Failed to read snapshot\n"); + } + else + { + std::ostringstream oss; + oss << "Trace Packet Lister : Snapshot path" << ss_path << " not found\n"; + logger.LogMsg(oss.str()); + } + + return 0; +} + +void print_help() +{ + std::ostringstream oss; + oss << "Trace Packet Lister - commands\n\n"; + oss << "Snapshot:\n\n"; + oss << "-ss_dir <dir> Set the directory path to a trace snapshot\n"; + oss << "-ss_verbose Verbose output when reading the snapshot\n"; + oss << "\nDecode:\n\n"; + oss << "-id <n> Set an ID to list (may be used multiple times) - default if no id set is for all IDs to be printed\n"; + oss << "-src_name <name> List packets from a given snapshot source name (defaults to first source found)\n"; + oss << "-dstream_format Input is DSTREAM framed.\n"; + oss << "-tpiu Input from TPIU - sync by FSYNC.\n"; + oss << "-tpiu_hsync Input from TPIU - sync by FSYNC and HSYNC.\n"; + oss << "-decode Full decode of the packets from the trace snapshot (default is to list undecoded packets only\n"; + oss << "-decode_only Does not list the undecoded packets, just the trace decode.\n"; + oss << "-o_raw_packed Output raw packed trace frames\n"; + oss << "-o_raw_unpacked Output raw unpacked trace data per ID\n"; + oss << "-test_waits <N> Force wait from packet printer for N packets - test the wait/flush mechanisms for the decoder\n"; + oss << "-src_addr_n ETE protocol: Split source address ranges on N atoms\n"; + oss << "-stats Output packet processing statistics (if available).\n"; + oss << "\nOutput:\n"; + oss << " Setting any of these options cancels the default output to file & stdout,\n using _only_ the options supplied.\n\n"; + oss << "-logstdout Output to stdout -> console.\n"; + oss << "-logstderr Output to stderr.\n"; + oss << "-logfile Output to default file - " << logfileName << "\n"; + oss << "-logfilename <name> Output to file <name> \n"; + + + logger.LogMsg(oss.str()); +} + +void log_cmd_line_opts(int argc, char* argv[]) +{ + std::ostringstream oss; + oss << "Test Command Line:-\n"; + oss << argv[0] << " "; + for(int i = 1; i < argc; i++) + { + oss << argv[i] << " "; + } + oss << "\n\n"; + logger.LogMsg(oss.str()); +} + +// true if element ID filtered out +bool element_filtered(uint8_t elemID) +{ + bool filtered = false; + if(!all_source_ids) + { + filtered = true; + std::vector<uint8_t>::const_iterator it; + it = id_list.begin(); + while((it != id_list.end()) && filtered) + { + if(*it == elemID) + filtered = false; + it++; + } + } + return filtered; +} + +bool process_cmd_line_logger_opts(int argc, char* argv[]) +{ + bool badLoggerOpts = false; + bool bChangingOptFlags = false; + int newlogOpts = ocsdMsgLogger::OUT_NONE; + std::string opt; + if(argc > 1) + { + int options_to_process = argc - 1; + int optIdx = 1; + while(options_to_process > 0) + { + opt = argv[optIdx]; + if(opt == "-logstdout") + { + newlogOpts |= ocsdMsgLogger::OUT_STDOUT; + bChangingOptFlags = true; + } + else if(opt == "-logstderr") + { + newlogOpts |= ocsdMsgLogger::OUT_STDERR; + bChangingOptFlags = true; + } + else if(opt == "-logfile") + { + newlogOpts |= ocsdMsgLogger::OUT_FILE; + bChangingOptFlags = true; + } + else if(opt == "-logfilename") + { + options_to_process--; + optIdx++; + if(options_to_process) + { + logfileName = argv[optIdx]; + newlogOpts |= ocsdMsgLogger::OUT_FILE; + bChangingOptFlags = true; + } + else + { + badLoggerOpts = true; + } + } + options_to_process--; + optIdx++; + } + } + if(bChangingOptFlags) + logOpts = newlogOpts; + return badLoggerOpts; +} + +bool process_cmd_line_opts(int argc, char* argv[]) +{ + bool bOptsOK = true; + std::string opt; + if(argc > 1) + { + int options_to_process = argc - 1; + int optIdx = 1; + while((options_to_process > 0) && bOptsOK) + { + opt = argv[optIdx]; + if(opt == "-ss_dir") + { + options_to_process--; + optIdx++; + if(options_to_process) + ss_path = argv[optIdx]; + else + { + logger.LogMsg("Trace Packet Lister : Error: Missing directory string on -ss_dir option\n"); + bOptsOK = false; + } + } + else if(opt == "-id") + { + options_to_process--; + optIdx++; + if(options_to_process) + { + uint8_t Id = (uint8_t)strtoul(argv[optIdx],0,0); + if((Id == 0) || (Id >= 0x70)) + { + std::ostringstream iderrstr; + iderrstr << "Trace Packet Lister : Error: invalid ID number 0x" << std::hex << (uint32_t)Id << " on -id option" << std::endl; + logger.LogMsg(iderrstr.str()); + bOptsOK = false; + } + else + { + all_source_ids = false; + id_list.push_back(Id); + } + } + else + { + logger.LogMsg("Trace Packet Lister : Error: No ID number on -id option\n"); + bOptsOK = false; + } + } + else if(strcmp(argv[optIdx], "-src_name") == 0) + { + options_to_process--; + optIdx++; + if(options_to_process) + source_buffer_name = argv[optIdx]; + else + { + logger.LogMsg("Trace Packet Lister : Error: Missing source name string on -src_name option\n"); + bOptsOK = false; + } + } + else if(strcmp(argv[optIdx], "-test_waits") == 0) + { + options_to_process--; + optIdx++; + if(options_to_process) + { + test_waits = (int)strtol(argv[optIdx],0,0); + if(test_waits < 0) + test_waits = 0; + } + else + { + logger.LogMsg("Trace Packet Lister : Error: wait count value on -test_waits option\n"); + bOptsOK = false; + } + } + else if(strcmp(argv[optIdx], "-o_raw_packed") == 0) + { + outRawPacked = true; + } + else if(strcmp(argv[optIdx], "-o_raw_unpacked") == 0) + { + outRawUnpacked = true; + } + else if(strcmp(argv[optIdx], "-ss_verbose") == 0) + { + ss_verbose = true; + } + else if(strcmp(argv[optIdx], "-decode") == 0) + { + decode = true; + } + else if(strcmp(argv[optIdx], "-pkt_mon") == 0) + { + pkt_mon = true; + } + else if(strcmp(argv[optIdx], "-decode_only") == 0) + { + no_undecoded_packets = true; + decode = true; + } + else if (strcmp(argv[optIdx], "-src_addr_n") == 0) + { + src_addr_n = true; + } + else if (strcmp(argv[optIdx], "-stats") == 0) + { + stats = true; + } + else if((strcmp(argv[optIdx], "-help") == 0) || (strcmp(argv[optIdx], "--help") == 0) || (strcmp(argv[optIdx], "-h") == 0)) + { + print_help(); + bOptsOK = false; + } + else if((opt == "-logstdout") || (opt == "-logstderr") || + (opt == "-logfile") || (opt == "-logfilename")) + { + // skip all these as processed earlier + + // also additionally skip any filename parameter + if(opt == "-logfilename") + { + options_to_process--; + optIdx++; + } + } + else if (strcmp(argv[optIdx], "-dstream_format") == 0) + { + dstream_format = true; + } + else if (strcmp(argv[optIdx], "-tpiu") == 0) + { + tpiu_format = true; + } + else if (strcmp(argv[optIdx], "-tpiu_hsync") == 0) + { + has_hsync = true; + tpiu_format = true; + } + else + { + std::ostringstream errstr; + errstr << "Trace Packet Lister : Warning: Ignored unknown option " << argv[optIdx] << "." << std::endl; + logger.LogMsg(errstr.str()); + } + options_to_process--; + optIdx++; + } + + } + return bOptsOK; +} + +// +// if decoding the gen elem printer will be injecting waits, but we may ge a cont from the packet processors if a complete packet is not available. +// if packet processing only, then waits will be coming from there until the count is extinguished +// wait testing with packet processor only really works correctly if we are doing a single source as there is no way at this +// point to know which source has sent the _WAIT. with multi packet processor waiting may get false warnings once the _WAITs run out. +bool ExpectingPPrintWaitResp(DecodeTree *dcd_tree, TrcGenericElementPrinter &genElemPrinter) +{ + bool ExpectingWaits = false; + std::vector<ItemPrinter *> &printers = dcd_tree->getPrinterList(); + if(test_waits > 0) + { + // see if last response was from the Gen elem printer expecting a wait + ExpectingWaits = genElemPrinter.needAckWait(); + + // now see if any of the active packet printers are returing wait responses. + if(!ExpectingWaits) + { + std::vector<ItemPrinter *>::iterator it; + it = printers.begin(); + while((it != printers.end()) && !ExpectingWaits) + { + ExpectingWaits = (bool)((*it)->getTestWaits() != 0); + it++; + } + } + + // nothing waiting - and no outstanding wait cycles in the Gen elem printer. + if(!ExpectingWaits && (genElemPrinter.getTestWaits() == 0)) + test_waits = 0; // zero out the input value if none of the printers currently have waits scheduled. + } + return ExpectingWaits; +} + +void AttachPacketPrinters( DecodeTree *dcd_tree) +{ + uint8_t elemID; + std::ostringstream oss; + + // attach packet printers to each trace source in the tree + DecodeTreeElement *pElement = dcd_tree->getFirstElement(elemID); + while(pElement && !no_undecoded_packets) + { + if(!element_filtered(elemID)) + { + oss.str(""); + + ItemPrinter *pPrinter; + ocsd_err_t err = dcd_tree->addPacketPrinter(elemID, (bool)(decode || pkt_mon),&pPrinter); + if (err == OCSD_OK) + { + // if not decoding or monitor only + if((!(decode || pkt_mon)) && test_waits) + pPrinter->setTestWaits(test_waits); + + oss << "Trace Packet Lister : Protocol printer " << pElement->getDecoderTypeName() << " on Trace ID 0x" << std::hex << (uint32_t)elemID << "\n"; + } + else + oss << "Trace Packet Lister : Failed to Protocol printer " << pElement->getDecoderTypeName() << " on Trace ID 0x" << std::hex << (uint32_t)elemID << "\n"; + logger.LogMsg(oss.str()); + + } + pElement = dcd_tree->getNextElement(elemID); + } + +} + +void ConfigureFrameDeMux(DecodeTree *dcd_tree, RawFramePrinter **framePrinter) +{ + // configure the frame deformatter, and attach a frame printer to the frame deformatter if needed + TraceFormatterFrameDecoder *pDeformatter = dcd_tree->getFrameDeformatter(); + if(pDeformatter != 0) + { + // configuration - memory alinged buffer + uint32_t configFlags = pDeformatter->getConfigFlags(); + + // check for TPIU FSYNC & HSYNC + if (tpiu_format) configFlags |= OCSD_DFRMTR_HAS_FSYNCS; + if (has_hsync) configFlags |= OCSD_DFRMTR_HAS_HSYNCS; + // if FSYNC (& HSYNC) - cannot be mem frame aligned. + if (tpiu_format) configFlags &= ~OCSD_DFRMTR_FRAME_MEM_ALIGN; + + if (!configFlags) + { + configFlags = OCSD_DFRMTR_FRAME_MEM_ALIGN; + } + pDeformatter->Configure(configFlags); + + if (outRawPacked || outRawUnpacked) + { + if (outRawPacked) configFlags |= OCSD_DFRMTR_PACKED_RAW_OUT; + if (outRawUnpacked) configFlags |= OCSD_DFRMTR_UNPACKED_RAW_OUT; + dcd_tree->addRawFramePrinter(framePrinter, configFlags); + } + } +} + +void PrintDecodeStats(DecodeTree *dcd_tree) +{ + uint8_t elemID; + std::ostringstream oss; + ocsd_decode_stats_t *pStats = 0; + ocsd_err_t err; + bool gotDemuxStats = false; + ocsd_demux_stats_t demux_stats; + + oss << "\nReading packet decoder statistics....\n\n"; + logger.LogMsg(oss.str()); + + DecodeTreeElement *pElement = dcd_tree->getFirstElement(elemID); + while (pElement) + { + oss.str(""); + err = dcd_tree->getDecoderStats(elemID, &pStats); + if (!err && pStats) + { + oss << "Decode stats ID 0x" << std::hex << (uint32_t)elemID << "\n"; + oss << "Total Bytes: " << std::dec << pStats->channel_total << "; Unsynced Bytes: " << std::dec << pStats->channel_unsynced << "\n"; + oss << "Bad Header Errors: " << std::dec << pStats->bad_header_errs << "; Bad Sequence Errors: " << std::dec << pStats->bad_sequence_errs << "\n"; + + // demux stats same for all IDs - grab them at the first opportunity.. + if (!gotDemuxStats) { + memcpy(&demux_stats, &pStats->demux, sizeof(ocsd_demux_stats_t)); + gotDemuxStats = true; + } + + } + else + oss << "Decode stats unavailable on Trace ID 0x" << std::hex << (uint32_t)elemID << "\n"; + + + logger.LogMsg(oss.str()); + pElement = dcd_tree->getNextElement(elemID); + } + + // if we have copied over the stats and there is at least 1 frame byte (impossible for there to be 0 if demuxing) + if (gotDemuxStats && demux_stats.frame_bytes) { + uint64_t total = demux_stats.valid_id_bytes + demux_stats.no_id_bytes + demux_stats.unknown_id_bytes + + demux_stats.reserved_id_bytes + demux_stats.frame_bytes; + oss.str(""); + oss << "\nFrame Demux Stats\n"; + oss << "Trace data bytes sent to registered ID decoders: " << std::dec << demux_stats.valid_id_bytes << "\n"; + oss << "Trace data bytes without registered ID decoders: " << std::dec << demux_stats.no_id_bytes << "\n"; + oss << "Trace data bytes with unknown ID: " << std::dec << demux_stats.unknown_id_bytes << "\n"; + oss << "Trace data bytes with reserved ID: " << std::dec << demux_stats.reserved_id_bytes << "\n"; + oss << "Frame demux bytes, ID bytes and sync bytes: " << std::dec << demux_stats.frame_bytes << "\n"; + oss << "Total bytes processed by frame demux: " << std::dec << total << "\n\n"; + logger.LogMsg(oss.str()); + } +} + +void ListTracePackets(ocsdDefaultErrorLogger &err_logger, SnapShotReader &reader, const std::string &trace_buffer_name) +{ + CreateDcdTreeFromSnapShot tree_creator; + + tree_creator.initialise(&reader, &err_logger); + + if(tree_creator.createDecodeTree(trace_buffer_name, (decode == false), src_addr_n ? ETE_OPFLG_PKTDEC_SRCADDR_N_ATOMS : 0)) + { + DecodeTree *dcd_tree = tree_creator.getDecodeTree(); + dcd_tree->setAlternateErrorLogger(&err_logger); + + RawFramePrinter *framePrinter = 0; + TrcGenericElementPrinter *genElemPrinter = 0; + + AttachPacketPrinters(dcd_tree); + + ConfigureFrameDeMux(dcd_tree, &framePrinter); + + // if decoding set the generic element printer to the output interface on the tree. + if(decode) + { + std::ostringstream oss; + //dcd_tree->setGenTraceElemOutI(genElemPrinter); + dcd_tree->addGenElemPrinter(&genElemPrinter); + oss << "Trace Packet Lister : Set trace element decode printer\n"; + logger.LogMsg(oss.str()); + genElemPrinter->setTestWaits(test_waits); + } + + if(decode) + dcd_tree->logMappedRanges(); // print out the mapped ranges + + // check if we have attached at least one printer + if(decode || (PktPrinterFact::numPrinters(dcd_tree->getPrinterList()) > 0)) + { + // set up the filtering at the tree level (avoid pushing to processors with no attached printers) + if(!all_source_ids) + dcd_tree->setIDFilter(id_list); + else + dcd_tree->clearIDFilter(); + + // need to push the data through the decode tree. + std::ifstream in; + in.open(tree_creator.getBufferFileName(),std::ifstream::in | std::ifstream::binary); + if(in.is_open()) + { + ocsd_datapath_resp_t dataPathResp = OCSD_RESP_CONT; + static const int bufferSize = 1024; + uint8_t trace_buffer[bufferSize]; // temporary buffer to load blocks of data from the file + uint32_t trace_index = 0; // index into the overall trace buffer (file). + + // process the file, a buffer load at a time + while(!in.eof() && !OCSD_DATA_RESP_IS_FATAL(dataPathResp)) + { + if (dstream_format) + { + in.read((char *)&trace_buffer[0], 512 - 8); + } + else + in.read((char *)&trace_buffer[0],bufferSize); // load a block of data into the buffer + + std::streamsize nBuffRead = in.gcount(); // get count of data loaded. + std::streamsize nBuffProcessed = 0; // amount processed in this buffer. + uint32_t nUsedThisTime = 0; + + // process the current buffer load until buffer done, or fatal error occurs + while((nBuffProcessed < nBuffRead) && !OCSD_DATA_RESP_IS_FATAL(dataPathResp)) + { + if(OCSD_DATA_RESP_IS_CONT(dataPathResp)) + { + dataPathResp = dcd_tree->TraceDataIn( + OCSD_OP_DATA, + trace_index, + (uint32_t)(nBuffRead - nBuffProcessed), + &(trace_buffer[0])+nBuffProcessed, + &nUsedThisTime); + + nBuffProcessed += nUsedThisTime; + trace_index += nUsedThisTime; + + // test printers can inject _WAIT responses - see if we are expecting one... + if(ExpectingPPrintWaitResp(dcd_tree, *genElemPrinter)) + { + if(OCSD_DATA_RESP_IS_CONT(dataPathResp)) + { + // not wait or fatal - log a warning here. + std::ostringstream oss; + oss << "Trace Packet Lister : WARNING : Data in; data Path expected WAIT response\n"; + logger.LogMsg(oss.str()); + } + } + } + else // last response was _WAIT + { + // may need to acknowledge a wait from the gen elem printer + if(genElemPrinter->needAckWait()) + genElemPrinter->ackWait(); + + // dataPathResp not continue or fatal so must be wait... + dataPathResp = dcd_tree->TraceDataIn(OCSD_OP_FLUSH,0,0,0,0); + } + } + + /* dump dstream footers */ + if (dstream_format) { + in.read((char *)&trace_buffer[0], 8); + if (outRawPacked) + { + std::ostringstream oss; + oss << "DSTREAM footer ["; + for (int i = 0; i < 8; i++) + { + oss << "0x" << std::hex << (int)trace_buffer[i] << " "; + } + oss << "]\n"; + logger.LogMsg(oss.str()); + } + } + } + + // fatal error - no futher processing + if(OCSD_DATA_RESP_IS_FATAL(dataPathResp)) + { + std::ostringstream oss; + oss << "Trace Packet Lister : Data Path fatal error\n"; + logger.LogMsg(oss.str()); + ocsdError *perr = err_logger.GetLastError(); + if(perr != 0) + logger.LogMsg(ocsdError::getErrorString(perr)); + + } + else + { + // mark end of trace into the data path + dcd_tree->TraceDataIn(OCSD_OP_EOT,0,0,0,0); + } + + // close the input file. + in.close(); + + std::ostringstream oss; + oss << "Trace Packet Lister : Trace buffer done, processed " << trace_index << " bytes.\n"; + logger.LogMsg(oss.str()); + if (stats) + PrintDecodeStats(dcd_tree); + } + else + { + std::ostringstream oss; + oss << "Trace Packet Lister : Error : Unable to open trace buffer.\n"; + logger.LogMsg(oss.str()); + } + + } + else + { + std::ostringstream oss; + oss << "Trace Packet Lister : No supported protocols found.\n"; + logger.LogMsg(oss.str()); + } + + // clean up + + // get rid of the decode tree. + tree_creator.destroyDecodeTree(); + } +} + +/* End of File trc_pkt_lister.cpp */ |