Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(388)

Side by Side Diff: runtime/vm/thread_interrupter_fuchsia.cc

Issue 2916313003: [Fuchsia] Enable CPU profiling for the standalone VM (Closed)
Patch Set: Format Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/vm/os_thread_fuchsia.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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) {
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:
77 #if defined(TARGET_ARCH_X64)
40 static bool GrabRegisters(mx_handle_t thread, InterruptedThreadState* state) { 78 static bool GrabRegisters(mx_handle_t thread, InterruptedThreadState* state) {
41 // TODO(zra): Enable this when mx_thread_read_state() works on suspended 79 mx_x86_64_general_regs_t regs;
42 // threads. 80 uint32_t regset_size;
43 while (false) { 81 mx_status_t status = mx_thread_read_state(
44 char buf[MX_MAX_THREAD_STATE_SIZE]; 82 thread, MX_THREAD_STATE_REGSET0, &regs, sizeof(regs), &regset_size);
45 uint32_t regset_size = MX_MAX_THREAD_STATE_SIZE; 83 if (status != NO_ERROR) {
46 mx_status_t status = mx_thread_read_state( 84 if (FLAG_trace_thread_interrupter) {
47 thread, MX_THREAD_STATE_REGSET0, &buf[0], regset_size, &regset_size);
48 if (status != NO_ERROR) {
49 OS::PrintErr("ThreadInterrupter failed to get registers: %s\n", 85 OS::PrintErr("ThreadInterrupter failed to get registers: %s\n",
50 mx_status_get_string(status)); 86 mx_status_get_string(status));
51 return false; 87 }
52 } 88 return false;
53 #if defined(TARGET_ARCH_X64) 89 }
54 mx_x86_64_general_regs_t* regs = 90 state->pc = static_cast<uintptr_t>(regs.rip);
55 reinterpret_cast<mx_x86_64_general_regs_t*>(&buf[0]); 91 state->fp = static_cast<uintptr_t>(regs.rbp);
56 state->pc = static_cast<uintptr_t>(regs->rip); 92 state->csp = static_cast<uintptr_t>(regs.rsp);
57 state->fp = static_cast<uintptr_t>(regs->rbp); 93 state->dsp = static_cast<uintptr_t>(regs.rsp);
58 state->csp = static_cast<uintptr_t>(regs->rsp); 94 return true;
59 state->dsp = static_cast<uintptr_t>(regs->rsp); 95 }
60 #elif defined(TARGET_ARCH_ARM64) 96 #elif defined(TARGET_ARCH_ARM64)
61 mx_arm64_general_regs_t* regs = 97 static bool GrabRegisters(mx_handle_t thread, InterruptedThreadState* state) {
62 reinterpret_cast<mx_arm64_general_regs_t*>(&buf[0]); 98 mx_arm64_general_regs_t regs;
63 state->pc = static_cast<uintptr_t>(regs->pc); 99 uint32_t regset_size;
64 state->fp = static_cast<uintptr_t>(regs->r[FPREG]); 100 mx_status_t status = mx_thread_read_state(
65 state->csp = static_cast<uintptr_t>(regs->sp); 101 thread, MX_THREAD_STATE_REGSET0, &regs, sizeof(regs), &regset_size);
66 state->dsp = static_cast<uintptr_t>(regs->r[SPREG]); 102 if (status != NO_ERROR) {
67 state->lr = static_cast<uintptr_t>(regs->lr); 103 if (FLAG_trace_thread_interrupter) {
104 OS::PrintErr("ThreadInterrupter failed to get registers: %s\n",
105 mx_status_get_string(status));
106 }
107 return false;
108 }
109 state->pc = static_cast<uintptr_t>(regs.pc);
110 state->fp = static_cast<uintptr_t>(regs.r[FPREG]);
111 state->csp = static_cast<uintptr_t>(regs.sp);
112 state->dsp = static_cast<uintptr_t>(regs.r[SPREG]);
113 state->lr = static_cast<uintptr_t>(regs.lr);
114 return true;
115 }
68 #else 116 #else
69 #error "Unsupported architecture" 117 #error "Unsupported architecture"
70 #endif 118 #endif
71 }
72 return true;
73 }
74
75 119
76 static void Interrupt(OSThread* os_thread) { 120 static void Interrupt(OSThread* os_thread) {
121 ASSERT(os_thread->id() != MX_KOID_INVALID);
77 ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id())); 122 ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id()));
78 mx_status_t status; 123 mx_status_t status;
79 124
80 // Get a handle on the target thread. 125 // Get a handle on the target thread.
81 mx_koid_t target_thread_koid = os_thread->id(); 126 const mx_koid_t target_thread_koid = os_thread->id();
82 if (FLAG_trace_thread_interrupter) { 127 if (FLAG_trace_thread_interrupter) {
83 OS::Print("ThreadInterrupter: interrupting thread with koid=%d\n", 128 OS::PrintErr("ThreadInterrupter: interrupting thread with koid=%d\n",
84 target_thread_koid); 129 target_thread_koid);
85 } 130 }
86 mx_handle_t target_thread_handle; 131 mx_handle_t target_thread_handle;
87 status = mx_object_get_child(mx_process_self(), target_thread_koid, 132 status = mx_object_get_child(mx_process_self(), target_thread_koid,
88 MX_RIGHT_SAME_RIGHTS, &target_thread_handle); 133 MX_RIGHT_SAME_RIGHTS, &target_thread_handle);
89 if (status != NO_ERROR) { 134 if (status != NO_ERROR) {
90 if (FLAG_trace_thread_interrupter) { 135 if (FLAG_trace_thread_interrupter) {
91 OS::Print("ThreadInterrupter failed to get the thread handle: %s\n", 136 OS::PrintErr("ThreadInterrupter: mx_object_get_child failed: %s\n",
92 mx_status_get_string(status)); 137 mx_status_get_string(status));
93 } 138 }
94 FATAL1("mx_object_get_child failed: %s", mx_status_get_string(status)); 139 return;
95 } 140 }
96 if (target_thread_handle == MX_HANDLE_INVALID) { 141 if (target_thread_handle == MX_HANDLE_INVALID) {
97 FATAL("ThreadInterrupter got an invalid target thread handle!"); 142 if (FLAG_trace_thread_interrupter) {
98 } 143 OS::PrintErr(
99 144 "ThreadInterrupter: mx_object_get_child gave an invalid "
100 // Pause the target thread. 145 "thread handle!");
101 status = mx_task_suspend(target_thread_handle); 146 }
102 if (status != NO_ERROR) { 147 return;
103 if (FLAG_trace_thread_interrupter) { 148 }
104 OS::Print("ThreadInterrupter failed to suspend thread %ld: %s\n", 149
105 static_cast<intptr_t>(os_thread->id()), 150 // This scope suspends the thread. When we exit the scope, the thread is
106 mx_status_get_string(status)); 151 // resumed, and the thread handle is closed.
107 } 152 ThreadSuspendScope tss(target_thread_handle);
108 mx_handle_close(target_thread_handle); 153 if (!tss.suspended()) {
109 FATAL1("mx_task_suspend failed: %s", mx_status_get_string(status)); 154 return;
110 } 155 }
111 156
112 // TODO(zra): Enable this when mx_thread_read_state() works on suspended 157 // Check that the thread is suspended.
113 // threads. 158 status = PollThreadUntilSuspended(target_thread_handle);
114 while (false) { 159 if (status != NO_ERROR) {
115 // Grab the target thread's registers. 160 return;
116 InterruptedThreadState its; 161 }
117 if (!GrabRegisters(target_thread_handle, &its)) { 162
118 // Failed to get thread registers. 163 // Grab the target thread's registers.
119 status = mx_task_resume(target_thread_handle, 0); 164 InterruptedThreadState its;
120 if (status != NO_ERROR) { 165 if (!GrabRegisters(target_thread_handle, &its)) {
121 FATAL1("mx_task_resume failed: %s", mx_status_get_string(status)); 166 return;
167 }
168 // Currently we sample only threads that are associated
169 // with an isolate. It is safe to call 'os_thread->thread()'
170 // here as the thread which is being queried is suspended.
171 Thread* thread = os_thread->thread();
172 if (thread != NULL) {
173 Profiler::SampleThread(thread, its);
174 }
175 }
176
177 private:
178 static const char* ThreadStateGetString(uint32_t state) {
179 switch (state) {
180 case MX_THREAD_STATE_NEW:
181 return "MX_THREAD_STATE_NEW";
182 case MX_THREAD_STATE_RUNNING:
183 return "MX_THREAD_STATE_RUNNING";
184 case MX_THREAD_STATE_SUSPENDED:
185 return "MX_THREAD_STATE_SUSPENDED";
186 case MX_THREAD_STATE_BLOCKED:
187 return "MX_THREAD_STATE_BLOCKED";
188 case MX_THREAD_STATE_DYING:
189 return "MX_THREAD_STATE_DYING";
190 case MX_THREAD_STATE_DEAD:
191 return "MX_THREAD_STATE_DEAD";
192 default:
193 return "<Unknown>";
194 }
195 }
196
197 static mx_status_t PollThreadUntilSuspended(mx_handle_t thread_handle) {
198 const intptr_t kMaxPollAttempts = 10;
199 intptr_t poll_tries = 0;
200 while (poll_tries < kMaxPollAttempts) {
201 mx_info_thread_t thread_info;
202 mx_status_t status =
203 mx_object_get_info(thread_handle, MX_INFO_THREAD, &thread_info,
204 sizeof(thread_info), NULL, NULL);
205 poll_tries++;
206 if (status != NO_ERROR) {
207 if (FLAG_trace_thread_interrupter) {
208 OS::PrintErr("ThreadInterrupter: mx_object_get_info failed: %s\n",
209 mx_status_get_string(status));
122 } 210 }
123 mx_handle_close(target_thread_handle); 211 return status;
124 return; 212 }
125 } 213 if (thread_info.state == MX_THREAD_STATE_SUSPENDED) {
126 // Currently we sample only threads that are associated 214 // Success.
127 // with an isolate. It is safe to call 'os_thread->thread()' 215 return NO_ERROR;
128 // here as the thread which is being queried is suspended. 216 }
129 Thread* thread = os_thread->thread(); 217 if (thread_info.state == MX_THREAD_STATE_RUNNING) {
130 if (thread != NULL) { 218 // Poll.
131 Profiler::SampleThread(thread, its); 219 continue;
132 } 220 }
133 } 221 if (FLAG_trace_thread_interrupter) {
134 222 OS::PrintErr("ThreadInterrupter: Thread is not suspended: %s\n",
135 // Resume the target thread. 223 ThreadStateGetString(thread_info.state));
136 status = mx_task_resume(target_thread_handle, 0); 224 }
137 if (status != NO_ERROR) { 225 return ERR_BAD_STATE;
138 FATAL1("mx_task_resume failed: %s", mx_status_get_string(status)); 226 }
139 } 227 if (FLAG_trace_thread_interrupter) {
140 mx_handle_close(target_thread_handle); 228 OS::PrintErr("ThreadInterrupter: Exceeded max suspend poll tries\n");
229 }
230 return ERR_BAD_STATE;
141 } 231 }
142 }; 232 };
143 233
144 234
145 bool ThreadInterrupter::IsDebuggerAttached() { 235 bool ThreadInterrupter::IsDebuggerAttached() {
146 return false; 236 return false;
147 } 237 }
148 238
149 239
150 void ThreadInterrupter::InterruptThread(OSThread* thread) { 240 void ThreadInterrupter::InterruptThread(OSThread* thread) {
151 if (FLAG_trace_thread_interrupter) { 241 if (FLAG_trace_thread_interrupter) {
152 OS::Print("ThreadInterrupter suspending %p\n", 242 OS::PrintErr("ThreadInterrupter suspending %p\n",
153 reinterpret_cast<void*>(thread->id())); 243 reinterpret_cast<void*>(thread->id()));
154 } 244 }
155 ThreadInterrupterFuchsia::Interrupt(thread); 245 ThreadInterrupterFuchsia::Interrupt(thread);
156 if (FLAG_trace_thread_interrupter) { 246 if (FLAG_trace_thread_interrupter) {
157 OS::Print("ThreadInterrupter resuming %p\n", 247 OS::PrintErr("ThreadInterrupter resuming %p\n",
158 reinterpret_cast<void*>(thread->id())); 248 reinterpret_cast<void*>(thread->id()));
159 } 249 }
160 } 250 }
161 251
162 252
163 void ThreadInterrupter::InstallSignalHandler() { 253 void ThreadInterrupter::InstallSignalHandler() {
164 // Nothing to do on Fuchsia. 254 // Nothing to do on Fuchsia.
165 } 255 }
166 256
167 257
168 void ThreadInterrupter::RemoveSignalHandler() { 258 void ThreadInterrupter::RemoveSignalHandler() {
169 // Nothing to do on Fuchsia. 259 // Nothing to do on Fuchsia.
170 } 260 }
171 261
172 #endif // !PRODUCT 262 #endif // !PRODUCT
173 263
174 } // namespace dart 264 } // namespace dart
175 265
176 #endif // defined(HOST_OS_FUCHSIA) 266 #endif // defined(HOST_OS_FUCHSIA)
OLDNEW
« no previous file with comments | « runtime/vm/os_thread_fuchsia.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698