1e122877fSGreg Clayton //===-- Progress.cpp ------------------------------------------------------===// 2e122877fSGreg Clayton // 3e122877fSGreg Clayton // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e122877fSGreg Clayton // See https://llvm.org/LICENSE.txt for license information. 5e122877fSGreg Clayton // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e122877fSGreg Clayton // 7e122877fSGreg Clayton //===----------------------------------------------------------------------===// 8e122877fSGreg Clayton 9e122877fSGreg Clayton #include "lldb/Core/Progress.h" 10e122877fSGreg Clayton 11e122877fSGreg Clayton #include "lldb/Core/Debugger.h" 12e122877fSGreg Clayton #include "lldb/Utility/StreamString.h" 1390f077cbSJonas Devlieghere #include "llvm/Support/Signposts.h" 140dbdc23eSPavel Labath #include <atomic> 150dbdc23eSPavel Labath #include <chrono> 16137ed170SChelsea Cassanova #include <cstdint> 17327d2f8cSChelsea Cassanova #include <mutex> 18f1ef910bSChelsea Cassanova #include <optional> 19f1ef910bSChelsea Cassanova 20e122877fSGreg Clayton using namespace lldb; 21e122877fSGreg Clayton using namespace lldb_private; 22e122877fSGreg Clayton 23e122877fSGreg Clayton std::atomic<uint64_t> Progress::g_id(0); 24e122877fSGreg Clayton 2590f077cbSJonas Devlieghere // Instrument progress events with signposts when supported. 2690f077cbSJonas Devlieghere static llvm::ManagedStatic<llvm::SignpostEmitter> g_progress_signposts; 2790f077cbSJonas Devlieghere 28f1ef910bSChelsea Cassanova Progress::Progress(std::string title, std::string details, 29f1ef910bSChelsea Cassanova std::optional<uint64_t> total, 300dbdc23eSPavel Labath lldb_private::Debugger *debugger, 31*774c2268SJacob Lalonde Timeout<std::nano> minimum_report_time, 32*774c2268SJacob Lalonde Progress::Origin origin) 330dbdc23eSPavel Labath : m_total(total.value_or(Progress::kNonDeterministicTotal)), 340dbdc23eSPavel Labath m_minimum_report_time(minimum_report_time), 35137ed170SChelsea Cassanova m_progress_data{title, ++g_id, 360dbdc23eSPavel Labath debugger ? std::optional<user_id_t>(debugger->GetID()) 37*774c2268SJacob Lalonde : std::nullopt, 38*774c2268SJacob Lalonde origin}, 390dbdc23eSPavel Labath m_last_report_time_ns( 400dbdc23eSPavel Labath std::chrono::nanoseconds( 410dbdc23eSPavel Labath std::chrono::steady_clock::now().time_since_epoch()) 420dbdc23eSPavel Labath .count()), 430dbdc23eSPavel Labath m_details(std::move(details)) { 44afe8f20bSPavel Labath std::lock_guard<std::mutex> guard(m_mutex); 45afe8f20bSPavel Labath ReportProgress(); 46156c2907SJonas Devlieghere 47156c2907SJonas Devlieghere // Report to the ProgressManager if that subsystem is enabled. 48156c2907SJonas Devlieghere if (ProgressManager::Enabled()) 49137ed170SChelsea Cassanova ProgressManager::Instance().Increment(m_progress_data); 5090f077cbSJonas Devlieghere 5190f077cbSJonas Devlieghere // Start signpost interval right before the meaningful work starts. 5290f077cbSJonas Devlieghere g_progress_signposts->startInterval(this, m_progress_data.title); 53e122877fSGreg Clayton } 54e122877fSGreg Clayton 55e122877fSGreg Clayton Progress::~Progress() { 5690f077cbSJonas Devlieghere // End signpost interval as soon as possible. 5790f077cbSJonas Devlieghere g_progress_signposts->endInterval(this, m_progress_data.title); 5890f077cbSJonas Devlieghere 59afe8f20bSPavel Labath // Make sure to always report progress completed when this object is 60afe8f20bSPavel Labath // destructed so it indicates the progress dialog/activity should go away. 61afe8f20bSPavel Labath std::lock_guard<std::mutex> guard(m_mutex); 62afe8f20bSPavel Labath m_completed = m_total; 63afe8f20bSPavel Labath ReportProgress(); 64156c2907SJonas Devlieghere 65156c2907SJonas Devlieghere // Report to the ProgressManager if that subsystem is enabled. 66156c2907SJonas Devlieghere if (ProgressManager::Enabled()) 67137ed170SChelsea Cassanova ProgressManager::Instance().Decrement(m_progress_data); 68e122877fSGreg Clayton } 69e122877fSGreg Clayton 70f1ef910bSChelsea Cassanova void Progress::Increment(uint64_t amount, 71f1ef910bSChelsea Cassanova std::optional<std::string> updated_detail) { 720dbdc23eSPavel Labath if (amount == 0) 730dbdc23eSPavel Labath return; 740dbdc23eSPavel Labath 750dbdc23eSPavel Labath m_completed.fetch_add(amount, std::memory_order_relaxed); 760dbdc23eSPavel Labath 770dbdc23eSPavel Labath if (m_minimum_report_time) { 780dbdc23eSPavel Labath using namespace std::chrono; 790dbdc23eSPavel Labath 800dbdc23eSPavel Labath nanoseconds now; 810dbdc23eSPavel Labath uint64_t last_report_time_ns = 820dbdc23eSPavel Labath m_last_report_time_ns.load(std::memory_order_relaxed); 830dbdc23eSPavel Labath 840dbdc23eSPavel Labath do { 850dbdc23eSPavel Labath now = steady_clock::now().time_since_epoch(); 860dbdc23eSPavel Labath if (now < nanoseconds(last_report_time_ns) + *m_minimum_report_time) 870dbdc23eSPavel Labath return; // Too little time has passed since the last report. 880dbdc23eSPavel Labath 890dbdc23eSPavel Labath } while (!m_last_report_time_ns.compare_exchange_weak( 900dbdc23eSPavel Labath last_report_time_ns, now.count(), std::memory_order_relaxed, 910dbdc23eSPavel Labath std::memory_order_relaxed)); 920dbdc23eSPavel Labath } 930dbdc23eSPavel Labath 94afe8f20bSPavel Labath std::lock_guard<std::mutex> guard(m_mutex); 95f1ef910bSChelsea Cassanova if (updated_detail) 96f1ef910bSChelsea Cassanova m_details = std::move(updated_detail.value()); 97f1ef910bSChelsea Cassanova ReportProgress(); 98afe8f20bSPavel Labath } 99afe8f20bSPavel Labath 100f1ef910bSChelsea Cassanova void Progress::ReportProgress() { 1010dbdc23eSPavel Labath // NB: Comparisons with optional<T> rely on the fact that std::nullopt is 1020dbdc23eSPavel Labath // "smaller" than zero. 1030dbdc23eSPavel Labath if (m_prev_completed >= m_total) 1040dbdc23eSPavel Labath return; // We've reported completion already. 1050dbdc23eSPavel Labath 1060dbdc23eSPavel Labath uint64_t completed = 1070dbdc23eSPavel Labath std::min(m_completed.load(std::memory_order_relaxed), m_total); 1080dbdc23eSPavel Labath if (completed < m_prev_completed) 1090dbdc23eSPavel Labath return; // An overflow in the m_completed counter. Just ignore these events. 1100dbdc23eSPavel Labath 111*774c2268SJacob Lalonde // Change the category bit if we're an internal or external progress. 112*774c2268SJacob Lalonde uint32_t progress_category_bit = 113*774c2268SJacob Lalonde m_progress_data.origin == Progress::Origin::eExternal 114*774c2268SJacob Lalonde ? lldb::eBroadcastBitExternalProgress 115*774c2268SJacob Lalonde : lldb::eBroadcastBitProgress; 116*774c2268SJacob Lalonde 117137ed170SChelsea Cassanova Debugger::ReportProgress(m_progress_data.progress_id, m_progress_data.title, 1180dbdc23eSPavel Labath m_details, completed, m_total, 119*774c2268SJacob Lalonde m_progress_data.debugger_id, progress_category_bit); 1200dbdc23eSPavel Labath m_prev_completed = completed; 121e122877fSGreg Clayton } 122327d2f8cSChelsea Cassanova 123156c2907SJonas Devlieghere ProgressManager::ProgressManager() 124156c2907SJonas Devlieghere : m_entries(), m_alarm(std::chrono::milliseconds(100)) {} 125327d2f8cSChelsea Cassanova 126327d2f8cSChelsea Cassanova ProgressManager::~ProgressManager() {} 127327d2f8cSChelsea Cassanova 128156c2907SJonas Devlieghere void ProgressManager::Initialize() { 129156c2907SJonas Devlieghere assert(!InstanceImpl() && "Already initialized."); 130156c2907SJonas Devlieghere InstanceImpl().emplace(); 131156c2907SJonas Devlieghere } 132156c2907SJonas Devlieghere 133156c2907SJonas Devlieghere void ProgressManager::Terminate() { 134156c2907SJonas Devlieghere assert(InstanceImpl() && "Already terminated."); 135156c2907SJonas Devlieghere InstanceImpl().reset(); 136156c2907SJonas Devlieghere } 137156c2907SJonas Devlieghere 138156c2907SJonas Devlieghere bool ProgressManager::Enabled() { return InstanceImpl().operator bool(); } 139156c2907SJonas Devlieghere 140327d2f8cSChelsea Cassanova ProgressManager &ProgressManager::Instance() { 141156c2907SJonas Devlieghere assert(InstanceImpl() && "ProgressManager must be initialized"); 142156c2907SJonas Devlieghere return *InstanceImpl(); 143156c2907SJonas Devlieghere } 144156c2907SJonas Devlieghere 145156c2907SJonas Devlieghere std::optional<ProgressManager> &ProgressManager::InstanceImpl() { 146156c2907SJonas Devlieghere static std::optional<ProgressManager> g_progress_manager; 147156c2907SJonas Devlieghere return g_progress_manager; 148327d2f8cSChelsea Cassanova } 149327d2f8cSChelsea Cassanova 150137ed170SChelsea Cassanova void ProgressManager::Increment(const Progress::ProgressData &progress_data) { 151156c2907SJonas Devlieghere std::lock_guard<std::mutex> lock(m_entries_mutex); 152156c2907SJonas Devlieghere 153156c2907SJonas Devlieghere llvm::StringRef key = progress_data.title; 154156c2907SJonas Devlieghere bool new_entry = !m_entries.contains(key); 155156c2907SJonas Devlieghere Entry &entry = m_entries[progress_data.title]; 156156c2907SJonas Devlieghere 157156c2907SJonas Devlieghere if (new_entry) { 158156c2907SJonas Devlieghere // This is a new progress event. Report progress and store the progress 159156c2907SJonas Devlieghere // data. 160156c2907SJonas Devlieghere ReportProgress(progress_data, EventType::Begin); 161156c2907SJonas Devlieghere entry.data = progress_data; 162156c2907SJonas Devlieghere } else if (entry.refcount == 0) { 163156c2907SJonas Devlieghere // This is an existing entry that was scheduled to be deleted but a new one 164156c2907SJonas Devlieghere // came in before the timer expired. 165156c2907SJonas Devlieghere assert(entry.handle != Alarm::INVALID_HANDLE); 166156c2907SJonas Devlieghere 167156c2907SJonas Devlieghere if (!m_alarm.Cancel(entry.handle)) { 168156c2907SJonas Devlieghere // The timer expired before we had a chance to cancel it. We have to treat 169156c2907SJonas Devlieghere // this as an entirely new progress event. 170ea49e04bSJonas Devlieghere ReportProgress(progress_data, EventType::Begin); 171137ed170SChelsea Cassanova } 172156c2907SJonas Devlieghere // Clear the alarm handle. 173156c2907SJonas Devlieghere entry.handle = Alarm::INVALID_HANDLE; 174156c2907SJonas Devlieghere } 175156c2907SJonas Devlieghere 176156c2907SJonas Devlieghere // Regardless of how we got here, we need to bump the reference count. 177156c2907SJonas Devlieghere entry.refcount++; 178327d2f8cSChelsea Cassanova } 179327d2f8cSChelsea Cassanova 180137ed170SChelsea Cassanova void ProgressManager::Decrement(const Progress::ProgressData &progress_data) { 181156c2907SJonas Devlieghere std::lock_guard<std::mutex> lock(m_entries_mutex); 182156c2907SJonas Devlieghere llvm::StringRef key = progress_data.title; 183327d2f8cSChelsea Cassanova 184282ab2f1SKazu Hirata auto it = m_entries.find(key); 185282ab2f1SKazu Hirata if (it == m_entries.end()) 186327d2f8cSChelsea Cassanova return; 187327d2f8cSChelsea Cassanova 188282ab2f1SKazu Hirata Entry &entry = it->second; 189156c2907SJonas Devlieghere entry.refcount--; 190156c2907SJonas Devlieghere 191156c2907SJonas Devlieghere if (entry.refcount == 0) { 192156c2907SJonas Devlieghere assert(entry.handle == Alarm::INVALID_HANDLE); 193156c2907SJonas Devlieghere 194156c2907SJonas Devlieghere // Copy the key to a std::string so we can pass it by value to the lambda. 195156c2907SJonas Devlieghere // The underlying StringRef will not exist by the time the callback is 196156c2907SJonas Devlieghere // called. 197156c2907SJonas Devlieghere std::string key_str = std::string(key); 198156c2907SJonas Devlieghere 199156c2907SJonas Devlieghere // Start a timer. If it expires before we see another progress event, it 200156c2907SJonas Devlieghere // will be reported. 201156c2907SJonas Devlieghere entry.handle = m_alarm.Create([=]() { Expire(key_str); }); 202137ed170SChelsea Cassanova } 203137ed170SChelsea Cassanova } 204137ed170SChelsea Cassanova 205137ed170SChelsea Cassanova void ProgressManager::ReportProgress( 206ea49e04bSJonas Devlieghere const Progress::ProgressData &progress_data, EventType type) { 207137ed170SChelsea Cassanova // The category bit only keeps track of when progress report categories have 208137ed170SChelsea Cassanova // started and ended, so clear the details and reset other fields when 209137ed170SChelsea Cassanova // broadcasting to it since that bit doesn't need that information. 210ea49e04bSJonas Devlieghere const uint64_t completed = 211ea49e04bSJonas Devlieghere (type == EventType::Begin) ? 0 : Progress::kNonDeterministicTotal; 212*774c2268SJacob Lalonde const uint32_t progress_category_bit = 213*774c2268SJacob Lalonde progress_data.origin == Progress::Origin::eExternal 214*774c2268SJacob Lalonde ? lldb::eBroadcastBitExternalProgressCategory 215*774c2268SJacob Lalonde : lldb::eBroadcastBitProgressCategory; 216ea49e04bSJonas Devlieghere Debugger::ReportProgress(progress_data.progress_id, progress_data.title, "", 217ea49e04bSJonas Devlieghere completed, Progress::kNonDeterministicTotal, 218*774c2268SJacob Lalonde progress_data.debugger_id, progress_category_bit); 219327d2f8cSChelsea Cassanova } 220156c2907SJonas Devlieghere 221156c2907SJonas Devlieghere void ProgressManager::Expire(llvm::StringRef key) { 222156c2907SJonas Devlieghere std::lock_guard<std::mutex> lock(m_entries_mutex); 223156c2907SJonas Devlieghere 224156c2907SJonas Devlieghere // This shouldn't happen but be resilient anyway. 225156c2907SJonas Devlieghere if (!m_entries.contains(key)) 226156c2907SJonas Devlieghere return; 227156c2907SJonas Devlieghere 228156c2907SJonas Devlieghere // A new event came in and the alarm fired before we had a chance to restart 229156c2907SJonas Devlieghere // it. 230156c2907SJonas Devlieghere if (m_entries[key].refcount != 0) 231156c2907SJonas Devlieghere return; 232156c2907SJonas Devlieghere 233156c2907SJonas Devlieghere // We're done with this entry. 234156c2907SJonas Devlieghere ReportProgress(m_entries[key].data, EventType::End); 235156c2907SJonas Devlieghere m_entries.erase(key); 236156c2907SJonas Devlieghere } 237