| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Target/ThreadPlanRunToBreakpoint.h" | |
| #include "lldb/Breakpoint/BreakpointLocation.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/RegisterContext.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Target/Thread.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/Stream.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| ThreadPlanRunToBreakpoint::ThreadPlanRunToBreakpoint(Thread &thread, | |
| BreakpointSP bkpt_sp, | |
| bool stop_others) | |
| : ThreadPlan(ThreadPlan::eKindRunToBreakpoint, "Run to breakpoint plan", | |
| thread, eVoteNoOpinion, eVoteNoOpinion), | |
| m_stop_others(stop_others) { | |
| if (bkpt_sp) | |
| m_breakpoints.push_back(std::move(bkpt_sp)); | |
| } | |
| ThreadPlanRunToBreakpoint::ThreadPlanRunToBreakpoint( | |
| Thread &thread, const std::vector<BreakpointSP> &breakpoints, | |
| bool stop_others) | |
| : ThreadPlan(ThreadPlan::eKindRunToBreakpoint, "Run to breakpoint plan", | |
| thread, eVoteNoOpinion, eVoteNoOpinion), | |
| m_stop_others(stop_others) { | |
| for (auto bkpt_sp : breakpoints) { | |
| // Make the breakpoints thread specific: | |
| if (bkpt_sp) { | |
| bkpt_sp->SetThreadID(thread.GetID()); | |
| m_breakpoints.push_back(bkpt_sp); | |
| } | |
| } | |
| } | |
| ThreadPlanRunToBreakpoint::~ThreadPlanRunToBreakpoint() { | |
| for (auto bkpt_sp : m_breakpoints) | |
| GetTarget().RemoveBreakpointByID(bkpt_sp->GetID()); | |
| } | |
| void ThreadPlanRunToBreakpoint::GetDescription(Stream *s, | |
| lldb::DescriptionLevel level) { | |
| size_t num_bkpts = m_breakpoints.size(); | |
| if (level == lldb::eDescriptionLevelBrief) { | |
| if (num_bkpts == 0) { | |
| s->PutCString("run to breakpoint with no breakpoints given."); | |
| return; | |
| } else if (num_bkpts == 1) | |
| s->PutCString("run to breakpoint: "); | |
| else | |
| s->PutCString("run to breakpoints: "); | |
| for (auto bkpt_sp : m_breakpoints) { | |
| *s << llvm::formatv("{0} ", bkpt_sp->GetID()); | |
| } | |
| } else { | |
| if (num_bkpts == 0) { | |
| s->PutCString("run to breakpoint with no breakpoints given."); | |
| return; | |
| } else if (num_bkpts == 1) | |
| s->PutCString("Run to breakpoint: "); | |
| else { | |
| s->PutCString("Run to breakpoints: "); | |
| } | |
| // FIXME: I'm not preserving breakpoint ID's | |
| // in case I'm asked GetDescription after I've removed them. | |
| for (auto bkpt_sp : m_breakpoints) { | |
| s->PutCString("\n"); | |
| s->Indent(); | |
| bkpt_sp->Dump(s); | |
| } | |
| } | |
| } | |
| bool ThreadPlanRunToBreakpoint::ValidatePlan(Stream *error) { | |
| // We don't require that the breakpoints we were given actually | |
| // resolve to something when the plan was pushed. The only error | |
| // was not to have passed any breakpoints. | |
| return m_breakpoints.size() != 0; | |
| } | |
| bool ThreadPlanRunToBreakpoint::DoPlanExplainsStop(Event *event_ptr) { | |
| return AtOurBreakpoint(); | |
| } | |
| bool ThreadPlanRunToBreakpoint::ShouldStop(Event *event_ptr) { | |
| return AtOurBreakpoint(); | |
| } | |
| bool ThreadPlanRunToBreakpoint::StopOthers() { return m_stop_others; } | |
| void ThreadPlanRunToBreakpoint::SetStopOthers(bool new_value) { | |
| m_stop_others = new_value; | |
| } | |
| StateType ThreadPlanRunToBreakpoint::GetPlanRunState() { return eStateRunning; } | |
| bool ThreadPlanRunToBreakpoint::WillStop() { return true; } | |
| bool ThreadPlanRunToBreakpoint::MischiefManaged() { | |
| Log *log = GetLog(LLDBLog::Step); | |
| if (AtOurBreakpoint()) { | |
| // Remove the breakpoints | |
| for (auto bkpt_sp : m_breakpoints) { | |
| GetTarget().RemoveBreakpointByID(bkpt_sp->GetID()); | |
| } | |
| m_breakpoints.clear(); | |
| LLDB_LOGF(log, "Completed run to breakpoint plan."); | |
| ThreadPlan::MischiefManaged(); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool ThreadPlanRunToBreakpoint::AtOurBreakpoint() { | |
| lldb::addr_t current_addr = GetThread().GetRegisterContext()->GetPC(); | |
| Address current_address; | |
| current_address.SetLoadAddress(current_addr, &GetTarget()); | |
| bool found_it = false; | |
| for (auto bkpt_sp : m_breakpoints) { | |
| if (auto location_sp = bkpt_sp->FindLocationByAddress(current_address)) { | |
| StreamString s; | |
| location_sp->GetDescription(&s, eDescriptionLevelBrief); | |
| LLDB_LOG(GetLog(LLDBLog::Step), "RunToBreakpoint hit breakpoint: {0}", s); | |
| found_it = true; | |
| break; | |
| } | |
| } | |
| return found_it; | |
| } |