xref: /llvm-project/lldb/source/Core/Progress.cpp (revision 774c22686330f3ca43e48a1b8076eb30ae03dbd8)
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