|
| 1 | +.. _instrumentation: |
| 2 | + |
| 3 | +Instrumentation |
| 4 | +############### |
| 5 | + |
| 6 | +The instrumentation subsystem provides compiler-managed runtime system instrumentation capabilities |
| 7 | +for Zephyr applications. It enables developers to trace function calls, observe context switches, |
| 8 | +and profile application performance with minimal manual instrumentation effort. |
| 9 | + |
| 10 | +Unlike the :ref:`tracing <tracing>` subsystem, which provides RTOS-aware tracing with structured |
| 11 | +event APIs, the instrumentation subsystem works at a lower level by leveraging compiler |
| 12 | +instrumentation hooks. This approach makes it possible to capture virtually any function entry and |
| 13 | +exit events without requiring manual tracing calls in the code. |
| 14 | + |
| 15 | +.. admonition:: Tracing vs. Instrumentation |
| 16 | + :class: hint |
| 17 | + |
| 18 | + **When to use Tracing**: Choose the tracing subsystem when you need RTOS-aware event tracing |
| 19 | + (e.g. thread switches, semaphore operations, etc.) and want to minimize overhead. |
| 20 | + |
| 21 | + **When to use Instrumentation**: Choose instrumentation when you need a detailed view of |
| 22 | + function-level execution to better understand code flow, or to identify performance bottlenecks |
| 23 | + without adding manual trace points. |
| 24 | + |
| 25 | +The instrumentation subsystem relies on compiler support for automatic function instrumentation. |
| 26 | +When enabled, the compiler automatically inserts calls to special instrumentation handler functions |
| 27 | +at the entry and exit of every function in your application (excluding those explicitly marked with |
| 28 | +``__no_instrumentation__``). Currently, only GCC is supported with the ``-finstrument-functions`` |
| 29 | +compiler flag. |
| 30 | + |
| 31 | +The subsystem initializes automatically after RAM initialization and uses trigger/stopper functions |
| 32 | +to control when recording is active. The default trigger and stopper functions are both set to |
| 33 | +``main()`` (configurable via Kconfig), meaning instrumentation captures the entire execution from |
| 34 | +when ``main()`` starts until it returns. |
| 35 | + |
| 36 | +The recorded data is stored in RAM and can be accessed from a host computer thanks to a UART backend |
| 37 | +that exposes a set of simple commands. :zephyr_file:`scripts/instrumentation/zaru.py` script allows |
| 38 | +to execute these commands through a high-level command-line interface and makes it easy to obtain |
| 39 | +data in a format suitable for further analysis (e.g. using `Perfetto`_). |
| 40 | + |
| 41 | +Operational Modes |
| 42 | +***************** |
| 43 | + |
| 44 | +The instrumentation subsystem supports two modes that can be enabled independently or together: |
| 45 | + |
| 46 | +Callgraph Mode (Tracing) |
| 47 | +========================= |
| 48 | + |
| 49 | +In callgraph mode (enabled with :kconfig:option:`CONFIG_INSTRUMENTATION_MODE_CALLGRAPH`), the |
| 50 | +subsystem records function entry and exit events along with timestamps and context information in a |
| 51 | +memory buffer. This enables: |
| 52 | + |
| 53 | +- Reconstruction of the complete function call graph |
| 54 | +- Observation of thread context switches |
| 55 | +- Analysis of execution flow and timing relationships |
| 56 | + |
| 57 | +The trace buffer can operate in ring buffer mode (default, overwrites old entries) or fixed buffer |
| 58 | +mode (stops when full). Buffer size is configurable via |
| 59 | +:kconfig:option:`CONFIG_INSTRUMENTATION_MODE_CALLGRAPH_TRACE_BUFFER_SIZE`. |
| 60 | + |
| 61 | +Statistical Mode (Profiling) |
| 62 | +============================= |
| 63 | + |
| 64 | +In statistical mode (enabled with :kconfig:option:`CONFIG_INSTRUMENTATION_MODE_STATISTICAL`), the |
| 65 | +subsystem accumulates timing statistics for each unique function executed between the trigger and |
| 66 | +stopper points. This provides total execution time per function and helps identify performance |
| 67 | +bottlenecks. The subsystem tracks up to |
| 68 | +:kconfig:option:`CONFIG_INSTRUMENTATION_MODE_STATISTICAL_MAX_NUM_FUNC` unique functions. |
| 69 | + |
| 70 | +Configuration |
| 71 | +************* |
| 72 | + |
| 73 | +Enable instrumentation with: |
| 74 | + |
| 75 | +.. code-block:: kconfig |
| 76 | +
|
| 77 | + CONFIG_INSTRUMENTATION=y |
| 78 | + CONFIG_INSTRUMENTATION_MODE_CALLGRAPH=y # For tracing |
| 79 | + CONFIG_INSTRUMENTATION_MODE_STATISTICAL=y # For profiling |
| 80 | +
|
| 81 | +The instrumentation subsystem uses :ref:`retained memory <retention_api>` to persist trigger/stopper |
| 82 | +function addresses across reboots. This must be configured in the devicetree: |
| 83 | + |
| 84 | +.. code-block:: devicetree |
| 85 | +
|
| 86 | + / { |
| 87 | + sram@2003FC00 { |
| 88 | + compatible = "zephyr,memory-region", "mmio-sram"; |
| 89 | + reg = <0x2003FC00 DT_SIZE_K(1)>; |
| 90 | + zephyr,memory-region = "RetainedMem"; |
| 91 | +
|
| 92 | + retainedmem { |
| 93 | + compatible = "zephyr,retained-ram"; |
| 94 | + status = "okay"; |
| 95 | +
|
| 96 | + instrumentation_triggers: retention@0 { |
| 97 | + compatible = "zephyr,retention"; |
| 98 | + status = "okay"; |
| 99 | + reg = <0x0 0x10>; |
| 100 | + }; |
| 101 | + }; |
| 102 | + }; |
| 103 | + }; |
| 104 | +
|
| 105 | + /* Adjust main SRAM to exclude retained region */ |
| 106 | + &sram0 { |
| 107 | + reg = <0x20000000 DT_SIZE_K(255)>; |
| 108 | + }; |
| 109 | +
|
| 110 | +See the :zephyr:code-sample:`instrumentation` sample for complete configuration examples. |
| 111 | +Additional options include buffer sizes, trigger functions, and function/file exclusion lists (see |
| 112 | +Kconfig options starting with :kconfig:option-regex:`CONFIG_INSTRUMENTATION_*`). |
| 113 | + |
| 114 | +``zaru.py`` Usage |
| 115 | +***************** |
| 116 | + |
| 117 | +The ``zaru.py`` command-line tool (located in :zephyr_file:`scripts/instrumentation/zaru.py`) |
| 118 | +provides an interface for controlling instrumentation and extracting data from the target over UART. |
| 119 | + |
| 120 | +The tool offers several commands: |
| 121 | + |
| 122 | +- ``status``: Check if the target device supports callgraph (tracing) and statistical (profiling) |
| 123 | + modes. |
| 124 | +- ``trace``: Capture and display function call traces. |
| 125 | +- ``profile``: Capture and display function profiling data. |
| 126 | +- ``reboot``: Reboot the target device. |
| 127 | + |
| 128 | +You can get help for each command by running ``zaru.py <command> --help``. |
| 129 | + |
| 130 | +By default, ``zaru.py`` attempts to connect to the target device using ``/dev/ttyACM0``. You can |
| 131 | +specify a different serial port using the ``--serial`` option: |
| 132 | + |
| 133 | +.. code-block:: console |
| 134 | +
|
| 135 | + $ ./scripts/instrumentation/zaru.py --serial /dev/ttyACM1 status |
| 136 | +
|
| 137 | +The ``--build-dir`` option can be used to specify the Zephyr build directory, which is needed to |
| 138 | +locate the ELF file for symbol resolution. If not provided, ``zaru.py`` will attempt to find it |
| 139 | +automatically. |
| 140 | + |
| 141 | +See the :zephyr:code-sample:`instrumentation` sample documentation for detailed usage instructions. |
| 142 | + |
| 143 | +Limitations and Considerations |
| 144 | +******************************* |
| 145 | + |
| 146 | +- **Compiler support**: Currently requires GCC with ``-finstrument-functions`` support. Other |
| 147 | + compilers are not supported. |
| 148 | + |
| 149 | +- **Stack size requirements**: Instrumentation adds overhead to every function call, which increases |
| 150 | + stack usage. You will likely need to increase thread stack sizes to accommodate the additional |
| 151 | + space required by instrumentation handlers and nested function calls. |
| 152 | + |
| 153 | +- **Execution overhead**: All function calls incur instrumentation overhead. Code size will increase |
| 154 | + due to added instrumentation calls, and performance will be impacted. |
| 155 | + |
| 156 | +- **Initialization constraints**: Code that runs before RAM initialization (e.g., early boot |
| 157 | + functions) is not captured. |
| 158 | + |
| 159 | +To reduce overhead, use trigger/stopper functions to instrument only code regions of interest, and |
| 160 | +exclude performance-critical functions via |
| 161 | +:kconfig:option:`CONFIG_INSTRUMENTATION_EXCLUDE_FUNCTION_LIST` and |
| 162 | +:kconfig:option:`CONFIG_INSTRUMENTATION_EXCLUDE_FILE_LIST`. |
| 163 | + |
| 164 | +API Reference |
| 165 | +************* |
| 166 | + |
| 167 | +.. doxygengroup:: instrumentation_api |
| 168 | + |
| 169 | +.. _Perfetto: https://perfetto.dev/ |
0 commit comments