| 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 |