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