QP/C 8.1.3
Real-Time Event Framework
Loading...
Searching...
No Matches
Observability Viewpoint

Interface Viewpoint

Viewpoint


SDS_QP_OBS_VP

Observability Viewpoint

Purpose
The Observability Viewpoint focuses on the design of the software tracing facilities, as the main method for obtaining diagnostic information from a running system consisting of Active Objects. This design viewpoint focuses on the software tracing component inside the QP/C Framework, called QS (from "QP Spy").

Design Concerns

  • data protocol
  • target-resident component


QP/Spy Data Protocol

One of the greatest strengths of any software tracing system is the data transmission protocol. The protocol must be agreed upon by both the target-resident component and the host-resident component of the whole software tracing system (called QP/Spy).

The QP/Spy protocol is lightweight, but contains the mechanisms for checking both data integrity and continuity. It has many elements of the High Level Data Link Control (HDLC↑) protocol defined by the International Standards Organization (ISO). The QP/Spy protocol has been specifically designed to simplify the data management overhead in the target, yet to allow detection of any data dropouts due to trace buffer overruns. The protocol not only has provisions for detecting gaps in the data and other errors, but it also allows for instantaneous re-synchronization after any buffering or transmission error to minimize loss of useful data.

QP/Spy transmission protocol

The QP/Spy protocol transmits each trace record in an HDLC-like frame. The upper part of the figure above shows the serial data stream transmitted from the target, containing frames of different lengths. The bottom part of the figure above shows the details of a single frame:

  1. Each frame starts with the Frame Sequence-No byte. The target QS component increments the Frame Sequence Number for every frame inserted into the circular buffer. The Sequence Number naturally rolls over from 255 to 0. The Frame Sequence Number allows the QSPY host component to detect any data discontinuities.
  2. Following the Frame Sequence Number is the Record-Type byte, which is one of the predefined QS records, or an application-specific record.
  3. Following the Record-Type is zero or more Data bytes.
  4. Following the data is the Checksum. The Checksum is computed over the Frame Sequence Number, the Record ID, and all the Data bytes. The next section gives the detailed Checksum computation formula.
  5. Following the Checksum is the HDLC Flag, which delimits the frame. The HDLC flag is the 01111110 binary string (0x7E hexadecimal). Please note that the QP/Spy protocol uses only one HDLC Flag at the end of each frame and no HDLC Flag at the beginning of a frame. In other words, only one Flag is inserted between frames.
Note
The QS target component performs the HDLC-like framing described above at the time the bytes are inserted into the circular trace buffer. This means that the data in the buffer is already cleanly divided into frames and can be transmitted in any chunks, typically not aligned with the frame boundaries.

Transparency

One of the most essential characteristics of HDLC-type protocols is establishing very easily identifiable frames in the serial data stream. Any receiver of such a protocol can instantaneously synchronize to the frame boundary by simply finding the Flag byte. This is because the special Flag byte can never occur within the content of a frame. To avoid confusing unintentional Flag bytes that can naturally occur in the data stream with an intentionally sent Flag, HDLC uses a technique known as transparency (a.k.a. byte-stuffing or escaping) to make the Flag bytes transparent during transmission. Whenever the transmitter encounters a Flag byte in the data, it inserts a two-byte escape sequence into the output stream. The first byte is the Escape byte, defined as binary 01111101 (hexadecimal 0x7D). The second byte is the original byte XOR-ed with 0x20.

Of course, now the Escape byte itself must also be transparent to avoid interpreting an unintentional Escape byte as the two-byte escape sequence. The procedure of escaping the Escape byte is identical to that of escaping the Flag byte.

The transparency of the Flag and Escape bytes complicates the computation of the Checksum slightly. The transmitter computes the Checksum over the Frame Sequence Number, the Record-Type, and all Data bytes before performing any "byte-stuffing". The receiver must apply the exact reversed procedure of performing the "byte-un-stuffing" before computing the Checksum.

An example may make this clearer. Suppose that the following trace record needs to be inserted into the trace buffer (the transparent bytes are shown in code format):

     Record-Type = 0x7D, Record Data = 0x7D 0x08 0x01

Assuming that the current Frame Sequence Number is, say, 0x7E, the Checksum will be computed over the following bytes:

     Checksum == (uint8_t)(~(0x7E + 0x7D + 0x7D + 0x08 + 0x01)) == 0x7E

