Coverage Report

Created: 2023-11-11 10:31

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Target/ThreadPlanStack.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- ThreadPlanStack.cpp -------------------------------------*- 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
#include "lldb/Target/ThreadPlanStack.h"
10
#include "lldb/Target/Process.h"
11
#include "lldb/Target/Target.h"
12
#include "lldb/Target/Thread.h"
13
#include "lldb/Target/ThreadPlan.h"
14
#include "lldb/Utility/Log.h"
15
16
using namespace lldb;
17
using namespace lldb_private;
18
19
static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan,
20
                             lldb::DescriptionLevel desc_level,
21
18
                             int32_t elem_idx) {
22
18
  s.IndentMore();
23
18
  s.Indent();
24
18
  s.Printf("Element %d: ", elem_idx);
25
18
  plan->GetDescription(&s, desc_level);
26
18
  s.EOL();
27
18
  s.IndentLess();
28
18
}
29
30
2.84k
ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) {
31
2.84k
  if (make_null) {
32
    // The ThreadPlanNull doesn't do anything to the Thread, so this is actually
33
    // still a const operation.
34
10
    m_plans.push_back(
35
10
        ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread))));
36
10
  }
37
2.84k
}
38
39
void ThreadPlanStack::DumpThreadPlans(Stream &s,
40
                                      lldb::DescriptionLevel desc_level,
41
8
                                      bool include_internal) const {
42
8
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
43
8
  s.IndentMore();
44
8
  PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal);
45
8
  PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level,
46
8
                include_internal);
47
8
  PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level,
48
8
                include_internal);
49
8
  s.IndentLess();
50
8
}
51
52
void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name,
53
                                    const PlanStack &stack,
54
                                    lldb::DescriptionLevel desc_level,
55
24
                                    bool include_internal) const {
56
24
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
57
  // If the stack is empty, just exit:
58
24
  if (stack.empty())
59
14
    return;
60
61
  // Make sure there are public completed plans:
62
10
  bool any_public = false;
63
10
  if (!include_internal) {
64
12
    for (auto plan : stack) {
65
12
      if (!plan->GetPrivate()) {
66
9
        any_public = true;
67
9
        break;
68
9
      }
69
12
    }
70
9
  }
71
72
10
  if (include_internal || 
any_public9
) {
73
10
    int print_idx = 0;
74
10
    s.Indent();
75
10
    s << stack_name << ":\n";
76
27
    for (auto plan : stack) {
77
27
      if (!include_internal && 
plan->GetPrivate()23
)
78
9
        continue;
79
18
      PrintPlanElement(s, plan, desc_level, print_idx++);
80
18
    }
81
10
  }
82
10
}
83
84
3.36k
size_t ThreadPlanStack::CheckpointCompletedPlans() {
85
3.36k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
86
3.36k
  m_completed_plan_checkpoint++;
87
3.36k
  m_completed_plan_store.insert(
88
3.36k
      std::make_pair(m_completed_plan_checkpoint, m_completed_plans));
89
3.36k
  return m_completed_plan_checkpoint;
90
3.36k
}
91
92
3.32k
void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) {
93
3.32k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
94
3.32k
  auto result = m_completed_plan_store.find(checkpoint);
95
3.32k
  assert(result != m_completed_plan_store.end() &&
96
3.32k
         "Asked for a checkpoint that didn't exist");
97
3.32k
  m_completed_plans.swap((*result).second);
98
3.32k
  m_completed_plan_store.erase(result);
99
3.32k
}
100
101
0
void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) {
102
0
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
103
0
  m_completed_plan_store.erase(checkpoint);
104
0
}
105
106
2.83k
void ThreadPlanStack::ThreadDestroyed(Thread *thread) {
107
  // Tell the plan stacks that this thread is going away:
108
2.83k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
109
2.83k
  for (ThreadPlanSP plan : m_plans)
110
2.84k
    plan->ThreadDestroyed();
111
112
2.83k
  for (ThreadPlanSP plan : m_discarded_plans)
113
71
    plan->ThreadDestroyed();
114
115
2.83k
  for (ThreadPlanSP plan : m_completed_plans)
116
191
    plan->ThreadDestroyed();
117
118
  // Now clear the current plan stacks:
119
2.83k
  m_plans.clear();
120
2.83k
  m_discarded_plans.clear();
121
2.83k
  m_completed_plans.clear();
122
123
  // Push a ThreadPlanNull on the plan stack.  That way we can continue
124
  // assuming that the plan stack is never empty, but if somebody errantly asks
125
  // questions of a destroyed thread without checking first whether it is
126
  // destroyed, they won't crash.
127
2.83k
  if (thread != nullptr) {
128
0
    lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
129
0
    m_plans.push_back(null_plan_sp);
130
0
  }
131
2.83k
}
132
133
14.2k
void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) {
134
  // If the thread plan doesn't already have a tracer, give it its parent's
135
  // tracer:
136
  // The first plan has to be a base plan:
137
14.2k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
138
14.2k
  assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) &&
