| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "platform/globals.h" | |
| #if defined(DART_HOST_OS_MACOS) | |
| #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" | |
| #include "vm/profiler.h" | |
| #include "vm/signal_handler.h" | |
| #include "vm/thread_interrupter.h" | |
| namespace dart { | |
| #if defined(DART_INCLUDE_PROFILER) | |
| DECLARE_FLAG(bool, trace_thread_interrupter); | |
| #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_... | |
| class ThreadInterrupterMacOS { | |
| public: | |
| 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); | |
| #if !defined(DART_HOST_OS_WATCH) | |
| res = thread_suspend(mach_thread_); | |
| #else | |
| res = KERN_FAILURE; | |
| #endif | |
| } | |
| 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; | |
| #if !defined(DART_HOST_OS_WATCH) | |
| kern_return_t res = | |
| thread_get_state(mach_thread_, THREAD_STATE_FLAVOR, | |
| reinterpret_cast<thread_state_t>(&state), &count); | |
| #else | |
| USE(count); | |
| kern_return_t res = KERN_FAILURE; | |
| #endif | |
| ASSERT(res == KERN_SUCCESS); | |
| Thread* thread = static_cast<Thread*>(os_thread_->thread_ignore_race()); | |
| if (thread == nullptr) { | |
| return; | |
| } | |
| ThreadInterruptScope signal_handler_scope; | |
| // Mac profiling is cross-thread and TSAN doesn't know that thread_suspend | |
| // establishes synchronization. Also, the suspended thread might hold a | |
| // TSAN-internal lock for a location the sampling thread might read, which | |
| // would cause a deadlock. | |
| TsanIgnoreScope tsan_ignore_scope; | |
| Profiler::SampleThread(thread, ProcessState(state)); | |
| } | |
| ~ThreadInterrupterMacOS() { | |
| if (res != KERN_SUCCESS) { | |
| return; | |
| } | |
| #if !defined(DART_HOST_OS_WATCH) | |
| res = thread_resume(mach_thread_); | |
| #else | |
| res = KERN_FAILURE; | |
| #endif | |
| 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_ARM64E) | |
| its.pc = __darwin_arm_thread_state64_get_pc(state); | |
| its.fp = __darwin_arm_thread_state64_get_fp(state); | |
| its.csp = __darwin_arm_thread_state64_get_sp(state); | |
| its.dsp = state.__x[15]; | |
| its.lr = __darwin_arm_thread_state64_get_lr(state); | |
| #elif defined(HOST_ARCH_ARM64) | |
| its.pc = state.__pc; | |
| its.fp = state.__fp; | |
| its.csp = state.__sp; | |
| its.dsp = state.__x[15]; | |
| 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(DART_INCLUDE_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* os_thread) { | |
| ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id())); | |
| if (FLAG_trace_thread_interrupter) { | |
| OS::PrintErr("ThreadInterrupter interrupting %p\n", os_thread->id()); | |
| } | |
| ThreadInterrupterMacOS interrupter(os_thread); | |
| interrupter.CollectSample(); | |
| } | |
| void ThreadInterrupter::InstallSignalHandler() { | |
| // Nothing to do on MacOS. | |
| } | |
| void ThreadInterrupter::RemoveSignalHandler() { | |
| // Nothing to do on MacOS. | |
| } | |
| #endif // defined(DART_INCLUDE_PROFILER) | |
| } // namespace dart | |
| #endif // defined(DART_HOST_OS_MACOS) |