And the actual frame inserted into the QS trace buffer will be as follows:

     0x7D 0x5E 0x7D 0x5D 0x7D 0x5D 0x08 0x01 0x7D 0x5E 0x7E

Remarks
This is a degenerate example, where the Frame Sequence Number, the Record-Type, a Data byte, and the Checksum itself turned out to be the transparent bytes. A more typical overhead of transparency with real trace data is one escape sequence per several trace records.
Note
Byte-stuffing (escaping) is not performed on the ASCII strings produced by the QS_STR() macro in the application-specific trace records because most printable ASCII characters are below the 0x7D limit. However, this leaves two printable ASCII characters unusable in the application-specific strings. More specifically, characters } (0x7D) and ~ (0x7E) must be avoided. Please also see comments in the description of the QS_STR() macro.

Endianness

In addition to the HDLC-like framing, the QP/Spy transmission protocol specifies the endianness of the data to be little-endian. All multi-byte data elements, such as 16-, 32-, or 64-bit integers, pointers, and floating point numbers are inserted into the QS trace buffer in the little-endian byte order (least-significant byte first). The QS data inserting macros place the data in the trace buffer in a platform-neutral manner, meaning that the data is inserted into the buffer in the little-endian order regardless of the endianness of the CPU. Also, the data-inserting macros copy the data to the buffer one byte at a time, thus avoiding any potential data misalignment problems. Many embedded CPUs, such as ARM (Cortex-M0), require specific alignment of 16-, 32-, or 64-bit quantities.

Last-is-Best Data Policy

The QS Trace Buffers (transmit QS buffer and receive QS-RX buffer) store only complete HDLC frames, which is the pivotal point in the design of the QS target component and has two important consequences:

  1. The use of HDLC frames in the buffers enables the last is best tracing policy. The QP/Spy transmission protocol maintains both the Frame Sequence Number and the Checksum over each trace record, which means that any data corruption caused by overwriting the old data with the new data can always be reliably detected. Therefore, the new trace data is inserted into the circular trace buffers, regardless of whether it overwrites the old data that hasn't been sent out yet or is in the process of being sent. The burden of detecting any data corruption is placed on the QSPY host component. When you start missing frames (which the host component easily detects by discontinuities in the Frame Sequence Number), you have several options. You can apply some additional filtering, increase the size of the buffer, or improve the data transfer throughput.
  2. The use of HDLC-formatted data in the trace buffers allows decoupling data insertion into the trace buffers from data removal out of the trace buffers. You can remove the data in whichever chunks you like, without any consideration for frame boundaries. You can employ just about any available physical data link on the target for transferring the trace data from the target to the host.

QS Target-Resident Component

The target-resident component of the QP/Spy tracing system is called QS. The purpose of QS is to provide facilities for instrumenting the target code so it will produce an interesting real-time trace from code execution. In this sense, it is similar to peppering the code with printf statements. However, the main difference between QS and printf is where the data formatting and sending is done. When you use printfs, the data formatting and sending occur in the time-critical paths through your embedded code. In contrast, the QS target-resident component inserts raw binary data into the QS ring buffer, so all the time-consuming formatting is removed from the Target system and is done after the fact in the Host. Additionally, in QS, data logging and sending to the Host are separated so that the target system can typically perform the transmission outside of the time-critical path, for example, in the idle processing of the target CPU.

The QS target component consists of the QS ring buffer, the QS filters, as well as the instrumentation added to the QP/C Framework and the application, as shown in the figure below. Additionally, the QS target component contains the receive-channel (QS-RX) with its own receive buffer, which can receive data from the QSPY host component.

Structure of the QP/Spy software tracing system

A nice byproduct of removing the data formatting from the Target is a natural data compression. For example, formatted output of a single byte takes two hexadecimal digits (and 3 decimal digits), so avoiding the formatting gives at least a factor of two in data density. On top of this natural compression, QS uses such techniques as data dictionaries and compressed format information, which in practice result in a compression factor of 4-5 compared to the expanded human-readable format.

Attention
The QS instrumentation is designed such that it is active only in the Spy build configuration (when the external macro Q_SPY is defined) and is inactive otherwise. This means that you don't need to comment out or remove the instrumentation in the Debug or Release build configurations, but instead you can leave the instrumentation in your code for its future development, testing, profiling, and maintenance.

Predefined QS Trace Records

The QP/C Framework contains the QS instrumentation for tracing the interesting occurrences within the framework, such as state machine activity (dispatching events, entering/exiting a state, executing transitions, etc.), Active Object activity (allocating events, posting/publishing events, time events, etc.), and more. All this instrumentation reserves 100 predefined QS trace records, which are enumerated in QS_GlbPredef. These QS records have a predefined (hard-coded) structure both in the QS target-resident component and in the QSPY host-based application↑. See also the documentation of the human-readable output↑ generated from the predefined QS records.

Application-Specific QS Trace Records

In addition to the predefined QS records, you can add your own, flexible, application-specific trace records, which are not known in advance to the QSPY host-resident component. You can think of the application-specific records as an equivalent to printf(), but with much less overhead. The following code snippet shows an example of an application-specific QS record from your embedded code:

enum MyAppRecords {
PHILO_STAT = QS_USER, // offset for User QS Records
COMMAND_STAT,
~ ~ ~
};
~ ~ ~
QS_BEGIN_ID(PHILO_STAT, AO_Philo[n]->prio) // application-specific record begin
QS_U8(1, n); // application-specific data element (Philo number)
QS_STR(stat); // application-specific data element (Philo status)
QS_END() // application-specific record end
#define QS_U8(width_, data_)
Definition qs.h:302
#define QS_USER
Definition qs.h:194
#define QS_END()
Definition qs.h:273
#define QS_STR(str_)
Definition qs.h:329

As you can see from the example above, an application-specific trace record always begins with QS_BEGIN_ID(), followed by several application-specific data elements, followed by QS_END().

Attention
The application-specific records use the 25 Record-Types in the range QS_USER .. 124 (see also User-All group in Global Filter). Therefore, you need to offset your application-specific trace records by the QS_USER constant to avoid overlap with the predefined %QS records already instrumented into the QP components.
Note
As with all QS trace records, the application-specific records are produced in a critical section of code (with interrupts disabled). Therefore, you should keep the number of application-specific data elements of the record reasonably short.

Application-Specific Record Representation
The biggest challenge in supporting flexible "application-specific" trace records is to provide the data type information with the data itself, so that QSPY "knows" how to parse such records and move on to the next data element within the record. The figure below shows the encoding of the application-specific trace record from the previous listing.

Structure of an Application-Specific trace record

The application-specific trace record, like all QS records, starts with the Sequence Number and the Record-Type. Every application-specific trace record also contains the timestamp immediately following the Record Type. The number of bytes used by the timestamp is configurable by the macro QS_TIME_SIZE. After the timestamp, you see the data elements, such as a byte (QS_U8()) and a string (QS_STR()). Each of these data elements starts with a fmt (format) byte, which actually contains both the data-type information (in the lower nibble) and the format width for displaying that element (in the upper nibble). For example, the data element QS_U8(1, n) will cause the value 'n' to be encoded as uint8_t with the format width of 1 decimal digit.

Remarks
The maximum allowed format width is 15 decimal digits, while a format width of 0 means that a numeric value should be formatted in the minimum number of digits.

As shown in the listing above, typically, the application-specific records are enclosed with the QS_BEGIN_ID() / QS_END() pair of macros. This pair of macros disables interrupts at the beginning and enables them again at the end of each record. Occasionally, you might want to generate trace data from within already-established critical sections or ISRs. In such rare occasions, you would use the macros QS_BEGIN_NOCRIT() / QS_END_NOCRIT() to avoid nesting of critical sections.

The record-begin macro QS_BEGIN_ID() takes two arguments. The first argument (e.g., PHILO_STAT) is the enumerated Record-Type, which is used in the global filter and is part of each record header.

The second argument (e.g., AO_Philo[n]->prio in the example above) is used for the local filter, which allows you to log only specific objects selectively. The code snippet shows an example of an application-specific trace record, including use of the second parameter of the QS_BEGIN_ID() macro.

Application-Specific Record Examples
The following examples show the QS application-specific trace records as C code on the left, and the output generated by the QSPY host application from these records on the right. The examples assume that the QS dictionaries have been produced for the Record-Types and function/object pointers used.

