From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../BHRTelemetryService.sys.mjs | 161 +++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs (limited to 'toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs') 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; + } + }, +}); -- cgit v1.2.3