////////////////////////////////////////////////////////////////////////////// // Product: QP/C++ // Last Updated for Version: 4.5.03 // Date of the Last Update: Oct 10, 2012 // // Q u a n t u m L e a P s // --------------------------- // innovating embedded systems // // Copyright (C) 2002-2012 Quantum Leaps, LLC. All rights reserved. // // This program is open source software: you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // Alternatively, this program may be distributed and modified under the // terms of Quantum Leaps commercial licenses, which expressly supersede // the GNU General Public License and are specifically designed for // licensees interested in retaining the proprietary status of their code. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // Contact information: // Quantum Leaps Web sites: http://www.quantum-leaps.com // http://www.state-machine.com // e-mail: info@quantum-leaps.com ////////////////////////////////////////////////////////////////////////////// #ifndef qs_h #define qs_h /// \file /// \ingroup qep qf qk qs /// \brief QS/C++ platform-independent public interface. /// This header file must be included directly or indirectly /// in all modules (*.cpp files) that use QS/C++. #ifndef Q_SPY #error "Q_SPY must be defined to include qs.h" #endif #ifndef Q_ROM // provide the default if Q_ROM NOT defined #define Q_ROM #endif #ifndef Q_ROM_VAR // provide the default if Q_ROM_VAR NOT defined #define Q_ROM_VAR #endif #ifndef Q_ROM_BYTE // provide the default if Q_ROM_BYTE NOT defined #define Q_ROM_BYTE(rom_var_) (rom_var_) #endif #ifndef QS_TIME_SIZE /// \brief The size (in bytes) of the QS time stamp. Valid values: 1, 2, /// or 4; default 4. /// /// This macro can be defined in the QS port file (qs_port.h) to /// configure the ::QSTimeCtr type. Here the macro is not defined so the /// default of 4 byte is chosen. #define QS_TIME_SIZE 4 #endif ////////////////////////////////////////////////////////////////////////////// QP_BEGIN_ /// \brief Quantum Spy record types. /// /// The following constants specify the QS record types used in QP components. /// You can specify your own record types starting from the ::QS_USER offset. /// Currently, the maximum of all records cannot exceed 256. /// \sa QS::filterOn()/#QS_FILTER_ON and QS::filterOff()/#QS_FILTER_OFF enum QSpyRecords { QS_QP_RESET, ///< reset the QP (start of a new QS session) // QEP records QS_QEP_STATE_ENTRY, ///< a state was entered QS_QEP_STATE_EXIT, ///< a state was exited QS_QEP_STATE_INIT, ///< an intial transition was taken in a state QS_QEP_INIT_TRAN, ///< the top-most initial transition was taken QS_QEP_INTERN_TRAN, ///< an internal transition was taken QS_QEP_TRAN, ///< a regular transition was taken QS_QEP_IGNORED, ///< an event was ignored (silently discarded) QS_QEP_DISPATCH, ///< an event was dispatched (begin of RTC step) QS_QEP_UNHANDLED, ///< an event was unhandled due to a guard // QF records QS_QF_ACTIVE_ADD, ///< an AO has been added to QF (started) QS_QF_ACTIVE_REMOVE, ///< an AO has been removed from QF (stopped) QS_QF_ACTIVE_SUBSCRIBE, ///< an AO subscribed to an event QS_QF_ACTIVE_UNSUBSCRIBE, ///< an AO unsubscribed to an event QS_QF_ACTIVE_POST_FIFO, ///< an event was posted (FIFO) directly to an AO QS_QF_ACTIVE_POST_LIFO, ///< an event was posted (LIFO) directly to an AO QS_QF_ACTIVE_GET, ///< an AO got an event and its queue is still not empty QS_QF_ACTIVE_GET_LAST, ///< an AO got an event and its queue is empty QS_QF_EQUEUE_INIT, ///< an event queue was initialized QS_QF_EQUEUE_POST_FIFO, ///< an event was posted (FIFO) to a raw queue QS_QF_EQUEUE_POST_LIFO, ///< an event was posted (LIFO) to a raw queue QS_QF_EQUEUE_GET, ///< get an event and queue still not empty QS_QF_EQUEUE_GET_LAST, ///< get the last event from the queue QS_QF_MPOOL_INIT, ///< a memory pool was initialized QS_QF_MPOOL_GET, ///< a memory block was removed from a memory pool QS_QF_MPOOL_PUT, ///< a memory block was returned to a memory pool QS_QF_PUBLISH, ///< an event was truly published to some subscribers QS_QF_RESERVED8, QS_QF_NEW, ///< new event creation QS_QF_GC_ATTEMPT, ///< garbage collection attempt QS_QF_GC, ///< garbage collection QS_QF_TICK, ///< QF::tick() was called QS_QF_TIMEEVT_ARM, ///< a time event was armed QS_QF_TIMEEVT_AUTO_DISARM, ///< a time event expired and was disarmed QS_QF_TIMEEVT_DISARM_ATTEMPT,///< an attempt to disarmed a disarmed tevent QS_QF_TIMEEVT_DISARM, ///< true disarming of an armed time event QS_QF_TIMEEVT_REARM, ///< rearming of a time event QS_QF_TIMEEVT_POST, ///< a time event posted itself directly to an AO QS_QF_TIMEEVT_CTR, ///< a time event counter was requested QS_QF_CRIT_ENTRY, ///< critical section was entered QS_QF_CRIT_EXIT, ///< critical section was exited QS_QF_ISR_ENTRY, ///< an ISR was entered QS_QF_ISR_EXIT, ///< an ISR was exited QS_QF_INT_DISABLE, ///< interrupts were disabled QS_QF_INT_ENABLE, ///< interrupts were enabled QS_QF_RESERVED4, QS_QF_RESERVED3, QS_QF_RESERVED2, QS_QF_RESERVED1, QS_QF_RESERVED0, // QK records QS_QK_MUTEX_LOCK, ///< the QK mutex was locked QS_QK_MUTEX_UNLOCK, ///< the QK mutex was unlocked QS_QK_SCHEDULE, ///< the QK scheduler scheduled a new task to execute QS_QK_RESERVED6, QS_QK_RESERVED5, QS_QK_RESERVED4, QS_QK_RESERVED3, QS_QK_RESERVED2, QS_QK_RESERVED1, QS_QK_RESERVED0, // Miscellaneous QS records QS_SIG_DIC, ///< signal dictionary entry QS_OBJ_DIC, ///< object dictionary entry QS_FUN_DIC, ///< function dictionary entry QS_USR_DIC, ///< user QS record dictionary entry QS_RESERVED4, QS_RESERVED3, QS_RESERVED2, QS_RESERVED1, QS_RESERVED0, QS_ASSERT, ///< assertion fired in the code // User records QS_USER ///< the first record available for user QS records }; /// \brief Specification of all QS records for the QS::filterOn() and /// QS::filterOff() uint8_t const QS_ALL_RECORDS = static_cast(0xFF); /// \brief Constant representing End-Of-Data condition returned from the /// QS::getByte() function. uint16_t const QS_EOD = static_cast(0xFFFF); #if (QS_TIME_SIZE == 1) typedef uint8_t QSTimeCtr; #define QS_TIME_() (QP_ QS::u8_(QP_ QS::onGetTime())) #elif (QS_TIME_SIZE == 2) typedef uint16_t QSTimeCtr; #define QS_TIME_() (QP_ QS::u16_(QP_ QS::onGetTime())) #elif (QS_TIME_SIZE == 4) /// \brief The type of the QS time stamp /// /// This type determines the dynamic range of QS time stamps typedef uint32_t QSTimeCtr; /// \brief Internal macro to output time stamp to the QS record #define QS_TIME_() (QP_ QS::u32_(QP_ QS::onGetTime())) #else #error "QS_TIME_SIZE defined incorrectly, expected 1, 2, or 4" #endif /// \brief Quantum Spy logging facilities /// /// This class groups together QS services. It has only static members and /// should not be instantiated. class QS { public: /// \brief Get the current version of QS /// /// \return version of the QS as a constant 6-character string of the form /// x.y.zz, where x is a 1-digit major version number, y is a 1-digit /// minor version number, and zz is a 2-digit release number. static char_t const Q_ROM * Q_ROM_VAR getVersion(void); /// \brief Initialize the QS data buffer. /// /// This function should be called from QS_init() to provide QS with the /// data buffer. The first argument \a sto[] is the address of the memory /// block, and the second argument \a stoSize is the size of this block /// in bytes. Currently the size of the QS buffer cannot exceed 64KB. /// /// QS can work with quite small data buffers, but you will start losing /// data if the buffer is too small for the bursts of logging activity. /// The right size of the buffer depends on the data production rate and /// the data output rate. QS offers flexible filtering to reduce the data /// production rate. /// /// \note If the data output rate cannot keep up with the production rate, /// QS will start overwriting the older data with newer data. This is /// consistent with the "last-is-best" QS policy. The record sequence /// counters and checksums on each record allow to easily detect data /// loss. static void initBuf(uint8_t sto[], uint32_t const stoSize); /// \brief Turn the global Filter on for a given record type \a rec. /// /// This function sets up the QS filter to enable the record type \a rec. /// The argument #QS_ALL_RECORDS specifies to filter-on all records. /// This function should be called indirectly through the macro /// #QS_FILTER_ON. /// /// \note Filtering based on the record-type is only the first layer of /// filtering. The second layer is based on the object-type. Both filter /// layers must be enabled for the QS record to be inserted into the QS /// buffer. /// \sa QS_filterOff(), #QS_FILTER_SM_OBJ, #QS_FILTER_AO_OBJ, /// #QS_FILTER_MP_OBJ, #QS_FILTER_EQ_OBJ, and #QS_FILTER_TE_OBJ. static void filterOn(uint8_t const rec); /// \brief Turn the global Filter off for a given record type \a rec. /// /// This function sets up the QS filter to disable the record type \a rec. /// The argument #QS_ALL_RECORDS specifies to suppress all records. /// This function should be called indirectly through the macro /// #QS_FILTER_OFF. /// /// \note Filtering records based on the record-type is only the first /// layer of filtering. The second layer is based on the object-type. /// Both filter layers must be enabled for the QS record to be inserted /// into the QS buffer. /// \sa static void filterOff(uint8_t const rec); /// \brief Mark the begin of a QS record \a rec /// /// This function must be called at the beginning of each QS record. /// This function should be called indirectly through the macro #QS_BEGIN, /// or #QS_BEGIN_NOCRIT, depending if it's called in a normal code or from /// a critical section. static void begin(uint8_t const rec); /// \brief Mark the end of a QS record \a rec /// /// This function must be called at the end of each QS record. /// This function should be called indirectly through the macro #QS_END, /// or #QS_END_NOCRIT, depending if it's called in a normal code or from /// a critical section. static void end(void); // unformatted data elements output ...................................... /// \brief output uint8_t data element without format information /// \note This function is only to be used through macros, never in the /// client code directly. static void u8_(uint8_t const d); /// \brief Output uint16_t data element without format information /// \note This function is only to be used through macros, never in the /// client code directly. static void u16_(uint16_t d); /// \brief Output uint32_t data element without format information /// \note This function is only to be used through macros, never in the /// client code directly. static void u32_(uint32_t d); /// \brief Output zero-terminated ASCII string element without format /// information /// \note This function is only to be used through macros, never in the /// client code directly. static void str_(char_t const *s); /// \brief Output zero-terminated ASCII string element allocated in ROM /// without format information /// \note This function is only to be used through macros, never in the /// client code directly. static void str_ROM_(char_t const Q_ROM * Q_ROM_VAR s); // formatted data elements output ........................................ /// \brief Output uint8_t data element with format information /// \note This function is only to be used through macros, never in the /// client code directly. static void u8(uint8_t const format, uint8_t const d); /// \brief output uint16_t data element with format information /// \note This function is only to be used through macros, never in the /// client code directly. static void u16(uint8_t const format, uint16_t d); /// \brief Output uint32_t data element with format information /// \note This function is only to be used through macros, never in the /// client code directly. static void u32(uint8_t const format, uint32_t d); /// \brief Output 32-bit floating point data element with format /// information /// \note This function is only to be used through macros, never in the /// client code directly. static void f32(uint8_t const format, float32_t const d); /// \brief Output 64-bit floating point data element with format /// information /// \note This function is only to be used through macros, never in the /// client code directly. static void f64(uint8_t const format, float64_t const d); /// \brief Output zero-terminated ASCII string element with format /// information /// \note This function is only to be used through macros, never in the /// client code directly. static void str(char_t const *s); /// \brief Output zero-terminated ASCII string element allocated in ROM /// with format information /// \note This function is only to be used through macros, never in the /// client code directly. static void str_ROM(char_t const Q_ROM * Q_ROM_VAR s); /// \brief Output memory block of up to 255-bytes with format information /// \note This function is only to be used through macros, never in the /// client code directly. static void mem(uint8_t const *blk, uint8_t size); #if (QS_OBJ_PTR_SIZE == 8) || (QS_FUN_PTR_SIZE == 8) /// \brief Output uint64_t data element without format information /// \note This function is only to be used through macros, never in the /// client code directly. static void u64_(uint64_t d); /// \brief Output uint64_t data element with format information /// \note This function is only to be used through macros, never in the /// client code directly. static void u64(uint8_t format, uint64_t d); #endif // QS buffer access ...................................................... /// \brief Byte-oriented interface to the QS data buffer. /// /// This function delivers one byte at a time from the QS data buffer. /// The function returns the byte in the least-significant 8-bits of the /// 16-bit return value if the byte is available. If no more data is /// available at the time, the function returns QS_EOD (End-Of-Data). /// /// \note QS::getByte() is NOT protected with a critical section. static uint16_t getByte(void); /// \brief Block-oriented interface to the QS data buffer. /// /// This function delivers a contiguous block of data from the QS data /// buffer. The function returns the pointer to the beginning of the /// block, and writes the number of bytes in the block to the location /// pointed to by \a pNbytes. The argument \a pNbytes is also used as /// input to provide the maximum size of the data block that the caller /// can accept. /// /// If no bytes are available in the QS buffer when the function is /// called, the function returns a NULL pointer and sets the value /// pointed to by \a pNbytes to zero. /// /// \note Only the NULL return from QS::getBlock() indicates that the QS /// buffer is empty at the time of the call. The non-NULL return often /// means that the block is at the end of the buffer and you need to call /// QS::getBlock() again to obtain the rest of the data that "wrapped /// around" to the beginning of the QS data buffer. /// /// \note QS::getBlock() is NOT protected with a critical section. static uint8_t const *getBlock(uint16_t * const pNbytes); ////////////////////////////////////////////////////////////////////////// // platform-dependent callback functions to be implemented by clients // platform-specific callback functions, need to be implemented by clients /// \brief Callback to startup the QS facility /// /// This is a platform-dependent "callback" function invoked through the /// macro #QS_INIT. You need to implement this function in your /// application. At a minimum, the function must configure the QS buffer /// by calling QS::initBuf(). Typically, you will also want to open/ /// configure the QS output channel, such as a serial port, or a file. /// The void* argument \a arg can be used to pass parameter(s) needed to /// configure the output channel. /// /// The function returns true if the QS initialization was successful, /// or false if it failed. /// /// The following example illustrates an implementation of QS_onStartup(): /// \include qs_startup.cpp static bool onStartup(void const *arg); /// \brief Callback to cleanup the QS facility /// /// This is a platform-dependent "callback" function invoked through the /// macro #QS_EXIT. You need to implement this function in your /// application. The main purpose of this function is to close the QS /// output channel, if necessary. static void onCleanup(void); /// \brief Callback to flush the QS trace data to the host /// /// This is a platform-dependent "callback" function to flush the QS /// trace buffer to the host. The function typically busy-waits until all /// the data in the buffer is sent to the host. This is acceptable only /// in the initial transient. static void onFlush(void); /// \brief Callback to obtain a timestamp for a QS record. /// /// This is a platform-dependent "callback" function invoked from the /// macro #QS_TIME_ to add the time stamp to the QS record. /// /// \note Some of the pre-defined QS records from QP do not output the /// time stamp. However, ALL user records do output the time stamp. /// \note QS::onGetTime() is called in a critical section and should not /// exit critical section. /// /// The following example shows using a system call to implement QS /// time stamping: /// \include qs_onGetTime.cpp static QSTimeCtr onGetTime(void); ////////////////////////////////////////////////////////////////////////// // Global and Local QS filters static uint8_t glbFilter_[32]; ///< global on/off QS filter static void const *smObj_; ///< state machine for QEP local filter static void const *aoObj_; ///< active object for QF/QK local filter static void const *mpObj_; ///< event pool for QF local filter static void const *eqObj_; ///< raw queue for QF local filter static void const *teObj_; ///< time event for QF local filter static void const *apObj_;///< generic object Application QF local filter ////////////////////////////////////////////////////////////////////////// // Miscallaneous /// tick counter for the QS_QF_TICK record static QSTimeCtr tickCtr_; }; /// \brief Enumerates data formats recognized by QS /// /// QS uses this enumeration is used only internally for the formatted user /// data elements. enum QSType { QS_I8_T, ///< signed 8-bit integer format QS_U8_T, ///< unsigned 8-bit integer format QS_I16_T, ///< signed 16-bit integer format QS_U16_T, ///< unsigned 16-bit integer format QS_I32_T, ///< signed 32-bit integer format QS_U32_T, ///< unsigned 32-bit integer format QS_F32_T, ///< 32-bit floating point format QS_F64_T, ///< 64-bit floating point format QS_STR_T, ///< zero-terminated ASCII string format QS_MEM_T, ///< up to 255-bytes memory block format QS_SIG_T, ///< event signal format QS_OBJ_T, ///< object pointer format QS_FUN_T, ///< function pointer format QS_I64_T, ///< signed 64-bit integer format QS_U64_T, ///< unsigned 64-bit integer format QS_U32_HEX_T ///< unsigned 32-bit integer in hex format }; /// \brief critical section nesting level /// /// \note Not to be used by Clients directly, only in ports of QF extern uint8_t QF_critNest_; QP_END_ ////////////////////////////////////////////////////////////////////////////// // Macros for adding QS instrumentation to the client code /// \brief Initialize the QS facility. /// /// This macro provides an indirection layer to invoke the QS initialization /// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. /// \sa QS::onStartup(), example of setting up a QS filter in #QS_FILTER_IN #define QS_INIT(arg_) (QP_ QS::onStartup(arg_)) /// \brief Cleanup the QS facility. /// /// This macro provides an indirection layer to invoke the QS cleanup /// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. /// \sa QS::onCleanup() #define QS_EXIT() (QP_ QS::onCleanup()) /// \brief Global Filter ON for a given record type \a rec. /// /// This macro provides an indirection layer to call QS::filterOn() if #Q_SPY /// is defined, or do nothing if #Q_SPY is not defined. /// /// The following example shows how to use QS filters: /// \include qs_filter.cpp #define QS_FILTER_ON(rec_) (QP_ QS::filterOn(static_cast(rec_))) /// \brief Global filter OFF for a given record type \a rec. /// /// This macro provides an indirection layer to call QS::filterOff() if #Q_SPY /// is defined, or do nothing if #Q_SPY is not defined. /// /// \sa Example of using QS filters in #QS_FILTER_ON documentation #define QS_FILTER_OFF(rec_) (QP_ QS::filterOff(static_cast(rec_))) /// \brief Local Filter for a given state machine object \a obj_. /// /// This macro sets up the state machine object local filter if #Q_SPY is /// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ /// is the pointer to the state machine object that you want to monitor. /// /// The state machine object filter allows you to filter QS records pertaining /// only to a given state machine object. With this filter disabled, QS will /// output records from all state machines in your application. The object /// filter is disabled by setting the state machine pointer to NULL. /// /// The state machine filter affects the following QS records: /// ::QS_QEP_STATE_ENTRY, ::QS_QEP_STATE_EXIT, ::QS_QEP_STATE_INIT, /// ::QS_QEP_INIT_TRAN, ::QS_QEP_INTERN_TRAN, ::QS_QEP_TRAN, /// and ::QS_QEP_IGNORED. /// /// \note Because active objects are state machines at the same time, /// the state machine filter (#QS_FILTER_SM_OBJ) pertains to active /// objects as well. However, the state machine filter is more general, /// because it can be used only for state machines that are not active /// objects, such as "Orthogonal Components". /// /// \sa Example of using QS filters in #QS_FILTER_ON documentation #define QS_FILTER_SM_OBJ(obj_) (QP_ QS::smObj_ = (obj_)) /// \brief Local Filter for a given active object \a obj_. /// /// This macro sets up the active object local filter if #Q_SPY is defined, /// or does nothing if #Q_SPY is not defined. The argument \a obj_ is the /// pointer to the active object that you want to monitor. /// /// The active object filter allows you to filter QS records pertaining /// only to a given active object. With this filter disabled, QS will /// output records from all active objects in your application. The object /// filter is disabled by setting the active object pointer \a obj_ to NULL. /// /// The active object filter affects the following QS records: /// ::QS_QF_ACTIVE_ADD, ::QS_QF_ACTIVE_REMOVE, ::QS_QF_ACTIVE_SUBSCRIBE, /// ::QS_QF_ACTIVE_UNSUBSCRIBE, ::QS_QF_ACTIVE_POST_FIFO, /// ::QS_QF_ACTIVE_POST_LIFO, ::QS_QF_ACTIVE_GET, and ::QS_QF_ACTIVE_GET_LAST. /// /// \sa Example of using QS filters in #QS_FILTER_ON documentation #define QS_FILTER_AO_OBJ(obj_) (QP_ QS::aoObj_ = (obj_)) /// \brief Local Filter for a given memory pool object \a obj_. /// /// This macro sets up the memory pool object local filter if #Q_SPY is /// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ /// is the pointer to the memory buffer used during the initialization of the /// event pool with QF::poolInit(). /// /// The memory pool filter allows you to filter QS records pertaining /// only to a given memory pool. With this filter disabled, QS will /// output records from all memory pools in your application. The object /// filter is disabled by setting the memory pool pointer \a obj_ to NULL. /// /// The memory pool filter affects the following QS records: /// ::QS_QF_MPOOL_INIT, ::QS_QF_MPOOL_GET, and ::QS_QF_MPOOL_PUT. /// /// \sa Example of using QS filters in #QS_FILTER_ON documentation #define QS_FILTER_MP_OBJ(obj_) (QP_ QS::mpObj_ = (obj_)) /// \brief Filter for a given event queue object \a obj_. /// /// This macro sets up the event queue object filter if #Q_SPY is defined, /// or does nothing if #Q_SPY is not defined. The argument \a obj_ is the /// pointer to the "raw" thread-safe queue object you want to monitor. /// /// The event queue filter allows you to filter QS records pertaining /// only to a given event queue. With this filter disabled, QS will /// output records from all event queues in your application. The object /// filter is disabled by setting the event queue pointer \a obj_ to NULL. /// /// The event queue filter affects the following QS records: /// ::QS_QF_EQUEUE_INIT, ::QS_QF_EQUEUE_POST_FIFO, ::QS_QF_EQUEUE_POST_LIFO, /// ::QS_QF_EQUEUE_GET, and ::QS_QF_EQUEUE_GET_LAST. /// /// \sa Example of using QS filters in #QS_FILTER_IN documentation #define QS_FILTER_EQ_OBJ(obj_) (QP_ QS::eqObj_ = (obj_)) /// \brief Local Filter for a given time event object \a obj_. /// /// This macro sets up the time event object local filter if #Q_SPY is /// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ /// is the pointer to the time event object you want to monitor. /// /// The time event filter allows you to filter QS records pertaining /// only to a given time event. With this filter disabled, QS will /// output records from all time events in your application. The object /// filter is disabled by setting the time event pointer \a obj_ to NULL. /// /// The time event filter affects the following QS records: /// ::QS_QF_TIMEEVT_ARM, ::QS_QF_TIMEEVT_AUTO_DISARM, /// ::QS_QF_TIMEEVT_DISARM_ATTEMPT, ::QS_QF_TIMEEVT_DISARM, /// ::QS_QF_TIMEEVT_REARM, ::QS_QF_TIMEEVT_POST, and ::QS_QF_TIMEEVT_PUBLISH. /// /// \sa Example of using QS filters in #QS_FILTER_ON documentation #define QS_FILTER_TE_OBJ(obj_) (QP_ QS::teObj_ = (obj_)) /// \brief Local Filter for a generic application object \a obj_. /// /// This macro sets up the local application object filter if #Q_SPY is /// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ /// is the pointer to the application object you want to monitor. /// /// The application object filter allows you to filter QS records pertaining /// only to a given application object. With this filter disabled, QS will /// output records from all application-records enabled by the global filter. /// The local filter is disabled by setting the time event pointer \a obj_ /// to NULL. /// /// \sa Example of using QS filters in #QS_FILTER_ON documentation #define QS_FILTER_AP_OBJ(obj_) (QP_ QS::apObj_ = (obj_)) ////////////////////////////////////////////////////////////////////////////// // Macros to generate user QS records #define QS_GLB_FILTER_(rec_) \ ((QP_ QS::glbFilter_[static_cast(rec_) >> 3] \ & (static_cast(1U << (static_cast(rec_) \ & static_cast(7))))) \ != static_cast(0)) /// \brief Begin a QS user record without entering critical section. #define QS_BEGIN_NOCRIT(rec_, obj_) \ if (QS_GLB_FILTER_(rec_) \ && ((QP_ QS::apObj_ == static_cast(0)) \ || (QP_ QS::apObj_ == (obj_)))) \ { \ QP_ QS::begin(static_cast(rec_)); \ QS_TIME_(); /// \brief End a QS user record without exiting critical section. #define QS_END_NOCRIT() \ QS_END_NOCRIT_() // QS-specific critical section #ifndef QF_CRIT_STAT_TYPE /// \brief This is an internal macro for defining the critical section /// status type. /// /// The purpose of this macro is to enable writing the same code for the /// case when critical sectgion status type is defined and when it is not. /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro /// provides the definition of the critical section status variable. /// Otherwise this macro is empty. /// \sa #QF_CRIT_STAT_TYPE /// #define QS_CRIT_STAT_ /// \brief This is an internal macro for entering a critical section. /// /// The purpose of this macro is to enable writing the same code for the /// case when critical sectgion status type is defined and when it is not. /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro /// invokes #QF_CRIT_ENTRY passing the key variable as the parameter. /// Otherwise #QF_CRIT_ENTRY is invoked with a dummy parameter. /// \sa #QF_CRIT_ENTRY /// #define QS_CRIT_ENTRY_() QF_CRIT_ENTRY(dummy) /// \brief This is an internal macro for exiting a cricial section. /// /// The purpose of this macro is to enable writing the same code for the /// case when critical sectgion status type is defined and when it is not. /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro /// invokes #QF_CRIT_EXIT passing the key variable as the parameter. /// Otherwise #QF_CRIT_EXIT is invoked with a dummy parameter. /// \sa #QF_CRIT_EXIT /// #define QS_CRIT_EXIT_() QF_CRIT_EXIT(dummy) #else #define QS_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; #define QS_CRIT_ENTRY_() QF_CRIT_ENTRY(critStat_) #define QS_CRIT_EXIT_() QF_CRIT_EXIT(critStat_) #endif /// \brief Begin a user QS record with entering critical section. /// /// The following example shows how to build a user QS record using the /// macros #QS_BEGIN, #QS_END, and the formatted output macros: #QS_U8 and /// #QS_STR. /// \include qs_user.cpp /// \note Must always be used in pair with #QS_END #define QS_BEGIN(rec_, obj_) \ if (QS_GLB_FILTER_(rec_) \ && ((QP_ QS::apObj_ == static_cast(0)) \ || (QP_ QS::apObj_ == (obj_)))) \ { \ QS_CRIT_STAT_ \ QS_CRIT_ENTRY_(); \ QP_ QS::begin(static_cast(rec_)); \ QS_TIME_(); /// \brief End a QS record with exiting critical section. /// \sa example for #QS_BEGIN /// \note Must always be used in pair with #QS_BEGIN #define QS_END() \ QS_END_() ////////////////////////////////////////////////////////////////////////////// // Macros for use inside other macros or internally in the QP code /// \brief Internal QS macro to begin a QS record with entering critical /// section. /// \note This macro is intended to use only inside QP components and NOT /// at the application level. \sa #QS_BEGIN #define QS_BEGIN_(rec_, objFilter_, obj_) \ if (QS_GLB_FILTER_(rec_) \ && (((objFilter_) == static_cast(0)) \ || ((objFilter_) == (obj_)))) \ { \ QS_CRIT_ENTRY_(); \ QP_ QS::begin(static_cast(rec_)); /// \brief Internal QS macro to end a QS record with exiting critical /// section. /// \note This macro is intended to use only inside QP components and NOT /// at the application level. \sa #QS_END #define QS_END_() \ QP_ QS::end(); \ QS_CRIT_EXIT_(); \ } /// \brief Internal QS macro to begin a QS record without entering critical /// section. /// \note This macro is intended to use only inside QP components and NOT /// at the application level. \sa #QS_BEGIN_NOCRIT #define QS_BEGIN_NOCRIT_(rec_, objFilter_, obj_) \ if (QS_GLB_FILTER_(rec_) \ && (((objFilter_) == static_cast(0)) \ || ((objFilter_) == (obj_)))) \ { \ QP_ QS::begin(static_cast(rec_)); /// \brief Internal QS macro to end a QS record without exiting critical /// section. /// \note This macro is intended to use only inside QP components and NOT /// at the application level. \sa #QS_END_NOCRIT #define QS_END_NOCRIT_() \ QP_ QS::end(); \ } #if (Q_SIGNAL_SIZE == 1) /// \brief Internal QS macro to output an unformatted event signal /// data element /// \note the size of the pointer depends on the macro #Q_SIGNAL_SIZE. #define QS_SIG_(sig_) (QP_ QS::u8_(static_cast(sig_))) #elif (Q_SIGNAL_SIZE == 2) #define QS_SIG_(sig_) (QP_ QS::u16_(static_cast(sig_))) #elif (Q_SIGNAL_SIZE == 4) #define QS_SIG_(sig_) (QP_ QS::u32_(static_cast(sig_))) #endif /// \brief Internal QS macro to output an unformatted uint8_t data element #define QS_U8_(data_) (QP_ QS::u8_(data_)) /// \brief Internal QS macro to output an unformatted uint16_t data element #define QS_U16_(data_) (QP_ QS::u16_(data_)) /// \brief Internal QS macro to output an unformatted uint32_t data element #define QS_U32_(data_) (QP_ QS::u32_(data_)) #if (QS_OBJ_PTR_SIZE == 1) #define QS_OBJ_(obj_) (QP_ QS::u8_(reinterpret_cast(obj_))) #elif (QS_OBJ_PTR_SIZE == 2) #define QS_OBJ_(obj_) (QP_ QS::u16_(reinterpret_cast(obj_))) #elif (QS_OBJ_PTR_SIZE == 4) #define QS_OBJ_(obj_) (QP_ QS::u32_(reinterpret_cast(obj_))) #elif (QS_OBJ_PTR_SIZE == 8) #define QS_OBJ_(obj_) (QP_ QS::u64_(reinterpret_cast(obj_))) #else /// \brief Internal QS macro to output an unformatted object pointer /// data element /// \note the size of the pointer depends on the macro #QS_OBJ_PTR_SIZE. /// If the size is not defined the size of pointer is assumed 4-bytes. #define QS_OBJ_(obj_) (QP_ QS::u32_(reinterpret_cast(obj_))) #endif #if (QS_FUN_PTR_SIZE == 1) #define QS_FUN_(fun_) (QP_ QS::u8_(reinterpret_cast(fun_))) #elif (QS_FUN_PTR_SIZE == 2) #define QS_FUN_(fun_) (QP_ QS::u16_(reinterpret_cast(fun_))) #elif (QS_FUN_PTR_SIZE == 4) #define QS_FUN_(fun_) (QP_ QS::u32_(reinterpret_cast(fun_))) #elif (QS_FUN_PTR_SIZE == 8) #define QS_FUN_(fun_) (QP_ QS::u64_(reinterpret_cast(fun_))) #else /// \brief Internal QS macro to output an unformatted function pointer /// data element /// \note the size of the pointer depends on the macro #QS_FUN_PTR_SIZE. /// If the size is not defined the size of pointer is assumed 4-bytes. #define QS_FUN_(fun_) (QP_ QS::u32_(reinterpret_cast(fun_))) #endif /// \brief Internal QS macro to output a zero-terminated ASCII string /// data element #define QS_STR_(msg_) (QP_ QS::str_(msg_)) /// \brief Internal QS macro to output a zero-terminated ASCII string /// allocated in ROM data element #define QS_STR_ROM_(msg_) (QP_ QS::str_ROM_(msg_)) ////////////////////////////////////////////////////////////////////////////// // Macros for use in the client code /// \brief Output formatted int8_t to the QS record #define QS_I8(width_, data_) \ (QP_ QS::u8(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_I8_T)), (data_))) /// \brief Output formatted uint8_t to the QS record #define QS_U8(width_, data_) \ (QP_ QS::u8(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_U8_T)), (data_))) /// \brief Output formatted int16_t to the QS record #define QS_I16(width_, data_) \ (QP_ QS::u16(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_I16_T)), (data_))) /// \brief Output formatted uint16_t to the QS record #define QS_U16(width_, data_) \ (QP_ QS::u16(static_cast((((width_) << 4)) \ | static_cast(QP_ QS_U16_T)), (data_))) /// \brief Output formatted int32_t to the QS record #define QS_I32(width_, data_) \ (QP_ QS::u32(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_I32_T)), (data_))) /// \brief Output formatted uint32_t to the QS record #define QS_U32(width_, data_) \ (QP_ QS::u32(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_U32_T)), (data_))) /// \brief Output formatted 32-bit floating point number to the QS record #define QS_F32(width_, data_) \ (QP_ QS::f32(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_F32_T)), (data_))) /// \brief Output formatted 64-bit floating point number to the QS record #define QS_F64(width_, data_) \ (QP_ QS::f64(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_F64_T)), (data_))) /// \brief Output formatted int64_t to the QS record #define QS_I64(width_, data_) \ (QP_ QS::u64(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_I64_T)), (data_))) /// \brief Output formatted uint64_t to the QS record #define QS_U64(width_, data_) \ (QP_ QS::u64(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_U64_T)), (data_))) /// \brief Output formatted uint32_t to the QS record #define QS_U32_HEX(width_, data_) \ (QP_ QS::u32(static_cast((static_cast((width_) << 4)) \ | static_cast(QP_ QS_U32_HEX_T)), (data_))) /// \brief Output formatted zero-terminated ASCII string to the QS record #define QS_STR(str_) (QP_ QS::str(str_)) /// \brief Output formatted zero-terminated ASCII string from ROM /// to the QS record #define QS_STR_ROM(str_) (QP_ QS::str_ROM(str_)) /// \brief Output formatted memory block of up to 255 bytes to the QS /// record #define QS_MEM(mem_, size_) (QP_ QS::mem((mem_), (size_))) #if (QS_OBJ_PTR_SIZE == 1) #define QS_OBJ(obj_) (QP_ QS::u8(QS_OBJ_T, (uint8_t)(obj_))) #elif (QS_OBJ_PTR_SIZE == 2) #define QS_OBJ(obj_) (QP_ QS::u16(QS_OBJ_T, (uint16_t)(obj_))) #elif (QS_OBJ_PTR_SIZE == 4) #define QS_OBJ(obj_) (QP_ QS::u32(QS_OBJ_T, (uint32_t)(obj_))) #elif (QS_OBJ_PTR_SIZE == 8) #define QS_OBJ(obj_) (QP_ QS::u64(QS_OBJ_T, (uint64_t)(obj_))) #else /// \brief Output formatted object pointer to the QS record #define QS_OBJ(obj_) (QP_ QS::u32(QS_OBJ_T, (uint32_t)(obj_))) #endif #if (QS_FUN_PTR_SIZE == 1) #define QS_FUN(fun_) (QP_ QS::u8(QS_FUN_T, (uint8_t)(fun_))) #elif (QS_FUN_PTR_SIZE == 2) #define QS_FUN(fun_) (QP_ QS::u16(QS_FUN_T, (uint16_t)(fun_))) #elif (QS_FUN_PTR_SIZE == 4) #define QS_FUN(fun_) (QP_ QS::u32(QS_FUN_T, (uint32_t)(fun_))) #elif (QS_FUN_PTR_SIZE == 8) #define QS_FUN(fun_) (QP_ QS::u64(QS_FUN_T, (uint64_t)(fun_))) #else /// \brief Output formatted function pointer to the QS record #define QS_FUN(fun_) (QP_ QS::u32(QS_FUN_T, (uint32_t)(fun_))) #endif /// \brief Reset the QS session. /// /// This trace record should be generated at the beginning of the QS session. /// It informs the QSPY host application that the new session has been started. #define QS_RESET() do { \ if (QS_GLB_FILTER_(QP_ QS_QP_RESET)) { \ QS_CRIT_STAT_ \ QS_CRIT_ENTRY_(); \ QP_ QS::begin(static_cast(QP_ QS_QP_RESET)); \ QP_ QS::end(); \ QS_CRIT_EXIT_(); \ QP_ QS::onFlush(); \ } \ } while (false) /// \brief Output signal dictionary record /// /// A signal dictionary record associates the numerical value of the signal /// and the binary address of the state machine that consumes that signal /// with the human-readable name of the signal. /// /// Providing a signal dictionary QS record can vastly improve readability of /// the QS log, because instead of dealing with cryptic machine addresses the /// QSpy host utility can display human-readable names. /// /// A signal dictionary entry is associated with both the signal value \a sig_ /// and the state machine \a obj_, because signals are required to be unique /// only within a given state machine and therefore the same numerical values /// can represent different signals in different state machines. /// /// For the "global" signals that have the same meaning in all state machines /// (such as globally published signals), you can specify a signal dictionary /// entry with the \a obj_ parameter set to NULL. /// /// The following example shows the definition of signal dictionary entries /// in the initial transition of the Table active object. Please note that /// signals HUNGRY_SIG and DONE_SIG are associated with the Table state /// machine only ("me" \a obj_ pointer). The EAT_SIG signal, on the other /// hand, is global (0 \a obj_ pointer): /// \include qs_sigDic.cpp /// /// \note The QSpy log utility must capture the signal dictionary record /// in order to use the human-readable information. You need to connect to /// the target before the dictionary entries have been transmitted. /// /// The following QSpy log example shows the signal dictionary records /// generated from the Table initial transition and subsequent records that /// show human-readable names of the signals: /// \include qs_sigLog.txt /// /// The following QSpy log example shows the same sequence of records, but /// with dictionary records removed. The human-readable signal names are not /// available. /// \include qs_sigLog0.txt #define QS_SIG_DICTIONARY(sig_, obj_) do { \ if (QS_GLB_FILTER_(QP_ QS_SIG_DIC)) { \ static char_t const Q_ROM Q_ROM_VAR sig_name_[] = #sig_; \ QS_CRIT_STAT_ \ QS_CRIT_ENTRY_(); \ QP_ QS::begin(static_cast(QP_ QS_SIG_DIC)); \ QS_SIG_(sig_); \ QS_OBJ_(obj_); \ QS_STR_ROM_(&sig_name_[0]); \ QP_ QS::end(); \ QS_CRIT_EXIT_(); \ QP_ QS::onFlush(); \ } \ } while (false) /// \brief Output object dictionary record /// /// An object dictionary record associates the binary address of an object /// in the target's memory with the human-readable name of the object. /// /// Providing an object dictionary QS record can vastly improve readability of /// the QS log, because instead of dealing with cryptic machine addresses the /// QSpy host utility can display human-readable object names. /// /// The following example shows the definition of object dictionary entry /// for the Table active object: /// \include qs_objDic.cpp #define QS_OBJ_DICTIONARY(obj_) do { \ if (QS_GLB_FILTER_(QP_ QS_OBJ_DIC)) { \ static char_t const Q_ROM Q_ROM_VAR obj_name_[] = #obj_; \ QS_CRIT_STAT_ \ QS_CRIT_ENTRY_(); \ QP_ QS::begin(static_cast(QP_ QS_OBJ_DIC)); \ QS_OBJ_(obj_); \ QS_STR_ROM_(&obj_name_[0]); \ QP_ QS::end(); \ QS_CRIT_EXIT_(); \ QP_ QS::onFlush(); \ } \ } while (false) /// \brief Output function dictionary record /// /// A function dictionary record associates the binary address of a function /// in the target's memory with the human-readable name of the function. /// /// Providing a function dictionary QS record can vastly improve readability /// of the QS log, because instead of dealing with cryptic machine addresses /// the QSpy host utility can display human-readable function names. /// /// The example from #QS_SIG_DICTIONARY shows the definition of a function /// dictionary. #define QS_FUN_DICTIONARY(fun_) do { \ if (QS_GLB_FILTER_(QP_ QS_FUN_DIC)) { \ static char_t const Q_ROM Q_ROM_VAR fun_name_[] = #fun_; \ QS_CRIT_STAT_ \ QS_CRIT_ENTRY_(); \ QP_ QS::begin(static_cast(QP_ QS_FUN_DIC)); \ QS_FUN_(fun_); \ QS_STR_ROM_(&fun_name_[0]); \ QP_ QS::end(); \ QS_CRIT_EXIT_(); \ QP_ QS::onFlush(); \ } \ } while (false) /// \brief Output user QS rectord dictionary record /// /// A user QS record dictionary record associates the numerical value of a /// user record with the human-readable identifier. #define QS_USR_DICTIONARY(rec_) do { \ if (QS_GLB_FILTER_(QP_ QS_USR_DIC)) { \ static char_t const Q_ROM Q_ROM_VAR usr_name_[] = #rec_; \ QS_CRIT_STAT_ \ QS_CRIT_ENTRY_(); \ QP_ QS::begin(static_cast(QP_ QS_USR_DIC)); \ QS_U8_(static_cast(rec_)); \ QS_STR_ROM_(&usr_name_[0]); \ QP_ QS::end(); \ QS_CRIT_EXIT_(); \ QP_ QS::onFlush(); \ } \ } while (false) /// \brief Output the assertion violation #define QS_ASSERTION(module_, loc_) do { \ QS_BEGIN_NOCRIT_(QP_ QS_ASSERT, \ static_cast(0), static_cast(0)) \ QS_TIME_(); \ QS_U16_(static_cast(loc_)); \ QS_STR_ROM_(module_); \ QS_END_NOCRIT_() \ QP_ QS::onFlush(); \ } while (false) /// \brief Flush the QS trace data to the host /// /// This macro invokes the QS::flush() platform-dependent callback function /// to flush the QS trace buffer to the host. The function typically /// busy-waits until all the data in the buffer is sent to the host. /// This is acceptable only in the initial transient. #define QS_FLUSH() (QP_ QS::onFlush()) /// \brief Output the critical section entry record #define QF_QS_CRIT_ENTRY() \ QS_BEGIN_NOCRIT_(QP_ QS_QF_CRIT_ENTRY, \ static_cast(0), static_cast(0)) \ QS_TIME_(); \ QS_U8_((uint8_t)(++QF_critNest_)); \ QS_END_NOCRIT_() /// \brief Output the critical section exit record #define QF_QS_CRIT_EXIT() \ QS_BEGIN_NOCRIT_(QP_ QS_QF_CRIT_EXIT, \ static_cast(0), static_cast(0)) \ QS_TIME_(); \ QS_U8_((uint8_t)(QF_critNest_--)); \ QS_END_NOCRIT_() /// \brief Output the interrupt entry record #define QF_QS_ISR_ENTRY(isrnest_, prio_) \ QS_BEGIN_NOCRIT_(QP_ QS_QF_ISR_ENTRY, \ static_cast(0), static_cast(0)) \ QS_TIME_(); \ QS_U8_(isrnest_); \ QS_U8_(prio_); \ QS_END_NOCRIT_() /// \brief Output the interrupt exit record #define QF_QS_ISR_EXIT(isrnest_, prio_) \ QS_BEGIN_NOCRIT_(QP_ QS_QF_ISR_EXIT, \ static_cast(0), static_cast(0)) \ QS_TIME_(); \ QS_U8_(isrnest_); \ QS_U8_(prio_); \ QS_END_NOCRIT_() /// \brief Execute an action that is only necessary for QS output #define QF_QS_ACTION(act_) (act_) #endif // qs_h