• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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