FreeRTOS Logo

Tracealyzer allows you to trace the real-time behavior of your FreeRTOS application and visualize it through 30+ graphical views, that complement your debugger’s low-level perspective with event timelines and overviews. Tracealyzer makes it far easier to spot unexpected runtime issues, that are hard to notice in the source code.

FreeRTOS contains over 100 “trace hooks” at strategic locations in the kernel code, like the example below. Tracealyzer provides a trace recorder library for FreeRTOS that uses these trace hooks to record events. No modifications of the FreeRTOS source code are needed, only a rebuild to enable the hooks.

The trace recorder library also allows for adding your own custom logging in the application code, as described in the end of this guide.

Getting Started

Tracealyzer has two main operational modes, snapshot mode and streaming mode. In snapshot mode, the data is stored in a ring-buffer in Target RAM, from which you upload a “snapshot” when the target system is halted.  Streaming mode means continuous data upload to the host system and allows for recording longer traces, of several minutes or even hours. This requires a relatively fast connection between target and host, such as a premium debug probe or a network connection. Learn more about snapshot and streaming mode in the User Manual.

We recommend to start with snapshot mode, to make sure the basic setup is correct. Most of the steps are common for snapshot and streaming, while a few additional steps are needed for streaming.

The below video tutorial on snapshot mode is a good starting point. Further down you find detailed step-by-step guides, covering both snapshot and streaming mode.

 

Step-by-Step Guides

The step-by-step guide that follows below is divided into two parts, for snapshot mode and streaming mode.

Start with the Snapshot guide to get the basic integration right. For streaming mode, the provided example is for J-Link streaming (using RTT). If you don’t have a SEGGER J-Link, you may also check these guides for IAR Embedded Workbench, Keil µVision and the built-in support for ST-Link. Other setups for streaming are also possible.

Tracealyzer can be used in parallel with traditional debug sessions in your IDE. The trace is typically not affected by halting the target system for traditional debugging, e.g. by stopping on a breakpoint or single-stepping. The overhead of the recorder is typically a few microseconds of processor time per event. The recorder also needs a few KB of RAM and 8-16 KB of program memory, depending on configuration. Tracealyzer can be used with most embedded processors and development tools.

If you encounter any difficulties, please refer to the Troubleshooting section in the end.

1. Getting Started with Tracealyzer for FreeRTOS – Snapshot Mode

Follow the steps below to integrate the trace recorder in an existing project. In snapshot mode, the data is stored in a ring-buffer in Target RAM, from which you may read a “snapshot” at any time, as long as the target system is halted. Learn more in the Snapshot Mode section of the Tracealyzer User Manual.

Step 1.1. Get Tracealyzer from https://percepio.com/downloadform. In the registration form, make sure to select FreeRTOS as “Target Platform”. The download link is emailed to you and usually arrives within 1 minute. Evaluation licenses are sent in a separate email and may take a few minutes to arrive.

Step 1.2. Install Tracealyzer and select “Help” -> “FreeRTOS Trace Recorder” to locate the trace recorder code in the installation directory.

    Tracealyzer Trace Recorder Library

Step 1.3. Copy these source files into your project:

  • trcKernelPort.c
  • trcSnapshotRecorder.c
  • trcStreamingRecorder.c

Step 1.4. Copy all header (.h) files from the config and include folders into a suitable folder in your project, i.e. where you keep other header files.

Step 1.5. Open trcConfig.h and check the following:

  • Replace the #error line with an #include of the processor header file (e.g. #include “stm32f4xx.h”).
  • Set TRC_CFG_HARDWARE_PORT to match your processor family.
    • If your device has an Arm Cortex-M core (examples), use TRC_HARDWARE_PORT_ARM_Cortex_M.
    • Support for other processors is listed in trcHardwarePort.h and include e.g. PIC32, Cortex-A9, Renesas RX, etc.
  • Make sure that TRC_CFG_FREERTOS_VERSION matches your version of FreeRTOS.

Step 1.6. Open trcSnapshotConfig.h and make sure that the settings TRC_CFG_NTASK, TRC_CFG_NQUEUE etc. are large enough to accommodate the number of tasks, queues, semaphores etc. in your system. If not sure, use large values. You can optimize these settings later, once a trace has been recorded.

Step 1.7. Open FreeRTOSConfig.h and make sure you have the following lines included:

  • #define configUSE_TRACE_FACILITY 1
  • #include "trcRecorder.h" /* Should be in the end, after the #define. */

NOTE: If using IAR or MPLAB X IDE, you need to use a conditional include like shown below.

/* If using IAR Embedded Workbench */

#ifndef __IASMARM__
#include "trcRecorder.h"
#endif
/* If using Microchip MPLAB X IDE */

#ifndef __LANGUAGE_ASSEMBLY
#include "trcRecorder.h"       
#endif

If using Xilinx SDK, you need to edit the FreeRTOSConfig.h file found in the libsrc folder, e.g. bsp/ps7_cortexa9_0/libsrc/freertos823_xilinx_v1_3/src/FreeRTOSConfig.h. Do not edit the FreeRTOSConfig.h found in the include folder, as this will be overwritten by the libsrc version during the BSP code generation.

Step 1.8. Initialize and start the trace recorder library by calling vTraceEnable(TRC_START).

This should be placed after the initial hardware setup, but before any FreeRTOS calls have been made (like xTaskCreate).

Step 1.9. Test it. Build the project, let the system run for a while, then halt the execution.

Done!

