Reland "[memory] Move process memory info to base"
This is a reland of commit 6a0ed21330f202eae185ea827ae19f51f0537d82
Reason for revert:
Speculatively reverting due to Linux MSan bot failures on
RenderFrameHostManagerTest.RenderViewInitAfterNewProxyAndProcessKill
Fix 1: initialize ProcessMemoryInfo fields on linux.
Fix 2: use /proc/pid/statm as fallback for pre linux 4.5
Original change's description:
> [memory] Move process memory info to base
>
> This will be re-used in a follow-up in SystemMetricsSampler.
> This should also replaced copied code in blink::MemoryUsageMonitor.
>
> This changes Linux's FillOSMemoryDump from GetResidentAndSharedPagesFromStatmFile() (proc/pid/statm) to ReadProcFileToTrimmedStringPairs() (proc/pid/status)
>
> This drops supports for pre MAC_OS_X_VERSION_10_11 missing
> phys_footprint: https://issues.chromium.org/issues/40669926
>
> Change-Id: I2002e69abbe852e82d0d1cb37017e77602538d6b
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6039432
> Reviewed-by: Primiano Tucci <primiano@chromium.org>
> Reviewed-by: Gabriel Charette <gab@chromium.org>
> Owners-Override: Gabriel Charette <gab@chromium.org>
> Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1390345}
Change-Id: I8b98ca2ba24a0cabd9f49b5e1e85e54932dfbf32
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6062359
Owners-Override: Gabriel Charette <gab@chromium.org>
Reviewed-by: Primiano Tucci <primiano@chromium.org>
Reviewed-by: Gabriel Charette <gab@chromium.org>
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1391983}
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index c5d994f..86b448af 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -64,7 +64,7 @@
// Convert a POSIX timeval to microseconds.
BASE_EXPORT int64_t TimeValToMicroseconds(const struct timeval& tv);
-enum class ProcessCPUUsageError {
+enum class ProcessUsageError {
// The OS returned an error while measuring the CPU usage. The possible causes
// vary by platform.
kSystemError,
@@ -74,6 +74,32 @@
kProcessNotFound,
};
+using ProcessCPUUsageError = ProcessUsageError;
+
+struct ProcessMemoryInfo {
+ uint64_t resident_set_bytes = 0;
+
+#if BUILDFLAG(IS_APPLE)
+ uint64_t physical_footprint_bytes = 0;
+ uint64_t internal_bytes = 0;
+ uint64_t compressed_bytes = 0;
+#endif // BUILDFLAG(IS_APPLE)
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
+ BUILDFLAG(IS_FUCHSIA)
+ uint64_t rss_anon_bytes = 0;
+ uint64_t vm_swap_bytes = 0;
+#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
+ // BUILDFLAG(IS_ANDROID)
+
+#if BUILDFLAG(IS_WIN)
+ uint64_t private_bytes = 0;
+#endif // BUILDFLAG(IS_WIN)
+
+ // On iOS,
+ // TBD: https://crbug.com/714961
+};
+
// Provides performance metrics for a specified process (CPU usage and IO
// counters). Use CreateCurrentProcessMetrics() to get an instance for the
// current process, or CreateProcessMetrics() to get an instance for an
@@ -113,11 +139,16 @@
// convenience wrapper for CreateProcessMetrics().
static std::unique_ptr<ProcessMetrics> CreateCurrentProcessMetrics();
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
- // Resident Set Size is a Linux/Android specific memory concept. Do not
- // attempt to extend this to other platforms.
- BASE_EXPORT size_t GetResidentSetSize() const;
-#endif
+ // Provides synchronous access to memory metrics for a process. This interface
+ // has platform-specific restrictions:
+ // * On Android, due to sandboxing restrictions, processes can only access
+ // memory metrics for themselves.
+ // * On Linux, due to sandboxing restrictions, only the privileged browser
+ // process has access to memory metrics for sandboxed child processes.
+ // * On Fuchsia, due to the API expecting a ProcessId rather than a
+ // ProcessHandle, processes can only access memory metrics for themselves
+ // or for children of base::GetDefaultJob().
+ base::expected<ProcessMemoryInfo, ProcessUsageError> GetMemoryInfo() const;
// Returns the percentage of time spent executing, across all threads of the
// process, in the interval since the last time the method was called, using
@@ -193,9 +224,6 @@
#endif // BUILDFLAG(IS_POSIX)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
- // Bytes of swap as reported by /proc/[pid]/status.
- uint64_t GetVmSwapBytes() const;
-
// Minor and major page fault count as reported by /proc/[pid]/stat.
// Returns true for success.
bool GetPageFaultCounts(PageFaultCounts* counts) const;
diff --git a/base/process/process_metrics_apple.cc b/base/process/process_metrics_apple.cc
index 12e772d..8bf40141 100644
--- a/base/process/process_metrics_apple.cc
+++ b/base/process/process_metrics_apple.cc
@@ -86,6 +86,34 @@
return kr == KERN_SUCCESS;
}
+// Don't simply use sizeof(task_vm_info) / sizeof(natural_t):
+// In the 10.15 SDK, this structure is 87 32-bit words long, and in
+// mach_types.defs:
+//
+// type task_info_t = array[*:87] of integer_t;
+//
+// However in the 10.14 SDK, this structure is 42 32-bit words, and in
+// mach_types.defs:
+//
+// type task_info_t = array[*:52] of integer_t;
+//
+// As a result, the 10.15 SDK's task_vm_info won't fit inside the 10.14 SDK's
+// task_info_t, so the *rest of the system* (on 10.14 and earlier) can't handle
+// calls that request the full 10.15 structure. We have to request a prefix of
+// it that 10.14 and earlier can handle by limiting the length we request. The
+// rest of the fields just get ignored, but we don't use them anyway.
+
+constexpr mach_msg_type_number_t ChromeTaskVMInfoCount =
+ TASK_VM_INFO_REV2_COUNT;
+
+// The count field is in units of natural_t, which is the machine's word size
+// (64 bits on all modern machines), but the task_info_t array is in units of
+// integer_t, which is 32 bits.
+constexpr mach_msg_type_number_t MAX_MIG_SIZE_FOR_1014 =
+ 52 / (sizeof(natural_t) / sizeof(integer_t));
+static_assert(ChromeTaskVMInfoCount <= MAX_MIG_SIZE_FOR_1014,
+ "task_vm_info must be small enough for 10.14 MIG interfaces");
+
} // namespace
// Implementations of ProcessMetrics class shared by Mac and iOS.
@@ -102,6 +130,28 @@
return task;
}
+base::expected<ProcessMemoryInfo, ProcessUsageError>
+ProcessMetrics::GetMemoryInfo() const {
+ mach_port_t task = TaskForHandle(process_);
+ if (task == MACH_PORT_NULL) {
+ return base::unexpected(ProcessUsageError::kProcessNotFound);
+ }
+ task_vm_info info;
+ mach_msg_type_number_t count = ChromeTaskVMInfoCount;
+ kern_return_t result = task_info(
+ task, TASK_VM_INFO, reinterpret_cast<task_info_t>(&info), &count);
+ if (result != KERN_SUCCESS) {
+ return base::unexpected(ProcessUsageError::kSystemError);
+ }
+
+ ProcessMemoryInfo memory_info;
+ memory_info.internal_bytes = info.internal;
+ memory_info.compressed_bytes = info.compressed;
+ memory_info.resident_set_bytes = info.resident_size;
+ memory_info.physical_footprint_bytes = info.phys_footprint;
+ return memory_info;
+}
+
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
mach_port_t task = TaskForHandle(process_);
diff --git a/base/process/process_metrics_fuchsia.cc b/base/process/process_metrics_fuchsia.cc
index b1f94c4..f67b66b5 100644
--- a/base/process/process_metrics_fuchsia.cc
+++ b/base/process/process_metrics_fuchsia.cc
@@ -48,6 +48,24 @@
return base::ok(TimeDelta::FromZxDuration(stats.cpu_time));
}
+base::expected<ProcessMemoryInfo, ProcessUsageError>
+ProcessMetrics::GetMemoryInfo() const {
+ zx_info_task_stats_t info;
+ zx_status_t status = zx::unowned_process(process_)->get_info(
+ ZX_INFO_TASK_STATS, &info, sizeof(info), nullptr, nullptr);
+ if (status != ZX_OK) {
+ return base::unexpected(ProcessUsageError::kSystemError);
+ }
+
+ ProcessMemoryInfo memory_info;
+ memory_info.resident_set_bytes =
+ info.mem_private_bytes + info.mem_shared_bytes;
+ memory_info.rss_anon_bytes = info.mem_private_bytes;
+ // Fuchsia has no swap.
+ memory_info.vm_swap_bytes = 0;
+ return memory_info;
+}
+
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
// TODO(crbug.com/42050627).
return false;
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index b0abc364..5ef8c17 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -29,6 +29,7 @@
#include "base/files/dir_reader_posix.h"
#include "base/files/file_util.h"
#include "base/logging.h"
+#include "base/memory/page_size.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/numerics/clamped_math.h"
@@ -88,6 +89,15 @@
return base::ok(cpu_time);
}
+size_t GetKbFieldAsSizeT(std::string_view value_str) {
+ std::vector<std::string_view> split_value_str =
+ SplitStringPiece(value_str, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ CHECK(split_value_str.size() == 2 && split_value_str[1] == "kB");
+ size_t value;
+ CHECK(StringToSizeT(split_value_str[0], &value));
+ return value;
+}
+
} // namespace
// static
@@ -96,11 +106,6 @@
return WrapUnique(new ProcessMetrics(process));
}
-size_t ProcessMetrics::GetResidentSetSize() const {
- return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) *
- checked_cast<size_t>(getpagesize());
-}
-
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
std::string buffer;
@@ -140,9 +145,48 @@
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
-uint64_t ProcessMetrics::GetVmSwapBytes() const {
- return internal::ReadProcStatusAndGetKbFieldAsSizeT(process_, "VmSwap") *
- 1024;
+base::expected<ProcessMemoryInfo, ProcessUsageError>
+ProcessMetrics::GetMemoryInfo() const {
+ StringPairs pairs;
+ if (!internal::ReadProcFileToTrimmedStringPairs(process_, "status", &pairs)) {
+ return base::unexpected(ProcessUsageError::kSystemError);
+ }
+ ProcessMemoryInfo dump;
+ for (const auto& pair : pairs) {
+ const std::string& key = pair.first;
+ const std::string& value_str = pair.second;
+ if (key == "VmSwap") {
+ dump.vm_swap_bytes =
+ static_cast<uint64_t>(GetKbFieldAsSizeT(value_str)) * 1024;
+ } else if (key == "VmRSS") {
+ dump.resident_set_bytes =
+ static_cast<uint64_t>(GetKbFieldAsSizeT(value_str)) * 1024;
+ } else if (key == "RssAnon") {
+ dump.rss_anon_bytes =
+ static_cast<uint64_t>(GetKbFieldAsSizeT(value_str)) * 1024;
+ } else {
+ continue;
+ }
+ }
+ if (dump.rss_anon_bytes != 0) {
+ return dump;
+ }
+ // RssAnon was introduced in Linux 4.5, use /proc/pid/statm as fallback.
+ std::string statm_data;
+ FilePath statm_file = internal::GetProcPidDir(process_).Append("statm");
+ if (!internal::ReadProcFile(statm_file, &statm_data)) {
+ return base::unexpected(ProcessUsageError::kSystemError);
+ }
+ std::vector<std::string_view> values = SplitStringPieceUsingSubstr(
+ statm_data, " ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ CHECK_GE(values.size(), 3U);
+ uint64_t resident_pages = 0;
+ uint64_t shared_pages = 0;
+ CHECK(StringToUint64(values[1], &resident_pages));
+ CHECK(StringToUint64(values[2], &shared_pages));
+ static const size_t page_size = GetPageSize();
+ dump.rss_anon_bytes = (resident_pages - shared_pages) * page_size;
+ return dump;
}
bool ProcessMetrics::GetPageFaultCounts(PageFaultCounts* counts) const {
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc
index 2b636279..eb45247 100644
--- a/base/process/process_metrics_unittest.cc
+++ b/base/process/process_metrics_unittest.cc
@@ -736,6 +736,32 @@
#endif // ENABLE_CPU_TESTS
+TEST_F(SystemMetricsTest, TestValidMemoryInfo) {
+ std::unique_ptr<ProcessMetrics> metrics =
+ ProcessMetrics::CreateCurrentProcessMetrics();
+
+ auto memory_info = metrics->GetMemoryInfo();
+ EXPECT_TRUE(memory_info.has_value());
+ EXPECT_GT(memory_info->resident_set_bytes, 0U);
+
+#if BUILDFLAG(IS_APPLE)
+ EXPECT_GT(memory_info->physical_footprint_bytes, 0U);
+ EXPECT_GT(memory_info->internal_bytes, 0U);
+ EXPECT_GE(memory_info->compressed_bytes, 0U);
+#endif // BUILDFLAG(IS_APPLE)
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
+ BUILDFLAG(IS_FUCHSIA)
+ EXPECT_GT(memory_info->rss_anon_bytes, 0U);
+ EXPECT_GE(memory_info->vm_swap_bytes, 0U);
+#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
+ // BUILDFLAG(IS_ANDROID)
+
+#if BUILDFLAG(IS_WIN)
+ EXPECT_GT(memory_info->private_bytes, 0U);
+#endif // BUILDFLAG(IS_WIN)
+}
+
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(SystemMetricsTest, ParseZramMmStat) {
SwapInfo swapinfo;
diff --git a/base/process/process_metrics_win.cc b/base/process/process_metrics_win.cc
index 8514c76c..40e30a6 100644
--- a/base/process/process_metrics_win.cc
+++ b/base/process/process_metrics_win.cc
@@ -160,6 +160,23 @@
return WrapUnique(new ProcessMetrics(process));
}
+base::expected<ProcessMemoryInfo, ProcessUsageError>
+ProcessMetrics::GetMemoryInfo() const {
+ if (!process_.is_valid()) {
+ return base::unexpected(ProcessUsageError::kProcessNotFound);
+ }
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ if (!::GetProcessMemoryInfo(process_.get(),
+ reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
+ sizeof(pmc))) {
+ return base::unexpected(ProcessUsageError::kSystemError);
+ }
+ ProcessMemoryInfo counters;
+ counters.private_bytes = pmc.PrivateUsage;
+ counters.resident_set_bytes = pmc.WorkingSetSize;
+ return counters;
+}
+
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
#if defined(ARCH_CPU_ARM64)
diff --git a/chrome/browser/ash/arc/memory_pressure/container_app_killer.cc b/chrome/browser/ash/arc/memory_pressure/container_app_killer.cc
index d4bc2f4f..8dfa393 100644
--- a/chrome/browser/ash/arc/memory_pressure/container_app_killer.cc
+++ b/chrome/browser/ash/arc/memory_pressure/container_app_killer.cc
@@ -155,9 +155,10 @@
uint64_t ContainerAppKiller::GetMemoryFootprintKB(base::ProcessId pid) {
auto process_metrics = base::ProcessMetrics::CreateProcessMetrics(pid);
- return (process_metrics->GetVmSwapBytes() +
- process_metrics->GetResidentSetSize()) /
- 1024;
+ auto info = process_metrics->GetMemoryInfo();
+ return info.has_value()
+ ? (info->vm_swap_bytes + info->resident_set_bytes) / 1024
+ : 0;
}
bool ContainerAppKiller::KillArcProcess(base::ProcessId nspid) {
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
index 39060df7e..9987daac 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
@@ -866,8 +866,8 @@
// To match with the memory maps, need to convert it to absolute path,
// which may hit ScopedBlockingCall.
base::ScopedAllowBlockingForTesting allow_blocking;
- auto maps =
- memory_instrumentation::OSMetrics::GetProcessMemoryMaps(process.Pid());
+ auto maps = memory_instrumentation::OSMetrics::GetProcessMemoryMaps(
+ process.Handle());
bool found = false;
for (const memory_instrumentation::mojom::VmRegionPtr& region : maps) {
if (region->module_debugid.empty())
diff --git a/chrome/browser/resource_coordinator/utils.cc b/chrome/browser/resource_coordinator/utils.cc
index 1c89633..b432099e7a 100644
--- a/chrome/browser/resource_coordinator/utils.cc
+++ b/chrome/browser/resource_coordinator/utils.cc
@@ -30,8 +30,8 @@
auto dump = memory_instrumentation::mojom::RawOSMemDump::New();
dump->platform_private_footprint =
memory_instrumentation::mojom::PlatformPrivateFootprint::New();
- bool success = memory_instrumentation::OSMetrics::FillOSMemoryDump(
- base::GetProcId(handle), dump.get());
+ bool success =
+ memory_instrumentation::OSMetrics::FillOSMemoryDump(handle, dump.get());
// Failed to get private memory for the process, e.g. the process has died.
if (!success)
diff --git a/chrome/browser/task_manager/sampling/task_group_sampler.cc b/chrome/browser/task_manager/sampling/task_group_sampler.cc
index fb04077..5366e24c 100644
--- a/chrome/browser/task_manager/sampling/task_group_sampler.cc
+++ b/chrome/browser/task_manager/sampling/task_group_sampler.cc
@@ -138,7 +138,11 @@
DCHECK_CALLED_ON_VALID_SEQUENCE(worker_pool_sequenced_checker_);
#if BUILDFLAG(IS_CHROMEOS_ASH)
- return process_metrics_->GetVmSwapBytes();
+ auto info = process_metrics_->GetMemoryInfo();
+ if (!info.has_value()) {
+ return 0;
+ }
+ return info->vm_swap_bytes;
#else
return 0;
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/components/services/heap_profiling/json_exporter_unittest.cc b/components/services/heap_profiling/json_exporter_unittest.cc
index cbe3f02..7e27d43 100644
--- a/components/services/heap_profiling/json_exporter_unittest.cc
+++ b/components/services/heap_profiling/json_exporter_unittest.cc
@@ -293,7 +293,7 @@
TEST(ProfilingJsonExporterTest, MAYBE_MemoryMaps) {
ExportParams params;
params.maps = memory_instrumentation::OSMetrics::GetProcessMemoryMaps(
- base::Process::Current().Pid());
+ base::Process::Current().Handle());
ASSERT_GT(params.maps.size(), 2u);
std::string json = ExportMemoryMapsAndV2StackTraceToJSON(¶ms);
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 183ddbb..93fb4523 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -5088,14 +5088,12 @@
dump->platform_private_footprint =
memory_instrumentation::mojom::PlatformPrivateFootprint::New();
#if BUILDFLAG(IS_APPLE)
- bool success =
- memory_instrumentation::OSMetrics::FillOSMemoryDumpFromTaskPort(
- ChildProcessTaskPortProvider::GetInstance()->TaskForHandle(
- GetProcess().Handle()),
- dump.get());
+ bool success = memory_instrumentation::OSMetrics::FillOSMemoryDump(
+ GetProcess().Handle(), ChildProcessTaskPortProvider::GetInstance(),
+ dump.get());
#else
bool success = memory_instrumentation::OSMetrics::FillOSMemoryDump(
- GetProcess().Pid(), dump.get());
+ GetProcess().Handle(), dump.get());
#endif
// Failed to get private memory for the process, e.g. the process has died.
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc
index 291774e..6386862 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc
@@ -8,6 +8,7 @@
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/not_fatal_until.h"
+#include "base/process/process.h"
#include "base/synchronization/lock.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/memory_dump_request_args.h"
@@ -169,12 +170,13 @@
bool global_success = true;
base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
for (const base::ProcessId& pid : args.pids) {
+ auto handle = base::Process::Open(pid).Handle();
mojom::RawOSMemDumpPtr result = mojom::RawOSMemDump::New();
result->platform_private_footprint = mojom::PlatformPrivateFootprint::New();
- bool success = OSMetrics::FillOSMemoryDump(pid, result.get());
+ bool success = OSMetrics::FillOSMemoryDump(handle, result.get());
if (args.mmap_option != mojom::MemoryMapOption::NONE) {
success = success && OSMetrics::FillProcessMemoryMaps(
- pid, args.mmap_option, result.get());
+ handle, args.mmap_option, result.get());
}
if (success) {
results[pid] = std::move(result);
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc
index ae67a8b..0e7ab66 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc
@@ -9,7 +9,7 @@
namespace memory_instrumentation {
// static
-bool OSMetrics::FillProcessMemoryMaps(base::ProcessId pid,
+bool OSMetrics::FillProcessMemoryMaps(base::ProcessHandle handle,
mojom::MemoryMapOption mmap_option,
mojom::RawOSMemDump* dump) {
DCHECK_NE(mmap_option, mojom::MemoryMapOption::NONE);
@@ -19,9 +19,9 @@
#if BUILDFLAG(IS_MAC)
// On macOS, fetching all memory maps is very slow. See
// https://crbug.com/826913 and https://crbug.com/1035401.
- results = GetProcessModules(pid);
+ results = GetProcessModules(handle);
#else
- results = GetProcessMemoryMaps(pid);
+ results = GetProcessMemoryMaps(handle);
#endif
@@ -33,4 +33,15 @@
return true;
}
+#if !BUILDFLAG(IS_APPLE)
+base::expected<base::ProcessMemoryInfo, base::ProcessUsageError>
+OSMetrics::GetMemoryInfo(base::ProcessHandle handle) {
+ auto process_metrics =
+ (handle == base::kNullProcessHandle)
+ ? base::ProcessMetrics::CreateCurrentProcessMetrics()
+ : base::ProcessMetrics::CreateProcessMetrics(handle);
+ return process_metrics->GetMemoryInfo();
+}
+#endif // !BUILDFLAG(IS_APPLE)
+
} // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
index f04f99d..b690f95 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
@@ -9,6 +9,7 @@
#include "base/component_export.h"
#include "base/gtest_prod_util.h"
#include "base/process/process_handle.h"
+#include "base/process/process_metrics.h"
#include "base/trace_event/process_memory_dump.h"
#include "build/build_config.h"
#include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
@@ -39,19 +40,22 @@
class COMPONENT_EXPORT(
RESOURCE_COORDINATOR_PUBLIC_MEMORY_INSTRUMENTATION) OSMetrics {
public:
- // Fills |dump| with memory information about |pid|. See class comments for
- // restrictions on |pid|. |dump.platform_private_footprint| must be allocated
- // before calling this function. If |pid| is null, the pid of the current
- // process is used
- static bool FillOSMemoryDump(base::ProcessId pid, mojom::RawOSMemDump* dump);
+ // Fills |dump| with memory information about |handle|. See class comments for
+ // restrictions on |handle|. |dump.platform_private_footprint| must be
+ // allocated before calling this function. If |handle| is null, the handle of
+ // the current process is used
+ static bool FillOSMemoryDump(base::ProcessHandle handle,
+ mojom::RawOSMemDump* dump);
#if BUILDFLAG(IS_APPLE)
- static bool FillOSMemoryDumpFromTaskPort(mach_port_t task_port,
- mojom::RawOSMemDump* dump);
+ static bool FillOSMemoryDump(base::ProcessHandle handle,
+ base::PortProvider* port_provider,
+ mojom::RawOSMemDump* dump);
#endif
- static bool FillProcessMemoryMaps(base::ProcessId,
+ static bool FillProcessMemoryMaps(base::ProcessHandle,
mojom::MemoryMapOption,
mojom::RawOSMemDump*);
- static std::vector<mojom::VmRegionPtr> GetProcessMemoryMaps(base::ProcessId);
+ static std::vector<mojom::VmRegionPtr> GetProcessMemoryMaps(
+ base::ProcessHandle);
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
static void SetProcSmapsForTesting(FILE*);
@@ -67,9 +71,14 @@
MemoryMaps);
#if BUILDFLAG(IS_MAC)
- static std::vector<mojom::VmRegionPtr> GetProcessModules(base::ProcessId);
+ static std::vector<mojom::VmRegionPtr> GetProcessModules(base::ProcessHandle);
#endif
+#if !BUILDFLAG(IS_APPLE)
+ static base::expected<base::ProcessMemoryInfo, base::ProcessUsageError>
+ GetMemoryInfo(base::ProcessHandle handle);
+#endif // !BUILDFLAG(IS_APPLE)
+
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
// Provides information on the dump state of resident pages. These values are
// written to logs. New enum values can be added, but existing enums must
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_fuchsia.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_fuchsia.cc
index 36f4fa1..b7dedda 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_fuchsia.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_fuchsia.cc
@@ -18,32 +18,23 @@
namespace memory_instrumentation {
// static
-bool OSMetrics::FillOSMemoryDump(base::ProcessId pid,
+bool OSMetrics::FillOSMemoryDump(base::ProcessHandle handle,
mojom::RawOSMemDump* dump) {
- base::Process process = pid == base::kNullProcessId
- ? base::Process::Current()
- : base::Process::Open(pid);
- zx::unowned<zx::process> zx_process(process.Handle());
- zx_info_task_stats_t info;
- zx_status_t status = zx_process->get_info(ZX_INFO_TASK_STATS, &info,
- sizeof(info), nullptr, nullptr);
- if (status != ZX_OK) {
+ auto info = GetMemoryInfo(handle);
+ if (!info.has_value()) {
return false;
}
- size_t rss_bytes = info.mem_private_bytes + info.mem_shared_bytes;
- size_t rss_anon_bytes = info.mem_private_bytes;
-
- dump->resident_set_kb = rss_bytes / 1024;
- dump->platform_private_footprint->rss_anon_bytes = rss_anon_bytes;
- // Fuchsia has no swap.
- dump->platform_private_footprint->vm_swap_bytes = 0;
+ dump->platform_private_footprint->rss_anon_bytes = info->rss_anon_bytes;
+ dump->platform_private_footprint->vm_swap_bytes = info->vm_swap_bytes;
+ dump->resident_set_kb =
+ base::saturated_cast<uint32_t>(info->resident_set_bytes / 1024);
return true;
}
// static
std::vector<mojom::VmRegionPtr> OSMetrics::GetProcessMemoryMaps(
- base::ProcessId) {
+ base::ProcessHandle) {
// TODO(crbug.com/40720107): Implement this.
NOTIMPLEMENTED();
return std::vector<mojom::VmRegionPtr>();
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
index 03edcc7..588c7f53 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
@@ -54,20 +54,6 @@
pid == base::kNullProcessId ? "self" : base::NumberToString(pid));
}
-bool GetResidentAndSharedPagesFromStatmFile(int fd,
- uint64_t* resident_pages,
- uint64_t* shared_pages) {
- lseek(fd, 0, SEEK_SET);
- char line[kMaxLineSize];
- int res = read(fd, line, kMaxLineSize - 1);
- if (res <= 0)
- return false;
- line[res] = '\0';
- int num_scanned =
- sscanf(line, "%*s %" SCNu64 " %" SCNu64, resident_pages, shared_pages);
- return num_scanned == 2;
-}
-
bool ResetPeakRSSIfPossible(base::ProcessId pid) {
static bool is_peak_rss_resettable = true;
if (!is_peak_rss_resettable)
@@ -80,14 +66,6 @@
return is_peak_rss_resettable;
}
-std::unique_ptr<base::ProcessMetrics> CreateProcessMetrics(
- base::ProcessId pid) {
- if (pid == base::kNullProcessId) {
- return base::ProcessMetrics::CreateCurrentProcessMetrics();
- }
- return base::ProcessMetrics::CreateProcessMetrics(pid);
-}
-
struct ModuleData {
std::string path;
std::string build_id;
@@ -290,36 +268,19 @@
}
// static
-bool OSMetrics::FillOSMemoryDump(base::ProcessId pid,
+bool OSMetrics::FillOSMemoryDump(base::ProcessHandle handle,
mojom::RawOSMemDump* dump) {
- // TODO(chiniforooshan): There is no need to read both /statm and /status
- // files. Refactor to get everything from /status using ProcessMetric.
- auto statm_file = GetProcPidDir(pid).Append("statm");
- auto autoclose = base::ScopedFD(open(statm_file.value().c_str(), O_RDONLY));
- int statm_fd = autoclose.get();
-
- if (statm_fd == -1)
+ auto info = GetMemoryInfo(handle);
+ if (!info.has_value()) {
return false;
+ }
- uint64_t resident_pages;
- uint64_t shared_pages;
- bool success = GetResidentAndSharedPagesFromStatmFile(
- statm_fd, &resident_pages, &shared_pages);
-
- if (!success)
- return false;
-
- auto process_metrics = CreateProcessMetrics(pid);
-
- static const size_t page_size = base::GetPageSize();
- uint64_t rss_anon_bytes = (resident_pages - shared_pages) * page_size;
- uint64_t vm_swap_bytes = process_metrics->GetVmSwapBytes();
-
- dump->platform_private_footprint->rss_anon_bytes = rss_anon_bytes;
- dump->platform_private_footprint->vm_swap_bytes = vm_swap_bytes;
- dump->resident_set_kb = process_metrics->GetResidentSetSize() / 1024;
- dump->peak_resident_set_kb = GetPeakResidentSetSize(pid);
- dump->is_peak_rss_resettable = ResetPeakRSSIfPossible(pid);
+ dump->platform_private_footprint->rss_anon_bytes = info->rss_anon_bytes;
+ dump->platform_private_footprint->vm_swap_bytes = info->vm_swap_bytes;
+ dump->resident_set_kb =
+ base::saturated_cast<uint32_t>(info->resident_set_bytes / 1024);
+ dump->peak_resident_set_kb = GetPeakResidentSetSize(handle);
+ dump->is_peak_rss_resettable = ResetPeakRSSIfPossible(handle);
#if BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
@@ -351,7 +312,8 @@
}
// static
-std::vector<VmRegionPtr> OSMetrics::GetProcessMemoryMaps(base::ProcessId pid) {
+std::vector<VmRegionPtr> OSMetrics::GetProcessMemoryMaps(
+ base::ProcessHandle handle) {
std::vector<VmRegionPtr> maps;
uint32_t res = 0;
if (g_proc_smaps_for_testing) {
@@ -359,7 +321,8 @@
} else {
std::string file_name =
"/proc/" +
- (pid == base::kNullProcessId ? "self" : base::NumberToString(pid)) +
+ (handle == base::kNullProcessHandle ? "self"
+ : base::NumberToString(handle)) +
"/smaps";
base::ScopedFILE smaps_file(fopen(file_name.c_str(), "r"));
res = ReadLinuxProcSmapsFile(smaps_file.get(), &maps);
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc
index c6c1833..a45ebda 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc
@@ -33,34 +33,6 @@
namespace {
-// Don't simply use sizeof(task_vm_info) / sizeof(natural_t):
-// In the 10.15 SDK, this structure is 87 32-bit words long, and in
-// mach_types.defs:
-//
-// type task_info_t = array[*:87] of integer_t;
-//
-// However in the 10.14 SDK, this structure is 42 32-bit words, and in
-// mach_types.defs:
-//
-// type task_info_t = array[*:52] of integer_t;
-//
-// As a result, the 10.15 SDK's task_vm_info won't fit inside the 10.14 SDK's
-// task_info_t, so the *rest of the system* (on 10.14 and earlier) can't handle
-// calls that request the full 10.15 structure. We have to request a prefix of
-// it that 10.14 and earlier can handle by limiting the length we request. The
-// rest of the fields just get ignored, but we don't use them anyway.
-
-constexpr mach_msg_type_number_t ChromeTaskVMInfoCount =
- TASK_VM_INFO_REV2_COUNT;
-
-// The count field is in units of natural_t, which is the machine's word size
-// (64 bits on all modern machines), but the task_info_t array is in units of
-// integer_t, which is 32 bits.
-constexpr mach_msg_type_number_t MAX_MIG_SIZE_FOR_1014 =
- 52 / (sizeof(natural_t) / sizeof(integer_t));
-static_assert(ChromeTaskVMInfoCount <= MAX_MIG_SIZE_FOR_1014,
- "task_vm_info must be small enough for 10.14 MIG interfaces");
-
using VMRegion = mojom::VmRegion;
bool IsAddressInSharedRegion(uint64_t address) {
@@ -254,41 +226,38 @@
} // namespace
// static
-bool OSMetrics::FillOSMemoryDump(base::ProcessId pid,
+bool OSMetrics::FillOSMemoryDump(base::ProcessHandle handle,
mojom::RawOSMemDump* dump) {
- if (pid != base::kNullProcessId && pid != base::GetCurrentProcId()) {
+ auto current_handle = base::GetCurrentProcessHandle();
+ if (handle != base::kNullProcessId && handle != current_handle) {
return false;
}
- return FillOSMemoryDumpFromTaskPort(mach_task_self(), dump);
+ return FillOSMemoryDump(current_handle, nullptr, dump);
}
// static
-bool OSMetrics::FillOSMemoryDumpFromTaskPort(mach_port_t task_port,
- mojom::RawOSMemDump* dump) {
- task_vm_info info;
- mach_msg_type_number_t count = ChromeTaskVMInfoCount;
- kern_return_t result = task_info(
- task_port, TASK_VM_INFO, reinterpret_cast<task_info_t>(&info), &count);
- if (result != KERN_SUCCESS)
+bool OSMetrics::FillOSMemoryDump(base::ProcessHandle handle,
+ base::PortProvider* port_provider,
+ mojom::RawOSMemDump* dump) {
+ auto process_metrics =
+ base::ProcessMetrics::CreateProcessMetrics(handle, port_provider);
+ auto info = process_metrics->GetMemoryInfo();
+ if (!info.has_value()) {
return false;
-
- dump->platform_private_footprint->internal_bytes = info.internal;
- dump->platform_private_footprint->compressed_bytes = info.compressed;
- dump->resident_set_kb = info.resident_size / 1024;
- dump->peak_resident_set_kb = info.resident_size_peak / 1024;
-
- // The |phys_footprint| field was introduced in 10.11.
- if (count == ChromeTaskVMInfoCount) {
- dump->platform_private_footprint->phys_footprint_bytes =
- info.phys_footprint;
}
+ dump->platform_private_footprint->phys_footprint_bytes =
+ info->physical_footprint_bytes;
+ dump->platform_private_footprint->internal_bytes = info->internal_bytes;
+ dump->platform_private_footprint->compressed_bytes = info->compressed_bytes;
+ dump->resident_set_kb =
+ base::saturated_cast<uint32_t>(info->resident_set_bytes / 1024);
return true;
}
// static
std::vector<mojom::VmRegionPtr> OSMetrics::GetProcessMemoryMaps(
- base::ProcessId pid) {
+ base::ProcessHandle handle) {
std::vector<mojom::VmRegionPtr> maps;
std::vector<VMRegion> dyld_regions;
@@ -340,7 +309,7 @@
#if !BUILDFLAG(IS_IOS)
std::vector<mojom::VmRegionPtr> OSMetrics::GetProcessModules(
- base::ProcessId pid) {
+ base::ProcessHandle handle) {
std::vector<mojom::VmRegionPtr> maps;
std::vector<VMRegion> dyld_regions;
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc
index 841a542..e825189 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc
@@ -137,10 +137,10 @@
// BUILDFLAG(IS_ANDROID)
TEST(OSMetricsTest, GivesNonZeroResults) {
- base::ProcessId pid = base::kNullProcessId;
+ base::ProcessHandle handle = base::kNullProcessHandle;
mojom::RawOSMemDump dump;
dump.platform_private_footprint = mojom::PlatformPrivateFootprint::New();
- EXPECT_TRUE(OSMetrics::FillOSMemoryDump(pid, &dump));
+ EXPECT_TRUE(OSMetrics::FillOSMemoryDump(handle, &dump));
EXPECT_TRUE(dump.platform_private_footprint);
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
BUILDFLAG(IS_FUCHSIA)
@@ -162,14 +162,14 @@
base::ScopedFILE empty_file(OpenFile(base::FilePath("/dev/null"), "r"));
ASSERT_TRUE(empty_file.get());
OSMetrics::SetProcSmapsForTesting(empty_file.get());
- auto no_maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessId);
+ auto no_maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessHandle);
ASSERT_TRUE(no_maps.empty());
// Parse the 1st smaps file.
base::ScopedFILE temp_file1;
CreateTempFileWithContents(kTestSmaps1, &temp_file1);
OSMetrics::SetProcSmapsForTesting(temp_file1.get());
- auto maps_1 = OSMetrics::GetProcessMemoryMaps(base::kNullProcessId);
+ auto maps_1 = OSMetrics::GetProcessMemoryMaps(base::kNullProcessHandle);
ASSERT_EQ(2UL, maps_1.size());
EXPECT_EQ(0x00400000UL, maps_1[0]->start_address);
@@ -200,7 +200,7 @@
base::ScopedFILE temp_file2;
CreateTempFileWithContents(kTestSmaps2, &temp_file2);
OSMetrics::SetProcSmapsForTesting(temp_file2.get());
- auto maps_2 = OSMetrics::GetProcessMemoryMaps(base::kNullProcessId);
+ auto maps_2 = OSMetrics::GetProcessMemoryMaps(base::kNullProcessHandle);
ASSERT_EQ(1UL, maps_2.size());
EXPECT_EQ(0x7fe7ce79c000UL, maps_2[0]->start_address);
EXPECT_EQ(0x7fe7ce7a8000UL - 0x7fe7ce79c000UL, maps_2[0]->size_in_bytes);
@@ -266,7 +266,7 @@
void DummyFunction() {}
TEST(OSMetricsTest, TestWinModuleReading) {
- auto maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessId);
+ auto maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessHandle);
wchar_t module_name[MAX_PATH];
DWORD result = GetModuleFileName(nullptr, module_name, MAX_PATH);
@@ -350,9 +350,9 @@
// Test failing on Mac ASan 64: https://crbug.com/852690
TEST(OSMetricsTest, DISABLED_TestMachOReading) {
- auto maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessId);
+ auto maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessHandle);
CheckMachORegions(maps);
- maps = OSMetrics::GetProcessModules(base::kNullProcessId);
+ maps = OSMetrics::GetProcessModules(base::kNullProcessHandle);
CheckMachORegions(maps);
}
#endif // BUILDFLAG(IS_MAC)
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc
index ddcf3cc..7b6b4ce 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc
@@ -40,32 +40,22 @@
} // namespace
// static
-bool OSMetrics::FillOSMemoryDump(base::ProcessId pid,
+bool OSMetrics::FillOSMemoryDump(base::ProcessHandle handle,
mojom::RawOSMemDump* dump) {
- base::Process process;
- if (pid == base::kNullProcessId) {
- process = base::Process::Current();
- } else {
- process = base::Process::Open(pid);
- }
- if (!process.IsValid()) {
+ auto info = GetMemoryInfo(handle);
+ if (!info.has_value()) {
return false;
}
- PROCESS_MEMORY_COUNTERS_EX pmc;
- if (::GetProcessMemoryInfo(process.Handle(),
- reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
- sizeof(pmc))) {
- dump->platform_private_footprint->private_bytes = pmc.PrivateUsage;
- dump->resident_set_kb =
- base::saturated_cast<uint32_t>(pmc.WorkingSetSize / 1024);
- return true;
- }
- return false;
+
+ dump->platform_private_footprint->private_bytes = info->private_bytes;
+ dump->resident_set_kb =
+ base::saturated_cast<uint32_t>(info->resident_set_bytes / 1024);
+ return true;
}
// static
std::vector<mojom::VmRegionPtr> OSMetrics::GetProcessMemoryMaps(
- base::ProcessId pid) {
+ base::ProcessHandle handle) {
std::vector<mojom::VmRegionPtr> maps;
std::vector<HMODULE> modules;
if (!base::win::GetLoadedModulesSnapshot(::GetCurrentProcess(), &modules))