#include "base/android/pmf_utils.h"
#include <fcntl.h>
#include <inttypes.h>
#include <unistd.h>
#include <optional>
#include "base/compiler_specific.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_restrictions.h"
namespace base::android {
namespace {
std::optional<ByteCount> CalculateProcessMemoryFootprint(
base::File& statm_file,
base::File& status_file) {
static size_t page_size = static_cast<size_t>(getpagesize());
uint64_t resident_pages = 0;
uint64_t shared_pages = 0;
uint64_t vm_size_pages = 0;
uint64_t swap_footprint_kb = 0;
constexpr uint32_t kMaxLineSize = 4096;
char line[kMaxLineSize];
base::span<uint8_t> line_span = base::as_writable_byte_span(line);
std::optional<size_t> n =
statm_file.ReadAtCurrentPos(line_span.first<kMaxLineSize - 1>());
if (n.value_or(0) == 0) {
return std::optional<ByteCount>();
}
line_span[*n] = 0;
int num_scanned =
UNSAFE_TODO(sscanf(line, "%" SCNu64 " %" SCNu64 " %" SCNu64,
&vm_size_pages, &resident_pages, &shared_pages));
if (num_scanned != 3) {
return std::optional<ByteCount>();
}
n = status_file.ReadAtCurrentPos(line_span.first<kMaxLineSize - 1>());
if (n.value_or(0) == 0) {
return std::optional<ByteCount>();
}
line_span[*n] = 0;
char* swap_line = UNSAFE_TODO(strstr(line, "VmSwap"));
if (!swap_line) {
return std::optional<ByteCount>();
}
num_scanned = UNSAFE_TODO(
sscanf(swap_line, "VmSwap: %" SCNu64 " kB", &swap_footprint_kb));
if (num_scanned != 1) {
return std::optional<ByteCount>();
}
return ByteCount::FromUnsigned((resident_pages - shared_pages) * page_size) +
KiB(swap_footprint_kb);
}
}
std::optional<ByteCount> PmfUtils::CalculatePrivateMemoryFootprintForTesting(
base::File& statm_file,
base::File& status_file) {
return CalculateProcessMemoryFootprint(statm_file, status_file);
}
std::optional<ByteCount>
PmfUtils::GetPrivateMemoryFootprintForCurrentProcess() {
base::ScopedAllowBlocking allow_blocking;
base::FilePath proc_self_dir = base::FilePath("/proc/self");
base::File status_file(
proc_self_dir.Append("status"),
base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
base::File statm_file(
proc_self_dir.Append("statm"),
base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
if (!status_file.IsValid() || !statm_file.IsValid()) {
return std::optional<ByteCount>();
}
return CalculateProcessMemoryFootprint(statm_file, status_file);
}
}