139
14.2k
         "Zeroth plan must be a base plan");
140
141
14.2k
  if (!new_plan_sp->GetThreadPlanTracer()) {
142
11.4k
    assert(!m_plans.empty());
143
11.4k
    new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());
144
11.4k
  }
145
14.2k
  m_plans.push_back(new_plan_sp);
146
14.2k
  new_plan_sp->DidPush();
147
14.2k
}
148
149
11.3k
lldb::ThreadPlanSP ThreadPlanStack::PopPlan() {
150
11.3k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
151
11.3k
  assert(m_plans.size() > 1 && "Can't pop the base thread plan");
152
153
  // Note that moving the top element of the vector would leave it in an
154
  // undefined state, and break the guarantee that the stack's thread plans are
155
  // all valid.
156
11.3k
  lldb::ThreadPlanSP plan_sp = m_plans.back();
157
11.3k
  m_plans.pop_back();
158
11.3k
  m_completed_plans.push_back(plan_sp);
159
11.3k
  plan_sp->DidPop();
160
11.3k
  return plan_sp;
161
11.3k
}
162
163
105
lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() {
164
105
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
165
105
  assert(m_plans.size() > 1 && "Can't discard the base thread plan");
166
167
  // Note that moving the top element of the vector would leave it in an
168
  // undefined state, and break the guarantee that the stack's thread plans are
169
  // all valid.
170
105
  lldb::ThreadPlanSP plan_sp = m_plans.back();
171
105
  m_plans.pop_back();
172
105
  m_discarded_plans.push_back(plan_sp);
173
105
  plan_sp->DidPop();
174
105
  return plan_sp;
175
105
}
176
177
// If the input plan is nullptr, discard all plans.  Otherwise make sure this
178
// plan is in the stack, and if so discard up to and including it.
179
3.34k
void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
180
3.34k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
181
3.34k
  int stack_size = m_plans.size();
182
183
3.34k
  if (up_to_plan_ptr == nullptr) {
184
0
    for (int i = stack_size - 1; i > 0; i--)
185
0
      DiscardPlan();
186
0
    return;
187
0
  }
188
189
3.34k
  bool found_it = false;
190
3.37k
  for (int i = stack_size - 1; i > 0; 
i--35
) {
191
48
    if (m_plans[i].get() == up_to_plan_ptr) {
192
13
      found_it = true;
193
13
      break;
194
13
    }
195
48
  }
196
197
3.34k
  if (found_it) {
198
13
    bool last_one = false;
199
35
    for (int i = stack_size - 1; i > 0 && 
!last_one24
;
i--22
) {
200
22
      if (GetCurrentPlan().get() == up_to_plan_ptr)
201
13
        last_one = true;
202
22
      DiscardPlan();
203
22
    }
204
13
  }
