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