1 //===-- SBQueue.cpp -------------------------------------------------------===//
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 #include <cinttypes>
10 
11 #include "lldb/Utility/ReproducerInstrumentation.h"
12 #include "lldb/API/SBQueue.h"
13 
14 #include "lldb/API/SBProcess.h"
15 #include "lldb/API/SBQueueItem.h"
16 #include "lldb/API/SBThread.h"
17 
18 #include "lldb/Target/Process.h"
19 #include "lldb/Target/Queue.h"
20 #include "lldb/Target/QueueItem.h"
21 #include "lldb/Target/Thread.h"
22 
23 using namespace lldb;
24 using namespace lldb_private;
25 
26 namespace lldb_private {
27 
28 class QueueImpl {
29 public:
30   QueueImpl() {}
31 
32   QueueImpl(const lldb::QueueSP &queue_sp)
33       : m_thread_list_fetched(false), m_pending_items_fetched(false) {
34     m_queue_wp = queue_sp;
35   }
36 
37   QueueImpl(const QueueImpl &rhs) {
38     if (&rhs == this)
39       return;
40     m_queue_wp = rhs.m_queue_wp;
41     m_threads = rhs.m_threads;
42     m_thread_list_fetched = rhs.m_thread_list_fetched;
43     m_pending_items = rhs.m_pending_items;
44     m_pending_items_fetched = rhs.m_pending_items_fetched;
45   }
46 
47   ~QueueImpl() = default;
48 
49   bool IsValid() { return m_queue_wp.lock() != nullptr; }
50 
51   void Clear() {
52     m_queue_wp.reset();
53     m_thread_list_fetched = false;
54     m_threads.clear();
55     m_pending_items_fetched = false;
56     m_pending_items.clear();
57   }
58 
59   void SetQueue(const lldb::QueueSP &queue_sp) {
60     Clear();
61     m_queue_wp = queue_sp;
62   }
63 
64   lldb::queue_id_t GetQueueID() const {
65     lldb::queue_id_t result = LLDB_INVALID_QUEUE_ID;
66     lldb::QueueSP queue_sp = m_queue_wp.lock();
67     if (queue_sp) {
68       result = queue_sp->GetID();
69     }
70     return result;
71   }
72 
73   uint32_t GetIndexID() const {
74     uint32_t result = LLDB_INVALID_INDEX32;
75     lldb::QueueSP queue_sp = m_queue_wp.lock();
76     if (queue_sp) {
77       result = queue_sp->GetIndexID();
78     }
79     return result;
80   }
81 
82   const char *GetName() const {
83     const char *name = nullptr;
84     lldb::QueueSP queue_sp = m_queue_wp.lock();
85     if (queue_sp.get()) {
86       name = queue_sp->GetName();
87     }
88     return name;
89   }
90 
91   void FetchThreads() {
92     if (!m_thread_list_fetched) {
93       lldb::QueueSP queue_sp = m_queue_wp.lock();
94       if (queue_sp) {
95         Process::StopLocker stop_locker;
96         if (stop_locker.TryLock(&queue_sp->GetProcess()->GetRunLock())) {
97           const std::vector<ThreadSP> thread_list(queue_sp->GetThreads());
98           m_thread_list_fetched = true;
99           const uint32_t num_threads = thread_list.size();
100           for (uint32_t idx = 0; idx < num_threads; ++idx) {
101             ThreadSP thread_sp = thread_list[idx];
102             if (thread_sp && thread_sp->IsValid()) {
103               m_threads.push_back(thread_sp);
104             }
105           }
106         }
107       }
108     }
109   }
110 
111   void FetchItems() {
112     if (!m_pending_items_fetched) {
113       QueueSP queue_sp = m_queue_wp.lock();
114       if (queue_sp) {
115         Process::StopLocker stop_locker;
116         if (stop_locker.TryLock(&queue_sp->GetProcess()->GetRunLock())) {
117           const std::vector<QueueItemSP> queue_items(
118               queue_sp->GetPendingItems());
119           m_pending_items_fetched = true;
120           const uint32_t num_pending_items = queue_items.size();
121           for (uint32_t idx = 0; idx < num_pending_items; ++idx) {
122             QueueItemSP item = queue_items[idx];
123             if (item && item->IsValid()) {
124               m_pending_items.push_back(item);
125             }
126           }
127         }
128       }
129     }
130   }
131 
132   uint32_t GetNumThreads() {
133     uint32_t result = 0;
134 
135     FetchThreads();
136     if (m_thread_list_fetched) {
137       result = m_threads.size();
138     }
139     return result;
140   }
141 
142   lldb::SBThread GetThreadAtIndex(uint32_t idx) {
143     FetchThreads();
144 
145     SBThread sb_thread;
146     QueueSP queue_sp = m_queue_wp.lock();
147     if (queue_sp && idx < m_threads.size()) {
148       ProcessSP process_sp = queue_sp->GetProcess();
149       if (process_sp) {
150         ThreadSP thread_sp = m_threads[idx].lock();
151         if (thread_sp) {
152           sb_thread.SetThread(thread_sp);
153         }
154       }
155     }
156     return sb_thread;
157   }
158 
159   uint32_t GetNumPendingItems() {
160     uint32_t result = 0;
161 
162     QueueSP queue_sp = m_queue_wp.lock();
163     if (!m_pending_items_fetched && queue_sp) {
164       result = queue_sp->GetNumPendingWorkItems();
165     } else {
166       result = m_pending_items.size();
167     }
168     return result;
169   }
170 
171   lldb::SBQueueItem GetPendingItemAtIndex(uint32_t idx) {
172     SBQueueItem result;
173     FetchItems();
174     if (m_pending_items_fetched && idx < m_pending_items.size()) {
175       result.SetQueueItem(m_pending_items[idx]);
176     }
177     return result;
178   }
179 
180   uint32_t GetNumRunningItems() {
181     uint32_t result = 0;
182     QueueSP queue_sp = m_queue_wp.lock();
183     if (queue_sp)
184       result = queue_sp->GetNumRunningWorkItems();
185     return result;
186   }
187 
188   lldb::SBProcess GetProcess() {
189     SBProcess result;
190     QueueSP queue_sp = m_queue_wp.lock();
191     if (queue_sp) {
192       result.SetSP(queue_sp->GetProcess());
193     }
194     return result;
195   }
196 
197   lldb::QueueKind GetKind() {
198     lldb::QueueKind kind = eQueueKindUnknown;
199     QueueSP queue_sp = m_queue_wp.lock();
200     if (queue_sp)
201       kind = queue_sp->GetKind();
202 
203     return kind;
204   }
205 
206 private:
207   lldb::QueueWP m_queue_wp;
208   std::vector<lldb::ThreadWP>
209       m_threads; // threads currently executing this queue's items
210   bool m_thread_list_fetched =
211       false; // have we tried to fetch the threads list already?
212   std::vector<lldb::QueueItemSP> m_pending_items; // items currently enqueued
213   bool m_pending_items_fetched =
214       false; // have we tried to fetch the item list already?
215 };
216 }
217 
218 SBQueue::SBQueue() : m_opaque_sp(new QueueImpl()) {
219   LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBQueue);
220 }
221 
222 SBQueue::SBQueue(const QueueSP &queue_sp)
223     : m_opaque_sp(new QueueImpl(queue_sp)) {
224   LLDB_RECORD_CONSTRUCTOR(SBQueue, (const lldb::QueueSP &), queue_sp);
225 }
226 
227 SBQueue::SBQueue(const SBQueue &rhs) {
228   LLDB_RECORD_CONSTRUCTOR(SBQueue, (const lldb::SBQueue &), rhs);
229 
230   if (&rhs == this)
231     return;
232 
233   m_opaque_sp = rhs.m_opaque_sp;
234 }
235 
236 const lldb::SBQueue &SBQueue::operator=(const lldb::SBQueue &rhs) {
237   LLDB_RECORD_METHOD(const lldb::SBQueue &,
238                      SBQueue, operator=,(const lldb::SBQueue &), rhs);
239 
240   m_opaque_sp = rhs.m_opaque_sp;
241   return *this;
242 }
243 
244 SBQueue::~SBQueue() = default;
245 
246 bool SBQueue::IsValid() const {
247   LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBQueue, IsValid);
248   return this->operator bool();
249 }
250 SBQueue::operator bool() const {
251   LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBQueue, operator bool);
252 
253   return m_opaque_sp->IsValid();
254 }
255 
256 void SBQueue::Clear() {
257   LLDB_RECORD_METHOD_NO_ARGS(void, SBQueue, Clear);
258 
259   m_opaque_sp->Clear();
260 }
261 
262 void SBQueue::SetQueue(const QueueSP &queue_sp) {
263   m_opaque_sp->SetQueue(queue_sp);
264 }
265 
266 lldb::queue_id_t SBQueue::GetQueueID() const {
267   LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::queue_id_t, SBQueue, GetQueueID);
268 
269   return m_opaque_sp->GetQueueID();
270 }
271 
272 uint32_t SBQueue::GetIndexID() const {
273   LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBQueue, GetIndexID);
274 
275   uint32_t index_id = m_opaque_sp->GetIndexID();
276   return index_id;
277 }
278 
279 const char *SBQueue::GetName() const {
280   LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBQueue, GetName);
281 
282   return m_opaque_sp->GetName();
283 }
284 
285 uint32_t SBQueue::GetNumThreads() {
286   LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBQueue, GetNumThreads);
287 
288   return m_opaque_sp->GetNumThreads();
289 }
290 
291 SBThread SBQueue::GetThreadAtIndex(uint32_t idx) {
292   LLDB_RECORD_METHOD(lldb::SBThread, SBQueue, GetThreadAtIndex, (uint32_t),
293                      idx);
294 
295   SBThread th = m_opaque_sp->GetThreadAtIndex(idx);
296   return th;
297 }
298 
299 uint32_t SBQueue::GetNumPendingItems() {
300   LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBQueue, GetNumPendingItems);
301 
302   return m_opaque_sp->GetNumPendingItems();
303 }
304 
305 SBQueueItem SBQueue::GetPendingItemAtIndex(uint32_t idx) {
306   LLDB_RECORD_METHOD(lldb::SBQueueItem, SBQueue, GetPendingItemAtIndex,
307                      (uint32_t), idx);
308 
309   return m_opaque_sp->GetPendingItemAtIndex(idx);
310 }
311 
312 uint32_t SBQueue::GetNumRunningItems() {
313   LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBQueue, GetNumRunningItems);
314 
315   return m_opaque_sp->GetNumRunningItems();
316 }
317 
318 SBProcess SBQueue::GetProcess() {
319   LLDB_RECORD_METHOD_NO_ARGS(lldb::SBProcess, SBQueue, GetProcess);
320 
321   return m_opaque_sp->GetProcess();
322 }
323 
324 lldb::QueueKind SBQueue::GetKind() {
325   LLDB_RECORD_METHOD_NO_ARGS(lldb::QueueKind, SBQueue, GetKind);
326 
327   return m_opaque_sp->GetKind();
328 }
329