205
3.34k
}
206
207
7.31k
void ThreadPlanStack::DiscardAllPlans() {
208
7.31k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
209
7.31k
  int stack_size = m_plans.size();
210
7.37k
  for (int i = stack_size - 1; i > 0; 
i--61
) {
211
61
    DiscardPlan();
212
61
  }
213
7.31k
}
214
215
5.57k
void ThreadPlanStack::DiscardConsultingControllingPlans() {
216
5.57k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
217
5.57k
  while (true) {
218
5.57k
    int controlling_plan_idx;
219
5.57k
    bool discard = true;
220
221
    // Find the first controlling plan, see if it wants discarding, and if yes
222
    // discard up to it.
223
5.61k
    for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0;
224
5.61k
         
controlling_plan_idx--46
) {
225
5.61k
      if (m_plans[controlling_plan_idx]->IsControllingPlan()) {
226
5.57k
        discard = m_plans[controlling_plan_idx]->OkayToDiscard();
227
5.57k
        break;
228
5.57k
      }
229
5.61k
    }
230
231
    // If the controlling plan doesn't want to get discarded, then we're done.
232
5.57k
    if (!discard)
233
5.57k
      return;
234
235
    // First pop all the dependent plans:
236
0
    for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) {
237
0
      DiscardPlan();
238
0
    }
239
240
    // Now discard the controlling plan itself.
241
    // The bottom-most plan never gets discarded.  "OkayToDiscard" for it
242
    // means discard it's dependent plans, but not it...
243
0
    if (controlling_plan_idx > 0) {
244
0
      DiscardPlan();
245
0
    }
246
0
  }
