Bug 1380081 - Part 8: Report bhr-thread-hang hangs to telemetry in a custom bhr ping, r=froydnj
authorMichael Layzell <michael@thelayzells.com>
Thu, 20 Jul 2017 14:33:53 -0400
changeset 374837 c9312138747143e11f608b7a9ecf4890f249336a
parent 374836 f35ba2fdb5182b8aab79361be5803cad9b570faf
child 374838 07452b30b4dc41361f6f7a9e02ebce3509fbe79a
push id93781
push usermichael@thelayzells.com
push dateTue, 15 Aug 2017 20:37:28 +0000
treeherdermozilla-inbound@ebd9e87fae8d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1380081
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1380081 - Part 8: Report bhr-thread-hang hangs to telemetry in a custom bhr ping, r=froydnj This patch adds the BHRTelemetryService which is a JS implemented XPCOM service that simply listens to the bhr-thread-hang observer notification, and uses the data it collects from it to submit telemetry pings. MozReview-Commit-ID: 2hPXAFmHrm5
toolkit/components/backgroundhangmonitor/BHRTelemetryService.js
toolkit/components/backgroundhangmonitor/BHRTelemetryService.manifest
toolkit/components/backgroundhangmonitor/moz.build
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/BHRTelemetryService.js
@@ -0,0 +1,114 @@
+/* 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 { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
+                                  "resource://gre/modules/TelemetryController.jsm");
+
+function BHRTelemetryService() {
+  Services.obs.addObserver(this, "profile-before-change");
+  Services.obs.addObserver(this, "bhr-thread-hang");
+
+  this.resetPayload();
+};
+
+BHRTelemetryService.prototype = Object.freeze({
+  classID: Components.ID("{117c8cdf-69e6-4f31-a439-b8a654c67127}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  TRANSMIT_HANG_COUNT: 50,
+
+  resetPayload() {
+    this.payload = {
+      modules: [],
+      hangs: [],
+    };
+  },
+
+  recordHang({duration, thread, runnableName, process, stack,
+              modules, annotations, pseudoStack}) {
+    if (!Services.telemetry.canRecordExtended) {
+      return;
+    }
+
+    // Create a mapping from module indicies in the original nsIHangDetails
+    // object to this.payload.modules indicies.
+    let moduleIdxs = modules.map(module => {
+      let idx = this.payload.modules.findIndex(m => {
+        return m[0] === module[0] && m[1] === module[1];
+      });
+      if (idx === -1) {
+        idx = this.payload.modules.length;
+        this.payload.modules.push(module);
+      }
+      return idx;
+    });
+
+    // Native stack frames are [modIdx, offset] arrays. If we have a valid
+    // module index, we want to map it to the this.payload.modules array.
+    for (let i = 0; i < stack.length; ++i) {
+      if (Array.isArray(stack[i]) && stack[i][0] !== -1) {
+        stack[i][0] = moduleIdxs[stack[i][0]];
+      }
+    }
+
+    // Create the hang object to record in the payload.
+    this.payload.hangs.push({
+      duration,
+      thread,
+      runnableName,
+      process,
+      annotations,
+      pseudoStack,
+      stack,
+    });
+
+    // If we have collected enough hangs, we can submit the hangs we have
+    // collected to telemetry.
+    if (this.payload.hangs.length > this.TRANSMIT_HANG_COUNT) {
+      this.submit();
+    }
+  },
+
+  submit() {
+    if (!Services.telemetry.canRecordExtended) {
+      return;
+    }
+
+    if (this.payload.hangs.length >= 0) {
+      TelemetryController.submitExternalPing("bhr", this.payload, {
+        addEnvironment: true,
+      });
+    }
+    this.resetPayload();
+  },
+
+  shutdown() {
+    Services.obs.removeObserver(this, "profile-before-change");
+    Services.obs.removeObserver(this, "bhr-thread-hang");
+    this.submit();
+  },
+
+  observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+    case "profile-after-change":
+      this.resetPayload();
+      break;
+    case "bhr-thread-hang":
+      this.recordHang(aSubject.QueryInterface(Ci.nsIHangDetails));
+      break;
+    case "profile-before-change":
+      this.shutdown();
+      break;
+    }
+  },
+});
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BHRTelemetryService]);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/BHRTelemetryService.manifest
@@ -0,0 +1,3 @@
+component {117c8cdf-69e6-4f31-a439-b8a654c67127} BHRTelemetryService.js
+contract @mozilla.org/bhr-telemetry-service;1 {117c8cdf-69e6-4f31-a439-b8a654c67127}
+category profile-after-change BHRTelemetryService @mozilla.org/bhr-telemetry-service;1
\ No newline at end of file
--- a/toolkit/components/backgroundhangmonitor/moz.build
+++ b/toolkit/components/backgroundhangmonitor/moz.build
@@ -1,14 +1,22 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+# We only want to record and report BHR in nightly builds. We don't need to load
+# the BHRTelemetryService outside of nightly builds.
+if CONFIG['NIGHTLY_BUILD']:
+    EXTRA_COMPONENTS += [
+        'BHRTelemetryService.js',
+        'BHRTelemetryService.manifest',
+    ]
+
 XPIDL_SOURCES += [
     'nsIHangDetails.idl',
 ]
 
 XPIDL_MODULE = 'bhr'
 
 EXPORTS.mozilla += [
     'BackgroundHangMonitor.h',