| 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 <sys/stat.h> | 9 #include <sys/stat.h> |
| 10 #include <sys/time.h> | 10 #include <sys/time.h> |
| 11 #include <sys/types.h> | 11 #include <sys/types.h> |
| 12 #include <unistd.h> | 12 #include <unistd.h> |
| 13 | 13 |
| 14 #include "base/files/file_util.h" | 14 #include "base/files/file_util.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/process/internal_linux.h" | 16 #include "base/process/internal_linux.h" |
| 17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/string_split.h" | 18 #include "base/strings/string_split.h" |
| 19 #include "base/strings/string_tokenizer.h" | 19 #include "base/strings/string_tokenizer.h" |
| 20 #include "base/strings/string_util.h" | 20 #include "base/strings/string_util.h" |
| 21 #include "base/sys_info.h" | 21 #include "base/sys_info.h" |
| 22 #include "base/threading/thread_restrictions.h" | 22 #include "base/threading/thread_restrictions.h" |
| 23 | 23 |
| 24 namespace base { | 24 namespace base { |
| 25 | 25 |
| 26 namespace { | 26 namespace { |
| 27 | 27 |
| 28 void TrimKeyValuePairs(StringPairs* pairs) { |
| 29 DCHECK(pairs); |
| 30 StringPairs& p_ref = *pairs; |
| 31 for (size_t i = 0; i < p_ref.size(); ++i) { |
| 32 TrimWhitespaceASCII(p_ref[i].first, TRIM_ALL, &p_ref[i].first); |
| 33 TrimWhitespaceASCII(p_ref[i].second, TRIM_ALL, &p_ref[i].second); |
| 34 } |
| 35 } |
| 36 |
| 28 #if defined(OS_CHROMEOS) | 37 #if defined(OS_CHROMEOS) |
| 29 // Read a file with a single number string and return the number as a uint64. | 38 // Read a file with a single number string and return the number as a uint64. |
| 30 static uint64 ReadFileToUint64(const FilePath file) { | 39 static uint64 ReadFileToUint64(const FilePath file) { |
| 31 std::string file_as_string; | 40 std::string file_as_string; |
| 32 if (!ReadFileToString(file, &file_as_string)) | 41 if (!ReadFileToString(file, &file_as_string)) |
| 33 return 0; | 42 return 0; |
| 34 TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string); | 43 TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string); |
| 35 uint64 file_as_uint64 = 0; | 44 uint64 file_as_uint64 = 0; |
| 36 if (!StringToUint64(file_as_string, &file_as_uint64)) | 45 if (!StringToUint64(file_as_string, &file_as_uint64)) |
| 37 return 0; | 46 return 0; |
| 38 return file_as_uint64; | 47 return file_as_uint64; |
| 39 } | 48 } |
| 40 #endif | 49 #endif |
| 41 | 50 |
| 42 // Read /proc/<pid>/status and return the value for |field|, or 0 on failure. | 51 // Read /proc/<pid>/status and return the value for |field|, or 0 on failure. |
| 43 // Only works for fields in the form of "Field: value kB". | 52 // Only works for fields in the form of "Field: value kB". |
| 44 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { | 53 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { |
| 45 std::string status; | 54 std::string status; |
| 46 { | 55 { |
| 47 // Synchronously reading files in /proc is safe. | 56 // Synchronously reading files in /proc does not hit the disk. |
| 48 ThreadRestrictions::ScopedAllowIO allow_io; | 57 ThreadRestrictions::ScopedAllowIO allow_io; |
| 49 FilePath stat_file = internal::GetProcPidDir(pid).Append("status"); | 58 FilePath stat_file = internal::GetProcPidDir(pid).Append("status"); |
| 50 if (!ReadFileToString(stat_file, &status)) | 59 if (!ReadFileToString(stat_file, &status)) |
| 51 return 0; | 60 return 0; |
| 52 } | 61 } |
| 53 | 62 |
| 54 std::vector<std::pair<std::string, std::string> > pairs; | 63 StringPairs pairs; |
| 55 SplitStringIntoKeyValuePairs(status, ':', '\n', &pairs); | 64 SplitStringIntoKeyValuePairs(status, ':', '\n', &pairs); |
| 65 TrimKeyValuePairs(&pairs); |
| 56 for (size_t i = 0; i < pairs.size(); ++i) { | 66 for (size_t i = 0; i < pairs.size(); ++i) { |
| 57 std::string key, value_str; | 67 const std::string& key = pairs[i].first; |
| 58 TrimWhitespaceASCII(pairs[i].first, TRIM_ALL, &key); | 68 const std::string& value_str = pairs[i].second; |
| 59 TrimWhitespaceASCII(pairs[i].second, TRIM_ALL, &value_str); | |
| 60 if (key == field) { | 69 if (key == field) { |
| 61 std::vector<std::string> split_value_str; | 70 std::vector<std::string> split_value_str; |
| 62 SplitString(value_str, ' ', &split_value_str); | 71 SplitString(value_str, ' ', &split_value_str); |
| 63 if (split_value_str.size() != 2 || split_value_str[1] != "kB") { | 72 if (split_value_str.size() != 2 || split_value_str[1] != "kB") { |
| 64 NOTREACHED(); | 73 NOTREACHED(); |
| 65 return 0; | 74 return 0; |
| 66 } | 75 } |
| 67 size_t value; | 76 size_t value; |
| 68 if (!StringToSizeT(split_value_str[0], &value)) { | 77 if (!StringToSizeT(split_value_str[0], &value)) { |
| 69 NOTREACHED(); | 78 NOTREACHED(); |
| 70 return 0; | 79 return 0; |
| 71 } | 80 } |
| 72 return value; | 81 return value; |
| 73 } | 82 } |
| 74 } | 83 } |
| 75 NOTREACHED(); | 84 NOTREACHED(); |
| 76 return 0; | 85 return 0; |
| 77 } | 86 } |
| 78 | 87 |
| 88 #if defined(OS_LINUX) |
| 89 // Read /proc/<pid>/sched and look for |field|. On succes, return true and |
| 90 // write the value for |field| into |result|. |
| 91 // Only works for fields in the form of "field : uint_value" |
| 92 bool ReadProcSchedAndGetFieldAsUint64(pid_t pid, |
| 93 const std::string& field, |
| 94 uint64* result) { |
| 95 std::string sched_data; |
| 96 { |
| 97 // Synchronously reading files in /proc does not hit the disk. |
| 98 ThreadRestrictions::ScopedAllowIO allow_io; |
| 99 FilePath sched_file = internal::GetProcPidDir(pid).Append("sched"); |
| 100 if (!ReadFileToString(sched_file, &sched_data)) |
| 101 return false; |
| 102 } |
| 103 |
| 104 StringPairs pairs; |
| 105 SplitStringIntoKeyValuePairs(sched_data, ':', '\n', &pairs); |
| 106 TrimKeyValuePairs(&pairs); |
| 107 for (size_t i = 0; i < pairs.size(); ++i) { |
| 108 const std::string& key = pairs[i].first; |
| 109 const std::string& value_str = pairs[i].second; |
| 110 if (key == field) { |
| 111 uint64 value; |
| 112 if (!StringToUint64(value_str, &value)) |
| 113 return false; |
| 114 *result = value; |
| 115 return true; |
| 116 } |
| 117 } |
| 118 return false; |
| 119 } |
| 120 #endif // defined(OS_LINUX) |
| 121 |
| 79 // Get the total CPU of a single process. Return value is number of jiffies | 122 // Get the total CPU of a single process. Return value is number of jiffies |
| 80 // on success or -1 on error. | 123 // on success or -1 on error. |
| 81 int GetProcessCPU(pid_t pid) { | 124 int GetProcessCPU(pid_t pid) { |
| 82 // Use /proc/<pid>/task to find all threads and parse their /stat file. | 125 // Use /proc/<pid>/task to find all threads and parse their /stat file. |
| 83 FilePath task_path = internal::GetProcPidDir(pid).Append("task"); | 126 FilePath task_path = internal::GetProcPidDir(pid).Append("task"); |
| 84 | 127 |
| 85 DIR* dir = opendir(task_path.value().c_str()); | 128 DIR* dir = opendir(task_path.value().c_str()); |
| 86 if (!dir) { | 129 if (!dir) { |
| 87 DPLOG(ERROR) << "opendir(" << task_path.value() << ")"; | 130 DPLOG(ERROR) << "opendir(" << task_path.value() << ")"; |
| 88 return -1; | 131 return -1; |
| 89 } | 132 } |
| 90 | 133 |
| 91 int total_cpu = 0; | 134 int total_cpu = 0; |
| 92 while (struct dirent* ent = readdir(dir)) { | 135 while (struct dirent* ent = readdir(dir)) { |
| 93 pid_t tid = internal::ProcDirSlotToPid(ent->d_name); | 136 pid_t tid = internal::ProcDirSlotToPid(ent->d_name); |
| 94 if (!tid) | 137 if (!tid) |
| 95 continue; | 138 continue; |
| 96 | 139 |
| 97 // Synchronously reading files in /proc is safe. | 140 // Synchronously reading files in /proc does not hit the disk. |
| 98 ThreadRestrictions::ScopedAllowIO allow_io; | 141 ThreadRestrictions::ScopedAllowIO allow_io; |
| 99 | 142 |
| 100 std::string stat; | 143 std::string stat; |
| 101 FilePath stat_path = | 144 FilePath stat_path = |
| 102 task_path.Append(ent->d_name).Append(internal::kStatFile); | 145 task_path.Append(ent->d_name).Append(internal::kStatFile); |
| 103 if (ReadFileToString(stat_path, &stat)) { | 146 if (ReadFileToString(stat_path, &stat)) { |
| 104 int cpu = ParseProcStatCPU(stat); | 147 int cpu = ParseProcStatCPU(stat); |
| 105 if (cpu > 0) | 148 if (cpu > 0) |
| 106 total_cpu += cpu; | 149 total_cpu += cpu; |
| 107 } | 150 } |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 | 233 |
| 191 last_cpu_time_ = time; | 234 last_cpu_time_ = time; |
| 192 last_cpu_ = cpu; | 235 last_cpu_ = cpu; |
| 193 | 236 |
| 194 return percentage; | 237 return percentage; |
| 195 } | 238 } |
| 196 | 239 |
| 197 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING | 240 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING |
| 198 // in your kernel configuration. | 241 // in your kernel configuration. |
| 199 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { | 242 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { |
| 200 // Synchronously reading files in /proc is safe. | 243 // Synchronously reading files in /proc does not hit the disk. |
| 201 ThreadRestrictions::ScopedAllowIO allow_io; | 244 ThreadRestrictions::ScopedAllowIO allow_io; |
| 202 | 245 |
| 203 std::string proc_io_contents; | 246 std::string proc_io_contents; |
| 204 FilePath io_file = internal::GetProcPidDir(process_).Append("io"); | 247 FilePath io_file = internal::GetProcPidDir(process_).Append("io"); |
| 205 if (!ReadFileToString(io_file, &proc_io_contents)) | 248 if (!ReadFileToString(io_file, &proc_io_contents)) |
| 206 return false; | 249 return false; |
| 207 | 250 |
| 208 io_counters->OtherOperationCount = 0; | 251 io_counters->OtherOperationCount = 0; |
| 209 io_counters->OtherTransferCount = 0; | 252 io_counters->OtherTransferCount = 0; |
| 210 | 253 |
| 211 std::vector<std::pair<std::string, std::string> > pairs; | 254 StringPairs pairs; |
| 212 SplitStringIntoKeyValuePairs(proc_io_contents, ':', '\n', &pairs); | 255 SplitStringIntoKeyValuePairs(proc_io_contents, ':', '\n', &pairs); |
| 256 TrimKeyValuePairs(&pairs); |
| 213 for (size_t i = 0; i < pairs.size(); ++i) { | 257 for (size_t i = 0; i < pairs.size(); ++i) { |
| 214 std::string key, value_str; | 258 const std::string& key = pairs[i].first; |
| 215 TrimWhitespaceASCII(pairs[i].first, TRIM_ALL, &key); | 259 const std::string& value_str = pairs[i].second; |
| 216 TrimWhitespaceASCII(pairs[i].second, TRIM_ALL, &value_str); | |
| 217 uint64* target_counter = NULL; | 260 uint64* target_counter = NULL; |
| 218 if (key == "syscr") | 261 if (key == "syscr") |
| 219 target_counter = &io_counters->ReadOperationCount; | 262 target_counter = &io_counters->ReadOperationCount; |
| 220 else if (key == "syscw") | 263 else if (key == "syscw") |
| 221 target_counter = &io_counters->WriteOperationCount; | 264 target_counter = &io_counters->WriteOperationCount; |
| 222 else if (key == "rchar") | 265 else if (key == "rchar") |
| 223 target_counter = &io_counters->ReadTransferCount; | 266 target_counter = &io_counters->ReadTransferCount; |
| 224 else if (key == "wchar") | 267 else if (key == "wchar") |
| 225 target_counter = &io_counters->WriteTransferCount; | 268 target_counter = &io_counters->WriteTransferCount; |
| 226 if (!target_counter) | 269 if (!target_counter) |
| 227 continue; | 270 continue; |
| 228 bool converted = StringToUint64(value_str, target_counter); | 271 bool converted = StringToUint64(value_str, target_counter); |
| 229 DCHECK(converted); | 272 DCHECK(converted); |
| 230 } | 273 } |
| 231 return true; | 274 return true; |
| 232 } | 275 } |
| 233 | 276 |
| 234 ProcessMetrics::ProcessMetrics(ProcessHandle process) | 277 ProcessMetrics::ProcessMetrics(ProcessHandle process) |
| 235 : process_(process), | 278 : process_(process), |
| 236 last_system_time_(0), | 279 last_system_time_(0), |
| 280 #if defined(OS_LINUX) |
| 281 last_absolute_idle_wakeups_(0), |
| 282 #endif |
| 237 last_cpu_(0) { | 283 last_cpu_(0) { |
| 238 processor_count_ = SysInfo::NumberOfProcessors(); | 284 processor_count_ = SysInfo::NumberOfProcessors(); |
| 239 } | 285 } |
| 240 | 286 |
| 241 #if defined(OS_CHROMEOS) | 287 #if defined(OS_CHROMEOS) |
| 242 // Private, Shared and Proportional working set sizes are obtained from | 288 // Private, Shared and Proportional working set sizes are obtained from |
| 243 // /proc/<pid>/totmaps | 289 // /proc/<pid>/totmaps |
| 244 bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) | 290 bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) |
| 245 const { | 291 const { |
| 246 // The format of /proc/<pid>/totmaps is: | 292 // The format of /proc/<pid>/totmaps is: |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 | 354 |
| 309 // First we need to get the page size, since everything is measured in pages. | 355 // First we need to get the page size, since everything is measured in pages. |
| 310 // For details, see: man 5 proc. | 356 // For details, see: man 5 proc. |
| 311 const int page_size_kb = getpagesize() / 1024; | 357 const int page_size_kb = getpagesize() / 1024; |
| 312 if (page_size_kb <= 0) | 358 if (page_size_kb <= 0) |
| 313 return false; | 359 return false; |
| 314 | 360 |
| 315 std::string statm; | 361 std::string statm; |
| 316 { | 362 { |
| 317 FilePath statm_file = internal::GetProcPidDir(process_).Append("statm"); | 363 FilePath statm_file = internal::GetProcPidDir(process_).Append("statm"); |
| 318 // Synchronously reading files in /proc is safe. | 364 // Synchronously reading files in /proc does not hit the disk. |
| 319 ThreadRestrictions::ScopedAllowIO allow_io; | 365 ThreadRestrictions::ScopedAllowIO allow_io; |
| 320 bool ret = ReadFileToString(statm_file, &statm); | 366 bool ret = ReadFileToString(statm_file, &statm); |
| 321 if (!ret || statm.length() == 0) | 367 if (!ret || statm.length() == 0) |
| 322 return false; | 368 return false; |
| 323 } | 369 } |
| 324 | 370 |
| 325 std::vector<std::string> statm_vec; | 371 std::vector<std::string> statm_vec; |
| 326 SplitString(statm, ' ', &statm_vec); | 372 SplitString(statm, ' ', &statm_vec); |
| 327 if (statm_vec.size() != 7) | 373 if (statm_vec.size() != 7) |
| 328 return false; // Not the format we expect. | 374 return false; // Not the format we expect. |
| (...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 | 756 |
| 711 // mmcblk[0-9]+ case | 757 // mmcblk[0-9]+ case |
| 712 for (size_t i = kMMCNameLen; i < candidate.length(); ++i) { | 758 for (size_t i = kMMCNameLen; i < candidate.length(); ++i) { |
| 713 if (!isdigit(candidate[i])) | 759 if (!isdigit(candidate[i])) |
| 714 return false; | 760 return false; |
| 715 } | 761 } |
| 716 return true; | 762 return true; |
| 717 } | 763 } |
| 718 | 764 |
| 719 bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { | 765 bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { |
| 720 // Synchronously reading files in /proc is safe. | 766 // Synchronously reading files in /proc does not hit the disk. |
| 721 ThreadRestrictions::ScopedAllowIO allow_io; | 767 ThreadRestrictions::ScopedAllowIO allow_io; |
| 722 | 768 |
| 723 FilePath diskinfo_file("/proc/diskstats"); | 769 FilePath diskinfo_file("/proc/diskstats"); |
| 724 std::string diskinfo_data; | 770 std::string diskinfo_data; |
| 725 if (!ReadFileToString(diskinfo_file, &diskinfo_data)) { | 771 if (!ReadFileToString(diskinfo_file, &diskinfo_data)) { |
| 726 DLOG(WARNING) << "Failed to open " << diskinfo_file.value(); | 772 DLOG(WARNING) << "Failed to open " << diskinfo_file.value(); |
| 727 return false; | 773 return false; |
| 728 } | 774 } |
| 729 | 775 |
| 730 std::vector<std::string> diskinfo_lines; | 776 std::vector<std::string> diskinfo_lines; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 807 if (compr_data_size > 0) | 853 if (compr_data_size > 0) |
| 808 res->SetDouble("compression_ratio", static_cast<double>(orig_data_size) / | 854 res->SetDouble("compression_ratio", static_cast<double>(orig_data_size) / |
| 809 static_cast<double>(compr_data_size)); | 855 static_cast<double>(compr_data_size)); |
| 810 else | 856 else |
| 811 res->SetDouble("compression_ratio", 0); | 857 res->SetDouble("compression_ratio", 0); |
| 812 | 858 |
| 813 return res.PassAs<Value>(); | 859 return res.PassAs<Value>(); |
| 814 } | 860 } |
| 815 | 861 |
| 816 void GetSwapInfo(SwapInfo* swap_info) { | 862 void GetSwapInfo(SwapInfo* swap_info) { |
| 817 // Synchronously reading files in /sys/block/zram0 is safe. | 863 // Synchronously reading files in /sys/block/zram0 does not hit the disk. |
| 818 ThreadRestrictions::ScopedAllowIO allow_io; | 864 ThreadRestrictions::ScopedAllowIO allow_io; |
| 819 | 865 |
| 820 FilePath zram_path("/sys/block/zram0"); | 866 FilePath zram_path("/sys/block/zram0"); |
| 821 uint64 orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size")); | 867 uint64 orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size")); |
| 822 if (orig_data_size <= 4096) { | 868 if (orig_data_size <= 4096) { |
| 823 // A single page is compressed at startup, and has a high compression | 869 // A single page is compressed at startup, and has a high compression |
| 824 // ratio. We ignore this as it doesn't indicate any real swapping. | 870 // ratio. We ignore this as it doesn't indicate any real swapping. |
| 825 swap_info->orig_data_size = 0; | 871 swap_info->orig_data_size = 0; |
| 826 swap_info->num_reads = 0; | 872 swap_info->num_reads = 0; |
| 827 swap_info->num_writes = 0; | 873 swap_info->num_writes = 0; |
| 828 swap_info->compr_data_size = 0; | 874 swap_info->compr_data_size = 0; |
| 829 swap_info->mem_used_total = 0; | 875 swap_info->mem_used_total = 0; |
| 830 return; | 876 return; |
| 831 } | 877 } |
| 832 swap_info->orig_data_size = orig_data_size; | 878 swap_info->orig_data_size = orig_data_size; |
| 833 swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads")); | 879 swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads")); |
| 834 swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes")); | 880 swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes")); |
| 835 swap_info->compr_data_size = | 881 swap_info->compr_data_size = |
| 836 ReadFileToUint64(zram_path.Append("compr_data_size")); | 882 ReadFileToUint64(zram_path.Append("compr_data_size")); |
| 837 swap_info->mem_used_total = | 883 swap_info->mem_used_total = |
| 838 ReadFileToUint64(zram_path.Append("mem_used_total")); | 884 ReadFileToUint64(zram_path.Append("mem_used_total")); |
| 839 } | 885 } |
| 840 #endif // defined(OS_CHROMEOS) | 886 #endif // defined(OS_CHROMEOS) |
| 841 | 887 |
| 888 #if defined(OS_LINUX) |
| 889 int ProcessMetrics::GetIdleWakeupsPerSecond() { |
| 890 uint64 wake_ups; |
| 891 const char kWakeupStat[] = "se.statistics.nr_wakeups"; |
| 892 return ReadProcSchedAndGetFieldAsUint64(process_, kWakeupStat, &wake_ups) ? |
| 893 CalculateIdleWakeupsPerSecond(wake_ups) : 0; |
| 894 } |
| 895 #endif // defined(OS_LINUX) |
| 896 |
| 842 } // namespace base | 897 } // namespace base |
| OLD | NEW |