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 "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 | |
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 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 | |
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 | |