mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1016441 - Switch to using real-time signal in ThreadStackHelper; r=snorp
This commit is contained in:
parent
90f3f1bc63
commit
e31d6feea5
@ -20,6 +20,17 @@ function run_test() {
|
||||
return;
|
||||
}
|
||||
|
||||
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.kernel_version || Services.sysinfo.version;
|
||||
if (Services.vc.compare(kernel, '2.6.31') < 0) {
|
||||
ok("Hang reporting not supported for old kernel.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Run two events in the event loop:
|
||||
// the first event causes a hang;
|
||||
// the second event checks results from the first event.
|
||||
|
@ -16,3 +16,5 @@ tail =
|
||||
[test_TelemetrySendOldPings.js]
|
||||
skip-if = debug == true || os == "android" # Disabled due to intermittent orange on Android
|
||||
[test_ThreadHangStats.js]
|
||||
# Bug 1032996: test fails consistently in Android x86 emulator
|
||||
skip-if = os == "android" && processor == "x86"
|
||||
|
@ -27,8 +27,12 @@
|
||||
#ifndef SYS_gettid
|
||||
#define SYS_gettid __NR_gettid
|
||||
#endif
|
||||
#ifndef SYS_tgkill
|
||||
#define SYS_tgkill __NR_tgkill
|
||||
#if defined(__arm__) && !defined(__NR_rt_tgsigqueueinfo)
|
||||
// Some NDKs don't define this constant even though the kernel supports it.
|
||||
#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
|
||||
#endif
|
||||
#ifndef SYS_rt_tgsigqueueinfo
|
||||
#define SYS_rt_tgsigqueueinfo __NR_rt_tgsigqueueinfo
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -40,7 +44,18 @@ ThreadStackHelper::Startup()
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sInitialized) {
|
||||
MOZ_ALWAYS_TRUE(!::sem_init(&sSem, 0, 0));
|
||||
// TODO: centralize signal number allocation
|
||||
sFillStackSignum = SIGRTMIN + 4;
|
||||
if (sFillStackSignum > SIGRTMAX) {
|
||||
// Leave uninitialized
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
struct sigaction sigact = {};
|
||||
sigact.sa_sigaction = FillStackHandler;
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
MOZ_ALWAYS_TRUE(!::sigaction(sFillStackSignum, &sigact, nullptr));
|
||||
}
|
||||
sInitialized++;
|
||||
#endif
|
||||
@ -52,7 +67,9 @@ ThreadStackHelper::Shutdown()
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (sInitialized == 1) {
|
||||
MOZ_ALWAYS_TRUE(!::sem_destroy(&sSem));
|
||||
struct sigaction sigact = {};
|
||||
sigact.sa_handler = SIG_DFL;
|
||||
MOZ_ALWAYS_TRUE(!::sigaction(sFillStackSignum, &sigact, nullptr));
|
||||
}
|
||||
sInitialized--;
|
||||
#endif
|
||||
@ -68,6 +85,7 @@ ThreadStackHelper::ThreadStackHelper()
|
||||
, mMaxBufferSize(0)
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ALWAYS_TRUE(!::sem_init(&mSem, 0, 0));
|
||||
mThreadID = ::syscall(SYS_gettid);
|
||||
#elif defined(XP_WIN)
|
||||
mInitialized = !!::DuplicateHandle(
|
||||
@ -82,41 +100,15 @@ ThreadStackHelper::ThreadStackHelper()
|
||||
|
||||
ThreadStackHelper::~ThreadStackHelper()
|
||||
{
|
||||
#if defined(XP_WIN)
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ALWAYS_TRUE(!::sem_destroy(&mSem));
|
||||
#elif defined(XP_WIN)
|
||||
if (mInitialized) {
|
||||
MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(XP_LINUX) && defined(__arm__)
|
||||
// Some (old) Linux kernels on ARM have a bug where a signal handler
|
||||
// can be called without clearing the IT bits in CPSR first. The result
|
||||
// is that the first few instructions of the handler could be skipped,
|
||||
// ultimately resulting in crashes. To workaround this bug, the handler
|
||||
// on ARM is a trampoline that starts with enough NOP instructions, so
|
||||
// that even if the IT bits are not cleared, only the NOP instructions
|
||||
// will be skipped over.
|
||||
|
||||
template <void (*H)(int, siginfo_t*, void*)>
|
||||
__attribute__((naked)) void
|
||||
SignalTrampoline(int aSignal, siginfo_t* aInfo, void* aContext)
|
||||
{
|
||||
asm volatile (
|
||||
"nop; nop; nop; nop"
|
||||
: : : "memory");
|
||||
|
||||
// Because the assembler may generate additional insturctions below, we
|
||||
// need to ensure NOPs are inserted first by separating them out above.
|
||||
|
||||
asm volatile (
|
||||
"bx %0"
|
||||
:
|
||||
: "r"(H), "l"(aSignal), "l"(aInfo), "l"(aContext)
|
||||
: "memory");
|
||||
}
|
||||
#endif // XP_LINUX && __arm__
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetStack(Stack& aStack)
|
||||
{
|
||||
@ -127,29 +119,23 @@ ThreadStackHelper::GetStack(Stack& aStack)
|
||||
}
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
if (profiler_is_active()) {
|
||||
// Profiler can interfere with our Linux signal handling
|
||||
return;
|
||||
}
|
||||
if (!sInitialized) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
sCurrent = this;
|
||||
struct sigaction sigact = {};
|
||||
#ifdef __arm__
|
||||
sigact.sa_sigaction = SignalTrampoline<SigAction>;
|
||||
#else
|
||||
sigact.sa_sigaction = SigAction;
|
||||
#endif
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
if (::sigaction(SIGPROF, &sigact, &sOldSigAction)) {
|
||||
MOZ_ASSERT(false);
|
||||
siginfo_t uinfo = {};
|
||||
uinfo.si_signo = sFillStackSignum;
|
||||
uinfo.si_code = SI_QUEUE;
|
||||
uinfo.si_pid = getpid();
|
||||
uinfo.si_uid = getuid();
|
||||
uinfo.si_value.sival_ptr = this;
|
||||
if (::syscall(SYS_rt_tgsigqueueinfo, uinfo.si_pid,
|
||||
mThreadID, sFillStackSignum, &uinfo)) {
|
||||
// rt_tgsigqueueinfo was added in Linux 2.6.31.
|
||||
// Could have failed because the syscall did not exist.
|
||||
return;
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(!::syscall(SYS_tgkill, getpid(), mThreadID, SIGPROF));
|
||||
MOZ_ALWAYS_TRUE(!::sem_wait(&sSem));
|
||||
MOZ_ALWAYS_TRUE(!::sem_wait(&mSem));
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
if (!mInitialized) {
|
||||
@ -177,17 +163,16 @@ ThreadStackHelper::GetStack(Stack& aStack)
|
||||
#ifdef XP_LINUX
|
||||
|
||||
int ThreadStackHelper::sInitialized;
|
||||
sem_t ThreadStackHelper::sSem;
|
||||
struct sigaction ThreadStackHelper::sOldSigAction;
|
||||
ThreadStackHelper* ThreadStackHelper::sCurrent;
|
||||
int ThreadStackHelper::sFillStackSignum;
|
||||
|
||||
void
|
||||
ThreadStackHelper::SigAction(int aSignal, siginfo_t* aInfo, void* aContext)
|
||||
ThreadStackHelper::FillStackHandler(int aSignal, siginfo_t* aInfo,
|
||||
void* aContext)
|
||||
{
|
||||
::sigaction(SIGPROF, &sOldSigAction, nullptr);
|
||||
sCurrent->FillStackBuffer();
|
||||
sCurrent = nullptr;
|
||||
::sem_post(&sSem);
|
||||
ThreadStackHelper* const helper =
|
||||
reinterpret_cast<ThreadStackHelper*>(aInfo->si_value.sival_ptr);
|
||||
helper->FillStackBuffer();
|
||||
::sem_post(&helper->mSem);
|
||||
}
|
||||
|
||||
#endif // XP_LINUX
|
||||
|
@ -84,12 +84,11 @@ public:
|
||||
#if defined(XP_LINUX)
|
||||
private:
|
||||
static int sInitialized;
|
||||
static sem_t sSem;
|
||||
static struct sigaction sOldSigAction;
|
||||
static ThreadStackHelper* sCurrent;
|
||||
static int sFillStackSignum;
|
||||
|
||||
static void SigAction(int aSignal, siginfo_t* aInfo, void* aContext);
|
||||
static void FillStackHandler(int aSignal, siginfo_t* aInfo, void* aContext);
|
||||
|
||||
sem_t mSem;
|
||||
pid_t mThreadID;
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
|
Loading…
Reference in New Issue
Block a user