diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/API/SBThread.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/API/SBThread.cpp | 1349 |
1 files changed, 1349 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/API/SBThread.cpp b/contrib/llvm-project/lldb/source/API/SBThread.cpp new file mode 100644 index 000000000000..53643362421d --- /dev/null +++ b/contrib/llvm-project/lldb/source/API/SBThread.cpp @@ -0,0 +1,1349 @@ +//===-- SBThread.cpp ------------------------------------------------------===// +// +// 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/API/SBThread.h" +#include "Utils.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFormat.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBThreadCollection.h" +#include "lldb/API/SBThreadPlan.h" +#include "lldb/API/SBValue.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Queue.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanStepInRange.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Utility/Instrumentation.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-enumerations.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +const char *SBThread::GetBroadcasterClassName() { + LLDB_INSTRUMENT(); + + return ConstString(Thread::GetStaticBroadcasterClass()).AsCString(); +} + +// Constructors +SBThread::SBThread() : m_opaque_sp(new ExecutionContextRef()) { + LLDB_INSTRUMENT_VA(this); +} + +SBThread::SBThread(const ThreadSP &lldb_object_sp) + : m_opaque_sp(new ExecutionContextRef(lldb_object_sp)) { + LLDB_INSTRUMENT_VA(this, lldb_object_sp); +} + +SBThread::SBThread(const SBThread &rhs) { + LLDB_INSTRUMENT_VA(this, rhs); + + m_opaque_sp = clone(rhs.m_opaque_sp); +} + +// Assignment operator + +const lldb::SBThread &SBThread::operator=(const SBThread &rhs) { + LLDB_INSTRUMENT_VA(this, rhs); + + if (this != &rhs) + m_opaque_sp = clone(rhs.m_opaque_sp); + return *this; +} + +// Destructor +SBThread::~SBThread() = default; + +lldb::SBQueue SBThread::GetQueue() const { + LLDB_INSTRUMENT_VA(this); + + SBQueue sb_queue; + QueueSP queue_sp; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + queue_sp = exe_ctx.GetThreadPtr()->GetQueue(); + if (queue_sp) { + sb_queue.SetQueue(queue_sp); + } + } + } + + return sb_queue; +} + +bool SBThread::IsValid() const { + LLDB_INSTRUMENT_VA(this); + return this->operator bool(); +} +SBThread::operator bool() const { + LLDB_INSTRUMENT_VA(this); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + return m_opaque_sp->GetThreadSP().get() != nullptr; + } + // Without a valid target & process, this thread can't be valid. + return false; +} + +void SBThread::Clear() { + LLDB_INSTRUMENT_VA(this); + + m_opaque_sp->Clear(); +} + +StopReason SBThread::GetStopReason() { + LLDB_INSTRUMENT_VA(this); + + StopReason reason = eStopReasonInvalid; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + return exe_ctx.GetThreadPtr()->GetStopReason(); + } + } + + return reason; +} + +size_t SBThread::GetStopReasonDataCount() { + LLDB_INSTRUMENT_VA(this); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo(); + if (stop_info_sp) { + StopReason reason = stop_info_sp->GetStopReason(); + switch (reason) { + case eStopReasonInvalid: + case eStopReasonNone: + case eStopReasonTrace: + case eStopReasonExec: + case eStopReasonPlanComplete: + case eStopReasonThreadExiting: + case eStopReasonInstrumentation: + case eStopReasonProcessorTrace: + case eStopReasonVForkDone: + // There is no data for these stop reasons. + return 0; + + case eStopReasonBreakpoint: { + break_id_t site_id = stop_info_sp->GetValue(); + lldb::BreakpointSiteSP bp_site_sp( + exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID( + site_id)); + if (bp_site_sp) + return bp_site_sp->GetNumberOfConstituents() * 2; + else + return 0; // Breakpoint must have cleared itself... + } break; + + case eStopReasonWatchpoint: + return 1; + + case eStopReasonSignal: + return 1; + + case eStopReasonException: + return 1; + + case eStopReasonFork: + return 1; + + case eStopReasonVFork: + return 1; + } + } + } + } + return 0; +} + +uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) { + LLDB_INSTRUMENT_VA(this, idx); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + Thread *thread = exe_ctx.GetThreadPtr(); + StopInfoSP stop_info_sp = thread->GetStopInfo(); + if (stop_info_sp) { + StopReason reason = stop_info_sp->GetStopReason(); + switch (reason) { + case eStopReasonInvalid: + case eStopReasonNone: + case eStopReasonTrace: + case eStopReasonExec: + case eStopReasonPlanComplete: + case eStopReasonThreadExiting: + case eStopReasonInstrumentation: + case eStopReasonProcessorTrace: + case eStopReasonVForkDone: + // There is no data for these stop reasons. + return 0; + + case eStopReasonBreakpoint: { + break_id_t site_id = stop_info_sp->GetValue(); + lldb::BreakpointSiteSP bp_site_sp( + exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID( + site_id)); + if (bp_site_sp) { + uint32_t bp_index = idx / 2; + BreakpointLocationSP bp_loc_sp( + bp_site_sp->GetConstituentAtIndex(bp_index)); + if (bp_loc_sp) { + if (idx & 1) { + // Odd idx, return the breakpoint location ID + return bp_loc_sp->GetID(); + } else { + // Even idx, return the breakpoint ID + return bp_loc_sp->GetBreakpoint().GetID(); + } + } + } + return LLDB_INVALID_BREAK_ID; + } break; + + case eStopReasonWatchpoint: + return stop_info_sp->GetValue(); + + case eStopReasonSignal: + return stop_info_sp->GetValue(); + + case eStopReasonException: + return stop_info_sp->GetValue(); + + case eStopReasonFork: + return stop_info_sp->GetValue(); + + case eStopReasonVFork: + return stop_info_sp->GetValue(); + } + } + } + } + return 0; +} + +bool SBThread::GetStopReasonExtendedInfoAsJSON(lldb::SBStream &stream) { + LLDB_INSTRUMENT_VA(this, stream); + + Stream &strm = stream.ref(); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) + return false; + + StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo(); + StructuredData::ObjectSP info = stop_info->GetExtendedInfo(); + if (!info) + return false; + + info->Dump(strm); + + return true; +} + +SBThreadCollection +SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) { + LLDB_INSTRUMENT_VA(this, type); + + SBThreadCollection threads; + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) + return SBThreadCollection(); + + ProcessSP process_sp = exe_ctx.GetProcessSP(); + + StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo(); + StructuredData::ObjectSP info = stop_info->GetExtendedInfo(); + if (!info) + return threads; + + threads = process_sp->GetInstrumentationRuntime(type) + ->GetBacktracesFromExtendedStopInfo(info); + return threads; +} + +size_t SBThread::GetStopDescription(char *dst, size_t dst_len) { + LLDB_INSTRUMENT_VA(this, dst, dst_len); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (dst) + *dst = 0; + + if (!exe_ctx.HasThreadScope()) + return 0; + + Process::StopLocker stop_locker; + if (!stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + return 0; + + std::string thread_stop_desc = exe_ctx.GetThreadPtr()->GetStopDescription(); + if (thread_stop_desc.empty()) + return 0; + + if (dst) + return ::snprintf(dst, dst_len, "%s", thread_stop_desc.c_str()) + 1; + + // NULL dst passed in, return the length needed to contain the + // description. + return thread_stop_desc.size() + 1; // Include the NULL byte for size +} + +SBValue SBThread::GetStopReturnValue() { + LLDB_INSTRUMENT_VA(this); + + ValueObjectSP return_valobj_sp; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo(); + if (stop_info_sp) { + return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp); + } + } + } + + return SBValue(return_valobj_sp); +} + +void SBThread::SetThread(const ThreadSP &lldb_object_sp) { + m_opaque_sp->SetThreadSP(lldb_object_sp); +} + +lldb::tid_t SBThread::GetThreadID() const { + LLDB_INSTRUMENT_VA(this); + + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp->GetID(); + return LLDB_INVALID_THREAD_ID; +} + +uint32_t SBThread::GetIndexID() const { + LLDB_INSTRUMENT_VA(this); + + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp->GetIndexID(); + return LLDB_INVALID_INDEX32; +} + +const char *SBThread::GetName() const { + LLDB_INSTRUMENT_VA(this); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) + return nullptr; + + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + return ConstString(exe_ctx.GetThreadPtr()->GetName()).GetCString(); + + return nullptr; +} + +const char *SBThread::GetQueueName() const { + LLDB_INSTRUMENT_VA(this); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) + return nullptr; + + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + return ConstString(exe_ctx.GetThreadPtr()->GetQueueName()).GetCString(); + + return nullptr; +} + +lldb::queue_id_t SBThread::GetQueueID() const { + LLDB_INSTRUMENT_VA(this); + + queue_id_t id = LLDB_INVALID_QUEUE_ID; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + id = exe_ctx.GetThreadPtr()->GetQueueID(); + } + } + + return id; +} + +bool SBThread::GetInfoItemByPathAsString(const char *path, SBStream &strm) { + LLDB_INSTRUMENT_VA(this, path, strm); + + bool success = false; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + Thread *thread = exe_ctx.GetThreadPtr(); + StructuredData::ObjectSP info_root_sp = thread->GetExtendedInfo(); + if (info_root_sp) { + StructuredData::ObjectSP node = + info_root_sp->GetObjectForDotSeparatedPath(path); + if (node) { + if (node->GetType() == eStructuredDataTypeString) { + strm.ref() << node->GetAsString()->GetValue(); + success = true; + } + if (node->GetType() == eStructuredDataTypeInteger) { + strm.Printf("0x%" PRIx64, node->GetUnsignedIntegerValue()); + success = true; + } + if (node->GetType() == eStructuredDataTypeFloat) { + strm.Printf("0x%f", node->GetAsFloat()->GetValue()); + success = true; + } + if (node->GetType() == eStructuredDataTypeBoolean) { + if (node->GetAsBoolean()->GetValue()) + strm.Printf("true"); + else + strm.Printf("false"); + success = true; + } + if (node->GetType() == eStructuredDataTypeNull) { + strm.Printf("null"); + success = true; + } + } + } + } + } + + return success; +} + +SBError SBThread::ResumeNewPlan(ExecutionContext &exe_ctx, + ThreadPlan *new_plan) { + SBError sb_error; + + Process *process = exe_ctx.GetProcessPtr(); + if (!process) { + sb_error.SetErrorString("No process in SBThread::ResumeNewPlan"); + return sb_error; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + if (!thread) { + sb_error.SetErrorString("No thread in SBThread::ResumeNewPlan"); + return sb_error; + } + + // User level plans should be Controlling Plans so they can be interrupted, + // other plans executed, and then a "continue" will resume the plan. + if (new_plan != nullptr) { + new_plan->SetIsControllingPlan(true); + new_plan->SetOkayToDiscard(false); + } + + // Why do we need to set the current thread by ID here??? + process->GetThreadList().SetSelectedThreadByID(thread->GetID()); + + if (process->GetTarget().GetDebugger().GetAsyncExecution()) + sb_error.ref() = process->Resume(); + else + sb_error.ref() = process->ResumeSynchronous(nullptr); + + return sb_error; +} + +void SBThread::StepOver(lldb::RunMode stop_other_threads) { + LLDB_INSTRUMENT_VA(this, stop_other_threads); + + SBError error; // Ignored + StepOver(stop_other_threads, error); +} + +void SBThread::StepOver(lldb::RunMode stop_other_threads, SBError &error) { + LLDB_INSTRUMENT_VA(this, stop_other_threads, error); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) { + error.SetErrorString("this SBThread object is invalid"); + return; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + bool abort_other_plans = false; + StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0)); + + Status new_plan_status; + ThreadPlanSP new_plan_sp; + if (frame_sp) { + if (frame_sp->HasDebugInformation()) { + const LazyBool avoid_no_debug = eLazyBoolCalculate; + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + new_plan_sp = thread->QueueThreadPlanForStepOverRange( + abort_other_plans, sc.line_entry, sc, stop_other_threads, + new_plan_status, avoid_no_debug); + } else { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( + true, abort_other_plans, stop_other_threads, new_plan_status); + } + } + error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); +} + +void SBThread::StepInto(lldb::RunMode stop_other_threads) { + LLDB_INSTRUMENT_VA(this, stop_other_threads); + + StepInto(nullptr, stop_other_threads); +} + +void SBThread::StepInto(const char *target_name, + lldb::RunMode stop_other_threads) { + LLDB_INSTRUMENT_VA(this, target_name, stop_other_threads); + + SBError error; // Ignored + StepInto(target_name, LLDB_INVALID_LINE_NUMBER, error, stop_other_threads); +} + +void SBThread::StepInto(const char *target_name, uint32_t end_line, + SBError &error, lldb::RunMode stop_other_threads) { + LLDB_INSTRUMENT_VA(this, target_name, end_line, error, stop_other_threads); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) { + error.SetErrorString("this SBThread object is invalid"); + return; + } + + bool abort_other_plans = false; + + Thread *thread = exe_ctx.GetThreadPtr(); + StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0)); + ThreadPlanSP new_plan_sp; + Status new_plan_status; + + if (frame_sp && frame_sp->HasDebugInformation()) { + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + AddressRange range; + if (end_line == LLDB_INVALID_LINE_NUMBER) + range = sc.line_entry.range; + else { + if (!sc.GetAddressRangeFromHereToEndLine(end_line, range, error.ref())) + return; + } + + const LazyBool step_out_avoids_code_without_debug_info = + eLazyBoolCalculate; + const LazyBool step_in_avoids_code_without_debug_info = + eLazyBoolCalculate; + new_plan_sp = thread->QueueThreadPlanForStepInRange( + abort_other_plans, range, sc, target_name, stop_other_threads, + new_plan_status, step_in_avoids_code_without_debug_info, + step_out_avoids_code_without_debug_info); + } else { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( + false, abort_other_plans, stop_other_threads, new_plan_status); + } + + if (new_plan_status.Success()) + error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + else + error.SetErrorString(new_plan_status.AsCString()); +} + +void SBThread::StepOut() { + LLDB_INSTRUMENT_VA(this); + + SBError error; // Ignored + StepOut(error); +} + +void SBThread::StepOut(SBError &error) { + LLDB_INSTRUMENT_VA(this, error); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) { + error.SetErrorString("this SBThread object is invalid"); + return; + } + + bool abort_other_plans = false; + bool stop_other_threads = false; + + Thread *thread = exe_ctx.GetThreadPtr(); + + const LazyBool avoid_no_debug = eLazyBoolCalculate; + Status new_plan_status; + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut( + abort_other_plans, nullptr, false, stop_other_threads, eVoteYes, + eVoteNoOpinion, 0, new_plan_status, avoid_no_debug)); + + if (new_plan_status.Success()) + error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + else + error.SetErrorString(new_plan_status.AsCString()); +} + +void SBThread::StepOutOfFrame(SBFrame &sb_frame) { + LLDB_INSTRUMENT_VA(this, sb_frame); + + SBError error; // Ignored + StepOutOfFrame(sb_frame, error); +} + +void SBThread::StepOutOfFrame(SBFrame &sb_frame, SBError &error) { + LLDB_INSTRUMENT_VA(this, sb_frame, error); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!sb_frame.IsValid()) { + error.SetErrorString("passed invalid SBFrame object"); + return; + } + + StackFrameSP frame_sp(sb_frame.GetFrameSP()); + + if (!exe_ctx.HasThreadScope()) { + error.SetErrorString("this SBThread object is invalid"); + return; + } + + bool abort_other_plans = false; + bool stop_other_threads = false; + Thread *thread = exe_ctx.GetThreadPtr(); + if (sb_frame.GetThread().GetThreadID() != thread->GetID()) { + error.SetErrorString("passed a frame from another thread"); + return; + } + + Status new_plan_status; + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut( + abort_other_plans, nullptr, false, stop_other_threads, eVoteYes, + eVoteNoOpinion, frame_sp->GetFrameIndex(), new_plan_status)); + + if (new_plan_status.Success()) + error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + else + error.SetErrorString(new_plan_status.AsCString()); +} + +void SBThread::StepInstruction(bool step_over) { + LLDB_INSTRUMENT_VA(this, step_over); + + SBError error; // Ignored + StepInstruction(step_over, error); +} + +void SBThread::StepInstruction(bool step_over, SBError &error) { + LLDB_INSTRUMENT_VA(this, step_over, error); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) { + error.SetErrorString("this SBThread object is invalid"); + return; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + Status new_plan_status; + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepSingleInstruction( + step_over, false, true, new_plan_status)); + + if (new_plan_status.Success()) + error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + else + error.SetErrorString(new_plan_status.AsCString()); +} + +void SBThread::RunToAddress(lldb::addr_t addr) { + LLDB_INSTRUMENT_VA(this, addr); + + SBError error; // Ignored + RunToAddress(addr, error); +} + +void SBThread::RunToAddress(lldb::addr_t addr, SBError &error) { + LLDB_INSTRUMENT_VA(this, addr, error); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) { + error.SetErrorString("this SBThread object is invalid"); + return; + } + + bool abort_other_plans = false; + bool stop_other_threads = true; + + Address target_addr(addr); + + Thread *thread = exe_ctx.GetThreadPtr(); + + Status new_plan_status; + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress( + abort_other_plans, target_addr, stop_other_threads, new_plan_status)); + + if (new_plan_status.Success()) + error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + else + error.SetErrorString(new_plan_status.AsCString()); +} + +SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame, + lldb::SBFileSpec &sb_file_spec, uint32_t line) { + LLDB_INSTRUMENT_VA(this, sb_frame, sb_file_spec, line); + + SBError sb_error; + char path[PATH_MAX]; + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + StackFrameSP frame_sp(sb_frame.GetFrameSP()); + + if (exe_ctx.HasThreadScope()) { + Target *target = exe_ctx.GetTargetPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + + if (line == 0) { + sb_error.SetErrorString("invalid line argument"); + return sb_error; + } + + if (!frame_sp) { + // We don't want to run SelectMostRelevantFrame here, for instance if + // you called a sequence of StepOverUntil's you wouldn't want the + // frame changed out from under you because you stepped into a + // recognized frame. + frame_sp = thread->GetSelectedFrame(DoNoSelectMostRelevantFrame); + if (!frame_sp) + frame_sp = thread->GetStackFrameAtIndex(0); + } + + SymbolContext frame_sc; + if (!frame_sp) { + sb_error.SetErrorString("no valid frames in thread to step"); + return sb_error; + } + + // If we have a frame, get its line + frame_sc = frame_sp->GetSymbolContext( + eSymbolContextCompUnit | eSymbolContextFunction | + eSymbolContextLineEntry | eSymbolContextSymbol); + + if (frame_sc.comp_unit == nullptr) { + sb_error.SetErrorStringWithFormat( + "frame %u doesn't have debug information", frame_sp->GetFrameIndex()); + return sb_error; + } + + FileSpec step_file_spec; + if (sb_file_spec.IsValid()) { + // The file spec passed in was valid, so use it + step_file_spec = sb_file_spec.ref(); + } else { + if (frame_sc.line_entry.IsValid()) + step_file_spec = frame_sc.line_entry.GetFile(); + else { + sb_error.SetErrorString("invalid file argument or no file for frame"); + return sb_error; + } + } + + // Grab the current function, then we will make sure the "until" address is + // within the function. We discard addresses that are out of the current + // function, and then if there are no addresses remaining, give an + // appropriate error message. + + bool all_in_function = true; + AddressRange fun_range = frame_sc.function->GetAddressRange(); + + std::vector<addr_t> step_over_until_addrs; + const bool abort_other_plans = false; + const bool stop_other_threads = false; + // TODO: Handle SourceLocationSpec column information + SourceLocationSpec location_spec( + step_file_spec, line, /*column=*/std::nullopt, /*check_inlines=*/true, + /*exact_match=*/false); + + SymbolContextList sc_list; + frame_sc.comp_unit->ResolveSymbolContext(location_spec, + eSymbolContextLineEntry, sc_list); + for (const SymbolContext &sc : sc_list) { + addr_t step_addr = + sc.line_entry.range.GetBaseAddress().GetLoadAddress(target); + if (step_addr != LLDB_INVALID_ADDRESS) { + if (fun_range.ContainsLoadAddress(step_addr, target)) + step_over_until_addrs.push_back(step_addr); + else + all_in_function = false; + } + } + + if (step_over_until_addrs.empty()) { + if (all_in_function) { + step_file_spec.GetPath(path, sizeof(path)); + sb_error.SetErrorStringWithFormat("No line entries for %s:%u", path, + line); + } else + sb_error.SetErrorString("step until target not in current function"); + } else { + Status new_plan_status; + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepUntil( + abort_other_plans, &step_over_until_addrs[0], + step_over_until_addrs.size(), stop_other_threads, + frame_sp->GetFrameIndex(), new_plan_status)); + + if (new_plan_status.Success()) + sb_error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + else + sb_error.SetErrorString(new_plan_status.AsCString()); + } + } else { + sb_error.SetErrorString("this SBThread object is invalid"); + } + return sb_error; +} + +SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name) { + LLDB_INSTRUMENT_VA(this, script_class_name); + + return StepUsingScriptedThreadPlan(script_class_name, true); +} + +SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name, + bool resume_immediately) { + LLDB_INSTRUMENT_VA(this, script_class_name, resume_immediately); + + lldb::SBStructuredData no_data; + return StepUsingScriptedThreadPlan(script_class_name, no_data, + resume_immediately); +} + +SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name, + SBStructuredData &args_data, + bool resume_immediately) { + LLDB_INSTRUMENT_VA(this, script_class_name, args_data, resume_immediately); + + SBError error; + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) { + error.SetErrorString("this SBThread object is invalid"); + return error; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + Status new_plan_status; + StructuredData::ObjectSP obj_sp = args_data.m_impl_up->GetObjectSP(); + + ThreadPlanSP new_plan_sp = thread->QueueThreadPlanForStepScripted( + false, script_class_name, obj_sp, false, new_plan_status); + + if (new_plan_status.Fail()) { + error.SetErrorString(new_plan_status.AsCString()); + return error; + } + + if (!resume_immediately) + return error; + + if (new_plan_status.Success()) + error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + else + error.SetErrorString(new_plan_status.AsCString()); + + return error; +} + +SBError SBThread::JumpToLine(lldb::SBFileSpec &file_spec, uint32_t line) { + LLDB_INSTRUMENT_VA(this, file_spec, line); + + SBError sb_error; + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (!exe_ctx.HasThreadScope()) { + sb_error.SetErrorString("this SBThread object is invalid"); + return sb_error; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + + Status err = thread->JumpToLine(file_spec.ref(), line, true); + sb_error.SetError(err); + return sb_error; +} + +SBError SBThread::ReturnFromFrame(SBFrame &frame, SBValue &return_value) { + LLDB_INSTRUMENT_VA(this, frame, return_value); + + SBError sb_error; + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Thread *thread = exe_ctx.GetThreadPtr(); + sb_error.SetError( + thread->ReturnFromFrame(frame.GetFrameSP(), return_value.GetSP())); + } + + return sb_error; +} + +SBError SBThread::UnwindInnermostExpression() { + LLDB_INSTRUMENT_VA(this); + + SBError sb_error; + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Thread *thread = exe_ctx.GetThreadPtr(); + sb_error.SetError(thread->UnwindInnermostExpression()); + if (sb_error.Success()) + thread->SetSelectedFrameByIndex(0, false); + } + + return sb_error; +} + +bool SBThread::Suspend() { + LLDB_INSTRUMENT_VA(this); + + SBError error; // Ignored + return Suspend(error); +} + +bool SBThread::Suspend(SBError &error) { + LLDB_INSTRUMENT_VA(this, error); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + bool result = false; + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + exe_ctx.GetThreadPtr()->SetResumeState(eStateSuspended); + result = true; + } else { + error.SetErrorString("process is running"); + } + } else + error.SetErrorString("this SBThread object is invalid"); + return result; +} + +bool SBThread::Resume() { + LLDB_INSTRUMENT_VA(this); + + SBError error; // Ignored + return Resume(error); +} + +bool SBThread::Resume(SBError &error) { + LLDB_INSTRUMENT_VA(this, error); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + bool result = false; + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + const bool override_suspend = true; + exe_ctx.GetThreadPtr()->SetResumeState(eStateRunning, override_suspend); + result = true; + } else { + error.SetErrorString("process is running"); + } + } else + error.SetErrorString("this SBThread object is invalid"); + return result; +} + +bool SBThread::IsSuspended() { + LLDB_INSTRUMENT_VA(this); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) + return exe_ctx.GetThreadPtr()->GetResumeState() == eStateSuspended; + return false; +} + +bool SBThread::IsStopped() { + LLDB_INSTRUMENT_VA(this); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) + return StateIsStoppedState(exe_ctx.GetThreadPtr()->GetState(), true); + return false; +} + +SBProcess SBThread::GetProcess() { + LLDB_INSTRUMENT_VA(this); + + SBProcess sb_process; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + // Have to go up to the target so we can get a shared pointer to our + // process... + sb_process.SetSP(exe_ctx.GetProcessSP()); + } + + return sb_process; +} + +uint32_t SBThread::GetNumFrames() { + LLDB_INSTRUMENT_VA(this); + + uint32_t num_frames = 0; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + num_frames = exe_ctx.GetThreadPtr()->GetStackFrameCount(); + } + } + + return num_frames; +} + +SBFrame SBThread::GetFrameAtIndex(uint32_t idx) { + LLDB_INSTRUMENT_VA(this, idx); + + SBFrame sb_frame; + StackFrameSP frame_sp; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + frame_sp = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(idx); + sb_frame.SetFrameSP(frame_sp); + } + } + + return sb_frame; +} + +lldb::SBFrame SBThread::GetSelectedFrame() { + LLDB_INSTRUMENT_VA(this); + + SBFrame sb_frame; + StackFrameSP frame_sp; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + frame_sp = + exe_ctx.GetThreadPtr()->GetSelectedFrame(SelectMostRelevantFrame); + sb_frame.SetFrameSP(frame_sp); + } + } + + return sb_frame; +} + +lldb::SBFrame SBThread::SetSelectedFrame(uint32_t idx) { + LLDB_INSTRUMENT_VA(this, idx); + + SBFrame sb_frame; + StackFrameSP frame_sp; + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + Thread *thread = exe_ctx.GetThreadPtr(); + frame_sp = thread->GetStackFrameAtIndex(idx); + if (frame_sp) { + thread->SetSelectedFrame(frame_sp.get()); + sb_frame.SetFrameSP(frame_sp); + } + } + } + + return sb_frame; +} + +bool SBThread::EventIsThreadEvent(const SBEvent &event) { + LLDB_INSTRUMENT_VA(event); + + return Thread::ThreadEventData::GetEventDataFromEvent(event.get()) != nullptr; +} + +SBFrame SBThread::GetStackFrameFromEvent(const SBEvent &event) { + LLDB_INSTRUMENT_VA(event); + + return Thread::ThreadEventData::GetStackFrameFromEvent(event.get()); +} + +SBThread SBThread::GetThreadFromEvent(const SBEvent &event) { + LLDB_INSTRUMENT_VA(event); + + return Thread::ThreadEventData::GetThreadFromEvent(event.get()); +} + +bool SBThread::operator==(const SBThread &rhs) const { + LLDB_INSTRUMENT_VA(this, rhs); + + return m_opaque_sp->GetThreadSP().get() == + rhs.m_opaque_sp->GetThreadSP().get(); +} + +bool SBThread::operator!=(const SBThread &rhs) const { + LLDB_INSTRUMENT_VA(this, rhs); + + return m_opaque_sp->GetThreadSP().get() != + rhs.m_opaque_sp->GetThreadSP().get(); +} + +bool SBThread::GetStatus(SBStream &status) const { + LLDB_INSTRUMENT_VA(this, status); + + Stream &strm = status.ref(); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1, true); + } else + strm.PutCString("No status"); + + return true; +} + +bool SBThread::GetDescription(SBStream &description) const { + LLDB_INSTRUMENT_VA(this, description); + + return GetDescription(description, false); +} + +bool SBThread::GetDescription(SBStream &description, bool stop_format) const { + LLDB_INSTRUMENT_VA(this, description, stop_format); + + Stream &strm = description.ref(); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat( + strm, LLDB_INVALID_THREAD_ID, stop_format); + } else + strm.PutCString("No value"); + + return true; +} + +SBError SBThread::GetDescriptionWithFormat(const SBFormat &format, + SBStream &output) { + Stream &strm = output.ref(); + + SBError error; + if (!format) { + error.SetErrorString("The provided SBFormat object is invalid"); + return error; + } + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + if (exe_ctx.HasThreadScope()) { + if (exe_ctx.GetThreadPtr()->DumpUsingFormat( + strm, LLDB_INVALID_THREAD_ID, format.GetFormatEntrySP().get())) { + return error; + } + } + + error.SetErrorStringWithFormat( + "It was not possible to generate a thread description with the given " + "format string '%s'", + format.GetFormatEntrySP()->string.c_str()); + return error; +} + +SBThread SBThread::GetExtendedBacktraceThread(const char *type) { + LLDB_INSTRUMENT_VA(this, type); + + std::unique_lock<std::recursive_mutex> lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + SBThread sb_origin_thread; + + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { + if (exe_ctx.HasThreadScope()) { + ThreadSP real_thread(exe_ctx.GetThreadSP()); + if (real_thread) { + ConstString type_const(type); + Process *process = exe_ctx.GetProcessPtr(); + if (process) { + SystemRuntime *runtime = process->GetSystemRuntime(); + if (runtime) { + ThreadSP new_thread_sp( + runtime->GetExtendedBacktraceThread(real_thread, type_const)); + if (new_thread_sp) { + // Save this in the Process' ExtendedThreadList so a strong + // pointer retains the object. + process->GetExtendedThreadList().AddThread(new_thread_sp); + sb_origin_thread.SetThread(new_thread_sp); + } + } + } + } + } + } + + return sb_origin_thread; +} + +uint32_t SBThread::GetExtendedBacktraceOriginatingIndexID() { + LLDB_INSTRUMENT_VA(this); + + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp->GetExtendedBacktraceOriginatingIndexID(); + return LLDB_INVALID_INDEX32; +} + +SBValue SBThread::GetCurrentException() { + LLDB_INSTRUMENT_VA(this); + + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (!thread_sp) + return SBValue(); + + return SBValue(thread_sp->GetCurrentException()); +} + +SBThread SBThread::GetCurrentExceptionBacktrace() { + LLDB_INSTRUMENT_VA(this); + + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (!thread_sp) + return SBThread(); + + return SBThread(thread_sp->GetCurrentExceptionBacktrace()); +} + +bool SBThread::SafeToCallFunctions() { + LLDB_INSTRUMENT_VA(this); + + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp->SafeToCallFunctions(); + return true; +} + +lldb_private::Thread *SBThread::operator->() { + return get(); +} + +lldb_private::Thread *SBThread::get() { + return m_opaque_sp->GetThreadSP().get(); +} + +SBValue SBThread::GetSiginfo() { + LLDB_INSTRUMENT_VA(this); + + ThreadSP thread_sp = m_opaque_sp->GetThreadSP(); + if (!thread_sp) + return SBValue(); + return thread_sp->GetSiginfoValue(); +} |