xref: /aosp_15_r20/external/cronet/base/process/process_fuchsia.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/process/process.h"
6 
7 #include <lib/zx/process.h>
8 #include <zircon/process.h>
9 #include <zircon/syscalls.h>
10 
11 #include "base/clang_profiling_buildflags.h"
12 #include "base/fuchsia/default_job.h"
13 #include "base/fuchsia/fuchsia_logging.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "base/trace_event/base_tracing.h"
16 
17 #if BUILDFLAG(CLANG_PROFILING)
18 #include "base/test/clang_profiling.h"
19 #endif
20 
21 namespace base {
22 
23 namespace {
24 
FindProcessInJobTree(const zx::job & job,ProcessId pid)25 zx::process FindProcessInJobTree(const zx::job& job, ProcessId pid) {
26   zx::process process;
27   zx_status_t status = job.get_child(pid, ZX_RIGHT_SAME_RIGHTS, &process);
28 
29   if (status == ZX_OK)
30     return process;
31 
32   if (status == ZX_ERR_NOT_FOUND) {
33     std::vector<zx_koid_t> job_koids(32);
34     while (true) {
35       // Fetch the KOIDs of the job children of |job|.
36       size_t actual = 0u;
37       size_t available = 0u;
38       status = job.get_info(ZX_INFO_JOB_CHILDREN, job_koids.data(),
39                             job_koids.size() * sizeof(zx_koid_t), &actual,
40                             &available);
41 
42       if (status != ZX_OK) {
43         ZX_DLOG(ERROR, status) << "zx_object_get_info(JOB_CHILDREN)";
44         return zx::process();
45       }
46 
47       // If |job_koids| was too small then resize it and try again.
48       if (available > actual) {
49         job_koids.resize(available);
50         continue;
51       }
52 
53       // Break out of the loop and iterate over |job_koids|, to find the PID.
54       job_koids.resize(actual);
55       break;
56     }
57 
58     for (zx_koid_t job_koid : job_koids) {
59       zx::job child_job;
60       if (job.get_child(job_koid, ZX_RIGHT_SAME_RIGHTS, &child_job) != ZX_OK)
61         continue;
62       process = FindProcessInJobTree(child_job, pid);
63       if (process)
64         return process;
65     }
66 
67     return zx::process();
68   }
69 
70   ZX_DLOG(ERROR, status) << "zx_object_get_child";
71   return zx::process();
72 }
73 
74 }  // namespace
75 
Process(ProcessHandle handle)76 Process::Process(ProcessHandle handle)
77     : process_(handle), is_current_process_(false) {
78   CHECK_NE(handle, zx_process_self());
79 }
80 
~Process()81 Process::~Process() {
82   Close();
83 }
84 
Process(Process && other)85 Process::Process(Process&& other)
86     : process_(std::move(other.process_)),
87       is_current_process_(other.is_current_process_) {
88   other.is_current_process_ = false;
89 }
90 
operator =(Process && other)91 Process& Process::operator=(Process&& other) {
92   process_ = std::move(other.process_);
93   is_current_process_ = other.is_current_process_;
94   other.is_current_process_ = false;
95   return *this;
96 }
97 
98 // static
Current()99 Process Process::Current() {
100   Process process;
101   process.is_current_process_ = true;
102   return process;
103 }
104 
105 // static
Open(ProcessId pid)106 Process Process::Open(ProcessId pid) {
107   if (pid == GetCurrentProcId())
108     return Current();
109 
110   return Process(FindProcessInJobTree(*GetDefaultJob(), pid).release());
111 }
112 
113 // static
OpenWithExtraPrivileges(ProcessId pid)114 Process Process::OpenWithExtraPrivileges(ProcessId pid) {
115   // No privileges to set.
116   return Open(pid);
117 }
118 
119 // static
CanSetPriority()120 bool Process::CanSetPriority() {
121   return false;
122 }
123 
124 // static
TerminateCurrentProcessImmediately(int exit_code)125 void Process::TerminateCurrentProcessImmediately(int exit_code) {
126 #if BUILDFLAG(CLANG_PROFILING)
127   WriteClangProfilingProfile();
128 #endif
129   _exit(exit_code);
130 }
131 
IsValid() const132 bool Process::IsValid() const {
133   return process_.is_valid() || is_current();
134 }
135 
Handle() const136 ProcessHandle Process::Handle() const {
137   return is_current_process_ ? zx_process_self() : process_.get();
138 }
139 
Duplicate() const140 Process Process::Duplicate() const {
141   if (is_current())
142     return Current();
143 
144   if (!IsValid())
145     return Process();
146 
147   zx::process out;
148   zx_status_t result = process_.duplicate(ZX_RIGHT_SAME_RIGHTS, &out);
149   if (result != ZX_OK) {
150     ZX_DLOG(ERROR, result) << "zx_handle_duplicate";
151     return Process();
152   }
153 
154   return Process(out.release());
155 }
156 
Release()157 ProcessHandle Process::Release() {
158   if (is_current()) {
159     // Caller expects to own the reference, so duplicate the self handle.
160     zx::process handle;
161     zx_status_t result =
162         zx::process::self()->duplicate(ZX_RIGHT_SAME_RIGHTS, &handle);
163     if (result != ZX_OK) {
164       return kNullProcessHandle;
165     }
166     is_current_process_ = false;
167     return handle.release();
168   }
169   return process_.release();
170 }
171 
Pid() const172 ProcessId Process::Pid() const {
173   DCHECK(IsValid());
174   return GetProcId(Handle());
175 }
176 
CreationTime() const177 Time Process::CreationTime() const {
178   zx_info_process_t proc_info;
179   zx_status_t status =
180       zx_object_get_info(Handle(), ZX_INFO_PROCESS, &proc_info,
181                          sizeof(proc_info), nullptr, nullptr);
182   if (status != ZX_OK) {
183     ZX_DLOG(ERROR, status) << "zx_process_get_info";
184     return Time();
185   }
186   if ((proc_info.flags & ZX_INFO_PROCESS_FLAG_STARTED) == 0) {
187     DLOG(WARNING) << "zx_process_get_info: Not started.";
188     return Time();
189   }
190   // Process creation times are expressed in ticks since system boot, so
191   // perform a best-effort translation from that to UTC "wall-clock" time.
192   return Time::Now() +
193          (TimeTicks::FromZxTime(proc_info.start_time) - TimeTicks::Now());
194 }
195 
is_current() const196 bool Process::is_current() const {
197   return is_current_process_;
198 }
199 
Close()200 void Process::Close() {
201   is_current_process_ = false;
202   process_.reset();
203 }
204 
Terminate(int exit_code,bool wait) const205 bool Process::Terminate(int exit_code, bool wait) const {
206   // exit_code isn't supportable. https://crbug.com/753490.
207   zx_status_t status = zx_task_kill(Handle());
208   if (status == ZX_OK && wait) {
209     zx_signals_t signals;
210     status = zx_object_wait_one(Handle(), ZX_TASK_TERMINATED,
211                                 zx_deadline_after(ZX_SEC(60)), &signals);
212     if (status != ZX_OK) {
213       ZX_DLOG(ERROR, status) << "zx_object_wait_one(terminate)";
214     } else {
215       CHECK(signals & ZX_TASK_TERMINATED);
216     }
217   } else if (status != ZX_OK) {
218     ZX_DLOG(ERROR, status) << "zx_task_kill";
219   }
220 
221   return status >= 0;
222 }
223 
WaitForExit(int * exit_code) const224 bool Process::WaitForExit(int* exit_code) const {
225   return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
226 }
227 
WaitForExitWithTimeout(TimeDelta timeout,int * exit_code) const228 bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
229   if (is_current_process_)
230     return false;
231 
232   TRACE_EVENT0("base", "Process::WaitForExitWithTimeout");
233 
234   if (!timeout.is_zero()) {
235     // Assert that this thread is allowed to wait below. This intentionally
236     // doesn't use ScopedBlockingCallWithBaseSyncPrimitives because the process
237     // being waited upon tends to itself be using the CPU and considering this
238     // thread non-busy causes more issue than it fixes: http://crbug.com/905788
239     internal::AssertBaseSyncPrimitivesAllowed();
240   }
241 
242   zx::time deadline = timeout == TimeDelta::Max()
243                           ? zx::time::infinite()
244                           : zx::time((TimeTicks::Now() + timeout).ToZxTime());
245   zx_signals_t signals_observed = 0;
246   zx_status_t status =
247       process_.wait_one(ZX_TASK_TERMINATED, deadline, &signals_observed);
248   if (status != ZX_OK) {
249     ZX_DLOG_IF(ERROR, status != ZX_ERR_TIMED_OUT, status)
250         << "zx_object_wait_one";
251     return false;
252   }
253 
254   zx_info_process_t proc_info;
255   status = process_.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info),
256                              nullptr, nullptr);
257   if (status != ZX_OK) {
258     ZX_DLOG(ERROR, status) << "zx_object_get_info";
259     if (exit_code)
260       *exit_code = -1;
261     return false;
262   }
263 
264   if (exit_code)
265     *exit_code = static_cast<int>(proc_info.return_code);
266 
267   return true;
268 }
269 
Exited(int exit_code) const270 void Process::Exited(int exit_code) const {}
271 
GetPriority() const272 Process::Priority Process::GetPriority() const {
273   // See SetPriority().
274   DCHECK(IsValid());
275   return Priority::kUserBlocking;
276 }
277 
SetPriority(Priority priority)278 bool Process::SetPriority(Priority priority) {
279   // No process priorities on Fuchsia.
280   // TODO(fxbug.dev/30735): Update this later if priorities are implemented.
281   return false;
282 }
283 
GetOSPriority() const284 int Process::GetOSPriority() const {
285   DCHECK(IsValid());
286   // No process priorities on Fuchsia.
287   return 0;
288 }
289 
290 }  // namespace base
291