.. _tracing:
Tracing
#######
Overview
********
The tracing feature provides hooks that permits you to collect data from
your application and allows tools running on a host to visualize the inner-working of
the kernel and various subsystems.
Every system has application-specific events to trace out. Historically,
that has implied:
1. Determining the application-specific payload,
2. Choosing suitable serialization-format,
3. Writing the on-target serialization code,
4. Deciding on and writing the I/O transport mechanics,
5. Writing the PC-side deserializer/parser,
6. Writing custom ad-hoc tools for filtering and presentation.
An application can use one of the existing formats or define a custom
format by overriding the macros declared
in :zephyr_file:`include/tracing/tracing.h`.
.. doxygengroup:: tracing_apis
:project: Zephyr
Different formats, transports and host tools are avialable and supported in
Zephyr.
In fact, I/O varies greatly from system to system. Therefore, it is
instructive to create a taxonomy for I/O types when we must ensure the
interface between payload/format (Top Layer) and the transport mechanics
(bottom Layer) is generic and efficient enough to model these. See the
*I/O taxonomy* section below.
Serialization Formats
**********************
.. _ctf:
Common Trace Format (CTF) Support
=================================
Common Trace Format, CTF, is an open format and language to describe trace
formats. This enables tool reuse, of which line-textual (babeltrace) and
graphical (TraceCompass) variants already exist.
CTF should look familiar to C programmers but adds stronger typing.
See `CTF - A Flexible, High-performance Binary Trace Format
`_.
CTF allows us to formally describe application specific payload and the
serialization format, which enables common infrastructure for host tools
and parsers and tools for filtering and presentation.
A Generic Interface
--------------------
In CTF, an event is serialized to a packet containing one or more fields.
As seen from *I/O taxonomy* section below, a bottom layer may:
- perform actions at transaction-start (e.g. mutex-lock),
- process each field in some way (e.g. sync-push emit, concat, enqueue to
thread-bound FIFO),
- perform actions at transaction-stop (e.g. mutex-release, emit of concat
buffer).
CTF Top-Layer Example
----------------------
The CTF_EVENT macro will serialize each argument to a field::
/* Example for illustration */
static inline void ctf_top_foo(uint32_t thread_id, ctf_bounded_string_t name)
{
CTF_EVENT(
CTF_LITERAL(uint8_t, 42),
thread_id,
name,
"hello, I was emitted from function: ",
__func__ /* __func__ is standard since C99 */
);
}
How to serialize and emit fields as well as handling alignment, can be done
internally and statically at compile-time in the bottom-layer.
The CTF top layer is enabled using the configuration option
:option:`CONFIG_TRACING_CTF` and can be used with the different transport
backends both in synchronous and asynchronous modes.
SEGGER SystemView Support
=========================
Zephyr provides built-in support for `SEGGER SystemView`_ that can be enabled in
any application for platforms that have the required hardware support.
The payload and format used with SystemView is custom to the application and
relies on RTT as a transport. Newer versions of SystemView support other
transports, for example UART or using snapshot mode (both still not
supported in Zephyr).
To enable tracing support with `SEGGER SystemView`_ add the configuration option
:option:`CONFIG_SEGGER_SYSTEMVIEW` to your project configuration file and set
it to *y*. For example, this can be added to the
:ref:`synchronization_sample` to visualize fast switching between threads::
CONFIG_STDOUT_CONSOLE=y
# enable to use thread names
CONFIG_THREAD_NAME=y
CONFIG_SEGGER_SYSTEMVIEW=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_TRACING=y
.. figure:: segger_systemview.png
:align: center
:alt: SEGGER SystemView
:figclass: align-center
:width: 80%
.. _SEGGER SystemView: https://www.segger.com/products/development-tools/systemview/
CPU Stats
=========
A special tracing format which provides information about percentage of CPU
usage based on tracing hooks for threads switching in and out, interrupts enters
and exits (only distinguishes between idle thread, non idle thread and scheduler).
Enable this format with the :option:`CONFIG_TRACING_CPU_STATS` option.
Transport Backends
******************
The following backends are currently supported:
* UART
* USB
* File (Using native posix port)
* RTT (With SystemView)
Using Tracing
*************
The sample :zephyr_file:`samples/subsys/tracing` demonstrates tracing with
different formats and backends.
To get started, the simplest way is to use the CTF format with the ``native_posix``
port, build the sample as follows:
.. zephyr-app-commands::
:tool: all
:app: samples/subsys/tracing
:board: native_posix
:gen-args: -DCONF_FILE=prj_native_posix_ctf.conf
:goals: build
You can then run the resulting binary with the option ``-trace-file`` to generate
the tracing data::
mkdir data
cp $ZEPHYR_BASE/subsys/tracing/ctf/tsdl/metadata data/
./build/zephyr/zephyr.exe -trace-file=data/channel0_0
The resulting CTF output can be visualized using babeltrace or TraceCompass
by pointing the tool to the ``data`` directory with the metadata and trace files.
Visualisation Tools
*******************
TraceCompass
=============
TraceCompass is an open source tool that visualizes CTF events such as thread
scheduling and interrupts, and is helpful to find unintended interactions and
resource conflicts on complex systems.
See also the presentation by Ericsson,
`Advanced Trouble-shooting Of Real-time Systems
`_.
Future LTTng Inspiration
************************
Currently, the top-layer provided here is quite simple and bare-bones,
and needlessly copied from Zephyr's Segger SystemView debug module.
For an OS like Zephyr, it would make sense to draw inspiration from
Linux's LTTng and change the top-layer to serialize to the same format.
Doing this would enable direct reuse of TraceCompass' canned analyses
for Linux. Alternatively, LTTng-analyses in TraceCompass could be
customized to Zephyr. It is ongoing work to enable TraceCompass
visibility of Zephyr in a target-agnostic and open source way.
I/O Taxonomy
=============
- Atomic Push/Produce/Write/Enqueue:
- synchronous:
means data-transmission has completed with the return of the
call.
- asynchronous:
means data-transmission is pending or ongoing with the return
of the call. Usually, interrupts/callbacks/signals or polling
is used to determine completion.
- buffered:
means data-transmissions are copied and grouped together to
form a larger ones. Usually for amortizing overhead (burst
dequeue) or jitter-mitigation (steady dequeue).
Examples:
- sync unbuffered
E.g. PIO via GPIOs having steady stream, no extra FIFO memory needed.
Low jitter but may be less efficient (cant amortize the overhead of
writing).
- sync buffered
E.g. ``fwrite()`` or enqueuing into FIFO.
Blockingly burst the FIFO when its buffer-waterlevel exceeds threshold.
Jitter due to bursts may lead to missed deadlines.
- async unbuffered
E.g. DMA, or zero-copying in shared memory.
Be careful of data hazards, race conditions, etc!
- async buffered
E.g. enqueuing into FIFO.
- Atomic Pull/Consume/Read/Dequeue:
- synchronous:
means data-reception has completed with the return of the call.
- asynchronous:
means data-reception is pending or ongoing with the return of
the call. Usually, interrupts/callbacks/signals or polling is
used to determine completion.
- buffered:
means data is copied-in in larger chunks than request-size.
Usually for amortizing wait-time.
Examples:
- sync unbuffered
E.g. Blocking read-call, ``fread()`` or SPI-read, zero-copying in shared
memory.
- sync buffered
E.g. Blocking read-call with caching applied.
Makes sense if read pattern exhibits spatial locality.
- async unbuffered
E.g. zero-copying in shared memory.
Be careful of data hazards, race conditions, etc!
- async buffered
E.g. ``aio_read()`` or DMA.
Unfortunately, I/O may not be atomic and may, therefore, require locking.
Locking may not be needed if multiple independent channels are available.
- The system has non-atomic write and one shared channel
E.g. UART. Locking required.
``lock(); emit(a); emit(b); emit(c); release();``
- The system has non-atomic write but many channels
E.g. Multi-UART. Lock-free if the bottom-layer maps each Zephyr
thread+ISR to its own channel, thus alleviating races as each
thread is sequentially consistent with itself.
``emit(a,thread_id); emit(b,thread_id); emit(c,thread_id);``
- The system has atomic write but one shared channel
E.g. ``native_posix`` or board with DMA. May or may not need locking.
``emit(a ## b ## c); /* Concat to buffer */``
``lock(); emit(a); emit(b); emit(c); release(); /* No extra mem */``
- The system has atomic write and many channels
E.g. native_posix or board with multi-channel DMA. Lock-free.
``emit(a ## b ## c, thread_id);``