Trace Record QSPY output
QS_BEGIN_ID(PHILO_STAT, AO_Philo[n]->prio)
QS_U8(1, n);
QS_STR(stat);
#define QS_BEGIN_ID(rec_, qsId_)
Definition qs.h:264
1018004718 PHILO_STAT 1 thinking
NOTE: produced only when AO_Philo[n] is enabled in the QS Local Filter
QS_BEGIN_ID(IO_CALL, 0U)
QS_FUN(&IO_Read);
QS_I16(0, ret);
QS_U32(0, offset);
#define QS_U32(width_, data_)
Definition qs.h:314
#define QS_FUN(fun_)
Definition qs.h:354
#define QS_I16(width_, data_)
Definition qs.h:305

1055004424 IO_CALL IO_Read -129 0

QS_BEGIN_ID(DATA_RX, 0U)
QS_OBJ(me);
QS_U16(data_len);
QS_MEM(data_buf, 16);
#define QS_OBJ(obj_)
Definition qs.h:346
#define QS_U16(width_, data_)
Definition qs.h:308
#define QS_MEM(mem_, size_)
Definition qs.h:331

0207024814 DATA_RX l_uart2 10 17 84 BB 40 FD 15 00 00 99 0B 00 00 90 0D 00 20

QS_BEGIN_ID(FP_DATA, QS_AP_ID + 1U)
QS_F32(6, 3141.5F);
QS_F64(10, -2.718281828e5);
#define QS_AP_ID
Definition qpc.h:87
#define QS_F64(width_, data_)
Definition qs.h:326
#define QS_F32(width_, data_)
Definition qs.h:323
0991501750 FP_DATA 3.141500e+003 -2.7182818280e+005
NOTE: produced only when QS-ID QS_AP_ID + 1 is enabled in the QS Local Filter

Application-Specific Data Elements
The following table summarizes the supported data elements that can be used inside the Application-Specific trace records:

Data Element Example Comments
QS_U8() QS_U8(0, n); Outputs a uint8_t integer with format "u"
QS_I8() QS_I8(3, m); Outputs a int8_t integer with format "%3d"
QS_U16() QS_U16(5, n); Outputs a uint16_t integer with format "%5u"
QS_I16() QS_I16(0, m); Outputs a int16_t integer with format "d"
QS_U32() QS_U32(QS_HEX_FMT, n); Outputs a uint32_t integer with format "%8X"
QS_I32() QS_I32(0, m); Outputs a int32_t integer with format "d"
QS_U64() QS_U32(0, n); Outputs a uint32_t integer with format "%2"PRIi64
QS_F32() QS_F32(0, 3.1415F); Outputs a 32-bit float with format "%7.0e"
(zero digits after the comma)
QS_F64() QS_F64(4, sqrt(2.0)); Outputs a 64-bit double with format "%12.4e"
(4 digits after the comma)
QS_STR() QS_STR("Hello") Outputs a zero-terminated string with format "s"
QS_MEM() QS_MEM(&my_struct, 16) Outputs 16 bytes of memory starting from &my_struct.
The bytes are output using the hex format "%02X"
QS_OBJ() QS_OBJ(&my_obj); Outputs an object pointer.
If an object dictionary for the object exists,
QSPY will display the symbolic name of the object
QS_FUN() QS_OBJ(&foo); Outputs a function pointer.
If a function dictionary for the function exists,
QSPY will display the symbolic name of the function
QS_SIG() QS_SIG(TIMEOUT_SIG, (void*)0); Outputs a signal.
If signal dictionary for the signal exists,
QSPY will display the symbolic name of the signal

QS Filters

Obviously, QS cannot completely eliminate the overhead of software tracing. But with the fine-granularity filters available in QS, you can make this impact as small as necessary. For greatest flexibility, QS uses two complementary levels of filters: Global Filter and Local Filter described below. The combination of such two complementary filtering criteria results in very selective tracing capabilities.

Note
The Global and Local filters are initialized in the QS_INIT() macro. Subsequently, the QP/C Application can change the filters at runtime by means of the QS_GLB_FILTER() and QS_LOC_FILTER() macros. Also, if the QS receive channel is enabled (QS-RX), the filters can be changed from the QUTest or QView front-ends.

