Index: runtime/vm/thread_interrupter_android.cc |
diff --git a/runtime/vm/thread_interrupter_android.cc b/runtime/vm/thread_interrupter_android.cc |
index 82be148cbd07f3b85c7f594eaf585ef9b832ca6b..0cc1b2a71e4285f16b479aadb25c93051e5a7cc4 100644 |
--- a/runtime/vm/thread_interrupter_android.cc |
+++ b/runtime/vm/thread_interrupter_android.cc |
@@ -21,29 +21,59 @@ namespace dart { |
DECLARE_FLAG(bool, thread_interrupter); |
DECLARE_FLAG(bool, trace_thread_interrupter); |
-class ThreadInterrupterAndroid : public AllStatic { |
- public: |
- static void ThreadInterruptSignalHandler(int signal, siginfo_t* info, |
- void* context_) { |
- if (signal != SIGPROF) { |
- return; |
- } |
- Thread* thread = Thread::Current(); |
- if (thread == NULL) { |
- return; |
- } |
- // Extract thread state. |
- ucontext_t* context = reinterpret_cast<ucontext_t*>(context_); |
- mcontext_t mcontext = context->uc_mcontext; |
- InterruptedThreadState its; |
- its.pc = SignalHandler::GetProgramCounter(mcontext); |
- its.fp = SignalHandler::GetFramePointer(mcontext); |
- its.csp = SignalHandler::GetCStackPointer(mcontext); |
- its.dsp = SignalHandler::GetDartStackPointer(mcontext); |
- its.lr = SignalHandler::GetLinkRegister(mcontext); |
- Profiler::SampleThread(thread, its); |
+static void ThreadInterruptSignalHandler(int signal, siginfo_t* info, |
+ void* context_) { |
+ if (signal != SIGPROF) { |
+ return; |
} |
-}; |
+ Thread* thread = Thread::Current(); |
+ if (thread == NULL) { |
+ return; |
+ } |
+ // Extract thread state. |
+ ucontext_t* context = reinterpret_cast<ucontext_t*>(context_); |
+ mcontext_t mcontext = context->uc_mcontext; |
+ InterruptedThreadState its; |
+ its.pc = SignalHandler::GetProgramCounter(mcontext); |
+ its.fp = SignalHandler::GetFramePointer(mcontext); |
+ its.csp = SignalHandler::GetCStackPointer(mcontext); |
+ its.dsp = SignalHandler::GetDartStackPointer(mcontext); |
+ its.lr = SignalHandler::GetLinkRegister(mcontext); |
+ Profiler::SampleThread(thread, its); |
+} |
+ |
+ |
+// Work around for a bug in old kernels (only fixed in 3.18 Android kernel): |
+// kernel does not clear If-Then execution state bits when entering ARM signal |
+// handler which violates requirements imposed by ARM architecture reference. |
+// Some CPUs look at these bits even while in ARM mode which causes them |
+// to skip some instructions in the prologue of the signal handler. |
+// To work around the issue we insert enough NOPs in the prologue to ensure that |
+// no actual instructions are skipped and then branch to the actual signal |
+// handler. |
+// For the kernel patch that fixes the issue see: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6ecf830e5029598732e04067e325d946097519cb |
+// Note: this function is marked "naked" because we must guarantee that |
+// our NOPs occur before any compiler generated prologue. |
+static __attribute__((naked)) void SignalHandlerTrampoline(int signal, |
+ siginfo_t* info, |
+ void* context_) { |
+ // IT (If-Then) instruction makes up to four instructions that follow it |
+ // conditional. |
+ asm volatile("nop; nop; nop; nop" : : : "memory"); |
+ |
+ // Tail-call into the actual signal handler. |
+ // Note: this code is split into a separate inline assembly block because |
+ // any code that compiler generates to satisfy register constraints must |
+ // be generated after four NOPs. |
+ register int arg0 asm("r0") = signal; |
+ register siginfo_t* arg1 asm("r1") = info; |
+ register void* arg2 asm("r2") = context_; |
+ asm volatile("bx %3" |
Florian Schneider
2016/05/02 13:44:45
Need this only on ARM, not on Android ia32 for exa
|
+ : |
+ : "r"(arg0), "r"(arg1), "r"(arg2), |
+ "r"(&ThreadInterruptSignalHandler) |
+ : "memory"); |
+} |
void ThreadInterrupter::InterruptThread(OSThread* thread) { |
@@ -57,8 +87,7 @@ void ThreadInterrupter::InterruptThread(OSThread* thread) { |
void ThreadInterrupter::InstallSignalHandler() { |
- SignalHandler::Install( |
- ThreadInterrupterAndroid::ThreadInterruptSignalHandler); |
+ SignalHandler::Install(&SignalHandlerTrampoline); |
} |