Skip to content

Commit aa4685c

Browse files
walter-erquinigoWalter Erquinigo
authored and
Walter Erquinigo
committed
[lldb-vscode] only report long running progress events
When the number of shared libs is massive, there could be hundreds of thousands of short lived progress events sent to the IDE, which makes it irresponsive while it's processing all this data. As these small jobs take less than a second to process, the user doesn't even see them, because the IDE only display the progress of long operations. So it's better not to send these events. I'm fixing that by sending only the events that are taking longer than 5 seconds to process. In a specific run, I got the number of events from ~500k to 100, because there was only 1 big lib to parse. I've tried this on several small and massive targets, and it seems to work fine. Differential Revision: https://reviews.llvm.org/D101128
1 parent f7c54c4 commit aa4685c

File tree

5 files changed

+297
-64
lines changed

5 files changed

+297
-64
lines changed

lldb/tools/lldb-vscode/ProgressEvent.cpp

+168-34
Original file line numberDiff line numberDiff line change
@@ -13,43 +13,88 @@
1313
using namespace lldb_vscode;
1414
using namespace llvm;
1515

16-
ProgressEvent::ProgressEvent(uint64_t progress_id, const char *message,
17-
uint64_t completed, uint64_t total)
18-
: m_progress_id(progress_id), m_message(message) {
19-
if (completed == total)
20-
m_event_type = progressEnd;
21-
else if (completed == 0)
16+
// The minimum duration of an event for it to be reported
17+
const std::chrono::duration<double> kStartProgressEventReportDelay =
18+
std::chrono::seconds(1);
19+
// The minimum time interval between update events for reporting. If multiple
20+
// updates fall within the same time interval, only the latest is reported.
21+
const std::chrono::duration<double> kUpdateProgressEventReportDelay =
22+
std::chrono::milliseconds(250);
23+
24+
ProgressEvent::ProgressEvent(uint64_t progress_id, Optional<StringRef> message,
25+
uint64_t completed, uint64_t total,
26+
const ProgressEvent *prev_event)
27+
: m_progress_id(progress_id) {
28+
if (message)
29+
m_message = message->str();
30+
31+
const bool calculate_percentage = total != UINT64_MAX;
32+
if (completed == 0) {
33+
// Start event
2234
m_event_type = progressStart;
23-
else if (completed < total)
24-
m_event_type = progressUpdate;
25-
else
26-
m_event_type = progressInvalid;
35+
// Wait a bit before reporting the start event in case in completes really
36+
// quickly.
37+
m_minimum_allowed_report_time =
38+
m_creation_time + kStartProgressEventReportDelay;
39+
if (calculate_percentage)
40+
m_percentage = 0;
41+
} else if (completed == total) {
42+
// End event
43+
m_event_type = progressEnd;
44+
// We should report the end event right away.
45+
m_minimum_allowed_report_time = std::chrono::seconds::zero();
46+
if (calculate_percentage)
47+
m_percentage = 100;
48+
} else {
49+
// Update event
50+
assert(prev_event);
51+
m_percentage = std::min(
52+
(uint32_t)((double)completed / (double)total * 100.0), (uint32_t)99);
53+
if (prev_event->Reported()) {
54+
// Add a small delay between reports
55+
m_minimum_allowed_report_time =
56+
prev_event->m_minimum_allowed_report_time +
57+
kUpdateProgressEventReportDelay;
58+
} else {
59+
// We should use the previous timestamp, as it's still pending
60+
m_minimum_allowed_report_time = prev_event->m_minimum_allowed_report_time;
61+
}
62+
}
63+
}
64+
65+
Optional<ProgressEvent> ProgressEvent::Create(uint64_t progress_id,
66+
Optional<StringRef> message,
67+
uint64_t completed,
68+
uint64_t total,
69+
const ProgressEvent *prev_event) {
70+
ProgressEvent event(progress_id, message, completed, total, prev_event);
71+
// We shouldn't show unnamed start events in the IDE
72+
if (event.GetEventType() == progressStart && event.GetEventName().empty())
73+
return None;
2774

28-
if (0 < total && total < UINT64_MAX)
29-
m_percentage = (uint32_t)(((float)completed / (float)total) * 100.0);
75+
if (prev_event && prev_event->EqualsForIDE(event))
76+
return None;
77+
78+
return event;
3079
}
3180

32-
bool ProgressEvent::operator==(const ProgressEvent &other) const {
81+
bool ProgressEvent::EqualsForIDE(const ProgressEvent &other) const {
3382
return m_progress_id == other.m_progress_id &&
3483
m_event_type == other.m_event_type &&
3584
m_percentage == other.m_percentage;
3685
}
3786

38-
const char *ProgressEvent::GetEventName() const {
87+
ProgressEventType ProgressEvent::GetEventType() const { return m_event_type; }
88+
89+
StringRef ProgressEvent::GetEventName() const {
3990
if (m_event_type == progressStart)
4091
return "progressStart";
4192
else if (m_event_type == progressEnd)
4293
return "progressEnd";
43-
else if (m_event_type == progressUpdate)
44-
return "progressUpdate";
4594
else
46-
return "progressInvalid";
95+
return "progressUpdate";
4796
}
4897

49-
bool ProgressEvent::IsValid() const { return m_event_type != progressInvalid; }
50-
51-
uint64_t ProgressEvent::GetID() const { return m_progress_id; }
52-
5398
json::Value ProgressEvent::ToJSON() const {
5499
llvm::json::Object event(CreateEventObject(GetEventName()));
55100
llvm::json::Object body;
@@ -65,9 +110,7 @@ json::Value ProgressEvent::ToJSON() const {
65110
body.try_emplace("cancellable", false);
66111
}
67112

68-
auto now = std::chrono::duration<double>(
69-
std::chrono::system_clock::now().time_since_epoch());
70-
std::string timestamp(llvm::formatv("{0:f9}", now.count()));
113+
std::string timestamp(llvm::formatv("{0:f9}", m_creation_time.count()));
71114
EmplaceSafeString(body, "timestamp", timestamp);
72115

73116
if (m_percentage)
@@ -77,17 +120,108 @@ json::Value ProgressEvent::ToJSON() const {
77120
return json::Value(std::move(event));
78121
}
79122

80-
ProgressEventFilterQueue::ProgressEventFilterQueue(
81-
std::function<void(ProgressEvent)> callback)
82-
: m_callback(callback) {}
123+
bool ProgressEvent::Report(ProgressEventReportCallback callback) {
124+
if (Reported())
125+
return true;
126+
if (std::chrono::system_clock::now().time_since_epoch() <
127+
m_minimum_allowed_report_time)
128+
return false;
129+
130+
m_reported = true;
131+
callback(*this);
132+
return true;
133+
}
134+
135+
bool ProgressEvent::Reported() const { return m_reported; }
136+
137+
ProgressEventManager::ProgressEventManager(
138+
const ProgressEvent &start_event,
139+
ProgressEventReportCallback report_callback)
140+
: m_start_event(start_event), m_finished(false),
141+
m_report_callback(report_callback) {}
142+
143+
bool ProgressEventManager::ReportIfNeeded() {
144+
// The event finished before we were able to report it.
145+
if (!m_start_event.Reported() && Finished())
146+
return true;
147+
148+
if (!m_start_event.Report(m_report_callback))
149+
return false;
150+
151+
if (m_last_update_event)
152+
m_last_update_event->Report(m_report_callback);
153+
return true;
154+
}
155+
156+
const ProgressEvent &ProgressEventManager::GetMostRecentEvent() const {
157+
return m_last_update_event ? *m_last_update_event : m_start_event;
158+
}
159+
160+
void ProgressEventManager::Update(uint64_t progress_id, uint64_t completed,
161+
uint64_t total) {
162+
if (Optional<ProgressEvent> event = ProgressEvent::Create(
163+
progress_id, None, completed, total, &GetMostRecentEvent())) {
164+
if (event->GetEventType() == progressEnd)
165+
m_finished = true;
166+
167+
m_last_update_event = *event;
168+
ReportIfNeeded();
169+
}
170+
}
171+
172+
bool ProgressEventManager::Finished() const { return m_finished; }
173+
174+
ProgressEventReporter::ProgressEventReporter(
175+
ProgressEventReportCallback report_callback)
176+
: m_report_callback(report_callback) {
177+
m_thread_should_exit = false;
178+
m_thread = std::thread([&] {
179+
while (!m_thread_should_exit) {
180+
std::this_thread::sleep_for(kUpdateProgressEventReportDelay);
181+
ReportStartEvents();
182+
}
183+
});
184+
}
185+
186+
ProgressEventReporter::~ProgressEventReporter() {
187+
m_thread_should_exit = true;
188+
m_thread.join();
189+
}
83190

84-
void ProgressEventFilterQueue::Push(const ProgressEvent &event) {
85-
if (!event.IsValid())
86-
return;
191+
void ProgressEventReporter::ReportStartEvents() {
192+
std::lock_guard<std::mutex> locker(m_mutex);
193+
194+
while (!m_unreported_start_events.empty()) {
195+
ProgressEventManagerSP event_manager = m_unreported_start_events.front();
196+
if (event_manager->Finished())
197+
m_unreported_start_events.pop();
198+
else if (event_manager->ReportIfNeeded())
199+
m_unreported_start_events
200+
.pop(); // we remove it from the queue as it started reporting
201+
// already, the Push method will be able to continue its
202+
// reports.
203+
else
204+
break; // If we couldn't report it, then the next event in the queue won't
205+
// be able as well, as it came later.
206+
}
207+
}
87208

88-
auto it = m_last_events.find(event.GetID());
89-
if (it == m_last_events.end() || !(it->second == event)) {
90-
m_last_events[event.GetID()] = event;
91-
m_callback(event);
209+
void ProgressEventReporter::Push(uint64_t progress_id, const char *message,
210+
uint64_t completed, uint64_t total) {
211+
std::lock_guard<std::mutex> locker(m_mutex);
212+
213+
auto it = m_event_managers.find(progress_id);
214+
if (it == m_event_managers.end()) {
215+
if (Optional<ProgressEvent> event =
216+
ProgressEvent::Create(progress_id, StringRef(message), completed, total)) {
217+
ProgressEventManagerSP event_manager =
218+
std::make_shared<ProgressEventManager>(*event, m_report_callback);
219+
m_event_managers.insert({progress_id, event_manager});
220+
m_unreported_start_events.push(event_manager);
221+
}
222+
} else {
223+
it->second->Update(progress_id, completed, total);
224+
if (it->second->Finished())
225+
m_event_managers.erase(it);
92226
}
93227
}

0 commit comments

Comments
 (0)