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 | |
31 | using namespace lldb_private; |
32 | |
33 | class MemoryMonitorPoll : public MemoryMonitor { |
34 | public: |
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 | |
99 | private: |
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__) |
106 | std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) { |
107 | return std::make_unique<MemoryMonitorPoll>(args&: callback); |
108 | } |
109 | #endif |
110 | |