To trace the execution of exception/interrupt handlers (ISRs), you need to call vTraceStoreISRBegin and vTraceStoreISREnd in the beginning and in the end of the handler function.

The vTraceStoreISREnd routine normally assumes a return to the previously executing task. But sometimes another ISR is pending and executes in direct sequence, making this assumption incorrect. For instance, ARM Cortex-M devices contain a smart performance optimization that allows pending interrupt handlers to execute in direct sequence (tail-chained) without returning to the previous context in between. Without special handling, an extra fragment of the previous context may still be displayed in between tail-chained interrupt handlers.

Since interrupt tail-chaining is handled internally in the processor, we can only detect this indirectly – using the timestamps of the ISR events.

If Tracealyzer finds two adjacent ISRs with no other events in between (like kernel service calls), it checks the time from vTraceStoreISREnd in the earlier ISR to vTraceStoreISRBegin in the later ISR. If less or equal to TRC_CFG_ISR_TAILCHAINING_THRESHOLD, Tracealyzer assumes that the interrupt handlers executed in direct sequence, without a return to the previous context in between. This affects all views and reports in Tracealyzer.

The default value of TRC_CFG_ISR_TAILCHAINING_THRESHOLD is 0, meaning that this analysis is disabled and that you will get “extra” task fragments (very short) in between tail-chained ISRs. Avoid this by setting a suitable value for TRC_CFG_ISR_TAILCHAINING_THRESHOLD. We recommend the below process for measuring this threshold value.

Measuring the ISR tailchaining threshold

The threshold is specific for each type of processor, the compiler and its settings, so this setting must be “calibrated” to give a 100% correct result. This is however only required once.

1. If using snapshot mode, check the following in your setup (trcSnapshotConfig.h):

1.1)  TRC_CFG_NISR should be at least 1,

1.2) TRC_CFG_INCLUDE_ISR_TRACING should be enabled (= 1).


2. Set up a periodic timer that fires an interrupt, initially with minimum period (e.g. 0 or 1).

3. Create a traced interrupt handler, that gradually increases the time between interrupts (period), like below.

volatile int background_running = 0;
volatile int counter = 0;

void MyTimerInterruptHandler( void )
 if (! background_running)
   Timer1->period++; // The "reload" register

3. In your your main routine (before starting your RTOS), do the following:

3.1. Initiate and start the tracing. If using streaming, make sure to use vTraceEnable(TRC_START_AWAIT_HOST) since the trace control task is not yet started.

3.2. Start your interrupt timer with a minimal period (e.g., 0). The ISR will execute continuously for a while, but as the period (reload register) is gradually increased, the main routine will soon get to execute again.

3.3. Directly after starting the interrupt timer, set the “background_running” variable to 1. This way, the ISR period stops increasing as soon the main routine is allowed to execute. Put a breakpoint in the ISR and on this assignment to make sure the ISR executes several times before “background_running” is set.

3.4. Next follows a loop that waits for “counter” to reach a suitable number of ISRs executed, e.g. 5 or 10, so you have a couple of data points in your trace with the fixed ISR period. Then stop the timer.

3.5. If using streaming, you should now start the RTOS to allow the trace to be transferred.

4. Open the trace in Tracealyzer, and change to Time Format: Native Ticks (in the View menu). Open  “Actor Instance Graphs” -> “Separation – From Execution Start” and hide any other tasks and ISRs, except for your Timer ISR. Each data point corresponds to one execution of the ISR, and the Y-axis is the separation in between the ISRs. The separation should be stable, otherwise you have disturbances from other ISRs.

In the diagram, the first instances of the ISR should have minimal separation, i.e., resulting from the ISRs executing in direct sequence. Later in the trace, the separation should increase slightly as the ISR period has increased sufficiently to let the main routine to execute a few instructions. Find the place where the separation has increased use this separation value for your TRC_CFG_ISR_TAILCHAINING_THRESHOLD definition.

If using Free Edition, you can measure the ISR separation manually in the trace view.

So, your main routine should look something like this:

vTraceEnable(TRC_START); // or vTraceEnable(TRC_START_AWAIT_HOST);
TimerStart(); // The ISR will run a lot at first, but with gradually increasing period
background_running = 1; // Let the ISR know that main has been able to execute
while (count < 5); // Record a few more ISRs with gradually increasing periods
TimerStop();  // If using streaming, the TzCtrl task must be allowed to run
RTOSSchedulerStart(); // If using streaming...