QS Global Filter
The Global Filter is based on trace Record-Types associated with each QS record (see ::QSpyRecords). This filter allows you to disable or enable each individual Record-Type or a whole group of QS records. For example, you might enable or disable QS_QEP_STATE_ENTRY (entry to a state), QS_QEP_STATE_EXIT (exit from a state), QS_QEP_INIT_TRAN (state transition), QS_QF_ACTIVE_POST (event posting), QS_QF_PUBLISH (event publishing), and all other pre-defined and application-specific event types. This level works globally for all state machines, active objects, and time event objects in the entire system.

QS provides a simple interface, QS_GLB_FILTER(), for setting and clearing individual Record-Types as well as groups of Record-Types in the Target code. The following table summarizes the Record-Types and groups of Record-Types that you can use as arguments to QS_GLB_FILTER().


Record-Type
/Group
Example Applies to QS Records
All Record-Types QS_GLB_FILTER(QS_ALL_RECORDS);
QS_GLB_FILTER(-QS_ALL_RECORDS);
all Record-Types
State
Machine
Group
QS_GLB_FILTER(QS_SM_RECORDS);
QS_GLB_FILTER(-QS_SM_RECORDS);
QS_QEP_STATE_ENTRY,
QS_QEP_STATE_EXIT,
QS_QEP_STATE_INIT,
QS_QEP_INIT_TRAN,
QS_QEP_INTERN_TRAN,
QS_QEP_TRAN,
QS_QEP_IGNORED,
QS_QEP_TRAN_HIST
Active
Object
Group
QS_GLB_FILTER(QS_AO_RECORDS);
QS_GLB_FILTER(-QS_AO_RECORDS);
QS_QF_ACTIVE_SUBSCRIBE,
QS_QF_ACTIVE_UNSUBSCRIBE,
QS_QF_ACTIVE_POST,
QS_QF_ACTIVE_POST_LIFO,
QS_QF_ACTIVE_GET,
QS_QF_ACTIVE_GET_LAST
Event
Queue
Group
QS_GLB_FILTER(QS_EQ_RECORDS);
QS_GLB_FILTER(-QS_EQ_RECORDS);
QS_QF_EQUEUE_POST,
QS_QF_EQUEUE_POST_LIFO,
QS_QF_EQUEUE_GET,
QS_QF_EQUEUE_GET_LAST
Memory
Pool
Group
QS_GLB_FILTER(QS_MP_RECORDS);
QS_GLB_FILTER(-QS_MP_RECORDS);
QS_QF_MPOOL_GET,
QS_QF_MPOOL_PUT
Time
Event
Group
QS_GLB_FILTER(QS_TE_RECORDS);
QS_GLB_FILTER(-QS_TE_RECORDS);
QS_QF_TICK,
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
QF Group
Event
Management
QS_GLB_FILTER(QS_QF_RECORDS);
QS_GLB_FILTER(-QS_QF_RECORDS);
QS_QF_NEW,
QS_QF_NEW_ATTEMPT,
QS_QF_GC_ATTEMPT,
QS_QF_GC,
QS_QF_TIMEEVT_DISARM_ATTEMPT,
QS_QF_TICK,
Scheduler
Group
QS_GLB_FILTER(QS_SC_RECORDS);
QS_GLB_FILTER(-QS_SC_RECORDS);
QS_SCHED_LOCK,
QS_SCHED_UNLOCK,
QS_SCHED_NEXT,
QS_SCHED_IDLE,
QS_QF_TIMEEVT_DISARM_ATTEMPT,
QS_QF_TICK,
User Group-0 QS_GLB_FILTER(QS_U0_RECORDS);
QS_GLB_FILTER(-QS_U0_RECORDS);
QS_USER+0 .. QS_USER+4
User Group-1 QS_GLB_FILTER(QS_U1_RECORDS);
QS_GLB_FILTER(-QS_U1_RECORDS);
QS_USER+5 .. QS_USER+9
User Group-2 QS_GLB_FILTER(QS_U2_RECORDS);
QS_GLB_FILTER(-QS_U2_RECORDS);
QS_USER+10 .. QS_USER+14
User Group-3 QS_GLB_FILTER(QS_U3_RECORDS);
QS_GLB_FILTER(-QS_U3_RECORDS);
QS_USER+15 .. QS_USER+19
User Group-4 QS_GLB_FILTER(QS_U4_RECORDS);
QS_GLB_FILTER(-QS_U4_RECORDS);
QS_USER+20 .. QS_USER+24
User-All Group QS_GLB_FILTER(QS_UA_RECORDS);
QS_GLB_FILTER(-QS_UA_RECORDS);
QS_USER+0 .. QS_USER+24
Non-Maskable QS_SIG_DICT,
QS_OBJ_DICT,
QS_FUN_DICT,
QS_USR_DICT,
QS_TARGET_INFO,
QS_TARGET_DONE,
QS_EMPTY,
QS_TEST_PAUSED,
QS_TEST_PROBE_GET,
QS_RX_STATUS,
QS_QUERY_DATA,
QS_PEEK_DATA,
QS_ASSERT_FAIL,
QS_QF_RUN


