blob: 3a4917a0bbf6fd6bbd975f5ce0b3e7823f02de29 [file] [edit]
/*
* \file itm_decode_test.cpp
* \brief OpenCSD : Tests for ITM decoder
*
* \copyright Copyright (c) 2024, 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 - runs on internal sets of test data. */
#include <cstdio>
#include <string>
#include <iostream>
#include <sstream>
#include <cstring>
#include "opencsd.h" // the library
#include "itm_test.h" // header to access test data
#include "common/cs_frame_mux_data.h"
static ocsdMsgLogger logger;
static int logOpts = ocsdMsgLogger::OUT_STDOUT | ocsdMsgLogger::OUT_FILE;
static std::string logfileName = "itm_decode_test.ppl";
static ITMConfig config;
static ocsdDefaultErrorLogger errLogger;
static IDecoderMngr* pDecoderMngr = 0; // decoder manager - handle creation and destruction of ITM decoders
static ITrcDataIn* pDecoderIn = 0; // data in interface
static TraceComponent* pItmTraceComp = 0; // itm decoder
static std::vector<ItemPrinter*> printer_list; // list of object printers - for printer factory to descroy later
static std::string testName = "all";
static std::string dumpFileName = "itm_testdata";
static std::string dumpFileExt = ".bin";
static std::string inFileName = "";
static bool bProcessInFile = false;
static uint8_t muxCSID = 0; // CSID to read / dump with coresight format
static bool bDoFileDump = false;
static bool muteIDPrint = false; // test option : remove ID / Idx from print to allow compare of idfferent formats
static TraceFormatterFrameDecoder cs_frame_demux;
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
{
// no file name following -infile
std::ostringstream errstr;
errstr << "Missing <filename> following -logfilename option\n";
logger.LogMsg(errstr.str());
badLoggerOpts = true;
}
}
options_to_process--;
optIdx++;
}
}
if (bChangingOptFlags)
logOpts = newlogOpts;
return badLoggerOpts;
}
void print_help()
{
std::ostringstream oss;
oss << "ITM decode test - commands\n\n";
oss << "\nRunning built in tests:\n\n";
oss << "-test <name> Run test named \"name\", run all tests otherwise\n";
oss << "-list List available tests then exit\n";
oss << "\nUsing an input file for decode\n";
oss << "-infile <filename> use <filename> as binary input of raw ITM data, instead of built in tests\n";
oss << "-csid <id> set CSID to use for input file. Implies input file is CoreSight frame formatted\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";
// undocumented dev & test only options
// -fdump - dump all tests as a single buffer in a binary file. use -csid to CS frame format
// -muteid - prevent the printing of Idx:<N> ID:<M> in the files to better compare outputs between raw and CS files.
logger.LogMsg(oss.str());
}
void dump_test_data()
{
itm_test_data_t* tests = get_item_tests();
itm_test_data_t* curr_test = 0;
std::ostringstream oss;
int test_idx = 0;
int bytes = 0;
std::ofstream outfile;
CSFrameMuxData frameMux;
curr_test = &tests[test_idx];
oss << "Dumping test data to binary file";
if (muxCSID)
{
oss << " using 0x" << std::hex << std::setw(2) << std::setfill('0') << (uint16_t)muxCSID << " as trace ID in CoreSight formatted frames\n\n";
}
else
{
oss << " as raw ITM data with no CoreSight frame formatting\n\n";
}
logger.LogMsg(oss.str());
// create the dump file name - add CS ID value if used.
oss.str("");
oss << dumpFileName;
if (muxCSID)
oss << "_ID_0x" << std::hex << std::setw(2) << std::setfill('0') << (uint16_t)muxCSID;
oss << dumpFileExt;
dumpFileName = oss.str();
outfile.open(dumpFileName, std::ofstream::out | std::ofstream::binary);
if (!outfile.is_open()) {
oss.str("");
oss << "Error: failed to open file " << dumpFileName << "\n";
logger.LogMsg(oss.str());
}
while (curr_test->test_name != 0)
{
oss.str("Writing test ");
oss << curr_test->test_name << ", " << std::dec << curr_test->test_data_size << " bytes\n";
logger.LogMsg(oss.str());
if (muxCSID)
frameMux.muxInData(curr_test->test_packets, curr_test->test_data_size, muxCSID, false);
else
outfile.write((const char *)curr_test->test_packets, curr_test->test_data_size);
bytes += curr_test->test_data_size;
curr_test = &tests[++test_idx];
}
if (muxCSID)
{
// write framed data to the file - once padded.
if (frameMux.hasIncompleteFrame())
frameMux.muxInData(0, 0, 0, true);
// write frame buffer direct to file.
outfile.write((const char *)frameMux.getFrameBuffer(), frameMux.getFrameBufferSize());
// clear the frames we copied directly
frameMux.clearFrames((int)frameMux.getFrameBufferSize() / 16);
}
outfile.close();
oss.str("");
oss << "File dump complete - ";
oss << bytes << " total protocol bytes\n";
logger.LogMsg(oss.str());
}
void list_tests()
{
itm_test_data_t* tests = get_item_tests();
itm_test_data_t* curr_test = 0;
std::ostringstream oss;
int test_idx = 0;
curr_test = &tests[test_idx];
oss << "List of built in tests to run:-\n\n";
while (curr_test->test_name != 0)
{
oss << "Name: " << curr_test->test_name << "\nDescription: " << curr_test->test_description << "\n\n";
curr_test = &tests[++test_idx];
}
logger.LogMsg(oss.str());
}
bool process_cmd_line_opts(int argc, char* argv[], bool &optExit)
{
bool bOptsOK = true;
std::string opt;
std::ostringstream errstr;
optExit = false;
if (argc > 1)
{
int options_to_process = argc - 1;
int optIdx = 1;
while ((options_to_process > 0) && bOptsOK)
{
opt = argv[optIdx];
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 (opt == "-test")
{
options_to_process--;
optIdx++;
if (options_to_process)
{
testName = argv[optIdx];
}
else
{
// no test name following test
bOptsOK = false;
}
}
else if ((opt == "-help") || (opt == "--help") || (opt == "-h"))
{
print_help();
optExit = true;
bOptsOK = false;
}
else if (opt == "-list")
{
list_tests();
optExit = true;
bOptsOK = false;
}
else if (opt == "-fdump")
{
bDoFileDump = true;
}
else if (opt == "-csid")
{
options_to_process--;
optIdx++;
if (options_to_process)
{
muxCSID = ((uint8_t)strtoul(argv[optIdx], 0, 0)) & 0x7f;
}
else
{
bOptsOK = false;
errstr << "Missing CSID following -csid option\n";
logger.LogMsg(errstr.str());
}
}
else if (opt == "-muteid")
{
muteIDPrint = true;
}
else if (opt == "-infile")
{
options_to_process--;
optIdx++;
if (options_to_process)
{
inFileName = argv[optIdx];
bProcessInFile = true;
}
else
{
// no file name following -infile
bOptsOK = false;
errstr << "Missing <filename> following -infile option\n";
logger.LogMsg(errstr.str());
}
}
else
{
errstr << "ITM Decode test : Warning: Ignored unknown option " << argv[optIdx] << "." << std::endl;
logger.LogMsg(errstr.str());
}
options_to_process--;
optIdx++;
}
}
if (bDoFileDump && bOptsOK)
{
dump_test_data();
optExit = true;
bOptsOK = false;
}
return bOptsOK;
}
void setup_config()
{
ocsd_itm_cfg cfg;
// zero out control register
cfg.reg_tcr = 0;
// add in any CS ID value.
cfg.reg_tcr |= ((uint32_t)(muxCSID & 0x7f)) << 16;
config = &cfg;
}
void destroy_itm_objects()
{
if (pDecoderMngr && pItmTraceComp)
{
pDecoderMngr->destroyDecoder(pItmTraceComp);
pItmTraceComp = 0;
}
PktPrinterFact::destroyAllPrinters(printer_list);
pDecoderMngr = 0;
}
/* not using the normal decode tree - create and connect all elements for ITM decode
We get the decoder manager for ITM protocol from the list of registered decoders in the
library and use this to create the decode path.
Data flow:
[ binary data ] / [input file]
|
V
{{ CS frame deformatter }} := optional iff file input and csid set
|
V
{ ITrcDataIn }
|
V
{ TrcPktProcItm } -> { IPktRawDataMon } -> [ Itm packet printer using ItmTrcPacket ]
|
V
{ TrcPktDecodeItm }
|
V
[ TrcGenericElementPrinter for OcsdTraceElement : Packet printer for decoder generic elements ]
*/
ocsd_err_t setup_decoder()
{
int crtFlags = OCSD_CREATE_FLG_FULL_DECODER;
ocsd_err_t err = OCSD_OK;
uint8_t CSID;
ItemPrinter* pItemPrinter = 0;
// grab the list of decoders in the library
OcsdLibDcdRegister* lib_reg = OcsdLibDcdRegister::getDecoderRegister();
if (lib_reg == 0)
return OCSD_ERR_NOT_INIT;
// find the manager for the named ITM decoder
if ((err = lib_reg->getDecoderMngrByName(OCSD_BUILTIN_DCD_ITM, &pDecoderMngr)) != OCSD_OK)
return err;
// create the decoder - creates both TrcPktProcItm and TrcPktDecodeItm and attaches them together
setup_config();
CSID = config.getTraceID();
if ((err = pDecoderMngr->createDecoder(crtFlags, (int)CSID, &config, &pItmTraceComp)) != OCSD_OK)
return err;
// add an error logger
if (err == OCSD_OK)
err = pDecoderMngr->attachErrorLogger(pItmTraceComp, &errLogger);
// get a pointer to the input interface of the decoder
if (err == OCSD_OK)
err = pDecoderMngr->getDataInputI(pItmTraceComp, &pDecoderIn);
// if we are inputting a file and it has muxCSID - create the links to the demux here
if (bProcessInFile && muxCSID && (err == OCSD_OK))
{
// we assume no syncs for these tests
err = cs_frame_demux.Init();
if (err == OCSD_OK)
err = cs_frame_demux.Configure(OCSD_DFRMTR_FRAME_MEM_ALIGN);
if (err == OCSD_OK)
err = cs_frame_demux.getErrLogAttachPt()->attach(&errLogger);
// attach decoder to CSID input of demux
if (err == OCSD_OK)
err = cs_frame_demux.getIDStreamAttachPt(CSID)->attach(pDecoderIn);
// set data in to the demux input
if (err == OCSD_OK)
pDecoderIn = dynamic_cast<ITrcDataIn*>(&cs_frame_demux);
}
// create a packet printer typed for ITM printing
pItemPrinter = PktPrinterFact::createProtocolPrinter(printer_list, OCSD_PROTOCOL_ITM, CSID, &logger);
if (!pItemPrinter)
err = OCSD_ERR_MEM;
// attach the packet printer to the monitor output of the ITM packet processor
if (err == OCSD_OK)
{
// cast the base printer class to its protocol specific class.
PacketPrinter<ItmTrcPacket>* pTPrinter = dynamic_cast<PacketPrinter<ItmTrcPacket> *>(pItemPrinter);
err = pDecoderMngr->attachPktMonitor(pItmTraceComp, (IPktRawDataMon<ItmTrcPacket> *) pTPrinter);
pTPrinter->muteIDPrint(muteIDPrint);
}
// use printer factory to create a generic element output printer and attach message logger.
if (err == OCSD_OK) {
TrcGenericElementPrinter* pOutPrinter = PktPrinterFact::createGenElemPrinter(printer_list, &logger);
if (pOutPrinter)
{
// output for the decoded trace
err = pDecoderMngr->attachOutputSink(pItmTraceComp, pOutPrinter);
pOutPrinter->muteIDPrint(muteIDPrint);
}
else
err = OCSD_ERR_MEM;
}
// if any error after we created the decoder - destroy it now.
if (err != OCSD_OK)
destroy_itm_objects();
return err;
}
ocsd_err_t process_block(ocsd_trc_index_t &trace_index, const uint8_t* data, const uint32_t block_size,
uint32_t& num_processed, ocsd_datapath_resp_t &dataPathResp)
{
ocsd_err_t err = OCSD_OK;
uint32_t processedThisPass;
dataPathResp = OCSD_RESP_CONT;
while ((num_processed < block_size) && !OCSD_DATA_RESP_IS_FATAL(dataPathResp))
{
dataPathResp = pDecoderIn->TraceDataIn(OCSD_OP_DATA,
trace_index,
block_size - num_processed,
data + num_processed,
&processedThisPass);
num_processed += processedThisPass;
trace_index += processedThisPass;
}
// fatal error - no futher processing
if (OCSD_DATA_RESP_IS_FATAL(dataPathResp))
{
std::ostringstream oss;
oss << "Itm Test : Data Path fatal error\n";
logger.LogMsg(oss.str());
ocsdError* perr = errLogger.GetLastError();
if (perr != 0)
{
logger.LogMsg(ocsdError::getErrorString(perr));
err = perr->getErrorCode();
}
else
err = OCSD_ERR_FAIL;
}
return err;
}
ocsd_err_t run_test(itm_test_data_t* curr_test, ocsd_trc_index_t &trace_index)
{
ocsd_datapath_resp_t dataPathResp = OCSD_RESP_CONT;
ocsd_err_t err = OCSD_OK;
//ocsd_trc_index_t trace_index = 0;
uint32_t numBytesProcessed = 0;
err = process_block(trace_index, curr_test->test_packets, curr_test->test_data_size, numBytesProcessed, dataPathResp);
if (!OCSD_DATA_RESP_IS_FATAL(dataPathResp))
pDecoderIn->TraceDataIn(OCSD_OP_EOT, 0, 0, 0, 0);
// reset the decoder
pDecoderIn->TraceDataIn(OCSD_OP_RESET, 0, 0, 0, 0);
return err;
}
ocsd_err_t run_tests()
{
ocsd_err_t err = OCSD_OK;
itm_test_data_t* tests = get_item_tests();
itm_test_data_t* curr_test = 0;
int test_idx = 0;
std::ostringstream oss;
ocsd_trc_index_t trace_index = 0;
curr_test = &tests[test_idx];
while (curr_test->test_data_size && (err == OCSD_OK))
{
if ((testName == "all") || (testName == curr_test->test_name))
{
oss.str("");
oss << "\nRunning test " << curr_test->test_name << " - " << curr_test->test_description << ";...\n";
logger.LogMsg(oss.str());
// run test
err = run_test(curr_test, trace_index);
// done
oss.str("");
oss << "\nTest " << curr_test->test_name << " complete.\n";
logger.LogMsg(oss.str());
}
curr_test = &tests[++test_idx];
}
oss.str("");
oss << "All tests complete\n";
logger.LogMsg(oss.str());
return err;
}
// program can process binary file containing ITM raw data.
ocsd_err_t process_binfile()
{
ocsd_err_t err = OCSD_OK;
std::ostringstream oss;
std::ifstream in;
oss << "\nDecoding ITM data from file: " << inFileName << ",\n";
if (muxCSID) {
oss << "as CoreSight frame formatted data with ITM stream in Trace ID 0x";
oss << std::hex << std::setfill('0') << std::setw(2) << (uint16_t)muxCSID << ".\n";
}
else
oss << "as raw ITM data, with no CoreSight frame formatting.\n";
logger.LogMsg(oss.str());
in.open(inFileName, 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).
// read files a block at a time
while (!in.eof() && (err == OCSD_OK))
{
in.read((char*)&trace_buffer[0], bufferSize);
std::streamsize nBuffRead = in.gcount(); // get count of data loaded.
uint32_t nBuffProcessed = 0; // amount processed in this buffer.
err = process_block(trace_index, trace_buffer, (uint32_t)nBuffRead, nBuffProcessed, dataPathResp);
}
if (!OCSD_DATA_RESP_IS_FATAL(dataPathResp))
pDecoderIn->TraceDataIn(OCSD_OP_EOT, 0, 0, 0, 0);
}
else
{
oss.str("");
oss << "\nError: failed to open input file\n";
logger.LogMsg(oss.str());
}
oss.str("");
oss << "\nFile decode complete\n";
logger.LogMsg(oss.str());
return err;
}
int main(int argc, char* argv[])
{
std::ostringstream moss;
ocsd_err_t err = OCSD_OK;
ocsd_hndl_err_log_t err_src_handle;
bool OptExit = false;
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());
// set the output logger in the error logger.
errLogger.setOutputLogger(&logger);
err_src_handle = errLogger.RegisterErrorSource("ITM_TEST");
moss << "ITM decode test\n";
moss << "-----------------------------------------------\n\n";
moss << "** Library Version : " << ocsdVersion::vers_str() << "\n\n";
logger.LogMsg(moss.str());
if (!process_cmd_line_opts(argc, argv, OptExit))
{
// option just printed and wants to exit
if (OptExit)
return 0;
moss.str("");
moss << "\nBad command line options\nProgram exiting\n";
logger.LogMsg(moss.str());
print_help();
return -1;
}
// got through command line options - create the decoder and run tests.
err = setup_decoder();
if (err)
{
const ocsdError &rErr = ocsdError(OCSD_ERR_SEV_ERROR, err, "Failed to set up ITM decoder\n");
errLogger.LogError(err_src_handle, &rErr);
}
if (bProcessInFile)
err = process_binfile();
else
err = run_tests();
destroy_itm_objects();
return (err == OCSD_OK) ? 0 : -3;
}