| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/android/pmf_utils.h" |
| |
| #include <fcntl.h> |
| #include <inttypes.h> |
| #include <unistd.h> |
| |
| #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) { |
| // Get total resident and shared sizes from statm 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]; |
| |
| int n = UNSAFE_TODO(statm_file.ReadAtCurrentPos(line, sizeof(line) - 1)); |
| if (n <= 0) { |
| return std::optional<ByteCount>(); |
| } |
| UNSAFE_TODO(line[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>(); |
| } |
| |
| // Get swap size from status file. The format is: VmSwap : 10 kB. |
| n = UNSAFE_TODO(status_file.ReadAtCurrentPos(line, sizeof(line) - 1)); |
| if (n <= 0) { |
| return std::optional<ByteCount>(); |
| } |
| UNSAFE_TODO(line[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); |
| } |
| } // namespace |
| |
| // static |
| std::optional<ByteCount> PmfUtils::CalculatePrivateMemoryFootprintForTesting( |
| base::File& statm_file, |
| base::File& status_file) { |
| return CalculateProcessMemoryFootprint(statm_file, status_file); |
| } |
| |
| // static |
| std::optional<ByteCount> |
| PmfUtils::GetPrivateMemoryFootprintForCurrentProcess() { |
| // ScopedAllowBlocking is required to use base::File, but /proc/{pid}/status |
| // and /proc/{pid}/statm are not regular files. For example, regarding linux, |
| // proc_pid_statm() defined in fs/proc/array.c is invoked when reading |
| // /proc/{pid}/statm. proc_pid_statm() gets task information and directly |
| // writes the information into the given seq_file. This is different from |
| // regular file operations. |
| 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); |
| } |
| |
| } // namespace base::android |