• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_trace:
2
3========
4pw_trace
5========
6.. pigweed-module::
7   :name: pw_trace
8
9Pigweed's tracing module provides facilities for applications to trace
10information about the execution of their application. The module is split into
11two components:
12
131. The facade (this module) which is only a macro interface layer
142. The backend, provided elsewhere, implements the low level tracing.
15
16------
17Status
18------
19This module is currently in development, and is therefore still undergoing
20significant changes.
21
22Future work will add:
23
241. A Python library to generate trace files which can be viewed. (pwbug/205)
252. Add more examples with sample output. (pwbug/207)
263. Implement a trace backend module. (pwbug/260)
27
28--------
29Overview
30--------
31Traces provide a useful view which shows the flow of events through a system,
32and can greatly assist in understanding complex software problems. These traces
33can either show what tasks are running at any given time, or use added code
34(similar to logging), to help annotate specific interesting flows.
35
36Fundamentally, tracing is similar to logging in that it provides the developer
37with a view into what the system was doing at a particular time. The fundamental
38difference between logs and tracing is that logs present information as ad-hoc
39human-readable strings and are better suited to providing the current state of
40the system. Instead, traces capture sequences of events with precise timestamps,
41and are therefore useful at understanding the flow of events in the system over
42time.
43
44The default backend for pw_trace is pw_trace_null, which disables tracing.
45
46Compatibility
47-------------
48Most of the facade is compatible with C and C++, the only exception to this is
49the Scope and Function tracing macros which are convenience wrappers only
50available in C++.
51
52pw_trace:null
53-------------
54``pw_trace_null`` is a ``pw_trace backend`` that ignores all ``pw_trace``
55statements. The backend implements ``pw_trace`` with empty inline functions.
56Using empty functions ensure that the arguments are evaluated and their types
57are correct. Since the functions are inline in the header, the compiler will
58optimize out the function call.
59
60This backend can be used to completely disable ``pw_trace``.
61
62Dependencies
63-------------
64``pw_preprocessor``
65
66Example
67-------
68
69.. code-block:: cpp
70
71   #define PW_TRACE_MODULE_NAME "Input"
72   #include "pw_trace/trace.h"
73
74   void SendButton() {
75     PW_TRACE_FUNCTION()
76     // do something
77   }
78
79   void InputLoop() {
80     while(1) {
81       auto event = WaitNewInputEvent()
82       TRACE_SCOPE("Handle Event");  // measure until loop finished
83       if (event == kNewButton){
84         SendButton();
85         PW_TRACE_END("button");  // Trace event was started in ButtonIsr
86       } else {
87         PW_TRACE_INSTANT("Unknown event");
88       }
89     }
90   }
91
92   void ButtonIsr() {
93     PW_TRACE_START("button");
94     SendNewInputEvent(kNewButton);
95   }
96
97
98------------
99Trace macros
100------------
101
102The ``pw_trace`` public API provides three basic trace events:
103
104- ``PW_TRACE_INSTANT`` - This is used to trace an instant event, which has no
105  duration.
106- ``PW_TRACE_START`` & ``PW_TRACE_END`` - Trace 'start' and 'end' events are
107  paired together to measure the duration of an event.
108
109These trace event macros all have the same arguments:
110
111- *label* - Each of these trace events must have a label, which is a string
112  describing the event. In addition to the required label, each of these traces
113  can optionally provide a group label and trace id.
114- *group_label* - The *optional* group label is used if many traces are all
115  measuring the same thing and should be grouped together. This information will
116  be used when visualizing the trace to ensure the information appears together.
117- *trace_id* - The *optional* trace id is similar to the group_id, but instead
118  groups events using a runtime value. This can be used if multiple trace flow
119  might happen at the same time and need to be grouped together.
120  For example, this could be used to trace data packets flowing through the
121  system; when a new sample might arrive before the previous packet is finished
122  processing. This would result in two start events occurring before the end
123  event. By providing a trace id with a different value for each packet, these
124  can be separated when decoding.
125
126.. tip::
127
128  All of these arguments must be the same for a *start* and *end* pair.
129
130This results in 9 different trace calls:
131
132.. cpp:function:: PW_TRACE_INSTANT(label)
133.. cpp:function:: PW_TRACE_INSTANT(label, group_label)
134.. cpp:function:: PW_TRACE_INSTANT(label, group_label, trace_id)
135.. cpp:function:: PW_TRACE_START(label)
136.. cpp:function:: PW_TRACE_START(label, group_label)
137.. cpp:function:: PW_TRACE_START(label, group_label, trace_id)
138.. cpp:function:: PW_TRACE_END(label)
139.. cpp:function:: PW_TRACE_END(label, group_label)
140.. cpp:function:: PW_TRACE_END(label, group_label, trace_id)
141
142Modules
143-------
144In addition to these arguments, traces can be grouped into modules similar to
145logging. This is done by redefining the ``PW_TRACE_MODULE_NAME``.
146
147Flags
148-----
149Each trace event also has a flags field which can be used to provide additional
150compile time trace information. Each trace macro has a matching macro which
151allows specifying the flag:
152
153.. cpp:function:: PW_TRACE_INSTANT_FLAG(flag, label)
154.. cpp:function:: PW_TRACE_INSTANT_FLAG(flag, label, group_label)
155.. cpp:function:: PW_TRACE_INSTANT_FLAG(flag, label, group_label, trace_id)
156.. cpp:function:: PW_TRACE_START_FLAG(flag, label)
157.. cpp:function:: PW_TRACE_START_FLAG(flag, label, group_label)
158.. cpp:function:: PW_TRACE_START_FLAG(flag, label, group_label, trace_id)
159.. cpp:function:: PW_TRACE_END_FLAG(flag, label)
160.. cpp:function:: PW_TRACE_END_FLAG(flag, label, group_label)
161.. cpp:function:: PW_TRACE_END_FLAG(flag, label, group_label, trace_id)
162
163When not specified the flag uses the value of the macro ``PW_TRACE_FLAGS``.
164
165Data
166----
167Each macro also has a variant which allows appending additional data:
168
169.. cpp:function:: PW_TRACE_INSTANT_DATA(label, data_format_string, data, size)
170.. cpp:function:: PW_TRACE_INSTANT_DATA(\
171   label, group_label, data_format_string, data, size)
172.. cpp:function:: PW_TRACE_INSTANT_DATA(\
173   label, group_label, trace_id, data_format_string, data, size)
174.. cpp:function:: PW_TRACE_START_DATA(label, data_format_string, data, size)
175.. cpp:function:: PW_TRACE_START_DATA(\
176   label, group_label, data_format_string, data, size)
177.. cpp:function:: PW_TRACE_START_DATA(\
178   label, group_label, trace_id, data_format_string, data, size)
179.. cpp:function:: PW_TRACE_END_DATA(label, data_format_string, data, size)
180.. cpp:function:: PW_TRACE_END_DATA(\
181   label, group_label, data_format_string, data, size)
182.. cpp:function:: PW_TRACE_END_DATA(\
183   label, group_label, trace_id, data_format_string, data, size)
184
185These macros require 3 additional arguments:
186
187- *data_format_string* - A string which is used by the decoder to identify the
188  data. This could for example either be printf style tokens, python struct
189  packed fmt string or a custom label recognized by the decoder.
190- *data* - A pointer to a buffer of arbitrary caller-provided data (void*).
191- *size* - The size of the data (size_t).
192
193Currently the included python tool supports a few different options for
194*data_format_string*:
195
196- *@pw_arg_label* - Uses the string in the data as the trace event label.
197- *@pw_arg_group* - Uses the string in the data as the trace event group.
198- *@pw_arg_counter* - Uses the data as a little endian integer value, and
199  visualizes it as a counter value in the trace (on a graph).
200- *@pw_py_struct_fmt:* - Interprets the string after the ":" as a python struct
201  format string, and uses that format string to unpack the data elements. This
202  can be used to either provide a single value type, or provide multiple
203  different values with a variety of types. Options for format string types can
204  be found here: https://docs.python.org/3/library/struct.html#format-characters
205  . The data is always assumed to be packed with little-endian ordering if not
206  indicated otherwise::
207
208    // Example
209    data_format_string = "@pw_py_struct_fmt:ll"
210    data = 0x1400000014000000
211    args = {data_0: 20, data_1:20}
212- *@pw_py_map_fmt:* - Interprets the string after the ":" as a dictionary
213  relating the data field name to the python struct format string. Once
214  collected, the format strings are concatenated and used to unpack the data
215  elements as above. The data is always assumed to be packed with little-endian
216  ordering if not indicated otherwise. To specify a different ordering,
217  construct the format string as ``@pw_py_map_fmt:[@=<>!]{k:v,...}``::
218
219    // Example
220    data_format_string = "@pw_py_map_fmt:{Field: l, Field2: l }"
221    data = 0x1400000014000000
222    args = {Field: 20, Field2:20}
223
224.. tip::
225
226  It is ok for only one event of a start/end pair to contain data, as long the
227  *label*, *group_label*, and *trace_id*, are all the same.
228
229C++ Only Traces
230---------------
231Scope API measures durations until the object loses scope. This can for
232example, provide a convenient method of tracing functions or loops.
233
234.. cpp:function:: PW_TRACE_SCOPE(label)
235.. cpp:function:: PW_TRACE_SCOPE(label, group_label)
236.. cpp:function:: PW_TRACE_SCOPE(label, group_label, trace_id)
237
238Function API measures durations until the function returns. This is the only
239macro which does not require a *label*, and instead uses the function name as the
240label. It still can optionally be provided a *group_id*.
241
242.. cpp:function:: PW_TRACE_FUNCTION()
243.. cpp:function:: PW_TRACE_FUNCTION(group_label)
244.. cpp:function:: PW_TRACE_FUNCTION(group_label, trace_id)
245
246Compile time enabling/disabling
247-------------------------------
248Traces in a file can be enabled/disabled at compile time by defining through
249the ``PW_TRACE_ENABLE`` macro.  A value of 0 causes traces to be disabled.
250A non-zero value will enable traces.  While tracing defaults to enabled,
251it is best practice to define ``PW_TRACE_ENABLE`` explicitly in files that
252use tracing as the default may change in the future.
253
254A good pattern is to have a module level configuration parameter for enabling
255tracing and define ``PW_TRACE_ENABLE`` in terms of that at the top of each
256of the module's files:
257
258
259.. code-block:: cpp
260
261   // Enable tracing based on pw_example module config parameter.
262   #define PW_TRACE_ENABLE PW_EXAMPLE_TRACE_ENABLE
263
264
265Additionally specific trace points (or sets of points) can be enabled/disabled
266using the following pattern:
267
268.. code-block:: cpp
269
270   // Assuming tracing is disabled at the top of the file.
271
272   // Enable specific trace.
273   #undef PW_TRACE_ENABLE
274   #define PW_TRACE_ENABLE 1
275   PW_TRACE_INSTANT("important trace");
276
277   // Set traces back to disabled.  PW_TRACE_ENABLE can not be left
278   // undefined.
279   #undef PW_TRACE_ENABLE
280   #define PW_TRACE_ENABLE 0
281
282-----------
283Backend API
284-----------
285Each of the trace event macros get sent to one of two macros which are
286implemented by the backend:
287
288.. cpp:function:: PW_TRACE(event_type, flags, label, group_label, trace_id)
289.. cpp:function:: PW_TRACE_DATA(event_type, flags, label, group_label, \
290   trace_id, data_format_string, data, size)
291
292The ``event_type`` value will be whatever the backend defined for that specific
293trace type using the macros defined below.
294
295The backend can optionally not define ``PW_TRACE_DATA`` to have those traces
296disabled.
297
298Trace types
299-----------
300Although there are only 3 basic trace types, each has 3 variants:
301
302- Label only
303- Label and group
304- Label, group, and trace_id
305
306This combination creates 9 different trace event types:
307
308- *PW_TRACE_TYPE_INSTANT*: Instant trace, with only a label.
309- *PW_TRACE_TYPE_DURATION_START*: Start trace, with only a label.
310- *PW_TRACE_TYPE_DURATION_END*: End trace, with only a label.
311- *PW_TRACE_TYPE_INSTANT_GROUP*: Instant trace, with a label and a group.
312- *PW_TRACE_TYPE_DURATION_GROUP_START*: Start trace, with a label and a group.
313- *PW_TRACE_TYPE_DURATION_GROUP_END*: End trace, with a label and a group.
314- *PW_TRACE_TYPE_ASYNC_INSTANT*: Instant trace, with label, group, and trace_id
315- *PW_TRACE_TYPE_ASYNC_START*: Start trace, with label, group, and trace_id.
316- *PW_TRACE_TYPE_ASYNC_END*: End trace, with label, group, and trace_id.
317
318The backend must define these macros to have them enabled. If any are left
319undefined, any traces of that type are removed.
320
321Defaults
322--------
323The backend can use these macros to change what the default value is if not
324provided.
325
326- *PW_TRACE_FLAGS_DEFAULT*: Default value if no flags are provided.
327- *PW_TRACE_TRACE_ID_DEFAULT*: Default value if no trace_id provided.
328- *PW_TRACE_GROUP_LABEL_DEFAULT*: Default value if no group_label provided.
329
330----------
331Sample App
332----------
333A sample application is provided in the examples folder. This code attempts to
334provide examples of the multiple ways tracing can be used. Furthermore,
335trace backends can include the sample app in their own examples to show how to
336use other features.
337
338The sample app contains 3 "fake" tasks, which are each in their own
339`PW_TRACE_MODULE`.
340
341- *Input*: Simulating a driver, which gets data periodically, and sends to
342  *Processing* task.
343- *Processing*: Has a work queue, which handles processing the jobs.
344- *Kernel*: A simple work loop which demonstrates a possible integration of
345  tracing with a RTOS/Kernel.
346
347Jobs are intentionally made to have multiple stages of processing (simulated by
348being re-added to the work-queue). This results in multiple jobs being handled
349at the same time, the trace_id is used to separate these traces.
350
351----------------------
352Python Trace Generator
353----------------------
354The Python tool is still in early development, but currently it supports
355generating a list of json lines from a list of trace events.
356
357To view the trace, these lines can be saved to a file and loaded into
358chrome://tracing.
359
360Future work will look to add:
361
362- Config options to customize output.
363- A method of providing custom data formatters.
364- Perfetto support.
365
366
367.. toctree::
368   :hidden:
369   :maxdepth: 1
370
371   backends
372