| Index: runtime/vm/thread_interrupter_fuchsia.cc
|
| diff --git a/runtime/vm/thread_interrupter_fuchsia.cc b/runtime/vm/thread_interrupter_fuchsia.cc
|
| index 9f2507bb29028bb7e96d37f8c242eb3fed04d674..e587fb6021d74e3640611903b025fb867cbe8a02 100644
|
| --- a/runtime/vm/thread_interrupter_fuchsia.cc
|
| +++ b/runtime/vm/thread_interrupter_fuchsia.cc
|
| @@ -7,28 +7,170 @@
|
|
|
| #include "vm/thread_interrupter.h"
|
|
|
| -#include "platform/assert.h"
|
| +#include <magenta/process.h>
|
| +#include <magenta/status.h>
|
| +#include <magenta/syscalls.h>
|
| +#include <magenta/syscalls/debug.h>
|
| +#include <magenta/types.h>
|
| +
|
| +#include "vm/flags.h"
|
| +#include "vm/instructions.h"
|
| +#include "vm/os.h"
|
| +#include "vm/profiler.h"
|
|
|
| namespace dart {
|
|
|
| +#ifndef PRODUCT
|
| +
|
| +DECLARE_FLAG(bool, thread_interrupter);
|
| +DECLARE_FLAG(bool, trace_thread_interrupter);
|
| +
|
| +// TODO(MG-430): Currently, CPU profiling for Fuchsia is arranged very similarly
|
| +// to our Windows profiling. That is, the interrupter thread iterates over
|
| +// all threads, suspends them, samples various things, and then resumes them.
|
| +// When MG-430 is resolved, the code below should be rewritten to use whatever
|
| +// feature is added for it.
|
| +
|
| +// TODO(zra): The profiler is currently off by default on Fuchsia because
|
| +// suspending a thread that is in a call to pthread_cond_wait() causes
|
| +// pthread_cond_wait() to return ETIMEDOUT.
|
| +
|
| +class ThreadInterrupterFuchsia : public AllStatic {
|
| + public:
|
| + static bool GrabRegisters(mx_handle_t thread, InterruptedThreadState* state) {
|
| + // TODO(zra): Enable this when mx_thread_read_state() works on suspended
|
| + // threads.
|
| + while (false) {
|
| + char buf[MX_MAX_THREAD_STATE_SIZE];
|
| + uint32_t regset_size = MX_MAX_THREAD_STATE_SIZE;
|
| + mx_status_t status = mx_thread_read_state(
|
| + thread, MX_THREAD_STATE_REGSET0, &buf[0], regset_size, ®set_size);
|
| + if (status != NO_ERROR) {
|
| + OS::PrintErr("ThreadInterrupter failed to get registers: %s\n",
|
| + mx_status_get_string(status));
|
| + return false;
|
| + }
|
| +#if defined(TARGET_ARCH_X64)
|
| + mx_x86_64_general_regs_t* regs =
|
| + reinterpret_cast<mx_x86_64_general_regs_t*>(&buf[0]);
|
| + state->pc = static_cast<uintptr_t>(regs->rip);
|
| + state->fp = static_cast<uintptr_t>(regs->rbp);
|
| + state->csp = static_cast<uintptr_t>(regs->rsp);
|
| + state->dsp = static_cast<uintptr_t>(regs->rsp);
|
| +#elif defined(TARGET_ARCH_ARM64)
|
| + mx_aarch64_general_regs_t* regs =
|
| + reinterpret_cast<mx_aarch64_general_regs_t*>(&buf[0]);
|
| + state->pc = static_cast<uintptr_t>(regs->pc);
|
| + state->fp = static_cast<uintptr_t>(regs->r[FPREG]);
|
| + state->csp = static_cast<uintptr_t>(regs->sp);
|
| + state->dsp = static_cast<uintptr_t>(regs->r[SPREG]);
|
| + state->lr = static_cast<uintptr_t>(regs->lr);
|
| +#else
|
| +#error "Unsupported architecture"
|
| +#endif
|
| + }
|
| + return true;
|
| + }
|
| +
|
| +
|
| + static void Interrupt(OSThread* os_thread) {
|
| + ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id()));
|
| + mx_status_t status;
|
| +
|
| + // Get a handle on the target thread.
|
| + mx_koid_t target_thread_koid = os_thread->id();
|
| + if (FLAG_trace_thread_interrupter) {
|
| + OS::Print("ThreadInterrupter: interrupting thread with koid=%d\n",
|
| + target_thread_koid);
|
| + }
|
| + mx_handle_t target_thread_handle;
|
| + status = mx_object_get_child(mx_process_self(), target_thread_koid,
|
| + MX_RIGHT_SAME_RIGHTS, &target_thread_handle);
|
| + if (status != NO_ERROR) {
|
| + if (FLAG_trace_thread_interrupter) {
|
| + OS::Print("ThreadInterrupter failed to get the thread handle: %s\n",
|
| + mx_status_get_string(status));
|
| + }
|
| + FATAL1("mx_object_get_child failed: %s", mx_status_get_string(status));
|
| + }
|
| + if (target_thread_handle == MX_HANDLE_INVALID) {
|
| + FATAL("ThreadInterrupter got an invalid target thread handle!");
|
| + }
|
| +
|
| + // Pause the target thread.
|
| + status = mx_task_suspend(target_thread_handle);
|
| + if (status != NO_ERROR) {
|
| + if (FLAG_trace_thread_interrupter) {
|
| + OS::Print("ThreadInterrupter failed to suspend thread %ld: %s\n",
|
| + static_cast<intptr_t>(os_thread->id()),
|
| + mx_status_get_string(status));
|
| + }
|
| + mx_handle_close(target_thread_handle);
|
| + FATAL1("mx_task_suspend failed: %s", mx_status_get_string(status));
|
| + }
|
| +
|
| + // TODO(zra): Enable this when mx_thread_read_state() works on suspended
|
| + // threads.
|
| + while (false) {
|
| + // Grab the target thread's registers.
|
| + InterruptedThreadState its;
|
| + if (!GrabRegisters(target_thread_handle, &its)) {
|
| + // Failed to get thread registers.
|
| + status = mx_task_resume(target_thread_handle, 0);
|
| + if (status != NO_ERROR) {
|
| + FATAL1("mx_task_resume failed: %s", mx_status_get_string(status));
|
| + }
|
| + mx_handle_close(target_thread_handle);
|
| + return;
|
| + }
|
| + // Currently we sample only threads that are associated
|
| + // with an isolate. It is safe to call 'os_thread->thread()'
|
| + // here as the thread which is being queried is suspended.
|
| + Thread* thread = os_thread->thread();
|
| + if (thread != NULL) {
|
| + Profiler::SampleThread(thread, its);
|
| + }
|
| + }
|
| +
|
| + // Resume the target thread.
|
| + status = mx_task_resume(target_thread_handle, 0);
|
| + if (status != NO_ERROR) {
|
| + FATAL1("mx_task_resume failed: %s", mx_status_get_string(status));
|
| + }
|
| + mx_handle_close(target_thread_handle);
|
| + }
|
| +};
|
| +
|
| +
|
| bool ThreadInterrupter::IsDebuggerAttached() {
|
| return false;
|
| }
|
|
|
| +
|
| void ThreadInterrupter::InterruptThread(OSThread* thread) {
|
| - UNIMPLEMENTED();
|
| + if (FLAG_trace_thread_interrupter) {
|
| + OS::Print("ThreadInterrupter suspending %p\n",
|
| + reinterpret_cast<void*>(thread->id()));
|
| + }
|
| + ThreadInterrupterFuchsia::Interrupt(thread);
|
| + if (FLAG_trace_thread_interrupter) {
|
| + OS::Print("ThreadInterrupter resuming %p\n",
|
| + reinterpret_cast<void*>(thread->id()));
|
| + }
|
| }
|
|
|
|
|
| void ThreadInterrupter::InstallSignalHandler() {
|
| - UNIMPLEMENTED();
|
| + // Nothing to do on Fuchsia.
|
| }
|
|
|
|
|
| void ThreadInterrupter::RemoveSignalHandler() {
|
| - UNIMPLEMENTED();
|
| + // Nothing to do on Fuchsia.
|
| }
|
|
|
| +#endif // !PRODUCT
|
| +
|
| } // namespace dart
|
|
|
| #endif // defined(HOST_OS_FUCHSIA)
|
|
|