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 |