LLDB mainline
MemoryMonitor.cpp
Go to the documentation of this file.
1//===-- MemoryMonitor.cpp -------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
13#include "lldb/Utility/Log.h"
14#include "llvm/ADT/ScopeExit.h"
15#include "llvm/Support/Error.h"
16#include <cstddef>
17#include <cstdio>
18#include <cstring>
19
20#if defined(__linux__)
21#include <fcntl.h>
22#include <poll.h>
23#include <sys/eventfd.h>
24#include <sys/poll.h>
25#include <unistd.h>
26#endif
27
28#if defined(_WIN32)
29#include <atomic>
30#include <windows.h>
31#endif
32
33using namespace lldb_private;
34
35#if defined(__linux__)
36class MemoryMonitorLinux : public MemoryMonitor {
37public:
39
40 explicit MemoryMonitorLinux(Callback callback)
41 : MemoryMonitor(std::move(callback)),
42 m_stop_fd(::eventfd(0, EFD_NONBLOCK)) {}
43
44 ~MemoryMonitorLinux() {
45 if (m_memory_monitor_thread.IsJoinable())
46 m_memory_monitor_thread.Join(nullptr);
47 if (m_stop_fd != 1)
48 ::close(m_stop_fd);
49 }
50
51 void Start() override {
52 if (m_stop_fd < 0) {
54 GetLog(LLDBLog::Host),
55 llvm::errorCodeToError(llvm::errnoAsErrorCode()),
56 "failed to create stop file descriptor for memory monitor: {0}");
57 return;
58 }
59
60 llvm::Expected<HostThread> memory_monitor_thread =
61 ThreadLauncher::LaunchThread("memory.monitor",
62 [this] { return MonitorThread(); });
63 if (memory_monitor_thread) {
64 m_memory_monitor_thread = *memory_monitor_thread;
65 } else {
66 LLDB_LOG_ERROR(GetLog(LLDBLog::Host), memory_monitor_thread.takeError(),
67 "failed to launch host thread: {0}");
68 }
69 }
70
71 void Stop() override {
72 if (m_memory_monitor_thread.IsJoinable()) {
73 if (m_stop_fd != -1)
74 ::eventfd_write(m_stop_fd, 1);
75 m_memory_monitor_thread.Join(nullptr);
76 }
77 }
78
79private:
81 constexpr size_t pressure_idx = 0;
82 constexpr size_t stop_idx = 1;
83 constexpr size_t fd_count = 2;
84 std::array<pollfd, fd_count> pfds{};
85
86 // Setup stop file descriptor.
87 pfds[stop_idx].fd = m_stop_fd;
88 pfds[stop_idx].events = POLLIN;
89
90 // Setup pressure file descriptor.
91 pfds[pressure_idx].fd =
92 ::open("/proc/pressure/memory", O_RDWR | O_NONBLOCK);
93 if (pfds[pressure_idx].fd < 0)
94 return {};
95 pfds[pressure_idx].events = POLLPRI;
96
97 llvm::scope_exit cleanup([&]() { ::close(pfds[pressure_idx].fd); });
98
99 // Detect a 50ms stall in a 2 second time window.
100 constexpr llvm::StringRef trigger = "some 50000 2000000";
101 if (::write(pfds[pressure_idx].fd, trigger.data(), trigger.size() + 1) < 0)
102 return {};
103
104 while (true) {
105 constexpr int timeout_infinite = -1;
106 const int n = ::poll(pfds.data(), pfds.size(), timeout_infinite);
107 if (n > 0) {
108 // Handle stop event.
109 if (pfds[stop_idx].revents & (POLLIN | POLLERR))
110 return {};
111
112 if (pfds[pressure_idx].revents & POLLERR)
113 return {};
114 if (pfds[pressure_idx].revents & POLLPRI)
115 m_callback();
116 }
117 }
118 return {};
119 }
120 int m_stop_fd = -1;
121 HostThread m_memory_monitor_thread;
122};
123#elif defined(_WIN32)
124
125class MemoryMonitorWindows : public MemoryMonitor {
126public:
128
130 HANDLE low_memory_notification =
131 CreateMemoryResourceNotification(LowMemoryResourceNotification);
132 if (!low_memory_notification)
133 return {};
134
135 while (!m_done) {
136 if (WaitForSingleObject(low_memory_notification, g_timeout) ==
137 WAIT_OBJECT_0) {
138 m_callback();
139 }
140 }
141 return {};
142 }
143
144 void Start() override {
145 llvm::Expected<HostThread> memory_monitor_thread =
146 ThreadLauncher::LaunchThread("lldb.debugger.memory-monitor",
147 [this] { return MonitorThread(); });
148 if (memory_monitor_thread) {
149 m_memory_monitor_thread = *memory_monitor_thread;
150 } else {
151 LLDB_LOG_ERROR(GetLog(LLDBLog::Host), memory_monitor_thread.takeError(),
152 "failed to launch host thread: {0}");
153 }
154 }
155
156 void Stop() override {
157 if (m_memory_monitor_thread.IsJoinable()) {
158 m_done = true;
159 m_memory_monitor_thread.Join(nullptr);
160 }
161 }
162
163private:
164 static constexpr uint32_t g_timeout = 1000;
165 std::atomic<bool> m_done = false;
166 HostThread m_memory_monitor_thread;
167};
168#endif
169
170#if !defined(__APPLE__)
171std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {
172#if defined(__linux__)
173 return std::make_unique<MemoryMonitorLinux>(std::move(callback));
174#elif defined(_WIN32)
175 return std::make_unique<MemoryMonitorWindows>(std::move(callback));
176#else
177 return nullptr;
178#endif
179}
180#endif
static lldb::thread_result_t MonitorThread(const Host::MonitorChildProcessCallback &callback, HANDLE process_handle)
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:392
void * HANDLE
MemoryMonitor(Callback callback)
static std::unique_ptr< MemoryMonitor > Create(Callback callback)
std::function< void()> Callback
static llvm::Expected< HostThread > LaunchThread(llvm::StringRef name, std::function< lldb::thread_result_t()> thread_function, size_t min_stack_byte_size=0)
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:332
void * thread_result_t
Definition lldb-types.h:62
#define O_NONBLOCK