blob: 499604bcb51034fbd1860ef19ec23eef134200e5 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2011 The Chromium Authors
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
[email protected]76bea672013-07-19 16:48:565#include "base/process/process.h"
[email protected]dd4b51262013-07-25 21:38:236
Sebastien Marchandbd02bc29e2020-03-11 15:53:367#include "base/clang_profiling_buildflags.h"
initial.commitd7cae122008-07-26 21:49:388#include "base/logging.h"
rvargas85728972015-03-03 20:46:199#include "base/numerics/safe_conversions.h"
rvargas126fd5822014-12-12 00:25:1410#include "base/process/kill.h"
Francois Doraya678fc12017-10-30 22:18:0611#include "base/threading/thread_restrictions.h"
Gabriel Charette6836c0d52021-01-11 17:40:2612#include "base/trace_event/base_tracing.h"
Zheda Chen2808be72022-12-19 06:26:5513#include "base/win/windows_version.h"
initial.commitd7cae122008-07-26 21:49:3814
Bruce Dawsonbfdc3fd2018-01-03 20:32:3615#include <windows.h>
16
Sebastien Marchandbd02bc29e2020-03-11 15:53:3617#if BUILDFLAG(CLANG_PROFILING)
18#include "base/test/clang_profiling.h"
Yannic Bonenberger6801e6c2019-06-07 10:42:5319#endif
20
rvargas747ff242015-01-17 02:46:4721namespace {
22
23DWORD kBasicProcessAccess =
24 PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE;
25
26} // namespace
27
[email protected]176aa482008-11-14 03:25:1528namespace base {
29
Zheda Chen2808be72022-12-19 06:26:5530// Sets Eco QoS (Quality of Service) level for background process which would
31// select efficient CPU frequency and schedule the process to efficient cores
32// (available on hybrid CPUs).
33// QoS is a scheduling Win API which indicates the desired performance and power
34// efficiency of a process/thread. EcoQoS is introduced since Windows 11.
35BASE_FEATURE(kUseEcoQoSForBackgroundProcess,
36 "UseEcoQoSForBackgroundProcess",
François Doray57694362023-05-09 14:31:5337 FEATURE_ENABLED_BY_DEFAULT);
Zheda Chen2808be72022-12-19 06:26:5538
rvargas079d1842014-10-17 22:32:1639Process::Process(ProcessHandle handle)
scottmg297cc932017-05-24 03:45:5840 : process_(handle), is_current_process_(false) {
rvargas079d1842014-10-17 22:32:1641 CHECK_NE(handle, ::GetCurrentProcess());
42}
43
dchenge1b0277c2015-12-01 12:09:5244Process::Process(Process&& other)
Lei Zhang04c0bcb2022-02-04 04:13:4545 : process_(other.process_.release()),
scottmg297cc932017-05-24 03:45:5846 is_current_process_(other.is_current_process_) {
dchenge1b0277c2015-12-01 12:09:5247 other.Close();
rvargas079d1842014-10-17 22:32:1648}
49
thakis3096dac2015-04-20 16:44:4850Process::~Process() {
51}
52
dchenge1b0277c2015-12-01 12:09:5253Process& Process::operator=(Process&& other) {
54 DCHECK_NE(this, &other);
Lei Zhang04c0bcb2022-02-04 04:13:4555 process_.Set(other.process_.release());
dchenge1b0277c2015-12-01 12:09:5256 is_current_process_ = other.is_current_process_;
57 other.Close();
rvargas079d1842014-10-17 22:32:1658 return *this;
59}
60
61// static
62Process Process::Current() {
63 Process process;
64 process.is_current_process_ = true;
dcheng70c49422016-03-02 23:20:3465 return process;
rvargas079d1842014-10-17 22:32:1666}
67
68// static
rvargas6b039c372015-02-04 21:11:2969Process Process::Open(ProcessId pid) {
70 return Process(::OpenProcess(kBasicProcessAccess, FALSE, pid));
71}
72
73// static
rvargas1c376a82015-03-16 23:03:5274Process Process::OpenWithExtraPrivileges(ProcessId pid) {
rvargas747ff242015-01-17 02:46:4775 DWORD access = kBasicProcessAccess | PROCESS_DUP_HANDLE | PROCESS_VM_READ;
rvargas6b039c372015-02-04 21:11:2976 return Process(::OpenProcess(access, FALSE, pid));
rvargas747ff242015-01-17 02:46:4777}
78
79// static
rvargas17a407d2015-01-23 20:36:4480Process Process::OpenWithAccess(ProcessId pid, DWORD desired_access) {
81 return Process(::OpenProcess(desired_access, FALSE, pid));
82}
83
84// static
rvargas079d1842014-10-17 22:32:1685bool Process::CanBackgroundProcesses() {
86 return true;
87}
88
haraken940efb92017-02-08 05:58:1589// static
90void Process::TerminateCurrentProcessImmediately(int exit_code) {
Sebastien Marchandbd02bc29e2020-03-11 15:53:3691#if BUILDFLAG(CLANG_PROFILING)
92 WriteClangProfilingProfile();
Wezd2031d302018-08-16 23:34:2593#endif
Peter Kasting4d3664b2022-06-16 19:27:5494 ::TerminateProcess(GetCurrentProcess(), static_cast<UINT>(exit_code));
Bruce Dawson33ce2ff2017-11-28 20:47:5195 // There is some ambiguity over whether the call above can return. Rather than
96 // hitting confusing crashes later on we should crash right here.
Peter Boström25c6ec72022-11-02 23:25:1997 ImmediateCrash();
haraken940efb92017-02-08 05:58:1598}
99
rvargas079d1842014-10-17 22:32:16100bool Process::IsValid() const {
Lei Zhang04c0bcb2022-02-04 04:13:45101 return process_.is_valid() || is_current();
rvargas079d1842014-10-17 22:32:16102}
103
104ProcessHandle Process::Handle() const {
Lei Zhang04c0bcb2022-02-04 04:13:45105 return is_current_process_ ? GetCurrentProcess() : process_.get();
rvargas079d1842014-10-17 22:32:16106}
107
108Process Process::Duplicate() const {
109 if (is_current())
110 return Current();
111
112 ProcessHandle out_handle;
113 if (!IsValid() || !::DuplicateHandle(GetCurrentProcess(),
114 Handle(),
115 GetCurrentProcess(),
116 &out_handle,
117 0,
118 FALSE,
119 DUPLICATE_SAME_ACCESS)) {
120 return Process();
121 }
122 return Process(out_handle);
123}
124
Robert Sesek0e7f165a2020-11-20 21:31:45125ProcessHandle Process::Release() {
126 if (is_current())
127 return ::GetCurrentProcess();
Lei Zhang04c0bcb2022-02-04 04:13:45128 return process_.release();
Robert Sesek0e7f165a2020-11-20 21:31:45129}
130
rvargas960db882015-01-24 00:27:25131ProcessId Process::Pid() const {
rvargas079d1842014-10-17 22:32:16132 DCHECK(IsValid());
133 return GetProcId(Handle());
134}
135
Francois Doray7d1315342018-10-21 03:54:47136Time Process::CreationTime() const {
137 FILETIME creation_time = {};
138 FILETIME ignore1 = {};
139 FILETIME ignore2 = {};
140 FILETIME ignore3 = {};
141 if (!::GetProcessTimes(Handle(), &creation_time, &ignore1, &ignore2,
142 &ignore3)) {
143 return Time();
144 }
145 return Time::FromFileTime(creation_time);
146}
147
rvargas079d1842014-10-17 22:32:16148bool Process::is_current() const {
149 return is_current_process_;
150}
151
[email protected]176aa482008-11-14 03:25:15152void Process::Close() {
rvargas079d1842014-10-17 22:32:16153 is_current_process_ = false;
Lei Zhang04c0bcb2022-02-04 04:13:45154 if (!process_.is_valid())
[email protected]176aa482008-11-14 03:25:15155 return;
[email protected]b987e90e2011-08-15 19:22:44156
rvargas079d1842014-10-17 22:32:16157 process_.Close();
[email protected]176aa482008-11-14 03:25:15158}
159
rvargas02ad7832015-04-02 01:36:06160bool Process::Terminate(int exit_code, bool wait) const {
Brian White6c8f3b9c2017-12-01 18:55:31161 constexpr DWORD kWaitMs = 60 * 1000;
162
rvargaseedb763e2015-03-09 23:53:45163 DCHECK(IsValid());
Peter Kasting4d3664b2022-06-16 19:27:54164 bool result =
165 ::TerminateProcess(Handle(), static_cast<UINT>(exit_code)) != FALSE;
Brian Whiteae2a8b9a2017-11-02 19:10:36166 if (result) {
rvargas02ad7832015-04-02 01:36:06167 // The process may not end immediately due to pending I/O
Brian White6c8f3b9c2017-12-01 18:55:31168 if (wait && ::WaitForSingleObject(Handle(), kWaitMs) != WAIT_OBJECT_0)
rvargas02ad7832015-04-02 01:36:06169 DPLOG(ERROR) << "Error waiting for process exit";
Brian Whiteae2a8b9a2017-11-02 19:10:36170 Exited(exit_code);
Brian Whiteccb2c6e2017-11-14 19:09:44171 } else {
Bruce Dawsond9111de2019-06-18 23:29:38172 // The process can't be terminated, perhaps because it has already exited or
173 // is in the process of exiting. An error code of ERROR_ACCESS_DENIED is the
174 // undocumented-but-expected result if the process has already exited or
175 // started exiting when TerminateProcess is called, so don't print an error
176 // message in that case.
177 if (GetLastError() != ERROR_ACCESS_DENIED)
178 DPLOG(ERROR) << "Unable to terminate process";
179 // A non-zero timeout is necessary here for the same reasons as above.
Brian White6c8f3b9c2017-12-01 18:55:31180 if (::WaitForSingleObject(Handle(), kWaitMs) == WAIT_OBJECT_0) {
Brian Whiteccb2c6e2017-11-14 19:09:44181 DWORD actual_exit;
Peter Kasting4d3664b2022-06-16 19:27:54182 Exited(::GetExitCodeProcess(Handle(), &actual_exit)
183 ? static_cast<int>(actual_exit)
184 : exit_code);
Brian Whiteccb2c6e2017-11-14 19:09:44185 result = true;
186 }
rvargas02ad7832015-04-02 01:36:06187 }
188 return result;
[email protected]176aa482008-11-14 03:25:15189}
190
Mike Rorkef36971b2020-03-06 17:56:29191Process::WaitExitStatus Process::WaitForExitOrEvent(
192 const base::win::ScopedHandle& stop_event_handle,
193 int* exit_code) const {
Lei Zhang04c0bcb2022-02-04 04:13:45194 HANDLE events[] = {Handle(), stop_event_handle.get()};
Mike Rorkef36971b2020-03-06 17:56:29195 DWORD wait_result =
Daniel Chengf45f47602022-02-28 22:38:32196 ::WaitForMultipleObjects(std::size(events), events, FALSE, INFINITE);
Mike Rorkef36971b2020-03-06 17:56:29197
198 if (wait_result == WAIT_OBJECT_0) {
199 DWORD temp_code; // Don't clobber out-parameters in case of failure.
200 if (!::GetExitCodeProcess(Handle(), &temp_code))
201 return Process::WaitExitStatus::FAILED;
202
203 if (exit_code)
Peter Kasting4d3664b2022-06-16 19:27:54204 *exit_code = static_cast<int>(temp_code);
Mike Rorkef36971b2020-03-06 17:56:29205
Peter Kasting4d3664b2022-06-16 19:27:54206 Exited(static_cast<int>(temp_code));
Mike Rorkef36971b2020-03-06 17:56:29207 return Process::WaitExitStatus::PROCESS_EXITED;
208 }
209
210 if (wait_result == WAIT_OBJECT_0 + 1) {
211 return Process::WaitExitStatus::STOP_EVENT_SIGNALED;
212 }
213
214 return Process::WaitExitStatus::FAILED;
215}
216
jcivellif4462a352017-01-10 04:45:59217bool Process::WaitForExit(int* exit_code) const {
Bruce Dawsond3e5d6e2021-07-16 21:17:26218 return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
rvargas126fd5822014-12-12 00:25:14219}
220
jcivellif4462a352017-01-10 04:45:59221bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
Gabriel Charette6836c0d52021-01-11 17:40:26222 TRACE_EVENT0("base", "Process::WaitForExitWithTimeout");
Francois Doraya678fc12017-10-30 22:18:06223
Gabriel Charette6836c0d52021-01-11 17:40:26224 if (!timeout.is_zero()) {
Gabriel Charette6836c0d52021-01-11 17:40:26225 // Assert that this thread is allowed to wait below. This intentionally
226 // doesn't use ScopedBlockingCallWithBaseSyncPrimitives because the process
227 // being waited upon tends to itself be using the CPU and considering this
228 // thread non-busy causes more issue than it fixes: http://crbug.com/905788
229 internal::AssertBaseSyncPrimitivesAllowed();
230 }
bcwhited9705962016-08-10 03:10:03231
rvargas85728972015-03-03 20:46:19232 // Limit timeout to INFINITE.
233 DWORD timeout_ms = saturated_cast<DWORD>(timeout.InMilliseconds());
234 if (::WaitForSingleObject(Handle(), timeout_ms) != WAIT_OBJECT_0)
235 return false;
236
237 DWORD temp_code; // Don't clobber out-parameters in case of failure.
238 if (!::GetExitCodeProcess(Handle(), &temp_code))
239 return false;
240
g.mehndirattfd19e232015-05-29 08:17:14241 if (exit_code)
Peter Kasting4d3664b2022-06-16 19:27:54242 *exit_code = static_cast<int>(temp_code);
bcwhite815054b52017-03-16 18:41:12243
Peter Kasting4d3664b2022-06-16 19:27:54244 Exited(static_cast<int>(temp_code));
rvargas85728972015-03-03 20:46:19245 return true;
rvargas126fd5822014-12-12 00:25:14246}
247
Joe Masona0ba58b052023-02-22 20:46:29248void Process::Exited(int exit_code) const {}
Brian Whiteae2a8b9a2017-11-02 19:10:36249
[email protected]2f15de42008-11-11 22:35:19250bool Process::IsProcessBackgrounded() const {
rvargas079d1842014-10-17 22:32:16251 DCHECK(IsValid());
Patrick Monette740b81b2023-07-21 21:09:09252 int priority = GetOSPriority();
initial.commitd7cae122008-07-26 21:49:38253 if (priority == 0)
254 return false; // Failure case.
[email protected]568bfb02011-04-28 23:51:53255 return ((priority == BELOW_NORMAL_PRIORITY_CLASS) ||
256 (priority == IDLE_PRIORITY_CLASS));
initial.commitd7cae122008-07-26 21:49:38257}
258
259bool Process::SetProcessBackgrounded(bool value) {
rvargas079d1842014-10-17 22:32:16260 DCHECK(IsValid());
Bruce Dawsonb7e516e2022-12-13 18:41:43261 // Having a process remove itself from background mode is a potential
262 // priority inversion, and having a process put itself in background mode is
263 // broken in Windows 11 22H2. So, it is no longer supported. See
264 // https://crbug.com/1396155 for details.
265 DCHECK(!is_current());
266 const DWORD priority = value ? IDLE_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
[email protected]568bfb02011-04-28 23:51:53267
Zheda Chen2808be72022-12-19 06:26:55268 if (base::win::OSInfo::GetInstance()->version() >=
269 base::win::Version::WIN11 &&
270 FeatureList::IsEnabled(kUseEcoQoSForBackgroundProcess)) {
271 PROCESS_POWER_THROTTLING_STATE power_throttling;
272 RtlZeroMemory(&power_throttling, sizeof(power_throttling));
273 power_throttling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
274
275 if (value) {
276 // Sets Eco QoS level.
277 power_throttling.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
278 power_throttling.StateMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
279 } else {
280 // Uses system default.
281 power_throttling.ControlMask = 0;
282 power_throttling.StateMask = 0;
283 }
284 bool ret =
285 ::SetProcessInformation(Handle(), ProcessPowerThrottling,
286 &power_throttling, sizeof(power_throttling));
287 if (ret == 0) {
288 DPLOG(ERROR) << "Setting process QoS policy fails";
289 }
290 }
291
rvargas079d1842014-10-17 22:32:16292 return (::SetPriorityClass(Handle(), priority) != 0);
[email protected]8a420802011-12-02 16:14:46293}
294
Patrick Monette740b81b2023-07-21 21:09:09295int Process::GetOSPriority() const {
rvargas079d1842014-10-17 22:32:16296 DCHECK(IsValid());
Peter Kasting4d3664b2022-06-16 19:27:54297 return static_cast<int>(::GetPriorityClass(Handle()));
[email protected]276aa6a2009-10-29 17:43:44298}
299
[email protected]176aa482008-11-14 03:25:15300} // namespace base