DevTools/OperationInstrument: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
(What is a good candidate for tracing?)
(→‎Recording Markers in a Different Thread: link bug for different threads)
Line 101: Line 101:
= 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.

Revision as of 22:13, 9 April 2015

Operation Instrument is a project that aims to add tracing instrumentation to Gecko and provide a holistic view of where time is being spent and why.

Examples of traced operations include:

  • Style Recalculation
  • Layout
  • Painting
  • JavaScript run-to-completion
  • HTML parsing
  • Etc...

The traced operations are displayed in the DevTools Performance tool's timeline.

DevTools Performance Tool's Timeline

This is a meta bug for tracking instrumentation: (operation-instrument) Instrument Gecko w/ moar timeline markers

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

The Firefox performance tool breaks down Gecko operations into labeled chunks of time, displayed as a waterfall. Each of these chunks is registered by the platform with a pair of markers. A marker for when the operation starts, and a marker for when the operation ends. To add a new type of marker pair, (1) add the instrumentation at the Gecko platform level, and (2) provide some minimal configuration on the devtools frontend side.

This is a good example bug, to see what adding new instrumentation is like: Bug 1151703 - Add profiler timeline markers for HTML/XML parsing — TL;DR it is pretty easy!

1. Adding the Instrumentation to Gecko

The easiest way to trace Gecko events/tasks with start and end timeline markers is to use the mozilla::AutoTimelineMarker 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.

--- a/dom/base/nsContentUtils.cpp
+++ 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 nsDocShell::AddProfilerTimelineMarker method to manually add start and end marker pairs.

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 browser/devtools/shared/timeline/global.js.

Add a property to the TIMELINE_BLUEPRINT object. The property key should be the const char * literal that you added in step 1 ("Parse HTML" in our example). The new property should be an object with the following properties:

  • group: 0, 1, or 2. Controls which line the markers are rendered on in the trace events summary along the top of the performance tool. When in doubt, just use 0 and ask your reviewer.
  • colorName: The DevTools color that the trace should be rendered as in the UI. Its value should be the string name of a CSS variable from that MDN page, without the leading -- prefix.
  • label: The L10N string name for the marker.
--- a/browser/devtools/shared/timeline/global.js
+++ b/browser/devtools/shared/timeline/global.js
@@ -48,16 +48,26 @@ const TIMELINE_BLUEPRINT = {
     colorName: "highlight-lightorange",
     label: L10N.getStr("timeline.label.javascript2")
   },
+  "Parse HTML": {
+    group: 1,
+    colorName: "highlight-purple",
+    label: L10N.getStr("timeline.label.parseHTML")
+  },
   "ConsoleTime": {
     group: 2,
     colorName: "highlight-bluegrey",

Finally, add a localizable string label for the new marker:

--- a/browser/locales/en-US/chrome/browser/devtools/timeline.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/timeline.properties
@@ -35,16 +35,18 @@ timeline.records=RECORDS
 timeline.label.reflow2=Layout
 timeline.label.paint=Paint
 timeline.label.javascript2=Function Call
+timeline.label.parseHTML=Parse HTML
 timeline.label.domevent=DOM Event
 timeline.label.consoleTime=Console

Adding Custom Metadata to Markers

Under the hood, AutoTimelineMarker is using the TimelineMarker class is defined in docshell/base/TimelineMarker.h. A timestamp and the current JS stack (if any) is recorded when the marker is instantiated. It's also possible to attach additional, arbitrary metadata to a marker by subclassing TimelineMarker.

To display a marker's custom metadata add a rendering function in browser/devtools/timeline/widgets/marker-details.js.

Recording Markers in a Different Thread

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.