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