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