Skip to content

[lldb-dap] Implement a MemoryMonitor #129332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lldb/tools/lldb-dap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ add_lldb_tool(lldb-dap
RunInTerminal.cpp
SourceBreakpoint.cpp
Watchpoint.cpp
MemoryMonitor.cpp

Handler/ResponseHandler.cpp
Handler/AttachRequestHandler.cpp
Expand Down
114 changes: 114 additions & 0 deletions lldb/tools/lldb-dap/MemoryMonitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//===-- MemoryMonitor.cpp -------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "MemoryMonitor.h"
#include "llvm/ADT/ScopeExit.h"
#include <atomic>
#include <cstdio>
#include <cstring>
#include <thread>

#if defined(__APPLE__)
#include <dispatch/dispatch.h>
#endif

#if defined(__linux__)
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#endif

using namespace lldb_dap;

#if defined(__APPLE__)
class MemoryMonitorDarwin : public MemoryMonitor {
using MemoryMonitor::MemoryMonitor;
void Start() override {
m_memory_pressure_source = dispatch_source_create(
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE,
0, // system-wide monitoring
DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL,
dispatch_get_main_queue());

dispatch_source_set_event_handler(m_memory_pressure_source, ^{
dispatch_source_memorypressure_flags_t pressureLevel =
dispatch_source_get_data(m_memory_pressure_source);
if (pressureLevel &
(DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL)) {
m_callback();
}
});
}

void Stop() override { dispatch_source_cancel(m_memory_pressure_source); }

private:
dispatch_source_t m_memory_pressure_source;
};
#endif

#if defined(__linux__)
static void MonitorThread(std::atomic<bool> &done,
MemoryMonitor::Callback callback) {
struct pollfd fds;
fds.fd = open("/proc/pressure/memory", O_RDWR | O_NONBLOCK);
if (fds.fd < 0)
return;
fds.events = POLLPRI;

auto cleanup = llvm::make_scope_exit([&]() { close(fds.fd); });

// Detect a 50ms stall in a 2 second time window.
const char trig[] = "some 50000 2000000";
if (write(fds.fd, trig, strlen(trig) + 1) < 0)
return;

while (!done) {
int n = poll(&fds, 1, 1000);
if (n > 0) {
if (fds.revents & POLLERR)
return;
if (fds.revents & POLLPRI)
callback();
}
}
}

class MemoryMonitorLinux : public MemoryMonitor {
public:
using MemoryMonitor::MemoryMonitor;

void Start() override {
m_memory_pressure_thread =
std::thread(MonitorThread, std::ref(m_done), m_callback);
}

void Stop() override {
if (m_memory_pressure_thread.joinable()) {
m_done = true;
m_memory_pressure_thread.join();
}
}

private:
std::atomic<bool> m_done = false;
std::thread m_memory_pressure_thread;
};
#endif

std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {
#if defined(__APPLE__)
return std::make_unique<MemoryMonitorDarwin>(callback);
#endif

#if defined(__linux__)
return std::make_unique<MemoryMonitorLinux>(callback);
#endif

return nullptr;
}
41 changes: 41 additions & 0 deletions lldb/tools/lldb-dap/MemoryMonitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===-- MemoryMonitor.h ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_TOOLS_LLDB_DAP_WATCHPOINT_H
#define LLDB_TOOLS_LLDB_DAP_WATCHPOINT_H

#include <functional>
#include <memory>

namespace lldb_dap {

class MemoryMonitor {
public:
using Callback = std::function<void()>;

MemoryMonitor(Callback callback) : m_callback(callback) {}
virtual ~MemoryMonitor() = default;

/// MemoryMonitor is not copyable.
/// @{
MemoryMonitor(const MemoryMonitor &) = delete;
MemoryMonitor &operator=(const MemoryMonitor &) = delete;
/// @}

static std::unique_ptr<MemoryMonitor> Create(Callback callback);

virtual void Start() = 0;
virtual void Stop() = 0;

protected:
Callback m_callback;
};

} // namespace lldb_dap

#endif
17 changes: 15 additions & 2 deletions lldb/tools/lldb-dap/lldb-dap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include "DAP.h"
#include "EventHelper.h"
#include "Handler/RequestHandler.h"
#include "MemoryMonitor.h"
#include "RunInTerminal.h"
#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBStream.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/File.h"
Expand Down Expand Up @@ -504,9 +506,20 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}

// Create a memory monitor. This can return nullptr if the host platform is
// not supported.
std::unique_ptr<MemoryMonitor> memory_monitor = MemoryMonitor::Create(
[]() { lldb::SBDebugger::MemoryPressureDetected(); });

if (memory_monitor)
memory_monitor->Start();

// Terminate the debugger before the C++ destructor chain kicks in.
auto terminate_debugger =
llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
auto terminate_debugger = llvm::make_scope_exit([&] {
if (memory_monitor)
memory_monitor->Stop();
lldb::SBDebugger::Terminate();
});

std::vector<std::string> pre_init_commands;
for (const std::string &arg :
Expand Down