blob: 8558d53deeb74073fbaa2e3fc99b4501912938d4 [file] [log] [blame]
// Copyright 2019 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 "third_party/blink/renderer/controller/memory_usage_monitor_posix.h"
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <utility>
#include "third_party/blink/public/platform/platform.h"
namespace blink {
namespace {
bool ReadFileContents(int fd, base::span<char> contents) {
lseek(fd, 0, SEEK_SET);
int res = read(fd, contents.data(), contents.size() - 1);
if (res <= 0)
return false;
contents.data()[res] = '\0';
return true;
}
static MemoryUsageMonitor* g_instance_for_testing = nullptr;
MemoryUsageMonitorPosix& GetMemoryUsageMonitor() {
DEFINE_STATIC_LOCAL(MemoryUsageMonitorPosix, monitor, ());
return monitor;
}
} // namespace
// static
MemoryUsageMonitor& MemoryUsageMonitor::Instance() {
return g_instance_for_testing ? *g_instance_for_testing
: GetMemoryUsageMonitor();
}
// static
void MemoryUsageMonitor::SetInstanceForTesting(MemoryUsageMonitor* instance) {
g_instance_for_testing = instance;
}
// Since the measurement is done every second in background, optimizations are
// in place to get just the metrics we need from the proc files. So, this
// calculation exists here instead of using the cross-process memory-infra code.
bool MemoryUsageMonitorPosix::CalculateProcessMemoryFootprint(
int statm_fd,
int status_fd,
uint64_t* private_footprint,
uint64_t* swap_footprint,
uint64_t* vm_size,
uint64_t* vm_hwm_size) {
// Get total resident and shared sizes from statm file.
static size_t page_size = getpagesize();
uint64_t resident_pages;
uint64_t shared_pages;
uint64_t vm_size_pages;
constexpr uint32_t kMaxLineSize = 4096;
char line[kMaxLineSize];
if (!ReadFileContents(statm_fd, line))
return false;
int num_scanned = sscanf(line, "%" SCNu64 " %" SCNu64 " %" SCNu64,
&vm_size_pages, &resident_pages, &shared_pages);
if (num_scanned != 3)
return false;
// Get swap size from status file. The format is: VmSwap : 10 kB.
if (!ReadFileContents(status_fd, line))
return false;
char* swap_line = strstr(line, "VmSwap");
if (!swap_line)
return false;
num_scanned = sscanf(swap_line, "VmSwap: %" SCNu64 " kB", swap_footprint);
if (num_scanned != 1)
return false;
char* hwm_line = strstr(line, "VmHWM");
if (!hwm_line)
return false;
num_scanned = sscanf(hwm_line, "VmHWM: %" SCNu64 " kB", vm_hwm_size);
if (num_scanned != 1)
return false;
*vm_hwm_size *= 1024;
*swap_footprint *= 1024;
*private_footprint =
(resident_pages - shared_pages) * page_size + *swap_footprint;
*vm_size = vm_size_pages * page_size;
return true;
}
void MemoryUsageMonitorPosix::GetProcessMemoryUsage(MemoryUsage& usage) {
#if defined(OS_ANDROID)
ResetFileDescriptors();
#endif
if (!statm_fd_.is_valid() || !status_fd_.is_valid())
return;
uint64_t private_footprint, swap, vm_size, vm_hwm_size;
if (CalculateProcessMemoryFootprint(statm_fd_.get(), status_fd_.get(),
&private_footprint, &swap, &vm_size,
&vm_hwm_size)) {
usage.private_footprint_bytes = static_cast<double>(private_footprint);
usage.swap_bytes = static_cast<double>(swap);
usage.vm_size_bytes = static_cast<double>(vm_size);
usage.peak_resident_bytes = static_cast<double>(vm_hwm_size);
}
}
#if defined(OS_ANDROID)
void MemoryUsageMonitorPosix::ResetFileDescriptors() {
if (file_descriptors_reset_)
return;
file_descriptors_reset_ = true;
// See https://goo.gl/KjWnZP For details about why we read these files from
// sandboxed renderer. Keep these files open when detection is enabled.
if (!statm_fd_.is_valid())
statm_fd_.reset(open("/proc/self/statm", O_RDONLY));
if (!status_fd_.is_valid())
status_fd_.reset(open("/proc/self/status", O_RDONLY));
}
#endif
void MemoryUsageMonitorPosix::SetProcFiles(base::File statm_file,
base::File status_file) {
DCHECK(statm_file.IsValid());
DCHECK(status_file.IsValid());
DCHECK_EQ(-1, statm_fd_.get());
DCHECK_EQ(-1, status_fd_.get());
statm_fd_.reset(statm_file.TakePlatformFile());
status_fd_.reset(status_file.TakePlatformFile());
}
#if defined(OS_LINUX)
// static
void MemoryUsageMonitorPosix::Bind(
mojo::PendingReceiver<mojom::blink::MemoryUsageMonitorLinux> receiver) {
// This should be called only once per process on RenderProcessWillLaunch.
DCHECK(!GetMemoryUsageMonitor().receiver_.is_bound());
GetMemoryUsageMonitor().receiver_.Bind(std::move(receiver));
}
#endif
} // namespace blink