Skip to content

Commit c63350d

Browse files
committed
[lldb-dap] Implement a MemoryMonitor for macOS & Linux
This implements a memory monitor for macOS & Linux, and registers a callback that invokes SBDebugger::MemoryPressureDetected() when a low memory event is detected.
1 parent b8337bc commit c63350d

File tree

4 files changed

+171
-2
lines changed

4 files changed

+171
-2
lines changed

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ add_lldb_tool(lldb-dap
3636
RunInTerminal.cpp
3737
SourceBreakpoint.cpp
3838
Watchpoint.cpp
39+
MemoryMonitor.cpp
3940

4041
Handler/ResponseHandler.cpp
4142
Handler/AttachRequestHandler.cpp
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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 "MemoryMonitor.h"
10+
#include "llvm/ADT/ScopeExit.h"
11+
#include <atomic>
12+
#include <cstdio>
13+
#include <cstring>
14+
#include <thread>
15+
16+
#if defined(__APPLE__)
17+
#include <dispatch/dispatch.h>
18+
#endif
19+
20+
#if defined(__linux__)
21+
#include <fcntl.h>
22+
#include <poll.h>
23+
#include <unistd.h>
24+
#endif
25+
26+
using namespace lldb_dap;
27+
28+
#if defined(__APPLE__)
29+
class MemoryMonitorDarwin : public MemoryMonitor {
30+
using MemoryMonitor::MemoryMonitor;
31+
void Start() override {
32+
m_memory_pressure_source = dispatch_source_create(
33+
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE,
34+
0, // system-wide monitoring
35+
DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL,
36+
dispatch_get_main_queue());
37+
38+
dispatch_source_set_event_handler(m_memory_pressure_source, ^{
39+
dispatch_source_memorypressure_flags_t pressureLevel =
40+
dispatch_source_get_data(m_memory_pressure_source);
41+
if (pressureLevel &
42+
(DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL)) {
43+
m_callback();
44+
}
45+
});
46+
}
47+
48+
void Stop() override { dispatch_source_cancel(m_memory_pressure_source); }
49+
50+
private:
51+
dispatch_source_t m_memory_pressure_source;
52+
};
53+
#endif
54+
55+
#if defined(__linux__)
56+
static void MonitorThread(std::atomic<bool> &done,
57+
MemoryMonitor::Callback callback) {
58+
struct pollfd fds;
59+
fds.fd = open("/proc/pressure/memory", O_RDWR | O_NONBLOCK);
60+
if (fds.fd < 0)
61+
return;
62+
fds.events = POLLPRI;
63+
64+
auto cleanup = llvm::make_scope_exit([&]() { close(fds.fd); });
65+
66+
// Detect a 50ms stall in a 2 second time window.
67+
const char trig[] = "some 50000 2000000";
68+
if (write(fds.fd, trig, strlen(trig) + 1) < 0)
69+
return;
70+
71+
while (!done) {
72+
int n = poll(&fds, 1, 1000);
73+
if (n > 0) {
74+
if (fds.revents & POLLERR)
75+
return;
76+
if (fds.revents & POLLPRI)
77+
callback();
78+
}
79+
}
80+
}
81+
82+
class MemoryMonitorLinux : public MemoryMonitor {
83+
public:
84+
using MemoryMonitor::MemoryMonitor;
85+
86+
void Start() override {
87+
m_memory_pressure_thread =
88+
std::thread(MonitorThread, std::ref(m_done), m_callback);
89+
}
90+
91+
void Stop() override {
92+
if (m_memory_pressure_thread.joinable()) {
93+
m_done = true;
94+
m_memory_pressure_thread.join();
95+
}
96+
}
97+
98+
private:
99+
std::atomic<bool> m_done = false;
100+
std::thread m_memory_pressure_thread;
101+
};
102+
#endif
103+
104+
std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {
105+
#if defined(__APPLE__)
106+
return std::make_unique<MemoryMonitorDarwin>(callback);
107+
#endif
108+
109+
#if defined(__linux__)
110+
return std::make_unique<MemoryMonitorLinux>(callback);
111+
#endif
112+
113+
return nullptr;
114+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===-- MemoryMonitor.h ---------------------------------------------------===//
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+
#ifndef LLDB_TOOLS_LLDB_DAP_WATCHPOINT_H
10+
#define LLDB_TOOLS_LLDB_DAP_WATCHPOINT_H
11+
12+
#include <functional>
13+
#include <memory>
14+
15+
namespace lldb_dap {
16+
17+
class MemoryMonitor {
18+
public:
19+
using Callback = std::function<void()>;
20+
21+
MemoryMonitor(Callback callback) : m_callback(callback) {}
22+
virtual ~MemoryMonitor() = default;
23+
24+
/// MemoryMonitor is not copyable.
25+
/// @{
26+
MemoryMonitor(const MemoryMonitor &) = delete;
27+
MemoryMonitor &operator=(const MemoryMonitor &) = delete;
28+
/// @}
29+
30+
static std::unique_ptr<MemoryMonitor> Create(Callback callback);
31+
32+
virtual void Start() = 0;
33+
virtual void Stop() = 0;
34+
35+
protected:
36+
Callback m_callback;
37+
};
38+
39+
} // namespace lldb_dap
40+
41+
#endif

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#include "DAP.h"
1010
#include "EventHelper.h"
1111
#include "Handler/RequestHandler.h"
12+
#include "MemoryMonitor.h"
1213
#include "RunInTerminal.h"
14+
#include "lldb/API/SBDebugger.h"
1315
#include "lldb/API/SBStream.h"
1416
#include "lldb/Host/Config.h"
1517
#include "lldb/Host/File.h"
@@ -504,9 +506,20 @@ int main(int argc, char *argv[]) {
504506
return EXIT_FAILURE;
505507
}
506508

509+
// Create a memory monitor. This can return nullptr if the host platform is
510+
// not supported.
511+
std::unique_ptr<MemoryMonitor> memory_monitor = MemoryMonitor::Create(
512+
[]() { lldb::SBDebugger::MemoryPressureDetected(); });
513+
514+
if (memory_monitor)
515+
memory_monitor->Start();
516+
507517
// Terminate the debugger before the C++ destructor chain kicks in.
508-
auto terminate_debugger =
509-
llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
518+
auto terminate_debugger = llvm::make_scope_exit([&] {
519+
if (memory_monitor)
520+
memory_monitor->Stop();
521+
lldb::SBDebugger::Terminate();
522+
});
510523

511524
std::vector<std::string> pre_init_commands;
512525
for (const std::string &arg :

0 commit comments

Comments
 (0)