Bug 1641000 - Start documenting the Gecko Profiler; r=gerald

This patch represents joint authoriship by Greg Tatum and Gerald Squelart.
This is a work in progress start to documenting the Gecko Profiler. We
collaborated on this on GitHub.

https://github.com/gregtatum/gecko-dev/tree/profiler-docs/tools/profiler/docs

Differential Revision: https://phabricator.services.mozilla.com/D97655
This commit is contained in:
Greg Tatum 2020-11-20 18:55:26 +00:00
parent 005a453c33
commit dbae6744d9
7 changed files with 684 additions and 0 deletions

View File

@ -25,6 +25,7 @@ categories:
- widget/cocoa
- code-quality
- writing-rust-code
- tools/profiler
build_doc:
- mach
- tools/try

View File

@ -66,6 +66,8 @@ SPHINX_TREES["sanitizer"] = "sanitizer/docs"
SPHINX_TREES["code-coverage"] = "code-coverage/docs"
SPHINX_TREES["profiler"] = "profiler/docs"
with Files("tryselect/docs/**"):
SCHEDULES.exclusive = ["docs"]

View File

@ -0,0 +1,69 @@
Buffers and Memory Management
=============================
In a post-Fission world, precise memory management across many threads and processes is
especially important. In order for the profiler to achieve this, it uses a chunked buffer
strategy.
The `ProfileBuffer`_ is the overall buffer class that controls the memory and storage
for the profile, it allows allocating objects into it. This can be used freely
by things like markers and samples to store data as entries, without needing to know
about the general strategy for how the memory is managed.
The `ProfileBuffer`_ is then backed by the `ProfileChunkedBuffer`_. This specialized
buffer grows incrementally, by allocating additional `ProfileBufferChunk`_ objects.
More and more chunks will be allocated until a memory limit is reached, where they will
be released. After releasing, the chunk will either be recycled or freed.
The limiting of memory usage is coordinated by the `ProfilerParent`_ in the parent
process. The `ProfilerParent`_ and `ProfilerChild`_ exchange IPC messages with information
about how much memory is being used. When the maximum byte threshold is passed,
the ProfileChunkManager in the parent process removes the oldest chunk, and then the
`ProfilerParent`_ sends a `DestroyReleasedChunksAtOrBefore`_ message to all of child
processes so that the oldest chunks in the profile are released. This helps long profiles
to keep having data in a similar time frame.
Profile Buffer Terminology
##########################
ProfilerParent
The main profiler machinery is installed in the parent process. It uses IPC to
communicate to the child processes. The PProfiler is the actor which is used
to communicate across processes to coordinate things. See `ProfilerParent.h`_. The
ProfilerParent uses the DestroyReleasedChunksAtOrBefore meessage to control the
overall chunk limit.
ProfilerChild
ProfilerChild is installed in every child process, it will receive requests from
DestroyReleasedChunksAtOrBefore.
Entry
This is an individual entry in the `ProfileBuffer.h`_,. These entry sizes are not
related to the chunks sizes. An individual entry can straddle two different chunks.
An entry can contain various pieces of data, like markers, samples, and stacks.
Chunk
An arbitrary sized chunk of memory, managed by the `ProfileChunkedBuffer`_, and
IPC calls from the ProfilerParent.
Unreleased Chunk
This chunk is currently being used to write entries into.
Released chunk
This chunk is full of data. When memory limits happen, it can either be recycled
or freed.
Recycled chunk
This is a chunk that was previously written into, and full. When memory limits occur,
rather than freeing the memory, it is re-used as the next chunk.
.. _ProfileChunkedBuffer: https://searchfox.org/mozilla-central/search?q=ProfileChunkedBuffer&path=&case=true&regexp=false
.. _ProfileChunkManager: https://searchfox.org/mozilla-central/search?q=ProfileBufferChunkManager.h&path=&case=true&regexp=false
.. _ProfileBufferChunk: https://searchfox.org/mozilla-central/search?q=ProfileBufferChunk&path=&case=true&regexp=false
.. _ProfileBufferChunkManagerWithLocalLimit: https://searchfox.org/mozilla-central/search?q=ProfileBufferChunkManagerWithLocalLimit&case=true&path=
.. _ProfilerParent.h: https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerParent.h
.. _ProfilerChild.h: https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerChild.h
.. _ProfileBuffer.h: https://searchfox.org/mozilla-central/source/tools/profiler/core/ProfileBuffer.h
.. _ProfileBuffer: https://searchfox.org/mozilla-central/search?q=ProfileBuffer&path=&case=true&regexp=false
.. _ProfilerParent: https://searchfox.org/mozilla-central/search?q=ProfilerParent&path=&case=true&regexp=false
.. _DestroyReleasedChunksAtOrBefore: https://searchfox.org/mozilla-central/search?q=DestroyReleasedChunksAtOrBefore&path=&case=true&regexp=false

