• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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