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 --- .../tests/child_cause_hang.js | 23 +++ .../tests/test_BHRObserver.js | 164 +++++++++++++++++++++ .../backgroundhangmonitor/tests/xpcshell.toml | 14 ++ 3 files changed, 201 insertions(+) create mode 100644 toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js create mode 100644 toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js create mode 100644 toolkit/components/backgroundhangmonitor/tests/xpcshell.toml (limited to 'toolkit/components/backgroundhangmonitor/tests') diff --git a/toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js b/toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js new file mode 100644 index 0000000000..adf96170b6 --- /dev/null +++ b/toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function ensureProfilerInitialized() { + // Starting and stopping the profiler with the "stackwalk" flag will cause the + // profiler's stackwalking features to be synchronously initialized. This + // should prevent us from not initializing BHR quickly enough. + let features = ["stackwalk"]; + Services.profiler.StartProfiler(1000, 10, features); + Services.profiler.StopProfiler(); +} + +add_task(async function childCauseHang() { + ensureProfilerInitialized(); + + executeSoon(() => { + let startTime = Date.now(); + // eslint-disable-next-line no-empty + while (Date.now() - startTime < 2000) {} + }); + + await do_await_remote_message("bhr_hangs_detected"); +}); diff --git a/toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js b/toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js new file mode 100644 index 0000000000..cf6d6633b8 --- /dev/null +++ b/toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js @@ -0,0 +1,164 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { TelemetryUtils } = ChromeUtils.importESModule( + "resource://gre/modules/TelemetryUtils.sys.mjs" +); + +function ensureProfilerInitialized() { + // Starting and stopping the profiler with the "stackwalk" flag will cause the + // profiler's stackwalking features to be synchronously initialized. This + // should prevent us from not initializing BHR quickly enough. + let features = ["stackwalk"]; + Services.profiler.StartProfiler(1000, 10, features); + Services.profiler.StopProfiler(); +} + +add_task(async function test_BHRObserver() { + if (!Services.telemetry.canRecordExtended) { + ok("Hang reporting not enabled."); + return; + } + + ensureProfilerInitialized(); + + let telSvc = + Cc["@mozilla.org/bhr-telemetry-service;1"].getService().wrappedJSObject; + ok(telSvc, "Should have BHRTelemetryService"); + let beforeLen = telSvc.payload.hangs.length; + + if (Services.appinfo.OS === "Linux" || Services.appinfo.OS === "Android") { + // We use the rt_tgsigqueueinfo syscall on Linux which requires a + // certain kernel version. It's not an error if the system running + // the test is older than that. + let kernel = + Services.sysinfo.get("kernel_version") || Services.sysinfo.get("version"); + if (Services.vc.compare(kernel, "2.6.31") < 0) { + ok("Hang reporting not supported for old kernel."); + return; + } + } + + let hangsPromise = new Promise(resolve => { + let hangs = []; + const onThreadHang = subject => { + let hang = subject.QueryInterface(Ci.nsIHangDetails); + if (hang.thread.startsWith("Gecko")) { + hangs.push(hang); + if (hangs.length >= 3) { + Services.obs.removeObserver(onThreadHang, "bhr-thread-hang"); + resolve(hangs); + } + } + }; + Services.obs.addObserver(onThreadHang, "bhr-thread-hang"); + }); + + // We're going to trigger two hangs, of various lengths. One should be a + // transient hang, and the other a permanent hang. We'll wait for the hangs to + // be recorded. + + executeSoon(() => { + let startTime = Date.now(); + // eslint-disable-next-line no-empty + while (Date.now() - startTime < 10000) {} + }); + + executeSoon(() => { + let startTime = Date.now(); + // eslint-disable-next-line no-empty + while (Date.now() - startTime < 1000) {} + }); + + Services.prefs.setBoolPref( + TelemetryUtils.Preferences.OverridePreRelease, + true + ); + let childDone = run_test_in_child("child_cause_hang.js"); + + // Now we wait for the hangs to have their bhr-thread-hang message fired for + // them, collect them, and analyize the response. + let hangs = await hangsPromise; + equal(hangs.length, 3); + hangs.forEach(hang => { + Assert.greater(hang.duration, 0); + ok(hang.thread == "Gecko" || hang.thread == "Gecko_Child"); + equal(typeof hang.runnableName, "string"); + + // hang.stack + ok(Array.isArray(hang.stack)); + ok(!!hang.stack.length); + hang.stack.forEach(entry => { + // Each stack frame entry is either a native or pseudostack entry. A + // native stack entry is an array with module index (number), and offset + // (hex string), while the pseudostack entry is a bare string. + if (Array.isArray(entry)) { + equal(entry.length, 2); + equal(typeof entry[0], "number"); + equal(typeof entry[1], "string"); + } else { + equal(typeof entry, "string"); + } + }); + + // hang.modules + ok(Array.isArray(hang.modules)); + hang.modules.forEach(module => { + ok(Array.isArray(module)); + equal(module.length, 2); + equal(typeof module[0], "string"); + equal(typeof module[1], "string"); + }); + + // hang.annotations + ok(Array.isArray(hang.annotations)); + hang.annotations.forEach(annotation => { + ok(Array.isArray(annotation)); + equal(annotation.length, 2); + equal(typeof annotation[0], "string"); + equal(typeof annotation[1], "string"); + }); + }); + + // Check that the telemetry service collected pings which make sense + Assert.greaterOrEqual(telSvc.payload.hangs.length - beforeLen, 3); + ok(Array.isArray(telSvc.payload.modules)); + telSvc.payload.modules.forEach(module => { + ok(Array.isArray(module)); + equal(module.length, 2); + equal(typeof module[0], "string"); + equal(typeof module[1], "string"); + }); + + telSvc.payload.hangs.forEach(hang => { + Assert.greater(hang.duration, 0); + ok(hang.thread == "Gecko" || hang.thread == "Gecko_Child"); + equal(typeof hang.runnableName, "string"); + + // hang.stack + ok(Array.isArray(hang.stack)); + ok(!!hang.stack.length); + hang.stack.forEach(entry => { + // Each stack frame entry is either a native or pseudostack entry. A + // native stack entry is an array with module index (number), and offset + // (hex string), while the pseudostack entry is a bare string. + if (Array.isArray(entry)) { + equal(entry.length, 2); + equal(typeof entry[0], "number"); + Assert.less(entry[0], telSvc.payload.modules.length); + equal(typeof entry[1], "string"); + } else { + equal(typeof entry, "string"); + } + }); + + // hang.annotations + equal(typeof hang.annotations, "object"); + Object.keys(hang.annotations).forEach(key => { + equal(typeof hang.annotations[key], "string"); + }); + }); + + do_send_remote_message("bhr_hangs_detected"); + await childDone; +}); diff --git a/toolkit/components/backgroundhangmonitor/tests/xpcshell.toml b/toolkit/components/backgroundhangmonitor/tests/xpcshell.toml new file mode 100644 index 0000000000..d0ea28c83c --- /dev/null +++ b/toolkit/components/backgroundhangmonitor/tests/xpcshell.toml @@ -0,0 +1,14 @@ +[DEFAULT] + +["test_BHRObserver.js"] +# BHR is disabled on android and outside of nightly +skip-if = [ + "debug", + "os == 'android'", + "release_or_beta", + "os == 'mac'", # Bug 1417723 + "win11_2009 && bits == 32", # Bug 1760134 + "os == 'win' && msix", +] +support-files = ["child_cause_hang.js"] +run-sequentially = "very high failure rate in parallel" -- cgit v1.2.3