Bug 1390092 - Measure various performance times inside devtools r?jlast draft
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Mon, 17 Jul 2017 14:03:08 +0100
changeset 652134 5364a069007b2c2f41ece8d94c83181a6892ca91
parent 652133 3014bbe1a2b500ceea6faa614d48329658621bdf
child 727982 eaa2721b5336baa45167f252bd13757585e919e6
push id75942
push userbmo:mratcliffe@mozilla.com
push dateThu, 24 Aug 2017 12:30:42 +0000
reviewersjlast
bugs1390092
milestone57.0a1
Bug 1390092 - Measure various performance times inside devtools r?jlast Don't forget, this is not only for React... startMeasure(mark) and stopMeasure(mark) are used to measure some tool opening times and more will be added in the future (and possibly added to DAMP). Focus on perfService.js... the rest is pretty much adding the mixin getter. MozReview-Commit-ID: GJFECv8aQxP
devtools/client/aboutdebugging/components/aboutdebugging.js
devtools/client/aboutdebugging/components/addons/controls.js
devtools/client/aboutdebugging/components/addons/install-error.js
devtools/client/aboutdebugging/components/addons/panel.js
devtools/client/aboutdebugging/components/addons/target.js
devtools/client/aboutdebugging/components/panel-header.js
devtools/client/aboutdebugging/components/panel-menu-entry.js
devtools/client/aboutdebugging/components/panel-menu.js
devtools/client/aboutdebugging/components/tabs/panel.js
devtools/client/aboutdebugging/components/tabs/target.js
devtools/client/aboutdebugging/components/target-list.js
devtools/client/aboutdebugging/components/workers/multi-e10s-warning.js
devtools/client/aboutdebugging/components/workers/panel.js
devtools/client/aboutdebugging/components/workers/service-worker-target.js
devtools/client/aboutdebugging/components/workers/target.js
devtools/client/dom/content/components/dom-tree.js
devtools/client/dom/content/components/main-frame.js
devtools/client/dom/content/components/main-toolbar.js
devtools/client/framework/components/toolbox-controller.js
devtools/client/framework/components/toolbox-tab.js
devtools/client/framework/components/toolbox-tabs.js
devtools/client/framework/components/toolbox-toolbar.js
devtools/client/framework/toolbox.js
devtools/client/inspector/boxmodel/components/BoxModel.js
devtools/client/inspector/boxmodel/components/BoxModelApp.js
devtools/client/inspector/boxmodel/components/BoxModelEditable.js
devtools/client/inspector/boxmodel/components/BoxModelInfo.js
devtools/client/inspector/boxmodel/components/BoxModelMain.js
devtools/client/inspector/boxmodel/components/BoxModelProperties.js
devtools/client/inspector/boxmodel/components/ComputedProperty.js
devtools/client/inspector/breadcrumbs.js
devtools/client/inspector/components/inspector-tab-panel.js
devtools/client/inspector/fonts/components/App.js
devtools/client/inspector/fonts/components/Font.js
devtools/client/inspector/fonts/components/FontList.js
devtools/client/inspector/grids/components/Grid.js
devtools/client/inspector/grids/components/GridDisplaySettings.js
devtools/client/inspector/grids/components/GridItem.js
devtools/client/inspector/grids/components/GridList.js
devtools/client/inspector/grids/components/GridOutline.js
devtools/client/inspector/inspector.js
devtools/client/inspector/layout/components/Accordion.js
devtools/client/inspector/layout/components/App.js
devtools/client/inspector/layout/components/LayoutPromoteBar.js
devtools/client/inspector/rules/rules.js
devtools/client/jsonview/components/headers-panel.js
devtools/client/jsonview/components/headers.js
devtools/client/jsonview/components/json-panel.js
devtools/client/jsonview/components/main-tabbed-area.js
devtools/client/jsonview/components/reps/toolbar.js
devtools/client/jsonview/components/search-box.js
devtools/client/jsonview/components/text-panel.js
devtools/client/memory/app.js
devtools/client/memory/components/census-header.js
devtools/client/memory/components/census-tree-item.js
devtools/client/memory/components/census.js
devtools/client/memory/components/dominator-tree-header.js
devtools/client/memory/components/dominator-tree-item.js
devtools/client/memory/components/dominator-tree.js
devtools/client/memory/components/heap.js
devtools/client/memory/components/individuals-header.js
devtools/client/memory/components/individuals.js
devtools/client/memory/components/list.js
devtools/client/memory/components/shortest-paths.js
devtools/client/memory/components/snapshot-list-item.js
devtools/client/memory/components/toolbar.js
devtools/client/memory/components/tree-map.js
devtools/client/netmonitor/src/components/headers-panel.js
devtools/client/netmonitor/src/components/monitor-panel.js
devtools/client/netmonitor/src/components/properties-view.js
devtools/client/netmonitor/src/components/request-list-column-cause.js
devtools/client/netmonitor/src/components/request-list-column-content-size.js
devtools/client/netmonitor/src/components/request-list-column-cookies.js
devtools/client/netmonitor/src/components/request-list-column-domain.js
devtools/client/netmonitor/src/components/request-list-column-duration.js
devtools/client/netmonitor/src/components/request-list-column-end-time.js
devtools/client/netmonitor/src/components/request-list-column-file.js
devtools/client/netmonitor/src/components/request-list-column-latency.js
devtools/client/netmonitor/src/components/request-list-column-method.js
devtools/client/netmonitor/src/components/request-list-column-protocol.js
devtools/client/netmonitor/src/components/request-list-column-remote-ip.js
devtools/client/netmonitor/src/components/request-list-column-response-header.js
devtools/client/netmonitor/src/components/request-list-column-response-time.js
devtools/client/netmonitor/src/components/request-list-column-scheme.js
devtools/client/netmonitor/src/components/request-list-column-set-cookies.js
devtools/client/netmonitor/src/components/request-list-column-start-time.js
devtools/client/netmonitor/src/components/request-list-column-status.js
devtools/client/netmonitor/src/components/request-list-column-transferred-size.js
devtools/client/netmonitor/src/components/request-list-column-type.js
devtools/client/netmonitor/src/components/request-list-column-waterfall.js
devtools/client/netmonitor/src/components/request-list-content.js
devtools/client/netmonitor/src/components/request-list-empty-notice.js
devtools/client/netmonitor/src/components/request-list-header.js
devtools/client/netmonitor/src/components/request-list-item.js
devtools/client/netmonitor/src/components/response-panel.js
devtools/client/netmonitor/src/components/source-editor.js
devtools/client/netmonitor/src/components/statistics-panel.js
devtools/client/netmonitor/src/components/toolbar.js
devtools/client/performance/components/jit-optimizations-item.js
devtools/client/performance/components/jit-optimizations.js
devtools/client/performance/components/waterfall-tree.js
devtools/client/responsive.html/app.js
devtools/client/responsive.html/components/browser.js
devtools/client/responsive.html/components/device-adder.js
devtools/client/responsive.html/components/device-modal.js
devtools/client/responsive.html/components/device-selector.js
devtools/client/responsive.html/components/dpr-selector.js
devtools/client/responsive.html/components/global-toolbar.js
devtools/client/responsive.html/components/network-throttling-selector.js
devtools/client/responsive.html/components/resizable-viewport.js
devtools/client/responsive.html/components/viewport-dimension.js
devtools/client/responsive.html/components/viewport-toolbar.js
devtools/client/responsive.html/components/viewport.js
devtools/client/responsive.html/components/viewports.js
devtools/client/shared/components/autocomplete-popup.js
devtools/client/shared/components/frame.js
devtools/client/shared/components/h-split-box.js
devtools/client/shared/components/notification-box.js
devtools/client/shared/components/reps/reps.js
devtools/client/shared/components/search-box.js
devtools/client/shared/components/sidebar-toggle.js
devtools/client/shared/components/splitter/draggable.js
devtools/client/shared/components/splitter/split-box.js
devtools/client/shared/components/stack-trace.js
devtools/client/shared/components/tabs/tabbar.js
devtools/client/shared/components/tabs/tabs.js
devtools/client/shared/components/tree.js
devtools/client/shared/components/tree/label-cell.js
devtools/client/shared/components/tree/tree-cell.js
devtools/client/shared/components/tree/tree-header.js
devtools/client/shared/components/tree/tree-row.js
devtools/client/shared/components/tree/tree-view.js
devtools/client/shared/vendor/react-dev.js
devtools/client/webconsole/net/components/cookies-tab.js
devtools/client/webconsole/net/components/headers-tab.js
devtools/client/webconsole/net/components/net-info-body.js
devtools/client/webconsole/net/components/net-info-group-list.js
devtools/client/webconsole/net/components/net-info-group.js
devtools/client/webconsole/net/components/net-info-params.js
devtools/client/webconsole/net/components/params-tab.js
devtools/client/webconsole/net/components/post-tab.js
devtools/client/webconsole/net/components/response-tab.js
devtools/client/webconsole/net/components/size-limit.js
devtools/client/webconsole/net/components/spinner.js
devtools/client/webconsole/net/components/stacktrace-tab.js
devtools/client/webconsole/new-console-output/components/console-output.js
devtools/client/webconsole/new-console-output/components/console-table.js
devtools/client/webconsole/new-console-output/components/filter-bar.js
devtools/client/webconsole/new-console-output/components/message-container.js
devtools/client/webconsole/new-console-output/components/message.js
devtools/shared/moz.build
devtools/shared/perfService.js
modules/libpref/init/all.js
npm-shrinkwrap.json
--- a/devtools/client/aboutdebugging/components/aboutdebugging.js
+++ b/devtools/client/aboutdebugging/components/aboutdebugging.js
@@ -18,16 +18,18 @@ loader.lazyGetter(this, "TabsPanel",
   () => createFactory(require("./tabs/panel")));
 loader.lazyGetter(this, "WorkersPanel",
   () => createFactory(require("./workers/panel")));
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "Telemetry",
   "devtools/client/shared/telemetry");
