summaryrefslogtreecommitdiffstats
path: root/toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs')
-rw-r--r--toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs161
1 files changed, 161 insertions, 0 deletions
diff --git a/toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs b/toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs
new file mode 100644
index 0000000000..98c1274b5c
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs
@@ -0,0 +1,161 @@
+/* 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 lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ TelemetryController: "resource://gre/modules/TelemetryController.sys.mjs",
+});
+
+export function BHRTelemetryService() {
+ // Allow tests to get access to this object to verify it works correctly.
+ this.wrappedJSObject = this;
+
+ Services.obs.addObserver(this, "profile-before-change");
+ Services.obs.addObserver(this, "bhr-thread-hang");
+ Services.obs.addObserver(this, "idle-daily");
+
+ this.resetPayload();
+}
+
+BHRTelemetryService.prototype = Object.freeze({
+ classID: Components.ID("{117c8cdf-69e6-4f31-a439-b8a654c67127}"),
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+
+ TRANSMIT_HANG_COUNT: 50,
+
+ resetPayload() {
+ this.startTime = +new Date();
+ this.payload = {
+ modules: [],
+ hangs: [],
+ };
+ this.clearPermahangFile = false;
+ },
+
+ recordHang({
+ duration,
+ thread,
+ runnableName,
+ process,
+ stack,
+ remoteType,
+ modules,
+ annotations,
+ wasPersisted,
+ }) {
+ 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]];
+ } else if (typeof stack[i] == "string") {
+ // This is just a precaution - we don't currently know of sensitive
+ // URLs being included in label frames' dynamic strings which we
+ // include here, but this is just an added guard. Here we strip any
+ // string with a :// in it that isn't a chrome:// or resource://
+ // URL. This is not completely robust, but we are already trying to
+ // protect against this by only including dynamic strings from the
+ // opt-in AUTO_PROFILER_..._NONSENSITIVE macros.
+ let match = /[^\s]+:\/\/.*/.exec(stack[i]);
+ if (
+ match &&
+ !match[0].startsWith("chrome://") &&
+ !match[0].startsWith("resource://")
+ ) {
+ stack[i] = stack[i].replace(match[0], "(excluded)");
+ }
+ }
+ }
+
+ // Create the hang object to record in the payload.
+ this.payload.hangs.push({
+ duration,
+ thread,
+ runnableName,
+ process,
+ remoteType,
+ annotations,
+ stack,
+ });
+
+ if (wasPersisted) {
+ this.clearPermahangFile = true;
+ }
+
+ // 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 (this.clearPermahangFile) {
+ // NB: This is async but it is called from an Observer callback.
+ IOUtils.remove(
+ PathUtils.join(PathUtils.profileDir, "last_permahang.bin")
+ );
+ }
+
+ if (!Services.telemetry.canRecordExtended) {
+ return;
+ }
+
+ // NOTE: We check a separate bhrPing.enabled pref here. This pref is unset
+ // when running tests so that we run as much of BHR as possible (to catch
+ // errors) while avoiding timeouts caused by invoking `pingsender` during
+ // testing.
+ if (
+ Services.prefs.getBoolPref("toolkit.telemetry.bhrPing.enabled", false)
+ ) {
+ this.payload.timeSinceLastPing = new Date() - this.startTime;
+ lazy.TelemetryController.submitExternalPing("bhr", this.payload, {
+ addEnvironment: true,
+ });
+ }
+ this.resetPayload();
+ },
+
+ shutdown() {
+ Services.obs.removeObserver(this, "profile-before-change");
+ Services.obs.removeObserver(this, "bhr-thread-hang");
+ Services.obs.removeObserver(this, "idle-daily");
+ 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;
+ case "idle-daily":
+ this.submit();
+ break;
+ }
+ },
+});