OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "platform/globals.h" | 5 #include "platform/globals.h" |
6 #if defined(HOST_OS_FUCHSIA) | 6 #if defined(HOST_OS_FUCHSIA) |
7 | 7 |
8 #include "vm/thread_interrupter.h" | 8 #include "vm/thread_interrupter.h" |
9 | 9 |
10 #include "platform/assert.h" | 10 #include <magenta/process.h> |
| 11 #include <magenta/status.h> |
| 12 #include <magenta/syscalls.h> |
| 13 #include <magenta/syscalls/debug.h> |
| 14 #include <magenta/types.h> |
| 15 |
| 16 #include "vm/flags.h" |
| 17 #include "vm/instructions.h" |
| 18 #include "vm/os.h" |
| 19 #include "vm/profiler.h" |
11 | 20 |
12 namespace dart { | 21 namespace dart { |
13 | 22 |
| 23 #ifndef PRODUCT |
| 24 |
| 25 DECLARE_FLAG(bool, thread_interrupter); |
| 26 DECLARE_FLAG(bool, trace_thread_interrupter); |
| 27 |
| 28 // TODO(MG-430): Currently, CPU profiling for Fuchsia is arranged very similarly |
| 29 // to our Windows profiling. That is, the interrupter thread iterates over |
| 30 // all threads, suspends them, samples various things, and then resumes them. |
| 31 // When MG-430 is resolved, the code below should be rewritten to use whatever |
| 32 // feature is added for it. |
| 33 |
| 34 // TODO(zra): The profiler is currently off by default on Fuchsia because |
| 35 // suspending a thread that is in a call to pthread_cond_wait() causes |
| 36 // pthread_cond_wait() to return ETIMEDOUT. |
| 37 |
| 38 class ThreadInterrupterFuchsia : public AllStatic { |
| 39 public: |
| 40 static bool GrabRegisters(mx_handle_t thread, InterruptedThreadState* state) { |
| 41 // TODO(zra): Enable this when mx_thread_read_state() works on suspended |
| 42 // threads. |
| 43 while (false) { |
| 44 char buf[MX_MAX_THREAD_STATE_SIZE]; |
| 45 uint32_t regset_size = MX_MAX_THREAD_STATE_SIZE; |
| 46 mx_status_t status = mx_thread_read_state( |
| 47 thread, MX_THREAD_STATE_REGSET0, &buf[0], regset_size, ®set_size); |
| 48 if (status != NO_ERROR) { |
| 49 OS::PrintErr("ThreadInterrupter failed to get registers: %s\n", |
| 50 mx_status_get_string(status)); |
| 51 return false; |
| 52 } |
| 53 #if defined(TARGET_ARCH_X64) |
| 54 mx_x86_64_general_regs_t* regs = |
| 55 reinterpret_cast<mx_x86_64_general_regs_t*>(&buf[0]); |
| 56 state->pc = static_cast<uintptr_t>(regs->rip); |
| 57 state->fp = static_cast<uintptr_t>(regs->rbp); |
| 58 state->csp = static_cast<uintptr_t>(regs->rsp); |
| 59 state->dsp = static_cast<uintptr_t>(regs->rsp); |
| 60 #elif defined(TARGET_ARCH_ARM64) |
| 61 mx_aarch64_general_regs_t* regs = |
| 62 reinterpret_cast<mx_aarch64_general_regs_t*>(&buf[0]); |
| 63 state->pc = static_cast<uintptr_t>(regs->pc); |
| 64 state->fp = static_cast<uintptr_t>(regs->r[FPREG]); |
| 65 state->csp = static_cast<uintptr_t>(regs->sp); |
| 66 state->dsp = static_cast<uintptr_t>(regs->r[SPREG]); |
| 67 state->lr = static_cast<uintptr_t>(regs->lr); |
| 68 #else |
| 69 #error "Unsupported architecture" |
| 70 #endif |
| 71 } |
| 72 return true; |
| 73 } |
| 74 |
| 75 |
| 76 static void Interrupt(OSThread* os_thread) { |
| 77 ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id())); |
| 78 mx_status_t status; |
| 79 |
| 80 // Get a handle on the target thread. |
| 81 mx_koid_t target_thread_koid = os_thread->id(); |
| 82 if (FLAG_trace_thread_interrupter) { |
| 83 OS::Print("ThreadInterrupter: interrupting thread with koid=%d\n", |
| 84 target_thread_koid); |
| 85 } |
| 86 mx_handle_t target_thread_handle; |
| 87 status = mx_object_get_child(mx_process_self(), target_thread_koid, |
| 88 MX_RIGHT_SAME_RIGHTS, &target_thread_handle); |
| 89 if (status != NO_ERROR) { |
| 90 if (FLAG_trace_thread_interrupter) { |
| 91 OS::Print("ThreadInterrupter failed to get the thread handle: %s\n", |
| 92 mx_status_get_string(status)); |
| 93 } |
| 94 FATAL1("mx_object_get_child failed: %s", mx_status_get_string(status)); |
| 95 } |
| 96 if (target_thread_handle == MX_HANDLE_INVALID) { |
| 97 FATAL("ThreadInterrupter got an invalid target thread handle!"); |
| 98 } |
| 99 |
| 100 // Pause the target thread. |
| 101 status = mx_task_suspend(target_thread_handle); |
| 102 if (status != NO_ERROR) { |
| 103 if (FLAG_trace_thread_interrupter) { |
| 104 OS::Print("ThreadInterrupter failed to suspend thread %ld: %s\n", |
| 105 static_cast<intptr_t>(os_thread->id()), |
| 106 mx_status_get_string(status)); |
| 107 } |
| 108 mx_handle_close(target_thread_handle); |
| 109 FATAL1("mx_task_suspend failed: %s", mx_status_get_string(status)); |
| 110 } |
| 111 |
| 112 // TODO(zra): Enable this when mx_thread_read_state() works on suspended |
| 113 // threads. |
| 114 while (false) { |
| 115 // Grab the target thread's registers. |
| 116 InterruptedThreadState its; |
| 117 if (!GrabRegisters(target_thread_handle, &its)) { |
| 118 // Failed to get thread registers. |
| 119 status = mx_task_resume(target_thread_handle, 0); |
| 120 if (status != NO_ERROR) { |
| 121 FATAL1("mx_task_resume failed: %s", mx_status_get_string(status)); |
| 122 } |
| 123 mx_handle_close(target_thread_handle); |
| 124 return; |
| 125 } |
| 126 // Currently we sample only threads that are associated |
| 127 // with an isolate. It is safe to call 'os_thread->thread()' |
| 128 // here as the thread which is being queried is suspended. |
| 129 Thread* thread = os_thread->thread(); |
| 130 if (thread != NULL) { |
| 131 Profiler::SampleThread(thread, its); |
| 132 } |
| 133 } |
| 134 |
| 135 // Resume the target thread. |
| 136 status = mx_task_resume(target_thread_handle, 0); |
| 137 if (status != NO_ERROR) { |
| 138 FATAL1("mx_task_resume failed: %s", mx_status_get_string(status)); |
| 139 } |
| 140 mx_handle_close(target_thread_handle); |
| 141 } |
| 142 }; |
| 143 |
| 144 |
14 bool ThreadInterrupter::IsDebuggerAttached() { | 145 bool ThreadInterrupter::IsDebuggerAttached() { |
15 return false; | 146 return false; |
16 } | 147 } |
17 | 148 |
| 149 |
18 void ThreadInterrupter::InterruptThread(OSThread* thread) { | 150 void ThreadInterrupter::InterruptThread(OSThread* thread) { |
19 UNIMPLEMENTED(); | 151 if (FLAG_trace_thread_interrupter) { |
| 152 OS::Print("ThreadInterrupter suspending %p\n", |
| 153 reinterpret_cast<void*>(thread->id())); |
| 154 } |
| 155 ThreadInterrupterFuchsia::Interrupt(thread); |
| 156 if (FLAG_trace_thread_interrupter) { |
| 157 OS::Print("ThreadInterrupter resuming %p\n", |
| 158 reinterpret_cast<void*>(thread->id())); |
| 159 } |
20 } | 160 } |
21 | 161 |
22 | 162 |
23 void ThreadInterrupter::InstallSignalHandler() { | 163 void ThreadInterrupter::InstallSignalHandler() { |
24 UNIMPLEMENTED(); | 164 // Nothing to do on Fuchsia. |
25 } | 165 } |
26 | 166 |
27 | 167 |
28 void ThreadInterrupter::RemoveSignalHandler() { | 168 void ThreadInterrupter::RemoveSignalHandler() { |
29 UNIMPLEMENTED(); | 169 // Nothing to do on Fuchsia. |
30 } | 170 } |
31 | 171 |
| 172 #endif // !PRODUCT |
| 173 |
32 } // namespace dart | 174 } // namespace dart |
33 | 175 |
34 #endif // defined(HOST_OS_FUCHSIA) | 176 #endif // defined(HOST_OS_FUCHSIA) |
OLD | NEW |