// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/hud_display/memory_status.h" #include #include "base/files/file_util.h" #include "base/logging.h" #include "base/process/internal_linux.h" #include "base/process/process_iterator.h" #include "base/process/process_metrics.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/system/sys_info.h" #include "base/threading/thread_restrictions.h" namespace ash { namespace hud_display { namespace { constexpr char kProcDir[] = "/proc"; constexpr char kSysFsCgroupCpuDir[] = "/sys/fs/cgroup/cpu"; // Fields from /proc//statm, 0-based. See man 5 proc. // If the ordering ever changes, carefully review functions that use these // values. enum class ProcStatMFields { VM_SIZE = 0, // Virtual memory size in bytes. VM_RSS = 1, // Resident Set Size in pages. VM_SHARED = 2, // number of resident shared pages }; base::FilePath GetProcPidDir(pid_t pid) { return base::FilePath(kProcDir).Append(base::NumberToString(pid)); } std::string ReadProcFile(const base::FilePath& path) { std::string result; ReadFileToString(path, &result); return result; } // Reads and returns /proc//cmdline // Note: /proc//cmdline contains command line arguments separated by single // null characters. std::string GetProcCmdline(pid_t pid) { return ReadProcFile(GetProcPidDir(pid).Append("cmdline")); } int64_t GetProcVM_RSS(pid_t pid) { const std::string statm = ReadProcFile(GetProcPidDir(pid).Append("statm")); const std::vector parts = base::SplitStringPiece( statm, " \n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); if (parts.size() <= static_cast(ProcStatMFields::VM_RSS)) { DLOG(ERROR) << "GetProcVM_RSS(): No data in '" << statm << "'!"; return 0; } int64_t result; base::StringToInt64(parts[static_cast(ProcStatMFields::VM_RSS)], &result); return result * getpagesize(); } int64_t GetProcVM_SHARED(pid_t pid) { const std::string statm = ReadProcFile(GetProcPidDir(pid).Append("statm")); const std::vector parts = base::SplitStringPiece( statm, " \n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); if (parts.size() <= static_cast(ProcStatMFields::VM_SHARED)) { DLOG(ERROR) << "GetProcVM_SHARED(): No data!"; return 0; } int64_t result; base::StringToInt64(parts[static_cast(ProcStatMFields::VM_SHARED)], &result); return result * getpagesize(); } } // namespace //////////////////////////////////////////////////////////////////////////////// // ProcessMemoryCountersByFlag MemoryStatus::ProcessMemoryCountersByFlag::ProcessMemoryCountersByFlag( const std::string& cmd_line_flag) : flag_(cmd_line_flag) {} MemoryStatus::ProcessMemoryCountersByFlag::~ProcessMemoryCountersByFlag() = default; bool MemoryStatus::ProcessMemoryCountersByFlag::TryRead( const base::ProcessId& pid, const std::string& cmdline) { if (cmdline.find(flag_) == std::string::npos) return false; rss_ += GetProcVM_RSS(pid); rss_shared_ += GetProcVM_SHARED(pid); return true; } // ProcessMemoryCountersByCgroup MemoryStatus::ProcessMemoryCountersByCgroup::ProcessMemoryCountersByCgroup( const std::string& expected_cgroup) { const base::FilePath pids_filename = base::FilePath(kSysFsCgroupCpuDir) .Append(expected_cgroup) .Append("cgroup.procs"); const std::string pids_list_str = ReadProcFile(pids_filename); if (pids_list_str.empty()) { // Ignore read failures. return; } const std::vector pids = base::SplitStringPiece( pids_list_str, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); for (const auto& p : pids) { int64_t pid; if (base::StringToInt64(p, &pid)) pids_.insert(pid); } } MemoryStatus::ProcessMemoryCountersByCgroup::~ProcessMemoryCountersByCgroup() = default; bool MemoryStatus::ProcessMemoryCountersByCgroup::TryRead( const base::ProcessId& pid) { if (!pids_.contains(pid)) return false; rss_ += GetProcVM_RSS(pid); rss_shared_ += GetProcVM_SHARED(pid); return true; } // MemoryStatus MemoryStatus::MemoryStatus() { UpdatePerProcessStat(); UpdateMeminfo(); } void MemoryStatus::UpdatePerProcessStat() { // TODO: Can we remember process status in some way? base::ProcessIterator process_iter(/*filter=*/nullptr); while (const base::ProcessEntry* process_entry = process_iter.NextProcessEntry()) { const base::Process process(process_entry->pid()); if (process.is_current()) { browser_rss_ = GetProcVM_RSS(process.Pid()); browser_rss_shared_ = GetProcVM_SHARED(process.Pid()); continue; } const std::string cmdline = GetProcCmdline(process.Pid()); if (gpu_.TryRead(process.Pid(), cmdline) || renderers_.TryRead(process.Pid(), cmdline)) { continue; } arc_.TryRead(process.Pid()); } } void MemoryStatus::UpdateMeminfo() { base::SystemMemoryInfoKB meminfo; base::GetSystemMemoryInfo(&meminfo); total_ram_size_ = meminfo.total * 1024LL; total_free_ = meminfo.free * 1024LL; base::GraphicsMemoryInfoKB gpu_meminfo; if (base::GetGraphicsMemoryInfo(&gpu_meminfo)) gpu_kernel_ = gpu_meminfo.gpu_memory_size; else gpu_kernel_ = 0LL; } } // namespace hud_display } // namespace ash