247
5.57k
}
248
249
145k
lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {
250
145k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
251
145k
  assert(m_plans.size() != 0 && "There will always be a base plan.");
252
145k
  return m_plans.back();
253
145k
}
254
255
74.3k
lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const {
256
74.3k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
257
74.3k
  if (m_completed_plans.empty())
258
56.5k
    return {};
259
260
17.7k
  if (!skip_private)
261
3.71k
    return m_completed_plans.back();
262
263
14.1k
  
for (int i = m_completed_plans.size() - 1; 13.9k
i >= 0;
i--109
) {
264
14.0k
    lldb::ThreadPlanSP completed_plan_sp;
265
14.0k
    completed_plan_sp = m_completed_plans[i];
266
14.0k
    if (!completed_plan_sp->GetPrivate())
267
13.9k
      return completed_plan_sp;
268
14.0k
  }
269
81
  return {};
270
13.9k
}
271
272
lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx,
273
4
                                                   bool skip_private) const {
274
4
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
275
4
  uint32_t idx = 0;
276
277
8
  for (lldb::ThreadPlanSP plan_sp : m_plans) {
278
8
    if (skip_private && 
plan_sp->GetPrivate()4
)
279
0
      continue;
280
8
    if (idx == plan_idx)
281
3
      return plan_sp;
282
5
    idx++;
283
5
  }
284
1
  return {};
285
4
}
286
287
5.39k
lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {
288
5.39k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
289
5.39k
  if (m_completed_plans.empty())
290
0
    return {};
291
292
14.3k
  
for (int i = m_completed_plans.size() - 1; 5.39k
i >= 0;
i--8.95k
) {
293
9.08k
    lldb::ValueObjectSP return_valobj_sp;
294
9.08k
    return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
295
9.08k
    if (return_valobj_sp)
296
130
      return return_valobj_sp;
297
9.08k
  }
298
5.26k
  return {};
299
5.39k
}
300
301
5.39k
lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {
302
5.39k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
303
5.39k
  if (m_completed_plans.empty())
304
0
    return {};
305
306
14.4k
  
for (int i = m_completed_plans.size() - 1; 5.39k
i >= 0;
i--9.10k
) {
307
9.10k
    lldb::ExpressionVariableSP expression_variable_sp;
308
9.10k
    expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
309
9.10k
    if (expression_variable_sp)
310
4
      return expression_variable_sp;
311
9.10k
  }
312
5.38k
  return {};
313
5.39k
}
314
12
bool ThreadPlanStack::AnyPlans() const {
315
12
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
316
  // There is always a base plan...
317
12
  return m_plans.size() > 1;
318
12
}
319
320
14.9k
bool ThreadPlanStack::AnyCompletedPlans() const {
321
14.9k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
322
14.9k
  return !m_completed_plans.empty();
323
14.9k
}
324
325
3
bool ThreadPlanStack::AnyDiscardedPlans() const {
326
3
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
327
3
  return !m_discarded_plans.empty();
328
3
}
329
330
3.32k
bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {
331
3.32k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
332
3.32k
  for (auto plan : m_completed_plans) {
333
72
    if (plan.get() == in_plan)
334
27
      return true;
335
72
  }
336
3.30k
  return false;
337
3.32k
}
338
339
3.30k
bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {
340
3.30k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
341
3.30k
  for (auto plan : m_discarded_plans) {
342
2
    if (plan.get() == in_plan)
343
0
      return true;
344
2
  }
345
3.30k
  return false;
346
3.30k
}
347
348
35.8k
ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {
349
35.8k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
350
35.8k
  if (current_plan == nullptr)
351
0
    return nullptr;
352
353
  // Look first in the completed plans, if the plan is here and there is
354
  // a completed plan above it, return that.
355
35.8k
  int stack_size = m_completed_plans.size();
356
36.0k
  for (int i = stack_size - 1; i > 0; 
i--254
) {
357
353
    if (current_plan == m_completed_plans[i].get())
358
99
      return m_completed_plans[i - 1].get();
359
353
  }
360
361
  // If this is the first completed plan, the previous one is the
362
  // bottom of the regular plan stack.
363
35.7k
  if (stack_size > 0 && 
m_completed_plans[0].get() == current_plan197
) {
364
75
    return GetCurrentPlan().get();
365
75
  }
366
367
  // Otherwise look for it in the regular plans.
368
35.6k
  stack_size = m_plans.size();
369
55.9k
  for (int i = stack_size - 1; i > 0; 
i--20.3k
) {
370
35.9k
    if (current_plan == m_plans[i].get())
371
15.5k
      return m_plans[i - 1].get();
372
35.9k
  }
373
20.0k
  return nullptr;
374
35.6k
}
375
376
4
ThreadPlan *ThreadPlanStack::GetInnermostExpression() const {
377
4
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
378
4
  int stack_size = m_plans.size();
379
380
8
  for (int i = stack_size - 1; i > 0; 
i--4
) {
381
8
    if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)
382
4
      return m_plans[i].get();
383
8
  }
384
0
  return nullptr;
385
4
}
386
387
14.7k
void ThreadPlanStack::ClearThreadCache() {
388
14.7k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
389
14.7k
  for (lldb::ThreadPlanSP thread_plan_sp : m_plans)
390
27.3k
    thread_plan_sp->ClearThreadCache();
391
14.7k
}
392
393
15.2k
void ThreadPlanStack::WillResume() {
394
15.2k
  std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
395
15.2k
  m_completed_plans.clear();
396
15.2k
  m_discarded_plans.clear();
397
15.2k
}
398
399
void ThreadPlanStackMap::Update(ThreadList &current_threads,
400
                                bool delete_missing,
401
16.8k
                                bool check_for_new) {
402
403
16.8k
  std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
404
  // Now find all the new threads and add them to the map:
405
16.8k
  if (check_for_new) {
406
17.4k
    for (auto thread : current_threads.Threads()) {
407
17.4k
      lldb::tid_t cur_tid = thread->GetID();
408
17.4k
      if (!Find(cur_tid)) {
409
2.83k
        AddThread(*thread);
410
2.83k
        thread->QueueBasePlan(true);
411
2.83k
      }
412
17.4k
    }
413
16.8k
  }
414
415
  // If we aren't reaping missing threads at this point,
416
  // we are done.
417
16.8k
  if (!delete_missing)
418
16
    return;
419
  // Otherwise scan for absent TID's.
420
16.7k
  std::vector<lldb::tid_t> missing_threads;
421
  // If we are going to delete plans from the plan stack,
422
  // then scan for absent TID's:
423
17.5k
  for (auto &thread_plans : m_plans_list) {
424
17.5k
    lldb::tid_t cur_tid = thread_plans.first;
425
17.5k
    ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid);
426
17.5k
    if (!thread_sp)
427
124
      missing_threads.push_back(cur_tid);
428
17.5k
  }
