Chromium Code Reviews| 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 <magenta/process.h> | 10 #include <magenta/process.h> |
| 11 #include <magenta/status.h> | 11 #include <magenta/status.h> |
| 12 #include <magenta/syscalls.h> | 12 #include <magenta/syscalls.h> |
| 13 #include <magenta/syscalls/debug.h> | 13 #include <magenta/syscalls/debug.h> |
| 14 #include <magenta/syscalls/object.h> | |
| 14 #include <magenta/types.h> | 15 #include <magenta/types.h> |
| 15 | 16 |
| 16 #include "vm/flags.h" | 17 #include "vm/flags.h" |
| 17 #include "vm/instructions.h" | 18 #include "vm/instructions.h" |
| 18 #include "vm/os.h" | 19 #include "vm/os.h" |
| 19 #include "vm/profiler.h" | 20 #include "vm/profiler.h" |
| 20 | 21 |
| 21 namespace dart { | 22 namespace dart { |
| 22 | 23 |
| 23 #ifndef PRODUCT | 24 #ifndef PRODUCT |
| 24 | 25 |
| 25 DECLARE_FLAG(bool, thread_interrupter); | 26 DECLARE_FLAG(bool, thread_interrupter); |
| 26 DECLARE_FLAG(bool, trace_thread_interrupter); | 27 DECLARE_FLAG(bool, trace_thread_interrupter); |
| 27 | 28 |
| 28 // TODO(MG-430): Currently, CPU profiling for Fuchsia is arranged very similarly | 29 // 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 // to our Windows profiling. That is, the interrupter thread iterates over |
| 30 // all threads, suspends them, samples various things, and then resumes them. | 31 // 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 // When MG-430 is resolved, the code below should be rewritten to use whatever |
| 32 // feature is added for it. | 33 // feature is added for it. |
| 33 | 34 |
| 34 // TODO(MG-795): The profiler is currently off by default on Fuchsia because | 35 // A scope within which a target thread is suspended. When the scope is exited, |
| 35 // suspending a thread that is in a call to pthread_cond_wait() causes | 36 // the thread is resumed and its handle is closed. |
| 36 // pthread_cond_wait() to return ETIMEDOUT. | 37 class ThreadSuspendScope { |
| 38 public: | |
| 39 explicit ThreadSuspendScope(mx_handle_t thread_handle) | |
| 40 : thread_handle_(thread_handle), suspended_(true) { | |
| 41 mx_status_t status = mx_task_suspend(thread_handle); | |
| 42 // If a thread is somewhere where suspend is impossible, mx_task_suspend() | |
| 43 // can return ERR_NOT_SUPPORTED. | |
| 44 if ((status != NO_ERROR) && (status != ERR_NOT_SUPPORTED)) { | |
| 45 if (FLAG_trace_thread_interrupter) { | |
| 46 OS::PrintErr("ThreadInterrupter: mx_task_suspend failed: %s\n", | |
| 47 mx_status_get_string(status)); | |
| 48 } | |
| 49 suspended_ = false; | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 ~ThreadSuspendScope() { | |
| 54 if (suspended_) { | |
| 55 mx_status_t status = mx_task_resume(thread_handle_, 0); | |
| 56 if (status != NO_ERROR) { | |
| 57 // If we fail to resume a thread, then it's likely the program will | |
| 58 // hang. Crash instead. | |
| 59 FATAL1("mx_task_resume failed: %s", mx_status_get_string(status)); | |
| 60 } | |
| 61 } | |
| 62 mx_handle_close(thread_handle_); | |
| 63 } | |
| 64 | |
| 65 bool suspended() const { return suspended_; } | |
| 66 | |
| 67 private: | |
| 68 mx_handle_t thread_handle_; | |
| 69 bool suspended_; | |
| 70 | |
| 71 DISALLOW_ALLOCATION(); | |
| 72 DISALLOW_COPY_AND_ASSIGN(ThreadSuspendScope); | |
| 73 }; | |
| 37 | 74 |
| 38 class ThreadInterrupterFuchsia : public AllStatic { | 75 class ThreadInterrupterFuchsia : public AllStatic { |
| 39 public: | 76 public: |
| 40 static bool GrabRegisters(mx_handle_t thread, InterruptedThreadState* state) { | 77 static bool GrabRegisters(mx_handle_t thread, InterruptedThreadState* state) { |
| 41 // TODO(zra): Enable this when mx_thread_read_state() works on suspended | 78 char buf[MX_MAX_THREAD_STATE_SIZE]; |
|
dje
2017/06/02 17:09:06
This won't necessarily have sufficient alignment,
zra
2017/06/02 17:46:47
The docs don't mention alignment requirements for
dje
2017/06/02 18:40:07
The requirements are more related to how the conte
zra
2017/06/02 20:39:01
Done.
| |
| 42 // threads. | 79 uint32_t regset_size = MX_MAX_THREAD_STATE_SIZE; |
| 43 while (false) { | 80 mx_status_t status = mx_thread_read_state( |
| 44 char buf[MX_MAX_THREAD_STATE_SIZE]; | 81 thread, MX_THREAD_STATE_REGSET0, &buf[0], regset_size, ®set_size); |
| 45 uint32_t regset_size = MX_MAX_THREAD_STATE_SIZE; | 82 if (status != NO_ERROR) { |
| 46 mx_status_t status = mx_thread_read_state( | 83 if (FLAG_trace_thread_interrupter) { |
| 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", | 84 OS::PrintErr("ThreadInterrupter failed to get registers: %s\n", |
| 50 mx_status_get_string(status)); | 85 mx_status_get_string(status)); |
| 51 return false; | |
| 52 } | 86 } |
| 87 return false; | |
| 88 } | |
| 53 #if defined(TARGET_ARCH_X64) | 89 #if defined(TARGET_ARCH_X64) |
| 54 mx_x86_64_general_regs_t* regs = | 90 mx_x86_64_general_regs_t* regs = |
| 55 reinterpret_cast<mx_x86_64_general_regs_t*>(&buf[0]); | 91 reinterpret_cast<mx_x86_64_general_regs_t*>(&buf[0]); |
| 56 state->pc = static_cast<uintptr_t>(regs->rip); | 92 state->pc = static_cast<uintptr_t>(regs->rip); |
| 57 state->fp = static_cast<uintptr_t>(regs->rbp); | 93 state->fp = static_cast<uintptr_t>(regs->rbp); |
| 58 state->csp = static_cast<uintptr_t>(regs->rsp); | 94 state->csp = static_cast<uintptr_t>(regs->rsp); |
| 59 state->dsp = static_cast<uintptr_t>(regs->rsp); | 95 state->dsp = static_cast<uintptr_t>(regs->rsp); |
| 60 #elif defined(TARGET_ARCH_ARM64) | 96 #elif defined(TARGET_ARCH_ARM64) |
| 61 mx_arm64_general_regs_t* regs = | 97 mx_arm64_general_regs_t* regs = |
| 62 reinterpret_cast<mx_arm64_general_regs_t*>(&buf[0]); | 98 reinterpret_cast<mx_arm64_general_regs_t*>(&buf[0]); |
| 63 state->pc = static_cast<uintptr_t>(regs->pc); | 99 state->pc = static_cast<uintptr_t>(regs->pc); |
| 64 state->fp = static_cast<uintptr_t>(regs->r[FPREG]); | 100 state->fp = static_cast<uintptr_t>(regs->r[FPREG]); |
| 65 state->csp = static_cast<uintptr_t>(regs->sp); | 101 state->csp = static_cast<uintptr_t>(regs->sp); |
| 66 state->dsp = static_cast<uintptr_t>(regs->r[SPREG]); | 102 state->dsp = static_cast<uintptr_t>(regs->r[SPREG]); |
| 67 state->lr = static_cast<uintptr_t>(regs->lr); | 103 state->lr = static_cast<uintptr_t>(regs->lr); |
| 68 #else | 104 #else |
| 69 #error "Unsupported architecture" | 105 #error "Unsupported architecture" |
| 70 #endif | 106 #endif |
| 71 } | |
| 72 return true; | 107 return true; |
| 73 } | 108 } |
| 74 | 109 |
| 75 | 110 |
| 76 static void Interrupt(OSThread* os_thread) { | 111 static void Interrupt(OSThread* os_thread) { |
| 112 ASSERT(os_thread->id() != MX_KOID_INVALID); | |
| 77 ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id())); | 113 ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id())); |
| 78 mx_status_t status; | 114 mx_status_t status; |
| 79 | 115 |
| 80 // Get a handle on the target thread. | 116 // Get a handle on the target thread. |
| 81 mx_koid_t target_thread_koid = os_thread->id(); | 117 const mx_koid_t target_thread_koid = os_thread->id(); |
| 82 if (FLAG_trace_thread_interrupter) { | 118 if (FLAG_trace_thread_interrupter) { |
| 83 OS::Print("ThreadInterrupter: interrupting thread with koid=%d\n", | 119 OS::PrintErr("ThreadInterrupter: interrupting thread with koid=%d\n", |
| 84 target_thread_koid); | 120 target_thread_koid); |
| 85 } | 121 } |
| 86 mx_handle_t target_thread_handle; | 122 mx_handle_t target_thread_handle; |
| 87 status = mx_object_get_child(mx_process_self(), target_thread_koid, | 123 status = mx_object_get_child(mx_process_self(), target_thread_koid, |
| 88 MX_RIGHT_SAME_RIGHTS, &target_thread_handle); | 124 MX_RIGHT_SAME_RIGHTS, &target_thread_handle); |
| 89 if (status != NO_ERROR) { | 125 if (status != NO_ERROR) { |
| 90 if (FLAG_trace_thread_interrupter) { | 126 if (FLAG_trace_thread_interrupter) { |
| 91 OS::Print("ThreadInterrupter failed to get the thread handle: %s\n", | 127 OS::PrintErr("ThreadInterrupter: mx_object_get_child failed: %s\n", |
| 92 mx_status_get_string(status)); | 128 mx_status_get_string(status)); |
| 93 } | 129 } |
| 94 FATAL1("mx_object_get_child failed: %s", mx_status_get_string(status)); | 130 return; |
| 95 } | 131 } |
| 96 if (target_thread_handle == MX_HANDLE_INVALID) { | 132 if (target_thread_handle == MX_HANDLE_INVALID) { |
| 97 FATAL("ThreadInterrupter got an invalid target thread handle!"); | 133 if (FLAG_trace_thread_interrupter) { |
| 134 OS::PrintErr("ThreadInterrupter: mx_object_get_child gave an invalid " | |
| 135 "thread handle!"); | |
| 136 } | |
| 137 return; | |
| 98 } | 138 } |
| 99 | 139 |
| 100 // Pause the target thread. | 140 // This scope suspends the thread. When we exit the scope, the thread is |
| 101 status = mx_task_suspend(target_thread_handle); | 141 // resumed, and the thread handle is closed. |
| 142 ThreadSuspendScope tss(target_thread_handle); | |
| 143 if (!tss.suspended()) { | |
| 144 return; | |
| 145 } | |
| 146 | |
| 147 // Check that the thread is suspended. | |
| 148 mx_info_thread_t thread_info; | |
| 149 status = mx_object_get_info(target_thread_handle, | |
| 150 MX_INFO_THREAD, | |
| 151 &thread_info, | |
| 152 sizeof(thread_info), | |
| 153 NULL, | |
| 154 NULL); | |
| 102 if (status != NO_ERROR) { | 155 if (status != NO_ERROR) { |
| 103 if (FLAG_trace_thread_interrupter) { | 156 if (FLAG_trace_thread_interrupter) { |
| 104 OS::Print("ThreadInterrupter failed to suspend thread %ld: %s\n", | 157 OS::PrintErr("ThreadInterrupter: mx_object_get_info failed: %s\n", |
| 105 static_cast<intptr_t>(os_thread->id()), | 158 mx_status_get_string(status)); |
| 106 mx_status_get_string(status)); | |
| 107 } | 159 } |
| 108 mx_handle_close(target_thread_handle); | 160 return; |
| 109 FATAL1("mx_task_suspend failed: %s", mx_status_get_string(status)); | 161 } |
| 162 if (thread_info.state != MX_THREAD_STATE_SUSPENDED) { | |
|
zra
2017/06/02 16:51:23
@dje: Frequently, after calling mx_task_suspend()
dje
2017/06/02 17:09:06
Yeah, you currently have to poll to wait for the t
zra
2017/06/02 17:46:47
Changed to poll.
| |
| 163 if (FLAG_trace_thread_interrupter) { | |
| 164 OS::PrintErr("ThreadInterrupter: Thread is not suspended: state = %s\n", | |
| 165 ThreadStateGetString(thread_info.state)); | |
| 166 } | |
| 167 return; | |
| 110 } | 168 } |
| 111 | 169 |
| 112 // TODO(zra): Enable this when mx_thread_read_state() works on suspended | 170 // Grab the target thread's registers. |
| 113 // threads. | 171 InterruptedThreadState its; |
| 114 while (false) { | 172 if (!GrabRegisters(target_thread_handle, &its)) { |
| 115 // Grab the target thread's registers. | 173 return; |
| 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 } | 174 } |
| 175 // Currently we sample only threads that are associated | |
| 176 // with an isolate. It is safe to call 'os_thread->thread()' | |
| 177 // here as the thread which is being queried is suspended. | |
| 178 Thread* thread = os_thread->thread(); | |
| 179 if (thread != NULL) { | |
| 180 Profiler::SampleThread(thread, its); | |
| 181 } | |
| 182 } | |
| 134 | 183 |
| 135 // Resume the target thread. | 184 private: |
| 136 status = mx_task_resume(target_thread_handle, 0); | 185 static const char* ThreadStateGetString(uint32_t state) { |
| 137 if (status != NO_ERROR) { | 186 switch (state) { |
| 138 FATAL1("mx_task_resume failed: %s", mx_status_get_string(status)); | 187 case MX_THREAD_STATE_NEW: |
| 188 return "MX_THREAD_STATE_NEW"; | |
| 189 case MX_THREAD_STATE_RUNNING: | |
| 190 return "MX_THREAD_STATE_RUNNING"; | |
| 191 case MX_THREAD_STATE_SUSPENDED: | |
| 192 return "MX_THREAD_STATE_SUSPENDED"; | |
| 193 case MX_THREAD_STATE_BLOCKED: | |
| 194 return "MX_THREAD_STATE_BLOCKED"; | |
| 195 case MX_THREAD_STATE_DYING: | |
| 196 return "MX_THREAD_STATE_DYING"; | |
| 197 case MX_THREAD_STATE_DEAD: | |
| 198 return "MX_THREAD_STATE_DEAD"; | |
| 199 default: | |
| 200 return "<Unknown>"; | |
| 139 } | 201 } |
| 140 mx_handle_close(target_thread_handle); | |
| 141 } | 202 } |
| 142 }; | 203 }; |
| 143 | 204 |
| 144 | 205 |
| 145 bool ThreadInterrupter::IsDebuggerAttached() { | 206 bool ThreadInterrupter::IsDebuggerAttached() { |
| 146 return false; | 207 return false; |
| 147 } | 208 } |
| 148 | 209 |
| 149 | 210 |
| 150 void ThreadInterrupter::InterruptThread(OSThread* thread) { | 211 void ThreadInterrupter::InterruptThread(OSThread* thread) { |
| 151 if (FLAG_trace_thread_interrupter) { | 212 if (FLAG_trace_thread_interrupter) { |
| 152 OS::Print("ThreadInterrupter suspending %p\n", | 213 OS::PrintErr("ThreadInterrupter suspending %p\n", |
| 153 reinterpret_cast<void*>(thread->id())); | 214 reinterpret_cast<void*>(thread->id())); |
| 154 } | 215 } |
| 155 ThreadInterrupterFuchsia::Interrupt(thread); | 216 ThreadInterrupterFuchsia::Interrupt(thread); |
| 156 if (FLAG_trace_thread_interrupter) { | 217 if (FLAG_trace_thread_interrupter) { |
| 157 OS::Print("ThreadInterrupter resuming %p\n", | 218 OS::PrintErr("ThreadInterrupter resuming %p\n", |
| 158 reinterpret_cast<void*>(thread->id())); | 219 reinterpret_cast<void*>(thread->id())); |
| 159 } | 220 } |
| 160 } | 221 } |
| 161 | 222 |
| 162 | 223 |
| 163 void ThreadInterrupter::InstallSignalHandler() { | 224 void ThreadInterrupter::InstallSignalHandler() { |
| 164 // Nothing to do on Fuchsia. | 225 // Nothing to do on Fuchsia. |
| 165 } | 226 } |
| 166 | 227 |
| 167 | 228 |
| 168 void ThreadInterrupter::RemoveSignalHandler() { | 229 void ThreadInterrupter::RemoveSignalHandler() { |
| 169 // Nothing to do on Fuchsia. | 230 // Nothing to do on Fuchsia. |
| 170 } | 231 } |
| 171 | 232 |
| 172 #endif // !PRODUCT | 233 #endif // !PRODUCT |
| 173 | 234 |
| 174 } // namespace dart | 235 } // namespace dart |
| 175 | 236 |
| 176 #endif // defined(HOST_OS_FUCHSIA) | 237 #endif // defined(HOST_OS_FUCHSIA) |
| OLD | NEW |