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);
48 if (!m_completed)
51
52 // Report to the ProgressManager if that subsystem is enabled.
55}
56
57void Progress::Increment(uint64_t amount,
58 std::optional<std::string> updated_detail) {
59 if (amount > 0) {
60 std::lock_guard<std::mutex> guard(m_mutex);
61 if (updated_detail)
62 m_details = std::move(updated_detail.value());
63 // Watch out for unsigned overflow and make sure we don't increment too
64 // much and exceed the total.
65 if (m_total && (amount > (m_total - m_completed)))
67 else
68 m_completed += amount;
70 }
71}
72
74 if (!m_complete) {
75 // Make sure we only send one notification that indicates the progress is
76 // complete
81 }
82}
83
85 : m_entries(), m_alarm(std::chrono::milliseconds(100)) {}
86
88
90 assert(!InstanceImpl() && "Already initialized.");
91 InstanceImpl().emplace();
92}
93
95 assert(InstanceImpl() && "Already terminated.");
96 InstanceImpl().reset();
97}
98
99bool ProgressManager::Enabled() { return InstanceImpl().operator bool(); }
100
102 assert(InstanceImpl() && "ProgressManager must be initialized");
103 return *InstanceImpl();
104}
105
106std::optional<ProgressManager> &ProgressManager::InstanceImpl() {
107 static std::optional<ProgressManager> g_progress_manager;
108 return g_progress_manager;
109}
110
112 std::lock_guard<std::mutex> lock(m_entries_mutex);
113
114 llvm::StringRef key = progress_data.title;
115 bool new_entry = !m_entries.contains(key);
116 Entry &entry = m_entries[progress_data.title];
117
118 if (new_entry) {
119 // This is a new progress event. Report progress and store the progress
120 // data.
121 ReportProgress(progress_data, EventType::Begin);
122 entry.data = progress_data;
123 } else if (entry.refcount == 0) {
124 // This is an existing entry that was scheduled to be deleted but a new one
125 // came in before the timer expired.
126 assert(entry.handle != Alarm::INVALID_HANDLE);
127
128 if (!m_alarm.Cancel(entry.handle)) {
129 // The timer expired before we had a chance to cancel it. We have to treat
130 // this as an entirely new progress event.
131 ReportProgress(progress_data, EventType::Begin);
132 }
133 // Clear the alarm handle.
135 }
136
137 // Regardless of how we got here, we need to bump the reference count.
138 entry.refcount++;
139}
140
142 std::lock_guard<std::mutex> lock(m_entries_mutex);
143 llvm::StringRef key = progress_data.title;
144
145 if (!m_entries.contains(key))
146 return;
147
148 Entry &entry = m_entries[key];
149 entry.refcount--;
150
151 if (entry.refcount == 0) {
152 assert(entry.handle == Alarm::INVALID_HANDLE);
153
154 // Copy the key to a std::string so we can pass it by value to the lambda.
155 // The underlying StringRef will not exist by the time the callback is
156 // called.
157 std::string key_str = std::string(key);
158
159 // Start a timer. If it expires before we see another progress event, it
160 // will be reported.
161 entry.handle = m_alarm.Create([=]() { Expire(key_str); });
162 }
163}
164
166 const Progress::ProgressData &progress_data, EventType type) {
167 // The category bit only keeps track of when progress report categories have
168 // started and ended, so clear the details and reset other fields when
169 // broadcasting to it since that bit doesn't need that information.
170 const uint64_t completed =
172 Debugger::ReportProgress(progress_data.progress_id, progress_data.title, "",
174 progress_data.debugger_id,
176}
177
178void ProgressManager::Expire(llvm::StringRef key) {
179 std::lock_guard<std::mutex> lock(m_entries_mutex);
180
181 // This shouldn't happen but be resilient anyway.
182 if (!m_entries.contains(key))
183 return;
184
185 // A new event came in and the alarm fired before we had a chance to restart
186 // it.
187 if (m_entries[key].refcount != 0)
188 return;
189
190 // We're done with this entry.
192 m_entries.erase(key);
193}
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:79
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=eBroadcastBitProgress)
Report progress events.
Definition: Debugger.cpp:1450
A class used to group progress reports by category.
Definition: Progress.h:145
void Decrement(const Progress::ProgressData &)
Definition: Progress.cpp:141
static void ReportProgress(const Progress::ProgressData &progress_data, EventType type)
Definition: Progress.cpp:165
Alarm m_alarm
Alarm instance to coalesce progress events.
Definition: Progress.h:192
static ProgressManager & Instance()
Definition: Progress.cpp:101
void Expire(llvm::StringRef key)
Helper function for reporting progress when the alarm in the corresponding entry in the map expires.
Definition: Progress.cpp:178
static std::optional< ProgressManager > & InstanceImpl()
Definition: Progress.cpp:106
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:111
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 intalled callback.
Definition: Progress.cpp:57
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: SBAttachInfo.h:14
Definition: SBAddress.h:15
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