OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/process_util.h" | 5 #include "base/process_util.h" |
6 | 6 |
7 #include <dirent.h> | 7 #include <dirent.h> |
8 #include <malloc.h> | 8 #include <malloc.h> |
9 #include <sys/time.h> | 9 #include <sys/time.h> |
10 #include <sys/types.h> | 10 #include <sys/types.h> |
11 #include <unistd.h> | 11 #include <unistd.h> |
12 | 12 |
13 #include "base/file_util.h" | 13 #include "base/file_util.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/string_number_conversions.h" | 15 #include "base/string_number_conversions.h" |
16 #include "base/string_split.h" | 16 #include "base/string_split.h" |
17 #include "base/string_tokenizer.h" | |
18 #include "base/string_util.h" | 17 #include "base/string_util.h" |
| 18 #include "base/strings/string_tokenizer.h" |
19 #include "base/sys_info.h" | 19 #include "base/sys_info.h" |
20 #include "base/threading/thread_restrictions.h" | 20 #include "base/threading/thread_restrictions.h" |
21 | 21 |
| 22 namespace base { |
| 23 |
22 namespace { | 24 namespace { |
23 | 25 |
24 enum ParsingState { | 26 enum ParsingState { |
25 KEY_NAME, | 27 KEY_NAME, |
26 KEY_VALUE | 28 KEY_VALUE |
27 }; | 29 }; |
28 | 30 |
29 const char kProcDir[] = "/proc"; | 31 const char kProcDir[] = "/proc"; |
30 const char kStatFile[] = "stat"; | 32 const char kStatFile[] = "stat"; |
31 | 33 |
32 // Returns a FilePath to "/proc/pid". | 34 // Returns a FilePath to "/proc/pid". |
33 FilePath GetProcPidDir(pid_t pid) { | 35 FilePath GetProcPidDir(pid_t pid) { |
34 return FilePath(kProcDir).Append(base::IntToString(pid)); | 36 return FilePath(kProcDir).Append(IntToString(pid)); |
35 } | 37 } |
36 | 38 |
37 // Fields from /proc/<pid>/stat, 0-based. See man 5 proc. | 39 // Fields from /proc/<pid>/stat, 0-based. See man 5 proc. |
38 // If the ordering ever changes, carefully review functions that use these | 40 // If the ordering ever changes, carefully review functions that use these |
39 // values. | 41 // values. |
40 enum ProcStatsFields { | 42 enum ProcStatsFields { |
41 VM_COMM = 1, // Filename of executable, without parentheses. | 43 VM_COMM = 1, // Filename of executable, without parentheses. |
42 VM_STATE = 2, // Letter indicating the state of the process. | 44 VM_STATE = 2, // Letter indicating the state of the process. |
43 VM_PPID = 3, // PID of the parent. | 45 VM_PPID = 3, // PID of the parent. |
44 VM_PGRP = 4, // Process group id. | 46 VM_PGRP = 4, // Process group id. |
45 VM_UTIME = 13, // Time scheduled in user mode in clock ticks. | 47 VM_UTIME = 13, // Time scheduled in user mode in clock ticks. |
46 VM_STIME = 14, // Time scheduled in kernel mode in clock ticks. | 48 VM_STIME = 14, // Time scheduled in kernel mode in clock ticks. |
47 VM_VSIZE = 22, // Virtual memory size in bytes. | 49 VM_VSIZE = 22, // Virtual memory size in bytes. |
48 VM_RSS = 23, // Resident Set Size in pages. | 50 VM_RSS = 23, // Resident Set Size in pages. |
49 }; | 51 }; |
50 | 52 |
51 // Reads /proc/<pid>/stat into |buffer|. Returns true if the file can be read | 53 // Reads /proc/<pid>/stat into |buffer|. Returns true if the file can be read |
52 // and is non-empty. | 54 // and is non-empty. |
53 bool ReadProcStats(pid_t pid, std::string* buffer) { | 55 bool ReadProcStats(pid_t pid, std::string* buffer) { |
54 buffer->clear(); | 56 buffer->clear(); |
55 // Synchronously reading files in /proc is safe. | 57 // Synchronously reading files in /proc is safe. |
56 base::ThreadRestrictions::ScopedAllowIO allow_io; | 58 ThreadRestrictions::ScopedAllowIO allow_io; |
57 | 59 |
58 FilePath stat_file = GetProcPidDir(pid).Append(kStatFile); | 60 FilePath stat_file = GetProcPidDir(pid).Append(kStatFile); |
59 if (!file_util::ReadFileToString(stat_file, buffer)) { | 61 if (!file_util::ReadFileToString(stat_file, buffer)) { |
60 DLOG(WARNING) << "Failed to get process stats."; | 62 DLOG(WARNING) << "Failed to get process stats."; |
61 return false; | 63 return false; |
62 } | 64 } |
63 return !buffer->empty(); | 65 return !buffer->empty(); |
64 } | 66 } |
65 | 67 |
66 // Takes |stats_data| and populates |proc_stats| with the values split by | 68 // Takes |stats_data| and populates |proc_stats| with the values split by |
(...skipping 24 matching lines...) Expand all Loading... |
91 proc_stats->clear(); | 93 proc_stats->clear(); |
92 // PID. | 94 // PID. |
93 proc_stats->push_back(stats_data.substr(0, open_parens_idx)); | 95 proc_stats->push_back(stats_data.substr(0, open_parens_idx)); |
94 // Process name without parentheses. | 96 // Process name without parentheses. |
95 proc_stats->push_back( | 97 proc_stats->push_back( |
96 stats_data.substr(open_parens_idx + 1, | 98 stats_data.substr(open_parens_idx + 1, |
97 close_parens_idx - (open_parens_idx + 1))); | 99 close_parens_idx - (open_parens_idx + 1))); |
98 | 100 |
99 // Split the rest. | 101 // Split the rest. |
100 std::vector<std::string> other_stats; | 102 std::vector<std::string> other_stats; |
101 base::SplitString(stats_data.substr(close_parens_idx + 2), ' ', &other_stats); | 103 SplitString(stats_data.substr(close_parens_idx + 2), ' ', &other_stats); |
102 for (size_t i = 0; i < other_stats.size(); ++i) | 104 for (size_t i = 0; i < other_stats.size(); ++i) |
103 proc_stats->push_back(other_stats[i]); | 105 proc_stats->push_back(other_stats[i]); |
104 return true; | 106 return true; |
105 } | 107 } |
106 | 108 |
107 // Reads the |field_num|th field from |proc_stats|. Returns 0 on failure. | 109 // Reads the |field_num|th field from |proc_stats|. Returns 0 on failure. |
108 // This version does not handle the first 3 values, since the first value is | 110 // This version does not handle the first 3 values, since the first value is |
109 // simply |pid|, and the next two values are strings. | 111 // simply |pid|, and the next two values are strings. |
110 int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats, | 112 int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats, |
111 ProcStatsFields field_num) { | 113 ProcStatsFields field_num) { |
112 DCHECK_GE(field_num, VM_PPID); | 114 DCHECK_GE(field_num, VM_PPID); |
113 CHECK_LT(static_cast<size_t>(field_num), proc_stats.size()); | 115 CHECK_LT(static_cast<size_t>(field_num), proc_stats.size()); |
114 | 116 |
115 int value; | 117 int value; |
116 return base::StringToInt(proc_stats[field_num], &value) ? value : 0; | 118 return StringToInt(proc_stats[field_num], &value) ? value : 0; |
117 } | 119 } |
118 | 120 |
119 // Same as GetProcStatsFieldAsInt(), but for size_t values. | 121 // Same as GetProcStatsFieldAsInt(), but for size_t values. |
120 size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats, | 122 size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats, |
121 ProcStatsFields field_num) { | 123 ProcStatsFields field_num) { |
122 DCHECK_GE(field_num, VM_PPID); | 124 DCHECK_GE(field_num, VM_PPID); |
123 CHECK_LT(static_cast<size_t>(field_num), proc_stats.size()); | 125 CHECK_LT(static_cast<size_t>(field_num), proc_stats.size()); |
124 | 126 |
125 size_t value; | 127 size_t value; |
126 return base::StringToSizeT(proc_stats[field_num], &value) ? value : 0; | 128 return StringToSizeT(proc_stats[field_num], &value) ? value : 0; |
127 } | 129 } |
128 | 130 |
129 // Convenience wrapper around GetProcStatsFieldAsInt(), ParseProcStats() and | 131 // Convenience wrapper around GetProcStatsFieldAsInt(), ParseProcStats() and |
130 // ReadProcStats(). See GetProcStatsFieldAsInt() for details. | 132 // ReadProcStats(). See GetProcStatsFieldAsInt() for details. |
131 int ReadProcStatsAndGetFieldAsInt(pid_t pid, ProcStatsFields field_num) { | 133 int ReadProcStatsAndGetFieldAsInt(pid_t pid, ProcStatsFields field_num) { |
132 std::string stats_data; | 134 std::string stats_data; |
133 if (!ReadProcStats(pid, &stats_data)) | 135 if (!ReadProcStats(pid, &stats_data)) |
134 return 0; | 136 return 0; |
135 std::vector<std::string> proc_stats; | 137 std::vector<std::string> proc_stats; |
136 if (!ParseProcStats(stats_data, &proc_stats)) | 138 if (!ParseProcStats(stats_data, &proc_stats)) |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 return 0; | 170 return 0; |
169 } | 171 } |
170 | 172 |
171 // Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command | 173 // Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command |
172 // line arguments. Returns true if successful. | 174 // line arguments. Returns true if successful. |
173 // Note: /proc/<pid>/cmdline contains command line arguments separated by single | 175 // Note: /proc/<pid>/cmdline contains command line arguments separated by single |
174 // null characters. We tokenize it into a vector of strings using '\0' as a | 176 // null characters. We tokenize it into a vector of strings using '\0' as a |
175 // delimiter. | 177 // delimiter. |
176 bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { | 178 bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { |
177 // Synchronously reading files in /proc is safe. | 179 // Synchronously reading files in /proc is safe. |
178 base::ThreadRestrictions::ScopedAllowIO allow_io; | 180 ThreadRestrictions::ScopedAllowIO allow_io; |
179 | 181 |
180 FilePath cmd_line_file = GetProcPidDir(pid).Append("cmdline"); | 182 FilePath cmd_line_file = GetProcPidDir(pid).Append("cmdline"); |
181 std::string cmd_line; | 183 std::string cmd_line; |
182 if (!file_util::ReadFileToString(cmd_line_file, &cmd_line)) | 184 if (!file_util::ReadFileToString(cmd_line_file, &cmd_line)) |
183 return false; | 185 return false; |
184 std::string delimiters; | 186 std::string delimiters; |
185 delimiters.push_back('\0'); | 187 delimiters.push_back('\0'); |
186 Tokenize(cmd_line, delimiters, proc_cmd_line_args); | 188 Tokenize(cmd_line, delimiters, proc_cmd_line_args); |
187 return true; | 189 return true; |
188 } | 190 } |
189 | 191 |
190 // Take a /proc directory entry named |d_name|, and if it is the directory for | 192 // Take a /proc directory entry named |d_name|, and if it is the directory for |
191 // a process, convert it to a pid_t. | 193 // a process, convert it to a pid_t. |
192 // Returns 0 on failure. | 194 // Returns 0 on failure. |
193 // e.g. /proc/self/ will return 0, whereas /proc/1234 will return 1234. | 195 // e.g. /proc/self/ will return 0, whereas /proc/1234 will return 1234. |
194 pid_t ProcDirSlotToPid(const char* d_name) { | 196 pid_t ProcDirSlotToPid(const char* d_name) { |
195 int i; | 197 int i; |
196 for (i = 0; i < NAME_MAX && d_name[i]; ++i) { | 198 for (i = 0; i < NAME_MAX && d_name[i]; ++i) { |
197 if (!IsAsciiDigit(d_name[i])) { | 199 if (!IsAsciiDigit(d_name[i])) { |
198 return 0; | 200 return 0; |
199 } | 201 } |
200 } | 202 } |
201 if (i == NAME_MAX) | 203 if (i == NAME_MAX) |
202 return 0; | 204 return 0; |
203 | 205 |
204 // Read the process's command line. | 206 // Read the process's command line. |
205 pid_t pid; | 207 pid_t pid; |
206 std::string pid_string(d_name); | 208 std::string pid_string(d_name); |
207 if (!base::StringToInt(pid_string, &pid)) { | 209 if (!StringToInt(pid_string, &pid)) { |
208 NOTREACHED(); | 210 NOTREACHED(); |
209 return 0; | 211 return 0; |
210 } | 212 } |
211 return pid; | 213 return pid; |
212 } | 214 } |
213 | 215 |
214 // Get the total CPU of a single process. Return value is number of jiffies | 216 // Get the total CPU of a single process. Return value is number of jiffies |
215 // on success or -1 on error. | 217 // on success or -1 on error. |
216 int GetProcessCPU(pid_t pid) { | 218 int GetProcessCPU(pid_t pid) { |
217 // Use /proc/<pid>/task to find all threads and parse their /stat file. | 219 // Use /proc/<pid>/task to find all threads and parse their /stat file. |
218 FilePath task_path = GetProcPidDir(pid).Append("task"); | 220 FilePath task_path = GetProcPidDir(pid).Append("task"); |
219 | 221 |
220 DIR* dir = opendir(task_path.value().c_str()); | 222 DIR* dir = opendir(task_path.value().c_str()); |
221 if (!dir) { | 223 if (!dir) { |
222 DPLOG(ERROR) << "opendir(" << task_path.value() << ")"; | 224 DPLOG(ERROR) << "opendir(" << task_path.value() << ")"; |
223 return -1; | 225 return -1; |
224 } | 226 } |
225 | 227 |
226 int total_cpu = 0; | 228 int total_cpu = 0; |
227 while (struct dirent* ent = readdir(dir)) { | 229 while (struct dirent* ent = readdir(dir)) { |
228 pid_t tid = ProcDirSlotToPid(ent->d_name); | 230 pid_t tid = ProcDirSlotToPid(ent->d_name); |
229 if (!tid) | 231 if (!tid) |
230 continue; | 232 continue; |
231 | 233 |
232 // Synchronously reading files in /proc is safe. | 234 // Synchronously reading files in /proc is safe. |
233 base::ThreadRestrictions::ScopedAllowIO allow_io; | 235 ThreadRestrictions::ScopedAllowIO allow_io; |
234 | 236 |
235 std::string stat; | 237 std::string stat; |
236 FilePath stat_path = task_path.Append(ent->d_name).Append(kStatFile); | 238 FilePath stat_path = task_path.Append(ent->d_name).Append(kStatFile); |
237 if (file_util::ReadFileToString(stat_path, &stat)) { | 239 if (file_util::ReadFileToString(stat_path, &stat)) { |
238 int cpu = base::ParseProcStatCPU(stat); | 240 int cpu = ParseProcStatCPU(stat); |
239 if (cpu > 0) | 241 if (cpu > 0) |
240 total_cpu += cpu; | 242 total_cpu += cpu; |
241 } | 243 } |
242 } | 244 } |
243 closedir(dir); | 245 closedir(dir); |
244 | 246 |
245 return total_cpu; | 247 return total_cpu; |
246 } | 248 } |
247 | 249 |
248 // Read /proc/<pid>/status and returns the value for |field|, or 0 on failure. | 250 // Read /proc/<pid>/status and returns the value for |field|, or 0 on failure. |
249 // Only works for fields in the form of "Field: value kB". | 251 // Only works for fields in the form of "Field: value kB". |
250 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { | 252 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { |
251 FilePath stat_file = GetProcPidDir(pid).Append("status"); | 253 FilePath stat_file = GetProcPidDir(pid).Append("status"); |
252 std::string status; | 254 std::string status; |
253 { | 255 { |
254 // Synchronously reading files in /proc is safe. | 256 // Synchronously reading files in /proc is safe. |
255 base::ThreadRestrictions::ScopedAllowIO allow_io; | 257 ThreadRestrictions::ScopedAllowIO allow_io; |
256 if (!file_util::ReadFileToString(stat_file, &status)) | 258 if (!file_util::ReadFileToString(stat_file, &status)) |
257 return 0; | 259 return 0; |
258 } | 260 } |
259 | 261 |
260 StringTokenizer tokenizer(status, ":\n"); | 262 StringTokenizer tokenizer(status, ":\n"); |
261 ParsingState state = KEY_NAME; | 263 ParsingState state = KEY_NAME; |
262 base::StringPiece last_key_name; | 264 StringPiece last_key_name; |
263 while (tokenizer.GetNext()) { | 265 while (tokenizer.GetNext()) { |
264 switch (state) { | 266 switch (state) { |
265 case KEY_NAME: | 267 case KEY_NAME: |
266 last_key_name = tokenizer.token_piece(); | 268 last_key_name = tokenizer.token_piece(); |
267 state = KEY_VALUE; | 269 state = KEY_VALUE; |
268 break; | 270 break; |
269 case KEY_VALUE: | 271 case KEY_VALUE: |
270 DCHECK(!last_key_name.empty()); | 272 DCHECK(!last_key_name.empty()); |
271 if (last_key_name == field) { | 273 if (last_key_name == field) { |
272 std::string value_str; | 274 std::string value_str; |
273 tokenizer.token_piece().CopyToString(&value_str); | 275 tokenizer.token_piece().CopyToString(&value_str); |
274 std::string value_str_trimmed; | 276 std::string value_str_trimmed; |
275 TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed); | 277 TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed); |
276 std::vector<std::string> split_value_str; | 278 std::vector<std::string> split_value_str; |
277 base::SplitString(value_str_trimmed, ' ', &split_value_str); | 279 SplitString(value_str_trimmed, ' ', &split_value_str); |
278 if (split_value_str.size() != 2 || split_value_str[1] != "kB") { | 280 if (split_value_str.size() != 2 || split_value_str[1] != "kB") { |
279 NOTREACHED(); | 281 NOTREACHED(); |
280 return 0; | 282 return 0; |
281 } | 283 } |
282 size_t value; | 284 size_t value; |
283 if (!base::StringToSizeT(split_value_str[0], &value)) { | 285 if (!StringToSizeT(split_value_str[0], &value)) { |
284 NOTREACHED(); | 286 NOTREACHED(); |
285 return 0; | 287 return 0; |
286 } | 288 } |
287 return value; | 289 return value; |
288 } | 290 } |
289 state = KEY_NAME; | 291 state = KEY_NAME; |
290 break; | 292 break; |
291 } | 293 } |
292 } | 294 } |
293 NOTREACHED(); | 295 NOTREACHED(); |
294 return 0; | 296 return 0; |
295 } | 297 } |
296 | 298 |
297 } // namespace | 299 } // namespace |
298 | 300 |
299 namespace base { | |
300 | |
301 #if defined(USE_LINUX_BREAKPAD) | 301 #if defined(USE_LINUX_BREAKPAD) |
302 size_t g_oom_size = 0U; | 302 size_t g_oom_size = 0U; |
303 #endif | 303 #endif |
304 | 304 |
305 const char kProcSelfExe[] = "/proc/self/exe"; | 305 const char kProcSelfExe[] = "/proc/self/exe"; |
306 | 306 |
307 ProcessId GetParentProcessId(ProcessHandle process) { | 307 ProcessId GetParentProcessId(ProcessHandle process) { |
308 ProcessId pid = ReadProcStatsAndGetFieldAsInt(process, VM_PPID); | 308 ProcessId pid = ReadProcStatsAndGetFieldAsInt(process, VM_PPID); |
309 if (pid) | 309 if (pid) |
310 return pid; | 310 return pid; |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
453 // First we need to get the page size, since everything is measured in pages. | 453 // First we need to get the page size, since everything is measured in pages. |
454 // For details, see: man 5 proc. | 454 // For details, see: man 5 proc. |
455 const int page_size_kb = getpagesize() / 1024; | 455 const int page_size_kb = getpagesize() / 1024; |
456 if (page_size_kb <= 0) | 456 if (page_size_kb <= 0) |
457 return false; | 457 return false; |
458 | 458 |
459 std::string statm; | 459 std::string statm; |
460 { | 460 { |
461 FilePath statm_file = GetProcPidDir(process_).Append("statm"); | 461 FilePath statm_file = GetProcPidDir(process_).Append("statm"); |
462 // Synchronously reading files in /proc is safe. | 462 // Synchronously reading files in /proc is safe. |
463 base::ThreadRestrictions::ScopedAllowIO allow_io; | 463 ThreadRestrictions::ScopedAllowIO allow_io; |
464 bool ret = file_util::ReadFileToString(statm_file, &statm); | 464 bool ret = file_util::ReadFileToString(statm_file, &statm); |
465 if (!ret || statm.length() == 0) | 465 if (!ret || statm.length() == 0) |
466 return false; | 466 return false; |
467 } | 467 } |
468 | 468 |
469 std::vector<std::string> statm_vec; | 469 std::vector<std::string> statm_vec; |
470 base::SplitString(statm, ' ', &statm_vec); | 470 SplitString(statm, ' ', &statm_vec); |
471 if (statm_vec.size() != 7) | 471 if (statm_vec.size() != 7) |
472 return false; // Not the format we expect. | 472 return false; // Not the format we expect. |
473 | 473 |
474 int statm_rss, statm_shared; | 474 int statm_rss, statm_shared; |
475 base::StringToInt(statm_vec[1], &statm_rss); | 475 StringToInt(statm_vec[1], &statm_rss); |
476 base::StringToInt(statm_vec[2], &statm_shared); | 476 StringToInt(statm_vec[2], &statm_shared); |
477 | 477 |
478 ws_usage->priv = (statm_rss - statm_shared) * page_size_kb; | 478 ws_usage->priv = (statm_rss - statm_shared) * page_size_kb; |
479 ws_usage->shared = statm_shared * page_size_kb; | 479 ws_usage->shared = statm_shared * page_size_kb; |
480 | 480 |
481 // Sharable is not calculated, as it does not provide interesting data. | 481 // Sharable is not calculated, as it does not provide interesting data. |
482 ws_usage->shareable = 0; | 482 ws_usage->shareable = 0; |
483 | 483 |
484 return true; | 484 return true; |
485 } | 485 } |
486 | 486 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
524 last_time_ = time; | 524 last_time_ = time; |
525 last_cpu_ = cpu; | 525 last_cpu_ = cpu; |
526 | 526 |
527 return percentage; | 527 return percentage; |
528 } | 528 } |
529 | 529 |
530 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING | 530 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING |
531 // in your kernel configuration. | 531 // in your kernel configuration. |
532 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { | 532 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { |
533 // Synchronously reading files in /proc is safe. | 533 // Synchronously reading files in /proc is safe. |
534 base::ThreadRestrictions::ScopedAllowIO allow_io; | 534 ThreadRestrictions::ScopedAllowIO allow_io; |
535 | 535 |
536 std::string proc_io_contents; | 536 std::string proc_io_contents; |
537 FilePath io_file = GetProcPidDir(process_).Append("io"); | 537 FilePath io_file = GetProcPidDir(process_).Append("io"); |
538 if (!file_util::ReadFileToString(io_file, &proc_io_contents)) | 538 if (!file_util::ReadFileToString(io_file, &proc_io_contents)) |
539 return false; | 539 return false; |
540 | 540 |
541 (*io_counters).OtherOperationCount = 0; | 541 (*io_counters).OtherOperationCount = 0; |
542 (*io_counters).OtherTransferCount = 0; | 542 (*io_counters).OtherTransferCount = 0; |
543 | 543 |
544 StringTokenizer tokenizer(proc_io_contents, ": \n"); | 544 StringTokenizer tokenizer(proc_io_contents, ": \n"); |
545 ParsingState state = KEY_NAME; | 545 ParsingState state = KEY_NAME; |
546 StringPiece last_key_name; | 546 StringPiece last_key_name; |
547 while (tokenizer.GetNext()) { | 547 while (tokenizer.GetNext()) { |
548 switch (state) { | 548 switch (state) { |
549 case KEY_NAME: | 549 case KEY_NAME: |
550 last_key_name = tokenizer.token_piece(); | 550 last_key_name = tokenizer.token_piece(); |
551 state = KEY_VALUE; | 551 state = KEY_VALUE; |
552 break; | 552 break; |
553 case KEY_VALUE: | 553 case KEY_VALUE: |
554 DCHECK(!last_key_name.empty()); | 554 DCHECK(!last_key_name.empty()); |
555 if (last_key_name == "syscr") { | 555 if (last_key_name == "syscr") { |
556 base::StringToInt64(tokenizer.token_piece(), | 556 StringToInt64(tokenizer.token_piece(), |
557 reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount)); | 557 reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount)); |
558 } else if (last_key_name == "syscw") { | 558 } else if (last_key_name == "syscw") { |
559 base::StringToInt64(tokenizer.token_piece(), | 559 StringToInt64(tokenizer.token_piece(), |
560 reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount)); | 560 reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount)); |
561 } else if (last_key_name == "rchar") { | 561 } else if (last_key_name == "rchar") { |
562 base::StringToInt64(tokenizer.token_piece(), | 562 StringToInt64(tokenizer.token_piece(), |
563 reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount)); | 563 reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount)); |
564 } else if (last_key_name == "wchar") { | 564 } else if (last_key_name == "wchar") { |
565 base::StringToInt64(tokenizer.token_piece(), | 565 StringToInt64(tokenizer.token_piece(), |
566 reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount)); | 566 reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount)); |
567 } | 567 } |
568 state = KEY_NAME; | 568 state = KEY_NAME; |
569 break; | 569 break; |
570 } | 570 } |
571 } | 571 } |
572 return true; | 572 return true; |
573 } | 573 } |
574 | 574 |
575 ProcessMetrics::ProcessMetrics(ProcessHandle process) | 575 ProcessMetrics::ProcessMetrics(ProcessHandle process) |
576 : process_(process), | 576 : process_(process), |
577 last_time_(0), | 577 last_time_(0), |
578 last_system_time_(0), | 578 last_system_time_(0), |
579 last_cpu_(0) { | 579 last_cpu_(0) { |
580 processor_count_ = base::SysInfo::NumberOfProcessors(); | 580 processor_count_ = SysInfo::NumberOfProcessors(); |
581 } | 581 } |
582 | 582 |
583 | 583 |
584 // Exposed for testing. | 584 // Exposed for testing. |
585 int ParseProcStatCPU(const std::string& input) { | 585 int ParseProcStatCPU(const std::string& input) { |
586 std::vector<std::string> proc_stats; | 586 std::vector<std::string> proc_stats; |
587 if (!ParseProcStats(input, &proc_stats)) | 587 if (!ParseProcStats(input, &proc_stats)) |
588 return -1; | 588 return -1; |
589 | 589 |
590 if (proc_stats.size() <= VM_STIME) | 590 if (proc_stats.size() <= VM_STIME) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
623 inactive_anon(0), | 623 inactive_anon(0), |
624 active_file(0), | 624 active_file(0), |
625 inactive_file(0), | 625 inactive_file(0), |
626 shmem(0), | 626 shmem(0), |
627 gem_objects(-1), | 627 gem_objects(-1), |
628 gem_size(-1) { | 628 gem_size(-1) { |
629 } | 629 } |
630 | 630 |
631 bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { | 631 bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { |
632 // Synchronously reading files in /proc is safe. | 632 // Synchronously reading files in /proc is safe. |
633 base::ThreadRestrictions::ScopedAllowIO allow_io; | 633 ThreadRestrictions::ScopedAllowIO allow_io; |
634 | 634 |
635 // Used memory is: total - free - buffers - caches | 635 // Used memory is: total - free - buffers - caches |
636 FilePath meminfo_file("/proc/meminfo"); | 636 FilePath meminfo_file("/proc/meminfo"); |
637 std::string meminfo_data; | 637 std::string meminfo_data; |
638 if (!file_util::ReadFileToString(meminfo_file, &meminfo_data)) { | 638 if (!file_util::ReadFileToString(meminfo_file, &meminfo_data)) { |
639 DLOG(WARNING) << "Failed to open " << meminfo_file.value(); | 639 DLOG(WARNING) << "Failed to open " << meminfo_file.value(); |
640 return false; | 640 return false; |
641 } | 641 } |
642 std::vector<std::string> meminfo_fields; | 642 std::vector<std::string> meminfo_fields; |
643 SplitStringAlongWhitespace(meminfo_data, &meminfo_fields); | 643 SplitStringAlongWhitespace(meminfo_data, &meminfo_fields); |
644 | 644 |
645 if (meminfo_fields.size() < kMemCachedIndex) { | 645 if (meminfo_fields.size() < kMemCachedIndex) { |
646 DLOG(WARNING) << "Failed to parse " << meminfo_file.value() | 646 DLOG(WARNING) << "Failed to parse " << meminfo_file.value() |
647 << ". Only found " << meminfo_fields.size() << " fields."; | 647 << ". Only found " << meminfo_fields.size() << " fields."; |
648 return false; | 648 return false; |
649 } | 649 } |
650 | 650 |
651 DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:"); | 651 DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:"); |
652 DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:"); | 652 DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:"); |
653 DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:"); | 653 DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:"); |
654 DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:"); | 654 DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:"); |
655 DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):"); | 655 DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):"); |
656 DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):"); | 656 DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):"); |
657 DCHECK_EQ(meminfo_fields[kMemActiveFileIndex-1], "Active(file):"); | 657 DCHECK_EQ(meminfo_fields[kMemActiveFileIndex-1], "Active(file):"); |
658 DCHECK_EQ(meminfo_fields[kMemInactiveFileIndex-1], "Inactive(file):"); | 658 DCHECK_EQ(meminfo_fields[kMemInactiveFileIndex-1], "Inactive(file):"); |
659 | 659 |
660 base::StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total); | 660 StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total); |
661 base::StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free); | 661 StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free); |
662 base::StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers); | 662 StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers); |
663 base::StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached); | 663 StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached); |
664 base::StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon); | 664 StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon); |
665 base::StringToInt(meminfo_fields[kMemInactiveAnonIndex], | 665 StringToInt(meminfo_fields[kMemInactiveAnonIndex], |
666 &meminfo->inactive_anon); | 666 &meminfo->inactive_anon); |
667 base::StringToInt(meminfo_fields[kMemActiveFileIndex], &meminfo->active_file); | 667 StringToInt(meminfo_fields[kMemActiveFileIndex], &meminfo->active_file); |
668 base::StringToInt(meminfo_fields[kMemInactiveFileIndex], | 668 StringToInt(meminfo_fields[kMemInactiveFileIndex], |
669 &meminfo->inactive_file); | 669 &meminfo->inactive_file); |
670 #if defined(OS_CHROMEOS) | 670 #if defined(OS_CHROMEOS) |
671 // Chrome OS has a tweaked kernel that allows us to query Shmem, which is | 671 // Chrome OS has a tweaked kernel that allows us to query Shmem, which is |
672 // usually video memory otherwise invisible to the OS. Unfortunately, the | 672 // usually video memory otherwise invisible to the OS. Unfortunately, the |
673 // meminfo format varies on different hardware so we have to search for the | 673 // meminfo format varies on different hardware so we have to search for the |
674 // string. It always appears after "Cached:". | 674 // string. It always appears after "Cached:". |
675 for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) { | 675 for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) { |
676 if (meminfo_fields[i] == "Shmem:") { | 676 if (meminfo_fields[i] == "Shmem:") { |
677 base::StringToInt(meminfo_fields[i+1], &meminfo->shmem); | 677 StringToInt(meminfo_fields[i+1], &meminfo->shmem); |
678 break; | 678 break; |
679 } | 679 } |
680 } | 680 } |
681 #endif | 681 #endif |
682 | 682 |
683 // Check for graphics memory data and report if present. Synchronously | 683 // Check for graphics memory data and report if present. Synchronously |
684 // reading files in /sys is fast. | 684 // reading files in /sys is fast. |
685 #if defined(ARCH_CPU_ARM_FAMILY) | 685 #if defined(ARCH_CPU_ARM_FAMILY) |
686 FilePath geminfo_file("/sys/kernel/debug/dri/0/exynos_gem_objects"); | 686 FilePath geminfo_file("/sys/kernel/debug/dri/0/exynos_gem_objects"); |
687 #else | 687 #else |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
847 // also has its own C version. | 847 // also has its own C version. |
848 bool AdjustOOMScore(ProcessId process, int score) { | 848 bool AdjustOOMScore(ProcessId process, int score) { |
849 if (score < 0 || score > kMaxOomScore) | 849 if (score < 0 || score > kMaxOomScore) |
850 return false; | 850 return false; |
851 | 851 |
852 FilePath oom_path(GetProcPidDir(process)); | 852 FilePath oom_path(GetProcPidDir(process)); |
853 | 853 |
854 // Attempt to write the newer oom_score_adj file first. | 854 // Attempt to write the newer oom_score_adj file first. |
855 FilePath oom_file = oom_path.AppendASCII("oom_score_adj"); | 855 FilePath oom_file = oom_path.AppendASCII("oom_score_adj"); |
856 if (file_util::PathExists(oom_file)) { | 856 if (file_util::PathExists(oom_file)) { |
857 std::string score_str = base::IntToString(score); | 857 std::string score_str = IntToString(score); |
858 DVLOG(1) << "Adjusting oom_score_adj of " << process << " to " | 858 DVLOG(1) << "Adjusting oom_score_adj of " << process << " to " |
859 << score_str; | 859 << score_str; |
860 int score_len = static_cast<int>(score_str.length()); | 860 int score_len = static_cast<int>(score_str.length()); |
861 return (score_len == file_util::WriteFile(oom_file, | 861 return (score_len == file_util::WriteFile(oom_file, |
862 score_str.c_str(), | 862 score_str.c_str(), |
863 score_len)); | 863 score_len)); |
864 } | 864 } |
865 | 865 |
866 // If the oom_score_adj file doesn't exist, then we write the old | 866 // If the oom_score_adj file doesn't exist, then we write the old |
867 // style file and translate the oom_adj score to the range 0-15. | 867 // style file and translate the oom_adj score to the range 0-15. |
868 oom_file = oom_path.AppendASCII("oom_adj"); | 868 oom_file = oom_path.AppendASCII("oom_adj"); |
869 if (file_util::PathExists(oom_file)) { | 869 if (file_util::PathExists(oom_file)) { |
870 // Max score for the old oom_adj range. Used for conversion of new | 870 // Max score for the old oom_adj range. Used for conversion of new |
871 // values to old values. | 871 // values to old values. |
872 const int kMaxOldOomScore = 15; | 872 const int kMaxOldOomScore = 15; |
873 | 873 |
874 int converted_score = score * kMaxOldOomScore / kMaxOomScore; | 874 int converted_score = score * kMaxOldOomScore / kMaxOomScore; |
875 std::string score_str = base::IntToString(converted_score); | 875 std::string score_str = IntToString(converted_score); |
876 DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str; | 876 DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str; |
877 int score_len = static_cast<int>(score_str.length()); | 877 int score_len = static_cast<int>(score_str.length()); |
878 return (score_len == file_util::WriteFile(oom_file, | 878 return (score_len == file_util::WriteFile(oom_file, |
879 score_str.c_str(), | 879 score_str.c_str(), |
880 score_len)); | 880 score_len)); |
881 } | 881 } |
882 | 882 |
883 return false; | 883 return false; |
884 } | 884 } |
885 | 885 |
886 } // namespace base | 886 } // namespace base |
OLD | NEW |