diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h index b1019e870ce..1f6a0e35f8f 100644 --- a/runtime/vm/os_thread.h +++ b/runtime/vm/os_thread.h @@ -305,8 +305,9 @@ class OSThread : public BaseThread { friend class IsolateGroup; // to access set_thread(Thread*). friend class OSThreadIterator; - friend class ThreadInterrupterWin; friend class ThreadInterrupterFuchsia; + friend class ThreadInterrupterMacOS; + friend class ThreadInterrupterWin; friend class ThreadPool; // to access owning_thread_pool_worker_ }; diff --git a/runtime/vm/signal_handler_macos.cc b/runtime/vm/signal_handler_macos.cc index 70e5319dd67..0e0c5910caa 100644 --- a/runtime/vm/signal_handler_macos.cc +++ b/runtime/vm/signal_handler_macos.cc @@ -91,24 +91,11 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) { } void SignalHandler::Install(SignalAction action) { - struct sigaction act = {}; - act.sa_handler = NULL; - act.sa_sigaction = action; - sigemptyset(&act.sa_mask); - act.sa_flags = SA_RESTART | SA_SIGINFO; - int r = sigaction(SIGPROF, &act, NULL); - ASSERT(r == 0); + // Nothing to do on MacOS. } void SignalHandler::Remove() { - // Ignore future SIGPROF signals because by default SIGPROF will terminate - // the process and we may have some signals in flight. - struct sigaction act = {}; - act.sa_handler = SIG_IGN; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - int r = sigaction(SIGPROF, &act, NULL); - ASSERT(r == 0); + // Nothing to do on MacOS. } } // namespace dart diff --git a/runtime/vm/thread_interrupter.cc b/runtime/vm/thread_interrupter.cc index be990abb53a..978b4284a0c 100644 --- a/runtime/vm/thread_interrupter.cc +++ b/runtime/vm/thread_interrupter.cc @@ -66,15 +66,6 @@ void ThreadInterrupter::Init() { void ThreadInterrupter::Startup() { ASSERT(initialized_); - if (IsDebuggerAttached()) { - MonitorLocker shutdown_ml(monitor_); - shutdown_ = true; - if (FLAG_trace_thread_interrupter) { - OS::PrintErr( - "ThreadInterrupter disabled because a debugger is attached.\n"); - } - return; - } if (FLAG_trace_thread_interrupter) { OS::PrintErr("ThreadInterrupter starting up.\n"); } diff --git a/runtime/vm/thread_interrupter.h b/runtime/vm/thread_interrupter.h index c5d770ec5b1..8921ebef008 100644 --- a/runtime/vm/thread_interrupter.h +++ b/runtime/vm/thread_interrupter.h @@ -102,8 +102,6 @@ class ThreadInterrupter : public AllStatic { static std::atomic sample_buffer_lock_; static std::atomic sample_buffer_waiters_; - static bool IsDebuggerAttached(); - static bool InDeepSleep() { return current_wait_time_ == Monitor::kNoTimeout; } diff --git a/runtime/vm/thread_interrupter_android.cc b/runtime/vm/thread_interrupter_android.cc index dbdd331e8fc..2f8ed986352 100644 --- a/runtime/vm/thread_interrupter_android.cc +++ b/runtime/vm/thread_interrupter_android.cc @@ -61,10 +61,6 @@ void ThreadInterruptSignalHandler(int signal, siginfo_t* info, void* context_) { #endif } // namespace -bool ThreadInterrupter::IsDebuggerAttached() { - return false; -} - void ThreadInterrupter::InterruptThread(OSThread* thread) { if (FLAG_trace_thread_interrupter) { OS::PrintErr("ThreadInterrupter interrupting %p\n", diff --git a/runtime/vm/thread_interrupter_fuchsia.cc b/runtime/vm/thread_interrupter_fuchsia.cc index b2955694ae9..235bbed3890 100644 --- a/runtime/vm/thread_interrupter_fuchsia.cc +++ b/runtime/vm/thread_interrupter_fuchsia.cc @@ -227,10 +227,6 @@ class ThreadInterrupterFuchsia : public AllStatic { } }; -bool ThreadInterrupter::IsDebuggerAttached() { - return false; -} - void ThreadInterrupter::InterruptThread(OSThread* thread) { if (FLAG_trace_thread_interrupter) { OS::PrintErr("ThreadInterrupter suspending %p\n", diff --git a/runtime/vm/thread_interrupter_linux.cc b/runtime/vm/thread_interrupter_linux.cc index ee623db77ce..5d7661df6b0 100644 --- a/runtime/vm/thread_interrupter_linux.cc +++ b/runtime/vm/thread_interrupter_linux.cc @@ -48,10 +48,6 @@ class ThreadInterrupterLinux : public AllStatic { } }; -bool ThreadInterrupter::IsDebuggerAttached() { - return false; -} - void ThreadInterrupter::InterruptThread(OSThread* thread) { if (FLAG_trace_thread_interrupter) { OS::PrintErr("ThreadInterrupter interrupting %p\n", diff --git a/runtime/vm/thread_interrupter_macos.cc b/runtime/vm/thread_interrupter_macos.cc index ba9ce5acc05..9676b53f486 100644 --- a/runtime/vm/thread_interrupter_macos.cc +++ b/runtime/vm/thread_interrupter_macos.cc @@ -5,12 +5,15 @@ #include "platform/globals.h" #if defined(DART_HOST_OS_MACOS) -#include // NOLINT -#include // NOLINT -#include // NOLINT -#include // NOLINT -#include // NOLINT -#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT #include "vm/flags.h" #include "vm/os.h" @@ -24,70 +27,110 @@ namespace dart { DECLARE_FLAG(bool, trace_thread_interrupter); -// Returns true if the current process is being debugged (either -// running under the debugger or has a debugger attached post facto). -// Code from https://developer.apple.com/library/content/qa/qa1361/_index.html -bool ThreadInterrupter::IsDebuggerAttached() { - struct kinfo_proc info; - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - info.kp_proc.p_flag = 0; - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; - size_t size = sizeof(info); +#if defined(HOST_ARCH_X64) +#define THREAD_STATE_FLAVOR x86_THREAD_STATE64 +#define THREAD_STATE_FLAVOR_SIZE x86_THREAD_STATE64_COUNT +typedef x86_thread_state64_t thread_state_flavor_t; +#elif defined(HOST_ARCH_ARM64) +#define THREAD_STATE_FLAVOR ARM_THREAD_STATE64 +#define THREAD_STATE_FLAVOR_SIZE ARM_THREAD_STATE64_COUNT +typedef arm_thread_state64_t thread_state_flavor_t; +#elif defined(HOST_ARCH_ARM) +#define THREAD_STATE_FLAVOR ARM_THREAD_STATE32 +#define THREAD_STATE_FLAVOR_SIZE ARM_THREAD_STATE32_COUNT +typedef arm_thread_state32_t thread_state_flavor_t; +#else +#error "Unsupported architecture." +#endif // HOST_ARCH_... - // Call sysctl. - size = sizeof(info); - int junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); - ASSERT(junk == 0); - // We're being debugged if the P_TRACED flag is set. - return ((info.kp_proc.p_flag & P_TRACED) != 0); -} - -class ThreadInterrupterMacOS : public AllStatic { +class ThreadInterrupterMacOS { public: - static void ThreadInterruptSignalHandler(int signal, - siginfo_t* info, - void* context_) { - if (signal != SIGPROF) { - return; - } - Thread* thread = Thread::Current(); - if (thread == NULL) { - return; - } - ThreadInterrupter::SampleBufferWriterScope scope; - if (!scope.CanSample()) { - return; - } - // Extract thread state. - ucontext_t* context = reinterpret_cast(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); + explicit ThreadInterrupterMacOS(OSThread* os_thread) : os_thread_(os_thread) { + ASSERT(os_thread != nullptr); + mach_thread_ = pthread_mach_thread_np(os_thread->id()); + ASSERT(reinterpret_cast(mach_thread_) != nullptr); + res = thread_suspend(mach_thread_); } + + void CollectSample() { + if (res != KERN_SUCCESS) { + return; + } + auto count = static_cast(THREAD_STATE_FLAVOR_SIZE); + thread_state_flavor_t state; + kern_return_t res = + thread_get_state(mach_thread_, THREAD_STATE_FLAVOR, + reinterpret_cast(&state), &count); + ASSERT(res == KERN_SUCCESS); + Thread* thread = static_cast(os_thread_->thread()); + if (thread == nullptr) { + return; + } + Profiler::SampleThread(thread, ProcessState(state)); + } + + ~ThreadInterrupterMacOS() { + if (res != KERN_SUCCESS) { + return; + } + res = thread_resume(mach_thread_); + ASSERT(res == KERN_SUCCESS); + } + + private: + static InterruptedThreadState ProcessState(thread_state_flavor_t state) { + InterruptedThreadState its; +#if defined(HOST_ARCH_X64) + its.pc = state.__rip; + its.fp = state.__rbp; + its.csp = state.__rsp; + its.dsp = state.__rsp; + its.lr = 0; +#elif defined(HOST_ARCH_ARM64) + its.pc = state.__pc; + its.fp = state.__fp; + its.csp = state.__sp; + its.dsp = state.__sp; + its.lr = state.__lr; +#elif defined(HOST_ARCH_ARM) + its.pc = state.__pc; + its.fp = state.__r[7]; + its.csp = state.__sp; + its.dsp = state.__sp; + its.lr = state.__lr; +#endif // HOST_ARCH_... + +#if defined(TARGET_ARCH_ARM64) && !defined(USING_SIMULATOR) + its.dsp = state.__x[SPREG]; +#endif + return its; + } + + kern_return_t res; + OSThread* os_thread_; + mach_port_t mach_thread_; }; -void ThreadInterrupter::InterruptThread(OSThread* thread) { +void ThreadInterrupter::InterruptThread(OSThread* os_thread) { + ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id())); if (FLAG_trace_thread_interrupter) { - OS::PrintErr("ThreadInterrupter interrupting %p\n", thread->id()); + OS::PrintErr("ThreadInterrupter interrupting %p\n", os_thread->id()); } - int result = pthread_kill(thread->id(), SIGPROF); - ASSERT((result == 0) || (result == ESRCH)); + + ThreadInterrupter::SampleBufferWriterScope scope; + if (!scope.CanSample()) { + return; + } + ThreadInterrupterMacOS interrupter(os_thread); + interrupter.CollectSample(); } void ThreadInterrupter::InstallSignalHandler() { - SignalHandler::Install(&ThreadInterrupterMacOS::ThreadInterruptSignalHandler); + // Nothing to do on MacOS. } void ThreadInterrupter::RemoveSignalHandler() { - SignalHandler::Remove(); + // Nothing to do on MacOS. } #endif // !PRODUCT diff --git a/runtime/vm/thread_interrupter_win.cc b/runtime/vm/thread_interrupter_win.cc index bfbf5d1e55d..3db50234b3f 100644 --- a/runtime/vm/thread_interrupter_win.cc +++ b/runtime/vm/thread_interrupter_win.cc @@ -90,10 +90,6 @@ class ThreadInterrupterWin : public AllStatic { } }; -bool ThreadInterrupter::IsDebuggerAttached() { - return false; -} - void ThreadInterrupter::InterruptThread(OSThread* thread) { if (FLAG_trace_thread_interrupter) { OS::PrintErr("ThreadInterrupter suspending %p\n",