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
9#include "lldb/Host/MemoryMonitor.h"
10#include "lldb/Host/HostThread.h"
11#include "lldb/Host/ThreadLauncher.h"
12#include "lldb/Utility/LLDBLog.h"
13#include "lldb/Utility/Log.h"
14#include "llvm/ADT/ScopeExit.h"
15#include "llvm/Support/Error.h"
16#include <atomic>
17#include <cstddef>
18#include <cstdio>
19#include <cstring>
20
21#if defined(__linux__)
22#include <fcntl.h>
23#include <poll.h>
24#include <unistd.h>
25#endif
26
27#if defined(_WIN32)
28#include <windows.h>
29#endif
30
31using namespace lldb_private;
32
33class MemoryMonitorPoll : public MemoryMonitor {
34public:
35 using MemoryMonitor::MemoryMonitor;
36
37 lldb::thread_result_t MonitorThread() {
38#if defined(__linux__)
39 struct pollfd fds;
40 fds.fd = open(file: "/proc/pressure/memory", O_RDWR | O_NONBLOCK);
41 if (fds.fd < 0)
42 return {};
43 fds.events = POLLPRI;
44
45 auto cleanup = llvm::make_scope_exit(F: [&]() { close(fd: fds.fd); });
46
47 // Detect a 50ms stall in a 2 second time window.
48 const char trig[] = "some 50000 2000000";
49 if (write(fd: fds.fd, buf: trig, n: strlen(s: trig) + 1) < 0)
50 return {};
51
52 while (!m_done) {
53 int n = poll(fds: &fds, nfds: 1, timeout: g_timeout);
54 if (n > 0) {
55 if (fds.revents & POLLERR)
56 return {};
57 if (fds.revents & POLLPRI)
58 m_callback();
59 }
60 }
61#endif
62
63#if defined(_WIN32)
64 HANDLE low_memory_notification =
65 CreateMemoryResourceNotification(LowMemoryResourceNotification);
66 if (!low_memory_notification)
67 return {};
68
69 while (!m_done) {
70 if (WaitForSingleObject(low_memory_notification, g_timeout) ==
71 WAIT_OBJECT_0) {
72 m_callback();
73 }
74 }
75#endif
76
77 return {};
78 }
79
80 void Start() override {
81 llvm::Expected<HostThread> memory_monitor_thread =
82 ThreadLauncher::LaunchThread(name: "lldb.debugger.memory-monitor",
83 thread_function: [this] { return MonitorThread(); });
84 if (memory_monitor_thread) {
85 m_memory_monitor_thread = *memory_monitor_thread;
86 } else {
87 LLDB_LOG_ERROR(GetLog(LLDBLog::Host), memory_monitor_thread.takeError(),
88 "failed to launch host thread: {0}");
89 }
90 }
91
92 void Stop() override {
93 if (m_memory_monitor_thread.IsJoinable()) {
94 m_done = true;
95 m_memory_monitor_thread.Join(result: nullptr);
96 }
97 }
98
99private:
100 static constexpr uint32_t g_timeout = 1000;
101 std::atomic<bool> m_done = false;
102 HostThread m_memory_monitor_thread;
103};
104
105#if !defined(__APPLE__)
106std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {
107 return std::make_unique<MemoryMonitorPoll>(args&: callback);
108}
109#endif
110

source code of lldb/source/Host/common/MemoryMonitor.cpp