mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:18:13 +00:00
[ VM / Profiler ] Update MacOS thread interrupter to use Mach APIs instead of SIGPROF
Enabling the CPU profiler on OSX/iOS with a lldb connection would cause applications to slow to a crawl due to lldb performing checks on each SIGPROF. This change replaces the SIGPROF based thread interrupter for MacOS with one based on the Mach Thread APIs. Fixes https://github.com/dart-lang/sdk/issues/47139 TEST=Manual testing, CQ Change-Id: Iedfd73a83f92d51e01b98bfa281440c7d1ba9e08 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/216220 Commit-Queue: Ben Konyi <bkonyi@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
parent
62a5289e7a
commit
4daa257b32
|
@ -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_
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -102,8 +102,6 @@ class ThreadInterrupter : public AllStatic {
|
|||
static std::atomic<intptr_t> sample_buffer_lock_;
|
||||
static std::atomic<intptr_t> sample_buffer_waiters_;
|
||||
|
||||
static bool IsDebuggerAttached();
|
||||
|
||||
static bool InDeepSleep() {
|
||||
return current_wait_time_ == Monitor::kNoTimeout;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
#include "platform/globals.h"
|
||||
#if defined(DART_HOST_OS_MACOS)
|
||||
|
||||
#include <assert.h> // NOLINT
|
||||
#include <errno.h> // NOLINT
|
||||
#include <stdbool.h> // NOLINT
|
||||
#include <sys/sysctl.h> // NOLINT
|
||||
#include <sys/types.h> // NOLINT
|
||||
#include <unistd.h> // NOLINT
|
||||
#include <assert.h> // NOLINT
|
||||
#include <errno.h> // NOLINT
|
||||
#include <mach/kern_return.h> // NOLINT
|
||||
#include <mach/mach.h> // NOLINT
|
||||
#include <mach/thread_act.h> // NOLINT
|
||||
#include <stdbool.h> // NOLINT
|
||||
#include <sys/sysctl.h> // NOLINT
|
||||
#include <sys/types.h> // NOLINT
|
||||
#include <unistd.h> // 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<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);
|
||||
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<void*>(mach_thread_) != nullptr);
|
||||
res = thread_suspend(mach_thread_);
|
||||
}
|
||||
|
||||
void CollectSample() {
|
||||
if (res != KERN_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
auto count = static_cast<mach_msg_type_number_t>(THREAD_STATE_FLAVOR_SIZE);
|
||||
thread_state_flavor_t state;
|
||||
kern_return_t res =
|
||||
thread_get_state(mach_thread_, THREAD_STATE_FLAVOR,
|
||||
reinterpret_cast<thread_state_t>(&state), &count);
|
||||
ASSERT(res == KERN_SUCCESS);
|
||||
Thread* thread = static_cast<Thread*>(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.__fp;
|
||||
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
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue