1*6777b538SAndroid Build Coastguard Worker // Copyright 2016 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "base/process/process.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <mach/mach.h>
8*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
9*6777b538SAndroid Build Coastguard Worker #include <sys/resource.h>
10*6777b538SAndroid Build Coastguard Worker #include <sys/sysctl.h>
11*6777b538SAndroid Build Coastguard Worker #include <sys/time.h>
12*6777b538SAndroid Build Coastguard Worker #include <unistd.h>
13*6777b538SAndroid Build Coastguard Worker
14*6777b538SAndroid Build Coastguard Worker #include <iterator>
15*6777b538SAndroid Build Coastguard Worker #include <memory>
16*6777b538SAndroid Build Coastguard Worker #include <optional>
17*6777b538SAndroid Build Coastguard Worker #include <utility>
18*6777b538SAndroid Build Coastguard Worker
19*6777b538SAndroid Build Coastguard Worker #include "base/apple/mach_logging.h"
20*6777b538SAndroid Build Coastguard Worker #include "base/feature_list.h"
21*6777b538SAndroid Build Coastguard Worker #include "base/memory/free_deleter.h"
22*6777b538SAndroid Build Coastguard Worker
23*6777b538SAndroid Build Coastguard Worker namespace base {
24*6777b538SAndroid Build Coastguard Worker
25*6777b538SAndroid Build Coastguard Worker namespace {
26*6777b538SAndroid Build Coastguard Worker
27*6777b538SAndroid Build Coastguard Worker // Enables setting the task role of every child process to
28*6777b538SAndroid Build Coastguard Worker // TASK_DEFAULT_APPLICATION.
29*6777b538SAndroid Build Coastguard Worker BASE_FEATURE(kMacSetDefaultTaskRole,
30*6777b538SAndroid Build Coastguard Worker "MacSetDefaultTaskRole",
31*6777b538SAndroid Build Coastguard Worker FEATURE_ENABLED_BY_DEFAULT);
32*6777b538SAndroid Build Coastguard Worker
33*6777b538SAndroid Build Coastguard Worker // Returns the `task_role_t` of the process whose task port is `task_port`.
GetTaskCategoryPolicyRole(mach_port_t task_port)34*6777b538SAndroid Build Coastguard Worker std::optional<task_role_t> GetTaskCategoryPolicyRole(mach_port_t task_port) {
35*6777b538SAndroid Build Coastguard Worker task_category_policy_data_t category_policy;
36*6777b538SAndroid Build Coastguard Worker mach_msg_type_number_t task_info_count = TASK_CATEGORY_POLICY_COUNT;
37*6777b538SAndroid Build Coastguard Worker boolean_t get_default = FALSE;
38*6777b538SAndroid Build Coastguard Worker
39*6777b538SAndroid Build Coastguard Worker kern_return_t result =
40*6777b538SAndroid Build Coastguard Worker task_policy_get(task_port, TASK_CATEGORY_POLICY,
41*6777b538SAndroid Build Coastguard Worker reinterpret_cast<task_policy_t>(&category_policy),
42*6777b538SAndroid Build Coastguard Worker &task_info_count, &get_default);
43*6777b538SAndroid Build Coastguard Worker if (result != KERN_SUCCESS) {
44*6777b538SAndroid Build Coastguard Worker MACH_LOG(ERROR, result) << "task_policy_get TASK_CATEGORY_POLICY";
45*6777b538SAndroid Build Coastguard Worker return std::nullopt;
46*6777b538SAndroid Build Coastguard Worker }
47*6777b538SAndroid Build Coastguard Worker CHECK(!get_default);
48*6777b538SAndroid Build Coastguard Worker return category_policy.role;
49*6777b538SAndroid Build Coastguard Worker }
50*6777b538SAndroid Build Coastguard Worker
51*6777b538SAndroid Build Coastguard Worker // Sets the task role for `task_port`.
SetTaskCategoryPolicy(mach_port_t task_port,task_role_t task_role)52*6777b538SAndroid Build Coastguard Worker bool SetTaskCategoryPolicy(mach_port_t task_port, task_role_t task_role) {
53*6777b538SAndroid Build Coastguard Worker task_category_policy task_category_policy{.role = task_role};
54*6777b538SAndroid Build Coastguard Worker kern_return_t result =
55*6777b538SAndroid Build Coastguard Worker task_policy_set(task_port, TASK_CATEGORY_POLICY,
56*6777b538SAndroid Build Coastguard Worker reinterpret_cast<task_policy_t>(&task_category_policy),
57*6777b538SAndroid Build Coastguard Worker TASK_CATEGORY_POLICY_COUNT);
58*6777b538SAndroid Build Coastguard Worker if (result != KERN_SUCCESS) {
59*6777b538SAndroid Build Coastguard Worker MACH_LOG(ERROR, result) << "task_policy_set TASK_CATEGORY_POLICY";
60*6777b538SAndroid Build Coastguard Worker return false;
61*6777b538SAndroid Build Coastguard Worker }
62*6777b538SAndroid Build Coastguard Worker return true;
63*6777b538SAndroid Build Coastguard Worker }
64*6777b538SAndroid Build Coastguard Worker
65*6777b538SAndroid Build Coastguard Worker // Taken from task_policy_private.h.
66*6777b538SAndroid Build Coastguard Worker struct task_suppression_policy {
67*6777b538SAndroid Build Coastguard Worker integer_t active;
68*6777b538SAndroid Build Coastguard Worker integer_t lowpri_cpu;
69*6777b538SAndroid Build Coastguard Worker integer_t timer_throttle;
70*6777b538SAndroid Build Coastguard Worker integer_t disk_throttle;
71*6777b538SAndroid Build Coastguard Worker integer_t cpu_limit;
72*6777b538SAndroid Build Coastguard Worker integer_t suspend;
73*6777b538SAndroid Build Coastguard Worker integer_t throughput_qos;
74*6777b538SAndroid Build Coastguard Worker integer_t suppressed_cpu;
75*6777b538SAndroid Build Coastguard Worker integer_t background_sockets;
76*6777b538SAndroid Build Coastguard Worker integer_t reserved[7];
77*6777b538SAndroid Build Coastguard Worker };
78*6777b538SAndroid Build Coastguard Worker
79*6777b538SAndroid Build Coastguard Worker // Taken from task_policy_private.h.
80*6777b538SAndroid Build Coastguard Worker #define TASK_SUPPRESSION_POLICY_COUNT \
81*6777b538SAndroid Build Coastguard Worker ((mach_msg_type_number_t)(sizeof(struct task_suppression_policy) / \
82*6777b538SAndroid Build Coastguard Worker sizeof(integer_t)))
83*6777b538SAndroid Build Coastguard Worker
84*6777b538SAndroid Build Coastguard Worker // Activates or deactivates the suppression policy to match the effect of App
85*6777b538SAndroid Build Coastguard Worker // Nap.
SetTaskSuppressionPolicy(mach_port_t task_port,bool activate)86*6777b538SAndroid Build Coastguard Worker bool SetTaskSuppressionPolicy(mach_port_t task_port, bool activate) {
87*6777b538SAndroid Build Coastguard Worker task_suppression_policy suppression_policy = {
88*6777b538SAndroid Build Coastguard Worker .active = activate,
89*6777b538SAndroid Build Coastguard Worker .lowpri_cpu = activate,
90*6777b538SAndroid Build Coastguard Worker .timer_throttle =
91*6777b538SAndroid Build Coastguard Worker activate ? LATENCY_QOS_TIER_5 : LATENCY_QOS_TIER_UNSPECIFIED,
92*6777b538SAndroid Build Coastguard Worker .disk_throttle = activate,
93*6777b538SAndroid Build Coastguard Worker .cpu_limit = 0, /* unused */
94*6777b538SAndroid Build Coastguard Worker .suspend = false, /* unused */
95*6777b538SAndroid Build Coastguard Worker .throughput_qos = THROUGHPUT_QOS_TIER_UNSPECIFIED, /* unused */
96*6777b538SAndroid Build Coastguard Worker .suppressed_cpu = activate,
97*6777b538SAndroid Build Coastguard Worker .background_sockets = activate,
98*6777b538SAndroid Build Coastguard Worker };
99*6777b538SAndroid Build Coastguard Worker kern_return_t result =
100*6777b538SAndroid Build Coastguard Worker task_policy_set(task_port, TASK_SUPPRESSION_POLICY,
101*6777b538SAndroid Build Coastguard Worker reinterpret_cast<task_policy_t>(&suppression_policy),
102*6777b538SAndroid Build Coastguard Worker TASK_SUPPRESSION_POLICY_COUNT);
103*6777b538SAndroid Build Coastguard Worker if (result != KERN_SUCCESS) {
104*6777b538SAndroid Build Coastguard Worker MACH_LOG(ERROR, result) << "task_policy_set TASK_SUPPRESSION_POLICY";
105*6777b538SAndroid Build Coastguard Worker return false;
106*6777b538SAndroid Build Coastguard Worker }
107*6777b538SAndroid Build Coastguard Worker return true;
108*6777b538SAndroid Build Coastguard Worker }
109*6777b538SAndroid Build Coastguard Worker
110*6777b538SAndroid Build Coastguard Worker // Returns true if the task suppression policy is active for `task_port`.
IsTaskSuppressionPolicyActive(mach_port_t task_port)111*6777b538SAndroid Build Coastguard Worker bool IsTaskSuppressionPolicyActive(mach_port_t task_port) {
112*6777b538SAndroid Build Coastguard Worker task_suppression_policy suppression_policy = {
113*6777b538SAndroid Build Coastguard Worker .active = false,
114*6777b538SAndroid Build Coastguard Worker };
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker mach_msg_type_number_t task_info_count = TASK_SUPPRESSION_POLICY_COUNT;
117*6777b538SAndroid Build Coastguard Worker boolean_t get_default = FALSE;
118*6777b538SAndroid Build Coastguard Worker
119*6777b538SAndroid Build Coastguard Worker kern_return_t result =
120*6777b538SAndroid Build Coastguard Worker task_policy_get(task_port, TASK_SUPPRESSION_POLICY,
121*6777b538SAndroid Build Coastguard Worker reinterpret_cast<task_policy_t>(&suppression_policy),
122*6777b538SAndroid Build Coastguard Worker &task_info_count, &get_default);
123*6777b538SAndroid Build Coastguard Worker if (result != KERN_SUCCESS) {
124*6777b538SAndroid Build Coastguard Worker MACH_LOG(ERROR, result) << "task_policy_get TASK_SUPPRESSION_POLICY";
125*6777b538SAndroid Build Coastguard Worker return false;
126*6777b538SAndroid Build Coastguard Worker }
127*6777b538SAndroid Build Coastguard Worker CHECK(!get_default);
128*6777b538SAndroid Build Coastguard Worker
129*6777b538SAndroid Build Coastguard Worker // Only check the `active` property as it is sufficient to discern the state,
130*6777b538SAndroid Build Coastguard Worker // even though other properties could be used.
131*6777b538SAndroid Build Coastguard Worker return suppression_policy.active;
132*6777b538SAndroid Build Coastguard Worker }
133*6777b538SAndroid Build Coastguard Worker
134*6777b538SAndroid Build Coastguard Worker // Sets the task role and the suppression policy for `task_port`.
SetPriorityImpl(mach_port_t task_port,task_role_t task_role,bool activate_suppression_policy)135*6777b538SAndroid Build Coastguard Worker bool SetPriorityImpl(mach_port_t task_port,
136*6777b538SAndroid Build Coastguard Worker task_role_t task_role,
137*6777b538SAndroid Build Coastguard Worker bool activate_suppression_policy) {
138*6777b538SAndroid Build Coastguard Worker // Do both operations, even if the first one fails.
139*6777b538SAndroid Build Coastguard Worker bool succeeded = SetTaskCategoryPolicy(task_port, task_role);
140*6777b538SAndroid Build Coastguard Worker succeeded &= SetTaskSuppressionPolicy(task_port, activate_suppression_policy);
141*6777b538SAndroid Build Coastguard Worker return succeeded;
142*6777b538SAndroid Build Coastguard Worker }
143*6777b538SAndroid Build Coastguard Worker
144*6777b538SAndroid Build Coastguard Worker } // namespace
145*6777b538SAndroid Build Coastguard Worker
CreationTime() const146*6777b538SAndroid Build Coastguard Worker Time Process::CreationTime() const {
147*6777b538SAndroid Build Coastguard Worker int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, Pid()};
148*6777b538SAndroid Build Coastguard Worker size_t len = 0;
149*6777b538SAndroid Build Coastguard Worker if (sysctl(mib, std::size(mib), NULL, &len, NULL, 0) < 0)
150*6777b538SAndroid Build Coastguard Worker return Time();
151*6777b538SAndroid Build Coastguard Worker
152*6777b538SAndroid Build Coastguard Worker std::unique_ptr<struct kinfo_proc, base::FreeDeleter> proc(
153*6777b538SAndroid Build Coastguard Worker static_cast<struct kinfo_proc*>(malloc(len)));
154*6777b538SAndroid Build Coastguard Worker if (sysctl(mib, std::size(mib), proc.get(), &len, NULL, 0) < 0)
155*6777b538SAndroid Build Coastguard Worker return Time();
156*6777b538SAndroid Build Coastguard Worker return Time::FromTimeVal(proc->kp_proc.p_un.__p_starttime);
157*6777b538SAndroid Build Coastguard Worker }
158*6777b538SAndroid Build Coastguard Worker
CanSetPriority()159*6777b538SAndroid Build Coastguard Worker bool Process::CanSetPriority() {
160*6777b538SAndroid Build Coastguard Worker return true;
161*6777b538SAndroid Build Coastguard Worker }
162*6777b538SAndroid Build Coastguard Worker
GetPriority(PortProvider * port_provider) const163*6777b538SAndroid Build Coastguard Worker Process::Priority Process::GetPriority(PortProvider* port_provider) const {
164*6777b538SAndroid Build Coastguard Worker CHECK(IsValid());
165*6777b538SAndroid Build Coastguard Worker CHECK(port_provider);
166*6777b538SAndroid Build Coastguard Worker
167*6777b538SAndroid Build Coastguard Worker mach_port_t task_port = port_provider->TaskForHandle(Handle());
168*6777b538SAndroid Build Coastguard Worker if (task_port == TASK_NULL) {
169*6777b538SAndroid Build Coastguard Worker // Upon failure, return the default value.
170*6777b538SAndroid Build Coastguard Worker return Priority::kUserBlocking;
171*6777b538SAndroid Build Coastguard Worker }
172*6777b538SAndroid Build Coastguard Worker
173*6777b538SAndroid Build Coastguard Worker std::optional<task_role_t> task_role = GetTaskCategoryPolicyRole(task_port);
174*6777b538SAndroid Build Coastguard Worker if (!task_role) {
175*6777b538SAndroid Build Coastguard Worker // Upon failure, return the default value.
176*6777b538SAndroid Build Coastguard Worker return Priority::kUserBlocking;
177*6777b538SAndroid Build Coastguard Worker }
178*6777b538SAndroid Build Coastguard Worker bool is_suppression_policy_active = IsTaskSuppressionPolicyActive(task_port);
179*6777b538SAndroid Build Coastguard Worker if (*task_role == TASK_BACKGROUND_APPLICATION &&
180*6777b538SAndroid Build Coastguard Worker is_suppression_policy_active) {
181*6777b538SAndroid Build Coastguard Worker return Priority::kBestEffort;
182*6777b538SAndroid Build Coastguard Worker } else if (*task_role == TASK_BACKGROUND_APPLICATION &&
183*6777b538SAndroid Build Coastguard Worker !is_suppression_policy_active) {
184*6777b538SAndroid Build Coastguard Worker return Priority::kUserVisible;
185*6777b538SAndroid Build Coastguard Worker } else if (*task_role == TASK_FOREGROUND_APPLICATION &&
186*6777b538SAndroid Build Coastguard Worker !is_suppression_policy_active) {
187*6777b538SAndroid Build Coastguard Worker return Priority::kUserBlocking;
188*6777b538SAndroid Build Coastguard Worker }
189*6777b538SAndroid Build Coastguard Worker
190*6777b538SAndroid Build Coastguard Worker // It is possible to get a different state very early in the process lifetime,
191*6777b538SAndroid Build Coastguard Worker // before SetCurrentTaskDefaultRole() has been invoked. Assume highest
192*6777b538SAndroid Build Coastguard Worker // priority then.
193*6777b538SAndroid Build Coastguard Worker return Priority::kUserBlocking;
194*6777b538SAndroid Build Coastguard Worker }
195*6777b538SAndroid Build Coastguard Worker
SetPriority(PortProvider * port_provider,Priority priority)196*6777b538SAndroid Build Coastguard Worker bool Process::SetPriority(PortProvider* port_provider, Priority priority) {
197*6777b538SAndroid Build Coastguard Worker CHECK(IsValid());
198*6777b538SAndroid Build Coastguard Worker CHECK(port_provider);
199*6777b538SAndroid Build Coastguard Worker
200*6777b538SAndroid Build Coastguard Worker if (!CanSetPriority()) {
201*6777b538SAndroid Build Coastguard Worker return false;
202*6777b538SAndroid Build Coastguard Worker }
203*6777b538SAndroid Build Coastguard Worker
204*6777b538SAndroid Build Coastguard Worker mach_port_t task_port = port_provider->TaskForHandle(Handle());
205*6777b538SAndroid Build Coastguard Worker if (task_port == TASK_NULL) {
206*6777b538SAndroid Build Coastguard Worker return false;
207*6777b538SAndroid Build Coastguard Worker }
208*6777b538SAndroid Build Coastguard Worker
209*6777b538SAndroid Build Coastguard Worker switch (priority) {
210*6777b538SAndroid Build Coastguard Worker case Priority::kBestEffort:
211*6777b538SAndroid Build Coastguard Worker // Activate the suppression policy.
212*6777b538SAndroid Build Coastguard Worker // Note:
213*6777b538SAndroid Build Coastguard Worker // App Nap keeps the task role to TASK_FOREGROUND_APPLICATION when it
214*6777b538SAndroid Build Coastguard Worker // activates the suppression policy. Here TASK_BACKGROUND_APPLICATION is
215*6777b538SAndroid Build Coastguard Worker // used instead to keep the kBestEffort role consistent with the value for
216*6777b538SAndroid Build Coastguard Worker // kUserVisible (so that its is not greater than kUserVisible). This
217*6777b538SAndroid Build Coastguard Worker // difference is unlikely to matter.
218*6777b538SAndroid Build Coastguard Worker return SetPriorityImpl(task_port, TASK_BACKGROUND_APPLICATION, true);
219*6777b538SAndroid Build Coastguard Worker case Priority::kUserVisible:
220*6777b538SAndroid Build Coastguard Worker // Set a task role with a lower priority than kUserBlocking, but do not
221*6777b538SAndroid Build Coastguard Worker // activate the suppression policy.
222*6777b538SAndroid Build Coastguard Worker return SetPriorityImpl(task_port, TASK_BACKGROUND_APPLICATION, false);
223*6777b538SAndroid Build Coastguard Worker case Priority::kUserBlocking:
224*6777b538SAndroid Build Coastguard Worker default:
225*6777b538SAndroid Build Coastguard Worker // Set the highest priority with the suppression policy inactive.
226*6777b538SAndroid Build Coastguard Worker return SetPriorityImpl(task_port, TASK_FOREGROUND_APPLICATION, false);
227*6777b538SAndroid Build Coastguard Worker }
228*6777b538SAndroid Build Coastguard Worker }
229*6777b538SAndroid Build Coastguard Worker
230*6777b538SAndroid Build Coastguard Worker // static
SetCurrentTaskDefaultRole()231*6777b538SAndroid Build Coastguard Worker void Process::SetCurrentTaskDefaultRole() {
232*6777b538SAndroid Build Coastguard Worker if (!base::FeatureList::IsEnabled(kMacSetDefaultTaskRole)) {
233*6777b538SAndroid Build Coastguard Worker return;
234*6777b538SAndroid Build Coastguard Worker }
235*6777b538SAndroid Build Coastguard Worker
236*6777b538SAndroid Build Coastguard Worker SetTaskCategoryPolicy(mach_task_self(), TASK_FOREGROUND_APPLICATION);
237*6777b538SAndroid Build Coastguard Worker
238*6777b538SAndroid Build Coastguard Worker // Set the QoS settings to tier 0, to match the default value given to App Nap
239*6777b538SAndroid Build Coastguard Worker // enabled applications.
240*6777b538SAndroid Build Coastguard Worker task_qos_policy task_qos_policy = {
241*6777b538SAndroid Build Coastguard Worker .task_latency_qos_tier = LATENCY_QOS_TIER_0,
242*6777b538SAndroid Build Coastguard Worker .task_throughput_qos_tier = THROUGHPUT_QOS_TIER_0,
243*6777b538SAndroid Build Coastguard Worker };
244*6777b538SAndroid Build Coastguard Worker task_policy_set(mach_task_self(), TASK_BASE_QOS_POLICY,
245*6777b538SAndroid Build Coastguard Worker reinterpret_cast<task_policy_t>(&task_qos_policy),
246*6777b538SAndroid Build Coastguard Worker TASK_QOS_POLICY_COUNT);
247*6777b538SAndroid Build Coastguard Worker }
248*6777b538SAndroid Build Coastguard Worker
249*6777b538SAndroid Build Coastguard Worker } // namespace base
250