1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "base/android/pmf_utils.h"
11
12 #include <fcntl.h>
13 #include <inttypes.h>
14 #include <unistd.h>
15
16 #include "base/strings/string_number_conversions.h"
17 #include "base/threading/thread_restrictions.h"
18
19 namespace base::android {
20 namespace {
CalculateProcessMemoryFootprint(base::File & statm_file,base::File & status_file)21 std::optional<uint64_t> CalculateProcessMemoryFootprint(
22 base::File& statm_file,
23 base::File& status_file) {
24 // Get total resident and shared sizes from statm file.
25 static size_t page_size = static_cast<size_t>(getpagesize());
26 uint64_t resident_pages = 0;
27 uint64_t shared_pages = 0;
28 uint64_t vm_size_pages = 0;
29 uint64_t swap_footprint = 0;
30 constexpr uint32_t kMaxLineSize = 4096;
31 char line[kMaxLineSize];
32
33 int n = statm_file.ReadAtCurrentPos(line, sizeof(line) - 1);
34 if (n <= 0) {
35 return std::optional<size_t>();
36 }
37 line[n] = '\0';
38
39 int num_scanned = sscanf(line, "%" SCNu64 " %" SCNu64 " %" SCNu64,
40 &vm_size_pages, &resident_pages, &shared_pages);
41 if (num_scanned != 3) {
42 return std::optional<size_t>();
43 }
44
45 // Get swap size from status file. The format is: VmSwap : 10 kB.
46 n = status_file.ReadAtCurrentPos(line, sizeof(line) - 1);
47 if (n <= 0) {
48 return std::optional<size_t>();
49 }
50 line[n] = '\0';
51
52 char* swap_line = strstr(line, "VmSwap");
53 if (!swap_line) {
54 return std::optional<size_t>();
55 }
56 num_scanned = sscanf(swap_line, "VmSwap: %" SCNu64 " kB", &swap_footprint);
57 if (num_scanned != 1) {
58 return std::optional<size_t>();
59 }
60
61 swap_footprint *= 1024;
62 return (resident_pages - shared_pages) * page_size + swap_footprint;
63 }
64 } // namespace
65
66 // static
CalculatePrivateMemoryFootprintForTesting(base::File & statm_file,base::File & status_file)67 std::optional<uint64_t> PmfUtils::CalculatePrivateMemoryFootprintForTesting(
68 base::File& statm_file,
69 base::File& status_file) {
70 return CalculateProcessMemoryFootprint(statm_file, status_file);
71 }
72
73 // static
GetPrivateMemoryFootprintForCurrentProcess()74 std::optional<uint64_t> PmfUtils::GetPrivateMemoryFootprintForCurrentProcess() {
75 // ScopedAllowBlocking is required to use base::File, but /proc/{pid}/status
76 // and /proc/{pid}/statm are not regular files. For example, regarding linux,
77 // proc_pid_statm() defined in fs/proc/array.c is invoked when reading
78 // /proc/{pid}/statm. proc_pid_statm() gets task information and directly
79 // writes the information into the given seq_file. This is different from
80 // regular file operations.
81 base::ScopedAllowBlocking allow_blocking;
82
83 base::FilePath proc_self_dir = base::FilePath("/proc/self");
84 base::File status_file(
85 proc_self_dir.Append("status"),
86 base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
87 base::File statm_file(
88 proc_self_dir.Append("statm"),
89 base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
90 if (!status_file.IsValid() || !statm_file.IsValid()) {
91 return std::optional<size_t>();
92 }
93
94 return CalculateProcessMemoryFootprint(statm_file, status_file);
95 }
96
97 } // namespace base::android
98