process_metrics_linux.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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. 4 5#include "base/process/process_metrics.h" 6 7#include <dirent.h> 8#include <fcntl.h> 9#include <sys/stat.h> 10#include <sys/time.h> 11#include <sys/types.h> 12#include <unistd.h> 13 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" 23 24namespace base { 25 26namespace { 27 28enum ParsingState { 29 KEY_NAME, 30 KEY_VALUE 31}; 32 33#ifdef OS_CHROMEOS 34// Read a file with a single number string and return the number as a uint64. 35static uint64 ReadFileToUint64(const base::FilePath file) { 36 std::string file_as_string; 37 if (!ReadFileToString(file, &file_as_string)) 38 return 0; 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)) 42 return 0; 43 return file_as_uint64; 44} 45#endif 46 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". 49size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { 50 FilePath stat_file = internal::GetProcPidDir(pid).Append("status"); 51 std::string status; 52 { 53 // Synchronously reading files in /proc is safe. 54 ThreadRestrictions::ScopedAllowIO allow_io; 55 if (!ReadFileToString(stat_file, &status)) 56 return 0; 57 } 58 59 StringTokenizer tokenizer(status, ":\n"); 60 ParsingState state = KEY_NAME; 61 StringPiece last_key_name; 62 while (tokenizer.GetNext()) { 63 switch (state) { 64 case KEY_NAME: 65 last_key_name = tokenizer.token_piece(); 66 state = KEY_VALUE; 67 break; 68 case KEY_VALUE: 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, 75 &value_str_trimmed); 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") { 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 } 89 state = KEY_NAME; 90 break; 91 } 92 } 93 NOTREACHED(); 94 return 0; 95} 96 97// Get the total CPU of a single process. Return value is number of jiffies 98// on success or -1 on error. 99int 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"); 102 103 DIR* dir = opendir(task_path.value().c_str()); 104 if (!dir) { 105 DPLOG(ERROR) << "opendir(" << task_path.value() << ")"; 106 return -1; 107 } 108 109 int total_cpu = 0; 110 while (struct dirent* ent = readdir(dir)) { 111 pid_t tid = internal::ProcDirSlotToPid(ent->d_name); 112 if (!tid) 113 continue; 114 115 // Synchronously reading files in /proc is safe. 116 ThreadRestrictions::ScopedAllowIO allow_io; 117 118 std::string stat; 119 FilePath stat_path = 120 task_path.Append(ent->d_name).Append(internal::kStatFile); 121 if (ReadFileToString(stat_path, &stat)) { 122 int cpu = ParseProcStatCPU(stat); 123 if (cpu > 0) 124 total_cpu += cpu; 125 } 126 } 127 closedir(dir); 128 129 return total_cpu; 130} 131 132} // namespace 133 134// static 135ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { 136 return new ProcessMetrics(process); 137} 138 139// On linux, we return vsize. 140size_t ProcessMetrics::GetPagefileUsage() const { 141 return internal::ReadProcStatsAndGetFieldAsSizeT(process_, 142 internal::VM_VSIZE); 143} 144 145// On linux, we return the high water mark of vsize. 146size_t ProcessMetrics::GetPeakPagefileUsage() const { 147 return ReadProcStatusAndGetFieldAsSizeT(process_, "VmPeak") * 1024; 148} 149 150// On linux, we return RSS. 151size_t ProcessMetrics::GetWorkingSetSize() const { 152 return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) * 153 getpagesize(); 154} 155 156// On linux, we return the high water mark of RSS. 157size_t ProcessMetrics::GetPeakWorkingSetSize() const { 158 return ReadProcStatusAndGetFieldAsSizeT(process_, "VmHWM") * 1024; 159} 160 161bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, 162 size_t* shared_bytes) { 163 WorkingSetKBytes ws_usage; 164 if (!GetWorkingSetKBytes(&ws_usage)) 165 return false; 166 167 if (private_bytes) 168 *private_bytes = ws_usage.priv * 1024; 169 170 if (shared_bytes) 171 *shared_bytes = ws_usage.shared * 1024; 172 173 return true; 174} 175 176bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { 177#if defined(OS_CHROMEOS) 178 if (GetWorkingSetKBytesTotmaps(ws_usage)) 179 return true; 180#endif 181 return GetWorkingSetKBytesStatm(ws_usage); 182} 183 184double ProcessMetrics::GetCPUUsage() { 185 struct timeval now; 186 int retval = gettimeofday(&now, NULL); 187 if (retval) 188 return 0; 189 int64 time = TimeValToMicroseconds(now); 190 191 if (last_cpu_time_ == 0) { 192 // First call, just set the last values. 193 last_cpu_time_ = time; 194 last_cpu_ = GetProcessCPU(process_); 195 return 0; 196 } 197 198 int64 time_delta = time - last_cpu_time_; 199 DCHECK_NE(time_delta, 0); 200 if (time_delta == 0) 201 return 0; 202 203 int cpu = GetProcessCPU(process_); 204 205 // We have the number of jiffies in the time period. Convert to percentage. 206 // Note this means we will go *over* 100 in the case where multiple threads 207 // are together adding to more than one CPU's worth. 208 TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu); 209 TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_); 210 int percentage = 100 * (cpu_time - last_cpu_time).InSecondsF() / 211 TimeDelta::FromMicroseconds(time_delta).InSecondsF(); 212 213 last_cpu_time_ = time; 214 last_cpu_ = cpu; 215 216 return percentage; 217} 218 219// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING 220// in your kernel configuration. 221bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { 222 // Synchronously reading files in /proc is safe. 223 ThreadRestrictions::ScopedAllowIO allow_io; 224 225 std::string proc_io_contents; 226 FilePath io_file = internal::GetProcPidDir(process_).Append("io"); 227 if (!ReadFileToString(io_file, &proc_io_contents)) 228 return false; 229 230 (*io_counters).OtherOperationCount = 0; 231 (*io_counters).OtherTransferCount = 0; 232 233 StringTokenizer tokenizer(proc_io_contents, ": \n"); 234 ParsingState state = KEY_NAME; 235 StringPiece last_key_name; 236 while (tokenizer.GetNext()) { 237 switch (state) { 238 case KEY_NAME: 239 last_key_name = tokenizer.token_piece(); 240 state = KEY_VALUE; 241 break; 242 case KEY_VALUE: 243 DCHECK(!last_key_name.empty()); 244 if (last_key_name == "syscr") { 245 StringToInt64(tokenizer.token_piece(), 246 reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount)); 247 } else if (last_key_name == "syscw") { 248 StringToInt64(tokenizer.token_piece(), 249 reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount)); 250 } else if (last_key_name == "rchar") { 251 StringToInt64(tokenizer.token_piece(), 252 reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount)); 253 } else if (last_key_name == "wchar") { 254 StringToInt64(tokenizer.token_piece(), 255 reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount)); 256 } 257 state = KEY_NAME; 258 break; 259 } 260 } 261 return true; 262} 263 264ProcessMetrics::ProcessMetrics(ProcessHandle process) 265 : process_(process), 266 last_cpu_time_(0), 267 last_system_time_(0), 268 last_cpu_(0) { 269 processor_count_ = base::SysInfo::NumberOfProcessors(); 270} 271 272#if defined(OS_CHROMEOS) 273// Private, Shared and Proportional working set sizes are obtained from 274// /proc/<pid>/totmaps 275bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) 276 const { 277 // The format of /proc/<pid>/totmaps is: 278 // 279 // Rss: 6120 kB 280 // Pss: 3335 kB 281 // Shared_Clean: 1008 kB 282 // Shared_Dirty: 4012 kB 283 // Private_Clean: 4 kB 284 // Private_Dirty: 1096 kB 285 // Referenced: XXX kB 286 // Anonymous: XXX kB 287 // AnonHugePages: XXX kB 288 // Swap: XXX kB 289 // Locked: XXX kB 290 const size_t kPssIndex = (1 * 3) + 1; 291 const size_t kPrivate_CleanIndex = (4 * 3) + 1; 292 const size_t kPrivate_DirtyIndex = (5 * 3) + 1; 293 const size_t kSwapIndex = (9 * 3) + 1; 294 295 std::string totmaps_data; 296 { 297 FilePath totmaps_file = internal::GetProcPidDir(process_).Append("totmaps"); 298 ThreadRestrictions::ScopedAllowIO allow_io; 299 bool ret = ReadFileToString(totmaps_file, &totmaps_data); 300 if (!ret || totmaps_data.length() == 0) 301 return false; 302 } 303 304 std::vector<std::string> totmaps_fields; 305 SplitStringAlongWhitespace(totmaps_data, &totmaps_fields); 306 307 DCHECK_EQ("Pss:", totmaps_fields[kPssIndex-1]); 308 DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]); 309 DCHECK_EQ("Private_Dirty:", totmaps_fields[kPrivate_DirtyIndex - 1]); 310 DCHECK_EQ("Swap:", totmaps_fields[kSwapIndex-1]); 311 312 int pss = 0; 313 int private_clean = 0; 314 int private_dirty = 0; 315 int swap = 0; 316 bool ret = true; 317 ret &= StringToInt(totmaps_fields[kPssIndex], &pss); 318 ret &= StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean); 319 ret &= StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty); 320 ret &= StringToInt(totmaps_fields[kSwapIndex], &swap); 321 322 // On ChromeOS swap is to zram. We count this as private / shared, as 323 // increased swap decreases available RAM to user processes, which would 324 // otherwise create surprising results. 325 ws_usage->priv = private_clean + private_dirty + swap; 326 ws_usage->shared = pss + swap; 327 ws_usage->shareable = 0; 328 ws_usage->swapped = swap; 329 return ret; 330} 331#endif 332 333// Private and Shared working set sizes are obtained from /proc/<pid>/statm. 334bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) 335 const { 336 // Use statm instead of smaps because smaps is: 337 // a) Large and slow to parse. 338 // b) Unavailable in the SUID sandbox. 339 340 // First we need to get the page size, since everything is measured in pages. 341 // For details, see: man 5 proc. 342 const int page_size_kb = getpagesize() / 1024; 343 if (page_size_kb <= 0) 344 return false; 345 346 std::string statm; 347 { 348 FilePath statm_file = internal::GetProcPidDir(process_).Append("statm"); 349 // Synchronously reading files in /proc is safe. 350 ThreadRestrictions::ScopedAllowIO allow_io; 351 bool ret = ReadFileToString(statm_file, &statm); 352 if (!ret || statm.length() == 0) 353 return false; 354 } 355 356 std::vector<std::string> statm_vec; 357 SplitString(statm, ' ', &statm_vec); 358 if (statm_vec.size() != 7) 359 return false; // Not the format we expect. 360 361 int statm_rss, statm_shared; 362 bool ret = true; 363 ret &= StringToInt(statm_vec[1], &statm_rss); 364 ret &= StringToInt(statm_vec[2], &statm_shared); 365 366 ws_usage->priv = (statm_rss - statm_shared) * page_size_kb; 367 ws_usage->shared = statm_shared * page_size_kb; 368 369 // Sharable is not calculated, as it does not provide interesting data. 370 ws_usage->shareable = 0; 371 372#if defined(OS_CHROMEOS) 373 // Can't get swapped memory from statm. 374 ws_usage->swapped = 0; 375#endif 376 377 return ret; 378} 379 380size_t GetSystemCommitCharge() { 381 SystemMemoryInfoKB meminfo; 382 if (!GetSystemMemoryInfo(&meminfo)) 383 return 0; 384 return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached; 385} 386 387// Exposed for testing. 388int ParseProcStatCPU(const std::string& input) { 389 std::vector<std::string> proc_stats; 390 if (!internal::ParseProcStats(input, &proc_stats)) 391 return -1; 392 393 if (proc_stats.size() <= internal::VM_STIME) 394 return -1; 395 int utime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME); 396 int stime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME); 397 return utime + stime; 398} 399 400const char kProcSelfExe[] = "/proc/self/exe"; 401 402int GetNumberOfThreads(ProcessHandle process) { 403 return internal::ReadProcStatsAndGetFieldAsInt64(process, 404 internal::VM_NUMTHREADS); 405} 406 407namespace { 408 409// The format of /proc/diskstats is: 410// Device major number 411// Device minor number 412// Device name 413// Field 1 -- # of reads completed 414// This is the total number of reads completed successfully. 415// Field 2 -- # of reads merged, field 6 -- # of writes merged 416// Reads and writes which are adjacent to each other may be merged for 417// efficiency. Thus two 4K reads may become one 8K read before it is 418// ultimately handed to the disk, and so it will be counted (and queued) 419// as only one I/O. This field lets you know how often this was done. 420// Field 3 -- # of sectors read 421// This is the total number of sectors read successfully. 422// Field 4 -- # of milliseconds spent reading 423// This is the total number of milliseconds spent by all reads (as 424// measured from __make_request() to end_that_request_last()). 425// Field 5 -- # of writes completed 426// This is the total number of writes completed successfully. 427// Field 6 -- # of writes merged 428// See the description of field 2. 429// Field 7 -- # of sectors written 430// This is the total number of sectors written successfully. 431// Field 8 -- # of milliseconds spent writing 432// This is the total number of milliseconds spent by all writes (as 433// measured from __make_request() to end_that_request_last()). 434// Field 9 -- # of I/Os currently in progress 435// The only field that should go to zero. Incremented as requests are 436// given to appropriate struct request_queue and decremented as they 437// finish. 438// Field 10 -- # of milliseconds spent doing I/Os 439// This field increases so long as field 9 is nonzero. 440// Field 11 -- weighted # of milliseconds spent doing I/Os 441// This field is incremented at each I/O start, I/O completion, I/O 442// merge, or read of these stats by the number of I/Os in progress 443// (field 9) times the number of milliseconds spent doing I/O since the 444// last update of this field. This can provide an easy measure of both 445// I/O completion time and the backlog that may be accumulating. 446 447const size_t kDiskDriveName = 2; 448const size_t kDiskReads = 3; 449const size_t kDiskReadsMerged = 4; 450const size_t kDiskSectorsRead = 5; 451const size_t kDiskReadTime = 6; 452const size_t kDiskWrites = 7; 453const size_t kDiskWritesMerged = 8; 454const size_t kDiskSectorsWritten = 9; 455const size_t kDiskWriteTime = 10; 456const size_t kDiskIO = 11; 457const size_t kDiskIOTime = 12; 458const size_t kDiskWeightedIOTime = 13; 459 460} // namespace 461 462SystemMemoryInfoKB::SystemMemoryInfoKB() { 463 total = 0; 464 free = 0; 465 buffers = 0; 466 cached = 0; 467 active_anon = 0; 468 inactive_anon = 0; 469 active_file = 0; 470 inactive_file = 0; 471 swap_total = 0; 472 swap_free = 0; 473 dirty = 0; 474 475 pswpin = 0; 476 pswpout = 0; 477 pgmajfault = 0; 478 479#ifdef OS_CHROMEOS 480 shmem = 0; 481 slab = 0; 482 gem_objects = -1; 483 gem_size = -1; 484#endif 485} 486 487scoped_ptr<Value> SystemMemoryInfoKB::ToValue() const { 488 scoped_ptr<DictionaryValue> res(new base::DictionaryValue()); 489 490 res->SetInteger("total", total); 491 res->SetInteger("free", free); 492 res->SetInteger("buffers", buffers); 493 res->SetInteger("cached", cached); 494 res->SetInteger("active_anon", active_anon); 495 res->SetInteger("inactive_anon", inactive_anon); 496 res->SetInteger("active_file", active_file); 497 res->SetInteger("inactive_file", inactive_file); 498 res->SetInteger("swap_total", swap_total); 499 res->SetInteger("swap_free", swap_free); 500 res->SetInteger("swap_used", swap_total - swap_free); 501 res->SetInteger("dirty", dirty); 502 res->SetInteger("pswpin", pswpin); 503 res->SetInteger("pswpout", pswpout); 504 res->SetInteger("pgmajfault", pgmajfault); 505#ifdef OS_CHROMEOS 506 res->SetInteger("shmem", shmem); 507 res->SetInteger("slab", slab); 508 res->SetInteger("gem_objects", gem_objects); 509 res->SetInteger("gem_size", gem_size); 510#endif 511 512 return res.PassAs<Value>(); 513} 514 515// exposed for testing 516bool ParseProcMeminfo(const std::string& meminfo_data, 517 SystemMemoryInfoKB* meminfo) { 518 // The format of /proc/meminfo is: 519 // 520 // MemTotal: 8235324 kB 521 // MemFree: 1628304 kB 522 // Buffers: 429596 kB 523 // Cached: 4728232 kB 524 // ... 525 // There is no guarantee on the ordering or position 526 // though it doesn't appear to change very often 527 528 // As a basic sanity check, let's make sure we at least get non-zero 529 // MemTotal value 530 meminfo->total = 0; 531 532 std::vector<std::string> meminfo_lines; 533 Tokenize(meminfo_data, "\n", &meminfo_lines); 534 for (std::vector<std::string>::iterator it = meminfo_lines.begin(); 535 it != meminfo_lines.end(); ++it) { 536 std::vector<std::string> tokens; 537 SplitStringAlongWhitespace(*it, &tokens); 538 // HugePages_* only has a number and no suffix so we can't rely on 539 // there being exactly 3 tokens. 540 if (tokens.size() > 1) { 541 if (tokens[0] == "MemTotal:") { 542 StringToInt(tokens[1], &meminfo->total); 543 continue; 544 } if (tokens[0] == "MemFree:") { 545 StringToInt(tokens[1], &meminfo->free); 546 continue; 547 } if (tokens[0] == "Buffers:") { 548 StringToInt(tokens[1], &meminfo->buffers); 549 continue; 550 } if (tokens[0] == "Cached:") { 551 StringToInt(tokens[1], &meminfo->cached); 552 continue; 553 } if (tokens[0] == "Active(anon):") { 554 StringToInt(tokens[1], &meminfo->active_anon); 555 continue; 556 } if (tokens[0] == "Inactive(anon):") { 557 StringToInt(tokens[1], &meminfo->inactive_anon); 558 continue; 559 } if (tokens[0] == "Active(file):") { 560 StringToInt(tokens[1], &meminfo->active_file); 561 continue; 562 } if (tokens[0] == "Inactive(file):") { 563 StringToInt(tokens[1], &meminfo->inactive_file); 564 continue; 565 } if (tokens[0] == "SwapTotal:") { 566 StringToInt(tokens[1], &meminfo->swap_total); 567 continue; 568 } if (tokens[0] == "SwapFree:") { 569 StringToInt(tokens[1], &meminfo->swap_free); 570 continue; 571 } if (tokens[0] == "Dirty:") { 572 StringToInt(tokens[1], &meminfo->dirty); 573 continue; 574#if defined(OS_CHROMEOS) 575 // Chrome OS has a tweaked kernel that allows us to query Shmem, which is 576 // usually video memory otherwise invisible to the OS. 577 } if (tokens[0] == "Shmem:") { 578 StringToInt(tokens[1], &meminfo->shmem); 579 continue; 580 } if (tokens[0] == "Slab:") { 581 StringToInt(tokens[1], &meminfo->slab); 582 continue; 583#endif 584 } 585 } else 586 DLOG(WARNING) << "meminfo: tokens: " << tokens.size() 587 << " malformed line: " << *it; 588 } 589 590 // Make sure we got a valid MemTotal. 591 if (!meminfo->total) 592 return false; 593 594 return true; 595} 596 597// exposed for testing 598bool ParseProcVmstat(const std::string& vmstat_data, 599 SystemMemoryInfoKB* meminfo) { 600 // The format of /proc/vmstat is: 601 // 602 // nr_free_pages 299878 603 // nr_inactive_anon 239863 604 // nr_active_anon 1318966 605 // nr_inactive_file 2015629 606 // ... 607 // 608 // We iterate through the whole file because the position of the 609 // fields are dependent on the kernel version and configuration. 610 611 std::vector<std::string> vmstat_lines; 612 Tokenize(vmstat_data, "\n", &vmstat_lines); 613 for (std::vector<std::string>::iterator it = vmstat_lines.begin(); 614 it != vmstat_lines.end(); ++it) { 615 std::vector<std::string> tokens; 616 SplitString(*it, ' ', &tokens); 617 if (tokens.size() == 2) { 618 if (tokens[0] == "pswpin") { 619 StringToInt(tokens[1], &meminfo->pswpin); 620 continue; 621 } if (tokens[0] == "pswpout") { 622 StringToInt(tokens[1], &meminfo->pswpout); 623 continue; 624 } if (tokens[0] == "pgmajfault") 625 StringToInt(tokens[1], &meminfo->pgmajfault); 626 } 627 } 628 629 return true; 630} 631 632bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { 633 // Synchronously reading files in /proc is safe. 634 ThreadRestrictions::ScopedAllowIO allow_io; 635 636 // Used memory is: total - free - buffers - caches 637 FilePath meminfo_file("/proc/meminfo"); 638 std::string meminfo_data; 639 if (!ReadFileToString(meminfo_file, &meminfo_data)) { 640 DLOG(WARNING) << "Failed to open " << meminfo_file.value(); 641 return false; 642 } 643 644 if (!ParseProcMeminfo(meminfo_data, meminfo)) { 645 DLOG(WARNING) << "Failed to parse " << meminfo_file.value(); 646 return false; 647 } 648 649#if defined(OS_CHROMEOS) 650 // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a 651 // bind mount into /sys/kernel/debug and synchronously reading the in-memory 652 // files in /sys is fast. 653#if defined(ARCH_CPU_ARM_FAMILY) 654 FilePath geminfo_file("/var/run/debugfs_gpu/exynos_gem_objects"); 655#else 656 FilePath geminfo_file("/var/run/debugfs_gpu/i915_gem_objects"); 657#endif 658 std::string geminfo_data; 659 meminfo->gem_objects = -1; 660 meminfo->gem_size = -1; 661 if (ReadFileToString(geminfo_file, &geminfo_data)) { 662 int gem_objects = -1; 663 long long gem_size = -1; 664 int num_res = sscanf(geminfo_data.c_str(), 665 "%d objects, %lld bytes", 666 &gem_objects, &gem_size); 667 if (num_res == 2) { 668 meminfo->gem_objects = gem_objects; 669 meminfo->gem_size = gem_size; 670 } 671 } 672 673#if defined(ARCH_CPU_ARM_FAMILY) 674 // Incorporate Mali graphics memory if present. 675 FilePath mali_memory_file("/sys/class/misc/mali0/device/memory"); 676 std::string mali_memory_data; 677 if (ReadFileToString(mali_memory_file, &mali_memory_data)) { 678 long long mali_size = -1; 679 int num_res = sscanf(mali_memory_data.c_str(), "%lld bytes", &mali_size); 680 if (num_res == 1) 681 meminfo->gem_size += mali_size; 682 } 683#endif // defined(ARCH_CPU_ARM_FAMILY) 684#endif // defined(OS_CHROMEOS) 685 686 FilePath vmstat_file("/proc/vmstat"); 687 std::string vmstat_data; 688 if (!ReadFileToString(vmstat_file, &vmstat_data)) { 689 DLOG(WARNING) << "Failed to open " << vmstat_file.value(); 690 return false; 691 } 692 if (!ParseProcVmstat(vmstat_data, meminfo)) { 693 DLOG(WARNING) << "Failed to parse " << vmstat_file.value(); 694 return false; 695 } 696 697 return true; 698} 699 700SystemDiskInfo::SystemDiskInfo() { 701 reads = 0; 702 reads_merged = 0; 703 sectors_read = 0; 704 read_time = 0; 705 writes = 0; 706 writes_merged = 0; 707 sectors_written = 0; 708 write_time = 0; 709 io = 0; 710 io_time = 0; 711 weighted_io_time = 0; 712} 713 714scoped_ptr<Value> SystemDiskInfo::ToValue() const { 715 scoped_ptr<DictionaryValue> res(new base::DictionaryValue()); 716 717 // Write out uint64 variables as doubles. 718 // Note: this may discard some precision, but for JS there's no other option. 719 res->SetDouble("reads", static_cast<double>(reads)); 720 res->SetDouble("reads_merged", static_cast<double>(reads_merged)); 721 res->SetDouble("sectors_read", static_cast<double>(sectors_read)); 722 res->SetDouble("read_time", static_cast<double>(read_time)); 723 res->SetDouble("writes", static_cast<double>(writes)); 724 res->SetDouble("writes_merged", static_cast<double>(writes_merged)); 725 res->SetDouble("sectors_written", static_cast<double>(sectors_written)); 726 res->SetDouble("write_time", static_cast<double>(write_time)); 727 res->SetDouble("io", static_cast<double>(io)); 728 res->SetDouble("io_time", static_cast<double>(io_time)); 729 res->SetDouble("weighted_io_time", static_cast<double>(weighted_io_time)); 730 731 return res.PassAs<Value>(); 732} 733 734bool IsValidDiskName(const std::string& candidate) { 735 if (candidate.length() < 3) 736 return false; 737 if (candidate.substr(0,2) == "sd" || candidate.substr(0,2) == "hd") { 738 // [sh]d[a-z]+ case 739 for (size_t i = 2; i < candidate.length(); i++) { 740 if (!islower(candidate[i])) 741 return false; 742 } 743 } else { 744 if (candidate.length() < 7) { 745 return false; 746 } 747 if (candidate.substr(0,6) == "mmcblk") { 748 // mmcblk[0-9]+ case 749 for (size_t i = 6; i < candidate.length(); i++) { 750 if (!isdigit(candidate[i])) 751 return false; 752 } 753 } else { 754 return false; 755 } 756 } 757 758 return true; 759} 760 761bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { 762 // Synchronously reading files in /proc is safe. 763 ThreadRestrictions::ScopedAllowIO allow_io; 764 765 FilePath diskinfo_file("/proc/diskstats"); 766 std::string diskinfo_data; 767 if (!ReadFileToString(diskinfo_file, &diskinfo_data)) { 768 DLOG(WARNING) << "Failed to open " << diskinfo_file.value(); 769 return false; 770 } 771 772 std::vector<std::string> diskinfo_lines; 773 size_t line_count = Tokenize(diskinfo_data, "\n", &diskinfo_lines); 774 if (line_count == 0) { 775 DLOG(WARNING) << "No lines found"; 776 return false; 777 } 778 779 diskinfo->reads = 0; 780 diskinfo->reads_merged = 0; 781 diskinfo->sectors_read = 0; 782 diskinfo->read_time = 0; 783 diskinfo->writes = 0; 784 diskinfo->writes_merged = 0; 785 diskinfo->sectors_written = 0; 786 diskinfo->write_time = 0; 787 diskinfo->io = 0; 788 diskinfo->io_time = 0; 789 diskinfo->weighted_io_time = 0; 790 791 uint64 reads = 0; 792 uint64 reads_merged = 0; 793 uint64 sectors_read = 0; 794 uint64 read_time = 0; 795 uint64 writes = 0; 796 uint64 writes_merged = 0; 797 uint64 sectors_written = 0; 798 uint64 write_time = 0; 799 uint64 io = 0; 800 uint64 io_time = 0; 801 uint64 weighted_io_time = 0; 802 803 for (size_t i = 0; i < line_count; i++) { 804 std::vector<std::string> disk_fields; 805 SplitStringAlongWhitespace(diskinfo_lines[i], &disk_fields); 806 807 // Fields may have overflowed and reset to zero. 808 if (IsValidDiskName(disk_fields[kDiskDriveName])) { 809 StringToUint64(disk_fields[kDiskReads], &reads); 810 StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged); 811 StringToUint64(disk_fields[kDiskSectorsRead], §ors_read); 812 StringToUint64(disk_fields[kDiskReadTime], &read_time); 813 StringToUint64(disk_fields[kDiskWrites], &writes); 814 StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged); 815 StringToUint64(disk_fields[kDiskSectorsWritten], §ors_written); 816 StringToUint64(disk_fields[kDiskWriteTime], &write_time); 817 StringToUint64(disk_fields[kDiskIO], &io); 818 StringToUint64(disk_fields[kDiskIOTime], &io_time); 819 StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time); 820 821 diskinfo->reads += reads; 822 diskinfo->reads_merged += reads_merged; 823 diskinfo->sectors_read += sectors_read; 824 diskinfo->read_time += read_time; 825 diskinfo->writes += writes; 826 diskinfo->writes_merged += writes_merged; 827 diskinfo->sectors_written += sectors_written; 828 diskinfo->write_time += write_time; 829 diskinfo->io += io; 830 diskinfo->io_time += io_time; 831 diskinfo->weighted_io_time += weighted_io_time; 832 } 833 } 834 835 return true; 836} 837 838#if defined(OS_CHROMEOS) 839scoped_ptr<Value> SwapInfo::ToValue() const { 840 scoped_ptr<DictionaryValue> res(new DictionaryValue()); 841 842 // Write out uint64 variables as doubles. 843 // Note: this may discard some precision, but for JS there's no other option. 844 res->SetDouble("num_reads", static_cast<double>(num_reads)); 845 res->SetDouble("num_writes", static_cast<double>(num_writes)); 846 res->SetDouble("orig_data_size", static_cast<double>(orig_data_size)); 847 res->SetDouble("compr_data_size", static_cast<double>(compr_data_size)); 848 res->SetDouble("mem_used_total", static_cast<double>(mem_used_total)); 849 if (compr_data_size > 0) 850 res->SetDouble("compression_ratio", static_cast<double>(orig_data_size) / 851 static_cast<double>(compr_data_size)); 852 else 853 res->SetDouble("compression_ratio", 0); 854 855 return res.PassAs<Value>(); 856} 857 858void GetSwapInfo(SwapInfo* swap_info) { 859 // Synchronously reading files in /sys/block/zram0 is safe. 860 ThreadRestrictions::ScopedAllowIO allow_io; 861 862 base::FilePath zram_path("/sys/block/zram0"); 863 uint64 orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size")); 864 if (orig_data_size <= 4096) { 865 // A single page is compressed at startup, and has a high compression 866 // ratio. We ignore this as it doesn't indicate any real swapping. 867 swap_info->orig_data_size = 0; 868 swap_info->num_reads = 0; 869 swap_info->num_writes = 0; 870 swap_info->compr_data_size = 0; 871 swap_info->mem_used_total = 0; 872 return; 873 } 874 swap_info->orig_data_size = orig_data_size; 875 swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads")); 876 swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes")); 877 swap_info->compr_data_size = 878 ReadFileToUint64(zram_path.Append("compr_data_size")); 879 swap_info->mem_used_total = 880 ReadFileToUint64(zram_path.Append("mem_used_total")); 881} 882#endif // defined(OS_CHROMEOS) 883 884} // namespace base 885