The trace recorder library is the embedded runtime component of Tracealyzer for FreeRTOS. The recorder is integrated in FreeRTOS and stores the event data in a RAM buffer for transfer to the development PC. Two alternative recorder implementations are provided, described below.
The streaming recorder is our latest and greatest solution. It allows for traces of unlimited length by streaming the data to the development PC. The data transfer can be channeled via SEGGER J-Link debug probes, TCP/IP, or any custom channel of your choice, for instance a device file system.
The snapshot recorder keeps the trace data in RAM and allows you to take a snapshot at any point, simply by making a RAM dump. This can be used as "black-box" on deployed systems, or as an alternative to streaming if no suitable transfer interface is available in your system.
Both are provided in C source code and found in the Tracealyzer for FreeRTOS installation directory (see "Trace Recorder Library" directory) as compressed files. The two recorders are quite similar with respect to source code structure, interface and integration, but they are not identical.
When using the streaming recorder the data is continuously transferred to the host PC. The data is initially stored in a RAM buffer, which is periodically flushed to the transfer interface, that transfers the data to the host PC. The streaming recorder has explicit support for SEGGER J-Link debug probes, and also for network streaming via TCP/IP. However, you may configure the streaming recorder to use any data channel of your choice, if you have some way of receiving the data and writing it to a file.
The trace recorder library is provided in C source code, found in the Tracealyzer for FreeRTOS installation directory. The user API has less than 10 functions (well documented), of which only one is required to use the recorder (Trace_Init()). The whole recorder code compiles to just 3-5 KB of object code on ARM Cortex-M4 MCUs, depending on compiler and optimization level.
The streaming method is selected by TRC_RECORDER_TRANSFER_METHOD in trcConfig.h:
If using this approach, the trace data is streamed from target to host via a SEGGER J-Link or J-Trace debug probe. This uses SEGGER's Real-Time Transfer (RTT) function, that works on all ARM Cortex-M MCUs and all Renesas RX MCUs. With a sufficient RAM buffer size, this allows for streaming the trace data at speeds upwards 700 KB/s on a standard J-Link (Version 9) and 2-3 times that on the high-end models. It works even on the J-Link On Board (OB), found integrated on many development boards, although these have lower (but still good) performance.
SEGGER RTT is based on RAM buffers in the target system, which the J-Link accesses in the background without disturbing the target system. The Tracealyzer for FreeRTOS recorder uses two RTT buffers, one control channel for receiving commands from Tracealyzer for FreeRTOS and a data channel for streaming the trace. SEGGER RTT is already integrated in the trace recorder library and is configured in SEGGER_RTT_Conf.h and trcConfig.h.
The control channel is only used when starting or stopping the trace recorder, and only requires a small RTT buffer of 32 bytes.
The trace upload channel needs a bit larger RTT buffer. We recommend a buffer size of 1024 byte if available, otherwise 512 byte (or even less) is often sufficient. The J-Link pulls out the buffer data periodically, but if the buffer is too small, it will become full between the J-Link reads. The behavior when the buffer gets full depends on the transfer method (TRC_RECORDER_TRANSFER_METHOD) in trcConfig.h:
If there would be recorder blocking or dropped trace events, try the following:
Note: If you set the J-Link Speed higher than the maximum supported speed, the J-Link driver will instead use the highest speed supported. The actual speed used is shown in the title bar of the Target Connection window. The RTT transfer may become unstable and drop packets if very high speeds are selected.
Before making your first trace recording, check J-Link Settings and Streaming Trace Settings, found under File -> Settings... in Tracealyzer for FreeRTOS.


All tools using the J-Link driver (i.e., Tracealyzer for FreeRTOS and the debugger IDE) must use the same setting for Debugger Interface (JTAG/SWD). If using the "Default" setting for Debugger Interface, the J-Link debugger will use JTAG by default until the J-Link driver is told otherwise, e.g., by the debugger IDE.
A higher J-Link Speed gives better streaming performance, i.e., lower risk of blocking or lost data. In many cases speeds of 10-20 MHz is possible, but according to SEGGER there is a small risk of getting poor signal quality (i.e., problems) if going above 6 MHz, depending on the development board. Default is therefore set to 4 MHz. However, we recommend that you try higher speeds, 10 MHz or higher. Don't worry, nothing will break and any corrupted data will most likely manifest directly as error messages when processing the trace.
To stream the trace data over a network connection, i.e., TCP/IP transfer, select TRC_RECORDER_TRANSFER_METHOD_TCPIP in trcConfig.h. The TCP/IP support also needs to be configured for the available TCP/IP stack. To do this, modify the functions stubs in trcTCPIPConfig.h.
In TCP/IP mode, the streaming recorder stores the event data in a multi-page RAM buffer. Once a page is full, the recorder switches to a new page and the full page is sent over the network connection. This separate RAM buffer is needed since a TCP/IP stack often can't be called directly from the kernel instrumentation code, as this might cause infinite recursive calls.
On the applicataion side, check the Streaming Trace Settings, found under File -> Settings... in Tracealyzer for FreeRTOS.

