xref: /aosp_15_r20/external/cronet/base/android/pmf_utils.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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