110 lines · cpp
1//===-- MemoryMonitor.cpp -------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/MemoryMonitor.h"10#include "lldb/Host/HostThread.h"11#include "lldb/Host/ThreadLauncher.h"12#include "lldb/Utility/LLDBLog.h"13#include "lldb/Utility/Log.h"14#include "llvm/ADT/ScopeExit.h"15#include "llvm/Support/Error.h"16#include <atomic>17#include <cstddef>18#include <cstdio>19#include <cstring>20 21#if defined(__linux__)22#include <fcntl.h>23#include <poll.h>24#include <unistd.h>25#endif26 27#if defined(_WIN32)28#include <windows.h>29#endif30 31using namespace lldb_private;32 33class MemoryMonitorPoll : public MemoryMonitor {34public:35 using MemoryMonitor::MemoryMonitor;36 37 lldb::thread_result_t MonitorThread() {38#if defined(__linux__)39 struct pollfd fds;40 fds.fd = open("/proc/pressure/memory", O_RDWR | O_NONBLOCK);41 if (fds.fd < 0)42 return {};43 fds.events = POLLPRI;44 45 auto cleanup = llvm::make_scope_exit([&]() { close(fds.fd); });46 47 // Detect a 50ms stall in a 2 second time window.48 const char trig[] = "some 50000 2000000";49 if (write(fds.fd, trig, strlen(trig) + 1) < 0)50 return {};51 52 while (!m_done) {53 int n = poll(&fds, 1, g_timeout);54 if (n > 0) {55 if (fds.revents & POLLERR)56 return {};57 if (fds.revents & POLLPRI)58 m_callback();59 }60 }61#endif62 63#if defined(_WIN32)64 HANDLE low_memory_notification =65 CreateMemoryResourceNotification(LowMemoryResourceNotification);66 if (!low_memory_notification)67 return {};68 69 while (!m_done) {70 if (WaitForSingleObject(low_memory_notification, g_timeout) ==71 WAIT_OBJECT_0) {72 m_callback();73 }74 }75#endif76 77 return {};78 }79 80 void Start() override {81 llvm::Expected<HostThread> memory_monitor_thread =82 ThreadLauncher::LaunchThread("lldb.debugger.memory-monitor",83 [this] { return MonitorThread(); });84 if (memory_monitor_thread) {85 m_memory_monitor_thread = *memory_monitor_thread;86 } else {87 LLDB_LOG_ERROR(GetLog(LLDBLog::Host), memory_monitor_thread.takeError(),88 "failed to launch host thread: {0}");89 }90 }91 92 void Stop() override {93 if (m_memory_monitor_thread.IsJoinable()) {94 m_done = true;95 m_memory_monitor_thread.Join(nullptr);96 }97 }98 99private:100 static constexpr uint32_t g_timeout = 1000;101 std::atomic<bool> m_done = false;102 HostThread m_memory_monitor_thread;103};104 105#if !defined(__APPLE__)106std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {107 return std::make_unique<MemoryMonitorPoll>(callback);108}109#endif110