Skip to content

Commit 137ed17

Browse files
[lldb][progress] Hook up new broadcast bit and progress manager (#83069)
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 3a30d8e commit 137ed17

File tree

5 files changed

+177
-38
lines changed

5 files changed

+177
-38
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: 25 additions & 12 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

@@ -100,27 +101,36 @@ class Progress {
100101
/// Used to indicate a non-deterministic progress report
101102
static constexpr uint64_t kNonDeterministicTotal = UINT64_MAX;
102103

104+
/// Data belonging to this Progress event that is used for bookkeeping by
105+
/// ProgressManager.
106+
struct ProgressData {
107+
/// The title of the progress activity, also used as a category.
108+
std::string title;
109+
/// A unique integer identifier for progress reporting.
110+
uint64_t progress_id;
111+
/// The optional debugger ID to report progress to. If this has no value
112+
/// then all debuggers will receive this event.
113+
std::optional<lldb::user_id_t> debugger_id;
114+
};
115+
103116
private:
104117
void ReportProgress();
105118
static std::atomic<uint64_t> g_id;
106-
/// The title of the progress activity.
107-
std::string m_title;
119+
/// More specific information about the current file being displayed in the
120+
/// report.
108121
std::string m_details;
109-
std::mutex m_mutex;
110-
/// A unique integer identifier for progress reporting.
111-
const uint64_t m_id;
112122
/// How much work ([0...m_total]) that has been completed.
113123
uint64_t m_completed;
114124
/// Total amount of work, use a std::nullopt in the constructor for non
115125
/// deterministic progress.
116126
uint64_t m_total;
117-
/// The optional debugger ID to report progress to. If this has no value then
118-
/// all debuggers will receive this event.
119-
std::optional<lldb::user_id_t> m_debugger_id;
127+
std::mutex m_mutex;
120128
/// Set to true when progress has been reported where m_completed == m_total
121129
/// to ensure that we don't send progress updates after progress has
122130
/// completed.
123131
bool m_complete = false;
132+
/// Data needed by the debugger to broadcast a progress event.
133+
ProgressData m_progress_data;
124134
};
125135

126136
/// A class used to group progress reports by category. This is done by using a
@@ -133,13 +143,16 @@ class ProgressManager {
133143
~ProgressManager();
134144

135145
/// Control the refcount of the progress report category as needed.
136-
void Increment(std::string category);
137-
void Decrement(std::string category);
146+
void Increment(const Progress::ProgressData &);
147+
void Decrement(const Progress::ProgressData &);
138148

139149
static ProgressManager &Instance();
140150

151+
static void ReportProgress(const Progress::ProgressData &);
152+
141153
private:
142-
llvm::StringMap<uint64_t> m_progress_category_map;
154+
llvm::StringMap<std::pair<uint64_t, Progress::ProgressData>>
155+
m_progress_category_map;
143156
std::mutex m_progress_map_mutex;
144157
};
145158

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: 42 additions & 14 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,15 +23,19 @@ 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_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} {
2730
if (total)
2831
m_total = *total;
2932

3033
if (debugger)
31-
m_debugger_id = debugger->GetID();
34+
m_progress_data.debugger_id = debugger->GetID();
35+
3236
std::lock_guard<std::mutex> guard(m_mutex);
3337
ReportProgress();
38+
ProgressManager::Instance().Increment(m_progress_data);
3439
}
3540

