DevTools/OperationInstrument: Difference between revisions

m
 
(9 intermediate revisions by 2 users not shown)
Line 17: Line 17:


'''You can contribute to Operation Instrument by adding more instrumentation!'''
'''You can contribute to Operation Instrument by adding more instrumentation!'''
What is a good candidate for tracing? Any single operation that can take more than a millisecond of time is a good rule of thumb. Parsing HTML is a good example because it can easily take tens of milliseconds or even longer on really big HTML snippets. Flattening a JS rope string is not as good an example, because while it may take up a lot of time in aggregate over a period of time, each individual flattening should be much faster than a millisecond.


= Tutorial: Instrumenting New Operations =
= Tutorial: Instrumenting New Operations =
Line 26: Line 28:
== 1. Adding the Instrumentation to Gecko ==
== 1. Adding the Instrumentation to Gecko ==


The easiest way to trace Gecko events/tasks with start and end timeline markers is to use the '''<code>mozilla::AutoTimelineMarker</code>''' RAII class. It automatically adds the start marker on construction, and adds the end marker on destruction. Don't worry too much about potential performance impact! It only actually adds the markers when the given docshell is being recorded.
Depending on the required fine level of control, there's a couple of ways to do this, and more coming soon. Currently, markers may only be added from the main thread, with plans to support them everywhere in the near future (see ''<code>otmt-markers</code>'' [https://bugzilla.mozilla.org/show_bug.cgi?id=1183219 bug 1183219]).
 
===== Using ''<code>mozilla::AutoTimelineMarker</code>'' =====
The easiest way to trace Gecko events/tasks with start and end timeline markers is to use the '''<code>mozilla::AutoTimelineMarker</code>''' RAII class. It automatically adds the start marker on construction, and adds the end marker on destruction. Don't worry too much about potential performance impact! It only actually adds the markers when the given docshell is being observed by a timeline consumer, so essentially nothing will happen if a tool to inspect those markers isn't specifically open.
 
This class may only be used on the main thread, and pointer to a docshell is necessary. If the docshell is a nullptr, nothing happens and this operation fails silently.
 
Example:
 
''<code>AutoTimelineMarker marker(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");</code>''
 
===== Using ''<code>mozilla::AutoGlobalTimelineMarker</code>'' =====
Similar to the previous RAII class, but doesn't expect a specific docshell, and the marker will be visible in all timeline consumers. This is useful for generic operations that don't involve a particular dochsell, or where a docshell isn't accessible. May also only be used on the main thread.
 
Example:
 
''<code>AutoGlobalTimelineMarker marker("Some global operation");</code>''
 
===== Using ''<code>TimelineConsumers</code>'' =====
A few static methods exist on the [https://dxr.mozilla.org/mozilla-central/source/docshell/base/timeline/TimelineConsumers.h?from=TimelineConsumers <code>TimelineConsumers</code>] class, like ''<code>AddMarkerForDocShell</code>'', ''<code>AddMarkerForDocShellsList</code>'', ''<code>AddMarkerForAllObservedDocShells</code>'' that give you fine grained control over creating the markers and what meta-data is attached to them.


--- a/dom/base/nsContentUtils.cpp
Example:
+++ b/dom/base/nsContentUtils.cpp
@@ -4241,16 +4242,18 @@
  nsresult
  nsContentUtils::ParseFragmentHTML(const nsAString& aSourceBuffer,
                                    nsIContent* aTargetNode,
                                    nsIAtom* aContextLocalName,
                                    int32_t aContextNamespace,
                                    bool aQuirks,
                                    bool aPreventScriptExecution)
  {
+  AutoTimelineMarker marker(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
+
    if (nsContentUtils::sFragmentParsingActive) {
      NS_NOTREACHED("Re-entrant fragment parsing attempted.");
      return NS_ERROR_DOM_INVALID_STATE_ERR;
    }
    mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
    nsContentUtils::sFragmentParsingActive = true;
    if (!sHTMLFragmentParser) {
      NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());


Alternatively, you can use [https://dxr.mozilla.org/mozilla-central/search?q=nsDocShell%3A%3AAddProfileTimelineMarker <code>nsDocShell::AddProfilerTimelineMarker</code>] method to manually add start and end marker pairs.
''<code>TimelineConsumers::AddMarkerToDocShellsList(aDocShells, "Some specific operation", aMetaData);</code>''


== 2. Telling the DevTools Frontend About the New Markers ==
== 2. Telling the DevTools Frontend About the New Markers ==


To get your new markers displayed in the performance tool's UI, edit the configuration data in '''<code>browser/devtools/shared/timeline/global.js</code>'''.
To get your new markers displayed in the performance tool's UI, edit the configuration data in '''<code>devtools/client/performance/modules/markers.js</code>'''.


Add a property to the '''<code>TIMELINE_BLUEPRINT</code>''' object. The property key should be the <code>const char *</code> literal that you added in step 1 ("Parse HTML" in our example). The new property should be an object with the following properties:
Add a property to the '''<code>TIMELINE_BLUEPRINT</code>''' object. The property key should be the <code>const char *</code> literal that you added in step 1 ("Parse HTML" in our example). The new property should be an object with the following properties:
Line 90: Line 91:
   timeline.label.domevent=DOM Event
   timeline.label.domevent=DOM Event
   timeline.label.consoleTime=Console
   timeline.label.consoleTime=Console
Any of the people [[DevTools/GetInvolved#Communication|listed here under "Performance"]] are good candidates to review the devtools config changes.


= Adding Custom Metadata to Markers =
= Adding Custom Metadata to Markers =
Line 99: Line 102:
= Recording Markers in a Different Thread =
= Recording Markers in a Different Thread =


For now, it's not supported. Only operations in the main thread are instrumented.
For now, it's not supported. Only operations in the main thread are instrumented. Follow https://bugzilla.mozilla.org/show_bug.cgi?id=1152988 to watch for support for markers from different threads.
 
'''Notes from Tom:'''
 
First, right now when a marker object is created, it acquires the time
from the docshell.  In docshell/base/TimelineMarker.cpp:
 
    TimelineMarker::TimelineMarker(nsDocShell* aDocShell, const char* aName,
                                  TracingMetadata aMetaData)
    [...]
      aDocShell->Now(&mTime);
 
Now, I think nsDocShell::Now is actually thread-safe.  However, I would probably move the logic into TimelineMarker itself and avoid the need to have a docshell at TimelineMarker creation.  The code was written this way mostly for historical reasons, but also perhaps to make sure that the timeline epoch is dealt with in a single place.
 
At this point you could make a new TimelineMarker object on any thread.
 
So then the next step is to ship it to the main thread.  Basically I'd make a new nsIRunnable that holds the TimelineMarker object, and use NS_DispatchToMainThread. This runnable would call nsDocShell::AddProfileTimelineMarker when it ran.
 
This last step is the trickiest -- you have to pick which docshell to notify.  I don't know of a generic way to do this; it's been the trickiest part of the patches I worked on. ('''Note from Paul''': depending on the type of thread, we might register these markers at the global level. For example, it doesn't make sense to attach markers from the compositor thread to a docshell. It's not the case to network threads though).
 
Because we add the start and stop-markers to the docshell separately, it's simple to add a start marker from one thread and an end marker from another thread; or whatever you like.
Confirmed users
125

edits