diff options
Diffstat (limited to 'decoder/tests/source/mem_buff_demo.cpp')
-rw-r--r-- | decoder/tests/source/mem_buff_demo.cpp | 421 |
1 files changed, 421 insertions, 0 deletions
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; +} |