3641
Progress::~Progress() {
@@ -40,6 +45,7 @@ Progress::~Progress() {
4045
if (!m_completed)
4146
m_completed = m_total;
4247
ReportProgress();
48+
ProgressManager::Instance().Decrement(m_progress_data);
4349
}
4450

4551
void Progress::Increment(uint64_t amount,
@@ -49,7 +55,7 @@ void Progress::Increment(uint64_t amount,
4955
if (updated_detail)
5056
m_details = std::move(updated_detail.value());
5157
// Watch out for unsigned overflow and make sure we don't increment too
52-
// much and exceed m_total.
58+
// much and exceed the total.
5359
if (m_total && (amount > (m_total - m_completed)))
5460
m_completed = m_total;
5561
else
@@ -63,8 +69,9 @@ void Progress::ReportProgress() {
6369
// Make sure we only send one notification that indicates the progress is
6470
// complete
6571
m_complete = m_completed == m_total;
66-
Debugger::ReportProgress(m_id, m_title, m_details, m_completed, m_total,
67-
m_debugger_id);
72+
Debugger::ReportProgress(m_progress_data.progress_id, m_progress_data.title,
73+
m_details, m_completed, m_total,
74+
m_progress_data.debugger_id);
6875
}
6976
}
7077

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

85-
void ProgressManager::Increment(std::string title) {
92+
void ProgressManager::Increment(const 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. Also, store the current
96+
// progress data in the map so that we have a note of the ID used for the
97+
// initial progress report.
98+
if (!m_progress_category_map.contains(progress_data.title)) {
99+
m_progress_category_map[progress_data.title].second = progress_data;
100+
ReportProgress(progress_data);
101+
}
102+
m_progress_category_map[progress_data.title].first++;
88103
}
89104

90-
void ProgressManager::Decrement(std::string title) {
105+
void ProgressManager::Decrement(const Progress::ProgressData &progress_data) {
91106
std::lock_guard<std::mutex> lock(m_progress_map_mutex);
92-
auto pos = m_progress_category_map.find(title);
107+
auto pos = m_progress_category_map.find(progress_data.title);
93108

94109
if (pos == m_progress_category_map.end())
95110
return;
96111

97-
if (pos->second <= 1)
98-
m_progress_category_map.erase(title);
99-
else
100-
--pos->second;
112+
if (pos->second.first <= 1) {
113+
ReportProgress(pos->second.second);
114+
m_progress_category_map.erase(progress_data.title);
115+
} else {
116+
--pos->second.first;
117+
}
118+
}
119+
120+
void ProgressManager::ReportProgress(
121+
const 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 and reset other fields when
124+
// broadcasting to it since that bit doesn't need that information.
125+
Debugger::ReportProgress(
126+
progress_data.progress_id, progress_data.title, "",
127+
Progress::kNonDeterministicTotal, Progress::kNonDeterministicTotal,
128+
progress_data.debugger_id, Debugger::eBroadcastBitProgressCategory);
101129
}

lldb/unittests/Core/ProgressReportTest.cpp

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
#include "lldb/Host/HostInfo.h"
1717
#include "lldb/Utility/Listener.h"
1818
#include "gtest/gtest.h"
19+
#include <memory>
1920
#include <mutex>
20-
#include <thread>
2121

2222
using namespace lldb;
2323
using namespace lldb_private;
@@ -126,3 +126,94 @@ 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_TRUE(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+
186+
// Create two progress reports of the same category that overlap with each
187+
// other. Here we want to ensure that the ID broadcasted for the initial and
188+
// final reports for this category are the same.
189+
std::unique_ptr<Progress> overlap_progress1 =
190+
std::make_unique<Progress>("Overlapping report 1", "Starting report 1");
191+
std::unique_ptr<Progress> overlap_progress2 =
192+
std::make_unique<Progress>("Overlapping report 1", "Starting report 2");
193+
overlap_progress1.reset();
194+
195+
EXPECT_TRUE(listener_sp->GetEvent(event_sp, timeout));
196+
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
197+
// Get the ID used in the first report for this category.
198+
uint64_t expected_progress_id = data->GetID();
199+
200+
ASSERT_EQ(data->GetDetails(), "");
201+
ASSERT_FALSE(data->IsFinite());
202+
ASSERT_TRUE(data->GetCompleted());
203+
ASSERT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
204+
ASSERT_EQ(data->GetMessage(), "Overlapping report 1");
205+
206+
overlap_progress2.reset();
207+
208+
EXPECT_TRUE(listener_sp->GetEvent(event_sp, timeout));
209+
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
210+
211+
ASSERT_EQ(data->GetDetails(), "");
212+
ASSERT_FALSE(data->IsFinite());
213+
ASSERT_TRUE(data->GetCompleted());
214+
ASSERT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
215+
ASSERT_EQ(data->GetMessage(), "Overlapping report 1");
216+
// The progress ID for the final report should be the same as that for the
217+
// initial report.
218+
ASSERT_EQ(data->GetID(), expected_progress_id);
219+
}

0 commit comments

Comments
 (0)