Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/* 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/. */
ChromeUtils.defineESModuleGetters(this, {
});
const { ctypes } = ChromeUtils.importESModule(
);
// Derived from functionality in js/src/devtools/rootAnalysis/utility.js
function openLibrary(names) {
for (const name of names) {
try {
return ctypes.open(name);
} catch (e) {}
}
return undefined;
}
// Derived heavily from equivalent sandbox testing code.
// For more details see:
function raiseSignal(pid, sig) {
try {
const libc = openLibrary([
"libc.so.6",
"libc.so",
"libc.dylib",
"libSystem.B.dylib",
]);
if (!libc) {
info("Failed to open any libc shared object");
return { ok: false };
}
// This choice of typing for `pid` is complex, and brittle, as it's
// platform dependent. Getting it wrong can result in incoreect
// generation/calling of the `kill` function. Unfortunately, as it's
// defined as `pid_t` in a header, we can't easily get access to it.
// For now, we just use an integer, and hope that the system int size
// aligns with the `pid_t` size.
const kill = libc.declare(
"kill",
ctypes.default_abi,
ctypes.int, // return value
ctypes.int32_t, // pid
ctypes.int // sig
);
let kres = kill(pid, sig);
if (kres != 0) {
info(`Kill returned a non-zero result ${kres}.`);
return { ok: false };
}
libc.close();
} catch (e) {
info(`Exception ${e} thrown while trying to call kill`);
return { ok: false };
}
return { ok: true };
}
async function cleanupAfterTest() {
// We need to cleanup written profiles after a test
// Get the system downloads directory, and use it to build a profile file
let profile = FileUtils.File(await Downloads.getSystemDownloadsDirectory());
// Get the process ID
let pid = Services.appinfo.processID;
// write it to the profile file name
profile.append(`profile_0_${pid}.json`);
// remove the file!
await IOUtils.remove(profile.path, { ignoreAbsent: true });
// Make sure the profiler is fully stopped, even if the test failed
await Services.profiler.StopProfiler();
}
// Hardcode the constants SIGUSR1 and SIGUSR2.
// This is an absolutely terrible idea, as they are implementation defined!
// However, it turns out that for 99% of the platforms we care about, and for
// 99.999% of the platforms we test, these constants are, well, constant.
// Additionally, these constants are only for _testing_ the signal handling
// feature - the actual feature relies on platform specific definitions. This
// may cause a mismatch if we test on on, say, a gnu hurd kernel, or on a
// linux kernel running on sparc, but the feature will not break - only
// the testing.
const SIGUSR1 = Services.appinfo.OS === "Darwin" ? 30 : 10;
const SIGUSR2 = Services.appinfo.OS === "Darwin" ? 31 : 12;
add_task(async () => {
info("Test that starting the profiler with a posix signal works.");
Assert.ok(
!Services.profiler.IsActive(),
"The profiler should not begin the test active."
);
// Get the process ID
let pid = Services.appinfo.processID;
// Set up an observer to watch for the profiler starting
let startPromise = TestUtils.topicObserved("profiler-started");
// Try and start the profiler using a signal.
let result = raiseSignal(pid, SIGUSR1);
Assert.ok(result, "Raising a signal should succeed");
// Wait for the profiler to stop
Assert.ok(await startPromise, "The profiler should start");
// Wait until the profiler is active
Assert.ok(Services.profiler.IsActive(), "The profiler should now be active.");
// Let the profiler sample at least once
await Services.profiler.waitOnePeriodicSampling();
info("Waiting a periodic sampling completed");
// Stop the profiler
await Services.profiler.StopProfiler();
Assert.ok(
!Services.profiler.IsActive(),
"The profiler should now be inactive."
);
});
add_task(async () => {
info("Test that stopping the profiler with a posix signal works.");
registerCleanupFunction(cleanupAfterTest);
Assert.ok(
!Services.profiler.IsActive(),
"The profiler should not begin the test active."
);
const entries = 100;
const interval = 1;
const threads = [];
const features = [];
// Start the profiler, and ensure that it's active
await Services.profiler.StartProfiler(entries, interval, threads, features);
Assert.ok(Services.profiler.IsActive(), "The profiler should now be active.");
// Get the process ID
let pid = Services.appinfo.processID;
// Set up an observer to watch for the profiler stopping
let stopPromise = TestUtils.topicObserved("profiler-stopped");
// Try and stop the profiler using a signal.
let result = raiseSignal(pid, SIGUSR2);
Assert.ok(result, "Raising a SIGUSR2 signal should succeed.");
// Wait for the profiler to stop
Assert.ok(await stopPromise, "The profiler should stop");
Assert.ok(
!Services.profiler.IsActive(),
"The profiler should now be inactive."
);
});
add_task(async () => {
info(
"Test that stopping the profiler with a posix signal writes a profile file to the system download directory."
);
registerCleanupFunction(cleanupAfterTest);
Assert.ok(
!Services.profiler.IsActive(),
"The profiler should not begin the test active."
);
const entries = 100;
const interval = 1;
const threads = [];
const features = [];
// Get the system downloads directory, and use it to build a profile file
let profile = FileUtils.File(await Downloads.getSystemDownloadsDirectory());
// Get the process ID
let pid = Services.appinfo.processID;
// use the pid to construct the name of the profile, and resulting file
profile.append(`profile_0_${pid}.json`);
// Start the profiler, and ensure that it's active
await Services.profiler.StartProfiler(entries, interval, threads, features);
Assert.ok(Services.profiler.IsActive(), "The profiler should now be active.");
// Set up an observer to watch for the profiler stopping
let stopPromise = TestUtils.topicObserved("profiler-stopped");
// Try and stop the profiler using a signal.
let result = raiseSignal(pid, SIGUSR2);
Assert.ok(result, "Raising a SIGUSR2 signal should succeed.");
// Wait for the profiler to stop
Assert.ok(await stopPromise, "The profiler should stop");
// Now that it's stopped, make sure that we have a profile file
Assert.ok(
await IOUtils.exists(profile.path),
"A profile file should be written to disk."
);
Assert.ok(
!Services.profiler.IsActive(),
"The profiler should now be inactive."
);
});