View File

@ -0,0 +1,36 @@
Gecko Profiler
==============
The Firefox Profiler is the collection of tools used to profile Firefox. This is backed
by the Gecko Profiler, which is the primarily C++ component that instruments Gecko. It
is configurable, and supports a variety of data sources and recording modes. Primarily,
it is used as a stastical profiler, where the execution of threads that have been
registered with the profile is paused, and a sample is taken. Generally, this includes
a stackwalk with combined native stack frame, JavaScript stack frames, and custom stack
frame labels.
In addition to the sampling, the profiler can collect markers, which are collected
deterministically (as opposed to statistically, like samples). These include some
kind of text description, and optionally a payload with more information.
This documentation serves to document the Gecko Profiler and Base Profiler components,
while the profiler.firefox.com interface is documented at `profiler.firefox.com/docs/ <https://profiler.firefox.com/docs/>`_
.. toctree::
:maxdepth: 1
buffer
instrumenting-javascript
markers-guide
memory
The following areas still need documentation:
* LUL
* Instrumenting Java
* Instrumenting Rust
* Registering Threads
* Samples and Stack Walking
* Triggering Gecko Profiles in Automation
* JS Tracer
* Serialization

View File

@ -0,0 +1,55 @@
Instrumenting JavaScript
========================
There are multiple ways to use the profiler with JavaScript. There is the "JavaScript"
profiler feature (via about:profiling), which enables stack walking for JavaScript code.
This is most likely turned on already for every profiler preset.
In addition, markers can be created to specifically marker an instant in time, or a
duration. This can be helpful to make sense of a particular piece of the front-end,
or record events that normally wouldn't show up in samples.
Markers in Browser Chrome
*************************
If you have access to ChromeUtils, then adding a marker is relatively easily.
.. code-block:: javascript
// Add an instant marker, representing a single point in time
ChromeUtils.addProfilerMarker("MarkerName");
// Add a duration marker, representing a span of time.
const startTime = Cu.now();
doWork();
ChromeUtils.addProfilerMarker("MarkerName", startTime);
// Add a duration marker, representing a span of time, with some additional tex
const startTime = Cu.now();
doWork();
ChromeUtils.addProfilerMarker("MarkerName", startTime, "Details about this event");
// Add an instant marker, with some additional tex
const startTime = Cu.now();
doWork();
ChromeUtils.addProfilerMarker("MarkerName", undefined, "Details about this event");
Markers in Content Code
***********************
If instrumenting content code, then the `UserTiming`_ API is the best bet.
:code:`performance.mark` will create an instant marker, and a :code:`performance.measure`
will create a duration marker. These markers will show up under UserTiming in
the profiler UI.
.. code-block:: javascript
// Create an instant marker.
performance.mark("InstantMarkerName");
doWork();
// Measuring with the performance API will also create duration markers.
performance.measure("DurationMarkerName", "InstantMarkerName");
.. _UserTiming: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API

View File

