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
3 // found in the LICENSE file.
5 #include "base/process/process_metrics.h"
11 #include <sys/types.h>
14 #include "base/file_util.h"
15 #include "base/logging.h"
16 #include "base/process/internal_linux.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_split.h"
19 #include "base/strings/string_tokenizer.h"
20 #include "base/strings/string_util.h"
21 #include "base/sys_info.h"
22 #include "base/threading/thread_restrictions.h"
34 // Read a file with a single number string and return the number as a uint64.
35 static uint64
ReadFileToUint64(const base::FilePath file
) {
36 std::string file_as_string
;
37 if (!ReadFileToString(file
, &file_as_string
))
39 base::TrimWhitespaceASCII(file_as_string
, base::TRIM_ALL
, &file_as_string
);
40 uint64 file_as_uint64
= 0;
41 if (!base::StringToUint64(file_as_string
, &file_as_uint64
))
43 return file_as_uint64
;
47 // Read /proc/<pid>/status and returns the value for |field|, or 0 on failure.
48 // Only works for fields in the form of "Field: value kB".
49 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid
, const std::string
& field
) {
50 FilePath stat_file
= internal::GetProcPidDir(pid
).Append("status");
53 // Synchronously reading files in /proc is safe.
54 ThreadRestrictions::ScopedAllowIO allow_io
;
55 if (!ReadFileToString(stat_file
, &status
))
59 StringTokenizer
tokenizer(status
, ":\n");
60 ParsingState state
= KEY_NAME
;
61 StringPiece last_key_name
;
62 while (tokenizer
.GetNext()) {
65 last_key_name
= tokenizer
.token_piece();
69 DCHECK(!last_key_name
.empty());
70 if (last_key_name
== field
) {
71 std::string value_str
;
72 tokenizer
.token_piece().CopyToString(&value_str
);
73 std::string value_str_trimmed
;
74 base::TrimWhitespaceASCII(value_str
, base::TRIM_ALL
,
76 std::vector
<std::string
> split_value_str
;
77 SplitString(value_str_trimmed
, ' ', &split_value_str
);
78 if (split_value_str
.size() != 2 || split_value_str
[1] != "kB") {
83 if (!StringToSizeT(split_value_str
[0], &value
)) {
97 // Get the total CPU of a single process. Return value is number of jiffies
98 // on success or -1 on error.
99 int GetProcessCPU(pid_t pid
) {
100 // Use /proc/<pid>/task to find all threads and parse their /stat file.
101 FilePath task_path
= internal::GetProcPidDir(pid
).Append("task");
103 DIR* dir
= opendir(task_path
.value().c_str());
105 DPLOG(ERROR
) << "opendir(" << task_path
.value() << ")";
110 while (struct dirent
* ent
= readdir(dir
)) {
111 pid_t tid
= internal::ProcDirSlotToPid(ent
->d_name
);
115 // Synchronously reading files in /proc is safe.
116 ThreadRestrictions::ScopedAllowIO allow_io
;
120 task_path
.Append(ent
->d_name
).Append(internal::kStatFile
);
121 if (ReadFileToString(stat_path
, &stat
)) {
122 int cpu
= ParseProcStatCPU(stat
);
135 ProcessMetrics
* ProcessMetrics::CreateProcessMetrics(ProcessHandle process
) {
136 return new ProcessMetrics(process
);
139 // On linux, we return vsize.
140 size_t ProcessMetrics::GetPagefileUsage() const {
141 return internal::ReadProcStatsAndGetFieldAsSizeT(process_
,
145 // On linux, we return the high water mark of vsize.
146 size_t ProcessMetrics::GetPeakPagefileUsage() const {
147 return ReadProcStatusAndGetFieldAsSizeT(process_
, "VmPeak") * 1024;
150 // On linux, we return RSS.
151 size_t ProcessMetrics::GetWorkingSetSize() const {
152 return internal::ReadProcStatsAndGetFieldAsSizeT(process_
, internal::VM_RSS
) *
156 // On linux, we return the high water mark of RSS.
157 size_t ProcessMetrics::GetPeakWorkingSetSize() const {
158 return ReadProcStatusAndGetFieldAsSizeT(process_
, "VmHWM") * 1024;
161 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes
,
162 size_t* shared_bytes
) {
163 WorkingSetKBytes ws_usage
;
164 if (!GetWorkingSetKBytes(&ws_usage
))
168 *private_bytes
= ws_usage
.priv
* 1024;
171 *shared_bytes
= ws_usage
.shared
* 1024;
176 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes
* ws_usage
) const {
177 #if defined(OS_CHROMEOS)
178 if (GetWorkingSetKBytesTotmaps(ws_usage
))
181 return GetWorkingSetKBytesStatm(ws_usage
);
184 double ProcessMetrics::GetCPUUsage() {
185 TimeTicks time
= TimeTicks::Now();
187 if (last_cpu_
== 0) {
188 // First call, just set the last values.
189 last_cpu_time_
= time
;
190 last_cpu_
= GetProcessCPU(process_
);
194 int64 time_delta
= (time
- last_cpu_time_
).InMicroseconds();
195 DCHECK_NE(time_delta
, 0);
199 int cpu
= GetProcessCPU(process_
);
201 // We have the number of jiffies in the time period. Convert to percentage.
202 // Note this means we will go *over* 100 in the case where multiple threads
203 // are together adding to more than one CPU's worth.
204 TimeDelta cpu_time
= internal::ClockTicksToTimeDelta(cpu
);
205 TimeDelta last_cpu_time
= internal::ClockTicksToTimeDelta(last_cpu_
);
206 int percentage
= 100 * (cpu_time
- last_cpu_time
).InSecondsF() /
207 TimeDelta::FromMicroseconds(time_delta
).InSecondsF();
209 last_cpu_time_
= time
;
215 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING
216 // in your kernel configuration.
217 bool ProcessMetrics::GetIOCounters(IoCounters
* io_counters
) const {
218 // Synchronously reading files in /proc is safe.
219 ThreadRestrictions::ScopedAllowIO allow_io
;
221 std::string proc_io_contents
;
222 FilePath io_file
= internal::GetProcPidDir(process_
).Append("io");
223 if (!ReadFileToString(io_file
, &proc_io_contents
))
226 (*io_counters
).OtherOperationCount
= 0;
227 (*io_counters
).OtherTransferCount
= 0;
229 StringTokenizer
tokenizer(proc_io_contents
, ": \n");
230 ParsingState state
= KEY_NAME
;
231 StringPiece last_key_name
;
232 while (tokenizer
.GetNext()) {
235 last_key_name
= tokenizer
.token_piece();
239 DCHECK(!last_key_name
.empty());
240 if (last_key_name
== "syscr") {
241 StringToInt64(tokenizer
.token_piece(),
242 reinterpret_cast<int64
*>(&(*io_counters
).ReadOperationCount
));
243 } else if (last_key_name
== "syscw") {
244 StringToInt64(tokenizer
.token_piece(),
245 reinterpret_cast<int64
*>(&(*io_counters
).WriteOperationCount
));
246 } else if (last_key_name
== "rchar") {
247 StringToInt64(tokenizer
.token_piece(),
248 reinterpret_cast<int64
*>(&(*io_counters
).ReadTransferCount
));
249 } else if (last_key_name
== "wchar") {
250 StringToInt64(tokenizer
.token_piece(),
251 reinterpret_cast<int64
*>(&(*io_counters
).WriteTransferCount
));
260 ProcessMetrics::ProcessMetrics(ProcessHandle process
)
262 last_system_time_(0),
264 processor_count_
= base::SysInfo::NumberOfProcessors();
267 #if defined(OS_CHROMEOS)
268 // Private, Shared and Proportional working set sizes are obtained from
269 // /proc/<pid>/totmaps
270 bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes
*ws_usage
)
272 // The format of /proc/<pid>/totmaps is:
276 // Shared_Clean: 1008 kB
277 // Shared_Dirty: 4012 kB
278 // Private_Clean: 4 kB
279 // Private_Dirty: 1096 kB
280 // Referenced: XXX kB
282 // AnonHugePages: XXX kB
285 const size_t kPssIndex
= (1 * 3) + 1;
286 const size_t kPrivate_CleanIndex
= (4 * 3) + 1;
287 const size_t kPrivate_DirtyIndex
= (5 * 3) + 1;
288 const size_t kSwapIndex
= (9 * 3) + 1;
290 std::string totmaps_data
;
292 FilePath totmaps_file
= internal::GetProcPidDir(process_
).Append("totmaps");
293 ThreadRestrictions::ScopedAllowIO allow_io
;
294 bool ret
= ReadFileToString(totmaps_file
, &totmaps_data
);
295 if (!ret
|| totmaps_data
.length() == 0)
299 std::vector
<std::string
> totmaps_fields
;
300 SplitStringAlongWhitespace(totmaps_data
, &totmaps_fields
);
302 DCHECK_EQ("Pss:", totmaps_fields
[kPssIndex
-1]);
303 DCHECK_EQ("Private_Clean:", totmaps_fields
[kPrivate_CleanIndex
- 1]);
304 DCHECK_EQ("Private_Dirty:", totmaps_fields
[kPrivate_DirtyIndex
- 1]);
305 DCHECK_EQ("Swap:", totmaps_fields
[kSwapIndex
-1]);
308 int private_clean
= 0;
309 int private_dirty
= 0;
312 ret
&= StringToInt(totmaps_fields
[kPssIndex
], &pss
);
313 ret
&= StringToInt(totmaps_fields
[kPrivate_CleanIndex
], &private_clean
);
314 ret
&= StringToInt(totmaps_fields
[kPrivate_DirtyIndex
], &private_dirty
);
315 ret
&= StringToInt(totmaps_fields
[kSwapIndex
], &swap
);
317 // On ChromeOS swap is to zram. We count this as private / shared, as
318 // increased swap decreases available RAM to user processes, which would
319 // otherwise create surprising results.
320 ws_usage
->priv
= private_clean
+ private_dirty
+ swap
;
321 ws_usage
->shared
= pss
+ swap
;
322 ws_usage
->shareable
= 0;
323 ws_usage
->swapped
= swap
;
328 // Private and Shared working set sizes are obtained from /proc/<pid>/statm.
329 bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes
* ws_usage
)
331 // Use statm instead of smaps because smaps is:
332 // a) Large and slow to parse.
333 // b) Unavailable in the SUID sandbox.
335 // First we need to get the page size, since everything is measured in pages.
336 // For details, see: man 5 proc.
337 const int page_size_kb
= getpagesize() / 1024;
338 if (page_size_kb
<= 0)
343 FilePath statm_file
= internal::GetProcPidDir(process_
).Append("statm");
344 // Synchronously reading files in /proc is safe.
345 ThreadRestrictions::ScopedAllowIO allow_io
;
346 bool ret
= ReadFileToString(statm_file
, &statm
);
347 if (!ret
|| statm
.length() == 0)
351 std::vector
<std::string
> statm_vec
;
352 SplitString(statm
, ' ', &statm_vec
);
353 if (statm_vec
.size() != 7)
354 return false; // Not the format we expect.
356 int statm_rss
, statm_shared
;
358 ret
&= StringToInt(statm_vec
[1], &statm_rss
);
359 ret
&= StringToInt(statm_vec
[2], &statm_shared
);
361 ws_usage
->priv
= (statm_rss
- statm_shared
) * page_size_kb
;
362 ws_usage
->shared
= statm_shared
* page_size_kb
;
364 // Sharable is not calculated, as it does not provide interesting data.
365 ws_usage
->shareable
= 0;
367 #if defined(OS_CHROMEOS)
368 // Can't get swapped memory from statm.
369 ws_usage
->swapped
= 0;
375 size_t GetSystemCommitCharge() {
376 SystemMemoryInfoKB meminfo
;
377 if (!GetSystemMemoryInfo(&meminfo
))
379 return meminfo
.total
- meminfo
.free
- meminfo
.buffers
- meminfo
.cached
;
382 // Exposed for testing.
383 int ParseProcStatCPU(const std::string
& input
) {
384 std::vector
<std::string
> proc_stats
;
385 if (!internal::ParseProcStats(input
, &proc_stats
))
388 if (proc_stats
.size() <= internal::VM_STIME
)
390 int utime
= GetProcStatsFieldAsInt64(proc_stats
, internal::VM_UTIME
);
391 int stime
= GetProcStatsFieldAsInt64(proc_stats
, internal::VM_STIME
);
392 return utime
+ stime
;
395 const char kProcSelfExe
[] = "/proc/self/exe";
397 int GetNumberOfThreads(ProcessHandle process
) {
398 return internal::ReadProcStatsAndGetFieldAsInt64(process
,
399 internal::VM_NUMTHREADS
);
404 // The format of /proc/diskstats is:
405 // Device major number
406 // Device minor number
408 // Field 1 -- # of reads completed
409 // This is the total number of reads completed successfully.
410 // Field 2 -- # of reads merged, field 6 -- # of writes merged
411 // Reads and writes which are adjacent to each other may be merged for
412 // efficiency. Thus two 4K reads may become one 8K read before it is
413 // ultimately handed to the disk, and so it will be counted (and queued)
414 // as only one I/O. This field lets you know how often this was done.
415 // Field 3 -- # of sectors read
416 // This is the total number of sectors read successfully.
417 // Field 4 -- # of milliseconds spent reading
418 // This is the total number of milliseconds spent by all reads (as
419 // measured from __make_request() to end_that_request_last()).
420 // Field 5 -- # of writes completed
421 // This is the total number of writes completed successfully.
422 // Field 6 -- # of writes merged
423 // See the description of field 2.
424 // Field 7 -- # of sectors written
425 // This is the total number of sectors written successfully.
426 // Field 8 -- # of milliseconds spent writing
427 // This is the total number of milliseconds spent by all writes (as
428 // measured from __make_request() to end_that_request_last()).
429 // Field 9 -- # of I/Os currently in progress
430 // The only field that should go to zero. Incremented as requests are
431 // given to appropriate struct request_queue and decremented as they
433 // Field 10 -- # of milliseconds spent doing I/Os
434 // This field increases so long as field 9 is nonzero.
435 // Field 11 -- weighted # of milliseconds spent doing I/Os
436 // This field is incremented at each I/O start, I/O completion, I/O
437 // merge, or read of these stats by the number of I/Os in progress
438 // (field 9) times the number of milliseconds spent doing I/O since the
439 // last update of this field. This can provide an easy measure of both
440 // I/O completion time and the backlog that may be accumulating.
442 const size_t kDiskDriveName
= 2;
443 const size_t kDiskReads
= 3;
444 const size_t kDiskReadsMerged
= 4;
445 const size_t kDiskSectorsRead
= 5;
446 const size_t kDiskReadTime
= 6;
447 const size_t kDiskWrites
= 7;
448 const size_t kDiskWritesMerged
= 8;
449 const size_t kDiskSectorsWritten
= 9;
450 const size_t kDiskWriteTime
= 10;
451 const size_t kDiskIO
= 11;
452 const size_t kDiskIOTime
= 12;
453 const size_t kDiskWeightedIOTime
= 13;
457 SystemMemoryInfoKB::SystemMemoryInfoKB() {
482 scoped_ptr
<Value
> SystemMemoryInfoKB::ToValue() const {
483 scoped_ptr
<DictionaryValue
> res(new base::DictionaryValue());
485 res
->SetInteger("total", total
);
486 res
->SetInteger("free", free
);
487 res
->SetInteger("buffers", buffers
);
488 res
->SetInteger("cached", cached
);
489 res
->SetInteger("active_anon", active_anon
);
490 res
->SetInteger("inactive_anon", inactive_anon
);
491 res
->SetInteger("active_file", active_file
);
492 res
->SetInteger("inactive_file", inactive_file
);
493 res
->SetInteger("swap_total", swap_total
);
494 res
->SetInteger("swap_free", swap_free
);
495 res
->SetInteger("swap_used", swap_total
- swap_free
);
496 res
->SetInteger("dirty", dirty
);
497 res
->SetInteger("pswpin", pswpin
);
498 res
->SetInteger("pswpout", pswpout
);
499 res
->SetInteger("pgmajfault", pgmajfault
);
501 res
->SetInteger("shmem", shmem
);
502 res
->SetInteger("slab", slab
);
503 res
->SetInteger("gem_objects", gem_objects
);
504 res
->SetInteger("gem_size", gem_size
);
507 return res
.PassAs
<Value
>();
510 // exposed for testing
511 bool ParseProcMeminfo(const std::string
& meminfo_data
,
512 SystemMemoryInfoKB
* meminfo
) {
513 // The format of /proc/meminfo is:
515 // MemTotal: 8235324 kB
516 // MemFree: 1628304 kB
517 // Buffers: 429596 kB
518 // Cached: 4728232 kB
520 // There is no guarantee on the ordering or position
521 // though it doesn't appear to change very often
523 // As a basic sanity check, let's make sure we at least get non-zero
527 std::vector
<std::string
> meminfo_lines
;
528 Tokenize(meminfo_data
, "\n", &meminfo_lines
);
529 for (std::vector
<std::string
>::iterator it
= meminfo_lines
.begin();
530 it
!= meminfo_lines
.end(); ++it
) {
531 std::vector
<std::string
> tokens
;
532 SplitStringAlongWhitespace(*it
, &tokens
);
533 // HugePages_* only has a number and no suffix so we can't rely on
534 // there being exactly 3 tokens.
535 if (tokens
.size() > 1) {
536 if (tokens
[0] == "MemTotal:") {
537 StringToInt(tokens
[1], &meminfo
->total
);
539 } if (tokens
[0] == "MemFree:") {
540 StringToInt(tokens
[1], &meminfo
->free
);
542 } if (tokens
[0] == "Buffers:") {
543 StringToInt(tokens
[1], &meminfo
->buffers
);
545 } if (tokens
[0] == "Cached:") {
546 StringToInt(tokens
[1], &meminfo
->cached
);
548 } if (tokens
[0] == "Active(anon):") {
549 StringToInt(tokens
[1], &meminfo
->active_anon
);
551 } if (tokens
[0] == "Inactive(anon):") {
552 StringToInt(tokens
[1], &meminfo
->inactive_anon
);
554 } if (tokens
[0] == "Active(file):") {
555 StringToInt(tokens
[1], &meminfo
->active_file
);
557 } if (tokens
[0] == "Inactive(file):") {
558 StringToInt(tokens
[1], &meminfo
->inactive_file
);
560 } if (tokens
[0] == "SwapTotal:") {
561 StringToInt(tokens
[1], &meminfo
->swap_total
);
563 } if (tokens
[0] == "SwapFree:") {
564 StringToInt(tokens
[1], &meminfo
->swap_free
);
566 } if (tokens
[0] == "Dirty:") {
567 StringToInt(tokens
[1], &meminfo
->dirty
);
569 #if defined(OS_CHROMEOS)
570 // Chrome OS has a tweaked kernel that allows us to query Shmem, which is
571 // usually video memory otherwise invisible to the OS.
572 } if (tokens
[0] == "Shmem:") {
573 StringToInt(tokens
[1], &meminfo
->shmem
);
575 } if (tokens
[0] == "Slab:") {
576 StringToInt(tokens
[1], &meminfo
->slab
);
581 DLOG(WARNING
) << "meminfo: tokens: " << tokens
.size()
582 << " malformed line: " << *it
;
585 // Make sure we got a valid MemTotal.
592 // exposed for testing
593 bool ParseProcVmstat(const std::string
& vmstat_data
,
594 SystemMemoryInfoKB
* meminfo
) {
595 // The format of /proc/vmstat is:
597 // nr_free_pages 299878
598 // nr_inactive_anon 239863
599 // nr_active_anon 1318966
600 // nr_inactive_file 2015629
603 // We iterate through the whole file because the position of the
604 // fields are dependent on the kernel version and configuration.
606 std::vector
<std::string
> vmstat_lines
;
607 Tokenize(vmstat_data
, "\n", &vmstat_lines
);
608 for (std::vector
<std::string
>::iterator it
= vmstat_lines
.begin();
609 it
!= vmstat_lines
.end(); ++it
) {
610 std::vector
<std::string
> tokens
;
611 SplitString(*it
, ' ', &tokens
);
612 if (tokens
.size() == 2) {
613 if (tokens
[0] == "pswpin") {
614 StringToInt(tokens
[1], &meminfo
->pswpin
);
616 } if (tokens
[0] == "pswpout") {
617 StringToInt(tokens
[1], &meminfo
->pswpout
);
619 } if (tokens
[0] == "pgmajfault")
620 StringToInt(tokens
[1], &meminfo
->pgmajfault
);
627 bool GetSystemMemoryInfo(SystemMemoryInfoKB
* meminfo
) {
628 // Synchronously reading files in /proc is safe.
629 ThreadRestrictions::ScopedAllowIO allow_io
;
631 // Used memory is: total - free - buffers - caches
632 FilePath
meminfo_file("/proc/meminfo");
633 std::string meminfo_data
;
634 if (!ReadFileToString(meminfo_file
, &meminfo_data
)) {
635 DLOG(WARNING
) << "Failed to open " << meminfo_file
.value();
639 if (!ParseProcMeminfo(meminfo_data
, meminfo
)) {
640 DLOG(WARNING
) << "Failed to parse " << meminfo_file
.value();
644 #if defined(OS_CHROMEOS)
645 // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a
646 // bind mount into /sys/kernel/debug and synchronously reading the in-memory
647 // files in /sys is fast.
648 #if defined(ARCH_CPU_ARM_FAMILY)
649 FilePath
geminfo_file("/var/run/debugfs_gpu/exynos_gem_objects");
651 FilePath
geminfo_file("/var/run/debugfs_gpu/i915_gem_objects");
653 std::string geminfo_data
;
654 meminfo
->gem_objects
= -1;
655 meminfo
->gem_size
= -1;
656 if (ReadFileToString(geminfo_file
, &geminfo_data
)) {
657 int gem_objects
= -1;
658 long long gem_size
= -1;
659 int num_res
= sscanf(geminfo_data
.c_str(),
660 "%d objects, %lld bytes",
661 &gem_objects
, &gem_size
);
663 meminfo
->gem_objects
= gem_objects
;
664 meminfo
->gem_size
= gem_size
;
668 #if defined(ARCH_CPU_ARM_FAMILY)
669 // Incorporate Mali graphics memory if present.
670 FilePath
mali_memory_file("/sys/class/misc/mali0/device/memory");
671 std::string mali_memory_data
;
672 if (ReadFileToString(mali_memory_file
, &mali_memory_data
)) {
673 long long mali_size
= -1;
674 int num_res
= sscanf(mali_memory_data
.c_str(), "%lld bytes", &mali_size
);
676 meminfo
->gem_size
+= mali_size
;
678 #endif // defined(ARCH_CPU_ARM_FAMILY)
679 #endif // defined(OS_CHROMEOS)
681 FilePath
vmstat_file("/proc/vmstat");
682 std::string vmstat_data
;
683 if (!ReadFileToString(vmstat_file
, &vmstat_data
)) {
684 DLOG(WARNING
) << "Failed to open " << vmstat_file
.value();
687 if (!ParseProcVmstat(vmstat_data
, meminfo
)) {
688 DLOG(WARNING
) << "Failed to parse " << vmstat_file
.value();
695 SystemDiskInfo::SystemDiskInfo() {
706 weighted_io_time
= 0;
709 scoped_ptr
<Value
> SystemDiskInfo::ToValue() const {
710 scoped_ptr
<DictionaryValue
> res(new base::DictionaryValue());
712 // Write out uint64 variables as doubles.
713 // Note: this may discard some precision, but for JS there's no other option.
714 res
->SetDouble("reads", static_cast<double>(reads
));
715 res
->SetDouble("reads_merged", static_cast<double>(reads_merged
));
716 res
->SetDouble("sectors_read", static_cast<double>(sectors_read
));
717 res
->SetDouble("read_time", static_cast<double>(read_time
));
718 res
->SetDouble("writes", static_cast<double>(writes
));
719 res
->SetDouble("writes_merged", static_cast<double>(writes_merged
));
720 res
->SetDouble("sectors_written", static_cast<double>(sectors_written
));
721 res
->SetDouble("write_time", static_cast<double>(write_time
));
722 res
->SetDouble("io", static_cast<double>(io
));
723 res
->SetDouble("io_time", static_cast<double>(io_time
));
724 res
->SetDouble("weighted_io_time", static_cast<double>(weighted_io_time
));
726 return res
.PassAs
<Value
>();
729 bool IsValidDiskName(const std::string
& candidate
) {
730 if (candidate
.length() < 3)
732 if (candidate
.substr(0,2) == "sd" || candidate
.substr(0,2) == "hd") {
734 for (size_t i
= 2; i
< candidate
.length(); i
++) {
735 if (!islower(candidate
[i
]))
739 if (candidate
.length() < 7) {
742 if (candidate
.substr(0,6) == "mmcblk") {
744 for (size_t i
= 6; i
< candidate
.length(); i
++) {
745 if (!isdigit(candidate
[i
]))
756 bool GetSystemDiskInfo(SystemDiskInfo
* diskinfo
) {
757 // Synchronously reading files in /proc is safe.
758 ThreadRestrictions::ScopedAllowIO allow_io
;
760 FilePath
diskinfo_file("/proc/diskstats");
761 std::string diskinfo_data
;
762 if (!ReadFileToString(diskinfo_file
, &diskinfo_data
)) {
763 DLOG(WARNING
) << "Failed to open " << diskinfo_file
.value();
767 std::vector
<std::string
> diskinfo_lines
;
768 size_t line_count
= Tokenize(diskinfo_data
, "\n", &diskinfo_lines
);
769 if (line_count
== 0) {
770 DLOG(WARNING
) << "No lines found";
775 diskinfo
->reads_merged
= 0;
776 diskinfo
->sectors_read
= 0;
777 diskinfo
->read_time
= 0;
778 diskinfo
->writes
= 0;
779 diskinfo
->writes_merged
= 0;
780 diskinfo
->sectors_written
= 0;
781 diskinfo
->write_time
= 0;
783 diskinfo
->io_time
= 0;
784 diskinfo
->weighted_io_time
= 0;
787 uint64 reads_merged
= 0;
788 uint64 sectors_read
= 0;
789 uint64 read_time
= 0;
791 uint64 writes_merged
= 0;
792 uint64 sectors_written
= 0;
793 uint64 write_time
= 0;
796 uint64 weighted_io_time
= 0;
798 for (size_t i
= 0; i
< line_count
; i
++) {
799 std::vector
<std::string
> disk_fields
;
800 SplitStringAlongWhitespace(diskinfo_lines
[i
], &disk_fields
);
802 // Fields may have overflowed and reset to zero.
803 if (IsValidDiskName(disk_fields
[kDiskDriveName
])) {
804 StringToUint64(disk_fields
[kDiskReads
], &reads
);
805 StringToUint64(disk_fields
[kDiskReadsMerged
], &reads_merged
);
806 StringToUint64(disk_fields
[kDiskSectorsRead
], §ors_read
);
807 StringToUint64(disk_fields
[kDiskReadTime
], &read_time
);
808 StringToUint64(disk_fields
[kDiskWrites
], &writes
);
809 StringToUint64(disk_fields
[kDiskWritesMerged
], &writes_merged
);
810 StringToUint64(disk_fields
[kDiskSectorsWritten
], §ors_written
);
811 StringToUint64(disk_fields
[kDiskWriteTime
], &write_time
);
812 StringToUint64(disk_fields
[kDiskIO
], &io
);
813 StringToUint64(disk_fields
[kDiskIOTime
], &io_time
);
814 StringToUint64(disk_fields
[kDiskWeightedIOTime
], &weighted_io_time
);
816 diskinfo
->reads
+= reads
;
817 diskinfo
->reads_merged
+= reads_merged
;
818 diskinfo
->sectors_read
+= sectors_read
;
819 diskinfo
->read_time
+= read_time
;
820 diskinfo
->writes
+= writes
;
821 diskinfo
->writes_merged
+= writes_merged
;
822 diskinfo
->sectors_written
+= sectors_written
;
823 diskinfo
->write_time
+= write_time
;
825 diskinfo
->io_time
+= io_time
;
826 diskinfo
->weighted_io_time
+= weighted_io_time
;
833 #if defined(OS_CHROMEOS)
834 scoped_ptr
<Value
> SwapInfo::ToValue() const {
835 scoped_ptr
<DictionaryValue
> res(new DictionaryValue());
837 // Write out uint64 variables as doubles.
838 // Note: this may discard some precision, but for JS there's no other option.
839 res
->SetDouble("num_reads", static_cast<double>(num_reads
));
840 res
->SetDouble("num_writes", static_cast<double>(num_writes
));
841 res
->SetDouble("orig_data_size", static_cast<double>(orig_data_size
));
842 res
->SetDouble("compr_data_size", static_cast<double>(compr_data_size
));
843 res
->SetDouble("mem_used_total", static_cast<double>(mem_used_total
));
844 if (compr_data_size
> 0)
845 res
->SetDouble("compression_ratio", static_cast<double>(orig_data_size
) /
846 static_cast<double>(compr_data_size
));
848 res
->SetDouble("compression_ratio", 0);
850 return res
.PassAs
<Value
>();
853 void GetSwapInfo(SwapInfo
* swap_info
) {
854 // Synchronously reading files in /sys/block/zram0 is safe.
855 ThreadRestrictions::ScopedAllowIO allow_io
;
857 base::FilePath
zram_path("/sys/block/zram0");
858 uint64 orig_data_size
= ReadFileToUint64(zram_path
.Append("orig_data_size"));
859 if (orig_data_size
<= 4096) {
860 // A single page is compressed at startup, and has a high compression
861 // ratio. We ignore this as it doesn't indicate any real swapping.
862 swap_info
->orig_data_size
= 0;
863 swap_info
->num_reads
= 0;
864 swap_info
->num_writes
= 0;
865 swap_info
->compr_data_size
= 0;
866 swap_info
->mem_used_total
= 0;
869 swap_info
->orig_data_size
= orig_data_size
;
870 swap_info
->num_reads
= ReadFileToUint64(zram_path
.Append("num_reads"));
871 swap_info
->num_writes
= ReadFileToUint64(zram_path
.Append("num_writes"));
872 swap_info
->compr_data_size
=
873 ReadFileToUint64(zram_path
.Append("compr_data_size"));
874 swap_info
->mem_used_total
=
875 ReadFileToUint64(zram_path
.Append("mem_used_total"));
877 #endif // defined(OS_CHROMEOS)