diff --git a/Fw/CMakeLists.txt b/Fw/CMakeLists.txt index 8905c8647e..44aac98369 100644 --- a/Fw/CMakeLists.txt +++ b/Fw/CMakeLists.txt @@ -1,5 +1,5 @@ # Module subdirectories -set(FPRIME_FRAMEWORK_MODULES Fw_Prm Fw_Cmd Fw_Log Fw_Tlm Fw_Com Fw_Time Fw_Port Fw_Types Fw_Cfg CACHE INTERNAL "Fw mods") +set(FPRIME_FRAMEWORK_MODULES Fw_Prm Fw_Cmd Fw_Trace Fw_Log Fw_Tlm Fw_Com Fw_Time Fw_Port Fw_Types Fw_Cfg CACHE INTERNAL "Fw mods") # Port subdirectories add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Buffer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Cmd/") @@ -10,6 +10,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Logger/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Prm/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Time/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Tlm/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Trace/") # Framework subdirectories add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Cfg/") diff --git a/Fw/Cfg/SerIds.hpp b/Fw/Cfg/SerIds.hpp index 421922dfa9..ed4a32890d 100644 --- a/Fw/Cfg/SerIds.hpp +++ b/Fw/Cfg/SerIds.hpp @@ -51,6 +51,7 @@ namespace Fw { FW_TYPEID_PRM_BUFF = 46, //!< Parameter Buffer type id FW_TYPEID_PRM_STR = 47, //!< Parameter string type id FW_TYPEID_FILE_BUFF = 48, //!< File piece Buffer type id + FW_TYPEID_TRACE_BUFF= 49, //!< Trace Buffer type id // Other types diff --git a/Fw/Trace/CMakeLists.txt b/Fw/Trace/CMakeLists.txt new file mode 100644 index 0000000000..3aea88dd16 --- /dev/null +++ b/Fw/Trace/CMakeLists.txt @@ -0,0 +1,16 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# +#### +set(MOD_DEPS + Fw/Com +) +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/TraceBuffer.cpp" + "${CMAKE_CURRENT_LIST_DIR}/TraceEntry.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Trace.fpp" +) +register_fprime_module() \ No newline at end of file diff --git a/Fw/Trace/Trace.fpp b/Fw/Trace/Trace.fpp new file mode 100644 index 0000000000..bfcf5c78f4 --- /dev/null +++ b/Fw/Trace/Trace.fpp @@ -0,0 +1,13 @@ +module Fw { + + type TraceBuffer + + + @ Port for sending Traces + port Trace( + $id: FwTraceIdType @< Trace ID + ref timeTag: Fw.Time @< Time Tag + $type: TraceCfg.TraceType @< The trace type argument + ref args: TraceBuffer @< Buffer containing serialized trace entry + ) +} \ No newline at end of file diff --git a/Fw/Trace/TraceBuffer.cpp b/Fw/Trace/TraceBuffer.cpp new file mode 100644 index 0000000000..1c469d6bd0 --- /dev/null +++ b/Fw/Trace/TraceBuffer.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +namespace Fw { + + TraceBuffer::TraceBuffer(const U8 *args, NATIVE_UINT_TYPE size) { + FW_ASSERT(args != nullptr); + SerializeStatus stat = SerializeBufferBase::setBuff(args,size); + FW_ASSERT(FW_SERIALIZE_OK == stat, static_cast(stat)); + } + + TraceBuffer::TraceBuffer() { + } + + TraceBuffer::~TraceBuffer() { + } + + TraceBuffer::TraceBuffer(const TraceBuffer& other) : Fw::SerializeBufferBase() { + SerializeStatus stat = SerializeBufferBase::setBuff(other.m_bufferData,other.getBuffLength()); + FW_ASSERT(FW_SERIALIZE_OK == stat, static_cast(stat)); + } + + TraceBuffer& TraceBuffer::operator=(const TraceBuffer& other) { + if(this == &other) { + return *this; + } + + SerializeStatus stat = SerializeBufferBase::setBuff(other.m_bufferData,other.getBuffLength()); + FW_ASSERT(FW_SERIALIZE_OK == stat, static_cast(stat)); + return *this; + } + + NATIVE_UINT_TYPE TraceBuffer::getBuffCapacity() const { + return sizeof(this->m_bufferData); + } + + const U8* TraceBuffer::getBuffAddr() const { + return this->m_bufferData; + } + + U8* TraceBuffer::getBuffAddr() { + return this->m_bufferData; + } + + void TraceBuffer::toString(std::string& text) const{ + + //FW_ASSERT(text != nullptr); + FW_ASSERT(this->getBuffLength() < FW_TRACE_BUFFER_MAX_SIZE); + + std::string str_format(reinterpret_cast(this->m_bufferData),this->getBuffLength()); + text += str_format; + } +} diff --git a/Fw/Trace/TraceBuffer.hpp b/Fw/Trace/TraceBuffer.hpp new file mode 100644 index 0000000000..084e5465a9 --- /dev/null +++ b/Fw/Trace/TraceBuffer.hpp @@ -0,0 +1,47 @@ +/* + * TraceBuffer.hpp + * + * Author: sreddy + */ + +/* + * Description: + * This object contains the TraceBuffer type, used for storing trace entries + */ +#ifndef FW_TRACE_BUFFER_HPP +#define FW_TRACE_BUFFER_HPP + +#include +#include +#include +#include + +namespace Fw { + + class TraceBuffer : public SerializeBufferBase { + public: + + enum { + SERIALIZED_TYPE_ID = FW_TYPEID_TRACE_BUFF, + SERIALIZED_SIZE = FW_TRACE_BUFFER_MAX_SIZE+ sizeof(FwBuffSizeType) + }; + + TraceBuffer(const U8 *args, NATIVE_UINT_TYPE size); + TraceBuffer(); + TraceBuffer(const TraceBuffer& other); + virtual ~TraceBuffer(); + TraceBuffer& operator=(const TraceBuffer& other); + + NATIVE_UINT_TYPE getBuffCapacity() const; // !< returns capacity, not current size, of buffer + U8* getBuffAddr(); + const U8* getBuffAddr() const; + //! Supports writing this buffer to a string representation + void toString(std::string& text) const; + + private: + U8 m_bufferData[FW_TRACE_BUFFER_MAX_SIZE]; // command argument buffer + }; + +} + +#endif diff --git a/Fw/Trace/TraceEntry.cpp b/Fw/Trace/TraceEntry.cpp new file mode 100644 index 0000000000..6f195f1b2e --- /dev/null +++ b/Fw/Trace/TraceEntry.cpp @@ -0,0 +1,91 @@ +/* + * TraceEntry.cpp + * + */ + +#include +#include + +namespace Fw { + + TraceEntry::TraceEntry() : m_id(0) { + } + + TraceEntry::~TraceEntry() { + } + + SerializeStatus TraceEntry::serialize(SerializeBufferBase& buffer) const { + + SerializeStatus stat = ComPacket::serializeBase(buffer); + if (stat != FW_SERIALIZE_OK) { + return stat; + } + + stat = buffer.serialize(this->m_id); + if (stat != FW_SERIALIZE_OK) { + return stat; + } + + stat = buffer.serialize(this->m_timeTag); + if (stat != FW_SERIALIZE_OK) { + return stat; + } + + // We want to add data but not size for the ground software + return buffer.serialize(this->m_traceBuffer.getBuffAddr(),m_traceBuffer.getBuffLength(),true); + + } + + SerializeStatus TraceEntry::deserialize(SerializeBufferBase& buffer) { + SerializeStatus stat = deserializeBase(buffer); + if (stat != FW_SERIALIZE_OK) { + return stat; + } + + stat = buffer.deserialize(this->m_id); + if (stat != FW_SERIALIZE_OK) { + return stat; + } + + stat = buffer.deserialize(this->m_timeTag); + if (stat != FW_SERIALIZE_OK) { + return stat; + } + + // remainder of buffer must be telemetry value + NATIVE_UINT_TYPE size = buffer.getBuffLeft(); + stat = buffer.deserialize(this->m_traceBuffer.getBuffAddr(),size,true); + if (stat == FW_SERIALIZE_OK) { + // Shouldn't fail + stat = this->m_traceBuffer.setBuffLen(size); + FW_ASSERT(stat == FW_SERIALIZE_OK,static_cast(stat)); + } + return stat; + } + + void TraceEntry::setId(FwTraceIdType id) { + this->m_id = id; + } + + void TraceEntry::setTraceBuffer(const TraceBuffer& buffer) { + this->m_traceBuffer = buffer; + } + + void TraceEntry::setTimeTag(const Fw::Time& timeTag) { + this->m_timeTag = timeTag; + } + + FwTraceIdType TraceEntry::getId() { + return this->m_id; + } + + Fw::Time& TraceEntry::getTimeTag() { + return this->m_timeTag; + } + + TraceBuffer& TraceEntry::getTraceBuffer() { + return this->m_traceBuffer; + } + + +} /* namespace Fw */ diff --git a/Fw/Trace/TraceEntry.hpp b/Fw/Trace/TraceEntry.hpp new file mode 100644 index 0000000000..65b8c479b7 --- /dev/null +++ b/Fw/Trace/TraceEntry.hpp @@ -0,0 +1,41 @@ +/* + * TraceEntry.hpp + * + * Author: sreddy + */ + +#ifndef TRACE_ENTRY_HPP_ +#define TRACE_ENTRY_HPP_ + +#include +#include +#include + +namespace Fw { + + class TraceEntry : public ComPacket { + public: + + TraceEntry(); + virtual ~TraceEntry(); + + SerializeStatus serialize(SerializeBufferBase& buffer) const; //!< serialize contents + SerializeStatus deserialize(SerializeBufferBase& buffer); + + void setId(FwTraceIdType id); + void setTraceBuffer(const TraceBuffer& buffer); + void setTimeTag(const Fw::Time& timeTag); + + FwTraceIdType getId(); + Fw::Time& getTimeTag(); + TraceBuffer& getTraceBuffer(); + + protected: + FwTraceIdType m_id; // !< Channel id + Fw::Time m_timeTag; // !< time tag + TraceBuffer m_traceBuffer; // !< serialized argument data + }; + +} /* namespace Fw */ + +#endif /* TRACE_ENTRY_HPP_ */ diff --git a/Fw/Trace/docs/sdd.md b/Fw/Trace/docs/sdd.md new file mode 100644 index 0000000000..59d7a35ec6 --- /dev/null +++ b/Fw/Trace/docs/sdd.md @@ -0,0 +1,25 @@ +\page FwTracePort Fw::Trace Port +# Fw::Trace Port + +## 1. Introduction +The `Fw::Trace` port is used to pass serialized trace log values with time tags to record diagnostic data that indicates the order of execution in software. + +## 2. Requirements +Trace Requirements in FPrime: +1. F’Prime shall provide a feature called Trace to log diagnostic information on various points of software execution. + - It is a way to create time correlation events as a reaction for resetting software. +2. Trace shall provide a port to log the following: + - Event ID : A unique identifier recording a “Trace Event” + - Time Tag : Captures a high precision timestamp + - Client Data : A small sample of user arguments recorded into trace buffer +3. Trace shall create trace points and log events in software automatically for thread switching and port calls (in and out), and message queue and dequeue. +4. Trace shall provide a port for other components or user defined points to log events during software execution. +5. Trace shall record log events in an in-memory logger with data products to dump the buffers +6. Trace shall store data on a per thread basis so there is only one write to the buffer, preventing a need for mutual exclusion. +7. Trace shall maintain a double buffer, one for logs collected in the previous run before the latest reset and the other for current autopsy events. This way during init a data product can be generated for the trace events from prior run while actively collecting current trace logs. +8. Trace logs shall be stored in recoverable memory so they can be preserved over resets +9. Trace shall write the trace data (stored in buffers) to data products upon boot initialization, a reset and by ground command +10. Trace shall provide a ground command to dump trace logs upon request. +11. Trace shall provide a command to enable/disable Trace logging as it can consume significant code and processing +12. Trace entries shall be put in a dictionary so ground systems can automatically decode trace data + diff --git a/Ref/Top/RefTopology.cpp b/Ref/Top/RefTopology.cpp index 07d6434981..6b7cc65b80 100644 --- a/Ref/Top/RefTopology.cpp +++ b/Ref/Top/RefTopology.cpp @@ -137,6 +137,10 @@ void configureTopology() { // Note: Uncomment when using Svc:TlmPacketizer //tlmSend.setPacketList(RefPacketsPkts, RefPacketsIgnore, 1); + + //Configure Trace Logger filename and size to be used for storing trace data + trace.configure("TraceFile.dat",2720000); + trace.filterTraceType(0xF,Svc::TraceFileLogger_Enable::ENABLE); } // Public functions for use in main program are namespaced with deployment name Ref diff --git a/Ref/Top/instances.fpp b/Ref/Top/instances.fpp index 2dc3032762..51c35737e6 100644 --- a/Ref/Top/instances.fpp +++ b/Ref/Top/instances.fpp @@ -105,6 +105,11 @@ module Ref { instance typeDemo: Ref.TypeDemo base id 0x1100 + instance trace: Svc.TraceFileLogger base id 0x1200 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 96 + # ---------------------------------------------------------------------- # Queued component instances # ---------------------------------------------------------------------- @@ -129,9 +134,9 @@ module Ref { instance sendBuffComp: Ref.SendBuff base id 0x2600 \ queue size Default.QUEUE_SIZE + - # ---------------------------------------------------------------------- # Passive component instances # ---------------------------------------------------------------------- @@ -165,5 +170,5 @@ module Ref { instance dpBufferManager: Svc.BufferManager base id 0x4C00 instance version: Svc.Version base id 0x4D00 - + } diff --git a/Ref/Top/topology.fpp b/Ref/Top/topology.fpp index d890604f4a..0ad72af3ff 100644 --- a/Ref/Top/topology.fpp +++ b/Ref/Top/topology.fpp @@ -59,6 +59,7 @@ module Ref { instance dpWriter instance dpBufferManager instance version + instance trace # ---------------------------------------------------------------------- # Pattern graph specifiers diff --git a/Svc/CMakeLists.txt b/Svc/CMakeLists.txt index 2ab95efc03..55cde217fc 100644 --- a/Svc/CMakeLists.txt +++ b/Svc/CMakeLists.txt @@ -48,6 +48,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmChan/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmPacketizer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SystemResources/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/VersionPorts") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TraceFileLogger/") # Text logger components included by default, # but can be disabled if FW_ENABLE_TEXT_LOGGING=0 is desired. diff --git a/Svc/TraceFileLogger/ArrayProc.cpp b/Svc/TraceFileLogger/ArrayProc.cpp new file mode 100644 index 0000000000..98aee633ee --- /dev/null +++ b/Svc/TraceFileLogger/ArrayProc.cpp @@ -0,0 +1,75 @@ +// \copyright +// Copyright 2009-2015, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. + +#include +#include +#include +#include +#include +#include + + +namespace Svc { + + // ---------------------------------------------------------------------- + // Initialization/Exiting + // ---------------------------------------------------------------------- + + ArrayProc::ArrayProc() : + m_maxIndex(10),m_currentIndex(0),m_storedArray(nullptr) + { + } + ArrayProc::~ArrayProc() + { + } + + // ---------------------------------------------------------------------- + // Member Functions + // ---------------------------------------------------------------------- + + void ArrayProc::set_array(U32 *array_ptr, U8 array_size) + { + FW_ASSERT(array_ptr != nullptr); + m_storedArray = array_ptr; + m_maxIndex = array_size; + } + + bool ArrayProc::add_element(U32 element) { + if (m_currentIndex < m_maxIndex) { + this->m_storedArray[m_currentIndex] = element; + this->m_currentIndex++; + return true; + } + return false; + } + + bool ArrayProc::delete_element(U32 element) { + + U8 elementIndex = 0; + bool elementExists = this->search_array(element,&elementIndex); + if (elementExists == false) { + return false; + } + else { + for(U8 i = elementIndex; i < this->m_currentIndex ; i++) { + this->m_storedArray[i] = this->m_storedArray[i+1]; + } + this->m_currentIndex--; + return true; + } + } + + bool ArrayProc::search_array(U32 element, U8 *index) { + for(U8 i = 0 ; i < m_currentIndex ; i++) { + if(this->m_storedArray[i] == element) { + if (index != nullptr) { + *index = i; + } + return true; + } + } + return false; + } +} // namespace Svc diff --git a/Svc/TraceFileLogger/ArrayProc.hpp b/Svc/TraceFileLogger/ArrayProc.hpp new file mode 100644 index 0000000000..c1f324bfc3 --- /dev/null +++ b/Svc/TraceFileLogger/ArrayProc.hpp @@ -0,0 +1,77 @@ +// \copyright +// Copyright 2009-2015, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. + +#ifndef SVC_ARRAY_PROC_HPP_ +#define SVC_ARRAY_PROC_HPP_ + +#include + +namespace Svc { + + //! \class ArrayProc + //! \brief ArrayProc struct + //! + //! The object is used for supporting array functions. Making it a struct so all + //! members are public, for ease of use in object composition. + + struct ArrayProc { + + //! \brief Constructor + //! + ArrayProc(); + + //! \brief Destructor + //! + ~ArrayProc(); + + // ---------------------------------------------------------------------- + // Member Functions + // ---------------------------------------------------------------------- + + //! \brief Set pointer to the array + //! + //! \param array_ptr pointer to the array to be used + //! \param array_size Max array index + //! + void set_array(U32 *array_ptr, U8 array_size); + + //! \brief Add an element to the array + //! + //! \param element value to be added to the array + //! + //! \return true if adding an element to the array was successful, false otherwise + bool add_element(U32 element); + + //! \brief Delete element from the array and shift the rest of the array up + //! + //! \param element value to be deleted from the array + //! + //! \return true if deleting an element from the array was successful, false otherwise + bool delete_element(U32 element); + + //! \brief Search for an element in the array + //! + //! \param element value to be searched in the array + //! \param arrayIndex returns the index where (if) the array element exists + //! + //! \return true if the element is found in the array, false otherwise + bool search_array(U32 element, U8 *index); + + // ---------------------------------------------------------------------- + // Member Variables + // ---------------------------------------------------------------------- + + //Max size of the array + U8 m_maxIndex; + + //Current Array Index + U8 m_currentIndex; + + //Array + U32 *m_storedArray; + }; + +} +#endif /* SVC_ARRAY_PROC_HPP_ */ diff --git a/Svc/TraceFileLogger/CMakeLists.txt b/Svc/TraceFileLogger/CMakeLists.txt new file mode 100644 index 0000000000..80fae8ac4d --- /dev/null +++ b/Svc/TraceFileLogger/CMakeLists.txt @@ -0,0 +1,30 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# +# Note: using PROJECT_NAME as EXECUTABLE_NAME +#### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/TraceFileLogger.fpp" + "${CMAKE_CURRENT_LIST_DIR}/TraceFileLogger.cpp" + "${CMAKE_CURRENT_LIST_DIR}/ArrayProc.cpp" +) + +register_fprime_module() + +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/TraceFileLogger.fpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TraceFileLoggerTester.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TraceFileLoggerTestMain.cpp" +) + +set(UT_MOD_DEPS + Fw/Buffer + STest +) + +set(UT_AUTO_HELPERS ON) +register_fprime_ut() + diff --git a/Svc/TraceFileLogger/TraceFileLogger.cpp b/Svc/TraceFileLogger/TraceFileLogger.cpp new file mode 100644 index 0000000000..b0ce422926 --- /dev/null +++ b/Svc/TraceFileLogger/TraceFileLogger.cpp @@ -0,0 +1,238 @@ +// ====================================================================== +// \title TraceFileLogger.cpp +// \author sreddy +// \brief cpp file for TraceFileLogger component implementation class +// ====================================================================== + +#include "Svc/TraceFileLogger/TraceFileLogger.hpp" +#include "FpConfig.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Svc { + + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + TraceFileLogger::TraceFileLogger(const char* const compName) : + TraceFileLoggerComponentBase(compName), + m_log_file(), + m_mode(CLOSED), + m_fileName(), + m_maxFileSize(0), + m_byteCount(0), + m_log_init(false), + m_traceFilter(0) + { + //Initialize data file + this->m_enable_trace = (FW_TRACE_RECORD_TRACE == true) ? true : false; + ::memset(m_file_data, 0, sizeof(m_file_data)); + + //Set data and size for file buffer + this->m_file_buffer.setData(m_file_data); + this->m_file_buffer.setSize(FW_TRACE_MAX_SER_SIZE); + + //Set array to process trace id filtering + this->filterTraceId.set_array(traceId_array,FILTER_TRACE_ID_SIZE); + } + + TraceFileLogger::~TraceFileLogger() { + if(OPEN == this->m_mode) { + //Close file + this->m_log_file.close(); + this->m_mode = CLOSED; + } + } + + void TraceFileLogger :: + init(FwSizeType queueDepth, FwEnumStoreType instance) + { + TraceFileLoggerComponentBase::init(queueDepth,instance); + } + + void TraceFileLogger::set_log_file(const char* fileName, const U32 maxSize) { + + FW_ASSERT(fileName != nullptr); + + if(this->m_enable_trace == true) { + //If a file is already open then close it + if(this->m_mode == OPEN) { + this->m_mode = CLOSED; + this->m_log_file.close(); + } + + //If file name is too large then return failure + FwSizeType fileNameSize = Fw::StringUtils::string_length(fileName, static_cast(Fw::String::STRING_SIZE)); + if (fileNameSize >= Fw::String::STRING_SIZE) { + this->m_enable_trace = false; + return; + } + + Os::File::Status stat = this->m_log_file.open(fileName, Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE); + + // Bad status when trying to open the file: + if (stat != Os::File::OP_OK) { + this->m_enable_trace = false; + this->log_WARNING_LO_TraceFileOpenError(static_cast(fileName)); + } + + this->m_byteCount = 0; + this->m_maxFileSize = maxSize; + this->m_fileName = fileName; + this->m_mode = OPEN; + } + } + + void TraceFileLogger::configure(const char* file, const U32 maxSize) { + FW_ASSERT(file != nullptr); + this->set_log_file(file,maxSize); + } + + // The filter method selects which trace types to be logged and which to ignore. + // The bit mask is based on the enum list for trace type in TraceCfg.fpp + Fw::CmdResponse TraceFileLogger::filterTraceType(U16 traceType_bitmask, bool enable){ + if(traceType_bitmask == 0){ + return Fw::CmdResponse::VALIDATION_ERROR; + } + + if(enable == true) { + this->m_traceFilter |= traceType_bitmask; + } + else { //disable + U16 mask = 0xFFFF ^ traceType_bitmask; + this->m_traceFilter &= mask; + } + return Fw::CmdResponse::OK; + + } + + void TraceFileLogger::write_log_file(U8* data, U32 size) { + + //Write data to the trace logger + FW_ASSERT(data != nullptr); + + if(this->m_enable_trace && this->m_mode == OPEN){ //&& size > 0){ + + //Check if the file size exceeds + U32 expected_byte_count = this->m_byteCount+size; + if (expected_byte_count > this->m_maxFileSize) { + //Rewrite file + //Current design supports writing to a circular file + (void)this->m_log_file.seek(0,Os::FileInterface::SeekType::ABSOLUTE); + this->m_byteCount = 0; + } + FwSignedSizeType writeSize = size; + FwSignedSizeType fileSize; + (void) this->m_log_file.position(fileSize); + Os::File::Status stat = this->m_log_file.write(reinterpret_cast(data),writeSize,Os::File::WAIT); + // Assert if file is not already open + FW_ASSERT(stat != Os::File::NOT_OPENED); + this->m_byteCount += (size); + } + } + + Fw::CmdResponse TraceFileLogger::process_traceId_storage(U32 traceId,bool enable) { + + //Add trace ID to the list (if it doesn't already exist) to stop logging it + bool IdProcessed = false; + if (enable == false) { + if(this->filterTraceId.search_array(traceId, nullptr) == false) { + IdProcessed = this->filterTraceId.add_element(traceId); + } + else { + IdProcessed = true; //adding a duplicate element is ignored + } + } + else{ + //Remove trace ID from list (if its in the list) to start logging it + IdProcessed = this->filterTraceId.delete_element(traceId); + } + if(IdProcessed == false) { + return Fw::CmdResponse::VALIDATION_ERROR; + } + else { + return Fw::CmdResponse::OK; + } + } + + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + void TraceFileLogger::TraceBufferLogger_handler(FwIndexType portNum, + FwTraceIdType id, + Fw::Time& timeTag, + const Fw::TraceCfg::TraceType& type, + Fw::TraceBuffer& args) { + + FW_ASSERT(type.isValid()); + U16 bit_mask = static_cast (FILTER_BIT << type.e); + U32 traceSize = 0; + + //Only log trace types that are enabled by either config or user + if(!(this->m_traceFilter & bit_mask)) { + return; + } + //Only log trace Ids that are not in the list + if(this->filterTraceId.search_array(id,nullptr) == true) { + return; + } + + //Make a call to reset + Fw::SerializeBufferBase& buf_ref = m_file_buffer.getSerializeRepr(); + buf_ref.resetSer(); + (void)buf_ref.serialize(id); + (void)buf_ref.serialize(timeTag); + if(FW_TRACE_RECORD_MINIMAL == false) { + (void)buf_ref.serialize(args); + traceSize = m_file_buffer.getSize(); //Record max size of each trace record for circular file + } + else { + traceSize = buf_ref.getBuffLength(); //Record only id & timetag + } + //Note: Because its a circular file we're writing the full buffer capacity to the file + // instead of the actual buffer size (variable based on number of args). This will + // ensure when the file is overwritten, we preserve older records + this->write_log_file(m_file_buffer.getData(),traceSize); + } + + + // ---------------------------------------------------------------------- + // Handler implementations for commands + // ---------------------------------------------------------------------- + + void TraceFileLogger ::EnableTrace_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, Svc::TraceFileLogger_Enable enable) { + + FW_ASSERT(enable.isValid()); + this->m_enable_trace = enable; + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); + } + + void TraceFileLogger ::DumpTraceDp_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); + } + void TraceFileLogger ::FilterTraceType_cmdHandler(FwOpcodeType opCode, + U32 cmdSeq, + U16 bitmask, + Svc::TraceFileLogger_Enable enable) { + Fw::CmdResponse processStatus = this->filterTraceType(bitmask,enable); + this->cmdResponse_out(opCode, cmdSeq, processStatus); + } + + void TraceFileLogger ::FilterTraceId_cmdHandler(FwOpcodeType opCode, + U32 cmdSeq, + U32 traceId, + Svc::TraceFileLogger_Enable enable) { + //Add/remove trace IDs from the array + Fw::CmdResponse processStatus = this->process_traceId_storage(traceId,static_cast(enable)); + this->cmdResponse_out(opCode, cmdSeq, processStatus); + } +} // namespace Svc diff --git a/Svc/TraceFileLogger/TraceFileLogger.fpp b/Svc/TraceFileLogger/TraceFileLogger.fpp new file mode 100644 index 0000000000..9ddb4c35e7 --- /dev/null +++ b/Svc/TraceFileLogger/TraceFileLogger.fpp @@ -0,0 +1,89 @@ +module Svc { + + @ Component to record Trace Data + active component TraceFileLogger { + + # ---------------------------------------------------------------------- + # Types + # ---------------------------------------------------------------------- + enum Enable { + DISABLE = 0 @< Disabled state + ENABLE = 1 @< Enabled state + } + + # ---------------------------------------------------------------------- + # General ports + # ---------------------------------------------------------------------- + @ Logging Port + async input port TraceBufferLogger: Fw.Trace @< Input Trace port to write to file + + # ---------------------------------------------------------------------- + # Commands + # ---------------------------------------------------------------------- + @ Enable or disable logging all trace + async command EnableTrace ( + $enable : Enable + )\ + opcode 0x00 + + @ Dump Trace to a data product + async command DumpTraceDp\ + opcode 0x01 + + @Select which trace types to be enabled or disabled for logging + async command FilterTraceType ( + bitmask: U16 @< TraceTypes to filter on + $enable : Enable @< enable or disable filtering + )\ + opcode 0x02 + + @ Select which Trace Ids to be enabled or disabled for logging, can disable up to 10 IDs + async command FilterTraceId ( + $traceId : U32 @< Trace ID + $enable : Enable @< enable or disable trace ID + )\ + opcode 0x03 + # ---------------------------------------------------------------------- + # Events + # ---------------------------------------------------------------------- + @ Trace logging status + event TraceStatus( + $status: Enable @< Status of Trace + ) \ + severity diagnostic \ + id 0x00 \ + format "Trace has been {}." + + @Trace File open error + event TraceFileOpenError( + $fileName: string @ +#include +#include + +// some limits.h don't have PATH_MAX +#ifdef PATH_MAX +#define FILE_PATH_MAX PATH_MAX +#else +#define FILE_PATH_MAX 255 +#endif + +// some limits.h don't have NAME_MAX +#ifdef NAME_MAX +#define FILE_NAME_MAX NAME_MAX +#else +#define FILE_NAME_MAX 255 +#endif + +// Trace ID filter array size +#ifndef FILTER_TRACE_ID_SIZE +#define FILTER_TRACE_ID_SIZE 10 +#endif + +//Max size of the Trace buffer including metadata (id,timetag,arguments) +static const FwSizeType FW_TRACE_MAX_SER_SIZE = (FW_TRACE_BUFFER_MAX_SIZE + sizeof(FwTraceIdType) + Fw::Time::SERIALIZED_SIZE); + +//Mask bit for filtering on trace types +static const U16 FILTER_BIT = 1; + + +namespace Svc { + +class TraceFileLogger : public TraceFileLoggerComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct TraceFileLogger object + TraceFileLogger(const char* const compName //!< The component name + ); + + //! \brief Component initialization routine + //! + //! The initialization function calls the initialization + //! routine for the base class. + //! + //! \param queueDepth the depth of the message queue for the component + //! \param instance: instance identifier. Default: 0. + void init(FwSizeType queueDepth, FwEnumStoreType instance = 0); + + //! \brief Set log file and max size + //! + //! This is to create a log file to write all the trace buffers to. + //! The file will not be written to once the max size is hit. + //! + //! \param fileName The name of the file to create. Must be less than 80 characters. + //! \param maxSize The max size of the file + //! + //! \return true if creating the file was successful, false otherwise + void set_log_file(const char* fileName, const U32 maxSize); + + //! \brief Trace Logger configure method + //! + //! The configure method stores the file name to log traces. + //! + //! \param file file where traces are stored. + void configure(const char* file, const U32 maxSize); + + //! \brief Trace Logger filter method + //! + //! The filter method selects which trace types to be logged and which to ignore. + //! + //! \param traceType_bitmask provides bitmask for trace types to select. + //! \param enable to turn on/off filtering . + Fw::CmdResponse filterTraceType(U16 traceType_bitmask,bool enable); + + //! \brief Process trace ID storage method + //! + //! The process trace ID storage lets you add/remove trace IDs from the array + //! that is used to evaluate which trace IDs to be ignored while logging + //! + //! \param traceId which trace id to enable / disable. + //! \param enable to turn on/off filtering . + Fw::CmdResponse process_traceId_storage(U32 traceId, bool enable); + + + //! Destroy TraceFileLogger object + ~TraceFileLogger(); + + PRIVATE: + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for TraceBufferLogger + //! + //! Logging Port + //! Input Trace port to write to file + void TraceBufferLogger_handler(FwIndexType portNum, //!< The port number + FwTraceIdType id, //!< Trace ID + Fw::Time& timeTag, //!< Time Tag + const Fw::TraceCfg::TraceType& type, //!< The trace type argument + Fw::TraceBuffer& args //!< Buffer containing serialized trace entry + ) override; + + PRIVATE: + // ---------------------------------------------------------------------- + // Handler implementations for commands + // ---------------------------------------------------------------------- + + //! Handler implementation for command EnableTrace + //! + //! Enable or disable trace + void EnableTrace_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + Svc::TraceFileLogger_Enable enable) override; + + //! Handler implementation for command DumpTraceDp + //! + //! Dump Trace to a data product + void DumpTraceDp_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq //!< The command sequence number + ) override; + + //! Handler implementation for command FilterTrace + //! + //! Select which trace types to be logged + void FilterTraceType_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + U16 bitmask, //!< TraceTypes to log on + Svc::TraceFileLogger_Enable enable //!< enable or disable logging + ) override; + + //! Handler implementation for command DisableTraceId + //! + //! Enable or disable trace logging by id, can disable up to 10 IDs + void FilterTraceId_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + U32 traceId, //!< Trace ID to enable/disable + Svc::TraceFileLogger_Enable enable) override; + // ---------------------------------------------------------------------- + // Member Variables + // ---------------------------------------------------------------------- + + enum FileMode { + CLOSED = 0, + OPEN = 1 + }; + + // The filename data: + Os::File m_log_file; //Log file + FileMode m_mode; // file mode + Fw::String m_fileName; //File name + U32 m_maxFileSize; //max file size + U32 m_byteCount; //current byte count of the file + bool m_log_init; //Is logfile initialized + bool m_enable_trace; //Is trace logging enabled + U8 m_file_data[FW_TRACE_MAX_SER_SIZE]; //Holds a max size including metadata + Fw::Buffer m_file_buffer; + //filter trace types + U16 m_traceFilter; //Select which trace types to allow logging + //Array to filter on traceIds + U32 traceId_array[FILTER_TRACE_ID_SIZE] = {0}; + ArrayProc filterTraceId; + + // ---------------------------------------------------------------------- + // File functions: + // ---------------------------------------------------------------------- + void openFile( + ); + + void closeFile( + ); + + void write_log_file( + U8* data, + U32 size + ); + + }; + +} // namespace Svc + +#endif diff --git a/Svc/TraceFileLogger/test/ut/TraceFileLoggerTestMain.cpp b/Svc/TraceFileLogger/test/ut/TraceFileLoggerTestMain.cpp new file mode 100644 index 0000000000..951aeddcc5 --- /dev/null +++ b/Svc/TraceFileLogger/test/ut/TraceFileLoggerTestMain.cpp @@ -0,0 +1,46 @@ +// ====================================================================== +// \title TraceFileLoggerTestMain.cpp +// \author sreddy +// \brief cpp file for TraceFileLogger component test main function +// ====================================================================== + +#include "TraceFileLoggerTester.hpp" + +TEST(Nominal, test_startup) { + Svc::TraceFileLoggerTester tester; + tester.test_startup(); +} + +TEST(Nominal, test_log_file) { + Svc::TraceFileLoggerTester tester; + tester.test_startup(); + tester.test_file(); +} + +TEST(Nominal, test_filter_trace_type) { + Svc::TraceFileLoggerTester tester; + tester.test_startup(); + tester.test_filter_trace_type(); +} + +TEST(Nominal, test_filter_trace_id) { + Svc::TraceFileLoggerTester tester; + tester.test_startup(); + tester.test_filter_trace_id(); +} + +TEST(Nominal, test_trace_enable) { + Svc::TraceFileLoggerTester tester; + tester.test_startup(); + tester.test_trace_enable(); +} + +TEST(Nominal, test_code_coverage) { + Svc::TraceFileLoggerTester tester; + tester.test_startup(); + tester.test_code_coverage(); +} +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Svc/TraceFileLogger/test/ut/TraceFileLoggerTester.cpp b/Svc/TraceFileLogger/test/ut/TraceFileLoggerTester.cpp new file mode 100644 index 0000000000..b73857ffac --- /dev/null +++ b/Svc/TraceFileLogger/test/ut/TraceFileLoggerTester.cpp @@ -0,0 +1,432 @@ +// ====================================================================== +// \title TraceFileLoggerTester.cpp +// \author sreddy +// \brief cpp file for TraceFileLogger component test harness implementation class +// ====================================================================== + +#include "TraceFileLoggerTester.hpp" +#include +#include +#include +#include +#include +#include +namespace Svc { + +// ---------------------------------------------------------------------- +// Construction and destruction +// ---------------------------------------------------------------------- + +TraceFileLoggerTester ::TraceFileLoggerTester() + : TraceFileLoggerGTestBase("TraceFileLoggerTester", TraceFileLoggerTester::MAX_HISTORY_SIZE), component("TraceFileLogger") { + this->initComponents(); + this->connectPorts(); +} + +TraceFileLoggerTester ::~TraceFileLoggerTester() {} + +// ---------------------------------------------------------------------- +// Tests +// ---------------------------------------------------------------------- +void TraceFileLoggerTester::test_startup(){ + this->component.configure("TraceFileTest.dat",TEST_TRACE_FILE_SIZE_MAX); + this->component.filterTraceType(0xF,Svc::TraceFileLogger_Enable::ENABLE); //enable all trace types +} + +void TraceFileLoggerTester ::test_file() { + Fw::Time timeTag; + U8 trace_id; + U16 time_store_type; + //U8 time_context_type; + U8 time_secs; + U8 time_usecs; + U8 arg_size; + std::array arg_check; + U8* arg_check_ptr = &arg_check[0]; + + printf("Test File Writes, including exercising circular buffer fill\n"); + //Write to file until it circles back to the beginning into 2 entries + U16 total_records = (TEST_TRACE_FILE_SIZE_MAX / FW_TRACE_MAX_SER_SIZE); + U16 file_entries = total_records+2; + U8 type_entry = 0; //to iterate over trace type + std::array buffer = {0x15,0x26,0x37,0x48,0x59,170,0xBB,0xCC,0xDD,0xEE,0xFF}; //arguments to write + U8 *buff_ptr = &buffer[0]; + Fw::TraceBuffer trace_buffer_args(buff_ptr,buffer.size()); + + for (int i = 0; i < file_entries; i++) { + type_entry = (type_entry+1) % 4; + timeTag.set(TB_DONT_CARE ,i,i+10); + this->invoke_to_TraceBufferLogger(0,i,timeTag,static_cast(type_entry),trace_buffer_args); + this->component.doDispatch(); + } + //Read file to the storage buffer + this->read_file(); + + //Iterate through the buffer and verify contents + for(U16 i = 0 ; i < total_records ; i++ ) { + //Verify Type ID + U16 buff_ptr = i*FW_TRACE_MAX_SER_SIZE; + memcpy(&trace_id,&storage_buffer[buff_ptr+3],sizeof(trace_id)); + if(i == 0 || i == 1) { ASSERT_EQ(trace_id,total_records+i);} + else ASSERT_EQ(trace_id,i); + + //Verify Time Tag + memcpy(&time_store_type,&storage_buffer[buff_ptr+4],sizeof(time_store_type)); + memcpy(&time_secs,&storage_buffer[buff_ptr+10],sizeof(time_secs)); + memcpy(&time_usecs,&storage_buffer[buff_ptr+14],sizeof(time_usecs)); + ASSERT_EQ(time_store_type,TB_DONT_CARE); + if(i == 0 || i == 1) { + ASSERT_EQ(time_secs,total_records+i); + ASSERT_EQ(time_usecs,total_records+i+10); + } + else { + ASSERT_EQ(time_secs,i); + ASSERT_EQ(time_usecs,i+10); + + } + + //Verify Arguments + memcpy(&arg_size,&storage_buffer[buff_ptr+16],sizeof(arg_size)); + memcpy(arg_check_ptr,&storage_buffer[buff_ptr+17],arg_check.size()); + ASSERT_EQ(arg_size,sizeof(buffer)); + ASSERT_TRUE(arg_check == buffer); + } + +} + +void TraceFileLoggerTester :: test_filter_trace_type() { + printf("Test Trace Type Filter\n"); + Fw::Time timeTag; + U8 trace_id; + U16 time_store_type; + U8 time_secs; + U8 time_usecs; + U8 arg_size; + std::array arg_check; + U8* arg_check_ptr = &arg_check[0]; + std::array buffer = {0x15,0x26,0x37,0x48,0x59,170,0xBB,0xCC,0xDD,0xEE,0xFF}; //arguments to write + U8 *buff_ptr = &buffer[0]; + Fw::TraceBuffer trace_buffer_args(buff_ptr,buffer.size()); + timeTag.set(TB_DONT_CARE ,0xBE,0xEF); + + + //Filter out message_dequeue and port_call + this->sendCmd_FilterTraceType(0,1,0x6,Svc::TraceFileLogger_Enable::DISABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(0, 2, 1, Fw::CmdResponse::OK); + + this->invoke_to_TraceBufferLogger(0,0xAA,timeTag,Fw::TraceCfg::TraceType::MESSAGE_QUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xBB,timeTag,Fw::TraceCfg::TraceType::MESSAGE_DEQUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xCC,timeTag,Fw::TraceCfg::TraceType::PORT_CALL,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xDD,timeTag,Fw::TraceCfg::TraceType::USER,trace_buffer_args); + this->component.doDispatch(); + + //enable all trace type filters and ensure they're received + //Filter out message_dequeue and port_call + this->sendCmd_FilterTraceType(0,1,0xF,Svc::TraceFileLogger_Enable::ENABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(0, 2, 1, Fw::CmdResponse::OK); + this->invoke_to_TraceBufferLogger(0,0xAA,timeTag,Fw::TraceCfg::TraceType::MESSAGE_QUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xBB,timeTag,Fw::TraceCfg::TraceType::MESSAGE_DEQUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xCC,timeTag,Fw::TraceCfg::TraceType::PORT_CALL,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xDD,timeTag,Fw::TraceCfg::TraceType::USER,trace_buffer_args); + this->component.doDispatch(); + + this->read_file(); + U16 total_records = (this->file_size / FW_TRACE_MAX_SER_SIZE); + printf ("TOTAL RECORDS : %d \n", total_records); + + //Iterate through buffer and verify contents + for(U16 i = 0 ; i < total_records ; i++ ) { + + U16 buff_ptr = i*FW_TRACE_MAX_SER_SIZE; + memcpy(&trace_id,&storage_buffer[buff_ptr+3],sizeof(trace_id)); + //Verify Trace ID + switch(i) { + case 0: + ASSERT_EQ(trace_id,0xAA); + break; + case 1: + ASSERT_EQ(trace_id,0xDD); + break; + case 2: + ASSERT_EQ(trace_id,0xAA); + break; + case 3: + ASSERT_EQ(trace_id,0xBB); + break; + case 4: + ASSERT_EQ(trace_id,0xCC); + break; + case 5: + ASSERT_EQ(trace_id,0xDD); + break; + } + + //Verify time tag + memcpy(&time_store_type,&storage_buffer[buff_ptr+4],sizeof(time_store_type)); + memcpy(&time_secs,&storage_buffer[buff_ptr+10],sizeof(time_secs)); + memcpy(&time_usecs,&storage_buffer[buff_ptr+14],sizeof(time_usecs)); + ASSERT_EQ(time_secs, 0xBE); + ASSERT_EQ(time_usecs, 0xEF); + ASSERT_EQ(time_store_type,0xFFFF); + + //Verify Arguments + memcpy(&arg_size,&storage_buffer[buff_ptr+16],sizeof(arg_size)); + memcpy(arg_check_ptr,&storage_buffer[buff_ptr+17],arg_check.size()); + ASSERT_EQ(arg_size,sizeof(buffer)); //size of the arguments + ASSERT_TRUE(arg_check == buffer); //Buffer data matches + + } + + //Test command filter trace type with 0 bitmask + this->sendCmd_FilterTraceType(0,1,0x0,Svc::TraceFileLogger_Enable::DISABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(3); + ASSERT_CMD_RESPONSE(2, 2, 1, Fw::CmdResponse::VALIDATION_ERROR); +} + +void TraceFileLoggerTester :: test_filter_trace_id() { + printf("Test Trace ID Filtering Capability\n"); + Fw::Time timeTag; + U8 trace_id; + U16 time_store_type; + U8 time_secs; + U8 time_usecs; + U8 arg_size; + std::array arg_check; + U8* arg_check_ptr = &arg_check[0]; + std::array buffer = {0x15,0x26,0x37,0x48,0x59,170,0xBB,0xCC,0xDD,0xEE,0xFF}; //arguments to write + U8 *buff_ptr = &buffer[0]; + Fw::TraceBuffer trace_buffer_args(buff_ptr,buffer.size()); + timeTag.set(TB_DONT_CARE ,0xBE,0xEF); + + /* + 1. Send a few trace ids + 2. Verify they exist in log file + 3. Send command to disable a few trace ids; verify array + 4. Write to buffer logger and ensure they're filtered out + 5. Write duplicate commands and check trace id array contains unique ids + 6. Enable trace ids again, verify array + 7. Verify file that all of them exist. + */ + + //Filter out trace ids 0xAA and 0xDD + this->sendCmd_FilterTraceId(0,1,0xAA,Svc::TraceFileLogger_Enable::DISABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(0, 3, 1, Fw::CmdResponse::OK); + this->sendCmd_FilterTraceId(0,1,0xDD,Svc::TraceFileLogger_Enable::DISABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(1, 3, 1, Fw::CmdResponse::OK); + + //Verify Array + ASSERT_EQ(this->component.traceId_array[0], 0xAA); + ASSERT_EQ(this->component.traceId_array[1], 0xDD); + + //Send command with duplicate traceID and ensure the array only contains unique trace IDs + this->sendCmd_FilterTraceId(0,1,0xDD,Svc::TraceFileLogger_Enable::DISABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(2, 3, 1, Fw::CmdResponse::OK); + + //Verify Array + ASSERT_EQ(this->component.traceId_array[0], 0xAA); + ASSERT_EQ(this->component.traceId_array[1], 0xDD); + ASSERT_EQ(this->component.traceId_array[3], 0x00); + + this->invoke_to_TraceBufferLogger(0,0xAA,timeTag,Fw::TraceCfg::TraceType::MESSAGE_QUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xBB,timeTag,Fw::TraceCfg::TraceType::MESSAGE_DEQUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xCC,timeTag,Fw::TraceCfg::TraceType::PORT_CALL,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xDD,timeTag,Fw::TraceCfg::TraceType::USER,trace_buffer_args); + this->component.doDispatch(); + + //Filter out trace ids 0xAA and 0xDD + this->sendCmd_FilterTraceId(0,1,0xAA,Svc::TraceFileLogger_Enable::ENABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(3, 3, 1, Fw::CmdResponse::OK); + this->sendCmd_FilterTraceId(0,1,0xDD,Svc::TraceFileLogger_Enable::ENABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(4, 3, 1, Fw::CmdResponse::OK); + this->sendCmd_FilterTraceId(0,1,0xCC,Svc::TraceFileLogger_Enable::DISABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(5, 3, 1, Fw::CmdResponse::OK); + + //enable all trace type filters and ensure they're received + this->invoke_to_TraceBufferLogger(0,0xAA,timeTag,Fw::TraceCfg::TraceType::MESSAGE_QUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xBB,timeTag,Fw::TraceCfg::TraceType::MESSAGE_DEQUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xCC,timeTag,Fw::TraceCfg::TraceType::PORT_CALL,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xDD,timeTag,Fw::TraceCfg::TraceType::USER,trace_buffer_args); + this->component.doDispatch(); + + this->read_file(); + U16 total_records = (this->file_size / FW_TRACE_MAX_SER_SIZE); + + //Iterate through buffer and verify contents + for(U16 i = 0 ; i < total_records ; i++ ) { + + U16 buff_ptr = i*FW_TRACE_MAX_SER_SIZE; + memcpy(&trace_id,&storage_buffer[buff_ptr+3],sizeof(trace_id)); + //Verify Trace ID + switch(i) { + case 0: + ASSERT_EQ(trace_id,0xBB); + break; + case 1: + ASSERT_EQ(trace_id,0xCC); + break; + case 2: + ASSERT_EQ(trace_id,0xAA); + break; + case 3: + ASSERT_EQ(trace_id,0xBB); + break; + case 4: + ASSERT_EQ(trace_id,0xDD); + break; + default: + FW_ASSERT(false); + } + + //Verify time tag + memcpy(&time_store_type,&storage_buffer[buff_ptr+4],sizeof(time_store_type)); + memcpy(&time_secs,&storage_buffer[buff_ptr+10],sizeof(time_secs)); + memcpy(&time_usecs,&storage_buffer[buff_ptr+14],sizeof(time_usecs)); + ASSERT_EQ(time_secs, 0xBE); + ASSERT_EQ(time_usecs, 0xEF); + ASSERT_EQ(time_store_type,0xFFFF); + + //Verify Arguments + memcpy(&arg_size,&storage_buffer[buff_ptr+16],sizeof(arg_size)); + memcpy(arg_check_ptr,&storage_buffer[buff_ptr+17],arg_check.size()); + ASSERT_EQ(arg_size,sizeof(buffer)); //size of the arguments + ASSERT_TRUE(arg_check == buffer); //Buffer data matches + } + + //Cleanup + this->sendCmd_FilterTraceId(0,1,0xCC,Svc::TraceFileLogger_Enable::ENABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(6, 3, 1, Fw::CmdResponse::OK); + + //Verify command error when the storage array is full + for(U8 i = 0; i <= this->component.filterTraceId.m_maxIndex; i++){ + this->sendCmd_FilterTraceId(0,1,i+1,Svc::TraceFileLogger_Enable::DISABLE); + this->component.doDispatch(); + if (i < this->component.filterTraceId.m_maxIndex) + ASSERT_CMD_RESPONSE(7+i, 3, 1, Fw::CmdResponse::OK); + else + ASSERT_CMD_RESPONSE(7+i, 3, 1, Fw::CmdResponse::VALIDATION_ERROR); + } +} + +void TraceFileLoggerTester :: test_trace_enable() { + printf("Test Trace Enable command\n"); + + /* + 1. Send command to disable trace + 2. Make port calls to fileLogger + 3. Verify file size is 0 + 4. Send command to enable trace + 5. Make port calls to fileLogger + 6. Verify file size matches the number of records sent + */ + + Fw::Time timeTag; + timeTag.set(TB_DONT_CARE ,0xBE,0xEF); + std::array buffer = {0x15,0x26,0x37,0x48,0x59,170,0xBB,0xCC,0xDD,0xEE,0xFF}; //arguments to write + U8 *buff_ptr = &buffer[0]; + Fw::TraceBuffer trace_buffer_args(buff_ptr,buffer.size()); + + //Disable trace and verify nothing gets written to the file + this->sendCmd_EnableTrace(0,1,Svc::TraceFileLogger_Enable::DISABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(0, 0, 1, Fw::CmdResponse::OK); + + //Write to file logger + this->invoke_to_TraceBufferLogger(0,0xAB,timeTag,Fw::TraceCfg::TraceType::MESSAGE_QUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xBC,timeTag,Fw::TraceCfg::TraceType::MESSAGE_DEQUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xCD,timeTag,Fw::TraceCfg::TraceType::PORT_CALL,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xDE,timeTag,Fw::TraceCfg::TraceType::USER,trace_buffer_args); + this->component.doDispatch(); + + this->read_file(); + ASSERT_EQ(this->file_size,0); + + //Enable trace and verify file logger writes to the file + this->sendCmd_EnableTrace(0,1,Svc::TraceFileLogger_Enable::ENABLE); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE(0, 0, 1, Fw::CmdResponse::OK); + + //Write to file logger + this->invoke_to_TraceBufferLogger(0,0xFE,timeTag,Fw::TraceCfg::TraceType::MESSAGE_QUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xED,timeTag,Fw::TraceCfg::TraceType::MESSAGE_DEQUEUE,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xDC,timeTag,Fw::TraceCfg::TraceType::PORT_CALL,trace_buffer_args); + this->component.doDispatch(); + this->invoke_to_TraceBufferLogger(0,0xCB,timeTag,Fw::TraceCfg::TraceType::USER,trace_buffer_args); + this->component.doDispatch(); + + this->read_file(); + ASSERT_EQ(this->file_size,(FW_TRACE_MAX_SER_SIZE*4)); + +} +//Test code coverage - Corner cases +void TraceFileLoggerTester :: test_code_coverage() { + + //Test if file already open during init, it will open a clean file + //Run one of the previous tests that leaves a non-zero file size + this->test_trace_enable(); + ASSERT_NE(this->file_size,0); + //Recall setting up log file + this->component.set_log_file("TraceFileTest.dat",TEST_TRACE_FILE_SIZE_MAX); + this->read_file(); + ASSERT_EQ(this->file_size,0); + + //Test if file open fails a warning EVR is generated + //set file mode to closed + printf("Is file closed : %d",this->component.m_mode); + this->component.m_enable_trace = true; + this->component.m_mode = TraceFileLogger::FileMode::CLOSED; + this->component.set_log_file("TraceFileTest.dat",TEST_TRACE_FILE_SIZE_MAX); + ASSERT_EVENTS_TraceFileOpenError(0,"TraceFileTest.dat"); + + //Test file name >= 256 characters creates error + this->component.m_enable_trace = true; + this->component.set_log_file("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",TEST_TRACE_FILE_SIZE_MAX); + ASSERT_EQ(this->component.m_enable_trace,false); + + +} + +//Read log file into a buffer for analysis +void TraceFileLoggerTester :: read_file() { + std::ifstream trace_file("TraceFileTest.dat");//,std::ios::binary); + + trace_file.seekg(0, std::ios::end); + std::streamsize size = trace_file.tellg(); // Get the size of the file + printf("Size of the trace_file is %ld\n",size); + this->file_size = size; + trace_file.seekg(0, std::ios::beg); + + storage_buffer.resize(size); + fill(storage_buffer.begin(),storage_buffer.end(),0); + trace_file.read(reinterpret_cast(storage_buffer.data()),size); + + trace_file.close(); +} + +} // namespace Svc diff --git a/Svc/TraceFileLogger/test/ut/TraceFileLoggerTester.hpp b/Svc/TraceFileLogger/test/ut/TraceFileLoggerTester.hpp new file mode 100644 index 0000000000..496dbc5704 --- /dev/null +++ b/Svc/TraceFileLogger/test/ut/TraceFileLoggerTester.hpp @@ -0,0 +1,84 @@ +// ====================================================================== +// \title TraceFileLoggerTester.hpp +// \author sreddy +// \brief hpp file for TraceFileLogger component test harness implementation class +// ====================================================================== + +#ifndef Svc_TraceFileLoggerTester_HPP +#define Svc_TraceFileLoggerTester_HPP + +#include "Svc/TraceFileLogger/TraceFileLogger.hpp" +#include "Svc/TraceFileLogger/TraceFileLoggerGTestBase.hpp" +#include +namespace Svc { + +class TraceFileLoggerTester : public TraceFileLoggerGTestBase { + public: + // ---------------------------------------------------------------------- + // Constants + // ---------------------------------------------------------------------- + + // Maximum size of histories storing events, telemetry, and port outputs + static const FwSizeType MAX_HISTORY_SIZE = 20; + + // Instance ID supplied to the component instance under test + static const FwEnumStoreType TEST_INSTANCE_ID = 0; + + // Queue depth supplied to the component instance under test + static const FwQueueSizeType TEST_INSTANCE_QUEUE_DEPTH = 10; + + //File size to store Trace data + static const U32 TEST_TRACE_FILE_SIZE_MAX = 27200; + + public: + // ---------------------------------------------------------------------- + // Construction and destruction + // ---------------------------------------------------------------------- + + //! Construct object TraceFileLoggerTester + TraceFileLoggerTester(); + + //! Destroy object TraceFileLoggerTester + ~TraceFileLoggerTester(); + + public: + // ---------------------------------------------------------------------- + // Tests + // ---------------------------------------------------------------------- + + //! To do + void test_startup(); + void test_file(); + //Read trace file into a buffer + void read_file(); + //Test trace filtering commands + void test_filter_trace_id(); + void test_filter_trace_type(); + void test_trace_enable(); + void test_code_coverage(); + + private: + // ---------------------------------------------------------------------- + // Helper functions + // ---------------------------------------------------------------------- + + //! Connect ports + void connectPorts(); + + //! Initialize components + void initComponents(); + + private: + // ---------------------------------------------------------------------- + // Member variables + // ---------------------------------------------------------------------- + + //! The component under test + TraceFileLogger component; + std::vector storage_buffer; + U16 file_size; +}; + +} // namespace Svc + +#endif diff --git a/Svc/TraceFileLogger/test/ut/TraceFileLoggerTesterHelpers.cpp b/Svc/TraceFileLogger/test/ut/TraceFileLoggerTesterHelpers.cpp new file mode 100644 index 0000000000..34ad10c232 --- /dev/null +++ b/Svc/TraceFileLogger/test/ut/TraceFileLoggerTesterHelpers.cpp @@ -0,0 +1,42 @@ +// ====================================================================== +// \title TraceFileLoggerTesterHelpers.cpp +// \author Generated by fpp-to-cpp +// \brief cpp file for TraceFileLogger component test harness helper functions +// ====================================================================== + +#include "TraceFileLoggerTester.hpp" + +namespace Svc { + +// ---------------------------------------------------------------------- +// Helper functions +// ---------------------------------------------------------------------- + +void TraceFileLoggerTester ::connectPorts() { + // Connect special input ports + + this->connect_to_cmdIn(0, this->component.get_cmdIn_InputPort(0)); + + // Connect special output ports + + this->component.set_LogText_OutputPort(0, this->get_from_LogText(0)); + + this->component.set_cmdRegOut_OutputPort(0, this->get_from_cmdRegOut(0)); + + this->component.set_cmdResponseOut_OutputPort(0, this->get_from_cmdResponseOut(0)); + + this->component.set_logOut_OutputPort(0, this->get_from_logOut(0)); + + this->component.set_timeCaller_OutputPort(0, this->get_from_timeCaller(0)); + + // Connect typed input ports + + this->connect_to_TraceBufferLogger(0, this->component.get_TraceBufferLogger_InputPort(0)); +} + +void TraceFileLoggerTester ::initComponents() { + this->init(); + this->component.init(TraceFileLoggerTester::TEST_INSTANCE_QUEUE_DEPTH, TraceFileLoggerTester::TEST_INSTANCE_ID); +} + +} // namespace Svc diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index a63d68471d..fd06715a8e 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -9,5 +9,6 @@ set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/FpConfig.fpp" "${CMAKE_CURRENT_LIST_DIR}/PolyDbCfg.fpp" "${CMAKE_CURRENT_LIST_DIR}/VersionCfg.fpp" + "${CMAKE_CURRENT_LIST_DIR}/TraceCfg.fpp" ) register_fprime_module(config) diff --git a/config/FpConfig.h b/config/FpConfig.h index 31e42665a4..7fc60cd42b 100644 --- a/config/FpConfig.h +++ b/config/FpConfig.h @@ -202,6 +202,17 @@ typedef FwIndexType FwQueueSizeType; //!< for multi-note systems) #endif +//Trace configurations +//Configure Trace to off, full and minimal +#ifndef FW_TRACE_RECORD_TRACE +#define FW_TRACE_RECORD_TRACE 1 +#endif + +#ifndef FW_TRACE_RECORD_MINIMAL +#define FW_TRACE_RECORD_MINIMAL 0 +#endif + + // Component Facilities // Serialization @@ -325,6 +336,11 @@ typedef FwIndexType FwQueueSizeType; #define FW_TLM_STRING_MAX_SIZE 40 //!< Max size of channelized telemetry string type #endif +// Specifies the size of the buffer that contains the serialized trace value. +#ifndef FW_TRACE_BUFFER_MAX_SIZE +#define FW_TRACE_BUFFER_MAX_SIZE 257 +#endif + // Specifies the size of the buffer that contains the serialized parameter value. #ifndef FW_PARAM_BUFFER_MAX_SIZE #define FW_PARAM_BUFFER_MAX_SIZE (FW_COM_BUFFER_MAX_SIZE - sizeof(FwPrmIdType) - sizeof(FwPacketDescriptorType)) diff --git a/config/TraceCfg.fpp b/config/TraceCfg.fpp new file mode 100644 index 0000000000..3fef29e1ca --- /dev/null +++ b/config/TraceCfg.fpp @@ -0,0 +1,36 @@ +# ====================================================================== +# FPP file for Version configuration +# ====================================================================== +# From Design review, it was requested that we add more trace types such as +# interrupts, exceptions, context switch,queue add/remove, EVRs, +# semaphore takes(?), stack trace, DMA +# Also consider adding state machine transitions, timer acquisition +# and release traces, and EVRS to the category of traces that are automated. +# Also consider what a potential IO trace could look like. A ring buffer for IO +# is always requested for 1553 and recently M-Bus on Psyche. Presumably a +# limited IO trace of some could be packaged with the autopsy log and be useful. + +#Note: Trace Types are configurable by the project and defined here. +# It is stored as a bitmask that will be used for log filtering. +# Current design allows a max of 16 trace types + +module Fw { + + module TraceCfg { + + @Enum representing trace Types (aka Trace Categories) + enum TraceType { + MESSAGE_QUEUE = 0 @< Trace type of message wait + MESSAGE_DEQUEUE = 1 @< Trace type of message dequeue + PORT_CALL = 2 @< Trace type of port invocations + USER = 3 @< user invoked trace + #=====Do Not Delete above======# + INTERRUPTS = 4 @