cmvr_head/packages/erpc-1.14.0/erpcsniffer/src/erpcsniffer.cpp
2025-12-30 15:44:41 +08:00

463 lines
14 KiB
C++

/*
* Copyright 2017 NXP
* All rights reserved.
*
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "erpc_crc16.hpp"
#include "erpc_tcp_transport.hpp"
#include "erpc_transport.hpp"
#include "erpc_transport_setup.h"
#include "erpc_version.h"
#include "ErpcLexer.hpp"
#include "InterfaceDefinition.hpp"
#include "Logging.hpp"
#include "SearchPath.hpp"
#include "Sniffer.hpp"
#include "UniqueIdChecker.hpp"
#include "annotations.h"
#include "options.hpp"
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
#include <vector>
/*!
* @brief Entry point for the tool.
*/
int main(int argc, char *argv[], char *envp[]);
using namespace erpc;
using namespace erpcgen;
using namespace std;
namespace erpcsniffer {
////////////////////////////////////////////////////////////////////////////////
// Variables
////////////////////////////////////////////////////////////////////////////////
/*! The tool's name. */
const char k_toolName[] = "erpcsniffer";
/*! Current version number for the tool. */
const char k_version[] = ERPC_VERSION;
/*! Copyright string. */
const char k_copyright[] = "Copyright 2017 NXP. All rights reserved.";
static const char *k_optionsDefinition[] = { "?|help",
"V|version",
"o:output <filePath>",
"v|verbose",
"I:path <filePath>",
"t:transport <transport>",
"q:quantity <quantity>",
"b:baudrate <baudrate>",
"p:port <port>",
"h:host <host>",
NULL };
/*! Help string. */
const char k_usageText[] =
"\nOptions:\n\
-?/--help Show this help\n\
-V/--version Display tool version\n\
-o/--output <filePath> Set path to output file (file name included)\n\
-v/--verbose Print extra detailed log information\n\
-I/--path <filePath> Add search path for imports\n\
-t/--transport <transport> Type of transport.\n\
-q/--quantity <quantity> Record messages count (0 - infinity).\n\
-b/--baudrate <baudrate> Baud rate.\n\
-p/--port <port> Port name or port number.\n\
-h/--host <host> Host definition.\n\
\n\
Available transports (use with -t option):\n\
tcp Tcp transport type (host, port number).\n\
serial Serial transport type (port name, baud rate).\n\
\n";
////////////////////////////////////////////////////////////////////////////////
// Code
////////////////////////////////////////////////////////////////////////////////
/*!
* @brief Class that encapsulates the erpcsniffer tool.
*
* A single global logger instance is created during object construction. It is
* never freed because we need it up to the last possible minute, when an
* exception could be thrown.
*/
class erpcsnifferTool
{
protected:
enum class verbose_type_t
{
kWarning,
kInfo,
kDebug,
kExtraDebug
}; /*!< Types of verbose outputs from erpcsniffer application. */
enum class transports_t
{
kNoneTransport,
kTcpTransport,
kSerialTransport
}; /*!< Type of transport to use. */
typedef vector<string> string_vector_t;
int m_argc; /*!< Number of command line arguments. */
char **m_argv; /*!< String value for each command line argument. */
StdoutLogger *m_logger; /*!< Singleton logger instance. */
verbose_type_t m_verboseType; /*!< Which type of log is need to set (warning, info, debug). */
const char *m_outputFilePath; /*!< Path to the output file. */
const char *m_ErpcFile; /*!< ERPC file. */
string_vector_t m_positionalArgs;
transports_t m_transport; /*!< Transport used for receiving messages. */
uint64_t m_quantity; /*!< Quantity of logs to store. */
uint32_t m_baudrate; /*!< Baudrate rate speed. */
const char *m_port; /*!< Name or number of port. Based on used transport. */
const char *m_host; /*!< Host name */
public:
/*!
* @brief Constructor.
*
* @param[in] argc Count of arguments in argv variable.
* @param[in] argv Pointer to array of arguments.
*
* Creates the singleton logger instance.
*/
erpcsnifferTool(int argc, char *argv[]) :
m_argc(argc), m_argv(argv), m_logger(0), m_verboseType(verbose_type_t::kWarning), m_outputFilePath(NULL),
m_ErpcFile(NULL), m_transport(transports_t::kNoneTransport), m_quantity(10), m_baudrate(115200), m_port(NULL),
m_host(NULL)
{
// create logger instance
m_logger = new StdoutLogger();
m_logger->setFilterLevel(Logger::log_level_t::kWarning);
Log::setLogger(m_logger);
}
/*!
* @brief Destructor.
*/
~erpcsnifferTool() {}
/*!
* @brief Reads the command line options passed into the constructor.
*
* This method can return a return code to its caller, which will cause the
* tool to exit immediately with that return code value. Normally, though, it
* will return -1 to signal that the tool should continue to execute and
* all options were processed successfully.
*
* The Options class is used to parse command line options. See
* #k_optionsDefinition for the list of options and #k_usageText for the
* descriptive help for each option.
*
* @retval -1 The options were processed successfully. Let the tool run normally.
* @return A zero or positive result is a return code value that should be
* returned from the tool as it exits immediately.
*/
int processOptions()
{
Options options(*m_argv, k_optionsDefinition);
OptArgvIter iter(--m_argc, ++m_argv);
// process command line options
int optchar;
const char *optarg;
while ((optchar = options(iter, optarg)))
{
switch (optchar)
{
case '?':
{
printUsage(options);
return 0;
}
case 'V':
{
printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
return 0;
}
case 'o':
{
m_outputFilePath = optarg;
break;
}
case 'v':
{
if (m_verboseType != verbose_type_t::kExtraDebug)
{
m_verboseType = (verbose_type_t)(((int)m_verboseType) + 1);
}
break;
}
case 'I':
{
PathSearcher::getGlobalSearcher().addSearchPath(optarg);
break;
}
case 't':
{
string transport = optarg;
if (transport == "tcp")
{
m_transport = transports_t::kTcpTransport;
}
else if (transport == "serial")
{
m_transport = transports_t::kSerialTransport;
}
else
{
Log::error("error: unknown transport type %s", transport.c_str());
return 1;
}
break;
}
case 'q':
{
m_quantity = strtoul(optarg, NULL, 10);
break;
}
case 'b':
{
m_baudrate = strtoul(optarg, NULL, 10);
break;
}
case 'p':
{
m_port = optarg;
break;
}
case 'h':
{
m_host = optarg;
break;
}
default:
{
Log::error("error: unrecognized option\n\n");
printUsage(options);
return 0;
}
}
}
// handle positional args
if (iter.index() < m_argc)
{
// Log::debug("positional args:\n");
int i;
for (i = iter.index(); i < m_argc; ++i)
{
// Log::debug("%d: %s\n", i - iter.index(), m_argv[i]);
m_positionalArgs.push_back(m_argv[i]);
}
}
// all is well
return -1;
}
/*!
* @brief Prints help for the tool.
*
* @param[in] options Options, which can be used.
*/
void printUsage(Options &options)
{
options.usage(cout, "files...");
printf(k_usageText);
}
/*!
* @brief Core of the tool.
*
* Calls processOptions() to handle command line options before performing the
* real work the tool does.
*
* @retval 1 The functions wasn't processed successfully.
* @retval 0 The function was processed successfully.
*
* @exception Log::error This function is called, when function wasn't
* processed successfully.
* @exception runtime_error Thrown, when positional args is empty.
*/
int run()
{
try
{
// read command line options
int result;
if ((result = processOptions()) != -1)
{
return result;
}
// set verbose logging
setVerboseLogging();
// check argument values
checkArguments();
if (!m_positionalArgs.size())
{
throw runtime_error("no input file provided");
}
m_ErpcFile = m_positionalArgs[0].c_str();
if (!m_outputFilePath)
{
m_outputFilePath = "";
}
// Parse and build definition model.
InterfaceDefinition def;
// Check for duplicate function IDs
UniqueIdChecker uniqueIdCheck;
uniqueIdCheck.makeIdsUnique(def);
Transport *_transport;
switch (m_transport)
{
case transports_t::kTcpTransport:
{
uint16_t portNumber = strtoul(m_port, NULL, 10);
TCPTransport *tcpTransport = new TCPTransport(m_host, portNumber, true);
if (erpc_status_t err = tcpTransport->open())
{
return err;
}
_transport = tcpTransport;
break;
}
case transports_t::kSerialTransport:
{
erpc_transport_t transport = erpc_transport_serial_init(m_port, m_baudrate);
_transport = reinterpret_cast<Transport *>(transport);
assert(_transport);
break;
}
default:
{
break;
}
}
Crc16 crc;
if (def.hasProgramSymbol())
{
Program *program = def.getProgramSymbol();
if (program->findAnnotation(CRC_ANNOTATION, Annotation::program_lang_t::kC) != nullptr)
{
crc.setCrcStart(def.getIdlCrc16());
}
}
_transport->setCrc16(&crc);
Sniffer s(_transport, &def, m_outputFilePath, m_quantity);
return s.run();
}
catch (exception &e)
{
Log::error("error: %s\n", e.what());
return 1;
}
catch (...)
{
Log::error("error: unexpected exception\n");
return 1;
}
return 0;
}
/*!
* @brief Validate arguments that can be checked.
*
* @exception runtime_error Thrown if an argument value fails to pass validation.
*/
void checkArguments()
{
// if (m_outputFilePath == NULL)
// {
// throw runtime_error("no output file was specified");
// }
}
/*!
* @brief Turns on verbose logging.
*/
void setVerboseLogging()
{
// verbose only affects the INFO and DEBUG filter levels
// if the user has selected quiet mode, it overrides verbose
switch (m_verboseType)
{
case verbose_type_t::kWarning:
{
Log::getLogger()->setFilterLevel(Logger::log_level_t::kWarning);
break;
}
case verbose_type_t::kInfo:
{
Log::getLogger()->setFilterLevel(Logger::log_level_t::kInfo);
break;
}
case verbose_type_t::kDebug:
{
Log::getLogger()->setFilterLevel(Logger::log_level_t::kDebug);
break;
}
case verbose_type_t::kExtraDebug:
{
Log::getLogger()->setFilterLevel(Logger::log_level_t::kDebug2);
break;
}
}
}
};
} // namespace erpcsniffer
/*!
* @brief Main application entry point.
*
* Creates a tool instance and lets it take over.
*/
int main(int argc, char *argv[], char *envp[])
{
(void)envp;
try
{
return erpcsniffer::erpcsnifferTool(argc, argv).run();
}
catch (...)
{
Log::error("error: unexpected exception\n");
return 1;
}
return 0;
}