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 } | |
|
siva
2017/06/02 18:28:32
Not sure I understand why suspended_ remains true
zra
2017/06/02 20:39:01
Whoops. You're right. Changed to set suspended_ to
| |
| 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]; |
| 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. |
| 102 if (status != NO_ERROR) { | 142 ThreadSuspendScope tss(target_thread_handle); |
| 103 if (FLAG_trace_thread_interrupter) { | 143 if (!tss.suspended()) { |
| 104 OS::Print("ThreadInterrupter failed to suspend thread %ld: %s\n", | 144 return; |
| 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 } | 145 } |
| 111 | 146 |
| 112 // TODO(zra): Enable this when mx_thread_read_state() works on suspended | 147 // Check that the thread is suspended. |
| 113 // threads. | 148 status = PollThreadUntilSuspended(target_thread_handle); |
| 114 while (false) { | 149 if (status != NO_ERROR) { |
| 115 // Grab the target thread's registers. | 150 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 } | 151 } |
| 134 | 152 |
| 135 // Resume the target thread. | 153 // Grab the target thread's registers. |
| 136 status = mx_task_resume(target_thread_handle, 0); | 154 InterruptedThreadState its; |
| 137 if (status != NO_ERROR) { | 155 if (!GrabRegisters(target_thread_handle, &its)) { |
| 138 FATAL1("mx_task_resume failed: %s", mx_status_get_string(status)); | 156 return; |
| 139 } | 157 } |
|
siva
2017/06/02 18:28:32
Why not change the API of mx_thread_read_state(...
dje
2017/06/02 18:48:51
This exists as ERR_BAD_STATE.
https://fuchsia.goog
siva
2017/06/02 19:43:57
ERR_BAD_STATE could potentially be returned for ot
| |
| 140 mx_handle_close(target_thread_handle); | 158 // Currently we sample only threads that are associated |
| 159 // with an isolate. It is safe to call 'os_thread->thread()' | |
| 160 // here as the thread which is being queried is suspended. | |
| 161 Thread* thread = os_thread->thread(); | |
| 162 if (thread != NULL) { | |
| 163 Profiler::SampleThread(thread, its); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 private: | |
| 168 static const char* ThreadStateGetString(uint32_t state) { | |
| 169 switch (state) { | |
| 170 case MX_THREAD_STATE_NEW: | |
| 171 return "MX_THREAD_STATE_NEW"; | |
| 172 case MX_THREAD_STATE_RUNNING: | |
| 173 return "MX_THREAD_STATE_RUNNING"; | |
| 174 case MX_THREAD_STATE_SUSPENDED: | |
| 175 return "MX_THREAD_STATE_SUSPENDED"; | |
| 176 case MX_THREAD_STATE_BLOCKED: | |
| 177 return "MX_THREAD_STATE_BLOCKED"; | |
| 178 case MX_THREAD_STATE_DYING: | |
| 179 return "MX_THREAD_STATE_DYING"; | |
| 180 case MX_THREAD_STATE_DEAD: | |
| 181 return "MX_THREAD_STATE_DEAD"; | |
| 182 default: | |
| 183 return "<Unknown>"; | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 static mx_status_t PollThreadUntilSuspended(mx_handle_t thread_handle) { | |
| 188 while (true) { | |
| 189 mx_info_thread_t thread_info; | |
| 190 mx_status_t status = mx_object_get_info(thread_handle, | |
| 191 MX_INFO_THREAD, | |
| 192 &thread_info, | |
| 193 sizeof(thread_info), | |
| 194 NULL, | |
| 195 NULL); | |
| 196 if (status != NO_ERROR) { | |
| 197 if (FLAG_trace_thread_interrupter) { | |
| 198 OS::PrintErr("ThreadInterrupter: mx_object_get_info failed: %s\n", | |
| 199 mx_status_get_string(status)); | |
| 200 } | |
| 201 return status; | |
| 202 } | |
| 203 if (thread_info.state == MX_THREAD_STATE_SUSPENDED) { | |
| 204 // Success. | |
| 205 return NO_ERROR; | |
| 206 } | |
| 207 if (thread_info.state == MX_THREAD_STATE_RUNNING) { | |
| 208 // Poll. | |
| 209 continue; | |
| 210 } | |
| 211 if (FLAG_trace_thread_interrupter) { | |
| 212 OS::PrintErr("ThreadInterrupter: Thread is not suspended: %s\n", | |
| 213 ThreadStateGetString(thread_info.state)); | |
| 214 } | |
| 215 return ERR_BAD_STATE; | |
| 216 } | |
| 141 } | 217 } |
| 142 }; | 218 }; |
| 143 | 219 |
| 144 | 220 |
| 145 bool ThreadInterrupter::IsDebuggerAttached() { | 221 bool ThreadInterrupter::IsDebuggerAttached() { |
| 146 return false; | 222 return false; |
| 147 } | 223 } |
| 148 | 224 |
| 149 | 225 |
| 150 void ThreadInterrupter::InterruptThread(OSThread* thread) { | 226 void ThreadInterrupter::InterruptThread(OSThread* thread) { |
| 151 if (FLAG_trace_thread_interrupter) { | 227 if (FLAG_trace_thread_interrupter) { |
| 152 OS::Print("ThreadInterrupter suspending %p\n", | 228 OS::PrintErr("ThreadInterrupter suspending %p\n", |
| 153 reinterpret_cast<void*>(thread->id())); | 229 reinterpret_cast<void*>(thread->id())); |
| 154 } | 230 } |
| 155 ThreadInterrupterFuchsia::Interrupt(thread); | 231 ThreadInterrupterFuchsia::Interrupt(thread); |
| 156 if (FLAG_trace_thread_interrupter) { | 232 if (FLAG_trace_thread_interrupter) { |
| 157 OS::Print("ThreadInterrupter resuming %p\n", | 233 OS::PrintErr("ThreadInterrupter resuming %p\n", |
| 158 reinterpret_cast<void*>(thread->id())); | 234 reinterpret_cast<void*>(thread->id())); |
| 159 } | 235 } |
| 160 } | 236 } |
| 161 | 237 |
| 162 | 238 |
| 163 void ThreadInterrupter::InstallSignalHandler() { | 239 void ThreadInterrupter::InstallSignalHandler() { |
| 164 // Nothing to do on Fuchsia. | 240 // Nothing to do on Fuchsia. |
| 165 } | 241 } |
| 166 | 242 |
| 167 | 243 |
| 168 void ThreadInterrupter::RemoveSignalHandler() { | 244 void ThreadInterrupter::RemoveSignalHandler() { |
| 169 // Nothing to do on Fuchsia. | 245 // Nothing to do on Fuchsia. |
| 170 } | 246 } |
| 171 | 247 |
| 172 #endif // !PRODUCT | 248 #endif // !PRODUCT |
| 173 | 249 |
| 174 } // namespace dart | 250 } // namespace dart |
| 175 | 251 |
| 176 #endif // defined(HOST_OS_FUCHSIA) | 252 #endif // defined(HOST_OS_FUCHSIA) |
| OLD | NEW |