You may configure the streaming recorder to use any data channel of your choice, if you have some way of receiving the data and writing it to a file. For instance, you may use a fast serial connection (over USB). Or if you have permanent storage on your target system, e.g., a memory card, you can stream to a local file.
To setup a custom transfer method, select TRC_RECORDER_TRANSFER_METHOD_CUSTOM in trcConfig.h and define your transfer interface using the TRC_STREAM_CUSTOM macros. For examples on how to implement custom trace streaming, please refer to trcStreamPort.h.
In some cases, custom streaming needs to use our multi-page RAM buffer (trcPagedEventBuffer.c). The data is then read by a periodic task (TzCtrl) and written to the specified interface. It is however possible to omit the multi-page buffer and instead write directly to the transfer channel, if (1) the channel is a dedicated for this purpose and if (2) the writes do not generate any kernel events, e.g., from Mutex or Semaphore operations.
The trace data is a binary format and should be stored in raw format. Name the output file ".psf" to make it easier to find in Tracealyzer. The required channel throughput depends on your application, but typically you need something like 20-200 KB/s, the faster the better.
To connect to the target system, select "Connect to Target System..." in the File menu. This opens the Target Connection dialog, which directly establishes a connection with the target. You may connect while the target is running (with or without a debugger IDE) and, in the case of SEGGER RTT, also if halted, e.g. on a breakpoint. Do not try to connect while a debugger session is being initialized, i.e., during programming of the device, wait until the debugger IDE is ready.
Recording a Trace
The Target Connection dialog contains four buttons (explained below) and a "live", animated graph showing the target CPU usage profile while recording.
The blue line shows the CPU usage of the application tasks, i.e., the relative number of clock cycles used, excluding the idle task. If you enable tracing of Interrupt Service Routines (i.e., ISRs), a red line is also displayed showing the CPU usage of the traced ISRs.
Connection Issues
The first time you try to connect over SEGGER RTT you may be presented with a dialog where you should specify your device. This is required for Tracealyzer for FreeRTOS to locate the RAM address range(s) and find the RTT data structure. You can also access this dialog via File -> Settings... -> J-Link Settings -> Select Device....
When using the Debugger Interface setting "Default (don't change)" and the J-Link has just been powered up, the following issue may occur when starting a parallel debug session. If opening the Tracealyzer for FreeRTOS Target Connection before the debugger session is launched, the Tracealyzer for FreeRTOS J-Link connection will use the default mode, JTAG. When then launching a debug session using SWD mode, the J-Link driver will get conflicting settings. In that case, the J-Link driver will ask what to do, as illustrated below.

