| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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/process_metrics.h" | 5 #include "base/process/process_metrics.h" |
| 6 | 6 |
| 7 #include <dirent.h> | 7 #include <dirent.h> |
| 8 #include <fcntl.h> | 8 #include <fcntl.h> |
| 9 #include <stddef.h> | 9 #include <stddef.h> |
| 10 #include <stdint.h> | 10 #include <stdint.h> |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 #include "base/strings/string_util.h" | 25 #include "base/strings/string_util.h" |
| 26 #include "base/sys_info.h" | 26 #include "base/sys_info.h" |
| 27 #include "base/threading/thread_restrictions.h" | 27 #include "base/threading/thread_restrictions.h" |
| 28 #include "build/build_config.h" | 28 #include "build/build_config.h" |
| 29 | 29 |
| 30 namespace base { | 30 namespace base { |
| 31 | 31 |
| 32 namespace { | 32 namespace { |
| 33 | 33 |
| 34 void TrimKeyValuePairs(StringPairs* pairs) { | 34 void TrimKeyValuePairs(StringPairs* pairs) { |
| 35 DCHECK(pairs); | 35 for (auto& pair : *pairs) { |
| 36 StringPairs& p_ref = *pairs; | 36 TrimWhitespaceASCII(pair.first, TRIM_ALL, &pair.first); |
| 37 for (size_t i = 0; i < p_ref.size(); ++i) { | 37 TrimWhitespaceASCII(pair.second, TRIM_ALL, &pair.second); |
| 38 TrimWhitespaceASCII(p_ref[i].first, TRIM_ALL, &p_ref[i].first); | |
| 39 TrimWhitespaceASCII(p_ref[i].second, TRIM_ALL, &p_ref[i].second); | |
| 40 } | 38 } |
| 41 } | 39 } |
| 42 | 40 |
| 43 #if defined(OS_CHROMEOS) | 41 #if defined(OS_CHROMEOS) |
| 44 // Read a file with a single number string and return the number as a uint64_t. | 42 // Read a file with a single number string and return the number as a uint64_t. |
| 45 static uint64_t ReadFileToUint64(const FilePath file) { | 43 uint64_t ReadFileToUint64(const FilePath& file) { |
| 46 std::string file_as_string; | 44 std::string file_contents; |
| 47 if (!ReadFileToString(file, &file_as_string)) | 45 if (!ReadFileToString(file, &file_contents)) |
| 48 return 0; | 46 return 0; |
| 49 TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string); | 47 TrimWhitespaceASCII(file_contents, TRIM_ALL, &file_contents); |
| 50 uint64_t file_as_uint64 = 0; | 48 uint64_t file_contents_uint64 = 0; |
| 51 if (!StringToUint64(file_as_string, &file_as_uint64)) | 49 if (!StringToUint64(file_contents, &file_contents_uint64)) |
| 52 return 0; | 50 return 0; |
| 53 return file_as_uint64; | 51 return file_contents_uint64; |
| 54 } | 52 } |
| 55 #endif | 53 #endif |
| 56 | 54 |
| 57 // Read /proc/<pid>/status and return the value for |field|, or 0 on failure. | 55 // Read |filename| in /proc/<pid>/, split the entries into key/value pairs, and |
| 58 // Only works for fields in the form of "Field: value kB". | 56 // trim the key and value. On success, return true and write the trimmed |
| 59 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { | 57 // key/value pairs into |key_value_pairs|. |
| 58 bool ReadProcFileToTrimmedStringPairs(pid_t pid, |
| 59 StringPiece filename, |
| 60 StringPairs* key_value_pairs) { |
| 60 std::string status_data; | 61 std::string status_data; |
| 61 { | 62 { |
| 62 // Synchronously reading files in /proc does not hit the disk. | 63 // Synchronously reading files in /proc does not hit the disk. |
| 63 ThreadRestrictions::ScopedAllowIO allow_io; | 64 ThreadRestrictions::ScopedAllowIO allow_io; |
| 64 FilePath status_file = internal::GetProcPidDir(pid).Append("status"); | 65 FilePath status_file = internal::GetProcPidDir(pid).Append(filename); |
| 65 if (!ReadFileToString(status_file, &status_data)) | 66 if (!ReadFileToString(status_file, &status_data)) |
| 67 return false; |
| 68 } |
| 69 SplitStringIntoKeyValuePairs(status_data, ':', '\n', key_value_pairs); |
| 70 TrimKeyValuePairs(key_value_pairs); |
| 71 return true; |
| 72 } |
| 73 |
| 74 // Read /proc/<pid>/status and return the value for |field|, or 0 on failure. |
| 75 // Only works for fields in the form of "Field: value kB". |
| 76 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, StringPiece field) { |
| 77 StringPairs pairs; |
| 78 if (!ReadProcFileToTrimmedStringPairs(pid, "status", &pairs)) |
| 79 return 0; |
| 80 |
| 81 for (const auto& pair : pairs) { |
| 82 const std::string& key = pair.first; |
| 83 const std::string& value_str = pair.second; |
| 84 if (key != field) |
| 85 continue; |
| 86 |
| 87 std::vector<StringPiece> split_value_str = |
| 88 SplitStringPiece(value_str, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL); |
| 89 if (split_value_str.size() != 2 || split_value_str[1] != "kB") { |
| 90 NOTREACHED(); |
| 66 return 0; | 91 return 0; |
| 67 } | |
| 68 | |
| 69 StringPairs pairs; | |
| 70 SplitStringIntoKeyValuePairs(status_data, ':', '\n', &pairs); | |
| 71 TrimKeyValuePairs(&pairs); | |
| 72 for (size_t i = 0; i < pairs.size(); ++i) { | |
| 73 const std::string& key = pairs[i].first; | |
| 74 const std::string& value_str = pairs[i].second; | |
| 75 if (key == field) { | |
| 76 std::vector<StringPiece> split_value_str = | |
| 77 SplitStringPiece(value_str, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL); | |
| 78 if (split_value_str.size() != 2 || split_value_str[1] != "kB") { | |
| 79 NOTREACHED(); | |
| 80 return 0; | |
| 81 } | |
| 82 size_t value; | |
| 83 if (!StringToSizeT(split_value_str[0], &value)) { | |
| 84 NOTREACHED(); | |
| 85 return 0; | |
| 86 } | |
| 87 return value; | |
| 88 } | 92 } |
| 93 size_t value; |
| 94 if (!StringToSizeT(split_value_str[0], &value)) { |
| 95 NOTREACHED(); |
| 96 return 0; |
| 97 } |
| 98 return value; |
| 89 } | 99 } |
| 90 // This can be reached if the process dies when proc is read -- in that case, | 100 // This can be reached if the process dies when proc is read -- in that case, |
| 91 // the kernel can return missing fields. | 101 // the kernel can return missing fields. |
| 92 return 0; | 102 return 0; |
| 93 } | 103 } |
| 94 | 104 |
| 95 #if defined(OS_LINUX) || defined(OS_AIX) | 105 #if defined(OS_LINUX) || defined(OS_AIX) |
| 96 // Read /proc/<pid>/status and look for |field|. On success, return true and | 106 // Read /proc/<pid>/status and look for |field|. On success, return true and |
| 97 // write the value for |field| into |result|. | 107 // write the value for |field| into |result|. |
| 98 // Only works for fields in the form of "field : uint_value" | 108 // Only works for fields in the form of "field : uint_value" |
| 99 bool ReadProcStatusAndGetFieldAsUint64(pid_t pid, | 109 bool ReadProcStatusAndGetFieldAsUint64(pid_t pid, |
| 100 const std::string& field, | 110 StringPiece field, |
| 101 uint64_t* result) { | 111 uint64_t* result) { |
| 102 std::string status_data; | 112 StringPairs pairs; |
| 103 { | 113 if (!ReadProcFileToTrimmedStringPairs(pid, "status", &pairs)) |
| 104 // Synchronously reading files in /proc does not hit the disk. | 114 return false; |
| 105 ThreadRestrictions::ScopedAllowIO allow_io; | 115 |
| 106 FilePath status_file = internal::GetProcPidDir(pid).Append("status"); | 116 for (const auto& pair : pairs) { |
| 107 if (!ReadFileToString(status_file, &status_data)) | 117 const std::string& key = pair.first; |
| 118 const std::string& value_str = pair.second; |
| 119 if (key != field) |
| 120 continue; |
| 121 |
| 122 uint64_t value; |
| 123 if (!StringToUint64(value_str, &value)) |
| 108 return false; | 124 return false; |
| 109 } | 125 *result = value; |
| 110 | 126 return true; |
| 111 StringPairs pairs; | |
| 112 SplitStringIntoKeyValuePairs(status_data, ':', '\n', &pairs); | |
| 113 TrimKeyValuePairs(&pairs); | |
| 114 for (size_t i = 0; i < pairs.size(); ++i) { | |
| 115 const std::string& key = pairs[i].first; | |
| 116 const std::string& value_str = pairs[i].second; | |
| 117 if (key == field) { | |
| 118 uint64_t value; | |
| 119 if (!StringToUint64(value_str, &value)) | |
| 120 return false; | |
| 121 *result = value; | |
| 122 return true; | |
| 123 } | |
| 124 } | 127 } |
| 125 return false; | 128 return false; |
| 126 } | 129 } |
| 127 #endif // defined(OS_LINUX) || defined(OS_AIX) | 130 #endif // defined(OS_LINUX) || defined(OS_AIX) |
| 128 | 131 |
| 129 // Get the total CPU of a single process. Return value is number of jiffies | 132 // Get the total CPU of a single process. Return value is number of jiffies |
| 130 // on success or -1 on error. | 133 // on success or -1 on error. |
| 131 int GetProcessCPU(pid_t pid) { | 134 int GetProcessCPU(pid_t pid) { |
| 132 // Use /proc/<pid>/task to find all threads and parse their /stat file. | 135 // Use /proc/<pid>/task to find all threads and parse their /stat file. |
| 133 FilePath task_path = internal::GetProcPidDir(pid).Append("task"); | 136 FilePath task_path = internal::GetProcPidDir(pid).Append("task"); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 162 } | 165 } |
| 163 | 166 |
| 164 } // namespace | 167 } // namespace |
| 165 | 168 |
| 166 // static | 169 // static |
| 167 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics( | 170 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics( |
| 168 ProcessHandle process) { | 171 ProcessHandle process) { |
| 169 return WrapUnique(new ProcessMetrics(process)); | 172 return WrapUnique(new ProcessMetrics(process)); |
| 170 } | 173 } |
| 171 | 174 |
| 172 // On linux, we return vsize. | 175 // On Linux, return vsize. |
| 173 size_t ProcessMetrics::GetPagefileUsage() const { | 176 size_t ProcessMetrics::GetPagefileUsage() const { |
| 174 return internal::ReadProcStatsAndGetFieldAsSizeT(process_, | 177 return internal::ReadProcStatsAndGetFieldAsSizeT(process_, |
| 175 internal::VM_VSIZE); | 178 internal::VM_VSIZE); |
| 176 } | 179 } |
| 177 | 180 |
| 178 // On linux, we return the high water mark of vsize. | 181 // On Linux, return the high water mark of vsize. |
| 179 size_t ProcessMetrics::GetPeakPagefileUsage() const { | 182 size_t ProcessMetrics::GetPeakPagefileUsage() const { |
| 180 return ReadProcStatusAndGetFieldAsSizeT(process_, "VmPeak") * 1024; | 183 return ReadProcStatusAndGetFieldAsSizeT(process_, "VmPeak") * 1024; |
| 181 } | 184 } |
| 182 | 185 |
| 183 // On linux, we return RSS. | 186 // On Linux, return RSS. |
| 184 size_t ProcessMetrics::GetWorkingSetSize() const { | 187 size_t ProcessMetrics::GetWorkingSetSize() const { |
| 185 return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) * | 188 return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) * |
| 186 getpagesize(); | 189 getpagesize(); |
| 187 } | 190 } |
| 188 | 191 |
| 189 // On linux, we return the high water mark of RSS. | 192 // On Linux, return the high water mark of RSS. |
| 190 size_t ProcessMetrics::GetPeakWorkingSetSize() const { | 193 size_t ProcessMetrics::GetPeakWorkingSetSize() const { |
| 191 return ReadProcStatusAndGetFieldAsSizeT(process_, "VmHWM") * 1024; | 194 return ReadProcStatusAndGetFieldAsSizeT(process_, "VmHWM") * 1024; |
| 192 } | 195 } |
| 193 | 196 |
| 194 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, | 197 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, |
| 195 size_t* shared_bytes) const { | 198 size_t* shared_bytes) const { |
| 196 WorkingSetKBytes ws_usage; | 199 WorkingSetKBytes ws_usage; |
| 197 if (!GetWorkingSetKBytes(&ws_usage)) | 200 if (!GetWorkingSetKBytes(&ws_usage)) |
| 198 return false; | 201 return false; |
| 199 | 202 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 225 } | 228 } |
| 226 | 229 |
| 227 TimeDelta time_delta = time - last_cpu_time_; | 230 TimeDelta time_delta = time - last_cpu_time_; |
| 228 if (time_delta.is_zero()) { | 231 if (time_delta.is_zero()) { |
| 229 NOTREACHED(); | 232 NOTREACHED(); |
| 230 return 0.0; | 233 return 0.0; |
| 231 } | 234 } |
| 232 | 235 |
| 233 int cpu = GetProcessCPU(process_); | 236 int cpu = GetProcessCPU(process_); |
| 234 | 237 |
| 235 // We have the number of jiffies in the time period. Convert to percentage. | 238 // The number of jiffies in the time period. Convert to percentage. |
| 236 // Note this means we will go *over* 100 in the case where multiple threads | 239 // Note: this means this will go *over* 100 in the case where multiple threads |
| 237 // are together adding to more than one CPU's worth. | 240 // are together adding to more than one CPU's worth. |
| 238 TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu); | 241 TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu); |
| 239 TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_); | 242 TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_); |
| 240 | 243 |
| 241 // If the number of threads running in the process has decreased since the | 244 // If the number of threads running in the process has decreased since the |
| 242 // last time this function was called, |last_cpu_time| will be greater than | 245 // last time this function was called, |last_cpu_time| will be greater than |
| 243 // |cpu_time| which will result in a negative value in the below percentage | 246 // |cpu_time| which will result in a negative value in the below percentage |
| 244 // calculation. We prevent this by clamping to 0. crbug.com/546565. | 247 // calculation. Prevent this by clamping to 0. https://crbug.com/546565. |
| 245 // This computation is known to be shaky when threads are destroyed between | 248 // This computation is known to be shaky when threads are destroyed between |
| 246 // "last" and "now", but for our current purposes, it's all right. | 249 // "last" and "now", but for our current purposes, it's all right. |
| 247 double percentage = 0.0; | 250 double percentage = 0.0; |
| 248 if (last_cpu_time < cpu_time) { | 251 if (last_cpu_time < cpu_time) { |
| 249 percentage = 100.0 * (cpu_time - last_cpu_time).InSecondsF() / | 252 percentage = 100.0 * (cpu_time - last_cpu_time).InSecondsF() / |
| 250 time_delta.InSecondsF(); | 253 time_delta.InSecondsF(); |
| 251 } | 254 } |
| 252 | 255 |
| 253 last_cpu_time_ = time; | 256 last_cpu_time_ = time; |
| 254 last_cpu_ = cpu; | 257 last_cpu_ = cpu; |
| 255 | 258 |
| 256 return percentage; | 259 return percentage; |
| 257 } | 260 } |
| 258 | 261 |
| 259 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING | 262 // For the /proc/self/io file to exist, the Linux kernel must have |
| 260 // in your kernel configuration. | 263 // CONFIG_TASK_IO_ACCOUNTING enabled. |
| 261 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { | 264 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { |
| 262 // Synchronously reading files in /proc does not hit the disk. | 265 StringPairs pairs; |
| 263 ThreadRestrictions::ScopedAllowIO allow_io; | 266 if (!ReadProcFileToTrimmedStringPairs(process_, "io", &pairs)) |
| 264 | |
| 265 std::string proc_io_contents; | |
| 266 FilePath io_file = internal::GetProcPidDir(process_).Append("io"); | |
| 267 if (!ReadFileToString(io_file, &proc_io_contents)) | |
| 268 return false; | 267 return false; |
| 269 | 268 |
| 270 io_counters->OtherOperationCount = 0; | 269 io_counters->OtherOperationCount = 0; |
| 271 io_counters->OtherTransferCount = 0; | 270 io_counters->OtherTransferCount = 0; |
| 272 | 271 |
| 273 StringPairs pairs; | 272 for (const auto& pair : pairs) { |
| 274 SplitStringIntoKeyValuePairs(proc_io_contents, ':', '\n', &pairs); | 273 const std::string& key = pair.first; |
| 275 TrimKeyValuePairs(&pairs); | 274 const std::string& value_str = pair.second; |
| 276 for (size_t i = 0; i < pairs.size(); ++i) { | 275 uint64_t* target_counter = nullptr; |
| 277 const std::string& key = pairs[i].first; | |
| 278 const std::string& value_str = pairs[i].second; | |
| 279 uint64_t* target_counter = NULL; | |
| 280 if (key == "syscr") | 276 if (key == "syscr") |
| 281 target_counter = &io_counters->ReadOperationCount; | 277 target_counter = &io_counters->ReadOperationCount; |
| 282 else if (key == "syscw") | 278 else if (key == "syscw") |
| 283 target_counter = &io_counters->WriteOperationCount; | 279 target_counter = &io_counters->WriteOperationCount; |
| 284 else if (key == "rchar") | 280 else if (key == "rchar") |
| 285 target_counter = &io_counters->ReadTransferCount; | 281 target_counter = &io_counters->ReadTransferCount; |
| 286 else if (key == "wchar") | 282 else if (key == "wchar") |
| 287 target_counter = &io_counters->WriteTransferCount; | 283 target_counter = &io_counters->WriteTransferCount; |
| 288 if (!target_counter) | 284 if (!target_counter) |
| 289 continue; | 285 continue; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 313 } | 309 } |
| 314 | 310 |
| 315 int ProcessMetrics::GetOpenFdSoftLimit() const { | 311 int ProcessMetrics::GetOpenFdSoftLimit() const { |
| 316 // Use /proc/<pid>/limits to read the open fd limit. | 312 // Use /proc/<pid>/limits to read the open fd limit. |
| 317 FilePath fd_path = internal::GetProcPidDir(process_).Append("limits"); | 313 FilePath fd_path = internal::GetProcPidDir(process_).Append("limits"); |
| 318 | 314 |
| 319 std::string limits_contents; | 315 std::string limits_contents; |
| 320 if (!ReadFileToString(fd_path, &limits_contents)) | 316 if (!ReadFileToString(fd_path, &limits_contents)) |
| 321 return -1; | 317 return -1; |
| 322 | 318 |
| 323 for (const auto& line : | 319 for (const auto& line : SplitStringPiece( |
| 324 base::SplitStringPiece(limits_contents, "\n", base::KEEP_WHITESPACE, | 320 limits_contents, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { |
| 325 base::SPLIT_WANT_NONEMPTY)) { | 321 if (!line.starts_with("Max open files")) |
| 326 if (line.starts_with("Max open files")) { | 322 continue; |
| 327 auto tokens = base::SplitStringPiece(line, " ", base::TRIM_WHITESPACE, | 323 |
| 328 base::SPLIT_WANT_NONEMPTY); | 324 auto tokens = |
| 329 if (tokens.size() > 3) { | 325 SplitStringPiece(line, " ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); |
| 330 int limit = -1; | 326 if (tokens.size() > 3) { |
| 331 if (StringToInt(tokens[3], &limit)) | 327 int limit = -1; |
| 332 return limit; | 328 if (!StringToInt(tokens[3], &limit)) |
| 333 return -1; | 329 return -1; |
| 334 } | 330 return limit; |
| 335 } | 331 } |
| 336 } | 332 } |
| 337 return -1; | 333 return -1; |
| 338 } | 334 } |
| 339 | |
| 340 #endif // defined(OS_LINUX) || defined(OS_AIX) | 335 #endif // defined(OS_LINUX) || defined(OS_AIX) |
| 341 | 336 |
| 342 ProcessMetrics::ProcessMetrics(ProcessHandle process) | 337 ProcessMetrics::ProcessMetrics(ProcessHandle process) |
| 343 : process_(process), | 338 : process_(process), |
| 344 last_system_time_(0), | 339 last_system_time_(0), |
| 345 #if defined(OS_LINUX) || defined(OS_AIX) | 340 #if defined(OS_LINUX) || defined(OS_AIX) |
| 346 last_absolute_idle_wakeups_(0), | 341 last_absolute_idle_wakeups_(0), |
| 347 #endif | 342 #endif |
| 348 last_cpu_(0) { | 343 last_cpu_(0) { |
| 349 processor_count_ = SysInfo::NumberOfProcessors(); | 344 processor_count_ = SysInfo::NumberOfProcessors(); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 392 int pss = 0; | 387 int pss = 0; |
| 393 int private_clean = 0; | 388 int private_clean = 0; |
| 394 int private_dirty = 0; | 389 int private_dirty = 0; |
| 395 int swap = 0; | 390 int swap = 0; |
| 396 bool ret = true; | 391 bool ret = true; |
| 397 ret &= StringToInt(totmaps_fields[kPssIndex], &pss); | 392 ret &= StringToInt(totmaps_fields[kPssIndex], &pss); |
| 398 ret &= StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean); | 393 ret &= StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean); |
| 399 ret &= StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty); | 394 ret &= StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty); |
| 400 ret &= StringToInt(totmaps_fields[kSwapIndex], &swap); | 395 ret &= StringToInt(totmaps_fields[kSwapIndex], &swap); |
| 401 | 396 |
| 402 // On ChromeOS swap is to zram. We count this as private / shared, as | 397 // On ChromeOS, swap goes to zram. Count this as private / shared, as |
| 403 // increased swap decreases available RAM to user processes, which would | 398 // increased swap decreases available RAM to user processes, which would |
| 404 // otherwise create surprising results. | 399 // otherwise create surprising results. |
| 405 ws_usage->priv = private_clean + private_dirty + swap; | 400 ws_usage->priv = private_clean + private_dirty + swap; |
| 406 ws_usage->shared = pss + swap; | 401 ws_usage->shared = pss + swap; |
| 407 ws_usage->shareable = 0; | 402 ws_usage->shareable = 0; |
| 408 ws_usage->swapped = swap; | 403 ws_usage->swapped = swap; |
| 409 return ret; | 404 return ret; |
| 410 } | 405 } |
| 411 #endif | 406 #endif |
| 412 | 407 |
| 413 // Private and Shared working set sizes are obtained from /proc/<pid>/statm. | 408 // Private and Shared working set sizes are obtained from /proc/<pid>/statm. |
| 414 bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) | 409 bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) |
| 415 const { | 410 const { |
| 416 // Use statm instead of smaps because smaps is: | 411 // Use statm instead of smaps because smaps is: |
| 417 // a) Large and slow to parse. | 412 // a) Large and slow to parse. |
| 418 // b) Unavailable in the SUID sandbox. | 413 // b) Unavailable in the SUID sandbox. |
| 419 | 414 |
| 420 // First we need to get the page size, since everything is measured in pages. | 415 // First get the page size, since everything is measured in pages. |
| 421 // For details, see: man 5 proc. | 416 // For details, see: man 5 proc. |
| 422 const int page_size_kb = getpagesize() / 1024; | 417 const int page_size_kb = getpagesize() / 1024; |
| 423 if (page_size_kb <= 0) | 418 if (page_size_kb <= 0) |
| 424 return false; | 419 return false; |
| 425 | 420 |
| 426 std::string statm; | 421 std::string statm; |
| 427 { | 422 { |
| 428 FilePath statm_file = internal::GetProcPidDir(process_).Append("statm"); | 423 FilePath statm_file = internal::GetProcPidDir(process_).Append("statm"); |
| 429 // Synchronously reading files in /proc does not hit the disk. | 424 // Synchronously reading files in /proc does not hit the disk. |
| 430 ThreadRestrictions::ScopedAllowIO allow_io; | 425 ThreadRestrictions::ScopedAllowIO allow_io; |
| 431 bool ret = ReadFileToString(statm_file, &statm); | 426 bool ret = ReadFileToString(statm_file, &statm); |
| 432 if (!ret || statm.length() == 0) | 427 if (!ret || statm.length() == 0) |
| 433 return false; | 428 return false; |
| 434 } | 429 } |
| 435 | 430 |
| 436 std::vector<StringPiece> statm_vec = | 431 std::vector<StringPiece> statm_vec = |
| 437 SplitStringPiece(statm, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL); | 432 SplitStringPiece(statm, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL); |
| 438 if (statm_vec.size() != 7) | 433 if (statm_vec.size() != 7) |
| 439 return false; // Not the format we expect. | 434 return false; // Not the expected format. |
| 440 | 435 |
| 441 int statm_rss, statm_shared; | 436 int statm_rss; |
| 437 int statm_shared; |
| 442 bool ret = true; | 438 bool ret = true; |
| 443 ret &= StringToInt(statm_vec[1], &statm_rss); | 439 ret &= StringToInt(statm_vec[1], &statm_rss); |
| 444 ret &= StringToInt(statm_vec[2], &statm_shared); | 440 ret &= StringToInt(statm_vec[2], &statm_shared); |
| 445 | 441 |
| 446 ws_usage->priv = (statm_rss - statm_shared) * page_size_kb; | 442 ws_usage->priv = (statm_rss - statm_shared) * page_size_kb; |
| 447 ws_usage->shared = statm_shared * page_size_kb; | 443 ws_usage->shared = statm_shared * page_size_kb; |
| 448 | 444 |
| 449 // Sharable is not calculated, as it does not provide interesting data. | 445 // Sharable is not calculated, as it does not provide interesting data. |
| 450 ws_usage->shareable = 0; | 446 ws_usage->shareable = 0; |
| 451 | 447 |
| 452 #if defined(OS_CHROMEOS) | 448 #if defined(OS_CHROMEOS) |
| 453 // Can't get swapped memory from statm. | 449 // Can't get swapped memory from statm. |
| 454 ws_usage->swapped = 0; | 450 ws_usage->swapped = 0; |
| 455 #endif | 451 #endif |
| 456 | 452 |
| 457 return ret; | 453 return ret; |
| 458 } | 454 } |
| 459 | 455 |
| 460 size_t GetSystemCommitCharge() { | 456 size_t GetSystemCommitCharge() { |
| 461 SystemMemoryInfoKB meminfo; | 457 SystemMemoryInfoKB meminfo; |
| 462 if (!GetSystemMemoryInfo(&meminfo)) | 458 if (!GetSystemMemoryInfo(&meminfo)) |
| 463 return 0; | 459 return 0; |
| 464 return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached; | 460 return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached; |
| 465 } | 461 } |
| 466 | 462 |
| 467 int ParseProcStatCPU(const std::string& input) { | 463 int ParseProcStatCPU(StringPiece input) { |
| 468 // |input| may be empty if the process disappeared somehow. | 464 // |input| may be empty if the process disappeared somehow. |
| 469 // e.g. http://crbug.com/145811. | 465 // e.g. http://crbug.com/145811. |
| 470 if (input.empty()) | 466 if (input.empty()) |
| 471 return -1; | 467 return -1; |
| 472 | 468 |
| 473 size_t start = input.find_last_of(')'); | 469 size_t start = input.find_last_of(')'); |
| 474 if (start == input.npos) | 470 if (start == input.npos) |
| 475 return -1; | 471 return -1; |
| 476 | 472 |
| 477 // Number of spaces remaining until reaching utime's index starting after the | 473 // Number of spaces remaining until reaching utime's index starting after the |
| (...skipping 11 matching lines...) Expand all Loading... |
| 489 if (sscanf(&input.data()[i], "%d %d", &utime, &stime) != 2) | 485 if (sscanf(&input.data()[i], "%d %d", &utime, &stime) != 2) |
| 490 return -1; | 486 return -1; |
| 491 | 487 |
| 492 return utime + stime; | 488 return utime + stime; |
| 493 } | 489 } |
| 494 } | 490 } |
| 495 | 491 |
| 496 return -1; | 492 return -1; |
| 497 } | 493 } |
| 498 | 494 |
| 499 const char kProcSelfExe[] = "/proc/self/exe"; | |
| 500 | |
| 501 int GetNumberOfThreads(ProcessHandle process) { | 495 int GetNumberOfThreads(ProcessHandle process) { |
| 502 return internal::ReadProcStatsAndGetFieldAsInt64(process, | 496 return internal::ReadProcStatsAndGetFieldAsInt64(process, |
| 503 internal::VM_NUMTHREADS); | 497 internal::VM_NUMTHREADS); |
| 504 } | 498 } |
| 505 | 499 |
| 500 const char kProcSelfExe[] = "/proc/self/exe"; |
| 501 |
| 506 namespace { | 502 namespace { |
| 507 | 503 |
| 508 // The format of /proc/diskstats is: | 504 // The format of /proc/diskstats is: |
| 509 // Device major number | 505 // Device major number |
| 510 // Device minor number | 506 // Device minor number |
| 511 // Device name | 507 // Device name |
| 512 // Field 1 -- # of reads completed | 508 // Field 1 -- # of reads completed |
| 513 // This is the total number of reads completed successfully. | 509 // This is the total number of reads completed successfully. |
| 514 // Field 2 -- # of reads merged, field 6 -- # of writes merged | 510 // Field 2 -- # of reads merged, field 6 -- # of writes merged |
| 515 // Reads and writes which are adjacent to each other may be merged for | 511 // Reads and writes which are adjacent to each other may be merged for |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 552 const size_t kDiskWritesMerged = 8; | 548 const size_t kDiskWritesMerged = 8; |
| 553 const size_t kDiskSectorsWritten = 9; | 549 const size_t kDiskSectorsWritten = 9; |
| 554 const size_t kDiskWriteTime = 10; | 550 const size_t kDiskWriteTime = 10; |
| 555 const size_t kDiskIO = 11; | 551 const size_t kDiskIO = 11; |
| 556 const size_t kDiskIOTime = 12; | 552 const size_t kDiskIOTime = 12; |
| 557 const size_t kDiskWeightedIOTime = 13; | 553 const size_t kDiskWeightedIOTime = 13; |
| 558 | 554 |
| 559 } // namespace | 555 } // namespace |
| 560 | 556 |
| 561 std::unique_ptr<Value> SystemMemoryInfoKB::ToValue() const { | 557 std::unique_ptr<Value> SystemMemoryInfoKB::ToValue() const { |
| 562 std::unique_ptr<DictionaryValue> res(new DictionaryValue()); | 558 auto res = base::MakeUnique<DictionaryValue>(); |
| 563 | |
| 564 res->SetInteger("total", total); | 559 res->SetInteger("total", total); |
| 565 res->SetInteger("free", free); | 560 res->SetInteger("free", free); |
| 566 res->SetInteger("available", available); | 561 res->SetInteger("available", available); |
| 567 res->SetInteger("buffers", buffers); | 562 res->SetInteger("buffers", buffers); |
| 568 res->SetInteger("cached", cached); | 563 res->SetInteger("cached", cached); |
| 569 res->SetInteger("active_anon", active_anon); | 564 res->SetInteger("active_anon", active_anon); |
| 570 res->SetInteger("inactive_anon", inactive_anon); | 565 res->SetInteger("inactive_anon", inactive_anon); |
| 571 res->SetInteger("active_file", active_file); | 566 res->SetInteger("active_file", active_file); |
| 572 res->SetInteger("inactive_file", inactive_file); | 567 res->SetInteger("inactive_file", inactive_file); |
| 573 res->SetInteger("swap_total", swap_total); | 568 res->SetInteger("swap_total", swap_total); |
| 574 res->SetInteger("swap_free", swap_free); | 569 res->SetInteger("swap_free", swap_free); |
| 575 res->SetInteger("swap_used", swap_total - swap_free); | 570 res->SetInteger("swap_used", swap_total - swap_free); |
| 576 res->SetInteger("dirty", dirty); | 571 res->SetInteger("dirty", dirty); |
| 577 res->SetInteger("reclaimable", reclaimable); | 572 res->SetInteger("reclaimable", reclaimable); |
| 578 res->SetInteger("pswpin", pswpin); | 573 res->SetInteger("pswpin", pswpin); |
| 579 res->SetInteger("pswpout", pswpout); | 574 res->SetInteger("pswpout", pswpout); |
| 580 res->SetInteger("pgmajfault", pgmajfault); | 575 res->SetInteger("pgmajfault", pgmajfault); |
| 581 #ifdef OS_CHROMEOS | 576 #ifdef OS_CHROMEOS |
| 582 res->SetInteger("shmem", shmem); | 577 res->SetInteger("shmem", shmem); |
| 583 res->SetInteger("slab", slab); | 578 res->SetInteger("slab", slab); |
| 584 res->SetInteger("gem_objects", gem_objects); | 579 res->SetInteger("gem_objects", gem_objects); |
| 585 res->SetInteger("gem_size", gem_size); | 580 res->SetInteger("gem_size", gem_size); |
| 586 #endif | 581 #endif |
| 587 | 582 |
| 588 return std::move(res); | 583 return std::move(res); |
| 589 } | 584 } |
| 590 | 585 |
| 591 // exposed for testing | 586 bool ParseProcMeminfo(StringPiece meminfo_data, SystemMemoryInfoKB* meminfo) { |
| 592 bool ParseProcMeminfo(const std::string& meminfo_data, | |
| 593 SystemMemoryInfoKB* meminfo) { | |
| 594 // The format of /proc/meminfo is: | 587 // The format of /proc/meminfo is: |
| 595 // | 588 // |
| 596 // MemTotal: 8235324 kB | 589 // MemTotal: 8235324 kB |
| 597 // MemFree: 1628304 kB | 590 // MemFree: 1628304 kB |
| 598 // Buffers: 429596 kB | 591 // Buffers: 429596 kB |
| 599 // Cached: 4728232 kB | 592 // Cached: 4728232 kB |
| 600 // ... | 593 // ... |
| 601 // There is no guarantee on the ordering or position | 594 // There is no guarantee on the ordering or position |
| 602 // though it doesn't appear to change very often | 595 // though it doesn't appear to change very often |
| 603 | 596 |
| 604 // As a basic sanity check, let's make sure we at least get non-zero | 597 // As a basic sanity check at the end, make sure the MemTotal value will be at |
| 605 // MemTotal value | 598 // least non-zero. So start off with a zero total. |
| 606 meminfo->total = 0; | 599 meminfo->total = 0; |
| 607 | 600 |
| 608 for (const StringPiece& line : SplitStringPiece( | 601 for (const StringPiece& line : SplitStringPiece( |
| 609 meminfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { | 602 meminfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { |
| 610 std::vector<StringPiece> tokens = SplitStringPiece( | 603 std::vector<StringPiece> tokens = SplitStringPiece( |
| 611 line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); | 604 line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); |
| 612 // HugePages_* only has a number and no suffix so we can't rely on | 605 // HugePages_* only has a number and no suffix so there may not be exactly 3 |
| 613 // there being exactly 3 tokens. | 606 // tokens. |
| 614 if (tokens.size() <= 1) { | 607 if (tokens.size() <= 1) { |
| 615 DLOG(WARNING) << "meminfo: tokens: " << tokens.size() | 608 DLOG(WARNING) << "meminfo: tokens: " << tokens.size() |
| 616 << " malformed line: " << line.as_string(); | 609 << " malformed line: " << line.as_string(); |
| 617 continue; | 610 continue; |
| 618 } | 611 } |
| 619 | 612 |
| 620 int* target = NULL; | 613 int* target = nullptr; |
| 621 if (tokens[0] == "MemTotal:") | 614 if (tokens[0] == "MemTotal:") |
| 622 target = &meminfo->total; | 615 target = &meminfo->total; |
| 623 else if (tokens[0] == "MemFree:") | 616 else if (tokens[0] == "MemFree:") |
| 624 target = &meminfo->free; | 617 target = &meminfo->free; |
| 625 else if (tokens[0] == "MemAvailable:") | 618 else if (tokens[0] == "MemAvailable:") |
| 626 target = &meminfo->available; | 619 target = &meminfo->available; |
| 627 else if (tokens[0] == "Buffers:") | 620 else if (tokens[0] == "Buffers:") |
| 628 target = &meminfo->buffers; | 621 target = &meminfo->buffers; |
| 629 else if (tokens[0] == "Cached:") | 622 else if (tokens[0] == "Cached:") |
| 630 target = &meminfo->cached; | 623 target = &meminfo->cached; |
| 631 else if (tokens[0] == "Active(anon):") | 624 else if (tokens[0] == "Active(anon):") |
| 632 target = &meminfo->active_anon; | 625 target = &meminfo->active_anon; |
| 633 else if (tokens[0] == "Inactive(anon):") | 626 else if (tokens[0] == "Inactive(anon):") |
| 634 target = &meminfo->inactive_anon; | 627 target = &meminfo->inactive_anon; |
| 635 else if (tokens[0] == "Active(file):") | 628 else if (tokens[0] == "Active(file):") |
| 636 target = &meminfo->active_file; | 629 target = &meminfo->active_file; |
| 637 else if (tokens[0] == "Inactive(file):") | 630 else if (tokens[0] == "Inactive(file):") |
| 638 target = &meminfo->inactive_file; | 631 target = &meminfo->inactive_file; |
| 639 else if (tokens[0] == "SwapTotal:") | 632 else if (tokens[0] == "SwapTotal:") |
| 640 target = &meminfo->swap_total; | 633 target = &meminfo->swap_total; |
| 641 else if (tokens[0] == "SwapFree:") | 634 else if (tokens[0] == "SwapFree:") |
| 642 target = &meminfo->swap_free; | 635 target = &meminfo->swap_free; |
| 643 else if (tokens[0] == "Dirty:") | 636 else if (tokens[0] == "Dirty:") |
| 644 target = &meminfo->dirty; | 637 target = &meminfo->dirty; |
| 645 else if (tokens[0] == "SReclaimable:") | 638 else if (tokens[0] == "SReclaimable:") |
| 646 target = &meminfo->reclaimable; | 639 target = &meminfo->reclaimable; |
| 647 #if defined(OS_CHROMEOS) | 640 #if defined(OS_CHROMEOS) |
| 648 // Chrome OS has a tweaked kernel that allows us to query Shmem, which is | 641 // Chrome OS has a tweaked kernel that allows querying Shmem, which is |
| 649 // usually video memory otherwise invisible to the OS. | 642 // usually video memory otherwise invisible to the OS. |
| 650 else if (tokens[0] == "Shmem:") | 643 else if (tokens[0] == "Shmem:") |
| 651 target = &meminfo->shmem; | 644 target = &meminfo->shmem; |
| 652 else if (tokens[0] == "Slab:") | 645 else if (tokens[0] == "Slab:") |
| 653 target = &meminfo->slab; | 646 target = &meminfo->slab; |
| 654 #endif | 647 #endif |
| 655 if (target) | 648 if (target) |
| 656 StringToInt(tokens[1], target); | 649 StringToInt(tokens[1], target); |
| 657 } | 650 } |
| 658 | 651 |
| 659 // Make sure we got a valid MemTotal. | 652 // Make sure the MemTotal is valid. |
| 660 return meminfo->total > 0; | 653 return meminfo->total > 0; |
| 661 } | 654 } |
| 662 | 655 |
| 663 // exposed for testing | 656 bool ParseProcVmstat(StringPiece vmstat_data, SystemMemoryInfoKB* meminfo) { |
| 664 bool ParseProcVmstat(const std::string& vmstat_data, | |
| 665 SystemMemoryInfoKB* meminfo) { | |
| 666 // The format of /proc/vmstat is: | 657 // The format of /proc/vmstat is: |
| 667 // | 658 // |
| 668 // nr_free_pages 299878 | 659 // nr_free_pages 299878 |
| 669 // nr_inactive_anon 239863 | 660 // nr_inactive_anon 239863 |
| 670 // nr_active_anon 1318966 | 661 // nr_active_anon 1318966 |
| 671 // nr_inactive_file 2015629 | 662 // nr_inactive_file 2015629 |
| 672 // ... | 663 // ... |
| 673 // | 664 // |
| 674 // We iterate through the whole file because the position of the | 665 // Iterate through the whole file because the position of the |
| 675 // fields are dependent on the kernel version and configuration. | 666 // fields are dependent on the kernel version and configuration. |
| 676 | |
| 677 for (const StringPiece& line : SplitStringPiece( | 667 for (const StringPiece& line : SplitStringPiece( |
| 678 vmstat_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { | 668 vmstat_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { |
| 679 std::vector<StringPiece> tokens = SplitStringPiece( | 669 std::vector<StringPiece> tokens = SplitStringPiece( |
| 680 line, " ", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); | 670 line, " ", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); |
| 681 if (tokens.size() != 2) | 671 if (tokens.size() != 2) |
| 682 continue; | 672 continue; |
| 683 | 673 |
| 684 uint64_t val; | 674 uint64_t val; |
| 685 if (!StringToUint64(tokens[1], &val)) | 675 if (!StringToUint64(tokens[1], &val)) |
| 686 continue; | 676 continue; |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 775 sectors_written = 0; | 765 sectors_written = 0; |
| 776 write_time = 0; | 766 write_time = 0; |
| 777 io = 0; | 767 io = 0; |
| 778 io_time = 0; | 768 io_time = 0; |
| 779 weighted_io_time = 0; | 769 weighted_io_time = 0; |
| 780 } | 770 } |
| 781 | 771 |
| 782 SystemDiskInfo::SystemDiskInfo(const SystemDiskInfo& other) = default; | 772 SystemDiskInfo::SystemDiskInfo(const SystemDiskInfo& other) = default; |
| 783 | 773 |
| 784 std::unique_ptr<Value> SystemDiskInfo::ToValue() const { | 774 std::unique_ptr<Value> SystemDiskInfo::ToValue() const { |
| 785 std::unique_ptr<DictionaryValue> res(new DictionaryValue()); | 775 auto res = base::MakeUnique<DictionaryValue>(); |
| 786 | 776 |
| 787 // Write out uint64_t variables as doubles. | 777 // Write out uint64_t variables as doubles. |
| 788 // Note: this may discard some precision, but for JS there's no other option. | 778 // Note: this may discard some precision, but for JS there's no other option. |
| 789 res->SetDouble("reads", static_cast<double>(reads)); | 779 res->SetDouble("reads", static_cast<double>(reads)); |
| 790 res->SetDouble("reads_merged", static_cast<double>(reads_merged)); | 780 res->SetDouble("reads_merged", static_cast<double>(reads_merged)); |
| 791 res->SetDouble("sectors_read", static_cast<double>(sectors_read)); | 781 res->SetDouble("sectors_read", static_cast<double>(sectors_read)); |
| 792 res->SetDouble("read_time", static_cast<double>(read_time)); | 782 res->SetDouble("read_time", static_cast<double>(read_time)); |
| 793 res->SetDouble("writes", static_cast<double>(writes)); | 783 res->SetDouble("writes", static_cast<double>(writes)); |
| 794 res->SetDouble("writes_merged", static_cast<double>(writes_merged)); | 784 res->SetDouble("writes_merged", static_cast<double>(writes_merged)); |
| 795 res->SetDouble("sectors_written", static_cast<double>(sectors_written)); | 785 res->SetDouble("sectors_written", static_cast<double>(sectors_written)); |
| 796 res->SetDouble("write_time", static_cast<double>(write_time)); | 786 res->SetDouble("write_time", static_cast<double>(write_time)); |
| 797 res->SetDouble("io", static_cast<double>(io)); | 787 res->SetDouble("io", static_cast<double>(io)); |
| 798 res->SetDouble("io_time", static_cast<double>(io_time)); | 788 res->SetDouble("io_time", static_cast<double>(io_time)); |
| 799 res->SetDouble("weighted_io_time", static_cast<double>(weighted_io_time)); | 789 res->SetDouble("weighted_io_time", static_cast<double>(weighted_io_time)); |
| 800 | 790 |
| 801 return std::move(res); | 791 return std::move(res); |
| 802 } | 792 } |
| 803 | 793 |
| 804 bool IsValidDiskName(const std::string& candidate) { | 794 bool IsValidDiskName(StringPiece candidate) { |
| 805 if (candidate.length() < 3) | 795 if (candidate.length() < 3) |
| 806 return false; | 796 return false; |
| 797 |
| 807 if (candidate[1] == 'd' && | 798 if (candidate[1] == 'd' && |
| 808 (candidate[0] == 'h' || candidate[0] == 's' || candidate[0] == 'v')) { | 799 (candidate[0] == 'h' || candidate[0] == 's' || candidate[0] == 'v')) { |
| 809 // [hsv]d[a-z]+ case | 800 // [hsv]d[a-z]+ case |
| 810 for (size_t i = 2; i < candidate.length(); ++i) { | 801 for (size_t i = 2; i < candidate.length(); ++i) { |
| 811 if (!islower(candidate[i])) | 802 if (!islower(candidate[i])) |
| 812 return false; | 803 return false; |
| 813 } | 804 } |
| 814 return true; | 805 return true; |
| 815 } | 806 } |
| 816 | 807 |
| 817 const char kMMCName[] = "mmcblk"; | 808 const char kMMCName[] = "mmcblk"; |
| 818 const size_t kMMCNameLen = strlen(kMMCName); | 809 if (!candidate.starts_with(kMMCName)) |
| 819 if (candidate.length() < kMMCNameLen + 1) | |
| 820 return false; | |
| 821 if (candidate.compare(0, kMMCNameLen, kMMCName) != 0) | |
| 822 return false; | 810 return false; |
| 823 | 811 |
| 824 // mmcblk[0-9]+ case | 812 // mmcblk[0-9]+ case |
| 825 for (size_t i = kMMCNameLen; i < candidate.length(); ++i) { | 813 for (size_t i = strlen(kMMCName); i < candidate.length(); ++i) { |
| 826 if (!isdigit(candidate[i])) | 814 if (!isdigit(candidate[i])) |
| 827 return false; | 815 return false; |
| 828 } | 816 } |
| 829 return true; | 817 return true; |
| 830 } | 818 } |
| 831 | 819 |
| 832 bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { | 820 bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { |
| 833 // Synchronously reading files in /proc does not hit the disk. | 821 // Synchronously reading files in /proc does not hit the disk. |
| 834 ThreadRestrictions::ScopedAllowIO allow_io; | 822 ThreadRestrictions::ScopedAllowIO allow_io; |
| 835 | 823 |
| 836 FilePath diskinfo_file("/proc/diskstats"); | 824 FilePath diskinfo_file("/proc/diskstats"); |
| 837 std::string diskinfo_data; | 825 std::string diskinfo_data; |
| 838 if (!ReadFileToString(diskinfo_file, &diskinfo_data)) { | 826 if (!ReadFileToString(diskinfo_file, &diskinfo_data)) { |
| 839 DLOG(WARNING) << "Failed to open " << diskinfo_file.value(); | 827 DLOG(WARNING) << "Failed to open " << diskinfo_file.value(); |
| 840 return false; | 828 return false; |
| 841 } | 829 } |
| 842 | 830 |
| 843 std::vector<StringPiece> diskinfo_lines = SplitStringPiece( | 831 std::vector<StringPiece> diskinfo_lines = SplitStringPiece( |
| 844 diskinfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); | 832 diskinfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); |
| 845 if (diskinfo_lines.size() == 0) { | 833 if (diskinfo_lines.empty()) { |
| 846 DLOG(WARNING) << "No lines found"; | 834 DLOG(WARNING) << "No lines found"; |
| 847 return false; | 835 return false; |
| 848 } | 836 } |
| 849 | 837 |
| 850 diskinfo->reads = 0; | 838 diskinfo->reads = 0; |
| 851 diskinfo->reads_merged = 0; | 839 diskinfo->reads_merged = 0; |
| 852 diskinfo->sectors_read = 0; | 840 diskinfo->sectors_read = 0; |
| 853 diskinfo->read_time = 0; | 841 diskinfo->read_time = 0; |
| 854 diskinfo->writes = 0; | 842 diskinfo->writes = 0; |
| 855 diskinfo->writes_merged = 0; | 843 diskinfo->writes_merged = 0; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 869 uint64_t write_time = 0; | 857 uint64_t write_time = 0; |
| 870 uint64_t io = 0; | 858 uint64_t io = 0; |
| 871 uint64_t io_time = 0; | 859 uint64_t io_time = 0; |
| 872 uint64_t weighted_io_time = 0; | 860 uint64_t weighted_io_time = 0; |
| 873 | 861 |
| 874 for (const StringPiece& line : diskinfo_lines) { | 862 for (const StringPiece& line : diskinfo_lines) { |
| 875 std::vector<StringPiece> disk_fields = SplitStringPiece( | 863 std::vector<StringPiece> disk_fields = SplitStringPiece( |
| 876 line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); | 864 line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); |
| 877 | 865 |
| 878 // Fields may have overflowed and reset to zero. | 866 // Fields may have overflowed and reset to zero. |
| 879 if (IsValidDiskName(disk_fields[kDiskDriveName].as_string())) { | 867 if (!IsValidDiskName(disk_fields[kDiskDriveName].as_string())) |
| 880 StringToUint64(disk_fields[kDiskReads], &reads); | 868 continue; |
| 881 StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged); | |
| 882 StringToUint64(disk_fields[kDiskSectorsRead], §ors_read); | |
| 883 StringToUint64(disk_fields[kDiskReadTime], &read_time); | |
| 884 StringToUint64(disk_fields[kDiskWrites], &writes); | |
| 885 StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged); | |
| 886 StringToUint64(disk_fields[kDiskSectorsWritten], §ors_written); | |
| 887 StringToUint64(disk_fields[kDiskWriteTime], &write_time); | |
| 888 StringToUint64(disk_fields[kDiskIO], &io); | |
| 889 StringToUint64(disk_fields[kDiskIOTime], &io_time); | |
| 890 StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time); | |
| 891 | 869 |
| 892 diskinfo->reads += reads; | 870 StringToUint64(disk_fields[kDiskReads], &reads); |
| 893 diskinfo->reads_merged += reads_merged; | 871 StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged); |
| 894 diskinfo->sectors_read += sectors_read; | 872 StringToUint64(disk_fields[kDiskSectorsRead], §ors_read); |
| 895 diskinfo->read_time += read_time; | 873 StringToUint64(disk_fields[kDiskReadTime], &read_time); |
| 896 diskinfo->writes += writes; | 874 StringToUint64(disk_fields[kDiskWrites], &writes); |
| 897 diskinfo->writes_merged += writes_merged; | 875 StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged); |
| 898 diskinfo->sectors_written += sectors_written; | 876 StringToUint64(disk_fields[kDiskSectorsWritten], §ors_written); |
| 899 diskinfo->write_time += write_time; | 877 StringToUint64(disk_fields[kDiskWriteTime], &write_time); |
| 900 diskinfo->io += io; | 878 StringToUint64(disk_fields[kDiskIO], &io); |
| 901 diskinfo->io_time += io_time; | 879 StringToUint64(disk_fields[kDiskIOTime], &io_time); |
| 902 diskinfo->weighted_io_time += weighted_io_time; | 880 StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time); |
| 903 } | 881 |
| 882 diskinfo->reads += reads; |
| 883 diskinfo->reads_merged += reads_merged; |
| 884 diskinfo->sectors_read += sectors_read; |
| 885 diskinfo->read_time += read_time; |
| 886 diskinfo->writes += writes; |
| 887 diskinfo->writes_merged += writes_merged; |
| 888 diskinfo->sectors_written += sectors_written; |
| 889 diskinfo->write_time += write_time; |
| 890 diskinfo->io += io; |
| 891 diskinfo->io_time += io_time; |
| 892 diskinfo->weighted_io_time += weighted_io_time; |
| 904 } | 893 } |
| 905 | 894 |
| 906 return true; | 895 return true; |
| 907 } | 896 } |
| 908 | 897 |
| 909 TimeDelta GetUserCpuTimeSinceBoot() { | 898 TimeDelta GetUserCpuTimeSinceBoot() { |
| 910 return internal::GetUserCpuTimeSinceBoot(); | 899 return internal::GetUserCpuTimeSinceBoot(); |
| 911 } | 900 } |
| 912 | 901 |
| 913 #if defined(OS_CHROMEOS) | 902 #if defined(OS_CHROMEOS) |
| 914 std::unique_ptr<Value> SwapInfo::ToValue() const { | 903 std::unique_ptr<Value> SwapInfo::ToValue() const { |
| 915 std::unique_ptr<DictionaryValue> res(new DictionaryValue()); | 904 auto res = base::MakeUnique<DictionaryValue>(); |
| 916 | 905 |
| 917 // Write out uint64_t variables as doubles. | 906 // Write out uint64_t variables as doubles. |
| 918 // Note: this may discard some precision, but for JS there's no other option. | 907 // Note: this may discard some precision, but for JS there's no other option. |
| 919 res->SetDouble("num_reads", static_cast<double>(num_reads)); | 908 res->SetDouble("num_reads", static_cast<double>(num_reads)); |
| 920 res->SetDouble("num_writes", static_cast<double>(num_writes)); | 909 res->SetDouble("num_writes", static_cast<double>(num_writes)); |
| 921 res->SetDouble("orig_data_size", static_cast<double>(orig_data_size)); | 910 res->SetDouble("orig_data_size", static_cast<double>(orig_data_size)); |
| 922 res->SetDouble("compr_data_size", static_cast<double>(compr_data_size)); | 911 res->SetDouble("compr_data_size", static_cast<double>(compr_data_size)); |
| 923 res->SetDouble("mem_used_total", static_cast<double>(mem_used_total)); | 912 res->SetDouble("mem_used_total", static_cast<double>(mem_used_total)); |
| 924 if (compr_data_size > 0) | 913 double ratio = compr_data_size ? static_cast<double>(orig_data_size) / |
| 925 res->SetDouble("compression_ratio", static_cast<double>(orig_data_size) / | 914 static_cast<double>(compr_data_size) |
| 926 static_cast<double>(compr_data_size)); | 915 : 0; |
| 927 else | 916 res->SetDouble("compression_ratio", ratio); |
| 928 res->SetDouble("compression_ratio", 0); | |
| 929 | 917 |
| 930 return std::move(res); | 918 return std::move(res); |
| 931 } | 919 } |
| 932 | 920 |
| 933 void GetSwapInfo(SwapInfo* swap_info) { | 921 void GetSwapInfo(SwapInfo* swap_info) { |
| 934 // Synchronously reading files in /sys/block/zram0 does not hit the disk. | 922 // Synchronously reading files in /sys/block/zram0 does not hit the disk. |
| 935 ThreadRestrictions::ScopedAllowIO allow_io; | 923 ThreadRestrictions::ScopedAllowIO allow_io; |
| 936 | 924 |
| 937 FilePath zram_path("/sys/block/zram0"); | 925 FilePath zram_path("/sys/block/zram0"); |
| 938 uint64_t orig_data_size = | 926 uint64_t orig_data_size = |
| 939 ReadFileToUint64(zram_path.Append("orig_data_size")); | 927 ReadFileToUint64(zram_path.Append("orig_data_size")); |
| 940 if (orig_data_size <= 4096) { | 928 if (orig_data_size <= 4096) { |
| 941 // A single page is compressed at startup, and has a high compression | 929 // A single page is compressed at startup, and has a high compression |
| 942 // ratio. We ignore this as it doesn't indicate any real swapping. | 930 // ratio. Ignore this as it doesn't indicate any real swapping. |
| 943 swap_info->orig_data_size = 0; | 931 swap_info->orig_data_size = 0; |
| 944 swap_info->num_reads = 0; | 932 swap_info->num_reads = 0; |
| 945 swap_info->num_writes = 0; | 933 swap_info->num_writes = 0; |
| 946 swap_info->compr_data_size = 0; | 934 swap_info->compr_data_size = 0; |
| 947 swap_info->mem_used_total = 0; | 935 swap_info->mem_used_total = 0; |
| 948 return; | 936 return; |
| 949 } | 937 } |
| 950 swap_info->orig_data_size = orig_data_size; | 938 swap_info->orig_data_size = orig_data_size; |
| 951 swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads")); | 939 swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads")); |
| 952 swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes")); | 940 swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes")); |
| 953 swap_info->compr_data_size = | 941 swap_info->compr_data_size = |
| 954 ReadFileToUint64(zram_path.Append("compr_data_size")); | 942 ReadFileToUint64(zram_path.Append("compr_data_size")); |
| 955 swap_info->mem_used_total = | 943 swap_info->mem_used_total = |
| 956 ReadFileToUint64(zram_path.Append("mem_used_total")); | 944 ReadFileToUint64(zram_path.Append("mem_used_total")); |
| 957 } | 945 } |
| 958 #endif // defined(OS_CHROMEOS) | 946 #endif // defined(OS_CHROMEOS) |
| 959 | 947 |
| 960 #if defined(OS_LINUX) || defined(OS_AIX) | 948 #if defined(OS_LINUX) || defined(OS_AIX) |
| 961 int ProcessMetrics::GetIdleWakeupsPerSecond() { | 949 int ProcessMetrics::GetIdleWakeupsPerSecond() { |
| 962 uint64_t num_switches; | 950 uint64_t num_switches; |
| 963 static const char kSwitchStat[] = "voluntary_ctxt_switches"; | 951 static const char kSwitchStat[] = "voluntary_ctxt_switches"; |
| 964 return ReadProcStatusAndGetFieldAsUint64(process_, kSwitchStat, &num_switches) | 952 return ReadProcStatusAndGetFieldAsUint64(process_, kSwitchStat, &num_switches) |
| 965 ? CalculateIdleWakeupsPerSecond(num_switches) | 953 ? CalculateIdleWakeupsPerSecond(num_switches) |
| 966 : 0; | 954 : 0; |
| 967 } | 955 } |
| 968 #endif // defined(OS_LINUX) || defined(OS_AIX) | 956 #endif // defined(OS_LINUX) || defined(OS_AIX) |
| 969 | 957 |
| 970 } // namespace base | 958 } // namespace base |
| OLD | NEW |