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