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