1//===-- Progress.h ----------------------------------------------*- C++ -*-===//
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#ifndef LLDB_CORE_PROGRESS_H
10#define LLDB_CORE_PROGRESS_H
11
12#include "lldb/Host/Alarm.h"
13#include "lldb/lldb-forward.h"
14#include "lldb/lldb-types.h"
15#include "llvm/ADT/StringMap.h"
16#include <atomic>
17#include <cstdint>
18#include <mutex>
19#include <optional>
20
21namespace lldb_private {
22
23/// A Progress indicator helper class.
24///
25/// Any potentially long running sections of code in LLDB should report
26/// progress so that clients are aware of delays that might appear during
27/// debugging. Delays commonly include indexing debug information, parsing
28/// symbol tables for object files, downloading symbols from remote
29/// repositories, and many more things.
30///
31/// The Progress class helps make sure that progress is correctly reported
32/// and will always send an initial progress update, updates when
33/// Progress::Increment() is called, and also will make sure that a progress
34/// completed update is reported even if the user doesn't explicitly cause one
35/// to be sent.
36///
37/// The progress is reported via a callback whose type is ProgressCallback:
38///
39/// typedef void (*ProgressCallback)(uint64_t progress_id,
40/// const char *message,
41/// uint64_t completed,
42/// uint64_t total,
43/// void *baton);
44///
45/// This callback will always initially be called with "completed" set to zero
46/// and "total" set to the total amount specified in the contructor. This is
47/// considered the progress start event. As Progress::Increment() is called,
48/// the callback will be called as long as the Progress::m_completed has not
49/// yet exceeded the Progress::m_total. When the callback is called with
50/// Progress::m_completed == Progress::m_total, that is considered a progress
51/// completed event. If Progress::m_completed is non-zero and less than
52/// Progress::m_total, then this is considered a progress update event.
53///
54/// This callback will be called in the destructor if Progress::m_completed is
55/// not equal to Progress::m_total with the "completed" set to
56/// Progress::m_total. This ensures we always send a progress completed update
57/// even if the user does not.
58
59class Progress {
60public:
61 /// Construct a progress object that will report information.
62 ///
63 /// The constructor will create a unique progress reporting object and
64 /// immediately send out a progress update by calling the installed callback
65 /// with completed set to zero out of the specified total.
66 ///
67 /// @param [in] title The title of this progress activity.
68 ///
69 /// @param [in] details Specific information about what the progress report
70 /// is currently working on. Although not required, if the progress report is
71 /// updated with Progress::Increment() then this field will be overwritten
72 /// with the new set of details passed into that function, and the details
73 /// passed initially will act as an "item 0" for the total set of
74 /// items being reported on.
75 ///
76 /// @param [in] total The total units of work to be done if specified, if
77 /// set to std::nullopt then an indeterminate progress indicator should be
78 /// displayed.
79 ///
80 /// @param [in] debugger An optional debugger pointer to specify that this
81 /// progress is to be reported only to specific debuggers.
82 Progress(std::string title, std::string details = {},
83 std::optional<uint64_t> total = std::nullopt,
84 lldb_private::Debugger *debugger = nullptr);
85
86 /// Destroy the progress object.
87 ///
88 /// If the progress has not yet sent a completion update, the destructor
89 /// will send out a notification where the completed == m_total. This ensures
90 /// that we always send out a progress complete notification.
91 ~Progress();
92
93 /// Increment the progress and send a notification to the intalled callback.
94 ///
95 /// If incrementing ends up exceeding m_total, m_completed will be updated
96 /// to match m_total and no subsequent progress notifications will be sent.
97 /// If no total was specified in the constructor, this function will not do
98 /// anything nor send any progress updates.
99 ///
100 /// @param [in] amount The amount to increment m_completed by.
101 ///
102 /// @param [in] an optional message associated with this update.
103 void Increment(uint64_t amount = 1,
104 std::optional<std::string> updated_detail = {});
105
106 /// Used to indicate a non-deterministic progress report
107 static constexpr uint64_t kNonDeterministicTotal = UINT64_MAX;
108
109 /// Data belonging to this Progress event that is used for bookkeeping by
110 /// ProgressManager.
111 struct ProgressData {
112 /// The title of the progress activity, also used as a category.
113 std::string title;
114 /// A unique integer identifier for progress reporting.
115 uint64_t progress_id;
116 /// The optional debugger ID to report progress to. If this has no value
117 /// then all debuggers will receive this event.
118 std::optional<lldb::user_id_t> debugger_id;
119 };
120
121private:
122 void ReportProgress();
123 static std::atomic<uint64_t> g_id;
124 /// More specific information about the current file being displayed in the
125 /// report.
126 std::string m_details;
127 /// How much work ([0...m_total]) that has been completed.
128 uint64_t m_completed;
129 /// Total amount of work, use a std::nullopt in the constructor for non
130 /// deterministic progress.
131 uint64_t m_total;
132 std::mutex m_mutex;
133 /// Set to true when progress has been reported where m_completed == m_total
134 /// to ensure that we don't send progress updates after progress has
135 /// completed.
136 bool m_complete = false;
137 /// Data needed by the debugger to broadcast a progress event.
138 ProgressData m_progress_data;
139};
140
141/// A class used to group progress reports by category. This is done by using a
142/// map that maintains a refcount of each category of progress reports that have
143/// come in. Keeping track of progress reports this way will be done if a
144/// debugger is listening to the eBroadcastBitProgressByCategory broadcast bit.
145class ProgressManager {
146public:
147 ProgressManager();
148 ~ProgressManager();
149
150 /// Control the refcount of the progress report category as needed.
151 void Increment(const Progress::ProgressData &);
152 void Decrement(const Progress::ProgressData &);
153
154 static void Initialize();
155 static void Terminate();
156 static bool Enabled();
157 static ProgressManager &Instance();
158
159protected:
160 enum class EventType {
161 Begin,
162 End,
163 };
164 static void ReportProgress(const Progress::ProgressData &progress_data,
165 EventType type);
166
167 static std::optional<ProgressManager> &InstanceImpl();
168
169 /// Helper function for reporting progress when the alarm in the corresponding
170 /// entry in the map expires.
171 void Expire(llvm::StringRef key);
172
173 /// Entry used for bookkeeping.
174 struct Entry {
175 /// Reference count used for overlapping events.
176 uint64_t refcount = 0;
177
178 /// Data used to emit progress events.
179 Progress::ProgressData data;
180
181 /// Alarm handle used when the refcount reaches zero.
182 Alarm::Handle handle = Alarm::INVALID_HANDLE;
183 };
184
185 /// Map used for bookkeeping.
186 llvm::StringMap<Entry> m_entries;
187
188 /// Mutex to provide the map.
189 std::mutex m_entries_mutex;
190
191 /// Alarm instance to coalesce progress events.
192 Alarm m_alarm;
193};
194
195} // namespace lldb_private
196
197#endif // LLDB_CORE_PROGRESS_H
198

source code of lldb/include/lldb/Core/Progress.h