Attention
The QS Global Filter is initialized in the QS_INIT() macro to all OFF.

Here are some examples of setting and clearing the QS Global Filter with QS_GLB_FILTER():

void BSP_init(int argc, char *argv[]) {
~ ~ ~
if (!QS_INIT(argv)) { // Initialize QS target component
Q_ERROR(); // unable to initialize QSpy
}
~ ~ ~
// apply the QS Global Filter...
QS_GLB_FILTER(QS_QF_RECORDS); // turn QF-group ON
QS_GLB_FILTER(-QS_QF_TICK); // turn #QS_QF_TICK OFF
~ ~ ~
}
@ QS_QF_RECORDS
Definition qpc.h:64
@ QS_QF_TICK
QTimeEvt tick was called.
Definition qs.h:95
#define QS_GLB_FILTER(rec_)
check the global filter
Definition qs.h:260
#define QS_INIT(arg_)
Initialize the QS software tracing system.
Definition qs.h:256
#define Q_ERROR()
Assertion for a wrong path through the code (with ID provided in LINE).
Definition qsafe.h:94

QS Local Filter
The Local Filter is based on QS-IDs associated with various objects in the Target memory. The QS-IDs are small integer numbers, such as the unique priorities assigned to QP Active Objects, but there are more such QS-IDs which you can assign to various objects. Then, you can set up the QS Local Filter to trace only a specific groups of such QS-IDs.

The main use case for QS Local Filter is an application where certain active objects are very "noisy", and would overwhelm your trace. The QS Local Filter allows you to silence the "noisy" active objects and let the others through.

Please note that the QS Global Filter will not do the trick, because you don't want to suppress all QS records of a given Record-Type. Instead, you want to suppress only specific objects.

QS provides a simple interface, QS_LOC_FILTER(), for setting and clearing individual QS-IDs as well as groups of QS-IDs in the Target code. The following table summarizes the QS-IDs and groups of QS_IDs that you can use as arguments to QS_LOC_FILTER().

QS-ID
/Group
Range Example Comments
0 0 always enabled
QS_AO_IDS 1..64 QS_LOC_FILTER(QS_AO_IDS);
QS_LOC_FILTER(-QS_AO_IDS);
QS_LOC_FILTER(6);
QS_LOC_FILTER(-6);
QS_LOC_FILTER(AO_Table->prio)
Active Object priorities
QS_EP_IDS 65..80 QS_LOC_FILTER(QS_EP_ID + 1U); enable Event-Pool #1
QS_EQ_IDS 81..96 QS_LOC_FILTER(QS_EQ_ID + 1U); enable Event-Queue #1
QS_AP_IDS 97..127 QS_LOC_FILTER(QS_AP_ID + 1U); enable Application Specific QS_ID
Attention
The QS Local Filter is initialized in the QS_INIT() macro to all ON.

Here are some examples of setting and clearing QS Local Filter with QS_LOC_FILTER():

void BSP_init(int argc, char *argv[]) {
~ ~ ~
if (!QS_INIT(argv)) { // Initialize QS target component
Q_ERROR(); // unable to initialize QSpy
}
~ ~ ~
// apply the QS Local Filter...
QS_LOC_FILTER(-QS_EP_IDS); // turn EP (Event-Pool) group OFF
QS_LOC_FILTER(3); // turn AO with priority 3 ON
QS_LOC_FILTER(AO_Table->prio); // turn AO_Table ON
~ ~ ~
}
@ QS_EP_IDS
Definition qpc.h:80
#define QS_LOC_FILTER(qsId_)
check the local filter
Definition qs.h:261