In this case, click YES to make sure that the debugger IDE works as expected, and after that close and re-open the Target Connection dialog to establish a new connection in SWD mode.
The trace recorder library is implemented in C and provided with full source code. In the current version (v3.0.7), the recorder can only be started and stopped from the host-side application, which sends commands to the trace control task (TzCtrl). We plan to change this soon, to allow the target application to control the recording.
To integrate the recorder in an existing project, we recommend the following steps:
#define configUSE_TRACE_FACILITY 1
#include "trcKernelPort.h"
#ifdef __ICCARM__ #include "trcKernelPort.h" #endif
Public Interface
void Trace_Init(void)
The main initialization routine for the trace recorder. This is the only function that is required for basic use of the recorder. This initializes the selected trace transfer method and creates the TzCtrl task that listens for start and stop commands from the host application.
void vTracePrintF(const char* chn, const char* fmt, ...)
Parameter chn: User Event Channel (can be NULL)
Parameter fmt: Format string
Parameter ...: 0 - 15 data arguments (32-bit integers or char*)
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 in Tracealyzer for FreeRTOS.
User Events can be used for very efficient application logging, and are shown as yellow labels in the main trace view of Tracealyzer for FreeRTOS. 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, preceeded 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 vTraceStoreUserEventChannelName().
char* vTraceStoreUserEventChannelName(const char* name)
Parameter name: the channel name to store (const string literal)
Stores a name for a user event channel, returns the handle (just a pointer
to the provided string). Typically assigned to a "channel" variable that
keeps it for later calls to vTracePrintF();
void vTraceStoreKernelObjectName(void* object, const char* name)
Parameter object: pointer to the kernel object that shall be named.
Parameter name: the name to store (const string literal).
Stores a name for a kernel objects (Task, Semaphore, Mailbox, etc.) for display in
Tracealyzer for FreeRTOS.
void vTraceSetISRProperties(const char* name, char priority)
Parameter name: the name to give the the ISR, also serves as handle.
Parameter priority: the priority level of the ISR.
Stores a name and priority level for an Interrupt Service Routine, to allow
for better visualization. The string address is used as a unique handle.
void vTraceStoreISRBegin(void* handle)
Parameter handle: ID of the ISR, which is "name" in vTraceSetISRProperties.
Registers the beginning of an Interrupt Service Routine (ISR). Must have a matching call to TraceStoreISREnd().
void vTraceStoreISREnd()
Used in combination with vTraceStoreISRBegin, to store the end of an Interrupt Service Routine. Will attempt to automatically detect any task switches caused by this Interrupt Service Routine. Not available for all kernels!
void vTraceStoreISREndManual(int taskSwitchRequested)
Parameter taskSwitchRequested: Manually tells if the interrupt has requested a
task-switch (= 1) or if the interrupt returns to the earlier context (= 0)
Used in combination with vTraceStoreISRBegin, to store the end of an Interrupt Service Routine.
void vTraceInstanceFinishedNow(void)
Marks the current "task instance" (task loop iteration) as finished at this very instant. This is just for the Tracealyzer for FreeRTOS analysis. This causes Tracealyzer for FreeRTOS to split the current fragment at this point and begin a new "actor instance", even if no task-switch has occurred.
void vTraceInstanceFinishedNext(void)
Marks the current "task instance" (task loop iteration) as finished on the next kernel call. If that kernel call is blocking, the instance ends after the blocking event. If the kernel call is not blocking, the viewer instead splits the current fragment right before the entry of the kernel call.
When streaming trace is not suitable or possible, the Snapshot Recorder allows you to keep a small trace in RAM. This recorder is very memory efficient and often uses only four bytes per event, in some cases more. The trace data is typically uploaded to your host PC by taking a RAM dump using your debugger. This is a very common feature available in most debuggers, and usually called "Save Memory" or similar. Tracealyzer for FreeRTOS can open trace data files in binary format (.bin) and in Intel Hex format (.hex). The RAM dump does not need to match the exact location of the trace data, as long as the recorder data is included somewhere in the RAM dump. Tracealyzer for FreeRTOS locates the data automatically due to a special numeric signature found in the beginning and end of the trace data.
The snapshot recorder has two modes of operation:
The recorder library has several settings which you should inspect before using it, such as the two modes described above. The settings are found in the header file trcConfig.h, together with detailed documentation.
By default, all events are stored in the same buffer. User events, which are generated by the application, can however be stored in a separate buffer by enabling USE_SEPARATE_USER_EVENT_BUFFER in trcConfig.h. This allows for a longer history of less frequent but important user events, since they are not overwritten by kernel events. The size of the separated user event buffer is defined by the macro USER_EVENT_BUFFER_SIZE, also in trcConfig.h.
For further information about the recorder library, see trcUser.h (and trcUser.c, if you want to study the detailed implementation).
To integrate the recorder trace library in an existing FreeRTOS project, the following steps are suggested:
#define configUSE_TRACE_FACILITY 1
#include "trcKernelPort.h"
#ifdef __ICCARM__ #include "trcKernelPort.h" #endifIf using MPLAB X IDE:
#ifndef __LANGUAGE_ASSEMBLY #include "trcKernelPort.h" #endif
This section describes how to upload the trace data from the target system to Tracealyzer, when using the snapshot recorder. Your existing debugger is used to upload the trace data from the chip RAM. This is a plain RAM dump, that is done whenever you want to look at the trace buffer contents. This means it works with essentially with any debug probe and debugger IDE on the market, since saving the RAM contents is a commonly available function.
Built-in support for SEGGER J-Link/J-Trace
Tracealyzer supports SEGGER J-Link and J-Link compatible debuggers
directly, without any debugger IDE involved. Using other debug probes is
also possible, as described below.
If you have a SEGGER J-Link/J-Trace debug probe or another J-Link compatible
debug probe, just select "Read Trace" in the "J-Link" menu.
This opens a dialog where you get to enter the memory region where the recorder data structure is located. Normally you select the entire internal RAM according to the datasheet of your MCU, but the exact address can be found can by inspecting "RecorderDataPtr" with your debugger. Typical values are 0x0, 0x10000000 or 0x20000000 as start address and 0x10000 or 0x20000 as size (64 KB or 128 KB). This makes Tracealyzer read the chip RAM and locate the trace data. Note that this option is only available if a compatible debug probe is found. J-Link compatible debug probes also include Atmel SAM-ICE and many built-in debug interfaces on demonstration/evaluation boards (where there is a USB connection directly to the board). Look for a SEGGER J-Link label on the board.
Note: The J-Link integration does not work together with Renesas HEW, since this seems to lock the J-Link interface. Renesas HEW users are recommended to use the "Save Memory" function of Renesas HEW to save a RAM dump, as described below.
Atmel Studio Integration
Use the Atmel Studio plugin for Tracealyzer available in Atmel Gallery.
This provides a launch button in Atmel Studio (see "Tools" menu) and allows for uploading through any Atmel-supported debugger.
When Tracealyzer is launched from Atmel Studio it reads the trace directly, if possible. The upload requires that a debug session is active and the target system is halted, e.g., on a breakpoint.
If the plugin is installed in Atmel Studio, you may also note the "Atmel Studio" menu in Tracealyzer. This contains a command named "Read Trace", that uploads the trace from target into a file, which is then opened.
Note: If your are using an Atmel SAM-ICE or another J-Link compatibe device (such as a development board with an integrated J-Link debugger), you may also upload the trace using the integrated J-Link upload in Tracealyzer.
Using Microchip MPLAB X IDE
To upload the trace, use our MPLAB plugin. This is available on the Percepio downloads page.
Using other development environments and debug probes
Most debuggers are able to save the RAM contents to a file. Tracealyzer
supports the following common formats:
Using IAR Embedded Workbench for ARM
We recommend using our Percepio Trace Exporter plugin for IAR Embedded Workbench for ARM, which provides a smooth integration with Tracealyzer.
This is available at http://www.percepio.com/IAR and works for EWARM v7.x.
If using an older version of EWARM, you can save a RAM dump manually. In the debugger view, when stopped on a breakpoint:
Using Renesas High-performance Embedded Workshop
In the debugger view, when stopped on a breakpoint:
Using Microchip MPLAB v8
Using STM32 ST-Link
Using Rowley CrossStudio for ARM v3.2
Using Keil µVision v4.x or v5.x
exec("SAVE \"out.hex\" RecorderDataPtr , (RecorderDataPtr + 1)");
FUNC void SavePercepioRecorderData(void) {
printf("Saving Recorder Data in out.hex!\n") ;
exec("SAVE \"out.hex\" RecorderDataPtr , (RecorderDataPtr + 1)");
}
DEFINE BUTTON "Save Recorder Data", "SavePercepioRecorderData()"
Using other development environments and debug probes
Most debuggers are able to save the RAM contents to a file. Tracealyzer
supports the following common formats:
The recorder library needs a hardware timer port in order to give accurate timestamps on the recorded events. This is the only hardware dependency of the recorder library. Some hardware architectures are already supported in the recorder library, such as ARM Cortex M0, M3 and M4 (all chips using Cortex M cores), and other ports are in development. However, there are many chips that we do not yet have direct support for, with respect to hardware timestamping. In case your chip is not yet directly supported, the recorder library includes a hardware independent fallback option providing low resolution time stamping equivalent to the Real Time Engineers ltd tick, typically 1 ms. However, it is strongly recommended to use to a hardware timer for any serious use of this tool. Fortunatly, developing a hardware timer port yourself is quite easy. Read more in the "Developing a Hardware Timer Port" section below.
Developing a Hardware Timer Port
Developing a hardware timer port is quite easy if you already have a Real Time Engineers ltd environment up and running. The recorder library file trcHardwarePort.c contains a function vTracePortGetTimeStamp that is expected to read the time from the configured hardware timer/counter. This function is based on a set of macros (prefixed "HWTC") that you define according to your specific hardware in trcHardwarePort.h. Please refer to the comments in trcHardwarePort.h for detailed instructions. Note that it is typically not necessary to change the vTracePortGetTimeStamp function in trcHardwarePort.c, just the HWTC macros in the header file.
When you have identified the right hardware feature, you need to study the data sheet for your specific hardware to find the right registers to use. Make sure to read the timer/counter value in a way that does not also reset the timer/counter. For instance, the AT91SAM7 has two registers for accessing the PIT counter value, one just reads the value while the other also resets it When defining the HWTC_PERIOD macro, please use the timer/counter reload register, if possible, rather than a literal constant. This is to make the solution more portable between different versions of the same chip family.
For questions about hardware timer ports of the trace library, please contact support@percepio.com