
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.
110 lines
2.8 KiB
C++
110 lines
2.8 KiB
C++
//===-- 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
|