429
16.7k
  for (lldb::tid_t tid : missing_threads) {
430
124
    RemoveTID(tid);
431
124
  }
432
16.7k
}
433
434
void ThreadPlanStackMap::DumpPlans(Stream &strm,
435
                                   lldb::DescriptionLevel desc_level,
436
                                   bool internal, bool condense_if_trivial,
437
0
                                   bool skip_unreported) {
438
0
  std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
439
0
  for (auto &elem : m_plans_list) {
440
0
    lldb::tid_t tid = elem.first;
441
0
    uint32_t index_id = 0;
442
0
    ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
443
444
0
    if (skip_unreported) {
445
0
      if (!thread_sp)
446
0
        continue;
447
0
    }
448
0
    if (thread_sp)
449
0
      index_id = thread_sp->GetIndexID();
450
451
0
    if (condense_if_trivial) {
452
0
      if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
453
0
          !elem.second.AnyDiscardedPlans()) {
454
0
        strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
455
0
        strm.IndentMore();
456
0
        strm.Indent();
457
0
        strm.Printf("No active thread plans\n");
458
0
        strm.IndentLess();
459
0
        return;
460
0
      }
461
0
    }
462
463
0
    strm.Indent();
464
0
    strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
465
466
0
    elem.second.DumpThreadPlans(strm, desc_level, internal);
467
0
  }
468
0
}
469
470
bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid,
471
                                         lldb::DescriptionLevel desc_level,
472
                                         bool internal,
473
                                         bool condense_if_trivial,
474
14
                                         bool skip_unreported) {
475
14
  std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
476
14
  uint32_t index_id = 0;
477
14
  ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
478
479
14
  if (skip_unreported) {
480
9
    if (!thread_sp) {
481
3
      strm.Format("Unknown TID: {0}", tid);
482
3
      return false;
483
3
    }
484
9
  }
485
486
11
  if (thread_sp)
487
8
    index_id = thread_sp->GetIndexID();
488
11
  ThreadPlanStack *stack = Find(tid);
489
11
  if (!stack) {
490
1
    strm.Format("Unknown TID: {0}\n", tid);
491
1
    return false;
492
1
  }
493
494
10
  if (condense_if_trivial) {
495
10
    if (!stack->AnyPlans() && 
!stack->AnyCompletedPlans()4
&&
496
10
        
!stack->AnyDiscardedPlans()3
) {
497
2
      strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
498
2
      strm.IndentMore();
499
2
      strm.Indent();
500
2
      strm.Printf("No active thread plans\n");
501
2
      strm.IndentLess();
502
2
      return true;
503
2
    }
504
10
  }
505
506
8
  strm.Indent();
507
8
  strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
508
509
8
  stack->DumpThreadPlans(strm, desc_level, internal);
510
8
  return true;
511
10
}
512
513
1
bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) {
514
  // We only remove the plans for unreported TID's.
515
1
  std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
516
1
  ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
517
1
  if (thread_sp)
518
0
    return false;
519
520
1
  return RemoveTID(tid);
521
1
}