1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
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 <errno.h>
8 #include <sys/resource.h>
9
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/synchronization/lock.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "build/build_config.h"
18
19 // Not defined on AIX by default.
20 #if defined(OS_AIX)
21 #define RLIMIT_NICE 20
22 #endif
23
24 namespace base {
25
26 namespace {
27
28 const int kForegroundPriority = 0;
29
30 #if defined(OS_CHROMEOS)
31 // We are more aggressive in our lowering of background process priority
32 // for chromeos as we have much more control over other processes running
33 // on the machine.
34 //
35 // TODO(davemoore) Refactor this by adding support for higher levels to set
36 // the foregrounding / backgrounding process so we don't have to keep
37 // chrome / chromeos specific logic here.
38 const int kBackgroundPriority = 19;
39 const char kControlPath[] = "/sys/fs/cgroup/cpu%s/cgroup.procs";
40 const char kForeground[] = "/chrome_renderers/foreground";
41 const char kBackground[] = "/chrome_renderers/background";
42 const char kProcPath[] = "/proc/%d/cgroup";
43
44 struct CGroups {
45 // Check for cgroups files. ChromeOS supports these by default. It creates
46 // a cgroup mount in /sys/fs/cgroup and then configures two cpu task groups,
47 // one contains at most a single foreground renderer and the other contains
48 // all background renderers. This allows us to limit the impact of background
49 // renderers on foreground ones to a greater level than simple renicing.
50 bool enabled;
51 base::FilePath foreground_file;
52 base::FilePath background_file;
53
CGroupsbase::__anon31574d560111::CGroups54 CGroups() {
55 foreground_file =
56 base::FilePath(base::StringPrintf(kControlPath, kForeground));
57 background_file =
58 base::FilePath(base::StringPrintf(kControlPath, kBackground));
59 base::FileSystemType foreground_type;
60 base::FileSystemType background_type;
61 enabled =
62 base::GetFileSystemType(foreground_file, &foreground_type) &&
63 base::GetFileSystemType(background_file, &background_type) &&
64 foreground_type == FILE_SYSTEM_CGROUP &&
65 background_type == FILE_SYSTEM_CGROUP;
66 }
67
Getbase::__anon31574d560111::CGroups68 static CGroups& Get() {
69 static auto& groups = *new CGroups;
70 return groups;
71 }
72 };
73 #else
74 const int kBackgroundPriority = 5;
75 #endif // defined(OS_CHROMEOS)
76
CanReraisePriority()77 bool CanReraisePriority() {
78 // We won't be able to raise the priority if we don't have the right rlimit.
79 // The limit may be adjusted in /etc/security/limits.conf for PAM systems.
80 struct rlimit rlim;
81 return (getrlimit(RLIMIT_NICE, &rlim) == 0) &&
82 (20 - kForegroundPriority) <= static_cast<int>(rlim.rlim_cur);
83 }
84
85 } // namespace
86
87 // static
CanBackgroundProcesses()88 bool Process::CanBackgroundProcesses() {
89 #if defined(OS_CHROMEOS)
90 if (CGroups::Get().enabled)
91 return true;
92 #endif // defined(OS_CHROMEOS)
93
94 static const bool can_reraise_priority = CanReraisePriority();
95 return can_reraise_priority;
96 }
97
IsProcessBackgrounded() const98 bool Process::IsProcessBackgrounded() const {
99 DCHECK(IsValid());
100
101 #if defined(OS_CHROMEOS)
102 if (CGroups::Get().enabled) {
103 // Used to allow reading the process priority from proc on thread launch.
104 base::ThreadRestrictions::ScopedAllowIO allow_io;
105 std::string proc;
106 if (base::ReadFileToString(
107 base::FilePath(StringPrintf(kProcPath, process_)), &proc)) {
108 return IsProcessBackgroundedCGroup(proc);
109 }
110 return false;
111 }
112 #endif // defined(OS_CHROMEOS)
113
114 return GetPriority() == kBackgroundPriority;
115 }
116
SetProcessBackgrounded(bool background)117 bool Process::SetProcessBackgrounded(bool background) {
118 DCHECK(IsValid());
119
120 #if defined(OS_CHROMEOS)
121 if (CGroups::Get().enabled) {
122 std::string pid = IntToString(process_);
123 const base::FilePath file = background ? CGroups::Get().background_file
124 : CGroups::Get().foreground_file;
125 return base::WriteFile(file, pid.c_str(), pid.size()) > 0;
126 }
127 #endif // defined(OS_CHROMEOS)
128
129 if (!CanBackgroundProcesses())
130 return false;
131
132 int priority = background ? kBackgroundPriority : kForegroundPriority;
133 int result = setpriority(PRIO_PROCESS, process_, priority);
134 DPCHECK(result == 0);
135 return result == 0;
136 }
137
138 #if defined(OS_CHROMEOS)
IsProcessBackgroundedCGroup(const StringPiece & cgroup_contents)139 bool IsProcessBackgroundedCGroup(const StringPiece& cgroup_contents) {
140 // The process can be part of multiple control groups, and for each cgroup
141 // hierarchy there's an entry in the file. We look for a control group
142 // named "/chrome_renderers/background" to determine if the process is
143 // backgrounded. crbug.com/548818.
144 std::vector<StringPiece> lines = SplitStringPiece(
145 cgroup_contents, "\n", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
146 for (const auto& line : lines) {
147 std::vector<StringPiece> fields =
148 SplitStringPiece(line, ":", TRIM_WHITESPACE, SPLIT_WANT_ALL);
149 if (fields.size() != 3U) {
150 NOTREACHED();
151 continue;
152 }
153 if (fields[2] == kBackground)
154 return true;
155 }
156
157 return false;
158 }
159 #endif // defined(OS_CHROMEOS)
160
161 #if defined(OS_CHROMEOS)
162 // Reads /proc/<pid>/status and returns the PID in its PID namespace.
163 // If the process is not in a PID namespace or /proc/<pid>/status does not
164 // report NSpid, kNullProcessId is returned.
GetPidInNamespace() const165 ProcessId Process::GetPidInNamespace() const {
166 std::string status;
167 {
168 // Synchronously reading files in /proc does not hit the disk.
169 ThreadRestrictions::ScopedAllowIO allow_io;
170 FilePath status_file =
171 FilePath("/proc").Append(IntToString(process_)).Append("status");
172 if (!ReadFileToString(status_file, &status)) {
173 return kNullProcessId;
174 }
175 }
176
177 StringPairs pairs;
178 SplitStringIntoKeyValuePairs(status, ':', '\n', &pairs);
179 for (const auto& pair : pairs) {
180 const std::string& key = pair.first;
181 const std::string& value_str = pair.second;
182 if (key == "NSpid") {
183 std::vector<StringPiece> split_value_str = SplitStringPiece(
184 value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
185 if (split_value_str.size() <= 1) {
186 return kNullProcessId;
187 }
188 int value;
189 // The last value in the list is the PID in the namespace.
190 if (!StringToInt(split_value_str.back(), &value)) {
191 NOTREACHED();
192 return kNullProcessId;
193 }
194 return value;
195 }
196 }
197 return kNullProcessId;
198 }
199 #endif // defined(OS_CHROMEOS)
200
201 } // namespace base
202