зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset e4a4b630e627 (bug 1897914) for causing build bustages in platform.cpp CLOSED TREE
This commit is contained in:
Родитель
291613e267
Коммит
02e472a9ac
|
@ -45,12 +45,10 @@
|
|||
#include "ProfilerStackWalk.h"
|
||||
#include "ProfilerRustBindings.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "shared-libraries.h"
|
||||
#include "VTuneProfiler.h"
|
||||
|
@ -117,7 +115,6 @@
|
|||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <pthread.h>
|
||||
|
||||
// To simplify other code in this file, define a helper definition to avoid
|
||||
// repeating the same preprocessor checks.
|
||||
|
@ -767,7 +764,6 @@ class AsyncSignalControlThread {
|
|||
};
|
||||
|
||||
static void* AsyncSignalControlThreadEntry(void* aArg) {
|
||||
NS_SetCurrentThreadName("AsyncSignalControlThread");
|
||||
auto* thread = static_cast<AsyncSignalControlThread*>(aArg);
|
||||
thread->Watch();
|
||||
return nullptr;
|
||||
|
@ -5698,113 +5694,39 @@ Maybe<nsAutoCString> profiler_find_dump_path() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void profiler_dump_and_stop() {
|
||||
// Do nothing unless we're the parent process, as we're sandboxed and can't
|
||||
// write anyway.
|
||||
if (XRE_IsParentProcess()) {
|
||||
// pause the profiler until we are done dumping
|
||||
profiler_pause();
|
||||
|
||||
// Try to save the profile to a file
|
||||
if (auto path = profiler_find_dump_path()) {
|
||||
profiler_save_profile_to_file(path.value().get());
|
||||
} else {
|
||||
LOG("Failed to dump profile to disk");
|
||||
}
|
||||
|
||||
// Stop the profiler
|
||||
profiler_stop();
|
||||
}
|
||||
}
|
||||
|
||||
void profiler_start_from_signal() {
|
||||
// Do nothing unless we're the parent process, as we're sandboxed and can't
|
||||
// write any data that we gather anyway.
|
||||
if (XRE_IsParentProcess()) {
|
||||
// Start the profiler here directly, as we're on a background thread.
|
||||
// set of preferences, configuration of them is TODO, see Bug 1866007
|
||||
// Enabling the JS feature leaks an 8-byte object during testing, but is too
|
||||
// useful to disable. See Bug 1904897, Bug 1699681, and browser.toml for
|
||||
// more details.
|
||||
uint32_t features = ProfilerFeature::JS | ProfilerFeature::StackWalk |
|
||||
ProfilerFeature::CPUUtilization;
|
||||
// as we often don't know what threads we'll care about, tell the
|
||||
// profiler to profile all threads.
|
||||
const char* filters[] = {"*"};
|
||||
if (MOZ_UNLIKELY(NS_IsMainThread())) {
|
||||
// We are on the main thread here, so `NotifyProfilerStarted` will
|
||||
// start the profiler in content/child processes.
|
||||
profiler_start(PROFILER_DEFAULT_SIGHANDLE_ENTRIES,
|
||||
PROFILER_DEFAULT_INTERVAL, features, filters,
|
||||
MOZ_ARRAY_LENGTH(filters), 0);
|
||||
} else {
|
||||
// Directly start the profiler on this thread. We know we're not the main
|
||||
// thread here, so this will not start the profiler in child processes,
|
||||
// but we want to make sure that we do it here in case the main thread is
|
||||
// stuck.
|
||||
profiler_start(PROFILER_DEFAULT_SIGHANDLE_ENTRIES,
|
||||
PROFILER_DEFAULT_INTERVAL, features, filters,
|
||||
MOZ_ARRAY_LENGTH(filters), 0);
|
||||
// Now also try and start the profiler from the main thread, so that the
|
||||
// ParentProfiler will start child threads.
|
||||
NS_DispatchToMainThread(
|
||||
NS_NewRunnableFunction("StartProfilerInChildProcesses", [=] {
|
||||
Unused << NotifyProfilerStarted(PROFILER_DEFAULT_SIGHANDLE_ENTRIES,
|
||||
Nothing(),
|
||||
PROFILER_DEFAULT_INTERVAL, features,
|
||||
const_cast<const char**>(filters),
|
||||
MOZ_ARRAY_LENGTH(filters), 0);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void profiler_dump_and_stop() {
|
||||
// Do nothing unless we're the parent process, as we're sandboxed and can't
|
||||
// open a file handle anyway.
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// pause the profiler until we are done dumping
|
||||
profiler_pause();
|
||||
|
||||
// Try to save the profile to a file
|
||||
auto path = profiler_find_dump_path();
|
||||
|
||||
// Exit quickly if we can't find the path, while stopping the profiler
|
||||
if (!path) {
|
||||
LOG("Failed to find a valid dump path to write profile to disk");
|
||||
profiler_stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Dump the profile of this process first, in case the multi-process
|
||||
// gathering is unsuccessful (e.g. due to a blocked main threaed).
|
||||
profiler_save_profile_to_file(path.value().get());
|
||||
|
||||
// We are probably not the main thread, but check anyway, and dispatch
|
||||
// directly.
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIProfiler> nsProfiler(
|
||||
do_GetService("@mozilla.org/tools/profiler;1"));
|
||||
nsProfiler->DumpProfileToFileAsyncNoJs(path.value(), 0)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[](void_t ok) {
|
||||
LOG("Stopping profiler after dumping profile to disk");
|
||||
profiler_stop();
|
||||
},
|
||||
[](nsresult aRv) {
|
||||
LOG("Dumping to disk failed with error \"%s\", stopping "
|
||||
"profiler.",
|
||||
GetStaticErrorName(aRv));
|
||||
profiler_stop();
|
||||
});
|
||||
} else {
|
||||
// Dispatch a runnable, as nsProfiler classes are currently main-thread
|
||||
// only. We also stop the profiler within the runnable, as otherwise we
|
||||
// may find ourselves stopping the profiler before the runnable has
|
||||
// gathered all the profile data.
|
||||
NS_DispatchToMainThread(
|
||||
NS_NewRunnableFunction("WriteProfileDataToFile", [=] {
|
||||
nsCOMPtr<nsIProfiler> nsProfiler(
|
||||
do_GetService("@mozilla.org/tools/profiler;1"));
|
||||
nsProfiler->DumpProfileToFileAsyncNoJs(path.value(), 0)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[](void_t ok) {
|
||||
LOG("Stopping profiler after dumping profile to disk");
|
||||
profiler_stop();
|
||||
},
|
||||
[](nsresult aRv) {
|
||||
LOG("Dumping to disk failed with error \"%s\", stopping "
|
||||
"profiler.",
|
||||
GetStaticErrorName(aRv));
|
||||
profiler_stop();
|
||||
});
|
||||
}));
|
||||
profiler_start(PROFILER_DEFAULT_SIGHANDLE_ENTRIES,
|
||||
PROFILER_DEFAULT_INTERVAL, features, filters,
|
||||
MOZ_ARRAY_LENGTH(filters), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
[ref] native StringArrayRef(const nsTArray<nsCString>);
|
||||
native ProfileDataBufferMozPromise(RefPtr<mozilla::MozPromise<FallibleTArray<uint8_t>, nsresult, true>>);
|
||||
|
||||
native ProfileFileDumpMozPromise(RefPtr<mozilla::MozPromise<mozilla::void_t, nsresult, true>>);
|
||||
|
||||
/**
|
||||
* Start-up parameters for subprocesses are passed through nsIObserverService,
|
||||
* which, unfortunately, means we need to implement nsISupports in order to
|
||||
|
@ -108,15 +106,6 @@ interface nsIProfiler : nsISupports
|
|||
Promise dumpProfileToFileAsync(in ACString aFilename,
|
||||
[optional] in double aSinceTime);
|
||||
|
||||
|
||||
/**
|
||||
* Asynchronously dump the profile collected so far to a file. This is
|
||||
* essentially the same as `dumpProfileToFileAsync`, but rather than returning
|
||||
* a javascript Promise, it instead returns a MozPromise, meaning that we do
|
||||
* not require a js context.
|
||||
*/
|
||||
[notxpcom, nostdcall] ProfileFileDumpMozPromise dumpProfileToFileAsyncNoJs(in ACString aFilename, in double aSinceTime);
|
||||
|
||||
/**
|
||||
* Synchronously dump the profile collected so far in this process to a file.
|
||||
* This profile will only contain data from the parent process, and from child
|
||||
|
|
|
@ -668,44 +668,6 @@ nsProfiler::DumpProfileToFileAsync(const nsACString& aFilename,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<nsProfiler::GatheringPromiseFileDump>
|
||||
nsProfiler::DumpProfileToFileAsyncNoJs(const nsACString& aFilename,
|
||||
double aSinceTime) {
|
||||
if (!profiler_is_active()) {
|
||||
return GatheringPromiseFileDump::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
|
||||
nsCString filename(aFilename);
|
||||
|
||||
return StartGathering(aSinceTime)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[filename](const mozilla::ProfileAndAdditionalInformation& aResult) {
|
||||
if (aResult.mProfile.Length() >=
|
||||
size_t(std::numeric_limits<std::streamsize>::max())) {
|
||||
return GatheringPromiseFileDump::CreateAndReject(
|
||||
NS_ERROR_FILE_TOO_BIG, __func__);
|
||||
}
|
||||
|
||||
std::ofstream stream;
|
||||
stream.open(filename.get());
|
||||
if (!stream.is_open()) {
|
||||
return GatheringPromiseFileDump::CreateAndReject(
|
||||
NS_ERROR_FILE_UNRECOGNIZED_PATH, __func__);
|
||||
}
|
||||
|
||||
stream.write(aResult.mProfile.get(),
|
||||
std::streamsize(aResult.mProfile.Length()));
|
||||
stream.close();
|
||||
return GatheringPromiseFileDump::CreateAndResolve(void_t(),
|
||||
__func__);
|
||||
},
|
||||
[](nsresult aRv) {
|
||||
return GatheringPromiseFileDump::CreateAndReject(aRv, __func__);
|
||||
});
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsProfiler::GetSymbolTable(const nsACString& aDebugPath,
|
||||
const nsACString& aBreakpadID, JSContext* aCx,
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#ifndef nsProfiler_h
|
||||
#define nsProfiler_h
|
||||
|
||||
#include "ErrorList.h"
|
||||
#include "base/process.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
@ -17,7 +16,6 @@
|
|||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "mozilla/ipc/IPCCore.h"
|
||||
#include "nsIProfiler.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
@ -48,8 +46,6 @@ class nsProfiler final : public nsIProfiler {
|
|||
|
||||
using GatheringPromiseAndroid =
|
||||
mozilla::MozPromise<FallibleTArray<uint8_t>, nsresult, true>;
|
||||
using GatheringPromiseFileDump =
|
||||
mozilla::MozPromise<mozilla::void_t, nsresult, true>;
|
||||
using GatheringPromise =
|
||||
mozilla::MozPromise<mozilla::ProfileAndAdditionalInformation, nsresult,
|
||||
false>;
|
||||
|
|
|
@ -11,20 +11,6 @@ support-files = ["simple.html"]
|
|||
["browser_test_feature_jsallocations.js"]
|
||||
support-files = ["do_work_500ms.html"]
|
||||
|
||||
["browser_test_feature_multiprocess_capture_with_signal.js"]
|
||||
support-files = ["do_work_500ms.html"]
|
||||
skip-if = [
|
||||
# Enabling the JS feature leaks an 8-byte object. This causes failures when
|
||||
# the leak checker is enabled, and run with test-verify (i.e. with both
|
||||
# "debug" and "verify"). Having JS support for this feature is more important
|
||||
# than these tests, so we're disabling them for now. See Bug 1904897.
|
||||
"verify && debug",
|
||||
"tsan", # We have intermittent timeout issues in TSan, see Bug 1889828
|
||||
"ccov", # The signals for the profiler conflict with the ccov signals
|
||||
"os == 'win'", # Not yet supported on windows - Bug 1867328
|
||||
"os == 'android'", # Not yet supported on android - Bug 1904639
|
||||
]
|
||||
|
||||
["browser_test_feature_nostacksampling.js"]
|
||||
support-files = ["do_work_500ms.html"]
|
||||
|
||||
|
|
|
@ -1,182 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
requestLongerTimeout(10);
|
||||
|
||||
async function get_profile_path_on_disk(pid) {
|
||||
// Get the system downloads directory, and use it to build a profile file
|
||||
let profile = FileUtils.File(await Downloads.getSystemDownloadsDirectory());
|
||||
|
||||
// use the pid to construct the name of the profile, and resulting file
|
||||
profile.append(`profile_0_${pid}.json`);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
function check_profile_contains_parent_and_content_pids(
|
||||
parent_pid,
|
||||
content_pid,
|
||||
profile
|
||||
) {
|
||||
info(
|
||||
`Checking that the profile contains pids for the parent process ${parent_pid} and content process ${content_pid}`
|
||||
);
|
||||
Assert.equal(
|
||||
profile.threads[0].pid,
|
||||
parent_pid,
|
||||
"We expect the pid of the main profile thread to be the parent pid"
|
||||
);
|
||||
|
||||
// Keep a record of the pids found in the profile, so that we can give a
|
||||
// better error message.
|
||||
const child_pids = [];
|
||||
let found = false;
|
||||
for (const process of profile.processes) {
|
||||
child_pids.push(process.threads[0].pid);
|
||||
if (process.threads[0].pid === content_pid) {
|
||||
found = true;
|
||||
info(`Found content pid: ${process.threads[0].pid}.`);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.ok(
|
||||
found,
|
||||
`We expect the child process ids to contain the id of the content process (${content_pid}). Actually found: ${child_pids}`
|
||||
);
|
||||
}
|
||||
|
||||
function check_profile_for_synthetic_marker(profile) {
|
||||
// Essentially the same test as `browser_test_markers_parent_process.js`
|
||||
const markers = getInflatedMarkerData(profile.threads[0]);
|
||||
{
|
||||
const domEventStart = markers.find(
|
||||
({ phase, data }) =>
|
||||
phase === INTERVAL_START && data?.eventType === "synthetic"
|
||||
);
|
||||
const domEventEnd = markers.find(
|
||||
({ phase, data }) =>
|
||||
phase === INTERVAL_END && data?.eventType === "synthetic"
|
||||
);
|
||||
ok(domEventStart, "A start DOMEvent was generated");
|
||||
ok(domEventEnd, "An end DOMEvent was generated");
|
||||
Assert.greater(
|
||||
domEventEnd.data.latency,
|
||||
0,
|
||||
"DOMEvent had a a latency value generated."
|
||||
);
|
||||
Assert.strictEqual(domEventEnd.data.type, "DOMEvent");
|
||||
Assert.strictEqual(domEventEnd.name, "DOMEvent");
|
||||
}
|
||||
}
|
||||
|
||||
// Test signal handling within the profiler: Start the profiler with a POSIX
|
||||
// signal, and capture a profile with a POSIX signal. Along the way, record a
|
||||
// marker from a synthetic dom event. Check that the marker shows up in the
|
||||
// final profile, and that the processes that we expect to see are also there.
|
||||
//
|
||||
// We would ideally like to have three tests for the three following scenarios:
|
||||
//
|
||||
// 1) Starting the profiler normally, and stopping it with a signal,
|
||||
// 2) Starting the profiler with a signal, and stopping it normally
|
||||
// 3) Both starting and stopping the profiler with a signal
|
||||
//
|
||||
// That way, if any one of the three tests fails, we can quickly isolate which
|
||||
// part of the signal-handling code has failed. If (1 & 3) fail, then it's the
|
||||
// stopping code, if (2 & 3) fail, it's the starting code, and if just (3)
|
||||
// fails, it's something else entirely. However, this would use up a lot of time
|
||||
// in CI, so instead we just have test (3). This can be easily modified to act
|
||||
// like (1) or (2) when debugging.
|
||||
//
|
||||
// - To make this test act like (1), replace the the call to
|
||||
// `raiseSignal(ppid.pid, SIGUSR1)` with (e.g.) `await startProfiler({
|
||||
// features: [""], threads: ["GeckoMain"] });`
|
||||
// - To make this test act like (2), replace the call to `raiseSignal(ppid.pid,
|
||||
// SIGUSR2)` etc with (e.g.) `const profile = await stopNowAndGetProfile();`
|
||||
//
|
||||
|
||||
add_task(
|
||||
async function test_profile_feature_multiprocess_start_and_capture_with_signal() {
|
||||
Assert.ok(
|
||||
!Services.profiler.IsActive(),
|
||||
"The profiler is not currently active"
|
||||
);
|
||||
|
||||
let ppid = await ChromeUtils.requestProcInfo();
|
||||
let parent_pid = ppid.pid;
|
||||
|
||||
let startPromise = TestUtils.topicObserved("profiler-started");
|
||||
|
||||
info(`Raising signal SIGUSR1 with pid ${parent_pid} to start the profiler`);
|
||||
// Try and start the profiler using a signal.
|
||||
let result = raiseSignal(parent_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."
|
||||
);
|
||||
|
||||
const url = BASE_URL + "do_work_500ms.html";
|
||||
await BrowserTestUtils.withNewTab(url, async contentBrowser => {
|
||||
info("Finding the PId of the content process.");
|
||||
const content_pid = await SpecialPowers.spawn(
|
||||
contentBrowser,
|
||||
[],
|
||||
() => Services.appinfo.processID
|
||||
);
|
||||
|
||||
// Dispatch a synthetic event so that we can search for the marker in the
|
||||
// profile
|
||||
info("Dispatching a synthetic DOMEvent");
|
||||
window.dispatchEvent(new Event("synthetic"));
|
||||
|
||||
// Wait 500ms so that the tab finishes executing.
|
||||
info("Waiting for the tab to do some work");
|
||||
await wait(500);
|
||||
|
||||
// Set up an observer to watch for the profiler stopping
|
||||
let stopPromise = TestUtils.topicObserved("profiler-stopped");
|
||||
|
||||
// Try and stop the profiler using a signal.
|
||||
info(
|
||||
`Raising signal SIGUSR2 with pid ${parent_pid} to stop the profiler`
|
||||
);
|
||||
let result = raiseSignal(parent_pid, SIGUSR2);
|
||||
Assert.ok(result, "Raising a SIGUSR2 signal should succeed.");
|
||||
|
||||
// Wait for the profiler to stop
|
||||
info(`Waiting for the profiler to stop.`);
|
||||
Assert.ok(await stopPromise, "The profiler should stop");
|
||||
|
||||
// Check that we have a profile written to disk:
|
||||
info(`Retrieving profile file.`);
|
||||
let profile_file = await get_profile_path_on_disk(parent_pid);
|
||||
Assert.ok(
|
||||
await IOUtils.exists(profile_file.path),
|
||||
"A profile file should be written to disk."
|
||||
);
|
||||
|
||||
// Read the profile from the json file
|
||||
let profile = await IOUtils.readJSON(profile_file.path);
|
||||
info("Found this many proceses: " + profile.processes.length);
|
||||
|
||||
// check for processes and the synthetic marker
|
||||
info(
|
||||
`Checking that the profile file contains the parent and content processes.`
|
||||
);
|
||||
check_profile_contains_parent_and_content_pids(
|
||||
parent_pid,
|
||||
content_pid,
|
||||
profile
|
||||
);
|
||||
|
||||
info(`Checking for the synthetic DOM marker`);
|
||||
check_profile_for_synthetic_marker(profile);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -389,78 +389,6 @@ function escapeStringRegexp(string) {
|
|||
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
||||
}
|
||||
|
||||
/** ------ Utility functions and definitions for raising POSIX signals ------ */
|
||||
|
||||
// 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;
|
||||
|
||||
// Derived heavily from equivalent sandbox testing code. For more details see:
|
||||
// https://searchfox.org/mozilla-central/rev/1aaacaeb4fa3aca6837ecc157e43e947229ba8ce/security/sandbox/test/browser_content_sandbox_utils.js#89
|
||||
function raiseSignal(pid, sig) {
|
||||
const { ctypes } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/ctypes.sys.mjs"
|
||||
);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
// c.f. https://man7.org/linux/man-pages/man2/kill.2.html
|
||||
// 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 };
|
||||
}
|
||||
|
||||
/** ------ Assertions helper ------ */
|
||||
/**
|
||||
* This assert helper function makes it easy to check a lot of properties in an
|
||||
|
|
|
@ -8,6 +8,66 @@ ChromeUtils.defineESModuleGetters(this, {
|
|||
TestUtils: "resource://testing-common/TestUtils.sys.mjs",
|
||||
});
|
||||
|
||||
const { ctypes } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/ctypes.sys.mjs"
|
||||
);
|
||||
|
||||
// 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:
|
||||
// https://searchfox.org/mozilla-central/rev/1aaacaeb4fa3aca6837ecc157e43e947229ba8ce/security/sandbox/test/browser_content_sandbox_utils.js#89
|
||||
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 };
|
||||
}
|
||||
|
||||
// c.f. https://man7.org/linux/man-pages/man2/kill.2.html
|
||||
// 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
|
||||
|
@ -26,6 +86,18 @@ async function cleanupAfterTest() {
|
|||
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.");
|
||||
|
||||
|
|
|
@ -51,8 +51,8 @@ skip-if = [
|
|||
skip-if = [
|
||||
"tsan", # We have intermittent timeout issues in TSan, see Bug 1889828
|
||||
"ccov", # The signals for the profiler conflict with the ccov signals
|
||||
"os == 'win'", # Not yet supported on windows - Bug 1867328
|
||||
"os == 'android'", # Not yet supported on android - Bug 1904639
|
||||
"os == 'win'", # Not yet supported on windows
|
||||
"os == 'android'", # Not yet supported on android
|
||||
]
|
||||
|
||||
# Native stackwalking is somewhat unreliable depending on the platform.
|
||||
|
|
Загрузка…
Ссылка в новой задаче