summaryrefslogtreecommitdiff
path: root/FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcStreamingRecorder.c
diff options
context:
space:
mode:
Diffstat (limited to 'FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcStreamingRecorder.c')
-rw-r--r--FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcStreamingRecorder.c1896
1 files changed, 1896 insertions, 0 deletions
diff --git a/FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcStreamingRecorder.c b/FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcStreamingRecorder.c
new file mode 100644
index 000000000..dc1b87865
--- /dev/null
+++ b/FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcStreamingRecorder.c
@@ -0,0 +1,1896 @@
+/*******************************************************************************
+ * Trace Recorder Library for Tracealyzer v4.1.5
+ * Percepio AB, www.percepio.com
+ *
+ * trcStreamingRecorder.c
+ *
+ * The generic core of the trace recorder's streaming mode.
+ *
+ * Terms of Use
+ * This file is part of the trace recorder library (RECORDER), which is the
+ * intellectual property of Percepio AB (PERCEPIO) and provided under a
+ * license as follows.
+ * The RECORDER may be used free of charge for the purpose of recording data
+ * intended for analysis in PERCEPIO products. It may not be used or modified
+ * for other purposes without explicit permission from PERCEPIO.
+ * You may distribute the RECORDER in its original source code form, assuming
+ * this text (terms of use, disclaimer, copyright notice) is unchanged. You are
+ * allowed to distribute the RECORDER with minor modifications intended for
+ * configuration or porting of the RECORDER, e.g., to allow using it on a
+ * specific processor, processor family or with a specific communication
+ * interface. Any such modifications should be documented directly below
+ * this comment block.
+ *
+ * Disclaimer
+ * The RECORDER is being delivered to you AS IS and PERCEPIO makes no warranty
+ * as to its use or performance. PERCEPIO does not and cannot warrant the
+ * performance or results you may obtain by using the RECORDER or documentation.
+ * PERCEPIO make no warranties, express or implied, as to noninfringement of
+ * third party rights, merchantability, or fitness for any particular purpose.
+ * In no event will PERCEPIO, its technology partners, or distributors be liable
+ * to you for any consequential, incidental or special damages, including any
+ * lost profits or lost savings, even if a representative of PERCEPIO has been
+ * advised of the possibility of such damages, or for any claim by any third
+ * party. Some jurisdictions do not allow the exclusion or limitation of
+ * incidental, consequential or special damages, or the exclusion of implied
+ * warranties or limitations on how long an implied warranty may last, so the
+ * above limitations may not apply to you.
+ *
+ * Tabs are used for indent in this file (1 tab = 4 spaces)
+ *
+ * Copyright Percepio AB, 2018.
+ * www.percepio.com
+ ******************************************************************************/
+
+#include "trcRecorder.h"
+
+#if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_STREAMING)
+
+#if (TRC_USE_TRACEALYZER_RECORDER == 1)
+
+#include <stdio.h>
+#include <stdarg.h>
+
+typedef struct{
+ uint16_t EventID;
+ uint16_t EventCount;
+ uint32_t TS;
+} BaseEvent;
+
+typedef struct{
+ BaseEvent base;
+ uint32_t param1;
+} EventWithParam_1;
+
+typedef struct{
+ BaseEvent base;
+ uint32_t param1;
+ uint32_t param2;
+} EventWithParam_2;
+
+typedef struct{
+ BaseEvent base;
+ uint32_t param1;
+ uint32_t param2;
+ uint32_t param3;
+} EventWithParam_3;
+
+/* Used in event functions with variable number of parameters. */
+typedef struct
+{
+ BaseEvent base;
+ uint32_t data[15]; /* maximum payload size */
+} largestEventType;
+
+typedef struct{
+ uint32_t psf;
+ uint16_t version;
+ uint16_t platform;
+ uint32_t options;
+ uint16_t symbolSize;
+ uint16_t symbolCount;
+ uint16_t objectDataSize;
+ uint16_t objectDataCount;
+} PSFHeaderInfo;
+
+
+/* The size of each slot in the Symbol Table */
+#define SYMBOL_TABLE_SLOT_SIZE (sizeof(uint32_t) + (((TRC_CFG_SYMBOL_MAX_LENGTH)+(sizeof(uint32_t)-1))/sizeof(uint32_t))*sizeof(uint32_t))
+
+#define OBJECT_DATA_SLOT_SIZE (sizeof(uint32_t) + sizeof(uint32_t))
+
+/* The total size of the Symbol Table */
+#define SYMBOL_TABLE_BUFFER_SIZE ((TRC_CFG_SYMBOL_TABLE_SLOTS) * SYMBOL_TABLE_SLOT_SIZE)
+
+/* The total size of the Object Data Table */
+#define OBJECT_DATA_TABLE_BUFFER_SIZE ((TRC_CFG_OBJECT_DATA_SLOTS) * OBJECT_DATA_SLOT_SIZE)
+
+/* The Symbol Table type - just a byte array */
+typedef struct{
+ union
+ {
+ uint32_t pSymbolTableBufferUINT32[SYMBOL_TABLE_BUFFER_SIZE / sizeof(uint32_t)];
+ uint8_t pSymbolTableBufferUINT8[SYMBOL_TABLE_BUFFER_SIZE];
+ } SymbolTableBuffer;
+} SymbolTable;
+
+/* The Object Data Table type - just a byte array */
+typedef struct{
+ union
+ {
+ uint32_t pObjectDataTableBufferUINT32[OBJECT_DATA_TABLE_BUFFER_SIZE / sizeof(uint32_t)];
+ uint8_t pObjectDataTableBufferUINT8[OBJECT_DATA_TABLE_BUFFER_SIZE];
+ } ObjectDataTableBuffer;
+} ObjectDataTable;
+
+typedef struct{
+ uint16_t Status; /* 16 bit to avoid implicit padding (warnings) */
+ uint16_t BytesRemaining;
+ char* WritePointer;
+} PageType;
+
+/* Code used for "task address" when no task has started. (NULL = idle task) */
+#define HANDLE_NO_TASK 2
+
+#define PAGE_STATUS_FREE 0
+#define PAGE_STATUS_WRITE 1
+#define PAGE_STATUS_READ 2
+
+#define PSF_ASSERT(_assert, _err) if (! (_assert)){ prvTraceError(_err); return; }
+
+/* Part of the PSF format - encodes the number of 32-bit params in an event */
+#define PARAM_COUNT(n) ((n & 0xF) << 12)
+
+/* The Symbol Table instance - keeps names of tasks and other named objects. */
+static SymbolTable symbolTable = { { { 0 } } };
+
+/* This points to the first unused entry in the symbol table. */
+static uint32_t firstFreeSymbolTableIndex = 0;
+
+/* The Object Data Table instance - keeps initial priorities of tasks. */
+static ObjectDataTable objectDataTable = { { { 0 } } };
+
+/* This points to the first unused entry in the object data table. */
+static uint32_t firstFreeObjectDataTableIndex = 0;
+
+/* Keeps track of ISR nesting */
+static uint32_t ISR_stack[TRC_CFG_MAX_ISR_NESTING];
+
+/* Keeps track of ISR nesting */
+static int8_t ISR_stack_index = -1;
+
+/* Any error that occurred in the recorder (also creates User Event) */
+static int errorCode = 0;
+
+/* Counts the number of trace sessions (not yet used) */
+static uint32_t SessionCounter = 0u;
+
+/* Master switch for recording (0 => Disabled, 1 => Enabled) */
+uint32_t RecorderEnabled = 0u;
+
+/* Used to determine endian of data (big/little) */
+static uint32_t PSFEndianessIdentifier = 0x50534600;
+
+/* Used to interpret the data format */
+static uint16_t FormatVersion = 0x0004;
+
+/* The number of events stored. Used as event sequence number. */
+static uint32_t eventCounter = 0;
+
+/* Remembers if an earlier ISR in a sequence of adjacent ISRs has triggered a task switch.
+In that case, vTraceStoreISREnd does not store a return to the previously executing task. */
+int32_t isPendingContextSwitch = 0;
+
+uint32_t uiTraceTickCount = 0;
+uint32_t timestampFrequency = 0;
+uint32_t DroppedEventCounter = 0;
+uint32_t TotalBytesRemaining_LowWaterMark = (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT) * (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE);
+uint32_t TotalBytesRemaining = (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT) * (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE);
+
+PageType PageInfo[TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT];
+
+char* EventBuffer = NULL;
+
+/*******************************************************************************
+ * NoRoomForSymbol
+ *
+ * Incremented on prvTraceSaveSymbol if no room for saving the symbol name. This
+ * is used for storing the names of:
+ * - Tasks
+ * - Named ISRs (xTraceSetISRProperties)
+ * - Named kernel objects (vTraceStoreKernelObjectName)
+ * - User event channels (xTraceRegisterString)
+ *
+ * This variable should be zero. If not, it shows the number of missing slots so
+ * far. In that case, increment SYMBOL_TABLE_SLOTS with (at least) this value.
+ ******************************************************************************/
+volatile uint32_t NoRoomForSymbol = 0;
+
+/*******************************************************************************
+ * NoRoomForObjectData
+ *
+ * Incremented on prvTraceSaveObjectData if no room for saving the object data,
+ * i.e., the base priorities of tasks. There must be one slot for each task.
+ * If not, this variable will show the difference.
+ *
+ * This variable should be zero. If not, it shows the number of missing slots so
+ * far. In that case, increment OBJECT_DATA_SLOTS with (at least) this value.
+ ******************************************************************************/
+volatile uint32_t NoRoomForObjectData = 0;
+
+/*******************************************************************************
+ * LongestSymbolName
+ *
+ * Updated in prvTraceSaveSymbol. Should not exceed TRC_CFG_SYMBOL_MAX_LENGTH,
+ * otherwise symbol names will be truncated. In that case, set
+ * TRC_CFG_SYMBOL_MAX_LENGTH to (at least) this value.
+ ******************************************************************************/
+volatile uint32_t LongestSymbolName = 0;
+
+/*******************************************************************************
+ * MaxBytesTruncated
+ *
+ * Set in prvTraceStoreStringEvent if the total data payload exceeds 60 bytes,
+ * including data arguments and the string. For user events, that is 52 bytes
+ * for string and data arguments. In that is exceeded, the event is truncated
+ * (usually only the string, unless more than 15 parameters) and this variable
+ * holds the maximum number of truncated bytes, from any event.
+ ******************************************************************************/
+volatile uint32_t MaxBytesTruncated = 0;
+
+uint16_t CurrentFilterMask = 0xFFFF;
+
+uint16_t CurrentFilterGroup = FilterGroup0;
+
+/* Internal common function for storing string events */
+static void prvTraceStoreStringEventHelper( int nArgs,
+ uint16_t eventID,
+ traceString userEvtChannel,
+ int len,
+ const char* str,
+ va_list* vl);
+
+/* Not static to avoid warnings from SysGCC/PPC */
+void prvTraceStoreSimpleStringEventHelper(traceString userEvtChannel,
+ const char* str);
+
+
+/* Stores the header information on Start */
+static void prvTraceStoreHeader(void);
+
+/* Stores the symbol table on Start */
+static void prvTraceStoreSymbolTable(void);
+
+/* Stores the object table on Start */
+static void prvTraceStoreObjectDataTable(void);
+
+/* Store the Timestamp Config on Start */
+static void prvTraceStoreTSConfig(void);
+
+/* Store the current warnings */
+static void prvTraceStoreWarnings(void);
+
+/* Internal function for starting/stopping the recorder. */
+static void prvSetRecorderEnabled(uint32_t isEnabled);
+
+/* Mark the page read as complete. */
+static void prvPageReadComplete(int pageIndex);
+
+/* Retrieve a buffer page to write to. */
+static int prvAllocateBufferPage(int prevPage);
+
+/* Get the current buffer page index (return value) and the number
+of valid bytes in the buffer page (bytesUsed). */
+static int prvGetBufferPage(int32_t* bytesUsed);
+
+/* Performs timestamping using definitions in trcHardwarePort.h */
+static uint32_t prvGetTimestamp32(void);
+
+/* Signal an error. */
+void prvTraceError(int errCode);
+
+/* Signal an warning (does not stop the recorder). */
+void prvTraceWarning(int errCode);
+
+/******************************************************************************
+ * vTraceInstanceFinishedNow
+ *
+ * Creates an event that ends the current task instance at this very instant.
+ * This makes the viewer to splits the current fragment at this point and begin
+ * a new actor instance, even if no task-switch has occurred.
+ *****************************************************************************/
+void vTraceInstanceFinishedNow(void)
+{
+ prvTraceStoreEvent0(PSF_EVENT_IFE_DIRECT);
+}
+
+/******************************************************************************
+ * vTraceInstanceFinishedNext
+ *
+ * Marks the current "task instance" as finished on the next kernel call.
+ *
+ * If that kernel call is blocking, the instance ends after the blocking event
+ * and the corresponding return event is then the start of the next instance.
+ * If the kernel call is not blocking, the viewer instead splits the current
+ * fragment right before the kernel call, which makes this call the first event
+ * of the next instance.
+ *****************************************************************************/
+void vTraceInstanceFinishedNext(void)
+{
+ prvTraceStoreEvent0(PSF_EVENT_IFE_NEXT);
+}
+
+/*******************************************************************************
+ * vTraceStoreKernelObjectName
+ *
+ * Parameter object: pointer to the Event Group that shall be named
+ * Parameter name: the name to set (const string literal)
+ *
+ * Sets a name for a kernel object for display in Tracealyzer.
+ ******************************************************************************/
+void vTraceStoreKernelObjectName(void* object, const char* name)
+{
+ /* Always save in symbol table, if the recording has not yet started */
+ prvTraceSaveSymbol(object, name);
+
+ prvTraceStoreStringEvent(1, PSF_EVENT_OBJ_NAME, name, (uint32_t)object);
+}
+
+
+/******************************************************************************
+* vTraceSetFrequency
+*
+* Registers the clock rate of the time source for the event timestamping.
+* This is normally not required, but if the default value (TRC_HWTC_FREQ_HZ)
+* should be incorrect for your setup, you can override it using this function.
+*
+* Must be called prior to vTraceEnable, and the time source is assumed to
+* have a fixed clock frequency after the startup.
+*****************************************************************************/
+void vTraceSetFrequency(uint32_t frequency)
+{
+ timestampFrequency = frequency;
+}
+
+#if (TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)
+
+/*******************************************************************************
+* xTraceRegisterString
+*
+* Stores a name for a user event channel, returns the handle.
+******************************************************************************/
+traceString xTraceRegisterString(const char* name)
+{
+ prvTraceSaveSymbol((const void*)name, name);
+
+ /* Always save in symbol table, if the recording has not yet started */
+ prvTraceStoreStringEvent(1, PSF_EVENT_OBJ_NAME, (const char*)name, (uint32_t)name);
+
+ return (traceString)name;
+}
+
+/******************************************************************************
+ * vTracePrint
+ *
+ * Generates "User Events", with unformatted text.
+ *
+ * User Events can be used for very efficient application logging, and are shown
+ * as yellow labels in the main trace view.
+ *
+ * You may group User Events into User Event Channels. The yellow User Event
+ * labels shows the logged string, preceded by the channel name within
+ * brackets. For example:
+ *
+ * "[MyChannel] Hello World!"
+ *
+ * The User Event Channels are shown in the View Filter, which makes it easy to
+ * select what User Events you wish to display. User Event Channels are created
+ * using xTraceRegisterString().
+ *
+ * Example:
+ *
+ * traceString chn = xTraceRegisterString("MyChannel");
+ * ...
+ * vTracePrint(chn, "Hello World!");
+ *
+ ******************************************************************************/
+void vTracePrint(traceString chn, const char* str)
+{
+ prvTraceStoreSimpleStringEventHelper(chn, str);
+}
+
+
+/*******************************************************************************
+* vTraceConsoleChannelPrintF
+*
+* Wrapper for vTracePrint, using the default channel. Can be used as a drop-in
+* replacement for printf and similar functions, e.g. in a debug logging macro.
+*
+* Example:
+*
+* // Old: #define LogString debug_console_printf
+*
+* // New, log to Tracealyzer instead:
+* #define LogString vTraceConsoleChannelPrintF
+* ...
+* LogString("My value is: %d", myValue);
+******************************************************************************/
+void vTraceConsoleChannelPrintF(const char* fmt, ...)
+{
+ va_list vl;
+ char tempBuf[60];
+ static traceString consoleChannel = NULL;
+
+ if (consoleChannel == NULL)
+ consoleChannel = xTraceRegisterString("Debug Console");
+
+ va_start(vl, fmt);
+ vsnprintf(tempBuf, 60, fmt, vl);
+ vTracePrint(consoleChannel, tempBuf);
+ va_end(vl);
+}
+
+/******************************************************************************
+ * vTracePrintF
+ *
+ * Generates "User Events", with formatted text and data, similar to a "printf".
+ * It is very fast since the actual formatting is done on the host side when the
+ * trace is displayed.
+ *
+ * User Events can be used for very efficient application logging, and are shown
+ * as yellow labels in the main trace view.
+ * An advantage of User Events is that data can be plotted in the "User Event
+ * Signal Plot" view, visualizing any data you log as User Events, discrete
+ * states or control system signals (e.g. system inputs or outputs).
+ *
+ * You may group User Events into User Event Channels. The yellow User Event
+ * labels show the logged string, preceded by the channel name within brackets.
+ *
+ * Example:
+ *
+ * "[MyChannel] Hello World!"
+ *
+ * The User Event Channels are shown in the View Filter, which makes it easy to
+ * select what User Events you wish to display. User Event Channels are created
+ * using xTraceRegisterString().
+ *
+ * Example:
+ *
+ * traceString adc_uechannel = xTraceRegisterString("ADC User Events");
+ * ...
+ * vTracePrintF(adc_uechannel,
+ * "ADC channel %d: %d volts",
+ * ch, adc_reading);
+ *
+ * All data arguments are assumed to be 32 bit wide. The following formats are
+ * supported:
+ * %d - signed integer. The following width and padding format is supported: "%05d" -> "-0042" and "%5d" -> " -42"
+ * %u - unsigned integer. The following width and padding format is supported: "%05u" -> "00042" and "%5u" -> " 42"
+ * %X - hexadecimal (uppercase). The following width and padding format is supported: "%04X" -> "002A" and "%4X" -> " 2A"
+ * %x - hexadecimal (lowercase). The following width and padding format is supported: "%04x" -> "002a" and "%4x" -> " 2a"
+ * %s - string (currently, this must be an earlier stored symbol name)
+ *
+ * Up to 15 data arguments are allowed, with a total size of maximum 60 byte
+ * including 8 byte for the base event fields and the format string. So with
+ * one data argument, the maximum string length is 48 chars. If this is exceeded
+ * the string is truncated (4 bytes at a time).
+ *
+ ******************************************************************************/
+void vTracePrintF(traceString chn, const char* fmt, ...)
+{
+ va_list vl;
+ int i = 0;
+
+ int nArgs = 0;
+
+ /* Count the number of arguments in the format string (e.g., %d) */
+ for (i = 0; (fmt[i] != 0) && (i < 52); i++)
+ {
+ if (fmt[i] == '%')
+ {
+ if (fmt[i + 1] != 0 && fmt[i + 1] != '%')
+ {
+ nArgs++; /* Found an argument */
+ }
+
+ i++; /* Move past format specifier or non-argument '%' */
+ }
+ }
+
+ va_start(vl, fmt);
+
+ if (chn != NULL)
+ {
+ prvTraceStoreStringEventHelper(nArgs, (uint16_t)(PSF_EVENT_USER_EVENT + nArgs + 1), chn, i, fmt, &vl);
+ }
+ else
+ {
+ prvTraceStoreStringEventHelper(nArgs, (uint16_t)(PSF_EVENT_USER_EVENT + nArgs), chn, i, fmt, &vl);
+ }
+ va_end(vl);
+}
+#endif /* (TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) */
+
+/*******************************************************************************
+ * xTraceSetISRProperties
+ *
+ * Stores a name and priority level for an Interrupt Service Routine, to allow
+ * for better visualization. Returns a traceHandle used by vTraceStoreISRBegin.
+ *
+ * Example:
+ * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
+ * ...
+ * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
+ * ...
+ * void ISR_handler()
+ * {
+ * vTraceStoreISRBegin(Timer1Handle);
+ * ...
+ * vTraceStoreISREnd(0);
+ * }
+ *
+ ******************************************************************************/
+traceHandle xTraceSetISRProperties(const char* name, uint8_t priority)
+{
+ /* Save object data in object data table */
+ prvTraceSaveObjectData((const void*)name, priority);
+
+ /* Note: "name" is used both as a string argument, and the address as ID */
+ prvTraceStoreStringEvent(2, PSF_EVENT_DEFINE_ISR, name, name, priority);
+
+ /* Always save in symbol table, if the recording has not yet started */
+ prvTraceSaveSymbol((const void*)name, name);
+
+ return (traceHandle)name;
+}
+
+/*******************************************************************************
+ * vTraceStoreISRBegin
+ *
+ * Registers the beginning of an Interrupt Service Routine, using a traceHandle
+ * provided by xTraceSetISRProperties.
+ *
+ * Example:
+ * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
+ * ...
+ * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
+ * ...
+ * void ISR_handler()
+ * {
+ * vTraceStoreISRBegin(Timer1Handle);
+ * ...
+ * vTraceStoreISREnd(0);
+ * }
+ *
+ ******************************************************************************/
+void vTraceStoreISRBegin(traceHandle handle)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ /* We are at the start of a possible ISR chain.
+ No context switches should have been triggered now. */
+ if (ISR_stack_index == -1)
+ isPendingContextSwitch = 0;
+
+ if (ISR_stack_index < (TRC_CFG_MAX_ISR_NESTING) - 1)
+ {
+ ISR_stack_index++;
+ ISR_stack[ISR_stack_index] = (uint32_t)handle;
+#if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
+ prvTraceStoreEvent1(PSF_EVENT_ISR_BEGIN, (uint32_t)handle);
+#endif
+ TRACE_EXIT_CRITICAL_SECTION();
+ }
+ else
+ {
+ TRACE_EXIT_CRITICAL_SECTION();
+ prvTraceError(PSF_ERROR_ISR_NESTING_OVERFLOW);
+ }
+}
+
+/*******************************************************************************
+ * vTraceStoreISREnd
+ *
+ * Registers the end of an Interrupt Service Routine.
+ *
+ * The parameter pendingISR indicates if the interrupt has requested a
+ * task-switch (= 1), e.g., by signaling a semaphore. Otherwise (= 0) the
+ * interrupt is assumed to return to the previous context.
+ *
+ * Example:
+ * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt
+ * traceHandle traceHandleIsrTimer1 = 0; // The ID set by the recorder
+ * ...
+ * traceHandleIsrTimer1 = xTraceSetISRProperties("ISRTimer1", PRIO_OF_ISR_TIMER1);
+ * ...
+ * void ISR_handler()
+ * {
+ * vTraceStoreISRBegin(traceHandleIsrTimer1);
+ * ...
+ * vTraceStoreISREnd(0);
+ * }
+ *
+ ******************************************************************************/
+void vTraceStoreISREnd(int isTaskSwitchRequired)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ (void)ISR_stack;
+
+ /* Is there a pending task-switch? (perhaps from an earlier ISR) */
+ isPendingContextSwitch |= isTaskSwitchRequired;
+
+ if (ISR_stack_index > 0)
+ {
+ ISR_stack_index--;
+
+#if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
+ /* Store return to interrupted ISR (if nested ISRs)*/
+ prvTraceStoreEvent1(PSF_EVENT_ISR_RESUME, (uint32_t)ISR_stack[ISR_stack_index]);
+#endif
+ }
+ else
+ {
+ ISR_stack_index--;
+
+ /* Store return to interrupted task, if no context switch will occur in between. */
+ if ((isPendingContextSwitch == 0) || (prvTraceIsSchedulerSuspended()))
+ {
+#if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
+ prvTraceStoreEvent1(PSF_EVENT_TS_RESUME, (uint32_t)TRACE_GET_CURRENT_TASK());
+#endif
+ }
+ }
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+
+/*******************************************************************************
+ * xTraceGetLastError
+ *
+ * Returns the last error or warning, as a string, or NULL if none.
+ *****************************************************************************/
+const char* xTraceGetLastError(void)
+{
+ /* Note: the error messages are short, in order to fit in a User Event.
+ Instead, the users can read more in the below comments.*/
+
+ switch (errorCode)
+ {
+
+ case PSF_WARNING_SYMBOL_TABLE_SLOTS:
+ /* There was not enough symbol table slots for storing symbol names.
+ The number of missing slots is counted by NoRoomForSymbol. Inspect this
+ variable and increase TRC_CFG_SYMBOL_TABLE_SLOTS by at least that value. */
+
+ return "Exceeded SYMBOL_TABLE_SLOTS (see xTraceGetLastError)";
+
+ case PSF_WARNING_SYMBOL_MAX_LENGTH:
+ /* A symbol name exceeded TRC_CFG_SYMBOL_MAX_LENGTH in length.
+ Make sure the symbol names are at most TRC_CFG_SYMBOL_MAX_LENGTH,
+ or inspect LongestSymbolName and increase TRC_CFG_SYMBOL_MAX_LENGTH
+ to at least this value. */
+
+ return "Exceeded SYMBOL_MAX_LENGTH (see xTraceGetLastError)";
+
+ case PSF_WARNING_OBJECT_DATA_SLOTS:
+ /* There was not enough symbol object table slots for storing object
+ properties, such as task priorites. The number of missing slots is
+ counted by NoRoomForObjectData. Inspect this variable and increase
+ TRC_CFG_OBJECT_DATA_SLOTS by at least that value. */
+
+ return "Exceeded OBJECT_DATA_SLOTS (see xTraceGetLastError)";
+
+ case PSF_WARNING_STRING_TOO_LONG:
+ /* Some string argument was longer than the maximum payload size
+ and has been truncated by "MaxBytesTruncated" bytes.
+
+ This may happen for the following functions:
+ - vTracePrint
+ - vTracePrintF
+ - vTraceStoreKernelObjectName
+ - xTraceRegisterString
+ - vTraceSetISRProperties
+
+ A PSF event may store maximum 60 bytes payload, including data
+ arguments and string characters. For User Events, also the User
+ Event Channel (4 bytes) must be squeezed in, if a channel is
+ specified (can be NULL). */
+
+ return "String too long (see xTraceGetLastError)";
+
+ case PSF_WARNING_STREAM_PORT_READ:
+ /* TRC_STREAM_PORT_READ_DATA is expected to return 0 when completed successfully.
+ This means there is an error in the communication with host/Tracealyzer. */
+
+ return "TRC_STREAM_PORT_READ_DATA returned error (!= 0).";
+
+ case PSF_WARNING_STREAM_PORT_WRITE:
+ /* TRC_STREAM_PORT_WRITE_DATA is expected to return 0 when completed successfully.
+ This means there is an error in the communication with host/Tracealyzer. */
+
+ return "TRC_STREAM_PORT_WRITE_DATA returned error (!= 0).";
+
+ case PSF_ERROR_EVENT_CODE_TOO_LARGE:
+ /* The highest allowed event code is 4095, anything higher is an unexpected error.
+ Please contact support@percepio.com for assistance.*/
+
+ return "Invalid event code (see xTraceGetLastError)";
+
+ case PSF_ERROR_ISR_NESTING_OVERFLOW:
+ /* Nesting of ISR trace calls exceeded the limit (TRC_CFG_MAX_ISR_NESTING).
+ If this is unlikely, make sure that you call vTraceStoreISRExit in the end
+ of all ISR handlers. Or increase TRC_CFG_MAX_ISR_NESTING. */
+
+ return "Exceeded ISR nesting (see xTraceGetLastError)";
+
+ case PSF_ERROR_DWT_NOT_SUPPORTED:
+ /* On ARM Cortex-M only - failed to initialize DWT Cycle Counter since not supported by this chip.
+ DWT timestamping is selected automatically for ART Cortex-M3, M4 and higher, based on the __CORTEX_M
+ macro normally set by ARM's CMSIS library, since typically available. You can however select
+ SysTick timestamping instead by defining adding "#define TRC_CFG_ARM_CM_USE_SYSTICK".*/
+
+ return "DWT not supported (see xTraceGetLastError)";
+
+ case PSF_ERROR_DWT_CYCCNT_NOT_SUPPORTED:
+ /* On ARM Cortex-M only - failed to initialize DWT Cycle Counter since not supported by this chip.
+ DWT timestamping is selected automatically for ART Cortex-M3, M4 and higher, based on the __CORTEX_M
+ macro normally set by ARM's CMSIS library, since typically available. You can however select
+ SysTick timestamping instead by defining adding "#define TRC_CFG_ARM_CM_USE_SYSTICK".*/
+
+ return "DWT_CYCCNT not supported (see xTraceGetLastError)";
+
+ case PSF_ERROR_TZCTRLTASK_NOT_CREATED:
+ /* vTraceEnable failed creating the trace control task (TzCtrl) - incorrect parameters (priority?)
+ or insufficient heap size? */
+ return "Could not create TzCtrl (see xTraceGetLastError)";
+
+ }
+
+ return NULL;
+}
+
+/*******************************************************************************
+ * vTraceClearError
+ *
+ * Clears any errors.
+ *****************************************************************************/
+void vTraceClearError(void)
+{
+ NoRoomForSymbol = 0;
+ LongestSymbolName = 0;
+ NoRoomForObjectData = 0;
+ MaxBytesTruncated = 0;
+ errorCode = PSF_ERROR_NONE;
+}
+
+/*******************************************************************************
+ * vTraceStop
+ *
+ * Stops the tracing.
+ *****************************************************************************/
+void vTraceStop(void)
+{
+ prvSetRecorderEnabled(0);
+}
+
+/*******************************************************************************
+ * vTraceSetRecorderDataBuffer
+ *
+ * If custom allocation is used, this function must be called so the recorder
+ * library knows where to save the trace data.
+ ******************************************************************************/
+#if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM)
+
+extern char* _TzTraceData;
+
+void vTraceSetRecorderDataBuffer(void* pRecorderData)
+{
+ _TzTraceData = pRecorderData;
+}
+#endif
+
+
+/*******************************************************************************
+* xTraceIsRecordingEnabled
+* Returns true (1) if the recorder is enabled (i.e. is recording), otherwise 0.
+******************************************************************************/
+int xTraceIsRecordingEnabled(void)
+{
+ return (int)RecorderEnabled;
+}
+
+void vTraceSetFilterMask(uint16_t filterMask)
+{
+ CurrentFilterMask = filterMask;
+}
+
+void vTraceSetFilterGroup(uint16_t filterGroup)
+{
+ CurrentFilterGroup = filterGroup;
+}
+
+
+/******************************************************************************/
+/*** INTERNAL FUNCTIONS *******************************************************/
+/******************************************************************************/
+
+/* Internal function for starting/stopping the recorder. */
+static void prvSetRecorderEnabled(uint32_t isEnabled)
+{
+ void* currentTask;
+
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ if (RecorderEnabled == isEnabled)
+ {
+ return;
+ }
+
+ currentTask = TRACE_GET_CURRENT_TASK();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ RecorderEnabled = isEnabled;
+
+ if (currentTask == NULL)
+ {
+ currentTask = (void*)HANDLE_NO_TASK;
+ }
+
+ if (RecorderEnabled)
+ {
+ TRC_STREAM_PORT_ON_TRACE_BEGIN();
+
+ #if (TRC_STREAM_PORT_USE_INTERNAL_BUFFER == 1)
+ prvPagedEventBufferInit(_TzTraceData);
+ #endif
+
+ eventCounter = 0;
+ ISR_stack_index = -1;
+ prvTraceStoreHeader();
+ prvTraceStoreSymbolTable();
+ prvTraceStoreObjectDataTable();
+ prvTraceStoreEvent3( PSF_EVENT_TRACE_START,
+ (uint32_t)TRACE_GET_OS_TICKS(),
+ (uint32_t)currentTask,
+ SessionCounter++);
+ prvTraceStoreTSConfig();
+ prvTraceStoreWarnings();
+ }
+ else
+ {
+ TRC_STREAM_PORT_ON_TRACE_END();
+ }
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Stores the symbol table on Start */
+static void prvTraceStoreSymbolTable(void)
+{
+ uint32_t i = 0;
+ uint32_t j = 0;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ for (i = 0; i < (sizeof(SymbolTable) / sizeof(uint32_t)); i += (SYMBOL_TABLE_SLOT_SIZE / sizeof(uint32_t)))
+ {
+ TRC_STREAM_PORT_ALLOCATE_EVENT(uint32_t, data, SYMBOL_TABLE_SLOT_SIZE);
+ if (data != NULL)
+ {
+ for (j = 0; j < (SYMBOL_TABLE_SLOT_SIZE / sizeof(uint32_t)); j++)
+ {
+ data[j] = symbolTable.SymbolTableBuffer.pSymbolTableBufferUINT32[i+j];
+ }
+ TRC_STREAM_PORT_COMMIT_EVENT(data, SYMBOL_TABLE_SLOT_SIZE);
+ }
+ }
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Stores the object table on Start */
+static void prvTraceStoreObjectDataTable(void)
+{
+ uint32_t i = 0;
+ uint32_t j = 0;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ for (i = 0; i < (sizeof(ObjectDataTable) / sizeof(uint32_t)); i += (OBJECT_DATA_SLOT_SIZE / sizeof(uint32_t)))
+ {
+ TRC_STREAM_PORT_ALLOCATE_EVENT(uint32_t, data, OBJECT_DATA_SLOT_SIZE);
+ if (data != NULL)
+ {
+ for (j = 0; j < (OBJECT_DATA_SLOT_SIZE / sizeof(uint32_t)); j++)
+ {
+ data[j] = objectDataTable.ObjectDataTableBuffer.pObjectDataTableBufferUINT32[i+j];
+ }
+ TRC_STREAM_PORT_COMMIT_EVENT(data, OBJECT_DATA_SLOT_SIZE);
+ }
+ }
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Stores the header information on Start */
+static void prvTraceStoreHeader(void)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ TRC_STREAM_PORT_ALLOCATE_EVENT(PSFHeaderInfo, header, sizeof(PSFHeaderInfo));
+ if (header != NULL)
+ {
+ header->psf = PSFEndianessIdentifier;
+ header->version = FormatVersion;
+ header->platform = TRACE_KERNEL_VERSION;
+ header->options = 0;
+ /* Lowest bit used for TRC_IRQ_PRIORITY_ORDER */
+ header->options = header->options | (TRC_IRQ_PRIORITY_ORDER << 0);
+ header->symbolSize = SYMBOL_TABLE_SLOT_SIZE;
+ header->symbolCount = (TRC_CFG_SYMBOL_TABLE_SLOTS);
+ header->objectDataSize = 8;
+ header->objectDataCount = (TRC_CFG_OBJECT_DATA_SLOTS);
+ TRC_STREAM_PORT_COMMIT_EVENT(header, sizeof(PSFHeaderInfo));
+ }
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Store the current warnings */
+static void prvTraceStoreWarnings(void)
+{
+ if (RecorderEnabled)
+ {
+ const char* errStr = xTraceGetLastError();
+
+ if (errStr != NULL)
+ {
+ vTracePrint(trcWarningChannel, errStr);
+ }
+ }
+}
+
+/* Store an event with zero parameters (event ID only) */
+void prvTraceStoreEvent0(uint16_t eventID)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ eventCounter++;
+
+ {
+ TRC_STREAM_PORT_ALLOCATE_EVENT(BaseEvent, event, sizeof(BaseEvent));
+ if (event != NULL)
+ {
+ event->EventID = eventID | PARAM_COUNT(0);
+ event->EventCount = (uint16_t)eventCounter;
+ event->TS = prvGetTimestamp32();
+ TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(BaseEvent));
+ }
+ }
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Store an event with one 32-bit parameter (pointer address or an int) */
+void prvTraceStoreEvent1(uint16_t eventID, uint32_t param1)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ eventCounter++;
+
+ {
+ TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_1, event, sizeof(EventWithParam_1));
+ if (event != NULL)
+ {
+ event->base.EventID = eventID | PARAM_COUNT(1);
+ event->base.EventCount = (uint16_t)eventCounter;
+ event->base.TS = prvGetTimestamp32();
+ event->param1 = (uint32_t)param1;
+ TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_1));
+ }
+ }
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Store an event with two 32-bit parameters */
+void prvTraceStoreEvent2(uint16_t eventID, uint32_t param1, uint32_t param2)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ eventCounter++;
+
+ {
+ TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_2, event, sizeof(EventWithParam_2));
+ if (event != NULL)
+ {
+ event->base.EventID = eventID | PARAM_COUNT(2);
+ event->base.EventCount = (uint16_t)eventCounter;
+ event->base.TS = prvGetTimestamp32();
+ event->param1 = (uint32_t)param1;
+ event->param2 = param2;
+ TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_2));
+ }
+ }
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Store an event with three 32-bit parameters */
+void prvTraceStoreEvent3( uint16_t eventID,
+ uint32_t param1,
+ uint32_t param2,
+ uint32_t param3)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ eventCounter++;
+
+ {
+ TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_3, event, sizeof(EventWithParam_3));
+ if (event != NULL)
+ {
+ event->base.EventID = eventID | PARAM_COUNT(3);
+ event->base.EventCount = (uint16_t)eventCounter;
+ event->base.TS = prvGetTimestamp32();
+ event->param1 = (uint32_t)param1;
+ event->param2 = param2;
+ event->param3 = param3;
+ TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_3));
+ }
+ }
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Stores an event with <nParam> 32-bit integer parameters */
+void prvTraceStoreEvent(int nParam, uint16_t eventID, ...)
+{
+ va_list vl;
+ int i;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ int eventSize = (int)sizeof(BaseEvent) + nParam * (int)sizeof(uint32_t);
+
+ eventCounter++;
+
+ {
+ TRC_STREAM_PORT_ALLOCATE_DYNAMIC_EVENT(largestEventType, event, eventSize);
+ if (event != NULL)
+ {
+ event->base.EventID = eventID | (uint16_t)PARAM_COUNT(nParam);
+ event->base.EventCount = (uint16_t)eventCounter;
+ event->base.TS = prvGetTimestamp32();
+
+ va_start(vl, eventID);
+ for (i = 0; i < nParam; i++)
+ {
+ uint32_t* tmp = (uint32_t*) &(event->data[i]);
+ *tmp = va_arg(vl, uint32_t);
+ }
+ va_end(vl);
+
+ TRC_STREAM_PORT_COMMIT_EVENT(event, (uint32_t)eventSize);
+ }
+ }
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Stories an event with a string and <nParam> 32-bit integer parameters */
+void prvTraceStoreStringEvent(int nArgs, uint16_t eventID, const char* str, ...)
+{
+ int len;
+ va_list vl;
+
+ for (len = 0; (str[len] != 0) && (len < 52); len++); /* empty loop */
+
+ va_start(vl, str);
+ prvTraceStoreStringEventHelper(nArgs, eventID, NULL, len, str, &vl);
+ va_end(vl);
+}
+
+/* Internal common function for storing string events */
+static void prvTraceStoreStringEventHelper(int nArgs,
+ uint16_t eventID,
+ traceString userEvtChannel,
+ int len,
+ const char* str,
+ va_list* vl)
+{
+ int nWords;
+ int nStrWords;
+ int i;
+ int offset = 0;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);
+
+ /* The string length in multiples of 32 bit words (+1 for null character) */
+ nStrWords = (len+1+3)/4;
+
+ /* If a user event channel is specified, add in the list */
+ if (userEvtChannel)
+ nArgs++;
+
+ offset = nArgs * 4;
+
+ /* The total number of 32-bit words needed for the whole payload */
+ nWords = nStrWords + nArgs;
+
+ if (nWords > 15) /* if attempting to store more than 60 byte (= max) */
+ {
+ /* Truncate event if too large. The string characters are stored
+ last, so usually only the string is truncated, unless there a lot
+ of parameters... */
+
+ /* Diagnostics ... */
+ uint32_t bytesTruncated = (uint32_t)(nWords - 15) * 4;
+
+ if (bytesTruncated > MaxBytesTruncated)
+ {
+ MaxBytesTruncated = bytesTruncated;
+ }
+
+ nWords = 15;
+ len = 15 * 4 - offset;
+ }
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ int eventSize = (int)sizeof(BaseEvent) + nWords * (int)sizeof(uint32_t);
+
+ eventCounter++;
+
+ {
+ TRC_STREAM_PORT_ALLOCATE_DYNAMIC_EVENT(largestEventType, event, eventSize);
+ if (event != NULL)
+ {
+ uint32_t* data32;
+ uint8_t* data8;
+ event->base.EventID = (eventID) | (uint16_t)PARAM_COUNT(nWords);
+ event->base.EventCount = (uint16_t)eventCounter;
+ event->base.TS = prvGetTimestamp32();
+
+ /* 32-bit write-pointer for the data argument */
+ data32 = (uint32_t*) &(event->data[0]);
+
+ for (i = 0; i < nArgs; i++)
+ {
+ if ((userEvtChannel != NULL) && (i == 0))
+ {
+ /* First, add the User Event Channel if not NULL */
+ data32[i] = (uint32_t)userEvtChannel;
+ }
+ else
+ {
+ /* Add data arguments... */
+ data32[i] = va_arg(*vl, uint32_t);
+ }
+ }
+ data8 = (uint8_t*)&(event->data[0]);
+ for (i = 0; i < len; i++)
+ {
+ data8[offset + i] = str[i];
+ }
+
+ if (len < (15 * 4 - offset))
+ data8[offset + len] = 0; /* Only truncate if we don't fill up the buffer completely */
+ TRC_STREAM_PORT_COMMIT_EVENT(event, (uint32_t)eventSize);
+ }
+ }
+ }
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Internal common function for storing string events without additional arguments */
+void prvTraceStoreSimpleStringEventHelper(traceString userEvtChannel,
+ const char* str)
+{
+ int len;
+ int nWords;
+ int nStrWords;
+ int i;
+ int nArgs = 0;
+ int offset = 0;
+ uint16_t eventID = PSF_EVENT_USER_EVENT;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);
+
+ for (len = 0; (str[len] != 0) && (len < 52); len++); /* empty loop */
+
+ /* The string length in multiples of 32 bit words (+1 for null character) */
+ nStrWords = (len+1+3)/4;
+
+ /* If a user event channel is specified, add in the list */
+ if (userEvtChannel)
+ {
+ nArgs++;
+ eventID++;
+ }
+
+ offset = nArgs * 4;
+
+ /* The total number of 32-bit words needed for the whole payload */
+ nWords = nStrWords + nArgs;
+
+ if (nWords > 15) /* if attempting to store more than 60 byte (= max) */
+ {
+ /* Truncate event if too large. The string characters are stored
+ last, so usually only the string is truncated, unless there a lot
+ of parameters... */
+
+ /* Diagnostics ... */
+ uint32_t bytesTruncated = (uint32_t)(nWords - 15) * 4;
+
+ if (bytesTruncated > MaxBytesTruncated)
+ {
+ MaxBytesTruncated = bytesTruncated;
+ }
+
+ nWords = 15;
+ len = 15 * 4 - offset;
+ }
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ if (RecorderEnabled)
+ {
+ int eventSize = (int)sizeof(BaseEvent) + nWords * (int)sizeof(uint32_t);
+
+ eventCounter++;
+
+ {
+ TRC_STREAM_PORT_ALLOCATE_DYNAMIC_EVENT(largestEventType, event, eventSize);
+ if (event != NULL)
+ {
+ uint32_t* data32;
+ uint8_t* data8;
+ event->base.EventID = (eventID) | (uint16_t)PARAM_COUNT(nWords);
+ event->base.EventCount = (uint16_t)eventCounter;
+ event->base.TS = prvGetTimestamp32();
+
+ /* 32-bit write-pointer for the data argument */
+ data32 = (uint32_t*) &(event->data[0]);
+
+ if (userEvtChannel != NULL)
+ {
+ /* First, add the User Event Channel if not NULL */
+ data32[0] = (uint32_t)userEvtChannel;
+ }
+
+ data8 = (uint8_t*) &(event->data[0]);
+ for (i = 0; i < len; i++)
+ {
+ data8[offset + i] = str[i];
+ }
+
+ if (len < (15 * 4 - offset))
+ data8[offset + len] = 0; /* Only truncate if we don't fill up the buffer completely */
+ TRC_STREAM_PORT_COMMIT_EVENT(event, (uint32_t)eventSize);
+ }
+ }
+ }
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Saves a symbol name (task name etc.) in symbol table */
+void prvTraceSaveSymbol(const void *address, const char *name)
+{
+ uint32_t i;
+ uint32_t foundSlot;
+ uint32_t *ptrAddress;
+ uint8_t *ptrSymbol;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ foundSlot = firstFreeSymbolTableIndex;
+
+ /* First look for previous entries using this address */
+ for (i = 0; i < firstFreeSymbolTableIndex; i += SYMBOL_TABLE_SLOT_SIZE)
+ {
+ /* We access the symbol table via the union member pSymbolTableBufferUINT32 to avoid strict-aliasing issues */
+ ptrAddress = &symbolTable.SymbolTableBuffer.pSymbolTableBufferUINT32[i / sizeof(uint32_t)];
+ if (*ptrAddress == (uint32_t)address)
+ {
+ foundSlot = i;
+ break;
+ }
+ }
+
+ if (foundSlot < SYMBOL_TABLE_BUFFER_SIZE)
+ {
+ /* We access the symbol table via the union member pSymbolTableBufferUINT32 to avoid strict-aliasing issues */
+ symbolTable.SymbolTableBuffer.pSymbolTableBufferUINT32[foundSlot / sizeof(uint32_t)] = (uint32_t)address;
+
+ /* We access the symbol table via the union member pSymbolTableBufferUINT8 to avoid strict-aliasing issues */
+ ptrSymbol = &symbolTable.SymbolTableBuffer.pSymbolTableBufferUINT8[foundSlot + sizeof(uint32_t)];
+ for (i = 0; i < (TRC_CFG_SYMBOL_MAX_LENGTH); i++)
+ {
+ ptrSymbol[i] = (uint8_t)name[i]; /* We do this first to ensure we also get the 0 termination, if there is one */
+
+ if (name[i] == 0)
+ break;
+ }
+
+ /* Check the length of "name", if longer than SYMBOL_MAX_LENGTH */
+ while ((name[i] != 0) && i < 128)
+ {
+ i++;
+ }
+
+ /* Remember the longest symbol name, for diagnostic purposes */
+ if (i > LongestSymbolName)
+ {
+ LongestSymbolName = i;
+ }
+
+ /* Is this the last entry in the symbol table? */
+ if (foundSlot == firstFreeSymbolTableIndex)
+ {
+ firstFreeSymbolTableIndex += SYMBOL_TABLE_SLOT_SIZE;
+ }
+ }
+ else
+ {
+ NoRoomForSymbol++;
+ }
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Deletes a symbol name (task name etc.) from symbol table */
+void prvTraceDeleteSymbol(void *address)
+{
+ uint32_t i, j;
+ uint32_t *ptr, *lastEntryPtr;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ for (i = 0; i < firstFreeSymbolTableIndex; i += SYMBOL_TABLE_SLOT_SIZE)
+ {
+ /* We access the symbol table via the union member pSymbolTableBufferUINT32 to avoid strict-aliasing issues */
+ ptr = &symbolTable.SymbolTableBuffer.pSymbolTableBufferUINT32[i / sizeof(uint32_t)];
+ if (*ptr == (uint32_t)address)
+ {
+ /* See if we have another entry in the table, and that this isn't already the last entry */
+ if (firstFreeSymbolTableIndex > SYMBOL_TABLE_SLOT_SIZE && i != (firstFreeSymbolTableIndex - SYMBOL_TABLE_SLOT_SIZE))
+ {
+ /* Another entry is available, get pointer to the last one */
+ /* We access the symbol table via the union member pSymbolTableBufferUINT32 to avoid strict-aliasing issues */
+ lastEntryPtr = &symbolTable.SymbolTableBuffer.pSymbolTableBufferUINT32[(firstFreeSymbolTableIndex - SYMBOL_TABLE_SLOT_SIZE) / sizeof(uint32_t)];
+
+ /* Copy last entry to this position */
+ for (j = 0; j < (SYMBOL_TABLE_SLOT_SIZE) / sizeof(uint32_t); j++)
+ {
+ ptr[j] = lastEntryPtr[j];
+ }
+
+ /* For good measure we also zero out the original position */
+ *lastEntryPtr = 0;
+ }
+ else
+ *ptr = 0; /* No other entry found, or this is the last entry */
+
+ /* Lower index */
+ firstFreeSymbolTableIndex -= SYMBOL_TABLE_SLOT_SIZE;
+
+ break;
+ }
+ }
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Saves an object data entry (current task priority) in object data table */
+void prvTraceSaveObjectData(const void *address, uint32_t data)
+{
+ uint32_t i;
+ uint32_t foundSlot;
+ uint32_t *ptr;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ foundSlot = firstFreeObjectDataTableIndex;
+
+ /* First look for previous entries using this address */
+ for (i = 0; i < firstFreeObjectDataTableIndex; i += OBJECT_DATA_SLOT_SIZE)
+ {
+ /* We access the data table via the union member pObjectDataTableBufferUINT32 to avoid strict-aliasing issues */
+ ptr = &objectDataTable.ObjectDataTableBuffer.pObjectDataTableBufferUINT32[i / sizeof(uint32_t)];
+ if (*ptr == (uint32_t)address)
+ {
+ foundSlot = i;
+ break;
+ }
+ }
+
+ if (foundSlot < OBJECT_DATA_TABLE_BUFFER_SIZE)
+ {
+ /* We access the data table via the union member pObjectDataTableBufferUINT32 to avoid strict-aliasing issues */
+ objectDataTable.ObjectDataTableBuffer.pObjectDataTableBufferUINT32[foundSlot / sizeof(uint32_t)] = (uint32_t)address;
+ objectDataTable.ObjectDataTableBuffer.pObjectDataTableBufferUINT32[foundSlot / sizeof(uint32_t) + 1] = data;
+
+ /* Is this the last entry in the object data table? */
+ if (foundSlot == firstFreeObjectDataTableIndex)
+ {
+ firstFreeObjectDataTableIndex += OBJECT_DATA_SLOT_SIZE;
+ }
+ }
+ else
+ {
+ NoRoomForObjectData++;
+ }
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Removes an object data entry (task base priority) from object data table */
+void prvTraceDeleteObjectData(void *address)
+{
+ uint32_t i, j;
+ uint32_t *ptr, *lastEntryPtr;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+
+ for (i = 0; i < firstFreeObjectDataTableIndex; i += OBJECT_DATA_SLOT_SIZE)
+ {
+ /* We access the data table via the union member pObjectDataTableBufferUINT32 to avoid strict-aliasing issues */
+ ptr = &objectDataTable.ObjectDataTableBuffer.pObjectDataTableBufferUINT32[i / sizeof(uint32_t)];
+ if (*ptr == (uint32_t)address)
+ {
+ /* See if we have another entry in the table, and that this isn't already the last entry */
+ if (firstFreeObjectDataTableIndex > OBJECT_DATA_SLOT_SIZE && i != (firstFreeObjectDataTableIndex - OBJECT_DATA_SLOT_SIZE))
+ {
+ /* Another entry is available, get pointer to the last one */
+ /* We access the data table via the union member pObjectDataTableBufferUINT32 to avoid strict-aliasing issues */
+ lastEntryPtr = &objectDataTable.ObjectDataTableBuffer.pObjectDataTableBufferUINT32[(firstFreeObjectDataTableIndex - OBJECT_DATA_SLOT_SIZE) / sizeof(uint32_t)];
+
+ /* Copy last entry to this position */
+ for (j = 0; j < (OBJECT_DATA_SLOT_SIZE) / sizeof(uint32_t); j++)
+ {
+ ptr[j] = lastEntryPtr[j];
+ }
+
+ /* For good measure we also zero out the original position */
+ *lastEntryPtr = 0;
+ }
+ else
+ *ptr = 0; /* No other entry found, or this is the last entry */
+
+ /* Lower index */
+ firstFreeObjectDataTableIndex -= OBJECT_DATA_SLOT_SIZE;
+
+ break;
+ }
+ }
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Checks if the provided command is a valid command */
+int prvIsValidCommand(TracealyzerCommandType* cmd)
+{
+ uint16_t checksum = (uint16_t)(0xFFFF - ( cmd->cmdCode +
+ cmd->param1 +
+ cmd->param2 +
+ cmd->param3 +
+ cmd->param4 +
+ cmd->param5));
+
+ if (cmd->checksumMSB != (unsigned char)(checksum >> 8))
+ return 0;
+
+ if (cmd->checksumLSB != (unsigned char)(checksum & 0xFF))
+ return 0;
+
+ if (cmd->cmdCode > CMD_LAST_COMMAND)
+ return 0;
+
+ return 1;
+}
+
+/* Executed the received command (Start or Stop) */
+void prvProcessCommand(TracealyzerCommandType* cmd)
+{
+ switch(cmd->cmdCode)
+ {
+ case CMD_SET_ACTIVE:
+ prvSetRecorderEnabled(cmd->param1);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Called on warnings, when the recording can continue. */
+void prvTraceWarning(int errCode)
+{
+ if (!errorCode)
+ {
+ errorCode = errCode;
+ prvTraceStoreWarnings();
+ }
+}
+
+/* Called on critical errors in the recorder. Stops the recorder! */
+void prvTraceError(int errCode)
+{
+ if (! errorCode)
+ {
+ errorCode = errCode;
+ prvTraceStoreWarnings();
+ vTracePrintF(trcWarningChannel, "Recorder stopped in prvTraceError()");
+
+ prvSetRecorderEnabled(0);
+ }
+}
+
+/* If using DWT timestamping (default on ARM Cortex-M3, M4 and M7), make sure the DWT unit is initialized. */
+#ifndef TRC_CFG_ARM_CM_USE_SYSTICK
+#if ((TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_ARM_Cortex_M) && (defined (__CORTEX_M) && (__CORTEX_M >= 0x03)))
+
+void prvTraceInitCortexM()
+{
+ /* Make sure the DWT registers are unlocked, in case the debugger doesn't do this. */
+ TRC_REG_ITM_LOCKACCESS = TRC_ITM_LOCKACCESS_UNLOCK;
+
+ /* Make sure DWT is enabled is enabled, if supported */
+ TRC_REG_DEMCR |= TRC_DEMCR_TRCENA;
+
+ do
+ {
+ /* Verify that DWT is supported */
+ if (TRC_REG_DEMCR == 0)
+ {
+ /* This function is called on Cortex-M3, M4 and M7 devices to initialize
+ the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
+
+ If the below error is produced, the DWT unit does not seem to be available.
+
+ In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
+ to use SysTick timestamping instead, or define your own timestamping by
+ setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
+ and make the necessary definitions, as explained in trcHardwarePort.h.*/
+
+ prvTraceError(PSF_ERROR_DWT_NOT_SUPPORTED);
+ break;
+ }
+
+ /* Verify that DWT_CYCCNT is supported */
+ if (TRC_REG_DWT_CTRL & TRC_DWT_CTRL_NOCYCCNT)
+ {
+ /* This function is called on Cortex-M3, M4 and M7 devices to initialize
+ the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
+
+ If the below error is produced, the cycle counter does not seem to be available.
+
+ In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
+ to use SysTick timestamping instead, or define your own timestamping by
+ setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
+ and make the necessary definitions, as explained in trcHardwarePort.h.*/
+
+ prvTraceError(PSF_ERROR_DWT_CYCCNT_NOT_SUPPORTED);
+ break;
+ }
+
+ /* Reset the cycle counter */
+ TRC_REG_DWT_CYCCNT = 0;
+
+ /* Enable the cycle counter */
+ TRC_REG_DWT_CTRL |= TRC_DWT_CTRL_CYCCNTENA;
+
+ } while(0); /* breaks above jump here */
+}
+#endif
+#endif
+
+/* Performs timestamping using definitions in trcHardwarePort.h */
+static uint32_t prvGetTimestamp32(void)
+{
+#if ((TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR) || (TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR))
+ return TRC_HWTC_COUNT;
+#endif
+
+#if ((TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_INCR) || (TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_DECR))
+ return TRC_HWTC_COUNT;
+#endif
+
+#if ((TRC_HWTC_TYPE == TRC_OS_TIMER_INCR) || (TRC_HWTC_TYPE == TRC_OS_TIMER_DECR))
+ uint32_t ticks = TRACE_GET_OS_TICKS();
+ return ((TRC_HWTC_COUNT) & 0x00FFFFFFU) + ((ticks & 0x000000FFU) << 24);
+#endif
+}
+
+/* Store the Timestamp Config event */
+static void prvTraceStoreTSConfig(void)
+{
+ /* If not overridden using vTraceSetFrequency, use default value */
+ if (timestampFrequency == 0)
+ {
+ timestampFrequency = TRC_HWTC_FREQ_HZ;
+ }
+
+ #if (TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_INCR || TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_DECR)
+
+ prvTraceStoreEvent(5,
+ PSF_EVENT_TS_CONFIG,
+ (uint32_t)timestampFrequency,
+ (uint32_t)(TRACE_TICK_RATE_HZ),
+ (uint32_t)(TRC_HWTC_TYPE),
+ (uint32_t)(TRC_CFG_ISR_TAILCHAINING_THRESHOLD),
+ (uint32_t)(TRC_HWTC_PERIOD));
+
+ #else
+
+ prvTraceStoreEvent(4,
+ PSF_EVENT_TS_CONFIG,
+ (uint32_t)timestampFrequency,
+ (uint32_t)(TRACE_TICK_RATE_HZ),
+ (uint32_t)(TRC_HWTC_TYPE),
+ (uint32_t)(TRC_CFG_ISR_TAILCHAINING_THRESHOLD));
+ #endif
+}
+
+/* Retrieve a buffer page to write to. */
+static int prvAllocateBufferPage(int prevPage)
+{
+ int index;
+ int count = 0;
+
+ index = (prevPage + 1) % (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT);
+
+ while((PageInfo[index].Status != PAGE_STATUS_FREE) && (count ++ < (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT)))
+ {
+ index = (index + 1) % (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT);
+ }
+
+ if (PageInfo[index].Status == PAGE_STATUS_FREE)
+ {
+ return index;
+ }
+
+ return -1;
+}
+
+/* Mark the page read as complete. */
+static void prvPageReadComplete(int pageIndex)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ENTER_CRITICAL_SECTION();
+ PageInfo[pageIndex].BytesRemaining = (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE);
+ PageInfo[pageIndex].WritePointer = &EventBuffer[pageIndex * (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE)];
+ PageInfo[pageIndex].Status = PAGE_STATUS_FREE;
+
+ TotalBytesRemaining += (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE);
+
+ TRACE_EXIT_CRITICAL_SECTION();
+}
+
+/* Get the current buffer page index and remaining number of bytes. */
+static int prvGetBufferPage(int32_t* bytesUsed)
+{
+ static int8_t lastPage = -1;
+ int count = 0;
+ int8_t index = (int8_t) ((lastPage + 1) % (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT));
+
+ while((PageInfo[index].Status != PAGE_STATUS_READ) && (count++ < (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT)))
+ {
+ index = (int8_t)((index + 1) % (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT));
+ }
+
+ if (PageInfo[index].Status == PAGE_STATUS_READ)
+ {
+ *bytesUsed = (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE) - PageInfo[index].BytesRemaining;
+ lastPage = index;
+ return index;
+ }
+
+ *bytesUsed = 0;
+
+ return -1;
+}
+
+/*******************************************************************************
+ * uint32_t prvPagedEventBufferTransfer(void)
+ *
+ * Transfers one buffer page of trace data, if a full page is available, using
+ * the macro TRC_STREAM_PORT_WRITE_DATA as defined in trcStreamingPort.h.
+ *
+ * This function is intended to be called the periodic TzCtrl task with a suitable
+ * delay (e.g. 10-100 ms).
+ *
+ * Returns the number of bytes sent. If non-zero, it is good to call this
+ * again, in order to send any additional data waiting in the buffer.
+ * If zero, wait a while before calling again.
+ *
+ * In case of errors from the streaming interface, it registers a warning
+ * (PSF_WARNING_STREAM_PORT_WRITE) provided by xTraceGetLastError().
+ *
+ *******************************************************************************/
+uint32_t prvPagedEventBufferTransfer(void)
+{
+ int8_t pageToTransfer = -1;
+ int32_t bytesTransferredTotal = 0;
+ int32_t bytesTransferredNow = 0;
+ int32_t bytesToTransfer;
+
+ pageToTransfer = (int8_t)prvGetBufferPage(&bytesToTransfer);
+
+ /* bytesToTransfer now contains the number of "valid" bytes in the buffer page, that should be transmitted.
+ There might be some unused junk bytes in the end, that must be ignored. */
+
+ if (pageToTransfer > -1)
+ {
+ while (1) /* Keep going until we have transferred all that we intended to */
+ {
+ if (TRC_STREAM_PORT_WRITE_DATA(
+ &EventBuffer[pageToTransfer * (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE) + bytesTransferredTotal],
+ (uint32_t)(bytesToTransfer - bytesTransferredTotal),
+ &bytesTransferredNow) == 0)
+ {
+ /* Write was successful. Update the number of transferred bytes. */
+ bytesTransferredTotal += bytesTransferredNow;
+
+ if (bytesTransferredTotal == bytesToTransfer)
+ {
+ /* All bytes have been transferred. Mark the buffer page as "Read Complete" (so it can be written to) and return OK. */
+ prvPageReadComplete(pageToTransfer);
+ return (uint32_t)bytesTransferredTotal;
+ }
+ }
+ else
+ {
+ /* Some error from the streaming interface... */
+ prvTraceWarning(PSF_WARNING_STREAM_PORT_WRITE);
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/*******************************************************************************
+ * void* prvPagedEventBufferGetWritePointer(int sizeOfEvent)
+ *
+ * Returns a pointer to an available location in the buffer able to store the
+ * requested size.
+ *
+ * Return value: The pointer.
+ *
+ * Parameters:
+ * - sizeOfEvent: The size of the event that is to be placed in the buffer.
+ *
+*******************************************************************************/
+void* prvPagedEventBufferGetWritePointer(int sizeOfEvent)
+{
+ void* ret;
+ static int currentWritePage = -1;
+
+ if (currentWritePage == -1)
+ {
+ currentWritePage = prvAllocateBufferPage(currentWritePage);
+ if (currentWritePage == -1)
+ {
+ DroppedEventCounter++;
+ return NULL;
+ }
+ }
+
+ if (PageInfo[currentWritePage].BytesRemaining - sizeOfEvent < 0)
+ {
+ PageInfo[currentWritePage].Status = PAGE_STATUS_READ;
+
+ TotalBytesRemaining -= PageInfo[currentWritePage].BytesRemaining; // Last trailing bytes
+
+ if (TotalBytesRemaining < TotalBytesRemaining_LowWaterMark)
+ TotalBytesRemaining_LowWaterMark = TotalBytesRemaining;
+
+ currentWritePage = prvAllocateBufferPage(currentWritePage);
+ if (currentWritePage == -1)
+ {
+ DroppedEventCounter++;
+ return NULL;
+ }
+ }
+ ret = PageInfo[currentWritePage].WritePointer;
+ PageInfo[currentWritePage].WritePointer += sizeOfEvent;
+ PageInfo[currentWritePage].BytesRemaining = (uint16_t)(PageInfo[currentWritePage].BytesRemaining -sizeOfEvent);
+
+ TotalBytesRemaining = (TotalBytesRemaining-(uint16_t)sizeOfEvent);
+
+ if (TotalBytesRemaining < TotalBytesRemaining_LowWaterMark)
+ TotalBytesRemaining_LowWaterMark = TotalBytesRemaining;
+
+ return ret;
+}
+
+/*******************************************************************************
+ * void prvPagedEventBufferInit(char* buffer)
+ *
+ * Assigns the buffer to use and initializes the PageInfo structure.
+ *
+ * Return value: void
+ *
+ * Parameters:
+ * - char* buffer: pointer to the trace data buffer, allocated by the caller.
+ *
+*******************************************************************************/
+void prvPagedEventBufferInit(char* buffer)
+{
+ int i;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ EventBuffer = buffer;
+
+ TRACE_ENTER_CRITICAL_SECTION();
+ for (i = 0; i < (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT); i++)
+ {
+ PageInfo[i].BytesRemaining = (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE);
+ PageInfo[i].WritePointer = &EventBuffer[i * (TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE)];
+ PageInfo[i].Status = PAGE_STATUS_FREE;
+ }
+ TRACE_EXIT_CRITICAL_SECTION();
+
+}
+
+#endif /*(TRC_USE_TRACEALYZER_RECORDER == 1)*/
+
+#endif /*(TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_STREAMING)*/