1 // Copyright 2019 The Chromium Authors. All rights reserved.
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 "third_party/blink/renderer/controller/memory_usage_monitor_posix.h"
6 
7 #include <ctype.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <utility>
11 
12 #include "third_party/blink/public/platform/platform.h"
13 
14 namespace blink {
15 
16 namespace {
17 
ReadFileContents(int fd,base::span<char> contents)18 bool ReadFileContents(int fd, base::span<char> contents) {
19   lseek(fd, 0, SEEK_SET);
20   int res = read(fd, contents.data(), contents.size() - 1);
21   if (res <= 0)
22     return false;
23   contents.data()[res] = '\0';
24   return true;
25 }
26 
27 static MemoryUsageMonitor* g_instance_for_testing = nullptr;
28 
GetMemoryUsageMonitor()29 MemoryUsageMonitorPosix& GetMemoryUsageMonitor() {
30   DEFINE_STATIC_LOCAL(MemoryUsageMonitorPosix, monitor, ());
31   return monitor;
32 }
33 
34 }  // namespace
35 
36 // static
Instance()37 MemoryUsageMonitor& MemoryUsageMonitor::Instance() {
38   return g_instance_for_testing ? *g_instance_for_testing
39                                 : GetMemoryUsageMonitor();
40 }
41 
42 // static
SetInstanceForTesting(MemoryUsageMonitor * instance)43 void MemoryUsageMonitor::SetInstanceForTesting(MemoryUsageMonitor* instance) {
44   g_instance_for_testing = instance;
45 }
46 
47 // Since the measurement is done every second in background, optimizations are
48 // in place to get just the metrics we need from the proc files. So, this
49 // calculation exists here instead of using the cross-process memory-infra code.
CalculateProcessMemoryFootprint(int statm_fd,int status_fd,uint64_t * private_footprint,uint64_t * swap_footprint,uint64_t * vm_size,uint64_t * vm_hwm_size)50 bool MemoryUsageMonitorPosix::CalculateProcessMemoryFootprint(
51     int statm_fd,
52     int status_fd,
53     uint64_t* private_footprint,
54     uint64_t* swap_footprint,
55     uint64_t* vm_size,
56     uint64_t* vm_hwm_size) {
57   // Get total resident and shared sizes from statm file.
58   static size_t page_size = getpagesize();
59   uint64_t resident_pages;
60   uint64_t shared_pages;
61   uint64_t vm_size_pages;
62   constexpr uint32_t kMaxLineSize = 4096;
63   char line[kMaxLineSize];
64   if (!ReadFileContents(statm_fd, line))
65     return false;
66   int num_scanned = sscanf(line, "%" SCNu64 " %" SCNu64 " %" SCNu64,
67                            &vm_size_pages, &resident_pages, &shared_pages);
68   if (num_scanned != 3)
69     return false;
70 
71   // Get swap size from status file. The format is: VmSwap :  10 kB.
72   if (!ReadFileContents(status_fd, line))
73     return false;
74   char* swap_line = strstr(line, "VmSwap");
75   if (!swap_line)
76     return false;
77   num_scanned = sscanf(swap_line, "VmSwap: %" SCNu64 " kB", swap_footprint);
78   if (num_scanned != 1)
79     return false;
80 
81   char* hwm_line = strstr(line, "VmHWM");
82   if (!hwm_line)
83     return false;
84   num_scanned = sscanf(hwm_line, "VmHWM: %" SCNu64 " kB", vm_hwm_size);
85   if (num_scanned != 1)
86     return false;
87 
88   *vm_hwm_size *= 1024;
89   *swap_footprint *= 1024;
90   *private_footprint =
91       (resident_pages - shared_pages) * page_size + *swap_footprint;
92   *vm_size = vm_size_pages * page_size;
93   return true;
94 }
95 
GetProcessMemoryUsage(MemoryUsage & usage)96 void MemoryUsageMonitorPosix::GetProcessMemoryUsage(MemoryUsage& usage) {
97 #if defined(OS_ANDROID)
98   ResetFileDescriptors();
99 #endif
100   if (!statm_fd_.is_valid() || !status_fd_.is_valid())
101     return;
102   uint64_t private_footprint, swap, vm_size, vm_hwm_size;
103   if (CalculateProcessMemoryFootprint(statm_fd_.get(), status_fd_.get(),
104                                       &private_footprint, &swap, &vm_size,
105                                       &vm_hwm_size)) {
106     usage.private_footprint_bytes = static_cast<double>(private_footprint);
107     usage.swap_bytes = static_cast<double>(swap);
108     usage.vm_size_bytes = static_cast<double>(vm_size);
109     usage.peak_resident_bytes = static_cast<double>(vm_hwm_size);
110   }
111 }
112 
113 #if defined(OS_ANDROID)
ResetFileDescriptors()114 void MemoryUsageMonitorPosix::ResetFileDescriptors() {
115   if (file_descriptors_reset_)
116     return;
117   file_descriptors_reset_ = true;
118   // See https://goo.gl/KjWnZP For details about why we read these files from
119   // sandboxed renderer. Keep these files open when detection is enabled.
120   if (!statm_fd_.is_valid())
121     statm_fd_.reset(open("/proc/self/statm", O_RDONLY));
122   if (!status_fd_.is_valid())
123     status_fd_.reset(open("/proc/self/status", O_RDONLY));
124 }
125 #endif
126 
SetProcFiles(base::File statm_file,base::File status_file)127 void MemoryUsageMonitorPosix::SetProcFiles(base::File statm_file,
128                                            base::File status_file) {
129   DCHECK(statm_file.IsValid());
130   DCHECK(status_file.IsValid());
131   DCHECK_EQ(-1, statm_fd_.get());
132   DCHECK_EQ(-1, status_fd_.get());
133   statm_fd_.reset(statm_file.TakePlatformFile());
134   status_fd_.reset(status_file.TakePlatformFile());
135 }
136 
137 #if defined(OS_LINUX) || defined(OS_BSD)
138 // static
Bind(mojo::PendingReceiver<mojom::blink::MemoryUsageMonitorLinux> receiver)139 void MemoryUsageMonitorPosix::Bind(
140     mojo::PendingReceiver<mojom::blink::MemoryUsageMonitorLinux> receiver) {
141   // This should be called only once per process on RenderProcessWillLaunch.
142   DCHECK(!GetMemoryUsageMonitor().receiver_.is_bound());
143   GetMemoryUsageMonitor().receiver_.Bind(std::move(receiver));
144 }
145 #endif
146 
147 }  // namespace blink
148