process_linux.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  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. #include "base/process/process.h"
  5. #include <errno.h>
  6. #include <sys/resource.h>
  7. #include <cstring>
  8. #include "base/check.h"
  9. #include "base/files/file_util.h"
  10. #include "base/location.h"
  11. #include "base/logging.h"
  12. #include "base/notreached.h"
  13. #include "base/posix/can_lower_nice_to.h"
  14. #include "base/process/internal_linux.h"
  15. #include "base/strings/string_number_conversions.h"
  16. #include "base/strings/string_split.h"
  17. #include "base/strings/stringprintf.h"
  18. #include "base/threading/thread_restrictions.h"
  19. #include "build/build_config.h"
  20. #include "build/chromeos_buildflags.h"
  21. #if BUILDFLAG(IS_CHROMEOS)
  22. #include "base/bind.h"
  23. #include "base/feature_list.h"
  24. #include "base/files/file_enumerator.h"
  25. #include "base/files/file_path.h"
  26. #include "base/process/process_handle.h"
  27. #include "base/strings/strcat.h"
  28. #include "base/strings/string_util.h"
  29. #include "base/task/thread_pool.h"
  30. #include "base/unguessable_token.h"
  31. #endif // BUILDFLAG(IS_CHROMEOS)
  32. namespace base {
  33. #if BUILDFLAG(IS_CHROMEOS)
  34. const Feature kOneGroupPerRenderer {
  35. "OneGroupPerRenderer",
  36. #if BUILDFLAG(IS_CHROMEOS_LACROS)
  37. FEATURE_ENABLED_BY_DEFAULT
  38. };
  39. #else
  40. FEATURE_DISABLED_BY_DEFAULT
  41. };
  42. #endif // BUILDFLAG(IS_CHROMEOS_LACROS)
  43. #endif // BUILDFLAG(IS_CHROMEOS)
  44. namespace {
  45. const int kForegroundPriority = 0;
  46. #if BUILDFLAG(IS_CHROMEOS)
  47. // We are more aggressive in our lowering of background process priority
  48. // for chromeos as we have much more control over other processes running
  49. // on the machine.
  50. //
  51. // TODO(davemoore) Refactor this by adding support for higher levels to set
  52. // the foregrounding / backgrounding process so we don't have to keep
  53. // chrome / chromeos specific logic here.
  54. const int kBackgroundPriority = 19;
  55. const char kControlPath[] = "/sys/fs/cgroup/cpu%s/cgroup.procs";
  56. const char kFullRendererCgroupRoot[] = "/sys/fs/cgroup/cpu/chrome_renderers";
  57. const char kForeground[] = "/chrome_renderers/foreground";
  58. const char kBackground[] = "/chrome_renderers/background";
  59. const char kProcPath[] = "/proc/%d/cgroup";
  60. const char kUclampMinFile[] = "cpu.uclamp.min";
  61. const char kUclampMaxFile[] = "cpu.uclamp.max";
  62. constexpr int kCgroupDeleteRetries = 3;
  63. constexpr TimeDelta kCgroupDeleteRetryTime(Seconds(1));
  64. #if BUILDFLAG(IS_CHROMEOS_LACROS)
  65. const char kCgroupPrefix[] = "l-";
  66. #elif BUILDFLAG(IS_CHROMEOS_ASH)
  67. const char kCgroupPrefix[] = "a-";
  68. #endif
  69. struct CGroups {
  70. // Check for cgroups files. ChromeOS supports these by default. It creates
  71. // a cgroup mount in /sys/fs/cgroup and then configures two cpu task groups,
  72. // one contains at most a single foreground renderer and the other contains
  73. // all background renderers. This allows us to limit the impact of background
  74. // renderers on foreground ones to a greater level than simple renicing.
  75. bool enabled;
  76. FilePath foreground_file;
  77. FilePath background_file;
  78. // A unique token for this instance of the browser.
  79. std::string group_prefix_token;
  80. // UCLAMP settings for the foreground cgroups.
  81. std::string uclamp_min;
  82. std::string uclamp_max;
  83. CGroups() {
  84. foreground_file = FilePath(StringPrintf(kControlPath, kForeground));
  85. background_file = FilePath(StringPrintf(kControlPath, kBackground));
  86. FileSystemType foreground_type;
  87. FileSystemType background_type;
  88. enabled = GetFileSystemType(foreground_file, &foreground_type) &&
  89. GetFileSystemType(background_file, &background_type) &&
  90. foreground_type == FILE_SYSTEM_CGROUP &&
  91. background_type == FILE_SYSTEM_CGROUP;
  92. if (!enabled || !FeatureList::IsEnabled(kOneGroupPerRenderer)) {
  93. return;
  94. }
  95. // Generate a unique token for the full browser process
  96. group_prefix_token =
  97. StrCat({kCgroupPrefix, UnguessableToken::Create().ToString(), "-"});
  98. // Reads the ULCAMP settings from the foreground cgroup that will be used
  99. // for each renderer's cgroup.
  100. FilePath foreground_path = foreground_file.DirName();
  101. ReadFileToString(foreground_path.Append(kUclampMinFile), &uclamp_min);
  102. ReadFileToString(foreground_path.Append(kUclampMaxFile), &uclamp_max);
  103. }
  104. // Returns the full path to a the cgroup dir of a process using
  105. // the supplied token.
  106. static FilePath GetForegroundCgroupDir(const std::string& token) {
  107. // Get individualized cgroup if the feature is enabled
  108. std::string cgroup_path_str;
  109. StrAppend(&cgroup_path_str, {kFullRendererCgroupRoot, "/", token});
  110. return FilePath(cgroup_path_str);
  111. }
  112. // Returns the path to the cgroup.procs file of the foreground cgroup.
  113. static FilePath GetForegroundCgroupFile(const std::string& token) {
  114. // Processes with an empty token use the default foreground cgroup.
  115. if (token.empty()) {
  116. return CGroups::Get().foreground_file;
  117. }
  118. FilePath cgroup_path = GetForegroundCgroupDir(token);
  119. return cgroup_path.Append("cgroup.procs");
  120. }
  121. static CGroups& Get() {
  122. static auto& groups = *new CGroups;
  123. return groups;
  124. }
  125. };
  126. // Returns true if the 'OneGroupPerRenderer' feature is enabled. The feature
  127. // is enabled if the kOneGroupPerRenderer feature flag is enabled and the
  128. // system supports the chrome cgroups. Will block if this is the first call
  129. // that will read the cgroup configs.
  130. bool OneGroupPerRendererEnabled() {
  131. return FeatureList::IsEnabled(kOneGroupPerRenderer) && CGroups::Get().enabled;
  132. }
  133. #else
  134. const int kBackgroundPriority = 5;
  135. #endif // BUILDFLAG(IS_CHROMEOS)
  136. } // namespace
  137. Time Process::CreationTime() const {
  138. int64_t start_ticks = is_current()
  139. ? internal::ReadProcSelfStatsAndGetFieldAsInt64(
  140. internal::VM_STARTTIME)
  141. : internal::ReadProcStatsAndGetFieldAsInt64(
  142. Pid(), internal::VM_STARTTIME);
  143. if (!start_ticks)
  144. return Time();
  145. TimeDelta start_offset = internal::ClockTicksToTimeDelta(start_ticks);
  146. Time boot_time = internal::GetBootTime();
  147. if (boot_time.is_null())
  148. return Time();
  149. return Time(boot_time + start_offset);
  150. }
  151. // static
  152. bool Process::CanBackgroundProcesses() {
  153. #if BUILDFLAG(IS_CHROMEOS)
  154. if (CGroups::Get().enabled)
  155. return true;
  156. #endif // BUILDFLAG(IS_CHROMEOS)
  157. static const bool can_reraise_priority =
  158. internal::CanLowerNiceTo(kForegroundPriority);
  159. return can_reraise_priority;
  160. }
  161. bool Process::IsProcessBackgrounded() const {
  162. DCHECK(IsValid());
  163. #if BUILDFLAG(IS_CHROMEOS)
  164. if (CGroups::Get().enabled) {
  165. // Used to allow reading the process priority from proc on thread launch.
  166. ThreadRestrictions::ScopedAllowIO allow_io;
  167. std::string proc;
  168. if (ReadFileToString(FilePath(StringPrintf(kProcPath, process_)), &proc)) {
  169. return IsProcessBackgroundedCGroup(proc);
  170. }
  171. return false;
  172. }
  173. #endif // BUILDFLAG(IS_CHROMEOS)
  174. return GetPriority() == kBackgroundPriority;
  175. }
  176. bool Process::SetProcessBackgrounded(bool background) {
  177. DCHECK(IsValid());
  178. #if BUILDFLAG(IS_CHROMEOS)
  179. if (CGroups::Get().enabled) {
  180. std::string pid = NumberToString(process_);
  181. const FilePath file =
  182. background ? CGroups::Get().background_file
  183. : CGroups::Get().GetForegroundCgroupFile(unique_token_);
  184. return WriteFile(file, pid);
  185. }
  186. #endif // BUILDFLAG(IS_CHROMEOS)
  187. if (!CanBackgroundProcesses())
  188. return false;
  189. int priority = background ? kBackgroundPriority : kForegroundPriority;
  190. int result = setpriority(PRIO_PROCESS, static_cast<id_t>(process_), priority);
  191. DPCHECK(result == 0);
  192. return result == 0;
  193. }
  194. #if BUILDFLAG(IS_CHROMEOS)
  195. bool IsProcessBackgroundedCGroup(const StringPiece& cgroup_contents) {
  196. // The process can be part of multiple control groups, and for each cgroup
  197. // hierarchy there's an entry in the file. We look for a control group
  198. // named "/chrome_renderers/background" to determine if the process is
  199. // backgrounded. crbug.com/548818.
  200. std::vector<StringPiece> lines = SplitStringPiece(
  201. cgroup_contents, "\n", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
  202. for (const auto& line : lines) {
  203. std::vector<StringPiece> fields =
  204. SplitStringPiece(line, ":", TRIM_WHITESPACE, SPLIT_WANT_ALL);
  205. if (fields.size() != 3U) {
  206. NOTREACHED();
  207. continue;
  208. }
  209. if (fields[2] == kBackground)
  210. return true;
  211. }
  212. return false;
  213. }
  214. #endif // BUILDFLAG(IS_CHROMEOS)
  215. #if BUILDFLAG(IS_CHROMEOS_ASH)
  216. // Reads /proc/<pid>/status and returns the PID in its PID namespace.
  217. // If the process is not in a PID namespace or /proc/<pid>/status does not
  218. // report NSpid, kNullProcessId is returned.
  219. ProcessId Process::GetPidInNamespace() const {
  220. std::string status;
  221. {
  222. // Synchronously reading files in /proc does not hit the disk.
  223. ThreadRestrictions::ScopedAllowIO allow_io;
  224. FilePath status_file =
  225. FilePath("/proc").Append(NumberToString(process_)).Append("status");
  226. if (!ReadFileToString(status_file, &status)) {
  227. return kNullProcessId;
  228. }
  229. }
  230. StringPairs pairs;
  231. SplitStringIntoKeyValuePairs(status, ':', '\n', &pairs);
  232. for (const auto& pair : pairs) {
  233. const std::string& key = pair.first;
  234. const std::string& value_str = pair.second;
  235. if (key == "NSpid") {
  236. std::vector<StringPiece> split_value_str = SplitStringPiece(
  237. value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
  238. if (split_value_str.size() <= 1) {
  239. return kNullProcessId;
  240. }
  241. int value;
  242. // The last value in the list is the PID in the namespace.
  243. if (!StringToInt(split_value_str.back(), &value)) {
  244. NOTREACHED();
  245. return kNullProcessId;
  246. }
  247. return value;
  248. }
  249. }
  250. return kNullProcessId;
  251. }
  252. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  253. #if BUILDFLAG(IS_CHROMEOS)
  254. // static
  255. bool Process::OneGroupPerRendererEnabledForTesting() {
  256. return OneGroupPerRendererEnabled();
  257. }
  258. // On Chrome OS, each renderer runs in its own cgroup when running in the
  259. // foreground. After process creation the cgroup is created using a
  260. // unique token.
  261. void Process::InitializePriority() {
  262. if (!OneGroupPerRendererEnabled() || !IsValid() || !unique_token_.empty()) {
  263. return;
  264. }
  265. // The token has the following format:
  266. // {cgroup_prefix}{UnguessableToken}
  267. // The cgroup prefix is to distinguish ash from lacros tokens for stale
  268. // cgroup cleanup.
  269. unique_token_ = StrCat({CGroups::Get().group_prefix_token,
  270. UnguessableToken::Create().ToString()});
  271. FilePath cgroup_path = CGroups::Get().GetForegroundCgroupDir(unique_token_);
  272. // Note that CreateDirectoryAndGetError() does not fail if the directory
  273. // already exits.
  274. if (!CreateDirectoryAndGetError(cgroup_path, nullptr)) {
  275. // If creating the directory fails, fall back to use the foreground group.
  276. int saved_errno = errno;
  277. LOG(ERROR) << "Failed to create cgroup, falling back to foreground"
  278. << ", cgroup=" << cgroup_path
  279. << ", errno=" << strerror(saved_errno);
  280. unique_token_.clear();
  281. return;
  282. }
  283. if (!CGroups::Get().uclamp_min.empty() &&
  284. !WriteFile(cgroup_path.Append(kUclampMinFile),
  285. CGroups::Get().uclamp_min)) {
  286. LOG(ERROR) << "Failed to write uclamp min file, cgroup_path="
  287. << cgroup_path;
  288. }
  289. if (!CGroups::Get().uclamp_min.empty() &&
  290. !WriteFile(cgroup_path.Append(kUclampMaxFile),
  291. CGroups::Get().uclamp_max)) {
  292. LOG(ERROR) << "Failed to write uclamp max file, cgroup_path="
  293. << cgroup_path;
  294. }
  295. }
  296. // static
  297. void Process::CleanUpProcessScheduled(Process process, int remaining_retries) {
  298. process.CleanUpProcess(remaining_retries);
  299. }
  300. void Process::CleanUpProcessAsync() const {
  301. if (!FeatureList::IsEnabled(kOneGroupPerRenderer) || unique_token_.empty()) {
  302. return;
  303. }
  304. ThreadPool::PostTask(FROM_HERE, {MayBlock(), TaskPriority::BEST_EFFORT},
  305. BindOnce(&Process::CleanUpProcessScheduled, Duplicate(),
  306. kCgroupDeleteRetries));
  307. }
  308. void Process::CleanUpProcess(int remaining_retries) const {
  309. if (!OneGroupPerRendererEnabled() || unique_token_.empty()) {
  310. return;
  311. }
  312. // Try to delete the cgroup
  313. // TODO(1322562): We can use notify_on_release to automoatically delete the
  314. // cgroup when the process has left the cgroup.
  315. FilePath cgroup = CGroups::Get().GetForegroundCgroupDir(unique_token_);
  316. if (!DeleteFile(cgroup)) {
  317. auto saved_errno = errno;
  318. LOG(ERROR) << "Failed to delete cgroup " << cgroup
  319. << ", errno=" << strerror(saved_errno);
  320. // If the delete failed, then the process is still potentially in the
  321. // cgroup. Move the process to background and schedule a callback to try
  322. // again.
  323. if (remaining_retries > 0) {
  324. std::string pidstr = NumberToString(process_);
  325. if (!WriteFile(CGroups::Get().background_file, pidstr)) {
  326. // Failed to move the process, LOG a warning but try again.
  327. saved_errno = errno;
  328. LOG(WARNING) << "Failed to move the process to background"
  329. << ", pid=" << pidstr
  330. << ", errno=" << strerror(saved_errno);
  331. }
  332. ThreadPool::PostDelayedTask(FROM_HERE,
  333. {MayBlock(), TaskPriority::BEST_EFFORT},
  334. BindOnce(&Process::CleanUpProcessScheduled,
  335. Duplicate(), remaining_retries - 1),
  336. kCgroupDeleteRetryTime);
  337. }
  338. }
  339. }
  340. // static
  341. void Process::CleanUpStaleProcessStates() {
  342. if (!OneGroupPerRendererEnabled()) {
  343. return;
  344. }
  345. FileEnumerator traversal(FilePath(kFullRendererCgroupRoot), false,
  346. FileEnumerator::DIRECTORIES);
  347. for (FilePath path = traversal.Next(); !path.empty();
  348. path = traversal.Next()) {
  349. std::string dirname = path.BaseName().value();
  350. if (dirname == FilePath(kForeground).BaseName().value() ||
  351. dirname == FilePath(kBackground).BaseName().value()) {
  352. continue;
  353. }
  354. if (!StartsWith(dirname, kCgroupPrefix) ||
  355. StartsWith(dirname, CGroups::Get().group_prefix_token)) {
  356. continue;
  357. }
  358. if (!DeleteFile(path)) {
  359. auto saved_errno = errno;
  360. LOG(ERROR) << "Failed to delete " << path
  361. << ", errno=" << strerror(saved_errno);
  362. }
  363. }
  364. }
  365. #endif // BUILDFLAG(IS_CHROMEOS)
  366. } // namespace base