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