1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/ash/system/procfs_util.h"
#include <string_view>
#include "base/files/file_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
namespace ash {
namespace system {
std::optional<SingleProcStat> GetSingleProcStat(
const base::FilePath& stat_file) {
SingleProcStat stat;
std::string stat_contents;
if (!base::ReadFileToString(stat_file, &stat_contents))
return std::nullopt;
// This file looks like:
// <num1> (<str>) <char> <num2> <num3> ...
// The entries at 0-based index 0 is the PID.
// The entry at index 1 represents a filename, which can have an arbitrary
// number of spaces, so skip it by finding the last parenthesis.
// The entry at index 3, represents the PPID of the process.
// The entries at indices 13 and 14 represent the amount of time the
// process was in user mode and kernel mode in jiffies.
// The entry at index 23 represents process resident memory in pages.
const auto first_space = stat_contents.find(' ');
if (first_space == std::string::npos)
return std::nullopt;
if (!base::StringToInt(stat_contents.substr(0, first_space), &stat.pid))
return std::nullopt;
const auto left_parenthesis = stat_contents.find('(');
if (left_parenthesis == std::string::npos)
return std::nullopt;
const auto right_parenthesis = stat_contents.find(')');
if (right_parenthesis == std::string::npos)
return std::nullopt;
if ((right_parenthesis - left_parenthesis - 1) <= 0)
return std::nullopt;
stat.name = stat_contents.substr(left_parenthesis + 1,
right_parenthesis - left_parenthesis - 1);
// Skip the comm field.
const auto last_parenthesis = stat_contents.find_last_of(')');
if (last_parenthesis == std::string::npos ||
last_parenthesis + 1 > stat_contents.length())
return std::nullopt;
// Skip the parenthesis itself.
const std::string truncated_proc_stat_contents =
stat_contents.substr(last_parenthesis + 1);
std::vector<std::string_view> proc_stat_split =
base::SplitStringPiece(truncated_proc_stat_contents, " \t\n",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
// The first 2 entries of the file were removed earlier, so all the indices
// for the entries will be shifted by 2.
if (proc_stat_split.size() < 21)
return std::nullopt;
if (!base::StringToInt(proc_stat_split[1], &stat.ppid))
return std::nullopt;
// These two entries contain the total time this process spent in user mode
// and kernel mode. This is roughly the total CPU time that the process has
// used.
if (!base::StringToInt64(proc_stat_split[11], &stat.utime))
return std::nullopt;
if (!base::StringToInt64(proc_stat_split[12], &stat.stime))
return std::nullopt;
if (!base::StringToInt64(proc_stat_split[21], &stat.rss))
return std::nullopt;
return stat;
}
std::optional<int64_t> GetCpuTimeJiffies(const base::FilePath& stat_file) {
std::string stat_contents;
if (!base::ReadFileToString(stat_file, &stat_contents))
return std::nullopt;
// This file looks like:
// cpu <num1> <num2> ...
// cpu0 <num1> <num2> ...
// cpu1 <num1> <num2> ...
// ...
// Where each number represents the amount of time in jiffies a certain CPU is
// in some state. The first line presents the total amount of time in jiffies
// the system is in some state across all CPUs. The first line beginning with
// "cpu " needs to be singled out. The first 8 of the 10 numbers on that line
// need to be summed to obtain the total amount of time in jiffies the system
// has been running across all states. The last 2 numbers are guest and
// guest_nice, which are already accounted for in the first 2 numbers of user
// and nice respectively.
std::vector<std::string_view> stat_lines = base::SplitStringPiece(
stat_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const auto& line : stat_lines) {
// Find the line that starts with "cpu " and sum the first 8 numbers to
// get the total amount of jiffies used.
if (base::StartsWith(line, "cpu ", base::CompareCase::SENSITIVE)) {
std::vector<std::string_view> cpu_info_parts = base::SplitStringPiece(
line, " \t", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (cpu_info_parts.size() != 11)
return std::nullopt;
int64_t total_time = 0;
// Sum the first 8 numbers. Element 0 is "cpu".
for (int i = 1; i <= 8; i++) {
int64_t curr;
if (!base::StringToInt64(cpu_info_parts.at(i), &curr))
return std::nullopt;
total_time += curr;
}
return total_time;
}
}
return std::nullopt;
}
ProcStatFile::ProcStatFile(base::ProcessId process_id) {
base::FilePath procfs_stat_path =
base::FilePath("/proc")
.Append(base::NumberToString(process_id))
.Append("stat");
// Opening procfs file is not blocking.
base::ScopedAllowBlocking allow_blocking;
file_ = base::File(procfs_stat_path,
base::File::FLAG_OPEN | base::File::FLAG_READ);
}
ProcStatFile::~ProcStatFile() {
// Closing procfs file is not blocking.
base::ScopedAllowBlocking allow_blocking;
file_.Close();
}
bool ProcStatFile::IsValid() const {
return file_.IsValid();
}
bool ProcStatFile::IsPidAlive() {
char buf;
// Reading procfs is not blocking.
base::ScopedAllowBlocking allow_blocking;
// If the process/thread dies, read(2)ing stat file fails as ESRCH.
return file_.IsValid() && file_.Read(0, &buf, 1) == 1;
}
} // namespace system
} // namespace ash
|