[lldb-dap] Implement a MemoryMonitor (#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.
This commit is contained in:
committed by
GitHub
parent
e04f4cce85
commit
6c4febee29
41
lldb/include/lldb/Host/MemoryMonitor.h
Normal file
41
lldb/include/lldb/Host/MemoryMonitor.h
Normal file
@@ -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
|
||||
@@ -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
|
||||
@@ -136,7 +137,7 @@ else()
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "AIX")
|
||||
add_host_subdirectory(aix
|
||||
aix/HostInfoAIX.cpp
|
||||
)
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
109
lldb/source/Host/common/MemoryMonitor.cpp
Normal file
109
lldb/source/Host/common/MemoryMonitor.cpp
Normal file
@@ -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;
|
||||
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)
|
||||
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
|
||||
@@ -6,6 +6,7 @@ add_lldb_library(lldbHostMacOSXObjCXX NO_PLUGIN_DEPENDENCIES
|
||||
Host.mm
|
||||
HostInfoMacOSX.mm
|
||||
HostThreadMacOSX.mm
|
||||
MemoryMonitorMacOSX.mm
|
||||
|
||||
LINK_LIBS
|
||||
lldbUtility
|
||||
|
||||
51
lldb/source/Host/macosx/objcxx/MemoryMonitorMacOSX.mm
Normal file
51
lldb/source/Host/macosx/objcxx/MemoryMonitorMacOSX.mm
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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 :
|
||||
|
||||
Reference in New Issue
Block a user