Backed out 5 changesets (bug 1672493) for causing frequent wpt failures at RemoteWork...
[gecko.git] / tools / profiler / docs / markers-guide.rst
blobf252c7fbc5e143f0c735a296bca8cacca09b4ff2
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, Rust or JVM, please take a look at their documentation
13     in :doc:`instrumenting-javascript`, :doc:`instrumenting-rust` or
14     :doc:`instrumenting-android` respectively.
16 Example
17 -------
19 Short example, details below.
21 Note: Most marker-related identifiers are in the ``mozilla`` namespace, to be added where necessary.
23 .. code-block:: cpp
25     // Record a simple marker with the category of DOM.
26     PROFILER_MARKER_UNTYPED("Marker Name", DOM);
28     // Create a marker with some additional text information. (Be wary of printf!)
29     PROFILER_MARKER_TEXT("Marker Name", JS, MarkerOptions{}, "Additional text information.");
31     // Record a custom marker of type `ExampleNumberMarker` (see definition below).
32     PROFILER_MARKER("Number", OTHER, MarkerOptions{}, ExampleNumberMarker, 42);
34 .. code-block:: cpp
36     // Marker type definition.
37     struct ExampleNumberMarker : public BaseMarkerType<ExampleNumberMarker> {
38       // Unique marker type name.
39       static constexpr const char* Name = "number";
40       // Marker description.
41       static constexpr const char* Description = "This is a number marker.";
43       // For convenience.
44       using MS = MarkerSchema;
45       // Fields of payload for the marker.
46       static constexpr MS::PayloadField PayloadFields[] = {
47           {"number", MS::InputType::Uint32t, "Number", MS::Format::Integer}};
49       // Locations this marker should be displayed.
50       static constexpr MS::Location Locations[] = {MS::Location::MarkerChart,
51                                                    MS::Location::MarkerTable};
52       // Location specific label for this marker.
53       static constexpr const char* ChartLabel = "Number: {marker.data.number}";
55       // Data specific to this marker type, as passed to PROFILER_MARKER/profiler_add_marker.
56       static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, uint32_t a number) {
57         // Custom writer for marker fields, or using the default parent
58         // implementation if the function arguments match the schema.
59         StreamJSONMarkerDataImpl(aWriter, a number);
60       }
61     };
63 When adding a marker whose arguments differ from the schema, a translator
64 function and a custom implementation of StreamJSONMarkerData can be used.
66 .. code-block:: c++
68     // Marker type definition.
69     struct ExampleBooleanMarker : public BaseMarkerType<ExampleBooleanMarker> {
70       // Unique marker type name.
71       static constexpr const char* Name = "boolean";
72       // Marker description.
73       static constexpr const char* Description = "This is a boolean marker.";
75       // For convenience.
76       using MS = MarkerSchema;
77       // Fields of payload for the marker.
78       static constexpr MS::PayloadField PayloadFields[] = {
79           {"boolean", MS::InputType::CString, "Boolean"}};
81       // Locations this marker should be displayed.
82       static constexpr MS::Location Locations[] = {MS::Location::MarkerChart,
83                                                    MS::Location::MarkerTable};
84       // Location specific label for this marker.
85       static constexpr const char* ChartLabel = "Boolean: {marker.data.boolean}";
87       // Data specific to this marker type, as passed to PROFILER_MARKER/profiler_add_marker.
88       static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, bool aBoolean) {
89         // Note the schema expects a string, we cannot use the default implementation.
90         if (aBoolean) {
91           aWriter.StringProperty("boolean", "true");
92         } else {
93           aWriter.StringProperty("boolean", "false");
94         }
95       }
97       // The translation to the schema must also be defined in a translator function.
98       // The argument list should match that to PROFILER_MARKER/profiler_add_marker.
99       static void TranslateMarkerInputToSchema(void* aContext, bool aBoolean) {
100         // This should call ETW::OutputMarkerSchema with an argument list matching the schema.
101         if (aIsStart) {
102           ETW::OutputMarkerSchema(aContext, ExampleBooleanMarker{}, ProfilerStringView("true"));
103         } else {
104           ETW::OutputMarkerSchema(aContext, ExampleBooleanMarker{}, ProfilerStringView("false"));
105         }
106       }
107     };
109 A more detailed description is offered below.
112 How to Record Markers
113 ---------------------
115 Header to Include
116 ^^^^^^^^^^^^^^^^^
118 If the compilation unit only defines and records untyped, text, and/or its own markers, include
119 `the main profiler markers header <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkers.h>`_:
121 .. code-block:: cpp
123     #include "mozilla/ProfilerMarkers.h"
125 If it also records one of the other common markers defined in
126 `ProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkerTypes.h>`_,
127 include that one instead:
129 .. code-block:: cpp
131     #include "mozilla/ProfilerMarkerTypes.h"
133 And if it uses any other profiler functions (e.g., labels), use
134 `the main Gecko Profiler header <https://searchfox.org/mozilla-central/source/tools/profiler/public/GeckoProfiler.h>`_
135 instead:
137 .. code-block:: cpp
139     #include "GeckoProfiler.h"
141 The above works from source files that end up in libxul, which is true for the majority
142 of Firefox source code. But some files live outside of libxul, such as mfbt, in which
143 case the advice is the same but the equivalent headers are from the Base Profiler instead:
145 .. code-block:: cpp
147     #include "mozilla/BaseProfilerMarkers.h" // Only own/untyped/text markers
148     #include "mozilla/BaseProfilerMarkerTypes.h" // Only common markers
149     #include "BaseProfiler.h" // Markers and other profiler functions
151 Untyped Markers
152 ^^^^^^^^^^^^^^^
154 Untyped markers don't carry any information apart from common marker data:
155 Name, category, options.
157 .. code-block:: cpp
159     PROFILER_MARKER_UNTYPED(
160         // Name, and category pair.
161         "Marker Name", OTHER,
162         // Marker options, may be omitted if all defaults are acceptable.
163         MarkerOptions(MarkerStack::Capture(), ...));
165 ``PROFILER_MARKER_UNTYPED`` is a macro that simplifies the use of the main
166 ``profiler_add_marker`` function, by adding the appropriate namespaces, and a surrounding
167 ``#ifdef MOZ_GECKO_PROFILER`` guard.
169 1. Marker name
170     The first argument is the name of this marker. This will be displayed in most places
171     the marker is shown. It can be a literal C string, or any dynamic string object.
172 2. `Category pair name <https://searchfox.org/mozilla-central/source/__GENERATED__/mozglue/baseprofiler/public/ProfilingCategoryList.h>`_
173     Choose a category + subcategory from the `the list of categories <https://searchfox.org/mozilla-central/source/mozglue/baseprofiler/build/profiling_categories.yaml>`_.
174     This is the second parameter of each ``SUBCATEGORY`` line, for instance ``LAYOUT_Reflow``.
175     (Internally, this is really a `MarkerCategory <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerCategory>`_
176     object, in case you need to construct it elsewhere.)
177 3. `MarkerOptions <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerOptions>`_
178     See the options below. It can be omitted if there are no other arguments, ``{}``, or
179     ``MarkerOptions()`` (no specified options); only one of the following option types
180     alone; or ``MarkerOptions(...)`` with one or more of the following options types:
182     * `MarkerThreadId <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerThreadId>`_
183         Rarely used, as it defaults to the current thread. Otherwise it specifies the target
184         "thread id" (aka "track") where the marker should appear; This may be useful when
185         referring to something that happened on another thread (use ``profiler_current_thread_id()``
186         from the original thread to get its id); or for some important markers, they may be
187         sent to the "main thread", which can be specified with ``MarkerThreadId::MainThread()``.
188     * `MarkerTiming <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerTiming>`_
189         This specifies an instant or interval of time. It defaults to the current instant if
190         left unspecified. Otherwise use ``MarkerTiming::InstantAt(timestamp)`` or
191         ``MarkerTiming::Interval(ts1, ts2)``; timestamps are usually captured with
192         ``TimeStamp::Now()``. It is also possible to record only the start or the end of an
193         interval, pairs of start/end markers will be matched by their name. *Note: The
194         upcoming "marker sets" feature will make this pairing more reliable, and also
195         allow more than two markers to be connected*.
196     * `MarkerStack <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerStack>`_
197         By default, markers do not record a "stack" (or "backtrace"). To record a stack at
198         this point, in the most efficient manner, specify ``MarkerStack::Capture()``. To
199         record a previously captured stack, first store a stack into a
200         ``UniquePtr<ProfileChunkedBuffer>`` with ``profiler_capture_backtrace()``, then pass
201         it to the marker with ``MarkerStack::TakeBacktrace(std::move(stack))``.
202     * `MarkerInnerWindowId <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerInnerWindowId>`_
203         If you have access to an "inner window id", consider specifying it as an option, to
204         help profiler.firefox.com to classify them by tab.
206 "Auto" Scoped Interval Markers
207 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
209 To capture time intervals around some important operations, it is common to store a timestamp, do the work,
210 and then record a marker, e.g.:
212 .. code-block:: cpp
214     void DoTimedWork() {
215       TimeStamp start = TimeStamp::Now();
216       DoWork();
217       PROFILER_MARKER_TEXT("Timed work", OTHER, MarkerTiming::IntervalUntilNowFrom(start), "Details");
218     }
220 `RAII <https://en.cppreference.com/w/cpp/language/raii>`_ objects automate this, by recording the time
221 when the object is constructed, and later recording the marker when the object is destroyed at the end
222 of its C++ scope.
223 This is especially useful if there are multiple scope exit points.
225 ``AUTO_PROFILER_MARKER_TEXT`` is `the only one implemented <https://searchfox.org/mozilla-central/search?q=id%3AAUTO_PROFILER_MARKER_TEXT>`_ at this time.
227 .. code-block:: cpp
229     void MaybeDoTimedWork(bool aDoIt) {
230       AUTO_PROFILER_MARKER_TEXT("Timed work", OTHER, "Details");
231       if (!aDoIt) { /* Marker recorded here... */ return; }
232       DoWork();
233       /* ... or here. */
234     }
236 Note that these RAII objects only record one marker. In some situation, a very long
237 operation could be missed if it hasn't completed by the end of the profiling session.
238 In this case, consider recording two distinct markers, using
239 ``MarkerTiming::IntervalStart()`` and ``MarkerTiming::IntervalEnd()``.
241 Text Markers
242 ^^^^^^^^^^^^
244 Text markers are very common, they carry an extra text as a fourth argument, in addition to
245 the marker name. Use the following macro:
247 .. code-block:: cpp
249     PROFILER_MARKER_TEXT(
250         // Name, category pair, options.
251         "Marker Name", OTHER, {},
252         // Text string.
253         "Here are some more details."
254     );
256 As useful as it is, using an expensive ``printf`` operation to generate a complex text
257 comes with a variety of issues string. It can leak potentially sensitive information
258 such as URLs can be leaked during the profile sharing step. profiler.firefox.com cannot
259 access the information programmatically. It won't get the formatting benefits of the
260 built-in marker schema. Please consider using a custom marker type to separate and
261 better present the data.
263 Other Typed Markers
264 ^^^^^^^^^^^^^^^^^^^
266 From C++ code, a marker of some type ``YourMarker`` (details about type definition follow) can be
267 recorded like this:
269 .. code-block:: cpp
271     PROFILER_MARKER(
272         "YourMarker name", OTHER,
273         MarkerOptions(MarkerTiming::IntervalUntilNowFrom(someStartTimestamp),
274                       MarkerInnerWindowId(innerWindowId))),
275         YourMarker, "some string", 12345, "http://example.com", someTimeStamp);
277 After the first three common arguments (like in ``PROFILER_MARKER_UNTYPED``), there are:
279 4. The marker type, which is the name of the C++ ``struct`` that defines that type.
280 5. A variadic list of type-specific argument. They must match the number of, and must
281    be convertible to the types defined in the schema. If they are not, they must match
282    the number of and be convertible to the types in ``StreamJSONMarkerData`` and
283    ``TranslateMarkerInputToSchema``.
285 Where to Define New Marker Types
286 --------------------------------
288 The first step is to determine the location of the marker type definition:
290 * If this type is only used in one function, or a component, it can be defined in a
291   local common place relative to its use.
292 * For a more common type that could be used from multiple locations:
294   * If there is no dependency on XUL, it can be defined in the Base Profiler, which can
295     be used in most locations in the codebase:
296     `mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h>`__
298   * However, if there is a XUL dependency, then it needs to be defined in the Gecko Profiler:
299     `tools/profiler/public/ProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkerTypes.h>`__
301 .. _how-to-define-new-marker-types:
303 How to Define New Marker Types
304 ------------------------------
306 Each marker type must be defined once and only once.
307 The definition is a C++ ``struct``, that inherits from ``BaseMarkerType``, its identifier is used when recording
308 markers of that type in C++.
309 By convention, the suffix "Marker" is recommended to better distinguish them
310 from non-profiler entities in the source.
312 .. code-block:: cpp
314     struct YourMarker : BaseMarkerType<YourMarker> {
316 Marker Type Name & Description
317 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
319 A marker type must have a unique name, it is used to keep track of the type of
320 markers in the profiler storage, and to identify them uniquely on profiler.firefox.com.
321 (It does not need to be the same as the ``struct``'s name.)
323 This type name is defined in a special static data member ``Name``:
325 .. code-block:: cpp
327     // …
328       static constexpr const char* Name = "YourMarker";
330 In addition you must add a description of your marker in a special static data member ``Description``:
332 .. code-block:: cpp
334     // …
335       static constexpr const char* Description = "This is my marker!";
337 If you expect users to be passing unique names for individual instances of the marker,
338 you may want to add the following to ensure those names get stored when using ETW:
340 .. code-block:: cpp
342     // …
343       static constexpr bool StoreName = true;
345 Marker Type Data
346 ^^^^^^^^^^^^^^^^
348 All markers of any type have some common data: A name, a category, options like
349 timing, etc. as previously explained.
351 In addition, a certain marker type may carry zero of more arbitrary pieces of
352 information, and they are always the same for all markers of that type.
354 These are defined in a special static member data array of ``PayloadField`` s.
355 Each payload field specifies a key, a C++ type description, a label, a format,
356 and optionally some additional options (see the ``PayloadField`` type). The
357 most important fields are:
359 * Key: Element property name as streamed in ``StreamJSONMarkerData``.
360 * Type: An enum value describing the C++ type specified to PROFILER_MARKER/profiler_add_marker.
361 * Label: Prefix to display to label the field.
362 * 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>`_.
364 .. code-block:: cpp
366     // …
367       // This will be used repeatedly and is done for convenience.
368       using MS = MarkerSchema;
369       static constexpr MS::PayloadField PayloadFields[] = {
370           {"number", MS::InputType::Uint32t, "Number", MS::Format::Integer}};
372 In addition, a ``StreamJSONMarkerData`` function must be defined that matches
373 the C++ argument types to PROFILER_MARKER.
375 The first function parameters is always ``SpliceableJSONWriter& aWriter``,
376 it will be used to stream the data as JSON, to later be read by
377 profiler.firefox.com.
379 .. code-block:: cpp
381     // …
382       static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
384 The following function parameters is how the data is received as C++ objects
385 from the call sites.
387 * Most C/C++ `POD (Plain Old Data) <https://en.cppreference.com/w/cpp/named_req/PODType>`_
388   and `trivially-copyable <https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable>`_
389   types should work as-is, including ``TimeStamp``.
390 * Character strings should be passed using ``const ProfilerString8View&`` (this handles
391   literal strings, and various ``std::string`` and ``nsCString`` types, and spans with or
392   without null terminator). Use ``const ProfilerString16View&`` for 16-bit strings such as
393   ``nsString``.
394 * Other types can be used if they define specializations for ``ProfileBufferEntryWriter::Serializer``
395   and ``ProfileBufferEntryReader::Deserializer``. You should rarely need to define new
396   ones, but if needed see how existing specializations are written, or contact the
397   `perf-tools team for help <https://chat.mozilla.org/#/room/#profiler:mozilla.org>`_.
399 Passing by value or by reference-to-const is recommended, because arguments are serialized
400 in binary form (i.e., there are no optimizable ``move`` operations).
402 For example, here's how to handle a string, a 64-bit number, another string, and
403 a timestamp:
405 .. code-block:: cpp
407     // …
408                                        const ProfilerString8View& aString,
409                                        const int64_t aBytes,
410                                        const ProfilerString8View& aURL,
411                                        const TimeStamp& aTime) {
413 Then the body of the function turns these parameters into a JSON stream.
415 If these parameter types match the types specified in the schema, both in order
416 and number. It can simply call the default implementation.
418 .. code-block:: cpp
420     // …
421       static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
422                                        const ProfilerString8View& aString,
423                                        const int64_t aBytes,
424                                        const ProfilerString8View& aURL,
425                                        const TimeStamp& aTime) {
426         StreamJSONMarkerDataImpl(aWrite, aString, aBytes, aURL, aTime);
427       }
430 If the parameters passed to PROFILER_MARKER do not match the schema, some
431 additional work is required.
433 When this function is called, the writer has just started a JSON object, so
434 everything that is written should be a named object property. Use
435 ``SpliceableJSONWriter`` functions, in most cases ``...Property`` functions
436 from its parent class ``JSONWriter``: ``NullProperty``, ``BoolProperty``,
437 ``IntProperty``, ``DoubleProperty``, ``StringProperty``. (Other nested JSON
438 types like arrays or objects are not supported by the profiler.)
440 As a special case, ``TimeStamps`` must be streamed using ``aWriter.TimeProperty(timestamp)``.
442 The property names will be used to identify where each piece of data is stored and
443 how it should be displayed on profiler.firefox.com (see next section).
445 Suppose our marker schema defines a string for a boolean, here is how that could be streamed.
447 .. code-block:: cpp
449     // …
451       static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
452                                        bool aBoolean) {
453         aWriter.StringProperty("myBoolean", aBoolean ? "true" : "false");
454       }
456 In addition, a ``TranslateMarkerInputToSchema`` function must be added to
457 ensure correct output to ETW.
459 .. code-block:: c++
461       // The translation to the schema must also be defined in a translator function.
462       // The argument list should match that to PROFILER_MARKER/profiler_add_marker.
463       static void TranslateMarkerInputToSchema(void* aContext, bool aBoolean) {
464         // This should call ETW::OutputMarkerSchema with an argument list matching the schema.
465         if (aIsStart) {
466           ETW::OutputMarkerSchema(aContext, YourMarker{}, ProfilerStringView("true"));
467         } else {
468           ETW::OutputMarkerSchema(aContext, YourMarker{}, ProfilerStringView("false"));
469         }
470       }
472 .. _marker-type-display-schema:
474 Marker Type Display Schema
475 ^^^^^^^^^^^^^^^^^^^^^^^^^^
477 Now that we have defined how to stream type-specific data (from Firefox to
478 profiler.firefox.com), we need to describe where and how this data will be
479 displayed on profiler.firefox.com.
481 The location data member determines where this marker will be displayed in
482 the profiler.firefox.com UI. See the `MarkerSchema::Location enumeration for the
483 full list <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerSchema%3A%3ALocation>`_.
485 Here is the most common set of locations, showing markers of that type in both the
486 Marker Chart and the Marker Table panels:
488 .. code-block:: cpp
490     // …
491       static constexpr MS::Location Locations[] = {MS::Location::MarkerChart,
492                                                    MS::Location::MarkerTable};
494 Some labels can optionally be specified, to display certain information in different
495 locations: ``ChartLabel``, ``TooltipLabel``, and ``TableLabel``; or ``AllLabels`` to
496 define all of them the same way.
498 The arguments is a string that may refer to marker data within braces:
500 * ``{marker.name}``: Marker name.
501 * ``{marker.data.X}``: Type-specific data, as streamed with property name "X" from ``StreamJSONMarkerData`` (e.g., ``aWriter.IntProperty("X", a number);``
503 For example, here's how to set the Marker Chart label to show the marker name and the
504 ``myBytes`` number of bytes:
506 .. code-block:: cpp
508     // …
509         static constexpr const char* ChartLabel = "{marker.name} – {marker.data.myBytes}";
511 profiler.firefox.com will apply the label with the data in a consistent manner. For
512 example, with this label definition, it could display marker information like the
513 following in the Firefox Profiler's Marker Chart:
515  * "Marker Name – 10B"
516  * "Marker Name – 25.204KB"
517  * "Marker Name – 512.54MB"
519 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>`_
520 in the profiler's front-end.
522 Any other ``struct`` member function is ignored. There could be utility functions used by the above
523 compulsory functions, to make the code clearer.
525 And that is the end of the marker definition ``struct``.
527 .. code-block:: cpp
529     // …
530     };
532 Performance Considerations
533 --------------------------
535 During profiling, it is best to reduce the amount of work spent doing profiler
536 operations, as they can influence the performance of the code that you want to profile.
538 Whenever possible, consider passing simple types to marker functions, such that
539 ``StreamJSONMarkerData`` will do the minimum amount of work necessary to serialize
540 the marker type-specific arguments to its internal buffer representation. POD types
541 (numbers) and strings are the easiest and cheapest to serialize. Look at the
542 corresponding ``ProfileBufferEntryWriter::Serializer`` specializations if you
543 want to better understand the work done.
545 Avoid doing expensive operations when recording markers. E.g.: ``printf`` of
546 different things into a string, or complex computations; instead pass the
547 ``printf``/computation arguments straight through to the marker function, so that
548 ``StreamJSONMarkerData`` can do the expensive work at the end of the profiling session.
550 Marker Architecture Description
551 -------------------------------
553 The above sections should give all the information needed for adding your own marker
554 types. However, if you are wanting to work on the marker architecture itself, this
555 section will describe how the system works.
557 TODO:
558  * Briefly describe the buffer and serialization.
559  * Describe the template strategy for generating marker types
560  * Describe the serialization and link to profiler front-end docs on marker processing (if they exist)