To access the trace data, you need to upload the trace data from the target RAM and save it in a file on the host computer, which is then opened in Tracealyzer. This can be automated in several ways, depending on your debugging tools. Please refer to the tool-specific documentation below.

Also see this section in the User Manual for additional options and more detailed instructions.


Note: The hardware port setting TRC_HARDWARE_PORT_ARM_Cortex_M works with all Arm Cortex-M devices, such as

  • Cypress PSoC 4, PSoC 5, PSoC 6
  • Infineon XMC1000, XMC4000
  • Microchip (Atmel) SAM 4S, SAM 4E, SAM E5x, SAM E70, SAM S70
  • NXP LPC800, LPC1700, LPC1800, LPC4300, LPC5500, i.MX RT, Kinetis
  • Renesas Synergy, RA
  • Silicon Labs EFM32
  • STMicro STM32F, STM32L, STM32G, STM32H, STM32W

 

2. Getting Started with Tracealyzer for FreeRTOS- Streaming Mode

Using streaming trace allows for recording much longer traces, of several minutes or even hours. This requires a relatively fast connection between target and host, such as a premium debug probe or a network connection. The below guide describes the most common solution for streaming on Arm Cortex-M devices, using a SEGGER J-Link debug probe.

If not using a J-Link, check these guides for IAR Embedded Workbench, Keil µVision or the built-in support for ST-Link. Other setups for streaming are also possible.

Streaming using SEGGER J-Link

Step 2.1. After having installed Tracealyzer, also install the latest version of the J-Link drivers. This ensures that your IDE and Tracealyzer are using the same J-Link driver.

Step 2.2. Make sure you have followed the guide for snapshot tracing. (Step 1.6 can be omitted if you are only interested in streaming.)

Step 2.3: Open trcConfig.h and set TRC_CFG_RECORDER_MODE to TRC_RECORDER_MODE_STREAMING.

Step 2.4. Copy all code found in /streamports/Jlink_RTT/ and /streamports/Jlink_RTT/include into your project.

Step 2.5. In your vTraceEnable call, make sure to use the parameter TRC_INIT.

Step 2.6. Start Tracealyzer and open File -> Settings -> PSF Streaming Settings and select Target Connection: SEGGER RTT.

Make sure you have the following settings (the defaults):

  • RTT Control Block Address: auto-detect (= 0)
  • Target RTT Up Buffer Index: 1
  • Target RTT Down Buffer Index: 1
  • Reset Target on Connect: Not checked
  • Target Starts Tracing: Not checked

Step 2.7. Open File -> Settings -> J-Link Settings, and set the “Debugger Interface” setting to SWD or JTAG. This setting should match the corresponding setting in your IDE if debugging sessions are done in parallel with Tracealyzer recordings.

We don’t recommend using the “Default (don’t change)” option. The J-Link driver seems to require that you specify JTAG or SWD, otherwise it may ignore the J-Link Speed setting and instead uses a default speed of 200 Khz, which is likely to cause poor throughput.

Step 2.8. Click “Select Device” and specify which processor that is used. If your device is not listed, download and install the latest J-Link driver.

Step 2.9. Start your target system, then click on “Record Streaming Trace” on the welcome screen, or “Start Recording” in the Tracealyzer navigation bar.

You should now see a live trace of your system in Tracealyzer, like this.

Adding User Events

To get more information from your application, such as variable values and debug messages, you can generate User Events that are displayed in Tracealyzer. They appear as yellow labels in the trace view.

To generate a User Event, you first register a User Event Channel using xTraceRegisterString(). This channel is used to group related events, and sets a name for this channel. You can have multiple such channels in parallel.

Then call vTracePrint() or vTracePrintF(), using the handle from xTraceRegisterString() as first parameter.

The syntax is similar to the classic “printf”, although not every format specifier is supported.

Example:

traceString chn = xTraceRegisterString("MyChannel"); 
... 
vTracePrint(chn, "Hello World!"); 
vTracePrintF(chn, "Value: %d", myValue);

Tracing Interrupt Handlers

To trace interrupt handlers, first call xTraceSetISRProperties() to specify the interrupt name and priority. This is typically done in your main function, not in the interrupt handler. This returns a handle, that you need to store in a global variable.

Then call vTraceStoreISRBegin() in the beginning of your handler, and vTraceStoreISREnd() in the end.

Example:

#define PRIO_ISR_TIMER1 3 /* the hardware priority level */ 
... 
traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1); 
... 
void ISR_handler(void)
{ 
    signed BaseType_t xHigherPriorityTaskWoken;
    vTraceStoreISRBegin(Timer1Handle); 

    /* Wakes up a pending task, causing a task-switch. */
    xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken); 

    vTraceStoreISREnd(xHigherPriorityTaskWoken); 
}

Troubleshooting

In some cases, the recorder may detect an error caused by incorrect configuration or bad arguments. In such cases, the error handler prvTraceError is called. If using an IDE debugger in parallel with Tracealyzer, you may put a breakpoint in prvTraceError to learn more about the problem. The error message or error code is provided as parameter to this function, and your debugger’s “call stack” view typically shows the context of the error. (Note that prvTraceError has two implementations, one in trcStreamingRecorder.c and another in trcSnapshotRecorder.c, so make sure to put the breakpoint in the right one.)

In case you have issues related to J-Link streaming, see our specific guide about Troubleshooting J-Link RTT Streaming.

For other issues, check the Troubleshooting section in the User Manual.

If you have any questions, feel free to contact us at support@percepio.com.

Learning More

Make sure to check out our RTOS Debug Portal, with many articles and hands-on examples.

To purchase Tracealyzer, see the Licensing page.