Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / tools / profiler / docs / markers-guide.rst
blob8c796b9d57361030b57f466a9b217fed7b98e714
1 Markers
2 =======
4 Markers are packets of arbitrary data that are added to a profile by the Firefox code, usually to
5 indicate something important happening at a point in time, or during an interval of time.
7 Each marker has a name, a category, some common optional information (timing, backtrace, etc.),
8 and an optional payload of a specific type (containing arbitrary data relevant to that type).
10 .. note::
11     This guide explains C++ markers in depth. To learn more about how to add a
12     marker in JavaScript or Rust, please take a look at their documentation
13     in :doc:`instrumenting-javascript` or :doc:`instrumenting-rust` respectively.
15 Example
16 -------
18 Short example, details below.
20 Note: Most marker-related identifiers are in the ``mozilla`` namespace, to be added where necessary.
22 .. code-block:: c++
24     // Record a simple marker with the category of DOM.
25     PROFILER_MARKER_UNTYPED("Marker Name", DOM);
27     // Create a marker with some additional text information. (Be wary of printf!)
28     PROFILER_MARKER_TEXT("Marker Name", JS, MarkerOptions{}, "Additional text information.");
30     // Record a custom marker of type `ExampleNumberMarker` (see definition below).
31     PROFILER_MARKER("Number", OTHER, MarkerOptions{}, ExampleNumberMarker, 42);
33 .. code-block:: c++
35     // Marker type definition.
36     struct ExampleNumberMarker {
37       // Unique marker type name.
38       static constexpr Span<const char> MarkerTypeName() { return MakeStringSpan("number"); }
39       // Data specific to this marker type, serialized to JSON for profiler.firefox.com.
40       static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, int a number) {
41         aWriter.IntProperty("number", a number);
42       }
43       // Where and how to display the marker and its data.
44       static MarkerSchema MarkerTypeDisplay() {
45         using MS = MarkerSchema;
46         MS schema(MS::Location::MarkerChart, MS::Location::MarkerTable);
47         schema.SetChartLabel("Number: {marker.data.number}");
48         schema.AddKeyLabelFormat("number", "Number", MS::Format::Number);
49         return schema;
50       }
51     };
54 How to Record Markers
55 ---------------------
57 Header to Include
58 ^^^^^^^^^^^^^^^^^
60 If the compilation unit only defines and records untyped, text, and/or its own markers, include
61 `the main profiler markers header <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkers.h>`_:
63 .. code-block:: c++
65     #include "mozilla/ProfilerMarkers.h"
67 If it also records one of the other common markers defined in
68 `ProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkerTypes.h>`_,
69 include that one instead:
71 .. code-block:: c++
73     #include "mozilla/ProfilerMarkerTypes.h"
75 And if it uses any other profiler functions (e.g., labels), use
76 `the main Gecko Profiler header <https://searchfox.org/mozilla-central/source/tools/profiler/public/GeckoProfiler.h>`_
77 instead:
79 .. code-block:: c++
81     #include "GeckoProfiler.h"
83 The above works from source files that end up in libxul, which is true for the majority
84 of Firefox source code. But some files live outside of libxul, such as mfbt, in which
85 case the advice is the same but the equivalent headers are from the Base Profiler instead:
87 .. code-block:: c++
89     #include "mozilla/BaseProfilerMarkers.h" // Only own/untyped/text markers
90     #include "mozilla/BaseProfilerMarkerTypes.h" // Only common markers
91     #include "BaseProfiler.h" // Markers and other profiler functions
93 Untyped Markers
94 ^^^^^^^^^^^^^^^
96 Untyped markers don't carry any information apart from common marker data:
97 Name, category, options.
99 .. code-block:: c++
101     PROFILER_MARKER_UNTYPED(
102         // Name, and category pair.
103         "Marker Name", OTHER,
104         // Marker options, may be omitted if all defaults are acceptable.
105         MarkerOptions(MarkerStack::Capture(), ...));
107 ``PROFILER_MARKER_UNTYPED`` is a macro that simplifies the use of the main
108 ``profiler_add_marker`` function, by adding the appropriate namespaces, and a surrounding
109 ``#ifdef MOZ_GECKO_PROFILER`` guard.
111 1. Marker name
112     The first argument is the name of this marker. This will be displayed in most places
113     the marker is shown. It can be a literal C string, or any dynamic string object.
114 2. `Category pair name <https://searchfox.org/mozilla-central/source/__GENERATED__/mozglue/baseprofiler/public/ProfilingCategoryList.h>`_
115     Choose a category + subcategory from the `the list of categories <https://searchfox.org/mozilla-central/source/mozglue/baseprofiler/build/profiling_categories.yaml>`_.
116     This is the second parameter of each ``SUBCATEGORY`` line, for instance ``LAYOUT_Reflow``.
117     (Internally, this is really a `MarkerCategory <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerCategory>`_
118     object, in case you need to construct it elsewhere.)
119 3. `MarkerOptions <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerOptions>`_
120     See the options below. It can be omitted if there are no other arguments, ``{}``, or
121     ``MarkerOptions()`` (no specified options); only one of the following option types
122     alone; or ``MarkerOptions(...)`` with one or more of the following options types:
124     * `MarkerThreadId <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerThreadId>`_
125         Rarely used, as it defaults to the current thread. Otherwise it specifies the target
126         "thread id" (aka "track") where the marker should appear; This may be useful when
127         referring to something that happened on another thread (use ``profiler_current_thread_id()``
128         from the original thread to get its id); or for some important markers, they may be
129         sent to the "main thread", which can be specified with ``MarkerThreadId::MainThread()``.
130     * `MarkerTiming <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerTiming>`_
131         This specifies an instant or interval of time. It defaults to the current instant if
132         left unspecified. Otherwise use ``MarkerTiming::InstantAt(timestamp)`` or
133         ``MarkerTiming::Interval(ts1, ts2)``; timestamps are usually captured with
134         ``TimeStamp::Now()``. It is also possible to record only the start or the end of an
135         interval, pairs of start/end markers will be matched by their name. *Note: The
136         upcoming "marker sets" feature will make this pairing more reliable, and also
137         allow more than two markers to be connected*.
138     * `MarkerStack <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerStack>`_
139         By default, markers do not record a "stack" (or "backtrace"). To record a stack at
140         this point, in the most efficient manner, specify ``MarkerStack::Capture()``. To
141         record a previously captured stack, first store a stack into a
142         ``UniquePtr<ProfileChunkedBuffer>`` with ``profiler_capture_backtrace()``, then pass
143         it to the marker with ``MarkerStack::TakeBacktrace(std::move(stack))``.
144     * `MarkerInnerWindowId <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerInnerWindowId>`_
145         If you have access to an "inner window id", consider specifying it as an option, to
146         help profiler.firefox.com to classify them by tab.
148 Text Markers
149 ^^^^^^^^^^^^
151 Text markers are very common, they carry an extra text as a fourth argument, in addition to
152 the marker name. Use the following macro:
154 .. code-block:: c++
156     PROFILER_MARKER_TEXT(
157         // Name, category pair, options.
158         "Marker Name", OTHER, {},
159         // Text string.
160         "Here are some more details."
161     );
163 As useful as it is, using an expensive ``printf`` operation to generate a complex text
164 comes with a variety of issues string. It can leak potentially sensitive information
165 such as URLs can be leaked during the profile sharing step. profiler.firefox.com cannot
166 access the information programmatically. It won't get the formatting benefits of the
167 built-in marker schema. Please consider using a custom marker type to separate and
168 better present the data.
170 Other Typed Markers
171 ^^^^^^^^^^^^^^^^^^^
173 From C++ code, a marker of some type ``YourMarker`` (details about type definition follow) can be
174 recorded like this:
176 .. code-block:: c++
178     PROFILER_MARKER(
179         "YourMarker name", OTHER,
180         MarkerOptions(MarkerTiming::IntervalUntilNowFrom(someStartTimestamp),
181                       MarkerInnerWindowId(innerWindowId))),
182         YourMarker, "some string", 12345, "http://example.com", someTimeStamp);
184 After the first three common arguments (like in ``PROFILER_MARKER_UNTYPED``), there are:
186 4. The marker type, which is the name of the C++ ``struct`` that defines that type.
187 5. A variadic list of type-specific argument. They must match the number of, and must
188    be convertible to, ``StreamJSONMarkerData`` parameters as specified in the marker type definition.
190 "Auto" Scoped Interval Markers
191 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
193 To capture time intervals around some important operations, it is common to store a timestamp, do the work,
194 and then record a marker, e.g.:
196 .. code-block:: c++
198     void DoTimedWork() {
199       TimeStamp start = TimeStamp::Now();
200       DoWork();
201       PROFILER_MARKER_TEXT("Timed work", OTHER, MarkerTiming::IntervalUntilNowFrom(start), "Details");
202     }
204 `RAII <https://en.cppreference.com/w/cpp/language/raii>`_ objects automate this, by recording the time
205 when the object is constructed, and later recording the marker when the object is destroyed at the end
206 of its C++ scope.
207 This is especially useful if there are multiple scope exit points.
209 ``AUTO_PROFILER_MARKER_TEXT`` is `the only one implemented <https://searchfox.org/mozilla-central/search?q=id%3AAUTO_PROFILER_MARKER_TEXT`_ at this time.
211 .. code-block:: c++
213     void MaybeDoTimedWork(bool aDoIt) {
214       AUTO_PROFILER_MARKER_TEXT("Timed work", OTHER, "Details");
215       if (!aDoIt) { /* Marker recorded here... */ return; }
216       DoWork();
217       /* ... or here. */
218     }
220 Note that these RAII objects only record one marker. In some situation, a very long
221 operation could be missed if it hasn't completed by the end of the profiling session.
222 In this case, consider recording two distinct markers, using
223 ``MarkerTiming::IntervalStart()`` and ``MarkerTiming::IntervalEnd()``.
225 Where to Define New Marker Types
226 --------------------------------
228 The first step is to determine the location of the marker type definition:
230 * If this type is only used in one function, or a component, it can be defined in a
231   local common place relative to its use.
232 * For a more common type that could be used from multiple locations:
234   * If there is no dependency on XUL, it can be defined in the Base Profiler, which can
235     be used in most locations in the codebase:
236     `mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h>`__
238   * However, if there is a XUL dependency, then it needs to be defined in the Gecko Profiler:
239     `tools/profiler/public/ProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkerTypes.h>`__
241 .. _how-to-define-new-marker-types:
243 How to Define New Marker Types
244 ------------------------------
246 Each marker type must be defined once and only once.
247 The definition is a C++ ``struct``, its identifier is used when recording
248 markers of that type in C++.
249 By convention, the suffix "Marker" is recommended to better distinguish them
250 from non-profiler entities in the source.
252 .. code-block:: c++
254     struct YourMarker {
256 Marker Type Name
257 ^^^^^^^^^^^^^^^^
259 A marker type must have a unique name, it is used to keep track of the type of
260 markers in the profiler storage, and to identify them uniquely on profiler.firefox.com.
261 (It does not need to be the same as the ``struct``'s name.)
263 This name is defined in a special static member function ``MarkerTypeName``:
265 .. code-block:: c++
267     // …
268       static constexpr Span<const char> MarkerTypeName() {
269         return MakeStringSpan("YourMarker");
270       }
272 Marker Type Data
273 ^^^^^^^^^^^^^^^^
275 All markers of any type have some common data: A name, a category, options like
276 timing, etc. as previously explained.
278 In addition, a certain marker type may carry zero of more arbitrary pieces of
279 information, and they are always the same for all markers of that type.
281 These are defined in a special static member function ``StreamJSONMarkerData``.
283 The first function parameters is always ``SpliceableJSONWriter& aWriter``,
284 it will be used to stream the data as JSON, to later be read by
285 profiler.firefox.com.
287 .. code-block:: c++
289     // …
290       static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
292 The following function parameters is how the data is received as C++ objects
293 from the call sites.
295 * Most C/C++ `POD (Plain Old Data) <https://en.cppreference.com/w/cpp/named_req/PODType>`_
296   and `trivially-copyable <https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable>`_
297   types should work as-is, including ``TimeStamp``.
298 * Character strings should be passed using ``const ProfilerString8View&`` (this handles
299   literal strings, and various ``std::string`` and ``nsCString`` types, and spans with or
300   without null terminator). Use ``const ProfilerString16View&`` for 16-bit strings such as
301   ``nsString``.
302 * Other types can be used if they define specializations for ``ProfileBufferEntryWriter::Serializer``
303   and ``ProfileBufferEntryReader::Deserializer``. You should rarely need to define new
304   ones, but if needed see how existing specializations are written, or contact the
305   `perf-tools team for help <https://chat.mozilla.org/#/room/#profiler:mozilla.org>`_.
307 Passing by value or by reference-to-const is recommended, because arguments are serialized
308 in binary form (i.e., there are no optimizable ``move`` operations).
310 For example, here's how to handle a string, a 64-bit number, another string, and
311 a timestamp:
313 .. code-block:: c++
315     // …
316                                        const ProfilerString8View& aString,
317                                        const int64_t aBytes,
318                                        const ProfilerString8View& aURL,
319                                        const TimeStamp& aTime) {
321 Then the body of the function turns these parameters into a JSON stream.
323 When this function is called, the writer has just started a JSON object, so
324 everything that is written should be a named object property. Use
325 ``SpliceableJSONWriter`` functions, in most cases ``...Property`` functions
326 from its parent class ``JSONWriter``: ``NullProperty``, ``BoolProperty``,
327 ``IntProperty``, ``DoubleProperty``, ``StringProperty``. (Other nested JSON
328 types like arrays or objects are not supported by the profiler.)
330 As a special case, ``TimeStamps`` must be streamed using ``aWriter.TimeProperty(timestamp)``.
332 The property names will be used to identify where each piece of data is stored and
333 how it should be displayed on profiler.firefox.com (see next section).
335 Here's how the above functions parameters could be streamed:
337 .. code-block:: c++
339     // …
340         aWriter.StringProperty("myString", aString);
341         aWriter.IntProperty("myBytes", aBytes);
342         aWriter.StringProperty("myURL", aURL);
343         aWriter.TimeProperty("myTime", aTime);
344       }
346 .. _marker-type-display-schema:
348 Marker Type Display Schema
349 ^^^^^^^^^^^^^^^^^^^^^^^^^^
351 Now that we have defined how to stream type-specific data (from Firefox to
352 profiler.firefox.com), we need to describe where and how this data will be
353 displayed on profiler.firefox.com.
355 The static member function ``MarkerTypeDisplay`` returns an opaque ``MarkerSchema``
356 object, which will be forwarded to profiler.firefox.com.
358 .. code-block:: c++
360     // …
361       static MarkerSchema MarkerTypeDisplay() {
363 The ``MarkerSchema`` type will be used repeatedly, so for convenience we can define
364 a local type alias:
366 .. code-block:: c++
368     // …
369         using MS = MarkerSchema;
371 First, we construct the ``MarkerSchema`` object to be returned at the end.
373 One or more constructor arguments determine where this marker will be displayed in
374 the profiler.firefox.com UI. See the `MarkerSchema::Location enumeration for the
375 full list <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerSchema%3A%3ALocation>`_.
377 Here is the most common set of locations, showing markers of that type in both the
378 Marker Chart and the Marker Table panels:
380 .. code-block:: c++
382     // …
383         MS schema(MS::Location::MarkerChart, MS::Location::MarkerTable);
385 Some labels can optionally be specified, to display certain information in different
386 locations: ``SetChartLabel``, ``SetTooltipLabel``, and ``SetTableLabel``; or
387 ``SetAllLabels`` to define all of them the same way.
389 The arguments is a string that may refer to marker data within braces:
391 * ``{marker.name}``: Marker name.
392 * ``{marker.data.X}``: Type-specific data, as streamed with property name "X" from ``StreamJSONMarkerData`` (e.g., ``aWriter.IntProperty("X", a number);``
394 For example, here's how to set the Marker Chart label to show the marker name and the
395 ``myBytes`` number of bytes:
397 .. code-block:: c++
399     // …
400         schema.SetChartLabel("{marker.name} – {marker.data.myBytes}");
402 profiler.firefox.com will apply the label with the data in a consistent manner. For
403 example, with this label definition, it could display marker information like the
404 following in the Firefox Profiler's Marker Chart:
406  * "Marker Name – 10B"
407  * "Marker Name – 25.204KB"
408  * "Marker Name – 512.54MB"
410 For implementation details on this processing, see `src/profiler-logic/marker-schema.js <https://github.com/firefox-devtools/profiler/blob/main/src/profile-logic/marker-schema.js>`_
411 in the profiler's front-end.
413 Next, define the main display of marker data, which will appear in the Marker
414 Chart tooltips and the Marker Table sidebar.
416 Each row may either be:
418 * A dynamic key-value pair, using one of the ``MarkerSchema::AddKey...`` functions. Each function is given:
420   * Key: Element property name as streamed in ``StreamJSONMarkerData``.
421   * Label: Optional prefix. Defaults to the key name.
422   * Format: How to format the data element value, see `MarkerSchema::Format for details <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerSchema%3A%3AFormat>`_.
423   * Searchable: Optional boolean, indicates if the value is used in searches, defaults to false.
425 * Or a fixed label and value strings, using ``MarkerSchema::AddStaticLabelValue``.
427 .. code-block:: c++
429     // …
430         schema.AddKeyLabelFormatSearchable(
431             "myString", "My String", MS::Format::String, true);
432         schema.AddKeyLabelFormat(
433             "myBytes", "My Bytes", MS::Format::Bytes);
434         schema.AddKeyLabelFormat(
435             "myUrl", "My URL", MS::Format::Url);
436         schema.AddKeyLabelFormat(
437             "myTime", "Event time", MS::Format::Time);
439 Finally the ``schema`` object is returned from the function:
441 .. code-block:: c++
443     // …
444         return schema;
445       }
447 Any other ``struct`` member function is ignored. There could be utility functions used by the above
448 compulsory functions, to make the code clearer.
450 And that is the end of the marker definition ``struct``.
452 .. code-block:: c++
454     // …
455     };
457 Performance Considerations
458 --------------------------
460 During profiling, it is best to reduce the amount of work spent doing profiler
461 operations, as they can influence the performance of the code that you want to profile.
463 Whenever possible, consider passing simple types to marker functions, such that
464 ``StreamJSONMarkerData`` will do the minimum amount of work necessary to serialize
465 the marker type-specific arguments to its internal buffer representation. POD types
466 (numbers) and strings are the easiest and cheapest to serialize. Look at the
467 corresponding ``ProfileBufferEntryWriter::Serializer`` specializations if you
468 want to better understand the work done.
470 Avoid doing expensive operations when recording markers. E.g.: ``printf`` of
471 different things into a string, or complex computations; instead pass the
472 ``printf``/computation arguments straight through to the marker function, so that
473 ``StreamJSONMarkerData`` can do the expensive work at the end of the profiling session.
475 Marker Architecture Description
476 -------------------------------
478 The above sections should give all the information needed for adding your own marker
479 types. However, if you are wanting to work on the marker architecture itself, this
480 section will describe how the system works.
482 TODO:
483  * Briefly describe the buffer and serialization.
484  * Describe the template strategy for generating marker types
485  * Describe the serialization and link to profiler front-end docs on marker processing (if they exist)