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