Skip to content

Commit d2854ec

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 d2854ec

File tree

5 files changed

+146
-31
lines changed

5 files changed

+146
-31
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: 32 additions & 11 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,44 @@ 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:
123+
friend class Debugger;
101124
void ReportProgress();
102125
static std::atomic<uint64_t> g_id;
103-
/// The title of the progress activity.
104126
std::string m_title;
105127
std::string m_details;
106128
std::mutex m_mutex;
107-
/// A unique integer identifier for progress reporting.
108129
const uint64_t m_id;
109-
/// How much work ([0...m_total]) that has been completed.
110130
uint64_t m_completed;
111-
/// Total amount of work, use a std::nullopt in the constructor for non
112-
/// deterministic progress.
113131
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.
116132
std::optional<lldb::user_id_t> m_debugger_id;
117133
/// Set to true when progress has been reported where m_completed == m_total
118134
/// to ensure that we don't send progress updates after progress has
119135
/// completed.
120136
bool m_complete = false;
137+
/// Data needed by the debugger to broadcast a progress event.
138+
ProgressData m_progress_data;
121139
};
122140

123141
/// A class used to group progress reports by category. This is done by using a
@@ -130,13 +148,16 @@ class ProgressManager {
130148
~ProgressManager();
131149

132150
/// Control the refcount of the progress report category as needed.
133-
void Increment(std::string category);
134-
void Decrement(std::string category);
151+
void Increment(Progress::ProgressData);
152+
void Decrement(Progress::ProgressData);
135153

136154
static ProgressManager &Instance();
137155

156+
void ReportProgress(Progress::ProgressData);
157+
138158
private:
139-
llvm::StringMap<uint64_t> m_progress_category_map;
159+
llvm::StringMap<std::pair<uint64_t, Progress::ProgressData>>
160+
m_progress_category_map;
140161
std::mutex m_progress_map_mutex;
141162
};
142163

lldb/source/Core/Debugger.cpp

Lines changed: 18 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,17 @@ 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.
14381440
const uint32_t event_type = Debugger::eBroadcastBitProgress;
1439-
if (!debugger.GetBroadcaster().EventTypeHasListeners(event_type))
1441+
const uint32_t category_event_type = Debugger::eBroadcastBitProgressCategory;
1442+
if (!debugger.GetBroadcaster().EventTypeHasListeners(event_type |
1443+
category_event_type))
14401444
return;
1445+
14411446
EventSP event_sp(new Event(
1442-
event_type,
1447+
progress_broadcast_bit,
14431448
new ProgressEventData(progress_id, std::move(title), std::move(details),
14441449
completed, total, is_debugger_specific)));
14451450
debugger.GetBroadcaster().BroadcastEvent(event_sp);
@@ -1448,7 +1453,8 @@ static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id,
14481453
void Debugger::ReportProgress(uint64_t progress_id, std::string title,
14491454
std::string details, uint64_t completed,
14501455
uint64_t total,
1451-
std::optional<lldb::user_id_t> debugger_id) {
1456+
std::optional<lldb::user_id_t> debugger_id,
1457+
uint32_t progress_category_bit) {
14521458
// Check if this progress is for a specific debugger.
14531459
if (debugger_id) {
14541460
// It is debugger specific, grab it and deliver the event if the debugger
@@ -1457,7 +1463,8 @@ void Debugger::ReportProgress(uint64_t progress_id, std::string title,
14571463
if (debugger_sp)
14581464
PrivateReportProgress(*debugger_sp, progress_id, std::move(title),
14591465
std::move(details), completed, total,
1460-
/*is_debugger_specific*/ true);
1466+
/*is_debugger_specific*/ true,
1467+
progress_category_bit);
14611468
return;
14621469
}
14631470
// The progress event is not debugger specific, iterate over all debuggers
@@ -1467,7 +1474,8 @@ void Debugger::ReportProgress(uint64_t progress_id, std::string title,
14671474
DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
14681475
for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos)
14691476
PrivateReportProgress(*(*pos), progress_id, title, details, completed,
1470-
total, /*is_debugger_specific*/ false);
1477+
total, /*is_debugger_specific*/ false,
1478+
progress_category_bit);
14711479
}
14721480
}
14731481

