diff options
Diffstat (limited to 'base/process/process_metrics_linux.cc')
-rw-r--r-- | base/process/process_metrics_linux.cc | 150 |
1 files changed, 92 insertions, 58 deletions
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc index c564a6798b..bcebcf5729 100644 --- a/base/process/process_metrics_linux.cc +++ b/base/process/process_metrics_linux.cc @@ -6,11 +6,15 @@ #include <dirent.h> #include <fcntl.h> +#include <stddef.h> +#include <stdint.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> +#include <utility> +#include "base/files/dir_reader_posix.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/process/internal_linux.h" @@ -20,6 +24,7 @@ #include "base/strings/string_util.h" #include "base/sys_info.h" #include "base/threading/thread_restrictions.h" +#include "build/build_config.h" namespace base { @@ -35,13 +40,13 @@ void TrimKeyValuePairs(StringPairs* pairs) { } #if defined(OS_CHROMEOS) -// Read a file with a single number string and return the number as a uint64. -static uint64 ReadFileToUint64(const FilePath file) { +// Read a file with a single number string and return the number as a uint64_t. +static uint64_t ReadFileToUint64(const FilePath file) { std::string file_as_string; if (!ReadFileToString(file, &file_as_string)) return 0; TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string); - uint64 file_as_uint64 = 0; + uint64_t file_as_uint64 = 0; if (!StringToUint64(file_as_string, &file_as_uint64)) return 0; return file_as_uint64; @@ -67,8 +72,8 @@ size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { const std::string& key = pairs[i].first; const std::string& value_str = pairs[i].second; if (key == field) { - std::vector<std::string> split_value_str; - SplitString(value_str, ' ', &split_value_str); + std::vector<StringPiece> split_value_str = SplitStringPiece( + value_str, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); if (split_value_str.size() != 2 || split_value_str[1] != "kB") { NOTREACHED(); return 0; @@ -91,7 +96,7 @@ size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { // Only works for fields in the form of "field : uint_value" bool ReadProcSchedAndGetFieldAsUint64(pid_t pid, const std::string& field, - uint64* result) { + uint64_t* result) { std::string sched_data; { // Synchronously reading files in /proc does not hit the disk. @@ -108,7 +113,7 @@ bool ReadProcSchedAndGetFieldAsUint64(pid_t pid, const std::string& key = pairs[i].first; const std::string& value_str = pairs[i].second; if (key == field) { - uint64 value; + uint64_t value; if (!StringToUint64(value_str, &value)) return false; *result = value; @@ -213,13 +218,14 @@ double ProcessMetrics::GetCPUUsage() { // First call, just set the last values. last_cpu_time_ = time; last_cpu_ = GetProcessCPU(process_); - return 0; + return 0.0; } - int64 time_delta = (time - last_cpu_time_).InMicroseconds(); - DCHECK_NE(time_delta, 0); - if (time_delta == 0) - return 0; + TimeDelta time_delta = time - last_cpu_time_; + if (time_delta.is_zero()) { + NOTREACHED(); + return 0.0; + } int cpu = GetProcessCPU(process_); @@ -228,8 +234,18 @@ double ProcessMetrics::GetCPUUsage() { // are together adding to more than one CPU's worth. TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu); TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_); - double percentage = 100.0 * (cpu_time - last_cpu_time).InSecondsF() / - TimeDelta::FromMicroseconds(time_delta).InSecondsF(); + + // If the number of threads running in the process has decreased since the + // last time this function was called, |last_cpu_time| will be greater than + // |cpu_time| which will result in a negative value in the below percentage + // calculation. We prevent this by clamping to 0. crbug.com/546565. + // This computation is known to be shaky when threads are destroyed between + // "last" and "now", but for our current purposes, it's all right. + double percentage = 0.0; + if (last_cpu_time < cpu_time) { + percentage = 100.0 * (cpu_time - last_cpu_time).InSecondsF() / + time_delta.InSecondsF(); + } last_cpu_time_ = time; last_cpu_ = cpu; @@ -257,7 +273,7 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { for (size_t i = 0; i < pairs.size(); ++i) { const std::string& key = pairs[i].first; const std::string& value_str = pairs[i].second; - uint64* target_counter = NULL; + uint64_t* target_counter = NULL; if (key == "syscr") target_counter = &io_counters->ReadOperationCount; else if (key == "syscw") @@ -274,6 +290,26 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { return true; } +#if defined(OS_LINUX) +int ProcessMetrics::GetOpenFdCount() const { + // Use /proc/<pid>/fd to count the number of entries there. + FilePath fd_path = internal::GetProcPidDir(process_).Append("fd"); + + DirReaderPosix dir_reader(fd_path.value().c_str()); + if (!dir_reader.IsValid()) + return -1; + + int total_count = 0; + for (; dir_reader.Next(); ) { + const char* name = dir_reader.name(); + if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) + ++total_count; + } + + return total_count; +} +#endif // defined(OS_LINUX) + ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), last_system_time_(0), @@ -316,8 +352,9 @@ bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) return false; } - std::vector<std::string> totmaps_fields; - SplitStringAlongWhitespace(totmaps_data, &totmaps_fields); + std::vector<std::string> totmaps_fields = SplitString( + totmaps_data, base::kWhitespaceASCII, base::KEEP_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); DCHECK_EQ("Pss:", totmaps_fields[kPssIndex-1]); DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]); @@ -368,8 +405,8 @@ bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) return false; } - std::vector<std::string> statm_vec; - SplitString(statm, ' ', &statm_vec); + std::vector<StringPiece> statm_vec = SplitStringPiece( + statm, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); if (statm_vec.size() != 7) return false; // Not the format we expect. @@ -543,7 +580,7 @@ scoped_ptr<Value> SystemMemoryInfoKB::ToValue() const { res->SetInteger("gem_size", gem_size); #endif - return res.Pass(); + return std::move(res); } // exposed for testing @@ -563,17 +600,15 @@ bool ParseProcMeminfo(const std::string& meminfo_data, // MemTotal value meminfo->total = 0; - std::vector<std::string> meminfo_lines; - Tokenize(meminfo_data, "\n", &meminfo_lines); - for (std::vector<std::string>::iterator it = meminfo_lines.begin(); - it != meminfo_lines.end(); ++it) { - std::vector<std::string> tokens; - SplitStringAlongWhitespace(*it, &tokens); + for (const StringPiece& line : SplitStringPiece( + meminfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { + std::vector<StringPiece> tokens = SplitStringPiece( + line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); // HugePages_* only has a number and no suffix so we can't rely on // there being exactly 3 tokens. if (tokens.size() <= 1) { DLOG(WARNING) << "meminfo: tokens: " << tokens.size() - << " malformed line: " << *it; + << " malformed line: " << line.as_string(); continue; } @@ -630,12 +665,10 @@ bool ParseProcVmstat(const std::string& vmstat_data, // We iterate through the whole file because the position of the // fields are dependent on the kernel version and configuration. - std::vector<std::string> vmstat_lines; - Tokenize(vmstat_data, "\n", &vmstat_lines); - for (std::vector<std::string>::iterator it = vmstat_lines.begin(); - it != vmstat_lines.end(); ++it) { - std::vector<std::string> tokens; - SplitString(*it, ' ', &tokens); + for (const StringPiece& line : SplitStringPiece( + vmstat_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { + std::vector<StringPiece> tokens = SplitStringPiece( + line, " ", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); if (tokens.size() != 2) continue; @@ -736,7 +769,7 @@ SystemDiskInfo::SystemDiskInfo() { scoped_ptr<Value> SystemDiskInfo::ToValue() const { scoped_ptr<DictionaryValue> res(new DictionaryValue()); - // Write out uint64 variables as doubles. + // Write out uint64_t variables as doubles. // Note: this may discard some precision, but for JS there's no other option. res->SetDouble("reads", static_cast<double>(reads)); res->SetDouble("reads_merged", static_cast<double>(reads_merged)); @@ -750,7 +783,7 @@ scoped_ptr<Value> SystemDiskInfo::ToValue() const { res->SetDouble("io_time", static_cast<double>(io_time)); res->SetDouble("weighted_io_time", static_cast<double>(weighted_io_time)); - return res.Pass(); + return std::move(res); } bool IsValidDiskName(const std::string& candidate) { @@ -792,9 +825,9 @@ bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { return false; } - std::vector<std::string> diskinfo_lines; - size_t line_count = Tokenize(diskinfo_data, "\n", &diskinfo_lines); - if (line_count == 0) { + std::vector<StringPiece> diskinfo_lines = SplitStringPiece( + diskinfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); + if (diskinfo_lines.size() == 0) { DLOG(WARNING) << "No lines found"; return false; } @@ -811,24 +844,24 @@ bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { diskinfo->io_time = 0; diskinfo->weighted_io_time = 0; - uint64 reads = 0; - uint64 reads_merged = 0; - uint64 sectors_read = 0; - uint64 read_time = 0; - uint64 writes = 0; - uint64 writes_merged = 0; - uint64 sectors_written = 0; - uint64 write_time = 0; - uint64 io = 0; - uint64 io_time = 0; - uint64 weighted_io_time = 0; - - for (size_t i = 0; i < line_count; i++) { - std::vector<std::string> disk_fields; - SplitStringAlongWhitespace(diskinfo_lines[i], &disk_fields); + uint64_t reads = 0; + uint64_t reads_merged = 0; + uint64_t sectors_read = 0; + uint64_t read_time = 0; + uint64_t writes = 0; + uint64_t writes_merged = 0; + uint64_t sectors_written = 0; + uint64_t write_time = 0; + uint64_t io = 0; + uint64_t io_time = 0; + uint64_t weighted_io_time = 0; + + for (const StringPiece& line : diskinfo_lines) { + std::vector<StringPiece> disk_fields = SplitStringPiece( + line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); // Fields may have overflowed and reset to zero. - if (IsValidDiskName(disk_fields[kDiskDriveName])) { + if (IsValidDiskName(disk_fields[kDiskDriveName].as_string())) { StringToUint64(disk_fields[kDiskReads], &reads); StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged); StringToUint64(disk_fields[kDiskSectorsRead], §ors_read); @@ -862,7 +895,7 @@ bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { scoped_ptr<Value> SwapInfo::ToValue() const { scoped_ptr<DictionaryValue> res(new DictionaryValue()); - // Write out uint64 variables as doubles. + // Write out uint64_t variables as doubles. // Note: this may discard some precision, but for JS there's no other option. res->SetDouble("num_reads", static_cast<double>(num_reads)); res->SetDouble("num_writes", static_cast<double>(num_writes)); @@ -875,7 +908,7 @@ scoped_ptr<Value> SwapInfo::ToValue() const { else res->SetDouble("compression_ratio", 0); - return res.Pass(); + return std::move(res); } void GetSwapInfo(SwapInfo* swap_info) { @@ -883,7 +916,8 @@ void GetSwapInfo(SwapInfo* swap_info) { ThreadRestrictions::ScopedAllowIO allow_io; FilePath zram_path("/sys/block/zram0"); - uint64 orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size")); + uint64_t orig_data_size = + ReadFileToUint64(zram_path.Append("orig_data_size")); if (orig_data_size <= 4096) { // A single page is compressed at startup, and has a high compression // ratio. We ignore this as it doesn't indicate any real swapping. @@ -906,7 +940,7 @@ void GetSwapInfo(SwapInfo* swap_info) { #if defined(OS_LINUX) int ProcessMetrics::GetIdleWakeupsPerSecond() { - uint64 wake_ups; + uint64_t wake_ups; const char kWakeupStat[] = "se.statistics.nr_wakeups"; return ReadProcSchedAndGetFieldAsUint64(process_, kWakeupStat, &wake_ups) ? CalculateIdleWakeupsPerSecond(wake_ups) : 0; |