memory_status.cc 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright 2020 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. #include "ash/hud_display/memory_status.h"
  5. #include <unistd.h>
  6. #include "base/files/file_util.h"
  7. #include "base/logging.h"
  8. #include "base/process/internal_linux.h"
  9. #include "base/process/process_iterator.h"
  10. #include "base/process/process_metrics.h"
  11. #include "base/strings/string_number_conversions.h"
  12. #include "base/strings/string_split.h"
  13. #include "base/system/sys_info.h"
  14. #include "base/threading/thread_restrictions.h"
  15. namespace ash {
  16. namespace hud_display {
  17. namespace {
  18. constexpr char kProcDir[] = "/proc";
  19. constexpr char kSysFsCgroupCpuDir[] = "/sys/fs/cgroup/cpu";
  20. // Fields from /proc/<pid>/statm, 0-based. See man 5 proc.
  21. // If the ordering ever changes, carefully review functions that use these
  22. // values.
  23. enum class ProcStatMFields {
  24. VM_SIZE = 0, // Virtual memory size in bytes.
  25. VM_RSS = 1, // Resident Set Size in pages.
  26. VM_SHARED = 2, // number of resident shared pages
  27. };
  28. base::FilePath GetProcPidDir(pid_t pid) {
  29. return base::FilePath(kProcDir).Append(base::NumberToString(pid));
  30. }
  31. std::string ReadProcFile(const base::FilePath& path) {
  32. std::string result;
  33. ReadFileToString(path, &result);
  34. return result;
  35. }
  36. // Reads and returns /proc/<pid>/cmdline
  37. // Note: /proc/<pid>/cmdline contains command line arguments separated by single
  38. // null characters.
  39. std::string GetProcCmdline(pid_t pid) {
  40. return ReadProcFile(GetProcPidDir(pid).Append("cmdline"));
  41. }
  42. int64_t GetProcVM_RSS(pid_t pid) {
  43. const std::string statm = ReadProcFile(GetProcPidDir(pid).Append("statm"));
  44. const std::vector<base::StringPiece> parts = base::SplitStringPiece(
  45. statm, " \n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  46. if (parts.size() <= static_cast<size_t>(ProcStatMFields::VM_RSS)) {
  47. DLOG(ERROR) << "GetProcVM_RSS(): No data in '" << statm << "'!";
  48. return 0;
  49. }
  50. int64_t result;
  51. base::StringToInt64(parts[static_cast<size_t>(ProcStatMFields::VM_RSS)],
  52. &result);
  53. return result * getpagesize();
  54. }
  55. int64_t GetProcVM_SHARED(pid_t pid) {
  56. const std::string statm = ReadProcFile(GetProcPidDir(pid).Append("statm"));
  57. const std::vector<base::StringPiece> parts = base::SplitStringPiece(
  58. statm, " \n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  59. if (parts.size() <= static_cast<size_t>(ProcStatMFields::VM_SHARED)) {
  60. DLOG(ERROR) << "GetProcVM_SHARED(): No data!";
  61. return 0;
  62. }
  63. int64_t result;
  64. base::StringToInt64(parts[static_cast<size_t>(ProcStatMFields::VM_SHARED)],
  65. &result);
  66. return result * getpagesize();
  67. }
  68. } // namespace
  69. ////////////////////////////////////////////////////////////////////////////////
  70. // ProcessMemoryCountersByFlag
  71. MemoryStatus::ProcessMemoryCountersByFlag::ProcessMemoryCountersByFlag(
  72. const std::string& cmd_line_flag)
  73. : flag_(cmd_line_flag) {}
  74. MemoryStatus::ProcessMemoryCountersByFlag::~ProcessMemoryCountersByFlag() =
  75. default;
  76. bool MemoryStatus::ProcessMemoryCountersByFlag::TryRead(
  77. const base::ProcessId& pid,
  78. const std::string& cmdline) {
  79. if (cmdline.find(flag_) == std::string::npos)
  80. return false;
  81. rss_ += GetProcVM_RSS(pid);
  82. rss_shared_ += GetProcVM_SHARED(pid);
  83. return true;
  84. }
  85. // ProcessMemoryCountersByCgroup
  86. MemoryStatus::ProcessMemoryCountersByCgroup::ProcessMemoryCountersByCgroup(
  87. const std::string& expected_cgroup) {
  88. const base::FilePath pids_filename = base::FilePath(kSysFsCgroupCpuDir)
  89. .Append(expected_cgroup)
  90. .Append("cgroup.procs");
  91. const std::string pids_list_str = ReadProcFile(pids_filename);
  92. if (pids_list_str.empty()) {
  93. // Ignore read failures.
  94. return;
  95. }
  96. const std::vector<base::StringPiece> pids = base::SplitStringPiece(
  97. pids_list_str, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  98. for (const auto& p : pids) {
  99. int64_t pid;
  100. if (base::StringToInt64(p, &pid))
  101. pids_.insert(pid);
  102. }
  103. }
  104. MemoryStatus::ProcessMemoryCountersByCgroup::~ProcessMemoryCountersByCgroup() =
  105. default;
  106. bool MemoryStatus::ProcessMemoryCountersByCgroup::TryRead(
  107. const base::ProcessId& pid) {
  108. if (!pids_.contains(pid))
  109. return false;
  110. rss_ += GetProcVM_RSS(pid);
  111. rss_shared_ += GetProcVM_SHARED(pid);
  112. return true;
  113. }
  114. // MemoryStatus
  115. MemoryStatus::MemoryStatus() {
  116. UpdatePerProcessStat();
  117. UpdateMeminfo();
  118. }
  119. void MemoryStatus::UpdatePerProcessStat() {
  120. // TODO: Can we remember process status in some way?
  121. base::ProcessIterator process_iter(/*filter=*/nullptr);
  122. while (const base::ProcessEntry* process_entry =
  123. process_iter.NextProcessEntry()) {
  124. const base::Process process(process_entry->pid());
  125. if (process.is_current()) {
  126. browser_rss_ = GetProcVM_RSS(process.Pid());
  127. browser_rss_shared_ = GetProcVM_SHARED(process.Pid());
  128. continue;
  129. }
  130. const std::string cmdline = GetProcCmdline(process.Pid());
  131. if (gpu_.TryRead(process.Pid(), cmdline) ||
  132. renderers_.TryRead(process.Pid(), cmdline)) {
  133. continue;
  134. }
  135. arc_.TryRead(process.Pid());
  136. }
  137. }
  138. void MemoryStatus::UpdateMeminfo() {
  139. base::SystemMemoryInfoKB meminfo;
  140. base::GetSystemMemoryInfo(&meminfo);
  141. total_ram_size_ = meminfo.total * 1024LL;
  142. total_free_ = meminfo.free * 1024LL;
  143. base::GraphicsMemoryInfoKB gpu_meminfo;
  144. if (base::GetGraphicsMemoryInfo(&gpu_meminfo))
  145. gpu_kernel_ = gpu_meminfo.gpu_memory_size;
  146. else
  147. gpu_kernel_ = 0LL;
  148. }
  149. } // namespace hud_display
  150. } // namespace ash