aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/API/SBThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/API/SBThread.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/API/SBThread.cpp1349
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();
+}