@ -0,0 +1,475 @@
Markers
=======
Markers are packets of arbitrary data that are added to a profile by the Firefox code, usually to
indicate something important happening at a point in time, or during an interval of time.
Each marker has a name, a category, some common optional information (timing, backtrace, etc.),
and an optional payload of a specific type (containing arbitrary data relevant to that type).
Example
-------
Short example, details below.
Note: Most marker-related identifiers are in the ``mozilla`` namespace, to be added where necessary.
.. code-block:: c++
// Record a simple marker with the category of DOM.
PROFILER_MARKER_UNTYPED("Marker Name", DOM);
// Create a marker with some additional text information. (Be wary of printf!)
PROFILER_MARKER_TEXT("Marker Name", JS, MarkerOptions{}, "Additional text information.");
// Record a custom marker of type `ExampleNumberMarker` (see definition below).
PROFILER_MARKER("Number", OTHER, MarkerOptions{}, ExampleNumberMarker, 42);
.. code-block:: c++
// Marker type definition.
struct ExampleNumberMarker {
// Unique marker type name.
static constexpr Span<const char> MarkerTypeName() { return MakeStringSpan("number"); }
// Data specific to this marker type, serialized to JSON for profiler.firefox.com.
static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, int aNumber) {
aWriter.IntProperty("number", aNumber);
}
// Where and how to display the marker and its data.
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema(MS::Location::markerChart, MS::Location::markerTable);
schema.SetChartLabel("Number: {marker.data.number}");
schema.AddKeyLabelFormat("number", "Number", MS::Format::number);
return schema;
}
};
How to Record Markers
---------------------
Header to Include
^^^^^^^^^^^^^^^^^
If the compilation unit only defines and records untyped, text, and/or its own markers, include
`the main profiler markers header <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkers.h>`_:
.. code-block:: c++
#include "mozilla/ProfilerMarkers.h"
If it also records one of the other common markers defined in
`ProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkerTypes.h>`_,
include that one instead:
.. code-block:: c++
#include "mozilla/ProfilerMarkerTypes.h"
And if it uses any other profiler functions (e.g., labels), use
`the main Gecko Profiler header <https://searchfox.org/mozilla-central/source/tools/profiler/public/GeckoProfiler.h>`_
instead:
.. code-block:: c++
#include "GeckoProfiler.h"
The above works from source files that end up in libxul, which is true for the majority
of Firefox source code. But some files live outside of libxul, such as mfbt, in which
case the advice is the same but the equivalent headers are from the Base Profiler instead:
.. code-block:: c++
#include "mozilla/BaseProfilerMarkers.h" // Only own/untyped/text markers
#include "mozilla/BaseProfilerMarkerTypes.h" // Only common markers
#include "BaseProfiler.h" // Markers and other profiler functions
Untyped Markers
^^^^^^^^^^^^^^^
Untyped markers don't carry any information apart from common marker data:
Name, category, options.
.. code-block:: c++
PROFILER_MARKER_UNTYPED(
// Name, and category pair.
"Marker Name", OTHER,
// Marker options, may be omitted if all defaults are acceptable.
MarkerOptions(MarkerStack::Capture(), ...));
``PROFILER_MARKER_UNTYPED`` is a macro that simplifies the use of the main
``profiler_add_marker`` function, by adding the appropriate namespaces, and a surrounding
``#ifdef MOZ_GECKO_PROFILER`` guard.
1. Marker name
The first argument is the name of this marker. This will be displayed in most places
the marker is shown. It can be a literal C string, or any dynamic string object.
2. `Category pair name <https://searchfox.org/mozilla-central/define?q=M_174bb0de187ee7d9>`_
Choose a category + subcategory from the `the list of categories <https://searchfox.org/mozilla-central/define?q=M_174bb0de187ee7d9>`_.
This is the second parameter of each ``SUBCATEGORY`` line, for instance ``LAYOUT_Reflow``.
(Internally, this is really a `MarkerCategory <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerCategory>`_
object, in case you need to construct it elsewhere.)
3. `MarkerOptions <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerOptions>`_
See the options below. It can be omitted if there are no other arguments, ``{}``, or
``MarkerOptions()`` (no specified options); only one of the following option types
alone; or ``MarkerOptions(...)`` with one or more of the following options types:
* `MarkerThreadId <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerThreadId>`_
Rarely used, as it defaults to the current thread. Otherwise it specifies the target
"thread id" (aka "track") where the marker should appear; This may be useful when
referring to something that happened on another thread (use ``profiler_current_thread_id()``
from the original thread to get its id); or for some important markers, they may be
sent to the "main thread", which can be specified with ``MarkerThreadId::MainThread()``.
* `MarkerTiming <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerTiming>`_
This specifies an instant or interval of time. It defaults to the current instant if
left unspecified. Otherwise use ``MarkerTiming::InstantAt(timestamp)`` or
``MarkerTiming::Interval(ts1, ts2)``; timestamps are usually captured with
``TimeStamp::Now()``. It is also possible to record only the start or the end of an
interval, pairs of start/end markers will be matched by their name. *Note: The
upcoming "marker sets" feature will make this pairing more reliable, and also
allow more than two markers to be connected*.
* `MarkerStack <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerStack>`_
By default, markers do not record a "stack" (or "backtrace"). To record a stack at
this point, in the most efficient manner, specify ``MarkerStack::Capture()``. To
record a previously captured stack, first store a stack into a
``UniquePtr<ProfileChunkedBuffer>`` with ``profiler_capture_backtrace()``, then pass
it to the marker with ``MarkerStack::TakeBacktrace(std::move(stack))``.
* `MarkerInnerWindowId <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerInnerWindowId>`_
If you have access to an "inner window id", consider specifying it as an option, to
help profiler.firefox.com to classify them by tab.
Text Markers
^^^^^^^^^^^^
Text markers are very common, they carry an extra text as a fourth argument, in addition to
the marker name. Use the following macro:
.. code-block:: c++
PROFILER_MARKER_TEXT(
// Name, category pair, options.
"Marker Name", OTHER, {},
// Text string.
"Here are some more details."
);
As useful as it is, using an expensive ``printf`` operation to generate a complex text
comes with a variety of issues string. It can leak potentially sensitive information
such as URLs can be leaked during the profile sharing step. profiler.firefox.com cannot
access the information programmatically. It won't get the formatting benefits of the
built-in marker schema. Please consider using a custom marker type to separate and
better present the data.
Other Typed Markers
^^^^^^^^^^^^^^^^^^^
From C++ code, a marker of some type ``YourMarker`` (details about type definition follow) can be
recorded like this:
.. code-block:: c++
PROFILER_MARKER(
"YourMarker name", OTHER,
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(someStartTimestamp),
MarkerInnerWindowId(innerWindowId))),
YourMarker, "some string", 12345, "http://example.com", someTimeStamp);
After the first three common arguments (like in ``PROFILER_MARKER_UNTYPED``), there are:
4. The marker type, which is the name of the C++ ``struct`` that defines that type.
5. A variadic list of type-specific argument. They must match the number of, and must
be convertible to, ``StreamJSONMarkerData`` parameters as specified in the marker type definition.
"Auto" Scoped Interval Markers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To capture time intervals around some important operations, it is common to store a timestamp, do the work,
and then record a marker, e.g.:
.. code-block:: c++
void DoTimedWork() {
TimeStamp start = TimeStamp::Now();
DoWork();
PROFILER_MARKER_TEXT("Timed work", OTHER, MarkerTiming::IntervalUntilNowFrom(start), "Details");
}
`RAII <https://en.cppreference.com/w/cpp/language/raii>`_ objects automate this, by recording the time
when the object is constructed, and later recording the marker when the object is destroyed at the end
of its C++ scope.
This is especially useful if there are multiple scope exit points.
``AUTO_PROFILER_MARKER_TEXT`` is `the only one implemented <https://searchfox.org/mozilla-central/define?q=M_ac7b392646edf5a5>`_ at this time.
.. code-block:: c++
void MaybeDoTimedWork(bool aDoIt) {
AUTO_PROFILER_MARKER_TEXT("Timed work", OTHER, "Details");
if (!aDoIt) { /* Marker recorded here... */ return; }
DoWork();
/* ... or here. */
}
Note that these RAII objects only record one marker. In some situation, a very long
operation could be missed if it hasn't completed by the end of the profiling session.
In this case, consider recording two distinct markers, using
``MarkerTiming::IntervalStart()`` and ``MarkerTiming::IntervalEnd()``.
Where to Define New Marker Types
--------------------------------
The first step is to determine the location of the marker type definition:
* If this type is only used in one function, or a component, it can be defined in a
local common place relative to its use.
* For a more common type that could be used from multiple locations:
* If there is no dependency on XUL, it can be defined in the Base Profiler, which can
be used in most locations in the codebase:
`mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h>`__
* However, if there is a XUL dependency, then it needs to be defined in the Gecko Profiler:
`tools/profiler/public/ProfilerMarkerTypes.h <https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerMarkerTypes.h>`__
How to Define New Marker Types
------------------------------
Each marker type must be defined once and only once.
The definition is a C++ ``struct``, its name is used when recording markers of
that type in C++:
.. code-block:: c++
struct YourMarker {
Marker Type Name
^^^^^^^^^^^^^^^^
A marker type must have a unique name, it is used to keep track of the type of
markers in the profiler storage, and to identify them uniquely on profiler.firefox.com.
(It does not need to be the same as the ``struct``'s name.)
This name is defined in a special static member function ``MarkerTypeName``:
.. code-block:: c++
// …
static constexpr Span<const char> MarkerTypeName() {
return MakeStringSpan("YourMarker");
}
Marker Type Data
^^^^^^^^^^^^^^^^
All markers of any type have some common data: A name, a category, options like
timing, etc. as previously explained.
In addition, a certain marker type may carry zero of more arbitrary pieces of
information, and they are always the same for all markers of that type.
These are defined in a special static member function ``StreamJSONMarkerData``.
The first function parameters is always ``SpliceableJSONWriter& aWriter``,
it will be used to stream the data as JSON, to later be read by
profiler.firefox.com.
.. code-block:: c++
// …
static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
The following function parameters is how the data is received as C++ objects
from the call sites.
* Most C/C++ `POD (Plain Old Data) <https://en.cppreference.com/w/cpp/named_req/PODType>`_
and `trivially-copyable <https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable>`_
types should work as-is, including ``TimeStamp``.
* Character strings should be passed using ``const ProfilerString8View&`` (this handles
literal strings, and various ``std::string`` and ``nsCString`` types, and spans with or
without null terminator). Use ``const ProfilerString16View&`` for 16-bit strings such as
``nsString``.
* Other types can be used if they define specializations for ``ProfileBufferEntryWriter::Serializer``
and ``ProfileBufferEntryReader::Deserializer``. You should rarely need to define new
ones, but if needed see how existing specializations are written, or contact the
`perf-tools team for help <https://chat.mozilla.org/#/room/#profiler:mozilla.org>`_.
Passing by value or by reference-to-const is recommended, because arguments are serialized
in binary form (i.e., there are no optimizable ``move`` operations).
For example, here's how to handle a string, a 64-bit number, another string, and
a timestamp:
.. code-block:: c++
// …
const ProfilerString8View& aString,
const int64_t aBytes,
const ProfilerString8View& aURL,
const TimeStamp& aTime) {
Then the body of the function turns these parameters into a JSON stream.
When this function is called, the writer has just started a JSON object, so
everything that is written should be a named object property. Use
``SpliceableJSONWriter`` functions, in most cases ``...Property`` functions
from its parent class ``JSONWriter``: ``NullProperty``, ``BoolProperty``,
``IntProperty``, ``DoubleProperty``, ``StringProperty``. (Other nested JSON
types like arrays or objects are not supported by the profiler.)
As a special case, ``TimeStamps`` must be streamed using ``aWriter.TimeProperty(timestamp)``.
The property names will be used to identify where each piece of data is stored and
how it should be displayed on profiler.firefox.com (see next section).
Here's how the above functions parameters could be streamed:
.. code-block:: c++
// …
aWriter.StringProperty("myString", aString);
aWriter.IntProperty("myBytes", aBytes);
aWriter.StringProperty("myURL", aURL);
aWriter.TimeProperty("myTime", aTime);
}
Marker Type Display Schema
^^^^^^^^^^^^^^^^^^^^^^^^^^
Now that we have defined how to stream type-specific data (from Firefox to
profiler.firefox.com), we need to describe where and how this data will be
displayed on profiler.firefox.com.
The static member function ``MarkerTypeDisplay`` returns an opaque ``MarkerSchema``
object, which will be forwarded to profiler.firefox.com.
.. code-block:: c++
// …
static MarkerSchema MarkerTypeDisplay() {
The ``MarkerSchema`` type will be used repeatedly, so for convenience we can define
a local type alias:
.. code-block:: c++
// …
using MS = MarkerSchema;
First, we construct the ``MarkerSchema`` object to be returned at the end.
One or more constructor arguments determine where this marker will be displayed in
the profiler.firefox.com UI. See the `MarkerSchema::Location enumeration for the
full list <https://searchfox.org/mozilla-central/define?q=T_mozilla%3A%3AMarkerSchema%3A%3ALocation>`_.
Here is the most common set of locations, showing markers of that type in both the
Marker Chart and the Marker Table panels:
.. code-block:: c++
// …
MS schema(MS::Location::markerChart, MS::Location::markerTable);
Some labels can optionally be specified, to display certain information in different
locations: ``SetChartLabel``, ``SetTooltipLabel``, and ``SetTableLabel``; or
``SetAllLabels`` to define all of them the same way.
The arguments is a string that may refer to marker data within braces:
* ``{marker.name}``: Marker name.
* ``{marker.data.X}``: Type-specific data, as streamed with property name "X" from ``StreamJSONMarkerData`` (e.g., ``aWriter.IntProperty("X", aNumber);``
For example, here's how to set the Marker Chart label to show the marker name and the
``myBytes`` number of bytes:
.. code-block:: c++
// …
schema.SetChartLabel("{marker.name} {marker.data.myBytes}");
profiler.firefox.com will apply the label with the data in a consistent manner. For
example, with this label definition, it could display marker information like the
following in the Firefox Profiler's Marker Chart:
* "Marker Name 10B"
* "Marker Name 25.204KB"
* "Marker Name 512.54MB"
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>`_
in the profiler's front-end.
Next, define the main display of marker data, which will appear in the Marker
Chart tooltips and the Marker Table sidebar.
Each row may either be:
* A dynamic key-value pair, using one of the ``MarkerSchema::AddKey...`` functions. Each function is given:
* Key: Element property name as streamed in ``StreamJSONMarkerData``.
* Label: Optional prefix. Defaults to the key name.
* 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>`_.
* Searchable: Optional boolean, indicates if the value is used in searches, defaults to false.
* Or a fixed label and value strings, using ``MarkerSchema::AddStaticLabelValue``.
.. code-block:: c++
// …
schema.AddKeyLabelFormatSearchable(
"myString", "My String", MS::Format::string, true);
schema.AddKeyLabelFormat(
"myBytes", "My Bytes", MS::Format::bytes);
schema.AddKeyLabelFormat(
"myUrl", "My URL", MS::Format::url);
schema.AddKeyLabelFormat(
"myTime", "Event time", MS::Format::time);
Finally the ``schema`` object is returned from the function:
.. code-block:: c++
// …
return schema;
}
Any other ``struct`` member function is ignored. There could be utility functions used by the above
compulsory functions, to make the code clearer.
And that is the end of the marker definition ``struct``.
.. code-block:: c++
// …
};
Performance Considerations
--------------------------
During profiling, it is best to reduce the amount of work spent doing profiler
operations, as they can influence the performance of the code that you want to profile.
Whenever possible, consider passing simple types to marker functions, such that
``StreamJSONMarkerData`` will do the minimum amount of work necessary to serialize
the marker type-specific arguments to its internal buffer representation. POD types
(numbers) and strings are the easiest and cheapest to serialize. Look at the
corresponding ``ProfileBufferEntryWriter::Serializer`` specializations if you
want to better understand the work done.
Avoid doing expensive operations when recording markers. E.g.: ``printf`` of
different things into a string, or complex computations; instead pass the
``printf``/computation arguments straight through to the marker function, so that
``StreamJSONMarkerData`` can do the expensive work at the end of the profiling session.
Marker Architecture Description
-------------------------------
The above sections should give all the information needed for adding your own marker
types. However, if you are wanting to work on the marker architecture itself, this
section will describe how the system works.
TODO:
* Briefly describe the buffer and serialization.
* Describe the template strategy for generating marker types
* Describe the serialization and link to profiler front-end docs on marker processing (if they exist)

View File

@ -0,0 +1,46 @@
Profiling Memory
================
Sampling stacks from native allocations
---------------------------------------
The profiler can sample allocations and de-allocations from malloc using the
"Native Allocations" feature. This can be enabled by going to `about:profiling` and
enabling the "Native Allocations" checkbox. It is only available in Nightly, as it
uses a technique of hooking into malloc that could be a little more risky to apply to
the broader population of Firefox users.
This implementation is located in: `tools/profiler/core/memory_hooks.cpp
<https://searchfox.org/mozilla-central/source/tools/profiler/core/memory_hooks.cpp>`_
It works by hooking into all of the malloc calls. When the profiler is running, it
performs a `Bernoulli trial`_, that will pass for a given probability of per-byte
allocated. What this means is that larger allocations have a higher chance of being
recorded compared to smaller allocations. Currently, there is no way to configure
the per-byte probability. This means that sampled allocation sizes will be closer
to the actual allocated bytes.
This infrastructure is quite similar to DMD, but with the additional motiviations of
making it easy to turn on and use with the profiler. The overhead is quite high,
especially on systems with more expensive stack walking, like Linux. Turning off
thee "Native Stacks" feature can help lower overhead, but will give less information.
For more information on analyzing these profiles, see the `Firefox Profiler docs`_.
Memory counters
---------------
Similar to the Native Allocations feature, memory counters use the malloc memory hook
that is only available in Nightly. When it's available, the memory counters are always
turned on. This is a lightweight way to count in a very granular fashion how much
memory is being allocated and deallocated during the profiling session.
This information is then visualized in the `Firefox Profiler memory track`_.
This feature uses the `Profiler Counters`_, which can be used to create other types
of cheap counting instrumentation.
.. _Bernoulli trial: https://en.wikipedia.org/wiki/Bernoulli_trial
.. _Firefox Profiler docs: https://profiler.firefox.com/docs/#/./memory-allocations
.. _Firefox Profiler memory track: https://profiler.firefox.com/docs/#/./memory-allocations?id=memory-track
.. _Profiler Counters: https://searchfox.org/mozilla-central/source/tools/profiler/public/ProfilerCounts.h