+loader.lazyRequireGetter(this, "perfService",
+  "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const panels = [{
   id: "addons",
   name: Strings.GetStringFromName("addons"),
   icon: "chrome://devtools/skin/images/debugging-addons.svg",
@@ -44,16 +46,18 @@ const panels = [{
   component: WorkersPanel
 }];
 
 const defaultPanelId = "addons";
 
 module.exports = createClass({
   displayName: "AboutDebuggingApp",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     telemetry: PropTypes.instanceOf(Telemetry).isRequired
   },
 
   getInitialState() {
     return {
       selectedPanelId: defaultPanelId
--- a/devtools/client/aboutdebugging/components/addons/controls.js
+++ b/devtools/client/aboutdebugging/components/addons/controls.js
@@ -9,27 +9,32 @@
 
 loader.lazyImporter(this, "AddonManager",
   "resource://gre/modules/AddonManager.jsm");
 
 const { Cc, Ci } = require("chrome");
 const { createFactory, createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const Services = require("Services");
+
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const AddonsInstallError = createFactory(require("./install-error"));
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
                       "/about:debugging#Enabling_add-on_debugging";
 
 module.exports = createClass({
   displayName: "AddonsControls",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     debugDisabled: PropTypes.bool
   },
 
   getInitialState() {
     return {
       installError: null,
     };
--- a/devtools/client/aboutdebugging/components/addons/install-error.js
+++ b/devtools/client/aboutdebugging/components/addons/install-error.js
@@ -4,22 +4,26 @@
 
 /* eslint-env browser */
 "use strict";
 
 const { createClass, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
 
 const Services = require("Services");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 module.exports = createClass({
   displayName: "AddonsInstallError",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     error: PropTypes.string,
     retryInstall: PropTypes.func,
   },
 
   render() {
     if (!this.props.error) {
       return null;
--- a/devtools/client/aboutdebugging/components/addons/panel.js
+++ b/devtools/client/aboutdebugging/components/addons/panel.js
@@ -12,29 +12,33 @@ const Services = require("Services");
 
 const AddonsControls = createFactory(require("./controls"));
 const AddonTarget = createFactory(require("./target"));
 const PanelHeader = createFactory(require("../panel-header"));
 const TargetList = createFactory(require("../target-list"));
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "perfService",
+  "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 const CHROME_ENABLED_PREF = "devtools.chrome.enabled";
 const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
 const WEB_EXT_URL = "https://developer.mozilla.org/Add-ons" +
                     "/WebExtensions/Getting_started_with_web-ext";
 
 module.exports = createClass({
   displayName: "AddonsPanel",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     id: PropTypes.string.isRequired
   },
 
   getInitialState() {
     return {
       extensions: [],
--- a/devtools/client/aboutdebugging/components/addons/target.js
+++ b/devtools/client/aboutdebugging/components/addons/target.js
@@ -12,16 +12,18 @@ const { debugAddon, isTemporaryID, parse
   require("../../modules/addon");
 const Services = require("Services");
 
 loader.lazyImporter(this, "BrowserToolboxProcess",
   "resource://devtools/client/framework/ToolboxProcess.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "perfService",
+  "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const TEMP_ID_URL = "https://developer.mozilla.org/Add-ons" +
                     "/WebExtensions/WebExtensions_and_the_Add-on_ID";
 
 function filePathForTarget(target) {
@@ -120,16 +122,18 @@ function warningMessages(warnings = []) 
       { className: "addon-target-warning-message addon-target-message" },
       warning);
   });
 }
 
 module.exports = createClass({
   displayName: "AddonTarget",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     debugDisabled: PropTypes.bool,
     target: PropTypes.shape({
       addonActor: PropTypes.string.isRequired,
       addonID: PropTypes.string.isRequired,
       icon: PropTypes.string,
       name: PropTypes.string.isRequired,
--- a/devtools/client/aboutdebugging/components/panel-header.js
+++ b/devtools/client/aboutdebugging/components/panel-header.js
@@ -2,19 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "PanelHeader",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     id: PropTypes.string.isRequired,
     name: PropTypes.string.isRequired
   },
 
   render() {
     let { name, id } = this.props;
 
--- a/devtools/client/aboutdebugging/components/panel-menu-entry.js
+++ b/devtools/client/aboutdebugging/components/panel-menu-entry.js
@@ -2,19 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "PanelMenuEntry",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     icon: PropTypes.string.isRequired,
     id: PropTypes.string.isRequired,
     name: PropTypes.string.isRequired,
     selected: PropTypes.bool,
     selectPanel: PropTypes.func.isRequired
   },
 
--- a/devtools/client/aboutdebugging/components/panel-menu.js
+++ b/devtools/client/aboutdebugging/components/panel-menu.js
@@ -3,19 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const PanelMenuEntry = createFactory(require("./panel-menu-entry"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "PanelMenu",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     panels: PropTypes.arrayOf(PropTypes.shape({
       id: PropTypes.string.isRequired,
       name: PropTypes.string.isRequired,
       icon: PropTypes.string.isRequired,
       component: PropTypes.func.isRequired
     })).isRequired,
     selectPanel: PropTypes.func.isRequired,
--- a/devtools/client/aboutdebugging/components/tabs/panel.js
+++ b/devtools/client/aboutdebugging/components/tabs/panel.js
@@ -9,25 +9,27 @@
 const { createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
 const PanelHeader = createFactory(require("../panel-header"));
 const TargetList = createFactory(require("../target-list"));
 const TabTarget = createFactory(require("./target"));
 
-loader.lazyRequireGetter(this, "DebuggerClient",
-  "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 module.exports = createClass({
   displayName: "TabsPanel",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     id: PropTypes.string.isRequired
   },
 
   getInitialState() {
     return {
       tabs: []
--- a/devtools/client/aboutdebugging/components/tabs/target.js
+++ b/devtools/client/aboutdebugging/components/tabs/target.js
@@ -5,22 +5,26 @@
 /* eslint-env browser */
 
 "use strict";
 
 const { createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 module.exports = createClass({
   displayName: "TabTarget",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     target: PropTypes.shape({
       icon: PropTypes.string,
       outerWindowID: PropTypes.number.isRequired,
       title: PropTypes.string,
       url: PropTypes.string.isRequired
     }).isRequired
   },
--- a/devtools/client/aboutdebugging/components/target-list.js
+++ b/devtools/client/aboutdebugging/components/target-list.js
@@ -3,29 +3,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
-loader.lazyRequireGetter(this, "DebuggerClient",
-  "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const LocaleCompare = (a, b) => {
   return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
 };
 
 module.exports = createClass({
   displayName: "TargetList",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     debugDisabled: PropTypes.bool,
     error: PropTypes.node,
     id: PropTypes.string.isRequired,
     name: PropTypes.string,
     sort: PropTypes.bool,
     targetClass: PropTypes.func.isRequired,
--- a/devtools/client/aboutdebugging/components/workers/multi-e10s-warning.js
+++ b/devtools/client/aboutdebugging/components/workers/multi-e10s-warning.js
@@ -13,23 +13,27 @@ const { createClass, DOM: dom } =
 const Services = require("Services");
 const { Ci } = require("chrome");
 
 loader.lazyImporter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "perfService",
+  "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle("chrome://devtools/locale/aboutdebugging.properties");
 const MULTI_OPT_OUT_PREF = "dom.ipc.multiOptOut";
 
 module.exports = createClass({
   displayName: "multiE10SWarning",
 
+  mixins: perfService.mixins,
+
   onUpdatePreferenceClick() {
     let message = Strings.GetStringFromName("multiProcessWarningConfirmUpdate2");
     if (window.confirm(message)) {
       // Disable multi until at least the next experiment.
       Services.prefs.setIntPref(MULTI_OPT_OUT_PREF,
                                 Services.appinfo.E10S_MULTI_EXPERIMENT);
       // Restart the browser.
       Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
--- a/devtools/client/aboutdebugging/components/workers/panel.js
+++ b/devtools/client/aboutdebugging/components/workers/panel.js
@@ -19,29 +19,33 @@ const WorkerTarget = createFactory(requi
 const MultiE10SWarning = createFactory(require("./multi-e10s-warning"));
 const ServiceWorkerTarget = createFactory(require("./service-worker-target"));
 
 loader.lazyImporter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "perfService",
+  "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
 const MORE_INFO_URL = "https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging" +
                       "#Service_workers_not_compatible";
 const PROCESS_COUNT_PREF = "dom.ipc.processCount";
 const MULTI_OPTOUT_PREF = "dom.ipc.multiOptOut";
 
 module.exports = createClass({
   displayName: "WorkersPanel",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     id: PropTypes.string.isRequired
   },
 
   getInitialState() {
     return {
       workers: {
--- a/devtools/client/aboutdebugging/components/workers/service-worker-target.js
+++ b/devtools/client/aboutdebugging/components/workers/service-worker-target.js
@@ -6,25 +6,27 @@
 
 "use strict";
 
 const { createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { debugWorker } = require("../../modules/worker");
 const Services = require("Services");
 
-loader.lazyRequireGetter(this, "DebuggerClient",
-  "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 module.exports = createClass({
   displayName: "ServiceWorkerTarget",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     debugDisabled: PropTypes.bool,
     target: PropTypes.shape({
       active: PropTypes.bool,
       fetch: PropTypes.bool.isRequired,
       icon: PropTypes.string,
       name: PropTypes.string.isRequired,
--- a/devtools/client/aboutdebugging/components/workers/target.js
+++ b/devtools/client/aboutdebugging/components/workers/target.js
@@ -6,25 +6,27 @@
 
 "use strict";
 
 const { createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { debugWorker } = require("../../modules/worker");
 const Services = require("Services");
 
-loader.lazyRequireGetter(this, "DebuggerClient",
-  "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 module.exports = createClass({
   displayName: "WorkerTarget",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     debugDisabled: PropTypes.bool,
     target: PropTypes.shape({
       icon: PropTypes.string,
       name: PropTypes.string.isRequired,
       workerActor: PropTypes.string
     }).isRequired
--- a/devtools/client/dom/content/components/dom-tree.js
+++ b/devtools/client/dom/content/components/dom-tree.js
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // React & Redux
 const React = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const TreeView = React.createFactory(require("devtools/client/shared/components/tree/tree-view"));
 
 // Reps
 const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const { Rep } = REPS;
 const Grip = REPS.Grip;
 
 // DOM Panel
@@ -24,16 +26,18 @@ const { DomDecorator } = require("../dom
 const PropTypes = React.PropTypes;
 
 /**
  * Renders DOM panel tree.
  */
 var DomTree = React.createClass({
   displayName: "DomTree",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     object: PropTypes.any,
     filter: PropTypes.string,
     dispatch: PropTypes.func.isRequired,
     grips: PropTypes.object,
   },
 
   /**
--- a/devtools/client/dom/content/components/main-frame.js
+++ b/devtools/client/dom/content/components/main-frame.js
@@ -4,31 +4,35 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // React & Redux
 const React = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // DOM Panel
 const DomTree = React.createFactory(require("./dom-tree"));
 const MainToolbar = React.createFactory(require("./main-toolbar"));
 
 // Shortcuts
 const { div } = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * Renders basic layout of the DOM panel. The DOM panel cotent consists
  * from two main parts: toolbar and tree.
  */
 var MainFrame = React.createClass({
   displayName: "MainFrame",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     object: PropTypes.any,
     filter: PropTypes.string,
     dispatch: PropTypes.func.isRequired,
   },
 
   /**
    * Render DOM panel content
--- a/devtools/client/dom/content/components/main-toolbar.js
+++ b/devtools/client/dom/content/components/main-toolbar.js
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // React
 const React = require("devtools/client/shared/vendor/react");
 const { l10n } = require("../utils");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Reps
 const { createFactories } = require("devtools/client/shared/react-utils");
 const { Toolbar, ToolbarButton } = createFactories(require("devtools/client/jsonview/components/reps/toolbar"));
 
 // DOM Panel
 const SearchBox = React.createFactory(require("devtools/client/shared/components/search-box"));
 
 // Actions
@@ -25,16 +27,18 @@ const PropTypes = React.PropTypes;
 
 /**
  * This template is responsible for rendering a toolbar
  * within the 'Headers' panel.
  */
 var MainToolbar = React.createClass({
   displayName: "MainToolbar",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     object: PropTypes.any.isRequired,
     dispatch: PropTypes.func.isRequired,
   },
 
   onRefresh: function () {
     this.props.dispatch(fetchProperties(this.props.object));
   },
--- a/devtools/client/framework/components/toolbox-controller.js
+++ b/devtools/client/framework/components/toolbox-controller.js
@@ -2,25 +2,29 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {createClass, createFactory} = require("devtools/client/shared/vendor/react");
 const ToolboxToolbar = createFactory(require("devtools/client/framework/components/toolbox-toolbar"));
 const ELEMENT_PICKER_ID = "command-button-pick";
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * This component serves as a state controller for the toolbox React component. It's a
  * thin layer for translating events and state of the outside world into the React update
  * cycle. This solution was used to keep the amount of code changes to a minimimum while
  * adapting the existing codebase to start using React.
  */
 module.exports = createClass({
   displayName: "ToolboxController",
 
+  mixins: perfService.mixins,
+
   getInitialState() {
     // See the ToolboxToolbar propTypes for documentation on each of these items in state,
     // and for the defintions of the props that are expected to be passed in.
     return {
       focusedButton: ELEMENT_PICKER_ID,
       currentToolId: null,
       canRender: false,
       highlightedTool: "",
--- a/devtools/client/framework/components/toolbox-tab.js
+++ b/devtools/client/framework/components/toolbox-tab.js
@@ -1,19 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const {DOM, createClass} = require("devtools/client/shared/vendor/react");
 const {img, button, span} = DOM;
 
 module.exports = createClass({
   displayName: "ToolboxTab",
 
+  mixins: perfService.mixins,
+
   renderIcon(definition, isHighlighted) {
     const {icon, highlightedicon} = definition;
     if (!icon) {
       return [];
     }
     return [
       img({
         className: "default-icon",
--- a/devtools/client/framework/components/toolbox-tabs.js
+++ b/devtools/client/framework/components/toolbox-tabs.js
@@ -3,23 +3,27 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {DOM, createClass, createFactory, PropTypes} = require("devtools/client/shared/vendor/react");
 
 const {findDOMNode} = require("devtools/client/shared/vendor/react-dom");
 const {button, div} = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));
 
 module.exports = createClass({
   displayName: "ToolboxTabs",
 
+  mixins: perfService.mixins,
+
   // See toolbox-toolbar propTypes for details on the props used here.
   propTypes: {
     currentToolId: PropTypes.string,
     focusButton: PropTypes.func,
     focusedButton: PropTypes.string,
     highlightedTool: PropTypes.string,
     panelDefinitions: PropTypes.array,
     selectTool: PropTypes.func,
--- a/devtools/client/framework/components/toolbox-toolbar.js
+++ b/devtools/client/framework/components/toolbox-toolbar.js
@@ -1,28 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {DOM, createClass, createFactory, PropTypes} = require("devtools/client/shared/vendor/react");
 const {div, button} = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));
 const ToolboxTabs = createFactory(require("devtools/client/framework/components/toolbox-tabs"));
 
 /**
  * This is the overall component for the toolbox toolbar. It is designed to not know how
  * the state is being managed, and attempts to be as pure as possible. The
  * ToolboxController component controls the changing state, and passes in everything as
  * props.
  */
 module.exports = createClass({
   displayName: "ToolboxToolbar",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // The currently focused item (for arrow keyboard navigation)
     // This ID determines the tabindex being 0 or -1.
     focusedButton: PropTypes.string,
     // List of command button definitions.
     toolboxButtons: PropTypes.array,
     // The id of the currently selected tool, e.g. "inspector"
     currentToolId: PropTypes.string,
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -61,16 +61,18 @@ loader.lazyRequireGetter(this, "settleAl
 loader.lazyRequireGetter(this, "ToolboxButtons",
   "devtools/client/definitions", true);
 loader.lazyRequireGetter(this, "SourceMapURLService",
   "devtools/client/framework/source-map-url-service", true);
 loader.lazyRequireGetter(this, "HUDService",
   "devtools/client/webconsole/hudservice", true);
 loader.lazyRequireGetter(this, "viewSource",
   "devtools/client/shared/view-source");
+loader.lazyRequireGetter(this, "perfService",
+  "devtools/shared/perfService", true);
 
 loader.lazyGetter(this, "domNodeConstants", () => {
   return require("devtools/shared/dom-node-constants");
 });
 
 loader.lazyGetter(this, "registerHarOverlay", () => {
   return require("devtools/client/netmonitor/src/har/toolbox-overlay").register;
 });
@@ -92,16 +94,18 @@ loader.lazyGetter(this, "registerHarOver
  *        A unique identifier to differentiate toolbox documents from the
  *        chrome codebase when passing DOM messages
  */
 function Toolbox(target, selectedTool, hostType, contentWindow, frameId) {
   this._target = target;
   this._win = contentWindow;
   this.frameId = frameId;
 
+  perfService.startMeasure("toolbox-open");
+
   this._toolPanels = new Map();
   this._telemetry = new Telemetry();
 
   this._initInspector = null;
   this._inspector = null;
 
   // Map of frames (id => frame-info) and currently selected frame id.
   this.frameMap = new Map();
@@ -519,16 +523,18 @@ Toolbox.prototype = {
       // so we don't have to explicitly wait for this in tests; ideally, all tests
       // will handle this on their own, but each have their own tear down function.
       if (flags.testing) {
         yield performanceFrontConnection;
       }
 
       this.emit("ready");
       this._isOpenDeferred.resolve();
+
+      perfService.stopMeasure("toolbox-open");
     }.bind(this)).catch(console.error.bind(console));
   },
 
   /**
    * loading React modules when needed (to avoid performance penalties
    * during Firefox start up time).
    */
   get React() {
@@ -2540,16 +2546,18 @@ Toolbox.prototype = {
           this.emit("destroyed");
 
           // Free _host after the call to destroyed in order to let a chance
           // to destroyed listeners to still query toolbox attributes
           this._host = null;
           this._win = null;
           this._toolPanels.clear();
 
+          perfService.dumpMeasures();
+
           // Force GC to prevent long GC pauses when running tests and to free up
           // memory in general when the toolbox is closed.
           if (flags.testing) {
             win.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIDOMWindowUtils)
               .garbageCollect();
           }
         }).catch(console.error));
--- a/devtools/client/inspector/boxmodel/components/BoxModel.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModel.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const BoxModelInfo = createFactory(require("./BoxModelInfo"));
 const BoxModelMain = createFactory(require("./BoxModelMain"));
 const BoxModelProperties = createFactory(require("./BoxModelProperties"));
 
 const Types = require("../types");
 
 module.exports = createClass({
 
@@ -23,17 +25,17 @@ module.exports = createClass({
     showBoxModelProperties: PropTypes.bool.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelEditor: PropTypes.func.isRequired,
     onShowBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
     onToggleGeometryEditor: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   onKeyDown(event) {
     let { target } = event;
 
     if (target == this.boxModelContainer) {
       this.boxModelMain.onKeyDown(event);
     }
   },
--- a/devtools/client/inspector/boxmodel/components/BoxModelApp.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelApp.js
@@ -12,16 +12,18 @@ const { connect } = require("devtools/cl
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const Accordion =
   createFactory(require("devtools/client/inspector/layout/components/Accordion"));
 const BoxModel = createFactory(require("./BoxModel"));
 
 const Types = require("../types");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
 const BOXMODEL_OPENED_PREF = "devtools.computed.boxmodel.opened";
 
 const BoxModelApp = createClass({
 
   displayName: "BoxModelApp",
@@ -32,17 +34,17 @@ const BoxModelApp = createClass({
     showBoxModelProperties: PropTypes.bool.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelEditor: PropTypes.func.isRequired,
     onShowBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
     onToggleGeometryEditor: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   render() {
     return Accordion({
       items: [
         {
           header: BOXMODEL_L10N.getStr("boxmodel.title"),
           component: BoxModel,
           componentProps: this.props,
--- a/devtools/client/inspector/boxmodel/components/BoxModelEditable.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelEditable.js
@@ -3,33 +3,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { addons, createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { editableItem } = require("devtools/client/shared/inplace-editor");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const LONG_TEXT_ROTATE_LIMIT = 3;
 
 module.exports = createClass({
 
   displayName: "BoxModelEditable",
 
   propTypes: {
     box: PropTypes.string.isRequired,
     direction: PropTypes.string,
     focusable: PropTypes.bool.isRequired,
     level: PropTypes.string,
     property: PropTypes.string.isRequired,
     textContent: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
     onShowBoxModelEditor: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   componentDidMount() {
     let { property, onShowBoxModelEditor } = this.props;
 
     editableItem({
       element: this.boxModelEditable,
     }, (element, event) => {
       onShowBoxModelEditor(element, event, property);
--- a/devtools/client/inspector/boxmodel/components/BoxModelInfo.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelInfo.js
@@ -3,16 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const { addons, createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const Types = require("../types");
 
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
 const SHARED_STRINGS_URI = "devtools/client/locales/shared.properties";
 const SHARED_L10N = new LocalizationHelper(SHARED_STRINGS_URI);
 
@@ -20,17 +22,17 @@ module.exports = createClass({
 
   displayName: "BoxModelInfo",
 
   propTypes: {
     boxModel: PropTypes.shape(Types.boxModel).isRequired,
     onToggleGeometryEditor: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   onToggleGeometryEditor(e) {
     this.props.onToggleGeometryEditor();
   },
 
   render() {
     let { boxModel } = this.props;
     let { geometryEditorEnabled, layout } = boxModel;
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js
@@ -10,16 +10,18 @@ const { findDOMNode } = require("devtool
 const { KeyCodes } = require("devtools/client/shared/keycodes");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const BoxModelEditable = createFactory(require("./BoxModelEditable"));
 
 const Types = require("../types");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
 const SHARED_STRINGS_URI = "devtools/client/locales/shared.properties";
 const SHARED_L10N = new LocalizationHelper(SHARED_STRINGS_URI);
 
 module.exports = createClass({
 
@@ -28,17 +30,17 @@ module.exports = createClass({
   propTypes: {
     boxModel: PropTypes.shape(Types.boxModel).isRequired,
     boxModelContainer: PropTypes.object,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelEditor: PropTypes.func.isRequired,
     onShowBoxModelHighlighter: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   getInitialState() {
     return {
       activeDescendant: null,
       focusable: false,
     };
   },
 
--- a/devtools/client/inspector/boxmodel/components/BoxModelProperties.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelProperties.js
@@ -8,31 +8,33 @@ const { addons, createClass, createFacto
   require("devtools/client/shared/vendor/react");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const ComputedProperty = createFactory(require("./ComputedProperty"));
 
 const Types = require("../types");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
 module.exports = createClass({
 
   displayName: "BoxModelProperties",
 
   propTypes: {
     boxModel: PropTypes.shape(Types.boxModel).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   getInitialState() {
     return {
       isOpen: true,
     };
   },
 
   /**
--- a/devtools/client/inspector/boxmodel/components/ComputedProperty.js
+++ b/devtools/client/inspector/boxmodel/components/ComputedProperty.js
@@ -4,31 +4,33 @@
 
 "use strict";
 
 const { addons, createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const { Rep } = REPS;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "ComputedProperty",
 
   propTypes: {
     name: PropTypes.string.isRequired,
     value: PropTypes.string,
     referenceElement: PropTypes.object,
     referenceElementType: PropTypes.string,
     setSelectedNode: PropTypes.func.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   /**
    * While waiting for a reps fix in https://github.com/devtools-html/reps/issues/92,
    * translate nodeFront to a grip-like object that can be used with an ElementNode rep.
    *
    * @param  {NodeFront} nodeFront
    *         The NodeFront for which we want to create a grip-like object.
    * @return {Object} a grip-like object that can be used with Reps.
--- a/devtools/client/inspector/breadcrumbs.js
+++ b/devtools/client/inspector/breadcrumbs.js
@@ -13,16 +13,19 @@ const {ELLIPSIS} = require("devtools/sha
 const MAX_LABEL_LENGTH = 40;
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
 const SCROLL_REPEAT_MS = 100;
 
 const EventEmitter = require("devtools/shared/old-event-emitter");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
+loader.lazyRequireGetter(this, "perfService",
+  "devtools/shared/perfService", true);
+
 // Some margin may be required for visible element detection.
 const SCROLL_MARGIN = 1;
 
 /**
  * Component to replicate functionality of XUL arrowscrollbox
  * for breadcrumbs
  *
  * @param {Window} win The window containing the breadcrumbs
@@ -352,16 +355,17 @@ ArrowScrollBox.prototype = {
  *   then display the ancestor of the selected node and the selected node;
  *   else select the node;
  * - If the selected node is the last node displayed, append its first (if any).
  *
  * @param {InspectorPanel} inspector The inspector hosting this widget.
  */
 function HTMLBreadcrumbs(inspector) {
   this.inspector = inspector;
+  perfService.startMeasure("breadcrumbs-open");
   this.selection = this.inspector.selection;
   this.win = this.inspector.panelWin;
   this.doc = this.inspector.panelDoc;
   this._init();
 }
 
 exports.HTMLBreadcrumbs = HTMLBreadcrumbs;
 
@@ -905,16 +909,20 @@ HTMLBreadcrumbs.prototype = {
     this.updateSelectors();
 
     // Make sure the selected node and its neighbours are visible.
     setTimeout(() => {
       try {
         this.scroll();
         this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
         doneUpdating();
+        if (!this._hasAlreadyMeasured) {
+          perfService.stopMeasure("breadcrumbs-open");
+          this._hasAlreadyMeasured = true;
+        }
       } catch (e) {
         // Only log this as an error if we haven't been destroyed in the meantime.
         if (!this.isDestroyed) {
           console.error(e);
         }
       }
     }, 0);
   }
--- a/devtools/client/inspector/components/inspector-tab-panel.js
+++ b/devtools/client/inspector/components/inspector-tab-panel.js
@@ -6,24 +6,28 @@
 
 "use strict";
 
 const { DOM, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
 // Shortcuts
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * Helper panel component that is using an existing DOM node
  * as the content. It's used by Sidebar as well as SplitBox
  * components.
  */
 var InspectorTabPanel = createClass({
   displayName: "InspectorTabPanel",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // ID of the node that should be rendered as the content.
     id: PropTypes.string.isRequired,
     // Optional prefix for panel IDs.
     idPrefix: PropTypes.string,
     // Optional mount callback
     onMount: PropTypes.func,
   },
--- a/devtools/client/inspector/fonts/components/App.js
+++ b/devtools/client/inspector/fonts/components/App.js
@@ -3,16 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
 const FontList = createFactory(require("./FontList"));
 
 const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 
 const PREVIEW_UPDATE_DELAY = 150;
 
@@ -21,17 +23,17 @@ const App = createClass({
   displayName: "App",
 
   propTypes: {
     fonts: PropTypes.arrayOf(PropTypes.shape(Types.font)).isRequired,
     onPreviewFonts: PropTypes.func.isRequired,
     onShowAllFont: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   render() {
     let {
       fonts,
       onPreviewFonts,
       onShowAllFont,
     } = this.props;
 
--- a/devtools/client/inspector/fonts/components/Font.js
+++ b/devtools/client/inspector/fonts/components/Font.js
@@ -4,23 +4,25 @@
 
 "use strict";
 
 const { addons, createClass, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
 
 const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "Font",
 
   propTypes: PropTypes.shape(Types.font).isRequired,
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   getSectionClasses() {
     let { font } = this.props;
 
     let classes = ["font"];
     classes.push((font.URI) ? "is-remote" : "is-local");
 
     if (font.rule) {
--- a/devtools/client/inspector/fonts/components/FontList.js
+++ b/devtools/client/inspector/fonts/components/FontList.js
@@ -6,25 +6,27 @@
 
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Font = createFactory(require("./Font"));
 
 const Types = require("../types");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "FontList",
 
   propTypes: {
     fonts: PropTypes.arrayOf(PropTypes.shape(Types.font)).isRequired
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   render() {
     let { fonts } = this.props;
 
     return dom.div(
       {
         id: "font-container"
       },
--- a/devtools/client/inspector/grids/components/Grid.js
+++ b/devtools/client/inspector/grids/components/Grid.js
@@ -9,16 +9,18 @@ const { addons, createClass, createFacto
 
 const GridDisplaySettings = createFactory(require("./GridDisplaySettings"));
 const GridList = createFactory(require("./GridList"));
 const GridOutline = createFactory(require("./GridOutline"));
 
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "Grid",
 
   propTypes: {
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
@@ -30,17 +32,17 @@ module.exports = createClass({
     onShowGridCellHighlight: PropTypes.func.isRequired,
     onShowGridLineNamesHighlight: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
     onToggleShowGridAreas: PropTypes.func.isRequired,
     onToggleShowGridLineNumbers: PropTypes.func.isRequired,
     onToggleShowInfiniteLines: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   render() {
     let {
       getSwatchColorPickerTooltip,
       grids,
       highlighterSettings,
       setSelectedNode,
       onHideBoxModelHighlighter,
--- a/devtools/client/inspector/grids/components/GridDisplaySettings.js
+++ b/devtools/client/inspector/grids/components/GridDisplaySettings.js
@@ -5,28 +5,30 @@
 "use strict";
 
 const { addons, createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "GridDisplaySettings",
 
   propTypes: {
     highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
     onToggleShowGridAreas: PropTypes.func.isRequired,
     onToggleShowGridLineNumbers: PropTypes.func.isRequired,
     onToggleShowInfiniteLines: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   onShowGridAreasCheckboxClick() {
     let {
       highlighterSettings,
       onToggleShowGridAreas,
     } = this.props;
 
     onToggleShowGridAreas(!highlighterSettings.showGridAreasOverlay);
--- a/devtools/client/inspector/grids/components/GridItem.js
+++ b/devtools/client/inspector/grids/components/GridItem.js
@@ -9,31 +9,33 @@ const { findDOMNode } = require("devtool
 
 // Reps
 const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const { Rep } = REPS;
 const ElementNode = REPS.ElementNode;
 
 const Types = require("../types");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "GridItem",
 
   propTypes: {
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grid: PropTypes.shape(Types.grid).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onSetGridOverlayColor: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, perfService.mixins ],
 
   componentDidMount() {
     let tooltip = this.props.getSwatchColorPickerTooltip();
     let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
 
     let previousColor;
     tooltip.addSwatch(swatchEl, {
       onCommit: this.setGridColor,
--- a/devtools/client/inspector/grids/components/GridList.js
+++ b/devtools/client/inspector/grids/components/GridList.js
@@ -7,31 +7,33 @@
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const GridItem = createFactory(require("./GridItem"));
 
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "GridList",
 
   propTypes: {
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onSetGridOverlayColor: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   render() {
     let {
       getSwatchColorPickerTooltip,
       grids,
       setSelectedNode,
       onHideBoxModelHighlighter,
       onSetGridOverlayColor,
--- a/devtools/client/inspector/grids/components/GridOutline.js
+++ b/devtools/client/inspector/grids/components/GridOutline.js
@@ -6,16 +6,18 @@
 
 const { addons, createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Services = require("Services");
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // The delay prior to executing the grid cell highlighting.
 const GRID_HIGHLIGHTING_DEBOUNCE = 50;
 
 // Prefs for the max number of rows/cols a grid container can have for
 // the outline to display.
 const GRID_OUTLINE_MAX_ROWS_PREF =
   Services.prefs.getIntPref("devtools.gridinspector.gridOutlineMaxRows");
 const GRID_OUTLINE_MAX_COLUMNS_PREF =
@@ -36,17 +38,17 @@ module.exports = createClass({
   displayName: "GridOutline",
 
   propTypes: {
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     onShowGridAreaHighlight: PropTypes.func.isRequired,
     onShowGridCellHighlight: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   getInitialState() {
     return {
       height: 0,
       selectedGrid: null,
       showOutline: true,
       width: 0,
     };
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -26,16 +26,17 @@ const GridInspector = require("devtools/
 const {InspectorSearch} = require("devtools/client/inspector/inspector-search");
 const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
 const ReflowTracker = require("devtools/client/inspector/shared/reflow-tracker");
 const {ToolSidebar} = require("devtools/client/inspector/toolsidebar");
 const MarkupView = require("devtools/client/inspector/markup/markup");
 const {CommandUtils} = require("devtools/client/shared/developer-toolbar");
 const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
+const {perfService} = require("devtools/shared/perfService");
 
 const Store = require("devtools/client/inspector/store");
 
 const {LocalizationHelper, localizeMarkup} = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
       new LocalizationHelper("devtools/client/locales/inspector.properties");
 const TOOLBOX_L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
@@ -86,16 +87,18 @@ function Inspector(toolbox) {
   EventEmitter.decorate(this);
 
   this._toolbox = toolbox;
   this._target = toolbox.target;
   this.panelDoc = window.document;
   this.panelWin = window;
   this.panelWin.inspector = this;
 
+  perfService.startMeasure("inspector-open");
+
   // Map [panel id => panel instance]
   // Stores all the instances of sidebar panels like rule view, computed view, ...
   this._panels = new Map();
 
   this.highlighters = new HighlightersOverlay(this);
   this.reflowTracker = new ReflowTracker(this._target);
   this.store = Store();
   this.telemetry = new Telemetry();
@@ -283,16 +286,17 @@ Inspector.prototype = {
           this.selection.setNodeFront(defaultSelection, "inspector-open");
           this.markup.expandNode(this.selection.nodeFront);
         }
 
         // And setup the toolbar only now because it may depend on the document.
         this.setupToolbar();
 
         this.emit("ready");
+        perfService.stopMeasure("inspector-open");
         resolve(this);
       });
 
       this.setupSearchBox();
       this.setupSidebar();
     });
   },
 
--- a/devtools/client/inspector/layout/components/Accordion.js
+++ b/devtools/client/inspector/layout/components/Accordion.js
@@ -9,24 +9,26 @@
 
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const { DOM: dom, PropTypes } = React;
 
 const { div, span } = dom;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const Accordion = React.createClass({
   displayName: "Accordion",
 
   propTypes: {
     items: PropTypes.array
   },
 
-  mixins: [ React.addons.PureRenderMixin ],
+  mixins: [ React.addons.PureRenderMixin, ...perfService.mixins ],
 
   getInitialState: function () {
     return { opened: this.props.items.map(item => item.opened),
              created: [] };
   },
 
   handleHeaderClick: function (i) {
     const opened = [...this.state.opened];
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -6,16 +6,18 @@
 
 const Services = require("Services");
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const BoxModel = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModel"));
 const Grid = createFactory(require("devtools/client/inspector/grids/components/Grid"));
 
 const BoxModelTypes = require("devtools/client/inspector/boxmodel/types");
 const GridTypes = require("devtools/client/inspector/grids/types");
 
 const Accordion = createFactory(require("./Accordion"));
 const LayoutPromoteBar = createFactory(require("./LayoutPromoteBar"));
@@ -46,17 +48,17 @@ const App = createClass({
     onShowBoxModelEditor: PropTypes.func.isRequired,
     onShowBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
     onToggleShowGridLineNumbers: PropTypes.func.isRequired,
     onToggleShowInfiniteLines: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   render() {
     let { onPromoteLearnMoreClick } = this.props;
 
     return dom.div(
       {
         id: "layout-container",
       },
--- a/devtools/client/inspector/layout/components/LayoutPromoteBar.js
+++ b/devtools/client/inspector/layout/components/LayoutPromoteBar.js
@@ -13,30 +13,32 @@
  */
 
 const Services = require("Services");
 const { addons, createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const LAYOUT_STRINGS_URI = "devtools/client/locales/layout.properties";
 const LAYOUT_L10N = new LocalizationHelper(LAYOUT_STRINGS_URI);
 
 const SHOW_PROMOTE_BAR_PREF = "devtools.promote.layoutview.showPromoteBar";
 
 module.exports = createClass({
 
   displayName: "LayoutPromoteBar",
 
   propTypes: {
     onPromoteLearnMoreClick: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   getInitialState() {
     return {
       showPromoteBar: Services.prefs.getBoolPref(SHOW_PROMOTE_BAR_PREF)
     };
   },
 
   onPromoteCloseButtonClick() {
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -31,16 +31,19 @@ const {
 const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
 const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
 const {createChild, promiseWarn, debounce} = require("devtools/client/inspector/shared/utils");
 const EventEmitter = require("devtools/shared/old-event-emitter");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
 
+loader.lazyRequireGetter(this, "perfService",
+  "devtools/shared/perfService", true);
+
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
 const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
 const PREF_ENABLE_MDN_DOCS_TOOLTIP =
       "devtools.inspector.mdnDocsTooltip.enabled";
 const FILTER_CHANGED_TIMEOUT = 150;
 const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
 
@@ -1592,16 +1595,18 @@ function getShapePoint(node) {
   }
   return point;
 }
 
 function RuleViewTool(inspector, window) {
   this.inspector = inspector;
   this.document = window.document;
 
+  perfService.startMeasure("rule-view-open");
+
   this.view = new CssRuleView(this.inspector, this.document);
 
   this.clearUserProperties = this.clearUserProperties.bind(this);
   this.refresh = this.refresh.bind(this);
   this.onLinkClicked = this.onLinkClicked.bind(this);
   this.onMutations = this.onMutations.bind(this);
   this.onPanelSelected = this.onPanelSelected.bind(this);
   this.onPropertyChanged = this.onPropertyChanged.bind(this);
@@ -1654,17 +1659,22 @@ RuleViewTool.prototype = {
         !this.inspector.selection.isElementNode()) {
       this.view.selectElement(null);
       return;
     }
 
     if (!event || event == "new-node-front") {
       let done = this.inspector.updating("rule-view");
       this.view.selectElement(this.inspector.selection.nodeFront)
-        .then(done, done);
+        .then(done, done).then(() => {
+          if (!this._hasAlreadyMeasured) {
+            perfService.stopMeasure("rule-view-open");
+            this._hasAlreadyMeasured = true;
+          }
+        });
     }
   },
 
   refresh: function () {
     if (this.isSidebarActive()) {
       this.view.refreshPanel();
     }
   },
--- a/devtools/client/jsonview/components/headers-panel.js
+++ b/devtools/client/jsonview/components/headers-panel.js
@@ -11,23 +11,27 @@ define(function (require, exports, modul
 
   const { createFactories } = require("devtools/client/shared/react-utils");
 
   const { Headers } = createFactories(require("./headers"));
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
 
   const { div } = dom;
 
+  const {perfService} = require("devtools/shared/perfService");
+
   /**
    * This template represents the 'Headers' panel
    * s responsible for rendering its content.
    */
   let HeadersPanel = createClass({
     displayName: "HeadersPanel",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       actions: PropTypes.object,
       data: PropTypes.object,
     },
 
     getInitialState: function () {
       return {
         data: {}
@@ -50,16 +54,18 @@ define(function (require, exports, modul
 
   /**
    * This template is responsible for rendering a toolbar
    * within the 'Headers' panel.
    */
   let HeadersToolbar = createFactory(createClass({
     displayName: "HeadersToolbar",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       actions: PropTypes.object,
     },
 
     // Commands
 
     onCopy: function (event) {
       this.props.actions.onCopyHeaders();
--- a/devtools/client/jsonview/components/headers.js
+++ b/devtools/client/jsonview/components/headers.js
@@ -4,26 +4,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
   const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
+  const {perfService} = require("devtools/shared/perfService");
+
   const { div, span, table, tbody, tr, td, } = dom;
 
   /**
    * This template is responsible for rendering basic layout
    * of the 'Headers' panel. It displays HTTP headers groups such as
    * received or response headers.
    */
   let Headers = createClass({
     displayName: "Headers",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       data: PropTypes.object,
     },
 
     getInitialState: function () {
       return {};
     },
 
@@ -55,16 +59,18 @@ define(function (require, exports, modul
 
   /**
    * This template renders headers list,
    * name + value pairs.
    */
   let HeaderList = createFactory(createClass({
     displayName: "HeaderList",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       headers: PropTypes.arrayOf(PropTypes.shape({
         name: PropTypes.string,
         value: PropTypes.string
       }))
     },
 
     getInitialState: function () {
--- a/devtools/client/jsonview/components/json-panel.js
+++ b/devtools/client/jsonview/components/json-panel.js
@@ -6,16 +6,18 @@
 
 "use strict";
 
 define(function (require, exports, module) {
   const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
   const TreeViewClass = require("devtools/client/shared/components/tree/tree-view");
   const TreeView = createFactory(TreeViewClass);
 
+  const {perfService} = require("devtools/shared/perfService");
+
   const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
   const { createFactories } = require("devtools/client/shared/react-utils");
   const { Rep } = REPS;
 
   const { SearchBox } = createFactories(require("./search-box"));
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
 
   const { div } = dom;
@@ -29,16 +31,18 @@ define(function (require, exports, modul
   /**
    * This template represents the 'JSON' panel. The panel is
    * responsible for rendering an expandable tree that allows simple
    * inspection of JSON structure.
    */
   let JsonPanel = createClass({
     displayName: "JsonPanel",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       data: PropTypes.oneOfType([
         PropTypes.string,
         PropTypes.array,
         PropTypes.object
       ]),
       jsonTextLength: PropTypes.number,
       searchFilter: PropTypes.string,
@@ -140,16 +144,18 @@ define(function (require, exports, modul
   });
 
   /**
    * This template represents a toolbar within the 'JSON' panel.
    */
   let JsonToolbar = createFactory(createClass({
     displayName: "JsonToolbar",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       actions: PropTypes.object,
     },
 
     // Commands
 
     onSave: function (event) {
       this.props.actions.onSaveJson();
--- a/devtools/client/jsonview/components/main-tabbed-area.js
+++ b/devtools/client/jsonview/components/main-tabbed-area.js
@@ -10,23 +10,27 @@ define(function (require, exports, modul
   const { createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
   const { createFactories } = require("devtools/client/shared/react-utils");
   const { JsonPanel } = createFactories(require("./json-panel"));
   const { TextPanel } = createFactories(require("./text-panel"));
   const { HeadersPanel } = createFactories(require("./headers-panel"));
   const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/tabs"));
 
+  const {perfService} = require("devtools/shared/perfService");
+
   /**
    * This object represents the root application template
    * responsible for rendering the basic tab layout.
    */
   let MainTabbedArea = createClass({
     displayName: "MainTabbedArea",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       jsonText: PropTypes.string,
       tabActive: PropTypes.number,
       actions: PropTypes.object,
       headers: PropTypes.object,
       searchFilter: PropTypes.string,
       json: PropTypes.oneOfType([
         PropTypes.string,
--- a/devtools/client/jsonview/components/reps/toolbar.js
+++ b/devtools/client/jsonview/components/reps/toolbar.js
@@ -5,22 +5,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
   const React = require("devtools/client/shared/vendor/react");
   const DOM = React.DOM;
 
+  const {perfService} = require("devtools/shared/perfService");
+
   /**
    * Renders a simple toolbar.
    */
   let Toolbar = React.createClass({
     displayName: "Toolbar",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       children: React.PropTypes.oneOfType([
         React.PropTypes.array,
         React.PropTypes.element
       ])
     },
 
     render: function () {
@@ -33,16 +37,18 @@ define(function (require, exports, modul
   });
 
   /**
    * Renders a simple toolbar button.
    */
   let ToolbarButton = React.createClass({
     displayName: "ToolbarButton",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       active: React.PropTypes.bool,
       disabled: React.PropTypes.bool,
       children: React.PropTypes.string,
     },
 
     render: function () {
       let props = Object.assign({className: "btn"}, this.props);
--- a/devtools/client/jsonview/components/search-box.js
+++ b/devtools/client/jsonview/components/search-box.js
@@ -4,28 +4,32 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
   const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
+  const {perfService} = require("devtools/shared/perfService");
+
   const { input } = dom;
 
   // For smooth incremental searching (in case the user is typing quickly).
   const searchDelay = 250;
 
   /**
    * This object represents a search box located at the
    * top right corner of the application.
    */
   let SearchBox = createClass({
     displayName: "SearchBox",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       actions: PropTypes.object,
     },
 
     onSearch: function (event) {
       let searchBox = event.target;
       let win = searchBox.ownerDocument.defaultView;
 
--- a/devtools/client/jsonview/components/text-panel.js
+++ b/devtools/client/jsonview/components/text-panel.js
@@ -8,23 +8,27 @@
 
 define(function (require, exports, module) {
   const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
   const { createFactories } = require("devtools/client/shared/react-utils");
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
   const { div, pre } = dom;
 
+  const {perfService} = require("devtools/shared/perfService");
+
   /**
    * This template represents the 'Raw Data' panel displaying
    * JSON as a text received from the server.
    */
   let TextPanel = createClass({
     displayName: "TextPanel",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       actions: PropTypes.object,
       data: PropTypes.string
     },
 
     getInitialState: function () {
       return {};
     },
@@ -45,16 +49,18 @@ define(function (require, exports, modul
 
   /**
    * This object represents a toolbar displayed within the
    * 'Raw Data' panel.
    */
   let TextToolbar = createFactory(createClass({
     displayName: "TextToolbar",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       actions: PropTypes.object,
     },
 
     // Commands
 
     onPrettify: function (event) {
       this.props.actions.onPrettify();
--- a/devtools/client/memory/app.js
+++ b/devtools/client/memory/app.js
@@ -46,19 +46,23 @@ const {
 const { changeViewAndRefresh, popViewAndRefresh } = require("./actions/view");
 const { resizeShortestPaths } = require("./actions/sizes");
 const Toolbar = createFactory(require("./components/toolbar"));
 const List = createFactory(require("./components/list"));
 const SnapshotListItem = createFactory(require("./components/snapshot-list-item"));
 const Heap = createFactory(require("./components/heap"));
 const { app: appModel } = require("./models");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const MemoryApp = createClass({
   displayName: "MemoryApp",
 
+  mixins: perfService.mixins,
+
   propTypes: appModel,
 
   childContextTypes: {
     front: PropTypes.any,
     heapWorker: PropTypes.any,
     toolbox: PropTypes.any,
   },
 
--- a/devtools/client/memory/components/census-header.js
+++ b/devtools/client/memory/components/census-header.js
@@ -3,19 +3,23 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { DOM: dom, createClass } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../utils");
 const models = require("../models");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "CensusHeader",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     diffing: models.diffingModel,
   },
 
   render() {
     let individualsCell;
     if (!this.props.diffing) {
       individualsCell = dom.span({
--- a/devtools/client/memory/components/census-tree-item.js
+++ b/devtools/client/memory/components/census-tree-item.js
@@ -10,19 +10,23 @@ const {
   createFactory,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const { L10N, formatNumber, formatPercent } = require("../utils");
 const Frame = createFactory(require("devtools/client/shared/components/frame"));
 const { TREE_ROW_HEIGHT } = require("../constants");
 const models = require("../models");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "CensusTreeItem",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     arrow: PropTypes.any,
     depth: PropTypes.number.isRequired,
     diffing: models.app.diffing,
     expanded: PropTypes.bool.isRequired,
     focused: PropTypes.bool.isRequired,
     getPercentBytes: PropTypes.func.isRequired,
     getPercentCount: PropTypes.func.isRequired,
--- a/devtools/client/memory/components/census.js
+++ b/devtools/client/memory/components/census.js
@@ -5,19 +5,23 @@
 "use strict";
 
 const { createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
 const Tree = createFactory(require("devtools/client/shared/components/tree"));
 const CensusTreeItem = createFactory(require("./census-tree-item"));
 const { TREE_ROW_HEIGHT } = require("../constants");
 const { censusModel, diffingModel } = require("../models");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "Census",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     census: censusModel,
     onExpand: PropTypes.func.isRequired,
     onCollapse: PropTypes.func.isRequired,
     onFocus: PropTypes.func.isRequired,
     onViewSourceInDebugger: PropTypes.func.isRequired,
     onViewIndividuals: PropTypes.func.isRequired,
     diffing: diffingModel,
--- a/devtools/client/memory/components/dominator-tree-header.js
+++ b/devtools/client/memory/components/dominator-tree-header.js
@@ -2,19 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { DOM: dom, createClass } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../utils");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "DominatorTreeHeader",
 
+  mixins: perfService.mixins,
+
   propTypes: { },
 
   render() {
     return dom.div(
       {
         className: "header"
       },
 
--- a/devtools/client/memory/components/dominator-tree-item.js
+++ b/devtools/client/memory/components/dominator-tree-item.js
@@ -5,27 +5,33 @@
 "use strict";
 
 const { assert, isSavedFrame } = require("devtools/shared/DevToolsUtils");
 const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
 const { L10N, formatNumber, formatPercent } = require("../utils");
 const Frame = createFactory(require("devtools/client/shared/components/frame"));
 const { TREE_ROW_HEIGHT } = require("../constants");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const Separator = createFactory(createClass({
   displayName: "Separator",
 
+  mixins: perfService.mixins,
+
   render() {
     return dom.span({ className: "separator" }, "›");
   }
 }));
 
 module.exports = createClass({
   displayName: "DominatorTreeItem",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
     depth: PropTypes.number.isRequired,
     arrow: PropTypes.object,
     expanded: PropTypes.bool.isRequired,
     focused: PropTypes.bool.isRequired,
     getPercentSize: PropTypes.func.isRequired,
     onViewSourceInDebugger: PropTypes.func.isRequired,
--- a/devtools/client/memory/components/dominator-tree.js
+++ b/devtools/client/memory/components/dominator-tree.js
@@ -11,23 +11,27 @@ const Tree = createFactory(require("devt
 const DominatorTreeItem = createFactory(require("./dominator-tree-item"));
 const { L10N } = require("../utils");
 const { TREE_ROW_HEIGHT, dominatorTreeState } = require("../constants");
 const { dominatorTreeModel } = require("../models");
 const DominatorTreeLazyChildren = require("../dominator-tree-lazy-children");
 
 const DOMINATOR_TREE_AUTO_EXPAND_DEPTH = 3;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * A throbber that represents a subtree in the dominator tree that is actively
  * being incrementally loaded and fetched from the `HeapAnalysesWorker`.
  */
 const DominatorTreeSubtreeFetching = createFactory(createClass({
   displayName: "DominatorTreeSubtreeFetching",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     depth: PropTypes.number.isRequired,
     focused: PropTypes.bool.isRequired,
   },
 
   shouldComponentUpdate(nextProps, nextState) {
     return this.props.depth !== nextProps.depth
       || this.props.focused !== nextProps.focused;
@@ -55,16 +59,18 @@ const DominatorTreeSubtreeFetching = cre
 
 /**
  * A link to fetch and load more siblings in the dominator tree, when there are
  * already many loaded above.
  */
 const DominatorTreeSiblingLink = createFactory(createClass({
   displayName: "DominatorTreeSiblingLink",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     depth: PropTypes.number.isRequired,
     focused: PropTypes.bool.isRequired,
     item: PropTypes.instanceOf(DominatorTreeLazyChildren).isRequired,
     onLoadMoreSiblings: PropTypes.func.isRequired,
   },
 
   shouldComponentUpdate(nextProps, nextState) {
@@ -103,16 +109,18 @@ const DominatorTreeSiblingLink = createF
 }));
 
 /**
  * The actual dominator tree rendered as an expandable and collapsible tree.
  */
 module.exports = createClass({
   displayName: "DominatorTree",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     dominatorTree: dominatorTreeModel.isRequired,
     onLoadMoreSiblings: PropTypes.func.isRequired,
     onViewSourceInDebugger: PropTypes.func.isRequired,
     onExpand: PropTypes.func.isRequired,
     onCollapse: PropTypes.func.isRequired,
     onFocus: PropTypes.func.isRequired,
   },
--- a/devtools/client/memory/components/heap.js
+++ b/devtools/client/memory/components/heap.js
@@ -23,16 +23,18 @@ const {
   censusState,
   treeMapState,
   dominatorTreeState,
   individualsState,
 } = require("../constants");
 const models = require("../models");
 const { snapshot: snapshotModel, diffingModel } = models;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * Get the app state's current state atom.
  *
  * @see the relevant state string constants in `../constants.js`.
  *
  * @param {models.view} view
  * @param {snapshotModel} snapshot
  * @param {diffingModel} diffing
@@ -178,16 +180,18 @@ function getError(snapshot, diffing, ind
  *
  * The Heap component contains several panels for different states; an initial
  * state of only a button to take a snapshot, loading states, the census view
  * tree, the dominator tree, etc.
  */
 module.exports = createClass({
   displayName: "Heap",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     onSnapshotClick: PropTypes.func.isRequired,
     onLoadMoreSiblings: PropTypes.func.isRequired,
     onCensusExpand: PropTypes.func.isRequired,
     onCensusCollapse: PropTypes.func.isRequired,
     onDominatorTreeExpand: PropTypes.func.isRequired,
     onDominatorTreeCollapse: PropTypes.func.isRequired,
     onCensusFocus: PropTypes.func.isRequired,
--- a/devtools/client/memory/components/individuals-header.js
+++ b/devtools/client/memory/components/individuals-header.js
@@ -2,19 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { DOM: dom, createClass } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../utils");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "IndividualsHeader",
 
+  mixins: perfService.mixins,
+
   propTypes: { },
 
   render() {
     return dom.div(
       {
         className: "header"
       },
 
--- a/devtools/client/memory/components/individuals.js
+++ b/devtools/client/memory/components/individuals.js
@@ -5,22 +5,26 @@
 "use strict";
 
 const { createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
 const Tree = createFactory(require("devtools/client/shared/components/tree"));
 const DominatorTreeItem = createFactory(require("./dominator-tree-item"));
 const { TREE_ROW_HEIGHT } = require("../constants");
 const models = require("../models");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * The list of individuals in a census group.
  */
 module.exports = createClass({
   displayName: "Individuals",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     onViewSourceInDebugger: PropTypes.func.isRequired,
     onFocus: PropTypes.func.isRequired,
     individuals: models.individuals,
     dominatorTree: models.dominatorTreeModel,
   },
 
   render() {
--- a/devtools/client/memory/components/list.js
+++ b/devtools/client/memory/components/list.js
@@ -1,24 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * Generic list component that takes another react component to represent
  * the children nodes as `itemComponent`, and a list of items to render
  * as that component with a click handler.
  */
 module.exports = createClass({
   displayName: "List",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     itemComponent: PropTypes.any.isRequired,
     onClick: PropTypes.func,
     items: PropTypes.array.isRequired,
   },
 
   render() {
     let { items, onClick, itemComponent: Item } = this.props;
--- a/devtools/client/memory/components/shortest-paths.js
+++ b/devtools/client/memory/components/shortest-paths.js
@@ -8,16 +8,18 @@ const {
   DOM: dom,
   createClass,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { isSavedFrame } = require("devtools/shared/DevToolsUtils");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const { L10N } = require("../utils");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const GRAPH_DEFAULTS = {
   translate: [20, 20],
   scale: 1
 };
 
 const NO_STACK = "noStack";
 const NO_FILENAME = "noFilename";
 const ROOT_LIST = "JS::ubi::RootList";
@@ -48,16 +50,18 @@ function stringifyLabel(label, id) {
   }
 
   return `${sanitized.join(" › ")} @ 0x${id.toString(16)}`;
 }
 
 module.exports = createClass({
   displayName: "ShortestPaths",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     graph: PropTypes.shape({
       nodes: PropTypes.arrayOf(PropTypes.object),
       edges: PropTypes.arrayOf(PropTypes.object),
     }),
   },
 
   getInitialState() {
--- a/devtools/client/memory/components/snapshot-list-item.js
+++ b/devtools/client/memory/components/snapshot-list-item.js
@@ -11,19 +11,23 @@ const {
   getSnapshotTotals,
   getStatusText,
   snapshotIsDiffable,
   getSavedCensus
 } = require("../utils");
 const { diffingState } = require("../constants");
 const { snapshot: snapshotModel, app: appModel } = require("../models");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "SnapshotListItem",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     onClick: PropTypes.func.isRequired,
     onSave: PropTypes.func.isRequired,
     onDelete: PropTypes.func.isRequired,
     item: snapshotModel.isRequired,
     index: PropTypes.number.isRequired,
     diffing: appModel.diffing,
   },
--- a/devtools/client/memory/components/toolbar.js
+++ b/devtools/client/memory/components/toolbar.js
@@ -4,19 +4,23 @@
 "use strict";
 
 const { assert } = require("devtools/shared/DevToolsUtils");
 const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../utils");
 const models = require("../models");
 const { viewState } = require("../constants");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "Toolbar",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     censusDisplays: PropTypes.arrayOf(PropTypes.shape({
       displayName: PropTypes.string.isRequired,
     })).isRequired,
     censusDisplay: PropTypes.shape({
       displayName: PropTypes.string.isRequired,
     }).isRequired,
     onTakeSnapshotClick: PropTypes.func.isRequired,
--- a/devtools/client/memory/components/tree-map.js
+++ b/devtools/client/memory/components/tree-map.js
@@ -3,19 +3,23 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { DOM: dom, createClass } = require("devtools/client/shared/vendor/react");
 const { treeMapModel } = require("../models");
 const startVisualization = require("./tree-map/start");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "TreeMap",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     treeMap: treeMapModel
   },
 
   getInitialState() {
     return {};
   },
 
--- a/devtools/client/netmonitor/src/components/headers-panel.js
+++ b/devtools/client/netmonitor/src/components/headers-panel.js
@@ -39,23 +39,27 @@ const REQUEST_HEADERS = L10N.getStr("req
 const REQUEST_HEADERS_FROM_UPLOAD = L10N.getStr("requestHeadersFromUpload");
 const RESPONSE_HEADERS = L10N.getStr("responseHeaders");
 const SUMMARY_ADDRESS = L10N.getStr("netmonitor.summary.address");
 const SUMMARY_METHOD = L10N.getStr("netmonitor.summary.method");
 const SUMMARY_URL = L10N.getStr("netmonitor.summary.url");
 const SUMMARY_STATUS = L10N.getStr("netmonitor.summary.status");
 const SUMMARY_VERSION = L10N.getStr("netmonitor.summary.version");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /*
  * Headers panel component
  * Lists basic information about the request
  */
 const HeadersPanel = createClass({
   displayName: "HeadersPanel",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     cloneSelectedRequest: PropTypes.func.isRequired,
     request: PropTypes.object.isRequired,
     renderValue: PropTypes.func
   },
 
   getInitialState() {
     return {
--- a/devtools/client/netmonitor/src/components/monitor-panel.js
+++ b/devtools/client/netmonitor/src/components/monitor-panel.js
@@ -21,23 +21,27 @@ const { getSelectedRequest } = require("
 // Components
 const SplitBox = createFactory(require("devtools/client/shared/components/splitter/split-box"));
 const NetworkDetailsPanel = createFactory(require("./network-details-panel"));
 const RequestList = createFactory(require("./request-list"));
 const Toolbar = createFactory(require("./toolbar"));
 const { div } = DOM;
 const MediaQueryList = window.matchMedia("(min-width: 700px)");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /*
  * Monitor panel component
  * The main panel for displaying various network request information
  */
 const MonitorPanel = createClass({
   displayName: "MonitorPanel",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     isEmpty: PropTypes.bool.isRequired,
     networkDetailsOpen: PropTypes.bool.isRequired,
     openNetworkDetails: PropTypes.func.isRequired,
     request: PropTypes.object,
     // Service to enable the source map feature.
     sourceMapService: PropTypes.object,
     updateRequest: PropTypes.func.isRequired,
--- a/devtools/client/netmonitor/src/components/properties-view.js
+++ b/devtools/client/netmonitor/src/components/properties-view.js
@@ -13,16 +13,18 @@ const {
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 
 const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const { Rep } = REPS;
 
 const { FILTER_SEARCH_DELAY } = require("../constants");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Components
 const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
 const TreeViewClass = require("devtools/client/shared/components/tree/tree-view");
 const TreeView = createFactory(TreeViewClass);
 const TreeRow = createFactory(require("devtools/client/shared/components/tree/tree-row"));
 const SourceEditor = createFactory(require("./source-editor"));
 
 const { div, tr, td } = DOM;
@@ -38,16 +40,18 @@ const EDITOR_CONFIG_ID = "EDITOR_CONFIG"
  * Search filter - Set enableFilter to enable / disable SearchBox feature.
  * Tree view - Default enabled.
  * Source editor - Enable by specifying object level 1 property name to EDITOR_CONFIG_ID.
  * Rep - Default enabled.
  */
 const PropertiesView = createClass({
   displayName: "PropertiesView",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     object: PropTypes.object,
     enableInput: PropTypes.bool,
     expandableStrings: PropTypes.bool,
     filterPlaceHolder: PropTypes.string,
     sectionNames: PropTypes.array,
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-cause.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-cause.js
@@ -5,21 +5,25 @@
 "use strict";
 
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const { div } = DOM;
 
 const RequestListColumnCause = createClass({
   displayName: "RequestListColumnCause",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
     onCauseBadgeMouseDown: PropTypes.func.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.cause !== nextProps.item.cause;
   },
--- a/devtools/client/netmonitor/src/components/request-list-column-content-size.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-content-size.js
@@ -8,19 +8,23 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedSize } = require("../utils/format-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnContentSize = createClass({
   displayName: "RequestListColumnContentSize",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.contentSize !== nextProps.item.contentSize;
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-cookies.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-cookies.js
@@ -7,19 +7,23 @@
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnCookies = createClass({
   displayName: "RequestListColumnCookies",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     let { requestCookies: currRequestCookies = { cookies: [] } } = this.props.item;
     let { requestCookies: nextRequestCookies = { cookies: [] } } = nextProps.item;
     currRequestCookies = currRequestCookies.cookies || currRequestCookies;
--- a/devtools/client/netmonitor/src/components/request-list-column-domain.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-domain.js
@@ -10,25 +10,29 @@ const {
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedIPAndPort } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const { propertiesEqual } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const UPDATED_DOMAIN_PROPS = [
   "remoteAddress",
   "securityState",
   "urlDetails",
 ];
 
 const RequestListColumnDomain = createClass({
   displayName: "RequestListColumnDomain",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
     onSecurityIconMouseDown: PropTypes.func.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
   },
--- a/devtools/client/netmonitor/src/components/request-list-column-duration.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-duration.js
@@ -8,19 +8,23 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedTime } = require("../utils/format-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnDuration = createClass({
   displayName: "RequestListColumnDuration",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.totalTime !== nextProps.item.totalTime;
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-end-time.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-end-time.js
@@ -9,19 +9,23 @@ const {
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedTime } = require("../utils/format-utils");
 const { getEndTime } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnEndTime = createClass({
   displayName: "RequestListColumnEndTime",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     firstRequestStartedMillis: PropTypes.number.isRequired,
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return getEndTime(this.props.item) !== getEndTime(nextProps.item);
   },
--- a/devtools/client/netmonitor/src/components/request-list-column-file.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-file.js
@@ -8,24 +8,28 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { propertiesEqual } = require("../utils/request-utils");
 
 const { div, img } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const UPDATED_FILE_PROPS = [
   "responseContentDataUri",
   "urlDetails",
 ];
 
 const RequestListColumnFile = createClass({
   displayName: "RequestListColumnFile",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
     onThumbnailMouseDown: PropTypes.func.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
   },
--- a/devtools/client/netmonitor/src/components/request-list-column-latency.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-latency.js
@@ -8,19 +8,23 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedTime } = require("../utils/format-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnLatency = createClass({
   displayName: "RequestListColumnLatency",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     let { eventTimings: currEventTimings = { timings: {} } } = this.props.item;
     let { eventTimings: nextEventTimings = { timings: {} } } = nextProps.item;
     return currEventTimings.timings.wait !== nextEventTimings.timings.wait;
--- a/devtools/client/netmonitor/src/components/request-list-column-method.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-method.js
@@ -7,19 +7,23 @@
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnMethod = createClass({
   displayName: "RequestListColumnMethod",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.method !== nextProps.item.method;
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-protocol.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-protocol.js
@@ -8,19 +8,23 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedProtocol } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnProtocol = createClass({
   displayName: "RequestListColumnProtocol",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return getFormattedProtocol(this.props.item) !==
       getFormattedProtocol(nextProps.item);
   },
--- a/devtools/client/netmonitor/src/components/request-list-column-remote-ip.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-remote-ip.js
@@ -8,19 +8,23 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedIPAndPort } = require("../utils/format-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnRemoteIP = createClass({
   displayName: "RequestListColumnRemoteIP",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.remoteAddress !== nextProps.item.remoteAddress;
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-response-header.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-response-header.js
@@ -8,23 +8,27 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getResponseHeader } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * Renders a response header column in the requests list.  The actual
  * header to show is passed as a prop.
  */
 const RequestListColumnResponseHeader = createClass({
   displayName: "RequestListColumnResponseHeader",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
     header: PropTypes.string.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     const currHeader = getResponseHeader(this.props.item, this.props.header);
     const nextHeader = getResponseHeader(nextProps.item, nextProps.header);
--- a/devtools/client/netmonitor/src/components/request-list-column-response-time.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-response-time.js
@@ -9,19 +9,23 @@ const {
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedTime } = require("../utils/format-utils");
 const { getResponseTime } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnResponseTime = createClass({
   displayName: "RequestListColumnResponseTime",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     firstRequestStartedMillis: PropTypes.number.isRequired,
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return getResponseTime(this.props.item) !== getResponseTime(nextProps.item);
   },
--- a/devtools/client/netmonitor/src/components/request-list-column-scheme.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-scheme.js
@@ -7,19 +7,23 @@
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnScheme = createClass({
   displayName: "RequestListColumnScheme",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.urlDetails.scheme !== nextProps.item.urlDetails.scheme;
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-set-cookies.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-set-cookies.js
@@ -7,19 +7,23 @@
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnSetCookies = createClass({
   displayName: "RequestListColumnSetCookies",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     let { responseCookies: currResponseCookies = { cookies: [] } } = this.props.item;
     let { responseCookies: nextResponseCookies = { cookies: [] } } = nextProps.item;
     currResponseCookies = currResponseCookies.cookies || currResponseCookies;
--- a/devtools/client/netmonitor/src/components/request-list-column-start-time.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-start-time.js
@@ -9,19 +9,23 @@ const {
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedTime } = require("../utils/format-utils");
 const { getStartTime } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnStartTime = createClass({
   displayName: "RequestListColumnStartTime",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     firstRequestStartedMillis: PropTypes.number.isRequired,
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return getStartTime(this.props.item, this.props.firstRequestStartedMillis)
       !== getStartTime(nextProps.item, nextProps.firstRequestStartedMillis);
--- a/devtools/client/netmonitor/src/components/request-list-column-status.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-status.js
@@ -9,26 +9,30 @@ const {
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../utils/l10n");
 const { propertiesEqual } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const UPDATED_STATUS_PROPS = [
   "fromCache",
   "fromServiceWorker",
   "status",
   "statusText",
 ];
 
 const RequestListColumnStatus = createClass({
   displayName: "RequestListColumnStatus",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_STATUS_PROPS, this.props.item, nextProps.item);
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-transferred-size.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-transferred-size.js
@@ -10,25 +10,29 @@ const {
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getFormattedSize } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const { propertiesEqual } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const UPDATED_TRANSFERRED_PROPS = [
   "transferredSize",
   "fromCache",
   "fromServiceWorker",
 ];
 
 const RequestListColumnTransferredSize = createClass({
   displayName: "RequestListColumnTransferredSize",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_TRANSFERRED_PROPS, this.props.item, nextProps.item);
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-type.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-type.js
@@ -8,19 +8,23 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { getAbbreviatedMimeType } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const RequestListColumnType = createClass({
   displayName: "RequestListColumnType",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.mimeType !== nextProps.item.mimeType;
   },
 
--- a/devtools/client/netmonitor/src/components/request-list-column-waterfall.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-waterfall.js
@@ -9,28 +9,32 @@ const {
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../utils/l10n");
 const { propertiesEqual } = require("../utils/request-utils");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const UPDATED_WATERFALL_PROPS = [
   "eventTimings",
   "fromCache",
   "fromServiceWorker",
   "totalTime",
 ];
 // List of properties of the timing info we want to create boxes for
 const TIMING_KEYS = ["blocked", "dns", "connect", "ssl", "send", "wait", "receive"];
 
 const RequestListColumnWaterfall = createClass({
   displayName: "RequestListColumnWaterfall",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     firstRequestStartedMillis: PropTypes.number.isRequired,
     item: PropTypes.object.isRequired,
     onWaterfallMouseDown: PropTypes.func.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_WATERFALL_PROPS, this.props.item, nextProps.item) ||
--- a/devtools/client/netmonitor/src/components/request-list-content.js
+++ b/devtools/client/netmonitor/src/components/request-list-content.js
@@ -14,31 +14,35 @@ const { connect } = require("devtools/cl
 const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 const Actions = require("../actions/index");
 const { setTooltipImageContent } = require("../request-list-tooltip");
 const {
   getDisplayedRequests,
   getWaterfallScale,
 } = require("../selectors/index");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Components
 const RequestListItem = createFactory(require("./request-list-item"));
 const RequestListContextMenu = require("../request-list-context-menu");
 
 const { div } = DOM;
 
 // tooltip show/hide delay in ms
 const REQUESTS_TOOLTIP_TOGGLE_DELAY = 500;
 
 /**
  * Renders the actual contents of the request list.
  */
 const RequestListContent = createClass({
   displayName: "RequestListContent",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     columns: PropTypes.object.isRequired,
     dispatch: PropTypes.func.isRequired,
     displayedRequests: PropTypes.object.isRequired,
     firstRequestStartedMillis: PropTypes.number.isRequired,
     fromCache: PropTypes.bool,
     onCauseBadgeMouseDown: PropTypes.func.isRequired,
     onItemMouseDown: PropTypes.func.isRequired,
--- a/devtools/client/netmonitor/src/components/request-list-empty-notice.js
+++ b/devtools/client/netmonitor/src/components/request-list-empty-notice.js
@@ -12,28 +12,32 @@ const {
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const Actions = require("../actions/index");
 const { triggerActivity } = require("../connector/index");
 const { ACTIVITY_TYPE } = require("../constants");
 const { L10N } = require("../utils/l10n");
 const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Components
 const MDNLink = createFactory(require("./mdn-link"));
 
 const { button, div, span } = DOM;
 
 /**
  * UI displayed when the request list is empty. Contains instructions on reloading
  * the page and on triggering performance analysis of the page.
  */
 const RequestListEmptyNotice = createClass({
   displayName: "RequestListEmptyNotice",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     onReloadClick: PropTypes.func.isRequired,
     onPerfClick: PropTypes.func.isRequired,
   },
 
   render() {
     return div(
       {
--- a/devtools/client/netmonitor/src/components/request-list-header.js
+++ b/devtools/client/netmonitor/src/components/request-list-header.js
@@ -15,24 +15,28 @@ const { HEADERS, REQUESTS_WATERFALL } = 
 const { getWaterfallScale } = require("../selectors/index");
 const { getFormattedTime } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const WaterfallBackground = require("../waterfall-background");
 const RequestListHeaderContextMenu = require("../request-list-header-context-menu");
 
 const { div, button } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * Render the request list header with sorting arrows for columns.
  * Displays tick marks in the waterfall column header.
  * Also draws the waterfall background canvas and updates it when needed.
  */
 const RequestListHeader = createClass({
   displayName: "RequestListHeader",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     columns: PropTypes.object.isRequired,
     resetColumns: PropTypes.func.isRequired,
     resizeWaterfall: PropTypes.func.isRequired,
     scale: PropTypes.number,
     sort: PropTypes.object,
     sortBy: PropTypes.func.isRequired,
     toggleColumn: PropTypes.func.isRequired,
--- a/devtools/client/netmonitor/src/components/request-list-item.js
+++ b/devtools/client/netmonitor/src/components/request-list-item.js
@@ -33,16 +33,18 @@ const RequestListColumnSetCookies = crea
 const RequestListColumnStartTime = createFactory(require("./request-list-column-start-time"));
 const RequestListColumnStatus = createFactory(require("./request-list-column-status"));
 const RequestListColumnTransferredSize = createFactory(require("./request-list-column-transferred-size"));
 const RequestListColumnType = createFactory(require("./request-list-column-type"));
 const RequestListColumnWaterfall = createFactory(require("./request-list-column-waterfall"));
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * Used by shouldComponentUpdate: compare two items, and compare only properties
  * relevant for rendering the RequestListItem. Other properties (like request and
  * response headers, cookies, bodies) are ignored. These are very useful for the
  * network details, but not here.
  */
 const UPDATED_REQ_ITEM_PROPS = [
   "mimeType",
@@ -71,16 +73,18 @@ const UPDATED_REQ_PROPS = [
 ];
 
 /**
  * Render one row in the request list.
  */
 const RequestListItem = createClass({
   displayName: "RequestListItem",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     columns: PropTypes.object.isRequired,
     item: PropTypes.object.isRequired,
     index: PropTypes.number.isRequired,
     isSelected: PropTypes.bool.isRequired,
     firstRequestStartedMillis: PropTypes.number.isRequired,
     fromCache: PropTypes.bool,
     onCauseBadgeMouseDown: PropTypes.func.isRequired,
--- a/devtools/client/netmonitor/src/components/response-panel.js
+++ b/devtools/client/netmonitor/src/components/response-panel.js
@@ -19,23 +19,27 @@ const PropertiesView = createFactory(req
 const { div, img } = DOM;
 const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
 const JSON_FILTER_TEXT = L10N.getStr("jsonFilterText");
 const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
 const RESPONSE_IMG_DIMENSIONS = L10N.getStr("netmonitor.response.dimensions");
 const RESPONSE_IMG_MIMETYPE = L10N.getStr("netmonitor.response.mime");
 const RESPONSE_PAYLOAD = L10N.getStr("responsePayload");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /*
  * Response panel component
  * Displays the GET parameters and POST data of a request
  */
 const ResponsePanel = createClass({
   displayName: "ResponsePanel",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     request: PropTypes.object.isRequired,
   },
 
   getInitialState() {
     return {
       imageDimensions: {
         width: 0,
--- a/devtools/client/netmonitor/src/components/source-editor.js
+++ b/devtools/client/netmonitor/src/components/source-editor.js
@@ -9,22 +9,26 @@ const {
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const Editor = require("devtools/client/sourceeditor/editor");
 const { SOURCE_EDITOR_SYNTAX_HIGHLIGHT_MAX_SIZE } = require("../constants");
 
 const { div } = DOM;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * CodeMirror editor as a React component
  */
 const SourceEditor = createClass({
   displayName: "SourceEditor",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // Source editor syntax hightligh mode, which is a mime type defined in CodeMirror
     mode: PropTypes.string,
     // Source editor content
     text: PropTypes.string,
   },
 
   componentDidMount() {
--- a/devtools/client/netmonitor/src/components/statistics-panel.js
+++ b/devtools/client/netmonitor/src/components/statistics-panel.js
@@ -18,16 +18,18 @@ const Actions = require("../actions/inde
 const { Filters } = require("../utils/filter-predicates");
 const {
   getSizeWithDecimals,
   getTimeWithDecimals
 } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Components
 const MDNLink = createFactory(require("./mdn-link"));
 
 const { button, div } = DOM;
 const MediaQueryList = window.matchMedia("(min-width: 700px)");
 
 const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
 const BACK_BUTTON = L10N.getStr("netmonitor.backButton");
@@ -37,16 +39,18 @@ const CHARTS_CACHE_DISABLED = L10N.getSt
 /*
  * Statistics panel component
  * Performance analysis tool which shows you how long the browser takes to
  * download the different parts of your site.
  */
 const StatisticsPanel = createClass({
   displayName: "StatisticsPanel",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     closeStatistics: PropTypes.func.isRequired,
     enableRequestFilterTypeOnly: PropTypes.func.isRequired,
     requests: PropTypes.object,
   },
 
   getInitialState() {
     return {
--- a/devtools/client/netmonitor/src/components/toolbar.js
+++ b/devtools/client/netmonitor/src/components/toolbar.js
@@ -19,16 +19,18 @@ const {
   getRequestFilterTypes,
   getTypeFilteredRequests,
   isNetworkDetailsToggleButtonDisabled,
 } = require("../selectors/index");
 
 const { autocompleteProvider } = require("../utils/filter-autocomplete-provider");
 const { L10N } = require("../utils/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Components
 const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
 
 const { button, div, input, label, span } = DOM;
 
 const COLLPASE_DETAILS_PANE = L10N.getStr("collapseDetailsPane");
 const EXPAND_DETAILS_PANE = L10N.getStr("expandDetailsPane");
 const SEARCH_KEY_SHORTCUT = L10N.getStr("netmonitor.toolbar.filterFreetext.key");
@@ -39,16 +41,18 @@ const DEVTOOLS_DISABLE_CACHE_PREF = "dev
 
 /*
  * Network monitor toolbar component
  * Toolbar contains a set of useful tools to control network requests
  */
 const Toolbar = createClass({
   displayName: "Toolbar",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     clearRequests: PropTypes.func.isRequired,
     requestFilterTypes: PropTypes.array.isRequired,
     setRequestFilterText: PropTypes.func.isRequired,
     networkDetailsToggleDisabled: PropTypes.bool.isRequired,
     networkDetailsOpen: PropTypes.bool.isRequired,
     toggleNetworkDetails: PropTypes.func.isRequired,
     disableBrowserCache: PropTypes.func.isRequired,
--- a/devtools/client/performance/components/jit-optimizations-item.js
+++ b/devtools/client/performance/components/jit-optimizations-item.js
@@ -13,16 +13,18 @@ const Frame = createFactory(require("dev
 const PROPNAME_MAX_LENGTH = 4;
 // If TREE_ROW_HEIGHT changes, be sure to change `var(--jit-tree-row-height)`
 // in `devtools/client/themes/jit-optimizations.css`
 const TREE_ROW_HEIGHT = 14;
 
 const OPTIMIZATION_ITEM_TYPES = ["site", "attempts", "types", "attempt", "type",
                                  "observedtype"];
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /* eslint-disable no-unused-vars */
 /**
  * TODO - Re-enable this eslint rule. The JIT tool is a work in progress, and isn't fully
  *        integrated as of yet.
  */
 const {
   JITOptimizations, hasSuccessfulOutcome, isSuccessfulOutcome
 } = require("devtools/client/performance/modules/logic/jit");
@@ -30,16 +32,18 @@ const OPTIMIZATION_FAILURE = L10N.getStr
 const JIT_SAMPLES = L10N.getStr("jit.samples");
 const JIT_TYPES = L10N.getStr("jit.types");
 const JIT_ATTEMPTS = L10N.getStr("jit.attempts");
 /* eslint-enable no-unused-vars */
 
 const JITOptimizationsItem = createClass({
   displayName: "JITOptimizationsItem",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     onViewSourceInDebugger: PropTypes.func.isRequired,
     frameData: PropTypes.object.isRequired,
     type: PropTypes.oneOf(OPTIMIZATION_ITEM_TYPES).isRequired,
     depth: PropTypes.number.isRequired,
     arrow: PropTypes.element.isRequired,
     item: PropTypes.object,
     focused: PropTypes.bool
--- a/devtools/client/performance/components/jit-optimizations.js
+++ b/devtools/client/performance/components/jit-optimizations.js
@@ -12,16 +12,18 @@ const { DOM: dom, createClass, createFac
 const Tree = createFactory(require("../../shared/components/tree"));
 const OptimizationsItem = createFactory(require("./jit-optimizations-item"));
 const FrameView = createFactory(require("../../shared/components/frame"));
 const JIT_TITLE = L10N.getStr("jit.title");
 // If TREE_ROW_HEIGHT changes, be sure to change `var(--jit-tree-row-height)`
 // in `devtools/client/themes/jit-optimizations.css`
 const TREE_ROW_HEIGHT = 14;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /* eslint-disable no-unused-vars */
 /**
  * TODO - Re-enable this eslint rule. The JIT tool is a work in progress, and isn't fully
  *        integrated as of yet, and this may represent intended functionality.
  */
 const onClickTooltipString = frame =>
   L10N.getFormatStr("viewsourceindebugger",
                     `${frame.source}:${frame.line}:${frame.column}`);
@@ -56,16 +58,18 @@ const optimizationSiteModel = {
     attempts: PropTypes.arrayOf(optimizationAttemptModel).isRequired,
     types: PropTypes.arrayOf(optimizationIonTypeModel).isRequired,
   }).isRequired,
 };
 
 const JITOptimizations = createClass({
   displayName: "JITOptimizations",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     onViewSourceInDebugger: PropTypes.func.isRequired,
     frameData: PropTypes.object.isRequired,
     optimizationSites: PropTypes.arrayOf(optimizationSiteModel).isRequired,
     autoExpandDepth: PropTypes.number,
   },
 
   getDefaultProps() {
--- a/devtools/client/performance/components/waterfall-tree.js
+++ b/devtools/client/performance/components/waterfall-tree.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
 const Tree = createFactory(require("devtools/client/shared/components/tree"));
 const WaterfallTreeRow = createFactory(require("./waterfall-tree-row"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Keep in sync with var(--waterfall-tree-row-height) in performance.css
 const WATERFALL_TREE_ROW_HEIGHT = 15; // px
 
 /**
  * Checks if a given marker is in the specified time range.
  *
  * @param object e
  *        The marker containing the { start, end } timestamps.
@@ -36,16 +38,18 @@ function isMarkerInRange(e, start, end) 
     // overlap end
     (mEnd > end && mStart >= start && mStart <= end)
   );
 }
 
 const WaterfallTree = createClass({
   displayName: "WaterfallTree",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     marker: PropTypes.object.isRequired,
     startTime: PropTypes.number.isRequired,
     endTime: PropTypes.number.isRequired,
     dataScale: PropTypes.number.isRequired,
     sidebarWidth: PropTypes.number.isRequired,
     waterfallWidth: PropTypes.number.isRequired,
     onFocus: PropTypes.func,
--- a/devtools/client/responsive.html/app.js
+++ b/devtools/client/responsive.html/app.js
@@ -27,19 +27,23 @@ const {
   resizeViewport,
   rotateViewport,
 } = require("./actions/viewports");
 const DeviceModal = createFactory(require("./components/device-modal"));
 const GlobalToolbar = createFactory(require("./components/global-toolbar"));
 const Viewports = createFactory(require("./components/viewports"));
 const Types = require("./types");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 let App = createClass({
   displayName: "App",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     dispatch: PropTypes.func.isRequired,
     displayPixelRatio: Types.pixelRatio.value.isRequired,
     networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
     screenshot: PropTypes.shape(Types.screenshot).isRequired,
     touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
     viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
--- a/devtools/client/responsive.html/components/browser.js
+++ b/devtools/client/responsive.html/components/browser.js
@@ -10,33 +10,35 @@ const { Task } = require("devtools/share
 const flags = require("devtools/shared/flags");
 const { getToplevelWindow } = require("../utils/window");
 const { DOM: dom, createClass, addons, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const e10s = require("../utils/e10s");
 const message = require("../utils/message");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   /**
    * This component is not allowed to depend directly on frequently changing
    * data (width, height) due to the use of `dangerouslySetInnerHTML` below.
    * Any changes in props will cause the <iframe> to be removed and added again,
    * throwing away the current state of the page.
    */
   displayName: "Browser",
 
   propTypes: {
     swapAfterMount: PropTypes.bool.isRequired,
     onBrowserMounted: PropTypes.func.isRequired,
     onContentResize: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   /**
    * Once the browser element has mounted, load the frame script and enable
    * various features, like floating scrollbars.
    */
   componentDidMount: Task.async(function* () {
     // If we are not swapping browsers after mount, it's safe to start the frame
     // script now.
--- a/devtools/client/responsive.html/components/device-adder.js
+++ b/devtools/client/responsive.html/components/device-adder.js
@@ -8,26 +8,28 @@
 
 const { DOM: dom, createClass, createFactory, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 
 const { getFormatStr, getStr } = require("../utils/l10n");
 const Types = require("../types");
 const ViewportDimension = createFactory(require("./viewport-dimension"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "DeviceAdder",
 
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     viewportTemplate: PropTypes.shape(Types.viewport).isRequired,
     onAddCustomDevice: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   getInitialState() {
     return {};
   },
 
   componentWillReceiveProps(nextProps) {
     let {
       width,
--- a/devtools/client/responsive.html/components/device-modal.js
+++ b/devtools/client/responsive.html/components/device-modal.js
@@ -8,30 +8,32 @@
 
 const { DOM: dom, createClass, createFactory, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 
 const { getStr, getFormatStr } = require("../utils/l10n");
 const Types = require("../types");
 const DeviceAdder = createFactory(require("./device-adder"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "DeviceModal",
 
   propTypes: {
     deviceAdderViewportTemplate: PropTypes.shape(Types.viewport).isRequired,
     devices: PropTypes.shape(Types.devices).isRequired,
     onAddCustomDevice: PropTypes.func.isRequired,
     onDeviceListUpdate: PropTypes.func.isRequired,
     onRemoveCustomDevice: PropTypes.func.isRequired,
     onUpdateDeviceDisplayed: PropTypes.func.isRequired,
     onUpdateDeviceModal: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   getInitialState() {
     return {};
   },
 
   componentDidMount() {
     window.addEventListener("keydown", this.onKeyDown, true);
   },
--- a/devtools/client/responsive.html/components/device-selector.js
+++ b/devtools/client/responsive.html/components/device-selector.js
@@ -6,29 +6,31 @@
 
 const { getStr } = require("../utils/l10n");
 const { DOM: dom, createClass, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 
 const Types = require("../types");
 const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "DeviceSelector",
 
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     selectedDevice: PropTypes.string.isRequired,
     viewportId: PropTypes.number.isRequired,
     onChangeDevice: PropTypes.func.isRequired,
     onResizeViewport: PropTypes.func.isRequired,
     onUpdateDeviceModal: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   onSelectChange({ target }) {
     let {
       devices,
       viewportId,
       onChangeDevice,
       onResizeViewport,
       onUpdateDeviceModal,
--- a/devtools/client/responsive.html/components/dpr-selector.js
+++ b/devtools/client/responsive.html/components/dpr-selector.js
@@ -7,16 +7,18 @@
 "use strict";
 
 const { DOM: dom, createClass, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 
 const Types = require("../types");
 const { getStr, getFormatStr } = require("../utils/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const PIXEL_RATIO_PRESET = [1, 2, 3];
 
 const createVisibleOption = value =>
   dom.option({
     value,
     title: value,
     key: value,
   }, value);
@@ -35,17 +37,17 @@ module.exports = createClass({
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     displayPixelRatio: Types.pixelRatio.value.isRequired,
     selectedDevice: PropTypes.string.isRequired,
     selectedPixelRatio: PropTypes.shape(Types.pixelRatio).isRequired,
     onChangePixelRatio: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   getInitialState() {
     return {
       isFocused: false
     };
   },
 
   onFocusChange({type}) {
--- a/devtools/client/responsive.html/components/global-toolbar.js
+++ b/devtools/client/responsive.html/components/global-toolbar.js
@@ -7,16 +7,18 @@
 const { DOM: dom, createClass, createFactory, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 
 const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 const DPRSelector = createFactory(require("./dpr-selector"));
 const NetworkThrottlingSelector = createFactory(require("./network-throttling-selector"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "GlobalToolbar",
 
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     displayPixelRatio: Types.pixelRatio.value.isRequired,
     networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
     screenshot: PropTypes.shape(Types.screenshot).isRequired,
@@ -25,17 +27,17 @@ module.exports = createClass({
     touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
     onChangeNetworkThrottling: PropTypes.func.isRequired,
     onChangePixelRatio: PropTypes.func.isRequired,
     onChangeTouchSimulation: PropTypes.func.isRequired,
     onExit: PropTypes.func.isRequired,
     onScreenshot: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   render() {
     let {
       devices,
       displayPixelRatio,
       networkThrottling,
       screenshot,
       selectedDevice,
--- a/devtools/client/responsive.html/components/network-throttling-selector.js
+++ b/devtools/client/responsive.html/components/network-throttling-selector.js
@@ -6,26 +6,28 @@
 
 const { DOM: dom, createClass, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 const throttlingProfiles = require("devtools/client/shared/network-throttling-profiles");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "NetworkThrottlingSelector",
 
   propTypes: {
     networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
     onChangeNetworkThrottling: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [addons.PureRenderMixin, ...perfService.mixins ],
 
   onSelectChange({ target }) {
     let {
       onChangeNetworkThrottling,
     } = this.props;
 
     if (target.value == getStr("responsive.noThrottling")) {
       onChangeNetworkThrottling(false, "");
--- a/devtools/client/responsive.html/components/resizable-viewport.js
+++ b/devtools/client/responsive.html/components/resizable-viewport.js
@@ -9,23 +9,27 @@
 const { DOM: dom, createClass, createFactory, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Constants = require("../constants");
 const Types = require("../types");
 const Browser = createFactory(require("./browser"));
 const ViewportToolbar = createFactory(require("./viewport-toolbar"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const VIEWPORT_MIN_WIDTH = Constants.MIN_VIEWPORT_DIMENSION;
 const VIEWPORT_MIN_HEIGHT = Constants.MIN_VIEWPORT_DIMENSION;
 
 module.exports = createClass({
 
   displayName: "ResizableViewport",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     screenshot: PropTypes.shape(Types.screenshot).isRequired,
     swapAfterMount: PropTypes.bool.isRequired,
     viewport: PropTypes.shape(Types.viewport).isRequired,
     onBrowserMounted: PropTypes.func.isRequired,
     onChangeDevice: PropTypes.func.isRequired,
     onContentResize: PropTypes.func.isRequired,
--- a/devtools/client/responsive.html/components/viewport-dimension.js
+++ b/devtools/client/responsive.html/components/viewport-dimension.js
@@ -5,19 +5,23 @@
 "use strict";
 
 const { DOM: dom, createClass, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Constants = require("../constants");
 const Types = require("../types");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "ViewportDimension",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     viewport: PropTypes.shape(Types.viewport).isRequired,
     onChangeSize: PropTypes.func.isRequired,
     onRemoveDeviceAssociation: PropTypes.func.isRequired,
   },
 
   getInitialState() {
     let { width, height } = this.props.viewport;
--- a/devtools/client/responsive.html/components/viewport-toolbar.js
+++ b/devtools/client/responsive.html/components/viewport-toolbar.js
@@ -6,29 +6,31 @@
 
 const { DOM: dom, createClass, createFactory, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 
 const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 const DeviceSelector = createFactory(require("./device-selector"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "ViewportToolbar",
 
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     viewport: PropTypes.shape(Types.viewport).isRequired,
     onChangeDevice: PropTypes.func.isRequired,
     onResizeViewport: PropTypes.func.isRequired,
     onRotateViewport: PropTypes.func.isRequired,
     onUpdateDeviceModal: PropTypes.func.isRequired,
   },
 
-  mixins: [ addons.PureRenderMixin ],
+  mixins: [ addons.PureRenderMixin, ...perfService.mixins ],
 
   render() {
     let {
       devices,
       viewport,
       onChangeDevice,
       onResizeViewport,
       onRotateViewport,
--- a/devtools/client/responsive.html/components/viewport.js
+++ b/devtools/client/responsive.html/components/viewport.js
@@ -6,20 +6,24 @@
 
 const { DOM: dom, createClass, createFactory, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Types = require("../types");
 const ResizableViewport = createFactory(require("./resizable-viewport"));
 const ViewportDimension = createFactory(require("./viewport-dimension"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "Viewport",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     screenshot: PropTypes.shape(Types.screenshot).isRequired,
     swapAfterMount: PropTypes.bool.isRequired,
     viewport: PropTypes.shape(Types.viewport).isRequired,
     onBrowserMounted: PropTypes.func.isRequired,
     onChangeDevice: PropTypes.func.isRequired,
     onContentResize: PropTypes.func.isRequired,
--- a/devtools/client/responsive.html/components/viewports.js
+++ b/devtools/client/responsive.html/components/viewports.js
@@ -5,20 +5,24 @@
 "use strict";
 
 const { DOM: dom, createClass, createFactory, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Types = require("../types");
 const Viewport = createFactory(require("./viewport"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
 
   displayName: "Viewports",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     screenshot: PropTypes.shape(Types.screenshot).isRequired,
     viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
     onBrowserMounted: PropTypes.func.isRequired,
     onChangeDevice: PropTypes.func.isRequired,
     onContentResize: PropTypes.func.isRequired,
     onRemoveDeviceAssociation: PropTypes.func.isRequired,
--- a/devtools/client/shared/components/autocomplete-popup.js
+++ b/devtools/client/shared/components/autocomplete-popup.js
@@ -1,19 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "AutocompletePopup",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     /**
      * autocompleteProvider takes search-box's entire input text as `filter` argument
      * ie. "is:cached pr"
      * returned value is array of objects like below
      * [{value: "is:cached protocol", displayValue: "protocol"}[, ...]]
      * `value` is used to update the search-box input box for given item
      * `displayValue` is used to render the autocomplete list
--- a/devtools/client/shared/components/frame.js
+++ b/devtools/client/shared/components/frame.js
@@ -4,22 +4,26 @@
 
 "use strict";
 
 const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 const { getSourceNames, parseURL,
         isScratchpadScheme, getSourceMappedFile } = require("devtools/client/shared/source-utils");
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const l10n = new LocalizationHelper("devtools/client/locales/components.properties");
 const webl10n = new LocalizationHelper("devtools/client/locales/webconsole.properties");
 
 module.exports = createClass({
   displayName: "Frame",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // SavedFrame, or an object containing all the required properties.
     frame: PropTypes.shape({
       functionDisplayName: PropTypes.string,
       source: PropTypes.string.isRequired,
       line: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
       column: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
     }).isRequired,
--- a/devtools/client/shared/components/h-split-box.js
+++ b/devtools/client/shared/components/h-split-box.js
@@ -25,19 +25,23 @@
 
 const {
   DOM: dom,
   createClass,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { assert } = require("devtools/shared/DevToolsUtils");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 module.exports = createClass({
   displayName: "HSplitBox",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // The contents of the start pane.
     start: PropTypes.any.isRequired,
 
     // The contents of the end pane.
     end: PropTypes.any.isRequired,
 
     // The relative width of the start pane, expressed as a number between 0 and
--- a/devtools/client/shared/components/notification-box.js
+++ b/devtools/client/shared/components/notification-box.js
@@ -4,16 +4,18 @@
 
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const Immutable = require("devtools/client/shared/vendor/immutable");
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const l10n = new LocalizationHelper("devtools/client/locales/components.properties");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const { PropTypes, createClass, DOM } = React;
 const { div, span, button } = DOM;
 
 // Priority Levels
 const PriorityLevels = {
   PRIORITY_INFO_LOW: 1,
   PRIORITY_INFO_MEDIUM: 2,
@@ -32,16 +34,18 @@ const PriorityLevels = {
  * <xul:notificationbox> binding.
  *
  * See also MDN for more info about <xul:notificationbox>:
  * https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/notificationbox
  */
 var NotificationBox = createClass({
   displayName: "NotificationBox",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // List of notifications appended into the box.
     notifications: PropTypes.arrayOf(PropTypes.shape({
       // label to appear on the notification.
       label: PropTypes.string.isRequired,
 
       // Value used to identify the notification
       value: PropTypes.string.isRequired,
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -5005,28 +5005,31 @@ module.exports = {
 
 /***/ }),
 /* 58 */
 /***/ (function(module, exports, __webpack_require__) {
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+const {perfService} = require("devtools/shared/perfService");
 
 const { DOM: dom, createClass, createFactory, PropTypes } = __webpack_require__(0);
 
 const AUTO_EXPAND_DEPTH = 0; // depth
 
 /**
  * An arrow that displays whether its node is expanded (▼) or collapsed
  * (▶). When its node has no children, it is hidden.
  */
 const ArrowExpander = createFactory(createClass({
   displayName: "ArrowExpander",
 
+  mixins: perfService.mixins,
+
   shouldComponentUpdate(nextProps, nextState) {
     return this.props.item !== nextProps.item || this.props.visible !== nextProps.visible || this.props.expanded !== nextProps.expanded;
   },
 
   render() {
     const attrs = {
       className: "arrow theme-twisty",
       onClick: this.props.expanded ? () => this.props.onCollapse(this.props.item) : e => this.props.onExpand(this.props.item, e.altKey)
@@ -5044,16 +5047,18 @@ const ArrowExpander = createFactory(crea
 
     return dom.div(attrs, this.props.children);
   }
 }));
 
 const TreeNode = createFactory(createClass({
   displayName: "TreeNode",
 
+  mixins: perfService.mixins,
+
   componentDidMount() {
     if (this.props.focused) {
       this.refs.button.focus();
     }
   },
 
   componentDidUpdate() {
     if (this.props.focused) {
@@ -5138,16 +5143,18 @@ const NUMBER_OF_OFFSCREEN_ITEMS = 1;
  * A generic tree component. See propTypes for the public API.
  *
  * @see `devtools/client/memory/components/test/mochitest/head.js` for usage
  * @see `devtools/client/memory/components/heap.js` for usage
  */
 const Tree = module.exports = createClass({
   displayName: "Tree",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // Required props
 
     // A function to get an item's parent, or null if it is a root.
     getParent: PropTypes.func.isRequired,
     // A function to get an item's children.
     getChildren: PropTypes.func.isRequired,
     // A function which takes an item and ArrowExpander and returns a
@@ -5726,37 +5733,37 @@ var freeGlobal = typeof global == 'objec
 module.exports = freeGlobal;
 
 /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(65)))
 
 /***/ }),
 /* 65 */
 /***/ (function(module, exports) {
 
-var g;
-
-// This works in non-strict mode
-g = (function() {
-	return this;
-})();
-
-try {
-	// This works if eval is allowed (see CSP)
-	g = g || Function("return this")() || (1,eval)("this");
-} catch(e) {
-	// This works if the window reference is available
-	if(typeof window === "object")
-		g = window;
-}
-
-// g can still be undefined, but nothing to do about it...
-// We return undefined, instead of nothing here, so it's
-// easier to handle this case. if(!global) { ...}
-
-module.exports = g;
+var g;
+
+// This works in non-strict mode
+g = (function() {
+	return this;
+})();
+
+try {
+	// This works if eval is allowed (see CSP)
+	g = g || Function("return this")() || (1,eval)("this");
+} catch(e) {
+	// This works if the window reference is available
+	if(typeof window === "object")
+		g = window;
+}
+
+// g can still be undefined, but nothing to do about it...
+// We return undefined, instead of nothing here, so it's
+// easier to handle this case. if(!global) { ...}
+
+module.exports = g;
 
 
 /***/ }),
 /* 66 */
 /***/ (function(module, exports, __webpack_require__) {
 
 var Symbol = __webpack_require__(13);
 
--- a/devtools/client/shared/components/search-box.js
+++ b/devtools/client/shared/components/search-box.js
@@ -5,22 +5,26 @@
 /* global window */
 
 "use strict";
 
 const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const AutocompletePopup = createFactory(require("devtools/client/shared/components/autocomplete-popup"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * A generic search box component for use across devtools
  */
 module.exports = createClass({
   displayName: "SearchBox",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     delay: PropTypes.number,
     keyShortcut: PropTypes.string,
     onChange: PropTypes.func,
     placeholder: PropTypes.string,
     type: PropTypes.string,
     autocompleteProvider: PropTypes.func,
   },
--- a/devtools/client/shared/components/sidebar-toggle.js
+++ b/devtools/client/shared/components/sidebar-toggle.js
@@ -3,26 +3,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { DOM, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const { button } = DOM;
 
 /**
  * Sidebar toggle button. This button is used to exapand
  * and collapse Sidebar.
  */
 var SidebarToggle = createClass({
   displayName: "SidebarToggle",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // Set to true if collapsed.
     collapsed: PropTypes.bool.isRequired,
     // Tooltip text used when the button indicates expanded state.
     collapsePaneTitle: PropTypes.string.isRequired,
     // Tooltip text used when the button indicates collapsed state.
     expandPaneTitle: PropTypes.string.isRequired,
     // Click callback
--- a/devtools/client/shared/components/splitter/draggable.js
+++ b/devtools/client/shared/components/splitter/draggable.js
@@ -3,19 +3,23 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { DOM: dom, PropTypes } = React;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const Draggable = React.createClass({
   displayName: "Draggable",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     onMove: PropTypes.func.isRequired,
     onStart: PropTypes.func,
     onStop: PropTypes.func,
     style: PropTypes.object,
     className: PropTypes.string
   },
 
--- a/devtools/client/shared/components/splitter/split-box.js
+++ b/devtools/client/shared/components/splitter/split-box.js
@@ -4,23 +4,27 @@
 
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const Draggable = React.createFactory(require("devtools/client/shared/components/splitter/draggable"));
 const { DOM: dom, PropTypes } = React;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 /**
  * This component represents a Splitter. The splitter supports vertical
  * as well as horizontal mode.
  */
 const SplitBox = React.createClass({
   displayName: "SplitBox",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // Custom class name. You can use more names separated by a space.
     className: PropTypes.string,
     // Initial size of controlled panel.
     initialSize: PropTypes.string,
     // Initial width of controlled panel.
     initialWidth: PropTypes.string,
     // Initial height of controlled panel.
--- a/devtools/client/shared/components/stack-trace.js
+++ b/devtools/client/shared/components/stack-trace.js
@@ -6,19 +6,23 @@
 
 const React = require("devtools/client/shared/vendor/react");
 const { DOM: dom, createClass, createFactory, PropTypes } = React;
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const Frame = createFactory(require("./frame"));
 
 const l10n = new LocalizationHelper("devtools/client/locales/webconsole.properties");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const AsyncFrame = createFactory(createClass({
   displayName: "AsyncFrame",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     asyncCause: PropTypes.string.isRequired
   },
 
   render() {
     let { asyncCause } = this.props;
 
     return dom.span(
@@ -26,16 +30,18 @@ const AsyncFrame = createFactory(createC
       l10n.getFormatStr("stacktrace.asyncStack", asyncCause)
     );
   }
 }));
 
 const StackTrace = createClass({
   displayName: "StackTrace",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     stacktrace: PropTypes.array.isRequired,
     onViewSourceInDebugger: PropTypes.func.isRequired,
     onViewSourceInScratchpad: PropTypes.func,
     // Service to enable the source map feature.
     sourceMapService: PropTypes.object,
   },
 
--- a/devtools/client/shared/components/tabs/tabbar.js
+++ b/devtools/client/shared/components/tabs/tabbar.js
@@ -9,25 +9,29 @@
 "use strict";
 
 const { DOM, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
 const Tabs = createFactory(require("devtools/client/shared/components/tabs/tabs").Tabs);
 
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const { div } = DOM;
 
 /**
  * Renders Tabbar component.
  */
 let Tabbar = createClass({
   displayName: "Tabbar",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     children: PropTypes.array,
     onSelect: PropTypes.func,
     showAllTabsMenu: PropTypes.bool,
     activeTabId: PropTypes.string,
     renderOnlySelected: PropTypes.bool,
   },
 
--- a/devtools/client/shared/components/tabs/tabs.js
+++ b/devtools/client/shared/components/tabs/tabs.js
@@ -6,16 +6,18 @@
 
 "use strict";
 
 define(function (require, exports, module) {
   const React = require("devtools/client/shared/vendor/react");
   const { DOM } = React;
   const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 
+  const {perfService} = require("devtools/shared/perfService");
+
   /**
    * Renders simple 'tab' widget.
    *
    * Based on ReactSimpleTabs component
    * https://github.com/pedronauck/react-simpletabs
    *
    * Component markup (+CSS) example:
    *
@@ -29,16 +31,18 @@ define(function (require, exports, modul
    *  <div class='panels'>
    *    The content of active panel here
    *  </div>
    * <div>
    */
   let Tabs = React.createClass({
     displayName: "Tabs",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       className: React.PropTypes.oneOfType([
         React.PropTypes.array,
         React.PropTypes.string,
         React.PropTypes.object
       ]),
       tabActive: React.PropTypes.number,
       onMount: React.PropTypes.func,
@@ -365,16 +369,18 @@ define(function (require, exports, modul
   });
 
   /**
    * Renders simple tab 'panel'.
    */
   let Panel = React.createClass({
     displayName: "Panel",
 
+    mixins: perfService.mixins,
+
     propTypes: {
       title: React.PropTypes.string.isRequired,
       children: React.PropTypes.oneOfType([
         React.PropTypes.array,
         React.PropTypes.element
       ]).isRequired
     },
 
--- a/devtools/client/shared/components/tree.js
+++ b/devtools/client/shared/components/tree.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint-env browser */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const { DOM: dom, createClass, createFactory, PropTypes } = React;
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const AUTO_EXPAND_DEPTH = 0;
 const NUMBER_OF_OFFSCREEN_ITEMS = 1;
 
 /**
  * A fast, generic, expandable and collapsible tree component.
  *
  * This tree component is fast: it can handle trees with *many* items. It only
  * renders the subset of those items which are visible in the viewport. It's
@@ -95,16 +97,18 @@ const NUMBER_OF_OFFSCREEN_ITEMS = 1;
  *           onCollapse: item => dispatchCollapseActionToRedux(item),
  *         });
  *       }
  *     });
  */
 module.exports = createClass({
   displayName: "Tree",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     // Required props
 
     // A function to get an item's parent, or null if it is a root.
     //
     // Type: getParent(item: Item) -> Maybe<Item>
     //
     // Example:
@@ -660,16 +664,18 @@ module.exports = createClass({
 
 /**
  * An arrow that displays whether its node is expanded (▼) or collapsed
  * (▶). When its node has no children, it is hidden.
  */
 const ArrowExpander = createFactory(createClass({
   displayName: "ArrowExpander",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     item: PropTypes.any.isRequired,
     visible: PropTypes.bool.isRequired,
     expanded: PropTypes.bool.isRequired,
     onCollapse: PropTypes.func.isRequired,
     onExpand: PropTypes.func.isRequired,
   },
 
--- a/devtools/client/shared/components/tree/label-cell.js
+++ b/devtools/client/shared/components/tree/label-cell.js
@@ -5,26 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
+  const {perfService} = require("devtools/shared/perfService");
+
   // Shortcuts
   const { td, span } = React.DOM;
   const PropTypes = React.PropTypes;
 
   /**
    * Render the default cell used for toggle buttons
    */
   let LabelCell = React.createClass({
     displayName: "LabelCell",
 
+    mixins: perfService.mixins,
+
     // See the TreeView component for details related
     // to the 'member' object.
     propTypes: {
       id: PropTypes.string.isRequired,
       member: PropTypes.object.isRequired
     },
 
     render: function () {
--- a/devtools/client/shared/components/tree/tree-cell.js
+++ b/devtools/client/shared/components/tree/tree-cell.js
@@ -4,27 +4,31 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   const React = require("devtools/client/shared/vendor/react");
 
+  const {perfService} = require("devtools/shared/perfService");
+
   // Shortcuts
   const { input, span, td } = React.DOM;
   const PropTypes = React.PropTypes;
 
   /**
    * This template represents a cell in TreeView row. It's rendered
    * using <td> element (the row is <tr> and the entire tree is <table>).
    */
   let TreeCell = React.createClass({
     displayName: "TreeCell",
 
+    mixins: perfService.mixins,
+
     // See TreeView component for detailed property explanation.
     propTypes: {
       value: PropTypes.any,
       decorator: PropTypes.object,
       id: PropTypes.string.isRequired,
       member: PropTypes.object.isRequired,
       renderValue: PropTypes.func.isRequired,
       enableInput: PropTypes.bool,
--- a/devtools/client/shared/components/tree/tree-header.js
+++ b/devtools/client/shared/components/tree/tree-header.js
@@ -5,27 +5,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
+  const {perfService} = require("devtools/shared/perfService");
+
   // Shortcuts
   const { thead, tr, td, div } = React.DOM;
   const PropTypes = React.PropTypes;
 
   /**
    * This component is responsible for rendering tree header.
    * It's based on <thead> element.
    */
   let TreeHeader = React.createClass({
     displayName: "TreeHeader",
 
+    mixins: perfService.mixins,
+
     // See also TreeView component for detailed info about properties.
     propTypes: {
       // Custom tree decorator
       decorator: PropTypes.object,
       // True if the header should be visible
       header: PropTypes.bool,
       // Array with column definition
       columns: PropTypes.array
--- a/devtools/client/shared/components/tree/tree-row.js
+++ b/devtools/client/shared/components/tree/tree-row.js
@@ -13,27 +13,31 @@ define(function (require, exports, modul
 
   // Tree
   const TreeCell = React.createFactory(require("./tree-cell"));
   const LabelCell = React.createFactory(require("./label-cell"));
 
   // Scroll
   const { scrollIntoViewIfNeeded } = require("devtools/client/shared/scroll");
 
+  const {perfService} = require("devtools/shared/perfService");
+
   // Shortcuts
   const { tr } = React.DOM;
   const PropTypes = React.PropTypes;
 
   /**
    * This template represents a node in TreeView component. It's rendered
    * using <tr> element (the entire tree is one big <table>).
    */
   let TreeRow = React.createClass({
     displayName: "TreeRow",
 
+    mixins: perfService.mixins,
+
     // See TreeView component for more details about the props and
     // the 'member' object.
     propTypes: {
       member: PropTypes.shape({
         object: PropTypes.obSject,
         name: PropTypes.sring,
         type: PropTypes.string.isRequired,
         rowClass: PropTypes.string.isRequired,
--- a/devtools/client/shared/components/tree/tree-view.js
+++ b/devtools/client/shared/components/tree/tree-view.js
@@ -10,16 +10,18 @@ define(function (require, exports, modul
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
   const { ObjectProvider } = require("./object-provider");
   const TreeRow = React.createFactory(require("./tree-row"));
   const TreeHeader = React.createFactory(require("./tree-header"));
 
+  const {perfService} = require("devtools/shared/perfService");
+
   // Shortcuts
   const DOM = React.DOM;
   const PropTypes = React.PropTypes;
 
   /**
    * This component represents a tree view with expandable/collapsible nodes.
    * The tree is rendered using <table> element where every node is represented
    * by <tr> element. The tree is one big table where nodes (rows) are properly
@@ -51,16 +53,18 @@ define(function (require, exports, modul
    *   renderRow: function(object);
    *   renderCell: function(object, colId);
    *   renderLabelCell: function(object);
    * }
    */
   let TreeView = React.createClass({
     displayName: "TreeView",
 
+    mixins: perfService.mixins,
+
     // The only required property (not set by default) is the input data
     // object that is used to puputate the tree.
     propTypes: {
       // The input data object.
       object: PropTypes.any,
       className: PropTypes.string,
       label: PropTypes.string,
       // Data provider (see also the interface above)
--- a/devtools/client/shared/vendor/react-dev.js
+++ b/devtools/client/shared/vendor/react-dev.js
@@ -22916,9 +22916,9 @@ module.exports = shouldUseNative() ? Obj
 			}
 		}
 	}
 
 	return to;
 };
 
 },{}]},{},[110])(110)
-});
\ No newline at end of file
+});
--- a/devtools/client/webconsole/net/components/cookies-tab.js
+++ b/devtools/client/webconsole/net/components/cookies-tab.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const NetInfoGroupList = React.createFactory(require("./net-info-group-list"));
 const Spinner = React.createFactory(require("./spinner"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template represents 'Cookies' tab displayed when the user
  * expands network log in the Console panel. It's responsible for rendering
  * sent and received cookies.
@@ -21,16 +23,18 @@ var CookiesTab = React.createClass({
     actions: PropTypes.shape({
       requestData: PropTypes.func.isRequired
     }),
     data: PropTypes.object.isRequired,
   },
 
   displayName: "CookiesTab",
 
+  mixins: perfService.mixins,
+
   componentDidMount() {
     let { actions, data } = this.props;
     let requestCookies = data.request.cookies;
     let responseCookies = data.response.cookies;
 
     // TODO: use async action objects as soon as Redux is in place
     if (!requestCookies || !requestCookies.length) {
       actions.requestData("requestCookies");
--- a/devtools/client/webconsole/net/components/headers-tab.js
+++ b/devtools/client/webconsole/net/components/headers-tab.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const NetInfoGroupList = React.createFactory(require("./net-info-group-list"));
 const Spinner = React.createFactory(require("./spinner"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template represents 'Headers' tab displayed when the user
  * expands network log in the Console panel. It's responsible for rendering
  * request and response HTTP headers.
@@ -21,16 +23,18 @@ var HeadersTab = React.createClass({
     actions: PropTypes.shape({
       requestData: PropTypes.func.isRequired
     }),
     data: PropTypes.object.isRequired,
   },
 
   displayName: "HeadersTab",
 
+  mixins: perfService.mixins,
+
   componentDidMount() {
     let { actions, data } = this.props;
     let requestHeaders = data.request.headers;
     let responseHeaders = data.response.headers;
 
     // Request headers if they are not available yet.
     // TODO: use async action objects as soon as Redux is in place
     if (!requestHeaders) {
--- a/devtools/client/webconsole/net/components/net-info-body.js
+++ b/devtools/client/webconsole/net/components/net-info-body.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const { createFactories } = require("devtools/client/shared/react-utils");
 const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/tabs"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Network
 const HeadersTab = React.createFactory(require("./headers-tab"));
 const ResponseTab = React.createFactory(require("./response-tab"));
 const ParamsTab = React.createFactory(require("./params-tab"));
 const CookiesTab = React.createFactory(require("./cookies-tab"));
 const PostTab = React.createFactory(require("./post-tab"));
 const StackTraceTab = React.createFactory(require("./stacktrace-tab"));
 const NetUtils = require("../utils/net");
@@ -40,16 +42,18 @@ var NetInfoBody = React.createClass({
       response: PropTypes.object.isRequired
     }),
     // Service to enable the source map feature.
     sourceMapService: PropTypes.object,
   },
 
   displayName: "NetInfoBody",
 
+  mixins: perfService.mixins,
+
   getDefaultProps() {
     return {
       tabActive: 0
     };
   },
 
   getInitialState() {
     return {
--- a/devtools/client/webconsole/net/components/net-info-group-list.js
+++ b/devtools/client/webconsole/net/components/net-info-group-list.js
@@ -1,31 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const NetInfoGroup = React.createFactory(require("./net-info-group"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template is responsible for rendering sections/groups inside tabs.
  * It's used e.g to display Response and Request headers as separate groups.
  */
 var NetInfoGroupList = React.createClass({
   propTypes: {
     groups: PropTypes.array.isRequired,
   },
 
   displayName: "NetInfoGroupList",
 
+  mixins: perfService.mixins,
+
   render() {
     let groups = this.props.groups;
 
     // Filter out empty groups.
     groups = groups.filter(group => {
       return group && ((group.params && group.params.length) || group.content);
     });
 
--- a/devtools/client/webconsole/net/components/net-info-group.js
+++ b/devtools/client/webconsole/net/components/net-info-group.js
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const NetInfoParams = React.createFactory(require("./net-info-params"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template represents a group of data within a tab. For example,
  * Headers tab has two groups 'Request Headers' and 'Response Headers'
  * The Response tab can also have two groups 'Raw Data' and 'JSON'
@@ -21,16 +23,18 @@ var NetInfoGroup = React.createClass({
     name: PropTypes.string.isRequired,
     params: PropTypes.array,
     content: PropTypes.element,
     open: PropTypes.bool
   },
 
   displayName: "NetInfoGroup",
 
+  mixins: perfService.mixins,
+
   getDefaultProps() {
     return {
       open: true,
     };
   },
 
   getInitialState() {
     return {
--- a/devtools/client/webconsole/net/components/net-info-params.js
+++ b/devtools/client/webconsole/net/components/net-info-params.js
@@ -1,26 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template renders list of parameters within a group.
  * It's essentially a list of name + value pairs.
  */
 var NetInfoParams = React.createClass({
   displayName: "NetInfoParams",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     params: PropTypes.arrayOf(PropTypes.shape({
       name: PropTypes.string.isRequired,
       value: PropTypes.string.isRequired
     })).isRequired,
   },
 
   render() {
--- a/devtools/client/webconsole/net/components/params-tab.js
+++ b/devtools/client/webconsole/net/components/params-tab.js
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const NetInfoParams = React.createFactory(require("./net-info-params"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template represents 'Params' tab displayed when the user
  * expands network log in the Console panel. It's responsible for
  * displaying URL parameters (query string).
@@ -19,16 +21,18 @@ var ParamsTab = React.createClass({
   propTypes: {
     data: PropTypes.shape({
       request: PropTypes.object.isRequired
     })
   },
 
   displayName: "ParamsTab",
 
+  mixins: perfService.mixins,
+
   render() {
     let data = this.props.data;
 
     return (
       DOM.div({className: "paramsTabBox"},
         DOM.div({className: "panelContent"},
           NetInfoParams({params: data.request.queryString})
         )
--- a/devtools/client/webconsole/net/components/post-tab.js
+++ b/devtools/client/webconsole/net/components/post-tab.js
@@ -13,16 +13,18 @@ const { Rep } = REPS;
 // Network
 const NetInfoParams = React.createFactory(require("./net-info-params"));
 const NetInfoGroupList = React.createFactory(require("./net-info-group-list"));
 const Spinner = React.createFactory(require("./spinner"));
 const SizeLimit = React.createFactory(require("./size-limit"));
 const NetUtils = require("../utils/net");
 const Json = require("../utils/json");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template represents 'Post' tab displayed when the user
  * expands network log in the Console panel. It's responsible for
  * displaying posted data (HTTP post body).
@@ -32,16 +34,18 @@ var PostTab = React.createClass({
     data: PropTypes.shape({
       request: PropTypes.object.isRequired
     }),
     actions: PropTypes.object.isRequired
   },
 
   displayName: "PostTab",
 
+  mixins: perfService.mixins,
+
   isJson(file) {
     let text = file.request.postData.text;
     let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
     return Json.isJSON(value, text);
   },
 
   parseJson(file) {
     let postData = file.request.postData;
--- a/devtools/client/webconsole/net/components/response-tab.js
+++ b/devtools/client/webconsole/net/components/response-tab.js
@@ -12,16 +12,18 @@ const { Rep } = REPS;
 
 // Network
 const SizeLimit = React.createFactory(require("./size-limit"));
 const NetInfoGroupList = React.createFactory(require("./net-info-group-list"));
 const Spinner = React.createFactory(require("./spinner"));
 const Json = require("../utils/json");
 const NetUtils = require("../utils/net");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template represents 'Response' tab displayed when the user
  * expands network log in the Console panel. It's responsible for
  * rendering HTTP response body.
@@ -36,16 +38,18 @@ var ResponseTab = React.createClass({
       request: PropTypes.object.isRequired,
       response: PropTypes.object.isRequired
     }),
     actions: PropTypes.object.isRequired
   },
 
   displayName: "ResponseTab",
 
+  mixins: perfService.mixins,
+
   // Response Types
 
   isJson(content) {
     if (isLongString(content.text)) {
       return false;
     }
 
     return Json.isJSON(content.mimeType, content.text);
--- a/devtools/client/webconsole/net/components/size-limit.js
+++ b/devtools/client/webconsole/net/components/size-limit.js
@@ -1,15 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 const PropTypes = React.PropTypes;
 
 /**
  * This template represents a size limit notification message
  * used e.g. in the Response tab when response body exceeds
  * size limit. The message contains a link allowing the user
@@ -22,16 +24,18 @@ var SizeLimit = React.createClass({
     link: PropTypes.string.isRequired,
     actions: PropTypes.shape({
       resolveString: PropTypes.func.isRequired
     }),
   },
 
   displayName: "SizeLimit",
 
+  mixins: perfService.mixins,
+
   // Event Handlers
 
   onClickLimit(event) {
     let actions = this.props.actions;
     let content = this.props.data;
 
     actions.resolveString(content, "text");
   },
--- a/devtools/client/webconsole/net/components/spinner.js
+++ b/devtools/client/webconsole/net/components/spinner.js
@@ -1,25 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 // Shortcuts
 const DOM = React.DOM;
 
 /**
  * This template represents a throbber displayed when the UI
  * is waiting for data coming from the backend (debugging server).
  */
 var Spinner = React.createClass({
   displayName: "Spinner",
 
+  mixins: perfService.mixins,
+
   render() {
     return (
       DOM.div({className: "devtools-throbber"})
     );
   }
 });
 
 // Exports from this module
--- a/devtools/client/webconsole/net/components/stacktrace-tab.js
+++ b/devtools/client/webconsole/net/components/stacktrace-tab.js
@@ -1,19 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { PropTypes, createClass, createFactory } = require("devtools/client/shared/vendor/react");
 const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const StackTraceTab = createClass({
   displayName: "StackTraceTab",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     data: PropTypes.object.isRequired,
     actions: PropTypes.shape({
       onViewSourceInDebugger: PropTypes.func.isRequired
     }),
     // Service to enable the source map feature.
     sourceMapService: PropTypes.object,
   },
--- a/devtools/client/webconsole/new-console-output/components/console-output.js
+++ b/devtools/client/webconsole/new-console-output/components/console-output.js
@@ -21,20 +21,24 @@ const {
   getVisibleMessages,
   getAllRepeatById,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
 const {
   MESSAGE_TYPE,
 } = require("devtools/client/webconsole/new-console-output/constants");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const ConsoleOutput = createClass({
 
   displayName: "ConsoleOutput",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     messages: PropTypes.object.isRequired,
     messagesUi: PropTypes.object.isRequired,
     serviceContainer: PropTypes.shape({
       attachRefToHud: PropTypes.func.isRequired,
       openContextMenu: PropTypes.func.isRequired,
       sourceMapService: PropTypes.object,
     }),
--- a/devtools/client/webconsole/new-console-output/components/console-table.js
+++ b/devtools/client/webconsole/new-console-output/components/console-table.js
@@ -10,23 +10,27 @@ const {
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const { ObjectClient } = require("devtools/shared/client/main");
 const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
 const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
 const { MODE } = require("devtools/client/shared/components/reps/reps");
 const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const TABLE_ROW_MAX_ITEMS = 1000;
 const TABLE_COLUMN_MAX_ITEMS = 10;
 
 const ConsoleTable = createClass({
 
   displayName: "ConsoleTable",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     dispatch: PropTypes.func.isRequired,
     parameters: PropTypes.array.isRequired,
     serviceContainer: PropTypes.shape({
       hudProxyClient: PropTypes.object.isRequired,
     }),
     id: PropTypes.string.isRequired,
     tableData: PropTypes.object,
--- a/devtools/client/webconsole/new-console-output/components/filter-bar.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js
@@ -17,20 +17,24 @@ const {
   messagesClear
 } = require("devtools/client/webconsole/new-console-output/actions/index");
 const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
 const {
   MESSAGE_LEVEL
 } = require("../constants");
 const FilterButton = require("devtools/client/webconsole/new-console-output/components/filter-button");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const FilterBar = createClass({
 
   displayName: "FilterBar",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     dispatch: PropTypes.func.isRequired,
     filter: PropTypes.object.isRequired,
     serviceContainer: PropTypes.shape({
       attachRefToHud: PropTypes.func.isRequired,
     }).isRequired,
     filterBarVisible: PropTypes.bool.isRequired,
   },
--- a/devtools/client/webconsole/new-console-output/components/message-container.js
+++ b/devtools/client/webconsole/new-console-output/components/message-container.js
@@ -12,28 +12,32 @@ const {
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 
 const {
   MESSAGE_SOURCE,
   MESSAGE_TYPE
 } = require("devtools/client/webconsole/new-console-output/constants");
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const componentMap = new Map([
   ["ConsoleApiCall", require("./message-types/console-api-call")],
   ["ConsoleCommand", require("./message-types/console-command")],
   ["DefaultRenderer", require("./message-types/default-renderer")],
   ["EvaluationResult", require("./message-types/evaluation-result")],
   ["NetworkEventMessage", require("./message-types/network-event-message")],
   ["PageError", require("./message-types/page-error")]
 ]);
 
 const MessageContainer = createClass({
   displayName: "MessageContainer",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     messageId: PropTypes.string.isRequired,
     open: PropTypes.bool.isRequired,
     serviceContainer: PropTypes.object.isRequired,
     tableData: PropTypes.object,
     timestampsVisible: PropTypes.bool.isRequired,
     repeat: PropTypes.number,
     networkMessageUpdate: PropTypes.object,
--- a/devtools/client/webconsole/new-console-output/components/message.js
+++ b/devtools/client/webconsole/new-console-output/components/message.js
@@ -18,19 +18,23 @@ const actions = require("devtools/client
 const {MESSAGE_SOURCE} = require("devtools/client/webconsole/new-console-output/constants");
 const CollapseButton = require("devtools/client/webconsole/new-console-output/components/collapse-button");
 const MessageIndent = require("devtools/client/webconsole/new-console-output/components/message-indent").MessageIndent;
 const MessageIcon = require("devtools/client/webconsole/new-console-output/components/message-icon");
 const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/message-repeat");
 const FrameView = createFactory(require("devtools/client/shared/components/frame"));
 const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
 
+loader.lazyRequireGetter(this, "perfService", "devtools/shared/perfService", true);
+
 const Message = createClass({
   displayName: "Message",
 
+  mixins: perfService.mixins,
+
   propTypes: {
     open: PropTypes.bool,
     collapsible: PropTypes.bool,
     collapseTitle: PropTypes.string,
     source: PropTypes.string.isRequired,
     type: PropTypes.string.isRequired,
     level: PropTypes.string.isRequired,
     indent: PropTypes.number.isRequired,
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -59,16 +59,17 @@ DevToolsModules(
     'indentation.js',
     'indexed-db.js',
     'l10n.js',
     'loader-plugin-raw.jsm',
     'Loader.jsm',
     'old-event-emitter.js',
     'Parser.jsm',
     'path.js',
+    'perfService.js',
     'plural-form.js',
     'protocol.js',
     'system.js',
     'task.js',
     'ThreadSafeDevToolsUtils.js',
     'wasm-source-map.js',
 )
 
new file mode 100644
--- /dev/null
+++ b/devtools/shared/perfService.js
@@ -0,0 +1,287 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* globals Services, perfService */
+
+"use strict";
+
+const { Cu } = require("chrome");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+class PerfService {
+  constructor(options) {
+    // For testing, so that we can use a fake Window.performance object with
+    // known state.
+    if (options && options.performanceObj) {
+      this._perf = options.performanceObj;
+    } else {
+      this._perf = Services.appShell.hiddenDOMWindow.performance;
+    }
+
+    this.startMeasure = this.startMeasure.bind(this);
+    this.stopMeasure = this.stopMeasure.bind(this);
+    this.dumpMeasures = this.dumpMeasures.bind(this);
+    this.storeValue = this.storeValue.bind(this);
+
+    this.markCountMap = new Map();
+    this.once = new Set();
+    this.values = new Map();
+
+    this.timingEnabled =
+      Services.prefs.getBoolPref("devtools.performance.measure.timing");
+  }
+
+  get mixins() {
+    let _mixins = [];
+
+    if (this.timingEnabled) {
+      _mixins.push(TimingMixin);
+    }
+
+    return _mixins;
+  }
+
+  /**
+   * Removes the named mark from the browser's performance entry buffer. If the method is
+   * called with no arguments, all performance entries with an entry type of "mark" will
+   * be removed from the performance entry buffer.
+   *
+   * @param  {String} name
+   *         The name of the timestamp
+   */
+  clearMarks(name) {
+    this._perf.clearMarks(name);
+  }
+
+  /**
+   * Removes the named measure from the browser's performance entry buffer. If the method
+   * is called with no arguments, all performance entries with an entry type of "measure"
+   * will be removed from the performance entry buffer.
+   *
+   * @param  {String} name
+   *         The name of the timestamp
+   */
+  clearMeasures(name) {
+    this._perf.clearMeasures(name);
+  }
+
+  /**
+   * Returns a list of PerformanceEntry objects for a given filter.
+   *
+   * @param  {Object} [filter]
+   *         An optional filter in the following format: {
+   *           name: "entry_name",
+   *           entryType: "mark",
+   *           initiatorType: Examples are "div", "css", "xmlhttprequest" or ""
+   *         }
+   */
+  getEntries(filter) {
+    this._perf.getEntries(filter);
+  }
+
+  /**
+   * Calls the underlying getEntriesByName on the appropriate Window.performance
+   * object.
+   *
+   * @param  {String} name
+   *         Name of the mark to get
+   * @param  {String}
+   *         Type e.g. "mark"
+   */
+  getEntriesByName(name, type) {
+    return this._perf.getEntriesByName(name, type);
+  }
+
+  /**
+   * Calls the underlying getEntriesByName on the appropriate Window.performance
+   * object.
+   *
+   * @param  {String}
+   *         Type e.g. "mark"
+   */
+  getEntriesByType(type) {
+    return this._perf.getEntriesByType(type);
+  }
+
+  /**
+   * Calls the underlying mark() method on the appropriate Window.performance object to
+   * add a mark with the given name to the appropriate performance timeline.
+   *
+   * @param  {String} name
+   *         Name to give the current mark
+   */
+  mark(str) {
+    this._perf.mark(str);
+  }
+
+  /**
+   * Creates a named timestamp in the browser's performance entry buffer between two
+   * specified marks.
+   *
+   * @param  {String} name
+   *         The name of the measure
+   * @param  {String} startMark
+   *         The name of the measure's start mark. If omitted the value 0 is used.
+   * @param  {String} endMark
+   *         The name of the measure's end mark. If omitted the value of
+   *         performance.now() is used.
+   */
+  measure(name, startMark, endMark) {
+    this._perf.measure(name, startMark, endMark);
+  }
+
+  /**
+   * Start measuring a tool's performance.
+   *
+   * @param {String} startMark
+   *        The name to use for the mark. To make the mark unique we add a
+   *        hyphen and index to the name e.g. devtoolbar-7.
+   */
+  startMeasure(startMark) {
+    if (!this.timingEnabled) {
+      return;
+    }
+
+    let nextIndex = this.getNextMarkIndex(startMark);
+    this.markCountMap.set(startMark, nextIndex);
+    this._perf.mark(`${startMark}-${nextIndex}`);
+  }
+
+  /**
+   * Stop measuring a tool's performance.
+   *
+   * @param {String} endMark
+   *        The name to use for the mark. To make the mark unique we add a
+   *        hyphen and index to the name e.g. devtoolbar-7.
+   */
+  stopMeasure(endMark) {
+    if (!this.timingEnabled) {
+      return;
+    }
+
+    let index = this.getCurrentMarkIndex(endMark);
+    endMark = `${endMark}-${index}`;
+
+    try {
+      // endMark defaults to performance.now() if omitted.
+      this._perf.measure(endMark, endMark);
+    } catch (e) {
+      // We get random "invalid string" messages for valid strings without
+      // try / catch so let's throw the error into the void.
+    }
+  }
+
+  /**
+   * Gets the current mark index.
+   *
+   * @param {String} mark
+   *        The name to use for the mark. To make the mark unique we add a
+   *        hyphen and index to the name e.g. devtoolbar-7.
+   */
+  getCurrentMarkIndex(mark) {
+    return this.markCountMap.get(mark) || 0;
+  }
+
+  /**
+   * Gets the next available mark index.
+   *
+   * @param {String} mark
+   *        The name to use for the mark. To make the mark unique we add a
+   *        hyphen and index to the name e.g. devtoolbar-7.
+   */
+  getNextMarkIndex(mark) {
+    let lastCount = this.markCountMap.get(mark) || 0;
+    return lastCount + 1;
+  }
+
+  /**
+   * Store a value linked to a particular mark e.g. a payload.
+   *
+   * @param {String} mark
+   *        The name to use for the mark. To make the mark unique we add a
+   *        hyphen and index to the name e.g. devtoolbar-7.
+   * @param {Object} newPropValue
+   *        Any value related to the mark.
+   */
+  storeValue(mark, newPropValue) {
+    let index = this.getCurrentMarkIndex(mark);
+    mark = `${mark}-${index}`;
+    this.values.set(mark, newPropValue);
+  }
+
+  /**
+   * Outputs the values produced by the devtools.performance.measure.timing
+   * preference.
+   */
+  dumpMeasures() {
+    if (!this.timingEnabled) {
+      return;
+    }
+
+    let measures = this._perf.getEntriesByType("measure");
+
+    console.group("DevTools Measures");
+    for (let {name, duration} of measures) {
+      let value = this.values.get(name);
+
+      if (typeof value === "undefined") {
+        console.log(name, duration);
+      } else {
+        console.log(name, duration, value);
+      }
+    }
+    console.groupEnd();
+  }
+}
+
+/**
+ * Timing mixin added using the devtools.performance.measure.timing preference.
+ */
+const TimingMixin = {
+  componentWillUpdate(nextProps, nextState, nextContext) {
+    this.logPerfStats(nextProps, nextState, nextContext, false);
+  },
+
+  componentDidUpdate(prevProps, prevState, prevContext) {
+    this.logPerfStats(prevProps, prevState, prevContext, true);
+  },
+
+  logPerfStats(prevNextProps, prevNextState, prevNextContext, stop) {
+    this.processValues(this.props, prevNextProps, "prop", stop);
+    this.processValues(this.state, prevNextState, "state", stop);
+    this.processValues(this.state, prevNextContext, "context", stop);
+  },
+
+  processValues(storedValues, prevNextValues, type, stop) {
+    if (!prevNextValues) {
+      return;
+    }
+
+    let displayName = this.constructor.displayName;
+
+    for (let key in prevNextValues) {
+      let newValue = prevNextValues[key];
+      let storedValue = storedValues[key];
+      let action = typeof storedValue === "undefined" ? "added" : "changed";
+      let mark = `${displayName}-${type}-${action}-${key}`;
+
+      if (stop) {
+        perfService.stopMeasure(mark);
+      } else {
+        perfService.startMeasure(mark);
+      }
+      perfService.storeValue(mark, newValue);
+    }
+  }
+};
+
+// Make this.perfService a singleton.
+if (!this.perfService) {
+  this.perfService = new PerfService();
+}
+
+exports.perfService = this.perfService;
+
+// Export a version to be used for testing. This allows passing in a new
+// performanceObj (fake window.Perf) for testing purposes.
+exports.perfServiceForTesting = PerfService;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1072,16 +1072,19 @@ pref("devtools.debugger.forbid-certified
 pref("devtools.defaultColorUnit", "authored");
 
 // Used for devtools debugging
 pref("devtools.dump.emit", false);
 
 // Controls whether EventEmitter module throws dump message on each emit
 pref("toolkit.dump.emit", false);
 
+// Performance measurement options
+pref("devtools.performance.measure.timing", false);
+
 // Disable device discovery logging
 pref("devtools.discovery.log", false);
 // Whether to scan for DevTools devices via WiFi
 pref("devtools.remote.wifi.scan", true);
 // Whether UI options for controlling device visibility over WiFi are shown
 // N.B.: This does not set whether the device can be discovered via WiFi, only
 // whether the UI control to make such a choice is shown to the user
 pref("devtools.remote.wifi.visible", true);
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -981,17 +981,17 @@
     "safe-buffer": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
       "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM="
     },
     "sax": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-      "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk="
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
     },
     "shelljs": {
       "version": "0.7.8",
       "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz",
       "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=",
       "requires": {
         "glob": "7.1.2",
         "interpret": "1.0.3",