LLDB mainline
Progress.cpp
Go to the documentation of this file.
1//===-- Progress.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-exception
6//
7//===----------------------------------------------------------------------===//
8
10
11#include "lldb/Core/Debugger.h"
13#include "llvm/Support/Signposts.h"
14#include <atomic>
15#include <chrono>
16#include <cstdint>
17#include <mutex>
18#include <optional>
19
20using namespace lldb;
21using namespace lldb_private;
22
23std::atomic<uint64_t> Progress::g_id(0);
24
25// Instrument progress events with signposts when supported.
26static llvm::ManagedStatic<llvm::SignpostEmitter> g_progress_signposts;
27
28Progress::Progress(std::string title, std::string details,
29 std::optional<uint64_t> total,
30 lldb_private::Debugger *debugger,
31 Timeout<std::nano> minimum_report_time,
32 Progress::Origin origin)
33 : m_total(total.value_or(Progress::kNonDeterministicTotal)),
34 m_minimum_report_time(minimum_report_time),
35 m_progress_data{title, ++g_id,
36 debugger ? std::optional<user_id_t>(debugger->GetID())
37 : std::nullopt,
38 origin},
39 m_last_report_time_ns(
40 std::chrono::nanoseconds(
41 std::chrono::steady_clock::now().time_since_epoch())
42 .count()),
43 m_details(std::move(details)) {
44 std::lock_guard<std::mutex> guard(m_mutex);
46
47 // Report to the ProgressManager if that subsystem is enabled.
50
51 // Start signpost interval right before the meaningful work starts.
52 g_progress_signposts->startInterval(this, m_progress_data.title);
53}
54
56 // End signpost interval as soon as possible.
57 g_progress_signposts->endInterval(this, m_progress_data.title);
58
59 // Make sure to always report progress completed when this object is
60 // destructed so it indicates the progress dialog/activity should go away.
61 std::lock_guard<std::mutex> guard(m_mutex);
64
65 // Report to the ProgressManager if that subsystem is enabled.
68}
69
70void Progress::Increment(uint64_t amount,
71 std::optional<std::string> updated_detail) {
72 if (amount == 0)
73 return;
74
75 m_completed.fetch_add(amount, std::memory_order_relaxed);
76
78 using namespace std::chrono;
79
80 nanoseconds now;
81 uint64_t last_report_time_ns =
82 m_last_report_time_ns.load(std::memory_order_relaxed);
83
84 do {
85 now = steady_clock::now().time_since_epoch();
86 if (now < nanoseconds(last_report_time_ns) + *m_minimum_report_time)
87 return; // Too little time has passed since the last report.
88
89 } while (!m_last_report_time_ns.compare_exchange_weak(
90 last_report_time_ns, now.count(), std::memory_order_relaxed,
91 std::memory_order_relaxed));
92 }
93
94 std::lock_guard<std::mutex> guard(m_mutex);
95 if (updated_detail)
96 m_details = std::move(updated_detail.value());
98}
99
101 // NB: Comparisons with optional<T> rely on the fact that std::nullopt is
102 // "smaller" than zero.
104 return; // We've reported completion already.
105
106 uint64_t completed =
107 std::min(m_completed.load(std::memory_order_relaxed), m_total);
108 if (completed < m_prev_completed)
109 return; // An overflow in the m_completed counter. Just ignore these events.
110
111 // Change the category bit if we're an internal or external progress.
112 uint32_t progress_category_bit =
116
118 m_details, completed, m_total,
119 m_progress_data.debugger_id, progress_category_bit);
120 m_prev_completed = completed;
121}
122
124 : m_entries(), m_alarm(std::chrono::milliseconds(100)) {}
125
127
129 assert(!InstanceImpl() && "Already initialized.");
130 InstanceImpl().emplace();
131}
132
134 assert(InstanceImpl() && "Already terminated.");
135 InstanceImpl().reset();
136}
137
138bool ProgressManager::Enabled() { return InstanceImpl().operator bool(); }
139
141 assert(InstanceImpl() && "ProgressManager must be initialized");
142 return *InstanceImpl();
143}
144
145std::optional<ProgressManager> &ProgressManager::InstanceImpl() {
146 static std::optional<ProgressManager> g_progress_manager;
147 return g_progress_manager;
148}
149
151 std::lock_guard<std::mutex> lock(m_entries_mutex);
152
153 llvm::StringRef key = progress_data.title;
154 bool new_entry = !m_entries.contains(key);
155 Entry &entry = m_entries[progress_data.title];
156
157 if (new_entry) {
158 // This is a new progress event. Report progress and store the progress
159 // data.
160 ReportProgress(progress_data, EventType::Begin);
161 entry.data = progress_data;
162 } else if (entry.refcount == 0) {
163 // This is an existing entry that was scheduled to be deleted but a new one
164 // came in before the timer expired.
165 assert(entry.handle != Alarm::INVALID_HANDLE);
166
167 if (!m_alarm.Cancel(entry.handle)) {
168 // The timer expired before we had a chance to cancel it. We have to treat
169 // this as an entirely new progress event.
170 ReportProgress(progress_data, EventType::Begin);
171 }
172 // Clear the alarm handle.
174 }
175
176 // Regardless of how we got here, we need to bump the reference count.
177 entry.refcount++;
178}
179
181 std::lock_guard<std::mutex> lock(m_entries_mutex);
182 llvm::StringRef key = progress_data.title;
183
184 auto it = m_entries.find(key);
185 if (it == m_entries.end())
186 return;
187
188 Entry &entry = it->second;
189 entry.refcount--;
190
191 if (entry.refcount == 0) {
192 assert(entry.handle == Alarm::INVALID_HANDLE);
193
194 // Copy the key to a std::string so we can pass it by value to the lambda.
195 // The underlying StringRef will not exist by the time the callback is
196 // called.
197 std::string key_str = std::string(key);
198
199 // Start a timer. If it expires before we see another progress event, it
200 // will be reported.
201 entry.handle = m_alarm.Create([=]() { Expire(key_str); });
202 }
203}
204
206 const Progress::ProgressData &progress_data, EventType type) {
207 // The category bit only keeps track of when progress report categories have
208 // started and ended, so clear the details and reset other fields when
209 // broadcasting to it since that bit doesn't need that information.
210 const uint64_t completed =
212 const uint32_t progress_category_bit =
213 progress_data.origin == Progress::Origin::eExternal
216 Debugger::ReportProgress(progress_data.progress_id, progress_data.title, "",
218 progress_data.debugger_id, progress_category_bit);
219}
220
221void ProgressManager::Expire(llvm::StringRef key) {
222 std::lock_guard<std::mutex> lock(m_entries_mutex);
223
224 // This shouldn't happen but be resilient anyway.
225 if (!m_entries.contains(key))
226 return;
227
228 // A new event came in and the alarm fired before we had a chance to restart
229 // it.
230 if (m_entries[key].refcount != 0)
231 return;
232
233 // We're done with this entry.
235 m_entries.erase(key);
236}
static llvm::ManagedStatic< llvm::SignpostEmitter > g_progress_signposts
Definition: Progress.cpp:26
static constexpr Handle INVALID_HANDLE
Definition: Alarm.h:58
bool Cancel(Handle handle)
Cancel the alarm for the given Handle.
Definition: Alarm.cpp:82
Handle Create(Callback callback)
Create an alarm for the given callback.
Definition: Alarm.cpp:24
A class to manage flag bits.
Definition: Debugger.h:80
static void ReportProgress(uint64_t progress_id, std::string title, std::string details, uint64_t completed, uint64_t total, std::optional< lldb::user_id_t > debugger_id, uint32_t progress_category_bit=lldb::eBroadcastBitProgress)
Report progress events.
Definition: Debugger.cpp:1520
A class used to group progress reports by category.
Definition: Progress.h:168
void Decrement(const Progress::ProgressData &)
Definition: Progress.cpp:180
static void ReportProgress(const Progress::ProgressData &progress_data, EventType type)
Definition: Progress.cpp:205
Alarm m_alarm
Alarm instance to coalesce progress events.
Definition: Progress.h:215
static ProgressManager & Instance()
Definition: Progress.cpp:140
void Expire(llvm::StringRef key)
Helper function for reporting progress when the alarm in the corresponding entry in the map expires.
Definition: Progress.cpp:221
static std::optional< ProgressManager > & InstanceImpl()
Definition: Progress.cpp:145
llvm::StringMap< Entry > m_entries
Map used for bookkeeping.
Definition: Progress.h:209
void Increment(const Progress::ProgressData &)
Control the refcount of the progress report category as needed.
Definition: Progress.cpp:150
std::mutex m_entries_mutex
Mutex to provide the map.
Definition: Progress.h:212
A Progress indicator helper class.
Definition: Progress.h:60
const Timeout< std::nano > m_minimum_report_time
Definition: Progress.h:142
void Increment(uint64_t amount=1, std::optional< std::string > updated_detail={})
Increment the progress and send a notification to the installed callback.
Definition: Progress.cpp:70
static std::atomic< uint64_t > g_id
Definition: Progress.h:135
std::atomic< uint64_t > m_last_report_time_ns
Time (in nanoseconds since epoch) of the last progress report.
Definition: Progress.h:151
std::string m_details
More specific information about the current file being displayed in the report.
Definition: Progress.h:158
Origin
Enum to indicate the origin of a progress event, internal or external.
Definition: Progress.h:63
static constexpr uint64_t kNonDeterministicTotal
Used to indicate a non-deterministic progress report.
Definition: Progress.h:116
std::mutex m_mutex
Guards non-const non-atomic members of the class.
Definition: Progress.h:154
std::atomic< uint64_t > m_completed
How much work ([0...m_total]) that has been completed.
Definition: Progress.h:148
std::optional< uint64_t > m_prev_completed
The "completed" value of the last reported event.
Definition: Progress.h:161
Progress(std::string title, std::string details={}, std::optional< uint64_t > total=std::nullopt, lldb_private::Debugger *debugger=nullptr, Timeout< std::nano > minimum_report_time=std::nullopt, Origin origin=Origin::eInternal)
Construct a progress object that will report information.
Definition: Progress.cpp:28
~Progress()
Destroy the progress object.
Definition: Progress.cpp:55
const ProgressData m_progress_data
Data needed by the debugger to broadcast a progress event.
Definition: Progress.h:145
const uint64_t m_total
Total amount of work, use a std::nullopt in the constructor for non deterministic progress.
Definition: Progress.h:139
A class that represents a running process on the host machine.
Definition: SBAddress.h:15
@ eBroadcastBitProgressCategory
@ eBroadcastBitExternalProgress
@ eBroadcastBitProgress
@ eBroadcastBitExternalProgressCategory
uint64_t user_id_t
Definition: lldb-types.h:82
Entry used for bookkeeping.
Definition: Progress.h:197
Progress::ProgressData data
Data used to emit progress events.
Definition: Progress.h:202
Alarm::Handle handle
Alarm handle used when the refcount reaches zero.
Definition: Progress.h:205
uint64_t refcount
Reference count used for overlapping events.
Definition: Progress.h:199
Data belonging to this Progress event that is used for bookkeeping by ProgressManager.
Definition: Progress.h:120
Origin origin
The origin of the progress event, wheter it is internal or external.
Definition: Progress.h:130
std::optional< lldb::user_id_t > debugger_id
The optional debugger ID to report progress to.
Definition: Progress.h:127
std::string title
The title of the progress activity, also used as a category.
Definition: Progress.h:122
uint64_t progress_id
A unique integer identifier for progress reporting.
Definition: Progress.h:124