Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(600)

Side by Side Diff: base/process/process_metrics_linux.cc

Issue 546193002: Add an 'Idle Wake Ups' metric to the Linux task manager, implement backend. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@wakeclean
Patch Set: rebase Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/process/process_metrics.cc ('k') | base/process/process_metrics_mac.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « base/process/process_metrics.cc ('k') | base/process/process_metrics_mac.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698