1//===-- Progress.cpp ------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Core/Progress.h"
10
11#include "lldb/Core/Debugger.h"
12#include "lldb/Utility/StreamString.h"
13#include "llvm/Support/Signposts.h"
14#include <atomic>
15#include <chrono>
16#include <cstdint>
17#include <mutex>
18#include <optional>
19
20using namespace lldb;
21using namespace lldb_private;
22
23std::atomic<uint64_t> Progress::g_id(0);
24
25// Instrument progress events with signposts when supported.
26static llvm::ManagedStatic<llvm::SignpostEmitter> g_progress_signposts;
27
28Progress::Progress(std::string title, std::string details,
29 std::optional<uint64_t> total,
30 lldb_private::Debugger *debugger,
31 Timeout<std::nano> minimum_report_time,
32 Progress::Origin origin)
33 : m_total(total.value_or(u: Progress::kNonDeterministicTotal)),
34 m_minimum_report_time(minimum_report_time), m_title(title),
35 m_progress_id(++g_id),
36 m_debugger_id(debugger ? std::optional<user_id_t>(debugger->GetID())
37 : std::nullopt),
38 m_origin(origin),
39 m_last_report_time_ns(
40 std::chrono::nanoseconds(
41 std::chrono::steady_clock::now().time_since_epoch())
42 .count()),
43 m_details(std::move(details)) {
44 std::lock_guard<std::mutex> guard(m_mutex);
45 ReportProgress();
46
47 // Start signpost interval right before the meaningful work starts.
48 g_progress_signposts->startInterval(O: this, Name: m_title);
49}
50
51Progress::~Progress() {
52 // End signpost interval as soon as possible.
53 g_progress_signposts->endInterval(O: this, Name: m_title);
54
55 // Make sure to always report progress completed when this object is
56 // destructed so it indicates the progress dialog/activity should go away.
57 std::lock_guard<std::mutex> guard(m_mutex);
58 m_completed = m_total;
59 ReportProgress();
60}
61
62void Progress::Increment(uint64_t amount,
63 std::optional<std::string> updated_detail) {
64 if (amount == 0)
65 return;
66
67 m_completed.fetch_add(i: amount, m: std::memory_order_relaxed);
68
69 if (m_minimum_report_time) {
70 using namespace std::chrono;
71
72 nanoseconds now;
73 uint64_t last_report_time_ns =
74 m_last_report_time_ns.load(m: std::memory_order_relaxed);
75
76 do {
77 now = steady_clock::now().time_since_epoch();
78 if (now < nanoseconds(last_report_time_ns) + *m_minimum_report_time)
79 return; // Too little time has passed since the last report.
80
81 } while (!m_last_report_time_ns.compare_exchange_weak(
82 i1&: last_report_time_ns, i2: now.count(), m1: std::memory_order_relaxed,
83 m2: std::memory_order_relaxed));
84 }
85
86 std::lock_guard<std::mutex> guard(m_mutex);
87 if (updated_detail)
88 m_details = std::move(updated_detail.value());
89 ReportProgress();
90}
91
92void Progress::ReportProgress() {
93 // NB: Comparisons with optional<T> rely on the fact that std::nullopt is
94 // "smaller" than zero.
95 if (m_prev_completed >= m_total)
96 return; // We've reported completion already.
97
98 uint64_t completed =
99 std::min(a: m_completed.load(m: std::memory_order_relaxed), b: m_total);
100 if (completed < m_prev_completed)
101 return; // An overflow in the m_completed counter. Just ignore these events.
102
103 // Change the category bit if we're an internal or external progress.
104 uint32_t progress_category_bit = m_origin == Progress::Origin::eExternal
105 ? lldb::eBroadcastBitExternalProgress
106 : lldb::eBroadcastBitProgress;
107
108 Debugger::ReportProgress(progress_id: m_progress_id, title: m_title, details: m_details, completed,
109 total: m_total, debugger_id: m_debugger_id, progress_category_bit);
110 m_prev_completed = completed;
111}
112

source code of lldb/source/Core/Progress.cpp