2021-03-01 15:26:35 -08:00
|
|
|
//===-- Progress.cpp ------------------------------------------------------===//
|
|
|
|
|
//
|
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
#include "lldb/Core/Progress.h"
|
|
|
|
|
|
|
|
|
|
#include "lldb/Core/Debugger.h"
|
|
|
|
|
#include "lldb/Utility/StreamString.h"
|
2024-09-13 13:42:55 -07:00
|
|
|
#include "llvm/Support/Signposts.h"
|
2024-12-16 11:35:43 +01:00
|
|
|
#include <atomic>
|
|
|
|
|
#include <chrono>
|
2024-03-01 10:56:45 -08:00
|
|
|
#include <cstdint>
|
2024-02-15 15:46:47 -08:00
|
|
|
#include <mutex>
|
2024-01-16 07:57:18 -08:00
|
|
|
#include <optional>
|
|
|
|
|
|
2021-03-01 15:26:35 -08:00
|
|
|
using namespace lldb;
|
|
|
|
|
using namespace lldb_private;
|
|
|
|
|
|
|
|
|
|
std::atomic<uint64_t> Progress::g_id(0);
|
|
|
|
|
|
2024-09-13 13:42:55 -07:00
|
|
|
// Instrument progress events with signposts when supported.
|
|
|
|
|
static llvm::ManagedStatic<llvm::SignpostEmitter> g_progress_signposts;
|
|
|
|
|
|
2024-01-16 07:57:18 -08:00
|
|
|
Progress::Progress(std::string title, std::string details,
|
|
|
|
|
std::optional<uint64_t> total,
|
2024-12-16 11:35:43 +01:00
|
|
|
lldb_private::Debugger *debugger,
|
2025-01-06 12:49:15 -08:00
|
|
|
Timeout<std::nano> minimum_report_time,
|
|
|
|
|
Progress::Origin origin)
|
2024-12-16 11:35:43 +01:00
|
|
|
: m_total(total.value_or(Progress::kNonDeterministicTotal)),
|
2025-03-08 11:10:46 -06:00
|
|
|
m_minimum_report_time(minimum_report_time), m_title(title),
|
|
|
|
|
m_progress_id(++g_id),
|
|
|
|
|
m_debugger_id(debugger ? std::optional<user_id_t>(debugger->GetID())
|
|
|
|
|
: std::nullopt),
|
|
|
|
|
m_origin(origin),
|
2024-12-16 11:35:43 +01:00
|
|
|
m_last_report_time_ns(
|
|
|
|
|
std::chrono::nanoseconds(
|
|
|
|
|
std::chrono::steady_clock::now().time_since_epoch())
|
|
|
|
|
.count()),
|
|
|
|
|
m_details(std::move(details)) {
|
2023-06-16 09:09:41 +02:00
|
|
|
std::lock_guard<std::mutex> guard(m_mutex);
|
|
|
|
|
ReportProgress();
|
[lldb] Implement coalescing of disjoint progress events (#84854)
This implements coalescing of progress events using a timeout, as
discussed in the RFC on Discourse [1]. This PR consists of two commits
which, depending on the feedback, I may split up into two PRs. For now,
I think it's easier to review this as a whole.
1. The first commit introduces a new generic `Alarm` class. The class
lets you to schedule a function (callback) to be executed after a given
timeout expires. You can cancel and reset a callback before its
corresponding timeout expires. It achieves this with the help of a
worker thread that sleeps until the next timeout expires. The only
guarantee it provides is that your function is called no sooner than the
requested timeout. Because the callback is called directly from the
worker thread, a long running callback could potentially block the
worker thread. I intentionally kept the implementation as simple as
possible while addressing the needs for the `ProgressManager` use case.
If we want to rely on this somewhere else, we can reassess whether we
need to address those limitations.
2. The second commit uses the Alarm class to coalesce progress events.
To recap the Discourse discussion, when multiple progress events with
the same title execute in close succession, they get broadcast as one to
`eBroadcastBitProgressCategory`. The `ProgressManager` keeps track of
the in-flight progress events and when the refcount hits zero, the Alarm
class is used to schedule broadcasting the event. If a new progress
event comes in before the alarm fires, the alarm is reset (and the
process repeats when the new progress event ends). If no new event comes
in before the timeout expires, the progress event is broadcast.
[1]
https://discourse.llvm.org/t/rfc-improve-lldb-progress-reporting/75717/
2024-03-25 14:50:58 -07:00
|
|
|
|
2024-09-13 13:42:55 -07:00
|
|
|
// Start signpost interval right before the meaningful work starts.
|
2025-03-08 11:10:46 -06:00
|
|
|
g_progress_signposts->startInterval(this, m_title);
|
2021-03-01 15:26:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Progress::~Progress() {
|
2024-09-13 13:42:55 -07:00
|
|
|
// End signpost interval as soon as possible.
|
2025-03-08 11:10:46 -06:00
|
|
|
g_progress_signposts->endInterval(this, m_title);
|
2024-09-13 13:42:55 -07:00
|
|
|
|
2023-06-16 09:09:41 +02:00
|
|
|
// Make sure to always report progress completed when this object is
|
|
|
|
|
// destructed so it indicates the progress dialog/activity should go away.
|
|
|
|
|
std::lock_guard<std::mutex> guard(m_mutex);
|
2024-08-07 07:58:34 -07:00
|
|
|
m_completed = m_total;
|
2023-11-28 13:45:02 -05:00
|
|
|
ReportProgress();
|
2021-03-01 15:26:35 -08:00
|
|
|
}
|
|
|
|
|
|
2024-01-16 07:57:18 -08:00
|
|
|
void Progress::Increment(uint64_t amount,
|
|
|
|
|
std::optional<std::string> updated_detail) {
|
2024-12-16 11:35:43 +01:00
|
|
|
if (amount == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_completed.fetch_add(amount, std::memory_order_relaxed);
|
|
|
|
|
|
|
|
|
|
if (m_minimum_report_time) {
|
|
|
|
|
using namespace std::chrono;
|
|
|
|
|
|
|
|
|
|
nanoseconds now;
|
|
|
|
|
uint64_t last_report_time_ns =
|
|
|
|
|
m_last_report_time_ns.load(std::memory_order_relaxed);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
now = steady_clock::now().time_since_epoch();
|
|
|
|
|
if (now < nanoseconds(last_report_time_ns) + *m_minimum_report_time)
|
|
|
|
|
return; // Too little time has passed since the last report.
|
|
|
|
|
|
|
|
|
|
} while (!m_last_report_time_ns.compare_exchange_weak(
|
|
|
|
|
last_report_time_ns, now.count(), std::memory_order_relaxed,
|
|
|
|
|
std::memory_order_relaxed));
|
2021-03-01 15:26:35 -08:00
|
|
|
}
|
2024-12-16 11:35:43 +01:00
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> guard(m_mutex);
|
|
|
|
|
if (updated_detail)
|
|
|
|
|
m_details = std::move(updated_detail.value());
|
|
|
|
|
ReportProgress();
|
2021-03-01 15:26:35 -08:00
|
|
|
}
|
|
|
|
|
|
2024-01-16 07:57:18 -08:00
|
|
|
void Progress::ReportProgress() {
|
2024-12-16 11:35:43 +01:00
|
|
|
// NB: Comparisons with optional<T> rely on the fact that std::nullopt is
|
|
|
|
|
// "smaller" than zero.
|
|
|
|
|
if (m_prev_completed >= m_total)
|
|
|
|
|
return; // We've reported completion already.
|
|
|
|
|
|
|
|
|
|
uint64_t completed =
|
|
|
|
|
std::min(m_completed.load(std::memory_order_relaxed), m_total);
|
|
|
|
|
if (completed < m_prev_completed)
|
|
|
|
|
return; // An overflow in the m_completed counter. Just ignore these events.
|
|
|
|
|
|
2025-01-06 12:49:15 -08:00
|
|
|
// Change the category bit if we're an internal or external progress.
|
2025-03-08 11:10:46 -06:00
|
|
|
uint32_t progress_category_bit = m_origin == Progress::Origin::eExternal
|
|
|
|
|
? lldb::eBroadcastBitExternalProgress
|
|
|
|
|
: lldb::eBroadcastBitProgress;
|
[lldb] Implement coalescing of disjoint progress events (#84854)
This implements coalescing of progress events using a timeout, as
discussed in the RFC on Discourse [1]. This PR consists of two commits
which, depending on the feedback, I may split up into two PRs. For now,
I think it's easier to review this as a whole.
1. The first commit introduces a new generic `Alarm` class. The class
lets you to schedule a function (callback) to be executed after a given
timeout expires. You can cancel and reset a callback before its
corresponding timeout expires. It achieves this with the help of a
worker thread that sleeps until the next timeout expires. The only
guarantee it provides is that your function is called no sooner than the
requested timeout. Because the callback is called directly from the
worker thread, a long running callback could potentially block the
worker thread. I intentionally kept the implementation as simple as
possible while addressing the needs for the `ProgressManager` use case.
If we want to rely on this somewhere else, we can reassess whether we
need to address those limitations.
2. The second commit uses the Alarm class to coalesce progress events.
To recap the Discourse discussion, when multiple progress events with
the same title execute in close succession, they get broadcast as one to
`eBroadcastBitProgressCategory`. The `ProgressManager` keeps track of
the in-flight progress events and when the refcount hits zero, the Alarm
class is used to schedule broadcasting the event. If a new progress
event comes in before the alarm fires, the alarm is reset (and the
process repeats when the new progress event ends). If no new event comes
in before the timeout expires, the progress event is broadcast.
[1]
https://discourse.llvm.org/t/rfc-improve-lldb-progress-reporting/75717/
2024-03-25 14:50:58 -07:00
|
|
|
|
2025-03-08 11:10:46 -06:00
|
|
|
Debugger::ReportProgress(m_progress_id, m_title, m_details, completed,
|
|
|
|
|
m_total, m_debugger_id, progress_category_bit);
|
|
|
|
|
m_prev_completed = completed;
|
[lldb] Implement coalescing of disjoint progress events (#84854)
This implements coalescing of progress events using a timeout, as
discussed in the RFC on Discourse [1]. This PR consists of two commits
which, depending on the feedback, I may split up into two PRs. For now,
I think it's easier to review this as a whole.
1. The first commit introduces a new generic `Alarm` class. The class
lets you to schedule a function (callback) to be executed after a given
timeout expires. You can cancel and reset a callback before its
corresponding timeout expires. It achieves this with the help of a
worker thread that sleeps until the next timeout expires. The only
guarantee it provides is that your function is called no sooner than the
requested timeout. Because the callback is called directly from the
worker thread, a long running callback could potentially block the
worker thread. I intentionally kept the implementation as simple as
possible while addressing the needs for the `ProgressManager` use case.
If we want to rely on this somewhere else, we can reassess whether we
need to address those limitations.
2. The second commit uses the Alarm class to coalesce progress events.
To recap the Discourse discussion, when multiple progress events with
the same title execute in close succession, they get broadcast as one to
`eBroadcastBitProgressCategory`. The `ProgressManager` keeps track of
the in-flight progress events and when the refcount hits zero, the Alarm
class is used to schedule broadcasting the event. If a new progress
event comes in before the alarm fires, the alarm is reset (and the
process repeats when the new progress event ends). If no new event comes
in before the timeout expires, the progress event is broadcast.
[1]
https://discourse.llvm.org/t/rfc-improve-lldb-progress-reporting/75717/
2024-03-25 14:50:58 -07:00
|
|
|
}
|