xref: /freebsd/contrib/llvm-project/lldb/source/Core/Progress.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
20 using namespace lldb;
21 using namespace lldb_private;
22 
23 std::atomic<uint64_t> Progress::g_id(0);
24 
25 // Instrument progress events with signposts when supported.
26 static llvm::ManagedStatic<llvm::SignpostEmitter> g_progress_signposts;
27 
Progress(std::string title,std::string details,std::optional<uint64_t> total,lldb_private::Debugger * debugger,Timeout<std::nano> minimum_report_time,Progress::Origin origin)28 Progress::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(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(this, m_title);
49 }
50 
~Progress()51 Progress::~Progress() {
52   // End signpost interval as soon as possible.
53   g_progress_signposts->endInterval(this, 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 
Increment(uint64_t amount,std::optional<std::string> updated_detail)62 void Progress::Increment(uint64_t amount,
63                          std::optional<std::string> updated_detail) {
64   if (amount == 0)
65     return;
66 
67   m_completed.fetch_add(amount, 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(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         last_report_time_ns, now.count(), std::memory_order_relaxed,
83         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 
ReportProgress()92 void 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(m_completed.load(std::memory_order_relaxed), 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(m_progress_id, m_title, m_details, completed,
109                            m_total, m_debugger_id, progress_category_bit);
110   m_prev_completed = completed;
111 }
112