Skip to content

Commit 6c4febe

Browse files
authored
[lldb-dap] Implement a MemoryMonitor (llvm#129332)
This implements a memory monitor for macOS, Linux and Windows. It registers a callback that invokes `SBDebugger::MemoryPressureDetected` when a low memory event is detected. This is motivated by the new server mode, where the lldb-dap process will live across multiple debug sessions and will use more memory due to caching.
1 parent e04f4cc commit 6c4febe

File tree

6 files changed

+223
-3
lines changed

6 files changed

+223
-3
lines changed
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_HOST_MEMORYMONITOR_H
10+
#define LLDB_HOST_MEMORYMONITOR_H
11+
12+
#include <functional>
13+
#include <memory>
14+
15+
namespace lldb_private {
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_private
40+
41+
#endif

lldb/source/Host/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_host_subdirectory(common
2727
common/LockFileBase.cpp
2828
common/LZMA.cpp
2929
common/MainLoopBase.cpp
30+
common/MemoryMonitor.cpp
3031
common/MonitoringProcessLauncher.cpp
3132
common/NativeProcessProtocol.cpp
3233
common/NativeRegisterContext.cpp
@@ -136,7 +137,7 @@ else()
136137
elseif (CMAKE_SYSTEM_NAME MATCHES "AIX")
137138
add_host_subdirectory(aix
138139
aix/HostInfoAIX.cpp
139-
)
140+
)
140141
endif()
141142
endif()
142143

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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("/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([&]() { close(fds.fd); });
46+
47+
// Detect a 50ms stall in a 2 second time window.
48+
const char trig[] = "some 50000 2000000";
49+
if (write(fds.fd, trig, strlen(trig) + 1) < 0)
50+
return {};
51+
52+
while (!m_done) {
53+
int n = poll(&fds, 1, 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("lldb.debugger.memory-monitor",
83+
[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(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>(callback);
108+
}
109+
#endif

lldb/source/Host/macosx/objcxx/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_lldb_library(lldbHostMacOSXObjCXX NO_PLUGIN_DEPENDENCIES
66
Host.mm
77
HostInfoMacOSX.mm
88
HostThreadMacOSX.mm
9+
MemoryMonitorMacOSX.mm
910

1011
LINK_LIBS
1112
lldbUtility
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===-- MemoryMonitorMacOSX.mm --------------------------------------------===//
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 <cassert>
11+
#include <dispatch/dispatch.h>
12+
13+
using namespace lldb_private;
14+
15+
class MemoryMonitorMacOSX : public MemoryMonitor {
16+
using MemoryMonitor::MemoryMonitor;
17+
void Start() override {
18+
m_memory_pressure_source = dispatch_source_create(
19+
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0,
20+
DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL,
21+
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
22+
23+
if (!m_memory_pressure_source)
24+
return;
25+
26+
dispatch_source_set_event_handler(m_memory_pressure_source, ^{
27+
dispatch_source_memorypressure_flags_t pressureLevel =
28+
dispatch_source_get_data(m_memory_pressure_source);
29+
if (pressureLevel &
30+
(DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL)) {
31+
m_callback();
32+
}
33+
});
34+
dispatch_activate(m_memory_pressure_source);
35+
printf("Started\n");
36+
}
37+
38+
void Stop() override {
39+
if (m_memory_pressure_source) {
40+
dispatch_source_cancel(m_memory_pressure_source);
41+
dispatch_release(m_memory_pressure_source);
42+
}
43+
}
44+
45+
private:
46+
dispatch_source_t m_memory_pressure_source;
47+
};
48+
49+
std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {
50+
return std::make_unique<MemoryMonitorMacOSX>(callback);
51+
}

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
#include "EventHelper.h"
1111
#include "Handler/RequestHandler.h"
1212
#include "RunInTerminal.h"
13+
#include "lldb/API/SBDebugger.h"
1314
#include "lldb/API/SBStream.h"
1415
#include "lldb/Host/Config.h"
1516
#include "lldb/Host/File.h"
1617
#include "lldb/Host/MainLoop.h"
1718
#include "lldb/Host/MainLoopBase.h"
19+
#include "lldb/Host/MemoryMonitor.h"
1820
#include "lldb/Host/Socket.h"
1921
#include "lldb/Utility/Status.h"
2022
#include "lldb/Utility/UriParser.h"
@@ -504,9 +506,24 @@ 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<lldb_private::MemoryMonitor> memory_monitor =
512+
lldb_private::MemoryMonitor::Create([&]() {
513+
if (log)
514+
*log << "memory pressure detected\n";
515+
lldb::SBDebugger::MemoryPressureDetected();
516+
});
517+
518+
if (memory_monitor)
519+
memory_monitor->Start();
520+
507521
// Terminate the debugger before the C++ destructor chain kicks in.
508-
auto terminate_debugger =
509-
llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
522+
auto terminate_debugger = llvm::make_scope_exit([&] {
523+
if (memory_monitor)
524+
memory_monitor->Stop();
525+
lldb::SBDebugger::Terminate();
526+
});
510527

511528
std::vector<std::string> pre_init_commands;
512529
for (const std::string &arg :

0 commit comments

Comments
 (0)