QS Target Time-Stamps

Most QS trace records produced by QS are time-stamped with a high-resolution counter (the resolution depends on the availability of a hardware timer-counter in the Target, but typically provides sub-microsecond granularity). QS provides an efficient API for obtaining platform-specific timestamp information. Given the right timer-counter resource in your Target system, you can provide QS with as precise timestamp information as required. The size of the timestamp is configurable to be 1, 2, or 4 bytes (see QS_TIME_SIZE).

Note
The QS timestamps are used differently in QUTest unit testing. In that case the timestamps are used to count the QS records produced.

Current Objects

QS maintains a set of Current Objects to which it applies commands received through the QS-RX channel. For example, the event-post operation is applied to the current Active Object, while the peek/poke/fill operations are applied to the current Application Object. QS maintains the following Current Objects:

Note
Current Objects can be set only by sending commands to the QS-RX receive channel.

QS Dictionaries

By the time you compile and load your application image to the Target, the symbolic names of various objects, function names, and event signal names are stripped from the code. Therefore, if you want to have the symbolic information available to the QSPY host-resident component, you need to supply it somehow to the software tracing system.

The QS Target-resident component provides special dictionary trace records designed expressly for providing the symbolic information about the target code in the trace itself. These "dictionary records" are very much like the symbolic information embedded in the object files for the traditional single-step debugger. QS can supply five types of dictionary trace records:

The dictionary trace records are typically generated during the system initialization and this is the only time they are sent to the QSPY host component. It is your responsibility to code them in (by means of the QS_???_DICTIONARY() macros). The following code snippet provides some examples of generating QS dictionaries:

void BSP_init(int argc, char *argv[]) {
~ ~ ~
if (!QS_INIT(argv)) { // Initialize QS target component
Q_ERROR(); // unable to initialize QSpy
}
// dictionaries...
QS_OBJ_DICTIONARY(&myObject);
QS_OBJ_DICTIONARY(AO_Table); // opaque pointer
QS_SIG_DICTIONARY(TIMEOUT_SIG, (void*)0);
QS_USR_DICTIONARY(ON_TEST_SETUP);
QS_USR_DICTIONARY(ON_TEST_TEARDOWN);
QS_ENUM_DICTIONARY(COMMAND_X, QS_CMD); // for command
~ ~ ~
}
#define QS_OBJ_DICTIONARY(obj_)
Produce object dictionary.
Definition qs.h:366
#define QS_FUN_DICTIONARY(fun_)
Produce function dictionary (typically for state-handler functions).
Definition qs.h:372
#define QS_CMD
Definition qs.h:215
#define QS_SIG_DICTIONARY(sig_, obj_)
Produce event signal dictionary.
Definition qs.h:363
#define QS_ENUM_DICTIONARY(value_, group_)
Produce enumeration dictionary for application-defined enumerations.
Definition qs.h:378
#define QS_USR_DICTIONARY(rec_)
Produce user-record dictionary for application-specific trace records.
Definition qs.h:375
Remarks
The dictionary trace records are not absolutely required to generate the human-readable output, in the same way that the symbolic information in the object files is not absolutely required to debug your code. However, in both cases, the availability of the symbolic information greatly improves productivity in working with the software trace or the debugger.

QS-RX Receive-Channel

The QS target component contains the receive-channel (QS-RX), which can receive data from the QSPY host application. The QS-RX channel provides the following services:

  • Remotely reset the Target
  • Request target information (version, all sizes of objects, build time-stamp)
  • Execute a user-defined command inside the Target with arguments supplied from QSPY
  • Inject an arbitrary event to the Target (dispatch, post or publish)
  • Set QS Global Filter inside the Target
  • Set QS Local Filter inside the Target
  • Set current QS object inside the Target
  • Peek data inside the Target and send to QSPY
  • Poke data (supplied from QSPY) into the Target
  • Fill a specified memory area in the Target with a bit pattern supplied from QSPY
  • Execute clock tick inside the Target
  • Execute test setup inside the Target
  • Execute test teardown inside the Target
  • Store a Test Probe supplied from QSPY inside the Target
Note
The QS-RX channel is the backbone for interacting with the target system and implementing such features as Unit Testing and Visualization/Monitoring of the target system.

Interface Viewpoint