Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
thread_interrupter_fuchsia.cc
Go to the documentation of this 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
3// BSD-style license that can be found in the LICENSE file.
4
5#include "platform/globals.h"
6#if defined(DART_HOST_OS_FUCHSIA)
7
9
10#include <zircon/process.h>
11#include <zircon/status.h>
12#include <zircon/syscalls.h>
13#include <zircon/syscalls/debug.h>
14#include <zircon/syscalls/object.h>
15#include <zircon/types.h>
16
17#include "vm/flags.h"
18#include "vm/instructions.h"
19#include "vm/os.h"
20#include "vm/profiler.h"
21
22namespace dart {
23
24#ifndef PRODUCT
25
26DECLARE_FLAG(bool, trace_thread_interrupter);
27
28// TODO(ZX-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 ZX-430 is resolved, the code below should be rewritten to use whatever
32// feature is added for it.
33
34// A scope within which a target thread is suspended. When the scope is exited,
35// the thread is resumed and its handle is closed.
36class ThreadSuspendScope {
37 public:
38 explicit ThreadSuspendScope(zx_handle_t thread_handle)
39 : thread_handle_(thread_handle), suspend_token_(ZX_HANDLE_INVALID) {
40 zx_status_t status = zx_task_suspend_token(thread_handle, &suspend_token_);
41 // If a thread is somewhere where suspend is impossible, zx_task_suspend()
42 // can return ZX_ERR_NOT_SUPPORTED.
43 if (status != ZX_OK) {
44 if (FLAG_trace_thread_interrupter) {
45 OS::PrintErr("ThreadInterrupter: zx_task_suspend failed: %s\n",
46 zx_status_get_string(status));
47 }
48 }
49 }
50
51 ~ThreadSuspendScope() {
52 if (suspend_token_ != ZX_HANDLE_INVALID) {
53 zx_handle_close(suspend_token_);
54 }
55 zx_handle_close(thread_handle_);
56 }
57
58 bool suspended() const { return suspend_token_ != ZX_HANDLE_INVALID; }
59
60 private:
61 zx_handle_t thread_handle_;
62 zx_handle_t suspend_token_; // ZX_HANDLE_INVALID when not suspended.
63
65 DISALLOW_COPY_AND_ASSIGN(ThreadSuspendScope);
66};
67
68class ThreadInterrupterFuchsia : public AllStatic {
69 public:
70 static bool GrabRegisters(zx_handle_t thread, InterruptedThreadState* state) {
71 zx_thread_state_general_regs_t regs;
72 zx_status_t status = zx_thread_read_state(
73 thread, ZX_THREAD_STATE_GENERAL_REGS, &regs, sizeof(regs));
74 if (status != ZX_OK) {
75 if (FLAG_trace_thread_interrupter) {
76 OS::PrintErr("ThreadInterrupter failed to get registers: %s\n",
77 zx_status_get_string(status));
78 }
79 return false;
80 }
81#if defined(TARGET_ARCH_X64)
82 state->pc = static_cast<uintptr_t>(regs.rip);
83 state->fp = static_cast<uintptr_t>(regs.rbp);
84 state->csp = static_cast<uintptr_t>(regs.rsp);
85 state->dsp = static_cast<uintptr_t>(regs.rsp);
86 state->lr = 0;
87#elif defined(TARGET_ARCH_ARM64)
88 state->pc = static_cast<uintptr_t>(regs.pc);
89 state->fp = static_cast<uintptr_t>(regs.r[FPREG]);
90 state->csp = static_cast<uintptr_t>(regs.sp);
91 state->dsp = static_cast<uintptr_t>(regs.r[SPREG]);
92 state->lr = static_cast<uintptr_t>(regs.lr);
93#elif defined(TARGET_ARCH_RISCV64)
94 state->pc = static_cast<uintptr_t>(regs.pc);
95 state->fp = static_cast<uintptr_t>(regs.r[FPREG]);
96 state->csp = static_cast<uintptr_t>(regs.r[SPREG]);
97 state->dsp = static_cast<uintptr_t>(regs.r[SPREG]);
98 state->lr = static_cast<uintptr_t>(regs.r[LINK_REGISTER]);
99#else
100#error "Unsupported architecture"
101#endif
102 return true;
103 }
104
105 static void Interrupt(OSThread* os_thread) {
106 ASSERT(os_thread->id() != ZX_KOID_INVALID);
107 ASSERT(!OSThread::Compare(OSThread::GetCurrentThreadId(), os_thread->id()));
108 zx_status_t status;
109
110 // Get a handle on the target thread.
111 const zx_koid_t target_thread_koid = os_thread->id();
112 if (FLAG_trace_thread_interrupter) {
113 OS::PrintErr("ThreadInterrupter: interrupting thread with koid=%ld\n",
114 target_thread_koid);
115 }
116 zx_handle_t target_thread_handle;
117 status = zx_object_get_child(zx_process_self(), target_thread_koid,
118 ZX_RIGHT_SAME_RIGHTS, &target_thread_handle);
119 if (status != ZX_OK) {
120 if (FLAG_trace_thread_interrupter) {
121 OS::PrintErr("ThreadInterrupter: zx_object_get_child failed: %s\n",
122 zx_status_get_string(status));
123 }
124 return;
125 }
126 if (target_thread_handle == ZX_HANDLE_INVALID) {
127 if (FLAG_trace_thread_interrupter) {
128 OS::PrintErr(
129 "ThreadInterrupter: zx_object_get_child gave an invalid "
130 "thread handle!");
131 }
132 return;
133 }
134
135 // This scope suspends the thread. When we exit the scope, the thread is
136 // resumed, and the thread handle is closed.
137 ThreadSuspendScope tss(target_thread_handle);
138 if (!tss.suspended()) {
139 return;
140 }
141
142 // Check that the thread is suspended.
143 status = PollThreadUntilSuspended(target_thread_handle);
144 if (status != ZX_OK) {
145 return;
146 }
147
148 // Grab the target thread's registers.
149 InterruptedThreadState its;
150 if (!GrabRegisters(target_thread_handle, &its)) {
151 return;
152 }
153 // Currently we sample only threads that are associated
154 // with an isolate. It is safe to call 'os_thread->thread()'
155 // here as the thread which is being queried is suspended.
156 Thread* thread = static_cast<Thread*>(os_thread->thread());
157 if (thread != nullptr) {
158 ThreadInterruptScope signal_handler_scope;
159 Profiler::SampleThread(thread, its);
160 }
161 }
162
163 private:
164 static const char* ThreadStateGetString(uint32_t state) {
165// TODO(dje): This #ifdef is temporary to handle the transition.
166// It can be deleted once the new version of zircon rolls out.
167#ifdef ZX_THREAD_STATE_BASIC
168 state = ZX_THREAD_STATE_BASIC(state);
169#endif
170 switch (state) {
171 case ZX_THREAD_STATE_NEW:
172 return "ZX_THREAD_STATE_NEW";
173 case ZX_THREAD_STATE_RUNNING:
174 return "ZX_THREAD_STATE_RUNNING";
175 case ZX_THREAD_STATE_SUSPENDED:
176 return "ZX_THREAD_STATE_SUSPENDED";
177 case ZX_THREAD_STATE_BLOCKED:
178 return "ZX_THREAD_STATE_BLOCKED";
179 case ZX_THREAD_STATE_DYING:
180 return "ZX_THREAD_STATE_DYING";
181 case ZX_THREAD_STATE_DEAD:
182 return "ZX_THREAD_STATE_DEAD";
183 default:
184 return "<Unknown>";
185 }
186 }
187
188 static zx_status_t PollThreadUntilSuspended(zx_handle_t thread_handle) {
189 const intptr_t kMaxPollAttempts = 10;
190 intptr_t poll_tries = 0;
191 while (poll_tries < kMaxPollAttempts) {
192 zx_info_thread_t thread_info;
193 zx_status_t status =
194 zx_object_get_info(thread_handle, ZX_INFO_THREAD, &thread_info,
195 sizeof(thread_info), nullptr, nullptr);
196 poll_tries++;
197 if (status != ZX_OK) {
198 if (FLAG_trace_thread_interrupter) {
199 OS::PrintErr("ThreadInterrupter: zx_object_get_info failed: %s\n",
200 zx_status_get_string(status));
201 }
202 return status;
203 }
204 if (thread_info.state == ZX_THREAD_STATE_SUSPENDED) {
205 // Success.
206 return ZX_OK;
207 }
208 if (thread_info.state == ZX_THREAD_STATE_RUNNING) {
209 // Poll.
210 continue;
211 }
212 if (FLAG_trace_thread_interrupter) {
213 OS::PrintErr("ThreadInterrupter: Thread is not suspended: %s\n",
214 ThreadStateGetString(thread_info.state));
215 }
216 return ZX_ERR_BAD_STATE;
217 }
218 if (FLAG_trace_thread_interrupter) {
219 OS::PrintErr("ThreadInterrupter: Exceeded max suspend poll tries\n");
220 }
221 return ZX_ERR_BAD_STATE;
222 }
223};
224
225void ThreadInterrupter::InterruptThread(OSThread* thread) {
226 if (FLAG_trace_thread_interrupter) {
227 OS::PrintErr("ThreadInterrupter suspending %p\n",
228 reinterpret_cast<void*>(thread->id()));
229 }
230 ThreadInterrupterFuchsia::Interrupt(thread);
231 if (FLAG_trace_thread_interrupter) {
232 OS::PrintErr("ThreadInterrupter resuming %p\n",
233 reinterpret_cast<void*>(thread->id()));
234 }
235}
236
237void ThreadInterrupter::InstallSignalHandler() {
238 // Nothing to do on Fuchsia.
239}
240
241void ThreadInterrupter::RemoveSignalHandler() {
242 // Nothing to do on Fuchsia.
243}
244
245#endif // !PRODUCT
246
247} // namespace dart
248
249#endif // defined(DART_HOST_OS_FUCHSIA)
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static void InterruptThread(OSThread *thread)
#define LINK_REGISTER
#define ASSERT(E)
AtkStateType state
#define DECLARE_FLAG(type, name)
Definition flags.h:14
const Register FPREG
const Register SPREG
#define DISALLOW_ALLOCATION()
Definition globals.h:604
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition globals.h:581