xref: /freebsd/contrib/llvm-project/lldb/include/lldb/Core/Progress.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
21 namespace 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 \a completed set to zero
46 /// and \a total set to the total amount specified in the constructor. 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 \a completed set to
56 /// Progress::m_total. This ensures we always send a progress completed update
57 /// even if the user does not.
58 
59 class Progress {
60 public:
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 \a 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 \a completed == m_total. This
90   /// ensures that we always send out a progress complete notification.
91   ~Progress();
92 
93   /// Increment the progress and send a notification to the installed 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 
121 private:
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.
145 class ProgressManager {
146 public:
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 
159 protected:
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