@@ -1875,7 +1883,8 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
18751883

18761884
listener_sp->StartListeningForEvents(
18771885
&m_broadcaster, eBroadcastBitProgress | eBroadcastBitWarning |
1878-
eBroadcastBitError | eBroadcastSymbolChange);
1886+
eBroadcastBitError | eBroadcastSymbolChange |
1887+
eBroadcastBitProgressCategory);
18791888

18801889
// Let the thread that spawned us know that we have started up and that we
18811890
// are now listening to all required events so no events get missed
@@ -1929,6 +1938,8 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
19291938
} else if (broadcaster == &m_broadcaster) {
19301939
if (event_type & Debugger::eBroadcastBitProgress)
19311940
HandleProgressEvent(event_sp);
1941+
else if (event_type & Debugger::eBroadcastBitProgressCategory)
1942+
HandleProgressEvent(event_sp);
19321943
else if (event_type & Debugger::eBroadcastBitWarning)
19331944
HandleDiagnosticEvent(event_sp);
19341945
else if (event_type & Debugger::eBroadcastBitError)

lldb/source/Core/Progress.cpp

Lines changed: 33 additions & 9 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

@@ -29,8 +30,12 @@ Progress::Progress(std::string title, std::string details,
2930

3031
if (debugger)
3132
m_debugger_id = debugger->GetID();
33+
34+
m_progress_data = {m_title, m_details, m_id,
35+
m_completed, m_total, m_debugger_id};
3236
std::lock_guard<std::mutex> guard(m_mutex);
3337
ReportProgress();
38+
ProgressManager::Instance().Increment(m_progress_data);
3439
}
3540

3641
Progress::~Progress() {
@@ -39,7 +44,9 @@ Progress::~Progress() {
3944
std::lock_guard<std::mutex> guard(m_mutex);
4045
if (!m_completed)
4146
m_completed = m_total;
47+
m_progress_data.completed = m_completed;
4248
ReportProgress();
49+
ProgressManager::Instance().Decrement(m_progress_data);
4350
}
4451

4552
void Progress::Increment(uint64_t amount,
@@ -64,7 +71,7 @@ void Progress::ReportProgress() {
6471
// complete
6572
m_complete = m_completed == m_total;
6673
Debugger::ReportProgress(m_id, m_title, m_details, m_completed, m_total,
67-
m_debugger_id);
74+
m_debugger_id, Debugger::eBroadcastBitProgress);
6875
}
6976
}
7077

@@ -82,20 +89,37 @@ ProgressManager &ProgressManager::Instance() {
8289
return *g_progress_manager;
8390
}
8491

85-
void ProgressManager::Increment(std::string title) {
92+
void ProgressManager::Increment(Progress::ProgressData progress_data) {
8693
std::lock_guard<std::mutex> lock(m_progress_map_mutex);
87-
m_progress_category_map[title]++;
94+
// If the current category exists in the map then it is not an initial report,
95+
// therefore don't broadcast to the category bit.
96+
if (!m_progress_category_map.contains(progress_data.title))
97+
ReportProgress(progress_data);
98+
m_progress_category_map[progress_data.title].first++;
8899
}
89100

90-
void ProgressManager::Decrement(std::string title) {
101+
void ProgressManager::Decrement(Progress::ProgressData progress_data) {
91102
std::lock_guard<std::mutex> lock(m_progress_map_mutex);
92-
auto pos = m_progress_category_map.find(title);
103+
auto pos = m_progress_category_map.find(progress_data.title);
93104

94105
if (pos == m_progress_category_map.end())
95106
return;
96107

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

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)