Skip to content

Commit 63dded3

Browse files
committed
[lldb][progress] Hook up new broadcast bit and progress manager
This commit adds the functionality to broadcast events using the `Debugger::eBroadcastProgressCategory` bit (#81169) by keeping track of these reports with the `ProgressManager` class (#81319). The new bit is used in such a way that it will only broadcast the initial and final progress reports for specific progress categories that are managed by the progress manager. This commit also adds a new test to the progress report unit test that checks that only the initial/final reports are broadcasted when using the new bit.
1 parent f01ed3b commit 63dded3

File tree

5 files changed

+157
-50
lines changed

5 files changed

+157
-50
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
593593
friend class CommandInterpreter;
594594
friend class REPL;
595595
friend class Progress;
596+
friend class ProgressManager;
596597

597598
/// Report progress events.
598599
///
@@ -623,10 +624,11 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
623624
/// debugger identifier that this progress should be delivered to. If this
624625
/// optional parameter does not have a value, the progress will be
625626
/// delivered to all debuggers.
626-
static void ReportProgress(uint64_t progress_id, std::string title,
627-
std::string details, uint64_t completed,
628-
uint64_t total,
629-
std::optional<lldb::user_id_t> debugger_id);
627+
static void
628+
ReportProgress(uint64_t progress_id, std::string title, std::string details,
629+
uint64_t completed, uint64_t total,
630+
std::optional<lldb::user_id_t> debugger_id,
631+
uint32_t progress_category_bit = eBroadcastBitProgress);
630632

631633
static void ReportDiagnosticImpl(DiagnosticEventData::Type type,
632634
std::string message,

lldb/include/lldb/Core/Progress.h

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
#ifndef LLDB_CORE_PROGRESS_H
1010
#define LLDB_CORE_PROGRESS_H
1111

12-
#include "lldb/Utility/ConstString.h"
12+
#include "lldb/lldb-forward.h"
1313
#include "lldb/lldb-types.h"
1414
#include "llvm/ADT/StringMap.h"
1515
#include <atomic>
16+
#include <cstdint>
1617
#include <mutex>
1718
#include <optional>
1819

@@ -97,27 +98,37 @@ class Progress {
9798
/// Used to indicate a non-deterministic progress report
9899
static constexpr uint64_t kNonDeterministicTotal = UINT64_MAX;
99100

101+
/// Use a struct to send data from a Progress object to
102+
/// the progress manager.
103+
struct ProgressData {
104+
/// The title of the progress activity, also used as a category to group
105+
/// reports.
106+
std::string title;
107+
/// More specific information about the current file being displayed in the
108+
/// report.
109+
std::string details;
110+
/// A unique integer identifier for progress reporting.
111+
uint64_t progress_id;
112+
/// How much work ([0...m_total]) that has been completed.
113+
uint64_t completed;
114+
/// Total amount of work, use a std::nullopt in the constructor for non
115+
/// deterministic progress.
116+
uint64_t total;
117+
/// The optional debugger ID to report progress to. If this has no value
118+
/// then all debuggers will receive this event.
119+
std::optional<lldb::user_id_t> debugger_id;
120+
};
121+
100122
private:
101123
void ReportProgress();
102124
static std::atomic<uint64_t> g_id;
103-
/// The title of the progress activity.
104-
std::string m_title;
105-
std::string m_details;
106125
std::mutex m_mutex;
107-
/// A unique integer identifier for progress reporting.
108-
const uint64_t m_id;
109-
/// How much work ([0...m_total]) that has been completed.
110-
uint64_t m_completed;
111-
/// Total amount of work, use a std::nullopt in the constructor for non
112-
/// deterministic progress.
113-
uint64_t m_total;
114-
/// The optional debugger ID to report progress to. If this has no value then
115-
/// all debuggers will receive this event.
116-
std::optional<lldb::user_id_t> m_debugger_id;
117126
/// Set to true when progress has been reported where m_completed == m_total
118127
/// to ensure that we don't send progress updates after progress has
119128
/// completed.
120129
bool m_complete = false;
130+
/// Data needed by the debugger to broadcast a progress event.
131+
ProgressData m_progress_data;
121132
};
122133

123134
/// A class used to group progress reports by category. This is done by using a
@@ -130,13 +141,16 @@ class ProgressManager {
130141
~ProgressManager();
131142

132143
/// Control the refcount of the progress report category as needed.
133-
void Increment(std::string category);
134-
void Decrement(std::string category);
144+
void Increment(Progress::ProgressData);
145+
void Decrement(Progress::ProgressData);
135146

136147
static ProgressManager &Instance();
137148

149+
void ReportProgress(Progress::ProgressData);
150+
138151
private:
139-
llvm::StringMap<uint64_t> m_progress_category_map;
152+
llvm::StringMap<std::pair<uint64_t, Progress::ProgressData>>
153+
m_progress_category_map;
140154
std::mutex m_progress_map_mutex;
141155
};
142156

lldb/source/Core/Debugger.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "lldb/Core/ModuleList.h"
1616
#include "lldb/Core/ModuleSpec.h"
1717
#include "lldb/Core/PluginManager.h"
18+
#include "lldb/Core/Progress.h"
1819
#include "lldb/Core/StreamAsynchronousIO.h"
1920
#include "lldb/DataFormatters/DataVisualization.h"
2021
#include "lldb/Expression/REPL.h"
@@ -1433,13 +1434,14 @@ void Debugger::SetDestroyCallback(
14331434
static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id,
14341435
std::string title, std::string details,
14351436
uint64_t completed, uint64_t total,
1436-
bool is_debugger_specific) {
1437+
bool is_debugger_specific,
1438+
uint32_t progress_broadcast_bit) {
14371439
// Only deliver progress events if we have any progress listeners.
1438-
const uint32_t event_type = Debugger::eBroadcastBitProgress;
1439-
if (!debugger.GetBroadcaster().EventTypeHasListeners(event_type))
1440+
if (!debugger.GetBroadcaster().EventTypeHasListeners(progress_broadcast_bit))
14401441
return;
1442+
14411443
EventSP event_sp(new Event(
1442-
event_type,
1444+
progress_broadcast_bit,
14431445
new ProgressEventData(progress_id, std::move(title), std::move(details),
14441446
completed, total, is_debugger_specific)));
14451447
debugger.GetBroadcaster().BroadcastEvent(event_sp);
@@ -1448,7 +1450,8 @@ static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id,
14481450
void Debugger::ReportProgress(uint64_t progress_id, std::string title,
14491451
std::string details, uint64_t completed,
14501452
uint64_t total,
1451-
std::optional<lldb::user_id_t> debugger_id) {
1453+
std::optional<lldb::user_id_t> debugger_id,
1454+
uint32_t progress_category_bit) {
14521455
// Check if this progress is for a specific debugger.
14531456
if (debugger_id) {
14541457
// It is debugger specific, grab it and deliver the event if the debugger
@@ -1457,7 +1460,8 @@ void Debugger::ReportProgress(uint64_t progress_id, std::string title,
14571460
if (debugger_sp)
14581461
PrivateReportProgress(*debugger_sp, progress_id, std::move(title),
14591462
std::move(details), completed, total,
1460-
/*is_debugger_specific*/ true);
1463+
/*is_debugger_specific*/ true,
1464+
progress_category_bit);
14611465
return;
14621466
}
14631467
// The progress event is not debugger specific, iterate over all debuggers
@@ -1467,7 +1471,8 @@ void Debugger::ReportProgress(uint64_t progress_id, std::string title,
14671471
DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
14681472
for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos)
14691473
PrivateReportProgress(*(*pos), progress_id, title, details, completed,
1470-
total, /*is_debugger_specific*/ false);
1474+
total, /*is_debugger_specific*/ false,
1475+
progress_category_bit);
14711476
}
14721477
}
14731478

lldb/source/Core/Progress.cpp

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "lldb/Core/Debugger.h"
1212
#include "lldb/Utility/StreamString.h"
1313

14+
#include <cstdint>
1415
#include <mutex>
1516
#include <optional>
1617

@@ -22,38 +23,47 @@ std::atomic<uint64_t> Progress::g_id(0);
2223
Progress::Progress(std::string title, std::string details,
2324
std::optional<uint64_t> total,
2425
lldb_private::Debugger *debugger)
25-
: m_title(title), m_details(details), m_id(++g_id), m_completed(0),
26-
m_total(Progress::kNonDeterministicTotal) {
26+
: m_progress_data{title,
27+
details,
28+
++g_id,
29+
/*m_progress_data.completed*/ 0,
30+
Progress::kNonDeterministicTotal,
31+
/*m_progress_data.debugger_id*/ std::nullopt} {
2732
if (total)
28-
m_total = *total;
33+
m_progress_data.total = *total;
2934

3035
if (debugger)
31-
m_debugger_id = debugger->GetID();
36+
m_progress_data.debugger_id = debugger->GetID();
37+
3238
std::lock_guard<std::mutex> guard(m_mutex);
3339
ReportProgress();
40+
ProgressManager::Instance().Increment(m_progress_data);
3441
}
3542

3643
Progress::~Progress() {
3744
// Make sure to always report progress completed when this object is
3845
// destructed so it indicates the progress dialog/activity should go away.
3946
std::lock_guard<std::mutex> guard(m_mutex);
40-
if (!m_completed)
41-
m_completed = m_total;
47+
if (!m_progress_data.completed)
48+
m_progress_data.completed = m_progress_data.total;
49+
m_progress_data.completed = m_progress_data.completed;
4250
ReportProgress();
51+
ProgressManager::Instance().Decrement(m_progress_data);
4352
}
4453

4554
void Progress::Increment(uint64_t amount,
4655
std::optional<std::string> updated_detail) {
4756
if (amount > 0) {
4857
std::lock_guard<std::mutex> guard(m_mutex);
4958
if (updated_detail)
50-
m_details = std::move(updated_detail.value());
59+
m_progress_data.details = std::move(updated_detail.value());
5160
// Watch out for unsigned overflow and make sure we don't increment too
52-
// much and exceed m_total.
53-
if (m_total && (amount > (m_total - m_completed)))
54-
m_completed = m_total;
61+
// much and exceed the total.
62+
if (m_progress_data.total &&
63+
(amount > (m_progress_data.total - m_progress_data.completed)))
64+
m_progress_data.completed = m_progress_data.total;
5565
else
56-
m_completed += amount;
66+
m_progress_data.completed += amount;
5767
ReportProgress();
5868
}
5969
}
@@ -62,9 +72,11 @@ void Progress::ReportProgress() {
6272
if (!m_complete) {
6373
// Make sure we only send one notification that indicates the progress is
6474
// complete
65-
m_complete = m_completed == m_total;
66-
Debugger::ReportProgress(m_id, m_title, m_details, m_completed, m_total,
67-
m_debugger_id);
75+
m_complete = m_progress_data.completed == m_progress_data.total;
76+
Debugger::ReportProgress(m_progress_data.progress_id, m_progress_data.title,
77+
m_progress_data.details, m_progress_data.completed,
78+
m_progress_data.total,
79+
m_progress_data.debugger_id);
6880
}
6981
}
7082

@@ -82,20 +94,37 @@ ProgressManager &ProgressManager::Instance() {
8294
return *g_progress_manager;
8395
}
8496

85-
void ProgressManager::Increment(std::string title) {
97+
void ProgressManager::Increment(Progress::ProgressData progress_data) {
8698
std::lock_guard<std::mutex> lock(m_progress_map_mutex);
87-
m_progress_category_map[title]++;
99+
// If the current category exists in the map then it is not an initial report,
100+
// therefore don't broadcast to the category bit.
101+
if (!m_progress_category_map.contains(progress_data.title))
102+
ReportProgress(progress_data);
103+
m_progress_category_map[progress_data.title].first++;
88104
}
89105

90-
void ProgressManager::Decrement(std::string title) {
106+
void ProgressManager::Decrement(Progress::ProgressData progress_data) {
91107
std::lock_guard<std::mutex> lock(m_progress_map_mutex);
92-
auto pos = m_progress_category_map.find(title);
108+
auto pos = m_progress_category_map.find(progress_data.title);
93109

94110
if (pos == m_progress_category_map.end())
95111
return;
96112

97-
if (pos->second <= 1)
98-
m_progress_category_map.erase(title);
99-
else
100-
--pos->second;
113+
if (pos->second.first <= 1) {
114+
m_progress_category_map.erase(progress_data.title);
115+
ReportProgress(progress_data);
116+
} else {
117+
--pos->second.first;
118+
}
119+
}
120+
121+
void ProgressManager::ReportProgress(Progress::ProgressData progress_data) {
122+
// The category bit only keeps track of when progress report categories have
123+
// started and ended, so clear the details when broadcasting to it since that
124+
// bit doesn't need that information.
125+
progress_data.details = "";
126+
Debugger::ReportProgress(progress_data.progress_id, progress_data.title,
127+
progress_data.details, progress_data.completed,
128+
progress_data.total, progress_data.debugger_id,
129+
Debugger::eBroadcastBitProgressCategory);
101130
}

lldb/unittests/Core/ProgressReportTest.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,60 @@ TEST_F(ProgressReportTest, TestReportCreation) {
126126
ASSERT_FALSE(data->IsFinite());
127127
ASSERT_EQ(data->GetMessage(), "Progress report 1: Starting report 1");
128128
}
129+
130+
TEST_F(ProgressReportTest, TestProgressManager) {
131+
std::chrono::milliseconds timeout(100);
132+
133+
// Set up the debugger, make sure that was done properly.
134+
ArchSpec arch("x86_64-apple-macosx-");
135+
Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
136+
137+
DebuggerSP debugger_sp = Debugger::CreateInstance();
138+
ASSERT_TRUE(debugger_sp);
139+
140+
// Get the debugger's broadcaster.
141+
Broadcaster &broadcaster = debugger_sp->GetBroadcaster();
142+
143+
// Create a listener, make sure it can receive events and that it's
144+
// listening to the correct broadcast bit.
145+
ListenerSP listener_sp = Listener::MakeListener("progress-category-listener");
146+
147+
listener_sp->StartListeningForEvents(&broadcaster,
148+
Debugger::eBroadcastBitProgressCategory);
149+
EXPECT_TRUE(broadcaster.EventTypeHasListeners(
150+
Debugger::eBroadcastBitProgressCategory));
151+
152+
EventSP event_sp;
153+
const ProgressEventData *data;
154+
155+
// Create three progress events with the same category then try to pop 2
156+
// events from the queue in a row before the progress reports are destroyed.
157+
// Since only 1 event should've been broadcast for this category, the second
158+
// GetEvent() call should return false.
159+
{
160+
Progress progress1("Progress report 1", "Starting report 1");
161+
Progress progress2("Progress report 1", "Starting report 2");
162+
Progress progress3("Progress report 1", "Starting report 3");
163+
EXPECT_TRUE(listener_sp->GetEvent(event_sp, timeout));
164+
EXPECT_FALSE(listener_sp->GetEvent(event_sp, timeout));
165+
}
166+
167+
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
168+
169+
ASSERT_EQ(data->GetDetails(), "");
170+
ASSERT_FALSE(data->IsFinite());
171+
ASSERT_FALSE(data->GetCompleted());
172+
ASSERT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
173+
ASSERT_EQ(data->GetMessage(), "Progress report 1");
174+
175+
// Pop another event from the queue, this should be the event for the final
176+
// report for this category.
177+
EXPECT_TRUE(listener_sp->GetEvent(event_sp, timeout));
178+
179+
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
180+
ASSERT_EQ(data->GetDetails(), "");
181+
ASSERT_FALSE(data->IsFinite());
182+
ASSERT_TRUE(data->GetCompleted());
183+
ASSERT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
184+
ASSERT_EQ(data->GetMessage(), "Progress report 1");
185+
}

0 commit comments

Comments
 (0)