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 all commits
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
41 changes: 41 additions & 0 deletions lldb/include/lldb/Host/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_HOST_MEMORYMONITOR_H
#define LLDB_HOST_MEMORYMONITOR_H

#include <functional>
#include <memory>

namespace lldb_private {

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_private

#endif
3 changes: 2 additions & 1 deletion lldb/source/Host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_host_subdirectory(common
common/LockFileBase.cpp
common/LZMA.cpp
common/MainLoopBase.cpp
common/MemoryMonitor.cpp
common/MonitoringProcessLauncher.cpp
common/NativeProcessProtocol.cpp
common/NativeRegisterContext.cpp
Expand Down Expand Up @@ -136,7 +137,7 @@ else()
elseif (CMAKE_SYSTEM_NAME MATCHES "AIX")
add_host_subdirectory(aix
aix/HostInfoAIX.cpp
)
)
endif()
endif()

Expand Down
109 changes: 109 additions & 0 deletions lldb/source/Host/common/MemoryMonitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//===-- 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 "lldb/Host/MemoryMonitor.h"
#include "lldb/Host/HostThread.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/Error.h"
#include <atomic>
#include <cstddef>
#include <cstdio>
#include <cstring>

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

#if defined(_WIN32)
#include <windows.h>
#endif

using namespace lldb_private;

class MemoryMonitorPoll : public MemoryMonitor {
public:
using MemoryMonitor::MemoryMonitor;

lldb::thread_result_t MonitorThread() {
#if defined(__linux__)
struct pollfd fds;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need to zero out this struct?

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 (!m_done) {
int n = poll(&fds, 1, g_timeout);
if (n > 0) {
if (fds.revents & POLLERR)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there's an error, is there a way to restart the loop?

return {};
if (fds.revents & POLLPRI)
m_callback();
}
}
#endif

#if defined(_WIN32)
HANDLE low_memory_notification =
CreateMemoryResourceNotification(LowMemoryResourceNotification);
if (!low_memory_notification)
return {};

while (!m_done) {
if (WaitForSingleObject(low_memory_notification, g_timeout) ==
WAIT_OBJECT_0) {
m_callback();
}
}
#endif

return {};
}

void Start() override {
llvm::Expected<HostThread> memory_monitor_thread =
ThreadLauncher::LaunchThread("lldb.debugger.memory-monitor",
[this] { return MonitorThread(); });
if (memory_monitor_thread) {
m_memory_monitor_thread = *memory_monitor_thread;
} else {
LLDB_LOG_ERROR(GetLog(LLDBLog::Host), memory_monitor_thread.takeError(),
"failed to launch host thread: {0}");
}
}

void Stop() override {
if (m_memory_monitor_thread.IsJoinable()) {
m_done = true;
m_memory_monitor_thread.Join(nullptr);
}
}

private:
static constexpr uint32_t g_timeout = 1000;
std::atomic<bool> m_done = false;
HostThread m_memory_monitor_thread;
};

#if !defined(__APPLE__)
std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {
return std::make_unique<MemoryMonitorPoll>(callback);
}
#endif
1 change: 1 addition & 0 deletions lldb/source/Host/macosx/objcxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_lldb_library(lldbHostMacOSXObjCXX NO_PLUGIN_DEPENDENCIES
Host.mm
HostInfoMacOSX.mm
HostThreadMacOSX.mm
MemoryMonitorMacOSX.mm

LINK_LIBS
lldbUtility
Expand Down
51 changes: 51 additions & 0 deletions lldb/source/Host/macosx/objcxx/MemoryMonitorMacOSX.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===-- MemoryMonitorMacOSX.mm --------------------------------------------===//
//
// 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 "lldb/Host/MemoryMonitor.h"
#include <cassert>
#include <dispatch/dispatch.h>

using namespace lldb_private;

class MemoryMonitorMacOSX : public MemoryMonitor {
using MemoryMonitor::MemoryMonitor;
void Start() override {
m_memory_pressure_source = dispatch_source_create(
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0,
DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));

if (!m_memory_pressure_source)
return;

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();
}
});
dispatch_activate(m_memory_pressure_source);
printf("Started\n");
}

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

private:
dispatch_source_t m_memory_pressure_source;
};

std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {
return std::make_unique<MemoryMonitorMacOSX>(callback);
}
21 changes: 19 additions & 2 deletions lldb/tools/lldb-dap/lldb-dap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
#include "EventHelper.h"
#include "Handler/RequestHandler.h"
#include "RunInTerminal.h"
#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBStream.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/File.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/MainLoopBase.h"
#include "lldb/Host/MemoryMonitor.h"
#include "lldb/Host/Socket.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/UriParser.h"
Expand Down Expand Up @@ -504,9 +506,24 @@ 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<lldb_private::MemoryMonitor> memory_monitor =
lldb_private::MemoryMonitor::Create([&]() {
if (log)
*log << "memory pressure detected\n";
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