| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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_util.h" | 5 #include "base/process_util.h" |
| 6 | 6 |
| 7 #include <ctype.h> | 7 #include <ctype.h> |
| 8 #include <dirent.h> | 8 #include <dirent.h> |
| 9 #include <dlfcn.h> | 9 #include <dlfcn.h> |
| 10 #include <errno.h> | 10 #include <errno.h> |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 61 cmd_line_file = cmd_line_file.Append("cmdline"); | 61 cmd_line_file = cmd_line_file.Append("cmdline"); |
| 62 std::string cmd_line; | 62 std::string cmd_line; |
| 63 if (!file_util::ReadFileToString(cmd_line_file, &cmd_line)) | 63 if (!file_util::ReadFileToString(cmd_line_file, &cmd_line)) |
| 64 return false; | 64 return false; |
| 65 std::string delimiters; | 65 std::string delimiters; |
| 66 delimiters.push_back('\0'); | 66 delimiters.push_back('\0'); |
| 67 Tokenize(cmd_line, delimiters, proc_cmd_line_args); | 67 Tokenize(cmd_line, delimiters, proc_cmd_line_args); |
| 68 return true; | 68 return true; |
| 69 } | 69 } |
| 70 | 70 |
| 71 // Get the total CPU of a single process. Return value is number of jiffies |
| 72 // on success or -1 on error. |
| 73 int GetProcessCPU(pid_t pid) { |
| 74 // Synchronously reading files in /proc is safe. |
| 75 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 76 |
| 77 // Use /proc/<pid>/task to find all threads and parse their /stat file. |
| 78 FilePath path = FilePath(StringPrintf("/proc/%d/task/", pid)); |
| 79 |
| 80 DIR* dir = opendir(path.value().c_str()); |
| 81 if (!dir) { |
| 82 PLOG(ERROR) << "opendir(" << path.value() << ")"; |
| 83 return -1; |
| 84 } |
| 85 |
| 86 int total_cpu = 0; |
| 87 while (struct dirent* ent = readdir(dir)) { |
| 88 if (ent->d_name[0] == '.') |
| 89 continue; |
| 90 |
| 91 FilePath stat_path = path.AppendASCII(ent->d_name).AppendASCII("stat"); |
| 92 std::string stat; |
| 93 if (file_util::ReadFileToString(stat_path, &stat)) { |
| 94 int cpu = base::ParseProcStatCPU(stat); |
| 95 if (cpu > 0) |
| 96 total_cpu += cpu; |
| 97 } |
| 98 } |
| 99 closedir(dir); |
| 100 |
| 101 return total_cpu; |
| 102 } |
| 103 |
| 71 } // namespace | 104 } // namespace |
| 72 | 105 |
| 73 namespace base { | 106 namespace base { |
| 74 | 107 |
| 75 ProcessId GetParentProcessId(ProcessHandle process) { | 108 ProcessId GetParentProcessId(ProcessHandle process) { |
| 76 // Synchronously reading files in /proc is safe. | 109 // Synchronously reading files in /proc is safe. |
| 77 base::ThreadRestrictions::ScopedAllowIO allow_io; | 110 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 78 | 111 |
| 79 FilePath stat_file("/proc"); | 112 FilePath stat_file("/proc"); |
| 80 stat_file = stat_file.Append(base::IntToString(process)); | 113 stat_file = stat_file.Append(base::IntToString(process)); |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 219 return true; | 252 return true; |
| 220 } | 253 } |
| 221 | 254 |
| 222 bool NamedProcessIterator::IncludeEntry() { | 255 bool NamedProcessIterator::IncludeEntry() { |
| 223 if (executable_name_ != entry().exe_file()) | 256 if (executable_name_ != entry().exe_file()) |
| 224 return false; | 257 return false; |
| 225 return ProcessIterator::IncludeEntry(); | 258 return ProcessIterator::IncludeEntry(); |
| 226 } | 259 } |
| 227 | 260 |
| 228 | 261 |
| 229 ProcessMetrics::ProcessMetrics(ProcessHandle process) | |
| 230 : process_(process), | |
| 231 last_time_(0), | |
| 232 last_system_time_(0), | |
| 233 last_cpu_(0) { | |
| 234 processor_count_ = base::SysInfo::NumberOfProcessors(); | |
| 235 } | |
| 236 | |
| 237 // static | 262 // static |
| 238 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { | 263 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { |
| 239 return new ProcessMetrics(process); | 264 return new ProcessMetrics(process); |
| 240 } | 265 } |
| 241 | 266 |
| 242 // On linux, we return vsize. | 267 // On linux, we return vsize. |
| 243 size_t ProcessMetrics::GetPagefileUsage() const { | 268 size_t ProcessMetrics::GetPagefileUsage() const { |
| 244 std::vector<std::string> proc_stats; | 269 std::vector<std::string> proc_stats; |
| 245 if (!GetProcStats(process_, &proc_stats)) | 270 if (!GetProcStats(process_, &proc_stats)) |
| 246 LOG(WARNING) << "Failed to get process stats."; | 271 LOG(WARNING) << "Failed to get process stats."; |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 392 ws_usage->priv = private_kb; | 417 ws_usage->priv = private_kb; |
| 393 // Sharable is not calculated, as it does not provide interesting data. | 418 // Sharable is not calculated, as it does not provide interesting data. |
| 394 ws_usage->shareable = 0; | 419 ws_usage->shareable = 0; |
| 395 | 420 |
| 396 ws_usage->shared = 0; | 421 ws_usage->shared = 0; |
| 397 if (have_pss) | 422 if (have_pss) |
| 398 ws_usage->shared = pss_kb; | 423 ws_usage->shared = pss_kb; |
| 399 return true; | 424 return true; |
| 400 } | 425 } |
| 401 | 426 |
| 427 double ProcessMetrics::GetCPUUsage() { |
| 428 // This queries the /proc-specific scaling factor which is |
| 429 // conceptually the system hertz. To dump this value on another |
| 430 // system, try |
| 431 // od -t dL /proc/self/auxv |
| 432 // and look for the number after 17 in the output; mine is |
| 433 // 0000040 17 100 3 134512692 |
| 434 // which means the answer is 100. |
| 435 // It may be the case that this value is always 100. |
| 436 static const int kHertz = sysconf(_SC_CLK_TCK); |
| 437 |
| 438 struct timeval now; |
| 439 int retval = gettimeofday(&now, NULL); |
| 440 if (retval) |
| 441 return 0; |
| 442 int64 time = TimeValToMicroseconds(now); |
| 443 |
| 444 if (last_time_ == 0) { |
| 445 // First call, just set the last values. |
| 446 last_time_ = time; |
| 447 last_cpu_ = GetProcessCPU(process_); |
| 448 return 0; |
| 449 } |
| 450 |
| 451 int64 time_delta = time - last_time_; |
| 452 DCHECK_NE(time_delta, 0); |
| 453 if (time_delta == 0) |
| 454 return 0; |
| 455 |
| 456 int cpu = GetProcessCPU(process_); |
| 457 |
| 458 // We have the number of jiffies in the time period. Convert to percentage. |
| 459 // Note this means we will go *over* 100 in the case where multiple threads |
| 460 // are together adding to more than one CPU's worth. |
| 461 int percentage = 100 * (cpu - last_cpu_) / |
| 462 (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF()); |
| 463 |
| 464 last_time_ = time; |
| 465 last_cpu_ = cpu; |
| 466 |
| 467 return percentage; |
| 468 } |
| 469 |
| 402 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING | 470 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING |
| 403 // in your kernel configuration. | 471 // in your kernel configuration. |
| 404 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { | 472 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { |
| 405 // Synchronously reading files in /proc is safe. | 473 // Synchronously reading files in /proc is safe. |
| 406 base::ThreadRestrictions::ScopedAllowIO allow_io; | 474 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 407 | 475 |
| 408 std::string proc_io_contents; | 476 std::string proc_io_contents; |
| 409 FilePath io_file("/proc"); | 477 FilePath io_file("/proc"); |
| 410 io_file = io_file.Append(base::IntToString(process_)); | 478 io_file = io_file.Append(base::IntToString(process_)); |
| 411 io_file = io_file.Append("io"); | 479 io_file = io_file.Append("io"); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 439 base::StringToInt64(tokenizer.token(), | 507 base::StringToInt64(tokenizer.token(), |
| 440 reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount)); | 508 reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount)); |
| 441 } | 509 } |
| 442 state = KEY_NAME; | 510 state = KEY_NAME; |
| 443 break; | 511 break; |
| 444 } | 512 } |
| 445 } | 513 } |
| 446 return true; | 514 return true; |
| 447 } | 515 } |
| 448 | 516 |
| 517 ProcessMetrics::ProcessMetrics(ProcessHandle process) |
| 518 : process_(process), |
| 519 last_time_(0), |
| 520 last_system_time_(0), |
| 521 last_cpu_(0) { |
| 522 processor_count_ = base::SysInfo::NumberOfProcessors(); |
| 523 } |
| 524 |
| 449 | 525 |
| 450 // Exposed for testing. | 526 // Exposed for testing. |
| 451 int ParseProcStatCPU(const std::string& input) { | 527 int ParseProcStatCPU(const std::string& input) { |
| 452 // /proc/<pid>/stat contains the process name in parens. In case the | 528 // /proc/<pid>/stat contains the process name in parens. In case the |
| 453 // process name itself contains parens, skip past them. | 529 // process name itself contains parens, skip past them. |
| 454 std::string::size_type rparen = input.rfind(')'); | 530 std::string::size_type rparen = input.rfind(')'); |
| 455 if (rparen == std::string::npos) | 531 if (rparen == std::string::npos) |
| 456 return -1; | 532 return -1; |
| 457 | 533 |
| 458 // From here, we expect a bunch of space-separated fields, where the | 534 // From here, we expect a bunch of space-separated fields, where the |
| 459 // 0-indexed 11th and 12th are utime and stime. On two different machines | 535 // 0-indexed 11th and 12th are utime and stime. On two different machines |
| 460 // I found 42 and 39 fields, so let's just expect the ones we need. | 536 // I found 42 and 39 fields, so let's just expect the ones we need. |
| 461 std::vector<std::string> fields; | 537 std::vector<std::string> fields; |
| 462 base::SplitString(input.substr(rparen + 2), ' ', &fields); | 538 base::SplitString(input.substr(rparen + 2), ' ', &fields); |
| 463 if (fields.size() < 13) | 539 if (fields.size() < 13) |
| 464 return -1; // Output not in the format we expect. | 540 return -1; // Output not in the format we expect. |
| 465 | 541 |
| 466 int fields11, fields12; | 542 int fields11, fields12; |
| 467 base::StringToInt(fields[11], &fields11); | 543 base::StringToInt(fields[11], &fields11); |
| 468 base::StringToInt(fields[12], &fields12); | 544 base::StringToInt(fields[12], &fields12); |
| 469 return fields11 + fields12; | 545 return fields11 + fields12; |
| 470 } | 546 } |
| 471 | 547 |
| 472 // Get the total CPU of a single process. Return value is number of jiffies | |
| 473 // on success or -1 on error. | |
| 474 static int GetProcessCPU(pid_t pid) { | |
| 475 // Synchronously reading files in /proc is safe. | |
| 476 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 477 | |
| 478 // Use /proc/<pid>/task to find all threads and parse their /stat file. | |
| 479 FilePath path = FilePath(StringPrintf("/proc/%d/task/", pid)); | |
| 480 | |
| 481 DIR* dir = opendir(path.value().c_str()); | |
| 482 if (!dir) { | |
| 483 PLOG(ERROR) << "opendir(" << path.value() << ")"; | |
| 484 return -1; | |
| 485 } | |
| 486 | |
| 487 int total_cpu = 0; | |
| 488 while (struct dirent* ent = readdir(dir)) { | |
| 489 if (ent->d_name[0] == '.') | |
| 490 continue; | |
| 491 | |
| 492 FilePath stat_path = path.AppendASCII(ent->d_name).AppendASCII("stat"); | |
| 493 std::string stat; | |
| 494 if (file_util::ReadFileToString(stat_path, &stat)) { | |
| 495 int cpu = ParseProcStatCPU(stat); | |
| 496 if (cpu > 0) | |
| 497 total_cpu += cpu; | |
| 498 } | |
| 499 } | |
| 500 closedir(dir); | |
| 501 | |
| 502 return total_cpu; | |
| 503 } | |
| 504 | |
| 505 double ProcessMetrics::GetCPUUsage() { | |
| 506 // This queries the /proc-specific scaling factor which is | |
| 507 // conceptually the system hertz. To dump this value on another | |
| 508 // system, try | |
| 509 // od -t dL /proc/self/auxv | |
| 510 // and look for the number after 17 in the output; mine is | |
| 511 // 0000040 17 100 3 134512692 | |
| 512 // which means the answer is 100. | |
| 513 // It may be the case that this value is always 100. | |
| 514 static const int kHertz = sysconf(_SC_CLK_TCK); | |
| 515 | |
| 516 struct timeval now; | |
| 517 int retval = gettimeofday(&now, NULL); | |
| 518 if (retval) | |
| 519 return 0; | |
| 520 int64 time = TimeValToMicroseconds(now); | |
| 521 | |
| 522 if (last_time_ == 0) { | |
| 523 // First call, just set the last values. | |
| 524 last_time_ = time; | |
| 525 last_cpu_ = GetProcessCPU(process_); | |
| 526 return 0; | |
| 527 } | |
| 528 | |
| 529 int64 time_delta = time - last_time_; | |
| 530 DCHECK_NE(time_delta, 0); | |
| 531 if (time_delta == 0) | |
| 532 return 0; | |
| 533 | |
| 534 int cpu = GetProcessCPU(process_); | |
| 535 | |
| 536 // We have the number of jiffies in the time period. Convert to percentage. | |
| 537 // Note this means we will go *over* 100 in the case where multiple threads | |
| 538 // are together adding to more than one CPU's worth. | |
| 539 int percentage = 100 * (cpu - last_cpu_) / | |
| 540 (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF()); | |
| 541 | |
| 542 last_time_ = time; | |
| 543 last_cpu_ = cpu; | |
| 544 | |
| 545 return percentage; | |
| 546 } | |
| 547 | |
| 548 namespace { | 548 namespace { |
| 549 | 549 |
| 550 // The format of /proc/meminfo is: | 550 // The format of /proc/meminfo is: |
| 551 // | 551 // |
| 552 // MemTotal: 8235324 kB | 552 // MemTotal: 8235324 kB |
| 553 // MemFree: 1628304 kB | 553 // MemFree: 1628304 kB |
| 554 // Buffers: 429596 kB | 554 // Buffers: 429596 kB |
| 555 // Cached: 4728232 kB | 555 // Cached: 4728232 kB |
| 556 // ... | 556 // ... |
| 557 const size_t kMemTotalIndex = 1; | 557 const size_t kMemTotalIndex = 1; |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 706 | 706 |
| 707 if (!file_util::PathExists(oom_adj)) | 707 if (!file_util::PathExists(oom_adj)) |
| 708 return false; | 708 return false; |
| 709 | 709 |
| 710 std::string score_str = base::IntToString(score); | 710 std::string score_str = base::IntToString(score); |
| 711 return (static_cast<int>(score_str.length()) == | 711 return (static_cast<int>(score_str.length()) == |
| 712 file_util::WriteFile(oom_adj, score_str.c_str(), score_str.length())); | 712 file_util::WriteFile(oom_adj, score_str.c_str(), score_str.length())); |
| 713 } | 713 } |
| 714 | 714 |
| 715 } // namespace base | 715 } // namespace base |
| OLD | NEW |