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
14#include <cstdint>
15#include <mutex>
16#include <optional>
17
18using namespace lldb;
19using namespace lldb_private;
20
21std::atomic<uint64_t> Progress::g_id(0);
22
23Progress::Progress(std::string title, std::string details,
24 std::optional<uint64_t> total,
25 lldb_private::Debugger *debugger)
26 : m_details(details), m_completed(0),
27 m_total(Progress::kNonDeterministicTotal),
28 m_progress_data{title, ++g_id,
29 /*m_progress_data.debugger_id=*/std::nullopt} {
30 if (total)
31 m_total = *total;
32
33 if (debugger)
34 m_progress_data.debugger_id = debugger->GetID();
35
36 std::lock_guard<std::mutex> guard(m_mutex);
38
39 // Report to the ProgressManager if that subsystem is enabled.
42}
43
45 // Make sure to always report progress completed when this object is
46 // destructed so it indicates the progress dialog/activity should go away.
47 std::lock_guard<std::mutex> guard(m_mutex);
50
51 // Report to the ProgressManager if that subsystem is enabled.
54}
55
56void Progress::Increment(uint64_t amount,
57 std::optional<std::string> updated_detail) {
58 if (amount > 0) {
59 std::lock_guard<std::mutex> guard(m_mutex);
60 if (updated_detail)
61 m_details = std::move(updated_detail.value());
62 // Watch out for unsigned overflow and make sure we don't increment too
63 // much and exceed the total.
64 if (m_total && (amount > (m_total - m_completed)))
66 else
67 m_completed += amount;
69 }
70}
71
73 if (!m_complete) {
74 // Make sure we only send one notification that indicates the progress is
75 // complete
80 }
81}
82
84 : m_entries(), m_alarm(std::chrono::milliseconds(100)) {}
85
87
89 assert(!InstanceImpl() && "Already initialized.");
90 InstanceImpl().emplace();
91}
92
94 assert(InstanceImpl() && "Already terminated.");
95 InstanceImpl().reset();
96}
97
98bool ProgressManager::Enabled() { return InstanceImpl().operator bool(); }
99
101 assert(InstanceImpl() && "ProgressManager must be initialized");
102 return *InstanceImpl();
103}
104
105std::optional<ProgressManager> &ProgressManager::InstanceImpl() {
106 static std::optional<ProgressManager> g_progress_manager;
107 return g_progress_manager;
108}
109
111 std::lock_guard<std::mutex> lock(m_entries_mutex);
112
113 llvm::StringRef key = progress_data.title;
114 bool new_entry = !m_entries.contains(key);
115 Entry &entry = m_entries[progress_data.title];
116
117 if (new_entry) {
118 // This is a new progress event. Report progress and store the progress
119 // data.
120 ReportProgress(progress_data, EventType::Begin);
121 entry.data = progress_data;
122 } else if (entry.refcount == 0) {
123 // This is an existing entry that was scheduled to be deleted but a new one
124 // came in before the timer expired.
125 assert(entry.handle != Alarm::INVALID_HANDLE);
126
127 if (!m_alarm.Cancel(entry.handle)) {
128 // The timer expired before we had a chance to cancel it. We have to treat
129 // this as an entirely new progress event.
130 ReportProgress(progress_data, EventType::Begin);
131 }
132 // Clear the alarm handle.
134 }
135
136 // Regardless of how we got here, we need to bump the reference count.
137 entry.refcount++;
138}
139
141 std::lock_guard<std::mutex> lock(m_entries_mutex);
142 llvm::StringRef key = progress_data.title;
143
144 if (!m_entries.contains(key))
145 return;
146
147 Entry &entry = m_entries[key];
148 entry.refcount--;
149
150 if (entry.refcount == 0) {
151 assert(entry.handle == Alarm::INVALID_HANDLE);
152
153 // Copy the key to a std::string so we can pass it by value to the lambda.
154 // The underlying StringRef will not exist by the time the callback is
155 // called.
156 std::string key_str = std::string(key);
157
158 // Start a timer. If it expires before we see another progress event, it
159 // will be reported.
160 entry.handle = m_alarm.Create([=]() { Expire(key_str); });
161 }
162}
163
165 const Progress::ProgressData &progress_data, EventType type) {
166 // The category bit only keeps track of when progress report categories have
167 // started and ended, so clear the details and reset other fields when
168 // broadcasting to it since that bit doesn't need that information.
169 const uint64_t completed =
171 Debugger::ReportProgress(progress_data.progress_id, progress_data.title, "",
173 progress_data.debugger_id,
175}
176
177void ProgressManager::Expire(llvm::StringRef key) {
178 std::lock_guard<std::mutex> lock(m_entries_mutex);
179
180 // This shouldn't happen but be resilient anyway.
181 if (!m_entries.contains(key))
182 return;
183
184 // A new event came in and the alarm fired before we had a chance to restart
185 // it.
186 if (m_entries[key].refcount != 0)
187 return;
188
189 // We're done with this entry.
191 m_entries.erase(key);
192}
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:1485
A class used to group progress reports by category.
Definition: Progress.h:145
void Decrement(const Progress::ProgressData &)
Definition: Progress.cpp:140
static void ReportProgress(const Progress::ProgressData &progress_data, EventType type)
Definition: Progress.cpp:164
Alarm m_alarm
Alarm instance to coalesce progress events.
Definition: Progress.h:192
static ProgressManager & Instance()
Definition: Progress.cpp:100
void Expire(llvm::StringRef key)
Helper function for reporting progress when the alarm in the corresponding entry in the map expires.
Definition: Progress.cpp:177
static std::optional< ProgressManager > & InstanceImpl()
Definition: Progress.cpp:105
llvm::StringMap< Entry > m_entries
Map used for bookkeeping.
Definition: Progress.h:186
void Increment(const Progress::ProgressData &)
Control the refcount of the progress report category as needed.
Definition: Progress.cpp:110
std::mutex m_entries_mutex
Mutex to provide the map.
Definition: Progress.h:189
A Progress indicator helper class.
Definition: Progress.h:59
bool m_complete
Set to true when progress has been reported where m_completed == m_total to ensure that we don't send...
Definition: Progress.h:136
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:56
uint64_t m_completed
How much work ([0...m_total]) that has been completed.
Definition: Progress.h:128
static std::atomic< uint64_t > g_id
Definition: Progress.h:123
std::string m_details
More specific information about the current file being displayed in the report.
Definition: Progress.h:126
static constexpr uint64_t kNonDeterministicTotal
Used to indicate a non-deterministic progress report.
Definition: Progress.h:107
std::mutex m_mutex
Definition: Progress.h:132
uint64_t m_total
Total amount of work, use a std::nullopt in the constructor for non deterministic progress.
Definition: Progress.h:131
~Progress()
Destroy the progress object.
Definition: Progress.cpp:44
ProgressData m_progress_data
Data needed by the debugger to broadcast a progress event.
Definition: Progress.h:138
Progress(std::string title, std::string details={}, std::optional< uint64_t > total=std::nullopt, lldb_private::Debugger *debugger=nullptr)
Construct a progress object that will report information.
Definition: Progress.cpp:23
A class that represents a running process on the host machine.
Definition: SBAddress.h:15
@ eBroadcastBitProgressCategory
Entry used for bookkeeping.
Definition: Progress.h:174
Progress::ProgressData data
Data used to emit progress events.
Definition: Progress.h:179
Alarm::Handle handle
Alarm handle used when the refcount reaches zero.
Definition: Progress.h:182
uint64_t refcount
Reference count used for overlapping events.
Definition: Progress.h:176
Data belonging to this Progress event that is used for bookkeeping by ProgressManager.
Definition: Progress.h:111
std::optional< lldb::user_id_t > debugger_id
The optional debugger ID to report progress to.
Definition: Progress.h:118
std::string title
The title of the progress activity, also used as a category.
Definition: Progress.h:113
uint64_t progress_id
A unique integer identifier for progress reporting.
Definition: Progress.h:115
lldb::user_id_t GetID() const
Get accessor for the user ID.
Definition: UserID.h:47