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