//===-- 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 #include #include #include #if defined(__linux__) #include #include #include #endif #if defined(_WIN32) #include #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 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 m_done = false; HostThread m_memory_monitor_thread; }; #if !defined(__APPLE__) std::unique_ptr MemoryMonitor::Create(Callback callback) { return std::make_unique(callback); } #endif