diff options
Diffstat (limited to 'gnu/llvm/lldb/source/Target')
59 files changed, 30624 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Target/ABI.cpp b/gnu/llvm/lldb/source/Target/ABI.cpp new file mode 100644 index 00000000000..58396ba7058 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ABI.cpp @@ -0,0 +1,230 @@ +//===-- ABI.cpp -------------------------------------------------*- C++ -*-===// +// +// 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/ABI.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Log.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace lldb; +using namespace lldb_private; + +ABISP +ABI::FindPlugin(lldb::ProcessSP process_sp, const ArchSpec &arch) { + ABISP abi_sp; + ABICreateInstance create_callback; + + for (uint32_t idx = 0; + (create_callback = PluginManager::GetABICreateCallbackAtIndex(idx)) != + nullptr; + ++idx) { + abi_sp = create_callback(process_sp, arch); + + if (abi_sp) + return abi_sp; + } + abi_sp.reset(); + return abi_sp; +} + +ABI::~ABI() = default; + +bool ABI::GetRegisterInfoByName(ConstString name, RegisterInfo &info) { + uint32_t count = 0; + const RegisterInfo *register_info_array = GetRegisterInfoArray(count); + if (register_info_array) { + const char *unique_name_cstr = name.GetCString(); + uint32_t i; + for (i = 0; i < count; ++i) { + if (register_info_array[i].name == unique_name_cstr) { + info = register_info_array[i]; + return true; + } + } + for (i = 0; i < count; ++i) { + if (register_info_array[i].alt_name == unique_name_cstr) { + info = register_info_array[i]; + return true; + } + } + } + return false; +} + +ValueObjectSP ABI::GetReturnValueObject(Thread &thread, CompilerType &ast_type, + bool persistent) const { + if (!ast_type.IsValid()) + return ValueObjectSP(); + + ValueObjectSP return_valobj_sp; + + return_valobj_sp = GetReturnValueObjectImpl(thread, ast_type); + if (!return_valobj_sp) + return return_valobj_sp; + + // Now turn this into a persistent variable. + // FIXME: This code is duplicated from Target::EvaluateExpression, and it is + // used in similar form in a couple + // of other places. Figure out the correct Create function to do all this + // work. + + if (persistent) { + Target &target = *thread.CalculateTarget(); + PersistentExpressionState *persistent_expression_state = + target.GetPersistentExpressionStateForLanguage( + ast_type.GetMinimumLanguage()); + + if (!persistent_expression_state) + return {}; + + auto prefix = persistent_expression_state->GetPersistentVariablePrefix(); + ConstString persistent_variable_name = + persistent_expression_state->GetNextPersistentVariableName(target, + prefix); + + lldb::ValueObjectSP const_valobj_sp; + + // Check in case our value is already a constant value + if (return_valobj_sp->GetIsConstant()) { + const_valobj_sp = return_valobj_sp; + const_valobj_sp->SetName(persistent_variable_name); + } else + const_valobj_sp = + return_valobj_sp->CreateConstantValue(persistent_variable_name); + + lldb::ValueObjectSP live_valobj_sp = return_valobj_sp; + + return_valobj_sp = const_valobj_sp; + + ExpressionVariableSP expr_variable_sp( + persistent_expression_state->CreatePersistentVariable( + return_valobj_sp)); + + assert(expr_variable_sp); + + // Set flags and live data as appropriate + + const Value &result_value = live_valobj_sp->GetValue(); + + switch (result_value.GetValueType()) { + case Value::eValueTypeHostAddress: + case Value::eValueTypeFileAddress: + // we don't do anything with these for now + break; + case Value::eValueTypeScalar: + case Value::eValueTypeVector: + expr_variable_sp->m_flags |= + ExpressionVariable::EVIsFreezeDried; + expr_variable_sp->m_flags |= + ExpressionVariable::EVIsLLDBAllocated; + expr_variable_sp->m_flags |= + ExpressionVariable::EVNeedsAllocation; + break; + case Value::eValueTypeLoadAddress: + expr_variable_sp->m_live_sp = live_valobj_sp; + expr_variable_sp->m_flags |= + ExpressionVariable::EVIsProgramReference; + break; + } + + return_valobj_sp = expr_variable_sp->GetValueObject(); + } + return return_valobj_sp; +} + +ValueObjectSP ABI::GetReturnValueObject(Thread &thread, llvm::Type &ast_type, + bool persistent) const { + ValueObjectSP return_valobj_sp; + return_valobj_sp = GetReturnValueObjectImpl(thread, ast_type); + return return_valobj_sp; +} + +// specialized to work with llvm IR types +// +// for now we will specify a default implementation so that we don't need to +// modify other ABIs +lldb::ValueObjectSP ABI::GetReturnValueObjectImpl(Thread &thread, + llvm::Type &ir_type) const { + ValueObjectSP return_valobj_sp; + + /* this is a dummy and will only be called if an ABI does not override this */ + + return return_valobj_sp; +} + +bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, llvm::Type &returntype, + llvm::ArrayRef<ABI::CallArgument> args) const { + // dummy prepare trivial call + llvm_unreachable("Should never get here!"); +} + +bool ABI::GetFallbackRegisterLocation( + const RegisterInfo *reg_info, + UnwindPlan::Row::RegisterLocation &unwind_regloc) { + // Did the UnwindPlan fail to give us the caller's stack pointer? The stack + // pointer is defined to be the same as THIS frame's CFA, so return the CFA + // value as the caller's stack pointer. This is true on x86-32/x86-64 at + // least. + if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_SP) { + unwind_regloc.SetIsCFAPlusOffset(0); + return true; + } + + // If a volatile register is being requested, we don't want to forward the + // next frame's register contents up the stack -- the register is not + // retrievable at this frame. + if (RegisterIsVolatile(reg_info)) { + unwind_regloc.SetUndefined(); + return true; + } + + return false; +} + +std::unique_ptr<llvm::MCRegisterInfo> ABI::MakeMCRegisterInfo(const ArchSpec &arch) { + std::string triple = arch.GetTriple().getTriple(); + std::string lookup_error; + const llvm::Target *target = + llvm::TargetRegistry::lookupTarget(triple, lookup_error); + if (!target) { + LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "Failed to create an llvm target for {0}: {1}", triple, + lookup_error); + return nullptr; + } + std::unique_ptr<llvm::MCRegisterInfo> info_up( + target->createMCRegInfo(triple)); + assert(info_up); + return info_up; +} + +void ABI::AugmentRegisterInfo(RegisterInfo &info) { + if (info.kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM && + info.kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) + return; + + RegisterInfo abi_info; + if (!GetRegisterInfoByName(ConstString(info.name), abi_info)) + return; + + if (info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM) + info.kinds[eRegisterKindEHFrame] = abi_info.kinds[eRegisterKindEHFrame]; + if (info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) + info.kinds[eRegisterKindDWARF] = abi_info.kinds[eRegisterKindDWARF]; + if (info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM) + info.kinds[eRegisterKindGeneric] = abi_info.kinds[eRegisterKindGeneric]; +} diff --git a/gnu/llvm/lldb/source/Target/CMakeLists.txt b/gnu/llvm/lldb/source/Target/CMakeLists.txt new file mode 100644 index 00000000000..a92951e6a73 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/CMakeLists.txt @@ -0,0 +1,85 @@ +lldb_tablegen(TargetProperties.inc -gen-lldb-property-defs + SOURCE TargetProperties.td + TARGET LLDBTargetPropertiesGen) + +lldb_tablegen(TargetPropertiesEnum.inc -gen-lldb-property-enum-defs + SOURCE TargetProperties.td + TARGET LLDBTargetPropertiesEnumGen) + +add_lldb_library(lldbTarget + ABI.cpp + ExecutionContext.cpp + JITLoader.cpp + JITLoaderList.cpp + InstrumentationRuntime.cpp + InstrumentationRuntimeStopInfo.cpp + Language.cpp + LanguageRuntime.cpp + Memory.cpp + MemoryHistory.cpp + MemoryRegionInfo.cpp + ModuleCache.cpp + OperatingSystem.cpp + PathMappingList.cpp + Platform.cpp + Process.cpp + Queue.cpp + QueueItem.cpp + QueueList.cpp + RegisterContext.cpp + RegisterNumber.cpp + RemoteAwarePlatform.cpp + SectionLoadHistory.cpp + SectionLoadList.cpp + StackFrame.cpp + StackFrameList.cpp + StackFrameRecognizer.cpp + StackID.cpp + StopInfo.cpp + StructuredDataPlugin.cpp + SystemRuntime.cpp + Target.cpp + TargetList.cpp + Thread.cpp + ThreadCollection.cpp + ThreadList.cpp + ThreadPlan.cpp + ThreadPlanBase.cpp + ThreadPlanCallFunction.cpp + ThreadPlanCallFunctionUsingABI.cpp + ThreadPlanCallOnFunctionExit.cpp + ThreadPlanCallUserExpression.cpp + ThreadPlanPython.cpp + ThreadPlanRunToAddress.cpp + ThreadPlanShouldStopHere.cpp + ThreadPlanStepInRange.cpp + ThreadPlanStepInstruction.cpp + ThreadPlanStepOut.cpp + ThreadPlanStepOverBreakpoint.cpp + ThreadPlanStepOverRange.cpp + ThreadPlanStepRange.cpp + ThreadPlanStepThrough.cpp + ThreadPlanStepUntil.cpp + ThreadPlanTracer.cpp + ThreadSpec.cpp + UnixSignals.cpp + UnwindAssembly.cpp + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbExpression + lldbHost + lldbInterpreter + lldbSymbol + lldbUtility + lldbPluginExpressionParserClang + lldbPluginProcessUtility + + LINK_COMPONENTS + Support + ) + +add_dependencies(lldbTarget + LLDBTargetPropertiesGen + LLDBTargetPropertiesEnumGen) diff --git a/gnu/llvm/lldb/source/Target/ExecutionContext.cpp b/gnu/llvm/lldb/source/Target/ExecutionContext.cpp new file mode 100644 index 00000000000..a24a098eb30 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ExecutionContext.cpp @@ -0,0 +1,618 @@ +//===-- ExecutionContext.cpp ------------------------------------*- C++ -*-===// +// +// 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/ExecutionContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/State.h" + +using namespace lldb_private; + +ExecutionContext::ExecutionContext() + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {} + +ExecutionContext::ExecutionContext(const ExecutionContext &rhs) + : m_target_sp(rhs.m_target_sp), m_process_sp(rhs.m_process_sp), + m_thread_sp(rhs.m_thread_sp), m_frame_sp(rhs.m_frame_sp) {} + +ExecutionContext::ExecutionContext(const lldb::TargetSP &target_sp, + bool get_process) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (target_sp) + SetContext(target_sp, get_process); +} + +ExecutionContext::ExecutionContext(const lldb::ProcessSP &process_sp) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (process_sp) + SetContext(process_sp); +} + +ExecutionContext::ExecutionContext(const lldb::ThreadSP &thread_sp) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (thread_sp) + SetContext(thread_sp); +} + +ExecutionContext::ExecutionContext(const lldb::StackFrameSP &frame_sp) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (frame_sp) + SetContext(frame_sp); +} + +ExecutionContext::ExecutionContext(const lldb::TargetWP &target_wp, + bool get_process) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + lldb::TargetSP target_sp(target_wp.lock()); + if (target_sp) + SetContext(target_sp, get_process); +} + +ExecutionContext::ExecutionContext(const lldb::ProcessWP &process_wp) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + lldb::ProcessSP process_sp(process_wp.lock()); + if (process_sp) + SetContext(process_sp); +} + +ExecutionContext::ExecutionContext(const lldb::ThreadWP &thread_wp) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + lldb::ThreadSP thread_sp(thread_wp.lock()); + if (thread_sp) + SetContext(thread_sp); +} + +ExecutionContext::ExecutionContext(const lldb::StackFrameWP &frame_wp) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + lldb::StackFrameSP frame_sp(frame_wp.lock()); + if (frame_sp) + SetContext(frame_sp); +} + +ExecutionContext::ExecutionContext(Target *t, + bool fill_current_process_thread_frame) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (t) { + m_target_sp = t->shared_from_this(); + if (fill_current_process_thread_frame) { + m_process_sp = t->GetProcessSP(); + if (m_process_sp) { + m_thread_sp = m_process_sp->GetThreadList().GetSelectedThread(); + if (m_thread_sp) + m_frame_sp = m_thread_sp->GetSelectedFrame(); + } + } + } +} + +ExecutionContext::ExecutionContext(Process *process, Thread *thread, + StackFrame *frame) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (process) { + m_process_sp = process->shared_from_this(); + m_target_sp = process->GetTarget().shared_from_this(); + } + if (thread) + m_thread_sp = thread->shared_from_this(); + if (frame) + m_frame_sp = frame->shared_from_this(); +} + +ExecutionContext::ExecutionContext(const ExecutionContextRef &exe_ctx_ref) + : m_target_sp(exe_ctx_ref.GetTargetSP()), + m_process_sp(exe_ctx_ref.GetProcessSP()), + m_thread_sp(exe_ctx_ref.GetThreadSP()), + m_frame_sp(exe_ctx_ref.GetFrameSP()) {} + +ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr, + bool thread_and_frame_only_if_stopped) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (exe_ctx_ref_ptr) { + m_target_sp = exe_ctx_ref_ptr->GetTargetSP(); + m_process_sp = exe_ctx_ref_ptr->GetProcessSP(); + if (!thread_and_frame_only_if_stopped || + (m_process_sp && StateIsStoppedState(m_process_sp->GetState(), true))) { + m_thread_sp = exe_ctx_ref_ptr->GetThreadSP(); + m_frame_sp = exe_ctx_ref_ptr->GetFrameSP(); + } + } +} + +ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr, + std::unique_lock<std::recursive_mutex> &lock) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (exe_ctx_ref_ptr) { + m_target_sp = exe_ctx_ref_ptr->GetTargetSP(); + if (m_target_sp) { + lock = std::unique_lock<std::recursive_mutex>(m_target_sp->GetAPIMutex()); + + m_process_sp = exe_ctx_ref_ptr->GetProcessSP(); + m_thread_sp = exe_ctx_ref_ptr->GetThreadSP(); + m_frame_sp = exe_ctx_ref_ptr->GetFrameSP(); + } + } +} + +ExecutionContext::ExecutionContext(const ExecutionContextRef &exe_ctx_ref, + std::unique_lock<std::recursive_mutex> &lock) + : m_target_sp(exe_ctx_ref.GetTargetSP()), m_process_sp(), m_thread_sp(), + m_frame_sp() { + if (m_target_sp) { + lock = std::unique_lock<std::recursive_mutex>(m_target_sp->GetAPIMutex()); + + m_process_sp = exe_ctx_ref.GetProcessSP(); + m_thread_sp = exe_ctx_ref.GetThreadSP(); + m_frame_sp = exe_ctx_ref.GetFrameSP(); + } +} + +ExecutionContext::ExecutionContext(ExecutionContextScope *exe_scope_ptr) + : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { + if (exe_scope_ptr) + exe_scope_ptr->CalculateExecutionContext(*this); +} + +ExecutionContext::ExecutionContext(ExecutionContextScope &exe_scope_ref) { + exe_scope_ref.CalculateExecutionContext(*this); +} + +void ExecutionContext::Clear() { + m_target_sp.reset(); + m_process_sp.reset(); + m_thread_sp.reset(); + m_frame_sp.reset(); +} + +ExecutionContext::~ExecutionContext() = default; + +uint32_t ExecutionContext::GetAddressByteSize() const { + if (m_target_sp && m_target_sp->GetArchitecture().IsValid()) + return m_target_sp->GetArchitecture().GetAddressByteSize(); + if (m_process_sp) + return m_process_sp->GetAddressByteSize(); + return sizeof(void *); +} + +lldb::ByteOrder ExecutionContext::GetByteOrder() const { + if (m_target_sp && m_target_sp->GetArchitecture().IsValid()) + return m_target_sp->GetArchitecture().GetByteOrder(); + if (m_process_sp) + return m_process_sp->GetByteOrder(); + return endian::InlHostByteOrder(); +} + +RegisterContext *ExecutionContext::GetRegisterContext() const { + if (m_frame_sp) + return m_frame_sp->GetRegisterContext().get(); + else if (m_thread_sp) + return m_thread_sp->GetRegisterContext().get(); + return nullptr; +} + +Target *ExecutionContext::GetTargetPtr() const { + if (m_target_sp) + return m_target_sp.get(); + if (m_process_sp) + return &m_process_sp->GetTarget(); + return nullptr; +} + +Process *ExecutionContext::GetProcessPtr() const { + if (m_process_sp) + return m_process_sp.get(); + if (m_target_sp) + return m_target_sp->GetProcessSP().get(); + return nullptr; +} + +ExecutionContextScope *ExecutionContext::GetBestExecutionContextScope() const { + if (m_frame_sp) + return m_frame_sp.get(); + if (m_thread_sp) + return m_thread_sp.get(); + if (m_process_sp) + return m_process_sp.get(); + return m_target_sp.get(); +} + +Target &ExecutionContext::GetTargetRef() const { + assert(m_target_sp); + return *m_target_sp; +} + +Process &ExecutionContext::GetProcessRef() const { + assert(m_process_sp); + return *m_process_sp; +} + +Thread &ExecutionContext::GetThreadRef() const { + assert(m_thread_sp); + return *m_thread_sp; +} + +StackFrame &ExecutionContext::GetFrameRef() const { + assert(m_frame_sp); + return *m_frame_sp; +} + +void ExecutionContext::SetTargetSP(const lldb::TargetSP &target_sp) { + m_target_sp = target_sp; +} + +void ExecutionContext::SetProcessSP(const lldb::ProcessSP &process_sp) { + m_process_sp = process_sp; +} + +void ExecutionContext::SetThreadSP(const lldb::ThreadSP &thread_sp) { + m_thread_sp = thread_sp; +} + +void ExecutionContext::SetFrameSP(const lldb::StackFrameSP &frame_sp) { + m_frame_sp = frame_sp; +} + +void ExecutionContext::SetTargetPtr(Target *target) { + if (target) + m_target_sp = target->shared_from_this(); + else + m_target_sp.reset(); +} + +void ExecutionContext::SetProcessPtr(Process *process) { + if (process) + m_process_sp = process->shared_from_this(); + else + m_process_sp.reset(); +} + +void ExecutionContext::SetThreadPtr(Thread *thread) { + if (thread) + m_thread_sp = thread->shared_from_this(); + else + m_thread_sp.reset(); +} + +void ExecutionContext::SetFramePtr(StackFrame *frame) { + if (frame) + m_frame_sp = frame->shared_from_this(); + else + m_frame_sp.reset(); +} + +void ExecutionContext::SetContext(const lldb::TargetSP &target_sp, + bool get_process) { + m_target_sp = target_sp; + if (get_process && target_sp) + m_process_sp = target_sp->GetProcessSP(); + else + m_process_sp.reset(); + m_thread_sp.reset(); + m_frame_sp.reset(); +} + +void ExecutionContext::SetContext(const lldb::ProcessSP &process_sp) { + m_process_sp = process_sp; + if (process_sp) + m_target_sp = process_sp->GetTarget().shared_from_this(); + else + m_target_sp.reset(); + m_thread_sp.reset(); + m_frame_sp.reset(); +} + +void ExecutionContext::SetContext(const lldb::ThreadSP &thread_sp) { + m_frame_sp.reset(); + m_thread_sp = thread_sp; + if (thread_sp) { + m_process_sp = thread_sp->GetProcess(); + if (m_process_sp) + m_target_sp = m_process_sp->GetTarget().shared_from_this(); + else + m_target_sp.reset(); + } else { + m_target_sp.reset(); + m_process_sp.reset(); + } +} + +void ExecutionContext::SetContext(const lldb::StackFrameSP &frame_sp) { + m_frame_sp = frame_sp; + if (frame_sp) { + m_thread_sp = frame_sp->CalculateThread(); + if (m_thread_sp) { + m_process_sp = m_thread_sp->GetProcess(); + if (m_process_sp) + m_target_sp = m_process_sp->GetTarget().shared_from_this(); + else + m_target_sp.reset(); + } else { + m_target_sp.reset(); + m_process_sp.reset(); + } + } else { + m_target_sp.reset(); + m_process_sp.reset(); + m_thread_sp.reset(); + } +} + +ExecutionContext &ExecutionContext::operator=(const ExecutionContext &rhs) { + if (this != &rhs) { + m_target_sp = rhs.m_target_sp; + m_process_sp = rhs.m_process_sp; + m_thread_sp = rhs.m_thread_sp; + m_frame_sp = rhs.m_frame_sp; + } + return *this; +} + +bool ExecutionContext::operator==(const ExecutionContext &rhs) const { + // Check that the frame shared pointers match, or both are valid and their + // stack IDs match since sometimes we get new objects that represent the same + // frame within a thread. + if ((m_frame_sp == rhs.m_frame_sp) || + (m_frame_sp && rhs.m_frame_sp && + m_frame_sp->GetStackID() == rhs.m_frame_sp->GetStackID())) { + // Check that the thread shared pointers match, or both are valid and their + // thread IDs match since sometimes we get new objects that represent the + // same thread within a process. + if ((m_thread_sp == rhs.m_thread_sp) || + (m_thread_sp && rhs.m_thread_sp && + m_thread_sp->GetID() == rhs.m_thread_sp->GetID())) { + // Processes and targets don't change much + return m_process_sp == rhs.m_process_sp && m_target_sp == rhs.m_target_sp; + } + } + return false; +} + +bool ExecutionContext::operator!=(const ExecutionContext &rhs) const { + return !(*this == rhs); +} + +bool ExecutionContext::HasTargetScope() const { + return ((bool)m_target_sp && m_target_sp->IsValid()); +} + +bool ExecutionContext::HasProcessScope() const { + return (HasTargetScope() && ((bool)m_process_sp && m_process_sp->IsValid())); +} + +bool ExecutionContext::HasThreadScope() const { + return (HasProcessScope() && ((bool)m_thread_sp && m_thread_sp->IsValid())); +} + +bool ExecutionContext::HasFrameScope() const { + return HasThreadScope() && m_frame_sp; +} + +ExecutionContextRef::ExecutionContextRef() + : m_target_wp(), m_process_wp(), m_thread_wp(), + m_tid(LLDB_INVALID_THREAD_ID), m_stack_id() {} + +ExecutionContextRef::ExecutionContextRef(const ExecutionContext *exe_ctx) + : m_target_wp(), m_process_wp(), m_thread_wp(), + m_tid(LLDB_INVALID_THREAD_ID), m_stack_id() { + if (exe_ctx) + *this = *exe_ctx; +} + +ExecutionContextRef::ExecutionContextRef(const ExecutionContext &exe_ctx) + : m_target_wp(), m_process_wp(), m_thread_wp(), + m_tid(LLDB_INVALID_THREAD_ID), m_stack_id() { + *this = exe_ctx; +} + +ExecutionContextRef::ExecutionContextRef(Target *target, bool adopt_selected) + : m_target_wp(), m_process_wp(), m_thread_wp(), + m_tid(LLDB_INVALID_THREAD_ID), m_stack_id() { + SetTargetPtr(target, adopt_selected); +} + +ExecutionContextRef::ExecutionContextRef(const ExecutionContextRef &rhs) + : m_target_wp(rhs.m_target_wp), m_process_wp(rhs.m_process_wp), + m_thread_wp(rhs.m_thread_wp), m_tid(rhs.m_tid), + m_stack_id(rhs.m_stack_id) {} + +ExecutionContextRef &ExecutionContextRef:: +operator=(const ExecutionContextRef &rhs) { + if (this != &rhs) { + m_target_wp = rhs.m_target_wp; + m_process_wp = rhs.m_process_wp; + m_thread_wp = rhs.m_thread_wp; + m_tid = rhs.m_tid; + m_stack_id = rhs.m_stack_id; + } + return *this; +} + +ExecutionContextRef &ExecutionContextRef:: +operator=(const ExecutionContext &exe_ctx) { + m_target_wp = exe_ctx.GetTargetSP(); + m_process_wp = exe_ctx.GetProcessSP(); + lldb::ThreadSP thread_sp(exe_ctx.GetThreadSP()); + m_thread_wp = thread_sp; + if (thread_sp) + m_tid = thread_sp->GetID(); + else + m_tid = LLDB_INVALID_THREAD_ID; + lldb::StackFrameSP frame_sp(exe_ctx.GetFrameSP()); + if (frame_sp) + m_stack_id = frame_sp->GetStackID(); + else + m_stack_id.Clear(); + return *this; +} + +void ExecutionContextRef::Clear() { + m_target_wp.reset(); + m_process_wp.reset(); + ClearThread(); + ClearFrame(); +} + +ExecutionContextRef::~ExecutionContextRef() = default; + +void ExecutionContextRef::SetTargetSP(const lldb::TargetSP &target_sp) { + m_target_wp = target_sp; +} + +void ExecutionContextRef::SetProcessSP(const lldb::ProcessSP &process_sp) { + if (process_sp) { + m_process_wp = process_sp; + SetTargetSP(process_sp->GetTarget().shared_from_this()); + } else { + m_process_wp.reset(); + m_target_wp.reset(); + } +} + +void ExecutionContextRef::SetThreadSP(const lldb::ThreadSP &thread_sp) { + if (thread_sp) { + m_thread_wp = thread_sp; + m_tid = thread_sp->GetID(); + SetProcessSP(thread_sp->GetProcess()); + } else { + ClearThread(); + m_process_wp.reset(); + m_target_wp.reset(); + } +} + +void ExecutionContextRef::SetFrameSP(const lldb::StackFrameSP &frame_sp) { + if (frame_sp) { + m_stack_id = frame_sp->GetStackID(); + SetThreadSP(frame_sp->GetThread()); + } else { + ClearFrame(); + ClearThread(); + m_process_wp.reset(); + m_target_wp.reset(); + } +} + +void ExecutionContextRef::SetTargetPtr(Target *target, bool adopt_selected) { + Clear(); + if (target) { + lldb::TargetSP target_sp(target->shared_from_this()); + if (target_sp) { + m_target_wp = target_sp; + if (adopt_selected) { + lldb::ProcessSP process_sp(target_sp->GetProcessSP()); + if (process_sp) { + m_process_wp = process_sp; + if (process_sp) { + // Only fill in the thread and frame if our process is stopped + // Don't just check the state, since we might be in the middle of + // resuming. + Process::StopLocker stop_locker; + + if (stop_locker.TryLock(&process_sp->GetRunLock()) && + StateIsStoppedState(process_sp->GetState(), true)) { + lldb::ThreadSP thread_sp( + process_sp->GetThreadList().GetSelectedThread()); + if (!thread_sp) + thread_sp = process_sp->GetThreadList().GetThreadAtIndex(0); + + if (thread_sp) { + SetThreadSP(thread_sp); + lldb::StackFrameSP frame_sp(thread_sp->GetSelectedFrame()); + if (!frame_sp) + frame_sp = thread_sp->GetStackFrameAtIndex(0); + if (frame_sp) + SetFrameSP(frame_sp); + } + } + } + } + } + } + } +} + +void ExecutionContextRef::SetProcessPtr(Process *process) { + if (process) { + SetProcessSP(process->shared_from_this()); + } else { + m_process_wp.reset(); + m_target_wp.reset(); + } +} + +void ExecutionContextRef::SetThreadPtr(Thread *thread) { + if (thread) { + SetThreadSP(thread->shared_from_this()); + } else { + ClearThread(); + m_process_wp.reset(); + m_target_wp.reset(); + } +} + +void ExecutionContextRef::SetFramePtr(StackFrame *frame) { + if (frame) + SetFrameSP(frame->shared_from_this()); + else + Clear(); +} + +lldb::TargetSP ExecutionContextRef::GetTargetSP() const { + lldb::TargetSP target_sp(m_target_wp.lock()); + if (target_sp && !target_sp->IsValid()) + target_sp.reset(); + return target_sp; +} + +lldb::ProcessSP ExecutionContextRef::GetProcessSP() const { + lldb::ProcessSP process_sp(m_process_wp.lock()); + if (process_sp && !process_sp->IsValid()) + process_sp.reset(); + return process_sp; +} + +lldb::ThreadSP ExecutionContextRef::GetThreadSP() const { + lldb::ThreadSP thread_sp(m_thread_wp.lock()); + + if (m_tid != LLDB_INVALID_THREAD_ID) { + // We check if the thread has been destroyed in cases where clients might + // still have shared pointer to a thread, but the thread is not valid + // anymore (not part of the process) + if (!thread_sp || !thread_sp->IsValid()) { + lldb::ProcessSP process_sp(GetProcessSP()); + if (process_sp && process_sp->IsValid()) { + thread_sp = process_sp->GetThreadList().FindThreadByID(m_tid); + m_thread_wp = thread_sp; + } + } + } + + // Check that we aren't about to return an invalid thread sp. We might + // return a nullptr thread_sp, but don't return an invalid one. + + if (thread_sp && !thread_sp->IsValid()) + thread_sp.reset(); + + return thread_sp; +} + +lldb::StackFrameSP ExecutionContextRef::GetFrameSP() const { + if (m_stack_id.IsValid()) { + lldb::ThreadSP thread_sp(GetThreadSP()); + if (thread_sp) + return thread_sp->GetFrameWithStackID(m_stack_id); + } + return lldb::StackFrameSP(); +} + +ExecutionContext +ExecutionContextRef::Lock(bool thread_and_frame_only_if_stopped) const { + return ExecutionContext(this, thread_and_frame_only_if_stopped); +} diff --git a/gnu/llvm/lldb/source/Target/InstrumentationRuntime.cpp b/gnu/llvm/lldb/source/Target/InstrumentationRuntime.cpp new file mode 100644 index 00000000000..4c2eb064fbb --- /dev/null +++ b/gnu/llvm/lldb/source/Target/InstrumentationRuntime.cpp @@ -0,0 +1,75 @@ +//===-- InstrumentationRuntime.cpp ------------------------------*- C++ -*-===// +// +// 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/InstrumentationRuntime.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/lldb-private.h" + +using namespace lldb; +using namespace lldb_private; + +void InstrumentationRuntime::ModulesDidLoad( + lldb_private::ModuleList &module_list, lldb_private::Process *process, + InstrumentationRuntimeCollection &runtimes) { + InstrumentationRuntimeCreateInstance create_callback = nullptr; + InstrumentationRuntimeGetType get_type_callback; + for (uint32_t idx = 0;; ++idx) { + create_callback = + PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(idx); + if (create_callback == nullptr) + break; + get_type_callback = + PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex(idx); + InstrumentationRuntimeType type = get_type_callback(); + + InstrumentationRuntimeCollection::iterator pos; + pos = runtimes.find(type); + if (pos == runtimes.end()) { + runtimes[type] = create_callback(process->shared_from_this()); + } + } +} + +void InstrumentationRuntime::ModulesDidLoad( + lldb_private::ModuleList &module_list) { + if (IsActive()) + return; + + if (GetRuntimeModuleSP()) { + Activate(); + return; + } + + module_list.ForEach([this](const lldb::ModuleSP module_sp) -> bool { + const FileSpec &file_spec = module_sp->GetFileSpec(); + if (!file_spec) + return true; // Keep iterating. + + const RegularExpression &runtime_regex = GetPatternForRuntimeLibrary(); + if (runtime_regex.Execute(file_spec.GetFilename().GetCString()) || + module_sp->IsExecutable()) { + if (CheckIfRuntimeIsValid(module_sp)) { + SetRuntimeModuleSP(module_sp); + Activate(); + return false; // Stop iterating, we're done. + } + } + + return true; + }); +} + +lldb::ThreadCollectionSP +InstrumentationRuntime::GetBacktracesFromExtendedStopInfo( + StructuredData::ObjectSP info) { + return ThreadCollectionSP(new ThreadCollection()); +} diff --git a/gnu/llvm/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp b/gnu/llvm/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp new file mode 100644 index 00000000000..f2767ffbff0 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp @@ -0,0 +1,36 @@ +//===-- InstrumentationRuntimeStopInfo.cpp ----------------------*- C++ -*-===// +// +// 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/InstrumentationRuntimeStopInfo.h" + +#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/lldb-private.h" + +using namespace lldb; +using namespace lldb_private; + +InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo( + Thread &thread, std::string description, + StructuredData::ObjectSP additional_data) + : StopInfo(thread, 0) { + m_extended_info = additional_data; + m_description = description; +} + +const char *InstrumentationRuntimeStopInfo::GetDescription() { + return m_description.c_str(); +} + +StopInfoSP +InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData( + Thread &thread, std::string description, + StructuredData::ObjectSP additionalData) { + return StopInfoSP( + new InstrumentationRuntimeStopInfo(thread, description, additionalData)); +} diff --git a/gnu/llvm/lldb/source/Target/JITLoader.cpp b/gnu/llvm/lldb/source/Target/JITLoader.cpp new file mode 100644 index 00000000000..e7c13bddcb3 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/JITLoader.cpp @@ -0,0 +1,32 @@ +//===-- JITLoader.cpp -------------------------------------------*- C++ -*-===// +// +// 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/JITLoader.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/JITLoaderList.h" +#include "lldb/Target/Process.h" +#include "lldb/lldb-private.h" + +using namespace lldb; +using namespace lldb_private; + +void JITLoader::LoadPlugins(Process *process, JITLoaderList &list) { + JITLoaderCreateInstance create_callback = nullptr; + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetJITLoaderCreateCallbackAtIndex(idx)) != nullptr; + ++idx) { + JITLoaderSP instance_sp(create_callback(process, false)); + if (instance_sp) + list.Append(std::move(instance_sp)); + } +} + +JITLoader::JITLoader(Process *process) : m_process(process) {} + +JITLoader::~JITLoader() = default; diff --git a/gnu/llvm/lldb/source/Target/JITLoaderList.cpp b/gnu/llvm/lldb/source/Target/JITLoaderList.cpp new file mode 100644 index 00000000000..4d1f06d0843 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/JITLoaderList.cpp @@ -0,0 +1,55 @@ +//===-- JITLoaderList.cpp ---------------------------------------*- C++ -*-===// +// +// 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/JITLoader.h" +#include "lldb/Target/JITLoaderList.h" +#include "lldb/lldb-private.h" + +using namespace lldb; +using namespace lldb_private; + +JITLoaderList::JITLoaderList() : m_jit_loaders_vec(), m_jit_loaders_mutex() {} + +JITLoaderList::~JITLoaderList() {} + +void JITLoaderList::Append(const JITLoaderSP &jit_loader_sp) { + std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex); + m_jit_loaders_vec.push_back(jit_loader_sp); +} + +void JITLoaderList::Remove(const JITLoaderSP &jit_loader_sp) { + std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex); + m_jit_loaders_vec.erase(std::remove(m_jit_loaders_vec.begin(), + m_jit_loaders_vec.end(), jit_loader_sp), + m_jit_loaders_vec.end()); +} + +size_t JITLoaderList::GetSize() const { return m_jit_loaders_vec.size(); } + +JITLoaderSP JITLoaderList::GetLoaderAtIndex(size_t idx) { + std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex); + return m_jit_loaders_vec[idx]; +} + +void JITLoaderList::DidLaunch() { + std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex); + for (auto const &jit_loader : m_jit_loaders_vec) + jit_loader->DidLaunch(); +} + +void JITLoaderList::DidAttach() { + std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex); + for (auto const &jit_loader : m_jit_loaders_vec) + jit_loader->DidAttach(); +} + +void JITLoaderList::ModulesDidLoad(ModuleList &module_list) { + std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex); + for (auto const &jit_loader : m_jit_loaders_vec) + jit_loader->ModulesDidLoad(module_list); +} diff --git a/gnu/llvm/lldb/source/Target/Language.cpp b/gnu/llvm/lldb/source/Target/Language.cpp new file mode 100644 index 00000000000..10a9ddd56c4 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/Language.cpp @@ -0,0 +1,462 @@ +//===-- Language.cpp -------------------------------------------------*- C++ +//-*-===// +// +// 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 <functional> +#include <map> +#include <mutex> + +#include "lldb/Target/Language.h" + +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Stream.h" + +#include "llvm/Support/Threading.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +typedef std::unique_ptr<Language> LanguageUP; +typedef std::map<lldb::LanguageType, LanguageUP> LanguagesMap; + +static LanguagesMap &GetLanguagesMap() { + static LanguagesMap *g_map = nullptr; + static llvm::once_flag g_initialize; + + llvm::call_once(g_initialize, [] { + g_map = new LanguagesMap(); // NOTE: INTENTIONAL LEAK due to global + // destructor chain + }); + + return *g_map; +} +static std::mutex &GetLanguagesMutex() { + static std::mutex *g_mutex = nullptr; + static llvm::once_flag g_initialize; + + llvm::call_once(g_initialize, [] { + g_mutex = new std::mutex(); // NOTE: INTENTIONAL LEAK due to global + // destructor chain + }); + + return *g_mutex; +} + +Language *Language::FindPlugin(lldb::LanguageType language) { + std::lock_guard<std::mutex> guard(GetLanguagesMutex()); + LanguagesMap &map(GetLanguagesMap()); + auto iter = map.find(language), end = map.end(); + if (iter != end) + return iter->second.get(); + + Language *language_ptr = nullptr; + LanguageCreateInstance create_callback; + + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetLanguageCreateCallbackAtIndex(idx)) != nullptr; + ++idx) { + language_ptr = create_callback(language); + + if (language_ptr) { + map[language] = std::unique_ptr<Language>(language_ptr); + return language_ptr; + } + } + + return nullptr; +} + +Language *Language::FindPlugin(llvm::StringRef file_path) { + Language *result = nullptr; + ForEach([&result, file_path](Language *language) { + if (language->IsSourceFile(file_path)) { + result = language; + return false; + } + return true; + }); + return result; +} + +Language *Language::FindPlugin(LanguageType language, + llvm::StringRef file_path) { + Language *result = FindPlugin(language); + // Finding a language by file path is slower, we so we use this as the + // fallback. + if (!result) + result = FindPlugin(file_path); + return result; +} + +void Language::ForEach(std::function<bool(Language *)> callback) { + // If we want to iterate over all languages, we first have to complete the + // LanguagesMap. + static llvm::once_flag g_initialize; + llvm::call_once(g_initialize, [] { + for (unsigned lang = eLanguageTypeUnknown; lang < eNumLanguageTypes; + ++lang) { + FindPlugin(static_cast<lldb::LanguageType>(lang)); + } + }); + + std::lock_guard<std::mutex> guard(GetLanguagesMutex()); + LanguagesMap &map(GetLanguagesMap()); + for (const auto &entry : map) { + if (!callback(entry.second.get())) + break; + } +} + +bool Language::IsTopLevelFunction(Function &function) { return false; } + +lldb::TypeCategoryImplSP Language::GetFormatters() { return nullptr; } + +HardcodedFormatters::HardcodedFormatFinder Language::GetHardcodedFormats() { + return {}; +} + +HardcodedFormatters::HardcodedSummaryFinder Language::GetHardcodedSummaries() { + return {}; +} + +HardcodedFormatters::HardcodedSyntheticFinder +Language::GetHardcodedSynthetics() { + return {}; +} + +std::vector<ConstString> +Language::GetPossibleFormattersMatches(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + return {}; +} + +lldb_private::formatters::StringPrinter::EscapingHelper +Language::GetStringPrinterEscapingHelper( + lldb_private::formatters::StringPrinter::GetPrintableElementType + elem_type) { + return StringPrinter::GetDefaultEscapingHelper(elem_type); +} + +struct language_name_pair { + const char *name; + LanguageType type; +}; + +struct language_name_pair language_names[] = { + // To allow GetNameForLanguageType to be a simple array lookup, the first + // part of this array must follow enum LanguageType exactly. + {"unknown", eLanguageTypeUnknown}, + {"c89", eLanguageTypeC89}, + {"c", eLanguageTypeC}, + {"ada83", eLanguageTypeAda83}, + {"c++", eLanguageTypeC_plus_plus}, + {"cobol74", eLanguageTypeCobol74}, + {"cobol85", eLanguageTypeCobol85}, + {"fortran77", eLanguageTypeFortran77}, + {"fortran90", eLanguageTypeFortran90}, + {"pascal83", eLanguageTypePascal83}, + {"modula2", eLanguageTypeModula2}, + {"java", eLanguageTypeJava}, + {"c99", eLanguageTypeC99}, + {"ada95", eLanguageTypeAda95}, + {"fortran95", eLanguageTypeFortran95}, + {"pli", eLanguageTypePLI}, + {"objective-c", eLanguageTypeObjC}, + {"objective-c++", eLanguageTypeObjC_plus_plus}, + {"upc", eLanguageTypeUPC}, + {"d", eLanguageTypeD}, + {"python", eLanguageTypePython}, + {"opencl", eLanguageTypeOpenCL}, + {"go", eLanguageTypeGo}, + {"modula3", eLanguageTypeModula3}, + {"haskell", eLanguageTypeHaskell}, + {"c++03", eLanguageTypeC_plus_plus_03}, + {"c++11", eLanguageTypeC_plus_plus_11}, + {"ocaml", eLanguageTypeOCaml}, + {"rust", eLanguageTypeRust}, + {"c11", eLanguageTypeC11}, + {"swift", eLanguageTypeSwift}, + {"julia", eLanguageTypeJulia}, + {"dylan", eLanguageTypeDylan}, + {"c++14", eLanguageTypeC_plus_plus_14}, + {"fortran03", eLanguageTypeFortran03}, + {"fortran08", eLanguageTypeFortran08}, + // Vendor Extensions + {"mipsassem", eLanguageTypeMipsAssembler}, + {"renderscript", eLanguageTypeExtRenderScript}, + // Now synonyms, in arbitrary order + {"objc", eLanguageTypeObjC}, + {"objc++", eLanguageTypeObjC_plus_plus}, + {"pascal", eLanguageTypePascal83}}; + +static uint32_t num_languages = + sizeof(language_names) / sizeof(struct language_name_pair); + +LanguageType Language::GetLanguageTypeFromString(llvm::StringRef string) { + for (const auto &L : language_names) { + if (string.equals_lower(L.name)) + return static_cast<LanguageType>(L.type); + } + + return eLanguageTypeUnknown; +} + +const char *Language::GetNameForLanguageType(LanguageType language) { + if (language < num_languages) + return language_names[language].name; + else + return language_names[eLanguageTypeUnknown].name; +} + +void Language::PrintAllLanguages(Stream &s, const char *prefix, + const char *suffix) { + for (uint32_t i = 1; i < num_languages; i++) { + s.Printf("%s%s%s", prefix, language_names[i].name, suffix); + } +} + +void Language::ForAllLanguages( + std::function<bool(lldb::LanguageType)> callback) { + for (uint32_t i = 1; i < num_languages; i++) { + if (!callback(language_names[i].type)) + break; + } +} + +bool Language::LanguageIsCPlusPlus(LanguageType language) { + switch (language) { + case eLanguageTypeC_plus_plus: + case eLanguageTypeC_plus_plus_03: + case eLanguageTypeC_plus_plus_11: + case eLanguageTypeC_plus_plus_14: + case eLanguageTypeObjC_plus_plus: + return true; + default: + return false; + } +} + +bool Language::LanguageIsObjC(LanguageType language) { + switch (language) { + case eLanguageTypeObjC: + case eLanguageTypeObjC_plus_plus: + return true; + default: + return false; + } +} + +bool Language::LanguageIsC(LanguageType language) { + switch (language) { + case eLanguageTypeC: + case eLanguageTypeC89: + case eLanguageTypeC99: + case eLanguageTypeC11: + return true; + default: + return false; + } +} + +bool Language::LanguageIsCFamily(LanguageType language) { + switch (language) { + case eLanguageTypeC: + case eLanguageTypeC89: + case eLanguageTypeC99: + case eLanguageTypeC11: + case eLanguageTypeC_plus_plus: + case eLanguageTypeC_plus_plus_03: + case eLanguageTypeC_plus_plus_11: + case eLanguageTypeC_plus_plus_14: + case eLanguageTypeObjC_plus_plus: + case eLanguageTypeObjC: + return true; + default: + return false; + } +} + +bool Language::LanguageIsPascal(LanguageType language) { + switch (language) { + case eLanguageTypePascal83: + return true; + default: + return false; + } +} + +LanguageType Language::GetPrimaryLanguage(LanguageType language) { + switch (language) { + case eLanguageTypeC_plus_plus: + case eLanguageTypeC_plus_plus_03: + case eLanguageTypeC_plus_plus_11: + case eLanguageTypeC_plus_plus_14: + return eLanguageTypeC_plus_plus; + case eLanguageTypeC: + case eLanguageTypeC89: + case eLanguageTypeC99: + case eLanguageTypeC11: + return eLanguageTypeC; + case eLanguageTypeObjC: + case eLanguageTypeObjC_plus_plus: + return eLanguageTypeObjC; + case eLanguageTypePascal83: + case eLanguageTypeCobol74: + case eLanguageTypeCobol85: + case eLanguageTypeFortran77: + case eLanguageTypeFortran90: + case eLanguageTypeFortran95: + case eLanguageTypeFortran03: + case eLanguageTypeFortran08: + case eLanguageTypeAda83: + case eLanguageTypeAda95: + case eLanguageTypeModula2: + case eLanguageTypeJava: + case eLanguageTypePLI: + case eLanguageTypeUPC: + case eLanguageTypeD: + case eLanguageTypePython: + case eLanguageTypeOpenCL: + case eLanguageTypeGo: + case eLanguageTypeModula3: + case eLanguageTypeHaskell: + case eLanguageTypeOCaml: + case eLanguageTypeRust: + case eLanguageTypeSwift: + case eLanguageTypeJulia: + case eLanguageTypeDylan: + case eLanguageTypeMipsAssembler: + case eLanguageTypeExtRenderScript: + case eLanguageTypeUnknown: + default: + return language; + } +} + +std::set<lldb::LanguageType> Language::GetSupportedLanguages() { + std::set<lldb::LanguageType> supported_languages; + ForEach([&](Language *lang) { + supported_languages.emplace(lang->GetLanguageType()); + return true; + }); + return supported_languages; +} + +LanguageSet Language::GetLanguagesSupportingTypeSystems() { + return PluginManager::GetAllTypeSystemSupportedLanguagesForTypes(); +} + +LanguageSet Language::GetLanguagesSupportingTypeSystemsForExpressions() { + return PluginManager::GetAllTypeSystemSupportedLanguagesForExpressions(); +} + +LanguageSet Language::GetLanguagesSupportingREPLs() { + return PluginManager::GetREPLAllTypeSystemSupportedLanguages(); +} + +std::unique_ptr<Language::TypeScavenger> Language::GetTypeScavenger() { + return nullptr; +} + +const char *Language::GetLanguageSpecificTypeLookupHelp() { return nullptr; } + +size_t Language::TypeScavenger::Find(ExecutionContextScope *exe_scope, + const char *key, ResultSet &results, + bool append) { + if (!exe_scope || !exe_scope->CalculateTarget().get()) + return false; + + if (!key || !key[0]) + return false; + + if (!append) + results.clear(); + + size_t old_size = results.size(); + + if (this->Find_Impl(exe_scope, key, results)) + return results.size() - old_size; + return 0; +} + +bool Language::ImageListTypeScavenger::Find_Impl( + ExecutionContextScope *exe_scope, const char *key, ResultSet &results) { + bool result = false; + + Target *target = exe_scope->CalculateTarget().get(); + if (target) { + const auto &images(target->GetImages()); + ConstString cs_key(key); + llvm::DenseSet<SymbolFile *> searched_sym_files; + TypeList matches; + images.FindTypes(nullptr, cs_key, false, UINT32_MAX, searched_sym_files, + matches); + for (const auto &match : matches.Types()) { + if (match) { + CompilerType compiler_type(match->GetFullCompilerType()); + compiler_type = AdjustForInclusion(compiler_type); + if (!compiler_type) + continue; + std::unique_ptr<Language::TypeScavenger::Result> scavengeresult( + new Result(compiler_type)); + results.insert(std::move(scavengeresult)); + result = true; + } + } + } + + return result; +} + +bool Language::GetFormatterPrefixSuffix(ValueObject &valobj, + ConstString type_hint, + std::string &prefix, + std::string &suffix) { + return false; +} + +DumpValueObjectOptions::DeclPrintingHelper Language::GetDeclPrintingHelper() { + return nullptr; +} + +LazyBool Language::IsLogicalTrue(ValueObject &valobj, Status &error) { + return eLazyBoolCalculate; +} + +bool Language::IsNilReference(ValueObject &valobj) { return false; } + +bool Language::IsUninitializedReference(ValueObject &valobj) { return false; } + +bool Language::GetFunctionDisplayName(const SymbolContext *sc, + const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, + Stream &s) { + return false; +} + +void Language::GetExceptionResolverDescription(bool catch_on, bool throw_on, + Stream &s) { + GetDefaultExceptionResolverDescription(catch_on, throw_on, s); +} + +void Language::GetDefaultExceptionResolverDescription(bool catch_on, + bool throw_on, + Stream &s) { + s.Printf("Exception breakpoint (catch: %s throw: %s)", + catch_on ? "on" : "off", throw_on ? "on" : "off"); +} +// Constructor +Language::Language() {} + +// Destructor +Language::~Language() {} diff --git a/gnu/llvm/lldb/source/Target/LanguageRuntime.cpp b/gnu/llvm/lldb/source/Target/LanguageRuntime.cpp new file mode 100644 index 00000000000..32dd805a00b --- /dev/null +++ b/gnu/llvm/lldb/source/Target/LanguageRuntime.cpp @@ -0,0 +1,297 @@ +//===-- LanguageRuntime.cpp -------------------------------------*- C++ -*-===// +// +// 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/LanguageRuntime.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +char LanguageRuntime::ID = 0; + +ExceptionSearchFilter::ExceptionSearchFilter(const lldb::TargetSP &target_sp, + lldb::LanguageType language, + bool update_module_list) + : SearchFilter(target_sp, FilterTy::Exception), m_language(language), + m_language_runtime(nullptr), m_filter_sp() { + if (update_module_list) + UpdateModuleListIfNeeded(); +} + +bool ExceptionSearchFilter::ModulePasses(const lldb::ModuleSP &module_sp) { + UpdateModuleListIfNeeded(); + if (m_filter_sp) + return m_filter_sp->ModulePasses(module_sp); + return false; +} + +bool ExceptionSearchFilter::ModulePasses(const FileSpec &spec) { + UpdateModuleListIfNeeded(); + if (m_filter_sp) + return m_filter_sp->ModulePasses(spec); + return false; +} + +void ExceptionSearchFilter::Search(Searcher &searcher) { + UpdateModuleListIfNeeded(); + if (m_filter_sp) + m_filter_sp->Search(searcher); +} + +void ExceptionSearchFilter::GetDescription(Stream *s) { + UpdateModuleListIfNeeded(); + if (m_filter_sp) + m_filter_sp->GetDescription(s); +} + +void ExceptionSearchFilter::UpdateModuleListIfNeeded() { + ProcessSP process_sp(m_target_sp->GetProcessSP()); + if (process_sp) { + bool refreash_filter = !m_filter_sp; + if (m_language_runtime == nullptr) { + m_language_runtime = process_sp->GetLanguageRuntime(m_language); + refreash_filter = true; + } else { + LanguageRuntime *language_runtime = + process_sp->GetLanguageRuntime(m_language); + if (m_language_runtime != language_runtime) { + m_language_runtime = language_runtime; + refreash_filter = true; + } + } + + if (refreash_filter && m_language_runtime) { + m_filter_sp = m_language_runtime->CreateExceptionSearchFilter(); + } + } else { + m_filter_sp.reset(); + m_language_runtime = nullptr; + } +} + +SearchFilterSP +ExceptionSearchFilter::DoCopyForBreakpoint(Breakpoint &breakpoint) { + return SearchFilterSP( + new ExceptionSearchFilter(TargetSP(), m_language, false)); +} + +SearchFilter *ExceptionSearchFilter::CreateFromStructuredData( + Target &target, const StructuredData::Dictionary &data_dict, + Status &error) { + SearchFilter *result = nullptr; + return result; +} + +StructuredData::ObjectSP ExceptionSearchFilter::SerializeToStructuredData() { + StructuredData::ObjectSP result_sp; + + return result_sp; +} + +// The Target is the one that knows how to create breakpoints, so this function +// is meant to be used either by the target or internally in +// Set/ClearExceptionBreakpoints. +class ExceptionBreakpointResolver : public BreakpointResolver { +public: + ExceptionBreakpointResolver(lldb::LanguageType language, bool catch_bp, + bool throw_bp) + : BreakpointResolver(nullptr, BreakpointResolver::ExceptionResolver), + m_language(language), m_language_runtime(nullptr), m_catch_bp(catch_bp), + m_throw_bp(throw_bp) {} + + ~ExceptionBreakpointResolver() override = default; + + Searcher::CallbackReturn SearchCallback(SearchFilter &filter, + SymbolContext &context, + Address *addr) override { + + if (SetActualResolver()) + return m_actual_resolver_sp->SearchCallback(filter, context, addr); + else + return eCallbackReturnStop; + } + + lldb::SearchDepth GetDepth() override { + if (SetActualResolver()) + return m_actual_resolver_sp->GetDepth(); + else + return lldb::eSearchDepthTarget; + } + + void GetDescription(Stream *s) override { + Language *language_plugin = Language::FindPlugin(m_language); + if (language_plugin) + language_plugin->GetExceptionResolverDescription(m_catch_bp, m_throw_bp, + *s); + else + Language::GetDefaultExceptionResolverDescription(m_catch_bp, m_throw_bp, + *s); + + SetActualResolver(); + if (m_actual_resolver_sp) { + s->Printf(" using: "); + m_actual_resolver_sp->GetDescription(s); + } else + s->Printf(" the correct runtime exception handler will be determined " + "when you run"); + } + + void Dump(Stream *s) const override {} + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const BreakpointResolverName *) { return true; } + static inline bool classof(const BreakpointResolver *V) { + return V->getResolverID() == BreakpointResolver::ExceptionResolver; + } + +protected: + BreakpointResolverSP CopyForBreakpoint(Breakpoint &breakpoint) override { + BreakpointResolverSP ret_sp( + new ExceptionBreakpointResolver(m_language, m_catch_bp, m_throw_bp)); + ret_sp->SetBreakpoint(&breakpoint); + return ret_sp; + } + + bool SetActualResolver() { + ProcessSP process_sp; + if (m_breakpoint) { + process_sp = m_breakpoint->GetTarget().GetProcessSP(); + if (process_sp) { + bool refreash_resolver = !m_actual_resolver_sp; + if (m_language_runtime == nullptr) { + m_language_runtime = process_sp->GetLanguageRuntime(m_language); + refreash_resolver = true; + } else { + LanguageRuntime *language_runtime = + process_sp->GetLanguageRuntime(m_language); + if (m_language_runtime != language_runtime) { + m_language_runtime = language_runtime; + refreash_resolver = true; + } + } + + if (refreash_resolver && m_language_runtime) { + m_actual_resolver_sp = m_language_runtime->CreateExceptionResolver( + m_breakpoint, m_catch_bp, m_throw_bp); + } + } else { + m_actual_resolver_sp.reset(); + m_language_runtime = nullptr; + } + } else { + m_actual_resolver_sp.reset(); + m_language_runtime = nullptr; + } + return (bool)m_actual_resolver_sp; + } + + lldb::BreakpointResolverSP m_actual_resolver_sp; + lldb::LanguageType m_language; + LanguageRuntime *m_language_runtime; + bool m_catch_bp; + bool m_throw_bp; +}; + +LanguageRuntime *LanguageRuntime::FindPlugin(Process *process, + lldb::LanguageType language) { + std::unique_ptr<LanguageRuntime> language_runtime_up; + LanguageRuntimeCreateInstance create_callback; + + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(idx)) != + nullptr; + ++idx) { + language_runtime_up.reset(create_callback(process, language)); + + if (language_runtime_up) + return language_runtime_up.release(); + } + + return nullptr; +} + +LanguageRuntime::LanguageRuntime(Process *process) : m_process(process) {} + +LanguageRuntime::~LanguageRuntime() = default; + +BreakpointPreconditionSP +LanguageRuntime::GetExceptionPrecondition(LanguageType language, + bool throw_bp) { + LanguageRuntimeCreateInstance create_callback; + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(idx)) != + nullptr; + idx++) { + if (auto precondition_callback = + PluginManager::GetLanguageRuntimeGetExceptionPreconditionAtIndex( + idx)) { + if (BreakpointPreconditionSP precond = + precondition_callback(language, throw_bp)) + return precond; + } + } + return BreakpointPreconditionSP(); +} + +BreakpointSP LanguageRuntime::CreateExceptionBreakpoint( + Target &target, lldb::LanguageType language, bool catch_bp, bool throw_bp, + bool is_internal) { + BreakpointResolverSP resolver_sp( + new ExceptionBreakpointResolver(language, catch_bp, throw_bp)); + SearchFilterSP filter_sp( + new ExceptionSearchFilter(target.shared_from_this(), language)); + bool hardware = false; + bool resolve_indirect_functions = false; + BreakpointSP exc_breakpt_sp( + target.CreateBreakpoint(filter_sp, resolver_sp, is_internal, hardware, + resolve_indirect_functions)); + if (exc_breakpt_sp) { + if (auto precond = GetExceptionPrecondition(language, throw_bp)) + exc_breakpt_sp->SetPrecondition(precond); + + if (is_internal) + exc_breakpt_sp->SetBreakpointKind("exception"); + } + + return exc_breakpt_sp; +} + +void LanguageRuntime::InitializeCommands(CommandObject *parent) { + if (!parent) + return; + + if (!parent->IsMultiwordObject()) + return; + + LanguageRuntimeCreateInstance create_callback; + + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(idx)) != + nullptr; + ++idx) { + if (LanguageRuntimeGetCommandObject command_callback = + PluginManager::GetLanguageRuntimeGetCommandObjectAtIndex(idx)) { + CommandObjectSP command = + command_callback(parent->GetCommandInterpreter()); + if (command) { + // the CommandObject vended by a Language plugin cannot be created once + // and cached because we may create multiple debuggers and need one + // instance of the command each - the implementing function is meant to + // create a new instance of the command each time it is invoked. + parent->LoadSubCommand(command->GetCommandName().str().c_str(), command); + } + } + } +} diff --git a/gnu/llvm/lldb/source/Target/Memory.cpp b/gnu/llvm/lldb/source/Target/Memory.cpp new file mode 100644 index 00000000000..7c77cc06eb0 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/Memory.cpp @@ -0,0 +1,414 @@ +//===-- Memory.cpp ----------------------------------------------*- C++ -*-===// +// +// 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/Memory.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/State.h" + +#include <cinttypes> +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +// MemoryCache constructor +MemoryCache::MemoryCache(Process &process) + : m_mutex(), m_L1_cache(), m_L2_cache(), m_invalid_ranges(), + m_process(process), + m_L2_cache_line_byte_size(process.GetMemoryCacheLineSize()) {} + +// Destructor +MemoryCache::~MemoryCache() {} + +void MemoryCache::Clear(bool clear_invalid_ranges) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_L1_cache.clear(); + m_L2_cache.clear(); + if (clear_invalid_ranges) + m_invalid_ranges.Clear(); + m_L2_cache_line_byte_size = m_process.GetMemoryCacheLineSize(); +} + +void MemoryCache::AddL1CacheData(lldb::addr_t addr, const void *src, + size_t src_len) { + AddL1CacheData( + addr, DataBufferSP(new DataBufferHeap(DataBufferHeap(src, src_len)))); +} + +void MemoryCache::AddL1CacheData(lldb::addr_t addr, + const DataBufferSP &data_buffer_sp) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_L1_cache[addr] = data_buffer_sp; +} + +void MemoryCache::Flush(addr_t addr, size_t size) { + if (size == 0) + return; + + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + // Erase any blocks from the L1 cache that intersect with the flush range + if (!m_L1_cache.empty()) { + AddrRange flush_range(addr, size); + BlockMap::iterator pos = m_L1_cache.upper_bound(addr); + if (pos != m_L1_cache.begin()) { + --pos; + } + while (pos != m_L1_cache.end()) { + AddrRange chunk_range(pos->first, pos->second->GetByteSize()); + if (!chunk_range.DoesIntersect(flush_range)) + break; + pos = m_L1_cache.erase(pos); + } + } + + if (!m_L2_cache.empty()) { + const uint32_t cache_line_byte_size = m_L2_cache_line_byte_size; + const addr_t end_addr = (addr + size - 1); + const addr_t first_cache_line_addr = addr - (addr % cache_line_byte_size); + const addr_t last_cache_line_addr = + end_addr - (end_addr % cache_line_byte_size); + // Watch for overflow where size will cause us to go off the end of the + // 64 bit address space + uint32_t num_cache_lines; + if (last_cache_line_addr >= first_cache_line_addr) + num_cache_lines = ((last_cache_line_addr - first_cache_line_addr) / + cache_line_byte_size) + + 1; + else + num_cache_lines = + (UINT64_MAX - first_cache_line_addr + 1) / cache_line_byte_size; + + uint32_t cache_idx = 0; + for (addr_t curr_addr = first_cache_line_addr; cache_idx < num_cache_lines; + curr_addr += cache_line_byte_size, ++cache_idx) { + BlockMap::iterator pos = m_L2_cache.find(curr_addr); + if (pos != m_L2_cache.end()) + m_L2_cache.erase(pos); + } + } +} + +void MemoryCache::AddInvalidRange(lldb::addr_t base_addr, + lldb::addr_t byte_size) { + if (byte_size > 0) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + InvalidRanges::Entry range(base_addr, byte_size); + m_invalid_ranges.Append(range); + m_invalid_ranges.Sort(); + } +} + +bool MemoryCache::RemoveInvalidRange(lldb::addr_t base_addr, + lldb::addr_t byte_size) { + if (byte_size > 0) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + const uint32_t idx = m_invalid_ranges.FindEntryIndexThatContains(base_addr); + if (idx != UINT32_MAX) { + const InvalidRanges::Entry *entry = m_invalid_ranges.GetEntryAtIndex(idx); + if (entry->GetRangeBase() == base_addr && + entry->GetByteSize() == byte_size) + return m_invalid_ranges.RemoveEntrtAtIndex(idx); + } + } + return false; +} + +size_t MemoryCache::Read(addr_t addr, void *dst, size_t dst_len, + Status &error) { + size_t bytes_left = dst_len; + + // Check the L1 cache for a range that contain the entire memory read. If we + // find a range in the L1 cache that does, we use it. Else we fall back to + // reading memory in m_L2_cache_line_byte_size byte sized chunks. The L1 + // cache contains chunks of memory that are not required to be + // m_L2_cache_line_byte_size bytes in size, so we don't try anything tricky + // when reading from them (no partial reads from the L1 cache). + + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (!m_L1_cache.empty()) { + AddrRange read_range(addr, dst_len); + BlockMap::iterator pos = m_L1_cache.upper_bound(addr); + if (pos != m_L1_cache.begin()) { + --pos; + } + AddrRange chunk_range(pos->first, pos->second->GetByteSize()); + if (chunk_range.Contains(read_range)) { + memcpy(dst, pos->second->GetBytes() + (addr - chunk_range.GetRangeBase()), + dst_len); + return dst_len; + } + } + + // If this memory read request is larger than the cache line size, then we + // (1) try to read as much of it at once as possible, and (2) don't add the + // data to the memory cache. We don't want to split a big read up into more + // separate reads than necessary, and with a large memory read request, it is + // unlikely that the caller function will ask for the next + // 4 bytes after the large memory read - so there's little benefit to saving + // it in the cache. + if (dst && dst_len > m_L2_cache_line_byte_size) { + size_t bytes_read = + m_process.ReadMemoryFromInferior(addr, dst, dst_len, error); + // Add this non block sized range to the L1 cache if we actually read + // anything + if (bytes_read > 0) + AddL1CacheData(addr, dst, bytes_read); + return bytes_read; + } + + if (dst && bytes_left > 0) { + const uint32_t cache_line_byte_size = m_L2_cache_line_byte_size; + uint8_t *dst_buf = (uint8_t *)dst; + addr_t curr_addr = addr - (addr % cache_line_byte_size); + addr_t cache_offset = addr - curr_addr; + + while (bytes_left > 0) { + if (m_invalid_ranges.FindEntryThatContains(curr_addr)) { + error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, + curr_addr); + return dst_len - bytes_left; + } + + BlockMap::const_iterator pos = m_L2_cache.find(curr_addr); + BlockMap::const_iterator end = m_L2_cache.end(); + + if (pos != end) { + size_t curr_read_size = cache_line_byte_size - cache_offset; + if (curr_read_size > bytes_left) + curr_read_size = bytes_left; + + memcpy(dst_buf + dst_len - bytes_left, + pos->second->GetBytes() + cache_offset, curr_read_size); + + bytes_left -= curr_read_size; + curr_addr += curr_read_size + cache_offset; + cache_offset = 0; + + if (bytes_left > 0) { + // Get sequential cache page hits + for (++pos; (pos != end) && (bytes_left > 0); ++pos) { + assert((curr_addr % cache_line_byte_size) == 0); + + if (pos->first != curr_addr) + break; + + curr_read_size = pos->second->GetByteSize(); + if (curr_read_size > bytes_left) + curr_read_size = bytes_left; + + memcpy(dst_buf + dst_len - bytes_left, pos->second->GetBytes(), + curr_read_size); + + bytes_left -= curr_read_size; + curr_addr += curr_read_size; + + // We have a cache page that succeeded to read some bytes but not + // an entire page. If this happens, we must cap off how much data + // we are able to read... + if (pos->second->GetByteSize() != cache_line_byte_size) + return dst_len - bytes_left; + } + } + } + + // We need to read from the process + + if (bytes_left > 0) { + assert((curr_addr % cache_line_byte_size) == 0); + std::unique_ptr<DataBufferHeap> data_buffer_heap_up( + new DataBufferHeap(cache_line_byte_size, 0)); + size_t process_bytes_read = m_process.ReadMemoryFromInferior( + curr_addr, data_buffer_heap_up->GetBytes(), + data_buffer_heap_up->GetByteSize(), error); + if (process_bytes_read == 0) + return dst_len - bytes_left; + + if (process_bytes_read != cache_line_byte_size) + data_buffer_heap_up->SetByteSize(process_bytes_read); + m_L2_cache[curr_addr] = DataBufferSP(data_buffer_heap_up.release()); + // We have read data and put it into the cache, continue through the + // loop again to get the data out of the cache... + } + } + } + + return dst_len - bytes_left; +} + +AllocatedBlock::AllocatedBlock(lldb::addr_t addr, uint32_t byte_size, + uint32_t permissions, uint32_t chunk_size) + : m_range(addr, byte_size), m_permissions(permissions), + m_chunk_size(chunk_size) +{ + // The entire address range is free to start with. + m_free_blocks.Append(m_range); + assert(byte_size > chunk_size); +} + +AllocatedBlock::~AllocatedBlock() {} + +lldb::addr_t AllocatedBlock::ReserveBlock(uint32_t size) { + // We must return something valid for zero bytes. + if (size == 0) + size = 1; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + const size_t free_count = m_free_blocks.GetSize(); + for (size_t i=0; i<free_count; ++i) + { + auto &free_block = m_free_blocks.GetEntryRef(i); + const lldb::addr_t range_size = free_block.GetByteSize(); + if (range_size >= size) + { + // We found a free block that is big enough for our data. Figure out how + // many chunks we will need and calculate the resulting block size we + // will reserve. + addr_t addr = free_block.GetRangeBase(); + size_t num_chunks = CalculateChunksNeededForSize(size); + lldb::addr_t block_size = num_chunks * m_chunk_size; + lldb::addr_t bytes_left = range_size - block_size; + if (bytes_left == 0) + { + // The newly allocated block will take all of the bytes in this + // available block, so we can just add it to the allocated ranges and + // remove the range from the free ranges. + m_reserved_blocks.Insert(free_block, false); + m_free_blocks.RemoveEntryAtIndex(i); + } + else + { + // Make the new allocated range and add it to the allocated ranges. + Range<lldb::addr_t, uint32_t> reserved_block(free_block); + reserved_block.SetByteSize(block_size); + // Insert the reserved range and don't combine it with other blocks in + // the reserved blocks list. + m_reserved_blocks.Insert(reserved_block, false); + // Adjust the free range in place since we won't change the sorted + // ordering of the m_free_blocks list. + free_block.SetRangeBase(reserved_block.GetRangeEnd()); + free_block.SetByteSize(bytes_left); + } + LLDB_LOGV(log, "({0}) (size = {1} ({1:x})) => {2:x}", this, size, addr); + return addr; + } + } + + LLDB_LOGV(log, "({0}) (size = {1} ({1:x})) => {2:x}", this, size, + LLDB_INVALID_ADDRESS); + return LLDB_INVALID_ADDRESS; +} + +bool AllocatedBlock::FreeBlock(addr_t addr) { + bool success = false; + auto entry_idx = m_reserved_blocks.FindEntryIndexThatContains(addr); + if (entry_idx != UINT32_MAX) + { + m_free_blocks.Insert(m_reserved_blocks.GetEntryRef(entry_idx), true); + m_reserved_blocks.RemoveEntryAtIndex(entry_idx); + success = true; + } + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGV(log, "({0}) (addr = {1:x}) => {2}", this, addr, success); + return success; +} + +AllocatedMemoryCache::AllocatedMemoryCache(Process &process) + : m_process(process), m_mutex(), m_memory_map() {} + +AllocatedMemoryCache::~AllocatedMemoryCache() {} + +void AllocatedMemoryCache::Clear() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (m_process.IsAlive()) { + PermissionsToBlockMap::iterator pos, end = m_memory_map.end(); + for (pos = m_memory_map.begin(); pos != end; ++pos) + m_process.DoDeallocateMemory(pos->second->GetBaseAddress()); + } + m_memory_map.clear(); +} + +AllocatedMemoryCache::AllocatedBlockSP +AllocatedMemoryCache::AllocatePage(uint32_t byte_size, uint32_t permissions, + uint32_t chunk_size, Status &error) { + AllocatedBlockSP block_sp; + const size_t page_size = 4096; + const size_t num_pages = (byte_size + page_size - 1) / page_size; + const size_t page_byte_size = num_pages * page_size; + + addr_t addr = m_process.DoAllocateMemory(page_byte_size, permissions, error); + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) { + LLDB_LOGF(log, + "Process::DoAllocateMemory (byte_size = 0x%8.8" PRIx32 + ", permissions = %s) => 0x%16.16" PRIx64, + (uint32_t)page_byte_size, GetPermissionsAsCString(permissions), + (uint64_t)addr); + } + + if (addr != LLDB_INVALID_ADDRESS) { + block_sp = std::make_shared<AllocatedBlock>(addr, page_byte_size, + permissions, chunk_size); + m_memory_map.insert(std::make_pair(permissions, block_sp)); + } + return block_sp; +} + +lldb::addr_t AllocatedMemoryCache::AllocateMemory(size_t byte_size, + uint32_t permissions, + Status &error) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + addr_t addr = LLDB_INVALID_ADDRESS; + std::pair<PermissionsToBlockMap::iterator, PermissionsToBlockMap::iterator> + range = m_memory_map.equal_range(permissions); + + for (PermissionsToBlockMap::iterator pos = range.first; pos != range.second; + ++pos) { + addr = (*pos).second->ReserveBlock(byte_size); + if (addr != LLDB_INVALID_ADDRESS) + break; + } + + if (addr == LLDB_INVALID_ADDRESS) { + AllocatedBlockSP block_sp(AllocatePage(byte_size, permissions, 16, error)); + + if (block_sp) + addr = block_sp->ReserveBlock(byte_size); + } + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, + "AllocatedMemoryCache::AllocateMemory (byte_size = 0x%8.8" PRIx32 + ", permissions = %s) => 0x%16.16" PRIx64, + (uint32_t)byte_size, GetPermissionsAsCString(permissions), + (uint64_t)addr); + return addr; +} + +bool AllocatedMemoryCache::DeallocateMemory(lldb::addr_t addr) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + PermissionsToBlockMap::iterator pos, end = m_memory_map.end(); + bool success = false; + for (pos = m_memory_map.begin(); pos != end; ++pos) { + if (pos->second->Contains(addr)) { + success = pos->second->FreeBlock(addr); + break; + } + } + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, + "AllocatedMemoryCache::DeallocateMemory (addr = 0x%16.16" PRIx64 + ") => %i", + (uint64_t)addr, success); + return success; +} diff --git a/gnu/llvm/lldb/source/Target/MemoryHistory.cpp b/gnu/llvm/lldb/source/Target/MemoryHistory.cpp new file mode 100644 index 00000000000..37148e1e72e --- /dev/null +++ b/gnu/llvm/lldb/source/Target/MemoryHistory.cpp @@ -0,0 +1,28 @@ +//===-- MemoryHistory.cpp ---------------------------------------*- C++ -*-===// +// +// 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/MemoryHistory.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::MemoryHistorySP MemoryHistory::FindPlugin(const ProcessSP process) { + MemoryHistoryCreateInstance create_callback = nullptr; + + for (uint32_t idx = 0; + (create_callback = PluginManager::GetMemoryHistoryCreateCallbackAtIndex( + idx)) != nullptr; + ++idx) { + MemoryHistorySP memory_history_sp(create_callback(process)); + if (memory_history_sp) + return memory_history_sp; + } + + return MemoryHistorySP(); +} diff --git a/gnu/llvm/lldb/source/Target/MemoryRegionInfo.cpp b/gnu/llvm/lldb/source/Target/MemoryRegionInfo.cpp new file mode 100644 index 00000000000..2c31563786a --- /dev/null +++ b/gnu/llvm/lldb/source/Target/MemoryRegionInfo.cpp @@ -0,0 +1,40 @@ +//===-- MemoryRegionInfo.cpp ------------------------------------*- C++ -*-===// +// +// 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/MemoryRegionInfo.h" + +using namespace lldb_private; + +llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &OS, + const MemoryRegionInfo &Info) { + return OS << llvm::formatv("MemoryRegionInfo([{0}, {1}), {2:r}{3:w}{4:x}, " + "{5}, `{6}`, {7}, {8})", + Info.GetRange().GetRangeBase(), + Info.GetRange().GetRangeEnd(), Info.GetReadable(), + Info.GetWritable(), Info.GetExecutable(), + Info.GetMapped(), Info.GetName(), Info.GetFlash(), + Info.GetBlocksize()); +} + +void llvm::format_provider<MemoryRegionInfo::OptionalBool>::format( + const MemoryRegionInfo::OptionalBool &B, raw_ostream &OS, + StringRef Options) { + assert(Options.size() <= 1); + bool Empty = Options.empty(); + switch (B) { + case lldb_private::MemoryRegionInfo::eNo: + OS << (Empty ? "no" : "-"); + return; + case lldb_private::MemoryRegionInfo::eYes: + OS << (Empty ? "yes" : Options); + return; + case lldb_private::MemoryRegionInfo::eDontKnow: + OS << (Empty ? "don't know" : "?"); + return; + } +} diff --git a/gnu/llvm/lldb/source/Target/ModuleCache.cpp b/gnu/llvm/lldb/source/Target/ModuleCache.cpp new file mode 100644 index 00000000000..124cdacfb4d --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ModuleCache.cpp @@ -0,0 +1,335 @@ +//===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===// +// +// 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/ModuleCache.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/File.h" +#include "lldb/Host/LockFile.h" +#include "lldb/Utility/Log.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" + +#include <assert.h> + +#include <cstdio> + +using namespace lldb; +using namespace lldb_private; + +namespace { + +const char *kModulesSubdir = ".cache"; +const char *kLockDirName = ".lock"; +const char *kTempFileName = ".temp"; +const char *kTempSymFileName = ".symtemp"; +const char *kSymFileExtension = ".sym"; +const char *kFSIllegalChars = "\\/:*?\"<>|"; + +std::string GetEscapedHostname(const char *hostname) { + if (hostname == nullptr) + hostname = "unknown"; + std::string result(hostname); + size_t size = result.size(); + for (size_t i = 0; i < size; ++i) { + if ((result[i] >= 1 && result[i] <= 31) || + strchr(kFSIllegalChars, result[i]) != nullptr) + result[i] = '_'; + } + return result; +} + +class ModuleLock { +private: + FileUP m_file_up; + std::unique_ptr<lldb_private::LockFile> m_lock; + FileSpec m_file_spec; + +public: + ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error); + void Delete(); +}; + +static FileSpec JoinPath(const FileSpec &path1, const char *path2) { + FileSpec result_spec(path1); + result_spec.AppendPathComponent(path2); + return result_spec; +} + +static Status MakeDirectory(const FileSpec &dir_path) { + namespace fs = llvm::sys::fs; + + return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all); +} + +FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { + const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir); + return JoinPath(modules_dir_spec, uuid.GetAsString().c_str()); +} + +FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { + return FileSpec(module_file_spec.GetPath() + kSymFileExtension); +} + +void DeleteExistingModule(const FileSpec &root_dir_spec, + const FileSpec &sysroot_module_path_spec) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); + UUID module_uuid; + { + auto module_sp = + std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec)); + module_uuid = module_sp->GetUUID(); + } + + if (!module_uuid.IsValid()) + return; + + Status error; + ModuleLock lock(root_dir_spec, module_uuid, error); + if (error.Fail()) { + LLDB_LOGF(log, "Failed to lock module %s: %s", + module_uuid.GetAsString().c_str(), error.AsCString()); + } + + namespace fs = llvm::sys::fs; + fs::file_status st; + if (status(sysroot_module_path_spec.GetPath(), st)) + return; + + if (st.getLinkCount() > 2) // module is referred by other hosts. + return; + + const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid); + llvm::sys::fs::remove_directories(module_spec_dir.GetPath()); + lock.Delete(); +} + +void DecrementRefExistingModule(const FileSpec &root_dir_spec, + const FileSpec &sysroot_module_path_spec) { + // Remove $platform/.cache/$uuid folder if nobody else references it. + DeleteExistingModule(root_dir_spec, sysroot_module_path_spec); + + // Remove sysroot link. + llvm::sys::fs::remove(sysroot_module_path_spec.GetPath()); + + FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec); + llvm::sys::fs::remove(symfile_spec.GetPath()); +} + +Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, + const char *hostname, + const FileSpec &platform_module_spec, + const FileSpec &local_module_spec, + bool delete_existing) { + const auto sysroot_module_path_spec = + JoinPath(JoinPath(root_dir_spec, hostname), + platform_module_spec.GetPath().c_str()); + if (FileSystem::Instance().Exists(sysroot_module_path_spec)) { + if (!delete_existing) + return Status(); + + DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); + } + + const auto error = MakeDirectory( + FileSpec(sysroot_module_path_spec.GetDirectory().AsCString())); + if (error.Fail()) + return error; + + return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(), + sysroot_module_path_spec.GetPath()); +} + +} // namespace + +ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, + Status &error) { + const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName); + error = MakeDirectory(lock_dir_spec); + if (error.Fail()) + return; + + m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); + + auto file = FileSystem::Instance().Open( + m_file_spec, File::eOpenOptionWrite | File::eOpenOptionCanCreate | + File::eOpenOptionCloseOnExec); + if (file) + m_file_up = std::move(file.get()); + else { + m_file_up.reset(); + error = Status(file.takeError()); + return; + } + + m_lock.reset(new lldb_private::LockFile(m_file_up->GetDescriptor())); + error = m_lock->WriteLock(0, 1); + if (error.Fail()) + error.SetErrorStringWithFormat("Failed to lock file: %s", + error.AsCString()); +} + +void ModuleLock::Delete() { + if (!m_file_up) + return; + + m_file_up->Close(); + m_file_up.reset(); + llvm::sys::fs::remove(m_file_spec.GetPath()); +} + +///////////////////////////////////////////////////////////////////////// + +Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, + const ModuleSpec &module_spec, const FileSpec &tmp_file, + const FileSpec &target_file) { + const auto module_spec_dir = + GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); + const auto module_file_path = + JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); + + const auto tmp_file_path = tmp_file.GetPath(); + const auto err_code = + llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); + if (err_code) + return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), + module_file_path.GetPath().c_str(), + err_code.message().c_str()); + + const auto error = CreateHostSysRootModuleLink( + root_dir_spec, hostname, target_file, module_file_path, true); + if (error.Fail()) + return Status("Failed to create link to %s: %s", + module_file_path.GetPath().c_str(), error.AsCString()); + return Status(); +} + +Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, + const ModuleSpec &module_spec, + ModuleSP &cached_module_sp, bool *did_create_ptr) { + const auto find_it = + m_loaded_modules.find(module_spec.GetUUID().GetAsString()); + if (find_it != m_loaded_modules.end()) { + cached_module_sp = (*find_it).second.lock(); + if (cached_module_sp) + return Status(); + m_loaded_modules.erase(find_it); + } + + const auto module_spec_dir = + GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); + const auto module_file_path = JoinPath( + module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); + + if (!FileSystem::Instance().Exists(module_file_path)) + return Status("Module %s not found", module_file_path.GetPath().c_str()); + if (FileSystem::Instance().GetByteSize(module_file_path) != + module_spec.GetObjectSize()) + return Status("Module %s has invalid file size", + module_file_path.GetPath().c_str()); + + // We may have already cached module but downloaded from an another host - in + // this case let's create a link to it. + auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, + module_spec.GetFileSpec(), + module_file_path, false); + if (error.Fail()) + return Status("Failed to create link to %s: %s", + module_file_path.GetPath().c_str(), error.AsCString()); + + auto cached_module_spec(module_spec); + cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 + // content hash instead of real UUID. + cached_module_spec.GetFileSpec() = module_file_path; + cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); + + error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, + nullptr, nullptr, did_create_ptr, false); + if (error.Fail()) + return error; + + FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); + if (FileSystem::Instance().Exists(symfile_spec)) + cached_module_sp->SetSymbolFileFileSpec(symfile_spec); + + m_loaded_modules.insert( + std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); + + return Status(); +} + +Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec, + const char *hostname, + const ModuleSpec &module_spec, + const ModuleDownloader &module_downloader, + const SymfileDownloader &symfile_downloader, + lldb::ModuleSP &cached_module_sp, + bool *did_create_ptr) { + const auto module_spec_dir = + GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); + auto error = MakeDirectory(module_spec_dir); + if (error.Fail()) + return error; + + ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); + if (error.Fail()) + return Status("Failed to lock module %s: %s", + module_spec.GetUUID().GetAsString().c_str(), + error.AsCString()); + + const auto escaped_hostname(GetEscapedHostname(hostname)); + // Check local cache for a module. + error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, + cached_module_sp, did_create_ptr); + if (error.Success()) + return error; + + const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); + error = module_downloader(module_spec, tmp_download_file_spec); + llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); + if (error.Fail()) + return Status("Failed to download module: %s", error.AsCString()); + + // Put downloaded file into local module cache. + error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, + tmp_download_file_spec, module_spec.GetFileSpec()); + if (error.Fail()) + return Status("Failed to put module into cache: %s", error.AsCString()); + + tmp_file_remover.releaseFile(); + error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, + cached_module_sp, did_create_ptr); + if (error.Fail()) + return error; + + // Fetching a symbol file for the module + const auto tmp_download_sym_file_spec = + JoinPath(module_spec_dir, kTempSymFileName); + error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); + llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); + if (error.Fail()) + // Failed to download a symfile but fetching the module was successful. The + // module might contain the necessary symbols and the debugging is also + // possible without a symfile. + return Status(); + + error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, + tmp_download_sym_file_spec, + GetSymbolFileSpec(module_spec.GetFileSpec())); + if (error.Fail()) + return Status("Failed to put symbol file into cache: %s", + error.AsCString()); + + tmp_symfile_remover.releaseFile(); + + FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); + cached_module_sp->SetSymbolFileFileSpec(symfile_spec); + return Status(); +} diff --git a/gnu/llvm/lldb/source/Target/OperatingSystem.cpp b/gnu/llvm/lldb/source/Target/OperatingSystem.cpp new file mode 100644 index 00000000000..c78c197db5b --- /dev/null +++ b/gnu/llvm/lldb/source/Target/OperatingSystem.cpp @@ -0,0 +1,54 @@ +//===-- OperatingSystem.cpp -------------------------------------*- C++ -*-===// +// +// 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/OperatingSystem.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +OperatingSystem *OperatingSystem::FindPlugin(Process *process, + const char *plugin_name) { + OperatingSystemCreateInstance create_callback = nullptr; + if (plugin_name) { + ConstString const_plugin_name(plugin_name); + create_callback = + PluginManager::GetOperatingSystemCreateCallbackForPluginName( + const_plugin_name); + if (create_callback) { + std::unique_ptr<OperatingSystem> instance_up( + create_callback(process, true)); + if (instance_up) + return instance_up.release(); + } + } else { + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetOperatingSystemCreateCallbackAtIndex(idx)) != + nullptr; + ++idx) { + std::unique_ptr<OperatingSystem> instance_up( + create_callback(process, false)); + if (instance_up) + return instance_up.release(); + } + } + return nullptr; +} + +OperatingSystem::OperatingSystem(Process *process) : m_process(process) {} + +OperatingSystem::~OperatingSystem() = default; + +bool OperatingSystem::IsOperatingSystemPluginThread( + const lldb::ThreadSP &thread_sp) { + if (thread_sp) + return thread_sp->IsOperatingSystemPluginThread(); + return false; +} diff --git a/gnu/llvm/lldb/source/Target/PathMappingList.cpp b/gnu/llvm/lldb/source/Target/PathMappingList.cpp new file mode 100644 index 00000000000..58c2c43c936 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/PathMappingList.cpp @@ -0,0 +1,317 @@ +//===-- PathMappingList.cpp -------------------------------------*- C++ -*-===// +// +// 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 <climits> +#include <cstring> + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Target/PathMappingList.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/lldb-private-enumerations.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + // We must normalize our path pairs that we store because if we don't then + // things won't always work. We found a case where if we did: + // (lldb) settings set target.source-map . /tmp + // We would store a path pairs of "." and "/tmp" as raw strings. If the debug + // info contains "./foo/bar.c", the path will get normalized to "foo/bar.c". + // When PathMappingList::RemapPath() is called, it expects the path to start + // with the raw path pair, which doesn't work anymore because the paths have + // been normalized when the debug info was loaded. So we need to store + // nomalized path pairs to ensure things match up. + ConstString NormalizePath(ConstString path) { + // If we use "path" to construct a FileSpec, it will normalize the path for + // us. We then grab the string and turn it back into a ConstString. + return ConstString(FileSpec(path.GetStringRef()).GetPath()); + } +} +// PathMappingList constructor +PathMappingList::PathMappingList() + : m_pairs(), m_callback(nullptr), m_callback_baton(nullptr), m_mod_id(0) {} + +PathMappingList::PathMappingList(ChangedCallback callback, void *callback_baton) + : m_pairs(), m_callback(callback), m_callback_baton(callback_baton), + m_mod_id(0) {} + +PathMappingList::PathMappingList(const PathMappingList &rhs) + : m_pairs(rhs.m_pairs), m_callback(nullptr), m_callback_baton(nullptr), + m_mod_id(0) {} + +const PathMappingList &PathMappingList::operator=(const PathMappingList &rhs) { + if (this != &rhs) { + m_pairs = rhs.m_pairs; + m_callback = nullptr; + m_callback_baton = nullptr; + m_mod_id = rhs.m_mod_id; + } + return *this; +} + +PathMappingList::~PathMappingList() = default; + +void PathMappingList::Append(ConstString path, + ConstString replacement, bool notify) { + ++m_mod_id; + m_pairs.emplace_back(pair(NormalizePath(path), NormalizePath(replacement))); + if (notify && m_callback) + m_callback(*this, m_callback_baton); +} + +void PathMappingList::Append(const PathMappingList &rhs, bool notify) { + ++m_mod_id; + if (!rhs.m_pairs.empty()) { + const_iterator pos, end = rhs.m_pairs.end(); + for (pos = rhs.m_pairs.begin(); pos != end; ++pos) + m_pairs.push_back(*pos); + if (notify && m_callback) + m_callback(*this, m_callback_baton); + } +} + +void PathMappingList::Insert(ConstString path, + ConstString replacement, uint32_t index, + bool notify) { + ++m_mod_id; + iterator insert_iter; + if (index >= m_pairs.size()) + insert_iter = m_pairs.end(); + else + insert_iter = m_pairs.begin() + index; + m_pairs.emplace(insert_iter, pair(NormalizePath(path), + NormalizePath(replacement))); + if (notify && m_callback) + m_callback(*this, m_callback_baton); +} + +bool PathMappingList::Replace(ConstString path, + ConstString replacement, uint32_t index, + bool notify) { + if (index >= m_pairs.size()) + return false; + ++m_mod_id; + m_pairs[index] = pair(NormalizePath(path), NormalizePath(replacement)); + if (notify && m_callback) + m_callback(*this, m_callback_baton); + return true; +} + +bool PathMappingList::Remove(size_t index, bool notify) { + if (index >= m_pairs.size()) + return false; + + ++m_mod_id; + iterator iter = m_pairs.begin() + index; + m_pairs.erase(iter); + if (notify && m_callback) + m_callback(*this, m_callback_baton); + return true; +} + +// For clients which do not need the pair index dumped, pass a pair_index >= 0 +// to only dump the indicated pair. +void PathMappingList::Dump(Stream *s, int pair_index) { + unsigned int numPairs = m_pairs.size(); + + if (pair_index < 0) { + unsigned int index; + for (index = 0; index < numPairs; ++index) + s->Printf("[%d] \"%s\" -> \"%s\"\n", index, + m_pairs[index].first.GetCString(), + m_pairs[index].second.GetCString()); + } else { + if (static_cast<unsigned int>(pair_index) < numPairs) + s->Printf("%s -> %s", m_pairs[pair_index].first.GetCString(), + m_pairs[pair_index].second.GetCString()); + } +} + +void PathMappingList::Clear(bool notify) { + if (!m_pairs.empty()) + ++m_mod_id; + m_pairs.clear(); + if (notify && m_callback) + m_callback(*this, m_callback_baton); +} + +bool PathMappingList::RemapPath(ConstString path, + ConstString &new_path) const { + std::string remapped; + if (RemapPath(path.GetStringRef(), remapped)) { + new_path.SetString(remapped); + return true; + } + return false; +} + +bool PathMappingList::RemapPath(llvm::StringRef path, + std::string &new_path) const { + if (m_pairs.empty() || path.empty()) + return false; + LazyBool path_is_relative = eLazyBoolCalculate; + for (const auto &it : m_pairs) { + auto prefix = it.first.GetStringRef(); + if (!path.consume_front(prefix)) { + // Relative paths won't have a leading "./" in them unless "." is the + // only thing in the relative path so we need to work around "." + // carefully. + if (prefix != ".") + continue; + // We need to figure out if the "path" argument is relative. If it is, + // then we should remap, else skip this entry. + if (path_is_relative == eLazyBoolCalculate) { + path_is_relative = + FileSpec(path).IsRelative() ? eLazyBoolYes : eLazyBoolNo; + } + if (!path_is_relative) + continue; + } + FileSpec remapped(it.second.GetStringRef()); + remapped.AppendPathComponent(path); + new_path = remapped.GetPath(); + return true; + } + return false; +} + +bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const { + std::string path = file.GetPath(); + llvm::StringRef path_ref(path); + for (const auto &it : m_pairs) { + if (!path_ref.consume_front(it.second.GetStringRef())) + continue; + fixed.SetFile(it.first.GetStringRef(), FileSpec::Style::native); + fixed.AppendPathComponent(path_ref); + return true; + } + return false; +} + +bool PathMappingList::FindFile(const FileSpec &orig_spec, + FileSpec &new_spec) const { + if (m_pairs.empty()) + return false; + + std::string orig_path = orig_spec.GetPath(); + + if (orig_path.empty()) + return false; + + bool orig_is_relative = orig_spec.IsRelative(); + + for (auto entry : m_pairs) { + llvm::StringRef orig_ref(orig_path); + llvm::StringRef prefix_ref = entry.first.GetStringRef(); + if (orig_ref.size() < prefix_ref.size()) + continue; + // We consider a relative prefix or one of just "." to + // mean "only apply to relative paths". + bool prefix_is_relative = false; + + if (prefix_ref == ".") { + prefix_is_relative = true; + // Remove the "." since it will have been removed from the + // FileSpec paths already. + prefix_ref = prefix_ref.drop_front(); + } else { + FileSpec prefix_spec(prefix_ref, FileSpec::Style::native); + prefix_is_relative = prefix_spec.IsRelative(); + } + if (prefix_is_relative != orig_is_relative) + continue; + + if (orig_ref.consume_front(prefix_ref)) { + new_spec.SetFile(entry.second.GetCString(), FileSpec::Style::native); + new_spec.AppendPathComponent(orig_ref); + if (FileSystem::Instance().Exists(new_spec)) + return true; + } + } + + new_spec.Clear(); + return false; +} + +bool PathMappingList::Replace(ConstString path, + ConstString new_path, bool notify) { + uint32_t idx = FindIndexForPath(path); + if (idx < m_pairs.size()) { + ++m_mod_id; + m_pairs[idx].second = new_path; + if (notify && m_callback) + m_callback(*this, m_callback_baton); + return true; + } + return false; +} + +bool PathMappingList::Remove(ConstString path, bool notify) { + iterator pos = FindIteratorForPath(path); + if (pos != m_pairs.end()) { + ++m_mod_id; + m_pairs.erase(pos); + if (notify && m_callback) + m_callback(*this, m_callback_baton); + return true; + } + return false; +} + +PathMappingList::const_iterator +PathMappingList::FindIteratorForPath(ConstString path) const { + const_iterator pos; + const_iterator begin = m_pairs.begin(); + const_iterator end = m_pairs.end(); + + for (pos = begin; pos != end; ++pos) { + if (pos->first == path) + break; + } + return pos; +} + +PathMappingList::iterator +PathMappingList::FindIteratorForPath(ConstString path) { + iterator pos; + iterator begin = m_pairs.begin(); + iterator end = m_pairs.end(); + + for (pos = begin; pos != end; ++pos) { + if (pos->first == path) + break; + } + return pos; +} + +bool PathMappingList::GetPathsAtIndex(uint32_t idx, ConstString &path, + ConstString &new_path) const { + if (idx < m_pairs.size()) { + path = m_pairs[idx].first; + new_path = m_pairs[idx].second; + return true; + } + return false; +} + +uint32_t PathMappingList::FindIndexForPath(ConstString orig_path) const { + const ConstString path = NormalizePath(orig_path); + const_iterator pos; + const_iterator begin = m_pairs.begin(); + const_iterator end = m_pairs.end(); + + for (pos = begin; pos != end; ++pos) { + if (pos->first == path) + return std::distance(begin, pos); + } + return UINT32_MAX; +} diff --git a/gnu/llvm/lldb/source/Target/Platform.cpp b/gnu/llvm/lldb/source/Target/Platform.cpp new file mode 100644 index 00000000000..aaf48f35f92 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/Platform.cpp @@ -0,0 +1,1914 @@ +//===-- Platform.cpp --------------------------------------------*- C++ -*-===// +// +// 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 <algorithm> +#include <csignal> +#include <fstream> +#include <memory> +#include <vector> + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +#include "lldb/Breakpoint/BreakpointIDList.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ModuleCache.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StructuredData.h" + +#include "llvm/Support/FileSystem.h" + +// Define these constants from POSIX mman.h rather than include the file so +// that they will be correct even when compiled on Linux. +#define MAP_PRIVATE 2 +#define MAP_ANON 0x1000 + +using namespace lldb; +using namespace lldb_private; + +static uint32_t g_initialize_count = 0; + +// Use a singleton function for g_local_platform_sp to avoid init constructors +// since LLDB is often part of a shared library +static PlatformSP &GetHostPlatformSP() { + static PlatformSP g_platform_sp; + return g_platform_sp; +} + +const char *Platform::GetHostPlatformName() { return "host"; } + +namespace { + +#define LLDB_PROPERTIES_platform +#include "TargetProperties.inc" + +enum { +#define LLDB_PROPERTIES_platform +#include "TargetPropertiesEnum.inc" +}; + +} // namespace + +ConstString PlatformProperties::GetSettingName() { + static ConstString g_setting_name("platform"); + return g_setting_name; +} + +PlatformProperties::PlatformProperties() { + m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName()); + m_collection_sp->Initialize(g_platform_properties); + + auto module_cache_dir = GetModuleCacheDirectory(); + if (module_cache_dir) + return; + + llvm::SmallString<64> user_home_dir; + if (!llvm::sys::path::home_directory(user_home_dir)) + return; + + module_cache_dir = FileSpec(user_home_dir.c_str()); + module_cache_dir.AppendPathComponent(".lldb"); + module_cache_dir.AppendPathComponent("module_cache"); + SetModuleCacheDirectory(module_cache_dir); +} + +bool PlatformProperties::GetUseModuleCache() const { + const auto idx = ePropertyUseModuleCache; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_platform_properties[idx].default_uint_value != 0); +} + +bool PlatformProperties::SetUseModuleCache(bool use_module_cache) { + return m_collection_sp->SetPropertyAtIndexAsBoolean( + nullptr, ePropertyUseModuleCache, use_module_cache); +} + +FileSpec PlatformProperties::GetModuleCacheDirectory() const { + return m_collection_sp->GetPropertyAtIndexAsFileSpec( + nullptr, ePropertyModuleCacheDirectory); +} + +bool PlatformProperties::SetModuleCacheDirectory(const FileSpec &dir_spec) { + return m_collection_sp->SetPropertyAtIndexAsFileSpec( + nullptr, ePropertyModuleCacheDirectory, dir_spec); +} + +/// Get the native host platform plug-in. +/// +/// There should only be one of these for each host that LLDB runs +/// upon that should be statically compiled in and registered using +/// preprocessor macros or other similar build mechanisms. +/// +/// This platform will be used as the default platform when launching +/// or attaching to processes unless another platform is specified. +PlatformSP Platform::GetHostPlatform() { return GetHostPlatformSP(); } + +static std::vector<PlatformSP> &GetPlatformList() { + static std::vector<PlatformSP> g_platform_list; + return g_platform_list; +} + +static std::recursive_mutex &GetPlatformListMutex() { + static std::recursive_mutex g_mutex; + return g_mutex; +} + +void Platform::Initialize() { g_initialize_count++; } + +void Platform::Terminate() { + if (g_initialize_count > 0) { + if (--g_initialize_count == 0) { + std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex()); + GetPlatformList().clear(); + } + } +} + +const PlatformPropertiesSP &Platform::GetGlobalPlatformProperties() { + static const auto g_settings_sp(std::make_shared<PlatformProperties>()); + return g_settings_sp; +} + +void Platform::SetHostPlatform(const lldb::PlatformSP &platform_sp) { + // The native platform should use its static void Platform::Initialize() + // function to register itself as the native platform. + GetHostPlatformSP() = platform_sp; + + if (platform_sp) { + std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex()); + GetPlatformList().push_back(platform_sp); + } +} + +Status Platform::GetFileWithUUID(const FileSpec &platform_file, + const UUID *uuid_ptr, FileSpec &local_file) { + // Default to the local case + local_file = platform_file; + return Status(); +} + +FileSpecList +Platform::LocateExecutableScriptingResources(Target *target, Module &module, + Stream *feedback_stream) { + return FileSpecList(); +} + +// PlatformSP +// Platform::FindPlugin (Process *process, ConstString plugin_name) +//{ +// PlatformCreateInstance create_callback = nullptr; +// if (plugin_name) +// { +// create_callback = +// PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name); +// if (create_callback) +// { +// ArchSpec arch; +// if (process) +// { +// arch = process->GetTarget().GetArchitecture(); +// } +// PlatformSP platform_sp(create_callback(process, &arch)); +// if (platform_sp) +// return platform_sp; +// } +// } +// else +// { +// for (uint32_t idx = 0; (create_callback = +// PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != nullptr; +// ++idx) +// { +// PlatformSP platform_sp(create_callback(process, nullptr)); +// if (platform_sp) +// return platform_sp; +// } +// } +// return PlatformSP(); +//} + +Status Platform::GetSharedModule(const ModuleSpec &module_spec, + Process *process, ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) { + if (IsHost()) + return ModuleList::GetSharedModule( + module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, + did_create_ptr, false); + + // Module resolver lambda. + auto resolver = [&](const ModuleSpec &spec) { + Status error(eErrorTypeGeneric); + ModuleSpec resolved_spec; + // Check if we have sysroot set. + if (m_sdk_sysroot) { + // Prepend sysroot to module spec. + resolved_spec = spec; + resolved_spec.GetFileSpec().PrependPathComponent( + m_sdk_sysroot.GetStringRef()); + // Try to get shared module with resolved spec. + error = ModuleList::GetSharedModule( + resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, + did_create_ptr, false); + } + // If we don't have sysroot or it didn't work then + // try original module spec. + if (!error.Success()) { + resolved_spec = spec; + error = ModuleList::GetSharedModule( + resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, + did_create_ptr, false); + } + if (error.Success() && module_sp) + module_sp->SetPlatformFileSpec(resolved_spec.GetFileSpec()); + return error; + }; + + return GetRemoteSharedModule(module_spec, process, module_sp, resolver, + did_create_ptr); +} + +bool Platform::GetModuleSpec(const FileSpec &module_file_spec, + const ArchSpec &arch, ModuleSpec &module_spec) { + ModuleSpecList module_specs; + if (ObjectFile::GetModuleSpecifications(module_file_spec, 0, 0, + module_specs) == 0) + return false; + + ModuleSpec matched_module_spec; + return module_specs.FindMatchingModuleSpec(ModuleSpec(module_file_spec, arch), + module_spec); +} + +PlatformSP Platform::Find(ConstString name) { + if (name) { + static ConstString g_host_platform_name("host"); + if (name == g_host_platform_name) + return GetHostPlatform(); + + std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex()); + for (const auto &platform_sp : GetPlatformList()) { + if (platform_sp->GetName() == name) + return platform_sp; + } + } + return PlatformSP(); +} + +PlatformSP Platform::Create(ConstString name, Status &error) { + PlatformCreateInstance create_callback = nullptr; + lldb::PlatformSP platform_sp; + if (name) { + static ConstString g_host_platform_name("host"); + if (name == g_host_platform_name) + return GetHostPlatform(); + + create_callback = + PluginManager::GetPlatformCreateCallbackForPluginName(name); + if (create_callback) + platform_sp = create_callback(true, nullptr); + else + error.SetErrorStringWithFormat( + "unable to find a plug-in for the platform named \"%s\"", + name.GetCString()); + } else + error.SetErrorString("invalid platform name"); + + if (platform_sp) { + std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex()); + GetPlatformList().push_back(platform_sp); + } + + return platform_sp; +} + +PlatformSP Platform::Create(const ArchSpec &arch, ArchSpec *platform_arch_ptr, + Status &error) { + lldb::PlatformSP platform_sp; + if (arch.IsValid()) { + // Scope for locker + { + // First try exact arch matches across all platforms already created + std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex()); + for (const auto &platform_sp : GetPlatformList()) { + if (platform_sp->IsCompatibleArchitecture(arch, true, + platform_arch_ptr)) + return platform_sp; + } + + // Next try compatible arch matches across all platforms already created + for (const auto &platform_sp : GetPlatformList()) { + if (platform_sp->IsCompatibleArchitecture(arch, false, + platform_arch_ptr)) + return platform_sp; + } + } + + PlatformCreateInstance create_callback; + // First try exact arch matches across all platform plug-ins + uint32_t idx; + for (idx = 0; (create_callback = + PluginManager::GetPlatformCreateCallbackAtIndex(idx)); + ++idx) { + if (create_callback) { + platform_sp = create_callback(false, &arch); + if (platform_sp && + platform_sp->IsCompatibleArchitecture(arch, true, + platform_arch_ptr)) { + std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex()); + GetPlatformList().push_back(platform_sp); + return platform_sp; + } + } + } + // Next try compatible arch matches across all platform plug-ins + for (idx = 0; (create_callback = + PluginManager::GetPlatformCreateCallbackAtIndex(idx)); + ++idx) { + if (create_callback) { + platform_sp = create_callback(false, &arch); + if (platform_sp && + platform_sp->IsCompatibleArchitecture(arch, false, + platform_arch_ptr)) { + std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex()); + GetPlatformList().push_back(platform_sp); + return platform_sp; + } + } + } + } else + error.SetErrorString("invalid platform name"); + if (platform_arch_ptr) + platform_arch_ptr->Clear(); + platform_sp.reset(); + return platform_sp; +} + +ArchSpec Platform::GetAugmentedArchSpec(Platform *platform, llvm::StringRef triple) { + if (platform) + return platform->GetAugmentedArchSpec(triple); + return HostInfo::GetAugmentedArchSpec(triple); +} + +/// Default Constructor +Platform::Platform(bool is_host) + : m_is_host(is_host), m_os_version_set_while_connected(false), + m_system_arch_set_while_connected(false), m_sdk_sysroot(), m_sdk_build(), + m_working_dir(), m_remote_url(), m_name(), m_system_arch(), m_mutex(), + m_max_uid_name_len(0), m_max_gid_name_len(0), m_supports_rsync(false), + m_rsync_opts(), m_rsync_prefix(), m_supports_ssh(false), m_ssh_opts(), + m_ignores_remote_hostname(false), m_trap_handlers(), + m_calculated_trap_handlers(false), + m_module_cache(std::make_unique<ModuleCache>()) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, "%p Platform::Platform()", static_cast<void *>(this)); +} + +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +Platform::~Platform() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, "%p Platform::~Platform()", static_cast<void *>(this)); +} + +void Platform::GetStatus(Stream &strm) { + std::string s; + strm.Printf(" Platform: %s\n", GetPluginName().GetCString()); + + ArchSpec arch(GetSystemArchitecture()); + if (arch.IsValid()) { + if (!arch.GetTriple().str().empty()) { + strm.Printf(" Triple: "); + arch.DumpTriple(strm.AsRawOstream()); + strm.EOL(); + } + } + + llvm::VersionTuple os_version = GetOSVersion(); + if (!os_version.empty()) { + strm.Format("OS Version: {0}", os_version.getAsString()); + + if (GetOSBuildString(s)) + strm.Printf(" (%s)", s.c_str()); + + strm.EOL(); + } + + if (GetOSKernelDescription(s)) + strm.Printf(" Kernel: %s\n", s.c_str()); + + if (IsHost()) { + strm.Printf(" Hostname: %s\n", GetHostname()); + } else { + const bool is_connected = IsConnected(); + if (is_connected) + strm.Printf(" Hostname: %s\n", GetHostname()); + strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no"); + } + + if (GetWorkingDirectory()) { + strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetCString()); + } + if (!IsConnected()) + return; + + std::string specific_info(GetPlatformSpecificConnectionInformation()); + + if (!specific_info.empty()) + strm.Printf("Platform-specific connection: %s\n", specific_info.c_str()); +} + +llvm::VersionTuple Platform::GetOSVersion(Process *process) { + std::lock_guard<std::mutex> guard(m_mutex); + + if (IsHost()) { + if (m_os_version.empty()) { + // We have a local host platform + m_os_version = HostInfo::GetOSVersion(); + m_os_version_set_while_connected = !m_os_version.empty(); + } + } else { + // We have a remote platform. We can only fetch the remote + // OS version if we are connected, and we don't want to do it + // more than once. + + const bool is_connected = IsConnected(); + + bool fetch = false; + if (!m_os_version.empty()) { + // We have valid OS version info, check to make sure it wasn't manually + // set prior to connecting. If it was manually set prior to connecting, + // then lets fetch the actual OS version info if we are now connected. + if (is_connected && !m_os_version_set_while_connected) + fetch = true; + } else { + // We don't have valid OS version info, fetch it if we are connected + fetch = is_connected; + } + + if (fetch) + m_os_version_set_while_connected = GetRemoteOSVersion(); + } + + if (!m_os_version.empty()) + return m_os_version; + if (process) { + // Check with the process in case it can answer the question if a process + // was provided + return process->GetHostOSVersion(); + } + return llvm::VersionTuple(); +} + +bool Platform::GetOSBuildString(std::string &s) { + s.clear(); + + if (IsHost()) +#if !defined(__linux__) + return HostInfo::GetOSBuildString(s); +#else + return false; +#endif + else + return GetRemoteOSBuildString(s); +} + +bool Platform::GetOSKernelDescription(std::string &s) { + if (IsHost()) +#if !defined(__linux__) + return HostInfo::GetOSKernelDescription(s); +#else + return false; +#endif + else + return GetRemoteOSKernelDescription(s); +} + +void Platform::AddClangModuleCompilationOptions( + Target *target, std::vector<std::string> &options) { + std::vector<std::string> default_compilation_options = { + "-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc"}; + + options.insert(options.end(), default_compilation_options.begin(), + default_compilation_options.end()); +} + +FileSpec Platform::GetWorkingDirectory() { + if (IsHost()) { + llvm::SmallString<64> cwd; + if (llvm::sys::fs::current_path(cwd)) + return {}; + else { + FileSpec file_spec(cwd); + FileSystem::Instance().Resolve(file_spec); + return file_spec; + } + } else { + if (!m_working_dir) + m_working_dir = GetRemoteWorkingDirectory(); + return m_working_dir; + } +} + +struct RecurseCopyBaton { + const FileSpec &dst; + Platform *platform_ptr; + Status error; +}; + +static FileSystem::EnumerateDirectoryResult +RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, + llvm::StringRef path) { + RecurseCopyBaton *rc_baton = (RecurseCopyBaton *)baton; + FileSpec src(path); + namespace fs = llvm::sys::fs; + switch (ft) { + case fs::file_type::fifo_file: + case fs::file_type::socket_file: + // we have no way to copy pipes and sockets - ignore them and continue + return FileSystem::eEnumerateDirectoryResultNext; + break; + + case fs::file_type::directory_file: { + // make the new directory and get in there + FileSpec dst_dir = rc_baton->dst; + if (!dst_dir.GetFilename()) + dst_dir.GetFilename() = src.GetLastPathComponent(); + Status error = rc_baton->platform_ptr->MakeDirectory( + dst_dir, lldb::eFilePermissionsDirectoryDefault); + if (error.Fail()) { + rc_baton->error.SetErrorStringWithFormat( + "unable to setup directory %s on remote end", dst_dir.GetCString()); + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out + } + + // now recurse + std::string src_dir_path(src.GetPath()); + + // Make a filespec that only fills in the directory of a FileSpec so when + // we enumerate we can quickly fill in the filename for dst copies + FileSpec recurse_dst; + recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str()); + RecurseCopyBaton rc_baton2 = {recurse_dst, rc_baton->platform_ptr, + Status()}; + FileSystem::Instance().EnumerateDirectory(src_dir_path, true, true, true, + RecurseCopy_Callback, &rc_baton2); + if (rc_baton2.error.Fail()) { + rc_baton->error.SetErrorString(rc_baton2.error.AsCString()); + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out + } + return FileSystem::eEnumerateDirectoryResultNext; + } break; + + case fs::file_type::symlink_file: { + // copy the file and keep going + FileSpec dst_file = rc_baton->dst; + if (!dst_file.GetFilename()) + dst_file.GetFilename() = src.GetFilename(); + + FileSpec src_resolved; + + rc_baton->error = FileSystem::Instance().Readlink(src, src_resolved); + + if (rc_baton->error.Fail()) + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out + + rc_baton->error = + rc_baton->platform_ptr->CreateSymlink(dst_file, src_resolved); + + if (rc_baton->error.Fail()) + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out + + return FileSystem::eEnumerateDirectoryResultNext; + } break; + + case fs::file_type::regular_file: { + // copy the file and keep going + FileSpec dst_file = rc_baton->dst; + if (!dst_file.GetFilename()) + dst_file.GetFilename() = src.GetFilename(); + Status err = rc_baton->platform_ptr->PutFile(src, dst_file); + if (err.Fail()) { + rc_baton->error.SetErrorString(err.AsCString()); + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out + } + return FileSystem::eEnumerateDirectoryResultNext; + } break; + + default: + rc_baton->error.SetErrorStringWithFormat( + "invalid file detected during copy: %s", src.GetPath().c_str()); + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out + break; + } + llvm_unreachable("Unhandled file_type!"); +} + +Status Platform::Install(const FileSpec &src, const FileSpec &dst) { + Status error; + + Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); + LLDB_LOGF(log, "Platform::Install (src='%s', dst='%s')", + src.GetPath().c_str(), dst.GetPath().c_str()); + FileSpec fixed_dst(dst); + + if (!fixed_dst.GetFilename()) + fixed_dst.GetFilename() = src.GetFilename(); + + FileSpec working_dir = GetWorkingDirectory(); + + if (dst) { + if (dst.GetDirectory()) { + const char first_dst_dir_char = dst.GetDirectory().GetCString()[0]; + if (first_dst_dir_char == '/' || first_dst_dir_char == '\\') { + fixed_dst.GetDirectory() = dst.GetDirectory(); + } + // If the fixed destination file doesn't have a directory yet, then we + // must have a relative path. We will resolve this relative path against + // the platform's working directory + if (!fixed_dst.GetDirectory()) { + FileSpec relative_spec; + std::string path; + if (working_dir) { + relative_spec = working_dir; + relative_spec.AppendPathComponent(dst.GetPath()); + fixed_dst.GetDirectory() = relative_spec.GetDirectory(); + } else { + error.SetErrorStringWithFormat( + "platform working directory must be valid for relative path '%s'", + dst.GetPath().c_str()); + return error; + } + } + } else { + if (working_dir) { + fixed_dst.GetDirectory().SetCString(working_dir.GetCString()); + } else { + error.SetErrorStringWithFormat( + "platform working directory must be valid for relative path '%s'", + dst.GetPath().c_str()); + return error; + } + } + } else { + if (working_dir) { + fixed_dst.GetDirectory().SetCString(working_dir.GetCString()); + } else { + error.SetErrorStringWithFormat("platform working directory must be valid " + "when destination directory is empty"); + return error; + } + } + + LLDB_LOGF(log, "Platform::Install (src='%s', dst='%s') fixed_dst='%s'", + src.GetPath().c_str(), dst.GetPath().c_str(), + fixed_dst.GetPath().c_str()); + + if (GetSupportsRSync()) { + error = PutFile(src, dst); + } else { + namespace fs = llvm::sys::fs; + switch (fs::get_file_type(src.GetPath(), false)) { + case fs::file_type::directory_file: { + llvm::sys::fs::remove(fixed_dst.GetPath()); + uint32_t permissions = FileSystem::Instance().GetPermissions(src); + if (permissions == 0) + permissions = eFilePermissionsDirectoryDefault; + error = MakeDirectory(fixed_dst, permissions); + if (error.Success()) { + // Make a filespec that only fills in the directory of a FileSpec so + // when we enumerate we can quickly fill in the filename for dst copies + FileSpec recurse_dst; + recurse_dst.GetDirectory().SetCString(fixed_dst.GetCString()); + std::string src_dir_path(src.GetPath()); + RecurseCopyBaton baton = {recurse_dst, this, Status()}; + FileSystem::Instance().EnumerateDirectory( + src_dir_path, true, true, true, RecurseCopy_Callback, &baton); + return baton.error; + } + } break; + + case fs::file_type::regular_file: + llvm::sys::fs::remove(fixed_dst.GetPath()); + error = PutFile(src, fixed_dst); + break; + + case fs::file_type::symlink_file: { + llvm::sys::fs::remove(fixed_dst.GetPath()); + FileSpec src_resolved; + error = FileSystem::Instance().Readlink(src, src_resolved); + if (error.Success()) + error = CreateSymlink(dst, src_resolved); + } break; + case fs::file_type::fifo_file: + error.SetErrorString("platform install doesn't handle pipes"); + break; + case fs::file_type::socket_file: + error.SetErrorString("platform install doesn't handle sockets"); + break; + default: + error.SetErrorString( + "platform install doesn't handle non file or directory items"); + break; + } + } + return error; +} + +bool Platform::SetWorkingDirectory(const FileSpec &file_spec) { + if (IsHost()) { + Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); + LLDB_LOG(log, "{0}", file_spec); + if (std::error_code ec = llvm::sys::fs::set_current_path(file_spec.GetPath())) { + LLDB_LOG(log, "error: {0}", ec.message()); + return false; + } + return true; + } else { + m_working_dir.Clear(); + return SetRemoteWorkingDirectory(file_spec); + } +} + +Status Platform::MakeDirectory(const FileSpec &file_spec, + uint32_t permissions) { + if (IsHost()) + return llvm::sys::fs::create_directory(file_spec.GetPath(), permissions); + else { + Status error; + error.SetErrorStringWithFormat("remote platform %s doesn't support %s", + GetPluginName().GetCString(), + LLVM_PRETTY_FUNCTION); + return error; + } +} + +Status Platform::GetFilePermissions(const FileSpec &file_spec, + uint32_t &file_permissions) { + if (IsHost()) { + auto Value = llvm::sys::fs::getPermissions(file_spec.GetPath()); + if (Value) + file_permissions = Value.get(); + return Status(Value.getError()); + } else { + Status error; + error.SetErrorStringWithFormat("remote platform %s doesn't support %s", + GetPluginName().GetCString(), + LLVM_PRETTY_FUNCTION); + return error; + } +} + +Status Platform::SetFilePermissions(const FileSpec &file_spec, + uint32_t file_permissions) { + if (IsHost()) { + auto Perms = static_cast<llvm::sys::fs::perms>(file_permissions); + return llvm::sys::fs::setPermissions(file_spec.GetPath(), Perms); + } else { + Status error; + error.SetErrorStringWithFormat("remote platform %s doesn't support %s", + GetPluginName().GetCString(), + LLVM_PRETTY_FUNCTION); + return error; + } +} + +ConstString Platform::GetName() { return GetPluginName(); } + +const char *Platform::GetHostname() { + if (IsHost()) + return "127.0.0.1"; + + if (m_name.empty()) + return nullptr; + return m_name.c_str(); +} + +ConstString Platform::GetFullNameForDylib(ConstString basename) { + return basename; +} + +bool Platform::SetRemoteWorkingDirectory(const FileSpec &working_dir) { + Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); + LLDB_LOGF(log, "Platform::SetRemoteWorkingDirectory('%s')", + working_dir.GetCString()); + m_working_dir = working_dir; + return true; +} + +bool Platform::SetOSVersion(llvm::VersionTuple version) { + if (IsHost()) { + // We don't need anyone setting the OS version for the host platform, we + // should be able to figure it out by calling HostInfo::GetOSVersion(...). + return false; + } else { + // We have a remote platform, allow setting the target OS version if we + // aren't connected, since if we are connected, we should be able to + // request the remote OS version from the connected platform. + if (IsConnected()) + return false; + else { + // We aren't connected and we might want to set the OS version ahead of + // time before we connect so we can peruse files and use a local SDK or + // PDK cache of support files to disassemble or do other things. + m_os_version = version; + return true; + } + } + return false; +} + +Status +Platform::ResolveExecutable(const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) { + Status error; + if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { + if (module_spec.GetArchitecture().IsValid()) { + error = ModuleList::GetSharedModule(module_spec, exe_module_sp, + module_search_paths_ptr, nullptr, + nullptr); + } else { + // No valid architecture was specified, ask the platform for the + // architectures that we should be using (in the correct order) and see + // if we can find a match that way + ModuleSpec arch_module_spec(module_spec); + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( + idx, arch_module_spec.GetArchitecture()); + ++idx) { + error = ModuleList::GetSharedModule(arch_module_spec, exe_module_sp, + module_search_paths_ptr, nullptr, + nullptr); + // Did we find an executable using one of the + if (error.Success() && exe_module_sp) + break; + } + } + } else { + error.SetErrorStringWithFormat("'%s' does not exist", + module_spec.GetFileSpec().GetPath().c_str()); + } + return error; +} + +Status Platform::ResolveSymbolFile(Target &target, const ModuleSpec &sym_spec, + FileSpec &sym_file) { + Status error; + if (FileSystem::Instance().Exists(sym_spec.GetSymbolFileSpec())) + sym_file = sym_spec.GetSymbolFileSpec(); + else + error.SetErrorString("unable to resolve symbol file"); + return error; +} + +bool Platform::ResolveRemotePath(const FileSpec &platform_path, + FileSpec &resolved_platform_path) { + resolved_platform_path = platform_path; + FileSystem::Instance().Resolve(resolved_platform_path); + return true; +} + +const ArchSpec &Platform::GetSystemArchitecture() { + if (IsHost()) { + if (!m_system_arch.IsValid()) { + // We have a local host platform + m_system_arch = HostInfo::GetArchitecture(); + m_system_arch_set_while_connected = m_system_arch.IsValid(); + } + } else { + // We have a remote platform. We can only fetch the remote system + // architecture if we are connected, and we don't want to do it more than + // once. + + const bool is_connected = IsConnected(); + + bool fetch = false; + if (m_system_arch.IsValid()) { + // We have valid OS version info, check to make sure it wasn't manually + // set prior to connecting. If it was manually set prior to connecting, + // then lets fetch the actual OS version info if we are now connected. + if (is_connected && !m_system_arch_set_while_connected) + fetch = true; + } else { + // We don't have valid OS version info, fetch it if we are connected + fetch = is_connected; + } + + if (fetch) { + m_system_arch = GetRemoteSystemArchitecture(); + m_system_arch_set_while_connected = m_system_arch.IsValid(); + } + } + return m_system_arch; +} + +ArchSpec Platform::GetAugmentedArchSpec(llvm::StringRef triple) { + if (triple.empty()) + return ArchSpec(); + llvm::Triple normalized_triple(llvm::Triple::normalize(triple)); + if (!ArchSpec::ContainsOnlyArch(normalized_triple)) + return ArchSpec(triple); + + if (auto kind = HostInfo::ParseArchitectureKind(triple)) + return HostInfo::GetArchitecture(*kind); + + ArchSpec compatible_arch; + ArchSpec raw_arch(triple); + if (!IsCompatibleArchitecture(raw_arch, false, &compatible_arch)) + return raw_arch; + + if (!compatible_arch.IsValid()) + return ArchSpec(normalized_triple); + + const llvm::Triple &compatible_triple = compatible_arch.GetTriple(); + if (normalized_triple.getVendorName().empty()) + normalized_triple.setVendor(compatible_triple.getVendor()); + if (normalized_triple.getOSName().empty()) + normalized_triple.setOS(compatible_triple.getOS()); + if (normalized_triple.getEnvironmentName().empty()) + normalized_triple.setEnvironment(compatible_triple.getEnvironment()); + return ArchSpec(normalized_triple); +} + +Status Platform::ConnectRemote(Args &args) { + Status error; + if (IsHost()) + error.SetErrorStringWithFormat("The currently selected platform (%s) is " + "the host platform and is always connected.", + GetPluginName().GetCString()); + else + error.SetErrorStringWithFormat( + "Platform::ConnectRemote() is not supported by %s", + GetPluginName().GetCString()); + return error; +} + +Status Platform::DisconnectRemote() { + Status error; + if (IsHost()) + error.SetErrorStringWithFormat("The currently selected platform (%s) is " + "the host platform and is always connected.", + GetPluginName().GetCString()); + else + error.SetErrorStringWithFormat( + "Platform::DisconnectRemote() is not supported by %s", + GetPluginName().GetCString()); + return error; +} + +bool Platform::GetProcessInfo(lldb::pid_t pid, + ProcessInstanceInfo &process_info) { + // Take care of the host case so that each subclass can just call this + // function to get the host functionality. + if (IsHost()) + return Host::GetProcessInfo(pid, process_info); + return false; +} + +uint32_t Platform::FindProcesses(const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) { + // Take care of the host case so that each subclass can just call this + // function to get the host functionality. + uint32_t match_count = 0; + if (IsHost()) + match_count = Host::FindProcesses(match_info, process_infos); + return match_count; +} + +Status Platform::LaunchProcess(ProcessLaunchInfo &launch_info) { + Status error; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + LLDB_LOGF(log, "Platform::%s()", __FUNCTION__); + + // Take care of the host case so that each subclass can just call this + // function to get the host functionality. + if (IsHost()) { + if (::getenv("LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY")) + launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY); + + if (launch_info.GetFlags().Test(eLaunchFlagLaunchInShell)) { + const bool is_localhost = true; + const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug); + const bool first_arg_is_full_shell_command = false; + uint32_t num_resumes = GetResumeCountForLaunchInfo(launch_info); + if (log) { + const FileSpec &shell = launch_info.GetShell(); + std::string shell_str = (shell) ? shell.GetPath() : "<null>"; + LLDB_LOGF(log, + "Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32 + ", shell is '%s'", + __FUNCTION__, num_resumes, shell_str.c_str()); + } + + if (!launch_info.ConvertArgumentsForLaunchingInShell( + error, is_localhost, will_debug, first_arg_is_full_shell_command, + num_resumes)) + return error; + } else if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) { + error = ShellExpandArguments(launch_info); + if (error.Fail()) { + error.SetErrorStringWithFormat("shell expansion failed (reason: %s). " + "consider launching with 'process " + "launch'.", + error.AsCString("unknown")); + return error; + } + } + + LLDB_LOGF(log, "Platform::%s final launch_info resume count: %" PRIu32, + __FUNCTION__, launch_info.GetResumeCount()); + + error = Host::LaunchProcess(launch_info); + } else + error.SetErrorString( + "base lldb_private::Platform class can't launch remote processes"); + return error; +} + +Status Platform::ShellExpandArguments(ProcessLaunchInfo &launch_info) { + if (IsHost()) + return Host::ShellExpandArguments(launch_info); + return Status("base lldb_private::Platform class can't expand arguments"); +} + +Status Platform::KillProcess(const lldb::pid_t pid) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + LLDB_LOGF(log, "Platform::%s, pid %" PRIu64, __FUNCTION__, pid); + + // Try to find a process plugin to handle this Kill request. If we can't, + // fall back to the default OS implementation. + size_t num_debuggers = Debugger::GetNumDebuggers(); + for (size_t didx = 0; didx < num_debuggers; ++didx) { + DebuggerSP debugger = Debugger::GetDebuggerAtIndex(didx); + lldb_private::TargetList &targets = debugger->GetTargetList(); + for (int tidx = 0; tidx < targets.GetNumTargets(); ++tidx) { + ProcessSP process = targets.GetTargetAtIndex(tidx)->GetProcessSP(); + if (process->GetID() == pid) + return process->Destroy(true); + } + } + + if (!IsHost()) { + return Status( + "base lldb_private::Platform class can't kill remote processes unless " + "they are controlled by a process plugin"); + } + Host::Kill(pid, SIGTERM); + return Status(); +} + +lldb::ProcessSP +Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, + Target *target, // Can be nullptr, if nullptr create a + // new target, else use existing one + Status &error) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + LLDB_LOGF(log, "Platform::%s entered (target %p)", __FUNCTION__, + static_cast<void *>(target)); + + ProcessSP process_sp; + // Make sure we stop at the entry point + launch_info.GetFlags().Set(eLaunchFlagDebug); + // We always launch the process we are going to debug in a separate process + // group, since then we can handle ^C interrupts ourselves w/o having to + // worry about the target getting them as well. + launch_info.SetLaunchInSeparateProcessGroup(true); + + // Allow any StructuredData process-bound plugins to adjust the launch info + // if needed + size_t i = 0; + bool iteration_complete = false; + // Note iteration can't simply go until a nullptr callback is returned, as it + // is valid for a plugin to not supply a filter. + auto get_filter_func = PluginManager::GetStructuredDataFilterCallbackAtIndex; + for (auto filter_callback = get_filter_func(i, iteration_complete); + !iteration_complete; + filter_callback = get_filter_func(++i, iteration_complete)) { + if (filter_callback) { + // Give this ProcessLaunchInfo filter a chance to adjust the launch info. + error = (*filter_callback)(launch_info, target); + if (!error.Success()) { + LLDB_LOGF(log, + "Platform::%s() StructuredDataPlugin launch " + "filter failed.", + __FUNCTION__); + return process_sp; + } + } + } + + error = LaunchProcess(launch_info); + if (error.Success()) { + LLDB_LOGF(log, + "Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64 ")", + __FUNCTION__, launch_info.GetProcessID()); + if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { + ProcessAttachInfo attach_info(launch_info); + process_sp = Attach(attach_info, debugger, target, error); + if (process_sp) { + LLDB_LOGF(log, "Platform::%s Attach() succeeded, Process plugin: %s", + __FUNCTION__, process_sp->GetPluginName().AsCString()); + launch_info.SetHijackListener(attach_info.GetHijackListener()); + + // Since we attached to the process, it will think it needs to detach + // if the process object just goes away without an explicit call to + // Process::Kill() or Process::Detach(), so let it know to kill the + // process if this happens. + process_sp->SetShouldDetach(false); + + // If we didn't have any file actions, the pseudo terminal might have + // been used where the slave side was given as the file to open for + // stdin/out/err after we have already opened the master so we can + // read/write stdin/out/err. + int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); + if (pty_fd != PseudoTerminal::invalid_fd) { + process_sp->SetSTDIOFileDescriptor(pty_fd); + } + } else { + LLDB_LOGF(log, "Platform::%s Attach() failed: %s", __FUNCTION__, + error.AsCString()); + } + } else { + LLDB_LOGF(log, + "Platform::%s LaunchProcess() returned launch_info with " + "invalid process id", + __FUNCTION__); + } + } else { + LLDB_LOGF(log, "Platform::%s LaunchProcess() failed: %s", __FUNCTION__, + error.AsCString()); + } + + return process_sp; +} + +lldb::PlatformSP +Platform::GetPlatformForArchitecture(const ArchSpec &arch, + ArchSpec *platform_arch_ptr) { + lldb::PlatformSP platform_sp; + Status error; + if (arch.IsValid()) + platform_sp = Platform::Create(arch, platform_arch_ptr, error); + return platform_sp; +} + +/// Lets a platform answer if it is compatible with a given +/// architecture and the target triple contained within. +bool Platform::IsCompatibleArchitecture(const ArchSpec &arch, + bool exact_arch_match, + ArchSpec *compatible_arch_ptr) { + // If the architecture is invalid, we must answer true... + if (arch.IsValid()) { + ArchSpec platform_arch; + // Try for an exact architecture match first. + if (exact_arch_match) { + for (uint32_t arch_idx = 0; + GetSupportedArchitectureAtIndex(arch_idx, platform_arch); + ++arch_idx) { + if (arch.IsExactMatch(platform_arch)) { + if (compatible_arch_ptr) + *compatible_arch_ptr = platform_arch; + return true; + } + } + } else { + for (uint32_t arch_idx = 0; + GetSupportedArchitectureAtIndex(arch_idx, platform_arch); + ++arch_idx) { + if (arch.IsCompatibleMatch(platform_arch)) { + if (compatible_arch_ptr) + *compatible_arch_ptr = platform_arch; + return true; + } + } + } + } + if (compatible_arch_ptr) + compatible_arch_ptr->Clear(); + return false; +} + +Status Platform::PutFile(const FileSpec &source, const FileSpec &destination, + uint32_t uid, uint32_t gid) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + LLDB_LOGF(log, "[PutFile] Using block by block transfer....\n"); + + auto source_open_options = + File::eOpenOptionRead | File::eOpenOptionCloseOnExec; + namespace fs = llvm::sys::fs; + if (fs::is_symlink_file(source.GetPath())) + source_open_options |= File::eOpenOptionDontFollowSymlinks; + + auto source_file = FileSystem::Instance().Open(source, source_open_options, + lldb::eFilePermissionsUserRW); + if (!source_file) + return Status(source_file.takeError()); + Status error; + uint32_t permissions = source_file.get()->GetPermissions(error); + if (permissions == 0) + permissions = lldb::eFilePermissionsFileDefault; + + lldb::user_id_t dest_file = OpenFile( + destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite | + File::eOpenOptionTruncate | File::eOpenOptionCloseOnExec, + permissions, error); + LLDB_LOGF(log, "dest_file = %" PRIu64 "\n", dest_file); + + if (error.Fail()) + return error; + if (dest_file == UINT64_MAX) + return Status("unable to open target file"); + lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024 * 16, 0)); + uint64_t offset = 0; + for (;;) { + size_t bytes_read = buffer_sp->GetByteSize(); + error = source_file.get()->Read(buffer_sp->GetBytes(), bytes_read); + if (error.Fail() || bytes_read == 0) + break; + + const uint64_t bytes_written = + WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error); + if (error.Fail()) + break; + + offset += bytes_written; + if (bytes_written != bytes_read) { + // We didn't write the correct number of bytes, so adjust the file + // position in the source file we are reading from... + source_file.get()->SeekFromStart(offset); + } + } + CloseFile(dest_file, error); + + if (uid == UINT32_MAX && gid == UINT32_MAX) + return error; + + // TODO: ChownFile? + + return error; +} + +Status Platform::GetFile(const FileSpec &source, const FileSpec &destination) { + Status error("unimplemented"); + return error; +} + +Status +Platform::CreateSymlink(const FileSpec &src, // The name of the link is in src + const FileSpec &dst) // The symlink points to dst +{ + Status error("unimplemented"); + return error; +} + +bool Platform::GetFileExists(const lldb_private::FileSpec &file_spec) { + return false; +} + +Status Platform::Unlink(const FileSpec &path) { + Status error("unimplemented"); + return error; +} + +MmapArgList Platform::GetMmapArgumentList(const ArchSpec &arch, addr_t addr, + addr_t length, unsigned prot, + unsigned flags, addr_t fd, + addr_t offset) { + uint64_t flags_platform = 0; + if (flags & eMmapFlagsPrivate) + flags_platform |= MAP_PRIVATE; + if (flags & eMmapFlagsAnon) + flags_platform |= MAP_ANON; + + MmapArgList args({addr, length, prot, flags_platform, fd, offset}); + return args; +} + +lldb_private::Status Platform::RunShellCommand( + const char *command, // Shouldn't be nullptr + const FileSpec & + working_dir, // Pass empty FileSpec to use the current working directory + int *status_ptr, // Pass nullptr if you don't want the process exit status + int *signo_ptr, // Pass nullptr if you don't want the signal that caused the + // process to exit + std::string + *command_output, // Pass nullptr if you don't want the command output + const Timeout<std::micro> &timeout) { + if (IsHost()) + return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, + command_output, timeout); + else + return Status("unimplemented"); +} + +bool Platform::CalculateMD5(const FileSpec &file_spec, uint64_t &low, + uint64_t &high) { + if (!IsHost()) + return false; + auto Result = llvm::sys::fs::md5_contents(file_spec.GetPath()); + if (!Result) + return false; + std::tie(high, low) = Result->words(); + return true; +} + +void Platform::SetLocalCacheDirectory(const char *local) { + m_local_cache_directory.assign(local); +} + +const char *Platform::GetLocalCacheDirectory() { + return m_local_cache_directory.c_str(); +} + +static constexpr OptionDefinition g_rsync_option_table[] = { + {LLDB_OPT_SET_ALL, false, "rsync", 'r', OptionParser::eNoArgument, nullptr, + {}, 0, eArgTypeNone, "Enable rsync."}, + {LLDB_OPT_SET_ALL, false, "rsync-opts", 'R', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName, + "Platform-specific options required for rsync to work."}, + {LLDB_OPT_SET_ALL, false, "rsync-prefix", 'P', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName, + "Platform-specific rsync prefix put before the remote path."}, + {LLDB_OPT_SET_ALL, false, "ignore-remote-hostname", 'i', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Do not automatically fill in the remote hostname when composing the " + "rsync command."}, +}; + +static constexpr OptionDefinition g_ssh_option_table[] = { + {LLDB_OPT_SET_ALL, false, "ssh", 's', OptionParser::eNoArgument, nullptr, + {}, 0, eArgTypeNone, "Enable SSH."}, + {LLDB_OPT_SET_ALL, false, "ssh-opts", 'S', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeCommandName, + "Platform-specific options required for SSH to work."}, +}; + +static constexpr OptionDefinition g_caching_option_table[] = { + {LLDB_OPT_SET_ALL, false, "local-cache-dir", 'c', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePath, + "Path in which to store local copies of files."}, +}; + +llvm::ArrayRef<OptionDefinition> OptionGroupPlatformRSync::GetDefinitions() { + return llvm::makeArrayRef(g_rsync_option_table); +} + +void OptionGroupPlatformRSync::OptionParsingStarting( + ExecutionContext *execution_context) { + m_rsync = false; + m_rsync_opts.clear(); + m_rsync_prefix.clear(); + m_ignores_remote_hostname = false; +} + +lldb_private::Status +OptionGroupPlatformRSync::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + char short_option = (char)GetDefinitions()[option_idx].short_option; + switch (short_option) { + case 'r': + m_rsync = true; + break; + + case 'R': + m_rsync_opts.assign(option_arg); + break; + + case 'P': + m_rsync_prefix.assign(option_arg); + break; + + case 'i': + m_ignores_remote_hostname = true; + break; + + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +lldb::BreakpointSP +Platform::SetThreadCreationBreakpoint(lldb_private::Target &target) { + return lldb::BreakpointSP(); +} + +llvm::ArrayRef<OptionDefinition> OptionGroupPlatformSSH::GetDefinitions() { + return llvm::makeArrayRef(g_ssh_option_table); +} + +void OptionGroupPlatformSSH::OptionParsingStarting( + ExecutionContext *execution_context) { + m_ssh = false; + m_ssh_opts.clear(); +} + +lldb_private::Status +OptionGroupPlatformSSH::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + char short_option = (char)GetDefinitions()[option_idx].short_option; + switch (short_option) { + case 's': + m_ssh = true; + break; + + case 'S': + m_ssh_opts.assign(option_arg); + break; + + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +llvm::ArrayRef<OptionDefinition> OptionGroupPlatformCaching::GetDefinitions() { + return llvm::makeArrayRef(g_caching_option_table); +} + +void OptionGroupPlatformCaching::OptionParsingStarting( + ExecutionContext *execution_context) { + m_cache_dir.clear(); +} + +lldb_private::Status OptionGroupPlatformCaching::SetOptionValue( + uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + char short_option = (char)GetDefinitions()[option_idx].short_option; + switch (short_option) { + case 'c': + m_cache_dir.assign(option_arg); + break; + + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +Environment Platform::GetEnvironment() { return Environment(); } + +const std::vector<ConstString> &Platform::GetTrapHandlerSymbolNames() { + if (!m_calculated_trap_handlers) { + std::lock_guard<std::mutex> guard(m_mutex); + if (!m_calculated_trap_handlers) { + CalculateTrapHandlerSymbolNames(); + m_calculated_trap_handlers = true; + } + } + return m_trap_handlers; +} + +Status Platform::GetCachedExecutable( + ModuleSpec &module_spec, lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, Platform &remote_platform) { + const auto platform_spec = module_spec.GetFileSpec(); + const auto error = LoadCachedExecutable( + module_spec, module_sp, module_search_paths_ptr, remote_platform); + if (error.Success()) { + module_spec.GetFileSpec() = module_sp->GetFileSpec(); + module_spec.GetPlatformFileSpec() = platform_spec; + } + + return error; +} + +Status Platform::LoadCachedExecutable( + const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, Platform &remote_platform) { + return GetRemoteSharedModule(module_spec, nullptr, module_sp, + [&](const ModuleSpec &spec) { + return remote_platform.ResolveExecutable( + spec, module_sp, module_search_paths_ptr); + }, + nullptr); +} + +Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, + Process *process, + lldb::ModuleSP &module_sp, + const ModuleResolver &module_resolver, + bool *did_create_ptr) { + // Get module information from a target. + ModuleSpec resolved_module_spec; + bool got_module_spec = false; + if (process) { + // Try to get module information from the process + if (process->GetModuleSpec(module_spec.GetFileSpec(), + module_spec.GetArchitecture(), + resolved_module_spec)) { + if (!module_spec.GetUUID().IsValid() || + module_spec.GetUUID() == resolved_module_spec.GetUUID()) { + got_module_spec = true; + } + } + } + + if (!module_spec.GetArchitecture().IsValid()) { + Status error; + // No valid architecture was specified, ask the platform for the + // architectures that we should be using (in the correct order) and see if + // we can find a match that way + ModuleSpec arch_module_spec(module_spec); + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( + idx, arch_module_spec.GetArchitecture()); + ++idx) { + error = ModuleList::GetSharedModule(arch_module_spec, module_sp, nullptr, + nullptr, nullptr); + // Did we find an executable using one of the + if (error.Success() && module_sp) + break; + } + if (module_sp) + got_module_spec = true; + } + + if (!got_module_spec) { + // Get module information from a target. + if (!GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(), + resolved_module_spec)) { + if (!module_spec.GetUUID().IsValid() || + module_spec.GetUUID() == resolved_module_spec.GetUUID()) { + return module_resolver(module_spec); + } + } + } + + // If we are looking for a specific UUID, make sure resolved_module_spec has + // the same one before we search. + if (module_spec.GetUUID().IsValid()) { + resolved_module_spec.GetUUID() = module_spec.GetUUID(); + } + + // Trying to find a module by UUID on local file system. + const auto error = module_resolver(resolved_module_spec); + if (error.Fail()) { + if (GetCachedSharedModule(resolved_module_spec, module_sp, did_create_ptr)) + return Status(); + } + + return error; +} + +bool Platform::GetCachedSharedModule(const ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + bool *did_create_ptr) { + if (IsHost() || !GetGlobalPlatformProperties()->GetUseModuleCache() || + !GetGlobalPlatformProperties()->GetModuleCacheDirectory()) + return false; + + Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); + + // Check local cache for a module. + auto error = m_module_cache->GetAndPut( + GetModuleCacheRoot(), GetCacheHostname(), module_spec, + [this](const ModuleSpec &module_spec, + const FileSpec &tmp_download_file_spec) { + return DownloadModuleSlice( + module_spec.GetFileSpec(), module_spec.GetObjectOffset(), + module_spec.GetObjectSize(), tmp_download_file_spec); + + }, + [this](const ModuleSP &module_sp, + const FileSpec &tmp_download_file_spec) { + return DownloadSymbolFile(module_sp, tmp_download_file_spec); + }, + module_sp, did_create_ptr); + if (error.Success()) + return true; + + LLDB_LOGF(log, "Platform::%s - module %s not found in local cache: %s", + __FUNCTION__, module_spec.GetUUID().GetAsString().c_str(), + error.AsCString()); + return false; +} + +Status Platform::DownloadModuleSlice(const FileSpec &src_file_spec, + const uint64_t src_offset, + const uint64_t src_size, + const FileSpec &dst_file_spec) { + Status error; + + std::error_code EC; + llvm::raw_fd_ostream dst(dst_file_spec.GetPath(), EC, llvm::sys::fs::OF_None); + if (EC) { + error.SetErrorStringWithFormat("unable to open destination file: %s", + dst_file_spec.GetPath().c_str()); + return error; + } + + auto src_fd = OpenFile(src_file_spec, File::eOpenOptionRead, + lldb::eFilePermissionsFileDefault, error); + + if (error.Fail()) { + error.SetErrorStringWithFormat("unable to open source file: %s", + error.AsCString()); + return error; + } + + std::vector<char> buffer(1024); + auto offset = src_offset; + uint64_t total_bytes_read = 0; + while (total_bytes_read < src_size) { + const auto to_read = std::min(static_cast<uint64_t>(buffer.size()), + src_size - total_bytes_read); + const uint64_t n_read = + ReadFile(src_fd, offset, &buffer[0], to_read, error); + if (error.Fail()) + break; + if (n_read == 0) { + error.SetErrorString("read 0 bytes"); + break; + } + offset += n_read; + total_bytes_read += n_read; + dst.write(&buffer[0], n_read); + } + + Status close_error; + CloseFile(src_fd, close_error); // Ignoring close error. + + return error; +} + +Status Platform::DownloadSymbolFile(const lldb::ModuleSP &module_sp, + const FileSpec &dst_file_spec) { + return Status( + "Symbol file downloading not supported by the default platform."); +} + +FileSpec Platform::GetModuleCacheRoot() { + auto dir_spec = GetGlobalPlatformProperties()->GetModuleCacheDirectory(); + dir_spec.AppendPathComponent(GetName().AsCString()); + return dir_spec; +} + +const char *Platform::GetCacheHostname() { return GetHostname(); } + +const UnixSignalsSP &Platform::GetRemoteUnixSignals() { + static const auto s_default_unix_signals_sp = std::make_shared<UnixSignals>(); + return s_default_unix_signals_sp; +} + +UnixSignalsSP Platform::GetUnixSignals() { + if (IsHost()) + return UnixSignals::CreateForHost(); + return GetRemoteUnixSignals(); +} + +uint32_t Platform::LoadImage(lldb_private::Process *process, + const lldb_private::FileSpec &local_file, + const lldb_private::FileSpec &remote_file, + lldb_private::Status &error) { + if (local_file && remote_file) { + // Both local and remote file was specified. Install the local file to the + // given location. + if (IsRemote() || local_file != remote_file) { + error = Install(local_file, remote_file); + if (error.Fail()) + return LLDB_INVALID_IMAGE_TOKEN; + } + return DoLoadImage(process, remote_file, nullptr, error); + } + + if (local_file) { + // Only local file was specified. Install it to the current working + // directory. + FileSpec target_file = GetWorkingDirectory(); + target_file.AppendPathComponent(local_file.GetFilename().AsCString()); + if (IsRemote() || local_file != target_file) { + error = Install(local_file, target_file); + if (error.Fail()) + return LLDB_INVALID_IMAGE_TOKEN; + } + return DoLoadImage(process, target_file, nullptr, error); + } + + if (remote_file) { + // Only remote file was specified so we don't have to do any copying + return DoLoadImage(process, remote_file, nullptr, error); + } + + error.SetErrorString("Neither local nor remote file was specified"); + return LLDB_INVALID_IMAGE_TOKEN; +} + +uint32_t Platform::DoLoadImage(lldb_private::Process *process, + const lldb_private::FileSpec &remote_file, + const std::vector<std::string> *paths, + lldb_private::Status &error, + lldb_private::FileSpec *loaded_image) { + error.SetErrorString("LoadImage is not supported on the current platform"); + return LLDB_INVALID_IMAGE_TOKEN; +} + +uint32_t Platform::LoadImageUsingPaths(lldb_private::Process *process, + const lldb_private::FileSpec &remote_filename, + const std::vector<std::string> &paths, + lldb_private::Status &error, + lldb_private::FileSpec *loaded_path) +{ + FileSpec file_to_use; + if (remote_filename.IsAbsolute()) + file_to_use = FileSpec(remote_filename.GetFilename().GetStringRef(), + + remote_filename.GetPathStyle()); + else + file_to_use = remote_filename; + + return DoLoadImage(process, file_to_use, &paths, error, loaded_path); +} + +Status Platform::UnloadImage(lldb_private::Process *process, + uint32_t image_token) { + return Status("UnloadImage is not supported on the current platform"); +} + +lldb::ProcessSP Platform::ConnectProcess(llvm::StringRef connect_url, + llvm::StringRef plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Status &error) { + error.Clear(); + + if (!target) { + ArchSpec arch; + if (target && target->GetArchitecture().IsValid()) + arch = target->GetArchitecture(); + else + arch = Target::GetDefaultArchitecture(); + + const char *triple = ""; + if (arch.IsValid()) + triple = arch.GetTriple().getTriple().c_str(); + + TargetSP new_target_sp; + error = debugger.GetTargetList().CreateTarget( + debugger, "", triple, eLoadDependentsNo, nullptr, new_target_sp); + target = new_target_sp.get(); + } + + if (!target || error.Fail()) + return nullptr; + + debugger.GetTargetList().SetSelectedTarget(target); + + lldb::ProcessSP process_sp = + target->CreateProcess(debugger.GetListener(), plugin_name, nullptr); + if (!process_sp) + return nullptr; + + error = process_sp->ConnectRemote(&debugger.GetOutputStream(), connect_url); + if (error.Fail()) + return nullptr; + + return process_sp; +} + +size_t Platform::ConnectToWaitingProcesses(lldb_private::Debugger &debugger, + lldb_private::Status &error) { + error.Clear(); + return 0; +} + +size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target, + BreakpointSite *bp_site) { + ArchSpec arch = target.GetArchitecture(); + const uint8_t *trap_opcode = nullptr; + size_t trap_opcode_size = 0; + + switch (arch.GetMachine()) { + case llvm::Triple::aarch64_32: + case llvm::Triple::aarch64: { + static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4}; + trap_opcode = g_aarch64_opcode; + trap_opcode_size = sizeof(g_aarch64_opcode); + } break; + + case llvm::Triple::arc: { + static const uint8_t g_hex_opcode[] = { 0xff, 0x7f }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } break; + + // TODO: support big-endian arm and thumb trap codes. + case llvm::Triple::arm: { + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the + // linux kernel does otherwise. + static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; + static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde}; + + lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0)); + AddressClass addr_class = AddressClass::eUnknown; + + if (bp_loc_sp) { + addr_class = bp_loc_sp->GetAddress().GetAddressClass(); + if (addr_class == AddressClass::eUnknown && + (bp_loc_sp->GetAddress().GetFileAddress() & 1)) + addr_class = AddressClass::eCodeAlternateISA; + } + + if (addr_class == AddressClass::eCodeAlternateISA) { + trap_opcode = g_thumb_breakpoint_opcode; + trap_opcode_size = sizeof(g_thumb_breakpoint_opcode); + } else { + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + } + } break; + + case llvm::Triple::mips: + case llvm::Triple::mips64: { + static const uint8_t g_hex_opcode[] = {0x00, 0x00, 0x00, 0x0d}; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } break; + + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: { + static const uint8_t g_hex_opcode[] = {0x0d, 0x00, 0x00, 0x00}; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } break; + + case llvm::Triple::systemz: { + static const uint8_t g_hex_opcode[] = {0x00, 0x01}; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } break; + + case llvm::Triple::hexagon: { + static const uint8_t g_hex_opcode[] = {0x0c, 0xdb, 0x00, 0x54}; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: { + static const uint8_t g_ppc_opcode[] = {0x7f, 0xe0, 0x00, 0x08}; + trap_opcode = g_ppc_opcode; + trap_opcode_size = sizeof(g_ppc_opcode); + } break; + + case llvm::Triple::ppc64le: { + static const uint8_t g_ppc64le_opcode[] = {0x08, 0x00, 0xe0, 0x7f}; // trap + trap_opcode = g_ppc64le_opcode; + trap_opcode_size = sizeof(g_ppc64le_opcode); + } break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: { + static const uint8_t g_i386_opcode[] = {0xCC}; + trap_opcode = g_i386_opcode; + trap_opcode_size = sizeof(g_i386_opcode); + } break; + + default: + llvm_unreachable( + "Unhandled architecture in Platform::GetSoftwareBreakpointTrapOpcode"); + } + + assert(bp_site); + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + + return 0; +} diff --git a/gnu/llvm/lldb/source/Target/Process.cpp b/gnu/llvm/lldb/source/Target/Process.cpp new file mode 100644 index 00000000000..6711dc37eca --- /dev/null +++ b/gnu/llvm/lldb/source/Target/Process.cpp @@ -0,0 +1,6025 @@ +//===-- Process.cpp ---------------------------------------------*- C++ -*-===// +// +// 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 <atomic> +#include <memory> +#include <mutex> + +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/Threading.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/DynamicCheckerFunctions.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Host/Terminal.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/Target/JITLoader.h" +#include "lldb/Target/JITLoaderList.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/MemoryHistory.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/OperatingSystem.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/StructuredDataPlugin.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/NameMatches.h" +#include "lldb/Utility/ProcessInfo.h" +#include "lldb/Utility/SelectHelper.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std::chrono; + +// Comment out line below to disable memory caching, overriding the process +// setting target.process.disable-memory-cache +#define ENABLE_MEMORY_CACHING + +#ifdef ENABLE_MEMORY_CACHING +#define DISABLE_MEM_CACHE_DEFAULT false +#else +#define DISABLE_MEM_CACHE_DEFAULT true +#endif + +class ProcessOptionValueProperties : public OptionValueProperties { +public: + ProcessOptionValueProperties(ConstString name) + : OptionValueProperties(name) {} + + // This constructor is used when creating ProcessOptionValueProperties when + // it is part of a new lldb_private::Process instance. It will copy all + // current global property values as needed + ProcessOptionValueProperties(ProcessProperties *global_properties) + : OptionValueProperties(*global_properties->GetValueProperties()) {} + + const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const override { + // When getting the value for a key from the process options, we will + // always try and grab the setting from the current process if there is + // one. Else we just use the one from this instance. + if (exe_ctx) { + Process *process = exe_ctx->GetProcessPtr(); + if (process) { + ProcessOptionValueProperties *instance_properties = + static_cast<ProcessOptionValueProperties *>( + process->GetValueProperties().get()); + if (this != instance_properties) + return instance_properties->ProtectedGetPropertyAtIndex(idx); + } + } + return ProtectedGetPropertyAtIndex(idx); + } +}; + +#define LLDB_PROPERTIES_process +#include "TargetProperties.inc" + +enum { +#define LLDB_PROPERTIES_process +#include "TargetPropertiesEnum.inc" +}; + +ProcessProperties::ProcessProperties(lldb_private::Process *process) + : Properties(), + m_process(process) // Can be nullptr for global ProcessProperties +{ + if (process == nullptr) { + // Global process properties, set them up one time + m_collection_sp = + std::make_shared<ProcessOptionValueProperties>(ConstString("process")); + m_collection_sp->Initialize(g_process_properties); + m_collection_sp->AppendProperty( + ConstString("thread"), ConstString("Settings specific to threads."), + true, Thread::GetGlobalProperties()->GetValueProperties()); + } else { + m_collection_sp = std::make_shared<ProcessOptionValueProperties>( + Process::GetGlobalProperties().get()); + m_collection_sp->SetValueChangedCallback( + ePropertyPythonOSPluginPath, + [this] { m_process->LoadOperatingSystemPlugin(true); }); + } +} + +ProcessProperties::~ProcessProperties() = default; + +bool ProcessProperties::GetDisableMemoryCache() const { + const uint32_t idx = ePropertyDisableMemCache; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} + +uint64_t ProcessProperties::GetMemoryCacheLineSize() const { + const uint32_t idx = ePropertyMemCacheLineSize; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_process_properties[idx].default_uint_value); +} + +Args ProcessProperties::GetExtraStartupCommands() const { + Args args; + const uint32_t idx = ePropertyExtraStartCommand; + m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); + return args; +} + +void ProcessProperties::SetExtraStartupCommands(const Args &args) { + const uint32_t idx = ePropertyExtraStartCommand; + m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); +} + +FileSpec ProcessProperties::GetPythonOSPluginPath() const { + const uint32_t idx = ePropertyPythonOSPluginPath; + return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); +} + +void ProcessProperties::SetPythonOSPluginPath(const FileSpec &file) { + const uint32_t idx = ePropertyPythonOSPluginPath; + m_collection_sp->SetPropertyAtIndexAsFileSpec(nullptr, idx, file); +} + +bool ProcessProperties::GetIgnoreBreakpointsInExpressions() const { + const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} + +void ProcessProperties::SetIgnoreBreakpointsInExpressions(bool ignore) { + const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore); +} + +bool ProcessProperties::GetUnwindOnErrorInExpressions() const { + const uint32_t idx = ePropertyUnwindOnErrorInExpressions; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} + +void ProcessProperties::SetUnwindOnErrorInExpressions(bool ignore) { + const uint32_t idx = ePropertyUnwindOnErrorInExpressions; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore); +} + +bool ProcessProperties::GetStopOnSharedLibraryEvents() const { + const uint32_t idx = ePropertyStopOnSharedLibraryEvents; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} + +void ProcessProperties::SetStopOnSharedLibraryEvents(bool stop) { + const uint32_t idx = ePropertyStopOnSharedLibraryEvents; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop); +} + +bool ProcessProperties::GetDetachKeepsStopped() const { + const uint32_t idx = ePropertyDetachKeepsStopped; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} + +void ProcessProperties::SetDetachKeepsStopped(bool stop) { + const uint32_t idx = ePropertyDetachKeepsStopped; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop); +} + +bool ProcessProperties::GetWarningsOptimization() const { + const uint32_t idx = ePropertyWarningOptimization; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} + +bool ProcessProperties::GetStopOnExec() const { + const uint32_t idx = ePropertyStopOnExec; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} + +std::chrono::seconds ProcessProperties::GetUtilityExpressionTimeout() const { + const uint32_t idx = ePropertyUtilityExpressionTimeout; + uint64_t value = m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_process_properties[idx].default_uint_value); + return std::chrono::seconds(value); +} + +Status ProcessLaunchCommandOptions::SetOptionValue( + uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 's': // Stop at program entry point + launch_info.GetFlags().Set(eLaunchFlagStopAtEntry); + break; + + case 'i': // STDIN for read only + { + FileAction action; + if (action.Open(STDIN_FILENO, FileSpec(option_arg), true, false)) + launch_info.AppendFileAction(action); + break; + } + + case 'o': // Open STDOUT for write only + { + FileAction action; + if (action.Open(STDOUT_FILENO, FileSpec(option_arg), false, true)) + launch_info.AppendFileAction(action); + break; + } + + case 'e': // STDERR for write only + { + FileAction action; + if (action.Open(STDERR_FILENO, FileSpec(option_arg), false, true)) + launch_info.AppendFileAction(action); + break; + } + + case 'p': // Process plug-in name + launch_info.SetProcessPluginName(option_arg); + break; + + case 'n': // Disable STDIO + { + FileAction action; + const FileSpec dev_null(FileSystem::DEV_NULL); + if (action.Open(STDIN_FILENO, dev_null, true, false)) + launch_info.AppendFileAction(action); + if (action.Open(STDOUT_FILENO, dev_null, false, true)) + launch_info.AppendFileAction(action); + if (action.Open(STDERR_FILENO, dev_null, false, true)) + launch_info.AppendFileAction(action); + break; + } + + case 'w': + launch_info.SetWorkingDirectory(FileSpec(option_arg)); + break; + + case 't': // Open process in new terminal window + launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY); + break; + + case 'a': { + TargetSP target_sp = + execution_context ? execution_context->GetTargetSP() : TargetSP(); + PlatformSP platform_sp = + target_sp ? target_sp->GetPlatform() : PlatformSP(); + launch_info.GetArchitecture() = + Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); + } break; + + case 'A': // Disable ASLR. + { + bool success; + const bool disable_aslr_arg = + OptionArgParser::ToBoolean(option_arg, true, &success); + if (success) + disable_aslr = disable_aslr_arg ? eLazyBoolYes : eLazyBoolNo; + else + error.SetErrorStringWithFormat( + "Invalid boolean value for disable-aslr option: '%s'", + option_arg.empty() ? "<null>" : option_arg.str().c_str()); + break; + } + + case 'X': // shell expand args. + { + bool success; + const bool expand_args = + OptionArgParser::ToBoolean(option_arg, true, &success); + if (success) + launch_info.SetShellExpandArguments(expand_args); + else + error.SetErrorStringWithFormat( + "Invalid boolean value for shell-expand-args option: '%s'", + option_arg.empty() ? "<null>" : option_arg.str().c_str()); + break; + } + + case 'c': + if (!option_arg.empty()) + launch_info.SetShell(FileSpec(option_arg)); + else + launch_info.SetShell(HostInfo::GetDefaultShell()); + break; + + case 'v': + launch_info.GetEnvironment().insert(option_arg); + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option character '%c'", + short_option); + break; + } + return error; +} + +static constexpr OptionDefinition g_process_launch_options[] = { + {LLDB_OPT_SET_ALL, false, "stop-at-entry", 's', OptionParser::eNoArgument, + nullptr, {}, 0, eArgTypeNone, + "Stop at the entry point of the program when launching a process."}, + {LLDB_OPT_SET_ALL, false, "disable-aslr", 'A', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, + "Set whether to disable address space layout randomization when launching " + "a process."}, + {LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypePlugin, + "Name of the process plugin you want to use."}, + {LLDB_OPT_SET_ALL, false, "working-dir", 'w', + OptionParser::eRequiredArgument, nullptr, {}, 0, + eArgTypeDirectoryName, + "Set the current working directory to <path> when running the inferior."}, + {LLDB_OPT_SET_ALL, false, "arch", 'a', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeArchitecture, + "Set the architecture for the process to launch when ambiguous."}, + {LLDB_OPT_SET_ALL, false, "environment", 'v', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, + "Specify an environment variable name/value string (--environment " + "NAME=VALUE). Can be specified multiple times for subsequent environment " + "entries."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "shell", 'c', + OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeFilename, + "Run the process in a shell (not supported on all platforms)."}, + + {LLDB_OPT_SET_1, false, "stdin", 'i', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeFilename, + "Redirect stdin for the process to <filename>."}, + {LLDB_OPT_SET_1, false, "stdout", 'o', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeFilename, + "Redirect stdout for the process to <filename>."}, + {LLDB_OPT_SET_1, false, "stderr", 'e', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeFilename, + "Redirect stderr for the process to <filename>."}, + + {LLDB_OPT_SET_2, false, "tty", 't', OptionParser::eNoArgument, nullptr, + {}, 0, eArgTypeNone, + "Start the process in a terminal (not supported on all platforms)."}, + + {LLDB_OPT_SET_3, false, "no-stdio", 'n', OptionParser::eNoArgument, nullptr, + {}, 0, eArgTypeNone, + "Do not set up for terminal I/O to go to running process."}, + {LLDB_OPT_SET_4, false, "shell-expand-args", 'X', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, + "Set whether to shell expand arguments to the process when launching."}, +}; + +llvm::ArrayRef<OptionDefinition> ProcessLaunchCommandOptions::GetDefinitions() { + return llvm::makeArrayRef(g_process_launch_options); +} + +ProcessSP Process::FindPlugin(lldb::TargetSP target_sp, + llvm::StringRef plugin_name, + ListenerSP listener_sp, + const FileSpec *crash_file_path) { + static uint32_t g_process_unique_id = 0; + + ProcessSP process_sp; + ProcessCreateInstance create_callback = nullptr; + if (!plugin_name.empty()) { + ConstString const_plugin_name(plugin_name); + create_callback = + PluginManager::GetProcessCreateCallbackForPluginName(const_plugin_name); + if (create_callback) { + process_sp = create_callback(target_sp, listener_sp, crash_file_path); + if (process_sp) { + if (process_sp->CanDebug(target_sp, true)) { + process_sp->m_process_unique_id = ++g_process_unique_id; + } else + process_sp.reset(); + } + } + } else { + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetProcessCreateCallbackAtIndex(idx)) != nullptr; + ++idx) { + process_sp = create_callback(target_sp, listener_sp, crash_file_path); + if (process_sp) { + if (process_sp->CanDebug(target_sp, false)) { + process_sp->m_process_unique_id = ++g_process_unique_id; + break; + } else + process_sp.reset(); + } + } + } + return process_sp; +} + +ConstString &Process::GetStaticBroadcasterClass() { + static ConstString class_name("lldb.process"); + return class_name; +} + +Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp) + : Process(target_sp, listener_sp, + UnixSignals::Create(HostInfo::GetArchitecture())) { + // This constructor just delegates to the full Process constructor, + // defaulting to using the Host's UnixSignals. +} + +Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp, + const UnixSignalsSP &unix_signals_sp) + : ProcessProperties(this), UserID(LLDB_INVALID_PROCESS_ID), + Broadcaster((target_sp->GetDebugger().GetBroadcasterManager()), + Process::GetStaticBroadcasterClass().AsCString()), + m_target_wp(target_sp), m_public_state(eStateUnloaded), + m_private_state(eStateUnloaded), + m_private_state_broadcaster(nullptr, + "lldb.process.internal_state_broadcaster"), + m_private_state_control_broadcaster( + nullptr, "lldb.process.internal_state_control_broadcaster"), + m_private_state_listener_sp( + Listener::MakeListener("lldb.process.internal_state_listener")), + m_mod_id(), m_process_unique_id(0), m_thread_index_id(0), + m_thread_id_to_index_id_map(), m_exit_status(-1), m_exit_string(), + m_exit_status_mutex(), m_thread_mutex(), m_thread_list_real(this), + m_thread_list(this), m_extended_thread_list(this), + m_extended_thread_stop_id(0), m_queue_list(this), m_queue_list_stop_id(0), + m_notifications(), m_image_tokens(), m_listener_sp(listener_sp), + m_breakpoint_site_list(), m_dynamic_checkers_up(), + m_unix_signals_sp(unix_signals_sp), m_abi_sp(), m_process_input_reader(), + m_stdio_communication("process.stdio"), m_stdio_communication_mutex(), + m_stdin_forward(false), m_stdout_data(), m_stderr_data(), + m_profile_data_comm_mutex(), m_profile_data(), m_iohandler_sync(0), + m_memory_cache(*this), m_allocated_memory_cache(*this), + m_should_detach(false), m_next_event_action_up(), m_public_run_lock(), + m_private_run_lock(), m_finalizing(false), m_finalize_called(false), + m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false), + m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false), + m_can_interpret_function_calls(false), m_warnings_issued(), + m_run_thread_plan_lock(), m_can_jit(eCanJITDontKnow) { + CheckInWithManager(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, "%p Process::Process()", static_cast<void *>(this)); + + if (!m_unix_signals_sp) + m_unix_signals_sp = std::make_shared<UnixSignals>(); + + SetEventName(eBroadcastBitStateChanged, "state-changed"); + SetEventName(eBroadcastBitInterrupt, "interrupt"); + SetEventName(eBroadcastBitSTDOUT, "stdout-available"); + SetEventName(eBroadcastBitSTDERR, "stderr-available"); + SetEventName(eBroadcastBitProfileData, "profile-data-available"); + SetEventName(eBroadcastBitStructuredData, "structured-data-available"); + + m_private_state_control_broadcaster.SetEventName( + eBroadcastInternalStateControlStop, "control-stop"); + m_private_state_control_broadcaster.SetEventName( + eBroadcastInternalStateControlPause, "control-pause"); + m_private_state_control_broadcaster.SetEventName( + eBroadcastInternalStateControlResume, "control-resume"); + + m_listener_sp->StartListeningForEvents( + this, eBroadcastBitStateChanged | eBroadcastBitInterrupt | + eBroadcastBitSTDOUT | eBroadcastBitSTDERR | + eBroadcastBitProfileData | eBroadcastBitStructuredData); + + m_private_state_listener_sp->StartListeningForEvents( + &m_private_state_broadcaster, + eBroadcastBitStateChanged | eBroadcastBitInterrupt); + + m_private_state_listener_sp->StartListeningForEvents( + &m_private_state_control_broadcaster, + eBroadcastInternalStateControlStop | eBroadcastInternalStateControlPause | + eBroadcastInternalStateControlResume); + // We need something valid here, even if just the default UnixSignalsSP. + assert(m_unix_signals_sp && "null m_unix_signals_sp after initialization"); + + // Allow the platform to override the default cache line size + OptionValueSP value_sp = + m_collection_sp + ->GetPropertyAtIndex(nullptr, true, ePropertyMemCacheLineSize) + ->GetValue(); + uint32_t platform_cache_line_size = + target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize(); + if (!value_sp->OptionWasSet() && platform_cache_line_size != 0) + value_sp->SetUInt64Value(platform_cache_line_size); +} + +Process::~Process() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, "%p Process::~Process()", static_cast<void *>(this)); + StopPrivateStateThread(); + + // ThreadList::Clear() will try to acquire this process's mutex, so + // explicitly clear the thread list here to ensure that the mutex is not + // destroyed before the thread list. + m_thread_list.Clear(); +} + +const ProcessPropertiesSP &Process::GetGlobalProperties() { + // NOTE: intentional leak so we don't crash if global destructor chain gets + // called as other threads still use the result of this function + static ProcessPropertiesSP *g_settings_sp_ptr = + new ProcessPropertiesSP(new ProcessProperties(nullptr)); + return *g_settings_sp_ptr; +} + +void Process::Finalize() { + m_finalizing = true; + + // Destroy this process if needed + switch (GetPrivateState()) { + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateStopped: + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + Destroy(false); + break; + + case eStateInvalid: + case eStateUnloaded: + case eStateDetached: + case eStateExited: + break; + } + + // Clear our broadcaster before we proceed with destroying + Broadcaster::Clear(); + + // Do any cleanup needed prior to being destructed... Subclasses that + // override this method should call this superclass method as well. + + // We need to destroy the loader before the derived Process class gets + // destroyed since it is very likely that undoing the loader will require + // access to the real process. + m_dynamic_checkers_up.reset(); + m_abi_sp.reset(); + m_os_up.reset(); + m_system_runtime_up.reset(); + m_dyld_up.reset(); + m_jit_loaders_up.reset(); + m_thread_list_real.Destroy(); + m_thread_list.Destroy(); + m_extended_thread_list.Destroy(); + m_queue_list.Clear(); + m_queue_list_stop_id = 0; + std::vector<Notifications> empty_notifications; + m_notifications.swap(empty_notifications); + m_image_tokens.clear(); + m_memory_cache.Clear(); + m_allocated_memory_cache.Clear(); + { + std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex); + m_language_runtimes.clear(); + } + m_instrumentation_runtimes.clear(); + m_next_event_action_up.reset(); + // Clear the last natural stop ID since it has a strong reference to this + // process + m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); + //#ifdef LLDB_CONFIGURATION_DEBUG + // StreamFile s(stdout, false); + // EventSP event_sp; + // while (m_private_state_listener_sp->GetNextEvent(event_sp)) + // { + // event_sp->Dump (&s); + // s.EOL(); + // } + //#endif + // We have to be very careful here as the m_private_state_listener might + // contain events that have ProcessSP values in them which can keep this + // process around forever. These events need to be cleared out. + m_private_state_listener_sp->Clear(); + m_public_run_lock.TrySetRunning(); // This will do nothing if already locked + m_public_run_lock.SetStopped(); + m_private_run_lock.TrySetRunning(); // This will do nothing if already locked + m_private_run_lock.SetStopped(); + m_structured_data_plugin_map.clear(); + m_finalize_called = true; +} + +void Process::RegisterNotificationCallbacks(const Notifications &callbacks) { + m_notifications.push_back(callbacks); + if (callbacks.initialize != nullptr) + callbacks.initialize(callbacks.baton, this); +} + +bool Process::UnregisterNotificationCallbacks(const Notifications &callbacks) { + std::vector<Notifications>::iterator pos, end = m_notifications.end(); + for (pos = m_notifications.begin(); pos != end; ++pos) { + if (pos->baton == callbacks.baton && + pos->initialize == callbacks.initialize && + pos->process_state_changed == callbacks.process_state_changed) { + m_notifications.erase(pos); + return true; + } + } + return false; +} + +void Process::SynchronouslyNotifyStateChanged(StateType state) { + std::vector<Notifications>::iterator notification_pos, + notification_end = m_notifications.end(); + for (notification_pos = m_notifications.begin(); + notification_pos != notification_end; ++notification_pos) { + if (notification_pos->process_state_changed) + notification_pos->process_state_changed(notification_pos->baton, this, + state); + } +} + +// FIXME: We need to do some work on events before the general Listener sees +// them. +// For instance if we are continuing from a breakpoint, we need to ensure that +// we do the little "insert real insn, step & stop" trick. But we can't do +// that when the event is delivered by the broadcaster - since that is done on +// the thread that is waiting for new events, so if we needed more than one +// event for our handling, we would stall. So instead we do it when we fetch +// the event off of the queue. +// + +StateType Process::GetNextEvent(EventSP &event_sp) { + StateType state = eStateInvalid; + + if (m_listener_sp->GetEventForBroadcaster(this, event_sp, + std::chrono::seconds(0)) && + event_sp) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + return state; +} + +void Process::SyncIOHandler(uint32_t iohandler_id, + const Timeout<std::micro> &timeout) { + // don't sync (potentially context switch) in case where there is no process + // IO + if (!m_process_input_reader) + return; + + auto Result = m_iohandler_sync.WaitForValueNotEqualTo(iohandler_id, timeout); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (Result) { + LLDB_LOG( + log, + "waited from m_iohandler_sync to change from {0}. New value is {1}.", + iohandler_id, *Result); + } else { + LLDB_LOG(log, "timed out waiting for m_iohandler_sync to change from {0}.", + iohandler_id); + } +} + +StateType Process::WaitForProcessToStop(const Timeout<std::micro> &timeout, + EventSP *event_sp_ptr, bool wait_always, + ListenerSP hijack_listener_sp, + Stream *stream, bool use_run_lock) { + // We can't just wait for a "stopped" event, because the stopped event may + // have restarted the target. We have to actually check each event, and in + // the case of a stopped event check the restarted flag on the event. + if (event_sp_ptr) + event_sp_ptr->reset(); + StateType state = GetState(); + // If we are exited or detached, we won't ever get back to any other valid + // state... + if (state == eStateDetached || state == eStateExited) + return state; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOG(log, "timeout = {0}", timeout); + + if (!wait_always && StateIsStoppedState(state, true) && + StateIsStoppedState(GetPrivateState(), true)) { + LLDB_LOGF(log, + "Process::%s returning without waiting for events; process " + "private and public states are already 'stopped'.", + __FUNCTION__); + // We need to toggle the run lock as this won't get done in + // SetPublicState() if the process is hijacked. + if (hijack_listener_sp && use_run_lock) + m_public_run_lock.SetStopped(); + return state; + } + + while (state != eStateInvalid) { + EventSP event_sp; + state = GetStateChangedEvents(event_sp, timeout, hijack_listener_sp); + if (event_sp_ptr && event_sp) + *event_sp_ptr = event_sp; + + bool pop_process_io_handler = (hijack_listener_sp.get() != nullptr); + Process::HandleProcessStateChangedEvent(event_sp, stream, + pop_process_io_handler); + + switch (state) { + case eStateCrashed: + case eStateDetached: + case eStateExited: + case eStateUnloaded: + // We need to toggle the run lock as this won't get done in + // SetPublicState() if the process is hijacked. + if (hijack_listener_sp && use_run_lock) + m_public_run_lock.SetStopped(); + return state; + case eStateStopped: + if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) + continue; + else { + // We need to toggle the run lock as this won't get done in + // SetPublicState() if the process is hijacked. + if (hijack_listener_sp && use_run_lock) + m_public_run_lock.SetStopped(); + return state; + } + default: + continue; + } + } + return state; +} + +bool Process::HandleProcessStateChangedEvent(const EventSP &event_sp, + Stream *stream, + bool &pop_process_io_handler) { + const bool handle_pop = pop_process_io_handler; + + pop_process_io_handler = false; + ProcessSP process_sp = + Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); + + if (!process_sp) + return false; + + StateType event_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + if (event_state == eStateInvalid) + return false; + + switch (event_state) { + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateStepping: + case eStateDetached: + if (stream) + stream->Printf("Process %" PRIu64 " %s\n", process_sp->GetID(), + StateAsCString(event_state)); + if (event_state == eStateDetached) + pop_process_io_handler = true; + break; + + case eStateConnected: + case eStateRunning: + // Don't be chatty when we run... + break; + + case eStateExited: + if (stream) + process_sp->GetStatus(*stream); + pop_process_io_handler = true; + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // Make sure the program hasn't been auto-restarted: + if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { + if (stream) { + size_t num_reasons = + Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); + if (num_reasons > 0) { + // FIXME: Do we want to report this, or would that just be annoyingly + // chatty? + if (num_reasons == 1) { + const char *reason = + Process::ProcessEventData::GetRestartedReasonAtIndex( + event_sp.get(), 0); + stream->Printf("Process %" PRIu64 " stopped and restarted: %s\n", + process_sp->GetID(), + reason ? reason : "<UNKNOWN REASON>"); + } else { + stream->Printf("Process %" PRIu64 + " stopped and restarted, reasons:\n", + process_sp->GetID()); + + for (size_t i = 0; i < num_reasons; i++) { + const char *reason = + Process::ProcessEventData::GetRestartedReasonAtIndex( + event_sp.get(), i); + stream->Printf("\t%s\n", reason ? reason : "<UNKNOWN REASON>"); + } + } + } + } + } else { + StopInfoSP curr_thread_stop_info_sp; + // Lock the thread list so it doesn't change on us, this is the scope for + // the locker: + { + ThreadList &thread_list = process_sp->GetThreadList(); + std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex()); + + ThreadSP curr_thread(thread_list.GetSelectedThread()); + ThreadSP thread; + StopReason curr_thread_stop_reason = eStopReasonInvalid; + if (curr_thread) { + curr_thread_stop_reason = curr_thread->GetStopReason(); + curr_thread_stop_info_sp = curr_thread->GetStopInfo(); + } + if (!curr_thread || !curr_thread->IsValid() || + curr_thread_stop_reason == eStopReasonInvalid || + curr_thread_stop_reason == eStopReasonNone) { + // Prefer a thread that has just completed its plan over another + // thread as current thread. + ThreadSP plan_thread; + ThreadSP other_thread; + + const size_t num_threads = thread_list.GetSize(); + size_t i; + for (i = 0; i < num_threads; ++i) { + thread = thread_list.GetThreadAtIndex(i); + StopReason thread_stop_reason = thread->GetStopReason(); + switch (thread_stop_reason) { + case eStopReasonInvalid: + case eStopReasonNone: + break; + + case eStopReasonSignal: { + // Don't select a signal thread if we weren't going to stop at + // that signal. We have to have had another reason for stopping + // here, and the user doesn't want to see this thread. + uint64_t signo = thread->GetStopInfo()->GetValue(); + if (process_sp->GetUnixSignals()->GetShouldStop(signo)) { + if (!other_thread) + other_thread = thread; + } + break; + } + case eStopReasonTrace: + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonThreadExiting: + case eStopReasonInstrumentation: + if (!other_thread) + other_thread = thread; + break; + case eStopReasonPlanComplete: + if (!plan_thread) + plan_thread = thread; + break; + } + } + if (plan_thread) + thread_list.SetSelectedThreadByID(plan_thread->GetID()); + else if (other_thread) + thread_list.SetSelectedThreadByID(other_thread->GetID()); + else { + if (curr_thread && curr_thread->IsValid()) + thread = curr_thread; + else + thread = thread_list.GetThreadAtIndex(0); + + if (thread) + thread_list.SetSelectedThreadByID(thread->GetID()); + } + } + } + // Drop the ThreadList mutex by here, since GetThreadStatus below might + // have to run code, e.g. for Data formatters, and if we hold the + // ThreadList mutex, then the process is going to have a hard time + // restarting the process. + if (stream) { + Debugger &debugger = process_sp->GetTarget().GetDebugger(); + if (debugger.GetTargetList().GetSelectedTarget().get() == + &process_sp->GetTarget()) { + const bool only_threads_with_stop_reason = true; + const uint32_t start_frame = 0; + const uint32_t num_frames = 1; + const uint32_t num_frames_with_source = 1; + const bool stop_format = true; + process_sp->GetStatus(*stream); + process_sp->GetThreadStatus(*stream, only_threads_with_stop_reason, + start_frame, num_frames, + num_frames_with_source, + stop_format); + if (curr_thread_stop_info_sp) { + lldb::addr_t crashing_address; + ValueObjectSP valobj_sp = StopInfo::GetCrashingDereference( + curr_thread_stop_info_sp, &crashing_address); + if (valobj_sp) { + const bool qualify_cxx_base_classes = false; + + const ValueObject::GetExpressionPathFormat format = + ValueObject::GetExpressionPathFormat:: + eGetExpressionPathFormatHonorPointers; + stream->PutCString("Likely cause: "); + valobj_sp->GetExpressionPath(*stream, qualify_cxx_base_classes, + format); + stream->Printf(" accessed 0x%" PRIx64 "\n", crashing_address); + } + } + } else { + uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget( + process_sp->GetTarget().shared_from_this()); + if (target_idx != UINT32_MAX) + stream->Printf("Target %d: (", target_idx); + else + stream->Printf("Target <unknown index>: ("); + process_sp->GetTarget().Dump(stream, eDescriptionLevelBrief); + stream->Printf(") stopped.\n"); + } + } + + // Pop the process IO handler + pop_process_io_handler = true; + } + break; + } + + if (handle_pop && pop_process_io_handler) + process_sp->PopProcessIOHandler(); + + return true; +} + +bool Process::HijackProcessEvents(ListenerSP listener_sp) { + if (listener_sp) { + return HijackBroadcaster(listener_sp, eBroadcastBitStateChanged | + eBroadcastBitInterrupt); + } else + return false; +} + +void Process::RestoreProcessEvents() { RestoreBroadcaster(); } + +StateType Process::GetStateChangedEvents(EventSP &event_sp, + const Timeout<std::micro> &timeout, + ListenerSP hijack_listener_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); + + ListenerSP listener_sp = hijack_listener_sp; + if (!listener_sp) + listener_sp = m_listener_sp; + + StateType state = eStateInvalid; + if (listener_sp->GetEventForBroadcasterWithType( + this, eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp, + timeout)) { + if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + else + LLDB_LOG(log, "got no event or was interrupted."); + } + + LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout, state); + return state; +} + +Event *Process::PeekAtStateChangedEvents() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + LLDB_LOGF(log, "Process::%s...", __FUNCTION__); + + Event *event_ptr; + event_ptr = m_listener_sp->PeekAtNextEventForBroadcasterWithType( + this, eBroadcastBitStateChanged); + if (log) { + if (event_ptr) { + LLDB_LOGF(log, "Process::%s (event_ptr) => %s", __FUNCTION__, + StateAsCString(ProcessEventData::GetStateFromEvent(event_ptr))); + } else { + LLDB_LOGF(log, "Process::%s no events found", __FUNCTION__); + } + } + return event_ptr; +} + +StateType +Process::GetStateChangedEventsPrivate(EventSP &event_sp, + const Timeout<std::micro> &timeout) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); + + StateType state = eStateInvalid; + if (m_private_state_listener_sp->GetEventForBroadcasterWithType( + &m_private_state_broadcaster, + eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp, + timeout)) + if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout, + state == eStateInvalid ? "TIMEOUT" : StateAsCString(state)); + return state; +} + +bool Process::GetEventsPrivate(EventSP &event_sp, + const Timeout<std::micro> &timeout, + bool control_only) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); + + if (control_only) + return m_private_state_listener_sp->GetEventForBroadcaster( + &m_private_state_control_broadcaster, event_sp, timeout); + else + return m_private_state_listener_sp->GetEvent(event_sp, timeout); +} + +bool Process::IsRunning() const { + return StateIsRunningState(m_public_state.GetValue()); +} + +int Process::GetExitStatus() { + std::lock_guard<std::mutex> guard(m_exit_status_mutex); + + if (m_public_state.GetValue() == eStateExited) + return m_exit_status; + return -1; +} + +const char *Process::GetExitDescription() { + std::lock_guard<std::mutex> guard(m_exit_status_mutex); + + if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty()) + return m_exit_string.c_str(); + return nullptr; +} + +bool Process::SetExitStatus(int status, const char *cstr) { + // Use a mutex to protect setting the exit status. + std::lock_guard<std::mutex> guard(m_exit_status_mutex); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | + LIBLLDB_LOG_PROCESS)); + LLDB_LOGF( + log, "Process::SetExitStatus (status=%i (0x%8.8x), description=%s%s%s)", + status, status, cstr ? "\"" : "", cstr ? cstr : "NULL", cstr ? "\"" : ""); + + // We were already in the exited state + if (m_private_state.GetValue() == eStateExited) { + LLDB_LOGF(log, "Process::SetExitStatus () ignoring exit status because " + "state was already set to eStateExited"); + return false; + } + + m_exit_status = status; + if (cstr) + m_exit_string = cstr; + else + m_exit_string.clear(); + + // Clear the last natural stop ID since it has a strong reference to this + // process + m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); + + SetPrivateState(eStateExited); + + // Allow subclasses to do some cleanup + DidExit(); + + return true; +} + +bool Process::IsAlive() { + switch (m_private_state.GetValue()) { + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateStopped: + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + return true; + default: + return false; + } +} + +// This static callback can be used to watch for local child processes on the +// current host. The child process exits, the process will be found in the +// global target list (we want to be completely sure that the +// lldb_private::Process doesn't go away before we can deliver the signal. +bool Process::SetProcessExitStatus( + lldb::pid_t pid, bool exited, + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero + ) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, + "Process::SetProcessExitStatus (pid=%" PRIu64 + ", exited=%i, signal=%i, exit_status=%i)\n", + pid, exited, signo, exit_status); + + if (exited) { + TargetSP target_sp(Debugger::FindTargetWithProcessID(pid)); + if (target_sp) { + ProcessSP process_sp(target_sp->GetProcessSP()); + if (process_sp) { + const char *signal_cstr = nullptr; + if (signo) + signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo); + + process_sp->SetExitStatus(exit_status, signal_cstr); + } + } + return true; + } + return false; +} + +void Process::UpdateThreadListIfNeeded() { + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize(false) == 0 || + stop_id != m_thread_list.GetStopID()) { + const StateType state = GetPrivateState(); + if (StateIsStoppedState(state, true)) { + std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); + // m_thread_list does have its own mutex, but we need to hold onto the + // mutex between the call to UpdateThreadList(...) and the + // os->UpdateThreadList(...) so it doesn't change on us + ThreadList &old_thread_list = m_thread_list; + ThreadList real_thread_list(this); + ThreadList new_thread_list(this); + // Always update the thread list with the protocol specific thread list, + // but only update if "true" is returned + if (UpdateThreadList(m_thread_list_real, real_thread_list)) { + // Don't call into the OperatingSystem to update the thread list if we + // are shutting down, since that may call back into the SBAPI's, + // requiring the API lock which is already held by whoever is shutting + // us down, causing a deadlock. + OperatingSystem *os = GetOperatingSystem(); + if (os && !m_destroy_in_process) { + // Clear any old backing threads where memory threads might have been + // backed by actual threads from the lldb_private::Process subclass + size_t num_old_threads = old_thread_list.GetSize(false); + for (size_t i = 0; i < num_old_threads; ++i) + old_thread_list.GetThreadAtIndex(i, false)->ClearBackingThread(); + + // Turn off dynamic types to ensure we don't run any expressions. + // Objective-C can run an expression to determine if a SBValue is a + // dynamic type or not and we need to avoid this. OperatingSystem + // plug-ins can't run expressions that require running code... + + Target &target = GetTarget(); + const lldb::DynamicValueType saved_prefer_dynamic = + target.GetPreferDynamicValue(); + if (saved_prefer_dynamic != lldb::eNoDynamicValues) + target.SetPreferDynamicValue(lldb::eNoDynamicValues); + + // Now let the OperatingSystem plug-in update the thread list + + os->UpdateThreadList( + old_thread_list, // Old list full of threads created by OS plug-in + real_thread_list, // The actual thread list full of threads + // created by each lldb_private::Process + // subclass + new_thread_list); // The new thread list that we will show to the + // user that gets filled in + + if (saved_prefer_dynamic != lldb::eNoDynamicValues) + target.SetPreferDynamicValue(saved_prefer_dynamic); + } else { + // No OS plug-in, the new thread list is the same as the real thread + // list + new_thread_list = real_thread_list; + } + + m_thread_list_real.Update(real_thread_list); + m_thread_list.Update(new_thread_list); + m_thread_list.SetStopID(stop_id); + + if (GetLastNaturalStopID() != m_extended_thread_stop_id) { + // Clear any extended threads that we may have accumulated previously + m_extended_thread_list.Clear(); + m_extended_thread_stop_id = GetLastNaturalStopID(); + + m_queue_list.Clear(); + m_queue_list_stop_id = GetLastNaturalStopID(); + } + } + } + } +} + +void Process::UpdateQueueListIfNeeded() { + if (m_system_runtime_up) { + if (m_queue_list.GetSize() == 0 || + m_queue_list_stop_id != GetLastNaturalStopID()) { + const StateType state = GetPrivateState(); + if (StateIsStoppedState(state, true)) { + m_system_runtime_up->PopulateQueueList(m_queue_list); + m_queue_list_stop_id = GetLastNaturalStopID(); + } + } + } +} + +ThreadSP Process::CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context) { + OperatingSystem *os = GetOperatingSystem(); + if (os) + return os->CreateThread(tid, context); + return ThreadSP(); +} + +uint32_t Process::GetNextThreadIndexID(uint64_t thread_id) { + return AssignIndexIDToThread(thread_id); +} + +bool Process::HasAssignedIndexIDToThread(uint64_t thread_id) { + return (m_thread_id_to_index_id_map.find(thread_id) != + m_thread_id_to_index_id_map.end()); +} + +uint32_t Process::AssignIndexIDToThread(uint64_t thread_id) { + uint32_t result = 0; + std::map<uint64_t, uint32_t>::iterator iterator = + m_thread_id_to_index_id_map.find(thread_id); + if (iterator == m_thread_id_to_index_id_map.end()) { + result = ++m_thread_index_id; + m_thread_id_to_index_id_map[thread_id] = result; + } else { + result = iterator->second; + } + + return result; +} + +StateType Process::GetState() { + return m_public_state.GetValue(); +} + +void Process::SetPublicState(StateType new_state, bool restarted) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | + LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::SetPublicState (state = %s, restarted = %i)", + StateAsCString(new_state), restarted); + const StateType old_state = m_public_state.GetValue(); + m_public_state.SetValue(new_state); + + // On the transition from Run to Stopped, we unlock the writer end of the run + // lock. The lock gets locked in Resume, which is the public API to tell the + // program to run. + if (!StateChangedIsExternallyHijacked()) { + if (new_state == eStateDetached) { + LLDB_LOGF(log, + "Process::SetPublicState (%s) -- unlocking run lock for detach", + StateAsCString(new_state)); + m_public_run_lock.SetStopped(); + } else { + const bool old_state_is_stopped = StateIsStoppedState(old_state, false); + const bool new_state_is_stopped = StateIsStoppedState(new_state, false); + if ((old_state_is_stopped != new_state_is_stopped)) { + if (new_state_is_stopped && !restarted) { + LLDB_LOGF(log, "Process::SetPublicState (%s) -- unlocking run lock", + StateAsCString(new_state)); + m_public_run_lock.SetStopped(); + } + } + } + } +} + +Status Process::Resume() { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | + LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::Resume -- locking run lock"); + if (!m_public_run_lock.TrySetRunning()) { + Status error("Resume request failed - process still running."); + LLDB_LOGF(log, "Process::Resume: -- TrySetRunning failed, not resuming."); + return error; + } + Status error = PrivateResume(); + if (!error.Success()) { + // Undo running state change + m_public_run_lock.SetStopped(); + } + return error; +} + +static const char *g_resume_sync_name = "lldb.Process.ResumeSynchronous.hijack"; + +Status Process::ResumeSynchronous(Stream *stream) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | + LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::ResumeSynchronous -- locking run lock"); + if (!m_public_run_lock.TrySetRunning()) { + Status error("Resume request failed - process still running."); + LLDB_LOGF(log, "Process::Resume: -- TrySetRunning failed, not resuming."); + return error; + } + + ListenerSP listener_sp( + Listener::MakeListener(g_resume_sync_name)); + HijackProcessEvents(listener_sp); + + Status error = PrivateResume(); + if (error.Success()) { + StateType state = + WaitForProcessToStop(llvm::None, nullptr, true, listener_sp, stream); + const bool must_be_alive = + false; // eStateExited is ok, so this must be false + if (!StateIsStoppedState(state, must_be_alive)) + error.SetErrorStringWithFormat( + "process not in stopped state after synchronous resume: %s", + StateAsCString(state)); + } else { + // Undo running state change + m_public_run_lock.SetStopped(); + } + + // Undo the hijacking of process events... + RestoreProcessEvents(); + + return error; +} + +bool Process::StateChangedIsExternallyHijacked() { + if (IsHijackedForEvent(eBroadcastBitStateChanged)) { + const char *hijacking_name = GetHijackingListenerName(); + if (hijacking_name && + strcmp(hijacking_name, g_resume_sync_name)) + return true; + } + return false; +} + +bool Process::StateChangedIsHijackedForSynchronousResume() { + if (IsHijackedForEvent(eBroadcastBitStateChanged)) { + const char *hijacking_name = GetHijackingListenerName(); + if (hijacking_name && + strcmp(hijacking_name, g_resume_sync_name) == 0) + return true; + } + return false; +} + +StateType Process::GetPrivateState() { return m_private_state.GetValue(); } + +void Process::SetPrivateState(StateType new_state) { + if (m_finalize_called) + return; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | + LIBLLDB_LOG_PROCESS)); + bool state_changed = false; + + LLDB_LOGF(log, "Process::SetPrivateState (%s)", StateAsCString(new_state)); + + std::lock_guard<std::recursive_mutex> thread_guard(m_thread_list.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_private_state.GetMutex()); + + const StateType old_state = m_private_state.GetValueNoLock(); + state_changed = old_state != new_state; + + const bool old_state_is_stopped = StateIsStoppedState(old_state, false); + const bool new_state_is_stopped = StateIsStoppedState(new_state, false); + if (old_state_is_stopped != new_state_is_stopped) { + if (new_state_is_stopped) + m_private_run_lock.SetStopped(); + else + m_private_run_lock.SetRunning(); + } + + if (state_changed) { + m_private_state.SetValueNoLock(new_state); + EventSP event_sp( + new Event(eBroadcastBitStateChanged, + new ProcessEventData(shared_from_this(), new_state))); + if (StateIsStoppedState(new_state, false)) { + // Note, this currently assumes that all threads in the list stop when + // the process stops. In the future we will want to support a debugging + // model where some threads continue to run while others are stopped. + // When that happens we will either need a way for the thread list to + // identify which threads are stopping or create a special thread list + // containing only threads which actually stopped. + // + // The process plugin is responsible for managing the actual behavior of + // the threads and should have stopped any threads that are going to stop + // before we get here. + m_thread_list.DidStop(); + + m_mod_id.BumpStopID(); + if (!m_mod_id.IsLastResumeForUserExpression()) + m_mod_id.SetStopEventForLastNaturalStopID(event_sp); + m_memory_cache.Clear(); + LLDB_LOGF(log, "Process::SetPrivateState (%s) stop_id = %u", + StateAsCString(new_state), m_mod_id.GetStopID()); + } + + // Use our target to get a shared pointer to ourselves... + if (m_finalize_called && !PrivateStateThreadIsValid()) + BroadcastEvent(event_sp); + else + m_private_state_broadcaster.BroadcastEvent(event_sp); + } else { + LLDB_LOGF(log, + "Process::SetPrivateState (%s) state didn't change. Ignoring...", + StateAsCString(new_state)); + } +} + +void Process::SetRunningUserExpression(bool on) { + m_mod_id.SetRunningUserExpression(on); +} + +void Process::SetRunningUtilityFunction(bool on) { + m_mod_id.SetRunningUtilityFunction(on); +} + +addr_t Process::GetImageInfoAddress() { return LLDB_INVALID_ADDRESS; } + +const lldb::ABISP &Process::GetABI() { + if (!m_abi_sp) + m_abi_sp = ABI::FindPlugin(shared_from_this(), GetTarget().GetArchitecture()); + return m_abi_sp; +} + +std::vector<LanguageRuntime *> Process::GetLanguageRuntimes() { + std::vector<LanguageRuntime *> language_runtimes; + + if (m_finalizing) + return language_runtimes; + + std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex); + // Before we pass off a copy of the language runtimes, we must make sure that + // our collection is properly populated. It's possible that some of the + // language runtimes were not loaded yet, either because nobody requested it + // yet or the proper condition for loading wasn't yet met (e.g. libc++.so + // hadn't been loaded). + for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) { + if (LanguageRuntime *runtime = GetLanguageRuntime(lang_type)) + language_runtimes.emplace_back(runtime); + } + + return language_runtimes; +} + +LanguageRuntime *Process::GetLanguageRuntime(lldb::LanguageType language) { + if (m_finalizing) + return nullptr; + + LanguageRuntime *runtime = nullptr; + + std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex); + LanguageRuntimeCollection::iterator pos; + pos = m_language_runtimes.find(language); + if (pos == m_language_runtimes.end() || !pos->second) { + lldb::LanguageRuntimeSP runtime_sp( + LanguageRuntime::FindPlugin(this, language)); + + m_language_runtimes[language] = runtime_sp; + runtime = runtime_sp.get(); + } else + runtime = pos->second.get(); + + if (runtime) + // It's possible that a language runtime can support multiple LanguageTypes, + // for example, CPPLanguageRuntime will support eLanguageTypeC_plus_plus, + // eLanguageTypeC_plus_plus_03, etc. Because of this, we should get the + // primary language type and make sure that our runtime supports it. + assert(runtime->GetLanguageType() == Language::GetPrimaryLanguage(language)); + + return runtime; +} + +bool Process::IsPossibleDynamicValue(ValueObject &in_value) { + if (m_finalizing) + return false; + + if (in_value.IsDynamic()) + return false; + LanguageType known_type = in_value.GetObjectRuntimeLanguage(); + + if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC) { + LanguageRuntime *runtime = GetLanguageRuntime(known_type); + return runtime ? runtime->CouldHaveDynamicValue(in_value) : false; + } + + for (LanguageRuntime *runtime : GetLanguageRuntimes()) { + if (runtime->CouldHaveDynamicValue(in_value)) + return true; + } + + return false; +} + +void Process::SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) { + m_dynamic_checkers_up.reset(dynamic_checkers); +} + +BreakpointSiteList &Process::GetBreakpointSiteList() { + return m_breakpoint_site_list; +} + +const BreakpointSiteList &Process::GetBreakpointSiteList() const { + return m_breakpoint_site_list; +} + +void Process::DisableAllBreakpointSites() { + m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void { + // bp_site->SetEnabled(true); + DisableBreakpointSite(bp_site); + }); +} + +Status Process::ClearBreakpointSiteByID(lldb::user_id_t break_id) { + Status error(DisableBreakpointSiteByID(break_id)); + + if (error.Success()) + m_breakpoint_site_list.Remove(break_id); + + return error; +} + +Status Process::DisableBreakpointSiteByID(lldb::user_id_t break_id) { + Status error; + BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id); + if (bp_site_sp) { + if (bp_site_sp->IsEnabled()) + error = DisableBreakpointSite(bp_site_sp.get()); + } else { + error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, + break_id); + } + + return error; +} + +Status Process::EnableBreakpointSiteByID(lldb::user_id_t break_id) { + Status error; + BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id); + if (bp_site_sp) { + if (!bp_site_sp->IsEnabled()) + error = EnableBreakpointSite(bp_site_sp.get()); + } else { + error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, + break_id); + } + return error; +} + +lldb::break_id_t +Process::CreateBreakpointSite(const BreakpointLocationSP &owner, + bool use_hardware) { + addr_t load_addr = LLDB_INVALID_ADDRESS; + + bool show_error = true; + switch (GetState()) { + case eStateInvalid: + case eStateUnloaded: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateDetached: + case eStateExited: + show_error = false; + break; + + case eStateStopped: + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + show_error = IsAlive(); + break; + } + + // Reset the IsIndirect flag here, in case the location changes from pointing + // to a indirect symbol to a regular symbol. + owner->SetIsIndirect(false); + + if (owner->ShouldResolveIndirectFunctions()) { + Symbol *symbol = owner->GetAddress().CalculateSymbolContextSymbol(); + if (symbol && symbol->IsIndirect()) { + Status error; + Address symbol_address = symbol->GetAddress(); + load_addr = ResolveIndirectFunction(&symbol_address, error); + if (!error.Success() && show_error) { + GetTarget().GetDebugger().GetErrorStream().Printf( + "warning: failed to resolve indirect function at 0x%" PRIx64 + " for breakpoint %i.%i: %s\n", + symbol->GetLoadAddress(&GetTarget()), + owner->GetBreakpoint().GetID(), owner->GetID(), + error.AsCString() ? error.AsCString() : "unknown error"); + return LLDB_INVALID_BREAK_ID; + } + Address resolved_address(load_addr); + load_addr = resolved_address.GetOpcodeLoadAddress(&GetTarget()); + owner->SetIsIndirect(true); + } else + load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget()); + } else + load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget()); + + if (load_addr != LLDB_INVALID_ADDRESS) { + BreakpointSiteSP bp_site_sp; + + // Look up this breakpoint site. If it exists, then add this new owner, + // otherwise create a new breakpoint site and add it. + + bp_site_sp = m_breakpoint_site_list.FindByAddress(load_addr); + + if (bp_site_sp) { + bp_site_sp->AddOwner(owner); + owner->SetBreakpointSite(bp_site_sp); + return bp_site_sp->GetID(); + } else { + bp_site_sp.reset(new BreakpointSite(&m_breakpoint_site_list, owner, + load_addr, use_hardware)); + if (bp_site_sp) { + Status error = EnableBreakpointSite(bp_site_sp.get()); + if (error.Success()) { + owner->SetBreakpointSite(bp_site_sp); + return m_breakpoint_site_list.Add(bp_site_sp); + } else { + if (show_error || use_hardware) { + // Report error for setting breakpoint... + GetTarget().GetDebugger().GetErrorStream().Printf( + "warning: failed to set breakpoint site at 0x%" PRIx64 + " for breakpoint %i.%i: %s\n", + load_addr, owner->GetBreakpoint().GetID(), owner->GetID(), + error.AsCString() ? error.AsCString() : "unknown error"); + } + } + } + } + } + // We failed to enable the breakpoint + return LLDB_INVALID_BREAK_ID; +} + +void Process::RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id, + lldb::user_id_t owner_loc_id, + BreakpointSiteSP &bp_site_sp) { + uint32_t num_owners = bp_site_sp->RemoveOwner(owner_id, owner_loc_id); + if (num_owners == 0) { + // Don't try to disable the site if we don't have a live process anymore. + if (IsAlive()) + DisableBreakpointSite(bp_site_sp.get()); + m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress()); + } +} + +size_t Process::RemoveBreakpointOpcodesFromBuffer(addr_t bp_addr, size_t size, + uint8_t *buf) const { + size_t bytes_removed = 0; + BreakpointSiteList bp_sites_in_range; + + if (m_breakpoint_site_list.FindInRange(bp_addr, bp_addr + size, + bp_sites_in_range)) { + bp_sites_in_range.ForEach([bp_addr, size, + buf](BreakpointSite *bp_site) -> void { + if (bp_site->GetType() == BreakpointSite::eSoftware) { + addr_t intersect_addr; + size_t intersect_size; + size_t opcode_offset; + if (bp_site->IntersectsRange(bp_addr, size, &intersect_addr, + &intersect_size, &opcode_offset)) { + assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size); + assert(bp_addr < intersect_addr + intersect_size && + intersect_addr + intersect_size <= bp_addr + size); + assert(opcode_offset + intersect_size <= bp_site->GetByteSize()); + size_t buf_offset = intersect_addr - bp_addr; + ::memcpy(buf + buf_offset, + bp_site->GetSavedOpcodeBytes() + opcode_offset, + intersect_size); + } + } + }); + } + return bytes_removed; +} + +size_t Process::GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site) { + PlatformSP platform_sp(GetTarget().GetPlatform()); + if (platform_sp) + return platform_sp->GetSoftwareBreakpointTrapOpcode(GetTarget(), bp_site); + return 0; +} + +Status Process::EnableSoftwareBreakpoint(BreakpointSite *bp_site) { + Status error; + assert(bp_site != nullptr); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + const addr_t bp_addr = bp_site->GetLoadAddress(); + LLDB_LOGF( + log, "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64, + bp_site->GetID(), (uint64_t)bp_addr); + if (bp_site->IsEnabled()) { + LLDB_LOGF( + log, + "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 + " -- already enabled", + bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + + if (bp_addr == LLDB_INVALID_ADDRESS) { + error.SetErrorString("BreakpointSite contains an invalid load address."); + return error; + } + // Ask the lldb::Process subclass to fill in the correct software breakpoint + // trap for the breakpoint site + const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site); + + if (bp_opcode_size == 0) { + error.SetErrorStringWithFormat("Process::GetSoftwareBreakpointTrapOpcode() " + "returned zero, unable to get breakpoint " + "trap for address 0x%" PRIx64, + bp_addr); + } else { + const uint8_t *const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes(); + + if (bp_opcode_bytes == nullptr) { + error.SetErrorString( + "BreakpointSite doesn't contain a valid breakpoint trap opcode."); + return error; + } + + // Save the original opcode by reading it + if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size, + error) == bp_opcode_size) { + // Write a software breakpoint in place of the original opcode + if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) == + bp_opcode_size) { + uint8_t verify_bp_opcode_bytes[64]; + if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size, + error) == bp_opcode_size) { + if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes, + bp_opcode_size) == 0) { + bp_site->SetEnabled(true); + bp_site->SetType(BreakpointSite::eSoftware); + LLDB_LOGF(log, + "Process::EnableSoftwareBreakpoint (site_id = %d) " + "addr = 0x%" PRIx64 " -- SUCCESS", + bp_site->GetID(), (uint64_t)bp_addr); + } else + error.SetErrorString( + "failed to verify the breakpoint trap in memory."); + } else + error.SetErrorString( + "Unable to read memory to verify breakpoint trap."); + } else + error.SetErrorString("Unable to write breakpoint trap to memory."); + } else + error.SetErrorString("Unable to read memory at breakpoint address."); + } + if (log && error.Fail()) + LLDB_LOGF( + log, + "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 + " -- FAILED: %s", + bp_site->GetID(), (uint64_t)bp_addr, error.AsCString()); + return error; +} + +Status Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) { + Status error; + assert(bp_site != nullptr); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + addr_t bp_addr = bp_site->GetLoadAddress(); + lldb::user_id_t breakID = bp_site->GetID(); + LLDB_LOGF(log, + "Process::DisableSoftwareBreakpoint (breakID = %" PRIu64 + ") addr = 0x%" PRIx64, + breakID, (uint64_t)bp_addr); + + if (bp_site->IsHardware()) { + error.SetErrorString("Breakpoint site is a hardware breakpoint."); + } else if (bp_site->IsEnabled()) { + const size_t break_op_size = bp_site->GetByteSize(); + const uint8_t *const break_op = bp_site->GetTrapOpcodeBytes(); + if (break_op_size > 0) { + // Clear a software breakpoint instruction + uint8_t curr_break_op[8]; + assert(break_op_size <= sizeof(curr_break_op)); + bool break_op_found = false; + + // Read the breakpoint opcode + if (DoReadMemory(bp_addr, curr_break_op, break_op_size, error) == + break_op_size) { + bool verify = false; + // Make sure the breakpoint opcode exists at this address + if (::memcmp(curr_break_op, break_op, break_op_size) == 0) { + break_op_found = true; + // We found a valid breakpoint opcode at this address, now restore + // the saved opcode. + if (DoWriteMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), + break_op_size, error) == break_op_size) { + verify = true; + } else + error.SetErrorString( + "Memory write failed when restoring original opcode."); + } else { + error.SetErrorString( + "Original breakpoint trap is no longer in memory."); + // Set verify to true and so we can check if the original opcode has + // already been restored + verify = true; + } + + if (verify) { + uint8_t verify_opcode[8]; + assert(break_op_size < sizeof(verify_opcode)); + // Verify that our original opcode made it back to the inferior + if (DoReadMemory(bp_addr, verify_opcode, break_op_size, error) == + break_op_size) { + // compare the memory we just read with the original opcode + if (::memcmp(bp_site->GetSavedOpcodeBytes(), verify_opcode, + break_op_size) == 0) { + // SUCCESS + bp_site->SetEnabled(false); + LLDB_LOGF(log, + "Process::DisableSoftwareBreakpoint (site_id = %d) " + "addr = 0x%" PRIx64 " -- SUCCESS", + bp_site->GetID(), (uint64_t)bp_addr); + return error; + } else { + if (break_op_found) + error.SetErrorString("Failed to restore original opcode."); + } + } else + error.SetErrorString("Failed to read memory to verify that " + "breakpoint trap was restored."); + } + } else + error.SetErrorString( + "Unable to read memory that should contain the breakpoint trap."); + } + } else { + LLDB_LOGF( + log, + "Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 + " -- already disabled", + bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + + LLDB_LOGF( + log, + "Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 + " -- FAILED: %s", + bp_site->GetID(), (uint64_t)bp_addr, error.AsCString()); + return error; +} + +// Uncomment to verify memory caching works after making changes to caching +// code +//#define VERIFY_MEMORY_READS + +size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) { + error.Clear(); + if (!GetDisableMemoryCache()) { +#if defined(VERIFY_MEMORY_READS) + // Memory caching is enabled, with debug verification + + if (buf && size) { + // Uncomment the line below to make sure memory caching is working. + // I ran this through the test suite and got no assertions, so I am + // pretty confident this is working well. If any changes are made to + // memory caching, uncomment the line below and test your changes! + + // Verify all memory reads by using the cache first, then redundantly + // reading the same memory from the inferior and comparing to make sure + // everything is exactly the same. + std::string verify_buf(size, '\0'); + assert(verify_buf.size() == size); + const size_t cache_bytes_read = + m_memory_cache.Read(this, addr, buf, size, error); + Status verify_error; + const size_t verify_bytes_read = + ReadMemoryFromInferior(addr, const_cast<char *>(verify_buf.data()), + verify_buf.size(), verify_error); + assert(cache_bytes_read == verify_bytes_read); + assert(memcmp(buf, verify_buf.data(), verify_buf.size()) == 0); + assert(verify_error.Success() == error.Success()); + return cache_bytes_read; + } + return 0; +#else // !defined(VERIFY_MEMORY_READS) + // Memory caching is enabled, without debug verification + + return m_memory_cache.Read(addr, buf, size, error); +#endif // defined (VERIFY_MEMORY_READS) + } else { + // Memory caching is disabled + + return ReadMemoryFromInferior(addr, buf, size, error); + } +} + +size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str, + Status &error) { + char buf[256]; + out_str.clear(); + addr_t curr_addr = addr; + while (true) { + size_t length = ReadCStringFromMemory(curr_addr, buf, sizeof(buf), error); + if (length == 0) + break; + out_str.append(buf, length); + // If we got "length - 1" bytes, we didn't get the whole C string, we need + // to read some more characters + if (length == sizeof(buf) - 1) + curr_addr += length; + else + break; + } + return out_str.size(); +} + +size_t Process::ReadStringFromMemory(addr_t addr, char *dst, size_t max_bytes, + Status &error, size_t type_width) { + size_t total_bytes_read = 0; + if (dst && max_bytes && type_width && max_bytes >= type_width) { + // Ensure a null terminator independent of the number of bytes that is + // read. + memset(dst, 0, max_bytes); + size_t bytes_left = max_bytes - type_width; + + const char terminator[4] = {'\0', '\0', '\0', '\0'}; + assert(sizeof(terminator) >= type_width && "Attempting to validate a " + "string with more than 4 bytes " + "per character!"); + + addr_t curr_addr = addr; + const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); + char *curr_dst = dst; + + error.Clear(); + while (bytes_left > 0 && error.Success()) { + addr_t cache_line_bytes_left = + cache_line_size - (curr_addr % cache_line_size); + addr_t bytes_to_read = + std::min<addr_t>(bytes_left, cache_line_bytes_left); + size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error); + + if (bytes_read == 0) + break; + + // Search for a null terminator of correct size and alignment in + // bytes_read + size_t aligned_start = total_bytes_read - total_bytes_read % type_width; + for (size_t i = aligned_start; + i + type_width <= total_bytes_read + bytes_read; i += type_width) + if (::memcmp(&dst[i], terminator, type_width) == 0) { + error.Clear(); + return i; + } + + total_bytes_read += bytes_read; + curr_dst += bytes_read; + curr_addr += bytes_read; + bytes_left -= bytes_read; + } + } else { + if (max_bytes) + error.SetErrorString("invalid arguments"); + } + return total_bytes_read; +} + +// Deprecated in favor of ReadStringFromMemory which has wchar support and +// correct code to find null terminators. +size_t Process::ReadCStringFromMemory(addr_t addr, char *dst, + size_t dst_max_len, + Status &result_error) { + size_t total_cstr_len = 0; + if (dst && dst_max_len) { + result_error.Clear(); + // NULL out everything just to be safe + memset(dst, 0, dst_max_len); + Status error; + addr_t curr_addr = addr; + const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); + size_t bytes_left = dst_max_len - 1; + char *curr_dst = dst; + + while (bytes_left > 0) { + addr_t cache_line_bytes_left = + cache_line_size - (curr_addr % cache_line_size); + addr_t bytes_to_read = + std::min<addr_t>(bytes_left, cache_line_bytes_left); + size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error); + + if (bytes_read == 0) { + result_error = error; + dst[total_cstr_len] = '\0'; + break; + } + const size_t len = strlen(curr_dst); + + total_cstr_len += len; + + if (len < bytes_to_read) + break; + + curr_dst += bytes_read; + curr_addr += bytes_read; + bytes_left -= bytes_read; + } + } else { + if (dst == nullptr) + result_error.SetErrorString("invalid arguments"); + else + result_error.Clear(); + } + return total_cstr_len; +} + +size_t Process::ReadMemoryFromInferior(addr_t addr, void *buf, size_t size, + Status &error) { + if (buf == nullptr || size == 0) + return 0; + + size_t bytes_read = 0; + uint8_t *bytes = (uint8_t *)buf; + + while (bytes_read < size) { + const size_t curr_size = size - bytes_read; + const size_t curr_bytes_read = + DoReadMemory(addr + bytes_read, bytes + bytes_read, curr_size, error); + bytes_read += curr_bytes_read; + if (curr_bytes_read == curr_size || curr_bytes_read == 0) + break; + } + + // Replace any software breakpoint opcodes that fall into this range back + // into "buf" before we return + if (bytes_read > 0) + RemoveBreakpointOpcodesFromBuffer(addr, bytes_read, (uint8_t *)buf); + return bytes_read; +} + +uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr, + size_t integer_byte_size, + uint64_t fail_value, + Status &error) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, false, scalar, + error)) + return scalar.ULongLong(fail_value); + return fail_value; +} + +int64_t Process::ReadSignedIntegerFromMemory(lldb::addr_t vm_addr, + size_t integer_byte_size, + int64_t fail_value, + Status &error) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, true, scalar, + error)) + return scalar.SLongLong(fail_value); + return fail_value; +} + +addr_t Process::ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(vm_addr, GetAddressByteSize(), false, scalar, + error)) + return scalar.ULongLong(LLDB_INVALID_ADDRESS); + return LLDB_INVALID_ADDRESS; +} + +bool Process::WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value, + Status &error) { + Scalar scalar; + const uint32_t addr_byte_size = GetAddressByteSize(); + if (addr_byte_size <= 4) + scalar = (uint32_t)ptr_value; + else + scalar = ptr_value; + return WriteScalarToMemory(vm_addr, scalar, addr_byte_size, error) == + addr_byte_size; +} + +size_t Process::WriteMemoryPrivate(addr_t addr, const void *buf, size_t size, + Status &error) { + size_t bytes_written = 0; + const uint8_t *bytes = (const uint8_t *)buf; + + while (bytes_written < size) { + const size_t curr_size = size - bytes_written; + const size_t curr_bytes_written = DoWriteMemory( + addr + bytes_written, bytes + bytes_written, curr_size, error); + bytes_written += curr_bytes_written; + if (curr_bytes_written == curr_size || curr_bytes_written == 0) + break; + } + return bytes_written; +} + +size_t Process::WriteMemory(addr_t addr, const void *buf, size_t size, + Status &error) { +#if defined(ENABLE_MEMORY_CACHING) + m_memory_cache.Flush(addr, size); +#endif + + if (buf == nullptr || size == 0) + return 0; + + m_mod_id.BumpMemoryID(); + + // We need to write any data that would go where any current software traps + // (enabled software breakpoints) any software traps (breakpoints) that we + // may have placed in our tasks memory. + + BreakpointSiteList bp_sites_in_range; + if (!m_breakpoint_site_list.FindInRange(addr, addr + size, bp_sites_in_range)) + return WriteMemoryPrivate(addr, buf, size, error); + + // No breakpoint sites overlap + if (bp_sites_in_range.IsEmpty()) + return WriteMemoryPrivate(addr, buf, size, error); + + const uint8_t *ubuf = (const uint8_t *)buf; + uint64_t bytes_written = 0; + + bp_sites_in_range.ForEach([this, addr, size, &bytes_written, &ubuf, + &error](BreakpointSite *bp) -> void { + if (error.Fail()) + return; + + addr_t intersect_addr; + size_t intersect_size; + size_t opcode_offset; + const bool intersects = bp->IntersectsRange( + addr, size, &intersect_addr, &intersect_size, &opcode_offset); + UNUSED_IF_ASSERT_DISABLED(intersects); + assert(intersects); + assert(addr <= intersect_addr && intersect_addr < addr + size); + assert(addr < intersect_addr + intersect_size && + intersect_addr + intersect_size <= addr + size); + assert(opcode_offset + intersect_size <= bp->GetByteSize()); + + // Check for bytes before this breakpoint + const addr_t curr_addr = addr + bytes_written; + if (intersect_addr > curr_addr) { + // There are some bytes before this breakpoint that we need to just + // write to memory + size_t curr_size = intersect_addr - curr_addr; + size_t curr_bytes_written = + WriteMemoryPrivate(curr_addr, ubuf + bytes_written, curr_size, error); + bytes_written += curr_bytes_written; + if (curr_bytes_written != curr_size) { + // We weren't able to write all of the requested bytes, we are + // done looping and will return the number of bytes that we have + // written so far. + if (error.Success()) + error.SetErrorToGenericError(); + } + } + // Now write any bytes that would cover up any software breakpoints + // directly into the breakpoint opcode buffer + ::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, + intersect_size); + bytes_written += intersect_size; + }); + + // Write any remaining bytes after the last breakpoint if we have any left + if (bytes_written < size) + bytes_written += + WriteMemoryPrivate(addr + bytes_written, ubuf + bytes_written, + size - bytes_written, error); + + return bytes_written; +} + +size_t Process::WriteScalarToMemory(addr_t addr, const Scalar &scalar, + size_t byte_size, Status &error) { + if (byte_size == UINT32_MAX) + byte_size = scalar.GetByteSize(); + if (byte_size > 0) { + uint8_t buf[32]; + const size_t mem_size = + scalar.GetAsMemoryData(buf, byte_size, GetByteOrder(), error); + if (mem_size > 0) + return WriteMemory(addr, buf, mem_size, error); + else + error.SetErrorString("failed to get scalar as memory data"); + } else { + error.SetErrorString("invalid scalar value"); + } + return 0; +} + +size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size, + bool is_signed, Scalar &scalar, + Status &error) { + uint64_t uval = 0; + if (byte_size == 0) { + error.SetErrorString("byte size is zero"); + } else if (byte_size & (byte_size - 1)) { + error.SetErrorStringWithFormat("byte size %u is not a power of 2", + byte_size); + } else if (byte_size <= sizeof(uval)) { + const size_t bytes_read = ReadMemory(addr, &uval, byte_size, error); + if (bytes_read == byte_size) { + DataExtractor data(&uval, sizeof(uval), GetByteOrder(), + GetAddressByteSize()); + lldb::offset_t offset = 0; + if (byte_size <= 4) + scalar = data.GetMaxU32(&offset, byte_size); + else + scalar = data.GetMaxU64(&offset, byte_size); + if (is_signed) + scalar.SignExtend(byte_size * 8); + return bytes_read; + } + } else { + error.SetErrorStringWithFormat( + "byte size of %u is too large for integer scalar type", byte_size); + } + return 0; +} + +Status Process::WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) { + Status error; + for (const auto &Entry : entries) { + WriteMemory(Entry.Dest, Entry.Contents.data(), Entry.Contents.size(), + error); + if (!error.Success()) + break; + } + return error; +} + +#define USE_ALLOCATE_MEMORY_CACHE 1 +addr_t Process::AllocateMemory(size_t size, uint32_t permissions, + Status &error) { + if (GetPrivateState() != eStateStopped) { + error.SetErrorToGenericError(); + return LLDB_INVALID_ADDRESS; + } + +#if defined(USE_ALLOCATE_MEMORY_CACHE) + return m_allocated_memory_cache.AllocateMemory(size, permissions, error); +#else + addr_t allocated_addr = DoAllocateMemory(size, permissions, error); + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, + "Process::AllocateMemory(size=%" PRIu64 + ", permissions=%s) => 0x%16.16" PRIx64 + " (m_stop_id = %u m_memory_id = %u)", + (uint64_t)size, GetPermissionsAsCString(permissions), + (uint64_t)allocated_addr, m_mod_id.GetStopID(), + m_mod_id.GetMemoryID()); + return allocated_addr; +#endif +} + +addr_t Process::CallocateMemory(size_t size, uint32_t permissions, + Status &error) { + addr_t return_addr = AllocateMemory(size, permissions, error); + if (error.Success()) { + std::string buffer(size, 0); + WriteMemory(return_addr, buffer.c_str(), size, error); + } + return return_addr; +} + +bool Process::CanJIT() { + if (m_can_jit == eCanJITDontKnow) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + Status err; + + uint64_t allocated_memory = AllocateMemory( + 8, ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable, + err); + + if (err.Success()) { + m_can_jit = eCanJITYes; + LLDB_LOGF(log, + "Process::%s pid %" PRIu64 + " allocation test passed, CanJIT () is true", + __FUNCTION__, GetID()); + } else { + m_can_jit = eCanJITNo; + LLDB_LOGF(log, + "Process::%s pid %" PRIu64 + " allocation test failed, CanJIT () is false: %s", + __FUNCTION__, GetID(), err.AsCString()); + } + + DeallocateMemory(allocated_memory); + } + + return m_can_jit == eCanJITYes; +} + +void Process::SetCanJIT(bool can_jit) { + m_can_jit = (can_jit ? eCanJITYes : eCanJITNo); +} + +void Process::SetCanRunCode(bool can_run_code) { + SetCanJIT(can_run_code); + m_can_interpret_function_calls = can_run_code; +} + +Status Process::DeallocateMemory(addr_t ptr) { + Status error; +#if defined(USE_ALLOCATE_MEMORY_CACHE) + if (!m_allocated_memory_cache.DeallocateMemory(ptr)) { + error.SetErrorStringWithFormat( + "deallocation of memory at 0x%" PRIx64 " failed.", (uint64_t)ptr); + } +#else + error = DoDeallocateMemory(ptr); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, + "Process::DeallocateMemory(addr=0x%16.16" PRIx64 + ") => err = %s (m_stop_id = %u, m_memory_id = %u)", + ptr, error.AsCString("SUCCESS"), m_mod_id.GetStopID(), + m_mod_id.GetMemoryID()); +#endif + return error; +} + +ModuleSP Process::ReadModuleFromMemory(const FileSpec &file_spec, + lldb::addr_t header_addr, + size_t size_to_read) { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) { + LLDB_LOGF(log, + "Process::ReadModuleFromMemory reading %s binary from memory", + file_spec.GetPath().c_str()); + } + ModuleSP module_sp(new Module(file_spec, ArchSpec())); + if (module_sp) { + Status error; + ObjectFile *objfile = module_sp->GetMemoryObjectFile( + shared_from_this(), header_addr, error, size_to_read); + if (objfile) + return module_sp; + } + return ModuleSP(); +} + +bool Process::GetLoadAddressPermissions(lldb::addr_t load_addr, + uint32_t &permissions) { + MemoryRegionInfo range_info; + permissions = 0; + Status error(GetMemoryRegionInfo(load_addr, range_info)); + if (!error.Success()) + return false; + if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow || + range_info.GetWritable() == MemoryRegionInfo::eDontKnow || + range_info.GetExecutable() == MemoryRegionInfo::eDontKnow) { + return false; + } + + if (range_info.GetReadable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsReadable; + + if (range_info.GetWritable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsWritable; + + if (range_info.GetExecutable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsExecutable; + + return true; +} + +Status Process::EnableWatchpoint(Watchpoint *watchpoint, bool notify) { + Status error; + error.SetErrorString("watchpoints are not supported"); + return error; +} + +Status Process::DisableWatchpoint(Watchpoint *watchpoint, bool notify) { + Status error; + error.SetErrorString("watchpoints are not supported"); + return error; +} + +StateType +Process::WaitForProcessStopPrivate(EventSP &event_sp, + const Timeout<std::micro> &timeout) { + StateType state; + + while (true) { + event_sp.reset(); + state = GetStateChangedEventsPrivate(event_sp, timeout); + + if (StateIsStoppedState(state, false)) + break; + + // If state is invalid, then we timed out + if (state == eStateInvalid) + break; + + if (event_sp) + HandlePrivateEvent(event_sp); + } + return state; +} + +void Process::LoadOperatingSystemPlugin(bool flush) { + if (flush) + m_thread_list.Clear(); + m_os_up.reset(OperatingSystem::FindPlugin(this, nullptr)); + if (flush) + Flush(); +} + +Status Process::Launch(ProcessLaunchInfo &launch_info) { + Status error; + m_abi_sp.reset(); + m_dyld_up.reset(); + m_jit_loaders_up.reset(); + m_system_runtime_up.reset(); + m_os_up.reset(); + m_process_input_reader.reset(); + + Module *exe_module = GetTarget().GetExecutableModulePointer(); + if (!exe_module) { + error.SetErrorString("executable module does not exist"); + return error; + } + + char local_exec_file_path[PATH_MAX]; + char platform_exec_file_path[PATH_MAX]; + exe_module->GetFileSpec().GetPath(local_exec_file_path, + sizeof(local_exec_file_path)); + exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path, + sizeof(platform_exec_file_path)); + if (FileSystem::Instance().Exists(exe_module->GetFileSpec())) { + // Install anything that might need to be installed prior to launching. + // For host systems, this will do nothing, but if we are connected to a + // remote platform it will install any needed binaries + error = GetTarget().Install(&launch_info); + if (error.Fail()) + return error; + + if (PrivateStateThreadIsValid()) + PausePrivateStateThread(); + + error = WillLaunch(exe_module); + if (error.Success()) { + const bool restarted = false; + SetPublicState(eStateLaunching, restarted); + m_should_detach = false; + + if (m_public_run_lock.TrySetRunning()) { + // Now launch using these arguments. + error = DoLaunch(exe_module, launch_info); + } else { + // This shouldn't happen + error.SetErrorString("failed to acquire process run lock"); + } + + if (error.Fail()) { + if (GetID() != LLDB_INVALID_PROCESS_ID) { + SetID(LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == nullptr) + error_string = "launch failed"; + SetExitStatus(-1, error_string); + } + } else { + EventSP event_sp; + + // Now wait for the process to launch and return control to us, and then + // call DidLaunch: + StateType state = WaitForProcessStopPrivate(event_sp, seconds(10)); + + if (state == eStateInvalid || !event_sp) { + // We were able to launch the process, but we failed to catch the + // initial stop. + error.SetErrorString("failed to catch stop after launch"); + SetExitStatus(0, "failed to catch stop after launch"); + Destroy(false); + } else if (state == eStateStopped || state == eStateCrashed) { + DidLaunch(); + + DynamicLoader *dyld = GetDynamicLoader(); + if (dyld) + dyld->DidLaunch(); + + GetJITLoaders().DidLaunch(); + + SystemRuntime *system_runtime = GetSystemRuntime(); + if (system_runtime) + system_runtime->DidLaunch(); + + if (!m_os_up) + LoadOperatingSystemPlugin(false); + + // We successfully launched the process and stopped, now it the + // right time to set up signal filters before resuming. + UpdateAutomaticSignalFiltering(); + + // Note, the stop event was consumed above, but not handled. This + // was done to give DidLaunch a chance to run. The target is either + // stopped or crashed. Directly set the state. This is done to + // prevent a stop message with a bunch of spurious output on thread + // status, as well as not pop a ProcessIOHandler. + SetPublicState(state, false); + + if (PrivateStateThreadIsValid()) + ResumePrivateStateThread(); + else + StartPrivateStateThread(); + + // Target was stopped at entry as was intended. Need to notify the + // listeners about it. + if (state == eStateStopped && + launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) + HandlePrivateEvent(event_sp); + } else if (state == eStateExited) { + // We exited while trying to launch somehow. Don't call DidLaunch + // as that's not likely to work, and return an invalid pid. + HandlePrivateEvent(event_sp); + } + } + } + } else { + error.SetErrorStringWithFormat("file doesn't exist: '%s'", + local_exec_file_path); + } + + return error; +} + +Status Process::LoadCore() { + Status error = DoLoadCore(); + if (error.Success()) { + ListenerSP listener_sp( + Listener::MakeListener("lldb.process.load_core_listener")); + HijackProcessEvents(listener_sp); + + if (PrivateStateThreadIsValid()) + ResumePrivateStateThread(); + else + StartPrivateStateThread(); + + DynamicLoader *dyld = GetDynamicLoader(); + if (dyld) + dyld->DidAttach(); + + GetJITLoaders().DidAttach(); + + SystemRuntime *system_runtime = GetSystemRuntime(); + if (system_runtime) + system_runtime->DidAttach(); + + if (!m_os_up) + LoadOperatingSystemPlugin(false); + + // We successfully loaded a core file, now pretend we stopped so we can + // show all of the threads in the core file and explore the crashed state. + SetPrivateState(eStateStopped); + + // Wait for a stopped event since we just posted one above... + lldb::EventSP event_sp; + StateType state = + WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp); + + if (!StateIsStoppedState(state, false)) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::Halt() failed to stop, state is: %s", + StateAsCString(state)); + error.SetErrorString( + "Did not get stopped event after loading the core file."); + } + RestoreProcessEvents(); + } + return error; +} + +DynamicLoader *Process::GetDynamicLoader() { + if (!m_dyld_up) + m_dyld_up.reset(DynamicLoader::FindPlugin(this, nullptr)); + return m_dyld_up.get(); +} + +DataExtractor Process::GetAuxvData() { return DataExtractor(); } + +JITLoaderList &Process::GetJITLoaders() { + if (!m_jit_loaders_up) { + m_jit_loaders_up.reset(new JITLoaderList()); + JITLoader::LoadPlugins(this, *m_jit_loaders_up); + } + return *m_jit_loaders_up; +} + +SystemRuntime *Process::GetSystemRuntime() { + if (!m_system_runtime_up) + m_system_runtime_up.reset(SystemRuntime::FindPlugin(this)); + return m_system_runtime_up.get(); +} + +Process::AttachCompletionHandler::AttachCompletionHandler(Process *process, + uint32_t exec_count) + : NextEventAction(process), m_exec_count(exec_count) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF( + log, + "Process::AttachCompletionHandler::%s process=%p, exec_count=%" PRIu32, + __FUNCTION__, static_cast<void *>(process), exec_count); +} + +Process::NextEventAction::EventActionResult +Process::AttachCompletionHandler::PerformAction(lldb::EventSP &event_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + StateType state = ProcessEventData::GetStateFromEvent(event_sp.get()); + LLDB_LOGF(log, + "Process::AttachCompletionHandler::%s called with state %s (%d)", + __FUNCTION__, StateAsCString(state), static_cast<int>(state)); + + switch (state) { + case eStateAttaching: + return eEventActionSuccess; + + case eStateRunning: + case eStateConnected: + return eEventActionRetry; + + case eStateStopped: + case eStateCrashed: + // During attach, prior to sending the eStateStopped event, + // lldb_private::Process subclasses must set the new process ID. + assert(m_process->GetID() != LLDB_INVALID_PROCESS_ID); + // We don't want these events to be reported, so go set the + // ShouldReportStop here: + m_process->GetThreadList().SetShouldReportStop(eVoteNo); + + if (m_exec_count > 0) { + --m_exec_count; + + LLDB_LOGF(log, + "Process::AttachCompletionHandler::%s state %s: reduced " + "remaining exec count to %" PRIu32 ", requesting resume", + __FUNCTION__, StateAsCString(state), m_exec_count); + + RequestResume(); + return eEventActionRetry; + } else { + LLDB_LOGF(log, + "Process::AttachCompletionHandler::%s state %s: no more " + "execs expected to start, continuing with attach", + __FUNCTION__, StateAsCString(state)); + + m_process->CompleteAttach(); + return eEventActionSuccess; + } + break; + + default: + case eStateExited: + case eStateInvalid: + break; + } + + m_exit_string.assign("No valid Process"); + return eEventActionExit; +} + +Process::NextEventAction::EventActionResult +Process::AttachCompletionHandler::HandleBeingInterrupted() { + return eEventActionSuccess; +} + +const char *Process::AttachCompletionHandler::GetExitString() { + return m_exit_string.c_str(); +} + +ListenerSP ProcessAttachInfo::GetListenerForProcess(Debugger &debugger) { + if (m_listener_sp) + return m_listener_sp; + else + return debugger.GetListener(); +} + +Status Process::Attach(ProcessAttachInfo &attach_info) { + m_abi_sp.reset(); + m_process_input_reader.reset(); + m_dyld_up.reset(); + m_jit_loaders_up.reset(); + m_system_runtime_up.reset(); + m_os_up.reset(); + + lldb::pid_t attach_pid = attach_info.GetProcessID(); + Status error; + if (attach_pid == LLDB_INVALID_PROCESS_ID) { + char process_name[PATH_MAX]; + + if (attach_info.GetExecutableFile().GetPath(process_name, + sizeof(process_name))) { + const bool wait_for_launch = attach_info.GetWaitForLaunch(); + + if (wait_for_launch) { + error = WillAttachToProcessWithName(process_name, wait_for_launch); + if (error.Success()) { + if (m_public_run_lock.TrySetRunning()) { + m_should_detach = true; + const bool restarted = false; + SetPublicState(eStateAttaching, restarted); + // Now attach using these arguments. + error = DoAttachToProcessWithName(process_name, attach_info); + } else { + // This shouldn't happen + error.SetErrorString("failed to acquire process run lock"); + } + + if (error.Fail()) { + if (GetID() != LLDB_INVALID_PROCESS_ID) { + SetID(LLDB_INVALID_PROCESS_ID); + if (error.AsCString() == nullptr) + error.SetErrorString("attach failed"); + + SetExitStatus(-1, error.AsCString()); + } + } else { + SetNextEventAction(new Process::AttachCompletionHandler( + this, attach_info.GetResumeCount())); + StartPrivateStateThread(); + } + return error; + } + } else { + ProcessInstanceInfoList process_infos; + PlatformSP platform_sp(GetTarget().GetPlatform()); + + if (platform_sp) { + ProcessInstanceInfoMatch match_info; + match_info.GetProcessInfo() = attach_info; + match_info.SetNameMatchType(NameMatch::Equals); + platform_sp->FindProcesses(match_info, process_infos); + const uint32_t num_matches = process_infos.GetSize(); + if (num_matches == 1) { + attach_pid = process_infos.GetProcessIDAtIndex(0); + // Fall through and attach using the above process ID + } else { + match_info.GetProcessInfo().GetExecutableFile().GetPath( + process_name, sizeof(process_name)); + if (num_matches > 1) { + StreamString s; + ProcessInstanceInfo::DumpTableHeader(s, true, false); + for (size_t i = 0; i < num_matches; i++) { + process_infos.GetProcessInfoAtIndex(i).DumpAsTableRow( + s, platform_sp->GetUserIDResolver(), true, false); + } + error.SetErrorStringWithFormat( + "more than one process named %s:\n%s", process_name, + s.GetData()); + } else + error.SetErrorStringWithFormat( + "could not find a process named %s", process_name); + } + } else { + error.SetErrorString( + "invalid platform, can't find processes by name"); + return error; + } + } + } else { + error.SetErrorString("invalid process name"); + } + } + + if (attach_pid != LLDB_INVALID_PROCESS_ID) { + error = WillAttachToProcessWithID(attach_pid); + if (error.Success()) { + + if (m_public_run_lock.TrySetRunning()) { + // Now attach using these arguments. + m_should_detach = true; + const bool restarted = false; + SetPublicState(eStateAttaching, restarted); + error = DoAttachToProcessWithID(attach_pid, attach_info); + } else { + // This shouldn't happen + error.SetErrorString("failed to acquire process run lock"); + } + + if (error.Success()) { + SetNextEventAction(new Process::AttachCompletionHandler( + this, attach_info.GetResumeCount())); + StartPrivateStateThread(); + } else { + if (GetID() != LLDB_INVALID_PROCESS_ID) + SetID(LLDB_INVALID_PROCESS_ID); + + const char *error_string = error.AsCString(); + if (error_string == nullptr) + error_string = "attach failed"; + + SetExitStatus(-1, error_string); + } + } + } + return error; +} + +void Process::CompleteAttach() { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | + LIBLLDB_LOG_TARGET)); + LLDB_LOGF(log, "Process::%s()", __FUNCTION__); + + // Let the process subclass figure out at much as it can about the process + // before we go looking for a dynamic loader plug-in. + ArchSpec process_arch; + DidAttach(process_arch); + + if (process_arch.IsValid()) { + GetTarget().SetArchitecture(process_arch); + if (log) { + const char *triple_str = process_arch.GetTriple().getTriple().c_str(); + LLDB_LOGF(log, + "Process::%s replacing process architecture with DidAttach() " + "architecture: %s", + __FUNCTION__, triple_str ? triple_str : "<null>"); + } + } + + // We just attached. If we have a platform, ask it for the process + // architecture, and if it isn't the same as the one we've already set, + // switch architectures. + PlatformSP platform_sp(GetTarget().GetPlatform()); + assert(platform_sp); + if (platform_sp) { + const ArchSpec &target_arch = GetTarget().GetArchitecture(); + if (target_arch.IsValid() && + !platform_sp->IsCompatibleArchitecture(target_arch, false, nullptr)) { + ArchSpec platform_arch; + platform_sp = + platform_sp->GetPlatformForArchitecture(target_arch, &platform_arch); + if (platform_sp) { + GetTarget().SetPlatform(platform_sp); + GetTarget().SetArchitecture(platform_arch); + LLDB_LOGF(log, + "Process::%s switching platform to %s and architecture " + "to %s based on info from attach", + __FUNCTION__, platform_sp->GetName().AsCString(""), + platform_arch.GetTriple().getTriple().c_str()); + } + } else if (!process_arch.IsValid()) { + ProcessInstanceInfo process_info; + GetProcessInfo(process_info); + const ArchSpec &process_arch = process_info.GetArchitecture(); + if (process_arch.IsValid() && + !GetTarget().GetArchitecture().IsExactMatch(process_arch)) { + GetTarget().SetArchitecture(process_arch); + LLDB_LOGF(log, + "Process::%s switching architecture to %s based on info " + "the platform retrieved for pid %" PRIu64, + __FUNCTION__, process_arch.GetTriple().getTriple().c_str(), + GetID()); + } + } + } + + // We have completed the attach, now it is time to find the dynamic loader + // plug-in + DynamicLoader *dyld = GetDynamicLoader(); + if (dyld) { + dyld->DidAttach(); + if (log) { + ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + LLDB_LOGF(log, + "Process::%s after DynamicLoader::DidAttach(), target " + "executable is %s (using %s plugin)", + __FUNCTION__, + exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() + : "<none>", + dyld->GetPluginName().AsCString("<unnamed>")); + } + } + + GetJITLoaders().DidAttach(); + + SystemRuntime *system_runtime = GetSystemRuntime(); + if (system_runtime) { + system_runtime->DidAttach(); + if (log) { + ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + LLDB_LOGF(log, + "Process::%s after SystemRuntime::DidAttach(), target " + "executable is %s (using %s plugin)", + __FUNCTION__, + exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() + : "<none>", + system_runtime->GetPluginName().AsCString("<unnamed>")); + } + } + + if (!m_os_up) { + LoadOperatingSystemPlugin(false); + if (m_os_up) { + // Somebody might have gotten threads before now, but we need to force the + // update after we've loaded the OperatingSystem plugin or it won't get a + // chance to process the threads. + m_thread_list.Clear(); + UpdateThreadListIfNeeded(); + } + } + // Figure out which one is the executable, and set that in our target: + const ModuleList &target_modules = GetTarget().GetImages(); + std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex()); + size_t num_modules = target_modules.GetSize(); + ModuleSP new_executable_module_sp; + + for (size_t i = 0; i < num_modules; i++) { + ModuleSP module_sp(target_modules.GetModuleAtIndexUnlocked(i)); + if (module_sp && module_sp->IsExecutable()) { + if (GetTarget().GetExecutableModulePointer() != module_sp.get()) + new_executable_module_sp = module_sp; + break; + } + } + if (new_executable_module_sp) { + GetTarget().SetExecutableModule(new_executable_module_sp, + eLoadDependentsNo); + if (log) { + ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + LLDB_LOGF( + log, + "Process::%s after looping through modules, target executable is %s", + __FUNCTION__, + exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() + : "<none>"); + } + } +} + +Status Process::ConnectRemote(Stream *strm, llvm::StringRef remote_url) { + m_abi_sp.reset(); + m_process_input_reader.reset(); + + // Find the process and its architecture. Make sure it matches the + // architecture of the current Target, and if not adjust it. + + Status error(DoConnectRemote(strm, remote_url)); + if (error.Success()) { + if (GetID() != LLDB_INVALID_PROCESS_ID) { + EventSP event_sp; + StateType state = WaitForProcessStopPrivate(event_sp, llvm::None); + + if (state == eStateStopped || state == eStateCrashed) { + // If we attached and actually have a process on the other end, then + // this ended up being the equivalent of an attach. + CompleteAttach(); + + // This delays passing the stopped event to listeners till + // CompleteAttach gets a chance to complete... + HandlePrivateEvent(event_sp); + } + } + + if (PrivateStateThreadIsValid()) + ResumePrivateStateThread(); + else + StartPrivateStateThread(); + } + return error; +} + +Status Process::PrivateResume() { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | + LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, + "Process::PrivateResume() m_stop_id = %u, public state: %s " + "private state: %s", + m_mod_id.GetStopID(), StateAsCString(m_public_state.GetValue()), + StateAsCString(m_private_state.GetValue())); + + // If signals handing status changed we might want to update our signal + // filters before resuming. + UpdateAutomaticSignalFiltering(); + + Status error(WillResume()); + // Tell the process it is about to resume before the thread list + if (error.Success()) { + // Now let the thread list know we are about to resume so it can let all of + // our threads know that they are about to be resumed. Threads will each be + // called with Thread::WillResume(StateType) where StateType contains the + // state that they are supposed to have when the process is resumed + // (suspended/running/stepping). Threads should also check their resume + // signal in lldb::Thread::GetResumeSignal() to see if they are supposed to + // start back up with a signal. + if (m_thread_list.WillResume()) { + // Last thing, do the PreResumeActions. + if (!RunPreResumeActions()) { + error.SetErrorStringWithFormat( + "Process::PrivateResume PreResumeActions failed, not resuming."); + } else { + m_mod_id.BumpResumeID(); + error = DoResume(); + if (error.Success()) { + DidResume(); + m_thread_list.DidResume(); + LLDB_LOGF(log, "Process thinks the process has resumed."); + } else { + LLDB_LOGF(log, "Process::PrivateResume() DoResume failed."); + return error; + } + } + } else { + // Somebody wanted to run without running (e.g. we were faking a step + // from one frame of a set of inlined frames that share the same PC to + // another.) So generate a continue & a stopped event, and let the world + // handle them. + LLDB_LOGF(log, + "Process::PrivateResume() asked to simulate a start & stop."); + + SetPrivateState(eStateRunning); + SetPrivateState(eStateStopped); + } + } else + LLDB_LOGF(log, "Process::PrivateResume() got an error \"%s\".", + error.AsCString("<unknown error>")); + return error; +} + +Status Process::Halt(bool clear_thread_plans, bool use_run_lock) { + if (!StateIsRunningState(m_public_state.GetValue())) + return Status("Process is not running."); + + // Don't clear the m_clear_thread_plans_on_stop, only set it to true if in + // case it was already set and some thread plan logic calls halt on its own. + m_clear_thread_plans_on_stop |= clear_thread_plans; + + ListenerSP halt_listener_sp( + Listener::MakeListener("lldb.process.halt_listener")); + HijackProcessEvents(halt_listener_sp); + + EventSP event_sp; + + SendAsyncInterrupt(); + + if (m_public_state.GetValue() == eStateAttaching) { + // Don't hijack and eat the eStateExited as the code that was doing the + // attach will be waiting for this event... + RestoreProcessEvents(); + SetExitStatus(SIGKILL, "Cancelled async attach."); + Destroy(false); + return Status(); + } + + // Wait for 10 second for the process to stop. + StateType state = WaitForProcessToStop( + seconds(10), &event_sp, true, halt_listener_sp, nullptr, use_run_lock); + RestoreProcessEvents(); + + if (state == eStateInvalid || !event_sp) { + // We timed out and didn't get a stop event... + return Status("Halt timed out. State = %s", StateAsCString(GetState())); + } + + BroadcastEvent(event_sp); + + return Status(); +} + +Status Process::StopForDestroyOrDetach(lldb::EventSP &exit_event_sp) { + Status error; + + // Check both the public & private states here. If we're hung evaluating an + // expression, for instance, then the public state will be stopped, but we + // still need to interrupt. + if (m_public_state.GetValue() == eStateRunning || + m_private_state.GetValue() == eStateRunning) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::%s() About to stop.", __FUNCTION__); + + ListenerSP listener_sp( + Listener::MakeListener("lldb.Process.StopForDestroyOrDetach.hijack")); + HijackProcessEvents(listener_sp); + + SendAsyncInterrupt(); + + // Consume the interrupt event. + StateType state = + WaitForProcessToStop(seconds(10), &exit_event_sp, true, listener_sp); + + RestoreProcessEvents(); + + // If the process exited while we were waiting for it to stop, put the + // exited event into the shared pointer passed in and return. Our caller + // doesn't need to do anything else, since they don't have a process + // anymore... + + if (state == eStateExited || m_private_state.GetValue() == eStateExited) { + LLDB_LOGF(log, "Process::%s() Process exited while waiting to stop.", + __FUNCTION__); + return error; + } else + exit_event_sp.reset(); // It is ok to consume any non-exit stop events + + if (state != eStateStopped) { + LLDB_LOGF(log, "Process::%s() failed to stop, state is: %s", __FUNCTION__, + StateAsCString(state)); + // If we really couldn't stop the process then we should just error out + // here, but if the lower levels just bobbled sending the event and we + // really are stopped, then continue on. + StateType private_state = m_private_state.GetValue(); + if (private_state != eStateStopped) { + return Status( + "Attempt to stop the target in order to detach timed out. " + "State = %s", + StateAsCString(GetState())); + } + } + } + return error; +} + +Status Process::Detach(bool keep_stopped) { + EventSP exit_event_sp; + Status error; + m_destroy_in_process = true; + + error = WillDetach(); + + if (error.Success()) { + if (DetachRequiresHalt()) { + error = StopForDestroyOrDetach(exit_event_sp); + if (!error.Success()) { + m_destroy_in_process = false; + return error; + } else if (exit_event_sp) { + // We shouldn't need to do anything else here. There's no process left + // to detach from... + StopPrivateStateThread(); + m_destroy_in_process = false; + return error; + } + } + + m_thread_list.DiscardThreadPlans(); + DisableAllBreakpointSites(); + + error = DoDetach(keep_stopped); + if (error.Success()) { + DidDetach(); + StopPrivateStateThread(); + } else { + return error; + } + } + m_destroy_in_process = false; + + // If we exited when we were waiting for a process to stop, then forward the + // event here so we don't lose the event + if (exit_event_sp) { + // Directly broadcast our exited event because we shut down our private + // state thread above + BroadcastEvent(exit_event_sp); + } + + // If we have been interrupted (to kill us) in the middle of running, we may + // not end up propagating the last events through the event system, in which + // case we might strand the write lock. Unlock it here so when we do to tear + // down the process we don't get an error destroying the lock. + + m_public_run_lock.SetStopped(); + return error; +} + +Status Process::Destroy(bool force_kill) { + + // Tell ourselves we are in the process of destroying the process, so that we + // don't do any unnecessary work that might hinder the destruction. Remember + // to set this back to false when we are done. That way if the attempt + // failed and the process stays around for some reason it won't be in a + // confused state. + + if (force_kill) + m_should_detach = false; + + if (GetShouldDetach()) { + // FIXME: This will have to be a process setting: + bool keep_stopped = false; + Detach(keep_stopped); + } + + m_destroy_in_process = true; + + Status error(WillDestroy()); + if (error.Success()) { + EventSP exit_event_sp; + if (DestroyRequiresHalt()) { + error = StopForDestroyOrDetach(exit_event_sp); + } + + if (m_public_state.GetValue() != eStateRunning) { + // Ditch all thread plans, and remove all our breakpoints: in case we + // have to restart the target to kill it, we don't want it hitting a + // breakpoint... Only do this if we've stopped, however, since if we + // didn't manage to halt it above, then we're not going to have much luck + // doing this now. + m_thread_list.DiscardThreadPlans(); + DisableAllBreakpointSites(); + } + + error = DoDestroy(); + if (error.Success()) { + DidDestroy(); + StopPrivateStateThread(); + } + m_stdio_communication.Disconnect(); + m_stdio_communication.StopReadThread(); + m_stdin_forward = false; + + if (m_process_input_reader) { + m_process_input_reader->SetIsDone(true); + m_process_input_reader->Cancel(); + m_process_input_reader.reset(); + } + + // If we exited when we were waiting for a process to stop, then forward + // the event here so we don't lose the event + if (exit_event_sp) { + // Directly broadcast our exited event because we shut down our private + // state thread above + BroadcastEvent(exit_event_sp); + } + + // If we have been interrupted (to kill us) in the middle of running, we + // may not end up propagating the last events through the event system, in + // which case we might strand the write lock. Unlock it here so when we do + // to tear down the process we don't get an error destroying the lock. + m_public_run_lock.SetStopped(); + } + + m_destroy_in_process = false; + + return error; +} + +Status Process::Signal(int signal) { + Status error(WillSignal()); + if (error.Success()) { + error = DoSignal(signal); + if (error.Success()) + DidSignal(); + } + return error; +} + +void Process::SetUnixSignals(UnixSignalsSP &&signals_sp) { + assert(signals_sp && "null signals_sp"); + m_unix_signals_sp = signals_sp; +} + +const lldb::UnixSignalsSP &Process::GetUnixSignals() { + assert(m_unix_signals_sp && "null m_unix_signals_sp"); + return m_unix_signals_sp; +} + +lldb::ByteOrder Process::GetByteOrder() const { + return GetTarget().GetArchitecture().GetByteOrder(); +} + +uint32_t Process::GetAddressByteSize() const { + return GetTarget().GetArchitecture().GetAddressByteSize(); +} + +bool Process::ShouldBroadcastEvent(Event *event_ptr) { + const StateType state = + Process::ProcessEventData::GetStateFromEvent(event_ptr); + bool return_value = true; + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS | + LIBLLDB_LOG_PROCESS)); + + switch (state) { + case eStateDetached: + case eStateExited: + case eStateUnloaded: + m_stdio_communication.SynchronizeWithReadThread(); + m_stdio_communication.Disconnect(); + m_stdio_communication.StopReadThread(); + m_stdin_forward = false; + + LLVM_FALLTHROUGH; + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + // These events indicate changes in the state of the debugging session, + // always report them. + return_value = true; + break; + case eStateInvalid: + // We stopped for no apparent reason, don't report it. + return_value = false; + break; + case eStateRunning: + case eStateStepping: + // If we've started the target running, we handle the cases where we are + // already running and where there is a transition from stopped to running + // differently. running -> running: Automatically suppress extra running + // events stopped -> running: Report except when there is one or more no + // votes + // and no yes votes. + SynchronouslyNotifyStateChanged(state); + if (m_force_next_event_delivery) + return_value = true; + else { + switch (m_last_broadcast_state) { + case eStateRunning: + case eStateStepping: + // We always suppress multiple runnings with no PUBLIC stop in between. + return_value = false; + break; + default: + // TODO: make this work correctly. For now always report + // run if we aren't running so we don't miss any running events. If I + // run the lldb/test/thread/a.out file and break at main.cpp:58, run + // and hit the breakpoints on multiple threads, then somehow during the + // stepping over of all breakpoints no run gets reported. + + // This is a transition from stop to run. + switch (m_thread_list.ShouldReportRun(event_ptr)) { + case eVoteYes: + case eVoteNoOpinion: + return_value = true; + break; + case eVoteNo: + return_value = false; + break; + } + break; + } + } + break; + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // We've stopped. First see if we're going to restart the target. If we + // are going to stop, then we always broadcast the event. If we aren't + // going to stop, let the thread plans decide if we're going to report this + // event. If no thread has an opinion, we don't report it. + + m_stdio_communication.SynchronizeWithReadThread(); + RefreshStateAfterStop(); + if (ProcessEventData::GetInterruptedFromEvent(event_ptr)) { + LLDB_LOGF(log, + "Process::ShouldBroadcastEvent (%p) stopped due to an " + "interrupt, state: %s", + static_cast<void *>(event_ptr), StateAsCString(state)); + // Even though we know we are going to stop, we should let the threads + // have a look at the stop, so they can properly set their state. + m_thread_list.ShouldStop(event_ptr); + return_value = true; + } else { + bool was_restarted = ProcessEventData::GetRestartedFromEvent(event_ptr); + bool should_resume = false; + + // It makes no sense to ask "ShouldStop" if we've already been + // restarted... Asking the thread list is also not likely to go well, + // since we are running again. So in that case just report the event. + + if (!was_restarted) + should_resume = !m_thread_list.ShouldStop(event_ptr); + + if (was_restarted || should_resume || m_resume_requested) { + Vote stop_vote = m_thread_list.ShouldReportStop(event_ptr); + LLDB_LOGF(log, + "Process::ShouldBroadcastEvent: should_resume: %i state: " + "%s was_restarted: %i stop_vote: %d.", + should_resume, StateAsCString(state), was_restarted, + stop_vote); + + switch (stop_vote) { + case eVoteYes: + return_value = true; + break; + case eVoteNoOpinion: + case eVoteNo: + return_value = false; + break; + } + + if (!was_restarted) { + LLDB_LOGF(log, + "Process::ShouldBroadcastEvent (%p) Restarting process " + "from state: %s", + static_cast<void *>(event_ptr), StateAsCString(state)); + ProcessEventData::SetRestartedInEvent(event_ptr, true); + PrivateResume(); + } + } else { + return_value = true; + SynchronouslyNotifyStateChanged(state); + } + } + break; + } + + // Forcing the next event delivery is a one shot deal. So reset it here. + m_force_next_event_delivery = false; + + // We do some coalescing of events (for instance two consecutive running + // events get coalesced.) But we only coalesce against events we actually + // broadcast. So we use m_last_broadcast_state to track that. NB - you + // can't use "m_public_state.GetValue()" for that purpose, as was originally + // done, because the PublicState reflects the last event pulled off the + // queue, and there may be several events stacked up on the queue unserviced. + // So the PublicState may not reflect the last broadcasted event yet. + // m_last_broadcast_state gets updated here. + + if (return_value) + m_last_broadcast_state = state; + + LLDB_LOGF(log, + "Process::ShouldBroadcastEvent (%p) => new state: %s, last " + "broadcast state: %s - %s", + static_cast<void *>(event_ptr), StateAsCString(state), + StateAsCString(m_last_broadcast_state), + return_value ? "YES" : "NO"); + return return_value; +} + +bool Process::StartPrivateStateThread(bool is_secondary_thread) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); + + bool already_running = PrivateStateThreadIsValid(); + LLDB_LOGF(log, "Process::%s()%s ", __FUNCTION__, + already_running ? " already running" + : " starting private state thread"); + + if (!is_secondary_thread && already_running) + return true; + + // Create a thread that watches our internal state and controls which events + // make it to clients (into the DCProcess event queue). + char thread_name[1024]; + uint32_t max_len = llvm::get_max_thread_name_length(); + if (max_len > 0 && max_len <= 30) { + // On platforms with abbreviated thread name lengths, choose thread names + // that fit within the limit. + if (already_running) + snprintf(thread_name, sizeof(thread_name), "intern-state-OV"); + else + snprintf(thread_name, sizeof(thread_name), "intern-state"); + } else { + if (already_running) + snprintf(thread_name, sizeof(thread_name), + "<lldb.process.internal-state-override(pid=%" PRIu64 ")>", + GetID()); + else + snprintf(thread_name, sizeof(thread_name), + "<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID()); + } + + // Create the private state thread, and start it running. + PrivateStateThreadArgs *args_ptr = + new PrivateStateThreadArgs(this, is_secondary_thread); + llvm::Expected<HostThread> private_state_thread = + ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread, + (void *)args_ptr, 8 * 1024 * 1024); + if (!private_state_thread) { + LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), + "failed to launch host thread: {}", + llvm::toString(private_state_thread.takeError())); + return false; + } + + assert(private_state_thread->IsJoinable()); + m_private_state_thread = *private_state_thread; + ResumePrivateStateThread(); + return true; +} + +void Process::PausePrivateStateThread() { + ControlPrivateStateThread(eBroadcastInternalStateControlPause); +} + +void Process::ResumePrivateStateThread() { + ControlPrivateStateThread(eBroadcastInternalStateControlResume); +} + +void Process::StopPrivateStateThread() { + if (m_private_state_thread.IsJoinable()) + ControlPrivateStateThread(eBroadcastInternalStateControlStop); + else { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF( + log, + "Went to stop the private state thread, but it was already invalid."); + } +} + +void Process::ControlPrivateStateThread(uint32_t signal) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + assert(signal == eBroadcastInternalStateControlStop || + signal == eBroadcastInternalStateControlPause || + signal == eBroadcastInternalStateControlResume); + + LLDB_LOGF(log, "Process::%s (signal = %d)", __FUNCTION__, signal); + + // Signal the private state thread + if (m_private_state_thread.IsJoinable()) { + // Broadcast the event. + // It is important to do this outside of the if below, because it's + // possible that the thread state is invalid but that the thread is waiting + // on a control event instead of simply being on its way out (this should + // not happen, but it apparently can). + LLDB_LOGF(log, "Sending control event of type: %d.", signal); + std::shared_ptr<EventDataReceipt> event_receipt_sp(new EventDataReceipt()); + m_private_state_control_broadcaster.BroadcastEvent(signal, + event_receipt_sp); + + // Wait for the event receipt or for the private state thread to exit + bool receipt_received = false; + if (PrivateStateThreadIsValid()) { + while (!receipt_received) { + // Check for a receipt for n seconds and then check if the private + // state thread is still around. + receipt_received = + event_receipt_sp->WaitForEventReceived(GetUtilityExpressionTimeout()); + if (!receipt_received) { + // Check if the private state thread is still around. If it isn't + // then we are done waiting + if (!PrivateStateThreadIsValid()) + break; // Private state thread exited or is exiting, we are done + } + } + } + + if (signal == eBroadcastInternalStateControlStop) { + thread_result_t result = {}; + m_private_state_thread.Join(&result); + m_private_state_thread.Reset(); + } + } else { + LLDB_LOGF( + log, + "Private state thread already dead, no need to signal it to stop."); + } +} + +void Process::SendAsyncInterrupt() { + if (PrivateStateThreadIsValid()) + m_private_state_broadcaster.BroadcastEvent(Process::eBroadcastBitInterrupt, + nullptr); + else + BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr); +} + +void Process::HandlePrivateEvent(EventSP &event_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + m_resume_requested = false; + + const StateType new_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + // First check to see if anybody wants a shot at this event: + if (m_next_event_action_up) { + NextEventAction::EventActionResult action_result = + m_next_event_action_up->PerformAction(event_sp); + LLDB_LOGF(log, "Ran next event action, result was %d.", action_result); + + switch (action_result) { + case NextEventAction::eEventActionSuccess: + SetNextEventAction(nullptr); + break; + + case NextEventAction::eEventActionRetry: + break; + + case NextEventAction::eEventActionExit: + // Handle Exiting Here. If we already got an exited event, we should + // just propagate it. Otherwise, swallow this event, and set our state + // to exit so the next event will kill us. + if (new_state != eStateExited) { + // FIXME: should cons up an exited event, and discard this one. + SetExitStatus(0, m_next_event_action_up->GetExitString()); + SetNextEventAction(nullptr); + return; + } + SetNextEventAction(nullptr); + break; + } + } + + // See if we should broadcast this state to external clients? + const bool should_broadcast = ShouldBroadcastEvent(event_sp.get()); + + if (should_broadcast) { + const bool is_hijacked = IsHijackedForEvent(eBroadcastBitStateChanged); + if (log) { + LLDB_LOGF(log, + "Process::%s (pid = %" PRIu64 + ") broadcasting new state %s (old state %s) to %s", + __FUNCTION__, GetID(), StateAsCString(new_state), + StateAsCString(GetState()), + is_hijacked ? "hijacked" : "public"); + } + Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get()); + if (StateIsRunningState(new_state)) { + // Only push the input handler if we aren't fowarding events, as this + // means the curses GUI is in use... Or don't push it if we are launching + // since it will come up stopped. + if (!GetTarget().GetDebugger().IsForwardingEvents() && + new_state != eStateLaunching && new_state != eStateAttaching) { + PushProcessIOHandler(); + m_iohandler_sync.SetValue(m_iohandler_sync.GetValue() + 1, + eBroadcastAlways); + LLDB_LOGF(log, "Process::%s updated m_iohandler_sync to %d", + __FUNCTION__, m_iohandler_sync.GetValue()); + } + } else if (StateIsStoppedState(new_state, false)) { + if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { + // If the lldb_private::Debugger is handling the events, we don't want + // to pop the process IOHandler here, we want to do it when we receive + // the stopped event so we can carefully control when the process + // IOHandler is popped because when we stop we want to display some + // text stating how and why we stopped, then maybe some + // process/thread/frame info, and then we want the "(lldb) " prompt to + // show up. If we pop the process IOHandler here, then we will cause + // the command interpreter to become the top IOHandler after the + // process pops off and it will update its prompt right away... See the + // Debugger.cpp file where it calls the function as + // "process_sp->PopProcessIOHandler()" to see where I am talking about. + // Otherwise we end up getting overlapping "(lldb) " prompts and + // garbled output. + // + // If we aren't handling the events in the debugger (which is indicated + // by "m_target.GetDebugger().IsHandlingEvents()" returning false) or + // we are hijacked, then we always pop the process IO handler manually. + // Hijacking happens when the internal process state thread is running + // thread plans, or when commands want to run in synchronous mode and + // they call "process->WaitForProcessToStop()". An example of something + // that will hijack the events is a simple expression: + // + // (lldb) expr (int)puts("hello") + // + // This will cause the internal process state thread to resume and halt + // the process (and _it_ will hijack the eBroadcastBitStateChanged + // events) and we do need the IO handler to be pushed and popped + // correctly. + + if (is_hijacked || !GetTarget().GetDebugger().IsHandlingEvents()) + PopProcessIOHandler(); + } + } + + BroadcastEvent(event_sp); + } else { + if (log) { + LLDB_LOGF( + log, + "Process::%s (pid = %" PRIu64 + ") suppressing state %s (old state %s): should_broadcast == false", + __FUNCTION__, GetID(), StateAsCString(new_state), + StateAsCString(GetState())); + } + } +} + +Status Process::HaltPrivate() { + EventSP event_sp; + Status error(WillHalt()); + if (error.Fail()) + return error; + + // Ask the process subclass to actually halt our process + bool caused_stop; + error = DoHalt(caused_stop); + + DidHalt(); + return error; +} + +thread_result_t Process::PrivateStateThread(void *arg) { + std::unique_ptr<PrivateStateThreadArgs> args_up( + static_cast<PrivateStateThreadArgs *>(arg)); + thread_result_t result = + args_up->process->RunPrivateStateThread(args_up->is_secondary_thread); + return result; +} + +thread_result_t Process::RunPrivateStateThread(bool is_secondary_thread) { + bool control_only = true; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::%s (arg = %p, pid = %" PRIu64 ") thread starting...", + __FUNCTION__, static_cast<void *>(this), GetID()); + + bool exit_now = false; + bool interrupt_requested = false; + while (!exit_now) { + EventSP event_sp; + GetEventsPrivate(event_sp, llvm::None, control_only); + if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) { + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") got a control event: %d", + __FUNCTION__, static_cast<void *>(this), GetID(), + event_sp->GetType()); + + switch (event_sp->GetType()) { + case eBroadcastInternalStateControlStop: + exit_now = true; + break; // doing any internal state management below + + case eBroadcastInternalStateControlPause: + control_only = true; + break; + + case eBroadcastInternalStateControlResume: + control_only = false; + break; + } + + continue; + } else if (event_sp->GetType() == eBroadcastBitInterrupt) { + if (m_public_state.GetValue() == eStateAttaching) { + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") woke up with an interrupt while attaching - " + "forwarding interrupt.", + __FUNCTION__, static_cast<void *>(this), GetID()); + BroadcastEvent(eBroadcastBitInterrupt, nullptr); + } else if (StateIsRunningState(m_last_broadcast_state)) { + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") woke up with an interrupt - Halting.", + __FUNCTION__, static_cast<void *>(this), GetID()); + Status error = HaltPrivate(); + if (error.Fail() && log) + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") failed to halt the process: %s", + __FUNCTION__, static_cast<void *>(this), GetID(), + error.AsCString()); + // Halt should generate a stopped event. Make a note of the fact that + // we were doing the interrupt, so we can set the interrupted flag + // after we receive the event. We deliberately set this to true even if + // HaltPrivate failed, so that we can interrupt on the next natural + // stop. + interrupt_requested = true; + } else { + // This can happen when someone (e.g. Process::Halt) sees that we are + // running and sends an interrupt request, but the process actually + // stops before we receive it. In that case, we can just ignore the + // request. We use m_last_broadcast_state, because the Stopped event + // may not have been popped of the event queue yet, which is when the + // public state gets updated. + LLDB_LOGF(log, + "Process::%s ignoring interrupt as we have already stopped.", + __FUNCTION__); + } + continue; + } + + const StateType internal_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (internal_state != eStateInvalid) { + if (m_clear_thread_plans_on_stop && + StateIsStoppedState(internal_state, true)) { + m_clear_thread_plans_on_stop = false; + m_thread_list.DiscardThreadPlans(); + } + + if (interrupt_requested) { + if (StateIsStoppedState(internal_state, true)) { + // We requested the interrupt, so mark this as such in the stop event + // so clients can tell an interrupted process from a natural stop + ProcessEventData::SetInterruptedInEvent(event_sp.get(), true); + interrupt_requested = false; + } else if (log) { + LLDB_LOGF(log, + "Process::%s interrupt_requested, but a non-stopped " + "state '%s' received.", + __FUNCTION__, StateAsCString(internal_state)); + } + } + + HandlePrivateEvent(event_sp); + } + + if (internal_state == eStateInvalid || internal_state == eStateExited || + internal_state == eStateDetached) { + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") about to exit with internal state %s...", + __FUNCTION__, static_cast<void *>(this), GetID(), + StateAsCString(internal_state)); + + break; + } + } + + // Verify log is still enabled before attempting to write to it... + LLDB_LOGF(log, "Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", + __FUNCTION__, static_cast<void *>(this), GetID()); + + // If we are a secondary thread, then the primary thread we are working for + // will have already acquired the public_run_lock, and isn't done with what + // it was doing yet, so don't try to change it on the way out. + if (!is_secondary_thread) + m_public_run_lock.SetStopped(); + return {}; +} + +// Process Event Data + +Process::ProcessEventData::ProcessEventData() + : EventData(), m_process_wp(), m_state(eStateInvalid), m_restarted(false), + m_update_state(0), m_interrupted(false) {} + +Process::ProcessEventData::ProcessEventData(const ProcessSP &process_sp, + StateType state) + : EventData(), m_process_wp(), m_state(state), m_restarted(false), + m_update_state(0), m_interrupted(false) { + if (process_sp) + m_process_wp = process_sp; +} + +Process::ProcessEventData::~ProcessEventData() = default; + +ConstString Process::ProcessEventData::GetFlavorString() { + static ConstString g_flavor("Process::ProcessEventData"); + return g_flavor; +} + +ConstString Process::ProcessEventData::GetFlavor() const { + return ProcessEventData::GetFlavorString(); +} + +void Process::ProcessEventData::DoOnRemoval(Event *event_ptr) { + ProcessSP process_sp(m_process_wp.lock()); + + if (!process_sp) + return; + + // This function gets called twice for each event, once when the event gets + // pulled off of the private process event queue, and then any number of + // times, first when it gets pulled off of the public event queue, then other + // times when we're pretending that this is where we stopped at the end of + // expression evaluation. m_update_state is used to distinguish these three + // cases; it is 0 when we're just pulling it off for private handling, and > + // 1 for expression evaluation, and we don't want to do the breakpoint + // command handling then. + if (m_update_state != 1) + return; + + process_sp->SetPublicState( + m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr)); + + if (m_state == eStateStopped && !m_restarted) { + // Let process subclasses know we are about to do a public stop and do + // anything they might need to in order to speed up register and memory + // accesses. + process_sp->WillPublicStop(); + } + + // If this is a halt event, even if the halt stopped with some reason other + // than a plain interrupt (e.g. we had already stopped for a breakpoint when + // the halt request came through) don't do the StopInfo actions, as they may + // end up restarting the process. + if (m_interrupted) + return; + + // If we're stopped and haven't restarted, then do the StopInfo actions here: + if (m_state == eStateStopped && !m_restarted) { + ThreadList &curr_thread_list = process_sp->GetThreadList(); + uint32_t num_threads = curr_thread_list.GetSize(); + uint32_t idx; + + // The actions might change one of the thread's stop_info's opinions about + // whether we should stop the process, so we need to query that as we go. + + // One other complication here, is that we try to catch any case where the + // target has run (except for expressions) and immediately exit, but if we + // get that wrong (which is possible) then the thread list might have + // changed, and that would cause our iteration here to crash. We could + // make a copy of the thread list, but we'd really like to also know if it + // has changed at all, so we make up a vector of the thread ID's and check + // what we get back against this list & bag out if anything differs. + std::vector<uint32_t> thread_index_array(num_threads); + for (idx = 0; idx < num_threads; ++idx) + thread_index_array[idx] = + curr_thread_list.GetThreadAtIndex(idx)->GetIndexID(); + + // Use this to track whether we should continue from here. We will only + // continue the target running if no thread says we should stop. Of course + // if some thread's PerformAction actually sets the target running, then it + // doesn't matter what the other threads say... + + bool still_should_stop = false; + + // Sometimes - for instance if we have a bug in the stub we are talking to, + // we stop but no thread has a valid stop reason. In that case we should + // just stop, because we have no way of telling what the right thing to do + // is, and it's better to let the user decide than continue behind their + // backs. + + bool does_anybody_have_an_opinion = false; + + for (idx = 0; idx < num_threads; ++idx) { + curr_thread_list = process_sp->GetThreadList(); + if (curr_thread_list.GetSize() != num_threads) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | + LIBLLDB_LOG_PROCESS)); + LLDB_LOGF( + log, + "Number of threads changed from %u to %u while processing event.", + num_threads, curr_thread_list.GetSize()); + break; + } + + lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx); + + if (thread_sp->GetIndexID() != thread_index_array[idx]) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | + LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, + "The thread at position %u changed from %u to %u while " + "processing event.", + idx, thread_index_array[idx], thread_sp->GetIndexID()); + break; + } + + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); + if (stop_info_sp && stop_info_sp->IsValid()) { + does_anybody_have_an_opinion = true; + bool this_thread_wants_to_stop; + if (stop_info_sp->GetOverrideShouldStop()) { + this_thread_wants_to_stop = + stop_info_sp->GetOverriddenShouldStopValue(); + } else { + stop_info_sp->PerformAction(event_ptr); + // The stop action might restart the target. If it does, then we + // want to mark that in the event so that whoever is receiving it + // will know to wait for the running event and reflect that state + // appropriately. We also need to stop processing actions, since they + // aren't expecting the target to be running. + + // FIXME: we might have run. + if (stop_info_sp->HasTargetRunSinceMe()) { + SetRestarted(true); + break; + } + + this_thread_wants_to_stop = stop_info_sp->ShouldStop(event_ptr); + } + + if (!still_should_stop) + still_should_stop = this_thread_wants_to_stop; + } + } + + if (!GetRestarted()) { + if (!still_should_stop && does_anybody_have_an_opinion) { + // We've been asked to continue, so do that here. + SetRestarted(true); + // Use the public resume method here, since this is just extending a + // public resume. + process_sp->PrivateResume(); + } else { + bool hijacked = + process_sp->IsHijackedForEvent(eBroadcastBitStateChanged) && + !process_sp->StateChangedIsHijackedForSynchronousResume(); + + if (!hijacked) { + // If we didn't restart, run the Stop Hooks here. + // Don't do that if state changed events aren't hooked up to the + // public (or SyncResume) broadcasters. StopHooks are just for + // real public stops. They might also restart the target, + // so watch for that. + process_sp->GetTarget().RunStopHooks(); + if (process_sp->GetPrivateState() == eStateRunning) + SetRestarted(true); + } + } + } +} +} + +void Process::ProcessEventData::Dump(Stream *s) const { + ProcessSP process_sp(m_process_wp.lock()); + + if (process_sp) + s->Printf(" process = %p (pid = %" PRIu64 "), ", + static_cast<void *>(process_sp.get()), process_sp->GetID()); + else + s->PutCString(" process = NULL, "); + + s->Printf("state = %s", StateAsCString(GetState())); +} + +const Process::ProcessEventData * +Process::ProcessEventData::GetEventDataFromEvent(const Event *event_ptr) { + if (event_ptr) { + const EventData *event_data = event_ptr->GetData(); + if (event_data && + event_data->GetFlavor() == ProcessEventData::GetFlavorString()) + return static_cast<const ProcessEventData *>(event_ptr->GetData()); + } + return nullptr; +} + +ProcessSP +Process::ProcessEventData::GetProcessFromEvent(const Event *event_ptr) { + ProcessSP process_sp; + const ProcessEventData *data = GetEventDataFromEvent(event_ptr); + if (data) + process_sp = data->GetProcessSP(); + return process_sp; +} + +StateType Process::ProcessEventData::GetStateFromEvent(const Event *event_ptr) { + const ProcessEventData *data = GetEventDataFromEvent(event_ptr); + if (data == nullptr) + return eStateInvalid; + else + return data->GetState(); +} + +bool Process::ProcessEventData::GetRestartedFromEvent(const Event *event_ptr) { + const ProcessEventData *data = GetEventDataFromEvent(event_ptr); + if (data == nullptr) + return false; + else + return data->GetRestarted(); +} + +void Process::ProcessEventData::SetRestartedInEvent(Event *event_ptr, + bool new_value) { + ProcessEventData *data = + const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + data->SetRestarted(new_value); +} + +size_t +Process::ProcessEventData::GetNumRestartedReasons(const Event *event_ptr) { + ProcessEventData *data = + const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + return data->GetNumRestartedReasons(); + else + return 0; +} + +const char * +Process::ProcessEventData::GetRestartedReasonAtIndex(const Event *event_ptr, + size_t idx) { + ProcessEventData *data = + const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + return data->GetRestartedReasonAtIndex(idx); + else + return nullptr; +} + +void Process::ProcessEventData::AddRestartedReason(Event *event_ptr, + const char *reason) { + ProcessEventData *data = + const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + data->AddRestartedReason(reason); +} + +bool Process::ProcessEventData::GetInterruptedFromEvent( + const Event *event_ptr) { + const ProcessEventData *data = GetEventDataFromEvent(event_ptr); + if (data == nullptr) + return false; + else + return data->GetInterrupted(); +} + +void Process::ProcessEventData::SetInterruptedInEvent(Event *event_ptr, + bool new_value) { + ProcessEventData *data = + const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + data->SetInterrupted(new_value); +} + +bool Process::ProcessEventData::SetUpdateStateOnRemoval(Event *event_ptr) { + ProcessEventData *data = + const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr)); + if (data) { + data->SetUpdateStateOnRemoval(); + return true; + } + return false; +} + +lldb::TargetSP Process::CalculateTarget() { return m_target_wp.lock(); } + +void Process::CalculateExecutionContext(ExecutionContext &exe_ctx) { + exe_ctx.SetTargetPtr(&GetTarget()); + exe_ctx.SetProcessPtr(this); + exe_ctx.SetThreadPtr(nullptr); + exe_ctx.SetFramePtr(nullptr); +} + +// uint32_t +// Process::ListProcessesMatchingName (const char *name, StringList &matches, +// std::vector<lldb::pid_t> &pids) +//{ +// return 0; +//} +// +// ArchSpec +// Process::GetArchSpecForExistingProcess (lldb::pid_t pid) +//{ +// return Host::GetArchSpecForExistingProcess (pid); +//} +// +// ArchSpec +// Process::GetArchSpecForExistingProcess (const char *process_name) +//{ +// return Host::GetArchSpecForExistingProcess (process_name); +//} + +void Process::AppendSTDOUT(const char *s, size_t len) { + std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex); + m_stdout_data.append(s, len); + BroadcastEventIfUnique(eBroadcastBitSTDOUT, + new ProcessEventData(shared_from_this(), GetState())); +} + +void Process::AppendSTDERR(const char *s, size_t len) { + std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex); + m_stderr_data.append(s, len); + BroadcastEventIfUnique(eBroadcastBitSTDERR, + new ProcessEventData(shared_from_this(), GetState())); +} + +void Process::BroadcastAsyncProfileData(const std::string &one_profile_data) { + std::lock_guard<std::recursive_mutex> guard(m_profile_data_comm_mutex); + m_profile_data.push_back(one_profile_data); + BroadcastEventIfUnique(eBroadcastBitProfileData, + new ProcessEventData(shared_from_this(), GetState())); +} + +void Process::BroadcastStructuredData(const StructuredData::ObjectSP &object_sp, + const StructuredDataPluginSP &plugin_sp) { + BroadcastEvent( + eBroadcastBitStructuredData, + new EventDataStructuredData(shared_from_this(), object_sp, plugin_sp)); +} + +StructuredDataPluginSP +Process::GetStructuredDataPlugin(ConstString type_name) const { + auto find_it = m_structured_data_plugin_map.find(type_name); + if (find_it != m_structured_data_plugin_map.end()) + return find_it->second; + else + return StructuredDataPluginSP(); +} + +size_t Process::GetAsyncProfileData(char *buf, size_t buf_size, Status &error) { + std::lock_guard<std::recursive_mutex> guard(m_profile_data_comm_mutex); + if (m_profile_data.empty()) + return 0; + + std::string &one_profile_data = m_profile_data.front(); + size_t bytes_available = one_profile_data.size(); + if (bytes_available > 0) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::GetProfileData (buf = %p, size = %" PRIu64 ")", + static_cast<void *>(buf), static_cast<uint64_t>(buf_size)); + if (bytes_available > buf_size) { + memcpy(buf, one_profile_data.c_str(), buf_size); + one_profile_data.erase(0, buf_size); + bytes_available = buf_size; + } else { + memcpy(buf, one_profile_data.c_str(), bytes_available); + m_profile_data.erase(m_profile_data.begin()); + } + } + return bytes_available; +} + +// Process STDIO + +size_t Process::GetSTDOUT(char *buf, size_t buf_size, Status &error) { + std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::GetSTDOUT (buf = %p, size = %" PRIu64 ")", + static_cast<void *>(buf), static_cast<uint64_t>(buf_size)); + if (bytes_available > buf_size) { + memcpy(buf, m_stdout_data.c_str(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } else { + memcpy(buf, m_stdout_data.c_str(), bytes_available); + m_stdout_data.clear(); + } + } + return bytes_available; +} + +size_t Process::GetSTDERR(char *buf, size_t buf_size, Status &error) { + std::lock_guard<std::recursive_mutex> gaurd(m_stdio_communication_mutex); + size_t bytes_available = m_stderr_data.size(); + if (bytes_available > 0) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::GetSTDERR (buf = %p, size = %" PRIu64 ")", + static_cast<void *>(buf), static_cast<uint64_t>(buf_size)); + if (bytes_available > buf_size) { + memcpy(buf, m_stderr_data.c_str(), buf_size); + m_stderr_data.erase(0, buf_size); + bytes_available = buf_size; + } else { + memcpy(buf, m_stderr_data.c_str(), bytes_available); + m_stderr_data.clear(); + } + } + return bytes_available; +} + +void Process::STDIOReadThreadBytesReceived(void *baton, const void *src, + size_t src_len) { + Process *process = (Process *)baton; + process->AppendSTDOUT(static_cast<const char *>(src), src_len); +} + +class IOHandlerProcessSTDIO : public IOHandler { +public: + IOHandlerProcessSTDIO(Process *process, int write_fd) + : IOHandler(process->GetTarget().GetDebugger(), + IOHandler::Type::ProcessIO), + m_process(process), + m_read_file(GetInputFD(), File::eOpenOptionRead, false), + m_write_file(write_fd, File::eOpenOptionWrite, false) { + m_pipe.CreateNew(false); + } + + ~IOHandlerProcessSTDIO() override = default; + + // Each IOHandler gets to run until it is done. It should read data from the + // "in" and place output into "out" and "err and return when done. + void Run() override { + if (!m_read_file.IsValid() || !m_write_file.IsValid() || + !m_pipe.CanRead() || !m_pipe.CanWrite()) { + SetIsDone(true); + return; + } + + SetIsDone(false); + const int read_fd = m_read_file.GetDescriptor(); + TerminalState terminal_state; + terminal_state.Save(read_fd, false); + Terminal terminal(read_fd); + terminal.SetCanonical(false); + terminal.SetEcho(false); +// FD_ZERO, FD_SET are not supported on windows +#ifndef _WIN32 + const int pipe_read_fd = m_pipe.GetReadFileDescriptor(); + m_is_running = true; + while (!GetIsDone()) { + SelectHelper select_helper; + select_helper.FDSetRead(read_fd); + select_helper.FDSetRead(pipe_read_fd); + Status error = select_helper.Select(); + + if (error.Fail()) { + SetIsDone(true); + } else { + char ch = 0; + size_t n; + if (select_helper.FDIsSetRead(read_fd)) { + n = 1; + if (m_read_file.Read(&ch, n).Success() && n == 1) { + if (m_write_file.Write(&ch, n).Fail() || n != 1) + SetIsDone(true); + } else + SetIsDone(true); + } + if (select_helper.FDIsSetRead(pipe_read_fd)) { + size_t bytes_read; + // Consume the interrupt byte + Status error = m_pipe.Read(&ch, 1, bytes_read); + if (error.Success()) { + switch (ch) { + case 'q': + SetIsDone(true); + break; + case 'i': + if (StateIsRunningState(m_process->GetState())) + m_process->SendAsyncInterrupt(); + break; + } + } + } + } + } + m_is_running = false; +#endif + terminal_state.Restore(); + } + + void Cancel() override { + SetIsDone(true); + // Only write to our pipe to cancel if we are in + // IOHandlerProcessSTDIO::Run(). We can end up with a python command that + // is being run from the command interpreter: + // + // (lldb) step_process_thousands_of_times + // + // In this case the command interpreter will be in the middle of handling + // the command and if the process pushes and pops the IOHandler thousands + // of times, we can end up writing to m_pipe without ever consuming the + // bytes from the pipe in IOHandlerProcessSTDIO::Run() and end up + // deadlocking when the pipe gets fed up and blocks until data is consumed. + if (m_is_running) { + char ch = 'q'; // Send 'q' for quit + size_t bytes_written = 0; + m_pipe.Write(&ch, 1, bytes_written); + } + } + + bool Interrupt() override { + // Do only things that are safe to do in an interrupt context (like in a + // SIGINT handler), like write 1 byte to a file descriptor. This will + // interrupt the IOHandlerProcessSTDIO::Run() and we can look at the byte + // that was written to the pipe and then call + // m_process->SendAsyncInterrupt() from a much safer location in code. + if (m_active) { + char ch = 'i'; // Send 'i' for interrupt + size_t bytes_written = 0; + Status result = m_pipe.Write(&ch, 1, bytes_written); + return result.Success(); + } else { + // This IOHandler might be pushed on the stack, but not being run + // currently so do the right thing if we aren't actively watching for + // STDIN by sending the interrupt to the process. Otherwise the write to + // the pipe above would do nothing. This can happen when the command + // interpreter is running and gets a "expression ...". It will be on the + // IOHandler thread and sending the input is complete to the delegate + // which will cause the expression to run, which will push the process IO + // handler, but not run it. + + if (StateIsRunningState(m_process->GetState())) { + m_process->SendAsyncInterrupt(); + return true; + } + } + return false; + } + + void GotEOF() override {} + +protected: + Process *m_process; + NativeFile m_read_file; // Read from this file (usually actual STDIN for LLDB + NativeFile m_write_file; // Write to this file (usually the master pty for + // getting io to debuggee) + Pipe m_pipe; + std::atomic<bool> m_is_running{false}; +}; + +void Process::SetSTDIOFileDescriptor(int fd) { + // First set up the Read Thread for reading/handling process I/O + + std::unique_ptr<ConnectionFileDescriptor> conn_up( + new ConnectionFileDescriptor(fd, true)); + + if (conn_up) { + m_stdio_communication.SetConnection(conn_up.release()); + if (m_stdio_communication.IsConnected()) { + m_stdio_communication.SetReadThreadBytesReceivedCallback( + STDIOReadThreadBytesReceived, this); + m_stdio_communication.StartReadThread(); + + // Now read thread is set up, set up input reader. + + if (!m_process_input_reader) + m_process_input_reader = + std::make_shared<IOHandlerProcessSTDIO>(this, fd); + } + } +} + +bool Process::ProcessIOHandlerIsActive() { + IOHandlerSP io_handler_sp(m_process_input_reader); + if (io_handler_sp) + return GetTarget().GetDebugger().IsTopIOHandler(io_handler_sp); + return false; +} +bool Process::PushProcessIOHandler() { + IOHandlerSP io_handler_sp(m_process_input_reader); + if (io_handler_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::%s pushing IO handler", __FUNCTION__); + + io_handler_sp->SetIsDone(false); + // If we evaluate an utility function, then we don't cancel the current + // IOHandler. Our IOHandler is non-interactive and shouldn't disturb the + // existing IOHandler that potentially provides the user interface (e.g. + // the IOHandler for Editline). + bool cancel_top_handler = !m_mod_id.IsRunningUtilityFunction(); + GetTarget().GetDebugger().PushIOHandler(io_handler_sp, cancel_top_handler); + return true; + } + return false; +} + +bool Process::PopProcessIOHandler() { + IOHandlerSP io_handler_sp(m_process_input_reader); + if (io_handler_sp) + return GetTarget().GetDebugger().PopIOHandler(io_handler_sp); + return false; +} + +// The process needs to know about installed plug-ins +void Process::SettingsInitialize() { Thread::SettingsInitialize(); } + +void Process::SettingsTerminate() { Thread::SettingsTerminate(); } + +namespace { +// RestorePlanState is used to record the "is private", "is master" and "okay +// to discard" fields of the plan we are running, and reset it on Clean or on +// destruction. It will only reset the state once, so you can call Clean and +// then monkey with the state and it won't get reset on you again. + +class RestorePlanState { +public: + RestorePlanState(lldb::ThreadPlanSP thread_plan_sp) + : m_thread_plan_sp(thread_plan_sp), m_already_reset(false) { + if (m_thread_plan_sp) { + m_private = m_thread_plan_sp->GetPrivate(); + m_is_master = m_thread_plan_sp->IsMasterPlan(); + m_okay_to_discard = m_thread_plan_sp->OkayToDiscard(); + } + } + + ~RestorePlanState() { Clean(); } + + void Clean() { + if (!m_already_reset && m_thread_plan_sp) { + m_already_reset = true; + m_thread_plan_sp->SetPrivate(m_private); + m_thread_plan_sp->SetIsMasterPlan(m_is_master); + m_thread_plan_sp->SetOkayToDiscard(m_okay_to_discard); + } + } + +private: + lldb::ThreadPlanSP m_thread_plan_sp; + bool m_already_reset; + bool m_private; + bool m_is_master; + bool m_okay_to_discard; +}; +} // anonymous namespace + +static microseconds +GetOneThreadExpressionTimeout(const EvaluateExpressionOptions &options) { + const milliseconds default_one_thread_timeout(250); + + // If the overall wait is forever, then we don't need to worry about it. + if (!options.GetTimeout()) { + return options.GetOneThreadTimeout() ? *options.GetOneThreadTimeout() + : default_one_thread_timeout; + } + + // If the one thread timeout is set, use it. + if (options.GetOneThreadTimeout()) + return *options.GetOneThreadTimeout(); + + // Otherwise use half the total timeout, bounded by the + // default_one_thread_timeout. + return std::min<microseconds>(default_one_thread_timeout, + *options.GetTimeout() / 2); +} + +static Timeout<std::micro> +GetExpressionTimeout(const EvaluateExpressionOptions &options, + bool before_first_timeout) { + // If we are going to run all threads the whole time, or if we are only going + // to run one thread, we can just return the overall timeout. + if (!options.GetStopOthers() || !options.GetTryAllThreads()) + return options.GetTimeout(); + + if (before_first_timeout) + return GetOneThreadExpressionTimeout(options); + + if (!options.GetTimeout()) + return llvm::None; + else + return *options.GetTimeout() - GetOneThreadExpressionTimeout(options); +} + +static llvm::Optional<ExpressionResults> +HandleStoppedEvent(Thread &thread, const ThreadPlanSP &thread_plan_sp, + RestorePlanState &restorer, const EventSP &event_sp, + EventSP &event_to_broadcast_sp, + const EvaluateExpressionOptions &options, bool handle_interrupts) { + Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS); + + ThreadPlanSP plan = thread.GetCompletedPlan(); + if (plan == thread_plan_sp && plan->PlanSucceeded()) { + LLDB_LOG(log, "execution completed successfully"); + + // Restore the plan state so it will get reported as intended when we are + // done. + restorer.Clean(); + return eExpressionCompleted; + } + + StopInfoSP stop_info_sp = thread.GetStopInfo(); + if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint && + stop_info_sp->ShouldNotify(event_sp.get())) { + LLDB_LOG(log, "stopped for breakpoint: {0}.", stop_info_sp->GetDescription()); + if (!options.DoesIgnoreBreakpoints()) { + // Restore the plan state and then force Private to false. We are going + // to stop because of this plan so we need it to become a public plan or + // it won't report correctly when we continue to its termination later + // on. + restorer.Clean(); + thread_plan_sp->SetPrivate(false); + event_to_broadcast_sp = event_sp; + } + return eExpressionHitBreakpoint; + } + + if (!handle_interrupts && + Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get())) + return llvm::None; + + LLDB_LOG(log, "thread plan did not successfully complete"); + if (!options.DoesUnwindOnError()) + event_to_broadcast_sp = event_sp; + return eExpressionInterrupted; +} + +ExpressionResults +Process::RunThreadPlan(ExecutionContext &exe_ctx, + lldb::ThreadPlanSP &thread_plan_sp, + const EvaluateExpressionOptions &options, + DiagnosticManager &diagnostic_manager) { + ExpressionResults return_value = eExpressionSetupError; + + std::lock_guard<std::mutex> run_thread_plan_locker(m_run_thread_plan_lock); + + if (!thread_plan_sp) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "RunThreadPlan called with empty thread plan."); + return eExpressionSetupError; + } + + if (!thread_plan_sp->ValidatePlan(nullptr)) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "RunThreadPlan called with an invalid thread plan."); + return eExpressionSetupError; + } + + if (exe_ctx.GetProcessPtr() != this) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "RunThreadPlan called on wrong process."); + return eExpressionSetupError; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread == nullptr) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "RunThreadPlan called with invalid thread."); + return eExpressionSetupError; + } + + // We need to change some of the thread plan attributes for the thread plan + // runner. This will restore them when we are done: + + RestorePlanState thread_plan_restorer(thread_plan_sp); + + // We rely on the thread plan we are running returning "PlanCompleted" if + // when it successfully completes. For that to be true the plan can't be + // private - since private plans suppress themselves in the GetCompletedPlan + // call. + + thread_plan_sp->SetPrivate(false); + + // The plans run with RunThreadPlan also need to be terminal master plans or + // when they are done we will end up asking the plan above us whether we + // should stop, which may give the wrong answer. + + thread_plan_sp->SetIsMasterPlan(true); + thread_plan_sp->SetOkayToDiscard(false); + + // If we are running some utility expression for LLDB, we now have to mark + // this in the ProcesModID of this process. This RAII takes care of marking + // and reverting the mark it once we are done running the expression. + UtilityFunctionScope util_scope(options.IsForUtilityExpr() ? this : nullptr); + + if (m_private_state.GetValue() != eStateStopped) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "RunThreadPlan called while the private state was not stopped."); + return eExpressionSetupError; + } + + // Save the thread & frame from the exe_ctx for restoration after we run + const uint32_t thread_idx_id = thread->GetIndexID(); + StackFrameSP selected_frame_sp = thread->GetSelectedFrame(); + if (!selected_frame_sp) { + thread->SetSelectedFrame(nullptr); + selected_frame_sp = thread->GetSelectedFrame(); + if (!selected_frame_sp) { + diagnostic_manager.Printf( + eDiagnosticSeverityError, + "RunThreadPlan called without a selected frame on thread %d", + thread_idx_id); + return eExpressionSetupError; + } + } + + // Make sure the timeout values make sense. The one thread timeout needs to + // be smaller than the overall timeout. + if (options.GetOneThreadTimeout() && options.GetTimeout() && + *options.GetTimeout() < *options.GetOneThreadTimeout()) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "RunThreadPlan called with one thread " + "timeout greater than total timeout"); + return eExpressionSetupError; + } + + StackID ctx_frame_id = selected_frame_sp->GetStackID(); + + // N.B. Running the target may unset the currently selected thread and frame. + // We don't want to do that either, so we should arrange to reset them as + // well. + + lldb::ThreadSP selected_thread_sp = GetThreadList().GetSelectedThread(); + + uint32_t selected_tid; + StackID selected_stack_id; + if (selected_thread_sp) { + selected_tid = selected_thread_sp->GetIndexID(); + selected_stack_id = selected_thread_sp->GetSelectedFrame()->GetStackID(); + } else { + selected_tid = LLDB_INVALID_THREAD_ID; + } + + HostThread backup_private_state_thread; + lldb::StateType old_state = eStateInvalid; + lldb::ThreadPlanSP stopper_base_plan_sp; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | + LIBLLDB_LOG_PROCESS)); + if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) { + // Yikes, we are running on the private state thread! So we can't wait for + // public events on this thread, since we are the thread that is generating + // public events. The simplest thing to do is to spin up a temporary thread + // to handle private state thread events while we are fielding public + // events here. + LLDB_LOGF(log, "Running thread plan on private state thread, spinning up " + "another state thread to handle the events."); + + backup_private_state_thread = m_private_state_thread; + + // One other bit of business: we want to run just this thread plan and + // anything it pushes, and then stop, returning control here. But in the + // normal course of things, the plan above us on the stack would be given a + // shot at the stop event before deciding to stop, and we don't want that. + // So we insert a "stopper" base plan on the stack before the plan we want + // to run. Since base plans always stop and return control to the user, + // that will do just what we want. + stopper_base_plan_sp.reset(new ThreadPlanBase(*thread)); + thread->QueueThreadPlan(stopper_base_plan_sp, false); + // Have to make sure our public state is stopped, since otherwise the + // reporting logic below doesn't work correctly. + old_state = m_public_state.GetValue(); + m_public_state.SetValueNoLock(eStateStopped); + + // Now spin up the private state thread: + StartPrivateStateThread(true); + } + + thread->QueueThreadPlan( + thread_plan_sp, false); // This used to pass "true" does that make sense? + + if (options.GetDebug()) { + // In this case, we aren't actually going to run, we just want to stop + // right away. Flush this thread so we will refetch the stacks and show the + // correct backtrace. + // FIXME: To make this prettier we should invent some stop reason for this, + // but that + // is only cosmetic, and this functionality is only of use to lldb + // developers who can live with not pretty... + thread->Flush(); + return eExpressionStoppedForDebug; + } + + ListenerSP listener_sp( + Listener::MakeListener("lldb.process.listener.run-thread-plan")); + + lldb::EventSP event_to_broadcast_sp; + + { + // This process event hijacker Hijacks the Public events and its destructor + // makes sure that the process events get restored on exit to the function. + // + // If the event needs to propagate beyond the hijacker (e.g., the process + // exits during execution), then the event is put into + // event_to_broadcast_sp for rebroadcasting. + + ProcessEventHijacker run_thread_plan_hijacker(*this, listener_sp); + + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + LLDB_LOGF(log, + "Process::RunThreadPlan(): Resuming thread %u - 0x%4.4" PRIx64 + " to run thread plan \"%s\".", + thread->GetIndexID(), thread->GetID(), s.GetData()); + } + + bool got_event; + lldb::EventSP event_sp; + lldb::StateType stop_state = lldb::eStateInvalid; + + bool before_first_timeout = true; // This is set to false the first time + // that we have to halt the target. + bool do_resume = true; + bool handle_running_event = true; + + // This is just for accounting: + uint32_t num_resumes = 0; + + // If we are going to run all threads the whole time, or if we are only + // going to run one thread, then we don't need the first timeout. So we + // pretend we are after the first timeout already. + if (!options.GetStopOthers() || !options.GetTryAllThreads()) + before_first_timeout = false; + + LLDB_LOGF(log, "Stop others: %u, try all: %u, before_first: %u.\n", + options.GetStopOthers(), options.GetTryAllThreads(), + before_first_timeout); + + // This isn't going to work if there are unfetched events on the queue. Are + // there cases where we might want to run the remaining events here, and + // then try to call the function? That's probably being too tricky for our + // own good. + + Event *other_events = listener_sp->PeekAtNextEvent(); + if (other_events != nullptr) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "RunThreadPlan called with pending events on the queue."); + return eExpressionSetupError; + } + + // We also need to make sure that the next event is delivered. We might be + // calling a function as part of a thread plan, in which case the last + // delivered event could be the running event, and we don't want event + // coalescing to cause us to lose OUR running event... + ForceNextEventDelivery(); + +// This while loop must exit out the bottom, there's cleanup that we need to do +// when we are done. So don't call return anywhere within it. + +#ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT + // It's pretty much impossible to write test cases for things like: One + // thread timeout expires, I go to halt, but the process already stopped on + // the function call stop breakpoint. Turning on this define will make us + // not fetch the first event till after the halt. So if you run a quick + // function, it will have completed, and the completion event will be + // waiting, when you interrupt for halt. The expression evaluation should + // still succeed. + bool miss_first_event = true; +#endif + while (true) { + // We usually want to resume the process if we get to the top of the + // loop. The only exception is if we get two running events with no + // intervening stop, which can happen, we will just wait for then next + // stop event. + LLDB_LOGF(log, + "Top of while loop: do_resume: %i handle_running_event: %i " + "before_first_timeout: %i.", + do_resume, handle_running_event, before_first_timeout); + + if (do_resume || handle_running_event) { + // Do the initial resume and wait for the running event before going + // further. + + if (do_resume) { + num_resumes++; + Status resume_error = PrivateResume(); + if (!resume_error.Success()) { + diagnostic_manager.Printf( + eDiagnosticSeverityError, + "couldn't resume inferior the %d time: \"%s\".", num_resumes, + resume_error.AsCString()); + return_value = eExpressionSetupError; + break; + } + } + + got_event = + listener_sp->GetEvent(event_sp, GetUtilityExpressionTimeout()); + if (!got_event) { + LLDB_LOGF(log, + "Process::RunThreadPlan(): didn't get any event after " + "resume %" PRIu32 ", exiting.", + num_resumes); + + diagnostic_manager.Printf(eDiagnosticSeverityError, + "didn't get any event after resume %" PRIu32 + ", exiting.", + num_resumes); + return_value = eExpressionSetupError; + break; + } + + stop_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (stop_state != eStateRunning) { + bool restarted = false; + + if (stop_state == eStateStopped) { + restarted = Process::ProcessEventData::GetRestartedFromEvent( + event_sp.get()); + LLDB_LOGF( + log, + "Process::RunThreadPlan(): didn't get running event after " + "resume %d, got %s instead (restarted: %i, do_resume: %i, " + "handle_running_event: %i).", + num_resumes, StateAsCString(stop_state), restarted, do_resume, + handle_running_event); + } + + if (restarted) { + // This is probably an overabundance of caution, I don't think I + // should ever get a stopped & restarted event here. But if I do, + // the best thing is to Halt and then get out of here. + const bool clear_thread_plans = false; + const bool use_run_lock = false; + Halt(clear_thread_plans, use_run_lock); + } + + diagnostic_manager.Printf( + eDiagnosticSeverityError, + "didn't get running event after initial resume, got %s instead.", + StateAsCString(stop_state)); + return_value = eExpressionSetupError; + break; + } + + if (log) + log->PutCString("Process::RunThreadPlan(): resuming succeeded."); + // We need to call the function synchronously, so spin waiting for it + // to return. If we get interrupted while executing, we're going to + // lose our context, and won't be able to gather the result at this + // point. We set the timeout AFTER the resume, since the resume takes + // some time and we don't want to charge that to the timeout. + } else { + if (log) + log->PutCString("Process::RunThreadPlan(): waiting for next event."); + } + + do_resume = true; + handle_running_event = true; + + // Now wait for the process to stop again: + event_sp.reset(); + + Timeout<std::micro> timeout = + GetExpressionTimeout(options, before_first_timeout); + if (log) { + if (timeout) { + auto now = system_clock::now(); + LLDB_LOGF(log, + "Process::RunThreadPlan(): about to wait - now is %s - " + "endpoint is %s", + llvm::to_string(now).c_str(), + llvm::to_string(now + *timeout).c_str()); + } else { + LLDB_LOGF(log, "Process::RunThreadPlan(): about to wait forever."); + } + } + +#ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT + // See comment above... + if (miss_first_event) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + miss_first_event = false; + got_event = false; + } else +#endif + got_event = listener_sp->GetEvent(event_sp, timeout); + + if (got_event) { + if (event_sp) { + bool keep_going = false; + if (event_sp->GetType() == eBroadcastBitInterrupt) { + const bool clear_thread_plans = false; + const bool use_run_lock = false; + Halt(clear_thread_plans, use_run_lock); + return_value = eExpressionInterrupted; + diagnostic_manager.PutString(eDiagnosticSeverityRemark, + "execution halted by user interrupt."); + LLDB_LOGF(log, "Process::RunThreadPlan(): Got interrupted by " + "eBroadcastBitInterrupted, exiting."); + break; + } else { + stop_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + LLDB_LOGF(log, + "Process::RunThreadPlan(): in while loop, got event: %s.", + StateAsCString(stop_state)); + + switch (stop_state) { + case lldb::eStateStopped: { + // We stopped, figure out what we are going to do now. + ThreadSP thread_sp = + GetThreadList().FindThreadByIndexID(thread_idx_id); + if (!thread_sp) { + // Ooh, our thread has vanished. Unlikely that this was + // successful execution... + LLDB_LOGF(log, + "Process::RunThreadPlan(): execution completed " + "but our thread (index-id=%u) has vanished.", + thread_idx_id); + return_value = eExpressionInterrupted; + } else if (Process::ProcessEventData::GetRestartedFromEvent( + event_sp.get())) { + // If we were restarted, we just need to go back up to fetch + // another event. + if (log) { + LLDB_LOGF(log, "Process::RunThreadPlan(): Got a stop and " + "restart, so we'll continue waiting."); + } + keep_going = true; + do_resume = false; + handle_running_event = true; + } else { + const bool handle_interrupts = true; + return_value = *HandleStoppedEvent( + *thread, thread_plan_sp, thread_plan_restorer, event_sp, + event_to_broadcast_sp, options, handle_interrupts); + } + } break; + + case lldb::eStateRunning: + // This shouldn't really happen, but sometimes we do get two + // running events without an intervening stop, and in that case + // we should just go back to waiting for the stop. + do_resume = false; + keep_going = true; + handle_running_event = false; + break; + + default: + LLDB_LOGF(log, + "Process::RunThreadPlan(): execution stopped with " + "unexpected state: %s.", + StateAsCString(stop_state)); + + if (stop_state == eStateExited) + event_to_broadcast_sp = event_sp; + + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "execution stopped with unexpected state."); + return_value = eExpressionInterrupted; + break; + } + } + + if (keep_going) + continue; + else + break; + } else { + if (log) + log->PutCString("Process::RunThreadPlan(): got_event was true, but " + "the event pointer was null. How odd..."); + return_value = eExpressionInterrupted; + break; + } + } else { + // If we didn't get an event that means we've timed out... We will + // interrupt the process here. Depending on what we were asked to do + // we will either exit, or try with all threads running for the same + // timeout. + + if (log) { + if (options.GetTryAllThreads()) { + if (before_first_timeout) { + LLDB_LOG(log, + "Running function with one thread timeout timed out."); + } else + LLDB_LOG(log, "Restarting function with all threads enabled and " + "timeout: {0} timed out, abandoning execution.", + timeout); + } else + LLDB_LOG(log, "Running function with timeout: {0} timed out, " + "abandoning execution.", + timeout); + } + + // It is possible that between the time we issued the Halt, and we get + // around to calling Halt the target could have stopped. That's fine, + // Halt will figure that out and send the appropriate Stopped event. + // BUT it is also possible that we stopped & restarted (e.g. hit a + // signal with "stop" set to false.) In + // that case, we'll get the stopped & restarted event, and we should go + // back to waiting for the Halt's stopped event. That's what this + // while loop does. + + bool back_to_top = true; + uint32_t try_halt_again = 0; + bool do_halt = true; + const uint32_t num_retries = 5; + while (try_halt_again < num_retries) { + Status halt_error; + if (do_halt) { + LLDB_LOGF(log, "Process::RunThreadPlan(): Running Halt."); + const bool clear_thread_plans = false; + const bool use_run_lock = false; + Halt(clear_thread_plans, use_run_lock); + } + if (halt_error.Success()) { + if (log) + log->PutCString("Process::RunThreadPlan(): Halt succeeded."); + + got_event = + listener_sp->GetEvent(event_sp, GetUtilityExpressionTimeout()); + + if (got_event) { + stop_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + if (log) { + LLDB_LOGF(log, + "Process::RunThreadPlan(): Stopped with event: %s", + StateAsCString(stop_state)); + if (stop_state == lldb::eStateStopped && + Process::ProcessEventData::GetInterruptedFromEvent( + event_sp.get())) + log->PutCString(" Event was the Halt interruption event."); + } + + if (stop_state == lldb::eStateStopped) { + if (Process::ProcessEventData::GetRestartedFromEvent( + event_sp.get())) { + if (log) + log->PutCString("Process::RunThreadPlan(): Went to halt " + "but got a restarted event, there must be " + "an un-restarted stopped event so try " + "again... " + "Exiting wait loop."); + try_halt_again++; + do_halt = false; + continue; + } + + // Between the time we initiated the Halt and the time we + // delivered it, the process could have already finished its + // job. Check that here: + const bool handle_interrupts = false; + if (auto result = HandleStoppedEvent( + *thread, thread_plan_sp, thread_plan_restorer, event_sp, + event_to_broadcast_sp, options, handle_interrupts)) { + return_value = *result; + back_to_top = false; + break; + } + + if (!options.GetTryAllThreads()) { + if (log) + log->PutCString("Process::RunThreadPlan(): try_all_threads " + "was false, we stopped so now we're " + "quitting."); + return_value = eExpressionInterrupted; + back_to_top = false; + break; + } + + if (before_first_timeout) { + // Set all the other threads to run, and return to the top of + // the loop, which will continue; + before_first_timeout = false; + thread_plan_sp->SetStopOthers(false); + if (log) + log->PutCString( + "Process::RunThreadPlan(): about to resume."); + + back_to_top = true; + break; + } else { + // Running all threads failed, so return Interrupted. + if (log) + log->PutCString("Process::RunThreadPlan(): running all " + "threads timed out."); + return_value = eExpressionInterrupted; + back_to_top = false; + break; + } + } + } else { + if (log) + log->PutCString("Process::RunThreadPlan(): halt said it " + "succeeded, but I got no event. " + "I'm getting out of here passing Interrupted."); + return_value = eExpressionInterrupted; + back_to_top = false; + break; + } + } else { + try_halt_again++; + continue; + } + } + + if (!back_to_top || try_halt_again > num_retries) + break; + else + continue; + } + } // END WAIT LOOP + + // If we had to start up a temporary private state thread to run this + // thread plan, shut it down now. + if (backup_private_state_thread.IsJoinable()) { + StopPrivateStateThread(); + Status error; + m_private_state_thread = backup_private_state_thread; + if (stopper_base_plan_sp) { + thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp); + } + if (old_state != eStateInvalid) + m_public_state.SetValueNoLock(old_state); + } + + if (return_value != eExpressionCompleted && log) { + // Print a backtrace into the log so we can figure out where we are: + StreamString s; + s.PutCString("Thread state after unsuccessful completion: \n"); + thread->GetStackFrameStatus(s, 0, UINT32_MAX, true, UINT32_MAX); + log->PutString(s.GetString()); + } + // Restore the thread state if we are going to discard the plan execution. + // There are three cases where this could happen: 1) The execution + // successfully completed 2) We hit a breakpoint, and ignore_breakpoints + // was true 3) We got some other error, and discard_on_error was true + bool should_unwind = (return_value == eExpressionInterrupted && + options.DoesUnwindOnError()) || + (return_value == eExpressionHitBreakpoint && + options.DoesIgnoreBreakpoints()); + + if (return_value == eExpressionCompleted || should_unwind) { + thread_plan_sp->RestoreThreadState(); + } + + // Now do some processing on the results of the run: + if (return_value == eExpressionInterrupted || + return_value == eExpressionHitBreakpoint) { + if (log) { + StreamString s; + if (event_sp) + event_sp->Dump(&s); + else { + log->PutCString("Process::RunThreadPlan(): Stop event that " + "interrupted us is NULL."); + } + + StreamString ts; + + const char *event_explanation = nullptr; + + do { + if (!event_sp) { + event_explanation = "<no event>"; + break; + } else if (event_sp->GetType() == eBroadcastBitInterrupt) { + event_explanation = "<user interrupt>"; + break; + } else { + const Process::ProcessEventData *event_data = + Process::ProcessEventData::GetEventDataFromEvent( + event_sp.get()); + + if (!event_data) { + event_explanation = "<no event data>"; + break; + } + + Process *process = event_data->GetProcessSP().get(); + + if (!process) { + event_explanation = "<no process>"; + break; + } + + ThreadList &thread_list = process->GetThreadList(); + + uint32_t num_threads = thread_list.GetSize(); + uint32_t thread_index; + + ts.Printf("<%u threads> ", num_threads); + + for (thread_index = 0; thread_index < num_threads; ++thread_index) { + Thread *thread = thread_list.GetThreadAtIndex(thread_index).get(); + + if (!thread) { + ts.Printf("<?> "); + continue; + } + + ts.Printf("<0x%4.4" PRIx64 " ", thread->GetID()); + RegisterContext *register_context = + thread->GetRegisterContext().get(); + + if (register_context) + ts.Printf("[ip 0x%" PRIx64 "] ", register_context->GetPC()); + else + ts.Printf("[ip unknown] "); + + // Show the private stop info here, the public stop info will be + // from the last natural stop. + lldb::StopInfoSP stop_info_sp = thread->GetPrivateStopInfo(); + if (stop_info_sp) { + const char *stop_desc = stop_info_sp->GetDescription(); + if (stop_desc) + ts.PutCString(stop_desc); + } + ts.Printf(">"); + } + + event_explanation = ts.GetData(); + } + } while (false); + + if (event_explanation) + LLDB_LOGF(log, + "Process::RunThreadPlan(): execution interrupted: %s %s", + s.GetData(), event_explanation); + else + LLDB_LOGF(log, "Process::RunThreadPlan(): execution interrupted: %s", + s.GetData()); + } + + if (should_unwind) { + LLDB_LOGF(log, + "Process::RunThreadPlan: ExecutionInterrupted - " + "discarding thread plans up to %p.", + static_cast<void *>(thread_plan_sp.get())); + thread->DiscardThreadPlansUpToPlan(thread_plan_sp); + } else { + LLDB_LOGF(log, + "Process::RunThreadPlan: ExecutionInterrupted - for " + "plan: %p not discarding.", + static_cast<void *>(thread_plan_sp.get())); + } + } else if (return_value == eExpressionSetupError) { + if (log) + log->PutCString("Process::RunThreadPlan(): execution set up error."); + + if (options.DoesUnwindOnError()) { + thread->DiscardThreadPlansUpToPlan(thread_plan_sp); + } + } else { + if (thread->IsThreadPlanDone(thread_plan_sp.get())) { + if (log) + log->PutCString("Process::RunThreadPlan(): thread plan is done"); + return_value = eExpressionCompleted; + } else if (thread->WasThreadPlanDiscarded(thread_plan_sp.get())) { + if (log) + log->PutCString( + "Process::RunThreadPlan(): thread plan was discarded"); + return_value = eExpressionDiscarded; + } else { + if (log) + log->PutCString( + "Process::RunThreadPlan(): thread plan stopped in mid course"); + if (options.DoesUnwindOnError() && thread_plan_sp) { + if (log) + log->PutCString("Process::RunThreadPlan(): discarding thread plan " + "'cause unwind_on_error is set."); + thread->DiscardThreadPlansUpToPlan(thread_plan_sp); + } + } + } + + // Thread we ran the function in may have gone away because we ran the + // target Check that it's still there, and if it is put it back in the + // context. Also restore the frame in the context if it is still present. + thread = GetThreadList().FindThreadByIndexID(thread_idx_id, true).get(); + if (thread) { + exe_ctx.SetFrameSP(thread->GetFrameWithStackID(ctx_frame_id)); + } + + // Also restore the current process'es selected frame & thread, since this + // function calling may be done behind the user's back. + + if (selected_tid != LLDB_INVALID_THREAD_ID) { + if (GetThreadList().SetSelectedThreadByIndexID(selected_tid) && + selected_stack_id.IsValid()) { + // We were able to restore the selected thread, now restore the frame: + std::lock_guard<std::recursive_mutex> guard(GetThreadList().GetMutex()); + StackFrameSP old_frame_sp = + GetThreadList().GetSelectedThread()->GetFrameWithStackID( + selected_stack_id); + if (old_frame_sp) + GetThreadList().GetSelectedThread()->SetSelectedFrame( + old_frame_sp.get()); + } + } + } + + // If the process exited during the run of the thread plan, notify everyone. + + if (event_to_broadcast_sp) { + if (log) + log->PutCString("Process::RunThreadPlan(): rebroadcasting event."); + BroadcastEvent(event_to_broadcast_sp); + } + + return return_value; +} + +const char *Process::ExecutionResultAsCString(ExpressionResults result) { + const char *result_name; + + switch (result) { + case eExpressionCompleted: + result_name = "eExpressionCompleted"; + break; + case eExpressionDiscarded: + result_name = "eExpressionDiscarded"; + break; + case eExpressionInterrupted: + result_name = "eExpressionInterrupted"; + break; + case eExpressionHitBreakpoint: + result_name = "eExpressionHitBreakpoint"; + break; + case eExpressionSetupError: + result_name = "eExpressionSetupError"; + break; + case eExpressionParseError: + result_name = "eExpressionParseError"; + break; + case eExpressionResultUnavailable: + result_name = "eExpressionResultUnavailable"; + break; + case eExpressionTimedOut: + result_name = "eExpressionTimedOut"; + break; + case eExpressionStoppedForDebug: + result_name = "eExpressionStoppedForDebug"; + break; + } + return result_name; +} + +void Process::GetStatus(Stream &strm) { + const StateType state = GetState(); + if (StateIsStoppedState(state, false)) { + if (state == eStateExited) { + int exit_status = GetExitStatus(); + const char *exit_description = GetExitDescription(); + strm.Printf("Process %" PRIu64 " exited with status = %i (0x%8.8x) %s\n", + GetID(), exit_status, exit_status, + exit_description ? exit_description : ""); + } else { + if (state == eStateConnected) + strm.Printf("Connected to remote target.\n"); + else + strm.Printf("Process %" PRIu64 " %s\n", GetID(), StateAsCString(state)); + } + } else { + strm.Printf("Process %" PRIu64 " is running.\n", GetID()); + } +} + +size_t Process::GetThreadStatus(Stream &strm, + bool only_threads_with_stop_reason, + uint32_t start_frame, uint32_t num_frames, + uint32_t num_frames_with_source, + bool stop_format) { + size_t num_thread_infos_dumped = 0; + + // You can't hold the thread list lock while calling Thread::GetStatus. That + // very well might run code (e.g. if we need it to get return values or + // arguments.) For that to work the process has to be able to acquire it. + // So instead copy the thread ID's, and look them up one by one: + + uint32_t num_threads; + std::vector<lldb::tid_t> thread_id_array; + // Scope for thread list locker; + { + std::lock_guard<std::recursive_mutex> guard(GetThreadList().GetMutex()); + ThreadList &curr_thread_list = GetThreadList(); + num_threads = curr_thread_list.GetSize(); + uint32_t idx; + thread_id_array.resize(num_threads); + for (idx = 0; idx < num_threads; ++idx) + thread_id_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID(); + } + + for (uint32_t i = 0; i < num_threads; i++) { + ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_id_array[i])); + if (thread_sp) { + if (only_threads_with_stop_reason) { + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); + if (!stop_info_sp || !stop_info_sp->IsValid()) + continue; + } + thread_sp->GetStatus(strm, start_frame, num_frames, + num_frames_with_source, + stop_format); + ++num_thread_infos_dumped; + } else { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::GetThreadStatus - thread 0x" PRIu64 + " vanished while running Thread::GetStatus."); + } + } + return num_thread_infos_dumped; +} + +void Process::AddInvalidMemoryRegion(const LoadRange ®ion) { + m_memory_cache.AddInvalidRange(region.GetRangeBase(), region.GetByteSize()); +} + +bool Process::RemoveInvalidMemoryRange(const LoadRange ®ion) { + return m_memory_cache.RemoveInvalidRange(region.GetRangeBase(), + region.GetByteSize()); +} + +void Process::AddPreResumeAction(PreResumeActionCallback callback, + void *baton) { + m_pre_resume_actions.push_back(PreResumeCallbackAndBaton(callback, baton)); +} + +bool Process::RunPreResumeActions() { + bool result = true; + while (!m_pre_resume_actions.empty()) { + struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back(); + m_pre_resume_actions.pop_back(); + bool this_result = action.callback(action.baton); + if (result) + result = this_result; + } + return result; +} + +void Process::ClearPreResumeActions() { m_pre_resume_actions.clear(); } + +void Process::ClearPreResumeAction(PreResumeActionCallback callback, void *baton) +{ + PreResumeCallbackAndBaton element(callback, baton); + auto found_iter = std::find(m_pre_resume_actions.begin(), m_pre_resume_actions.end(), element); + if (found_iter != m_pre_resume_actions.end()) + { + m_pre_resume_actions.erase(found_iter); + } +} + +ProcessRunLock &Process::GetRunLock() { + if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) + return m_private_run_lock; + else + return m_public_run_lock; +} + +bool Process::CurrentThreadIsPrivateStateThread() +{ + return m_private_state_thread.EqualsThread(Host::GetCurrentThread()); +} + + +void Process::Flush() { + m_thread_list.Flush(); + m_extended_thread_list.Flush(); + m_extended_thread_stop_id = 0; + m_queue_list.Clear(); + m_queue_list_stop_id = 0; +} + +void Process::DidExec() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "Process::%s()", __FUNCTION__); + + Target &target = GetTarget(); + target.CleanupProcess(); + target.ClearModules(false); + m_dynamic_checkers_up.reset(); + m_abi_sp.reset(); + m_system_runtime_up.reset(); + m_os_up.reset(); + m_dyld_up.reset(); + m_jit_loaders_up.reset(); + m_image_tokens.clear(); + m_allocated_memory_cache.Clear(); + { + std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex); + m_language_runtimes.clear(); + } + m_instrumentation_runtimes.clear(); + m_thread_list.DiscardThreadPlans(); + m_memory_cache.Clear(true); + DoDidExec(); + CompleteAttach(); + // Flush the process (threads and all stack frames) after running + // CompleteAttach() in case the dynamic loader loaded things in new + // locations. + Flush(); + + // After we figure out what was loaded/unloaded in CompleteAttach, we need to + // let the target know so it can do any cleanup it needs to. + target.DidExec(); +} + +addr_t Process::ResolveIndirectFunction(const Address *address, Status &error) { + if (address == nullptr) { + error.SetErrorString("Invalid address argument"); + return LLDB_INVALID_ADDRESS; + } + + addr_t function_addr = LLDB_INVALID_ADDRESS; + + addr_t addr = address->GetLoadAddress(&GetTarget()); + std::map<addr_t, addr_t>::const_iterator iter = + m_resolved_indirect_addresses.find(addr); + if (iter != m_resolved_indirect_addresses.end()) { + function_addr = (*iter).second; + } else { + if (!CallVoidArgVoidPtrReturn(address, function_addr)) { + Symbol *symbol = address->CalculateSymbolContextSymbol(); + error.SetErrorStringWithFormat( + "Unable to call resolver for indirect function %s", + symbol ? symbol->GetName().AsCString() : "<UNKNOWN>"); + function_addr = LLDB_INVALID_ADDRESS; + } else { + m_resolved_indirect_addresses.insert( + std::pair<addr_t, addr_t>(addr, function_addr)); + } + } + return function_addr; +} + +void Process::ModulesDidLoad(ModuleList &module_list) { + SystemRuntime *sys_runtime = GetSystemRuntime(); + if (sys_runtime) { + sys_runtime->ModulesDidLoad(module_list); + } + + GetJITLoaders().ModulesDidLoad(module_list); + + // Give runtimes a chance to be created. + InstrumentationRuntime::ModulesDidLoad(module_list, this, + m_instrumentation_runtimes); + + // Tell runtimes about new modules. + for (auto pos = m_instrumentation_runtimes.begin(); + pos != m_instrumentation_runtimes.end(); ++pos) { + InstrumentationRuntimeSP runtime = pos->second; + runtime->ModulesDidLoad(module_list); + } + + // Let any language runtimes we have already created know about the modules + // that loaded. + + // Iterate over a copy of this language runtime list in case the language + // runtime ModulesDidLoad somehow causes the language runtime to be + // unloaded. + { + std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex); + LanguageRuntimeCollection language_runtimes(m_language_runtimes); + for (const auto &pair : language_runtimes) { + // We must check language_runtime_sp to make sure it is not nullptr as we + // might cache the fact that we didn't have a language runtime for a + // language. + LanguageRuntimeSP language_runtime_sp = pair.second; + if (language_runtime_sp) + language_runtime_sp->ModulesDidLoad(module_list); + } + } + + // If we don't have an operating system plug-in, try to load one since + // loading shared libraries might cause a new one to try and load + if (!m_os_up) + LoadOperatingSystemPlugin(false); + + // Give structured-data plugins a chance to see the modified modules. + for (auto pair : m_structured_data_plugin_map) { + if (pair.second) + pair.second->ModulesDidLoad(*this, module_list); + } +} + +void Process::PrintWarning(uint64_t warning_type, const void *repeat_key, + const char *fmt, ...) { + bool print_warning = true; + + StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); + if (!stream_sp) + return; + if (warning_type == eWarningsOptimization && !GetWarningsOptimization()) { + return; + } + + if (repeat_key != nullptr) { + WarningsCollection::iterator it = m_warnings_issued.find(warning_type); + if (it == m_warnings_issued.end()) { + m_warnings_issued[warning_type] = WarningsPointerSet(); + m_warnings_issued[warning_type].insert(repeat_key); + } else { + if (it->second.find(repeat_key) != it->second.end()) { + print_warning = false; + } else { + it->second.insert(repeat_key); + } + } + } + + if (print_warning) { + va_list args; + va_start(args, fmt); + stream_sp->PrintfVarArg(fmt, args); + va_end(args); + } +} + +void Process::PrintWarningOptimization(const SymbolContext &sc) { + if (GetWarningsOptimization() && sc.module_sp && + !sc.module_sp->GetFileSpec().GetFilename().IsEmpty() && sc.function && + sc.function->GetIsOptimized()) { + PrintWarning(Process::Warnings::eWarningsOptimization, sc.module_sp.get(), + "%s was compiled with optimization - stepping may behave " + "oddly; variables may not be available.\n", + sc.module_sp->GetFileSpec().GetFilename().GetCString()); + } +} + +bool Process::GetProcessInfo(ProcessInstanceInfo &info) { + info.Clear(); + + PlatformSP platform_sp = GetTarget().GetPlatform(); + if (!platform_sp) + return false; + + return platform_sp->GetProcessInfo(GetID(), info); +} + +ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) { + ThreadCollectionSP threads; + + const MemoryHistorySP &memory_history = + MemoryHistory::FindPlugin(shared_from_this()); + + if (!memory_history) { + return threads; + } + + threads = std::make_shared<ThreadCollection>( + memory_history->GetHistoryThreads(addr)); + + return threads; +} + +InstrumentationRuntimeSP +Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) { + InstrumentationRuntimeCollection::iterator pos; + pos = m_instrumentation_runtimes.find(type); + if (pos == m_instrumentation_runtimes.end()) { + return InstrumentationRuntimeSP(); + } else + return (*pos).second; +} + +bool Process::GetModuleSpec(const FileSpec &module_file_spec, + const ArchSpec &arch, ModuleSpec &module_spec) { + module_spec.Clear(); + return false; +} + +size_t Process::AddImageToken(lldb::addr_t image_ptr) { + m_image_tokens.push_back(image_ptr); + return m_image_tokens.size() - 1; +} + +lldb::addr_t Process::GetImagePtrFromToken(size_t token) const { + if (token < m_image_tokens.size()) + return m_image_tokens[token]; + return LLDB_INVALID_IMAGE_TOKEN; +} + +void Process::ResetImageToken(size_t token) { + if (token < m_image_tokens.size()) + m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN; +} + +Address +Process::AdvanceAddressToNextBranchInstruction(Address default_stop_addr, + AddressRange range_bounds) { + Target &target = GetTarget(); + DisassemblerSP disassembler_sp; + InstructionList *insn_list = nullptr; + + Address retval = default_stop_addr; + + if (!target.GetUseFastStepping()) + return retval; + if (!default_stop_addr.IsValid()) + return retval; + + ExecutionContext exe_ctx(this); + const char *plugin_name = nullptr; + const char *flavor = nullptr; + const bool prefer_file_cache = true; + disassembler_sp = Disassembler::DisassembleRange( + target.GetArchitecture(), plugin_name, flavor, exe_ctx, range_bounds, + prefer_file_cache); + if (disassembler_sp) + insn_list = &disassembler_sp->GetInstructionList(); + + if (insn_list == nullptr) { + return retval; + } + + size_t insn_offset = + insn_list->GetIndexOfInstructionAtAddress(default_stop_addr); + if (insn_offset == UINT32_MAX) { + return retval; + } + + uint32_t branch_index = + insn_list->GetIndexOfNextBranchInstruction(insn_offset, target, + false /* ignore_calls*/, + nullptr); + if (branch_index == UINT32_MAX) { + return retval; + } + + if (branch_index > insn_offset) { + Address next_branch_insn_address = + insn_list->GetInstructionAtIndex(branch_index)->GetAddress(); + if (next_branch_insn_address.IsValid() && + range_bounds.ContainsFileAddress(next_branch_insn_address)) { + retval = next_branch_insn_address; + } + } + + return retval; +} + +Status +Process::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) { + + Status error; + + lldb::addr_t range_end = 0; + + region_list.clear(); + do { + lldb_private::MemoryRegionInfo region_info; + error = GetMemoryRegionInfo(range_end, region_info); + // GetMemoryRegionInfo should only return an error if it is unimplemented. + if (error.Fail()) { + region_list.clear(); + break; + } + + range_end = region_info.GetRange().GetRangeEnd(); + if (region_info.GetMapped() == MemoryRegionInfo::eYes) { + region_list.push_back(std::move(region_info)); + } + } while (range_end != LLDB_INVALID_ADDRESS); + + return error; +} + +Status +Process::ConfigureStructuredData(ConstString type_name, + const StructuredData::ObjectSP &config_sp) { + // If you get this, the Process-derived class needs to implement a method to + // enable an already-reported asynchronous structured data feature. See + // ProcessGDBRemote for an example implementation over gdb-remote. + return Status("unimplemented"); +} + +void Process::MapSupportedStructuredDataPlugins( + const StructuredData::Array &supported_type_names) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Bail out early if there are no type names to map. + if (supported_type_names.GetSize() == 0) { + LLDB_LOGF(log, "Process::%s(): no structured data types supported", + __FUNCTION__); + return; + } + + // Convert StructuredData type names to ConstString instances. + std::set<ConstString> const_type_names; + + LLDB_LOGF(log, + "Process::%s(): the process supports the following async " + "structured data types:", + __FUNCTION__); + + supported_type_names.ForEach( + [&const_type_names, &log](StructuredData::Object *object) { + if (!object) { + // Invalid - shouldn't be null objects in the array. + return false; + } + + auto type_name = object->GetAsString(); + if (!type_name) { + // Invalid format - all type names should be strings. + return false; + } + + const_type_names.insert(ConstString(type_name->GetValue())); + LLDB_LOG(log, "- {0}", type_name->GetValue()); + return true; + }); + + // For each StructuredDataPlugin, if the plugin handles any of the types in + // the supported_type_names, map that type name to that plugin. Stop when + // we've consumed all the type names. + // FIXME: should we return an error if there are type names nobody + // supports? + for (uint32_t plugin_index = 0; !const_type_names.empty(); plugin_index++) { + auto create_instance = + PluginManager::GetStructuredDataPluginCreateCallbackAtIndex( + plugin_index); + if (!create_instance) + break; + + // Create the plugin. + StructuredDataPluginSP plugin_sp = (*create_instance)(*this); + if (!plugin_sp) { + // This plugin doesn't think it can work with the process. Move on to the + // next. + continue; + } + + // For any of the remaining type names, map any that this plugin supports. + std::vector<ConstString> names_to_remove; + for (auto &type_name : const_type_names) { + if (plugin_sp->SupportsStructuredDataType(type_name)) { + m_structured_data_plugin_map.insert( + std::make_pair(type_name, plugin_sp)); + names_to_remove.push_back(type_name); + LLDB_LOGF(log, + "Process::%s(): using plugin %s for type name " + "%s", + __FUNCTION__, plugin_sp->GetPluginName().GetCString(), + type_name.GetCString()); + } + } + + // Remove the type names that were consumed by this plugin. + for (auto &type_name : names_to_remove) + const_type_names.erase(type_name); + } +} + +bool Process::RouteAsyncStructuredData( + const StructuredData::ObjectSP object_sp) { + // Nothing to do if there's no data. + if (!object_sp) + return false; + + // The contract is this must be a dictionary, so we can look up the routing + // key via the top-level 'type' string value within the dictionary. + StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary(); + if (!dictionary) + return false; + + // Grab the async structured type name (i.e. the feature/plugin name). + ConstString type_name; + if (!dictionary->GetValueForKeyAsString("type", type_name)) + return false; + + // Check if there's a plugin registered for this type name. + auto find_it = m_structured_data_plugin_map.find(type_name); + if (find_it == m_structured_data_plugin_map.end()) { + // We don't have a mapping for this structured data type. + return false; + } + + // Route the structured data to the plugin. + find_it->second->HandleArrivalOfStructuredData(*this, type_name, object_sp); + return true; +} + +Status Process::UpdateAutomaticSignalFiltering() { + // Default implementation does nothign. + // No automatic signal filtering to speak of. + return Status(); +} + +UtilityFunction *Process::GetLoadImageUtilityFunction( + Platform *platform, + llvm::function_ref<std::unique_ptr<UtilityFunction>()> factory) { + if (platform != GetTarget().GetPlatform().get()) + return nullptr; + llvm::call_once(m_dlopen_utility_func_flag_once, + [&] { m_dlopen_utility_func_up = factory(); }); + return m_dlopen_utility_func_up.get(); +} + +bool Process::CallVoidArgVoidPtrReturn(const Address *address, + addr_t &returned_func, + bool trap_exceptions) { + Thread *thread = GetThreadList().GetExpressionExecutionThread().get(); + if (thread == nullptr || address == nullptr) + return false; + + EvaluateExpressionOptions options; + options.SetStopOthers(true); + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + options.SetTryAllThreads(true); + options.SetDebug(false); + options.SetTimeout(GetUtilityExpressionTimeout()); + options.SetTrapExceptions(trap_exceptions); + + auto type_system_or_err = + GetTarget().GetScratchTypeSystemForLanguage(eLanguageTypeC); + if (!type_system_or_err) { + llvm::consumeError(type_system_or_err.takeError()); + return false; + } + CompilerType void_ptr_type = + type_system_or_err->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType(); + lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction( + *thread, *address, void_ptr_type, llvm::ArrayRef<addr_t>(), options)); + if (call_plan_sp) { + DiagnosticManager diagnostics; + + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + if (frame) { + ExecutionContext exe_ctx; + frame->CalculateExecutionContext(exe_ctx); + ExpressionResults result = + RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics); + if (result == eExpressionCompleted) { + returned_func = + call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned( + LLDB_INVALID_ADDRESS); + + if (GetAddressByteSize() == 4) { + if (returned_func == UINT32_MAX) + return false; + } else if (GetAddressByteSize() == 8) { + if (returned_func == UINT64_MAX) + return false; + } + return true; + } + } + } + + return false; +} diff --git a/gnu/llvm/lldb/source/Target/Queue.cpp b/gnu/llvm/lldb/source/Target/Queue.cpp new file mode 100644 index 00000000000..fc2a93dbe89 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/Queue.cpp @@ -0,0 +1,89 @@ +//===-- Queue.cpp -----------------------------------------------*- C++ -*-===// +// +// 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/Queue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/QueueList.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +Queue::Queue(ProcessSP process_sp, lldb::queue_id_t queue_id, + const char *queue_name) + : m_process_wp(), m_queue_id(queue_id), m_queue_name(), + m_running_work_items_count(0), m_pending_work_items_count(0), + m_pending_items(), m_dispatch_queue_t_addr(LLDB_INVALID_ADDRESS), + m_kind(eQueueKindUnknown) { + if (queue_name) + m_queue_name = queue_name; + + m_process_wp = process_sp; +} + +Queue::~Queue() = default; + +queue_id_t Queue::GetID() { return m_queue_id; } + +const char *Queue::GetName() { + return (m_queue_name.empty() ? nullptr : m_queue_name.c_str()); +} + +uint32_t Queue::GetIndexID() { return m_queue_id; } + +std::vector<lldb::ThreadSP> Queue::GetThreads() { + std::vector<ThreadSP> result; + ProcessSP process_sp = m_process_wp.lock(); + if (process_sp) { + for (ThreadSP thread_sp : process_sp->Threads()) { + if (thread_sp->GetQueueID() == m_queue_id) { + result.push_back(thread_sp); + } + } + } + return result; +} + +void Queue::SetNumRunningWorkItems(uint32_t count) { + m_running_work_items_count = count; +} + +uint32_t Queue::GetNumRunningWorkItems() const { + return m_running_work_items_count; +} + +void Queue::SetNumPendingWorkItems(uint32_t count) { + m_pending_work_items_count = count; +} + +uint32_t Queue::GetNumPendingWorkItems() const { + return m_pending_work_items_count; +} + +void Queue::SetLibdispatchQueueAddress(addr_t dispatch_queue_t_addr) { + m_dispatch_queue_t_addr = dispatch_queue_t_addr; +} + +addr_t Queue::GetLibdispatchQueueAddress() const { + return m_dispatch_queue_t_addr; +} + +const std::vector<lldb::QueueItemSP> &Queue::GetPendingItems() { + if (m_pending_items.empty()) { + ProcessSP process_sp = m_process_wp.lock(); + if (process_sp && process_sp->GetSystemRuntime()) { + process_sp->GetSystemRuntime()->PopulatePendingItemsForQueue(this); + } + } + return m_pending_items; +} + +lldb::QueueKind Queue::GetKind() { return m_kind; } + +void Queue::SetKind(lldb::QueueKind kind) { m_kind = kind; } diff --git a/gnu/llvm/lldb/source/Target/QueueItem.cpp b/gnu/llvm/lldb/source/Target/QueueItem.cpp new file mode 100644 index 00000000000..47ff9e028fc --- /dev/null +++ b/gnu/llvm/lldb/source/Target/QueueItem.cpp @@ -0,0 +1,106 @@ +//===-- QueueItem.cpp -------------------------------------------*- C++ -*-===// +// +// 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/Queue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/QueueItem.h" +#include "lldb/Target/SystemRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +QueueItem::QueueItem(QueueSP queue_sp, ProcessSP process_sp, + lldb::addr_t item_ref, lldb_private::Address address) + : m_queue_wp(), m_process_wp(), m_item_ref(item_ref), m_address(address), + m_have_fetched_entire_item(false), m_kind(eQueueItemKindUnknown), + m_item_that_enqueued_this_ref(LLDB_INVALID_ADDRESS), + m_enqueueing_thread_id(LLDB_INVALID_THREAD_ID), + m_enqueueing_queue_id(LLDB_INVALID_QUEUE_ID), + m_target_queue_id(LLDB_INVALID_QUEUE_ID), m_stop_id(0), m_backtrace(), + m_thread_label(), m_queue_label(), m_target_queue_label() { + m_queue_wp = queue_sp; + m_process_wp = process_sp; +} + +QueueItem::~QueueItem() {} + +QueueItemKind QueueItem::GetKind() { + FetchEntireItem(); + return m_kind; +} + +void QueueItem::SetKind(QueueItemKind item_kind) { m_kind = item_kind; } + +Address &QueueItem::GetAddress() { return m_address; } + +void QueueItem::SetAddress(Address addr) { m_address = addr; } + +ThreadSP QueueItem::GetExtendedBacktraceThread(ConstString type) { + FetchEntireItem(); + ThreadSP return_thread; + QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) { + ProcessSP process_sp = queue_sp->GetProcess(); + if (process_sp && process_sp->GetSystemRuntime()) { + return_thread = + process_sp->GetSystemRuntime()->GetExtendedBacktraceForQueueItem( + this->shared_from_this(), type); + } + } + return return_thread; +} + +lldb::addr_t QueueItem::GetItemThatEnqueuedThis() { + FetchEntireItem(); + return m_item_that_enqueued_this_ref; +} + +lldb::tid_t QueueItem::GetEnqueueingThreadID() { + FetchEntireItem(); + return m_enqueueing_thread_id; +} + +lldb::queue_id_t QueueItem::GetEnqueueingQueueID() { + FetchEntireItem(); + return m_enqueueing_queue_id; +} + +uint32_t QueueItem::GetStopID() { + FetchEntireItem(); + return m_stop_id; +} + +std::vector<lldb::addr_t> &QueueItem::GetEnqueueingBacktrace() { + FetchEntireItem(); + return m_backtrace; +} + +std::string QueueItem::GetThreadLabel() { + FetchEntireItem(); + return m_thread_label; +} + +std::string QueueItem::GetQueueLabel() { + FetchEntireItem(); + return m_queue_label; +} + +ProcessSP QueueItem::GetProcessSP() { return m_process_wp.lock(); } + +void QueueItem::FetchEntireItem() { + if (m_have_fetched_entire_item) + return; + ProcessSP process_sp = m_process_wp.lock(); + if (process_sp) { + SystemRuntime *runtime = process_sp->GetSystemRuntime(); + if (runtime) { + runtime->CompleteQueueItem(this, m_item_ref); + m_have_fetched_entire_item = true; + } + } +} diff --git a/gnu/llvm/lldb/source/Target/QueueList.cpp b/gnu/llvm/lldb/source/Target/QueueList.cpp new file mode 100644 index 00000000000..79682513518 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/QueueList.cpp @@ -0,0 +1,69 @@ +//===-- QueueList.cpp -------------------------------------------*- C++ -*-===// +// +// 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/Queue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/QueueList.h" + +using namespace lldb; +using namespace lldb_private; + +QueueList::QueueList(Process *process) + : m_process(process), m_stop_id(0), m_queues(), m_mutex() {} + +QueueList::~QueueList() { Clear(); } + +uint32_t QueueList::GetSize() { + std::lock_guard<std::mutex> guard(m_mutex); + return m_queues.size(); +} + +lldb::QueueSP QueueList::GetQueueAtIndex(uint32_t idx) { + std::lock_guard<std::mutex> guard(m_mutex); + if (idx < m_queues.size()) { + return m_queues[idx]; + } else { + return QueueSP(); + } +} + +void QueueList::Clear() { + std::lock_guard<std::mutex> guard(m_mutex); + m_queues.clear(); +} + +void QueueList::AddQueue(QueueSP queue_sp) { + std::lock_guard<std::mutex> guard(m_mutex); + if (queue_sp.get()) { + m_queues.push_back(queue_sp); + } +} + +lldb::QueueSP QueueList::FindQueueByID(lldb::queue_id_t qid) { + QueueSP ret; + for (QueueSP queue_sp : Queues()) { + if (queue_sp->GetID() == qid) { + ret = queue_sp; + break; + } + } + return ret; +} + +lldb::QueueSP QueueList::FindQueueByIndexID(uint32_t index_id) { + QueueSP ret; + for (QueueSP queue_sp : Queues()) { + if (queue_sp->GetIndexID() == index_id) { + ret = queue_sp; + break; + } + } + return ret; +} + +std::mutex &QueueList::GetMutex() { return m_mutex; } diff --git a/gnu/llvm/lldb/source/Target/RegisterContext.cpp b/gnu/llvm/lldb/source/Target/RegisterContext.cpp new file mode 100644 index 00000000000..f29cf435d02 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/RegisterContext.cpp @@ -0,0 +1,447 @@ +//===-- RegisterContext.cpp -------------------------------------*- C++ -*-===// +// +// 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/RegisterContext.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContext::RegisterContext(Thread &thread, uint32_t concrete_frame_idx) + : m_thread(thread), m_concrete_frame_idx(concrete_frame_idx), + m_stop_id(thread.GetProcess()->GetStopID()) {} + +RegisterContext::~RegisterContext() = default; + +void RegisterContext::InvalidateIfNeeded(bool force) { + ProcessSP process_sp(m_thread.GetProcess()); + bool invalidate = force; + uint32_t process_stop_id = UINT32_MAX; + + if (process_sp) + process_stop_id = process_sp->GetStopID(); + else + invalidate = true; + + if (!invalidate) + invalidate = process_stop_id != GetStopID(); + + if (invalidate) { + InvalidateAllRegisters(); + SetStopID(process_stop_id); + } +} + +const RegisterInfo * +RegisterContext::GetRegisterInfoByName(llvm::StringRef reg_name, + uint32_t start_idx) { + if (reg_name.empty()) + return nullptr; + + const uint32_t num_registers = GetRegisterCount(); + for (uint32_t reg = start_idx; reg < num_registers; ++reg) { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); + + if (reg_name.equals_lower(reg_info->name) || + reg_name.equals_lower(reg_info->alt_name)) + return reg_info; + } + return nullptr; +} + +uint32_t +RegisterContext::UpdateDynamicRegisterSize(const lldb_private::ArchSpec &arch, + RegisterInfo *reg_info) { + ExecutionContext exe_ctx(CalculateThread()); + + // In MIPS, the floating point registers size is depends on FR bit of SR + // register. if SR.FR == 1 then all floating point registers are 64 bits. + // else they are all 32 bits. + + int expr_result; + uint32_t addr_size = arch.GetAddressByteSize(); + const uint8_t *dwarf_opcode_ptr = reg_info->dynamic_size_dwarf_expr_bytes; + const size_t dwarf_opcode_len = reg_info->dynamic_size_dwarf_len; + + DataExtractor dwarf_data(dwarf_opcode_ptr, dwarf_opcode_len, + arch.GetByteOrder(), addr_size); + ModuleSP opcode_ctx; + DWARFExpression dwarf_expr(opcode_ctx, dwarf_data, nullptr); + Value result; + Status error; + if (dwarf_expr.Evaluate(&exe_ctx, this, opcode_ctx, dwarf_data, nullptr, + eRegisterKindDWARF, nullptr, nullptr, result, + &error)) { + expr_result = result.GetScalar().SInt(-1); + switch (expr_result) { + case 0: + return 4; + case 1: + return 8; + default: + return reg_info->byte_size; + } + } else { + printf("Error executing DwarfExpression::Evaluate %s\n", error.AsCString()); + return reg_info->byte_size; + } +} + +const RegisterInfo *RegisterContext::GetRegisterInfo(lldb::RegisterKind kind, + uint32_t num) { + const uint32_t reg_num = ConvertRegisterKindToRegisterNumber(kind, num); + if (reg_num == LLDB_INVALID_REGNUM) + return nullptr; + return GetRegisterInfoAtIndex(reg_num); +} + +const char *RegisterContext::GetRegisterName(uint32_t reg) { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); + if (reg_info) + return reg_info->name; + return nullptr; +} + +uint64_t RegisterContext::GetPC(uint64_t fail_value) { + uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC); + uint64_t pc = ReadRegisterAsUnsigned(reg, fail_value); + + if (pc != fail_value) { + TargetSP target_sp = m_thread.CalculateTarget(); + if (target_sp) { + Target *target = target_sp.get(); + if (target) + pc = target->GetOpcodeLoadAddress(pc, AddressClass::eCode); + } + } + + return pc; +} + +bool RegisterContext::SetPC(uint64_t pc) { + uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC); + bool success = WriteRegisterFromUnsigned(reg, pc); + if (success) { + StackFrameSP frame_sp( + m_thread.GetFrameWithConcreteFrameIndex(m_concrete_frame_idx)); + if (frame_sp) + frame_sp->ChangePC(pc); + else + m_thread.ClearStackFrames(); + } + return success; +} + +bool RegisterContext::SetPC(Address addr) { + TargetSP target_sp = m_thread.CalculateTarget(); + Target *target = target_sp.get(); + + lldb::addr_t callAddr = addr.GetCallableLoadAddress(target); + if (callAddr == LLDB_INVALID_ADDRESS) + return false; + + return SetPC(callAddr); +} + +uint64_t RegisterContext::GetSP(uint64_t fail_value) { + uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP); + return ReadRegisterAsUnsigned(reg, fail_value); +} + +bool RegisterContext::SetSP(uint64_t sp) { + uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP); + return WriteRegisterFromUnsigned(reg, sp); +} + +uint64_t RegisterContext::GetFP(uint64_t fail_value) { + uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_FP); + return ReadRegisterAsUnsigned(reg, fail_value); +} + +bool RegisterContext::SetFP(uint64_t fp) { + uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_FP); + return WriteRegisterFromUnsigned(reg, fp); +} + +uint64_t RegisterContext::GetReturnAddress(uint64_t fail_value) { + uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_RA); + return ReadRegisterAsUnsigned(reg, fail_value); +} + +uint64_t RegisterContext::GetFlags(uint64_t fail_value) { + uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_FLAGS); + return ReadRegisterAsUnsigned(reg, fail_value); +} + +uint64_t RegisterContext::ReadRegisterAsUnsigned(uint32_t reg, + uint64_t fail_value) { + if (reg != LLDB_INVALID_REGNUM) + return ReadRegisterAsUnsigned(GetRegisterInfoAtIndex(reg), fail_value); + return fail_value; +} + +uint64_t RegisterContext::ReadRegisterAsUnsigned(const RegisterInfo *reg_info, + uint64_t fail_value) { + if (reg_info) { + RegisterValue value; + if (ReadRegister(reg_info, value)) + return value.GetAsUInt64(); + } + return fail_value; +} + +bool RegisterContext::WriteRegisterFromUnsigned(uint32_t reg, uint64_t uval) { + if (reg == LLDB_INVALID_REGNUM) + return false; + return WriteRegisterFromUnsigned(GetRegisterInfoAtIndex(reg), uval); +} + +bool RegisterContext::WriteRegisterFromUnsigned(const RegisterInfo *reg_info, + uint64_t uval) { + if (reg_info) { + RegisterValue value; + if (value.SetUInt(uval, reg_info->byte_size)) + return WriteRegister(reg_info, value); + } + return false; +} + +bool RegisterContext::CopyFromRegisterContext(lldb::RegisterContextSP context) { + uint32_t num_register_sets = context->GetRegisterSetCount(); + // We don't know that two threads have the same register context, so require + // the threads to be the same. + if (context->GetThreadID() != GetThreadID()) + return false; + + if (num_register_sets != GetRegisterSetCount()) + return false; + + RegisterContextSP frame_zero_context = m_thread.GetRegisterContext(); + + for (uint32_t set_idx = 0; set_idx < num_register_sets; ++set_idx) { + const RegisterSet *const reg_set = GetRegisterSet(set_idx); + + const uint32_t num_registers = reg_set->num_registers; + for (uint32_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) { + const uint32_t reg = reg_set->registers[reg_idx]; + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); + if (!reg_info || reg_info->value_regs) + continue; + RegisterValue reg_value; + + // If we can reconstruct the register from the frame we are copying from, + // then do so, otherwise use the value from frame 0. + if (context->ReadRegister(reg_info, reg_value)) { + WriteRegister(reg_info, reg_value); + } else if (frame_zero_context->ReadRegister(reg_info, reg_value)) { + WriteRegister(reg_info, reg_value); + } + } + } + return true; +} + +lldb::tid_t RegisterContext::GetThreadID() const { return m_thread.GetID(); } + +uint32_t RegisterContext::NumSupportedHardwareBreakpoints() { return 0; } + +uint32_t RegisterContext::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + return LLDB_INVALID_INDEX32; +} + +bool RegisterContext::ClearHardwareBreakpoint(uint32_t hw_idx) { return false; } + +uint32_t RegisterContext::NumSupportedHardwareWatchpoints() { return 0; } + +uint32_t RegisterContext::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + bool read, bool write) { + return LLDB_INVALID_INDEX32; +} + +bool RegisterContext::ClearHardwareWatchpoint(uint32_t hw_index) { + return false; +} + +bool RegisterContext::HardwareSingleStep(bool enable) { return false; } + +Status RegisterContext::ReadRegisterValueFromMemory( + const RegisterInfo *reg_info, lldb::addr_t src_addr, uint32_t src_len, + RegisterValue ®_value) { + Status error; + if (reg_info == nullptr) { + error.SetErrorString("invalid register info argument."); + return error; + } + + // Moving from addr into a register + // + // Case 1: src_len == dst_len + // + // |AABBCCDD| Address contents + // |AABBCCDD| Register contents + // + // Case 2: src_len > dst_len + // + // Status! (The register should always be big enough to hold the data) + // + // Case 3: src_len < dst_len + // + // |AABB| Address contents + // |AABB0000| Register contents [on little-endian hardware] + // |0000AABB| Register contents [on big-endian hardware] + if (src_len > RegisterValue::kMaxRegisterByteSize) { + error.SetErrorString("register too small to receive memory data"); + return error; + } + + const uint32_t dst_len = reg_info->byte_size; + + if (src_len > dst_len) { + error.SetErrorStringWithFormat( + "%u bytes is too big to store in register %s (%u bytes)", src_len, + reg_info->name, dst_len); + return error; + } + + ProcessSP process_sp(m_thread.GetProcess()); + if (process_sp) { + uint8_t src[RegisterValue::kMaxRegisterByteSize]; + + // Read the memory + const uint32_t bytes_read = + process_sp->ReadMemory(src_addr, src, src_len, error); + + // Make sure the memory read succeeded... + if (bytes_read != src_len) { + if (error.Success()) { + // This might happen if we read _some_ bytes but not all + error.SetErrorStringWithFormat("read %u of %u bytes", bytes_read, + src_len); + } + return error; + } + + // We now have a memory buffer that contains the part or all of the + // register value. Set the register value using this memory data. + // TODO: we might need to add a parameter to this function in case the byte + // order of the memory data doesn't match the process. For now we are + // assuming they are the same. + reg_value.SetFromMemoryData(reg_info, src, src_len, + process_sp->GetByteOrder(), error); + } else + error.SetErrorString("invalid process"); + + return error; +} + +Status RegisterContext::WriteRegisterValueToMemory( + const RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len, + const RegisterValue ®_value) { + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + + Status error; + + ProcessSP process_sp(m_thread.GetProcess()); + if (process_sp) { + + // TODO: we might need to add a parameter to this function in case the byte + // order of the memory data doesn't match the process. For now we are + // assuming they are the same. + + const uint32_t bytes_copied = reg_value.GetAsMemoryData( + reg_info, dst, dst_len, process_sp->GetByteOrder(), error); + + if (error.Success()) { + if (bytes_copied == 0) { + error.SetErrorString("byte copy failed."); + } else { + const uint32_t bytes_written = + process_sp->WriteMemory(dst_addr, dst, bytes_copied, error); + if (bytes_written != bytes_copied) { + if (error.Success()) { + // This might happen if we read _some_ bytes but not all + error.SetErrorStringWithFormat("only wrote %u of %u bytes", + bytes_written, bytes_copied); + } + } + } + } + } else + error.SetErrorString("invalid process"); + + return error; +} + +bool RegisterContext::ReadAllRegisterValues( + lldb_private::RegisterCheckpoint ®_checkpoint) { + return ReadAllRegisterValues(reg_checkpoint.GetData()); +} + +bool RegisterContext::WriteAllRegisterValues( + const lldb_private::RegisterCheckpoint ®_checkpoint) { + return WriteAllRegisterValues(reg_checkpoint.GetData()); +} + +TargetSP RegisterContext::CalculateTarget() { + return m_thread.CalculateTarget(); +} + +ProcessSP RegisterContext::CalculateProcess() { + return m_thread.CalculateProcess(); +} + +ThreadSP RegisterContext::CalculateThread() { + return m_thread.shared_from_this(); +} + +StackFrameSP RegisterContext::CalculateStackFrame() { + // Register contexts might belong to many frames if we have inlined functions + // inside a frame since all inlined functions share the same registers, so we + // can't definitively say which frame we come from... + return StackFrameSP(); +} + +void RegisterContext::CalculateExecutionContext(ExecutionContext &exe_ctx) { + m_thread.CalculateExecutionContext(exe_ctx); +} + +bool RegisterContext::ConvertBetweenRegisterKinds(lldb::RegisterKind source_rk, + uint32_t source_regnum, + lldb::RegisterKind target_rk, + uint32_t &target_regnum) { + const uint32_t num_registers = GetRegisterCount(); + for (uint32_t reg = 0; reg < num_registers; ++reg) { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); + + if (reg_info->kinds[source_rk] == source_regnum) { + target_regnum = reg_info->kinds[target_rk]; + return (target_regnum != LLDB_INVALID_REGNUM); + } + } + return false; +} diff --git a/gnu/llvm/lldb/source/Target/RegisterNumber.cpp b/gnu/llvm/lldb/source/Target/RegisterNumber.cpp new file mode 100644 index 00000000000..63b58d3582f --- /dev/null +++ b/gnu/llvm/lldb/source/Target/RegisterNumber.cpp @@ -0,0 +1,107 @@ +//===--------------------- RegisterNumber.cpp -------------------*- C++ -*-===// +// +// 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/RegisterNumber.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +RegisterNumber::RegisterNumber(lldb_private::Thread &thread, + lldb::RegisterKind kind, uint32_t num) + : m_reg_ctx_sp(thread.GetRegisterContext()), m_regnum(num), m_kind(kind), + m_kind_regnum_map(), m_name("") { + if (m_reg_ctx_sp.get()) { + const lldb_private::RegisterInfo *reginfo = + m_reg_ctx_sp->GetRegisterInfoAtIndex( + GetAsKind(lldb::eRegisterKindLLDB)); + if (reginfo && reginfo->name) { + m_name = reginfo->name; + } + } +} + +RegisterNumber::RegisterNumber() + : m_reg_ctx_sp(), m_regnum(LLDB_INVALID_REGNUM), + m_kind(lldb::kNumRegisterKinds), m_kind_regnum_map(), m_name(nullptr) {} + +void RegisterNumber::init(lldb_private::Thread &thread, lldb::RegisterKind kind, + uint32_t num) { + m_reg_ctx_sp = thread.GetRegisterContext(); + m_regnum = num; + m_kind = kind; + if (m_reg_ctx_sp.get()) { + const lldb_private::RegisterInfo *reginfo = + m_reg_ctx_sp->GetRegisterInfoAtIndex( + GetAsKind(lldb::eRegisterKindLLDB)); + if (reginfo && reginfo->name) { + m_name = reginfo->name; + } + } +} + +const RegisterNumber &RegisterNumber::operator=(const RegisterNumber &rhs) { + m_reg_ctx_sp = rhs.m_reg_ctx_sp; + m_regnum = rhs.m_regnum; + m_kind = rhs.m_kind; + for (auto it : rhs.m_kind_regnum_map) + m_kind_regnum_map[it.first] = it.second; + m_name = rhs.m_name; + return *this; +} + +bool RegisterNumber::operator==(RegisterNumber &rhs) { + if (IsValid() != rhs.IsValid()) + return false; + + if (m_kind == rhs.m_kind) { + return m_regnum == rhs.m_regnum; + } + + uint32_t rhs_regnum = rhs.GetAsKind(m_kind); + if (rhs_regnum != LLDB_INVALID_REGNUM) { + return m_regnum == rhs_regnum; + } + uint32_t lhs_regnum = GetAsKind(rhs.m_kind); + { return lhs_regnum == rhs.m_regnum; } + return false; +} + +bool RegisterNumber::operator!=(RegisterNumber &rhs) { return !(*this == rhs); } + +bool RegisterNumber::IsValid() const { + return m_reg_ctx_sp.get() && m_kind != lldb::kNumRegisterKinds && + m_regnum != LLDB_INVALID_REGNUM; +} + +uint32_t RegisterNumber::GetAsKind(lldb::RegisterKind kind) { + if (m_regnum == LLDB_INVALID_REGNUM) + return LLDB_INVALID_REGNUM; + + if (kind == m_kind) + return m_regnum; + + Collection::iterator iter = m_kind_regnum_map.find(kind); + if (iter != m_kind_regnum_map.end()) { + return iter->second; + } + uint32_t output_regnum = LLDB_INVALID_REGNUM; + if (m_reg_ctx_sp && + m_reg_ctx_sp->ConvertBetweenRegisterKinds(m_kind, m_regnum, kind, + output_regnum) && + output_regnum != LLDB_INVALID_REGNUM) { + m_kind_regnum_map[kind] = output_regnum; + } + return output_regnum; +} + +uint32_t RegisterNumber::GetRegisterNumber() const { return m_regnum; } + +lldb::RegisterKind RegisterNumber::GetRegisterKind() const { return m_kind; } + +const char *RegisterNumber::GetName() { return m_name; } diff --git a/gnu/llvm/lldb/source/Target/RemoteAwarePlatform.cpp b/gnu/llvm/lldb/source/Target/RemoteAwarePlatform.cpp new file mode 100644 index 00000000000..faa217ac83e --- /dev/null +++ b/gnu/llvm/lldb/source/Target/RemoteAwarePlatform.cpp @@ -0,0 +1,284 @@ +//===-- RemoteAwarePlatform.cpp ---------------------------------*- C++ -*-===// +// +// 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/RemoteAwarePlatform.h" +#include "lldb/Host/FileCache.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" + +using namespace lldb_private; + +bool RemoteAwarePlatform::GetModuleSpec(const FileSpec &module_file_spec, + const ArchSpec &arch, + ModuleSpec &module_spec) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetModuleSpec(module_file_spec, arch, + module_spec); + + return Platform::GetModuleSpec(module_file_spec, arch, module_spec); +} + +Status RemoteAwarePlatform::RunShellCommand( + const char *command, const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output, + const Timeout<std::micro> &timeout) { + if (IsHost()) + return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, + command_output, timeout); + if (m_remote_platform_sp) + return m_remote_platform_sp->RunShellCommand( + command, working_dir, status_ptr, signo_ptr, command_output, timeout); + return Status("unable to run a remote command without a platform"); +} + +Status RemoteAwarePlatform::MakeDirectory(const FileSpec &file_spec, + uint32_t file_permissions) { + if (m_remote_platform_sp) + return m_remote_platform_sp->MakeDirectory(file_spec, file_permissions); + return Platform::MakeDirectory(file_spec, file_permissions); +} + +Status RemoteAwarePlatform::GetFilePermissions(const FileSpec &file_spec, + uint32_t &file_permissions) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFilePermissions(file_spec, + file_permissions); + return Platform::GetFilePermissions(file_spec, file_permissions); +} + +Status RemoteAwarePlatform::SetFilePermissions(const FileSpec &file_spec, + uint32_t file_permissions) { + if (m_remote_platform_sp) + return m_remote_platform_sp->SetFilePermissions(file_spec, + file_permissions); + return Platform::SetFilePermissions(file_spec, file_permissions); +} + +lldb::user_id_t RemoteAwarePlatform::OpenFile(const FileSpec &file_spec, + File::OpenOptions flags, + uint32_t mode, Status &error) { + if (IsHost()) + return FileCache::GetInstance().OpenFile(file_spec, flags, mode, error); + if (m_remote_platform_sp) + return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error); + return Platform::OpenFile(file_spec, flags, mode, error); +} + +bool RemoteAwarePlatform::CloseFile(lldb::user_id_t fd, Status &error) { + if (IsHost()) + return FileCache::GetInstance().CloseFile(fd, error); + if (m_remote_platform_sp) + return m_remote_platform_sp->CloseFile(fd, error); + return Platform::CloseFile(fd, error); +} + +uint64_t RemoteAwarePlatform::ReadFile(lldb::user_id_t fd, uint64_t offset, + void *dst, uint64_t dst_len, + Status &error) { + if (IsHost()) + return FileCache::GetInstance().ReadFile(fd, offset, dst, dst_len, error); + if (m_remote_platform_sp) + return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error); + return Platform::ReadFile(fd, offset, dst, dst_len, error); +} + +uint64_t RemoteAwarePlatform::WriteFile(lldb::user_id_t fd, uint64_t offset, + const void *src, uint64_t src_len, + Status &error) { + if (IsHost()) + return FileCache::GetInstance().WriteFile(fd, offset, src, src_len, error); + if (m_remote_platform_sp) + return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error); + return Platform::WriteFile(fd, offset, src, src_len, error); +} + +lldb::user_id_t RemoteAwarePlatform::GetFileSize(const FileSpec &file_spec) { + if (IsHost()) { + uint64_t Size; + if (llvm::sys::fs::file_size(file_spec.GetPath(), Size)) + return 0; + return Size; + } + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFileSize(file_spec); + return Platform::GetFileSize(file_spec); +} + +Status RemoteAwarePlatform::CreateSymlink(const FileSpec &src, + const FileSpec &dst) { + if (IsHost()) + return FileSystem::Instance().Symlink(src, dst); + if (m_remote_platform_sp) + return m_remote_platform_sp->CreateSymlink(src, dst); + return Platform::CreateSymlink(src, dst); +} + +bool RemoteAwarePlatform::GetFileExists(const FileSpec &file_spec) { + if (IsHost()) + return FileSystem::Instance().Exists(file_spec); + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFileExists(file_spec); + return Platform::GetFileExists(file_spec); +} + +Status RemoteAwarePlatform::Unlink(const FileSpec &file_spec) { + if (IsHost()) + return llvm::sys::fs::remove(file_spec.GetPath()); + if (m_remote_platform_sp) + return m_remote_platform_sp->Unlink(file_spec); + return Platform::Unlink(file_spec); +} + +bool RemoteAwarePlatform::CalculateMD5(const FileSpec &file_spec, uint64_t &low, + uint64_t &high) { + if (IsHost()) + return Platform::CalculateMD5(file_spec, low, high); + if (m_remote_platform_sp) + return m_remote_platform_sp->CalculateMD5(file_spec, low, high); + return false; +} + +FileSpec RemoteAwarePlatform::GetRemoteWorkingDirectory() { + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteWorkingDirectory(); + return Platform::GetRemoteWorkingDirectory(); +} + +bool RemoteAwarePlatform::SetRemoteWorkingDirectory( + const FileSpec &working_dir) { + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->SetRemoteWorkingDirectory(working_dir); + return Platform::SetRemoteWorkingDirectory(working_dir); +} + +Status RemoteAwarePlatform::GetFileWithUUID(const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) { + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetFileWithUUID(platform_file, uuid_ptr, + local_file); + + // Default to the local case + local_file = platform_file; + return Status(); +} + +bool RemoteAwarePlatform::GetRemoteOSVersion() { + if (m_remote_platform_sp) { + m_os_version = m_remote_platform_sp->GetOSVersion(); + return !m_os_version.empty(); + } + return false; +} + +bool RemoteAwarePlatform::GetRemoteOSBuildString(std::string &s) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteOSBuildString(s); + s.clear(); + return false; +} + +bool RemoteAwarePlatform::GetRemoteOSKernelDescription(std::string &s) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteOSKernelDescription(s); + s.clear(); + return false; +} + +ArchSpec RemoteAwarePlatform::GetRemoteSystemArchitecture() { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteSystemArchitecture(); + return ArchSpec(); +} + +const char *RemoteAwarePlatform::GetHostname() { + if (IsHost()) + return Platform::GetHostname(); + if (m_remote_platform_sp) + return m_remote_platform_sp->GetHostname(); + return nullptr; +} + +UserIDResolver &RemoteAwarePlatform::GetUserIDResolver() { + if (IsHost()) + return HostInfo::GetUserIDResolver(); + if (m_remote_platform_sp) + return m_remote_platform_sp->GetUserIDResolver(); + return UserIDResolver::GetNoopResolver(); +} + +Environment RemoteAwarePlatform::GetEnvironment() { + if (IsRemote()) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetEnvironment(); + return Environment(); + } + return Host::GetEnvironment(); +} + +bool RemoteAwarePlatform::IsConnected() const { + if (IsHost()) + return true; + else if (m_remote_platform_sp) + return m_remote_platform_sp->IsConnected(); + return false; +} + +bool RemoteAwarePlatform::GetProcessInfo(lldb::pid_t pid, + ProcessInstanceInfo &process_info) { + if (IsHost()) + return Platform::GetProcessInfo(pid, process_info); + if (m_remote_platform_sp) + return m_remote_platform_sp->GetProcessInfo(pid, process_info); + return false; +} + +uint32_t +RemoteAwarePlatform::FindProcesses(const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) { + if (IsHost()) + return Platform::FindProcesses(match_info, process_infos); + if (m_remote_platform_sp) + return m_remote_platform_sp->FindProcesses(match_info, process_infos); + return 0; +} + +lldb::ProcessSP RemoteAwarePlatform::ConnectProcess(llvm::StringRef connect_url, + llvm::StringRef plugin_name, + Debugger &debugger, + Target *target, + Status &error) { + if (m_remote_platform_sp) + return m_remote_platform_sp->ConnectProcess(connect_url, plugin_name, + debugger, target, error); + return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, + error); +} + +Status RemoteAwarePlatform::LaunchProcess(ProcessLaunchInfo &launch_info) { + Status error; + + if (IsHost()) { + error = Platform::LaunchProcess(launch_info); + } else { + if (m_remote_platform_sp) + error = m_remote_platform_sp->LaunchProcess(launch_info); + else + error.SetErrorString("the platform is not currently connected"); + } + return error; +} + +Status RemoteAwarePlatform::KillProcess(const lldb::pid_t pid) { + if (IsHost()) + return Platform::KillProcess(pid); + if (m_remote_platform_sp) + return m_remote_platform_sp->KillProcess(pid); + return Status("the platform is not currently connected"); +} diff --git a/gnu/llvm/lldb/source/Target/SectionLoadHistory.cpp b/gnu/llvm/lldb/source/Target/SectionLoadHistory.cpp new file mode 100644 index 00000000000..ec16b58b445 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/SectionLoadHistory.cpp @@ -0,0 +1,164 @@ +//===-- SectionLoadHistory.cpp ----------------------------------*- C++ -*-===// +// +// 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/SectionLoadHistory.h" + +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +bool SectionLoadHistory::IsEmpty() const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + return m_stop_id_to_section_load_list.empty(); +} + +void SectionLoadHistory::Clear() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_stop_id_to_section_load_list.clear(); +} + +uint32_t SectionLoadHistory::GetLastStopID() const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (m_stop_id_to_section_load_list.empty()) + return 0; + else + return m_stop_id_to_section_load_list.rbegin()->first; +} + +SectionLoadList * +SectionLoadHistory::GetSectionLoadListForStopID(uint32_t stop_id, + bool read_only) { + if (!m_stop_id_to_section_load_list.empty()) { + if (read_only) { + // The section load list is for reading data only so we don't need to + // create a new SectionLoadList for the current stop ID, just return the + // section load list for the stop ID that is equal to or less than the + // current stop ID + if (stop_id == eStopIDNow) { + // If we are asking for the latest and greatest value, it is always at + // the end of our list because that will be the highest stop ID. + StopIDToSectionLoadList::reverse_iterator rpos = + m_stop_id_to_section_load_list.rbegin(); + return rpos->second.get(); + } else { + StopIDToSectionLoadList::iterator pos = + m_stop_id_to_section_load_list.lower_bound(stop_id); + if (pos != m_stop_id_to_section_load_list.end() && + pos->first == stop_id) + return pos->second.get(); + else if (pos != m_stop_id_to_section_load_list.begin()) { + --pos; + return pos->second.get(); + } + } + } else { + // You can only use "eStopIDNow" when reading from the section load + // history + assert(stop_id != eStopIDNow); + + // We are updating the section load list (not read only), so if the stop + // ID passed in isn't the same as the last stop ID in our collection, + // then create a new node using the current stop ID + StopIDToSectionLoadList::iterator pos = + m_stop_id_to_section_load_list.lower_bound(stop_id); + if (pos != m_stop_id_to_section_load_list.end() && + pos->first == stop_id) { + // We already have an entry for this value + return pos->second.get(); + } + + // We must make a new section load list that is based on the last valid + // section load list, so here we copy the last section load list and add + // a new node for the current stop ID. + StopIDToSectionLoadList::reverse_iterator rpos = + m_stop_id_to_section_load_list.rbegin(); + SectionLoadListSP section_load_list_sp( + new SectionLoadList(*rpos->second)); + m_stop_id_to_section_load_list[stop_id] = section_load_list_sp; + return section_load_list_sp.get(); + } + } + SectionLoadListSP section_load_list_sp(new SectionLoadList()); + if (stop_id == eStopIDNow) + stop_id = 0; + m_stop_id_to_section_load_list[stop_id] = section_load_list_sp; + return section_load_list_sp.get(); +} + +SectionLoadList &SectionLoadHistory::GetCurrentSectionLoadList() { + const bool read_only = true; + std::lock_guard<std::recursive_mutex> guard(m_mutex); + SectionLoadList *section_load_list = + GetSectionLoadListForStopID(eStopIDNow, read_only); + assert(section_load_list != nullptr); + return *section_load_list; +} + +addr_t +SectionLoadHistory::GetSectionLoadAddress(uint32_t stop_id, + const lldb::SectionSP §ion_sp) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + const bool read_only = true; + SectionLoadList *section_load_list = + GetSectionLoadListForStopID(stop_id, read_only); + return section_load_list->GetSectionLoadAddress(section_sp); +} + +bool SectionLoadHistory::ResolveLoadAddress(uint32_t stop_id, addr_t load_addr, + Address &so_addr) { + // First find the top level section that this load address exists in + std::lock_guard<std::recursive_mutex> guard(m_mutex); + const bool read_only = true; + SectionLoadList *section_load_list = + GetSectionLoadListForStopID(stop_id, read_only); + return section_load_list->ResolveLoadAddress(load_addr, so_addr); +} + +bool SectionLoadHistory::SetSectionLoadAddress( + uint32_t stop_id, const lldb::SectionSP §ion_sp, addr_t load_addr, + bool warn_multiple) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + const bool read_only = false; + SectionLoadList *section_load_list = + GetSectionLoadListForStopID(stop_id, read_only); + return section_load_list->SetSectionLoadAddress(section_sp, load_addr, + warn_multiple); +} + +size_t +SectionLoadHistory::SetSectionUnloaded(uint32_t stop_id, + const lldb::SectionSP §ion_sp) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + const bool read_only = false; + SectionLoadList *section_load_list = + GetSectionLoadListForStopID(stop_id, read_only); + return section_load_list->SetSectionUnloaded(section_sp); +} + +bool SectionLoadHistory::SetSectionUnloaded(uint32_t stop_id, + const lldb::SectionSP §ion_sp, + addr_t load_addr) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + const bool read_only = false; + SectionLoadList *section_load_list = + GetSectionLoadListForStopID(stop_id, read_only); + return section_load_list->SetSectionUnloaded(section_sp, load_addr); +} + +void SectionLoadHistory::Dump(Stream &s, Target *target) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + StopIDToSectionLoadList::iterator pos, + end = m_stop_id_to_section_load_list.end(); + for (pos = m_stop_id_to_section_load_list.begin(); pos != end; ++pos) { + s.Printf("StopID = %u:\n", pos->first); + pos->second->Dump(s, target); + s.EOL(); + } +} diff --git a/gnu/llvm/lldb/source/Target/SectionLoadList.cpp b/gnu/llvm/lldb/source/Target/SectionLoadList.cpp new file mode 100644 index 00000000000..f2546a89319 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/SectionLoadList.cpp @@ -0,0 +1,258 @@ +//===-- SectionLoadList.cpp -------------------------------------*- C++ -*-===// +// +// 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/SectionLoadList.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +SectionLoadList::SectionLoadList(const SectionLoadList &rhs) + : m_addr_to_sect(), m_sect_to_addr(), m_mutex() { + std::lock_guard<std::recursive_mutex> guard(rhs.m_mutex); + m_addr_to_sect = rhs.m_addr_to_sect; + m_sect_to_addr = rhs.m_sect_to_addr; +} + +void SectionLoadList::operator=(const SectionLoadList &rhs) { + std::lock(m_mutex, rhs.m_mutex); + std::lock_guard<std::recursive_mutex> lhs_guard(m_mutex, std::adopt_lock); + std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_mutex, std::adopt_lock); + m_addr_to_sect = rhs.m_addr_to_sect; + m_sect_to_addr = rhs.m_sect_to_addr; +} + +bool SectionLoadList::IsEmpty() const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + return m_addr_to_sect.empty(); +} + +void SectionLoadList::Clear() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_addr_to_sect.clear(); + m_sect_to_addr.clear(); +} + +addr_t +SectionLoadList::GetSectionLoadAddress(const lldb::SectionSP §ion) const { + // TODO: add support for the same section having multiple load addresses + addr_t section_load_addr = LLDB_INVALID_ADDRESS; + if (section) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + sect_to_addr_collection::const_iterator pos = + m_sect_to_addr.find(section.get()); + + if (pos != m_sect_to_addr.end()) + section_load_addr = pos->second; + } + return section_load_addr; +} + +bool SectionLoadList::SetSectionLoadAddress(const lldb::SectionSP §ion, + addr_t load_addr, + bool warn_multiple) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + ModuleSP module_sp(section->GetModule()); + + if (module_sp) { + LLDB_LOGV(log, "(section = {0} ({1}.{2}), load_addr = {3:x}) module = {4}", + section.get(), module_sp->GetFileSpec(), section->GetName(), + load_addr, module_sp.get()); + + if (section->GetByteSize() == 0) + return false; // No change + + // Fill in the section -> load_addr map + std::lock_guard<std::recursive_mutex> guard(m_mutex); + sect_to_addr_collection::iterator sta_pos = + m_sect_to_addr.find(section.get()); + if (sta_pos != m_sect_to_addr.end()) { + if (load_addr == sta_pos->second) + return false; // No change... + else + sta_pos->second = load_addr; + } else + m_sect_to_addr[section.get()] = load_addr; + + // Fill in the load_addr -> section map + addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr); + if (ats_pos != m_addr_to_sect.end()) { + // Some sections are ok to overlap, and for others we should warn. When + // we have multiple load addresses that correspond to a section, we will + // always attribute the section to the be last section that claims it + // exists at that address. Sometimes it is ok for more that one section + // to be loaded at a specific load address, and other times it isn't. The + // "warn_multiple" parameter tells us if we should warn in this case or + // not. The DynamicLoader plug-in subclasses should know which sections + // should warn and which shouldn't (darwin shared cache modules all + // shared the same "__LINKEDIT" sections, so the dynamic loader can pass + // false for "warn_multiple"). + if (warn_multiple && section != ats_pos->second) { + ModuleSP module_sp(section->GetModule()); + if (module_sp) { + ModuleSP curr_module_sp(ats_pos->second->GetModule()); + if (curr_module_sp) { + module_sp->ReportWarning( + "address 0x%16.16" PRIx64 + " maps to more than one section: %s.%s and %s.%s", + load_addr, module_sp->GetFileSpec().GetFilename().GetCString(), + section->GetName().GetCString(), + curr_module_sp->GetFileSpec().GetFilename().GetCString(), + ats_pos->second->GetName().GetCString()); + } + } + } + ats_pos->second = section; + } else + m_addr_to_sect[load_addr] = section; + return true; // Changed + + } else { + if (log) { + LLDB_LOGF( + log, + "SectionLoadList::%s (section = %p (%s), load_addr = 0x%16.16" PRIx64 + ") error: module has been deleted", + __FUNCTION__, static_cast<void *>(section.get()), + section->GetName().AsCString(), load_addr); + } + } + return false; +} + +size_t SectionLoadList::SetSectionUnloaded(const lldb::SectionSP §ion_sp) { + size_t unload_count = 0; + + if (section_sp) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + + if (log && log->GetVerbose()) { + ModuleSP module_sp = section_sp->GetModule(); + std::string module_name("<Unknown>"); + if (module_sp) { + const FileSpec &module_file_spec( + section_sp->GetModule()->GetFileSpec()); + module_name = module_file_spec.GetPath(); + } + LLDB_LOGF(log, "SectionLoadList::%s (section = %p (%s.%s))", __FUNCTION__, + static_cast<void *>(section_sp.get()), module_name.c_str(), + section_sp->GetName().AsCString()); + } + + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + sect_to_addr_collection::iterator sta_pos = + m_sect_to_addr.find(section_sp.get()); + if (sta_pos != m_sect_to_addr.end()) { + ++unload_count; + addr_t load_addr = sta_pos->second; + m_sect_to_addr.erase(sta_pos); + + addr_to_sect_collection::iterator ats_pos = + m_addr_to_sect.find(load_addr); + if (ats_pos != m_addr_to_sect.end()) + m_addr_to_sect.erase(ats_pos); + } + } + return unload_count; +} + +bool SectionLoadList::SetSectionUnloaded(const lldb::SectionSP §ion_sp, + addr_t load_addr) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + + if (log && log->GetVerbose()) { + ModuleSP module_sp = section_sp->GetModule(); + std::string module_name("<Unknown>"); + if (module_sp) { + const FileSpec &module_file_spec(section_sp->GetModule()->GetFileSpec()); + module_name = module_file_spec.GetPath(); + } + LLDB_LOGF( + log, + "SectionLoadList::%s (section = %p (%s.%s), load_addr = 0x%16.16" PRIx64 + ")", + __FUNCTION__, static_cast<void *>(section_sp.get()), + module_name.c_str(), section_sp->GetName().AsCString(), load_addr); + } + bool erased = false; + std::lock_guard<std::recursive_mutex> guard(m_mutex); + sect_to_addr_collection::iterator sta_pos = + m_sect_to_addr.find(section_sp.get()); + if (sta_pos != m_sect_to_addr.end()) { + erased = true; + m_sect_to_addr.erase(sta_pos); + } + + addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr); + if (ats_pos != m_addr_to_sect.end()) { + erased = true; + m_addr_to_sect.erase(ats_pos); + } + + return erased; +} + +bool SectionLoadList::ResolveLoadAddress(addr_t load_addr, Address &so_addr, + bool allow_section_end) const { + // First find the top level section that this load address exists in + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (!m_addr_to_sect.empty()) { + addr_to_sect_collection::const_iterator pos = + m_addr_to_sect.lower_bound(load_addr); + if (pos != m_addr_to_sect.end()) { + if (load_addr != pos->first && pos != m_addr_to_sect.begin()) + --pos; + const addr_t pos_load_addr = pos->first; + if (load_addr >= pos_load_addr) { + addr_t offset = load_addr - pos_load_addr; + if (offset < pos->second->GetByteSize() + (allow_section_end ? 1 : 0)) { + // We have found the top level section, now we need to find the + // deepest child section. + return pos->second->ResolveContainedAddress(offset, so_addr, + allow_section_end); + } + } + } else { + // There are no entries that have an address that is >= load_addr, so we + // need to check the last entry on our collection. + addr_to_sect_collection::const_reverse_iterator rpos = + m_addr_to_sect.rbegin(); + if (load_addr >= rpos->first) { + addr_t offset = load_addr - rpos->first; + if (offset < + rpos->second->GetByteSize() + (allow_section_end ? 1 : 0)) { + // We have found the top level section, now we need to find the + // deepest child section. + return rpos->second->ResolveContainedAddress(offset, so_addr, + allow_section_end); + } + } + } + } + so_addr.Clear(); + return false; +} + +void SectionLoadList::Dump(Stream &s, Target *target) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + addr_to_sect_collection::const_iterator pos, end; + for (pos = m_addr_to_sect.begin(), end = m_addr_to_sect.end(); pos != end; + ++pos) { + s.Printf("addr = 0x%16.16" PRIx64 ", section = %p: ", pos->first, + static_cast<void *>(pos->second.get())); + pos->second->Dump(&s, target, 0); + } +} diff --git a/gnu/llvm/lldb/source/Target/StackFrame.cpp b/gnu/llvm/lldb/source/Target/StackFrame.cpp new file mode 100644 index 00000000000..5c6ea7a0393 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/StackFrame.cpp @@ -0,0 +1,1971 @@ +//===-- StackFrame.cpp ------------------------------------------*- C++ -*-===// +// +// 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/StackFrame.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/FormatEntity.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContextScope.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrameRecognizer.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" + +#include "lldb/lldb-enumerations.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +// The first bits in the flags are reserved for the SymbolContext::Scope bits +// so we know if we have tried to look up information in our internal symbol +// context (m_sc) already. +#define RESOLVED_FRAME_CODE_ADDR (uint32_t(eSymbolContextEverything + 1)) +#define RESOLVED_FRAME_ID_SYMBOL_SCOPE (RESOLVED_FRAME_CODE_ADDR << 1) +#define GOT_FRAME_BASE (RESOLVED_FRAME_ID_SYMBOL_SCOPE << 1) +#define RESOLVED_VARIABLES (GOT_FRAME_BASE << 1) +#define RESOLVED_GLOBAL_VARIABLES (RESOLVED_VARIABLES << 1) + +StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, + user_id_t unwind_frame_index, addr_t cfa, + bool cfa_is_valid, addr_t pc, StackFrame::Kind kind, + bool behaves_like_zeroth_frame, + const SymbolContext *sc_ptr) + : m_thread_wp(thread_sp), m_frame_index(frame_idx), + m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(), + m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(), + m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid), + m_stack_frame_kind(kind), + m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), + m_variable_list_sp(), m_variable_list_value_objects(), + m_recognized_frame_sp(), m_disassembly(), m_mutex() { + // If we don't have a CFA value, use the frame index for our StackID so that + // recursive functions properly aren't confused with one another on a history + // stack. + if (IsHistorical() && !m_cfa_is_valid) { + m_id.SetCFA(m_frame_index); + } + + if (sc_ptr != nullptr) { + m_sc = *sc_ptr; + m_flags.Set(m_sc.GetResolvedMask()); + } +} + +StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, + user_id_t unwind_frame_index, + const RegisterContextSP ®_context_sp, addr_t cfa, + addr_t pc, bool behaves_like_zeroth_frame, + const SymbolContext *sc_ptr) + : m_thread_wp(thread_sp), m_frame_index(frame_idx), + m_concrete_frame_index(unwind_frame_index), + m_reg_context_sp(reg_context_sp), m_id(pc, cfa, nullptr), + m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), + m_frame_base_error(), m_cfa_is_valid(true), + m_stack_frame_kind(StackFrame::Kind::Regular), + m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), + m_variable_list_sp(), m_variable_list_value_objects(), + m_recognized_frame_sp(), m_disassembly(), m_mutex() { + if (sc_ptr != nullptr) { + m_sc = *sc_ptr; + m_flags.Set(m_sc.GetResolvedMask()); + } + + if (reg_context_sp && !m_sc.target_sp) { + m_sc.target_sp = reg_context_sp->CalculateTarget(); + if (m_sc.target_sp) + m_flags.Set(eSymbolContextTarget); + } +} + +StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, + user_id_t unwind_frame_index, + const RegisterContextSP ®_context_sp, addr_t cfa, + const Address &pc_addr, bool behaves_like_zeroth_frame, + const SymbolContext *sc_ptr) + : m_thread_wp(thread_sp), m_frame_index(frame_idx), + m_concrete_frame_index(unwind_frame_index), + m_reg_context_sp(reg_context_sp), + m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa, + nullptr), + m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(), + m_frame_base_error(), m_cfa_is_valid(true), + m_stack_frame_kind(StackFrame::Kind::Regular), + m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), + m_variable_list_sp(), m_variable_list_value_objects(), + m_recognized_frame_sp(), m_disassembly(), m_mutex() { + if (sc_ptr != nullptr) { + m_sc = *sc_ptr; + m_flags.Set(m_sc.GetResolvedMask()); + } + + if (!m_sc.target_sp && reg_context_sp) { + m_sc.target_sp = reg_context_sp->CalculateTarget(); + if (m_sc.target_sp) + m_flags.Set(eSymbolContextTarget); + } + + ModuleSP pc_module_sp(pc_addr.GetModule()); + if (!m_sc.module_sp || m_sc.module_sp != pc_module_sp) { + if (pc_module_sp) { + m_sc.module_sp = pc_module_sp; + m_flags.Set(eSymbolContextModule); + } else { + m_sc.module_sp.reset(); + } + } +} + +StackFrame::~StackFrame() = default; + +StackID &StackFrame::GetStackID() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + // Make sure we have resolved the StackID object's symbol context scope if we + // already haven't looked it up. + + if (m_flags.IsClear(RESOLVED_FRAME_ID_SYMBOL_SCOPE)) { + if (m_id.GetSymbolContextScope()) { + // We already have a symbol context scope, we just don't have our flag + // bit set. + m_flags.Set(RESOLVED_FRAME_ID_SYMBOL_SCOPE); + } else { + // Calculate the frame block and use this for the stack ID symbol context + // scope if we have one. + SymbolContextScope *scope = GetFrameBlock(); + if (scope == nullptr) { + // We don't have a block, so use the symbol + if (m_flags.IsClear(eSymbolContextSymbol)) + GetSymbolContext(eSymbolContextSymbol); + + // It is ok if m_sc.symbol is nullptr here + scope = m_sc.symbol; + } + // Set the symbol context scope (the accessor will set the + // RESOLVED_FRAME_ID_SYMBOL_SCOPE bit in m_flags). + SetSymbolContextScope(scope); + } + } + return m_id; +} + +uint32_t StackFrame::GetFrameIndex() const { + ThreadSP thread_sp = GetThread(); + if (thread_sp) + return thread_sp->GetStackFrameList()->GetVisibleStackFrameIndex( + m_frame_index); + else + return m_frame_index; +} + +void StackFrame::SetSymbolContextScope(SymbolContextScope *symbol_scope) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_flags.Set(RESOLVED_FRAME_ID_SYMBOL_SCOPE); + m_id.SetSymbolContextScope(symbol_scope); +} + +const Address &StackFrame::GetFrameCodeAddress() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) && + !m_frame_code_addr.IsSectionOffset()) { + m_flags.Set(RESOLVED_FRAME_CODE_ADDR); + + // Resolve the PC into a temporary address because if ResolveLoadAddress + // fails to resolve the address, it will clear the address object... + ThreadSP thread_sp(GetThread()); + if (thread_sp) { + TargetSP target_sp(thread_sp->CalculateTarget()); + if (target_sp) { + const bool allow_section_end = true; + if (m_frame_code_addr.SetOpcodeLoadAddress( + m_frame_code_addr.GetOffset(), target_sp.get(), + AddressClass::eCode, allow_section_end)) { + ModuleSP module_sp(m_frame_code_addr.GetModule()); + if (module_sp) { + m_sc.module_sp = module_sp; + m_flags.Set(eSymbolContextModule); + } + } + } + } + } + return m_frame_code_addr; +} + +bool StackFrame::ChangePC(addr_t pc) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + // We can't change the pc value of a history stack frame - it is immutable. + if (IsHistorical()) + return false; + m_frame_code_addr.SetRawAddress(pc); + m_sc.Clear(false); + m_flags.Reset(0); + ThreadSP thread_sp(GetThread()); + if (thread_sp) + thread_sp->ClearStackFrames(); + return true; +} + +const char *StackFrame::Disassemble() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (m_disassembly.Empty()) { + ExecutionContext exe_ctx(shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) { + const char *plugin_name = nullptr; + const char *flavor = nullptr; + Disassembler::Disassemble(target->GetDebugger(), + target->GetArchitecture(), plugin_name, flavor, + exe_ctx, 0, false, 0, 0, m_disassembly); + } + if (m_disassembly.Empty()) + return nullptr; + } + + return m_disassembly.GetData(); +} + +Block *StackFrame::GetFrameBlock() { + if (m_sc.block == nullptr && m_flags.IsClear(eSymbolContextBlock)) + GetSymbolContext(eSymbolContextBlock); + + if (m_sc.block) { + Block *inline_block = m_sc.block->GetContainingInlinedBlock(); + if (inline_block) { + // Use the block with the inlined function info as the frame block we + // want this frame to have only the variables for the inlined function + // and its non-inlined block child blocks. + return inline_block; + } else { + // This block is not contained within any inlined function blocks with so + // we want to use the top most function block. + return &m_sc.function->GetBlock(false); + } + } + return nullptr; +} + +// Get the symbol context if we already haven't done so by resolving the +// PC address as much as possible. This way when we pass around a +// StackFrame object, everyone will have as much information as possible and no +// one will ever have to look things up manually. +const SymbolContext & +StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + // Copy our internal symbol context into "sc". + if ((m_flags.Get() & resolve_scope) != resolve_scope) { + uint32_t resolved = 0; + + // If the target was requested add that: + if (!m_sc.target_sp) { + m_sc.target_sp = CalculateTarget(); + if (m_sc.target_sp) + resolved |= eSymbolContextTarget; + } + + // Resolve our PC to section offset if we haven't already done so and if we + // don't have a module. The resolved address section will contain the + // module to which it belongs + if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR)) + GetFrameCodeAddress(); + + // If this is not frame zero, then we need to subtract 1 from the PC value + // when doing address lookups since the PC will be on the instruction + // following the function call instruction... + + Address lookup_addr(GetFrameCodeAddress()); + if (!m_behaves_like_zeroth_frame && lookup_addr.IsValid()) { + addr_t offset = lookup_addr.GetOffset(); + if (offset > 0) { + lookup_addr.SetOffset(offset - 1); + + } else { + // lookup_addr is the start of a section. We need do the math on the + // actual load address and re-compute the section. We're working with + // a 'noreturn' function at the end of a section. + ThreadSP thread_sp(GetThread()); + if (thread_sp) { + TargetSP target_sp(thread_sp->CalculateTarget()); + if (target_sp) { + addr_t addr_minus_one = + lookup_addr.GetLoadAddress(target_sp.get()) - 1; + lookup_addr.SetLoadAddress(addr_minus_one, target_sp.get()); + } else { + lookup_addr.SetOffset(offset - 1); + } + } + } + } + + if (m_sc.module_sp) { + // We have something in our stack frame symbol context, lets check if we + // haven't already tried to lookup one of those things. If we haven't + // then we will do the query. + + SymbolContextItem actual_resolve_scope = SymbolContextItem(0); + + if (resolve_scope & eSymbolContextCompUnit) { + if (m_flags.IsClear(eSymbolContextCompUnit)) { + if (m_sc.comp_unit) + resolved |= eSymbolContextCompUnit; + else + actual_resolve_scope |= eSymbolContextCompUnit; + } + } + + if (resolve_scope & eSymbolContextFunction) { + if (m_flags.IsClear(eSymbolContextFunction)) { + if (m_sc.function) + resolved |= eSymbolContextFunction; + else + actual_resolve_scope |= eSymbolContextFunction; + } + } + + if (resolve_scope & eSymbolContextBlock) { + if (m_flags.IsClear(eSymbolContextBlock)) { + if (m_sc.block) + resolved |= eSymbolContextBlock; + else + actual_resolve_scope |= eSymbolContextBlock; + } + } + + if (resolve_scope & eSymbolContextSymbol) { + if (m_flags.IsClear(eSymbolContextSymbol)) { + if (m_sc.symbol) + resolved |= eSymbolContextSymbol; + else + actual_resolve_scope |= eSymbolContextSymbol; + } + } + + if (resolve_scope & eSymbolContextLineEntry) { + if (m_flags.IsClear(eSymbolContextLineEntry)) { + if (m_sc.line_entry.IsValid()) + resolved |= eSymbolContextLineEntry; + else + actual_resolve_scope |= eSymbolContextLineEntry; + } + } + + if (actual_resolve_scope) { + // We might be resolving less information than what is already in our + // current symbol context so resolve into a temporary symbol context + // "sc" so we don't clear out data we have already found in "m_sc" + SymbolContext sc; + // Set flags that indicate what we have tried to resolve + resolved |= m_sc.module_sp->ResolveSymbolContextForAddress( + lookup_addr, actual_resolve_scope, sc); + // Only replace what we didn't already have as we may have information + // for an inlined function scope that won't match what a standard + // lookup by address would match + if ((resolved & eSymbolContextCompUnit) && m_sc.comp_unit == nullptr) + m_sc.comp_unit = sc.comp_unit; + if ((resolved & eSymbolContextFunction) && m_sc.function == nullptr) + m_sc.function = sc.function; + if ((resolved & eSymbolContextBlock) && m_sc.block == nullptr) + m_sc.block = sc.block; + if ((resolved & eSymbolContextSymbol) && m_sc.symbol == nullptr) + m_sc.symbol = sc.symbol; + if ((resolved & eSymbolContextLineEntry) && + !m_sc.line_entry.IsValid()) { + m_sc.line_entry = sc.line_entry; + m_sc.line_entry.ApplyFileMappings(m_sc.target_sp); + } + } + } else { + // If we don't have a module, then we can't have the compile unit, + // function, block, line entry or symbol, so we can safely call + // ResolveSymbolContextForAddress with our symbol context member m_sc. + if (m_sc.target_sp) { + resolved |= m_sc.target_sp->GetImages().ResolveSymbolContextForAddress( + lookup_addr, resolve_scope, m_sc); + } + } + + // Update our internal flags so we remember what we have tried to locate so + // we don't have to keep trying when more calls to this function are made. + // We might have dug up more information that was requested (for example if + // we were asked to only get the block, we will have gotten the compile + // unit, and function) so set any additional bits that we resolved + m_flags.Set(resolve_scope | resolved); + } + + // Return the symbol context with everything that was possible to resolve + // resolved. + return m_sc; +} + +VariableList *StackFrame::GetVariableList(bool get_file_globals) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (m_flags.IsClear(RESOLVED_VARIABLES)) { + m_flags.Set(RESOLVED_VARIABLES); + + Block *frame_block = GetFrameBlock(); + + if (frame_block) { + const bool get_child_variables = true; + const bool can_create = true; + const bool stop_if_child_block_is_inlined_function = true; + m_variable_list_sp = std::make_shared<VariableList>(); + frame_block->AppendBlockVariables(can_create, get_child_variables, + stop_if_child_block_is_inlined_function, + [](Variable *v) { return true; }, + m_variable_list_sp.get()); + } + } + + if (m_flags.IsClear(RESOLVED_GLOBAL_VARIABLES) && get_file_globals) { + m_flags.Set(RESOLVED_GLOBAL_VARIABLES); + + if (m_flags.IsClear(eSymbolContextCompUnit)) + GetSymbolContext(eSymbolContextCompUnit); + + if (m_sc.comp_unit) { + VariableListSP global_variable_list_sp( + m_sc.comp_unit->GetVariableList(true)); + if (m_variable_list_sp) + m_variable_list_sp->AddVariables(global_variable_list_sp.get()); + else + m_variable_list_sp = global_variable_list_sp; + } + } + + return m_variable_list_sp.get(); +} + +VariableListSP +StackFrame::GetInScopeVariableList(bool get_file_globals, + bool must_have_valid_location) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + // We can't fetch variable information for a history stack frame. + if (IsHistorical()) + return VariableListSP(); + + VariableListSP var_list_sp(new VariableList); + GetSymbolContext(eSymbolContextCompUnit | eSymbolContextBlock); + + if (m_sc.block) { + const bool can_create = true; + const bool get_parent_variables = true; + const bool stop_if_block_is_inlined_function = true; + m_sc.block->AppendVariables( + can_create, get_parent_variables, stop_if_block_is_inlined_function, + [this, must_have_valid_location](Variable *v) { + return v->IsInScope(this) && (!must_have_valid_location || + v->LocationIsValidForFrame(this)); + }, + var_list_sp.get()); + } + + if (m_sc.comp_unit && get_file_globals) { + VariableListSP global_variable_list_sp( + m_sc.comp_unit->GetVariableList(true)); + if (global_variable_list_sp) + var_list_sp->AddVariables(global_variable_list_sp.get()); + } + + return var_list_sp; +} + +ValueObjectSP StackFrame::GetValueForVariableExpressionPath( + llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options, + VariableSP &var_sp, Status &error) { + llvm::StringRef original_var_expr = var_expr; + // We can't fetch variable information for a history stack frame. + if (IsHistorical()) + return ValueObjectSP(); + + if (var_expr.empty()) { + error.SetErrorStringWithFormat("invalid variable path '%s'", + var_expr.str().c_str()); + return ValueObjectSP(); + } + + const bool check_ptr_vs_member = + (options & eExpressionPathOptionCheckPtrVsMember) != 0; + const bool no_fragile_ivar = + (options & eExpressionPathOptionsNoFragileObjcIvar) != 0; + const bool no_synth_child = + (options & eExpressionPathOptionsNoSyntheticChildren) != 0; + // const bool no_synth_array = (options & + // eExpressionPathOptionsNoSyntheticArrayRange) != 0; + error.Clear(); + bool deref = false; + bool address_of = false; + ValueObjectSP valobj_sp; + const bool get_file_globals = true; + // When looking up a variable for an expression, we need only consider the + // variables that are in scope. + VariableListSP var_list_sp(GetInScopeVariableList(get_file_globals)); + VariableList *variable_list = var_list_sp.get(); + + if (!variable_list) + return ValueObjectSP(); + + // If first character is a '*', then show pointer contents + std::string var_expr_storage; + if (var_expr[0] == '*') { + deref = true; + var_expr = var_expr.drop_front(); // Skip the '*' + } else if (var_expr[0] == '&') { + address_of = true; + var_expr = var_expr.drop_front(); // Skip the '&' + } + + size_t separator_idx = var_expr.find_first_of(".-[=+~|&^%#@!/?,<>{}"); + StreamString var_expr_path_strm; + + ConstString name_const_string(var_expr.substr(0, separator_idx)); + + var_sp = variable_list->FindVariable(name_const_string, false); + + bool synthetically_added_instance_object = false; + + if (var_sp) { + var_expr = var_expr.drop_front(name_const_string.GetLength()); + } + + if (!var_sp && (options & eExpressionPathOptionsAllowDirectIVarAccess)) { + // Check for direct ivars access which helps us with implicit access to + // ivars with the "this->" or "self->" + GetSymbolContext(eSymbolContextFunction | eSymbolContextBlock); + lldb::LanguageType method_language = eLanguageTypeUnknown; + bool is_instance_method = false; + ConstString method_object_name; + if (m_sc.GetFunctionMethodInfo(method_language, is_instance_method, + method_object_name)) { + if (is_instance_method && method_object_name) { + var_sp = variable_list->FindVariable(method_object_name); + if (var_sp) { + separator_idx = 0; + var_expr_storage = "->"; + var_expr_storage += var_expr; + var_expr = var_expr_storage; + synthetically_added_instance_object = true; + } + } + } + } + + if (!var_sp && (options & eExpressionPathOptionsInspectAnonymousUnions)) { + // Check if any anonymous unions are there which contain a variable with + // the name we need + for (const VariableSP &variable_sp : *variable_list) { + if (!variable_sp) + continue; + if (!variable_sp->GetName().IsEmpty()) + continue; + + Type *var_type = variable_sp->GetType(); + if (!var_type) + continue; + + if (!var_type->GetForwardCompilerType().IsAnonymousType()) + continue; + valobj_sp = GetValueObjectForFrameVariable(variable_sp, use_dynamic); + if (!valobj_sp) + return valobj_sp; + valobj_sp = valobj_sp->GetChildMemberWithName(name_const_string, true); + if (valobj_sp) + break; + } + } + + if (var_sp && !valobj_sp) { + valobj_sp = GetValueObjectForFrameVariable(var_sp, use_dynamic); + if (!valobj_sp) + return valobj_sp; + } + if (!valobj_sp) { + error.SetErrorStringWithFormat("no variable named '%s' found in this frame", + name_const_string.GetCString()); + return ValueObjectSP(); + } + + // We are dumping at least one child + while (separator_idx != std::string::npos) { + // Calculate the next separator index ahead of time + ValueObjectSP child_valobj_sp; + const char separator_type = var_expr[0]; + bool expr_is_ptr = false; + switch (separator_type) { + case '-': + expr_is_ptr = true; + if (var_expr.size() >= 2 && var_expr[1] != '>') + return ValueObjectSP(); + + if (no_fragile_ivar) { + // Make sure we aren't trying to deref an objective + // C ivar if this is not allowed + const uint32_t pointer_type_flags = + valobj_sp->GetCompilerType().GetTypeInfo(nullptr); + if ((pointer_type_flags & eTypeIsObjC) && + (pointer_type_flags & eTypeIsPointer)) { + // This was an objective C object pointer and it was requested we + // skip any fragile ivars so return nothing here + return ValueObjectSP(); + } + } + + // If we have a non pointer type with a sythetic value then lets check if + // we have an sythetic dereference specified. + if (!valobj_sp->IsPointerType() && valobj_sp->HasSyntheticValue()) { + Status deref_error; + if (valobj_sp->GetCompilerType().IsReferenceType()) { + valobj_sp = valobj_sp->GetSyntheticValue()->Dereference(deref_error); + if (error.Fail()) { + error.SetErrorStringWithFormatv( + "Failed to dereference reference type: %s", deref_error); + return ValueObjectSP(); + } + } + + valobj_sp = valobj_sp->Dereference(deref_error); + if (error.Fail()) { + error.SetErrorStringWithFormatv( + "Failed to dereference sythetic value: {0}", deref_error); + return ValueObjectSP(); + } + // Some synthetic plug-ins fail to set the error in Dereference + if (!valobj_sp) { + error.SetErrorString("Failed to dereference sythetic value"); + return ValueObjectSP(); + } + expr_is_ptr = false; + } + + var_expr = var_expr.drop_front(); // Remove the '-' + LLVM_FALLTHROUGH; + case '.': { + var_expr = var_expr.drop_front(); // Remove the '.' or '>' + separator_idx = var_expr.find_first_of(".-["); + ConstString child_name(var_expr.substr(0, var_expr.find_first_of(".-["))); + + if (check_ptr_vs_member) { + // We either have a pointer type and need to verify valobj_sp is a + // pointer, or we have a member of a class/union/struct being accessed + // with the . syntax and need to verify we don't have a pointer. + const bool actual_is_ptr = valobj_sp->IsPointerType(); + + if (actual_is_ptr != expr_is_ptr) { + // Incorrect use of "." with a pointer, or "->" with a + // class/union/struct instance or reference. + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + if (actual_is_ptr) + error.SetErrorStringWithFormat( + "\"%s\" is a pointer and . was used to attempt to access " + "\"%s\". Did you mean \"%s->%s\"?", + var_expr_path_strm.GetData(), child_name.GetCString(), + var_expr_path_strm.GetData(), var_expr.str().c_str()); + else + error.SetErrorStringWithFormat( + "\"%s\" is not a pointer and -> was used to attempt to " + "access \"%s\". Did you mean \"%s.%s\"?", + var_expr_path_strm.GetData(), child_name.GetCString(), + var_expr_path_strm.GetData(), var_expr.str().c_str()); + return ValueObjectSP(); + } + } + child_valobj_sp = valobj_sp->GetChildMemberWithName(child_name, true); + if (!child_valobj_sp) { + if (!no_synth_child) { + child_valobj_sp = valobj_sp->GetSyntheticValue(); + if (child_valobj_sp) + child_valobj_sp = + child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + if (no_synth_child || !child_valobj_sp) { + // No child member with name "child_name" + if (synthetically_added_instance_object) { + // We added a "this->" or "self->" to the beginning of the + // expression and this is the first pointer ivar access, so just + // return the normal error + error.SetErrorStringWithFormat( + "no variable or instance variable named '%s' found in " + "this frame", + name_const_string.GetCString()); + } else { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + if (child_name) { + error.SetErrorStringWithFormat( + "\"%s\" is not a member of \"(%s) %s\"", + child_name.GetCString(), + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } else { + error.SetErrorStringWithFormat( + "incomplete expression path after \"%s\" in \"%s\"", + var_expr_path_strm.GetData(), + original_var_expr.str().c_str()); + } + } + return ValueObjectSP(); + } + } + synthetically_added_instance_object = false; + // Remove the child name from the path + var_expr = var_expr.drop_front(child_name.GetLength()); + if (use_dynamic != eNoDynamicValues) { + ValueObjectSP dynamic_value_sp( + child_valobj_sp->GetDynamicValue(use_dynamic)); + if (dynamic_value_sp) + child_valobj_sp = dynamic_value_sp; + } + } break; + + case '[': { + // Array member access, or treating pointer as an array Need at least two + // brackets and a number + if (var_expr.size() <= 2) { + error.SetErrorStringWithFormat( + "invalid square bracket encountered after \"%s\" in \"%s\"", + var_expr_path_strm.GetData(), var_expr.str().c_str()); + return ValueObjectSP(); + } + + // Drop the open brace. + var_expr = var_expr.drop_front(); + long child_index = 0; + + // If there's no closing brace, this is an invalid expression. + size_t end_pos = var_expr.find_first_of(']'); + if (end_pos == llvm::StringRef::npos) { + error.SetErrorStringWithFormat( + "missing closing square bracket in expression \"%s\"", + var_expr_path_strm.GetData()); + return ValueObjectSP(); + } + llvm::StringRef index_expr = var_expr.take_front(end_pos); + llvm::StringRef original_index_expr = index_expr; + // Drop all of "[index_expr]" + var_expr = var_expr.drop_front(end_pos + 1); + + if (index_expr.consumeInteger(0, child_index)) { + // If there was no integer anywhere in the index expression, this is + // erroneous expression. + error.SetErrorStringWithFormat("invalid index expression \"%s\"", + index_expr.str().c_str()); + return ValueObjectSP(); + } + + if (index_expr.empty()) { + // The entire index expression was a single integer. + + if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref) { + // what we have is *ptr[low]. the most similar C++ syntax is to deref + // ptr and extract bit low out of it. reading array item low would be + // done by saying ptr[low], without a deref * sign + Status error; + ValueObjectSP temp(valobj_sp->Dereference(error)); + if (error.Fail()) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "could not dereference \"(%s) %s\"", + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return ValueObjectSP(); + } + valobj_sp = temp; + deref = false; + } else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && + deref) { + // what we have is *arr[low]. the most similar C++ syntax is to get + // arr[0] (an operation that is equivalent to deref-ing arr) and + // extract bit low out of it. reading array item low would be done by + // saying arr[low], without a deref * sign + Status error; + ValueObjectSP temp(valobj_sp->GetChildAtIndex(0, true)); + if (error.Fail()) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "could not get item 0 for \"(%s) %s\"", + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return ValueObjectSP(); + } + valobj_sp = temp; + deref = false; + } + + bool is_incomplete_array = false; + if (valobj_sp->IsPointerType()) { + bool is_objc_pointer = true; + + if (valobj_sp->GetCompilerType().GetMinimumLanguage() != + eLanguageTypeObjC) + is_objc_pointer = false; + else if (!valobj_sp->GetCompilerType().IsPointerType()) + is_objc_pointer = false; + + if (no_synth_child && is_objc_pointer) { + error.SetErrorStringWithFormat( + "\"(%s) %s\" is an Objective-C pointer, and cannot be " + "subscripted", + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + + return ValueObjectSP(); + } else if (is_objc_pointer) { + // dereferencing ObjC variables is not valid.. so let's try and + // recur to synthetic children + ValueObjectSP synthetic = valobj_sp->GetSyntheticValue(); + if (!synthetic /* no synthetic */ + || synthetic == valobj_sp) /* synthetic is the same as + the original object */ + { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "\"(%s) %s\" is not an array type", + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } else if ( + static_cast<uint32_t>(child_index) >= + synthetic + ->GetNumChildren() /* synthetic does not have that many values */) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "array index %ld is not valid for \"(%s) %s\"", child_index, + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } else { + child_valobj_sp = synthetic->GetChildAtIndex(child_index, true); + if (!child_valobj_sp) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "array index %ld is not valid for \"(%s) %s\"", child_index, + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } + } + } else { + child_valobj_sp = + valobj_sp->GetSyntheticArrayMember(child_index, true); + if (!child_valobj_sp) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "failed to use pointer as array for index %ld for " + "\"(%s) %s\"", + child_index, + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } + } + } else if (valobj_sp->GetCompilerType().IsArrayType( + nullptr, nullptr, &is_incomplete_array)) { + // Pass false to dynamic_value here so we can tell the difference + // between no dynamic value and no member of this type... + child_valobj_sp = valobj_sp->GetChildAtIndex(child_index, true); + if (!child_valobj_sp && (is_incomplete_array || !no_synth_child)) + child_valobj_sp = + valobj_sp->GetSyntheticArrayMember(child_index, true); + + if (!child_valobj_sp) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "array index %ld is not valid for \"(%s) %s\"", child_index, + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } + } else if (valobj_sp->GetCompilerType().IsScalarType()) { + // this is a bitfield asking to display just one bit + child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild( + child_index, child_index, true); + if (!child_valobj_sp) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "bitfield range %ld-%ld is not valid for \"(%s) %s\"", + child_index, child_index, + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } + } else { + ValueObjectSP synthetic = valobj_sp->GetSyntheticValue(); + if (no_synth_child /* synthetic is forbidden */ || + !synthetic /* no synthetic */ + || synthetic == valobj_sp) /* synthetic is the same as the + original object */ + { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "\"(%s) %s\" is not an array type", + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } else if ( + static_cast<uint32_t>(child_index) >= + synthetic + ->GetNumChildren() /* synthetic does not have that many values */) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "array index %ld is not valid for \"(%s) %s\"", child_index, + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } else { + child_valobj_sp = synthetic->GetChildAtIndex(child_index, true); + if (!child_valobj_sp) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "array index %ld is not valid for \"(%s) %s\"", child_index, + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } + } + } + + if (!child_valobj_sp) { + // Invalid array index... + return ValueObjectSP(); + } + + separator_idx = var_expr.find_first_of(".-["); + if (use_dynamic != eNoDynamicValues) { + ValueObjectSP dynamic_value_sp( + child_valobj_sp->GetDynamicValue(use_dynamic)); + if (dynamic_value_sp) + child_valobj_sp = dynamic_value_sp; + } + // Break out early from the switch since we were able to find the child + // member + break; + } + + // this is most probably a BitField, let's take a look + if (index_expr.front() != '-') { + error.SetErrorStringWithFormat("invalid range expression \"'%s'\"", + original_index_expr.str().c_str()); + return ValueObjectSP(); + } + + index_expr = index_expr.drop_front(); + long final_index = 0; + if (index_expr.getAsInteger(0, final_index)) { + error.SetErrorStringWithFormat("invalid range expression \"'%s'\"", + original_index_expr.str().c_str()); + return ValueObjectSP(); + } + + // if the format given is [high-low], swap range + if (child_index > final_index) { + long temp = child_index; + child_index = final_index; + final_index = temp; + } + + if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref) { + // what we have is *ptr[low-high]. the most similar C++ syntax is to + // deref ptr and extract bits low thru high out of it. reading array + // items low thru high would be done by saying ptr[low-high], without a + // deref * sign + Status error; + ValueObjectSP temp(valobj_sp->Dereference(error)); + if (error.Fail()) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "could not dereference \"(%s) %s\"", + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return ValueObjectSP(); + } + valobj_sp = temp; + deref = false; + } else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && deref) { + // what we have is *arr[low-high]. the most similar C++ syntax is to + // get arr[0] (an operation that is equivalent to deref-ing arr) and + // extract bits low thru high out of it. reading array items low thru + // high would be done by saying arr[low-high], without a deref * sign + Status error; + ValueObjectSP temp(valobj_sp->GetChildAtIndex(0, true)); + if (error.Fail()) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "could not get item 0 for \"(%s) %s\"", + valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return ValueObjectSP(); + } + valobj_sp = temp; + deref = false; + } + + child_valobj_sp = + valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true); + if (!child_valobj_sp) { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "bitfield range %ld-%ld is not valid for \"(%s) %s\"", child_index, + final_index, valobj_sp->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + } + + if (!child_valobj_sp) { + // Invalid bitfield range... + return ValueObjectSP(); + } + + separator_idx = var_expr.find_first_of(".-["); + if (use_dynamic != eNoDynamicValues) { + ValueObjectSP dynamic_value_sp( + child_valobj_sp->GetDynamicValue(use_dynamic)); + if (dynamic_value_sp) + child_valobj_sp = dynamic_value_sp; + } + // Break out early from the switch since we were able to find the child + // member + break; + } + default: + // Failure... + { + valobj_sp->GetExpressionPath(var_expr_path_strm, false); + error.SetErrorStringWithFormat( + "unexpected char '%c' encountered after \"%s\" in \"%s\"", + separator_type, var_expr_path_strm.GetData(), + var_expr.str().c_str()); + + return ValueObjectSP(); + } + } + + if (child_valobj_sp) + valobj_sp = child_valobj_sp; + + if (var_expr.empty()) + break; + } + if (valobj_sp) { + if (deref) { + ValueObjectSP deref_valobj_sp(valobj_sp->Dereference(error)); + valobj_sp = deref_valobj_sp; + } else if (address_of) { + ValueObjectSP address_of_valobj_sp(valobj_sp->AddressOf(error)); + valobj_sp = address_of_valobj_sp; + } + } + return valobj_sp; +} + +bool StackFrame::GetFrameBaseValue(Scalar &frame_base, Status *error_ptr) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (!m_cfa_is_valid) { + m_frame_base_error.SetErrorString( + "No frame base available for this historical stack frame."); + return false; + } + + if (m_flags.IsClear(GOT_FRAME_BASE)) { + if (m_sc.function) { + m_frame_base.Clear(); + m_frame_base_error.Clear(); + + m_flags.Set(GOT_FRAME_BASE); + ExecutionContext exe_ctx(shared_from_this()); + Value expr_value; + addr_t loclist_base_addr = LLDB_INVALID_ADDRESS; + if (m_sc.function->GetFrameBaseExpression().IsLocationList()) + loclist_base_addr = + m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress( + exe_ctx.GetTargetPtr()); + + if (!m_sc.function->GetFrameBaseExpression().Evaluate( + &exe_ctx, nullptr, loclist_base_addr, nullptr, nullptr, + expr_value, &m_frame_base_error)) { + // We should really have an error if evaluate returns, but in case we + // don't, lets set the error to something at least. + if (m_frame_base_error.Success()) + m_frame_base_error.SetErrorString( + "Evaluation of the frame base expression failed."); + } else { + m_frame_base = expr_value.ResolveValue(&exe_ctx); + } + } else { + m_frame_base_error.SetErrorString("No function in symbol context."); + } + } + + if (m_frame_base_error.Success()) + frame_base = m_frame_base; + + if (error_ptr) + *error_ptr = m_frame_base_error; + return m_frame_base_error.Success(); +} + +DWARFExpression *StackFrame::GetFrameBaseExpression(Status *error_ptr) { + if (!m_sc.function) { + if (error_ptr) { + error_ptr->SetErrorString("No function in symbol context."); + } + return nullptr; + } + + return &m_sc.function->GetFrameBaseExpression(); +} + +RegisterContextSP StackFrame::GetRegisterContext() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (!m_reg_context_sp) { + ThreadSP thread_sp(GetThread()); + if (thread_sp) + m_reg_context_sp = thread_sp->CreateRegisterContextForFrame(this); + } + return m_reg_context_sp; +} + +bool StackFrame::HasDebugInformation() { + GetSymbolContext(eSymbolContextLineEntry); + return m_sc.line_entry.IsValid(); +} + +ValueObjectSP +StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, + DynamicValueType use_dynamic) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + ValueObjectSP valobj_sp; + if (IsHistorical()) { + return valobj_sp; + } + VariableList *var_list = GetVariableList(true); + if (var_list) { + // Make sure the variable is a frame variable + const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get()); + const uint32_t num_variables = var_list->GetSize(); + if (var_idx < num_variables) { + valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); + if (!valobj_sp) { + if (m_variable_list_value_objects.GetSize() < num_variables) + m_variable_list_value_objects.Resize(num_variables); + valobj_sp = ValueObjectVariable::Create(this, variable_sp); + m_variable_list_value_objects.SetValueObjectAtIndex(var_idx, valobj_sp); + } + } + } + if (use_dynamic != eNoDynamicValues && valobj_sp) { + ValueObjectSP dynamic_sp = valobj_sp->GetDynamicValue(use_dynamic); + if (dynamic_sp) + return dynamic_sp; + } + return valobj_sp; +} + +ValueObjectSP StackFrame::TrackGlobalVariable(const VariableSP &variable_sp, + DynamicValueType use_dynamic) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (IsHistorical()) + return ValueObjectSP(); + + // Check to make sure we aren't already tracking this variable? + ValueObjectSP valobj_sp( + GetValueObjectForFrameVariable(variable_sp, use_dynamic)); + if (!valobj_sp) { + // We aren't already tracking this global + VariableList *var_list = GetVariableList(true); + // If this frame has no variables, create a new list + if (var_list == nullptr) + m_variable_list_sp = std::make_shared<VariableList>(); + + // Add the global/static variable to this frame + m_variable_list_sp->AddVariable(variable_sp); + + // Now make a value object for it so we can track its changes + valobj_sp = GetValueObjectForFrameVariable(variable_sp, use_dynamic); + } + return valobj_sp; +} + +bool StackFrame::IsInlined() { + if (m_sc.block == nullptr) + GetSymbolContext(eSymbolContextBlock); + if (m_sc.block) + return m_sc.block->GetContainingInlinedBlock() != nullptr; + return false; +} + +bool StackFrame::IsHistorical() const { + return m_stack_frame_kind == StackFrame::Kind::History; +} + +bool StackFrame::IsArtificial() const { + return m_stack_frame_kind == StackFrame::Kind::Artificial; +} + +lldb::LanguageType StackFrame::GetLanguage() { + CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit; + if (cu) + return cu->GetLanguage(); + return lldb::eLanguageTypeUnknown; +} + +lldb::LanguageType StackFrame::GuessLanguage() { + LanguageType lang_type = GetLanguage(); + + if (lang_type == eLanguageTypeUnknown) { + SymbolContext sc = GetSymbolContext(eSymbolContextFunction + | eSymbolContextSymbol); + if (sc.function) { + lang_type = sc.function->GetMangled().GuessLanguage(); + } + else if (sc.symbol) + { + lang_type = sc.symbol->GetMangled().GuessLanguage(); + } + } + + return lang_type; +} + +namespace { +std::pair<const Instruction::Operand *, int64_t> +GetBaseExplainingValue(const Instruction::Operand &operand, + RegisterContext ®ister_context, lldb::addr_t value) { + switch (operand.m_type) { + case Instruction::Operand::Type::Dereference: + case Instruction::Operand::Type::Immediate: + case Instruction::Operand::Type::Invalid: + case Instruction::Operand::Type::Product: + // These are not currently interesting + return std::make_pair(nullptr, 0); + case Instruction::Operand::Type::Sum: { + const Instruction::Operand *immediate_child = nullptr; + const Instruction::Operand *variable_child = nullptr; + if (operand.m_children[0].m_type == Instruction::Operand::Type::Immediate) { + immediate_child = &operand.m_children[0]; + variable_child = &operand.m_children[1]; + } else if (operand.m_children[1].m_type == + Instruction::Operand::Type::Immediate) { + immediate_child = &operand.m_children[1]; + variable_child = &operand.m_children[0]; + } + if (!immediate_child) { + return std::make_pair(nullptr, 0); + } + lldb::addr_t adjusted_value = value; + if (immediate_child->m_negative) { + adjusted_value += immediate_child->m_immediate; + } else { + adjusted_value -= immediate_child->m_immediate; + } + std::pair<const Instruction::Operand *, int64_t> base_and_offset = + GetBaseExplainingValue(*variable_child, register_context, + adjusted_value); + if (!base_and_offset.first) { + return std::make_pair(nullptr, 0); + } + if (immediate_child->m_negative) { + base_and_offset.second -= immediate_child->m_immediate; + } else { + base_and_offset.second += immediate_child->m_immediate; + } + return base_and_offset; + } + case Instruction::Operand::Type::Register: { + const RegisterInfo *info = + register_context.GetRegisterInfoByName(operand.m_register.AsCString()); + if (!info) { + return std::make_pair(nullptr, 0); + } + RegisterValue reg_value; + if (!register_context.ReadRegister(info, reg_value)) { + return std::make_pair(nullptr, 0); + } + if (reg_value.GetAsUInt64() == value) { + return std::make_pair(&operand, 0); + } else { + return std::make_pair(nullptr, 0); + } + } + } + return std::make_pair(nullptr, 0); +} + +std::pair<const Instruction::Operand *, int64_t> +GetBaseExplainingDereference(const Instruction::Operand &operand, + RegisterContext ®ister_context, + lldb::addr_t addr) { + if (operand.m_type == Instruction::Operand::Type::Dereference) { + return GetBaseExplainingValue(operand.m_children[0], register_context, + addr); + } + return std::make_pair(nullptr, 0); +} +} + +lldb::ValueObjectSP StackFrame::GuessValueForAddress(lldb::addr_t addr) { + TargetSP target_sp = CalculateTarget(); + + const ArchSpec &target_arch = target_sp->GetArchitecture(); + + AddressRange pc_range; + pc_range.GetBaseAddress() = GetFrameCodeAddress(); + pc_range.SetByteSize(target_arch.GetMaximumOpcodeByteSize()); + + ExecutionContext exe_ctx(shared_from_this()); + + const char *plugin_name = nullptr; + const char *flavor = nullptr; + const bool prefer_file_cache = false; + + DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( + target_arch, plugin_name, flavor, exe_ctx, pc_range, prefer_file_cache); + + if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize()) { + return ValueObjectSP(); + } + + InstructionSP instruction_sp = + disassembler_sp->GetInstructionList().GetInstructionAtIndex(0); + + llvm::SmallVector<Instruction::Operand, 3> operands; + + if (!instruction_sp->ParseOperands(operands)) { + return ValueObjectSP(); + } + + RegisterContextSP register_context_sp = GetRegisterContext(); + + if (!register_context_sp) { + return ValueObjectSP(); + } + + for (const Instruction::Operand &operand : operands) { + std::pair<const Instruction::Operand *, int64_t> base_and_offset = + GetBaseExplainingDereference(operand, *register_context_sp, addr); + + if (!base_and_offset.first) { + continue; + } + + switch (base_and_offset.first->m_type) { + case Instruction::Operand::Type::Immediate: { + lldb_private::Address addr; + if (target_sp->ResolveLoadAddress(base_and_offset.first->m_immediate + + base_and_offset.second, + addr)) { + auto c_type_system_or_err = + target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC); + if (auto err = c_type_system_or_err.takeError()) { + LLDB_LOG_ERROR( + lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD), + std::move(err), "Unable to guess value for given address"); + return ValueObjectSP(); + } else { + CompilerType void_ptr_type = + c_type_system_or_err + ->GetBasicTypeFromAST(lldb::BasicType::eBasicTypeChar) + .GetPointerType(); + return ValueObjectMemory::Create(this, "", addr, void_ptr_type); + } + } else { + return ValueObjectSP(); + } + break; + } + case Instruction::Operand::Type::Register: { + return GuessValueForRegisterAndOffset(base_and_offset.first->m_register, + base_and_offset.second); + } + default: + return ValueObjectSP(); + } + } + + return ValueObjectSP(); +} + +namespace { +ValueObjectSP GetValueForOffset(StackFrame &frame, ValueObjectSP &parent, + int64_t offset) { + if (offset < 0 || uint64_t(offset) >= parent->GetByteSize()) { + return ValueObjectSP(); + } + + if (parent->IsPointerOrReferenceType()) { + return parent; + } + + for (int ci = 0, ce = parent->GetNumChildren(); ci != ce; ++ci) { + const bool can_create = true; + ValueObjectSP child_sp = parent->GetChildAtIndex(ci, can_create); + + if (!child_sp) { + return ValueObjectSP(); + } + + int64_t child_offset = child_sp->GetByteOffset(); + int64_t child_size = child_sp->GetByteSize(); + + if (offset >= child_offset && offset < (child_offset + child_size)) { + return GetValueForOffset(frame, child_sp, offset - child_offset); + } + } + + if (offset == 0) { + return parent; + } else { + return ValueObjectSP(); + } +} + +ValueObjectSP GetValueForDereferincingOffset(StackFrame &frame, + ValueObjectSP &base, + int64_t offset) { + // base is a pointer to something + // offset is the thing to add to the pointer We return the most sensible + // ValueObject for the result of *(base+offset) + + if (!base->IsPointerOrReferenceType()) { + return ValueObjectSP(); + } + + Status error; + ValueObjectSP pointee = base->Dereference(error); + + if (!pointee) { + return ValueObjectSP(); + } + + if (offset >= 0 && uint64_t(offset) >= pointee->GetByteSize()) { + int64_t index = offset / pointee->GetByteSize(); + offset = offset % pointee->GetByteSize(); + const bool can_create = true; + pointee = base->GetSyntheticArrayMember(index, can_create); + } + + if (!pointee || error.Fail()) { + return ValueObjectSP(); + } + + return GetValueForOffset(frame, pointee, offset); +} + +/// Attempt to reconstruct the ValueObject for the address contained in a +/// given register plus an offset. +/// +/// \params [in] frame +/// The current stack frame. +/// +/// \params [in] reg +/// The register. +/// +/// \params [in] offset +/// The offset from the register. +/// +/// \param [in] disassembler +/// A disassembler containing instructions valid up to the current PC. +/// +/// \param [in] variables +/// The variable list from the current frame, +/// +/// \param [in] pc +/// The program counter for the instruction considered the 'user'. +/// +/// \return +/// A string describing the base for the ExpressionPath. This could be a +/// variable, a register value, an argument, or a function return value. +/// The ValueObject if found. If valid, it has a valid ExpressionPath. +lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, + int64_t offset, Disassembler &disassembler, + VariableList &variables, const Address &pc) { + // Example of operation for Intel: + // + // +14: movq -0x8(%rbp), %rdi + // +18: movq 0x8(%rdi), %rdi + // +22: addl 0x4(%rdi), %eax + // + // f, a pointer to a struct, is known to be at -0x8(%rbp). + // + // DoGuessValueAt(frame, rdi, 4, dis, vars, 0x22) finds the instruction at + // +18 that assigns to rdi, and calls itself recursively for that dereference + // DoGuessValueAt(frame, rdi, 8, dis, vars, 0x18) finds the instruction at + // +14 that assigns to rdi, and calls itself recursively for that + // derefernece + // DoGuessValueAt(frame, rbp, -8, dis, vars, 0x14) finds "f" in the + // variable list. + // Returns a ValueObject for f. (That's what was stored at rbp-8 at +14) + // Returns a ValueObject for *(f+8) or f->b (That's what was stored at rdi+8 + // at +18) + // Returns a ValueObject for *(f->b+4) or f->b->a (That's what was stored at + // rdi+4 at +22) + + // First, check the variable list to see if anything is at the specified + // location. + + using namespace OperandMatchers; + + const RegisterInfo *reg_info = + frame.GetRegisterContext()->GetRegisterInfoByName(reg.AsCString()); + if (!reg_info) { + return ValueObjectSP(); + } + + Instruction::Operand op = + offset ? Instruction::Operand::BuildDereference( + Instruction::Operand::BuildSum( + Instruction::Operand::BuildRegister(reg), + Instruction::Operand::BuildImmediate(offset))) + : Instruction::Operand::BuildDereference( + Instruction::Operand::BuildRegister(reg)); + + for (VariableSP var_sp : variables) { + if (var_sp->LocationExpression().MatchesOperand(frame, op)) + return frame.GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); + } + + const uint32_t current_inst = + disassembler.GetInstructionList().GetIndexOfInstructionAtAddress(pc); + if (current_inst == UINT32_MAX) { + return ValueObjectSP(); + } + + for (uint32_t ii = current_inst - 1; ii != (uint32_t)-1; --ii) { + // This is not an exact algorithm, and it sacrifices accuracy for + // generality. Recognizing "mov" and "ld" instructions –– and which + // are their source and destination operands -- is something the + // disassembler should do for us. + InstructionSP instruction_sp = + disassembler.GetInstructionList().GetInstructionAtIndex(ii); + + if (instruction_sp->IsCall()) { + ABISP abi_sp = frame.CalculateProcess()->GetABI(); + if (!abi_sp) { + continue; + } + + const char *return_register_name; + if (!abi_sp->GetPointerReturnRegister(return_register_name)) { + continue; + } + + const RegisterInfo *return_register_info = + frame.GetRegisterContext()->GetRegisterInfoByName( + return_register_name); + if (!return_register_info) { + continue; + } + + int64_t offset = 0; + + if (!MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference), + MatchRegOp(*return_register_info))(op) && + !MatchUnaryOp( + MatchOpType(Instruction::Operand::Type::Dereference), + MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), + MatchRegOp(*return_register_info), + FetchImmOp(offset)))(op)) { + continue; + } + + llvm::SmallVector<Instruction::Operand, 1> operands; + if (!instruction_sp->ParseOperands(operands) || operands.size() != 1) { + continue; + } + + switch (operands[0].m_type) { + default: + break; + case Instruction::Operand::Type::Immediate: { + SymbolContext sc; + Address load_address; + if (!frame.CalculateTarget()->ResolveLoadAddress( + operands[0].m_immediate, load_address)) { + break; + } + frame.CalculateTarget()->GetImages().ResolveSymbolContextForAddress( + load_address, eSymbolContextFunction, sc); + if (!sc.function) { + break; + } + CompilerType function_type = sc.function->GetCompilerType(); + if (!function_type.IsFunctionType()) { + break; + } + CompilerType return_type = function_type.GetFunctionReturnType(); + RegisterValue return_value; + if (!frame.GetRegisterContext()->ReadRegister(return_register_info, + return_value)) { + break; + } + std::string name_str( + sc.function->GetName().AsCString("<unknown function>")); + name_str.append("()"); + Address return_value_address(return_value.GetAsUInt64()); + ValueObjectSP return_value_sp = ValueObjectMemory::Create( + &frame, name_str, return_value_address, return_type); + return GetValueForDereferincingOffset(frame, return_value_sp, offset); + } + } + + continue; + } + + llvm::SmallVector<Instruction::Operand, 2> operands; + if (!instruction_sp->ParseOperands(operands) || operands.size() != 2) { + continue; + } + + Instruction::Operand *origin_operand = nullptr; + auto clobbered_reg_matcher = [reg_info](const Instruction::Operand &op) { + return MatchRegOp(*reg_info)(op) && op.m_clobbered; + }; + + if (clobbered_reg_matcher(operands[0])) { + origin_operand = &operands[1]; + } + else if (clobbered_reg_matcher(operands[1])) { + origin_operand = &operands[0]; + } + else { + continue; + } + + // We have an origin operand. Can we track its value down? + ValueObjectSP source_path; + ConstString origin_register; + int64_t origin_offset = 0; + + if (FetchRegOp(origin_register)(*origin_operand)) { + source_path = DoGuessValueAt(frame, origin_register, 0, disassembler, + variables, instruction_sp->GetAddress()); + } else if (MatchUnaryOp( + MatchOpType(Instruction::Operand::Type::Dereference), + FetchRegOp(origin_register))(*origin_operand) || + MatchUnaryOp( + MatchOpType(Instruction::Operand::Type::Dereference), + MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), + FetchRegOp(origin_register), + FetchImmOp(origin_offset)))(*origin_operand)) { + source_path = + DoGuessValueAt(frame, origin_register, origin_offset, disassembler, + variables, instruction_sp->GetAddress()); + if (!source_path) { + continue; + } + source_path = + GetValueForDereferincingOffset(frame, source_path, offset); + } + + if (source_path) { + return source_path; + } + } + + return ValueObjectSP(); +} +} + +lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, + int64_t offset) { + TargetSP target_sp = CalculateTarget(); + + const ArchSpec &target_arch = target_sp->GetArchitecture(); + + Block *frame_block = GetFrameBlock(); + + if (!frame_block) { + return ValueObjectSP(); + } + + Function *function = frame_block->CalculateSymbolContextFunction(); + if (!function) { + return ValueObjectSP(); + } + + AddressRange pc_range = function->GetAddressRange(); + + if (GetFrameCodeAddress().GetFileAddress() < + pc_range.GetBaseAddress().GetFileAddress() || + GetFrameCodeAddress().GetFileAddress() - + pc_range.GetBaseAddress().GetFileAddress() >= + pc_range.GetByteSize()) { + return ValueObjectSP(); + } + + ExecutionContext exe_ctx(shared_from_this()); + + const char *plugin_name = nullptr; + const char *flavor = nullptr; + const bool prefer_file_cache = false; + DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( + target_arch, plugin_name, flavor, exe_ctx, pc_range, prefer_file_cache); + + if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize()) { + return ValueObjectSP(); + } + + const bool get_file_globals = false; + VariableList *variables = GetVariableList(get_file_globals); + + if (!variables) { + return ValueObjectSP(); + } + + return DoGuessValueAt(*this, reg, offset, *disassembler_sp, *variables, + GetFrameCodeAddress()); +} + +lldb::ValueObjectSP StackFrame::FindVariable(ConstString name) { + ValueObjectSP value_sp; + + if (!name) + return value_sp; + + TargetSP target_sp = CalculateTarget(); + ProcessSP process_sp = CalculateProcess(); + + if (!target_sp && !process_sp) + return value_sp; + + VariableList variable_list; + VariableSP var_sp; + SymbolContext sc(GetSymbolContext(eSymbolContextBlock)); + + if (sc.block) { + const bool can_create = true; + const bool get_parent_variables = true; + const bool stop_if_block_is_inlined_function = true; + + if (sc.block->AppendVariables( + can_create, get_parent_variables, stop_if_block_is_inlined_function, + [this](Variable *v) { return v->IsInScope(this); }, + &variable_list)) { + var_sp = variable_list.FindVariable(name); + } + + if (var_sp) + value_sp = GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); + } + + return value_sp; +} + +TargetSP StackFrame::CalculateTarget() { + TargetSP target_sp; + ThreadSP thread_sp(GetThread()); + if (thread_sp) { + ProcessSP process_sp(thread_sp->CalculateProcess()); + if (process_sp) + target_sp = process_sp->CalculateTarget(); + } + return target_sp; +} + +ProcessSP StackFrame::CalculateProcess() { + ProcessSP process_sp; + ThreadSP thread_sp(GetThread()); + if (thread_sp) + process_sp = thread_sp->CalculateProcess(); + return process_sp; +} + +ThreadSP StackFrame::CalculateThread() { return GetThread(); } + +StackFrameSP StackFrame::CalculateStackFrame() { return shared_from_this(); } + +void StackFrame::CalculateExecutionContext(ExecutionContext &exe_ctx) { + exe_ctx.SetContext(shared_from_this()); +} + +void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique, + const char *frame_marker) { + if (strm == nullptr) + return; + + GetSymbolContext(eSymbolContextEverything); + ExecutionContext exe_ctx(shared_from_this()); + StreamString s; + + if (frame_marker) + s.PutCString(frame_marker); + + const FormatEntity::Entry *frame_format = nullptr; + Target *target = exe_ctx.GetTargetPtr(); + if (target) { + if (show_unique) { + frame_format = target->GetDebugger().GetFrameFormatUnique(); + } else { + frame_format = target->GetDebugger().GetFrameFormat(); + } + } + if (frame_format && FormatEntity::Format(*frame_format, s, &m_sc, &exe_ctx, + nullptr, nullptr, false, false)) { + strm->PutCString(s.GetString()); + } else { + Dump(strm, true, false); + strm->EOL(); + } +} + +void StackFrame::Dump(Stream *strm, bool show_frame_index, + bool show_fullpaths) { + if (strm == nullptr) + return; + + if (show_frame_index) + strm->Printf("frame #%u: ", m_frame_index); + ExecutionContext exe_ctx(shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); + strm->Printf("0x%0*" PRIx64 " ", + target ? (target->GetArchitecture().GetAddressByteSize() * 2) + : 16, + GetFrameCodeAddress().GetLoadAddress(target)); + GetSymbolContext(eSymbolContextEverything); + const bool show_module = true; + const bool show_inline = true; + const bool show_function_arguments = true; + const bool show_function_name = true; + m_sc.DumpStopContext(strm, exe_ctx.GetBestExecutionContextScope(), + GetFrameCodeAddress(), show_fullpaths, show_module, + show_inline, show_function_arguments, + show_function_name); +} + +void StackFrame::UpdateCurrentFrameFromPreviousFrame(StackFrame &prev_frame) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + assert(GetStackID() == + prev_frame.GetStackID()); // TODO: remove this after some testing + m_variable_list_sp = prev_frame.m_variable_list_sp; + m_variable_list_value_objects.Swap(prev_frame.m_variable_list_value_objects); + if (!m_disassembly.GetString().empty()) { + m_disassembly.Clear(); + m_disassembly.PutCString(prev_frame.m_disassembly.GetString()); + } +} + +void StackFrame::UpdatePreviousFrameFromCurrentFrame(StackFrame &curr_frame) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + assert(GetStackID() == + curr_frame.GetStackID()); // TODO: remove this after some testing + m_id.SetPC(curr_frame.m_id.GetPC()); // Update the Stack ID PC value + assert(GetThread() == curr_frame.GetThread()); + m_frame_index = curr_frame.m_frame_index; + m_concrete_frame_index = curr_frame.m_concrete_frame_index; + m_reg_context_sp = curr_frame.m_reg_context_sp; + m_frame_code_addr = curr_frame.m_frame_code_addr; + assert(!m_sc.target_sp || !curr_frame.m_sc.target_sp || + m_sc.target_sp.get() == curr_frame.m_sc.target_sp.get()); + assert(!m_sc.module_sp || !curr_frame.m_sc.module_sp || + m_sc.module_sp.get() == curr_frame.m_sc.module_sp.get()); + assert(m_sc.comp_unit == nullptr || curr_frame.m_sc.comp_unit == nullptr || + m_sc.comp_unit == curr_frame.m_sc.comp_unit); + assert(m_sc.function == nullptr || curr_frame.m_sc.function == nullptr || + m_sc.function == curr_frame.m_sc.function); + m_sc = curr_frame.m_sc; + m_flags.Clear(GOT_FRAME_BASE | eSymbolContextEverything); + m_flags.Set(m_sc.GetResolvedMask()); + m_frame_base.Clear(); + m_frame_base_error.Clear(); +} + +bool StackFrame::HasCachedData() const { + if (m_variable_list_sp) + return true; + if (m_variable_list_value_objects.GetSize() > 0) + return true; + if (!m_disassembly.GetString().empty()) + return true; + return false; +} + +bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source, + bool show_unique, const char *frame_marker) { + if (show_frame_info) { + strm.Indent(); + DumpUsingSettingsFormat(&strm, show_unique, frame_marker); + } + + if (show_source) { + ExecutionContext exe_ctx(shared_from_this()); + bool have_source = false, have_debuginfo = false; + Debugger::StopDisassemblyType disasm_display = + Debugger::eStopDisassemblyTypeNever; + Target *target = exe_ctx.GetTargetPtr(); + if (target) { + Debugger &debugger = target->GetDebugger(); + const uint32_t source_lines_before = + debugger.GetStopSourceLineCount(true); + const uint32_t source_lines_after = + debugger.GetStopSourceLineCount(false); + disasm_display = debugger.GetStopDisassemblyDisplay(); + + GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry); + if (m_sc.comp_unit && m_sc.line_entry.IsValid()) { + have_debuginfo = true; + if (source_lines_before > 0 || source_lines_after > 0) { + size_t num_lines = + target->GetSourceManager().DisplaySourceLinesWithLineNumbers( + m_sc.line_entry.file, m_sc.line_entry.line, + m_sc.line_entry.column, source_lines_before, + source_lines_after, "->", &strm); + if (num_lines != 0) + have_source = true; + // TODO: Give here a one time warning if source file is missing. + } + } + switch (disasm_display) { + case Debugger::eStopDisassemblyTypeNever: + break; + + case Debugger::eStopDisassemblyTypeNoDebugInfo: + if (have_debuginfo) + break; + LLVM_FALLTHROUGH; + + case Debugger::eStopDisassemblyTypeNoSource: + if (have_source) + break; + LLVM_FALLTHROUGH; + + case Debugger::eStopDisassemblyTypeAlways: + if (target) { + const uint32_t disasm_lines = debugger.GetDisassemblyLineCount(); + if (disasm_lines > 0) { + const ArchSpec &target_arch = target->GetArchitecture(); + AddressRange pc_range; + pc_range.GetBaseAddress() = GetFrameCodeAddress(); + pc_range.SetByteSize(disasm_lines * + target_arch.GetMaximumOpcodeByteSize()); + const char *plugin_name = nullptr; + const char *flavor = nullptr; + const bool mixed_source_and_assembly = false; + Disassembler::Disassemble( + target->GetDebugger(), target_arch, plugin_name, flavor, + exe_ctx, pc_range, disasm_lines, mixed_source_and_assembly, 0, + Disassembler::eOptionMarkPCAddress, strm); + } + } + break; + } + } + } + return true; +} + +RecognizedStackFrameSP StackFrame::GetRecognizedFrame() { + if (!m_recognized_frame_sp) { + m_recognized_frame_sp = + StackFrameRecognizerManager::RecognizeFrame(CalculateStackFrame()); + } + return m_recognized_frame_sp; +} diff --git a/gnu/llvm/lldb/source/Target/StackFrameList.cpp b/gnu/llvm/lldb/source/Target/StackFrameList.cpp new file mode 100644 index 00000000000..87b49849bc9 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/StackFrameList.cpp @@ -0,0 +1,995 @@ +//===-- StackFrameList.cpp --------------------------------------*- C++ -*-===// +// +// 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/StackFrameList.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/Log.h" +#include "llvm/ADT/SmallPtrSet.h" + +#include <memory> + +//#define DEBUG_STACK_FRAMES 1 + +using namespace lldb; +using namespace lldb_private; + +// StackFrameList constructor +StackFrameList::StackFrameList(Thread &thread, + const lldb::StackFrameListSP &prev_frames_sp, + bool show_inline_frames) + : m_thread(thread), m_prev_frames_sp(prev_frames_sp), m_mutex(), m_frames(), + m_selected_frame_idx(0), m_concrete_frames_fetched(0), + m_current_inlined_depth(UINT32_MAX), + m_current_inlined_pc(LLDB_INVALID_ADDRESS), + m_show_inlined_frames(show_inline_frames) { + if (prev_frames_sp) { + m_current_inlined_depth = prev_frames_sp->m_current_inlined_depth; + m_current_inlined_pc = prev_frames_sp->m_current_inlined_pc; + } +} + +StackFrameList::~StackFrameList() { + // Call clear since this takes a lock and clears the stack frame list in case + // another thread is currently using this stack frame list + Clear(); +} + +void StackFrameList::CalculateCurrentInlinedDepth() { + uint32_t cur_inlined_depth = GetCurrentInlinedDepth(); + if (cur_inlined_depth == UINT32_MAX) { + ResetCurrentInlinedDepth(); + } +} + +uint32_t StackFrameList::GetCurrentInlinedDepth() { + if (m_show_inlined_frames && m_current_inlined_pc != LLDB_INVALID_ADDRESS) { + lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC(); + if (cur_pc != m_current_inlined_pc) { + m_current_inlined_pc = LLDB_INVALID_ADDRESS; + m_current_inlined_depth = UINT32_MAX; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + LLDB_LOGF( + log, + "GetCurrentInlinedDepth: invalidating current inlined depth.\n"); + } + return m_current_inlined_depth; + } else { + return UINT32_MAX; + } +} + +void StackFrameList::ResetCurrentInlinedDepth() { + if (!m_show_inlined_frames) + return; + + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + GetFramesUpTo(0); + if (m_frames.empty()) + return; + if (!m_frames[0]->IsInlined()) { + m_current_inlined_depth = UINT32_MAX; + m_current_inlined_pc = LLDB_INVALID_ADDRESS; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + LLDB_LOGF( + log, + "ResetCurrentInlinedDepth: Invalidating current inlined depth.\n"); + return; + } + + // We only need to do something special about inlined blocks when we are + // at the beginning of an inlined function: + // FIXME: We probably also have to do something special if the PC is at + // the END of an inlined function, which coincides with the end of either + // its containing function or another inlined function. + + Block *block_ptr = m_frames[0]->GetFrameBlock(); + if (!block_ptr) + return; + + Address pc_as_address; + lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC(); + pc_as_address.SetLoadAddress(curr_pc, &(m_thread.GetProcess()->GetTarget())); + AddressRange containing_range; + if (!block_ptr->GetRangeContainingAddress(pc_as_address, containing_range) || + pc_as_address != containing_range.GetBaseAddress()) + return; + + // If we got here because of a breakpoint hit, then set the inlined depth + // depending on where the breakpoint was set. If we got here because of a + // crash, then set the inlined depth to the deepest most block. Otherwise, + // we stopped here naturally as the result of a step, so set ourselves in the + // containing frame of the whole set of nested inlines, so the user can then + // "virtually" step into the frames one by one, or next over the whole mess. + // Note: We don't have to handle being somewhere in the middle of the stack + // here, since ResetCurrentInlinedDepth doesn't get called if there is a + // valid inlined depth set. + StopInfoSP stop_info_sp = m_thread.GetStopInfo(); + if (!stop_info_sp) + return; + switch (stop_info_sp->GetStopReason()) { + case eStopReasonWatchpoint: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonSignal: + // In all these cases we want to stop in the deepest frame. + m_current_inlined_pc = curr_pc; + m_current_inlined_depth = 0; + break; + case eStopReasonBreakpoint: { + // FIXME: Figure out what this break point is doing, and set the inline + // depth appropriately. Be careful to take into account breakpoints that + // implement step over prologue, since that should do the default + // calculation. For now, if the breakpoints corresponding to this hit are + // all internal, I set the stop location to the top of the inlined stack, + // since that will make things like stepping over prologues work right. + // But if there are any non-internal breakpoints I do to the bottom of the + // stack, since that was the old behavior. + uint32_t bp_site_id = stop_info_sp->GetValue(); + BreakpointSiteSP bp_site_sp( + m_thread.GetProcess()->GetBreakpointSiteList().FindByID(bp_site_id)); + bool all_internal = true; + if (bp_site_sp) { + uint32_t num_owners = bp_site_sp->GetNumberOfOwners(); + for (uint32_t i = 0; i < num_owners; i++) { + Breakpoint &bp_ref = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); + if (!bp_ref.IsInternal()) { + all_internal = false; + } + } + } + if (!all_internal) { + m_current_inlined_pc = curr_pc; + m_current_inlined_depth = 0; + break; + } + } + LLVM_FALLTHROUGH; + default: { + // Otherwise, we should set ourselves at the container of the inlining, so + // that the user can descend into them. So first we check whether we have + // more than one inlined block sharing this PC: + int num_inlined_functions = 0; + + for (Block *container_ptr = block_ptr->GetInlinedParent(); + container_ptr != nullptr; + container_ptr = container_ptr->GetInlinedParent()) { + if (!container_ptr->GetRangeContainingAddress(pc_as_address, + containing_range)) + break; + if (pc_as_address != containing_range.GetBaseAddress()) + break; + + num_inlined_functions++; + } + m_current_inlined_pc = curr_pc; + m_current_inlined_depth = num_inlined_functions + 1; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + LLDB_LOGF(log, + "ResetCurrentInlinedDepth: setting inlined " + "depth: %d 0x%" PRIx64 ".\n", + m_current_inlined_depth, curr_pc); + + break; + } + } +} + +bool StackFrameList::DecrementCurrentInlinedDepth() { + if (m_show_inlined_frames) { + uint32_t current_inlined_depth = GetCurrentInlinedDepth(); + if (current_inlined_depth != UINT32_MAX) { + if (current_inlined_depth > 0) { + m_current_inlined_depth--; + return true; + } + } + } + return false; +} + +void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) { + m_current_inlined_depth = new_depth; + if (new_depth == UINT32_MAX) + m_current_inlined_pc = LLDB_INVALID_ADDRESS; + else + m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC(); +} + +void StackFrameList::GetOnlyConcreteFramesUpTo(uint32_t end_idx, + Unwind *unwinder) { + assert(m_thread.IsValid() && "Expected valid thread"); + assert(m_frames.size() <= end_idx && "Expected there to be frames to fill"); + + if (end_idx < m_concrete_frames_fetched) + return; + + if (!unwinder) + return; + + uint32_t num_frames = unwinder->GetFramesUpTo(end_idx); + if (num_frames <= end_idx + 1) { + // Done unwinding. + m_concrete_frames_fetched = UINT32_MAX; + } + + // Don't create the frames eagerly. Defer this work to GetFrameAtIndex, + // which can lazily query the unwinder to create frames. + m_frames.resize(num_frames); +} + +/// Find the unique path through the call graph from \p begin (with return PC +/// \p return_pc) to \p end. On success this path is stored into \p path, and +/// on failure \p path is unchanged. +static void FindInterveningFrames(Function &begin, Function &end, + ExecutionContext &exe_ctx, Target &target, + addr_t return_pc, + std::vector<Function *> &path, + ModuleList &images, Log *log) { + LLDB_LOG(log, "Finding frames between {0} and {1}, retn-pc={2:x}", + begin.GetDisplayName(), end.GetDisplayName(), return_pc); + + // Find a non-tail calling edge with the correct return PC. + if (log) + for (const auto &edge : begin.GetCallEdges()) + LLDB_LOG(log, "FindInterveningFrames: found call with retn-PC = {0:x}", + edge->GetReturnPCAddress(begin, target)); + CallEdge *first_edge = begin.GetCallEdgeForReturnAddress(return_pc, target); + if (!first_edge) { + LLDB_LOG(log, "No call edge outgoing from {0} with retn-PC == {1:x}", + begin.GetDisplayName(), return_pc); + return; + } + + // The first callee may not be resolved, or there may be nothing to fill in. + Function *first_callee = first_edge->GetCallee(images, exe_ctx); + if (!first_callee) { + LLDB_LOG(log, "Could not resolve callee"); + return; + } + if (first_callee == &end) { + LLDB_LOG(log, "Not searching further, first callee is {0} (retn-PC: {1:x})", + end.GetDisplayName(), return_pc); + return; + } + + // Run DFS on the tail-calling edges out of the first callee to find \p end. + // Fully explore the set of functions reachable from the first edge via tail + // calls in order to detect ambiguous executions. + struct DFS { + std::vector<Function *> active_path = {}; + std::vector<Function *> solution_path = {}; + llvm::SmallPtrSet<Function *, 2> visited_nodes = {}; + bool ambiguous = false; + Function *end; + ModuleList &images; + ExecutionContext &context; + + DFS(Function *end, ModuleList &images, ExecutionContext &context) + : end(end), images(images), context(context) {} + + void search(Function &first_callee, std::vector<Function *> &path) { + dfs(first_callee); + if (!ambiguous) + path = std::move(solution_path); + } + + void dfs(Function &callee) { + // Found a path to the target function. + if (&callee == end) { + if (solution_path.empty()) + solution_path = active_path; + else + ambiguous = true; + return; + } + + // Terminate the search if tail recursion is found, or more generally if + // there's more than one way to reach a target. This errs on the side of + // caution: it conservatively stops searching when some solutions are + // still possible to save time in the average case. + if (!visited_nodes.insert(&callee).second) { + ambiguous = true; + return; + } + + // Search the calls made from this callee. + active_path.push_back(&callee); + for (const auto &edge : callee.GetTailCallingEdges()) { + Function *next_callee = edge->GetCallee(images, context); + if (!next_callee) + continue; + + dfs(*next_callee); + if (ambiguous) + return; + } + active_path.pop_back(); + } + }; + + DFS(&end, images, exe_ctx).search(*first_callee, path); +} + +/// Given that \p next_frame will be appended to the frame list, synthesize +/// tail call frames between the current end of the list and \p next_frame. +/// If any frames are added, adjust the frame index of \p next_frame. +/// +/// -------------- +/// | ... | <- Completed frames. +/// -------------- +/// | prev_frame | +/// -------------- +/// | ... | <- Artificial frames inserted here. +/// -------------- +/// | next_frame | +/// -------------- +/// | ... | <- Not-yet-visited frames. +/// -------------- +void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) { + TargetSP target_sp = next_frame.CalculateTarget(); + if (!target_sp) + return; + + lldb::RegisterContextSP next_reg_ctx_sp = next_frame.GetRegisterContext(); + if (!next_reg_ctx_sp) + return; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + assert(!m_frames.empty() && "Cannot synthesize frames in an empty stack"); + StackFrame &prev_frame = *m_frames.back().get(); + + // Find the functions prev_frame and next_frame are stopped in. The function + // objects are needed to search the lazy call graph for intervening frames. + Function *prev_func = + prev_frame.GetSymbolContext(eSymbolContextFunction).function; + if (!prev_func) { + LLDB_LOG(log, "SynthesizeTailCallFrames: can't find previous function"); + return; + } + Function *next_func = + next_frame.GetSymbolContext(eSymbolContextFunction).function; + if (!next_func) { + LLDB_LOG(log, "SynthesizeTailCallFrames: can't find next function"); + return; + } + + // Try to find the unique sequence of (tail) calls which led from next_frame + // to prev_frame. + std::vector<Function *> path; + addr_t return_pc = next_reg_ctx_sp->GetPC(); + Target &target = *target_sp.get(); + ModuleList &images = next_frame.CalculateTarget()->GetImages(); + ExecutionContext exe_ctx(target_sp, /*get_process=*/true); + exe_ctx.SetFramePtr(&next_frame); + FindInterveningFrames(*next_func, *prev_func, exe_ctx, target, return_pc, + path, images, log); + + // Push synthetic tail call frames. + for (Function *callee : llvm::reverse(path)) { + uint32_t frame_idx = m_frames.size(); + uint32_t concrete_frame_idx = next_frame.GetConcreteFrameIndex(); + addr_t cfa = LLDB_INVALID_ADDRESS; + bool cfa_is_valid = false; + addr_t pc = + callee->GetAddressRange().GetBaseAddress().GetLoadAddress(&target); + constexpr bool behaves_like_zeroth_frame = false; + SymbolContext sc; + callee->CalculateSymbolContext(&sc); + auto synth_frame = std::make_shared<StackFrame>( + m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa, + cfa_is_valid, pc, StackFrame::Kind::Artificial, + behaves_like_zeroth_frame, &sc); + m_frames.push_back(synth_frame); + LLDB_LOG(log, "Pushed frame {0}", callee->GetDisplayName()); + } + + // If any frames were created, adjust next_frame's index. + if (!path.empty()) + next_frame.SetFrameIndex(m_frames.size()); +} + +void StackFrameList::GetFramesUpTo(uint32_t end_idx) { + // Do not fetch frames for an invalid thread. + if (!m_thread.IsValid()) + return; + + // We've already gotten more frames than asked for, or we've already finished + // unwinding, return. + if (m_frames.size() > end_idx || GetAllFramesFetched()) + return; + + Unwind *unwinder = m_thread.GetUnwinder(); + + if (!m_show_inlined_frames) { + GetOnlyConcreteFramesUpTo(end_idx, unwinder); + return; + } + +#if defined(DEBUG_STACK_FRAMES) + StreamFile s(stdout, false); +#endif + // If we are hiding some frames from the outside world, we need to add + // those onto the total count of frames to fetch. However, we don't need + // to do that if end_idx is 0 since in that case we always get the first + // concrete frame and all the inlined frames below it... And of course, if + // end_idx is UINT32_MAX that means get all, so just do that... + + uint32_t inlined_depth = 0; + if (end_idx > 0 && end_idx != UINT32_MAX) { + inlined_depth = GetCurrentInlinedDepth(); + if (inlined_depth != UINT32_MAX) { + if (end_idx > 0) + end_idx += inlined_depth; + } + } + + StackFrameSP unwind_frame_sp; + do { + uint32_t idx = m_concrete_frames_fetched++; + lldb::addr_t pc = LLDB_INVALID_ADDRESS; + lldb::addr_t cfa = LLDB_INVALID_ADDRESS; + bool behaves_like_zeroth_frame = (idx == 0); + if (idx == 0) { + // We might have already created frame zero, only create it if we need + // to. + if (m_frames.empty()) { + RegisterContextSP reg_ctx_sp(m_thread.GetRegisterContext()); + + if (reg_ctx_sp) { + const bool success = unwinder && + unwinder->GetFrameInfoAtIndex( + idx, cfa, pc, behaves_like_zeroth_frame); + // There shouldn't be any way not to get the frame info for frame + // 0. But if the unwinder can't make one, lets make one by hand + // with the SP as the CFA and see if that gets any further. + if (!success) { + cfa = reg_ctx_sp->GetSP(); + pc = reg_ctx_sp->GetPC(); + } + + unwind_frame_sp = std::make_shared<StackFrame>( + m_thread.shared_from_this(), m_frames.size(), idx, reg_ctx_sp, + cfa, pc, behaves_like_zeroth_frame, nullptr); + m_frames.push_back(unwind_frame_sp); + } + } else { + unwind_frame_sp = m_frames.front(); + cfa = unwind_frame_sp->m_id.GetCallFrameAddress(); + } + } else { + const bool success = unwinder && + unwinder->GetFrameInfoAtIndex( + idx, cfa, pc, behaves_like_zeroth_frame); + if (!success) { + // We've gotten to the end of the stack. + SetAllFramesFetched(); + break; + } + const bool cfa_is_valid = true; + unwind_frame_sp = std::make_shared<StackFrame>( + m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid, + pc, StackFrame::Kind::Regular, behaves_like_zeroth_frame, nullptr); + + // Create synthetic tail call frames between the previous frame and the + // newly-found frame. The new frame's index may change after this call, + // although its concrete index will stay the same. + SynthesizeTailCallFrames(*unwind_frame_sp.get()); + + m_frames.push_back(unwind_frame_sp); + } + + assert(unwind_frame_sp); + SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext( + eSymbolContextBlock | eSymbolContextFunction); + Block *unwind_block = unwind_sc.block; + if (unwind_block) { + Address curr_frame_address(unwind_frame_sp->GetFrameCodeAddress()); + TargetSP target_sp = m_thread.CalculateTarget(); + // Be sure to adjust the frame address to match the address that was + // used to lookup the symbol context above. If we are in the first + // concrete frame, then we lookup using the current address, else we + // decrement the address by one to get the correct location. + if (idx > 0) { + if (curr_frame_address.GetOffset() == 0) { + // If curr_frame_address points to the first address in a section + // then after adjustment it will point to an other section. In that + // case resolve the address again to the correct section plus + // offset form. + addr_t load_addr = curr_frame_address.GetOpcodeLoadAddress( + target_sp.get(), AddressClass::eCode); + curr_frame_address.SetOpcodeLoadAddress( + load_addr - 1, target_sp.get(), AddressClass::eCode); + } else { + curr_frame_address.Slide(-1); + } + } + + SymbolContext next_frame_sc; + Address next_frame_address; + + while (unwind_sc.GetParentOfInlinedScope( + curr_frame_address, next_frame_sc, next_frame_address)) { + next_frame_sc.line_entry.ApplyFileMappings(target_sp); + behaves_like_zeroth_frame = false; + StackFrameSP frame_sp(new StackFrame( + m_thread.shared_from_this(), m_frames.size(), idx, + unwind_frame_sp->GetRegisterContextSP(), cfa, next_frame_address, + behaves_like_zeroth_frame, &next_frame_sc)); + + m_frames.push_back(frame_sp); + unwind_sc = next_frame_sc; + curr_frame_address = next_frame_address; + } + } + } while (m_frames.size() - 1 < end_idx); + + // Don't try to merge till you've calculated all the frames in this stack. + if (GetAllFramesFetched() && m_prev_frames_sp) { + StackFrameList *prev_frames = m_prev_frames_sp.get(); + StackFrameList *curr_frames = this; + +#if defined(DEBUG_STACK_FRAMES) + s.PutCString("\nprev_frames:\n"); + prev_frames->Dump(&s); + s.PutCString("\ncurr_frames:\n"); + curr_frames->Dump(&s); + s.EOL(); +#endif + size_t curr_frame_num, prev_frame_num; + + for (curr_frame_num = curr_frames->m_frames.size(), + prev_frame_num = prev_frames->m_frames.size(); + curr_frame_num > 0 && prev_frame_num > 0; + --curr_frame_num, --prev_frame_num) { + const size_t curr_frame_idx = curr_frame_num - 1; + const size_t prev_frame_idx = prev_frame_num - 1; + StackFrameSP curr_frame_sp(curr_frames->m_frames[curr_frame_idx]); + StackFrameSP prev_frame_sp(prev_frames->m_frames[prev_frame_idx]); + +#if defined(DEBUG_STACK_FRAMES) + s.Printf("\n\nCurr frame #%u ", curr_frame_idx); + if (curr_frame_sp) + curr_frame_sp->Dump(&s, true, false); + else + s.PutCString("NULL"); + s.Printf("\nPrev frame #%u ", prev_frame_idx); + if (prev_frame_sp) + prev_frame_sp->Dump(&s, true, false); + else + s.PutCString("NULL"); +#endif + + StackFrame *curr_frame = curr_frame_sp.get(); + StackFrame *prev_frame = prev_frame_sp.get(); + + if (curr_frame == nullptr || prev_frame == nullptr) + break; + + // Check the stack ID to make sure they are equal. + if (curr_frame->GetStackID() != prev_frame->GetStackID()) + break; + + prev_frame->UpdatePreviousFrameFromCurrentFrame(*curr_frame); + // Now copy the fixed up previous frame into the current frames so the + // pointer doesn't change. + m_frames[curr_frame_idx] = prev_frame_sp; + +#if defined(DEBUG_STACK_FRAMES) + s.Printf("\n Copying previous frame to current frame"); +#endif + } + // We are done with the old stack frame list, we can release it now. + m_prev_frames_sp.reset(); + } + +#if defined(DEBUG_STACK_FRAMES) + s.PutCString("\n\nNew frames:\n"); + Dump(&s); + s.EOL(); +#endif +} + +uint32_t StackFrameList::GetNumFrames(bool can_create) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + if (can_create) + GetFramesUpTo(UINT32_MAX); + + return GetVisibleStackFrameIndex(m_frames.size()); +} + +void StackFrameList::Dump(Stream *s) { + if (s == nullptr) + return; + + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + const_iterator pos, begin = m_frames.begin(), end = m_frames.end(); + for (pos = begin; pos != end; ++pos) { + StackFrame *frame = (*pos).get(); + s->Printf("%p: ", static_cast<void *>(frame)); + if (frame) { + frame->GetStackID().Dump(s); + frame->DumpUsingSettingsFormat(s); + } else + s->Printf("frame #%u", (uint32_t)std::distance(begin, pos)); + s->EOL(); + } + s->EOL(); +} + +StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) { + StackFrameSP frame_sp; + std::lock_guard<std::recursive_mutex> guard(m_mutex); + uint32_t original_idx = idx; + + uint32_t inlined_depth = GetCurrentInlinedDepth(); + if (inlined_depth != UINT32_MAX) + idx += inlined_depth; + + if (idx < m_frames.size()) + frame_sp = m_frames[idx]; + + if (frame_sp) + return frame_sp; + + // GetFramesUpTo will fill m_frames with as many frames as you asked for, if + // there are that many. If there weren't then you asked for too many frames. + GetFramesUpTo(idx); + if (idx < m_frames.size()) { + if (m_show_inlined_frames) { + // When inline frames are enabled we actually create all the frames in + // GetFramesUpTo. + frame_sp = m_frames[idx]; + } else { + Unwind *unwinder = m_thread.GetUnwinder(); + if (unwinder) { + addr_t pc, cfa; + bool behaves_like_zeroth_frame = (idx == 0); + if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc, + behaves_like_zeroth_frame)) { + const bool cfa_is_valid = true; + frame_sp = std::make_shared<StackFrame>( + m_thread.shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, + StackFrame::Kind::Regular, behaves_like_zeroth_frame, nullptr); + + Function *function = + frame_sp->GetSymbolContext(eSymbolContextFunction).function; + if (function) { + // When we aren't showing inline functions we always use the top + // most function block as the scope. + frame_sp->SetSymbolContextScope(&function->GetBlock(false)); + } else { + // Set the symbol scope from the symbol regardless if it is nullptr + // or valid. + frame_sp->SetSymbolContextScope( + frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol); + } + SetFrameAtIndex(idx, frame_sp); + } + } + } + } else if (original_idx == 0) { + // There should ALWAYS be a frame at index 0. If something went wrong with + // the CurrentInlinedDepth such that there weren't as many frames as we + // thought taking that into account, then reset the current inlined depth + // and return the real zeroth frame. + if (m_frames.empty()) { + // Why do we have a thread with zero frames, that should not ever + // happen... + assert(!m_thread.IsValid() && "A valid thread has no frames."); + } else { + ResetCurrentInlinedDepth(); + frame_sp = m_frames[original_idx]; + } + } + + return frame_sp; +} + +StackFrameSP +StackFrameList::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) { + // First try assuming the unwind index is the same as the frame index. The + // unwind index is always greater than or equal to the frame index, so it is + // a good place to start. If we have inlined frames we might have 5 concrete + // frames (frame unwind indexes go from 0-4), but we might have 15 frames + // after we make all the inlined frames. Most of the time the unwind frame + // index (or the concrete frame index) is the same as the frame index. + uint32_t frame_idx = unwind_idx; + StackFrameSP frame_sp(GetFrameAtIndex(frame_idx)); + while (frame_sp) { + if (frame_sp->GetFrameIndex() == unwind_idx) + break; + frame_sp = GetFrameAtIndex(++frame_idx); + } + return frame_sp; +} + +static bool CompareStackID(const StackFrameSP &stack_sp, + const StackID &stack_id) { + return stack_sp->GetStackID() < stack_id; +} + +StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) { + StackFrameSP frame_sp; + + if (stack_id.IsValid()) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + uint32_t frame_idx = 0; + // Do a binary search in case the stack frame is already in our cache + collection::const_iterator begin = m_frames.begin(); + collection::const_iterator end = m_frames.end(); + if (begin != end) { + collection::const_iterator pos = + std::lower_bound(begin, end, stack_id, CompareStackID); + if (pos != end) { + if ((*pos)->GetStackID() == stack_id) + return *pos; + } + } + do { + frame_sp = GetFrameAtIndex(frame_idx); + if (frame_sp && frame_sp->GetStackID() == stack_id) + break; + frame_idx++; + } while (frame_sp); + } + return frame_sp; +} + +bool StackFrameList::SetFrameAtIndex(uint32_t idx, StackFrameSP &frame_sp) { + if (idx >= m_frames.size()) + m_frames.resize(idx + 1); + // Make sure allocation succeeded by checking bounds again + if (idx < m_frames.size()) { + m_frames[idx] = frame_sp; + return true; + } + return false; // resize failed, out of memory? +} + +uint32_t StackFrameList::GetSelectedFrameIndex() const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + return m_selected_frame_idx; +} + +uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + const_iterator pos; + const_iterator begin = m_frames.begin(); + const_iterator end = m_frames.end(); + m_selected_frame_idx = 0; + for (pos = begin; pos != end; ++pos) { + if (pos->get() == frame) { + m_selected_frame_idx = std::distance(begin, pos); + uint32_t inlined_depth = GetCurrentInlinedDepth(); + if (inlined_depth != UINT32_MAX) + m_selected_frame_idx -= inlined_depth; + break; + } + } + SetDefaultFileAndLineToSelectedFrame(); + return m_selected_frame_idx; +} + +bool StackFrameList::SetSelectedFrameByIndex(uint32_t idx) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + StackFrameSP frame_sp(GetFrameAtIndex(idx)); + if (frame_sp) { + SetSelectedFrame(frame_sp.get()); + return true; + } else + return false; +} + +void StackFrameList::SetDefaultFileAndLineToSelectedFrame() { + if (m_thread.GetID() == + m_thread.GetProcess()->GetThreadList().GetSelectedThread()->GetID()) { + StackFrameSP frame_sp(GetFrameAtIndex(GetSelectedFrameIndex())); + if (frame_sp) { + SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextLineEntry); + if (sc.line_entry.file) + m_thread.CalculateTarget()->GetSourceManager().SetDefaultFileAndLine( + sc.line_entry.file, sc.line_entry.line); + } + } +} + +// The thread has been run, reset the number stack frames to zero so we can +// determine how many frames we have lazily. +void StackFrameList::Clear() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_frames.clear(); + m_concrete_frames_fetched = 0; +} + +void StackFrameList::Merge(std::unique_ptr<StackFrameList> &curr_up, + lldb::StackFrameListSP &prev_sp) { + std::unique_lock<std::recursive_mutex> current_lock, previous_lock; + if (curr_up) + current_lock = std::unique_lock<std::recursive_mutex>(curr_up->m_mutex); + if (prev_sp) + previous_lock = std::unique_lock<std::recursive_mutex>(prev_sp->m_mutex); + +#if defined(DEBUG_STACK_FRAMES) + StreamFile s(stdout, false); + s.PutCString("\n\nStackFrameList::Merge():\nPrev:\n"); + if (prev_sp) + prev_sp->Dump(&s); + else + s.PutCString("NULL"); + s.PutCString("\nCurr:\n"); + if (curr_up) + curr_up->Dump(&s); + else + s.PutCString("NULL"); + s.EOL(); +#endif + + if (!curr_up || curr_up->GetNumFrames(false) == 0) { +#if defined(DEBUG_STACK_FRAMES) + s.PutCString("No current frames, leave previous frames alone...\n"); +#endif + curr_up.release(); + return; + } + + if (!prev_sp || prev_sp->GetNumFrames(false) == 0) { +#if defined(DEBUG_STACK_FRAMES) + s.PutCString("No previous frames, so use current frames...\n"); +#endif + // We either don't have any previous frames, or since we have more than one + // current frames it means we have all the frames and can safely replace + // our previous frames. + prev_sp.reset(curr_up.release()); + return; + } + + const uint32_t num_curr_frames = curr_up->GetNumFrames(false); + + if (num_curr_frames > 1) { +#if defined(DEBUG_STACK_FRAMES) + s.PutCString( + "We have more than one current frame, so use current frames...\n"); +#endif + // We have more than one current frames it means we have all the frames and + // can safely replace our previous frames. + prev_sp.reset(curr_up.release()); + +#if defined(DEBUG_STACK_FRAMES) + s.PutCString("\nMerged:\n"); + prev_sp->Dump(&s); +#endif + return; + } + + StackFrameSP prev_frame_zero_sp(prev_sp->GetFrameAtIndex(0)); + StackFrameSP curr_frame_zero_sp(curr_up->GetFrameAtIndex(0)); + StackID curr_stack_id(curr_frame_zero_sp->GetStackID()); + StackID prev_stack_id(prev_frame_zero_sp->GetStackID()); + +#if defined(DEBUG_STACK_FRAMES) + const uint32_t num_prev_frames = prev_sp->GetNumFrames(false); + s.Printf("\n%u previous frames with one current frame\n", num_prev_frames); +#endif + + // We have only a single current frame + // Our previous stack frames only had a single frame as well... + if (curr_stack_id == prev_stack_id) { +#if defined(DEBUG_STACK_FRAMES) + s.Printf("\nPrevious frame #0 is same as current frame #0, merge the " + "cached data\n"); +#endif + + curr_frame_zero_sp->UpdateCurrentFrameFromPreviousFrame( + *prev_frame_zero_sp); + // prev_frame_zero_sp->UpdatePreviousFrameFromCurrentFrame + // (*curr_frame_zero_sp); + // prev_sp->SetFrameAtIndex (0, prev_frame_zero_sp); + } else if (curr_stack_id < prev_stack_id) { +#if defined(DEBUG_STACK_FRAMES) + s.Printf("\nCurrent frame #0 has a stack ID that is less than the previous " + "frame #0, insert current frame zero in front of previous\n"); +#endif + prev_sp->m_frames.insert(prev_sp->m_frames.begin(), curr_frame_zero_sp); + } + + curr_up.release(); + +#if defined(DEBUG_STACK_FRAMES) + s.PutCString("\nMerged:\n"); + prev_sp->Dump(&s); +#endif +} + +lldb::StackFrameSP +StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) { + const_iterator pos; + const_iterator begin = m_frames.begin(); + const_iterator end = m_frames.end(); + lldb::StackFrameSP ret_sp; + + for (pos = begin; pos != end; ++pos) { + if (pos->get() == stack_frame_ptr) { + ret_sp = (*pos); + break; + } + } + return ret_sp; +} + +size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame, + uint32_t num_frames, bool show_frame_info, + uint32_t num_frames_with_source, + bool show_unique, + const char *selected_frame_marker) { + size_t num_frames_displayed = 0; + + if (num_frames == 0) + return 0; + + StackFrameSP frame_sp; + uint32_t frame_idx = 0; + uint32_t last_frame; + + // Don't let the last frame wrap around... + if (num_frames == UINT32_MAX) + last_frame = UINT32_MAX; + else + last_frame = first_frame + num_frames; + + StackFrameSP selected_frame_sp = m_thread.GetSelectedFrame(); + const char *unselected_marker = nullptr; + std::string buffer; + if (selected_frame_marker) { + size_t len = strlen(selected_frame_marker); + buffer.insert(buffer.begin(), len, ' '); + unselected_marker = buffer.c_str(); + } + const char *marker = nullptr; + + for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx) { + frame_sp = GetFrameAtIndex(frame_idx); + if (!frame_sp) + break; + + if (selected_frame_marker != nullptr) { + if (frame_sp == selected_frame_sp) + marker = selected_frame_marker; + else + marker = unselected_marker; + } + + if (!frame_sp->GetStatus(strm, show_frame_info, + num_frames_with_source > (first_frame - frame_idx), + show_unique, marker)) + break; + ++num_frames_displayed; + } + + strm.IndentLess(); + return num_frames_displayed; +} diff --git a/gnu/llvm/lldb/source/Target/StackFrameRecognizer.cpp b/gnu/llvm/lldb/source/Target/StackFrameRecognizer.cpp new file mode 100644 index 00000000000..75a6cd21512 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/StackFrameRecognizer.cpp @@ -0,0 +1,192 @@ +//===-- StackFrameRecognizer.cpp --------------------------------*- C++ -*-===// +// +// 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 <vector> +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/StackFrameRecognizer.h" +#include "lldb/Utility/RegularExpression.h" + +using namespace lldb; +using namespace lldb_private; + +class ScriptedRecognizedStackFrame : public RecognizedStackFrame { +public: + ScriptedRecognizedStackFrame(ValueObjectListSP args) { + m_arguments = args; + } +}; + +ScriptedStackFrameRecognizer::ScriptedStackFrameRecognizer( + ScriptInterpreter *interpreter, const char *pclass) + : m_interpreter(interpreter), m_python_class(pclass) { + m_python_object_sp = + m_interpreter->CreateFrameRecognizer(m_python_class.c_str()); +} + +RecognizedStackFrameSP +ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) { + if (!m_python_object_sp || !m_interpreter) + return RecognizedStackFrameSP(); + + ValueObjectListSP args = + m_interpreter->GetRecognizedArguments(m_python_object_sp, frame); + auto args_synthesized = ValueObjectListSP(new ValueObjectList()); + for (const auto &o : args->GetObjects()) { + args_synthesized->Append(ValueObjectRecognizerSynthesizedValue::Create( + *o, eValueTypeVariableArgument)); + } + + return RecognizedStackFrameSP( + new ScriptedRecognizedStackFrame(args_synthesized)); +} + +class StackFrameRecognizerManagerImpl { +public: + void AddRecognizer(StackFrameRecognizerSP recognizer, + ConstString module, ConstString symbol, + bool first_instruction_only) { + m_recognizers.push_front({(uint32_t)m_recognizers.size(), false, recognizer, false, module, RegularExpressionSP(), + symbol, RegularExpressionSP(), + first_instruction_only}); + } + + void AddRecognizer(StackFrameRecognizerSP recognizer, + RegularExpressionSP module, RegularExpressionSP symbol, + bool first_instruction_only) { + m_recognizers.push_front({(uint32_t)m_recognizers.size(), false, recognizer, true, ConstString(), module, + ConstString(), symbol, first_instruction_only}); + } + + void ForEach( + std::function<void(uint32_t recognized_id, std::string recognizer_name, std::string module, + std::string symbol, bool regexp)> const &callback) { + for (auto entry : m_recognizers) { + if (entry.is_regexp) { + callback(entry.recognizer_id, entry.recognizer->GetName(), entry.module_regexp->GetText(), + entry.symbol_regexp->GetText(), true); + } else { + callback(entry.recognizer_id, entry.recognizer->GetName(), entry.module.GetCString(), + entry.symbol.GetCString(), false); + } + } + } + + bool RemoveRecognizerWithID(uint32_t recognizer_id) { + if (recognizer_id >= m_recognizers.size()) return false; + if (m_recognizers[recognizer_id].deleted) return false; + m_recognizers[recognizer_id].deleted = true; + return true; + } + + void RemoveAllRecognizers() { + m_recognizers.clear(); + } + + StackFrameRecognizerSP GetRecognizerForFrame(StackFrameSP frame) { + const SymbolContext &symctx = + frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction); + ConstString function_name = symctx.GetFunctionName(); + ModuleSP module_sp = symctx.module_sp; + if (!module_sp) return StackFrameRecognizerSP(); + ConstString module_name = module_sp->GetFileSpec().GetFilename(); + Symbol *symbol = symctx.symbol; + if (!symbol) return StackFrameRecognizerSP(); + Address start_addr = symbol->GetAddress(); + Address current_addr = frame->GetFrameCodeAddress(); + + for (auto entry : m_recognizers) { + if (entry.deleted) continue; + if (entry.module) + if (entry.module != module_name) continue; + + if (entry.module_regexp) + if (!entry.module_regexp->Execute(module_name.GetStringRef())) continue; + + if (entry.symbol) + if (entry.symbol != function_name) continue; + + if (entry.symbol_regexp) + if (!entry.symbol_regexp->Execute(function_name.GetStringRef())) + continue; + + if (entry.first_instruction_only) + if (start_addr != current_addr) continue; + + return entry.recognizer; + } + return StackFrameRecognizerSP(); + } + + RecognizedStackFrameSP RecognizeFrame(StackFrameSP frame) { + auto recognizer = GetRecognizerForFrame(frame); + if (!recognizer) return RecognizedStackFrameSP(); + return recognizer->RecognizeFrame(frame); + } + + private: + struct RegisteredEntry { + uint32_t recognizer_id; + bool deleted; + StackFrameRecognizerSP recognizer; + bool is_regexp; + ConstString module; + RegularExpressionSP module_regexp; + ConstString symbol; + RegularExpressionSP symbol_regexp; + bool first_instruction_only; + }; + + std::deque<RegisteredEntry> m_recognizers; +}; + +StackFrameRecognizerManagerImpl &GetStackFrameRecognizerManagerImpl() { + static StackFrameRecognizerManagerImpl instance = + StackFrameRecognizerManagerImpl(); + return instance; +} + +void StackFrameRecognizerManager::AddRecognizer( + StackFrameRecognizerSP recognizer, ConstString module, + ConstString symbol, bool first_instruction_only) { + GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol, + first_instruction_only); +} + +void StackFrameRecognizerManager::AddRecognizer( + StackFrameRecognizerSP recognizer, RegularExpressionSP module, + RegularExpressionSP symbol, bool first_instruction_only) { + GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol, + first_instruction_only); +} + +void StackFrameRecognizerManager::ForEach( + std::function<void(uint32_t recognized_id, std::string recognizer_name, std::string module, + std::string symbol, bool regexp)> const &callback) { + GetStackFrameRecognizerManagerImpl().ForEach(callback); +} + +void StackFrameRecognizerManager::RemoveAllRecognizers() { + GetStackFrameRecognizerManagerImpl().RemoveAllRecognizers(); +} + +bool StackFrameRecognizerManager::RemoveRecognizerWithID(uint32_t recognizer_id) { + return GetStackFrameRecognizerManagerImpl().RemoveRecognizerWithID(recognizer_id); +} + +RecognizedStackFrameSP StackFrameRecognizerManager::RecognizeFrame( + StackFrameSP frame) { + return GetStackFrameRecognizerManagerImpl().RecognizeFrame(frame); +} + +StackFrameRecognizerSP StackFrameRecognizerManager::GetRecognizerForFrame( + lldb::StackFrameSP frame) { + return GetStackFrameRecognizerManagerImpl().GetRecognizerForFrame(frame); +} diff --git a/gnu/llvm/lldb/source/Target/StackID.cpp b/gnu/llvm/lldb/source/Target/StackID.cpp new file mode 100644 index 00000000000..a8f6b787f4b --- /dev/null +++ b/gnu/llvm/lldb/source/Target/StackID.cpp @@ -0,0 +1,97 @@ +//===-- StackID.cpp ---------------------------------------------*- C++ -*-===// +// +// 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/StackID.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb_private; + +void StackID::Dump(Stream *s) { + s->Printf("StackID (pc = 0x%16.16" PRIx64 ", cfa = 0x%16.16" PRIx64 + ", symbol_scope = %p", + m_pc, m_cfa, static_cast<void *>(m_symbol_scope)); + if (m_symbol_scope) { + SymbolContext sc; + + m_symbol_scope->CalculateSymbolContext(&sc); + if (sc.block) + s->Printf(" (Block {0x%8.8" PRIx64 "})", sc.block->GetID()); + else if (sc.symbol) + s->Printf(" (Symbol{0x%8.8x})", sc.symbol->GetID()); + } + s->PutCString(") "); +} + +bool lldb_private::operator==(const StackID &lhs, const StackID &rhs) { + if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress()) + return false; + + SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope(); + SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope(); + + // Only compare the PC values if both symbol context scopes are nullptr + if (lhs_scope == nullptr && rhs_scope == nullptr) + return lhs.GetPC() == rhs.GetPC(); + + return lhs_scope == rhs_scope; +} + +bool lldb_private::operator!=(const StackID &lhs, const StackID &rhs) { + if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress()) + return true; + + SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope(); + SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope(); + + if (lhs_scope == nullptr && rhs_scope == nullptr) + return lhs.GetPC() != rhs.GetPC(); + + return lhs_scope != rhs_scope; +} + +bool lldb_private::operator<(const StackID &lhs, const StackID &rhs) { + const lldb::addr_t lhs_cfa = lhs.GetCallFrameAddress(); + const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddress(); + + // FIXME: We are assuming that the stacks grow downward in memory. That's not + // necessary, but true on + // all the machines we care about at present. If this changes, we'll have to + // deal with that. The ABI is the agent who knows this ordering, but the + // StackID has no access to the ABI. The most straightforward way to handle + // this is to add a "m_grows_downward" bool to the StackID, and set it in the + // constructor. But I'm not going to waste a bool per StackID on this till we + // need it. + + if (lhs_cfa != rhs_cfa) + return lhs_cfa < rhs_cfa; + + SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope(); + SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope(); + + if (lhs_scope != nullptr && rhs_scope != nullptr) { + // Same exact scope, lhs is not less than (younger than rhs) + if (lhs_scope == rhs_scope) + return false; + + SymbolContext lhs_sc; + SymbolContext rhs_sc; + lhs_scope->CalculateSymbolContext(&lhs_sc); + rhs_scope->CalculateSymbolContext(&rhs_sc); + + // Items with the same function can only be compared + if (lhs_sc.function == rhs_sc.function && lhs_sc.function != nullptr && + lhs_sc.block != nullptr && rhs_sc.function != nullptr && + rhs_sc.block != nullptr) { + return rhs_sc.block->Contains(lhs_sc.block); + } + } + return false; +} diff --git a/gnu/llvm/lldb/source/Target/StopInfo.cpp b/gnu/llvm/lldb/source/Target/StopInfo.cpp new file mode 100644 index 00000000000..28179b7e1ce --- /dev/null +++ b/gnu/llvm/lldb/source/Target/StopInfo.cpp @@ -0,0 +1,1198 @@ +//===-- StopInfo.cpp --------------------------------------------*- C++ -*-===// +// +// 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 <string> + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +StopInfo::StopInfo(Thread &thread, uint64_t value) + : m_thread_wp(thread.shared_from_this()), + m_stop_id(thread.GetProcess()->GetStopID()), + m_resume_id(thread.GetProcess()->GetResumeID()), m_value(value), + m_description(), m_override_should_notify(eLazyBoolCalculate), + m_override_should_stop(eLazyBoolCalculate), m_extended_info() {} + +bool StopInfo::IsValid() const { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) + return thread_sp->GetProcess()->GetStopID() == m_stop_id; + return false; +} + +void StopInfo::MakeStopInfoValid() { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + m_stop_id = thread_sp->GetProcess()->GetStopID(); + m_resume_id = thread_sp->GetProcess()->GetResumeID(); + } +} + +bool StopInfo::HasTargetRunSinceMe() { + ThreadSP thread_sp(m_thread_wp.lock()); + + if (thread_sp) { + lldb::StateType ret_type = thread_sp->GetProcess()->GetPrivateState(); + if (ret_type == eStateRunning) { + return true; + } else if (ret_type == eStateStopped) { + // This is a little tricky. We want to count "run and stopped again + // before you could ask this question as a "TRUE" answer to + // HasTargetRunSinceMe. But we don't want to include any running of the + // target done for expressions. So we track both resumes, and resumes + // caused by expressions, and check if there are any resumes + // NOT caused + // by expressions. + + uint32_t curr_resume_id = thread_sp->GetProcess()->GetResumeID(); + uint32_t last_user_expression_id = + thread_sp->GetProcess()->GetLastUserExpressionResumeID(); + if (curr_resume_id == m_resume_id) { + return false; + } else if (curr_resume_id > last_user_expression_id) { + return true; + } + } + } + return false; +} + +// StopInfoBreakpoint + +namespace lldb_private { +class StopInfoBreakpoint : public StopInfo { +public: + StopInfoBreakpoint(Thread &thread, break_id_t break_id) + : StopInfo(thread, break_id), m_should_stop(false), + m_should_stop_is_valid(false), m_should_perform_action(true), + m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID), + m_was_one_shot(false) { + StoreBPInfo(); + } + + StopInfoBreakpoint(Thread &thread, break_id_t break_id, bool should_stop) + : StopInfo(thread, break_id), m_should_stop(should_stop), + m_should_stop_is_valid(true), m_should_perform_action(true), + m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID), + m_was_one_shot(false) { + StoreBPInfo(); + } + + ~StopInfoBreakpoint() override = default; + + void StoreBPInfo() { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + BreakpointSiteSP bp_site_sp( + thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + if (bp_site_sp) { + if (bp_site_sp->GetNumberOfOwners() == 1) { + BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(0); + if (bp_loc_sp) { + m_break_id = bp_loc_sp->GetBreakpoint().GetID(); + m_was_one_shot = bp_loc_sp->GetBreakpoint().IsOneShot(); + } + } + m_address = bp_site_sp->GetLoadAddress(); + } + } + } + + bool IsValidForOperatingSystemThread(Thread &thread) override { + ProcessSP process_sp(thread.GetProcess()); + if (process_sp) { + BreakpointSiteSP bp_site_sp( + process_sp->GetBreakpointSiteList().FindByID(m_value)); + if (bp_site_sp) + return bp_site_sp->ValidForThisThread(&thread); + } + return false; + } + + StopReason GetStopReason() const override { return eStopReasonBreakpoint; } + + bool ShouldStopSynchronous(Event *event_ptr) override { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + if (!m_should_stop_is_valid) { + // Only check once if we should stop at a breakpoint + BreakpointSiteSP bp_site_sp( + thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + if (bp_site_sp) { + ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); + StoppointCallbackContext context(event_ptr, exe_ctx, true); + bp_site_sp->BumpHitCounts(); + m_should_stop = bp_site_sp->ShouldStop(&context); + } else { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + LLDB_LOGF(log, + "Process::%s could not find breakpoint site id: %" PRId64 + "...", + __FUNCTION__, m_value); + + m_should_stop = true; + } + m_should_stop_is_valid = true; + } + return m_should_stop; + } + return false; + } + + bool DoShouldNotify(Event *event_ptr) override { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + BreakpointSiteSP bp_site_sp( + thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + if (bp_site_sp) { + bool all_internal = true; + + for (uint32_t i = 0; i < bp_site_sp->GetNumberOfOwners(); i++) { + if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) { + all_internal = false; + break; + } + } + return !all_internal; + } + } + return true; + } + + const char *GetDescription() override { + if (m_description.empty()) { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + BreakpointSiteSP bp_site_sp( + thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + if (bp_site_sp) { + StreamString strm; + // If we have just hit an internal breakpoint, and it has a kind + // description, print that instead of the full breakpoint printing: + if (bp_site_sp->IsInternal()) { + size_t num_owners = bp_site_sp->GetNumberOfOwners(); + for (size_t idx = 0; idx < num_owners; idx++) { + const char *kind = bp_site_sp->GetOwnerAtIndex(idx) + ->GetBreakpoint() + .GetBreakpointKind(); + if (kind != nullptr) { + m_description.assign(kind); + return kind; + } + } + } + + strm.Printf("breakpoint "); + bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief); + m_description = strm.GetString(); + } else { + StreamString strm; + if (m_break_id != LLDB_INVALID_BREAK_ID) { + BreakpointSP break_sp = + thread_sp->GetProcess()->GetTarget().GetBreakpointByID( + m_break_id); + if (break_sp) { + if (break_sp->IsInternal()) { + const char *kind = break_sp->GetBreakpointKind(); + if (kind) + strm.Printf("internal %s breakpoint(%d).", kind, m_break_id); + else + strm.Printf("internal breakpoint(%d).", m_break_id); + } else { + strm.Printf("breakpoint %d.", m_break_id); + } + } else { + if (m_was_one_shot) + strm.Printf("one-shot breakpoint %d", m_break_id); + else + strm.Printf("breakpoint %d which has been deleted.", + m_break_id); + } + } else if (m_address == LLDB_INVALID_ADDRESS) + strm.Printf("breakpoint site %" PRIi64 + " which has been deleted - unknown address", + m_value); + else + strm.Printf("breakpoint site %" PRIi64 + " which has been deleted - was at 0x%" PRIx64, + m_value, m_address); + + m_description = strm.GetString(); + } + } + } + return m_description.c_str(); + } + +protected: + bool ShouldStop(Event *event_ptr) override { + // This just reports the work done by PerformAction or the synchronous + // stop. It should only ever get called after they have had a chance to + // run. + assert(m_should_stop_is_valid); + return m_should_stop; + } + + void PerformAction(Event *event_ptr) override { + if (!m_should_perform_action) + return; + m_should_perform_action = false; + bool internal_breakpoint = true; + + ThreadSP thread_sp(m_thread_wp.lock()); + + if (thread_sp) { + Log *log = lldb_private::GetLogIfAnyCategoriesSet( + LIBLLDB_LOG_BREAKPOINTS | LIBLLDB_LOG_STEP); + + if (!thread_sp->IsValid()) { + // This shouldn't ever happen, but just in case, don't do more harm. + if (log) { + LLDB_LOGF(log, "PerformAction got called with an invalid thread."); + } + m_should_stop = true; + m_should_stop_is_valid = true; + return; + } + + BreakpointSiteSP bp_site_sp( + thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + std::unordered_set<break_id_t> precondition_breakpoints; + + if (bp_site_sp) { + // Let's copy the owners list out of the site and store them in a local + // list. That way if one of the breakpoint actions changes the site, + // then we won't be operating on a bad list. + BreakpointLocationCollection site_locations; + size_t num_owners = bp_site_sp->CopyOwnersList(site_locations); + + if (num_owners == 0) { + m_should_stop = true; + } else { + // We go through each location, and test first its precondition - + // this overrides everything. Note, we only do this once per + // breakpoint - not once per location... Then check the condition. + // If the condition says to stop, then we run the callback for that + // location. If that callback says to stop as well, then we set + // m_should_stop to true; we are going to stop. But we still want to + // give all the breakpoints whose conditions say we are going to stop + // a chance to run their callbacks. Of course if any callback + // restarts the target by putting "continue" in the callback, then + // we're going to restart, without running the rest of the callbacks. + // And in this case we will end up not stopping even if another + // location said we should stop. But that's better than not running + // all the callbacks. + + m_should_stop = false; + + // We don't select threads as we go through them testing breakpoint + // conditions and running commands. So we need to set the thread for + // expression evaluation here: + ThreadList::ExpressionExecutionThreadPusher thread_pusher(thread_sp); + + ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); + Process *process = exe_ctx.GetProcessPtr(); + if (process->GetModIDRef().IsLastResumeForUserExpression()) { + // If we are in the middle of evaluating an expression, don't run + // asynchronous breakpoint commands or expressions. That could + // lead to infinite recursion if the command or condition re-calls + // the function with this breakpoint. + // TODO: We can keep a list of the breakpoints we've seen while + // running expressions in the nested + // PerformAction calls that can arise when the action runs a + // function that hits another breakpoint, and only stop running + // commands when we see the same breakpoint hit a second time. + + m_should_stop_is_valid = true; + + // It is possible that the user has a breakpoint at the same site + // as the completed plan had (e.g. user has a breakpoint + // on a module entry point, and `ThreadPlanCallFunction` ends + // also there). We can't find an internal breakpoint in the loop + // later because it was already removed on the plan completion. + // So check if the plan was completed, and stop if so. + if (thread_sp->CompletedPlanOverridesBreakpoint()) { + m_should_stop = true; + thread_sp->ResetStopInfo(); + return; + } + + LLDB_LOGF(log, "StopInfoBreakpoint::PerformAction - Hit a " + "breakpoint while running an expression," + " not running commands to avoid recursion."); + bool ignoring_breakpoints = + process->GetIgnoreBreakpointsInExpressions(); + if (ignoring_breakpoints) { + m_should_stop = false; + // Internal breakpoints will always stop. + for (size_t j = 0; j < num_owners; j++) { + lldb::BreakpointLocationSP bp_loc_sp = + bp_site_sp->GetOwnerAtIndex(j); + if (bp_loc_sp->GetBreakpoint().IsInternal()) { + m_should_stop = true; + break; + } + } + } else { + m_should_stop = true; + } + LLDB_LOGF(log, + "StopInfoBreakpoint::PerformAction - in expression, " + "continuing: %s.", + m_should_stop ? "true" : "false"); + process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf( + "Warning: hit breakpoint while running function, skipping " + "commands and conditions to prevent recursion.\n"); + return; + } + + StoppointCallbackContext context(event_ptr, exe_ctx, false); + + // For safety's sake let's also grab an extra reference to the + // breakpoint owners of the locations we're going to examine, since + // the locations are going to have to get back to their breakpoints, + // and the locations don't keep their owners alive. I'm just + // sticking the BreakpointSP's in a vector since I'm only using it to + // locally increment their retain counts. + + std::vector<lldb::BreakpointSP> location_owners; + + for (size_t j = 0; j < num_owners; j++) { + BreakpointLocationSP loc(site_locations.GetByIndex(j)); + location_owners.push_back(loc->GetBreakpoint().shared_from_this()); + } + + for (size_t j = 0; j < num_owners; j++) { + lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j); + StreamString loc_desc; + if (log) { + bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief); + } + // If another action disabled this breakpoint or its location, then + // don't run the actions. + if (!bp_loc_sp->IsEnabled() || + !bp_loc_sp->GetBreakpoint().IsEnabled()) + continue; + + // The breakpoint site may have many locations associated with it, + // not all of them valid for this thread. Skip the ones that + // aren't: + if (!bp_loc_sp->ValidForThisThread(thread_sp.get())) { + if (log) { + LLDB_LOGF(log, + "Breakpoint %s hit on thread 0x%llx but it was not " + "for this thread, continuing.", + loc_desc.GetData(), + static_cast<unsigned long long>(thread_sp->GetID())); + } + continue; + } + + internal_breakpoint = bp_loc_sp->GetBreakpoint().IsInternal(); + + // First run the precondition, but since the precondition is per + // breakpoint, only run it once per breakpoint. + std::pair<std::unordered_set<break_id_t>::iterator, bool> result = + precondition_breakpoints.insert( + bp_loc_sp->GetBreakpoint().GetID()); + if (!result.second) + continue; + + bool precondition_result = + bp_loc_sp->GetBreakpoint().EvaluatePrecondition(context); + if (!precondition_result) + continue; + + // Next run the condition for the breakpoint. If that says we + // should stop, then we'll run the callback for the breakpoint. If + // the callback says we shouldn't stop that will win. + + if (bp_loc_sp->GetConditionText() != nullptr) { + Status condition_error; + bool condition_says_stop = + bp_loc_sp->ConditionSaysStop(exe_ctx, condition_error); + + if (!condition_error.Success()) { + Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); + StreamSP error_sp = debugger.GetAsyncErrorStream(); + error_sp->Printf("Stopped due to an error evaluating condition " + "of breakpoint "); + bp_loc_sp->GetDescription(error_sp.get(), + eDescriptionLevelBrief); + error_sp->Printf(": \"%s\"", bp_loc_sp->GetConditionText()); + error_sp->EOL(); + const char *err_str = + condition_error.AsCString("<Unknown Error>"); + LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str); + + error_sp->PutCString(err_str); + error_sp->EOL(); + error_sp->Flush(); + } else { + LLDB_LOGF(log, + "Condition evaluated for breakpoint %s on thread " + "0x%llx conditon_says_stop: %i.", + loc_desc.GetData(), + static_cast<unsigned long long>(thread_sp->GetID()), + condition_says_stop); + if (!condition_says_stop) { + // We don't want to increment the hit count of breakpoints if + // the condition fails. We've already bumped it by the time + // we get here, so undo the bump: + bp_loc_sp->UndoBumpHitCount(); + continue; + } + } + } + + // Check the auto-continue bit on the location, do this before the + // callback since it may change this, but that would be for the + // NEXT hit. Note, you might think you could check auto-continue + // before the condition, and not evaluate the condition if it says + // to continue. But failing the condition means the breakpoint was + // effectively NOT HIT. So these two states are different. + bool auto_continue_says_stop = true; + if (bp_loc_sp->IsAutoContinue()) + { + LLDB_LOGF(log, + "Continuing breakpoint %s as AutoContinue was set.", + loc_desc.GetData()); + // We want this stop reported, so you will know we auto-continued + // but only for external breakpoints: + if (!internal_breakpoint) + thread_sp->SetShouldReportStop(eVoteYes); + auto_continue_says_stop = false; + } + + bool callback_says_stop = true; + + // FIXME: For now the callbacks have to run in async mode - the + // first time we restart we need + // to get out of there. So set it here. + // When we figure out how to nest breakpoint hits then this will + // change. + + Debugger &debugger = thread_sp->CalculateTarget()->GetDebugger(); + bool old_async = debugger.GetAsyncExecution(); + debugger.SetAsyncExecution(true); + + callback_says_stop = bp_loc_sp->InvokeCallback(&context); + + debugger.SetAsyncExecution(old_async); + + if (callback_says_stop && auto_continue_says_stop) + m_should_stop = true; + + // If we are going to stop for this breakpoint, then remove the + // breakpoint. + if (callback_says_stop && bp_loc_sp && + bp_loc_sp->GetBreakpoint().IsOneShot()) { + thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID( + bp_loc_sp->GetBreakpoint().GetID()); + } + // Also make sure that the callback hasn't continued the target. If + // it did, when we'll set m_should_start to false and get out of + // here. + if (HasTargetRunSinceMe()) { + m_should_stop = false; + break; + } + } + } + // We've figured out what this stop wants to do, so mark it as valid so + // we don't compute it again. + m_should_stop_is_valid = true; + } else { + m_should_stop = true; + m_should_stop_is_valid = true; + Log *log_process( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + LLDB_LOGF(log_process, + "Process::%s could not find breakpoint site id: %" PRId64 + "...", + __FUNCTION__, m_value); + } + + if ((!m_should_stop || internal_breakpoint) && + thread_sp->CompletedPlanOverridesBreakpoint()) { + + // Override should_stop decision when we have completed step plan + // additionally to the breakpoint + m_should_stop = true; + + // We know we're stopping for a completed plan and we don't want to + // show the breakpoint stop, so compute the public stop info immediately + // here. + thread_sp->CalculatePublicStopInfo(); + } + + LLDB_LOGF(log, + "Process::%s returning from action with m_should_stop: %d.", + __FUNCTION__, m_should_stop); + } + } + +private: + bool m_should_stop; + bool m_should_stop_is_valid; + bool m_should_perform_action; // Since we are trying to preserve the "state" + // of the system even if we run functions + // etc. behind the users backs, we need to make sure we only REALLY perform + // the action once. + lldb::addr_t m_address; // We use this to capture the breakpoint site address + // when we create the StopInfo, + // in case somebody deletes it between the time the StopInfo is made and the + // description is asked for. + lldb::break_id_t m_break_id; + bool m_was_one_shot; +}; + +// StopInfoWatchpoint + +class StopInfoWatchpoint : public StopInfo { +public: + // Make sure watchpoint is properly disabled and subsequently enabled while + // performing watchpoint actions. + class WatchpointSentry { + public: + WatchpointSentry(ProcessSP p_sp, WatchpointSP w_sp) : process_sp(p_sp), + watchpoint_sp(w_sp) { + if (process_sp && watchpoint_sp) { + const bool notify = false; + watchpoint_sp->TurnOnEphemeralMode(); + process_sp->DisableWatchpoint(watchpoint_sp.get(), notify); + process_sp->AddPreResumeAction(SentryPreResumeAction, this); + } + } + + void DoReenable() { + if (process_sp && watchpoint_sp) { + bool was_disabled = watchpoint_sp->IsDisabledDuringEphemeralMode(); + watchpoint_sp->TurnOffEphemeralMode(); + const bool notify = false; + if (was_disabled) { + process_sp->DisableWatchpoint(watchpoint_sp.get(), notify); + } else { + process_sp->EnableWatchpoint(watchpoint_sp.get(), notify); + } + } + } + + ~WatchpointSentry() { + DoReenable(); + if (process_sp) + process_sp->ClearPreResumeAction(SentryPreResumeAction, this); + } + + static bool SentryPreResumeAction(void *sentry_void) { + WatchpointSentry *sentry = (WatchpointSentry *) sentry_void; + sentry->DoReenable(); + return true; + } + + private: + ProcessSP process_sp; + WatchpointSP watchpoint_sp; + }; + + StopInfoWatchpoint(Thread &thread, break_id_t watch_id, + lldb::addr_t watch_hit_addr) + : StopInfo(thread, watch_id), m_should_stop(false), + m_should_stop_is_valid(false), m_watch_hit_addr(watch_hit_addr) {} + + ~StopInfoWatchpoint() override = default; + + StopReason GetStopReason() const override { return eStopReasonWatchpoint; } + + const char *GetDescription() override { + if (m_description.empty()) { + StreamString strm; + strm.Printf("watchpoint %" PRIi64, m_value); + m_description = strm.GetString(); + } + return m_description.c_str(); + } + +protected: + bool ShouldStopSynchronous(Event *event_ptr) override { + // ShouldStop() method is idempotent and should not affect hit count. See + // Process::RunPrivateStateThread()->Process()->HandlePrivateEvent() + // -->Process()::ShouldBroadcastEvent()->ThreadList::ShouldStop()-> + // Thread::ShouldStop()->ThreadPlanBase::ShouldStop()-> + // StopInfoWatchpoint::ShouldStop() and + // Event::DoOnRemoval()->Process::ProcessEventData::DoOnRemoval()-> + // StopInfoWatchpoint::PerformAction(). + if (m_should_stop_is_valid) + return m_should_stop; + + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + WatchpointSP wp_sp( + thread_sp->CalculateTarget()->GetWatchpointList().FindByID( + GetValue())); + if (wp_sp) { + // Check if we should stop at a watchpoint. + ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); + StoppointCallbackContext context(event_ptr, exe_ctx, true); + m_should_stop = wp_sp->ShouldStop(&context); + } else { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + LLDB_LOGF(log, + "Process::%s could not find watchpoint location id: %" PRId64 + "...", + __FUNCTION__, GetValue()); + + m_should_stop = true; + } + } + m_should_stop_is_valid = true; + return m_should_stop; + } + + bool ShouldStop(Event *event_ptr) override { + // This just reports the work done by PerformAction or the synchronous + // stop. It should only ever get called after they have had a chance to + // run. + assert(m_should_stop_is_valid); + return m_should_stop; + } + + void PerformAction(Event *event_ptr) override { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS); + // We're going to calculate if we should stop or not in some way during the + // course of this code. Also by default we're going to stop, so set that + // here. + m_should_stop = true; + + + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + + WatchpointSP wp_sp( + thread_sp->CalculateTarget()->GetWatchpointList().FindByID( + GetValue())); + if (wp_sp) { + ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); + ProcessSP process_sp = exe_ctx.GetProcessSP(); + + { + // check if this process is running on an architecture where + // watchpoints trigger before the associated instruction runs. if so, + // disable the WP, single-step and then re-enable the watchpoint + if (process_sp) { + uint32_t num; + bool wp_triggers_after; + + if (process_sp->GetWatchpointSupportInfo(num, wp_triggers_after) + .Success()) { + if (!wp_triggers_after) { + // We need to preserve the watch_index before watchpoint is + // disable. Since Watchpoint::SetEnabled will clear the watch + // index. This will fix TestWatchpointIter failure + Watchpoint *wp = wp_sp.get(); + uint32_t watch_index = wp->GetHardwareIndex(); + process_sp->DisableWatchpoint(wp, false); + StopInfoSP stored_stop_info_sp = thread_sp->GetStopInfo(); + assert(stored_stop_info_sp.get() == this); + + Status new_plan_status; + ThreadPlanSP new_plan_sp( + thread_sp->QueueThreadPlanForStepSingleInstruction( + false, // step-over + false, // abort_other_plans + true, // stop_other_threads + new_plan_status)); + if (new_plan_sp && new_plan_status.Success()) { + new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetOkayToDiscard(false); + new_plan_sp->SetPrivate(true); + } + process_sp->GetThreadList().SetSelectedThreadByID( + thread_sp->GetID()); + process_sp->ResumeSynchronous(nullptr); + process_sp->GetThreadList().SetSelectedThreadByID( + thread_sp->GetID()); + thread_sp->SetStopInfo(stored_stop_info_sp); + process_sp->EnableWatchpoint(wp, false); + wp->SetHardwareIndex(watch_index); + } + } + } + } + + // This sentry object makes sure the current watchpoint is disabled + // while performing watchpoint actions, and it is then enabled after we + // are finished. + WatchpointSentry sentry(process_sp, wp_sp); + + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. + * For example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is + * set at 'm', then + * watch exception is generated even when 'n' is read/written. To handle + * this case, + * server emulates the instruction at PC and finds the base address of + * the load/store + * instruction and appends it in the description of the stop-info + * packet. If watchpoint + * is not set on this address by user then this do not stop. + */ + if (m_watch_hit_addr != LLDB_INVALID_ADDRESS) { + WatchpointSP wp_hit_sp = + thread_sp->CalculateTarget()->GetWatchpointList().FindByAddress( + m_watch_hit_addr); + if (!wp_hit_sp) { + m_should_stop = false; + wp_sp->IncrementFalseAlarmsAndReviseHitCount(); + } + } + + // TODO: This condition should be checked in the synchronous part of the + // watchpoint code + // (Watchpoint::ShouldStop), so that we avoid pulling an event even if + // the watchpoint fails the ignore count condition. It is moved here + // temporarily, because for archs with + // watchpoint_exceptions_received=before, the code in the previous + // lines takes care of moving the inferior to next PC. We have to check + // the ignore count condition after this is done, otherwise we will hit + // same watchpoint multiple times until we pass ignore condition, but + // we won't actually be ignoring them. + if (wp_sp->GetHitCount() <= wp_sp->GetIgnoreCount()) + m_should_stop = false; + + Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); + + if (m_should_stop && wp_sp->GetConditionText() != nullptr) { + // We need to make sure the user sees any parse errors in their + // condition, so we'll hook the constructor errors up to the + // debugger's Async I/O. + ExpressionResults result_code; + EvaluateExpressionOptions expr_options; + expr_options.SetUnwindOnError(true); + expr_options.SetIgnoreBreakpoints(true); + ValueObjectSP result_value_sp; + Status error; + result_code = UserExpression::Evaluate( + exe_ctx, expr_options, wp_sp->GetConditionText(), + llvm::StringRef(), result_value_sp, error); + + if (result_code == eExpressionCompleted) { + if (result_value_sp) { + Scalar scalar_value; + if (result_value_sp->ResolveValue(scalar_value)) { + if (scalar_value.ULongLong(1) == 0) { + // We have been vetoed. This takes precedence over querying + // the watchpoint whether it should stop (aka ignore count + // and friends). See also StopInfoWatchpoint::ShouldStop() + // as well as Process::ProcessEventData::DoOnRemoval(). + m_should_stop = false; + } else + m_should_stop = true; + LLDB_LOGF(log, + "Condition successfully evaluated, result is %s.\n", + m_should_stop ? "true" : "false"); + } else { + m_should_stop = true; + LLDB_LOGF( + log, + "Failed to get an integer result from the expression."); + } + } + } else { + StreamSP error_sp = debugger.GetAsyncErrorStream(); + error_sp->Printf( + "Stopped due to an error evaluating condition of watchpoint "); + wp_sp->GetDescription(error_sp.get(), eDescriptionLevelBrief); + error_sp->Printf(": \"%s\"", wp_sp->GetConditionText()); + error_sp->EOL(); + const char *err_str = error.AsCString("<Unknown Error>"); + LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str); + + error_sp->PutCString(err_str); + error_sp->EOL(); + error_sp->Flush(); + // If the condition fails to be parsed or run, we should stop. + m_should_stop = true; + } + } + + // If the condition says to stop, we run the callback to further decide + // whether to stop. + if (m_should_stop) { + // FIXME: For now the callbacks have to run in async mode - the + // first time we restart we need + // to get out of there. So set it here. + // When we figure out how to nest watchpoint hits then this will + // change. + + bool old_async = debugger.GetAsyncExecution(); + debugger.SetAsyncExecution(true); + + StoppointCallbackContext context(event_ptr, exe_ctx, false); + bool stop_requested = wp_sp->InvokeCallback(&context); + + debugger.SetAsyncExecution(old_async); + + // Also make sure that the callback hasn't continued the target. If + // it did, when we'll set m_should_stop to false and get out of here. + if (HasTargetRunSinceMe()) + m_should_stop = false; + + if (m_should_stop && !stop_requested) { + // We have been vetoed by the callback mechanism. + m_should_stop = false; + } + } + // Finally, if we are going to stop, print out the new & old values: + if (m_should_stop) { + wp_sp->CaptureWatchedValue(exe_ctx); + + Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); + StreamSP output_sp = debugger.GetAsyncOutputStream(); + wp_sp->DumpSnapshots(output_sp.get()); + output_sp->EOL(); + output_sp->Flush(); + } + + } else { + Log *log_process( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + LLDB_LOGF(log_process, + "Process::%s could not find watchpoint id: %" PRId64 "...", + __FUNCTION__, m_value); + } + LLDB_LOGF(log, + "Process::%s returning from action with m_should_stop: %d.", + __FUNCTION__, m_should_stop); + + m_should_stop_is_valid = true; + } + } + +private: + bool m_should_stop; + bool m_should_stop_is_valid; + lldb::addr_t m_watch_hit_addr; +}; + +// StopInfoUnixSignal + +class StopInfoUnixSignal : public StopInfo { +public: + StopInfoUnixSignal(Thread &thread, int signo, const char *description) + : StopInfo(thread, signo) { + SetDescription(description); + } + + ~StopInfoUnixSignal() override = default; + + StopReason GetStopReason() const override { return eStopReasonSignal; } + + bool ShouldStopSynchronous(Event *event_ptr) override { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) + return thread_sp->GetProcess()->GetUnixSignals()->GetShouldStop(m_value); + return false; + } + + bool ShouldStop(Event *event_ptr) override { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) + return thread_sp->GetProcess()->GetUnixSignals()->GetShouldStop(m_value); + return false; + } + + // If should stop returns false, check if we should notify of this event + bool DoShouldNotify(Event *event_ptr) override { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + bool should_notify = + thread_sp->GetProcess()->GetUnixSignals()->GetShouldNotify(m_value); + if (should_notify) { + StreamString strm; + strm.Printf( + "thread %d received signal: %s", thread_sp->GetIndexID(), + thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString( + m_value)); + Process::ProcessEventData::AddRestartedReason(event_ptr, + strm.GetData()); + } + return should_notify; + } + return true; + } + + void WillResume(lldb::StateType resume_state) override { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + if (!thread_sp->GetProcess()->GetUnixSignals()->GetShouldSuppress( + m_value)) + thread_sp->SetResumeSignal(m_value); + } + } + + const char *GetDescription() override { + if (m_description.empty()) { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + StreamString strm; + const char *signal_name = + thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString( + m_value); + if (signal_name) + strm.Printf("signal %s", signal_name); + else + strm.Printf("signal %" PRIi64, m_value); + m_description = strm.GetString(); + } + } + return m_description.c_str(); + } +}; + +// StopInfoTrace + +class StopInfoTrace : public StopInfo { +public: + StopInfoTrace(Thread &thread) : StopInfo(thread, LLDB_INVALID_UID) {} + + ~StopInfoTrace() override = default; + + StopReason GetStopReason() const override { return eStopReasonTrace; } + + const char *GetDescription() override { + if (m_description.empty()) + return "trace"; + else + return m_description.c_str(); + } +}; + +// StopInfoException + +class StopInfoException : public StopInfo { +public: + StopInfoException(Thread &thread, const char *description) + : StopInfo(thread, LLDB_INVALID_UID) { + if (description) + SetDescription(description); + } + + ~StopInfoException() override = default; + + StopReason GetStopReason() const override { return eStopReasonException; } + + const char *GetDescription() override { + if (m_description.empty()) + return "exception"; + else + return m_description.c_str(); + } +}; + +// StopInfoThreadPlan + +class StopInfoThreadPlan : public StopInfo { +public: + StopInfoThreadPlan(ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp, + ExpressionVariableSP &expression_variable_sp) + : StopInfo(plan_sp->GetThread(), LLDB_INVALID_UID), m_plan_sp(plan_sp), + m_return_valobj_sp(return_valobj_sp), + m_expression_variable_sp(expression_variable_sp) {} + + ~StopInfoThreadPlan() override = default; + + StopReason GetStopReason() const override { return eStopReasonPlanComplete; } + + const char *GetDescription() override { + if (m_description.empty()) { + StreamString strm; + m_plan_sp->GetDescription(&strm, eDescriptionLevelBrief); + m_description = strm.GetString(); + } + return m_description.c_str(); + } + + ValueObjectSP GetReturnValueObject() { return m_return_valobj_sp; } + + ExpressionVariableSP GetExpressionVariable() { + return m_expression_variable_sp; + } + +protected: + bool ShouldStop(Event *event_ptr) override { + if (m_plan_sp) + return m_plan_sp->ShouldStop(event_ptr); + else + return StopInfo::ShouldStop(event_ptr); + } + +private: + ThreadPlanSP m_plan_sp; + ValueObjectSP m_return_valobj_sp; + ExpressionVariableSP m_expression_variable_sp; +}; + +// StopInfoExec + +class StopInfoExec : public StopInfo { +public: + StopInfoExec(Thread &thread) + : StopInfo(thread, LLDB_INVALID_UID), m_performed_action(false) {} + + ~StopInfoExec() override = default; + + bool ShouldStop(Event *event_ptr) override { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) + return thread_sp->GetProcess()->GetStopOnExec(); + return false; + } + + StopReason GetStopReason() const override { return eStopReasonExec; } + + const char *GetDescription() override { return "exec"; } + +protected: + void PerformAction(Event *event_ptr) override { + // Only perform the action once + if (m_performed_action) + return; + m_performed_action = true; + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) + thread_sp->GetProcess()->DidExec(); + } + + bool m_performed_action; +}; + +} // namespace lldb_private + +StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread, + break_id_t break_id) { + return StopInfoSP(new StopInfoBreakpoint(thread, break_id)); +} + +StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread, + break_id_t break_id, + bool should_stop) { + return StopInfoSP(new StopInfoBreakpoint(thread, break_id, should_stop)); +} + +StopInfoSP +StopInfo::CreateStopReasonWithWatchpointID(Thread &thread, break_id_t watch_id, + lldb::addr_t watch_hit_addr) { + return StopInfoSP(new StopInfoWatchpoint(thread, watch_id, watch_hit_addr)); +} + +StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo, + const char *description) { + return StopInfoSP(new StopInfoUnixSignal(thread, signo, description)); +} + +StopInfoSP StopInfo::CreateStopReasonToTrace(Thread &thread) { + return StopInfoSP(new StopInfoTrace(thread)); +} + +StopInfoSP StopInfo::CreateStopReasonWithPlan( + ThreadPlanSP &plan_sp, ValueObjectSP return_valobj_sp, + ExpressionVariableSP expression_variable_sp) { + return StopInfoSP(new StopInfoThreadPlan(plan_sp, return_valobj_sp, + expression_variable_sp)); +} + +StopInfoSP StopInfo::CreateStopReasonWithException(Thread &thread, + const char *description) { + return StopInfoSP(new StopInfoException(thread, description)); +} + +StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) { + return StopInfoSP(new StopInfoExec(thread)); +} + +ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) { + if (stop_info_sp && + stop_info_sp->GetStopReason() == eStopReasonPlanComplete) { + StopInfoThreadPlan *plan_stop_info = + static_cast<StopInfoThreadPlan *>(stop_info_sp.get()); + return plan_stop_info->GetReturnValueObject(); + } else + return ValueObjectSP(); +} + +ExpressionVariableSP StopInfo::GetExpressionVariable(StopInfoSP &stop_info_sp) { + if (stop_info_sp && + stop_info_sp->GetStopReason() == eStopReasonPlanComplete) { + StopInfoThreadPlan *plan_stop_info = + static_cast<StopInfoThreadPlan *>(stop_info_sp.get()); + return plan_stop_info->GetExpressionVariable(); + } else + return ExpressionVariableSP(); +} + +lldb::ValueObjectSP +StopInfo::GetCrashingDereference(StopInfoSP &stop_info_sp, + lldb::addr_t *crashing_address) { + if (!stop_info_sp) { + return ValueObjectSP(); + } + + const char *description = stop_info_sp->GetDescription(); + if (!description) { + return ValueObjectSP(); + } + + ThreadSP thread_sp = stop_info_sp->GetThread(); + if (!thread_sp) { + return ValueObjectSP(); + } + + StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); + + if (!frame_sp) { + return ValueObjectSP(); + } + + const char address_string[] = "address="; + + const char *address_loc = strstr(description, address_string); + if (!address_loc) { + return ValueObjectSP(); + } + + address_loc += (sizeof(address_string) - 1); + + uint64_t address = strtoull(address_loc, nullptr, 0); + if (crashing_address) { + *crashing_address = address; + } + + return frame_sp->GuessValueForAddress(address); +} diff --git a/gnu/llvm/lldb/source/Target/StructuredDataPlugin.cpp b/gnu/llvm/lldb/source/Target/StructuredDataPlugin.cpp new file mode 100644 index 00000000000..a22902d99f7 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/StructuredDataPlugin.cpp @@ -0,0 +1,67 @@ +//===-- StructuredDataPlugin.cpp --------------------------------*- C++ -*-===// +// +// 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/StructuredDataPlugin.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { +class CommandStructuredData : public CommandObjectMultiword { +public: + CommandStructuredData(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "structured-data", + "Parent for per-plugin structured data commands", + "plugin structured-data <plugin>") {} + + ~CommandStructuredData() override {} +}; +} + +StructuredDataPlugin::StructuredDataPlugin(const ProcessWP &process_wp) + : PluginInterface(), m_process_wp(process_wp) {} + +StructuredDataPlugin::~StructuredDataPlugin() {} + +bool StructuredDataPlugin::GetEnabled(ConstString type_name) const { + // By default, plugins are always enabled. Plugin authors should override + // this if there is an enabled/disabled state for their plugin. + return true; +} + +ProcessSP StructuredDataPlugin::GetProcess() const { + return m_process_wp.lock(); +} + +void StructuredDataPlugin::InitializeBasePluginForDebugger(Debugger &debugger) { + // Create our mutliword command anchor if it doesn't already exist. + auto &interpreter = debugger.GetCommandInterpreter(); + if (!interpreter.GetCommandObject("plugin structured-data")) { + // Find the parent command. + auto parent_command = + debugger.GetCommandInterpreter().GetCommandObject("plugin"); + if (!parent_command) + return; + + // Create the structured-data ommand object. + auto command_name = "structured-data"; + auto command_sp = CommandObjectSP(new CommandStructuredData(interpreter)); + + // Hook it up under the top-level plugin command. + parent_command->LoadSubCommand(command_name, command_sp); + } +} + +void StructuredDataPlugin::ModulesDidLoad(Process &process, + ModuleList &module_list) { + // Default implementation does nothing. +} diff --git a/gnu/llvm/lldb/source/Target/SystemRuntime.cpp b/gnu/llvm/lldb/source/Target/SystemRuntime.cpp new file mode 100644 index 00000000000..286bea09f85 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/SystemRuntime.cpp @@ -0,0 +1,51 @@ +//===-- SystemRuntime.cpp ---------------------------------------*- C++ -*-===// +// +// 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/SystemRuntime.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/Process.h" +#include "lldb/lldb-private.h" + +using namespace lldb; +using namespace lldb_private; + +SystemRuntime *SystemRuntime::FindPlugin(Process *process) { + SystemRuntimeCreateInstance create_callback = nullptr; + for (uint32_t idx = 0; + (create_callback = PluginManager::GetSystemRuntimeCreateCallbackAtIndex( + idx)) != nullptr; + ++idx) { + std::unique_ptr<SystemRuntime> instance_up(create_callback(process)); + if (instance_up) + return instance_up.release(); + } + return nullptr; +} + +// SystemRuntime constructor +SystemRuntime::SystemRuntime(Process *process) + : m_process(process), m_types() {} + +SystemRuntime::~SystemRuntime() = default; + +void SystemRuntime::DidAttach() {} + +void SystemRuntime::DidLaunch() {} + +void SystemRuntime::Detach() {} + +void SystemRuntime::ModulesDidLoad(ModuleList &module_list) {} + +const std::vector<ConstString> &SystemRuntime::GetExtendedBacktraceTypes() { + return m_types; +} + +ThreadSP SystemRuntime::GetExtendedBacktraceThread(ThreadSP thread, + ConstString type) { + return ThreadSP(); +} diff --git a/gnu/llvm/lldb/source/Target/Target.cpp b/gnu/llvm/lldb/source/Target/Target.cpp new file mode 100644 index 00000000000..83e6f306266 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/Target.cpp @@ -0,0 +1,4081 @@ +//===-- Target.cpp ----------------------------------------------*- C++ -*-===// +// +// 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/Target.h" +#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" +#include "lldb/Breakpoint/BreakpointIDList.h" +#include "lldb/Breakpoint/BreakpointPrecondition.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverAddress.h" +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" +#include "lldb/Breakpoint/BreakpointResolverFileRegex.h" +#include "lldb/Breakpoint/BreakpointResolverName.h" +#include "lldb/Breakpoint/BreakpointResolverScripted.h" +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/REPL.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionGroupWatchpoint.h" +#include "lldb/Interpreter/OptionValues.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" + +#include "llvm/ADT/ScopeExit.h" + +#include <memory> +#include <mutex> + +using namespace lldb; +using namespace lldb_private; + +constexpr std::chrono::milliseconds EvaluateExpressionOptions::default_timeout; + +Target::Arch::Arch(const ArchSpec &spec) + : m_spec(spec), + m_plugin_up(PluginManager::CreateArchitectureInstance(spec)) {} + +const Target::Arch &Target::Arch::operator=(const ArchSpec &spec) { + m_spec = spec; + m_plugin_up = PluginManager::CreateArchitectureInstance(spec); + return *this; +} + +ConstString &Target::GetStaticBroadcasterClass() { + static ConstString class_name("lldb.target"); + return class_name; +} + +Target::Target(Debugger &debugger, const ArchSpec &target_arch, + const lldb::PlatformSP &platform_sp, bool is_dummy_target) + : TargetProperties(this), + Broadcaster(debugger.GetBroadcasterManager(), + Target::GetStaticBroadcasterClass().AsCString()), + ExecutionContextScope(), m_debugger(debugger), m_platform_sp(platform_sp), + m_mutex(), m_arch(target_arch), m_images(this), m_section_load_history(), + m_breakpoint_list(false), m_internal_breakpoint_list(true), + m_watchpoint_list(), m_process_sp(), m_search_filter_sp(), + m_image_search_paths(ImageSearchPathsChanged, this), m_ast_importer_sp(), + m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0), + m_valid(true), m_suppress_stop_hooks(false), + m_is_dummy_target(is_dummy_target), + m_stats_storage(static_cast<int>(StatisticKind::StatisticMax)) + +{ + SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed"); + SetEventName(eBroadcastBitModulesLoaded, "modules-loaded"); + SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded"); + SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed"); + SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded"); + + CheckInWithManager(); + + LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT), + "{0} Target::Target()", static_cast<void *>(this)); + if (target_arch.IsValid()) { + LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET), + "Target::Target created with architecture {0} ({1})", + target_arch.GetArchitectureName(), + target_arch.GetTriple().getTriple().c_str()); + } +} + +Target::~Target() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + LLDB_LOG(log, "{0} Target::~Target()", static_cast<void *>(this)); + DeleteCurrentProcess(); +} + +void Target::PrimeFromDummyTarget(Target *target) { + if (!target) + return; + + m_stop_hooks = target->m_stop_hooks; + + for (BreakpointSP breakpoint_sp : target->m_breakpoint_list.Breakpoints()) { + if (breakpoint_sp->IsInternal()) + continue; + + BreakpointSP new_bp(new Breakpoint(*this, *breakpoint_sp.get())); + AddBreakpoint(new_bp, false); + } + + for (auto bp_name_entry : target->m_breakpoint_names) { + + BreakpointName *new_bp_name = new BreakpointName(*bp_name_entry.second); + AddBreakpointName(new_bp_name); + } +} + +void Target::Dump(Stream *s, lldb::DescriptionLevel description_level) { + // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + if (description_level != lldb::eDescriptionLevelBrief) { + s->Indent(); + s->PutCString("Target\n"); + s->IndentMore(); + m_images.Dump(s); + m_breakpoint_list.Dump(s); + m_internal_breakpoint_list.Dump(s); + s->IndentLess(); + } else { + Module *exe_module = GetExecutableModulePointer(); + if (exe_module) + s->PutCString(exe_module->GetFileSpec().GetFilename().GetCString()); + else + s->PutCString("No executable module."); + } +} + +void Target::CleanupProcess() { + // Do any cleanup of the target we need to do between process instances. + // NB It is better to do this before destroying the process in case the + // clean up needs some help from the process. + m_breakpoint_list.ClearAllBreakpointSites(); + m_internal_breakpoint_list.ClearAllBreakpointSites(); + // Disable watchpoints just on the debugger side. + std::unique_lock<std::recursive_mutex> lock; + this->GetWatchpointList().GetListMutex(lock); + DisableAllWatchpoints(false); + ClearAllWatchpointHitCounts(); + ClearAllWatchpointHistoricValues(); +} + +void Target::DeleteCurrentProcess() { + if (m_process_sp) { + m_section_load_history.Clear(); + if (m_process_sp->IsAlive()) + m_process_sp->Destroy(false); + + m_process_sp->Finalize(); + + CleanupProcess(); + + m_process_sp.reset(); + } +} + +const lldb::ProcessSP &Target::CreateProcess(ListenerSP listener_sp, + llvm::StringRef plugin_name, + const FileSpec *crash_file) { + if (!listener_sp) + listener_sp = GetDebugger().GetListener(); + DeleteCurrentProcess(); + m_process_sp = Process::FindPlugin(shared_from_this(), plugin_name, + listener_sp, crash_file); + return m_process_sp; +} + +const lldb::ProcessSP &Target::GetProcessSP() const { return m_process_sp; } + +lldb::REPLSP Target::GetREPL(Status &err, lldb::LanguageType language, + const char *repl_options, bool can_create) { + if (language == eLanguageTypeUnknown) { + LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs(); + + if (auto single_lang = repl_languages.GetSingularLanguage()) { + language = *single_lang; + } else if (repl_languages.Empty()) { + err.SetErrorStringWithFormat( + "LLDB isn't configured with REPL support for any languages."); + return REPLSP(); + } else { + err.SetErrorStringWithFormat( + "Multiple possible REPL languages. Please specify a language."); + return REPLSP(); + } + } + + REPLMap::iterator pos = m_repl_map.find(language); + + if (pos != m_repl_map.end()) { + return pos->second; + } + + if (!can_create) { + err.SetErrorStringWithFormat( + "Couldn't find an existing REPL for %s, and can't create a new one", + Language::GetNameForLanguageType(language)); + return lldb::REPLSP(); + } + + Debugger *const debugger = nullptr; + lldb::REPLSP ret = REPL::Create(err, language, debugger, this, repl_options); + + if (ret) { + m_repl_map[language] = ret; + return m_repl_map[language]; + } + + if (err.Success()) { + err.SetErrorStringWithFormat("Couldn't create a REPL for %s", + Language::GetNameForLanguageType(language)); + } + + return lldb::REPLSP(); +} + +void Target::SetREPL(lldb::LanguageType language, lldb::REPLSP repl_sp) { + lldbassert(!m_repl_map.count(language)); + + m_repl_map[language] = repl_sp; +} + +void Target::Destroy() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_valid = false; + DeleteCurrentProcess(); + m_platform_sp.reset(); + m_arch = ArchSpec(); + ClearModules(true); + m_section_load_history.Clear(); + const bool notify = false; + m_breakpoint_list.RemoveAll(notify); + m_internal_breakpoint_list.RemoveAll(notify); + m_last_created_breakpoint.reset(); + m_last_created_watchpoint.reset(); + m_search_filter_sp.reset(); + m_image_search_paths.Clear(notify); + m_stop_hooks.clear(); + m_stop_hook_next_id = 0; + m_suppress_stop_hooks = false; +} + +BreakpointList &Target::GetBreakpointList(bool internal) { + if (internal) + return m_internal_breakpoint_list; + else + return m_breakpoint_list; +} + +const BreakpointList &Target::GetBreakpointList(bool internal) const { + if (internal) + return m_internal_breakpoint_list; + else + return m_breakpoint_list; +} + +BreakpointSP Target::GetBreakpointByID(break_id_t break_id) { + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); + + return bp_sp; +} + +BreakpointSP Target::CreateSourceRegexBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *source_file_spec_list, + const std::unordered_set<std::string> &function_names, + RegularExpression source_regex, bool internal, bool hardware, + LazyBool move_to_nearest_code) { + SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( + containingModules, source_file_spec_list)); + if (move_to_nearest_code == eLazyBoolCalculate) + move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; + BreakpointResolverSP resolver_sp(new BreakpointResolverFileRegex( + nullptr, std::move(source_regex), function_names, + !static_cast<bool>(move_to_nearest_code))); + + return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); +} + +BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, + const FileSpec &file, uint32_t line_no, + uint32_t column, lldb::addr_t offset, + LazyBool check_inlines, + LazyBool skip_prologue, bool internal, + bool hardware, + LazyBool move_to_nearest_code) { + FileSpec remapped_file; + if (!GetSourcePathMap().ReverseRemapPath(file, remapped_file)) + remapped_file = file; + + if (check_inlines == eLazyBoolCalculate) { + const InlineStrategy inline_strategy = GetInlineStrategy(); + switch (inline_strategy) { + case eInlineBreakpointsNever: + check_inlines = eLazyBoolNo; + break; + + case eInlineBreakpointsHeaders: + if (remapped_file.IsSourceImplementationFile()) + check_inlines = eLazyBoolNo; + else + check_inlines = eLazyBoolYes; + break; + + case eInlineBreakpointsAlways: + check_inlines = eLazyBoolYes; + break; + } + } + SearchFilterSP filter_sp; + if (check_inlines == eLazyBoolNo) { + // Not checking for inlines, we are looking only for matching compile units + FileSpecList compile_unit_list; + compile_unit_list.Append(remapped_file); + filter_sp = GetSearchFilterForModuleAndCUList(containingModules, + &compile_unit_list); + } else { + filter_sp = GetSearchFilterForModuleList(containingModules); + } + if (skip_prologue == eLazyBoolCalculate) + skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; + if (move_to_nearest_code == eLazyBoolCalculate) + move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; + + BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine( + nullptr, remapped_file, line_no, column, offset, check_inlines, + skip_prologue, !static_cast<bool>(move_to_nearest_code))); + return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); +} + +BreakpointSP Target::CreateBreakpoint(lldb::addr_t addr, bool internal, + bool hardware) { + Address so_addr; + + // Check for any reason we want to move this breakpoint to other address. + addr = GetBreakableLoadAddress(addr); + + // Attempt to resolve our load address if possible, though it is ok if it + // doesn't resolve to section/offset. + + // Try and resolve as a load address if possible + GetSectionLoadList().ResolveLoadAddress(addr, so_addr); + if (!so_addr.IsValid()) { + // The address didn't resolve, so just set this as an absolute address + so_addr.SetOffset(addr); + } + BreakpointSP bp_sp(CreateBreakpoint(so_addr, internal, hardware)); + return bp_sp; +} + +BreakpointSP Target::CreateBreakpoint(const Address &addr, bool internal, + bool hardware) { + SearchFilterSP filter_sp( + new SearchFilterForUnconstrainedSearches(shared_from_this())); + BreakpointResolverSP resolver_sp( + new BreakpointResolverAddress(nullptr, addr)); + return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, false); +} + +lldb::BreakpointSP +Target::CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, bool internal, + const FileSpec *file_spec, + bool request_hardware) { + SearchFilterSP filter_sp( + new SearchFilterForUnconstrainedSearches(shared_from_this())); + BreakpointResolverSP resolver_sp(new BreakpointResolverAddress( + nullptr, file_addr, file_spec ? *file_spec : FileSpec())); + return CreateBreakpoint(filter_sp, resolver_sp, internal, request_hardware, + false); +} + +BreakpointSP Target::CreateBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, const char *func_name, + FunctionNameType func_name_type_mask, LanguageType language, + lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) { + BreakpointSP bp_sp; + if (func_name) { + SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( + containingModules, containingSourceFiles)); + + if (skip_prologue == eLazyBoolCalculate) + skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; + if (language == lldb::eLanguageTypeUnknown) + language = GetLanguage(); + + BreakpointResolverSP resolver_sp(new BreakpointResolverName( + nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact, + offset, skip_prologue)); + bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); + } + return bp_sp; +} + +lldb::BreakpointSP +Target::CreateBreakpoint(const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const std::vector<std::string> &func_names, + FunctionNameType func_name_type_mask, + LanguageType language, lldb::addr_t offset, + LazyBool skip_prologue, bool internal, bool hardware) { + BreakpointSP bp_sp; + size_t num_names = func_names.size(); + if (num_names > 0) { + SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( + containingModules, containingSourceFiles)); + + if (skip_prologue == eLazyBoolCalculate) + skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; + if (language == lldb::eLanguageTypeUnknown) + language = GetLanguage(); + + BreakpointResolverSP resolver_sp( + new BreakpointResolverName(nullptr, func_names, func_name_type_mask, + language, offset, skip_prologue)); + bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); + } + return bp_sp; +} + +BreakpointSP +Target::CreateBreakpoint(const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const char *func_names[], size_t num_names, + FunctionNameType func_name_type_mask, + LanguageType language, lldb::addr_t offset, + LazyBool skip_prologue, bool internal, bool hardware) { + BreakpointSP bp_sp; + if (num_names > 0) { + SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( + containingModules, containingSourceFiles)); + + if (skip_prologue == eLazyBoolCalculate) { + if (offset == 0) + skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; + else + skip_prologue = eLazyBoolNo; + } + if (language == lldb::eLanguageTypeUnknown) + language = GetLanguage(); + + BreakpointResolverSP resolver_sp(new BreakpointResolverName( + nullptr, func_names, num_names, func_name_type_mask, language, offset, + skip_prologue)); + resolver_sp->SetOffset(offset); + bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); + } + return bp_sp; +} + +SearchFilterSP +Target::GetSearchFilterForModule(const FileSpec *containingModule) { + SearchFilterSP filter_sp; + if (containingModule != nullptr) { + // TODO: We should look into sharing module based search filters + // across many breakpoints like we do for the simple target based one + filter_sp = std::make_shared<SearchFilterByModule>(shared_from_this(), + *containingModule); + } else { + if (!m_search_filter_sp) + m_search_filter_sp = + std::make_shared<SearchFilterForUnconstrainedSearches>( + shared_from_this()); + filter_sp = m_search_filter_sp; + } + return filter_sp; +} + +SearchFilterSP +Target::GetSearchFilterForModuleList(const FileSpecList *containingModules) { + SearchFilterSP filter_sp; + if (containingModules && containingModules->GetSize() != 0) { + // TODO: We should look into sharing module based search filters + // across many breakpoints like we do for the simple target based one + filter_sp = std::make_shared<SearchFilterByModuleList>(shared_from_this(), + *containingModules); + } else { + if (!m_search_filter_sp) + m_search_filter_sp = + std::make_shared<SearchFilterForUnconstrainedSearches>( + shared_from_this()); + filter_sp = m_search_filter_sp; + } + return filter_sp; +} + +SearchFilterSP Target::GetSearchFilterForModuleAndCUList( + const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles) { + if (containingSourceFiles == nullptr || containingSourceFiles->GetSize() == 0) + return GetSearchFilterForModuleList(containingModules); + + SearchFilterSP filter_sp; + if (containingModules == nullptr) { + // We could make a special "CU List only SearchFilter". Better yet was if + // these could be composable, but that will take a little reworking. + + filter_sp = std::make_shared<SearchFilterByModuleListAndCU>( + shared_from_this(), FileSpecList(), *containingSourceFiles); + } else { + filter_sp = std::make_shared<SearchFilterByModuleListAndCU>( + shared_from_this(), *containingModules, *containingSourceFiles); + } + return filter_sp; +} + +BreakpointSP Target::CreateFuncRegexBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, RegularExpression func_regex, + lldb::LanguageType requested_language, LazyBool skip_prologue, + bool internal, bool hardware) { + SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( + containingModules, containingSourceFiles)); + bool skip = (skip_prologue == eLazyBoolCalculate) + ? GetSkipPrologue() + : static_cast<bool>(skip_prologue); + BreakpointResolverSP resolver_sp(new BreakpointResolverName( + nullptr, std::move(func_regex), requested_language, 0, skip)); + + return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); +} + +lldb::BreakpointSP +Target::CreateExceptionBreakpoint(enum lldb::LanguageType language, + bool catch_bp, bool throw_bp, bool internal, + Args *additional_args, Status *error) { + BreakpointSP exc_bkpt_sp = LanguageRuntime::CreateExceptionBreakpoint( + *this, language, catch_bp, throw_bp, internal); + if (exc_bkpt_sp && additional_args) { + BreakpointPreconditionSP precondition_sp = exc_bkpt_sp->GetPrecondition(); + if (precondition_sp && additional_args) { + if (error) + *error = precondition_sp->ConfigurePrecondition(*additional_args); + else + precondition_sp->ConfigurePrecondition(*additional_args); + } + } + return exc_bkpt_sp; +} + +lldb::BreakpointSP Target::CreateScriptedBreakpoint( + const llvm::StringRef class_name, const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, bool internal, + bool request_hardware, StructuredData::ObjectSP extra_args_sp, + Status *creation_error) { + SearchFilterSP filter_sp; + + lldb::SearchDepth depth = lldb::eSearchDepthTarget; + bool has_files = + containingSourceFiles && containingSourceFiles->GetSize() > 0; + bool has_modules = containingModules && containingModules->GetSize() > 0; + + if (has_files && has_modules) { + filter_sp = GetSearchFilterForModuleAndCUList(containingModules, + containingSourceFiles); + } else if (has_files) { + filter_sp = + GetSearchFilterForModuleAndCUList(nullptr, containingSourceFiles); + } else if (has_modules) { + filter_sp = GetSearchFilterForModuleList(containingModules); + } else { + filter_sp = std::make_shared<SearchFilterForUnconstrainedSearches>( + shared_from_this()); + } + + StructuredDataImpl *extra_args_impl = new StructuredDataImpl(); + if (extra_args_sp) + extra_args_impl->SetObjectSP(extra_args_sp); + + BreakpointResolverSP resolver_sp(new BreakpointResolverScripted( + nullptr, class_name, depth, extra_args_impl)); + return CreateBreakpoint(filter_sp, resolver_sp, internal, false, true); +} + +BreakpointSP Target::CreateBreakpoint(SearchFilterSP &filter_sp, + BreakpointResolverSP &resolver_sp, + bool internal, bool request_hardware, + bool resolve_indirect_symbols) { + BreakpointSP bp_sp; + if (filter_sp && resolver_sp) { + const bool hardware = request_hardware || GetRequireHardwareBreakpoints(); + bp_sp.reset(new Breakpoint(*this, filter_sp, resolver_sp, hardware, + resolve_indirect_symbols)); + resolver_sp->SetBreakpoint(bp_sp.get()); + AddBreakpoint(bp_sp, internal); + } + return bp_sp; +} + +void Target::AddBreakpoint(lldb::BreakpointSP bp_sp, bool internal) { + if (!bp_sp) + return; + if (internal) + m_internal_breakpoint_list.Add(bp_sp, false); + else + m_breakpoint_list.Add(bp_sp, true); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) { + StreamString s; + bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + LLDB_LOGF(log, "Target::%s (internal = %s) => break_id = %s\n", + __FUNCTION__, bp_sp->IsInternal() ? "yes" : "no", s.GetData()); + } + + bp_sp->ResolveBreakpoint(); + + if (!internal) { + m_last_created_breakpoint = bp_sp; + } +} + +void Target::AddNameToBreakpoint(BreakpointID &id, const char *name, + Status &error) { + BreakpointSP bp_sp = + m_breakpoint_list.FindBreakpointByID(id.GetBreakpointID()); + if (!bp_sp) { + StreamString s; + id.GetDescription(&s, eDescriptionLevelBrief); + error.SetErrorStringWithFormat("Could not find breakpoint %s", s.GetData()); + return; + } + AddNameToBreakpoint(bp_sp, name, error); +} + +void Target::AddNameToBreakpoint(BreakpointSP &bp_sp, const char *name, + Status &error) { + if (!bp_sp) + return; + + BreakpointName *bp_name = FindBreakpointName(ConstString(name), true, error); + if (!bp_name) + return; + + bp_name->ConfigureBreakpoint(bp_sp); + bp_sp->AddName(name); +} + +void Target::AddBreakpointName(BreakpointName *bp_name) { + m_breakpoint_names.insert(std::make_pair(bp_name->GetName(), bp_name)); +} + +BreakpointName *Target::FindBreakpointName(ConstString name, bool can_create, + Status &error) { + BreakpointID::StringIsBreakpointName(name.GetStringRef(), error); + if (!error.Success()) + return nullptr; + + BreakpointNameList::iterator iter = m_breakpoint_names.find(name); + if (iter == m_breakpoint_names.end()) { + if (!can_create) { + error.SetErrorStringWithFormat("Breakpoint name \"%s\" doesn't exist and " + "can_create is false.", + name.AsCString()); + return nullptr; + } + + iter = m_breakpoint_names + .insert(std::make_pair(name, new BreakpointName(name))) + .first; + } + return (iter->second); +} + +void Target::DeleteBreakpointName(ConstString name) { + BreakpointNameList::iterator iter = m_breakpoint_names.find(name); + + if (iter != m_breakpoint_names.end()) { + const char *name_cstr = name.AsCString(); + m_breakpoint_names.erase(iter); + for (auto bp_sp : m_breakpoint_list.Breakpoints()) + bp_sp->RemoveName(name_cstr); + } +} + +void Target::RemoveNameFromBreakpoint(lldb::BreakpointSP &bp_sp, + ConstString name) { + bp_sp->RemoveName(name.AsCString()); +} + +void Target::ConfigureBreakpointName( + BreakpointName &bp_name, const BreakpointOptions &new_options, + const BreakpointName::Permissions &new_permissions) { + bp_name.GetOptions().CopyOverSetOptions(new_options); + bp_name.GetPermissions().MergeInto(new_permissions); + ApplyNameToBreakpoints(bp_name); +} + +void Target::ApplyNameToBreakpoints(BreakpointName &bp_name) { + llvm::Expected<std::vector<BreakpointSP>> expected_vector = + m_breakpoint_list.FindBreakpointsByName(bp_name.GetName().AsCString()); + + if (!expected_vector) { + LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS), + "invalid breakpoint name: {}", + llvm::toString(expected_vector.takeError())); + return; + } + + for (auto bp_sp : *expected_vector) + bp_name.ConfigureBreakpoint(bp_sp); +} + +void Target::GetBreakpointNames(std::vector<std::string> &names) { + names.clear(); + for (auto bp_name : m_breakpoint_names) { + names.push_back(bp_name.first.AsCString()); + } + llvm::sort(names.begin(), names.end()); +} + +bool Target::ProcessIsValid() { + return (m_process_sp && m_process_sp->IsAlive()); +} + +static bool CheckIfWatchpointsSupported(Target *target, Status &error) { + uint32_t num_supported_hardware_watchpoints; + Status rc = target->GetProcessSP()->GetWatchpointSupportInfo( + num_supported_hardware_watchpoints); + + // If unable to determine the # of watchpoints available, + // assume they are supported. + if (rc.Fail()) + return true; + + if (num_supported_hardware_watchpoints == 0) { + error.SetErrorStringWithFormat( + "Target supports (%u) hardware watchpoint slots.\n", + num_supported_hardware_watchpoints); + return false; + } + return true; +} + +// See also Watchpoint::SetWatchpointType(uint32_t type) and the +// OptionGroupWatchpoint::WatchType enum type. +WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size, + const CompilerType *type, uint32_t kind, + Status &error) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, + "Target::%s (addr = 0x%8.8" PRIx64 " size = %" PRIu64 + " type = %u)\n", + __FUNCTION__, addr, (uint64_t)size, kind); + + WatchpointSP wp_sp; + if (!ProcessIsValid()) { + error.SetErrorString("process is not alive"); + return wp_sp; + } + + if (addr == LLDB_INVALID_ADDRESS || size == 0) { + if (size == 0) + error.SetErrorString("cannot set a watchpoint with watch_size of 0"); + else + error.SetErrorStringWithFormat("invalid watch address: %" PRIu64, addr); + return wp_sp; + } + + if (!LLDB_WATCH_TYPE_IS_VALID(kind)) { + error.SetErrorStringWithFormat("invalid watchpoint type: %d", kind); + } + + if (!CheckIfWatchpointsSupported(this, error)) + return wp_sp; + + // Currently we only support one watchpoint per address, with total number of + // watchpoints limited by the hardware which the inferior is running on. + + // Grab the list mutex while doing operations. + const bool notify = false; // Don't notify about all the state changes we do + // on creating the watchpoint. + std::unique_lock<std::recursive_mutex> lock; + this->GetWatchpointList().GetListMutex(lock); + WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr); + if (matched_sp) { + size_t old_size = matched_sp->GetByteSize(); + uint32_t old_type = + (matched_sp->WatchpointRead() ? LLDB_WATCH_TYPE_READ : 0) | + (matched_sp->WatchpointWrite() ? LLDB_WATCH_TYPE_WRITE : 0); + // Return the existing watchpoint if both size and type match. + if (size == old_size && kind == old_type) { + wp_sp = matched_sp; + wp_sp->SetEnabled(false, notify); + } else { + // Nil the matched watchpoint; we will be creating a new one. + m_process_sp->DisableWatchpoint(matched_sp.get(), notify); + m_watchpoint_list.Remove(matched_sp->GetID(), true); + } + } + + if (!wp_sp) { + wp_sp = std::make_shared<Watchpoint>(*this, addr, size, type); + wp_sp->SetWatchpointType(kind, notify); + m_watchpoint_list.Add(wp_sp, true); + } + + error = m_process_sp->EnableWatchpoint(wp_sp.get(), notify); + LLDB_LOGF(log, "Target::%s (creation of watchpoint %s with id = %u)\n", + __FUNCTION__, error.Success() ? "succeeded" : "failed", + wp_sp->GetID()); + + if (error.Fail()) { + // Enabling the watchpoint on the device side failed. Remove the said + // watchpoint from the list maintained by the target instance. + m_watchpoint_list.Remove(wp_sp->GetID(), true); + // See if we could provide more helpful error message. + if (!OptionGroupWatchpoint::IsWatchSizeSupported(size)) + error.SetErrorStringWithFormat( + "watch size of %" PRIu64 " is not supported", (uint64_t)size); + + wp_sp.reset(); + } else + m_last_created_watchpoint = wp_sp; + return wp_sp; +} + +void Target::RemoveAllowedBreakpoints() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s \n", __FUNCTION__); + + m_breakpoint_list.RemoveAllowed(true); + + m_last_created_breakpoint.reset(); +} + +void Target::RemoveAllBreakpoints(bool internal_also) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s (internal_also = %s)\n", __FUNCTION__, + internal_also ? "yes" : "no"); + + m_breakpoint_list.RemoveAll(true); + if (internal_also) + m_internal_breakpoint_list.RemoveAll(false); + + m_last_created_breakpoint.reset(); +} + +void Target::DisableAllBreakpoints(bool internal_also) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s (internal_also = %s)\n", __FUNCTION__, + internal_also ? "yes" : "no"); + + m_breakpoint_list.SetEnabledAll(false); + if (internal_also) + m_internal_breakpoint_list.SetEnabledAll(false); +} + +void Target::DisableAllowedBreakpoints() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s", __FUNCTION__); + + m_breakpoint_list.SetEnabledAllowed(false); +} + +void Target::EnableAllBreakpoints(bool internal_also) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s (internal_also = %s)\n", __FUNCTION__, + internal_also ? "yes" : "no"); + + m_breakpoint_list.SetEnabledAll(true); + if (internal_also) + m_internal_breakpoint_list.SetEnabledAll(true); +} + +void Target::EnableAllowedBreakpoints() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s", __FUNCTION__); + + m_breakpoint_list.SetEnabledAllowed(true); +} + +bool Target::RemoveBreakpointByID(break_id_t break_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, + break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); + + if (DisableBreakpointByID(break_id)) { + if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) + m_internal_breakpoint_list.Remove(break_id, false); + else { + if (m_last_created_breakpoint) { + if (m_last_created_breakpoint->GetID() == break_id) + m_last_created_breakpoint.reset(); + } + m_breakpoint_list.Remove(break_id, true); + } + return true; + } + return false; +} + +bool Target::DisableBreakpointByID(break_id_t break_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, + break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); + + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); + if (bp_sp) { + bp_sp->SetEnabled(false); + return true; + } + return false; +} + +bool Target::EnableBreakpointByID(break_id_t break_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, + break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); + + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); + + if (bp_sp) { + bp_sp->SetEnabled(true); + return true; + } + return false; +} + +Status Target::SerializeBreakpointsToFile(const FileSpec &file, + const BreakpointIDList &bp_ids, + bool append) { + Status error; + + if (!file) { + error.SetErrorString("Invalid FileSpec."); + return error; + } + + std::string path(file.GetPath()); + StructuredData::ObjectSP input_data_sp; + + StructuredData::ArraySP break_store_sp; + StructuredData::Array *break_store_ptr = nullptr; + + if (append) { + input_data_sp = StructuredData::ParseJSONFromFile(file, error); + if (error.Success()) { + break_store_ptr = input_data_sp->GetAsArray(); + if (!break_store_ptr) { + error.SetErrorStringWithFormat( + "Tried to append to invalid input file %s", path.c_str()); + return error; + } + } + } + + if (!break_store_ptr) { + break_store_sp = std::make_shared<StructuredData::Array>(); + break_store_ptr = break_store_sp.get(); + } + + StreamFile out_file(path.c_str(), + File::eOpenOptionTruncate | File::eOpenOptionWrite | + File::eOpenOptionCanCreate | + File::eOpenOptionCloseOnExec, + lldb::eFilePermissionsFileDefault); + if (!out_file.GetFile().IsValid()) { + error.SetErrorStringWithFormat("Unable to open output file: %s.", + path.c_str()); + return error; + } + + std::unique_lock<std::recursive_mutex> lock; + GetBreakpointList().GetListMutex(lock); + + if (bp_ids.GetSize() == 0) { + const BreakpointList &breakpoints = GetBreakpointList(); + + size_t num_breakpoints = breakpoints.GetSize(); + for (size_t i = 0; i < num_breakpoints; i++) { + Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get(); + StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData(); + // If a breakpoint can't serialize it, just ignore it for now: + if (bkpt_save_sp) + break_store_ptr->AddItem(bkpt_save_sp); + } + } else { + + std::unordered_set<lldb::break_id_t> processed_bkpts; + const size_t count = bp_ids.GetSize(); + for (size_t i = 0; i < count; ++i) { + BreakpointID cur_bp_id = bp_ids.GetBreakpointIDAtIndex(i); + lldb::break_id_t bp_id = cur_bp_id.GetBreakpointID(); + + if (bp_id != LLDB_INVALID_BREAK_ID) { + // Only do each breakpoint once: + std::pair<std::unordered_set<lldb::break_id_t>::iterator, bool> + insert_result = processed_bkpts.insert(bp_id); + if (!insert_result.second) + continue; + + Breakpoint *bp = GetBreakpointByID(bp_id).get(); + StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData(); + // If the user explicitly asked to serialize a breakpoint, and we + // can't, then raise an error: + if (!bkpt_save_sp) { + error.SetErrorStringWithFormat("Unable to serialize breakpoint %d", + bp_id); + return error; + } + break_store_ptr->AddItem(bkpt_save_sp); + } + } + } + + break_store_ptr->Dump(out_file, false); + out_file.PutChar('\n'); + return error; +} + +Status Target::CreateBreakpointsFromFile(const FileSpec &file, + BreakpointIDList &new_bps) { + std::vector<std::string> no_names; + return CreateBreakpointsFromFile(file, no_names, new_bps); +} + +Status Target::CreateBreakpointsFromFile(const FileSpec &file, + std::vector<std::string> &names, + BreakpointIDList &new_bps) { + std::unique_lock<std::recursive_mutex> lock; + GetBreakpointList().GetListMutex(lock); + + Status error; + StructuredData::ObjectSP input_data_sp = + StructuredData::ParseJSONFromFile(file, error); + if (!error.Success()) { + return error; + } else if (!input_data_sp || !input_data_sp->IsValid()) { + error.SetErrorStringWithFormat("Invalid JSON from input file: %s.", + file.GetPath().c_str()); + return error; + } + + StructuredData::Array *bkpt_array = input_data_sp->GetAsArray(); + if (!bkpt_array) { + error.SetErrorStringWithFormat( + "Invalid breakpoint data from input file: %s.", file.GetPath().c_str()); + return error; + } + + size_t num_bkpts = bkpt_array->GetSize(); + size_t num_names = names.size(); + + for (size_t i = 0; i < num_bkpts; i++) { + StructuredData::ObjectSP bkpt_object_sp = bkpt_array->GetItemAtIndex(i); + // Peel off the breakpoint key, and feed the rest to the Breakpoint: + StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary(); + if (!bkpt_dict) { + error.SetErrorStringWithFormat( + "Invalid breakpoint data for element %zu from input file: %s.", i, + file.GetPath().c_str()); + return error; + } + StructuredData::ObjectSP bkpt_data_sp = + bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey()); + if (num_names && + !Breakpoint::SerializedBreakpointMatchesNames(bkpt_data_sp, names)) + continue; + + BreakpointSP bkpt_sp = + Breakpoint::CreateFromStructuredData(*this, bkpt_data_sp, error); + if (!error.Success()) { + error.SetErrorStringWithFormat( + "Error restoring breakpoint %zu from %s: %s.", i, + file.GetPath().c_str(), error.AsCString()); + return error; + } + new_bps.AddBreakpointID(BreakpointID(bkpt_sp->GetID())); + } + return error; +} + +// The flag 'end_to_end', default to true, signifies that the operation is +// performed end to end, for both the debugger and the debuggee. + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end +// to end operations. +bool Target::RemoveAllWatchpoints(bool end_to_end) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); + + if (!end_to_end) { + m_watchpoint_list.RemoveAll(true); + return true; + } + + // Otherwise, it's an end to end operation. + + if (!ProcessIsValid()) + return false; + + size_t num_watchpoints = m_watchpoint_list.GetSize(); + for (size_t i = 0; i < num_watchpoints; ++i) { + WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); + if (!wp_sp) + return false; + + Status rc = m_process_sp->DisableWatchpoint(wp_sp.get()); + if (rc.Fail()) + return false; + } + m_watchpoint_list.RemoveAll(true); + m_last_created_watchpoint.reset(); + return true; // Success! +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end +// to end operations. +bool Target::DisableAllWatchpoints(bool end_to_end) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); + + if (!end_to_end) { + m_watchpoint_list.SetEnabledAll(false); + return true; + } + + // Otherwise, it's an end to end operation. + + if (!ProcessIsValid()) + return false; + + size_t num_watchpoints = m_watchpoint_list.GetSize(); + for (size_t i = 0; i < num_watchpoints; ++i) { + WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); + if (!wp_sp) + return false; + + Status rc = m_process_sp->DisableWatchpoint(wp_sp.get()); + if (rc.Fail()) + return false; + } + return true; // Success! +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end +// to end operations. +bool Target::EnableAllWatchpoints(bool end_to_end) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); + + if (!end_to_end) { + m_watchpoint_list.SetEnabledAll(true); + return true; + } + + // Otherwise, it's an end to end operation. + + if (!ProcessIsValid()) + return false; + + size_t num_watchpoints = m_watchpoint_list.GetSize(); + for (size_t i = 0; i < num_watchpoints; ++i) { + WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); + if (!wp_sp) + return false; + + Status rc = m_process_sp->EnableWatchpoint(wp_sp.get()); + if (rc.Fail()) + return false; + } + return true; // Success! +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list. +bool Target::ClearAllWatchpointHitCounts() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); + + size_t num_watchpoints = m_watchpoint_list.GetSize(); + for (size_t i = 0; i < num_watchpoints; ++i) { + WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); + if (!wp_sp) + return false; + + wp_sp->ResetHitCount(); + } + return true; // Success! +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list. +bool Target::ClearAllWatchpointHistoricValues() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); + + size_t num_watchpoints = m_watchpoint_list.GetSize(); + for (size_t i = 0; i < num_watchpoints; ++i) { + WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); + if (!wp_sp) + return false; + + wp_sp->ResetHistoricValues(); + } + return true; // Success! +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list during +// these operations. +bool Target::IgnoreAllWatchpoints(uint32_t ignore_count) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); + + if (!ProcessIsValid()) + return false; + + size_t num_watchpoints = m_watchpoint_list.GetSize(); + for (size_t i = 0; i < num_watchpoints; ++i) { + WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); + if (!wp_sp) + return false; + + wp_sp->SetIgnoreCount(ignore_count); + } + return true; // Success! +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list. +bool Target::DisableWatchpointByID(lldb::watch_id_t watch_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); + + if (!ProcessIsValid()) + return false; + + WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); + if (wp_sp) { + Status rc = m_process_sp->DisableWatchpoint(wp_sp.get()); + if (rc.Success()) + return true; + + // Else, fallthrough. + } + return false; +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list. +bool Target::EnableWatchpointByID(lldb::watch_id_t watch_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); + + if (!ProcessIsValid()) + return false; + + WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); + if (wp_sp) { + Status rc = m_process_sp->EnableWatchpoint(wp_sp.get()); + if (rc.Success()) + return true; + + // Else, fallthrough. + } + return false; +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list. +bool Target::RemoveWatchpointByID(lldb::watch_id_t watch_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); + + WatchpointSP watch_to_remove_sp = m_watchpoint_list.FindByID(watch_id); + if (watch_to_remove_sp == m_last_created_watchpoint) + m_last_created_watchpoint.reset(); + + if (DisableWatchpointByID(watch_id)) { + m_watchpoint_list.Remove(watch_id, true); + return true; + } + return false; +} + +// Assumption: Caller holds the list mutex lock for m_watchpoint_list. +bool Target::IgnoreWatchpointByID(lldb::watch_id_t watch_id, + uint32_t ignore_count) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + LLDB_LOGF(log, "Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); + + if (!ProcessIsValid()) + return false; + + WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); + if (wp_sp) { + wp_sp->SetIgnoreCount(ignore_count); + return true; + } + return false; +} + +ModuleSP Target::GetExecutableModule() { + // search for the first executable in the module list + for (size_t i = 0; i < m_images.GetSize(); ++i) { + ModuleSP module_sp = m_images.GetModuleAtIndex(i); + lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); + if (obj == nullptr) + continue; + if (obj->GetType() == ObjectFile::Type::eTypeExecutable) + return module_sp; + } + // as fall back return the first module loaded + return m_images.GetModuleAtIndex(0); +} + +Module *Target::GetExecutableModulePointer() { + return GetExecutableModule().get(); +} + +static void LoadScriptingResourceForModule(const ModuleSP &module_sp, + Target *target) { + Status error; + StreamString feedback_stream; + if (module_sp && !module_sp->LoadScriptingResourceInTarget( + target, error, &feedback_stream)) { + if (error.AsCString()) + target->GetDebugger().GetErrorStream().Printf( + "unable to load scripting data for module %s - error reported was " + "%s\n", + module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), + error.AsCString()); + } + if (feedback_stream.GetSize()) + target->GetDebugger().GetErrorStream().Printf("%s\n", + feedback_stream.GetData()); +} + +void Target::ClearModules(bool delete_locations) { + ModulesDidUnload(m_images, delete_locations); + m_section_load_history.Clear(); + m_images.Clear(); + m_scratch_type_system_map.Clear(); + m_ast_importer_sp.reset(); +} + +void Target::DidExec() { + // When a process exec's we need to know about it so we can do some cleanup. + m_breakpoint_list.RemoveInvalidLocations(m_arch.GetSpec()); + m_internal_breakpoint_list.RemoveInvalidLocations(m_arch.GetSpec()); +} + +void Target::SetExecutableModule(ModuleSP &executable_sp, + LoadDependentFiles load_dependent_files) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); + ClearModules(false); + + if (executable_sp) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, + "Target::SetExecutableModule (executable = '%s')", + executable_sp->GetFileSpec().GetPath().c_str()); + + const bool notify = true; + m_images.Append(executable_sp, + notify); // The first image is our executable file + + // If we haven't set an architecture yet, reset our architecture based on + // what we found in the executable module. + if (!m_arch.GetSpec().IsValid()) { + m_arch = executable_sp->GetArchitecture(); + LLDB_LOG(log, + "setting architecture to {0} ({1}) based on executable file", + m_arch.GetSpec().GetArchitectureName(), + m_arch.GetSpec().GetTriple().getTriple()); + } + + FileSpecList dependent_files; + ObjectFile *executable_objfile = executable_sp->GetObjectFile(); + bool load_dependents = true; + switch (load_dependent_files) { + case eLoadDependentsDefault: + load_dependents = executable_sp->IsExecutable(); + break; + case eLoadDependentsYes: + load_dependents = true; + break; + case eLoadDependentsNo: + load_dependents = false; + break; + } + + if (executable_objfile && load_dependents) { + ModuleList added_modules; + executable_objfile->GetDependentModules(dependent_files); + for (uint32_t i = 0; i < dependent_files.GetSize(); i++) { + FileSpec dependent_file_spec(dependent_files.GetFileSpecAtIndex(i)); + FileSpec platform_dependent_file_spec; + if (m_platform_sp) + m_platform_sp->GetFileWithUUID(dependent_file_spec, nullptr, + platform_dependent_file_spec); + else + platform_dependent_file_spec = dependent_file_spec; + + ModuleSpec module_spec(platform_dependent_file_spec, m_arch.GetSpec()); + ModuleSP image_module_sp( + GetOrCreateModule(module_spec, false /* notify */)); + if (image_module_sp) { + added_modules.AppendIfNeeded(image_module_sp, false); + ObjectFile *objfile = image_module_sp->GetObjectFile(); + if (objfile) + objfile->GetDependentModules(dependent_files); + } + } + ModulesDidLoad(added_modules); + } + } +} + +bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); + bool missing_local_arch = !m_arch.GetSpec().IsValid(); + bool replace_local_arch = true; + bool compatible_local_arch = false; + ArchSpec other(arch_spec); + + // Changing the architecture might mean that the currently selected platform + // isn't compatible. Set the platform correctly if we are asked to do so, + // otherwise assume the user will set the platform manually. + if (set_platform) { + if (other.IsValid()) { + auto platform_sp = GetPlatform(); + if (!platform_sp || + !platform_sp->IsCompatibleArchitecture(other, false, nullptr)) { + ArchSpec platform_arch; + auto arch_platform_sp = + Platform::GetPlatformForArchitecture(other, &platform_arch); + if (arch_platform_sp) { + SetPlatform(arch_platform_sp); + if (platform_arch.IsValid()) + other = platform_arch; + } + } + } + } + + if (!missing_local_arch) { + if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { + other.MergeFrom(m_arch.GetSpec()); + + if (m_arch.GetSpec().IsCompatibleMatch(other)) { + compatible_local_arch = true; + bool arch_changed, vendor_changed, os_changed, os_ver_changed, + env_changed; + + m_arch.GetSpec().PiecewiseTripleCompare(other, arch_changed, + vendor_changed, os_changed, + os_ver_changed, env_changed); + + if (!arch_changed && !vendor_changed && !os_changed && !env_changed) + replace_local_arch = false; + } + } + } + + if (compatible_local_arch || missing_local_arch) { + // If we haven't got a valid arch spec, or the architectures are compatible + // update the architecture, unless the one we already have is more + // specified + if (replace_local_arch) + m_arch = other; + LLDB_LOG(log, "set architecture to {0} ({1})", + m_arch.GetSpec().GetArchitectureName(), + m_arch.GetSpec().GetTriple().getTriple()); + return true; + } + + // If we have an executable file, try to reset the executable to the desired + // architecture + LLDB_LOGF(log, "Target::SetArchitecture changing architecture to %s (%s)", + arch_spec.GetArchitectureName(), + arch_spec.GetTriple().getTriple().c_str()); + m_arch = other; + ModuleSP executable_sp = GetExecutableModule(); + + ClearModules(true); + // Need to do something about unsetting breakpoints. + + if (executable_sp) { + LLDB_LOGF(log, + "Target::SetArchitecture Trying to select executable file " + "architecture %s (%s)", + arch_spec.GetArchitectureName(), + arch_spec.GetTriple().getTriple().c_str()); + ModuleSpec module_spec(executable_sp->GetFileSpec(), other); + FileSpecList search_paths = GetExecutableSearchPaths(); + Status error = ModuleList::GetSharedModule(module_spec, executable_sp, + &search_paths, nullptr, nullptr); + + if (!error.Fail() && executable_sp) { + SetExecutableModule(executable_sp, eLoadDependentsYes); + return true; + } + } + return false; +} + +bool Target::MergeArchitecture(const ArchSpec &arch_spec) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); + if (arch_spec.IsValid()) { + if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { + // The current target arch is compatible with "arch_spec", see if we can + // improve our current architecture using bits from "arch_spec" + + LLDB_LOGF(log, + "Target::MergeArchitecture target has arch %s, merging with " + "arch %s", + m_arch.GetSpec().GetTriple().getTriple().c_str(), + arch_spec.GetTriple().getTriple().c_str()); + + // Merge bits from arch_spec into "merged_arch" and set our architecture + ArchSpec merged_arch(m_arch.GetSpec()); + merged_arch.MergeFrom(arch_spec); + return SetArchitecture(merged_arch); + } else { + // The new architecture is different, we just need to replace it + return SetArchitecture(arch_spec); + } + } + return false; +} + +void Target::NotifyWillClearList(const ModuleList &module_list) {} + +void Target::NotifyModuleAdded(const ModuleList &module_list, + const ModuleSP &module_sp) { + // A module is being added to this target for the first time + if (m_valid) { + ModuleList my_module_list; + my_module_list.Append(module_sp); + ModulesDidLoad(my_module_list); + } +} + +void Target::NotifyModuleRemoved(const ModuleList &module_list, + const ModuleSP &module_sp) { + // A module is being removed from this target. + if (m_valid) { + ModuleList my_module_list; + my_module_list.Append(module_sp); + ModulesDidUnload(my_module_list, false); + } +} + +void Target::NotifyModuleUpdated(const ModuleList &module_list, + const ModuleSP &old_module_sp, + const ModuleSP &new_module_sp) { + // A module is replacing an already added module + if (m_valid) { + m_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced(old_module_sp, + new_module_sp); + m_internal_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced( + old_module_sp, new_module_sp); + } +} + +void Target::NotifyModulesRemoved(lldb_private::ModuleList &module_list) { + ModulesDidUnload(module_list, false); +} + +void Target::ModulesDidLoad(ModuleList &module_list) { + const size_t num_images = module_list.GetSize(); + if (m_valid && num_images) { + for (size_t idx = 0; idx < num_images; ++idx) { + ModuleSP module_sp(module_list.GetModuleAtIndex(idx)); + LoadScriptingResourceForModule(module_sp, this); + } + m_breakpoint_list.UpdateBreakpoints(module_list, true, false); + m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false); + if (m_process_sp) { + m_process_sp->ModulesDidLoad(module_list); + } + BroadcastEvent(eBroadcastBitModulesLoaded, + new TargetEventData(this->shared_from_this(), module_list)); + } +} + +void Target::SymbolsDidLoad(ModuleList &module_list) { + if (m_valid && module_list.GetSize()) { + if (m_process_sp) { + for (LanguageRuntime *runtime : m_process_sp->GetLanguageRuntimes()) { + runtime->SymbolsDidLoad(module_list); + } + } + + m_breakpoint_list.UpdateBreakpoints(module_list, true, false); + m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false); + BroadcastEvent(eBroadcastBitSymbolsLoaded, + new TargetEventData(this->shared_from_this(), module_list)); + } +} + +void Target::ModulesDidUnload(ModuleList &module_list, bool delete_locations) { + if (m_valid && module_list.GetSize()) { + UnloadModuleSections(module_list); + m_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations); + m_internal_breakpoint_list.UpdateBreakpoints(module_list, false, + delete_locations); + BroadcastEvent(eBroadcastBitModulesUnloaded, + new TargetEventData(this->shared_from_this(), module_list)); + } +} + +bool Target::ModuleIsExcludedForUnconstrainedSearches( + const FileSpec &module_file_spec) { + if (GetBreakpointsConsultPlatformAvoidList()) { + ModuleList matchingModules; + ModuleSpec module_spec(module_file_spec); + GetImages().FindModules(module_spec, matchingModules); + size_t num_modules = matchingModules.GetSize(); + + // If there is more than one module for this file spec, only + // return true if ALL the modules are on the black list. + if (num_modules > 0) { + for (size_t i = 0; i < num_modules; i++) { + if (!ModuleIsExcludedForUnconstrainedSearches( + matchingModules.GetModuleAtIndex(i))) + return false; + } + return true; + } + } + return false; +} + +bool Target::ModuleIsExcludedForUnconstrainedSearches( + const lldb::ModuleSP &module_sp) { + if (GetBreakpointsConsultPlatformAvoidList()) { + if (m_platform_sp) + return m_platform_sp->ModuleIsExcludedForUnconstrainedSearches(*this, + module_sp); + } + return false; +} + +size_t Target::ReadMemoryFromFileCache(const Address &addr, void *dst, + size_t dst_len, Status &error) { + SectionSP section_sp(addr.GetSection()); + if (section_sp) { + // If the contents of this section are encrypted, the on-disk file is + // unusable. Read only from live memory. + if (section_sp->IsEncrypted()) { + error.SetErrorString("section is encrypted"); + return 0; + } + ModuleSP module_sp(section_sp->GetModule()); + if (module_sp) { + ObjectFile *objfile = section_sp->GetModule()->GetObjectFile(); + if (objfile) { + size_t bytes_read = objfile->ReadSectionData( + section_sp.get(), addr.GetOffset(), dst, dst_len); + if (bytes_read > 0) + return bytes_read; + else + error.SetErrorStringWithFormat("error reading data from section %s", + section_sp->GetName().GetCString()); + } else + error.SetErrorString("address isn't from a object file"); + } else + error.SetErrorString("address isn't in a module"); + } else + error.SetErrorString("address doesn't contain a section that points to a " + "section in a object file"); + + return 0; +} + +size_t Target::ReadMemory(const Address &addr, bool prefer_file_cache, + void *dst, size_t dst_len, Status &error, + lldb::addr_t *load_addr_ptr) { + error.Clear(); + + // if we end up reading this from process memory, we will fill this with the + // actual load address + if (load_addr_ptr) + *load_addr_ptr = LLDB_INVALID_ADDRESS; + + size_t bytes_read = 0; + + addr_t load_addr = LLDB_INVALID_ADDRESS; + addr_t file_addr = LLDB_INVALID_ADDRESS; + Address resolved_addr; + if (!addr.IsSectionOffset()) { + SectionLoadList §ion_load_list = GetSectionLoadList(); + if (section_load_list.IsEmpty()) { + // No sections are loaded, so we must assume we are not running yet and + // anything we are given is a file address. + file_addr = addr.GetOffset(); // "addr" doesn't have a section, so its + // offset is the file address + m_images.ResolveFileAddress(file_addr, resolved_addr); + } else { + // We have at least one section loaded. This can be because we have + // manually loaded some sections with "target modules load ..." or + // because we have have a live process that has sections loaded through + // the dynamic loader + load_addr = addr.GetOffset(); // "addr" doesn't have a section, so its + // offset is the load address + section_load_list.ResolveLoadAddress(load_addr, resolved_addr); + } + } + if (!resolved_addr.IsValid()) + resolved_addr = addr; + + if (prefer_file_cache) { + bytes_read = ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); + if (bytes_read > 0) + return bytes_read; + } + + if (ProcessIsValid()) { + if (load_addr == LLDB_INVALID_ADDRESS) + load_addr = resolved_addr.GetLoadAddress(this); + + if (load_addr == LLDB_INVALID_ADDRESS) { + ModuleSP addr_module_sp(resolved_addr.GetModule()); + if (addr_module_sp && addr_module_sp->GetFileSpec()) + error.SetErrorStringWithFormatv( + "{0:F}[{1:x+}] can't be resolved, {0:F} is not currently loaded", + addr_module_sp->GetFileSpec(), resolved_addr.GetFileAddress()); + else + error.SetErrorStringWithFormat("0x%" PRIx64 " can't be resolved", + resolved_addr.GetFileAddress()); + } else { + bytes_read = m_process_sp->ReadMemory(load_addr, dst, dst_len, error); + if (bytes_read != dst_len) { + if (error.Success()) { + if (bytes_read == 0) + error.SetErrorStringWithFormat( + "read memory from 0x%" PRIx64 " failed", load_addr); + else + error.SetErrorStringWithFormat( + "only %" PRIu64 " of %" PRIu64 + " bytes were read from memory at 0x%" PRIx64, + (uint64_t)bytes_read, (uint64_t)dst_len, load_addr); + } + } + if (bytes_read) { + if (load_addr_ptr) + *load_addr_ptr = load_addr; + return bytes_read; + } + // If the address is not section offset we have an address that doesn't + // resolve to any address in any currently loaded shared libraries and we + // failed to read memory so there isn't anything more we can do. If it is + // section offset, we might be able to read cached memory from the object + // file. + if (!resolved_addr.IsSectionOffset()) + return 0; + } + } + + if (!prefer_file_cache && resolved_addr.IsSectionOffset()) { + // If we didn't already try and read from the object file cache, then try + // it after failing to read from the process. + return ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); + } + return 0; +} + +size_t Target::ReadCStringFromMemory(const Address &addr, std::string &out_str, + Status &error) { + char buf[256]; + out_str.clear(); + addr_t curr_addr = addr.GetLoadAddress(this); + Address address(addr); + while (true) { + size_t length = ReadCStringFromMemory(address, buf, sizeof(buf), error); + if (length == 0) + break; + out_str.append(buf, length); + // If we got "length - 1" bytes, we didn't get the whole C string, we need + // to read some more characters + if (length == sizeof(buf) - 1) + curr_addr += length; + else + break; + address = Address(curr_addr); + } + return out_str.size(); +} + +size_t Target::ReadCStringFromMemory(const Address &addr, char *dst, + size_t dst_max_len, Status &result_error) { + size_t total_cstr_len = 0; + if (dst && dst_max_len) { + result_error.Clear(); + // NULL out everything just to be safe + memset(dst, 0, dst_max_len); + Status error; + addr_t curr_addr = addr.GetLoadAddress(this); + Address address(addr); + + // We could call m_process_sp->GetMemoryCacheLineSize() but I don't think + // this really needs to be tied to the memory cache subsystem's cache line + // size, so leave this as a fixed constant. + const size_t cache_line_size = 512; + + size_t bytes_left = dst_max_len - 1; + char *curr_dst = dst; + + while (bytes_left > 0) { + addr_t cache_line_bytes_left = + cache_line_size - (curr_addr % cache_line_size); + addr_t bytes_to_read = + std::min<addr_t>(bytes_left, cache_line_bytes_left); + size_t bytes_read = + ReadMemory(address, false, curr_dst, bytes_to_read, error); + + if (bytes_read == 0) { + result_error = error; + dst[total_cstr_len] = '\0'; + break; + } + const size_t len = strlen(curr_dst); + + total_cstr_len += len; + + if (len < bytes_to_read) + break; + + curr_dst += bytes_read; + curr_addr += bytes_read; + bytes_left -= bytes_read; + address = Address(curr_addr); + } + } else { + if (dst == nullptr) + result_error.SetErrorString("invalid arguments"); + else + result_error.Clear(); + } + return total_cstr_len; +} + +size_t Target::ReadScalarIntegerFromMemory(const Address &addr, + bool prefer_file_cache, + uint32_t byte_size, bool is_signed, + Scalar &scalar, Status &error) { + uint64_t uval; + + if (byte_size <= sizeof(uval)) { + size_t bytes_read = + ReadMemory(addr, prefer_file_cache, &uval, byte_size, error); + if (bytes_read == byte_size) { + DataExtractor data(&uval, sizeof(uval), m_arch.GetSpec().GetByteOrder(), + m_arch.GetSpec().GetAddressByteSize()); + lldb::offset_t offset = 0; + if (byte_size <= 4) + scalar = data.GetMaxU32(&offset, byte_size); + else + scalar = data.GetMaxU64(&offset, byte_size); + + if (is_signed) + scalar.SignExtend(byte_size * 8); + return bytes_read; + } + } else { + error.SetErrorStringWithFormat( + "byte size of %u is too large for integer scalar type", byte_size); + } + return 0; +} + +uint64_t Target::ReadUnsignedIntegerFromMemory(const Address &addr, + bool prefer_file_cache, + size_t integer_byte_size, + uint64_t fail_value, + Status &error) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(addr, prefer_file_cache, integer_byte_size, + false, scalar, error)) + return scalar.ULongLong(fail_value); + return fail_value; +} + +bool Target::ReadPointerFromMemory(const Address &addr, bool prefer_file_cache, + Status &error, Address &pointer_addr) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(addr, prefer_file_cache, + m_arch.GetSpec().GetAddressByteSize(), false, + scalar, error)) { + addr_t pointer_vm_addr = scalar.ULongLong(LLDB_INVALID_ADDRESS); + if (pointer_vm_addr != LLDB_INVALID_ADDRESS) { + SectionLoadList §ion_load_list = GetSectionLoadList(); + if (section_load_list.IsEmpty()) { + // No sections are loaded, so we must assume we are not running yet and + // anything we are given is a file address. + m_images.ResolveFileAddress(pointer_vm_addr, pointer_addr); + } else { + // We have at least one section loaded. This can be because we have + // manually loaded some sections with "target modules load ..." or + // because we have have a live process that has sections loaded through + // the dynamic loader + section_load_list.ResolveLoadAddress(pointer_vm_addr, pointer_addr); + } + // We weren't able to resolve the pointer value, so just return an + // address with no section + if (!pointer_addr.IsValid()) + pointer_addr.SetOffset(pointer_vm_addr); + return true; + } + } + return false; +} + +ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify, + Status *error_ptr) { + ModuleSP module_sp; + + Status error; + + // First see if we already have this module in our module list. If we do, + // then we're done, we don't need to consult the shared modules list. But + // only do this if we are passed a UUID. + + if (module_spec.GetUUID().IsValid()) + module_sp = m_images.FindFirstModule(module_spec); + + if (!module_sp) { + ModuleSP old_module_sp; // This will get filled in if we have a new version + // of the library + bool did_create_module = false; + FileSpecList search_paths = GetExecutableSearchPaths(); + // If there are image search path entries, try to use them first to acquire + // a suitable image. + if (m_image_search_paths.GetSize()) { + ModuleSpec transformed_spec(module_spec); + if (m_image_search_paths.RemapPath( + module_spec.GetFileSpec().GetDirectory(), + transformed_spec.GetFileSpec().GetDirectory())) { + transformed_spec.GetFileSpec().GetFilename() = + module_spec.GetFileSpec().GetFilename(); + error = ModuleList::GetSharedModule(transformed_spec, module_sp, + &search_paths, &old_module_sp, + &did_create_module); + } + } + + if (!module_sp) { + // If we have a UUID, we can check our global shared module list in case + // we already have it. If we don't have a valid UUID, then we can't since + // the path in "module_spec" will be a platform path, and we will need to + // let the platform find that file. For example, we could be asking for + // "/usr/lib/dyld" and if we do not have a UUID, we don't want to pick + // the local copy of "/usr/lib/dyld" since our platform could be a remote + // platform that has its own "/usr/lib/dyld" in an SDK or in a local file + // cache. + if (module_spec.GetUUID().IsValid()) { + // We have a UUID, it is OK to check the global module list... + error = + ModuleList::GetSharedModule(module_spec, module_sp, &search_paths, + &old_module_sp, &did_create_module); + } + + if (!module_sp) { + // The platform is responsible for finding and caching an appropriate + // module in the shared module cache. + if (m_platform_sp) { + error = m_platform_sp->GetSharedModule( + module_spec, m_process_sp.get(), module_sp, &search_paths, + &old_module_sp, &did_create_module); + } else { + error.SetErrorString("no platform is currently set"); + } + } + } + + // We found a module that wasn't in our target list. Let's make sure that + // there wasn't an equivalent module in the list already, and if there was, + // let's remove it. + if (module_sp) { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile) { + switch (objfile->GetType()) { + case ObjectFile::eTypeCoreFile: /// A core file that has a checkpoint of + /// a program's execution state + case ObjectFile::eTypeExecutable: /// A normal executable + case ObjectFile::eTypeDynamicLinker: /// The platform's dynamic linker + /// executable + case ObjectFile::eTypeObjectFile: /// An intermediate object file + case ObjectFile::eTypeSharedLibrary: /// A shared library that can be + /// used during execution + break; + case ObjectFile::eTypeDebugInfo: /// An object file that contains only + /// debug information + if (error_ptr) + error_ptr->SetErrorString("debug info files aren't valid target " + "modules, please specify an executable"); + return ModuleSP(); + case ObjectFile::eTypeStubLibrary: /// A library that can be linked + /// against but not used for + /// execution + if (error_ptr) + error_ptr->SetErrorString("stub libraries aren't valid target " + "modules, please specify an executable"); + return ModuleSP(); + default: + if (error_ptr) + error_ptr->SetErrorString( + "unsupported file type, please specify an executable"); + return ModuleSP(); + } + // GetSharedModule is not guaranteed to find the old shared module, for + // instance in the common case where you pass in the UUID, it is only + // going to find the one module matching the UUID. In fact, it has no + // good way to know what the "old module" relevant to this target is, + // since there might be many copies of a module with this file spec in + // various running debug sessions, but only one of them will belong to + // this target. So let's remove the UUID from the module list, and look + // in the target's module list. Only do this if there is SOMETHING else + // in the module spec... + if (!old_module_sp) { + if (module_spec.GetUUID().IsValid() && + !module_spec.GetFileSpec().GetFilename().IsEmpty() && + !module_spec.GetFileSpec().GetDirectory().IsEmpty()) { + ModuleSpec module_spec_copy(module_spec.GetFileSpec()); + module_spec_copy.GetUUID().Clear(); + + ModuleList found_modules; + m_images.FindModules(module_spec_copy, found_modules); + if (found_modules.GetSize() == 1) + old_module_sp = found_modules.GetModuleAtIndex(0); + } + } + + // Preload symbols outside of any lock, so hopefully we can do this for + // each library in parallel. + if (GetPreloadSymbols()) + module_sp->PreloadSymbols(); + + if (old_module_sp && m_images.GetIndexForModule(old_module_sp.get()) != + LLDB_INVALID_INDEX32) { + m_images.ReplaceModule(old_module_sp, module_sp); + Module *old_module_ptr = old_module_sp.get(); + old_module_sp.reset(); + ModuleList::RemoveSharedModuleIfOrphaned(old_module_ptr); + } else { + m_images.Append(module_sp, notify); + } + } else + module_sp.reset(); + } + } + if (error_ptr) + *error_ptr = error; + return module_sp; +} + +TargetSP Target::CalculateTarget() { return shared_from_this(); } + +ProcessSP Target::CalculateProcess() { return m_process_sp; } + +ThreadSP Target::CalculateThread() { return ThreadSP(); } + +StackFrameSP Target::CalculateStackFrame() { return StackFrameSP(); } + +void Target::CalculateExecutionContext(ExecutionContext &exe_ctx) { + exe_ctx.Clear(); + exe_ctx.SetTargetPtr(this); +} + +PathMappingList &Target::GetImageSearchPathList() { + return m_image_search_paths; +} + +void Target::ImageSearchPathsChanged(const PathMappingList &path_list, + void *baton) { + Target *target = (Target *)baton; + ModuleSP exe_module_sp(target->GetExecutableModule()); + if (exe_module_sp) + target->SetExecutableModule(exe_module_sp, eLoadDependentsYes); +} + +llvm::Expected<TypeSystem &> +Target::GetScratchTypeSystemForLanguage(lldb::LanguageType language, + bool create_on_demand) { + if (!m_valid) + return llvm::make_error<llvm::StringError>("Invalid Target", + llvm::inconvertibleErrorCode()); + + if (language == eLanguageTypeMipsAssembler // GNU AS and LLVM use it for all + // assembly code + || language == eLanguageTypeUnknown) { + LanguageSet languages_for_expressions = + Language::GetLanguagesSupportingTypeSystemsForExpressions(); + + if (languages_for_expressions[eLanguageTypeC]) { + language = eLanguageTypeC; // LLDB's default. Override by setting the + // target language. + } else { + if (languages_for_expressions.Empty()) + return llvm::make_error<llvm::StringError>( + "No expression support for any languages", + llvm::inconvertibleErrorCode()); + language = (LanguageType)languages_for_expressions.bitvector.find_first(); + } + } + + return m_scratch_type_system_map.GetTypeSystemForLanguage(language, this, + create_on_demand); +} + +std::vector<TypeSystem *> Target::GetScratchTypeSystems(bool create_on_demand) { + if (!m_valid) + return {}; + + std::vector<TypeSystem *> scratch_type_systems; + + LanguageSet languages_for_expressions = + Language::GetLanguagesSupportingTypeSystemsForExpressions(); + + for (auto bit : languages_for_expressions.bitvector.set_bits()) { + auto language = (LanguageType)bit; + auto type_system_or_err = + GetScratchTypeSystemForLanguage(language, create_on_demand); + if (!type_system_or_err) + LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET), + type_system_or_err.takeError(), + "Language '{}' has expression support but no scratch type " + "system available", + Language::GetNameForLanguageType(language)); + else + scratch_type_systems.emplace_back(&type_system_or_err.get()); + } + + return scratch_type_systems; +} + +PersistentExpressionState * +Target::GetPersistentExpressionStateForLanguage(lldb::LanguageType language) { + auto type_system_or_err = GetScratchTypeSystemForLanguage(language, true); + + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET), + std::move(err), + "Unable to get persistent expression state for language {}", + Language::GetNameForLanguageType(language)); + return nullptr; + } + + return type_system_or_err->GetPersistentExpressionState(); +} + +UserExpression *Target::GetUserExpressionForLanguage( + llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, + Expression::ResultType desired_type, + const EvaluateExpressionOptions &options, ValueObject *ctx_obj, + Status &error) { + auto type_system_or_err = GetScratchTypeSystemForLanguage(language); + if (auto err = type_system_or_err.takeError()) { + error.SetErrorStringWithFormat( + "Could not find type system for language %s: %s", + Language::GetNameForLanguageType(language), + llvm::toString(std::move(err)).c_str()); + return nullptr; + } + + auto *user_expr = type_system_or_err->GetUserExpression( + expr, prefix, language, desired_type, options, ctx_obj); + if (!user_expr) + error.SetErrorStringWithFormat( + "Could not create an expression for language %s", + Language::GetNameForLanguageType(language)); + + return user_expr; +} + +FunctionCaller *Target::GetFunctionCallerForLanguage( + lldb::LanguageType language, const CompilerType &return_type, + const Address &function_address, const ValueList &arg_value_list, + const char *name, Status &error) { + auto type_system_or_err = GetScratchTypeSystemForLanguage(language); + if (auto err = type_system_or_err.takeError()) { + error.SetErrorStringWithFormat( + "Could not find type system for language %s: %s", + Language::GetNameForLanguageType(language), + llvm::toString(std::move(err)).c_str()); + return nullptr; + } + + auto *persistent_fn = type_system_or_err->GetFunctionCaller( + return_type, function_address, arg_value_list, name); + if (!persistent_fn) + error.SetErrorStringWithFormat( + "Could not create an expression for language %s", + Language::GetNameForLanguageType(language)); + + return persistent_fn; +} + +UtilityFunction * +Target::GetUtilityFunctionForLanguage(const char *text, + lldb::LanguageType language, + const char *name, Status &error) { + auto type_system_or_err = GetScratchTypeSystemForLanguage(language); + + if (auto err = type_system_or_err.takeError()) { + error.SetErrorStringWithFormat( + "Could not find type system for language %s: %s", + Language::GetNameForLanguageType(language), + llvm::toString(std::move(err)).c_str()); + return nullptr; + } + + auto *utility_fn = type_system_or_err->GetUtilityFunction(text, name); + if (!utility_fn) + error.SetErrorStringWithFormat( + "Could not create an expression for language %s", + Language::GetNameForLanguageType(language)); + + return utility_fn; +} + +ClangASTImporterSP Target::GetClangASTImporter() { + if (m_valid) { + if (!m_ast_importer_sp) { + m_ast_importer_sp = std::make_shared<ClangASTImporter>(); + } + return m_ast_importer_sp; + } + return ClangASTImporterSP(); +} + +void Target::SettingsInitialize() { Process::SettingsInitialize(); } + +void Target::SettingsTerminate() { Process::SettingsTerminate(); } + +FileSpecList Target::GetDefaultExecutableSearchPaths() { + TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); + if (properties_sp) + return properties_sp->GetExecutableSearchPaths(); + return FileSpecList(); +} + +FileSpecList Target::GetDefaultDebugFileSearchPaths() { + TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); + if (properties_sp) + return properties_sp->GetDebugFileSearchPaths(); + return FileSpecList(); +} + +ArchSpec Target::GetDefaultArchitecture() { + TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); + if (properties_sp) + return properties_sp->GetDefaultArchitecture(); + return ArchSpec(); +} + +void Target::SetDefaultArchitecture(const ArchSpec &arch) { + TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); + if (properties_sp) { + LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET), + "Target::SetDefaultArchitecture setting target's " + "default architecture to {0} ({1})", + arch.GetArchitectureName(), arch.GetTriple().getTriple()); + return properties_sp->SetDefaultArchitecture(arch); + } +} + +Target *Target::GetTargetFromContexts(const ExecutionContext *exe_ctx_ptr, + const SymbolContext *sc_ptr) { + // The target can either exist in the "process" of ExecutionContext, or in + // the "target_sp" member of SymbolContext. This accessor helper function + // will get the target from one of these locations. + + Target *target = nullptr; + if (sc_ptr != nullptr) + target = sc_ptr->target_sp.get(); + if (target == nullptr && exe_ctx_ptr) + target = exe_ctx_ptr->GetTargetPtr(); + return target; +} + +ExpressionResults Target::EvaluateExpression( + llvm::StringRef expr, ExecutionContextScope *exe_scope, + lldb::ValueObjectSP &result_valobj_sp, + const EvaluateExpressionOptions &options, std::string *fixed_expression, + ValueObject *ctx_obj) { + result_valobj_sp.reset(); + + ExpressionResults execution_results = eExpressionSetupError; + + if (expr.empty()) + return execution_results; + + // We shouldn't run stop hooks in expressions. + bool old_suppress_value = m_suppress_stop_hooks; + m_suppress_stop_hooks = true; + auto on_exit = llvm::make_scope_exit([this, old_suppress_value]() { + m_suppress_stop_hooks = old_suppress_value; + }); + + ExecutionContext exe_ctx; + + if (exe_scope) { + exe_scope->CalculateExecutionContext(exe_ctx); + } else if (m_process_sp) { + m_process_sp->CalculateExecutionContext(exe_ctx); + } else { + CalculateExecutionContext(exe_ctx); + } + + // Make sure we aren't just trying to see the value of a persistent variable + // (something like "$0") + // Only check for persistent variables the expression starts with a '$' + lldb::ExpressionVariableSP persistent_var_sp; + if (expr[0] == '$') { + auto type_system_or_err = + GetScratchTypeSystemForLanguage(eLanguageTypeC); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET), + std::move(err), "Unable to get scratch type system"); + } else { + persistent_var_sp = + type_system_or_err->GetPersistentExpressionState()->GetVariable(expr); + } + } + if (persistent_var_sp) { + result_valobj_sp = persistent_var_sp->GetValueObject(); + execution_results = eExpressionCompleted; + } else { + llvm::StringRef prefix = GetExpressionPrefixContents(); + Status error; + execution_results = + UserExpression::Evaluate(exe_ctx, options, expr, prefix, + result_valobj_sp, error, fixed_expression, + nullptr, // Module + ctx_obj); + } + + return execution_results; +} + +lldb::ExpressionVariableSP Target::GetPersistentVariable(ConstString name) { + lldb::ExpressionVariableSP variable_sp; + m_scratch_type_system_map.ForEach( + [name, &variable_sp](TypeSystem *type_system) -> bool { + if (PersistentExpressionState *persistent_state = + type_system->GetPersistentExpressionState()) { + variable_sp = persistent_state->GetVariable(name); + + if (variable_sp) + return false; // Stop iterating the ForEach + } + return true; // Keep iterating the ForEach + }); + return variable_sp; +} + +lldb::addr_t Target::GetPersistentSymbol(ConstString name) { + lldb::addr_t address = LLDB_INVALID_ADDRESS; + + m_scratch_type_system_map.ForEach( + [name, &address](TypeSystem *type_system) -> bool { + if (PersistentExpressionState *persistent_state = + type_system->GetPersistentExpressionState()) { + address = persistent_state->LookupSymbol(name); + if (address != LLDB_INVALID_ADDRESS) + return false; // Stop iterating the ForEach + } + return true; // Keep iterating the ForEach + }); + return address; +} + +llvm::Expected<lldb_private::Address> Target::GetEntryPointAddress() { + Module *exe_module = GetExecutableModulePointer(); + llvm::Error error = llvm::Error::success(); + assert(!error); // Check the success value when assertions are enabled. + + if (!exe_module || !exe_module->GetObjectFile()) { + error = llvm::make_error<llvm::StringError>("No primary executable found", + llvm::inconvertibleErrorCode()); + } else { + Address entry_addr = exe_module->GetObjectFile()->GetEntryPointAddress(); + if (entry_addr.IsValid()) + return entry_addr; + + error = llvm::make_error<llvm::StringError>( + "Could not find entry point address for executable module \"" + + exe_module->GetFileSpec().GetFilename().GetStringRef() + "\"", + llvm::inconvertibleErrorCode()); + } + + const ModuleList &modules = GetImages(); + const size_t num_images = modules.GetSize(); + for (size_t idx = 0; idx < num_images; ++idx) { + ModuleSP module_sp(modules.GetModuleAtIndex(idx)); + if (!module_sp || !module_sp->GetObjectFile()) + continue; + + Address entry_addr = module_sp->GetObjectFile()->GetEntryPointAddress(); + if (entry_addr.IsValid()) { + // Discard the error. + llvm::consumeError(std::move(error)); + return entry_addr; + } + } + + return std::move(error); +} + +lldb::addr_t Target::GetCallableLoadAddress(lldb::addr_t load_addr, + AddressClass addr_class) const { + auto arch_plugin = GetArchitecturePlugin(); + return arch_plugin + ? arch_plugin->GetCallableLoadAddress(load_addr, addr_class) + : load_addr; +} + +lldb::addr_t Target::GetOpcodeLoadAddress(lldb::addr_t load_addr, + AddressClass addr_class) const { + auto arch_plugin = GetArchitecturePlugin(); + return arch_plugin ? arch_plugin->GetOpcodeLoadAddress(load_addr, addr_class) + : load_addr; +} + +lldb::addr_t Target::GetBreakableLoadAddress(lldb::addr_t addr) { + auto arch_plugin = GetArchitecturePlugin(); + return arch_plugin ? arch_plugin->GetBreakableLoadAddress(addr, *this) : addr; +} + +SourceManager &Target::GetSourceManager() { + if (!m_source_manager_up) + m_source_manager_up.reset(new SourceManager(shared_from_this())); + return *m_source_manager_up; +} + +ClangModulesDeclVendor *Target::GetClangModulesDeclVendor() { + static std::mutex s_clang_modules_decl_vendor_mutex; // If this is contended + // we can make it + // per-target + + { + std::lock_guard<std::mutex> guard(s_clang_modules_decl_vendor_mutex); + + if (!m_clang_modules_decl_vendor_up) { + m_clang_modules_decl_vendor_up.reset( + ClangModulesDeclVendor::Create(*this)); + } + } + + return m_clang_modules_decl_vendor_up.get(); +} + +Target::StopHookSP Target::CreateStopHook() { + lldb::user_id_t new_uid = ++m_stop_hook_next_id; + Target::StopHookSP stop_hook_sp(new StopHook(shared_from_this(), new_uid)); + m_stop_hooks[new_uid] = stop_hook_sp; + return stop_hook_sp; +} + +bool Target::RemoveStopHookByID(lldb::user_id_t user_id) { + size_t num_removed = m_stop_hooks.erase(user_id); + return (num_removed != 0); +} + +void Target::RemoveAllStopHooks() { m_stop_hooks.clear(); } + +Target::StopHookSP Target::GetStopHookByID(lldb::user_id_t user_id) { + StopHookSP found_hook; + + StopHookCollection::iterator specified_hook_iter; + specified_hook_iter = m_stop_hooks.find(user_id); + if (specified_hook_iter != m_stop_hooks.end()) + found_hook = (*specified_hook_iter).second; + return found_hook; +} + +bool Target::SetStopHookActiveStateByID(lldb::user_id_t user_id, + bool active_state) { + StopHookCollection::iterator specified_hook_iter; + specified_hook_iter = m_stop_hooks.find(user_id); + if (specified_hook_iter == m_stop_hooks.end()) + return false; + + (*specified_hook_iter).second->SetIsActive(active_state); + return true; +} + +void Target::SetAllStopHooksActiveState(bool active_state) { + StopHookCollection::iterator pos, end = m_stop_hooks.end(); + for (pos = m_stop_hooks.begin(); pos != end; pos++) { + (*pos).second->SetIsActive(active_state); + } +} + +void Target::RunStopHooks() { + if (m_suppress_stop_hooks) + return; + + if (!m_process_sp) + return; + + // Somebody might have restarted the process: + if (m_process_sp->GetState() != eStateStopped) + return; + + // <rdar://problem/12027563> make sure we check that we are not stopped + // because of us running a user expression since in that case we do not want + // to run the stop-hooks + if (m_process_sp->GetModIDRef().IsLastResumeForUserExpression()) + return; + + if (m_stop_hooks.empty()) + return; + + StopHookCollection::iterator pos, end = m_stop_hooks.end(); + + // If there aren't any active stop hooks, don't bother either. + // Also see if any of the active hooks want to auto-continue. + bool any_active_hooks = false; + bool auto_continue = false; + for (auto hook : m_stop_hooks) { + if (hook.second->IsActive()) { + any_active_hooks = true; + auto_continue |= hook.second->GetAutoContinue(); + } + } + if (!any_active_hooks) + return; + + CommandReturnObject result; + + std::vector<ExecutionContext> exc_ctx_with_reasons; + std::vector<SymbolContext> sym_ctx_with_reasons; + + ThreadList &cur_threadlist = m_process_sp->GetThreadList(); + size_t num_threads = cur_threadlist.GetSize(); + for (size_t i = 0; i < num_threads; i++) { + lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex(i); + if (cur_thread_sp->ThreadStoppedForAReason()) { + lldb::StackFrameSP cur_frame_sp = cur_thread_sp->GetStackFrameAtIndex(0); + exc_ctx_with_reasons.push_back(ExecutionContext( + m_process_sp.get(), cur_thread_sp.get(), cur_frame_sp.get())); + sym_ctx_with_reasons.push_back( + cur_frame_sp->GetSymbolContext(eSymbolContextEverything)); + } + } + + // If no threads stopped for a reason, don't run the stop-hooks. + size_t num_exe_ctx = exc_ctx_with_reasons.size(); + if (num_exe_ctx == 0) + return; + + result.SetImmediateOutputStream(m_debugger.GetAsyncOutputStream()); + result.SetImmediateErrorStream(m_debugger.GetAsyncErrorStream()); + + bool keep_going = true; + bool hooks_ran = false; + bool print_hook_header = (m_stop_hooks.size() != 1); + bool print_thread_header = (num_exe_ctx != 1); + bool did_restart = false; + + for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++) { + // result.Clear(); + StopHookSP cur_hook_sp = (*pos).second; + if (!cur_hook_sp->IsActive()) + continue; + + bool any_thread_matched = false; + for (size_t i = 0; keep_going && i < num_exe_ctx; i++) { + if ((cur_hook_sp->GetSpecifier() == nullptr || + cur_hook_sp->GetSpecifier()->SymbolContextMatches( + sym_ctx_with_reasons[i])) && + (cur_hook_sp->GetThreadSpecifier() == nullptr || + cur_hook_sp->GetThreadSpecifier()->ThreadPassesBasicTests( + exc_ctx_with_reasons[i].GetThreadRef()))) { + if (!hooks_ran) { + hooks_ran = true; + } + if (print_hook_header && !any_thread_matched) { + const char *cmd = + (cur_hook_sp->GetCommands().GetSize() == 1 + ? cur_hook_sp->GetCommands().GetStringAtIndex(0) + : nullptr); + if (cmd) + result.AppendMessageWithFormat("\n- Hook %" PRIu64 " (%s)\n", + cur_hook_sp->GetID(), cmd); + else + result.AppendMessageWithFormat("\n- Hook %" PRIu64 "\n", + cur_hook_sp->GetID()); + any_thread_matched = true; + } + + if (print_thread_header) + result.AppendMessageWithFormat( + "-- Thread %d\n", + exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID()); + + CommandInterpreterRunOptions options; + options.SetStopOnContinue(true); + options.SetStopOnError(true); + options.SetEchoCommands(false); + options.SetPrintResults(true); + options.SetPrintErrors(true); + options.SetAddToHistory(false); + + // Force Async: + bool old_async = GetDebugger().GetAsyncExecution(); + GetDebugger().SetAsyncExecution(true); + GetDebugger().GetCommandInterpreter().HandleCommands( + cur_hook_sp->GetCommands(), &exc_ctx_with_reasons[i], options, + result); + GetDebugger().SetAsyncExecution(old_async); + // If the command started the target going again, we should bag out of + // running the stop hooks. + if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) || + (result.GetStatus() == eReturnStatusSuccessContinuingResult)) { + // But only complain if there were more stop hooks to do: + StopHookCollection::iterator tmp = pos; + if (++tmp != end) + result.AppendMessageWithFormat( + "\nAborting stop hooks, hook %" PRIu64 + " set the program running.\n" + " Consider using '-G true' to make " + "stop hooks auto-continue.\n", + cur_hook_sp->GetID()); + keep_going = false; + did_restart = true; + } + } + } + } + // Finally, if auto-continue was requested, do it now: + if (!did_restart && auto_continue) + m_process_sp->PrivateResume(); + + result.GetImmediateOutputStream()->Flush(); + result.GetImmediateErrorStream()->Flush(); +} + +const TargetPropertiesSP &Target::GetGlobalProperties() { + // NOTE: intentional leak so we don't crash if global destructor chain gets + // called as other threads still use the result of this function + static TargetPropertiesSP *g_settings_sp_ptr = + new TargetPropertiesSP(new TargetProperties(nullptr)); + return *g_settings_sp_ptr; +} + +Status Target::Install(ProcessLaunchInfo *launch_info) { + Status error; + PlatformSP platform_sp(GetPlatform()); + if (platform_sp) { + if (platform_sp->IsRemote()) { + if (platform_sp->IsConnected()) { + // Install all files that have an install path, and always install the + // main executable when connected to a remote platform + const ModuleList &modules = GetImages(); + const size_t num_images = modules.GetSize(); + for (size_t idx = 0; idx < num_images; ++idx) { + ModuleSP module_sp(modules.GetModuleAtIndex(idx)); + if (module_sp) { + const bool is_main_executable = module_sp == GetExecutableModule(); + FileSpec local_file(module_sp->GetFileSpec()); + if (local_file) { + FileSpec remote_file(module_sp->GetRemoteInstallFileSpec()); + if (!remote_file) { + if (is_main_executable) // TODO: add setting for always + // installing main executable??? + { + // Always install the main executable + remote_file = platform_sp->GetRemoteWorkingDirectory(); + remote_file.AppendPathComponent( + module_sp->GetFileSpec().GetFilename().GetCString()); + } + } + if (remote_file) { + error = platform_sp->Install(local_file, remote_file); + if (error.Success()) { + module_sp->SetPlatformFileSpec(remote_file); + if (is_main_executable) { + platform_sp->SetFilePermissions(remote_file, 0700); + if (launch_info) + launch_info->SetExecutableFile(remote_file, false); + } + } else + break; + } + } + } + } + } + } + } + return error; +} + +bool Target::ResolveLoadAddress(addr_t load_addr, Address &so_addr, + uint32_t stop_id) { + return m_section_load_history.ResolveLoadAddress(stop_id, load_addr, so_addr); +} + +bool Target::ResolveFileAddress(lldb::addr_t file_addr, + Address &resolved_addr) { + return m_images.ResolveFileAddress(file_addr, resolved_addr); +} + +bool Target::SetSectionLoadAddress(const SectionSP §ion_sp, + addr_t new_section_load_addr, + bool warn_multiple) { + const addr_t old_section_load_addr = + m_section_load_history.GetSectionLoadAddress( + SectionLoadHistory::eStopIDNow, section_sp); + if (old_section_load_addr != new_section_load_addr) { + uint32_t stop_id = 0; + ProcessSP process_sp(GetProcessSP()); + if (process_sp) + stop_id = process_sp->GetStopID(); + else + stop_id = m_section_load_history.GetLastStopID(); + if (m_section_load_history.SetSectionLoadAddress( + stop_id, section_sp, new_section_load_addr, warn_multiple)) + return true; // Return true if the section load address was changed... + } + return false; // Return false to indicate nothing changed +} + +size_t Target::UnloadModuleSections(const ModuleList &module_list) { + size_t section_unload_count = 0; + size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; ++i) { + section_unload_count += + UnloadModuleSections(module_list.GetModuleAtIndex(i)); + } + return section_unload_count; +} + +size_t Target::UnloadModuleSections(const lldb::ModuleSP &module_sp) { + uint32_t stop_id = 0; + ProcessSP process_sp(GetProcessSP()); + if (process_sp) + stop_id = process_sp->GetStopID(); + else + stop_id = m_section_load_history.GetLastStopID(); + SectionList *sections = module_sp->GetSectionList(); + size_t section_unload_count = 0; + if (sections) { + const uint32_t num_sections = sections->GetNumSections(0); + for (uint32_t i = 0; i < num_sections; ++i) { + section_unload_count += m_section_load_history.SetSectionUnloaded( + stop_id, sections->GetSectionAtIndex(i)); + } + } + return section_unload_count; +} + +bool Target::SetSectionUnloaded(const lldb::SectionSP §ion_sp) { + uint32_t stop_id = 0; + ProcessSP process_sp(GetProcessSP()); + if (process_sp) + stop_id = process_sp->GetStopID(); + else + stop_id = m_section_load_history.GetLastStopID(); + return m_section_load_history.SetSectionUnloaded(stop_id, section_sp); +} + +bool Target::SetSectionUnloaded(const lldb::SectionSP §ion_sp, + addr_t load_addr) { + uint32_t stop_id = 0; + ProcessSP process_sp(GetProcessSP()); + if (process_sp) + stop_id = process_sp->GetStopID(); + else + stop_id = m_section_load_history.GetLastStopID(); + return m_section_load_history.SetSectionUnloaded(stop_id, section_sp, + load_addr); +} + +void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); } + +Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { + Status error; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); + + LLDB_LOGF(log, "Target::%s() called for %s", __FUNCTION__, + launch_info.GetExecutableFile().GetPath().c_str()); + + StateType state = eStateInvalid; + + // Scope to temporarily get the process state in case someone has manually + // remotely connected already to a process and we can skip the platform + // launching. + { + ProcessSP process_sp(GetProcessSP()); + + if (process_sp) { + state = process_sp->GetState(); + LLDB_LOGF(log, + "Target::%s the process exists, and its current state is %s", + __FUNCTION__, StateAsCString(state)); + } else { + LLDB_LOGF(log, "Target::%s the process instance doesn't currently exist.", + __FUNCTION__); + } + } + + launch_info.GetFlags().Set(eLaunchFlagDebug); + + // Get the value of synchronous execution here. If you wait till after you + // have started to run, then you could have hit a breakpoint, whose command + // might switch the value, and then you'll pick up that incorrect value. + Debugger &debugger = GetDebugger(); + const bool synchronous_execution = + debugger.GetCommandInterpreter().GetSynchronous(); + + PlatformSP platform_sp(GetPlatform()); + + FinalizeFileActions(launch_info); + + if (state == eStateConnected) { + if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { + error.SetErrorString( + "can't launch in tty when launching through a remote connection"); + return error; + } + } + + if (!launch_info.GetArchitecture().IsValid()) + launch_info.GetArchitecture() = GetArchitecture(); + + // If we're not already connected to the process, and if we have a platform + // that can launch a process for debugging, go ahead and do that here. + if (state != eStateConnected && platform_sp && + platform_sp->CanDebugProcess()) { + LLDB_LOGF(log, "Target::%s asking the platform to debug the process", + __FUNCTION__); + + // If there was a previous process, delete it before we make the new one. + // One subtle point, we delete the process before we release the reference + // to m_process_sp. That way even if we are the last owner, the process + // will get Finalized before it gets destroyed. + DeleteCurrentProcess(); + + m_process_sp = + GetPlatform()->DebugProcess(launch_info, debugger, this, error); + + } else { + LLDB_LOGF(log, + "Target::%s the platform doesn't know how to debug a " + "process, getting a process plugin to do this for us.", + __FUNCTION__); + + if (state == eStateConnected) { + assert(m_process_sp); + } else { + // Use a Process plugin to construct the process. + const char *plugin_name = launch_info.GetProcessPluginName(); + CreateProcess(launch_info.GetListener(), plugin_name, nullptr); + } + + // Since we didn't have a platform launch the process, launch it here. + if (m_process_sp) + error = m_process_sp->Launch(launch_info); + } + + if (!m_process_sp) { + if (error.Success()) + error.SetErrorString("failed to launch or debug process"); + return error; + } + + if (error.Success()) { + if (synchronous_execution || + !launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) { + ListenerSP hijack_listener_sp(launch_info.GetHijackListener()); + if (!hijack_listener_sp) { + hijack_listener_sp = + Listener::MakeListener("lldb.Target.Launch.hijack"); + launch_info.SetHijackListener(hijack_listener_sp); + m_process_sp->HijackProcessEvents(hijack_listener_sp); + } + + StateType state = m_process_sp->WaitForProcessToStop( + llvm::None, nullptr, false, hijack_listener_sp, nullptr); + + if (state == eStateStopped) { + if (!launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) { + if (synchronous_execution) { + // Now we have handled the stop-from-attach, and we are just + // switching to a synchronous resume. So we should switch to the + // SyncResume hijacker. + m_process_sp->RestoreProcessEvents(); + m_process_sp->ResumeSynchronous(stream); + } else { + m_process_sp->RestoreProcessEvents(); + error = m_process_sp->PrivateResume(); + } + if (!error.Success()) { + Status error2; + error2.SetErrorStringWithFormat( + "process resume at entry point failed: %s", error.AsCString()); + error = error2; + } + } + } else if (state == eStateExited) { + bool with_shell = !!launch_info.GetShell(); + const int exit_status = m_process_sp->GetExitStatus(); + const char *exit_desc = m_process_sp->GetExitDescription(); +#define LAUNCH_SHELL_MESSAGE \ + "\n'r' and 'run' are aliases that default to launching through a " \ + "shell.\nTry launching without going through a shell by using 'process " \ + "launch'." + if (exit_desc && exit_desc[0]) { + if (with_shell) + error.SetErrorStringWithFormat( + "process exited with status %i (%s)" LAUNCH_SHELL_MESSAGE, + exit_status, exit_desc); + else + error.SetErrorStringWithFormat("process exited with status %i (%s)", + exit_status, exit_desc); + } else { + if (with_shell) + error.SetErrorStringWithFormat( + "process exited with status %i" LAUNCH_SHELL_MESSAGE, + exit_status); + else + error.SetErrorStringWithFormat("process exited with status %i", + exit_status); + } + } else { + error.SetErrorStringWithFormat( + "initial process state wasn't stopped: %s", StateAsCString(state)); + } + } + m_process_sp->RestoreProcessEvents(); + } else { + Status error2; + error2.SetErrorStringWithFormat("process launch failed: %s", + error.AsCString()); + error = error2; + } + return error; +} + +Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { + auto state = eStateInvalid; + auto process_sp = GetProcessSP(); + if (process_sp) { + state = process_sp->GetState(); + if (process_sp->IsAlive() && state != eStateConnected) { + if (state == eStateAttaching) + return Status("process attach is in progress"); + return Status("a process is already being debugged"); + } + } + + const ModuleSP old_exec_module_sp = GetExecutableModule(); + + // If no process info was specified, then use the target executable name as + // the process to attach to by default + if (!attach_info.ProcessInfoSpecified()) { + if (old_exec_module_sp) + attach_info.GetExecutableFile().GetFilename() = + old_exec_module_sp->GetPlatformFileSpec().GetFilename(); + + if (!attach_info.ProcessInfoSpecified()) { + return Status("no process specified, create a target with a file, or " + "specify the --pid or --name"); + } + } + + const auto platform_sp = + GetDebugger().GetPlatformList().GetSelectedPlatform(); + ListenerSP hijack_listener_sp; + const bool async = attach_info.GetAsync(); + if (!async) { + hijack_listener_sp = + Listener::MakeListener("lldb.Target.Attach.attach.hijack"); + attach_info.SetHijackListener(hijack_listener_sp); + } + + Status error; + if (state != eStateConnected && platform_sp != nullptr && + platform_sp->CanDebugProcess()) { + SetPlatform(platform_sp); + process_sp = platform_sp->Attach(attach_info, GetDebugger(), this, error); + } else { + if (state != eStateConnected) { + const char *plugin_name = attach_info.GetProcessPluginName(); + process_sp = + CreateProcess(attach_info.GetListenerForProcess(GetDebugger()), + plugin_name, nullptr); + if (process_sp == nullptr) { + error.SetErrorStringWithFormat( + "failed to create process using plugin %s", + (plugin_name) ? plugin_name : "null"); + return error; + } + } + if (hijack_listener_sp) + process_sp->HijackProcessEvents(hijack_listener_sp); + error = process_sp->Attach(attach_info); + } + + if (error.Success() && process_sp) { + if (async) { + process_sp->RestoreProcessEvents(); + } else { + state = process_sp->WaitForProcessToStop( + llvm::None, nullptr, false, attach_info.GetHijackListener(), stream); + process_sp->RestoreProcessEvents(); + + if (state != eStateStopped) { + const char *exit_desc = process_sp->GetExitDescription(); + if (exit_desc) + error.SetErrorStringWithFormat("%s", exit_desc); + else + error.SetErrorString( + "process did not stop (no such process or permission problem?)"); + process_sp->Destroy(false); + } + } + } + return error; +} + +void Target::FinalizeFileActions(ProcessLaunchInfo &info) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Finalize the file actions, and if none were given, default to opening up a + // pseudo terminal + PlatformSP platform_sp = GetPlatform(); + const bool default_to_use_pty = + m_platform_sp ? m_platform_sp->IsHost() : false; + LLDB_LOG( + log, + "have platform={0}, platform_sp->IsHost()={1}, default_to_use_pty={2}", + bool(platform_sp), + platform_sp ? (platform_sp->IsHost() ? "true" : "false") : "n/a", + default_to_use_pty); + + // If nothing for stdin or stdout or stderr was specified, then check the + // process for any default settings that were set with "settings set" + if (info.GetFileActionForFD(STDIN_FILENO) == nullptr || + info.GetFileActionForFD(STDOUT_FILENO) == nullptr || + info.GetFileActionForFD(STDERR_FILENO) == nullptr) { + LLDB_LOG(log, "at least one of stdin/stdout/stderr was not set, evaluating " + "default handling"); + + if (info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { + // Do nothing, if we are launching in a remote terminal no file actions + // should be done at all. + return; + } + + if (info.GetFlags().Test(eLaunchFlagDisableSTDIO)) { + LLDB_LOG(log, "eLaunchFlagDisableSTDIO set, adding suppression action " + "for stdin, stdout and stderr"); + info.AppendSuppressFileAction(STDIN_FILENO, true, false); + info.AppendSuppressFileAction(STDOUT_FILENO, false, true); + info.AppendSuppressFileAction(STDERR_FILENO, false, true); + } else { + // Check for any values that might have gotten set with any of: (lldb) + // settings set target.input-path (lldb) settings set target.output-path + // (lldb) settings set target.error-path + FileSpec in_file_spec; + FileSpec out_file_spec; + FileSpec err_file_spec; + // Only override with the target settings if we don't already have an + // action for in, out or error + if (info.GetFileActionForFD(STDIN_FILENO) == nullptr) + in_file_spec = GetStandardInputPath(); + if (info.GetFileActionForFD(STDOUT_FILENO) == nullptr) + out_file_spec = GetStandardOutputPath(); + if (info.GetFileActionForFD(STDERR_FILENO) == nullptr) + err_file_spec = GetStandardErrorPath(); + + LLDB_LOG(log, "target stdin='{0}', target stdout='{1}', stderr='{1}'", + in_file_spec, out_file_spec, err_file_spec); + + if (in_file_spec) { + info.AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false); + LLDB_LOG(log, "appended stdin open file action for {0}", in_file_spec); + } + + if (out_file_spec) { + info.AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true); + LLDB_LOG(log, "appended stdout open file action for {0}", + out_file_spec); + } + + if (err_file_spec) { + info.AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true); + LLDB_LOG(log, "appended stderr open file action for {0}", + err_file_spec); + } + + if (default_to_use_pty && + (!in_file_spec || !out_file_spec || !err_file_spec)) { + llvm::Error Err = info.SetUpPtyRedirection(); + LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}"); + } + } + } +} + +// Target::StopHook +Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid) + : UserID(uid), m_target_sp(target_sp), m_commands(), m_specifier_sp(), + m_thread_spec_up() {} + +Target::StopHook::StopHook(const StopHook &rhs) + : UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp), + m_commands(rhs.m_commands), m_specifier_sp(rhs.m_specifier_sp), + m_thread_spec_up(), m_active(rhs.m_active), + m_auto_continue(rhs.m_auto_continue) { + if (rhs.m_thread_spec_up) + m_thread_spec_up.reset(new ThreadSpec(*rhs.m_thread_spec_up)); +} + +Target::StopHook::~StopHook() = default; + +void Target::StopHook::SetSpecifier(SymbolContextSpecifier *specifier) { + m_specifier_sp.reset(specifier); +} + +void Target::StopHook::SetThreadSpecifier(ThreadSpec *specifier) { + m_thread_spec_up.reset(specifier); +} + +void Target::StopHook::GetDescription(Stream *s, + lldb::DescriptionLevel level) const { + unsigned indent_level = s->GetIndentLevel(); + + s->SetIndentLevel(indent_level + 2); + + s->Printf("Hook: %" PRIu64 "\n", GetID()); + if (m_active) + s->Indent("State: enabled\n"); + else + s->Indent("State: disabled\n"); + + if (m_auto_continue) + s->Indent("AutoContinue on\n"); + + if (m_specifier_sp) { + s->Indent(); + s->PutCString("Specifier:\n"); + s->SetIndentLevel(indent_level + 4); + m_specifier_sp->GetDescription(s, level); + s->SetIndentLevel(indent_level + 2); + } + + if (m_thread_spec_up) { + StreamString tmp; + s->Indent("Thread:\n"); + m_thread_spec_up->GetDescription(&tmp, level); + s->SetIndentLevel(indent_level + 4); + s->Indent(tmp.GetString()); + s->PutCString("\n"); + s->SetIndentLevel(indent_level + 2); + } + + s->Indent("Commands: \n"); + s->SetIndentLevel(indent_level + 4); + uint32_t num_commands = m_commands.GetSize(); + for (uint32_t i = 0; i < num_commands; i++) { + s->Indent(m_commands.GetStringAtIndex(i)); + s->PutCString("\n"); + } + s->SetIndentLevel(indent_level); +} + +static constexpr OptionEnumValueElement g_dynamic_value_types[] = { + { + eNoDynamicValues, + "no-dynamic-values", + "Don't calculate the dynamic type of values", + }, + { + eDynamicCanRunTarget, + "run-target", + "Calculate the dynamic type of values " + "even if you have to run the target.", + }, + { + eDynamicDontRunTarget, + "no-run-target", + "Calculate the dynamic type of values, but don't run the target.", + }, +}; + +OptionEnumValues lldb_private::GetDynamicValueTypes() { + return OptionEnumValues(g_dynamic_value_types); +} + +static constexpr OptionEnumValueElement g_inline_breakpoint_enums[] = { + { + eInlineBreakpointsNever, + "never", + "Never look for inline breakpoint locations (fastest). This setting " + "should only be used if you know that no inlining occurs in your" + "programs.", + }, + { + eInlineBreakpointsHeaders, + "headers", + "Only check for inline breakpoint locations when setting breakpoints " + "in header files, but not when setting breakpoint in implementation " + "source files (default).", + }, + { + eInlineBreakpointsAlways, + "always", + "Always look for inline breakpoint locations when setting file and " + "line breakpoints (slower but most accurate).", + }, +}; + +enum x86DisassemblyFlavor { + eX86DisFlavorDefault, + eX86DisFlavorIntel, + eX86DisFlavorATT +}; + +static constexpr OptionEnumValueElement g_x86_dis_flavor_value_types[] = { + { + eX86DisFlavorDefault, + "default", + "Disassembler default (currently att).", + }, + { + eX86DisFlavorIntel, + "intel", + "Intel disassembler flavor.", + }, + { + eX86DisFlavorATT, + "att", + "AT&T disassembler flavor.", + }, +}; + +static constexpr OptionEnumValueElement g_hex_immediate_style_values[] = { + { + Disassembler::eHexStyleC, + "c", + "C-style (0xffff).", + }, + { + Disassembler::eHexStyleAsm, + "asm", + "Asm-style (0ffffh).", + }, +}; + +static constexpr OptionEnumValueElement g_load_script_from_sym_file_values[] = { + { + eLoadScriptFromSymFileTrue, + "true", + "Load debug scripts inside symbol files", + }, + { + eLoadScriptFromSymFileFalse, + "false", + "Do not load debug scripts inside symbol files.", + }, + { + eLoadScriptFromSymFileWarn, + "warn", + "Warn about debug scripts inside symbol files but do not load them.", + }, +}; + +static constexpr OptionEnumValueElement g_load_cwd_lldbinit_values[] = { + { + eLoadCWDlldbinitTrue, + "true", + "Load .lldbinit files from current directory", + }, + { + eLoadCWDlldbinitFalse, + "false", + "Do not load .lldbinit files from current directory", + }, + { + eLoadCWDlldbinitWarn, + "warn", + "Warn about loading .lldbinit files from current directory", + }, +}; + +static constexpr OptionEnumValueElement g_memory_module_load_level_values[] = { + { + eMemoryModuleLoadLevelMinimal, + "minimal", + "Load minimal information when loading modules from memory. Currently " + "this setting loads sections only.", + }, + { + eMemoryModuleLoadLevelPartial, + "partial", + "Load partial information when loading modules from memory. Currently " + "this setting loads sections and function bounds.", + }, + { + eMemoryModuleLoadLevelComplete, + "complete", + "Load complete information when loading modules from memory. Currently " + "this setting loads sections and all symbols.", + }, +}; + +#define LLDB_PROPERTIES_target +#include "TargetProperties.inc" + +enum { +#define LLDB_PROPERTIES_target +#include "TargetPropertiesEnum.inc" + ePropertyExperimental, +}; + +class TargetOptionValueProperties : public OptionValueProperties { +public: + TargetOptionValueProperties(ConstString name) + : OptionValueProperties(name), m_target(nullptr), m_got_host_env(false) {} + + // This constructor is used when creating TargetOptionValueProperties when it + // is part of a new lldb_private::Target instance. It will copy all current + // global property values as needed + TargetOptionValueProperties(Target *target, + const TargetPropertiesSP &target_properties_sp) + : OptionValueProperties(*target_properties_sp->GetValueProperties()), + m_target(target), m_got_host_env(false) {} + + const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const override { + // When getting the value for a key from the target options, we will always + // try and grab the setting from the current target if there is one. Else + // we just use the one from this instance. + if (idx == ePropertyEnvVars) + GetHostEnvironmentIfNeeded(); + + if (exe_ctx) { + Target *target = exe_ctx->GetTargetPtr(); + if (target) { + TargetOptionValueProperties *target_properties = + static_cast<TargetOptionValueProperties *>( + target->GetValueProperties().get()); + if (this != target_properties) + return target_properties->ProtectedGetPropertyAtIndex(idx); + } + } + return ProtectedGetPropertyAtIndex(idx); + } + + lldb::TargetSP GetTargetSP() { return m_target->shared_from_this(); } + +protected: + void GetHostEnvironmentIfNeeded() const { + if (!m_got_host_env) { + if (m_target) { + m_got_host_env = true; + const uint32_t idx = ePropertyInheritEnv; + if (GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0)) { + PlatformSP platform_sp(m_target->GetPlatform()); + if (platform_sp) { + Environment env = platform_sp->GetEnvironment(); + OptionValueDictionary *env_dict = + GetPropertyAtIndexAsOptionValueDictionary(nullptr, + ePropertyEnvVars); + if (env_dict) { + const bool can_replace = false; + for (const auto &KV : env) { + // Don't allow existing keys to be replaced with ones we get + // from the platform environment + env_dict->SetValueForKey( + ConstString(KV.first()), + OptionValueSP(new OptionValueString(KV.second.c_str())), + can_replace); + } + } + } + } + } + } + } + Target *m_target; + mutable bool m_got_host_env; +}; + +// TargetProperties +#define LLDB_PROPERTIES_experimental +#include "TargetProperties.inc" + +enum { +#define LLDB_PROPERTIES_experimental +#include "TargetPropertiesEnum.inc" +}; + +class TargetExperimentalOptionValueProperties : public OptionValueProperties { +public: + TargetExperimentalOptionValueProperties() + : OptionValueProperties( + ConstString(Properties::GetExperimentalSettingsName())) {} +}; + +TargetExperimentalProperties::TargetExperimentalProperties() + : Properties(OptionValuePropertiesSP( + new TargetExperimentalOptionValueProperties())) { + m_collection_sp->Initialize(g_experimental_properties); +} + +// TargetProperties +TargetProperties::TargetProperties(Target *target) + : Properties(), m_launch_info() { + if (target) { + m_collection_sp = std::make_shared<TargetOptionValueProperties>( + target, Target::GetGlobalProperties()); + + // Set callbacks to update launch_info whenever "settins set" updated any + // of these properties + m_collection_sp->SetValueChangedCallback( + ePropertyArg0, [this] { Arg0ValueChangedCallback(); }); + m_collection_sp->SetValueChangedCallback( + ePropertyRunArgs, [this] { RunArgsValueChangedCallback(); }); + m_collection_sp->SetValueChangedCallback( + ePropertyEnvVars, [this] { EnvVarsValueChangedCallback(); }); + m_collection_sp->SetValueChangedCallback( + ePropertyInputPath, [this] { InputPathValueChangedCallback(); }); + m_collection_sp->SetValueChangedCallback( + ePropertyOutputPath, [this] { OutputPathValueChangedCallback(); }); + m_collection_sp->SetValueChangedCallback( + ePropertyErrorPath, [this] { ErrorPathValueChangedCallback(); }); + m_collection_sp->SetValueChangedCallback(ePropertyDetachOnError, [this] { + DetachOnErrorValueChangedCallback(); + }); + m_collection_sp->SetValueChangedCallback( + ePropertyDisableASLR, [this] { DisableASLRValueChangedCallback(); }); + m_collection_sp->SetValueChangedCallback( + ePropertyDisableSTDIO, [this] { DisableSTDIOValueChangedCallback(); }); + + m_experimental_properties_up.reset(new TargetExperimentalProperties()); + m_collection_sp->AppendProperty( + ConstString(Properties::GetExperimentalSettingsName()), + ConstString("Experimental settings - setting these won't produce " + "errors if the setting is not present."), + true, m_experimental_properties_up->GetValueProperties()); + + // Update m_launch_info once it was created + Arg0ValueChangedCallback(); + RunArgsValueChangedCallback(); + // EnvVarsValueChangedCallback(); // FIXME: cause segfault in + // Target::GetPlatform() + InputPathValueChangedCallback(); + OutputPathValueChangedCallback(); + ErrorPathValueChangedCallback(); + DetachOnErrorValueChangedCallback(); + DisableASLRValueChangedCallback(); + DisableSTDIOValueChangedCallback(); + } else { + m_collection_sp = + std::make_shared<TargetOptionValueProperties>(ConstString("target")); + m_collection_sp->Initialize(g_target_properties); + m_experimental_properties_up.reset(new TargetExperimentalProperties()); + m_collection_sp->AppendProperty( + ConstString(Properties::GetExperimentalSettingsName()), + ConstString("Experimental settings - setting these won't produce " + "errors if the setting is not present."), + true, m_experimental_properties_up->GetValueProperties()); + m_collection_sp->AppendProperty( + ConstString("process"), ConstString("Settings specific to processes."), + true, Process::GetGlobalProperties()->GetValueProperties()); + } +} + +TargetProperties::~TargetProperties() = default; + +bool TargetProperties::GetInjectLocalVariables( + ExecutionContext *exe_ctx) const { + const Property *exp_property = m_collection_sp->GetPropertyAtIndex( + exe_ctx, false, ePropertyExperimental); + OptionValueProperties *exp_values = + exp_property->GetValue()->GetAsProperties(); + if (exp_values) + return exp_values->GetPropertyAtIndexAsBoolean( + exe_ctx, ePropertyInjectLocalVars, true); + else + return true; +} + +void TargetProperties::SetInjectLocalVariables(ExecutionContext *exe_ctx, + bool b) { + const Property *exp_property = + m_collection_sp->GetPropertyAtIndex(exe_ctx, true, ePropertyExperimental); + OptionValueProperties *exp_values = + exp_property->GetValue()->GetAsProperties(); + if (exp_values) + exp_values->SetPropertyAtIndexAsBoolean(exe_ctx, ePropertyInjectLocalVars, + true); +} + +ArchSpec TargetProperties::GetDefaultArchitecture() const { + OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch( + nullptr, ePropertyDefaultArch); + if (value) + return value->GetCurrentValue(); + return ArchSpec(); +} + +void TargetProperties::SetDefaultArchitecture(const ArchSpec &arch) { + OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch( + nullptr, ePropertyDefaultArch); + if (value) + return value->SetCurrentValue(arch, true); +} + +bool TargetProperties::GetMoveToNearestCode() const { + const uint32_t idx = ePropertyMoveToNearestCode; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +lldb::DynamicValueType TargetProperties::GetPreferDynamicValue() const { + const uint32_t idx = ePropertyPreferDynamic; + return (lldb::DynamicValueType) + m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +bool TargetProperties::SetPreferDynamicValue(lldb::DynamicValueType d) { + const uint32_t idx = ePropertyPreferDynamic; + return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx, d); +} + +bool TargetProperties::GetPreloadSymbols() const { + const uint32_t idx = ePropertyPreloadSymbols; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +void TargetProperties::SetPreloadSymbols(bool b) { + const uint32_t idx = ePropertyPreloadSymbols; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + +bool TargetProperties::GetDisableASLR() const { + const uint32_t idx = ePropertyDisableASLR; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +void TargetProperties::SetDisableASLR(bool b) { + const uint32_t idx = ePropertyDisableASLR; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + +bool TargetProperties::GetDetachOnError() const { + const uint32_t idx = ePropertyDetachOnError; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +void TargetProperties::SetDetachOnError(bool b) { + const uint32_t idx = ePropertyDetachOnError; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + +bool TargetProperties::GetDisableSTDIO() const { + const uint32_t idx = ePropertyDisableSTDIO; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +void TargetProperties::SetDisableSTDIO(bool b) { + const uint32_t idx = ePropertyDisableSTDIO; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + +const char *TargetProperties::GetDisassemblyFlavor() const { + const uint32_t idx = ePropertyDisassemblyFlavor; + const char *return_value; + + x86DisassemblyFlavor flavor_value = + (x86DisassemblyFlavor)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + return_value = g_x86_dis_flavor_value_types[flavor_value].string_value; + return return_value; +} + +InlineStrategy TargetProperties::GetInlineStrategy() const { + const uint32_t idx = ePropertyInlineStrategy; + return (InlineStrategy)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +llvm::StringRef TargetProperties::GetArg0() const { + const uint32_t idx = ePropertyArg0; + return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, + llvm::StringRef()); +} + +void TargetProperties::SetArg0(llvm::StringRef arg) { + const uint32_t idx = ePropertyArg0; + m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, arg); + m_launch_info.SetArg0(arg); +} + +bool TargetProperties::GetRunArguments(Args &args) const { + const uint32_t idx = ePropertyRunArgs; + return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); +} + +void TargetProperties::SetRunArguments(const Args &args) { + const uint32_t idx = ePropertyRunArgs; + m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); + m_launch_info.GetArguments() = args; +} + +Environment TargetProperties::GetEnvironment() const { + // TODO: Get rid of the Args intermediate step + Args env; + const uint32_t idx = ePropertyEnvVars; + m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, env); + return Environment(env); +} + +void TargetProperties::SetEnvironment(Environment env) { + // TODO: Get rid of the Args intermediate step + const uint32_t idx = ePropertyEnvVars; + m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, Args(env)); + m_launch_info.GetEnvironment() = std::move(env); +} + +bool TargetProperties::GetSkipPrologue() const { + const uint32_t idx = ePropertySkipPrologue; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +PathMappingList &TargetProperties::GetSourcePathMap() const { + const uint32_t idx = ePropertySourceMap; + OptionValuePathMappings *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValuePathMappings(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); +} + +void TargetProperties::AppendExecutableSearchPaths(const FileSpec &dir) { + const uint32_t idx = ePropertyExecutableSearchPaths; + OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + option_value->AppendCurrentValue(dir); +} + +FileSpecList TargetProperties::GetExecutableSearchPaths() { + const uint32_t idx = ePropertyExecutableSearchPaths; + const OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); +} + +FileSpecList TargetProperties::GetDebugFileSearchPaths() { + const uint32_t idx = ePropertyDebugFileSearchPaths; + const OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); +} + +FileSpecList TargetProperties::GetClangModuleSearchPaths() { + const uint32_t idx = ePropertyClangModuleSearchPaths; + const OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); +} + +bool TargetProperties::GetEnableAutoImportClangModules() const { + const uint32_t idx = ePropertyAutoImportClangModules; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +bool TargetProperties::GetEnableImportStdModule() const { + const uint32_t idx = ePropertyImportStdModule; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +bool TargetProperties::GetEnableAutoApplyFixIts() const { + const uint32_t idx = ePropertyAutoApplyFixIts; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +bool TargetProperties::GetEnableNotifyAboutFixIts() const { + const uint32_t idx = ePropertyNotifyAboutFixIts; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +bool TargetProperties::GetEnableSaveObjects() const { + const uint32_t idx = ePropertySaveObjects; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +bool TargetProperties::GetEnableSyntheticValue() const { + const uint32_t idx = ePropertyEnableSynthetic; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +uint32_t TargetProperties::GetMaxZeroPaddingInFloatFormat() const { + const uint32_t idx = ePropertyMaxZeroPaddingInFloatFormat; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +uint32_t TargetProperties::GetMaximumNumberOfChildrenToDisplay() const { + const uint32_t idx = ePropertyMaxChildrenCount; + return m_collection_sp->GetPropertyAtIndexAsSInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +uint32_t TargetProperties::GetMaximumSizeOfStringSummary() const { + const uint32_t idx = ePropertyMaxSummaryLength; + return m_collection_sp->GetPropertyAtIndexAsSInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +uint32_t TargetProperties::GetMaximumMemReadSize() const { + const uint32_t idx = ePropertyMaxMemReadSize; + return m_collection_sp->GetPropertyAtIndexAsSInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +FileSpec TargetProperties::GetStandardInputPath() const { + const uint32_t idx = ePropertyInputPath; + return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); +} + +void TargetProperties::SetStandardInputPath(llvm::StringRef path) { + const uint32_t idx = ePropertyInputPath; + m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); +} + +FileSpec TargetProperties::GetStandardOutputPath() const { + const uint32_t idx = ePropertyOutputPath; + return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); +} + +void TargetProperties::SetStandardOutputPath(llvm::StringRef path) { + const uint32_t idx = ePropertyOutputPath; + m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); +} + +FileSpec TargetProperties::GetStandardErrorPath() const { + const uint32_t idx = ePropertyErrorPath; + return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); +} + +void TargetProperties::SetStandardErrorPath(llvm::StringRef path) { + const uint32_t idx = ePropertyErrorPath; + m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); +} + +LanguageType TargetProperties::GetLanguage() const { + OptionValueLanguage *value = + m_collection_sp->GetPropertyAtIndexAsOptionValueLanguage( + nullptr, ePropertyLanguage); + if (value) + return value->GetCurrentValue(); + return LanguageType(); +} + +llvm::StringRef TargetProperties::GetExpressionPrefixContents() { + const uint32_t idx = ePropertyExprPrefix; + OptionValueFileSpec *file = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpec(nullptr, false, + idx); + if (file) { + DataBufferSP data_sp(file->GetFileContents()); + if (data_sp) + return llvm::StringRef( + reinterpret_cast<const char *>(data_sp->GetBytes()), + data_sp->GetByteSize()); + } + return ""; +} + +bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() { + const uint32_t idx = ePropertyBreakpointUseAvoidList; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +bool TargetProperties::GetUseHexImmediates() const { + const uint32_t idx = ePropertyUseHexImmediates; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +bool TargetProperties::GetUseFastStepping() const { + const uint32_t idx = ePropertyUseFastStepping; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +bool TargetProperties::GetDisplayExpressionsInCrashlogs() const { + const uint32_t idx = ePropertyDisplayExpressionsInCrashlogs; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +LoadScriptFromSymFile TargetProperties::GetLoadScriptFromSymbolFile() const { + const uint32_t idx = ePropertyLoadScriptFromSymbolFile; + return (LoadScriptFromSymFile) + m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +LoadCWDlldbinitFile TargetProperties::GetLoadCWDlldbinitFile() const { + const uint32_t idx = ePropertyLoadCWDlldbinitFile; + return (LoadCWDlldbinitFile)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +Disassembler::HexImmediateStyle TargetProperties::GetHexImmediateStyle() const { + const uint32_t idx = ePropertyHexImmediateStyle; + return (Disassembler::HexImmediateStyle) + m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +MemoryModuleLoadLevel TargetProperties::GetMemoryModuleLoadLevel() const { + const uint32_t idx = ePropertyMemoryModuleLoadLevel; + return (MemoryModuleLoadLevel) + m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + +bool TargetProperties::GetUserSpecifiedTrapHandlerNames(Args &args) const { + const uint32_t idx = ePropertyTrapHandlerNames; + return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); +} + +void TargetProperties::SetUserSpecifiedTrapHandlerNames(const Args &args) { + const uint32_t idx = ePropertyTrapHandlerNames; + m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); +} + +bool TargetProperties::GetDisplayRuntimeSupportValues() const { + const uint32_t idx = ePropertyDisplayRuntimeSupportValues; + return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); +} + +void TargetProperties::SetDisplayRuntimeSupportValues(bool b) { + const uint32_t idx = ePropertyDisplayRuntimeSupportValues; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + +bool TargetProperties::GetDisplayRecognizedArguments() const { + const uint32_t idx = ePropertyDisplayRecognizedArguments; + return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); +} + +void TargetProperties::SetDisplayRecognizedArguments(bool b) { + const uint32_t idx = ePropertyDisplayRecognizedArguments; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + +bool TargetProperties::GetNonStopModeEnabled() const { + const uint32_t idx = ePropertyNonStopModeEnabled; + return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); +} + +void TargetProperties::SetNonStopModeEnabled(bool b) { + const uint32_t idx = ePropertyNonStopModeEnabled; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + +const ProcessLaunchInfo &TargetProperties::GetProcessLaunchInfo() { + m_launch_info.SetArg0(GetArg0()); // FIXME: Arg0 callback doesn't work + return m_launch_info; +} + +void TargetProperties::SetProcessLaunchInfo( + const ProcessLaunchInfo &launch_info) { + m_launch_info = launch_info; + SetArg0(launch_info.GetArg0()); + SetRunArguments(launch_info.GetArguments()); + SetEnvironment(launch_info.GetEnvironment()); + const FileAction *input_file_action = + launch_info.GetFileActionForFD(STDIN_FILENO); + if (input_file_action) { + SetStandardInputPath(input_file_action->GetPath()); + } + const FileAction *output_file_action = + launch_info.GetFileActionForFD(STDOUT_FILENO); + if (output_file_action) { + SetStandardOutputPath(output_file_action->GetPath()); + } + const FileAction *error_file_action = + launch_info.GetFileActionForFD(STDERR_FILENO); + if (error_file_action) { + SetStandardErrorPath(error_file_action->GetPath()); + } + SetDetachOnError(launch_info.GetFlags().Test(lldb::eLaunchFlagDetachOnError)); + SetDisableASLR(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)); + SetDisableSTDIO(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableSTDIO)); +} + +bool TargetProperties::GetRequireHardwareBreakpoints() const { + const uint32_t idx = ePropertyRequireHardwareBreakpoints; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +void TargetProperties::SetRequireHardwareBreakpoints(bool b) { + const uint32_t idx = ePropertyRequireHardwareBreakpoints; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + +void TargetProperties::Arg0ValueChangedCallback() { + m_launch_info.SetArg0(GetArg0()); +} + +void TargetProperties::RunArgsValueChangedCallback() { + Args args; + if (GetRunArguments(args)) + m_launch_info.GetArguments() = args; +} + +void TargetProperties::EnvVarsValueChangedCallback() { + m_launch_info.GetEnvironment() = GetEnvironment(); +} + +void TargetProperties::InputPathValueChangedCallback() { + m_launch_info.AppendOpenFileAction(STDIN_FILENO, GetStandardInputPath(), true, + false); +} + +void TargetProperties::OutputPathValueChangedCallback() { + m_launch_info.AppendOpenFileAction(STDOUT_FILENO, GetStandardOutputPath(), + false, true); +} + +void TargetProperties::ErrorPathValueChangedCallback() { + m_launch_info.AppendOpenFileAction(STDERR_FILENO, GetStandardErrorPath(), + false, true); +} + +void TargetProperties::DetachOnErrorValueChangedCallback() { + if (GetDetachOnError()) + m_launch_info.GetFlags().Set(lldb::eLaunchFlagDetachOnError); + else + m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDetachOnError); +} + +void TargetProperties::DisableASLRValueChangedCallback() { + if (GetDisableASLR()) + m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableASLR); + else + m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableASLR); +} + +void TargetProperties::DisableSTDIOValueChangedCallback() { + if (GetDisableSTDIO()) + m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableSTDIO); + else + m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableSTDIO); +} + +// Target::TargetEventData + +Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp) + : EventData(), m_target_sp(target_sp), m_module_list() {} + +Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp, + const ModuleList &module_list) + : EventData(), m_target_sp(target_sp), m_module_list(module_list) {} + +Target::TargetEventData::~TargetEventData() = default; + +ConstString Target::TargetEventData::GetFlavorString() { + static ConstString g_flavor("Target::TargetEventData"); + return g_flavor; +} + +void Target::TargetEventData::Dump(Stream *s) const { + for (size_t i = 0; i < m_module_list.GetSize(); ++i) { + if (i != 0) + *s << ", "; + m_module_list.GetModuleAtIndex(i)->GetDescription( + s->AsRawOstream(), lldb::eDescriptionLevelBrief); + } +} + +const Target::TargetEventData * +Target::TargetEventData::GetEventDataFromEvent(const Event *event_ptr) { + if (event_ptr) { + const EventData *event_data = event_ptr->GetData(); + if (event_data && + event_data->GetFlavor() == TargetEventData::GetFlavorString()) + return static_cast<const TargetEventData *>(event_ptr->GetData()); + } + return nullptr; +} + +TargetSP Target::TargetEventData::GetTargetFromEvent(const Event *event_ptr) { + TargetSP target_sp; + const TargetEventData *event_data = GetEventDataFromEvent(event_ptr); + if (event_data) + target_sp = event_data->m_target_sp; + return target_sp; +} + +ModuleList +Target::TargetEventData::GetModuleListFromEvent(const Event *event_ptr) { + ModuleList module_list; + const TargetEventData *event_data = GetEventDataFromEvent(event_ptr); + if (event_data) + module_list = event_data->m_module_list; + return module_list; +} + +std::recursive_mutex &Target::GetAPIMutex() { + if (GetProcessSP() && GetProcessSP()->CurrentThreadIsPrivateStateThread()) + return m_private_mutex; + else + return m_mutex; +} diff --git a/gnu/llvm/lldb/source/Target/TargetList.cpp b/gnu/llvm/lldb/source/Target/TargetList.cpp new file mode 100644 index 00000000000..1b4db0c2aba --- /dev/null +++ b/gnu/llvm/lldb/source/Target/TargetList.cpp @@ -0,0 +1,617 @@ +//===-- TargetList.cpp ------------------------------------------*- C++ -*-===// +// +// 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/TargetList.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/TildeExpressionResolver.h" +#include "lldb/Utility/Timer.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" + +using namespace lldb; +using namespace lldb_private; + +ConstString &TargetList::GetStaticBroadcasterClass() { + static ConstString class_name("lldb.targetList"); + return class_name; +} + +// TargetList constructor +TargetList::TargetList(Debugger &debugger) + : Broadcaster(debugger.GetBroadcasterManager(), + TargetList::GetStaticBroadcasterClass().AsCString()), + m_target_list(), m_target_list_mutex(), m_selected_target_idx(0) { + CheckInWithManager(); +} + +// Destructor +TargetList::~TargetList() { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + m_target_list.clear(); +} + +Status TargetList::CreateTarget(Debugger &debugger, + llvm::StringRef user_exe_path, + llvm::StringRef triple_str, + LoadDependentFiles load_dependent_files, + const OptionGroupPlatform *platform_options, + TargetSP &target_sp) { + return CreateTargetInternal(debugger, user_exe_path, triple_str, + load_dependent_files, platform_options, target_sp, + false); +} + +Status TargetList::CreateTarget(Debugger &debugger, + llvm::StringRef user_exe_path, + const ArchSpec &specified_arch, + LoadDependentFiles load_dependent_files, + PlatformSP &platform_sp, TargetSP &target_sp) { + return CreateTargetInternal(debugger, user_exe_path, specified_arch, + load_dependent_files, platform_sp, target_sp, + false); +} + +Status TargetList::CreateTargetInternal( + Debugger &debugger, llvm::StringRef user_exe_path, + llvm::StringRef triple_str, LoadDependentFiles load_dependent_files, + const OptionGroupPlatform *platform_options, TargetSP &target_sp, + bool is_dummy_target) { + Status error; + PlatformSP platform_sp; + + // This is purposely left empty unless it is specified by triple_cstr. If not + // initialized via triple_cstr, then the currently selected platform will set + // the architecture correctly. + const ArchSpec arch(triple_str); + if (!triple_str.empty()) { + if (!arch.IsValid()) { + error.SetErrorStringWithFormat("invalid triple '%s'", + triple_str.str().c_str()); + return error; + } + } + + ArchSpec platform_arch(arch); + + bool prefer_platform_arch = false; + + CommandInterpreter &interpreter = debugger.GetCommandInterpreter(); + + // let's see if there is already an existing platform before we go creating + // another... + platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); + + if (platform_options && platform_options->PlatformWasSpecified()) { + // Create a new platform if it doesn't match the selected platform + if (!platform_options->PlatformMatches(platform_sp)) { + const bool select_platform = true; + platform_sp = platform_options->CreatePlatformWithOptions( + interpreter, arch, select_platform, error, platform_arch); + if (!platform_sp) + return error; + } + } + + if (!user_exe_path.empty()) { + ModuleSpecList module_specs; + ModuleSpec module_spec; + module_spec.GetFileSpec().SetFile(user_exe_path, FileSpec::Style::native); + FileSystem::Instance().Resolve(module_spec.GetFileSpec()); + + // Resolve the executable in case we are given a path to a application + // bundle like a .app bundle on MacOSX + Host::ResolveExecutableInBundle(module_spec.GetFileSpec()); + + lldb::offset_t file_offset = 0; + lldb::offset_t file_size = 0; + const size_t num_specs = ObjectFile::GetModuleSpecifications( + module_spec.GetFileSpec(), file_offset, file_size, module_specs); + if (num_specs > 0) { + ModuleSpec matching_module_spec; + + if (num_specs == 1) { + if (module_specs.GetModuleSpecAtIndex(0, matching_module_spec)) { + if (platform_arch.IsValid()) { + if (platform_arch.IsCompatibleMatch( + matching_module_spec.GetArchitecture())) { + // If the OS or vendor weren't specified, then adopt the module's + // architecture so that the platform matching can be more + // accurate + if (!platform_arch.TripleOSWasSpecified() || + !platform_arch.TripleVendorWasSpecified()) { + prefer_platform_arch = true; + platform_arch = matching_module_spec.GetArchitecture(); + } + } else { + StreamString platform_arch_strm; + StreamString module_arch_strm; + + platform_arch.DumpTriple(platform_arch_strm.AsRawOstream()); + matching_module_spec.GetArchitecture().DumpTriple( + module_arch_strm.AsRawOstream()); + error.SetErrorStringWithFormat( + "the specified architecture '%s' is not compatible with '%s' " + "in '%s'", + platform_arch_strm.GetData(), module_arch_strm.GetData(), + module_spec.GetFileSpec().GetPath().c_str()); + return error; + } + } else { + // Only one arch and none was specified + prefer_platform_arch = true; + platform_arch = matching_module_spec.GetArchitecture(); + } + } + } else { + if (arch.IsValid()) { + module_spec.GetArchitecture() = arch; + if (module_specs.FindMatchingModuleSpec(module_spec, + matching_module_spec)) { + prefer_platform_arch = true; + platform_arch = matching_module_spec.GetArchitecture(); + } + } else { + // No architecture specified, check if there is only one platform for + // all of the architectures. + + typedef std::vector<PlatformSP> PlatformList; + PlatformList platforms; + PlatformSP host_platform_sp = Platform::GetHostPlatform(); + for (size_t i = 0; i < num_specs; ++i) { + ModuleSpec module_spec; + if (module_specs.GetModuleSpecAtIndex(i, module_spec)) { + // See if there was a selected platform and check that first + // since the user may have specified it. + if (platform_sp) { + if (platform_sp->IsCompatibleArchitecture( + module_spec.GetArchitecture(), false, nullptr)) { + platforms.push_back(platform_sp); + continue; + } + } + + // Next check the host platform it if wasn't already checked + // above + if (host_platform_sp && + (!platform_sp || + host_platform_sp->GetName() != platform_sp->GetName())) { + if (host_platform_sp->IsCompatibleArchitecture( + module_spec.GetArchitecture(), false, nullptr)) { + platforms.push_back(host_platform_sp); + continue; + } + } + + // Just find a platform that matches the architecture in the + // executable file + PlatformSP fallback_platform_sp( + Platform::GetPlatformForArchitecture( + module_spec.GetArchitecture(), nullptr)); + if (fallback_platform_sp) { + platforms.push_back(fallback_platform_sp); + } + } + } + + Platform *platform_ptr = nullptr; + bool more_than_one_platforms = false; + for (const auto &the_platform_sp : platforms) { + if (platform_ptr) { + if (platform_ptr->GetName() != the_platform_sp->GetName()) { + more_than_one_platforms = true; + platform_ptr = nullptr; + break; + } + } else { + platform_ptr = the_platform_sp.get(); + } + } + + if (platform_ptr) { + // All platforms for all modules in the executable match, so we can + // select this platform + platform_sp = platforms.front(); + } else if (!more_than_one_platforms) { + // No platforms claim to support this file + error.SetErrorString("No matching platforms found for this file, " + "specify one with the --platform option"); + return error; + } else { + // More than one platform claims to support this file, so the + // --platform option must be specified + StreamString error_strm; + std::set<Platform *> platform_set; + error_strm.Printf( + "more than one platform supports this executable ("); + for (const auto &the_platform_sp : platforms) { + if (platform_set.find(the_platform_sp.get()) == + platform_set.end()) { + if (!platform_set.empty()) + error_strm.PutCString(", "); + error_strm.PutCString(the_platform_sp->GetName().GetCString()); + platform_set.insert(the_platform_sp.get()); + } + } + error_strm.Printf( + "), use the --platform option to specify a platform"); + error.SetErrorString(error_strm.GetString()); + return error; + } + } + } + } + } + + // If we have a valid architecture, make sure the current platform is + // compatible with that architecture + if (!prefer_platform_arch && arch.IsValid()) { + if (!platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) { + platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch); + if (!is_dummy_target && platform_sp) + debugger.GetPlatformList().SetSelectedPlatform(platform_sp); + } + } else if (platform_arch.IsValid()) { + // if "arch" isn't valid, yet "platform_arch" is, it means we have an + // executable file with a single architecture which should be used + ArchSpec fixed_platform_arch; + if (!platform_sp->IsCompatibleArchitecture(platform_arch, false, + &fixed_platform_arch)) { + platform_sp = Platform::GetPlatformForArchitecture(platform_arch, + &fixed_platform_arch); + if (!is_dummy_target && platform_sp) + debugger.GetPlatformList().SetSelectedPlatform(platform_sp); + } + } + + if (!platform_arch.IsValid()) + platform_arch = arch; + + error = TargetList::CreateTargetInternal( + debugger, user_exe_path, platform_arch, load_dependent_files, platform_sp, + target_sp, is_dummy_target); + return error; +} + +lldb::TargetSP TargetList::GetDummyTarget(lldb_private::Debugger &debugger) { + // FIXME: Maybe the dummy target should be per-Debugger + if (!m_dummy_target_sp || !m_dummy_target_sp->IsValid()) { + ArchSpec arch(Target::GetDefaultArchitecture()); + if (!arch.IsValid()) + arch = HostInfo::GetArchitecture(); + Status err = CreateDummyTarget( + debugger, arch.GetTriple().getTriple().c_str(), m_dummy_target_sp); + } + + return m_dummy_target_sp; +} + +Status TargetList::CreateDummyTarget(Debugger &debugger, + llvm::StringRef specified_arch_name, + lldb::TargetSP &target_sp) { + PlatformSP host_platform_sp(Platform::GetHostPlatform()); + return CreateTargetInternal( + debugger, (const char *)nullptr, specified_arch_name, eLoadDependentsNo, + (const OptionGroupPlatform *)nullptr, target_sp, true); +} + +Status TargetList::CreateTargetInternal(Debugger &debugger, + llvm::StringRef user_exe_path, + const ArchSpec &specified_arch, + LoadDependentFiles load_dependent_files, + lldb::PlatformSP &platform_sp, + lldb::TargetSP &target_sp, + bool is_dummy_target) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer( + func_cat, "TargetList::CreateTarget (file = '%s', arch = '%s')", + user_exe_path.str().c_str(), specified_arch.GetArchitectureName()); + Status error; + + ArchSpec arch(specified_arch); + + if (arch.IsValid()) { + if (!platform_sp || + !platform_sp->IsCompatibleArchitecture(arch, false, nullptr)) + platform_sp = Platform::GetPlatformForArchitecture(specified_arch, &arch); + } + + if (!platform_sp) + platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); + + if (!arch.IsValid()) + arch = specified_arch; + + FileSpec file(user_exe_path); + if (!FileSystem::Instance().Exists(file) && user_exe_path.startswith("~")) { + // we want to expand the tilde but we don't want to resolve any symbolic + // links so we can't use the FileSpec constructor's resolve flag + llvm::SmallString<64> unglobbed_path; + StandardTildeExpressionResolver Resolver; + Resolver.ResolveFullPath(user_exe_path, unglobbed_path); + + if (unglobbed_path.empty()) + file = FileSpec(user_exe_path); + else + file = FileSpec(unglobbed_path.c_str()); + } + + bool user_exe_path_is_bundle = false; + char resolved_bundle_exe_path[PATH_MAX]; + resolved_bundle_exe_path[0] = '\0'; + if (file) { + if (FileSystem::Instance().IsDirectory(file)) + user_exe_path_is_bundle = true; + + if (file.IsRelative() && !user_exe_path.empty()) { + llvm::SmallString<64> cwd; + if (! llvm::sys::fs::current_path(cwd)) { + FileSpec cwd_file(cwd.c_str()); + cwd_file.AppendPathComponent(file); + if (FileSystem::Instance().Exists(cwd_file)) + file = cwd_file; + } + } + + ModuleSP exe_module_sp; + if (platform_sp) { + FileSpecList executable_search_paths( + Target::GetDefaultExecutableSearchPaths()); + ModuleSpec module_spec(file, arch); + error = platform_sp->ResolveExecutable(module_spec, exe_module_sp, + executable_search_paths.GetSize() + ? &executable_search_paths + : nullptr); + } + + if (error.Success() && exe_module_sp) { + if (exe_module_sp->GetObjectFile() == nullptr) { + if (arch.IsValid()) { + error.SetErrorStringWithFormat( + "\"%s\" doesn't contain architecture %s", file.GetPath().c_str(), + arch.GetArchitectureName()); + } else { + error.SetErrorStringWithFormat("unsupported file type \"%s\"", + file.GetPath().c_str()); + } + return error; + } + target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); + target_sp->SetExecutableModule(exe_module_sp, load_dependent_files); + if (user_exe_path_is_bundle) + exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, + sizeof(resolved_bundle_exe_path)); + } + } else { + // No file was specified, just create an empty target with any arch if a + // valid arch was specified + target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); + } + + if (target_sp) { + // Set argv0 with what the user typed, unless the user specified a + // directory. If the user specified a directory, then it is probably a + // bundle that was resolved and we need to use the resolved bundle path + if (!user_exe_path.empty()) { + // Use exactly what the user typed as the first argument when we exec or + // posix_spawn + if (user_exe_path_is_bundle && resolved_bundle_exe_path[0]) { + target_sp->SetArg0(resolved_bundle_exe_path); + } else { + // Use resolved path + target_sp->SetArg0(file.GetPath().c_str()); + } + } + if (file.GetDirectory()) { + FileSpec file_dir; + file_dir.GetDirectory() = file.GetDirectory(); + target_sp->AppendExecutableSearchPaths(file_dir); + } + + // Don't put the dummy target in the target list, it's held separately. + if (!is_dummy_target) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + m_selected_target_idx = m_target_list.size(); + m_target_list.push_back(target_sp); + // Now prime this from the dummy target: + target_sp->PrimeFromDummyTarget(debugger.GetDummyTarget()); + } else { + m_dummy_target_sp = target_sp; + } + } + + return error; +} + +bool TargetList::DeleteTarget(TargetSP &target_sp) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::iterator pos, end = m_target_list.end(); + + for (pos = m_target_list.begin(); pos != end; ++pos) { + if (pos->get() == target_sp.get()) { + m_target_list.erase(pos); + return true; + } + } + return false; +} + +TargetSP TargetList::FindTargetWithExecutableAndArchitecture( + const FileSpec &exe_file_spec, const ArchSpec *exe_arch_ptr) const { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + TargetSP target_sp; + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + Module *exe_module = (*pos)->GetExecutableModulePointer(); + + if (exe_module) { + if (FileSpec::Match(exe_file_spec, exe_module->GetFileSpec())) { + if (exe_arch_ptr) { + if (!exe_arch_ptr->IsCompatibleMatch(exe_module->GetArchitecture())) + continue; + } + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +TargetSP TargetList::FindTargetWithProcessID(lldb::pid_t pid) const { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + TargetSP target_sp; + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + Process *process = (*pos)->GetProcessSP().get(); + if (process && process->GetID() == pid) { + target_sp = *pos; + break; + } + } + return target_sp; +} + +TargetSP TargetList::FindTargetWithProcess(Process *process) const { + TargetSP target_sp; + if (process) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + if (process == (*pos)->GetProcessSP().get()) { + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +TargetSP TargetList::GetTargetSP(Target *target) const { + TargetSP target_sp; + if (target) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + if (target == (*pos).get()) { + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +uint32_t TargetList::SendAsyncInterrupt(lldb::pid_t pid) { + uint32_t num_async_interrupts_sent = 0; + + if (pid != LLDB_INVALID_PROCESS_ID) { + TargetSP target_sp(FindTargetWithProcessID(pid)); + if (target_sp) { + Process *process = target_sp->GetProcessSP().get(); + if (process) { + process->SendAsyncInterrupt(); + ++num_async_interrupts_sent; + } + } + } else { + // We don't have a valid pid to broadcast to, so broadcast to the target + // list's async broadcaster... + BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr); + } + + return num_async_interrupts_sent; +} + +uint32_t TargetList::SignalIfRunning(lldb::pid_t pid, int signo) { + uint32_t num_signals_sent = 0; + Process *process = nullptr; + if (pid == LLDB_INVALID_PROCESS_ID) { + // Signal all processes with signal + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + process = (*pos)->GetProcessSP().get(); + if (process) { + if (process->IsAlive()) { + ++num_signals_sent; + process->Signal(signo); + } + } + } + } else { + // Signal a specific process with signal + TargetSP target_sp(FindTargetWithProcessID(pid)); + if (target_sp) { + process = target_sp->GetProcessSP().get(); + if (process) { + if (process->IsAlive()) { + ++num_signals_sent; + process->Signal(signo); + } + } + } + } + return num_signals_sent; +} + +int TargetList::GetNumTargets() const { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + return m_target_list.size(); +} + +lldb::TargetSP TargetList::GetTargetAtIndex(uint32_t idx) const { + TargetSP target_sp; + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + if (idx < m_target_list.size()) + target_sp = m_target_list[idx]; + return target_sp; +} + +uint32_t TargetList::GetIndexOfTarget(lldb::TargetSP target_sp) const { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + size_t num_targets = m_target_list.size(); + for (size_t idx = 0; idx < num_targets; idx++) { + if (target_sp == m_target_list[idx]) + return idx; + } + return UINT32_MAX; +} + +uint32_t TargetList::SetSelectedTarget(Target *target) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::const_iterator pos, begin = m_target_list.begin(), + end = m_target_list.end(); + for (pos = begin; pos != end; ++pos) { + if (pos->get() == target) { + m_selected_target_idx = std::distance(begin, pos); + return m_selected_target_idx; + } + } + m_selected_target_idx = 0; + return m_selected_target_idx; +} + +lldb::TargetSP TargetList::GetSelectedTarget() { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + if (m_selected_target_idx >= m_target_list.size()) + m_selected_target_idx = 0; + return GetTargetAtIndex(m_selected_target_idx); +} diff --git a/gnu/llvm/lldb/source/Target/TargetProperties.td b/gnu/llvm/lldb/source/Target/TargetProperties.td new file mode 100644 index 00000000000..ff8062aaa2c --- /dev/null +++ b/gnu/llvm/lldb/source/Target/TargetProperties.td @@ -0,0 +1,234 @@ +include "../../include/lldb/Core/PropertiesBase.td" + +let Definition = "experimental" in { + def InjectLocalVars : Property<"inject-local-vars", "Boolean">, + Global, DefaultTrue, + Desc<"If true, inject local variables explicitly into the expression text. This will fix symbol resolution when there are name collisions between ivars and local variables. But it can make expressions run much more slowly.">; +} + +let Definition = "target" in { + def DefaultArch: Property<"default-arch", "Arch">, + Global, + DefaultStringValue<"">, + Desc<"Default architecture to choose, when there's a choice.">; + def MoveToNearestCode: Property<"move-to-nearest-code", "Boolean">, + DefaultTrue, + Desc<"Move breakpoints to nearest code.">; + def Language: Property<"language", "Language">, + DefaultEnumValue<"eLanguageTypeUnknown">, + Desc<"The language to use when interpreting expressions entered in commands.">; + def ExprPrefix: Property<"expr-prefix", "FileSpec">, + DefaultStringValue<"">, + Desc<"Path to a file containing expressions to be prepended to all expressions.">; + def PreferDynamic: Property<"prefer-dynamic-value", "Enum">, + DefaultEnumValue<"eDynamicDontRunTarget">, + EnumValues<"OptionEnumValues(g_dynamic_value_types)">, + Desc<"Should printed values be shown as their dynamic value.">; + def EnableSynthetic: Property<"enable-synthetic-value", "Boolean">, + DefaultTrue, + Desc<"Should synthetic values be used by default whenever available.">; + def SkipPrologue: Property<"skip-prologue", "Boolean">, + DefaultTrue, + Desc<"Skip function prologues when setting breakpoints by name.">; + def SourceMap: Property<"source-map", "PathMap">, + DefaultStringValue<"">, + Desc<"Source path remappings are used to track the change of location between a source file when built, and where it exists on the current system. It consists of an array of duples, the first element of each duple is some part (starting at the root) of the path to the file when it was built, and the second is where the remainder of the original build hierarchy is rooted on the local system. Each element of the array is checked in order and the first one that results in a match wins.">; + def ExecutableSearchPaths: Property<"exec-search-paths", "FileSpecList">, + DefaultStringValue<"">, + Desc<"Executable search paths to use when locating executable files whose paths don't match the local file system.">; + def DebugFileSearchPaths: Property<"debug-file-search-paths", "FileSpecList">, + DefaultStringValue<"">, + Desc<"List of directories to be searched when locating debug symbol files. See also symbols.enable-external-lookup.">; + def ClangModuleSearchPaths: Property<"clang-module-search-paths", "FileSpecList">, + DefaultStringValue<"">, + Desc<"List of directories to be searched when locating modules for Clang.">; + def AutoImportClangModules: Property<"auto-import-clang-modules", "Boolean">, + DefaultTrue, + Desc<"Automatically load Clang modules referred to by the program.">; + def ImportStdModule: Property<"import-std-module", "Boolean">, + DefaultFalse, + Desc<"Import the C++ std module to improve debugging STL containers.">; + def AutoApplyFixIts: Property<"auto-apply-fixits", "Boolean">, + DefaultTrue, + Desc<"Automatically apply fix-it hints to expressions.">; + def NotifyAboutFixIts: Property<"notify-about-fixits", "Boolean">, + DefaultTrue, + Desc<"Print the fixed expression text.">; + def SaveObjects: Property<"save-jit-objects", "Boolean">, + DefaultFalse, + Desc<"Save intermediate object files generated by the LLVM JIT">; + def MaxZeroPaddingInFloatFormat: Property<"max-zero-padding-in-float-format", "UInt64">, + DefaultUnsignedValue<6>, + Desc<"The maximum number of zeroes to insert when displaying a very small float before falling back to scientific notation.">; + def MaxChildrenCount: Property<"max-children-count", "SInt64">, + DefaultUnsignedValue<256>, + Desc<"Maximum number of children to expand in any level of depth.">; + def MaxSummaryLength: Property<"max-string-summary-length", "SInt64">, + DefaultUnsignedValue<1024>, + Desc<"Maximum number of characters to show when using %s in summary strings.">; + def MaxMemReadSize: Property<"max-memory-read-size", "SInt64">, + DefaultUnsignedValue<1024>, + Desc<"Maximum number of bytes that 'memory read' will fetch before --force must be specified.">; + def BreakpointUseAvoidList: Property<"breakpoints-use-platform-avoid-list", "Boolean">, + DefaultTrue, + Desc<"Consult the platform module avoid list when setting non-module specific breakpoints.">; + def Arg0: Property<"arg0", "String">, + DefaultStringValue<"">, + Desc<"The first argument passed to the program in the argument array which can be different from the executable itself.">; + def RunArgs: Property<"run-args", "Args">, + DefaultStringValue<"">, + Desc<"A list containing all the arguments to be passed to the executable when it is run. Note that this does NOT include the argv[0] which is in target.arg0.">; + def EnvVars: Property<"env-vars", "Dictionary">, + DefaultUnsignedValue<16>, + Desc<"A list of all the environment variables to be passed to the executable's environment, and their values.">; + def InheritEnv: Property<"inherit-env", "Boolean">, + DefaultTrue, + Desc<"Inherit the environment from the process that is running LLDB.">; + def InputPath: Property<"input-path", "FileSpec">, + DefaultStringValue<"">, + Desc<"The file/path to be used by the executable program for reading its standard input.">; + def OutputPath: Property<"output-path", "FileSpec">, + DefaultStringValue<"">, + Desc<"The file/path to be used by the executable program for writing its standard output.">; + def ErrorPath: Property<"error-path", "FileSpec">, + DefaultStringValue<"">, + Desc<"The file/path to be used by the executable program for writing its standard error.">; + def DetachOnError: Property<"detach-on-error", "Boolean">, + DefaultTrue, + Desc<"debugserver will detach (rather than killing) a process if it loses connection with lldb.">; + def PreloadSymbols: Property<"preload-symbols", "Boolean">, + DefaultTrue, + Desc<"Enable loading of symbol tables before they are needed.">; + def DisableASLR: Property<"disable-aslr", "Boolean">, + DefaultTrue, + Desc<"Disable Address Space Layout Randomization (ASLR)">; + def DisableSTDIO: Property<"disable-stdio", "Boolean">, + DefaultFalse, + Desc<"Disable stdin/stdout for process (e.g. for a GUI application)">; + def InlineStrategy: Property<"inline-breakpoint-strategy", "Enum">, + DefaultEnumValue<"eInlineBreakpointsAlways">, + EnumValues<"OptionEnumValues(g_inline_breakpoint_enums)">, + Desc<"The strategy to use when settings breakpoints by file and line. Breakpoint locations can end up being inlined by the compiler, so that a compile unit 'a.c' might contain an inlined function from another source file. Usually this is limited to breakpoint locations from inlined functions from header or other include files, or more accurately non-implementation source files. Sometimes code might #include implementation files and cause inlined breakpoint locations in inlined implementation files. Always checking for inlined breakpoint locations can be expensive (memory and time), so if you have a project with many headers and find that setting breakpoints is slow, then you can change this setting to headers. This setting allows you to control exactly which strategy is used when setting file and line breakpoints.">; + def DisassemblyFlavor: Property<"x86-disassembly-flavor", "Enum">, + DefaultEnumValue<"eX86DisFlavorDefault">, + EnumValues<"OptionEnumValues(g_x86_dis_flavor_value_types)">, + Desc<"The default disassembly flavor to use for x86 or x86-64 targets.">; + def UseHexImmediates: Property<"use-hex-immediates", "Boolean">, + DefaultTrue, + Desc<"Show immediates in disassembly as hexadecimal.">; + def HexImmediateStyle: Property<"hex-immediate-style", "Enum">, + DefaultEnumValue<"Disassembler::eHexStyleC">, + EnumValues<"OptionEnumValues(g_hex_immediate_style_values)">, + Desc<"Which style to use for printing hexadecimal disassembly values.">; + def UseFastStepping: Property<"use-fast-stepping", "Boolean">, + DefaultTrue, + Desc<"Use a fast stepping algorithm based on running from branch to branch rather than instruction single-stepping.">; + def LoadScriptFromSymbolFile: Property<"load-script-from-symbol-file", "Enum">, + DefaultEnumValue<"eLoadScriptFromSymFileWarn">, + EnumValues<"OptionEnumValues(g_load_script_from_sym_file_values)">, + Desc<"Allow LLDB to load scripting resources embedded in symbol files when available.">; + def LoadCWDlldbinitFile: Property<"load-cwd-lldbinit", "Enum">, + DefaultEnumValue<"eLoadCWDlldbinitWarn">, + EnumValues<"OptionEnumValues(g_load_cwd_lldbinit_values)">, + Desc<"Allow LLDB to .lldbinit files from the current directory automatically.">; + def MemoryModuleLoadLevel: Property<"memory-module-load-level", "Enum">, + DefaultEnumValue<"eMemoryModuleLoadLevelComplete">, + EnumValues<"OptionEnumValues(g_memory_module_load_level_values)">, + Desc<"Loading modules from memory can be slow as reading the symbol tables and other data can take a long time depending on your connection to the debug target. This setting helps users control how much information gets loaded when loading modules from memory.'complete' is the default value for this setting which will load all sections and symbols by reading them from memory (slowest, most accurate). 'partial' will load sections and attempt to find function bounds without downloading the symbol table (faster, still accurate, missing symbol names). 'minimal' is the fastest setting and will load section data with no symbols, but should rarely be used as stack frames in these memory regions will be inaccurate and not provide any context (fastest). ">; + def DisplayExpressionsInCrashlogs: Property<"display-expression-in-crashlogs", "Boolean">, + DefaultFalse, + Desc<"Expressions that crash will show up in crash logs if the host system supports executable specific crash log strings and this setting is set to true.">; + def TrapHandlerNames: Property<"trap-handler-names", "Array">, + Global, + DefaultUnsignedValue<16>, + Desc<"A list of trap handler function names, e.g. a common Unix user process one is _sigtramp.">; + def DisplayRuntimeSupportValues: Property<"display-runtime-support-values", "Boolean">, + DefaultFalse, + Desc<"If true, LLDB will show variables that are meant to support the operation of a language's runtime support.">; + def DisplayRecognizedArguments: Property<"display-recognized-arguments", "Boolean">, + DefaultFalse, + Desc<"Show recognized arguments in variable listings by default.">; + def NonStopModeEnabled: Property<"non-stop-mode", "Boolean">, + DefaultFalse, + Desc<"Disable lock-step debugging, instead control threads independently.">; + def RequireHardwareBreakpoints: Property<"require-hardware-breakpoint", "Boolean">, + DefaultFalse, + Desc<"Require all breakpoints to be hardware breakpoints.">; +} + +let Definition = "process" in { + def DisableMemCache: Property<"disable-memory-cache", "Boolean">, + DefaultFalse, + Desc<"Disable reading and caching of memory in fixed-size units.">; + def ExtraStartCommand: Property<"extra-startup-command", "Array">, + DefaultUnsignedValue<16>, + Desc<"A list containing extra commands understood by the particular process plugin used. For instance, to turn on debugserver logging set this to 'QSetLogging:bitmask=LOG_DEFAULT;'">; + def IgnoreBreakpointsInExpressions: Property<"ignore-breakpoints-in-expressions", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, breakpoints will be ignored during expression evaluation.">; + def UnwindOnErrorInExpressions: Property<"unwind-on-error-in-expressions", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, errors in expression evaluation will unwind the stack back to the state before the call.">; + def PythonOSPluginPath: Property<"python-os-plugin-path", "FileSpec">, + DefaultUnsignedValue<1>, + Desc<"A path to a python OS plug-in module file that contains a OperatingSystemPlugIn class.">; + def StopOnSharedLibraryEvents: Property<"stop-on-sharedlibrary-events", "Boolean">, + Global, + DefaultFalse, + Desc<"If true, stop when a shared library is loaded or unloaded.">; + def DetachKeepsStopped: Property<"detach-keeps-stopped", "Boolean">, + Global, + DefaultFalse, + Desc<"If true, detach will attempt to keep the process stopped.">; + def MemCacheLineSize: Property<"memory-cache-line-size", "UInt64">, + DefaultUnsignedValue<512>, + Desc<"The memory cache line size">; + def WarningOptimization: Property<"optimization-warnings", "Boolean">, + DefaultTrue, + Desc<"If true, warn when stopped in code that is optimized where stepping and variable availability may not behave as expected.">; + def StopOnExec: Property<"stop-on-exec", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, stop when a shared library is loaded or unloaded.">; + def UtilityExpressionTimeout: Property<"utility-expression-timeout", "UInt64">, + DefaultUnsignedValue<15>, + Desc<"The time in seconds to wait for LLDB-internal utility expressions.">; +} + +let Definition = "platform" in { + def UseModuleCache: Property<"use-module-cache", "Boolean">, + Global, + DefaultTrue, + Desc<"Use module cache.">; + def ModuleCacheDirectory: Property<"module-cache-directory", "FileSpec">, + Global, + DefaultStringValue<"">, + Desc<"Root directory for cached modules.">; +} + +let Definition = "thread" in { + def StepInAvoidsNoDebug: Property<"step-in-avoid-nodebug", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, step-in will not stop in functions with no debug information.">; + def StepOutAvoidsNoDebug: Property<"step-out-avoid-nodebug", "Boolean">, + Global, + DefaultFalse, + Desc<"If true, when step-in/step-out/step-over leave the current frame, they will continue to step out till they come to a function with debug information. Passing a frame argument to step-out will override this option.">; + def StepAvoidRegex: Property<"step-avoid-regexp", "Regex">, + Global, + DefaultStringValue<"^std::">, + Desc<"A regular expression defining functions step-in won't stop in.">; + def StepAvoidLibraries: Property<"step-avoid-libraries", "FileSpecList">, + Global, + DefaultStringValue<"">, + Desc<"A list of libraries that source stepping won't stop in.">; + def EnableThreadTrace: Property<"trace-thread", "Boolean">, + DefaultFalse, + Desc<"If true, this thread will single-step and log execution.">; + def MaxBacktraceDepth: Property<"max-backtrace-depth", "UInt64">, + DefaultUnsignedValue<300000>, + Desc<"Maximum number of frames to backtrace.">; +} diff --git a/gnu/llvm/lldb/source/Target/Thread.cpp b/gnu/llvm/lldb/source/Target/Thread.cpp new file mode 100644 index 00000000000..e12b9050110 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/Thread.cpp @@ -0,0 +1,2225 @@ +//===-- Thread.cpp ----------------------------------------------*- C++ -*-===// +// +// 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/Thread.h" +#include "Plugins/Process/Utility/UnwindLLDB.h" +#include "Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FormatEntity.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/OptionValueFileSpecList.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrameRecognizer.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Target/ThreadPlanPython.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInRange.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepOverBreakpoint.h" +#include "lldb/Target/ThreadPlanStepOverRange.h" +#include "lldb/Target/ThreadPlanStepThrough.h" +#include "lldb/Target/ThreadPlanStepUntil.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/lldb-enumerations.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +const ThreadPropertiesSP &Thread::GetGlobalProperties() { + // NOTE: intentional leak so we don't crash if global destructor chain gets + // called as other threads still use the result of this function + static ThreadPropertiesSP *g_settings_sp_ptr = + new ThreadPropertiesSP(new ThreadProperties(true)); + return *g_settings_sp_ptr; +} + +#define LLDB_PROPERTIES_thread +#include "TargetProperties.inc" + +enum { +#define LLDB_PROPERTIES_thread +#include "TargetPropertiesEnum.inc" +}; + +class ThreadOptionValueProperties : public OptionValueProperties { +public: + ThreadOptionValueProperties(ConstString name) + : OptionValueProperties(name) {} + + // This constructor is used when creating ThreadOptionValueProperties when it + // is part of a new lldb_private::Thread instance. It will copy all current + // global property values as needed + ThreadOptionValueProperties(ThreadProperties *global_properties) + : OptionValueProperties(*global_properties->GetValueProperties()) {} + + const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const override { + // When getting the value for a key from the thread options, we will always + // try and grab the setting from the current thread if there is one. Else + // we just use the one from this instance. + if (exe_ctx) { + Thread *thread = exe_ctx->GetThreadPtr(); + if (thread) { + ThreadOptionValueProperties *instance_properties = + static_cast<ThreadOptionValueProperties *>( + thread->GetValueProperties().get()); + if (this != instance_properties) + return instance_properties->ProtectedGetPropertyAtIndex(idx); + } + } + return ProtectedGetPropertyAtIndex(idx); + } +}; + +ThreadProperties::ThreadProperties(bool is_global) : Properties() { + if (is_global) { + m_collection_sp = + std::make_shared<ThreadOptionValueProperties>(ConstString("thread")); + m_collection_sp->Initialize(g_thread_properties); + } else + m_collection_sp = std::make_shared<ThreadOptionValueProperties>( + Thread::GetGlobalProperties().get()); +} + +ThreadProperties::~ThreadProperties() = default; + +const RegularExpression *ThreadProperties::GetSymbolsToAvoidRegexp() { + const uint32_t idx = ePropertyStepAvoidRegex; + return m_collection_sp->GetPropertyAtIndexAsOptionValueRegex(nullptr, idx); +} + +FileSpecList ThreadProperties::GetLibrariesToAvoid() const { + const uint32_t idx = ePropertyStepAvoidLibraries; + const OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); +} + +bool ThreadProperties::GetTraceEnabledState() const { + const uint32_t idx = ePropertyEnableThreadTrace; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_thread_properties[idx].default_uint_value != 0); +} + +bool ThreadProperties::GetStepInAvoidsNoDebug() const { + const uint32_t idx = ePropertyStepInAvoidsNoDebug; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_thread_properties[idx].default_uint_value != 0); +} + +bool ThreadProperties::GetStepOutAvoidsNoDebug() const { + const uint32_t idx = ePropertyStepOutAvoidsNoDebug; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_thread_properties[idx].default_uint_value != 0); +} + +uint64_t ThreadProperties::GetMaxBacktraceDepth() const { + const uint32_t idx = ePropertyMaxBacktraceDepth; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_thread_properties[idx].default_uint_value != 0); +} + +// Thread Event Data + +ConstString Thread::ThreadEventData::GetFlavorString() { + static ConstString g_flavor("Thread::ThreadEventData"); + return g_flavor; +} + +Thread::ThreadEventData::ThreadEventData(const lldb::ThreadSP thread_sp) + : m_thread_sp(thread_sp), m_stack_id() {} + +Thread::ThreadEventData::ThreadEventData(const lldb::ThreadSP thread_sp, + const StackID &stack_id) + : m_thread_sp(thread_sp), m_stack_id(stack_id) {} + +Thread::ThreadEventData::ThreadEventData() : m_thread_sp(), m_stack_id() {} + +Thread::ThreadEventData::~ThreadEventData() = default; + +void Thread::ThreadEventData::Dump(Stream *s) const {} + +const Thread::ThreadEventData * +Thread::ThreadEventData::GetEventDataFromEvent(const Event *event_ptr) { + if (event_ptr) { + const EventData *event_data = event_ptr->GetData(); + if (event_data && + event_data->GetFlavor() == ThreadEventData::GetFlavorString()) + return static_cast<const ThreadEventData *>(event_ptr->GetData()); + } + return nullptr; +} + +ThreadSP Thread::ThreadEventData::GetThreadFromEvent(const Event *event_ptr) { + ThreadSP thread_sp; + const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); + if (event_data) + thread_sp = event_data->GetThread(); + return thread_sp; +} + +StackID Thread::ThreadEventData::GetStackIDFromEvent(const Event *event_ptr) { + StackID stack_id; + const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); + if (event_data) + stack_id = event_data->GetStackID(); + return stack_id; +} + +StackFrameSP +Thread::ThreadEventData::GetStackFrameFromEvent(const Event *event_ptr) { + const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); + StackFrameSP frame_sp; + if (event_data) { + ThreadSP thread_sp = event_data->GetThread(); + if (thread_sp) { + frame_sp = thread_sp->GetStackFrameList()->GetFrameWithStackID( + event_data->GetStackID()); + } + } + return frame_sp; +} + +// Thread class + +ConstString &Thread::GetStaticBroadcasterClass() { + static ConstString class_name("lldb.thread"); + return class_name; +} + +Thread::Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id) + : ThreadProperties(false), UserID(tid), + Broadcaster(process.GetTarget().GetDebugger().GetBroadcasterManager(), + Thread::GetStaticBroadcasterClass().AsCString()), + m_process_wp(process.shared_from_this()), m_stop_info_sp(), + m_stop_info_stop_id(0), m_stop_info_override_stop_id(0), + m_index_id(use_invalid_index_id ? LLDB_INVALID_INDEX32 + : process.GetNextThreadIndexID(tid)), + m_reg_context_sp(), m_state(eStateUnloaded), m_state_mutex(), + m_plan_stack(), m_completed_plan_stack(), m_frame_mutex(), + m_curr_frames_sp(), m_prev_frames_sp(), + m_resume_signal(LLDB_INVALID_SIGNAL_NUMBER), + m_resume_state(eStateRunning), m_temporary_resume_state(eStateRunning), + m_unwinder_up(), m_destroy_called(false), + m_override_should_notify(eLazyBoolCalculate), + m_extended_info_fetched(false), m_extended_info() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, "%p Thread::Thread(tid = 0x%4.4" PRIx64 ")", + static_cast<void *>(this), GetID()); + + CheckInWithManager(); + + QueueFundamentalPlan(true); +} + +Thread::~Thread() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, "%p Thread::~Thread(tid = 0x%4.4" PRIx64 ")", + static_cast<void *>(this), GetID()); + /// If you hit this assert, it means your derived class forgot to call + /// DoDestroy in its destructor. + assert(m_destroy_called); +} + +void Thread::DestroyThread() { + // Tell any plans on the plan stacks that the thread is being destroyed since + // any plans that have a thread go away in the middle of might need to do + // cleanup, or in some cases NOT do cleanup... + for (auto plan : m_plan_stack) + plan->ThreadDestroyed(); + + for (auto plan : m_discarded_plan_stack) + plan->ThreadDestroyed(); + + for (auto plan : m_completed_plan_stack) + plan->ThreadDestroyed(); + + m_destroy_called = true; + m_plan_stack.clear(); + m_discarded_plan_stack.clear(); + m_completed_plan_stack.clear(); + + // Push a ThreadPlanNull on the plan stack. That way we can continue + // assuming that the plan stack is never empty, but if somebody errantly asks + // questions of a destroyed thread without checking first whether it is + // destroyed, they won't crash. + ThreadPlanSP null_plan_sp(new ThreadPlanNull(*this)); + m_plan_stack.push_back(null_plan_sp); + + m_stop_info_sp.reset(); + m_reg_context_sp.reset(); + m_unwinder_up.reset(); + std::lock_guard<std::recursive_mutex> guard(m_frame_mutex); + m_curr_frames_sp.reset(); + m_prev_frames_sp.reset(); +} + +void Thread::BroadcastSelectedFrameChange(StackID &new_frame_id) { + if (EventTypeHasListeners(eBroadcastBitSelectedFrameChanged)) + BroadcastEvent(eBroadcastBitSelectedFrameChanged, + new ThreadEventData(this->shared_from_this(), new_frame_id)); +} + +lldb::StackFrameSP Thread::GetSelectedFrame() { + StackFrameListSP stack_frame_list_sp(GetStackFrameList()); + StackFrameSP frame_sp = stack_frame_list_sp->GetFrameAtIndex( + stack_frame_list_sp->GetSelectedFrameIndex()); + FunctionOptimizationWarning(frame_sp.get()); + return frame_sp; +} + +uint32_t Thread::SetSelectedFrame(lldb_private::StackFrame *frame, + bool broadcast) { + uint32_t ret_value = GetStackFrameList()->SetSelectedFrame(frame); + if (broadcast) + BroadcastSelectedFrameChange(frame->GetStackID()); + FunctionOptimizationWarning(frame); + return ret_value; +} + +bool Thread::SetSelectedFrameByIndex(uint32_t frame_idx, bool broadcast) { + StackFrameSP frame_sp(GetStackFrameList()->GetFrameAtIndex(frame_idx)); + if (frame_sp) { + GetStackFrameList()->SetSelectedFrame(frame_sp.get()); + if (broadcast) + BroadcastSelectedFrameChange(frame_sp->GetStackID()); + FunctionOptimizationWarning(frame_sp.get()); + return true; + } else + return false; +} + +bool Thread::SetSelectedFrameByIndexNoisily(uint32_t frame_idx, + Stream &output_stream) { + const bool broadcast = true; + bool success = SetSelectedFrameByIndex(frame_idx, broadcast); + if (success) { + StackFrameSP frame_sp = GetSelectedFrame(); + if (frame_sp) { + bool already_shown = false; + SymbolContext frame_sc( + frame_sp->GetSymbolContext(eSymbolContextLineEntry)); + if (GetProcess()->GetTarget().GetDebugger().GetUseExternalEditor() && + frame_sc.line_entry.file && frame_sc.line_entry.line != 0) { + already_shown = Host::OpenFileInExternalEditor( + frame_sc.line_entry.file, frame_sc.line_entry.line); + } + + bool show_frame_info = true; + bool show_source = !already_shown; + FunctionOptimizationWarning(frame_sp.get()); + return frame_sp->GetStatus(output_stream, show_frame_info, show_source); + } + return false; + } else + return false; +} + +void Thread::FunctionOptimizationWarning(StackFrame *frame) { + if (frame && frame->HasDebugInformation() && + GetProcess()->GetWarningsOptimization()) { + SymbolContext sc = + frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextModule); + GetProcess()->PrintWarningOptimization(sc); + } +} + +lldb::StopInfoSP Thread::GetStopInfo() { + if (m_destroy_called) + return m_stop_info_sp; + + ThreadPlanSP completed_plan_sp(GetCompletedPlan()); + ProcessSP process_sp(GetProcess()); + const uint32_t stop_id = process_sp ? process_sp->GetStopID() : UINT32_MAX; + + // Here we select the stop info according to priorirty: - m_stop_info_sp (if + // not trace) - preset value - completed plan stop info - new value with plan + // from completed plan stack - m_stop_info_sp (trace stop reason is OK now) - + // ask GetPrivateStopInfo to set stop info + + bool have_valid_stop_info = m_stop_info_sp && + m_stop_info_sp ->IsValid() && + m_stop_info_stop_id == stop_id; + bool have_valid_completed_plan = completed_plan_sp && completed_plan_sp->PlanSucceeded(); + bool plan_failed = completed_plan_sp && !completed_plan_sp->PlanSucceeded(); + bool plan_overrides_trace = + have_valid_stop_info && have_valid_completed_plan + && (m_stop_info_sp->GetStopReason() == eStopReasonTrace); + + if (have_valid_stop_info && !plan_overrides_trace && !plan_failed) { + return m_stop_info_sp; + } else if (completed_plan_sp) { + return StopInfo::CreateStopReasonWithPlan( + completed_plan_sp, GetReturnValueObject(), GetExpressionVariable()); + } else { + GetPrivateStopInfo(); + return m_stop_info_sp; + } +} + +void Thread::CalculatePublicStopInfo() { + ResetStopInfo(); + SetStopInfo(GetStopInfo()); +} + +lldb::StopInfoSP Thread::GetPrivateStopInfo() { + if (m_destroy_called) + return m_stop_info_sp; + + ProcessSP process_sp(GetProcess()); + if (process_sp) { + const uint32_t process_stop_id = process_sp->GetStopID(); + if (m_stop_info_stop_id != process_stop_id) { + if (m_stop_info_sp) { + if (m_stop_info_sp->IsValid() || IsStillAtLastBreakpointHit() || + GetCurrentPlan()->IsVirtualStep()) + SetStopInfo(m_stop_info_sp); + else + m_stop_info_sp.reset(); + } + + if (!m_stop_info_sp) { + if (!CalculateStopInfo()) + SetStopInfo(StopInfoSP()); + } + } + + // The stop info can be manually set by calling Thread::SetStopInfo() prior + // to this function ever getting called, so we can't rely on + // "m_stop_info_stop_id != process_stop_id" as the condition for the if + // statement below, we must also check the stop info to see if we need to + // override it. See the header documentation in + // Process::GetStopInfoOverrideCallback() for more information on the stop + // info override callback. + if (m_stop_info_override_stop_id != process_stop_id) { + m_stop_info_override_stop_id = process_stop_id; + if (m_stop_info_sp) { + if (const Architecture *arch = + process_sp->GetTarget().GetArchitecturePlugin()) + arch->OverrideStopInfo(*this); + } + } + } + return m_stop_info_sp; +} + +lldb::StopReason Thread::GetStopReason() { + lldb::StopInfoSP stop_info_sp(GetStopInfo()); + if (stop_info_sp) + return stop_info_sp->GetStopReason(); + return eStopReasonNone; +} + +bool Thread::StopInfoIsUpToDate() const { + ProcessSP process_sp(GetProcess()); + if (process_sp) + return m_stop_info_stop_id == process_sp->GetStopID(); + else + return true; // Process is no longer around so stop info is always up to + // date... +} + +void Thread::ResetStopInfo() { + if (m_stop_info_sp) { + m_stop_info_sp.reset(); + } +} + +void Thread::SetStopInfo(const lldb::StopInfoSP &stop_info_sp) { + m_stop_info_sp = stop_info_sp; + if (m_stop_info_sp) { + m_stop_info_sp->MakeStopInfoValid(); + // If we are overriding the ShouldReportStop, do that here: + if (m_override_should_notify != eLazyBoolCalculate) + m_stop_info_sp->OverrideShouldNotify(m_override_should_notify == + eLazyBoolYes); + } + + ProcessSP process_sp(GetProcess()); + if (process_sp) + m_stop_info_stop_id = process_sp->GetStopID(); + else + m_stop_info_stop_id = UINT32_MAX; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "%p: tid = 0x%" PRIx64 ": stop info = %s (stop_id = %u)", + static_cast<void *>(this), GetID(), + stop_info_sp ? stop_info_sp->GetDescription() : "<NULL>", + m_stop_info_stop_id); +} + +void Thread::SetShouldReportStop(Vote vote) { + if (vote == eVoteNoOpinion) + return; + else { + m_override_should_notify = (vote == eVoteYes ? eLazyBoolYes : eLazyBoolNo); + if (m_stop_info_sp) + m_stop_info_sp->OverrideShouldNotify(m_override_should_notify == + eLazyBoolYes); + } +} + +void Thread::SetStopInfoToNothing() { + // Note, we can't just NULL out the private reason, or the native thread + // implementation will try to go calculate it again. For now, just set it to + // a Unix Signal with an invalid signal number. + SetStopInfo( + StopInfo::CreateStopReasonWithSignal(*this, LLDB_INVALID_SIGNAL_NUMBER)); +} + +bool Thread::ThreadStoppedForAReason(void) { + return (bool)GetPrivateStopInfo(); +} + +bool Thread::CheckpointThreadState(ThreadStateCheckpoint &saved_state) { + saved_state.register_backup_sp.reset(); + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex(0)); + if (frame_sp) { + lldb::RegisterCheckpointSP reg_checkpoint_sp( + new RegisterCheckpoint(RegisterCheckpoint::Reason::eExpression)); + if (reg_checkpoint_sp) { + lldb::RegisterContextSP reg_ctx_sp(frame_sp->GetRegisterContext()); + if (reg_ctx_sp && reg_ctx_sp->ReadAllRegisterValues(*reg_checkpoint_sp)) + saved_state.register_backup_sp = reg_checkpoint_sp; + } + } + if (!saved_state.register_backup_sp) + return false; + + saved_state.stop_info_sp = GetStopInfo(); + ProcessSP process_sp(GetProcess()); + if (process_sp) + saved_state.orig_stop_id = process_sp->GetStopID(); + saved_state.current_inlined_depth = GetCurrentInlinedDepth(); + saved_state.m_completed_plan_stack = m_completed_plan_stack; + + return true; +} + +bool Thread::RestoreRegisterStateFromCheckpoint( + ThreadStateCheckpoint &saved_state) { + if (saved_state.register_backup_sp) { + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex(0)); + if (frame_sp) { + lldb::RegisterContextSP reg_ctx_sp(frame_sp->GetRegisterContext()); + if (reg_ctx_sp) { + bool ret = + reg_ctx_sp->WriteAllRegisterValues(*saved_state.register_backup_sp); + + // Clear out all stack frames as our world just changed. + ClearStackFrames(); + reg_ctx_sp->InvalidateIfNeeded(true); + if (m_unwinder_up) + m_unwinder_up->Clear(); + return ret; + } + } + } + return false; +} + +bool Thread::RestoreThreadStateFromCheckpoint( + ThreadStateCheckpoint &saved_state) { + if (saved_state.stop_info_sp) + saved_state.stop_info_sp->MakeStopInfoValid(); + SetStopInfo(saved_state.stop_info_sp); + GetStackFrameList()->SetCurrentInlinedDepth( + saved_state.current_inlined_depth); + m_completed_plan_stack = saved_state.m_completed_plan_stack; + return true; +} + +StateType Thread::GetState() const { + // If any other threads access this we will need a mutex for it + std::lock_guard<std::recursive_mutex> guard(m_state_mutex); + return m_state; +} + +void Thread::SetState(StateType state) { + std::lock_guard<std::recursive_mutex> guard(m_state_mutex); + m_state = state; +} + +void Thread::WillStop() { + ThreadPlan *current_plan = GetCurrentPlan(); + + // FIXME: I may decide to disallow threads with no plans. In which + // case this should go to an assert. + + if (!current_plan) + return; + + current_plan->WillStop(); +} + +void Thread::SetupForResume() { + if (GetResumeState() != eStateSuspended) { + // If we're at a breakpoint push the step-over breakpoint plan. Do this + // before telling the current plan it will resume, since we might change + // what the current plan is. + + lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext()); + if (reg_ctx_sp) { + const addr_t thread_pc = reg_ctx_sp->GetPC(); + BreakpointSiteSP bp_site_sp = + GetProcess()->GetBreakpointSiteList().FindByAddress(thread_pc); + if (bp_site_sp) { + // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the + // target may not require anything special to step over a breakpoint. + + ThreadPlan *cur_plan = GetCurrentPlan(); + + bool push_step_over_bp_plan = false; + if (cur_plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) { + ThreadPlanStepOverBreakpoint *bp_plan = + (ThreadPlanStepOverBreakpoint *)cur_plan; + if (bp_plan->GetBreakpointLoadAddress() != thread_pc) + push_step_over_bp_plan = true; + } else + push_step_over_bp_plan = true; + + if (push_step_over_bp_plan) { + ThreadPlanSP step_bp_plan_sp(new ThreadPlanStepOverBreakpoint(*this)); + if (step_bp_plan_sp) { + step_bp_plan_sp->SetPrivate(true); + + if (GetCurrentPlan()->RunState() != eStateStepping) { + ThreadPlanStepOverBreakpoint *step_bp_plan = + static_cast<ThreadPlanStepOverBreakpoint *>( + step_bp_plan_sp.get()); + step_bp_plan->SetAutoContinue(true); + } + QueueThreadPlan(step_bp_plan_sp, false); + } + } + } + } + } +} + +bool Thread::ShouldResume(StateType resume_state) { + // At this point clear the completed plan stack. + m_completed_plan_stack.clear(); + m_discarded_plan_stack.clear(); + m_override_should_notify = eLazyBoolCalculate; + + StateType prev_resume_state = GetTemporaryResumeState(); + + SetTemporaryResumeState(resume_state); + + lldb::ThreadSP backing_thread_sp(GetBackingThread()); + if (backing_thread_sp) + backing_thread_sp->SetTemporaryResumeState(resume_state); + + // Make sure m_stop_info_sp is valid. Don't do this for threads we suspended + // in the previous run. + if (prev_resume_state != eStateSuspended) + GetPrivateStopInfo(); + + // This is a little dubious, but we are trying to limit how often we actually + // fetch stop info from the target, 'cause that slows down single stepping. + // So assume that if we got to the point where we're about to resume, and we + // haven't yet had to fetch the stop reason, then it doesn't need to know + // about the fact that we are resuming... + const uint32_t process_stop_id = GetProcess()->GetStopID(); + if (m_stop_info_stop_id == process_stop_id && + (m_stop_info_sp && m_stop_info_sp->IsValid())) { + StopInfo *stop_info = GetPrivateStopInfo().get(); + if (stop_info) + stop_info->WillResume(resume_state); + } + + // Tell all the plans that we are about to resume in case they need to clear + // any state. We distinguish between the plan on the top of the stack and the + // lower plans in case a plan needs to do any special business before it + // runs. + + bool need_to_resume = false; + ThreadPlan *plan_ptr = GetCurrentPlan(); + if (plan_ptr) { + need_to_resume = plan_ptr->WillResume(resume_state, true); + + while ((plan_ptr = GetPreviousPlan(plan_ptr)) != nullptr) { + plan_ptr->WillResume(resume_state, false); + } + + // If the WillResume for the plan says we are faking a resume, then it will + // have set an appropriate stop info. In that case, don't reset it here. + + if (need_to_resume && resume_state != eStateSuspended) { + m_stop_info_sp.reset(); + } + } + + if (need_to_resume) { + ClearStackFrames(); + // Let Thread subclasses do any special work they need to prior to resuming + WillResume(resume_state); + } + + return need_to_resume; +} + +void Thread::DidResume() { SetResumeSignal(LLDB_INVALID_SIGNAL_NUMBER); } + +void Thread::DidStop() { SetState(eStateStopped); } + +bool Thread::ShouldStop(Event *event_ptr) { + ThreadPlan *current_plan = GetCurrentPlan(); + + bool should_stop = true; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (GetResumeState() == eStateSuspended) { + LLDB_LOGF(log, + "Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 + ", should_stop = 0 (ignore since thread was suspended)", + __FUNCTION__, GetID(), GetProtocolID()); + return false; + } + + if (GetTemporaryResumeState() == eStateSuspended) { + LLDB_LOGF(log, + "Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 + ", should_stop = 0 (ignore since thread was suspended)", + __FUNCTION__, GetID(), GetProtocolID()); + return false; + } + + // Based on the current thread plan and process stop info, check if this + // thread caused the process to stop. NOTE: this must take place before the + // plan is moved from the current plan stack to the completed plan stack. + if (!ThreadStoppedForAReason()) { + LLDB_LOGF(log, + "Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 + ", pc = 0x%16.16" PRIx64 + ", should_stop = 0 (ignore since no stop reason)", + __FUNCTION__, GetID(), GetProtocolID(), + GetRegisterContext() ? GetRegisterContext()->GetPC() + : LLDB_INVALID_ADDRESS); + return false; + } + + if (log) { + LLDB_LOGF(log, + "Thread::%s(%p) for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 + ", pc = 0x%16.16" PRIx64, + __FUNCTION__, static_cast<void *>(this), GetID(), GetProtocolID(), + GetRegisterContext() ? GetRegisterContext()->GetPC() + : LLDB_INVALID_ADDRESS); + LLDB_LOGF(log, "^^^^^^^^ Thread::ShouldStop Begin ^^^^^^^^"); + StreamString s; + s.IndentMore(); + DumpThreadPlans(&s); + LLDB_LOGF(log, "Plan stack initial state:\n%s", s.GetData()); + } + + // The top most plan always gets to do the trace log... + current_plan->DoTraceLog(); + + // First query the stop info's ShouldStopSynchronous. This handles + // "synchronous" stop reasons, for example the breakpoint command on internal + // breakpoints. If a synchronous stop reason says we should not stop, then + // we don't have to do any more work on this stop. + StopInfoSP private_stop_info(GetPrivateStopInfo()); + if (private_stop_info && + !private_stop_info->ShouldStopSynchronous(event_ptr)) { + LLDB_LOGF(log, "StopInfo::ShouldStop async callback says we should not " + "stop, returning ShouldStop of false."); + return false; + } + + // If we've already been restarted, don't query the plans since the state + // they would examine is not current. + if (Process::ProcessEventData::GetRestartedFromEvent(event_ptr)) + return false; + + // Before the plans see the state of the world, calculate the current inlined + // depth. + GetStackFrameList()->CalculateCurrentInlinedDepth(); + + // If the base plan doesn't understand why we stopped, then we have to find a + // plan that does. If that plan is still working, then we don't need to do + // any more work. If the plan that explains the stop is done, then we should + // pop all the plans below it, and pop it, and then let the plans above it + // decide whether they still need to do more work. + + bool done_processing_current_plan = false; + + if (!current_plan->PlanExplainsStop(event_ptr)) { + if (current_plan->TracerExplainsStop()) { + done_processing_current_plan = true; + should_stop = false; + } else { + // If the current plan doesn't explain the stop, then find one that does + // and let it handle the situation. + ThreadPlan *plan_ptr = current_plan; + while ((plan_ptr = GetPreviousPlan(plan_ptr)) != nullptr) { + if (plan_ptr->PlanExplainsStop(event_ptr)) { + should_stop = plan_ptr->ShouldStop(event_ptr); + + // plan_ptr explains the stop, next check whether plan_ptr is done, + // if so, then we should take it and all the plans below it off the + // stack. + + if (plan_ptr->MischiefManaged()) { + // We're going to pop the plans up to and including the plan that + // explains the stop. + ThreadPlan *prev_plan_ptr = GetPreviousPlan(plan_ptr); + + do { + if (should_stop) + current_plan->WillStop(); + PopPlan(); + } while ((current_plan = GetCurrentPlan()) != prev_plan_ptr); + // Now, if the responsible plan was not "Okay to discard" then + // we're done, otherwise we forward this to the next plan in the + // stack below. + done_processing_current_plan = + (plan_ptr->IsMasterPlan() && !plan_ptr->OkayToDiscard()); + } else + done_processing_current_plan = true; + + break; + } + } + } + } + + if (!done_processing_current_plan) { + bool over_ride_stop = current_plan->ShouldAutoContinue(event_ptr); + + LLDB_LOGF(log, "Plan %s explains stop, auto-continue %i.", + current_plan->GetName(), over_ride_stop); + + // We're starting from the base plan, so just let it decide; + if (PlanIsBasePlan(current_plan)) { + should_stop = current_plan->ShouldStop(event_ptr); + LLDB_LOGF(log, "Base plan says should stop: %i.", should_stop); + } else { + // Otherwise, don't let the base plan override what the other plans say + // to do, since presumably if there were other plans they would know what + // to do... + while (true) { + if (PlanIsBasePlan(current_plan)) + break; + + should_stop = current_plan->ShouldStop(event_ptr); + LLDB_LOGF(log, "Plan %s should stop: %d.", current_plan->GetName(), + should_stop); + if (current_plan->MischiefManaged()) { + if (should_stop) + current_plan->WillStop(); + + // If a Master Plan wants to stop, and wants to stick on the stack, + // we let it. Otherwise, see if the plan's parent wants to stop. + + if (should_stop && current_plan->IsMasterPlan() && + !current_plan->OkayToDiscard()) { + PopPlan(); + break; + } else { + PopPlan(); + + current_plan = GetCurrentPlan(); + if (current_plan == nullptr) { + break; + } + } + } else { + break; + } + } + } + + if (over_ride_stop) + should_stop = false; + } + + // One other potential problem is that we set up a master plan, then stop in + // before it is complete - for instance by hitting a breakpoint during a + // step-over - then do some step/finish/etc operations that wind up past the + // end point condition of the initial plan. We don't want to strand the + // original plan on the stack, This code clears stale plans off the stack. + + if (should_stop) { + ThreadPlan *plan_ptr = GetCurrentPlan(); + + // Discard the stale plans and all plans below them in the stack, plus move + // the completed plans to the completed plan stack + while (!PlanIsBasePlan(plan_ptr)) { + bool stale = plan_ptr->IsPlanStale(); + ThreadPlan *examined_plan = plan_ptr; + plan_ptr = GetPreviousPlan(examined_plan); + + if (stale) { + LLDB_LOGF( + log, + "Plan %s being discarded in cleanup, it says it is already done.", + examined_plan->GetName()); + while (GetCurrentPlan() != examined_plan) { + DiscardPlan(); + } + if (examined_plan->IsPlanComplete()) { + // plan is complete but does not explain the stop (example: step to a + // line with breakpoint), let us move the plan to + // completed_plan_stack anyway + PopPlan(); + } else + DiscardPlan(); + } + } + } + + if (log) { + StreamString s; + s.IndentMore(); + DumpThreadPlans(&s); + LLDB_LOGF(log, "Plan stack final state:\n%s", s.GetData()); + LLDB_LOGF(log, "vvvvvvvv Thread::ShouldStop End (returning %i) vvvvvvvv", + should_stop); + } + return should_stop; +} + +Vote Thread::ShouldReportStop(Event *event_ptr) { + StateType thread_state = GetResumeState(); + StateType temp_thread_state = GetTemporaryResumeState(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (thread_state == eStateSuspended || thread_state == eStateInvalid) { + LLDB_LOGF(log, + "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 + ": returning vote %i (state was suspended or invalid)", + GetID(), eVoteNoOpinion); + return eVoteNoOpinion; + } + + if (temp_thread_state == eStateSuspended || + temp_thread_state == eStateInvalid) { + LLDB_LOGF(log, + "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 + ": returning vote %i (temporary state was suspended or invalid)", + GetID(), eVoteNoOpinion); + return eVoteNoOpinion; + } + + if (!ThreadStoppedForAReason()) { + LLDB_LOGF(log, + "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 + ": returning vote %i (thread didn't stop for a reason.)", + GetID(), eVoteNoOpinion); + return eVoteNoOpinion; + } + + if (m_completed_plan_stack.size() > 0) { + // Don't use GetCompletedPlan here, since that suppresses private plans. + LLDB_LOGF(log, + "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 + ": returning vote for complete stack's back plan", + GetID()); + return m_completed_plan_stack.back()->ShouldReportStop(event_ptr); + } else { + Vote thread_vote = eVoteNoOpinion; + ThreadPlan *plan_ptr = GetCurrentPlan(); + while (true) { + if (plan_ptr->PlanExplainsStop(event_ptr)) { + thread_vote = plan_ptr->ShouldReportStop(event_ptr); + break; + } + if (PlanIsBasePlan(plan_ptr)) + break; + else + plan_ptr = GetPreviousPlan(plan_ptr); + } + LLDB_LOGF(log, + "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 + ": returning vote %i for current plan", + GetID(), thread_vote); + + return thread_vote; + } +} + +Vote Thread::ShouldReportRun(Event *event_ptr) { + StateType thread_state = GetResumeState(); + + if (thread_state == eStateSuspended || thread_state == eStateInvalid) { + return eVoteNoOpinion; + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (m_completed_plan_stack.size() > 0) { + // Don't use GetCompletedPlan here, since that suppresses private plans. + LLDB_LOGF(log, + "Current Plan for thread %d(%p) (0x%4.4" PRIx64 + ", %s): %s being asked whether we should report run.", + GetIndexID(), static_cast<void *>(this), GetID(), + StateAsCString(GetTemporaryResumeState()), + m_completed_plan_stack.back()->GetName()); + + return m_completed_plan_stack.back()->ShouldReportRun(event_ptr); + } else { + LLDB_LOGF(log, + "Current Plan for thread %d(%p) (0x%4.4" PRIx64 + ", %s): %s being asked whether we should report run.", + GetIndexID(), static_cast<void *>(this), GetID(), + StateAsCString(GetTemporaryResumeState()), + GetCurrentPlan()->GetName()); + + return GetCurrentPlan()->ShouldReportRun(event_ptr); + } +} + +bool Thread::MatchesSpec(const ThreadSpec *spec) { + return (spec == nullptr) ? true : spec->ThreadPassesBasicTests(*this); +} + +void Thread::PushPlan(ThreadPlanSP &thread_plan_sp) { + if (thread_plan_sp) { + // If the thread plan doesn't already have a tracer, give it its parent's + // tracer: + if (!thread_plan_sp->GetThreadPlanTracer()) { + assert(!m_plan_stack.empty()); + thread_plan_sp->SetThreadPlanTracer( + m_plan_stack.back()->GetThreadPlanTracer()); + } + m_plan_stack.push_back(thread_plan_sp); + + thread_plan_sp->DidPush(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull); + LLDB_LOGF(log, "Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".", + static_cast<void *>(this), s.GetData(), + thread_plan_sp->GetThread().GetID()); + } + } +} + +void Thread::PopPlan() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (m_plan_stack.size() <= 1) + return; + else { + ThreadPlanSP &plan = m_plan_stack.back(); + if (log) { + LLDB_LOGF(log, "Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".", + plan->GetName(), plan->GetThread().GetID()); + } + m_completed_plan_stack.push_back(plan); + plan->WillPop(); + m_plan_stack.pop_back(); + } +} + +void Thread::DiscardPlan() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (m_plan_stack.size() > 1) { + ThreadPlanSP &plan = m_plan_stack.back(); + LLDB_LOGF(log, "Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".", + plan->GetName(), plan->GetThread().GetID()); + + m_discarded_plan_stack.push_back(plan); + plan->WillPop(); + m_plan_stack.pop_back(); + } +} + +ThreadPlan *Thread::GetCurrentPlan() { + // There will always be at least the base plan. If somebody is mucking with + // a thread with an empty plan stack, we should assert right away. + return m_plan_stack.empty() ? nullptr : m_plan_stack.back().get(); +} + +ThreadPlanSP Thread::GetCompletedPlan() { + ThreadPlanSP empty_plan_sp; + if (!m_completed_plan_stack.empty()) { + for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { + ThreadPlanSP completed_plan_sp; + completed_plan_sp = m_completed_plan_stack[i]; + if (!completed_plan_sp->GetPrivate()) + return completed_plan_sp; + } + } + return empty_plan_sp; +} + +ValueObjectSP Thread::GetReturnValueObject() { + if (!m_completed_plan_stack.empty()) { + for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { + ValueObjectSP return_valobj_sp; + return_valobj_sp = m_completed_plan_stack[i]->GetReturnValueObject(); + if (return_valobj_sp) + return return_valobj_sp; + } + } + return ValueObjectSP(); +} + +ExpressionVariableSP Thread::GetExpressionVariable() { + if (!m_completed_plan_stack.empty()) { + for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { + ExpressionVariableSP expression_variable_sp; + expression_variable_sp = + m_completed_plan_stack[i]->GetExpressionVariable(); + if (expression_variable_sp) + return expression_variable_sp; + } + } + return ExpressionVariableSP(); +} + +bool Thread::IsThreadPlanDone(ThreadPlan *plan) { + if (!m_completed_plan_stack.empty()) { + for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { + if (m_completed_plan_stack[i].get() == plan) + return true; + } + } + return false; +} + +bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) { + if (!m_discarded_plan_stack.empty()) { + for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) { + if (m_discarded_plan_stack[i].get() == plan) + return true; + } + } + return false; +} + +bool Thread::CompletedPlanOverridesBreakpoint() { + return (!m_completed_plan_stack.empty()) ; +} + +ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) { + if (current_plan == nullptr) + return nullptr; + + int stack_size = m_completed_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) { + if (current_plan == m_completed_plan_stack[i].get()) + return m_completed_plan_stack[i - 1].get(); + } + + if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) { + return GetCurrentPlan(); + } + + stack_size = m_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) { + if (current_plan == m_plan_stack[i].get()) + return m_plan_stack[i - 1].get(); + } + return nullptr; +} + +Status Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp, + bool abort_other_plans) { + Status status; + StreamString s; + if (!thread_plan_sp->ValidatePlan(&s)) { + DiscardThreadPlansUpToPlan(thread_plan_sp); + thread_plan_sp.reset(); + status.SetErrorString(s.GetString()); + return status; + } + + if (abort_other_plans) + DiscardThreadPlans(true); + + PushPlan(thread_plan_sp); + + // This seems a little funny, but I don't want to have to split up the + // constructor and the DidPush in the scripted plan, that seems annoying. + // That means the constructor has to be in DidPush. So I have to validate the + // plan AFTER pushing it, and then take it off again... + if (!thread_plan_sp->ValidatePlan(&s)) { + DiscardThreadPlansUpToPlan(thread_plan_sp); + thread_plan_sp.reset(); + status.SetErrorString(s.GetString()); + return status; + } + + return status; +} + +void Thread::EnableTracer(bool value, bool single_stepping) { + int stack_size = m_plan_stack.size(); + for (int i = 0; i < stack_size; i++) { + if (m_plan_stack[i]->GetThreadPlanTracer()) { + m_plan_stack[i]->GetThreadPlanTracer()->EnableTracing(value); + m_plan_stack[i]->GetThreadPlanTracer()->EnableSingleStep(single_stepping); + } + } +} + +void Thread::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) { + int stack_size = m_plan_stack.size(); + for (int i = 0; i < stack_size; i++) + m_plan_stack[i]->SetThreadPlanTracer(tracer_sp); +} + +bool Thread::DiscardUserThreadPlansUpToIndex(uint32_t thread_index) { + // Count the user thread plans from the back end to get the number of the one + // we want to discard: + + uint32_t idx = 0; + ThreadPlan *up_to_plan_ptr = nullptr; + + for (ThreadPlanSP plan_sp : m_plan_stack) { + if (plan_sp->GetPrivate()) + continue; + if (idx == thread_index) { + up_to_plan_ptr = plan_sp.get(); + break; + } else + idx++; + } + + if (up_to_plan_ptr == nullptr) + return false; + + DiscardThreadPlansUpToPlan(up_to_plan_ptr); + return true; +} + +void Thread::DiscardThreadPlansUpToPlan(lldb::ThreadPlanSP &up_to_plan_sp) { + DiscardThreadPlansUpToPlan(up_to_plan_sp.get()); +} + +void Thread::DiscardThreadPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, + "Discarding thread plans for thread tid = 0x%4.4" PRIx64 + ", up to %p", + GetID(), static_cast<void *>(up_to_plan_ptr)); + + int stack_size = m_plan_stack.size(); + + // If the input plan is nullptr, discard all plans. Otherwise make sure this + // plan is in the stack, and if so discard up to and including it. + + if (up_to_plan_ptr == nullptr) { + for (int i = stack_size - 1; i > 0; i--) + DiscardPlan(); + } else { + bool found_it = false; + for (int i = stack_size - 1; i > 0; i--) { + if (m_plan_stack[i].get() == up_to_plan_ptr) + found_it = true; + } + if (found_it) { + bool last_one = false; + for (int i = stack_size - 1; i > 0 && !last_one; i--) { + if (GetCurrentPlan() == up_to_plan_ptr) + last_one = true; + DiscardPlan(); + } + } + } +} + +void Thread::DiscardThreadPlans(bool force) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) { + LLDB_LOGF(log, + "Discarding thread plans for thread (tid = 0x%4.4" PRIx64 + ", force %d)", + GetID(), force); + } + + if (force) { + int stack_size = m_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) { + DiscardPlan(); + } + return; + } + + while (true) { + int master_plan_idx; + bool discard = true; + + // Find the first master plan, see if it wants discarding, and if yes + // discard up to it. + for (master_plan_idx = m_plan_stack.size() - 1; master_plan_idx >= 0; + master_plan_idx--) { + if (m_plan_stack[master_plan_idx]->IsMasterPlan()) { + discard = m_plan_stack[master_plan_idx]->OkayToDiscard(); + break; + } + } + + if (discard) { + // First pop all the dependent plans: + for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--) { + // FIXME: Do we need a finalize here, or is the rule that + // "PrepareForStop" + // for the plan leaves it in a state that it is safe to pop the plan + // with no more notice? + DiscardPlan(); + } + + // Now discard the master plan itself. + // The bottom-most plan never gets discarded. "OkayToDiscard" for it + // means discard it's dependent plans, but not it... + if (master_plan_idx > 0) { + DiscardPlan(); + } + } else { + // If the master plan doesn't want to get discarded, then we're done. + break; + } + } +} + +bool Thread::PlanIsBasePlan(ThreadPlan *plan_ptr) { + if (plan_ptr->IsBasePlan()) + return true; + else if (m_plan_stack.size() == 0) + return false; + else + return m_plan_stack[0].get() == plan_ptr; +} + +Status Thread::UnwindInnermostExpression() { + Status error; + int stack_size = m_plan_stack.size(); + + // If the input plan is nullptr, discard all plans. Otherwise make sure this + // plan is in the stack, and if so discard up to and including it. + + for (int i = stack_size - 1; i > 0; i--) { + if (m_plan_stack[i]->GetKind() == ThreadPlan::eKindCallFunction) { + DiscardThreadPlansUpToPlan(m_plan_stack[i].get()); + return error; + } + } + error.SetErrorString("No expressions currently active on this thread"); + return error; +} + +ThreadPlanSP Thread::QueueFundamentalPlan(bool abort_other_plans) { + ThreadPlanSP thread_plan_sp(new ThreadPlanBase(*this)); + QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +ThreadPlanSP Thread::QueueThreadPlanForStepSingleInstruction( + bool step_over, bool abort_other_plans, bool stop_other_threads, + Status &status) { + ThreadPlanSP thread_plan_sp(new ThreadPlanStepInstruction( + *this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion)); + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( + bool abort_other_plans, const AddressRange &range, + const SymbolContext &addr_context, lldb::RunMode stop_other_threads, + Status &status, LazyBool step_out_avoids_code_withoug_debug_info) { + ThreadPlanSP thread_plan_sp; + thread_plan_sp = std::make_shared<ThreadPlanStepOverRange>( + *this, range, addr_context, stop_other_threads, + step_out_avoids_code_withoug_debug_info); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +// Call the QueueThreadPlanForStepOverRange method which takes an address +// range. +ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( + bool abort_other_plans, const LineEntry &line_entry, + const SymbolContext &addr_context, lldb::RunMode stop_other_threads, + Status &status, LazyBool step_out_avoids_code_withoug_debug_info) { + const bool include_inlined_functions = true; + auto address_range = + line_entry.GetSameLineContiguousAddressRange(include_inlined_functions); + return QueueThreadPlanForStepOverRange( + abort_other_plans, address_range, addr_context, stop_other_threads, + status, step_out_avoids_code_withoug_debug_info); +} + +ThreadPlanSP Thread::QueueThreadPlanForStepInRange( + bool abort_other_plans, const AddressRange &range, + const SymbolContext &addr_context, const char *step_in_target, + lldb::RunMode stop_other_threads, Status &status, + LazyBool step_in_avoids_code_without_debug_info, + LazyBool step_out_avoids_code_without_debug_info) { + ThreadPlanSP thread_plan_sp( + new ThreadPlanStepInRange(*this, range, addr_context, stop_other_threads, + step_in_avoids_code_without_debug_info, + step_out_avoids_code_without_debug_info)); + ThreadPlanStepInRange *plan = + static_cast<ThreadPlanStepInRange *>(thread_plan_sp.get()); + + if (step_in_target) + plan->SetStepInTarget(step_in_target); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +// Call the QueueThreadPlanForStepInRange method which takes an address range. +ThreadPlanSP Thread::QueueThreadPlanForStepInRange( + bool abort_other_plans, const LineEntry &line_entry, + const SymbolContext &addr_context, const char *step_in_target, + lldb::RunMode stop_other_threads, Status &status, + LazyBool step_in_avoids_code_without_debug_info, + LazyBool step_out_avoids_code_without_debug_info) { + const bool include_inlined_functions = false; + return QueueThreadPlanForStepInRange( + abort_other_plans, + line_entry.GetSameLineContiguousAddressRange(include_inlined_functions), + addr_context, step_in_target, stop_other_threads, status, + step_in_avoids_code_without_debug_info, + step_out_avoids_code_without_debug_info); +} + +ThreadPlanSP Thread::QueueThreadPlanForStepOut( + bool abort_other_plans, SymbolContext *addr_context, bool first_insn, + bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx, + Status &status, LazyBool step_out_avoids_code_without_debug_info) { + ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut( + *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, + frame_idx, step_out_avoids_code_without_debug_info)); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +ThreadPlanSP Thread::QueueThreadPlanForStepOutNoShouldStop( + bool abort_other_plans, SymbolContext *addr_context, bool first_insn, + bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx, + Status &status, bool continue_to_next_branch) { + const bool calculate_return_value = + false; // No need to calculate the return value here. + ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut( + *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, + frame_idx, eLazyBoolNo, continue_to_next_branch, calculate_return_value)); + + ThreadPlanStepOut *new_plan = + static_cast<ThreadPlanStepOut *>(thread_plan_sp.get()); + new_plan->ClearShouldStopHereCallbacks(); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +ThreadPlanSP Thread::QueueThreadPlanForStepThrough(StackID &return_stack_id, + bool abort_other_plans, + bool stop_other_threads, + Status &status) { + ThreadPlanSP thread_plan_sp( + new ThreadPlanStepThrough(*this, return_stack_id, stop_other_threads)); + if (!thread_plan_sp || !thread_plan_sp->ValidatePlan(nullptr)) + return ThreadPlanSP(); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +ThreadPlanSP Thread::QueueThreadPlanForRunToAddress(bool abort_other_plans, + Address &target_addr, + bool stop_other_threads, + Status &status) { + ThreadPlanSP thread_plan_sp( + new ThreadPlanRunToAddress(*this, target_addr, stop_other_threads)); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +ThreadPlanSP Thread::QueueThreadPlanForStepUntil( + bool abort_other_plans, lldb::addr_t *address_list, size_t num_addresses, + bool stop_other_threads, uint32_t frame_idx, Status &status) { + ThreadPlanSP thread_plan_sp(new ThreadPlanStepUntil( + *this, address_list, num_addresses, stop_other_threads, frame_idx)); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +lldb::ThreadPlanSP Thread::QueueThreadPlanForStepScripted( + bool abort_other_plans, const char *class_name, + StructuredData::ObjectSP extra_args_sp, bool stop_other_threads, + Status &status) { + + StructuredDataImpl *extra_args_impl = nullptr; + if (extra_args_sp) { + extra_args_impl = new StructuredDataImpl(); + extra_args_impl->SetObjectSP(extra_args_sp); + } + + ThreadPlanSP thread_plan_sp(new ThreadPlanPython(*this, class_name, + extra_args_impl)); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; +} + +uint32_t Thread::GetIndexID() const { return m_index_id; } + +static void PrintPlanElement(Stream *s, const ThreadPlanSP &plan, + lldb::DescriptionLevel desc_level, + int32_t elem_idx) { + s->IndentMore(); + s->Indent(); + s->Printf("Element %d: ", elem_idx); + plan->GetDescription(s, desc_level); + s->EOL(); + s->IndentLess(); +} + +static void PrintPlanStack(Stream *s, + const std::vector<lldb::ThreadPlanSP> &plan_stack, + lldb::DescriptionLevel desc_level, + bool include_internal) { + int32_t print_idx = 0; + for (ThreadPlanSP plan_sp : plan_stack) { + if (include_internal || !plan_sp->GetPrivate()) { + PrintPlanElement(s, plan_sp, desc_level, print_idx++); + } + } +} + +void Thread::DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level, + bool include_internal, + bool ignore_boring_threads) const { + uint32_t stack_size; + + if (ignore_boring_threads) { + uint32_t stack_size = m_plan_stack.size(); + uint32_t completed_stack_size = m_completed_plan_stack.size(); + uint32_t discarded_stack_size = m_discarded_plan_stack.size(); + if (stack_size == 1 && completed_stack_size == 0 && + discarded_stack_size == 0) { + s->Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", GetIndexID(), GetID()); + s->IndentMore(); + s->Indent(); + s->Printf("No active thread plans\n"); + s->IndentLess(); + return; + } + } + + s->Indent(); + s->Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID()); + s->IndentMore(); + s->Indent(); + s->Printf("Active plan stack:\n"); + PrintPlanStack(s, m_plan_stack, desc_level, include_internal); + + stack_size = m_completed_plan_stack.size(); + if (stack_size > 0) { + s->Indent(); + s->Printf("Completed Plan Stack:\n"); + PrintPlanStack(s, m_completed_plan_stack, desc_level, include_internal); + } + + stack_size = m_discarded_plan_stack.size(); + if (stack_size > 0) { + s->Indent(); + s->Printf("Discarded Plan Stack:\n"); + PrintPlanStack(s, m_discarded_plan_stack, desc_level, include_internal); + } + + s->IndentLess(); +} + +TargetSP Thread::CalculateTarget() { + TargetSP target_sp; + ProcessSP process_sp(GetProcess()); + if (process_sp) + target_sp = process_sp->CalculateTarget(); + return target_sp; +} + +ProcessSP Thread::CalculateProcess() { return GetProcess(); } + +ThreadSP Thread::CalculateThread() { return shared_from_this(); } + +StackFrameSP Thread::CalculateStackFrame() { return StackFrameSP(); } + +void Thread::CalculateExecutionContext(ExecutionContext &exe_ctx) { + exe_ctx.SetContext(shared_from_this()); +} + +StackFrameListSP Thread::GetStackFrameList() { + std::lock_guard<std::recursive_mutex> guard(m_frame_mutex); + + if (!m_curr_frames_sp) + m_curr_frames_sp = + std::make_shared<StackFrameList>(*this, m_prev_frames_sp, true); + + return m_curr_frames_sp; +} + +void Thread::ClearStackFrames() { + std::lock_guard<std::recursive_mutex> guard(m_frame_mutex); + + Unwind *unwinder = GetUnwinder(); + if (unwinder) + unwinder->Clear(); + + // Only store away the old "reference" StackFrameList if we got all its + // frames: + // FIXME: At some point we can try to splice in the frames we have fetched + // into + // the new frame as we make it, but let's not try that now. + if (m_curr_frames_sp && m_curr_frames_sp->GetAllFramesFetched()) + m_prev_frames_sp.swap(m_curr_frames_sp); + m_curr_frames_sp.reset(); + + m_extended_info.reset(); + m_extended_info_fetched = false; +} + +lldb::StackFrameSP Thread::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) { + return GetStackFrameList()->GetFrameWithConcreteFrameIndex(unwind_idx); +} + +Status Thread::ReturnFromFrameWithIndex(uint32_t frame_idx, + lldb::ValueObjectSP return_value_sp, + bool broadcast) { + StackFrameSP frame_sp = GetStackFrameAtIndex(frame_idx); + Status return_error; + + if (!frame_sp) { + return_error.SetErrorStringWithFormat( + "Could not find frame with index %d in thread 0x%" PRIx64 ".", + frame_idx, GetID()); + } + + return ReturnFromFrame(frame_sp, return_value_sp, broadcast); +} + +Status Thread::ReturnFromFrame(lldb::StackFrameSP frame_sp, + lldb::ValueObjectSP return_value_sp, + bool broadcast) { + Status return_error; + + if (!frame_sp) { + return_error.SetErrorString("Can't return to a null frame."); + return return_error; + } + + Thread *thread = frame_sp->GetThread().get(); + uint32_t older_frame_idx = frame_sp->GetFrameIndex() + 1; + StackFrameSP older_frame_sp = thread->GetStackFrameAtIndex(older_frame_idx); + if (!older_frame_sp) { + return_error.SetErrorString("No older frame to return to."); + return return_error; + } + + if (return_value_sp) { + lldb::ABISP abi = thread->GetProcess()->GetABI(); + if (!abi) { + return_error.SetErrorString("Could not find ABI to set return value."); + return return_error; + } + SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextFunction); + + // FIXME: ValueObject::Cast doesn't currently work correctly, at least not + // for scalars. + // Turn that back on when that works. + if (/* DISABLES CODE */ (false) && sc.function != nullptr) { + Type *function_type = sc.function->GetType(); + if (function_type) { + CompilerType return_type = + sc.function->GetCompilerType().GetFunctionReturnType(); + if (return_type) { + StreamString s; + return_type.DumpTypeDescription(&s); + ValueObjectSP cast_value_sp = return_value_sp->Cast(return_type); + if (cast_value_sp) { + cast_value_sp->SetFormat(eFormatHex); + return_value_sp = cast_value_sp; + } + } + } + } + + return_error = abi->SetReturnValueObject(older_frame_sp, return_value_sp); + if (!return_error.Success()) + return return_error; + } + + // Now write the return registers for the chosen frame: Note, we can't use + // ReadAllRegisterValues->WriteAllRegisterValues, since the read & write cook + // their data + + StackFrameSP youngest_frame_sp = thread->GetStackFrameAtIndex(0); + if (youngest_frame_sp) { + lldb::RegisterContextSP reg_ctx_sp(youngest_frame_sp->GetRegisterContext()); + if (reg_ctx_sp) { + bool copy_success = reg_ctx_sp->CopyFromRegisterContext( + older_frame_sp->GetRegisterContext()); + if (copy_success) { + thread->DiscardThreadPlans(true); + thread->ClearStackFrames(); + if (broadcast && EventTypeHasListeners(eBroadcastBitStackChanged)) + BroadcastEvent(eBroadcastBitStackChanged, + new ThreadEventData(this->shared_from_this())); + } else { + return_error.SetErrorString("Could not reset register values."); + } + } else { + return_error.SetErrorString("Frame has no register context."); + } + } else { + return_error.SetErrorString("Returned past top frame."); + } + return return_error; +} + +static void DumpAddressList(Stream &s, const std::vector<Address> &list, + ExecutionContextScope *exe_scope) { + for (size_t n = 0; n < list.size(); n++) { + s << "\t"; + list[n].Dump(&s, exe_scope, Address::DumpStyleResolvedDescription, + Address::DumpStyleSectionNameOffset); + s << "\n"; + } +} + +Status Thread::JumpToLine(const FileSpec &file, uint32_t line, + bool can_leave_function, std::string *warnings) { + ExecutionContext exe_ctx(GetStackFrameAtIndex(0)); + Target *target = exe_ctx.GetTargetPtr(); + TargetSP target_sp = exe_ctx.GetTargetSP(); + RegisterContext *reg_ctx = exe_ctx.GetRegisterContext(); + StackFrame *frame = exe_ctx.GetFramePtr(); + const SymbolContext &sc = frame->GetSymbolContext(eSymbolContextFunction); + + // Find candidate locations. + std::vector<Address> candidates, within_function, outside_function; + target->GetImages().FindAddressesForLine(target_sp, file, line, sc.function, + within_function, outside_function); + + // If possible, we try and stay within the current function. Within a + // function, we accept multiple locations (optimized code may do this, + // there's no solution here so we do the best we can). However if we're + // trying to leave the function, we don't know how to pick the right + // location, so if there's more than one then we bail. + if (!within_function.empty()) + candidates = within_function; + else if (outside_function.size() == 1 && can_leave_function) + candidates = outside_function; + + // Check if we got anything. + if (candidates.empty()) { + if (outside_function.empty()) { + return Status("Cannot locate an address for %s:%i.", + file.GetFilename().AsCString(), line); + } else if (outside_function.size() == 1) { + return Status("%s:%i is outside the current function.", + file.GetFilename().AsCString(), line); + } else { + StreamString sstr; + DumpAddressList(sstr, outside_function, target); + return Status("%s:%i has multiple candidate locations:\n%s", + file.GetFilename().AsCString(), line, sstr.GetData()); + } + } + + // Accept the first location, warn about any others. + Address dest = candidates[0]; + if (warnings && candidates.size() > 1) { + StreamString sstr; + sstr.Printf("%s:%i appears multiple times in this function, selecting the " + "first location:\n", + file.GetFilename().AsCString(), line); + DumpAddressList(sstr, candidates, target); + *warnings = sstr.GetString(); + } + + if (!reg_ctx->SetPC(dest)) + return Status("Cannot change PC to target address."); + + return Status(); +} + +void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx, + bool stop_format) { + ExecutionContext exe_ctx(shared_from_this()); + Process *process = exe_ctx.GetProcessPtr(); + if (process == nullptr) + return; + + StackFrameSP frame_sp; + SymbolContext frame_sc; + if (frame_idx != LLDB_INVALID_FRAME_ID) { + frame_sp = GetStackFrameAtIndex(frame_idx); + if (frame_sp) { + exe_ctx.SetFrameSP(frame_sp); + frame_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); + } + } + + const FormatEntity::Entry *thread_format; + if (stop_format) + thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat(); + else + thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadFormat(); + + assert(thread_format); + + FormatEntity::Format(*thread_format, strm, frame_sp ? &frame_sc : nullptr, + &exe_ctx, nullptr, nullptr, false, false); +} + +void Thread::SettingsInitialize() {} + +void Thread::SettingsTerminate() {} + +lldb::addr_t Thread::GetThreadPointer() { return LLDB_INVALID_ADDRESS; } + +addr_t Thread::GetThreadLocalData(const ModuleSP module, + lldb::addr_t tls_file_addr) { + // The default implementation is to ask the dynamic loader for it. This can + // be overridden for specific platforms. + DynamicLoader *loader = GetProcess()->GetDynamicLoader(); + if (loader) + return loader->GetThreadLocalData(module, shared_from_this(), + tls_file_addr); + else + return LLDB_INVALID_ADDRESS; +} + +bool Thread::SafeToCallFunctions() { + Process *process = GetProcess().get(); + if (process) { + SystemRuntime *runtime = process->GetSystemRuntime(); + if (runtime) { + return runtime->SafeToCallFunctionsOnThisThread(shared_from_this()); + } + } + return true; +} + +lldb::StackFrameSP +Thread::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) { + return GetStackFrameList()->GetStackFrameSPForStackFramePtr(stack_frame_ptr); +} + +const char *Thread::StopReasonAsCString(lldb::StopReason reason) { + switch (reason) { + case eStopReasonInvalid: + return "invalid"; + case eStopReasonNone: + return "none"; + case eStopReasonTrace: + return "trace"; + case eStopReasonBreakpoint: + return "breakpoint"; + case eStopReasonWatchpoint: + return "watchpoint"; + case eStopReasonSignal: + return "signal"; + case eStopReasonException: + return "exception"; + case eStopReasonExec: + return "exec"; + case eStopReasonPlanComplete: + return "plan complete"; + case eStopReasonThreadExiting: + return "thread exiting"; + case eStopReasonInstrumentation: + return "instrumentation break"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof(unknown_state_string), + "StopReason = %i", reason); + return unknown_state_string; +} + +const char *Thread::RunModeAsCString(lldb::RunMode mode) { + switch (mode) { + case eOnlyThisThread: + return "only this thread"; + case eAllThreads: + return "all threads"; + case eOnlyDuringStepping: + return "only during stepping"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof(unknown_state_string), "RunMode = %i", + mode); + return unknown_state_string; +} + +size_t Thread::GetStatus(Stream &strm, uint32_t start_frame, + uint32_t num_frames, uint32_t num_frames_with_source, + bool stop_format, bool only_stacks) { + + if (!only_stacks) { + ExecutionContext exe_ctx(shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + strm.Indent(); + bool is_selected = false; + if (process) { + if (process->GetThreadList().GetSelectedThread().get() == this) + is_selected = true; + } + strm.Printf("%c ", is_selected ? '*' : ' '); + if (target && target->GetDebugger().GetUseExternalEditor()) { + StackFrameSP frame_sp = GetStackFrameAtIndex(start_frame); + if (frame_sp) { + SymbolContext frame_sc( + frame_sp->GetSymbolContext(eSymbolContextLineEntry)); + if (frame_sc.line_entry.line != 0 && frame_sc.line_entry.file) { + Host::OpenFileInExternalEditor(frame_sc.line_entry.file, + frame_sc.line_entry.line); + } + } + } + + DumpUsingSettingsFormat(strm, start_frame, stop_format); + } + + size_t num_frames_shown = 0; + if (num_frames > 0) { + strm.IndentMore(); + + const bool show_frame_info = true; + const bool show_frame_unique = only_stacks; + const char *selected_frame_marker = nullptr; + if (num_frames == 1 || only_stacks || + (GetID() != GetProcess()->GetThreadList().GetSelectedThread()->GetID())) + strm.IndentMore(); + else + selected_frame_marker = "* "; + + num_frames_shown = GetStackFrameList()->GetStatus( + strm, start_frame, num_frames, show_frame_info, num_frames_with_source, + show_frame_unique, selected_frame_marker); + if (num_frames == 1) + strm.IndentLess(); + strm.IndentLess(); + } + return num_frames_shown; +} + +bool Thread::GetDescription(Stream &strm, lldb::DescriptionLevel level, + bool print_json_thread, bool print_json_stopinfo) { + const bool stop_format = false; + DumpUsingSettingsFormat(strm, 0, stop_format); + strm.Printf("\n"); + + StructuredData::ObjectSP thread_info = GetExtendedInfo(); + + if (print_json_thread || print_json_stopinfo) { + if (thread_info && print_json_thread) { + thread_info->Dump(strm); + strm.Printf("\n"); + } + + if (print_json_stopinfo && m_stop_info_sp) { + StructuredData::ObjectSP stop_info = m_stop_info_sp->GetExtendedInfo(); + if (stop_info) { + stop_info->Dump(strm); + strm.Printf("\n"); + } + } + + return true; + } + + if (thread_info) { + StructuredData::ObjectSP activity = + thread_info->GetObjectForDotSeparatedPath("activity"); + StructuredData::ObjectSP breadcrumb = + thread_info->GetObjectForDotSeparatedPath("breadcrumb"); + StructuredData::ObjectSP messages = + thread_info->GetObjectForDotSeparatedPath("trace_messages"); + + bool printed_activity = false; + if (activity && activity->GetType() == eStructuredDataTypeDictionary) { + StructuredData::Dictionary *activity_dict = activity->GetAsDictionary(); + StructuredData::ObjectSP id = activity_dict->GetValueForKey("id"); + StructuredData::ObjectSP name = activity_dict->GetValueForKey("name"); + if (name && name->GetType() == eStructuredDataTypeString && id && + id->GetType() == eStructuredDataTypeInteger) { + strm.Format(" Activity '{0}', {1:x}\n", + name->GetAsString()->GetValue(), + id->GetAsInteger()->GetValue()); + } + printed_activity = true; + } + bool printed_breadcrumb = false; + if (breadcrumb && breadcrumb->GetType() == eStructuredDataTypeDictionary) { + if (printed_activity) + strm.Printf("\n"); + StructuredData::Dictionary *breadcrumb_dict = + breadcrumb->GetAsDictionary(); + StructuredData::ObjectSP breadcrumb_text = + breadcrumb_dict->GetValueForKey("name"); + if (breadcrumb_text && + breadcrumb_text->GetType() == eStructuredDataTypeString) { + strm.Format(" Current Breadcrumb: {0}\n", + breadcrumb_text->GetAsString()->GetValue()); + } + printed_breadcrumb = true; + } + if (messages && messages->GetType() == eStructuredDataTypeArray) { + if (printed_breadcrumb) + strm.Printf("\n"); + StructuredData::Array *messages_array = messages->GetAsArray(); + const size_t msg_count = messages_array->GetSize(); + if (msg_count > 0) { + strm.Printf(" %zu trace messages:\n", msg_count); + for (size_t i = 0; i < msg_count; i++) { + StructuredData::ObjectSP message = messages_array->GetItemAtIndex(i); + if (message && message->GetType() == eStructuredDataTypeDictionary) { + StructuredData::Dictionary *message_dict = + message->GetAsDictionary(); + StructuredData::ObjectSP message_text = + message_dict->GetValueForKey("message"); + if (message_text && + message_text->GetType() == eStructuredDataTypeString) { + strm.Format(" {0}\n", message_text->GetAsString()->GetValue()); + } + } + } + } + } + } + + return true; +} + +size_t Thread::GetStackFrameStatus(Stream &strm, uint32_t first_frame, + uint32_t num_frames, bool show_frame_info, + uint32_t num_frames_with_source) { + return GetStackFrameList()->GetStatus( + strm, first_frame, num_frames, show_frame_info, num_frames_with_source); +} + +Unwind *Thread::GetUnwinder() { + if (!m_unwinder_up) { + const ArchSpec target_arch(CalculateTarget()->GetArchitecture()); + const llvm::Triple::ArchType machine = target_arch.GetMachine(); + switch (machine) { + case llvm::Triple::x86_64: + case llvm::Triple::x86: + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + case llvm::Triple::thumb: + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: + case llvm::Triple::systemz: + case llvm::Triple::hexagon: + case llvm::Triple::arc: + m_unwinder_up.reset(new UnwindLLDB(*this)); + break; + + default: + if (target_arch.GetTriple().getVendor() == llvm::Triple::Apple) + m_unwinder_up.reset(new UnwindMacOSXFrameBackchain(*this)); + break; + } + } + return m_unwinder_up.get(); +} + +void Thread::Flush() { + ClearStackFrames(); + m_reg_context_sp.reset(); +} + +bool Thread::IsStillAtLastBreakpointHit() { + // If we are currently stopped at a breakpoint, always return that stopinfo + // and don't reset it. This allows threads to maintain their breakpoint + // stopinfo, such as when thread-stepping in multithreaded programs. + if (m_stop_info_sp) { + StopReason stop_reason = m_stop_info_sp->GetStopReason(); + if (stop_reason == lldb::eStopReasonBreakpoint) { + uint64_t value = m_stop_info_sp->GetValue(); + lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext()); + if (reg_ctx_sp) { + lldb::addr_t pc = reg_ctx_sp->GetPC(); + BreakpointSiteSP bp_site_sp = + GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp && static_cast<break_id_t>(value) == bp_site_sp->GetID()) + return true; + } + } + } + return false; +} + +Status Thread::StepIn(bool source_step, + LazyBool step_in_avoids_code_without_debug_info, + LazyBool step_out_avoids_code_without_debug_info) + +{ + Status error; + Process *process = GetProcess().get(); + if (StateIsStoppedState(process->GetState(), true)) { + StackFrameSP frame_sp = GetStackFrameAtIndex(0); + ThreadPlanSP new_plan_sp; + const lldb::RunMode run_mode = eOnlyThisThread; + const bool abort_other_plans = false; + + if (source_step && frame_sp && frame_sp->HasDebugInformation()) { + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + new_plan_sp = QueueThreadPlanForStepInRange( + abort_other_plans, sc.line_entry, sc, nullptr, run_mode, error, + step_in_avoids_code_without_debug_info, + step_out_avoids_code_without_debug_info); + } else { + new_plan_sp = QueueThreadPlanForStepSingleInstruction( + false, abort_other_plans, run_mode, error); + } + + new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetOkayToDiscard(false); + + // Why do we need to set the current thread by ID here??? + process->GetThreadList().SetSelectedThreadByID(GetID()); + error = process->Resume(); + } else { + error.SetErrorString("process not stopped"); + } + return error; +} + +Status Thread::StepOver(bool source_step, + LazyBool step_out_avoids_code_without_debug_info) { + Status error; + Process *process = GetProcess().get(); + if (StateIsStoppedState(process->GetState(), true)) { + StackFrameSP frame_sp = GetStackFrameAtIndex(0); + ThreadPlanSP new_plan_sp; + + const lldb::RunMode run_mode = eOnlyThisThread; + const bool abort_other_plans = false; + + if (source_step && frame_sp && frame_sp->HasDebugInformation()) { + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + new_plan_sp = QueueThreadPlanForStepOverRange( + abort_other_plans, sc.line_entry, sc, run_mode, error, + step_out_avoids_code_without_debug_info); + } else { + new_plan_sp = QueueThreadPlanForStepSingleInstruction( + true, abort_other_plans, run_mode, error); + } + + new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetOkayToDiscard(false); + + // Why do we need to set the current thread by ID here??? + process->GetThreadList().SetSelectedThreadByID(GetID()); + error = process->Resume(); + } else { + error.SetErrorString("process not stopped"); + } + return error; +} + +Status Thread::StepOut() { + Status error; + Process *process = GetProcess().get(); + if (StateIsStoppedState(process->GetState(), true)) { + const bool first_instruction = false; + const bool stop_other_threads = false; + const bool abort_other_plans = false; + + ThreadPlanSP new_plan_sp(QueueThreadPlanForStepOut( + abort_other_plans, nullptr, first_instruction, stop_other_threads, + eVoteYes, eVoteNoOpinion, 0, error)); + + new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetOkayToDiscard(false); + + // Why do we need to set the current thread by ID here??? + process->GetThreadList().SetSelectedThreadByID(GetID()); + error = process->Resume(); + } else { + error.SetErrorString("process not stopped"); + } + return error; +} + +ValueObjectSP Thread::GetCurrentException() { + if (auto frame_sp = GetStackFrameAtIndex(0)) + if (auto recognized_frame = frame_sp->GetRecognizedFrame()) + if (auto e = recognized_frame->GetExceptionObject()) + return e; + + // NOTE: Even though this behavior is generalized, only ObjC is actually + // supported at the moment. + for (LanguageRuntime *runtime : GetProcess()->GetLanguageRuntimes()) { + if (auto e = runtime->GetExceptionObjectForThread(shared_from_this())) + return e; + } + + return ValueObjectSP(); +} + +ThreadSP Thread::GetCurrentExceptionBacktrace() { + ValueObjectSP exception = GetCurrentException(); + if (!exception) + return ThreadSP(); + + // NOTE: Even though this behavior is generalized, only ObjC is actually + // supported at the moment. + for (LanguageRuntime *runtime : GetProcess()->GetLanguageRuntimes()) { + if (auto bt = runtime->GetBacktraceThreadFromException(exception)) + return bt; + } + + return ThreadSP(); +} diff --git a/gnu/llvm/lldb/source/Target/ThreadCollection.cpp b/gnu/llvm/lldb/source/Target/ThreadCollection.cpp new file mode 100644 index 00000000000..cf3c1e24299 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadCollection.cpp @@ -0,0 +1,65 @@ +//===-- ThreadCollection.cpp ------------------------------------*- C++ -*-===// +// +// 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 <stdlib.h> + +#include <algorithm> +#include <mutex> + +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadCollection.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadCollection::ThreadCollection() : m_threads(), m_mutex() {} + +ThreadCollection::ThreadCollection(collection threads) + : m_threads(threads), m_mutex() {} + +void ThreadCollection::AddThread(const ThreadSP &thread_sp) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + m_threads.push_back(thread_sp); +} + +void ThreadCollection::AddThreadSortedByIndexID(const ThreadSP &thread_sp) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + // Make sure we always keep the threads sorted by thread index ID + const uint32_t thread_index_id = thread_sp->GetIndexID(); + if (m_threads.empty() || m_threads.back()->GetIndexID() < thread_index_id) + m_threads.push_back(thread_sp); + else { + m_threads.insert( + std::upper_bound(m_threads.begin(), m_threads.end(), thread_sp, + [](const ThreadSP &lhs, const ThreadSP &rhs) -> bool { + return lhs->GetIndexID() < rhs->GetIndexID(); + }), + thread_sp); + } +} + +void ThreadCollection::InsertThread(const lldb::ThreadSP &thread_sp, + uint32_t idx) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + if (idx < m_threads.size()) + m_threads.insert(m_threads.begin() + idx, thread_sp); + else + m_threads.push_back(thread_sp); +} + +uint32_t ThreadCollection::GetSize() { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + return m_threads.size(); +} + +ThreadSP ThreadCollection::GetThreadAtIndex(uint32_t idx) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + ThreadSP thread_sp; + if (idx < m_threads.size()) + thread_sp = m_threads[idx]; + return thread_sp; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadList.cpp b/gnu/llvm/lldb/source/Target/ThreadList.cpp new file mode 100644 index 00000000000..183b39c2cfa --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadList.cpp @@ -0,0 +1,754 @@ +//===-- ThreadList.cpp ------------------------------------------*- C++ -*-===// +// +// 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 <stdlib.h> + +#include <algorithm> + +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadList::ThreadList(Process *process) + : ThreadCollection(), m_process(process), m_stop_id(0), + m_selected_tid(LLDB_INVALID_THREAD_ID) {} + +ThreadList::ThreadList(const ThreadList &rhs) + : ThreadCollection(), m_process(rhs.m_process), m_stop_id(rhs.m_stop_id), + m_selected_tid() { + // Use the assignment operator since it uses the mutex + *this = rhs; +} + +const ThreadList &ThreadList::operator=(const ThreadList &rhs) { + if (this != &rhs) { + // Lock both mutexes to make sure neither side changes anyone on us while + // the assignment occurs + std::lock(GetMutex(), rhs.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(GetMutex(), std::adopt_lock); + std::lock_guard<std::recursive_mutex> rhs_guard(rhs.GetMutex(), + std::adopt_lock); + + m_process = rhs.m_process; + m_stop_id = rhs.m_stop_id; + m_threads = rhs.m_threads; + m_selected_tid = rhs.m_selected_tid; + } + return *this; +} + +ThreadList::~ThreadList() { + // Clear the thread list. Clear will take the mutex lock which will ensure + // that if anyone is using the list they won't get it removed while using it. + Clear(); +} + +lldb::ThreadSP ThreadList::GetExpressionExecutionThread() { + if (m_expression_tid_stack.empty()) + return GetSelectedThread(); + ThreadSP expr_thread_sp = FindThreadByID(m_expression_tid_stack.back()); + if (expr_thread_sp) + return expr_thread_sp; + else + return GetSelectedThread(); +} + +void ThreadList::PushExpressionExecutionThread(lldb::tid_t tid) { + m_expression_tid_stack.push_back(tid); +} + +void ThreadList::PopExpressionExecutionThread(lldb::tid_t tid) { + assert(m_expression_tid_stack.back() == tid); + m_expression_tid_stack.pop_back(); +} + +uint32_t ThreadList::GetStopID() const { return m_stop_id; } + +void ThreadList::SetStopID(uint32_t stop_id) { m_stop_id = stop_id; } + +uint32_t ThreadList::GetSize(bool can_update) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + return m_threads.size(); +} + +ThreadSP ThreadList::GetThreadAtIndex(uint32_t idx, bool can_update) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + if (idx < m_threads.size()) + thread_sp = m_threads[idx]; + return thread_sp; +} + +ThreadSP ThreadList::FindThreadByID(lldb::tid_t tid, bool can_update) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) { + if (m_threads[idx]->GetID() == tid) { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +ThreadSP ThreadList::FindThreadByProtocolID(lldb::tid_t tid, bool can_update) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) { + if (m_threads[idx]->GetProtocolID() == tid) { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +ThreadSP ThreadList::RemoveThreadByID(lldb::tid_t tid, bool can_update) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) { + if (m_threads[idx]->GetID() == tid) { + thread_sp = m_threads[idx]; + m_threads.erase(m_threads.begin() + idx); + break; + } + } + return thread_sp; +} + +ThreadSP ThreadList::RemoveThreadByProtocolID(lldb::tid_t tid, + bool can_update) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) { + if (m_threads[idx]->GetProtocolID() == tid) { + thread_sp = m_threads[idx]; + m_threads.erase(m_threads.begin() + idx); + break; + } + } + return thread_sp; +} + +ThreadSP ThreadList::GetThreadSPForThreadPtr(Thread *thread_ptr) { + ThreadSP thread_sp; + if (thread_ptr) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) { + if (m_threads[idx].get() == thread_ptr) { + thread_sp = m_threads[idx]; + break; + } + } + } + return thread_sp; +} + +ThreadSP ThreadList::GetBackingThread(const ThreadSP &real_thread) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + ThreadSP thread_sp; + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) { + if (m_threads[idx]->GetBackingThread() == real_thread) { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +ThreadSP ThreadList::FindThreadByIndexID(uint32_t index_id, bool can_update) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) { + if (m_threads[idx]->GetIndexID() == index_id) { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +bool ThreadList::ShouldStop(Event *event_ptr) { + // Running events should never stop, obviously... + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + // The ShouldStop method of the threads can do a whole lot of work, figuring + // out whether the thread plan conditions are met. So we don't want to keep + // the ThreadList locked the whole time we are doing this. + // FIXME: It is possible that running code could cause new threads + // to be created. If that happens, we will miss asking them whether they + // should stop. This is not a big deal since we haven't had a chance to hang + // any interesting operations on those threads yet. + + collection threads_copy; + { + // Scope for locker + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + m_process->UpdateThreadListIfNeeded(); + for (lldb::ThreadSP thread_sp : m_threads) { + // This is an optimization... If we didn't let a thread run in between + // the previous stop and this one, we shouldn't have to consult it for + // ShouldStop. So just leave it off the list we are going to inspect. On + // Linux, if a thread-specific conditional breakpoint was hit, it won't + // necessarily be the thread that hit the breakpoint itself that + // evaluates the conditional expression, so the thread that hit the + // breakpoint could still be asked to stop, even though it hasn't been + // allowed to run since the previous stop. + if (thread_sp->GetTemporaryResumeState() != eStateSuspended || + thread_sp->IsStillAtLastBreakpointHit()) + threads_copy.push_back(thread_sp); + } + + // It is possible the threads we were allowing to run all exited and then + // maybe the user interrupted or something, then fall back on looking at + // all threads: + + if (threads_copy.size() == 0) + threads_copy = m_threads; + } + + collection::iterator pos, end = threads_copy.end(); + + if (log) { + log->PutCString(""); + LLDB_LOGF(log, + "ThreadList::%s: %" PRIu64 " threads, %" PRIu64 + " unsuspended threads", + __FUNCTION__, (uint64_t)m_threads.size(), + (uint64_t)threads_copy.size()); + } + + bool did_anybody_stop_for_a_reason = false; + + // If the event is an Interrupt event, then we're going to stop no matter + // what. Otherwise, presume we won't stop. + bool should_stop = false; + if (Process::ProcessEventData::GetInterruptedFromEvent(event_ptr)) { + LLDB_LOGF( + log, "ThreadList::%s handling interrupt event, should stop set to true", + __FUNCTION__); + + should_stop = true; + } + + // Now we run through all the threads and get their stop info's. We want to + // make sure to do this first before we start running the ShouldStop, because + // one thread's ShouldStop could destroy information (like deleting a thread + // specific breakpoint another thread had stopped at) which could lead us to + // compute the StopInfo incorrectly. We don't need to use it here, we just + // want to make sure it gets computed. + + for (pos = threads_copy.begin(); pos != end; ++pos) { + ThreadSP thread_sp(*pos); + thread_sp->GetStopInfo(); + } + + for (pos = threads_copy.begin(); pos != end; ++pos) { + ThreadSP thread_sp(*pos); + + // We should never get a stop for which no thread had a stop reason, but + // sometimes we do see this - for instance when we first connect to a + // remote stub. In that case we should stop, since we can't figure out the + // right thing to do and stopping gives the user control over what to do in + // this instance. + // + // Note, this causes a problem when you have a thread specific breakpoint, + // and a bunch of threads hit the breakpoint, but not the thread which we + // are waiting for. All the threads that are not "supposed" to hit the + // breakpoint are marked as having no stop reason, which is right, they + // should not show a stop reason. But that triggers this code and causes + // us to stop seemingly for no reason. + // + // Since the only way we ever saw this error was on first attach, I'm only + // going to trigger set did_anybody_stop_for_a_reason to true unless this + // is the first stop. + // + // If this becomes a problem, we'll have to have another StopReason like + // "StopInfoHidden" which will look invalid everywhere but at this check. + + if (thread_sp->GetProcess()->GetStopID() > 1) + did_anybody_stop_for_a_reason = true; + else + did_anybody_stop_for_a_reason |= thread_sp->ThreadStoppedForAReason(); + + const bool thread_should_stop = thread_sp->ShouldStop(event_ptr); + if (thread_should_stop) + should_stop |= true; + } + + if (!should_stop && !did_anybody_stop_for_a_reason) { + should_stop = true; + LLDB_LOGF(log, + "ThreadList::%s we stopped but no threads had a stop reason, " + "overriding should_stop and stopping.", + __FUNCTION__); + } + + LLDB_LOGF(log, "ThreadList::%s overall should_stop = %i", __FUNCTION__, + should_stop); + + if (should_stop) { + for (pos = threads_copy.begin(); pos != end; ++pos) { + ThreadSP thread_sp(*pos); + thread_sp->WillStop(); + } + } + + return should_stop; +} + +Vote ThreadList::ShouldReportStop(Event *event_ptr) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + LLDB_LOGF(log, "ThreadList::%s %" PRIu64 " threads", __FUNCTION__, + (uint64_t)m_threads.size()); + + // Run through the threads and ask whether we should report this event. For + // stopping, a YES vote wins over everything. A NO vote wins over NO + // opinion. + for (pos = m_threads.begin(); pos != end; ++pos) { + ThreadSP thread_sp(*pos); + const Vote vote = thread_sp->ShouldReportStop(event_ptr); + switch (vote) { + case eVoteNoOpinion: + continue; + + case eVoteYes: + result = eVoteYes; + break; + + case eVoteNo: + if (result == eVoteNoOpinion) { + result = eVoteNo; + } else { + LLDB_LOG(log, + "Thread {0:x} voted {1}, but lost out because result was {2}", + thread_sp->GetID(), vote, result); + } + break; + } + } + LLDB_LOG(log, "Returning {0}", result); + return result; +} + +void ThreadList::SetShouldReportStop(Vote vote) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) { + ThreadSP thread_sp(*pos); + thread_sp->SetShouldReportStop(vote); + } +} + +Vote ThreadList::ShouldReportRun(Event *event_ptr) { + + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should report this event. The + // rule is NO vote wins over everything, a YES vote wins over no opinion. + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + for (pos = m_threads.begin(); pos != end; ++pos) { + if ((*pos)->GetResumeState() != eStateSuspended) { + switch ((*pos)->ShouldReportRun(event_ptr)) { + case eVoteNoOpinion: + continue; + case eVoteYes: + if (result == eVoteNoOpinion) + result = eVoteYes; + break; + case eVoteNo: + LLDB_LOGF(log, + "ThreadList::ShouldReportRun() thread %d (0x%4.4" PRIx64 + ") says don't report.", + (*pos)->GetIndexID(), (*pos)->GetID()); + result = eVoteNo; + break; + } + } + } + return result; +} + +void ThreadList::Clear() { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + m_stop_id = 0; + m_threads.clear(); + m_selected_tid = LLDB_INVALID_THREAD_ID; +} + +void ThreadList::Destroy() { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) { + m_threads[idx]->DestroyThread(); + } +} + +void ThreadList::RefreshStateAfterStop() { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + m_process->UpdateThreadListIfNeeded(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + LLDB_LOGF(log, + "Turning off notification of new threads while single stepping " + "a thread."); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->RefreshStateAfterStop(); +} + +void ThreadList::DiscardThreadPlans() { + // You don't need to update the thread list here, because only threads that + // you currently know about have any thread plans. + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->DiscardThreadPlans(true); +} + +bool ThreadList::WillResume() { + // Run through the threads and perform their momentary actions. But we only + // do this for threads that are running, user suspended threads stay where + // they are. + + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + + // See if any thread wants to run stopping others. If it does, then we won't + // setup the other threads for resume, since they aren't going to get a + // chance to run. This is necessary because the SetupForResume might add + // "StopOthers" plans which would then get to be part of the who-gets-to-run + // negotiation, but they're coming in after the fact, and the threads that + // are already set up should take priority. + + bool wants_solo_run = false; + + for (pos = m_threads.begin(); pos != end; ++pos) { + lldbassert((*pos)->GetCurrentPlan() && + "thread should not have null thread plan"); + if ((*pos)->GetResumeState() != eStateSuspended && + (*pos)->GetCurrentPlan()->StopOthers()) { + if ((*pos)->IsOperatingSystemPluginThread() && + !(*pos)->GetBackingThread()) + continue; + wants_solo_run = true; + break; + } + } + + if (wants_solo_run) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + LLDB_LOGF(log, "Turning on notification of new threads while single " + "stepping a thread."); + m_process->StartNoticingNewThreads(); + } else { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + LLDB_LOGF(log, "Turning off notification of new threads while single " + "stepping a thread."); + m_process->StopNoticingNewThreads(); + } + + // Give all the threads that are likely to run a last chance to set up their + // state before we negotiate who is actually going to get a chance to run... + // Don't set to resume suspended threads, and if any thread wanted to stop + // others, only call setup on the threads that request StopOthers... + + for (pos = m_threads.begin(); pos != end; ++pos) { + if ((*pos)->GetResumeState() != eStateSuspended && + (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers())) { + if ((*pos)->IsOperatingSystemPluginThread() && + !(*pos)->GetBackingThread()) + continue; + (*pos)->SetupForResume(); + } + } + + // Now go through the threads and see if any thread wants to run just itself. + // if so then pick one and run it. + + ThreadList run_me_only_list(m_process); + + run_me_only_list.SetStopID(m_process->GetStopID()); + + bool run_only_current_thread = false; + + for (pos = m_threads.begin(); pos != end; ++pos) { + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState() != eStateSuspended && + thread_sp->GetCurrentPlan()->StopOthers()) { + if ((*pos)->IsOperatingSystemPluginThread() && + !(*pos)->GetBackingThread()) + continue; + + // You can't say "stop others" and also want yourself to be suspended. + assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended); + + if (thread_sp == GetSelectedThread()) { + // If the currently selected thread wants to run on its own, always let + // it. + run_only_current_thread = true; + run_me_only_list.Clear(); + run_me_only_list.AddThread(thread_sp); + break; + } + + run_me_only_list.AddThread(thread_sp); + } + } + + bool need_to_resume = true; + + if (run_me_only_list.GetSize(false) == 0) { + // Everybody runs as they wish: + for (pos = m_threads.begin(); pos != end; ++pos) { + ThreadSP thread_sp(*pos); + StateType run_state; + if (thread_sp->GetResumeState() != eStateSuspended) + run_state = thread_sp->GetCurrentPlan()->RunState(); + else + run_state = eStateSuspended; + if (!thread_sp->ShouldResume(run_state)) + need_to_resume = false; + } + } else { + ThreadSP thread_to_run; + + if (run_only_current_thread) { + thread_to_run = GetSelectedThread(); + } else if (run_me_only_list.GetSize(false) == 1) { + thread_to_run = run_me_only_list.GetThreadAtIndex(0); + } else { + int random_thread = + (int)((run_me_only_list.GetSize(false) * (double)rand()) / + (RAND_MAX + 1.0)); + thread_to_run = run_me_only_list.GetThreadAtIndex(random_thread); + } + + for (pos = m_threads.begin(); pos != end; ++pos) { + ThreadSP thread_sp(*pos); + if (thread_sp == thread_to_run) { + if (!thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState())) + need_to_resume = false; + } else + thread_sp->ShouldResume(eStateSuspended); + } + } + + return need_to_resume; +} + +void ThreadList::DidResume() { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) { + // Don't clear out threads that aren't going to get a chance to run, rather + // leave their state for the next time around. + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState() != eStateSuspended) + thread_sp->DidResume(); + } +} + +void ThreadList::DidStop() { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) { + // Notify threads that the process just stopped. Note, this currently + // assumes that all threads in the list stop when the process stops. In + // the future we will want to support a debugging model where some threads + // continue to run while others are stopped. We either need to handle that + // somehow here or create a special thread list containing only threads + // which will stop in the code that calls this method (currently + // Process::SetPrivateState). + ThreadSP thread_sp(*pos); + if (StateIsRunningState(thread_sp->GetState())) + thread_sp->DidStop(); + } +} + +ThreadSP ThreadList::GetSelectedThread() { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + ThreadSP thread_sp = FindThreadByID(m_selected_tid); + if (!thread_sp.get()) { + if (m_threads.size() == 0) + return thread_sp; + m_selected_tid = m_threads[0]->GetID(); + thread_sp = m_threads[0]; + } + return thread_sp; +} + +bool ThreadList::SetSelectedThreadByID(lldb::tid_t tid, bool notify) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + ThreadSP selected_thread_sp(FindThreadByID(tid)); + if (selected_thread_sp) { + m_selected_tid = tid; + selected_thread_sp->SetDefaultFileAndLineToSelectedFrame(); + } else + m_selected_tid = LLDB_INVALID_THREAD_ID; + + if (notify) + NotifySelectedThreadChanged(m_selected_tid); + + return m_selected_tid != LLDB_INVALID_THREAD_ID; +} + +bool ThreadList::SetSelectedThreadByIndexID(uint32_t index_id, bool notify) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + ThreadSP selected_thread_sp(FindThreadByIndexID(index_id)); + if (selected_thread_sp.get()) { + m_selected_tid = selected_thread_sp->GetID(); + selected_thread_sp->SetDefaultFileAndLineToSelectedFrame(); + } else + m_selected_tid = LLDB_INVALID_THREAD_ID; + + if (notify) + NotifySelectedThreadChanged(m_selected_tid); + + return m_selected_tid != LLDB_INVALID_THREAD_ID; +} + +void ThreadList::NotifySelectedThreadChanged(lldb::tid_t tid) { + ThreadSP selected_thread_sp(FindThreadByID(tid)); + if (selected_thread_sp->EventTypeHasListeners( + Thread::eBroadcastBitThreadSelected)) + selected_thread_sp->BroadcastEvent( + Thread::eBroadcastBitThreadSelected, + new Thread::ThreadEventData(selected_thread_sp)); +} + +void ThreadList::Update(ThreadList &rhs) { + if (this != &rhs) { + // Lock both mutexes to make sure neither side changes anyone on us while + // the assignment occurs + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + + m_process = rhs.m_process; + m_stop_id = rhs.m_stop_id; + m_threads.swap(rhs.m_threads); + m_selected_tid = rhs.m_selected_tid; + + // Now we look for threads that we are done with and make sure to clear + // them up as much as possible so anyone with a shared pointer will still + // have a reference, but the thread won't be of much use. Using + // std::weak_ptr for all backward references (such as a thread to a + // process) will eventually solve this issue for us, but for now, we need + // to work around the issue + collection::iterator rhs_pos, rhs_end = rhs.m_threads.end(); + for (rhs_pos = rhs.m_threads.begin(); rhs_pos != rhs_end; ++rhs_pos) { + const lldb::tid_t tid = (*rhs_pos)->GetID(); + bool thread_is_alive = false; + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) { + ThreadSP backing_thread = m_threads[idx]->GetBackingThread(); + if (m_threads[idx]->GetID() == tid || + (backing_thread && backing_thread->GetID() == tid)) { + thread_is_alive = true; + break; + } + } + if (!thread_is_alive) + (*rhs_pos)->DestroyThread(); + } + } +} + +void ThreadList::Flush() { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->Flush(); +} + +std::recursive_mutex &ThreadList::GetMutex() const { + return m_process->m_thread_mutex; +} + +ThreadList::ExpressionExecutionThreadPusher::ExpressionExecutionThreadPusher( + lldb::ThreadSP thread_sp) + : m_thread_list(nullptr), m_tid(LLDB_INVALID_THREAD_ID) { + if (thread_sp) { + m_tid = thread_sp->GetID(); + m_thread_list = &thread_sp->GetProcess()->GetThreadList(); + m_thread_list->PushExpressionExecutionThread(m_tid); + } +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlan.cpp b/gnu/llvm/lldb/source/Target/ThreadPlan.cpp new file mode 100644 index 00000000000..ba56f8d15d8 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlan.cpp @@ -0,0 +1,275 @@ +//===-- ThreadPlan.cpp ------------------------------------------*- C++ -*-===// +// +// 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/ThreadPlan.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlan constructor +ThreadPlan::ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread, + Vote stop_vote, Vote run_vote) + : m_thread(thread), m_stop_vote(stop_vote), m_run_vote(run_vote), + m_takes_iteration_count(false), m_could_not_resolve_hw_bp(false), + m_kind(kind), m_name(name), m_plan_complete_mutex(), + m_cached_plan_explains_stop(eLazyBoolCalculate), m_plan_complete(false), + m_plan_private(false), m_okay_to_discard(true), m_is_master_plan(false), + m_plan_succeeded(true) { + SetID(GetNextID()); +} + +// Destructor +ThreadPlan::~ThreadPlan() = default; + +bool ThreadPlan::PlanExplainsStop(Event *event_ptr) { + if (m_cached_plan_explains_stop == eLazyBoolCalculate) { + bool actual_value = DoPlanExplainsStop(event_ptr); + m_cached_plan_explains_stop = actual_value ? eLazyBoolYes : eLazyBoolNo; + return actual_value; + } else { + return m_cached_plan_explains_stop == eLazyBoolYes; + } +} + +bool ThreadPlan::IsPlanComplete() { + std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex); + return m_plan_complete; +} + +void ThreadPlan::SetPlanComplete(bool success) { + std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex); + m_plan_complete = true; + m_plan_succeeded = success; +} + +bool ThreadPlan::MischiefManaged() { + std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex); + // Mark the plan is complete, but don't override the success flag. + m_plan_complete = true; + return true; +} + +Vote ThreadPlan::ShouldReportStop(Event *event_ptr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (m_stop_vote == eVoteNoOpinion) { + ThreadPlan *prev_plan = GetPreviousPlan(); + if (prev_plan) { + Vote prev_vote = prev_plan->ShouldReportStop(event_ptr); + LLDB_LOG(log, "returning previous thread plan vote: {0}", prev_vote); + return prev_vote; + } + } + LLDB_LOG(log, "Returning vote: {0}", m_stop_vote); + return m_stop_vote; +} + +Vote ThreadPlan::ShouldReportRun(Event *event_ptr) { + if (m_run_vote == eVoteNoOpinion) { + ThreadPlan *prev_plan = GetPreviousPlan(); + if (prev_plan) + return prev_plan->ShouldReportRun(event_ptr); + } + return m_run_vote; +} + +bool ThreadPlan::StopOthers() { + ThreadPlan *prev_plan; + prev_plan = GetPreviousPlan(); + return (prev_plan == nullptr) ? false : prev_plan->StopOthers(); +} + +void ThreadPlan::SetStopOthers(bool new_value) { + // SetStopOthers doesn't work up the hierarchy. You have to set the explicit + // ThreadPlan you want to affect. +} + +bool ThreadPlan::WillResume(StateType resume_state, bool current_plan) { + m_cached_plan_explains_stop = eLazyBoolCalculate; + + if (current_plan) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (log) { + RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); + assert(reg_ctx); + addr_t pc = reg_ctx->GetPC(); + addr_t sp = reg_ctx->GetSP(); + addr_t fp = reg_ctx->GetFP(); + LLDB_LOGF( + log, + "%s Thread #%u (0x%p): tid = 0x%4.4" PRIx64 ", pc = 0x%8.8" PRIx64 + ", sp = 0x%8.8" PRIx64 ", fp = 0x%8.8" PRIx64 ", " + "plan = '%s', state = %s, stop others = %d", + __FUNCTION__, m_thread.GetIndexID(), static_cast<void *>(&m_thread), + m_thread.GetID(), static_cast<uint64_t>(pc), + static_cast<uint64_t>(sp), static_cast<uint64_t>(fp), m_name.c_str(), + StateAsCString(resume_state), StopOthers()); + } + } + return DoWillResume(resume_state, current_plan); +} + +lldb::user_id_t ThreadPlan::GetNextID() { + static uint32_t g_nextPlanID = 0; + return ++g_nextPlanID; +} + +void ThreadPlan::DidPush() {} + +void ThreadPlan::WillPop() {} + +bool ThreadPlan::OkayToDiscard() { + return IsMasterPlan() ? m_okay_to_discard : true; +} + +lldb::StateType ThreadPlan::RunState() { + if (m_tracer_sp && m_tracer_sp->TracingEnabled() && + m_tracer_sp->SingleStepEnabled()) + return eStateStepping; + else + return GetPlanRunState(); +} + +bool ThreadPlan::IsUsuallyUnexplainedStopReason(lldb::StopReason reason) { + switch (reason) { + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonThreadExiting: + case eStopReasonInstrumentation: + return true; + default: + return false; + } +} + +// ThreadPlanNull + +ThreadPlanNull::ThreadPlanNull(Thread &thread) + : ThreadPlan(ThreadPlan::eKindNull, "Null Thread Plan", thread, + eVoteNoOpinion, eVoteNoOpinion) {} + +ThreadPlanNull::~ThreadPlanNull() = default; + +void ThreadPlanNull::GetDescription(Stream *s, lldb::DescriptionLevel level) { + s->PutCString("Null thread plan - thread has been destroyed."); +} + +bool ThreadPlanNull::ValidatePlan(Stream *error) { +#ifdef LLDB_CONFIGURATION_DEBUG + fprintf(stderr, + "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID()); +#else + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), + m_thread.GetProtocolID()); +#endif + return true; +} + +bool ThreadPlanNull::ShouldStop(Event *event_ptr) { +#ifdef LLDB_CONFIGURATION_DEBUG + fprintf(stderr, + "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID()); +#else + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), + m_thread.GetProtocolID()); +#endif + return true; +} + +bool ThreadPlanNull::WillStop() { +#ifdef LLDB_CONFIGURATION_DEBUG + fprintf(stderr, + "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID()); +#else + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), + m_thread.GetProtocolID()); +#endif + return true; +} + +bool ThreadPlanNull::DoPlanExplainsStop(Event *event_ptr) { +#ifdef LLDB_CONFIGURATION_DEBUG + fprintf(stderr, + "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID()); +#else + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), + m_thread.GetProtocolID()); +#endif + return true; +} + +// The null plan is never done. +bool ThreadPlanNull::MischiefManaged() { +// The null plan is never done. +#ifdef LLDB_CONFIGURATION_DEBUG + fprintf(stderr, + "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID()); +#else + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), + m_thread.GetProtocolID()); +#endif + return false; +} + +lldb::StateType ThreadPlanNull::GetPlanRunState() { +// Not sure what to return here. This is a dead thread. +#ifdef LLDB_CONFIGURATION_DEBUG + fprintf(stderr, + "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID()); +#else + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 + ", ptid = 0x%" PRIx64 ")", + LLVM_PRETTY_FUNCTION, m_thread.GetID(), + m_thread.GetProtocolID()); +#endif + return eStateRunning; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanBase.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanBase.cpp new file mode 100644 index 00000000000..821643d4bce --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanBase.cpp @@ -0,0 +1,197 @@ +//===-- ThreadPlanBase.cpp --------------------------------------*- C++ -*-===// +// +// 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/ThreadPlanBase.h" + +// +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanBase: This one always stops, and never has anything particular to +// do. +// FIXME: The "signal handling" policies should probably go here. + +ThreadPlanBase::ThreadPlanBase(Thread &thread) + : ThreadPlan(ThreadPlan::eKindBase, "base plan", thread, eVoteYes, + eVoteNoOpinion) { +// Set the tracer to a default tracer. +// FIXME: need to add a thread settings variable to pix various tracers... +#define THREAD_PLAN_USE_ASSEMBLY_TRACER 1 + +#ifdef THREAD_PLAN_USE_ASSEMBLY_TRACER + ThreadPlanTracerSP new_tracer_sp(new ThreadPlanAssemblyTracer(m_thread)); +#else + ThreadPlanTracerSP new_tracer_sp(new ThreadPlanTracer(m_thread)); +#endif + new_tracer_sp->EnableTracing(m_thread.GetTraceEnabledState()); + SetThreadPlanTracer(new_tracer_sp); + SetIsMasterPlan(true); +} + +ThreadPlanBase::~ThreadPlanBase() {} + +void ThreadPlanBase::GetDescription(Stream *s, lldb::DescriptionLevel level) { + s->Printf("Base thread plan."); +} + +bool ThreadPlanBase::ValidatePlan(Stream *error) { return true; } + +bool ThreadPlanBase::DoPlanExplainsStop(Event *event_ptr) { + // The base plan should defer to its tracer, since by default it always + // handles the stop. + return !TracerExplainsStop(); +} + +Vote ThreadPlanBase::ShouldReportStop(Event *event_ptr) { + StopInfoSP stop_info_sp = m_thread.GetStopInfo(); + if (stop_info_sp) { + bool should_notify = stop_info_sp->ShouldNotify(event_ptr); + if (should_notify) + return eVoteYes; + else + return eVoteNoOpinion; + } else + return eVoteNoOpinion; +} + +bool ThreadPlanBase::ShouldStop(Event *event_ptr) { + m_stop_vote = eVoteYes; + m_run_vote = eVoteYes; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + if (stop_info_sp) { + StopReason reason = stop_info_sp->GetStopReason(); + switch (reason) { + case eStopReasonInvalid: + case eStopReasonNone: + // This + m_run_vote = eVoteNoOpinion; + m_stop_vote = eVoteNo; + return false; + + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + if (stop_info_sp->ShouldStopSynchronous(event_ptr)) { + // If we are going to stop for a breakpoint, then unship the other + // plans at this point. Don't force the discard, however, so Master + // plans can stay in place if they want to. + LLDB_LOGF( + log, + "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 + " (breakpoint hit.)", + m_thread.GetID()); + m_thread.DiscardThreadPlans(false); + return true; + } + // If we aren't going to stop at this breakpoint, and it is internal, + // don't report this stop or the subsequent running event. Otherwise we + // will post the stopped & running, but the stopped event will get marked + // with "restarted" so the UI will know to wait and expect the consequent + // "running". + if (stop_info_sp->ShouldNotify(event_ptr)) { + m_stop_vote = eVoteYes; + m_run_vote = eVoteYes; + } else { + m_stop_vote = eVoteNo; + m_run_vote = eVoteNo; + } + return false; + + // TODO: the break below was missing, was this intentional??? If so + // please mention it + break; + + case eStopReasonException: + // If we crashed, discard thread plans and stop. Don't force the + // discard, however, since on rerun the target may clean up this + // exception and continue normally from there. + LLDB_LOGF( + log, + "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 + " (exception: %s)", + m_thread.GetID(), stop_info_sp->GetDescription()); + m_thread.DiscardThreadPlans(false); + return true; + + case eStopReasonExec: + // If we crashed, discard thread plans and stop. Don't force the + // discard, however, since on rerun the target may clean up this + // exception and continue normally from there. + LLDB_LOGF( + log, + "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 + " (exec.)", + m_thread.GetID()); + m_thread.DiscardThreadPlans(false); + return true; + + case eStopReasonThreadExiting: + case eStopReasonSignal: + if (stop_info_sp->ShouldStop(event_ptr)) { + LLDB_LOGF( + log, + "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 + " (signal: %s)", + m_thread.GetID(), stop_info_sp->GetDescription()); + m_thread.DiscardThreadPlans(false); + return true; + } else { + // We're not going to stop, but while we are here, let's figure out + // whether to report this. + if (stop_info_sp->ShouldNotify(event_ptr)) + m_stop_vote = eVoteYes; + else + m_stop_vote = eVoteNo; + } + return false; + + default: + return true; + } + + } else { + m_run_vote = eVoteNoOpinion; + m_stop_vote = eVoteNo; + } + + // If there's no explicit reason to stop, then we will continue. + return false; +} + +bool ThreadPlanBase::StopOthers() { return false; } + +StateType ThreadPlanBase::GetPlanRunState() { return eStateRunning; } + +bool ThreadPlanBase::WillStop() { return true; } + +bool ThreadPlanBase::DoWillResume(lldb::StateType resume_state, + bool current_plan) { + // Reset these to the default values so we don't set them wrong, then not get + // asked for a while, then return the wrong answer. + m_run_vote = eVoteNoOpinion; + m_stop_vote = eVoteNo; + return true; +} + +// The base plan is never done. +bool ThreadPlanBase::MischiefManaged() { + // The base plan is never done. + return false; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp new file mode 100644 index 00000000000..23d114e3099 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -0,0 +1,473 @@ +//===-- ThreadPlanCallFunction.cpp ------------------------------*- C++ -*-===// +// +// 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/ThreadPlanCallFunction.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/DumpRegisterValue.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanCallFunction: Plan to call a single function +bool ThreadPlanCallFunction::ConstructorSetup( + Thread &thread, ABI *&abi, lldb::addr_t &start_load_addr, + lldb::addr_t &function_load_addr) { + SetIsMasterPlan(true); + SetOkayToDiscard(false); + SetPrivate(true); + + ProcessSP process_sp(thread.GetProcess()); + if (!process_sp) + return false; + + abi = process_sp->GetABI().get(); + + if (!abi) + return false; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP)); + + SetBreakpoints(); + + m_function_sp = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize(); + // If we can't read memory at the point of the process where we are planning + // to put our function, we're not going to get any further... + Status error; + process_sp->ReadUnsignedIntegerFromMemory(m_function_sp, 4, 0, error); + if (!error.Success()) { + m_constructor_errors.Printf( + "Trying to put the stack in unreadable memory at: 0x%" PRIx64 ".", + m_function_sp); + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this), + m_constructor_errors.GetData()); + return false; + } + + llvm::Expected<Address> start_address = GetTarget().GetEntryPointAddress(); + if (!start_address) { + m_constructor_errors.Printf( + "%s", llvm::toString(start_address.takeError()).c_str()); + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this), + m_constructor_errors.GetData()); + return false; + } + + m_start_addr = *start_address; + start_load_addr = m_start_addr.GetLoadAddress(&GetTarget()); + + // Checkpoint the thread state so we can restore it later. + if (log && log->GetVerbose()) + ReportRegisterState("About to checkpoint thread before function call. " + "Original register state was:"); + + if (!thread.CheckpointThreadState(m_stored_thread_state)) { + m_constructor_errors.Printf("Setting up ThreadPlanCallFunction, failed to " + "checkpoint thread state."); + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this), + m_constructor_errors.GetData()); + return false; + } + function_load_addr = m_function_addr.GetLoadAddress(&GetTarget()); + + return true; +} + +ThreadPlanCallFunction::ThreadPlanCallFunction( + Thread &thread, const Address &function, const CompilerType &return_type, + llvm::ArrayRef<addr_t> args, const EvaluateExpressionOptions &options) + : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), m_stop_other_threads(options.GetStopOthers()), + m_unwind_on_error(options.DoesUnwindOnError()), + m_ignore_breakpoints(options.DoesIgnoreBreakpoints()), + m_debug_execution(options.GetDebug()), + m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function), + m_function_sp(0), m_takedown_done(false), + m_should_clear_objc_exception_bp(false), + m_should_clear_cxx_exception_bp(false), + m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(return_type) { + lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS; + ABI *abi = nullptr; + + if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr)) + return; + + if (!abi->PrepareTrivialCall(thread, m_function_sp, function_load_addr, + start_load_addr, args)) + return; + + ReportRegisterState("Function call was set up. Register state was:"); + + m_valid = true; +} + +ThreadPlanCallFunction::ThreadPlanCallFunction( + Thread &thread, const Address &function, + const EvaluateExpressionOptions &options) + : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), m_stop_other_threads(options.GetStopOthers()), + m_unwind_on_error(options.DoesUnwindOnError()), + m_ignore_breakpoints(options.DoesIgnoreBreakpoints()), + m_debug_execution(options.GetDebug()), + m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function), + m_function_sp(0), m_takedown_done(false), + m_should_clear_objc_exception_bp(false), + m_should_clear_cxx_exception_bp(false), + m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(CompilerType()) {} + +ThreadPlanCallFunction::~ThreadPlanCallFunction() { + DoTakedown(PlanSucceeded()); +} + +void ThreadPlanCallFunction::ReportRegisterState(const char *message) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) { + StreamString strm; + RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); + + log->PutCString(message); + + RegisterValue reg_value; + + for (uint32_t reg_idx = 0, num_registers = reg_ctx->GetRegisterCount(); + reg_idx < num_registers; ++reg_idx) { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx); + if (reg_ctx->ReadRegister(reg_info, reg_value)) { + DumpRegisterValue(reg_value, &strm, reg_info, true, false, + eFormatDefault); + strm.EOL(); + } + } + log->PutString(strm.GetString()); + } +} + +void ThreadPlanCallFunction::DoTakedown(bool success) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP)); + + if (!m_valid) { + // Don't call DoTakedown if we were never valid to begin with. + LLDB_LOGF(log, + "ThreadPlanCallFunction(%p): Log called on " + "ThreadPlanCallFunction that was never valid.", + static_cast<void *>(this)); + return; + } + + if (!m_takedown_done) { + if (success) { + SetReturnValue(); + } + LLDB_LOGF(log, + "ThreadPlanCallFunction(%p): DoTakedown called for thread " + "0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", + static_cast<void *>(this), m_thread.GetID(), m_valid, + IsPlanComplete()); + m_takedown_done = true; + m_stop_address = + m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC(); + m_real_stop_info_sp = GetPrivateStopInfo(); + if (!m_thread.RestoreRegisterStateFromCheckpoint(m_stored_thread_state)) { + LLDB_LOGF(log, + "ThreadPlanCallFunction(%p): DoTakedown failed to restore " + "register state", + static_cast<void *>(this)); + } + SetPlanComplete(success); + ClearBreakpoints(); + if (log && log->GetVerbose()) + ReportRegisterState("Restoring thread state after function call. " + "Restored register state:"); + } else { + LLDB_LOGF(log, + "ThreadPlanCallFunction(%p): DoTakedown called as no-op for " + "thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", + static_cast<void *>(this), m_thread.GetID(), m_valid, + IsPlanComplete()); + } +} + +void ThreadPlanCallFunction::WillPop() { DoTakedown(PlanSucceeded()); } + +void ThreadPlanCallFunction::GetDescription(Stream *s, DescriptionLevel level) { + if (level == eDescriptionLevelBrief) { + s->Printf("Function call thread plan"); + } else { + TargetSP target_sp(m_thread.CalculateTarget()); + s->Printf("Thread plan to call 0x%" PRIx64, + m_function_addr.GetLoadAddress(target_sp.get())); + } +} + +bool ThreadPlanCallFunction::ValidatePlan(Stream *error) { + if (!m_valid) { + if (error) { + if (m_constructor_errors.GetSize() > 0) + error->PutCString(m_constructor_errors.GetString()); + else + error->PutCString("Unknown error"); + } + return false; + } + + return true; +} + +Vote ThreadPlanCallFunction::ShouldReportStop(Event *event_ptr) { + if (m_takedown_done || IsPlanComplete()) + return eVoteYes; + else + return ThreadPlan::ShouldReportStop(event_ptr); +} + +bool ThreadPlanCallFunction::DoPlanExplainsStop(Event *event_ptr) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | + LIBLLDB_LOG_PROCESS)); + m_real_stop_info_sp = GetPrivateStopInfo(); + + // If our subplan knows why we stopped, even if it's done (which would + // forward the question to us) we answer yes. + if (m_subplan_sp && m_subplan_sp->PlanExplainsStop(event_ptr)) { + SetPlanComplete(); + return true; + } + + // Check if the breakpoint is one of ours. + + StopReason stop_reason; + if (!m_real_stop_info_sp) + stop_reason = eStopReasonNone; + else + stop_reason = m_real_stop_info_sp->GetStopReason(); + LLDB_LOGF(log, + "ThreadPlanCallFunction::PlanExplainsStop: Got stop reason - %s.", + Thread::StopReasonAsCString(stop_reason)); + + if (stop_reason == eStopReasonBreakpoint && BreakpointsExplainStop()) + return true; + + // One more quirk here. If this event was from Halt interrupting the target, + // then we should not consider ourselves complete. Return true to + // acknowledge the stop. + if (Process::ProcessEventData::GetInterruptedFromEvent(event_ptr)) { + LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop: The event is an " + "Interrupt, returning true."); + return true; + } + // We control breakpoints separately from other "stop reasons." So first, + // check the case where we stopped for an internal breakpoint, in that case, + // continue on. If it is not an internal breakpoint, consult + // m_ignore_breakpoints. + + if (stop_reason == eStopReasonBreakpoint) { + ProcessSP process_sp(m_thread.CalculateProcess()); + uint64_t break_site_id = m_real_stop_info_sp->GetValue(); + BreakpointSiteSP bp_site_sp; + if (process_sp) + bp_site_sp = process_sp->GetBreakpointSiteList().FindByID(break_site_id); + if (bp_site_sp) { + uint32_t num_owners = bp_site_sp->GetNumberOfOwners(); + bool is_internal = true; + for (uint32_t i = 0; i < num_owners; i++) { + Breakpoint &bp = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); + LLDB_LOGF(log, + "ThreadPlanCallFunction::PlanExplainsStop: hit " + "breakpoint %d while calling function", + bp.GetID()); + + if (!bp.IsInternal()) { + is_internal = false; + break; + } + } + if (is_internal) { + LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop hit an " + "internal breakpoint, not stopping."); + return false; + } + } + + if (m_ignore_breakpoints) { + LLDB_LOGF(log, + "ThreadPlanCallFunction::PlanExplainsStop: we are ignoring " + "breakpoints, overriding breakpoint stop info ShouldStop, " + "returning true"); + m_real_stop_info_sp->OverrideShouldStop(false); + return true; + } else { + LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop: we are not " + "ignoring breakpoints, overriding breakpoint stop info " + "ShouldStop, returning true"); + m_real_stop_info_sp->OverrideShouldStop(true); + return false; + } + } else if (!m_unwind_on_error) { + // If we don't want to discard this plan, than any stop we don't understand + // should be propagated up the stack. + return false; + } else { + // If the subplan is running, any crashes are attributable to us. If we + // want to discard the plan, then we say we explain the stop but if we are + // going to be discarded, let whoever is above us explain the stop. But + // don't discard the plan if the stop would restart itself (for instance if + // it is a signal that is set not to stop. Check that here first. We just + // say we explain the stop but aren't done and everything will continue on + // from there. + + if (m_real_stop_info_sp && + m_real_stop_info_sp->ShouldStopSynchronous(event_ptr)) { + SetPlanComplete(false); + return m_subplan_sp ? m_unwind_on_error : false; + } else + return true; + } +} + +bool ThreadPlanCallFunction::ShouldStop(Event *event_ptr) { + // We do some computation in DoPlanExplainsStop that may or may not set the + // plan as complete. We need to do that here to make sure our state is + // correct. + DoPlanExplainsStop(event_ptr); + + if (IsPlanComplete()) { + ReportRegisterState("Function completed. Register state was:"); + return true; + } else { + return false; + } +} + +bool ThreadPlanCallFunction::StopOthers() { return m_stop_other_threads; } + +StateType ThreadPlanCallFunction::GetPlanRunState() { return eStateRunning; } + +void ThreadPlanCallFunction::DidPush() { + //#define SINGLE_STEP_EXPRESSIONS + + // Now set the thread state to "no reason" so we don't run with whatever + // signal was outstanding... Wait till the plan is pushed so we aren't + // changing the stop info till we're about to run. + + GetThread().SetStopInfoToNothing(); + +#ifndef SINGLE_STEP_EXPRESSIONS + m_subplan_sp = std::make_shared<ThreadPlanRunToAddress>( + m_thread, m_start_addr, m_stop_other_threads); + + m_thread.QueueThreadPlan(m_subplan_sp, false); + m_subplan_sp->SetPrivate(true); +#endif +} + +bool ThreadPlanCallFunction::WillStop() { return true; } + +bool ThreadPlanCallFunction::MischiefManaged() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (IsPlanComplete()) { + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): Completed call function plan.", + static_cast<void *>(this)); + + ThreadPlan::MischiefManaged(); + return true; + } else { + return false; + } +} + +void ThreadPlanCallFunction::SetBreakpoints() { + ProcessSP process_sp(m_thread.CalculateProcess()); + if (m_trap_exceptions && process_sp) { + m_cxx_language_runtime = + process_sp->GetLanguageRuntime(eLanguageTypeC_plus_plus); + m_objc_language_runtime = process_sp->GetLanguageRuntime(eLanguageTypeObjC); + + if (m_cxx_language_runtime) { + m_should_clear_cxx_exception_bp = + !m_cxx_language_runtime->ExceptionBreakpointsAreSet(); + m_cxx_language_runtime->SetExceptionBreakpoints(); + } + if (m_objc_language_runtime) { + m_should_clear_objc_exception_bp = + !m_objc_language_runtime->ExceptionBreakpointsAreSet(); + m_objc_language_runtime->SetExceptionBreakpoints(); + } + } +} + +void ThreadPlanCallFunction::ClearBreakpoints() { + if (m_trap_exceptions) { + if (m_cxx_language_runtime && m_should_clear_cxx_exception_bp) + m_cxx_language_runtime->ClearExceptionBreakpoints(); + if (m_objc_language_runtime && m_should_clear_objc_exception_bp) + m_objc_language_runtime->ClearExceptionBreakpoints(); + } +} + +bool ThreadPlanCallFunction::BreakpointsExplainStop() { + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + + if (m_trap_exceptions) { + if ((m_cxx_language_runtime && + m_cxx_language_runtime->ExceptionBreakpointsExplainStop( + stop_info_sp)) || + (m_objc_language_runtime && + m_objc_language_runtime->ExceptionBreakpointsExplainStop( + stop_info_sp))) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, "ThreadPlanCallFunction::BreakpointsExplainStop - Hit an " + "exception breakpoint, setting plan complete."); + + SetPlanComplete(false); + + // If the user has set the ObjC language breakpoint, it would normally + // get priority over our internal catcher breakpoint, but in this case we + // can't let that happen, so force the ShouldStop here. + stop_info_sp->OverrideShouldStop(true); + return true; + } + } + + return false; +} + +void ThreadPlanCallFunction::SetStopOthers(bool new_value) { + m_subplan_sp->SetStopOthers(new_value); +} + +bool ThreadPlanCallFunction::RestoreThreadState() { + return GetThread().RestoreThreadStateFromCheckpoint(m_stored_thread_state); +} + +void ThreadPlanCallFunction::SetReturnValue() { + ProcessSP process_sp(m_thread.GetProcess()); + const ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr; + if (abi && m_return_type.IsValid()) { + const bool persistent = false; + m_return_valobj_sp = + abi->GetReturnValueObject(m_thread, m_return_type, persistent); + } +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanCallFunctionUsingABI.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanCallFunctionUsingABI.cpp new file mode 100644 index 00000000000..3155e6f7965 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanCallFunctionUsingABI.cpp @@ -0,0 +1,68 @@ +//===-- ThreadPlanCallFunctionUsingABI.cpp ----------------------*- C++ -*-===// +// +// 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/ThreadPlanCallFunctionUsingABI.h" +#include "lldb/Core/Address.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanCallFunctionUsingABI: Plan to call a single function using the ABI +// instead of JIT +ThreadPlanCallFunctionUsingABI::ThreadPlanCallFunctionUsingABI( + Thread &thread, const Address &function, llvm::Type &prototype, + llvm::Type &return_type, llvm::ArrayRef<ABI::CallArgument> args, + const EvaluateExpressionOptions &options) + : ThreadPlanCallFunction(thread, function, options), + m_return_type(return_type) { + lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS; + ABI *abi = nullptr; + + if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr)) + return; + + if (!abi->PrepareTrivialCall(thread, m_function_sp, function_load_addr, + start_load_addr, prototype, args)) + return; + + ReportRegisterState("ABI Function call was set up. Register state was:"); + + m_valid = true; +} + +ThreadPlanCallFunctionUsingABI::~ThreadPlanCallFunctionUsingABI() = default; + +void ThreadPlanCallFunctionUsingABI::GetDescription(Stream *s, + DescriptionLevel level) { + if (level == eDescriptionLevelBrief) { + s->Printf("Function call thread plan using ABI instead of JIT"); + } else { + TargetSP target_sp(m_thread.CalculateTarget()); + s->Printf("Thread plan to call 0x%" PRIx64 " using ABI instead of JIT", + m_function_addr.GetLoadAddress(target_sp.get())); + } +} + +void ThreadPlanCallFunctionUsingABI::SetReturnValue() { + ProcessSP process_sp(m_thread.GetProcess()); + const ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr; + + // Ask the abi for the return value + if (abi) { + const bool persistent = false; + m_return_valobj_sp = + abi->GetReturnValueObject(m_thread, m_return_type, persistent); + } +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp new file mode 100644 index 00000000000..3330adc0c2a --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp @@ -0,0 +1,97 @@ +//===-- ThreadPlanCallOnFunctionExit.cpp ------------------------*- C++ -*-===// +// +// 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/ThreadPlanCallOnFunctionExit.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadPlanCallOnFunctionExit::ThreadPlanCallOnFunctionExit( + Thread &thread, const Callback &callback) + : ThreadPlan(ThreadPlanKind::eKindGeneric, "CallOnFunctionExit", thread, + eVoteNoOpinion, eVoteNoOpinion // TODO check with Jim on these + ), + m_callback(callback) { + // We are not a user-generated plan. + SetIsMasterPlan(false); +} + +void ThreadPlanCallOnFunctionExit::DidPush() { + // We now want to queue the "step out" thread plan so it executes and + // completes. + + // Set stop vote to eVoteNo. + Status status; + m_step_out_threadplan_sp = GetThread().QueueThreadPlanForStepOut( + false, // abort other plans + nullptr, // addr_context + true, // first instruction + true, // stop other threads + eVoteNo, // do not say "we're stopping" + eVoteNoOpinion, // don't care about run state broadcasting + 0, // frame_idx + status, // status + eLazyBoolCalculate // avoid code w/o debinfo + ); +} + +// ThreadPlan API + +void ThreadPlanCallOnFunctionExit::GetDescription( + Stream *s, lldb::DescriptionLevel level) { + if (!s) + return; + s->Printf("Running until completion of current function, then making " + "callback."); +} + +bool ThreadPlanCallOnFunctionExit::ValidatePlan(Stream *error) { + // We'll say we're always good since I don't know what would make this + // invalid. + return true; +} + +bool ThreadPlanCallOnFunctionExit::ShouldStop(Event *event_ptr) { + // If this is where we find out that an internal stop came in, then: Check if + // the step-out plan completed. If it did, then we want to run the callback + // here (our reason for living...) + if (m_step_out_threadplan_sp && m_step_out_threadplan_sp->IsPlanComplete()) { + m_callback(); + + // We no longer need the pointer to the step-out thread plan. + m_step_out_threadplan_sp.reset(); + + // Indicate that this plan is done and can be discarded. + SetPlanComplete(); + + // We're done now, but we want to return false so that we don't cause the + // thread to really stop. + } + + return false; +} + +bool ThreadPlanCallOnFunctionExit::WillStop() { + // The code looks like the return value is ignored via ThreadList:: + // ShouldStop(). This is called when we really are going to stop. We don't + // care and don't need to do anything here. + return false; +} + +bool ThreadPlanCallOnFunctionExit::DoPlanExplainsStop(Event *event_ptr) { + // We don't ever explain a stop. The only stop that is relevant to us + // directly is the step_out plan we added to do the heavy lifting of getting + // us past the current method. + return false; +} + +lldb::StateType ThreadPlanCallOnFunctionExit::GetPlanRunState() { + // This value doesn't matter - we'll never be the top thread plan, so nobody + // will ask us this question. + return eStateRunning; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanCallUserExpression.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanCallUserExpression.cpp new file mode 100644 index 00000000000..436938c8f20 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanCallUserExpression.cpp @@ -0,0 +1,118 @@ +//===-- ThreadPlanCallUserExpression.cpp -------------------------*- C++-*-===// +// +// 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/ThreadPlanCallUserExpression.h" + + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Address.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/DynamicCheckerFunctions.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanCallUserExpression: Plan to call a single function + +ThreadPlanCallUserExpression::ThreadPlanCallUserExpression( + Thread &thread, Address &function, llvm::ArrayRef<lldb::addr_t> args, + const EvaluateExpressionOptions &options, + lldb::UserExpressionSP &user_expression_sp) + : ThreadPlanCallFunction(thread, function, CompilerType(), args, options), + m_user_expression_sp(user_expression_sp) { + // User expressions are generally "User generated" so we should set them up + // to stop when done. + SetIsMasterPlan(true); + SetOkayToDiscard(false); +} + +ThreadPlanCallUserExpression::~ThreadPlanCallUserExpression() {} + +void ThreadPlanCallUserExpression::GetDescription( + Stream *s, lldb::DescriptionLevel level) { + if (level == eDescriptionLevelBrief) + s->Printf("User Expression thread plan"); + else + ThreadPlanCallFunction::GetDescription(s, level); +} + +void ThreadPlanCallUserExpression::DidPush() { + ThreadPlanCallFunction::DidPush(); + if (m_user_expression_sp) + m_user_expression_sp->WillStartExecuting(); +} + +void ThreadPlanCallUserExpression::WillPop() { + ThreadPlanCallFunction::WillPop(); + if (m_user_expression_sp) + m_user_expression_sp.reset(); +} + +bool ThreadPlanCallUserExpression::MischiefManaged() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (IsPlanComplete()) { + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): Completed call function plan.", + static_cast<void *>(this)); + + if (m_manage_materialization && PlanSucceeded() && m_user_expression_sp) { + lldb::addr_t function_stack_top; + lldb::addr_t function_stack_bottom; + lldb::addr_t function_stack_pointer = GetFunctionStackPointer(); + + function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize(); + function_stack_top = function_stack_pointer; + + DiagnosticManager diagnostics; + + ExecutionContext exe_ctx(GetThread()); + + m_user_expression_sp->FinalizeJITExecution( + diagnostics, exe_ctx, m_result_var_sp, function_stack_bottom, + function_stack_top); + } + + ThreadPlan::MischiefManaged(); + return true; + } else { + return false; + } +} + +StopInfoSP ThreadPlanCallUserExpression::GetRealStopInfo() { + StopInfoSP stop_info_sp = ThreadPlanCallFunction::GetRealStopInfo(); + + if (stop_info_sp) { + lldb::addr_t addr = GetStopAddress(); + DynamicCheckerFunctions *checkers = + m_thread.GetProcess()->GetDynamicCheckers(); + StreamString s; + + if (checkers && checkers->DoCheckersExplainStop(addr, s)) + stop_info_sp->SetDescription(s.GetData()); + } + + return stop_info_sp; +} + +void ThreadPlanCallUserExpression::DoTakedown(bool success) { + ThreadPlanCallFunction::DoTakedown(success); + m_user_expression_sp->DidFinishExecuting(); +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanPython.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanPython.cpp new file mode 100644 index 00000000000..df432a0af3d --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanPython.cpp @@ -0,0 +1,193 @@ +//===-- ThreadPlanPython.cpp ------------------------------------*- C++ -*-===// +// +// 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/ThreadPlan.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanPython.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanPython + +ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name, + StructuredDataImpl *args_data) + : ThreadPlan(ThreadPlan::eKindPython, "Python based Thread Plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_class_name(class_name), m_args_data(args_data), m_did_push(false) { + SetIsMasterPlan(true); + SetOkayToDiscard(true); + SetPrivate(false); +} + +ThreadPlanPython::~ThreadPlanPython() { + // FIXME, do I need to decrement the ref count on this implementation object + // to make it go away? +} + +bool ThreadPlanPython::ValidatePlan(Stream *error) { + if (!m_did_push) + return true; + + if (!m_implementation_sp) { + if (error) + error->Printf("Error constructing Python ThreadPlan: %s", + m_error_str.empty() ? "<unknown error>" + : m_error_str.c_str()); + return false; + } + + return true; +} + +void ThreadPlanPython::DidPush() { + // We set up the script side in DidPush, so that it can push other plans in + // the constructor, and doesn't have to care about the details of DidPush. + m_did_push = true; + if (!m_class_name.empty()) { + ScriptInterpreter *script_interp = m_thread.GetProcess() + ->GetTarget() + .GetDebugger() + .GetScriptInterpreter(); + if (script_interp) { + m_implementation_sp = script_interp->CreateScriptedThreadPlan( + m_class_name.c_str(), m_args_data, m_error_str, + this->shared_from_this()); + } + } +} + +bool ThreadPlanPython::ShouldStop(Event *event_ptr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION, + m_class_name.c_str()); + + bool should_stop = true; + if (m_implementation_sp) { + ScriptInterpreter *script_interp = m_thread.GetProcess() + ->GetTarget() + .GetDebugger() + .GetScriptInterpreter(); + if (script_interp) { + bool script_error; + should_stop = script_interp->ScriptedThreadPlanShouldStop( + m_implementation_sp, event_ptr, script_error); + if (script_error) + SetPlanComplete(false); + } + } + return should_stop; +} + +bool ThreadPlanPython::IsPlanStale() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION, + m_class_name.c_str()); + + bool is_stale = true; + if (m_implementation_sp) { + ScriptInterpreter *script_interp = m_thread.GetProcess() + ->GetTarget() + .GetDebugger() + .GetScriptInterpreter(); + if (script_interp) { + bool script_error; + is_stale = script_interp->ScriptedThreadPlanIsStale(m_implementation_sp, + script_error); + if (script_error) + SetPlanComplete(false); + } + } + return is_stale; +} + +bool ThreadPlanPython::DoPlanExplainsStop(Event *event_ptr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION, + m_class_name.c_str()); + + bool explains_stop = true; + if (m_implementation_sp) { + ScriptInterpreter *script_interp = m_thread.GetProcess() + ->GetTarget() + .GetDebugger() + .GetScriptInterpreter(); + if (script_interp) { + bool script_error; + explains_stop = script_interp->ScriptedThreadPlanExplainsStop( + m_implementation_sp, event_ptr, script_error); + if (script_error) + SetPlanComplete(false); + } + } + return explains_stop; +} + +bool ThreadPlanPython::MischiefManaged() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION, + m_class_name.c_str()); + bool mischief_managed = true; + if (m_implementation_sp) { + // I don't really need mischief_managed, since it's simpler to just call + // SetPlanComplete in should_stop. + mischief_managed = IsPlanComplete(); + if (mischief_managed) + m_implementation_sp.reset(); + } + return mischief_managed; +} + +lldb::StateType ThreadPlanPython::GetPlanRunState() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION, + m_class_name.c_str()); + lldb::StateType run_state = eStateRunning; + if (m_implementation_sp) { + ScriptInterpreter *script_interp = m_thread.GetProcess() + ->GetTarget() + .GetDebugger() + .GetScriptInterpreter(); + if (script_interp) { + bool script_error; + run_state = script_interp->ScriptedThreadPlanGetRunState( + m_implementation_sp, script_error); + } + } + return run_state; +} + +// The ones below are not currently exported to Python. + +bool ThreadPlanPython::StopOthers() { + // For now Python plans run all threads, but we should add some controls for + // this. + return false; +} + +void ThreadPlanPython::GetDescription(Stream *s, lldb::DescriptionLevel level) { + s->Printf("Python thread plan implemented by class %s.", + m_class_name.c_str()); +} + +bool ThreadPlanPython::WillStop() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION, + m_class_name.c_str()); + return true; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanRunToAddress.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanRunToAddress.cpp new file mode 100644 index 00000000000..32ea2e67527 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanRunToAddress.cpp @@ -0,0 +1,203 @@ +//===-- ThreadPlanRunToAddress.cpp ------------------------------*- C++ -*-===// +// +// 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/ThreadPlanRunToAddress.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanRunToAddress: Continue plan + +ThreadPlanRunToAddress::ThreadPlanRunToAddress(Thread &thread, Address &address, + bool stop_others) + : ThreadPlan(ThreadPlan::eKindRunToAddress, "Run to address plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_stop_others(stop_others), m_addresses(), m_break_ids() { + m_addresses.push_back( + address.GetOpcodeLoadAddress(m_thread.CalculateTarget().get())); + SetInitialBreakpoints(); +} + +ThreadPlanRunToAddress::ThreadPlanRunToAddress(Thread &thread, + lldb::addr_t address, + bool stop_others) + : ThreadPlan(ThreadPlan::eKindRunToAddress, "Run to address plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_stop_others(stop_others), m_addresses(), m_break_ids() { + m_addresses.push_back( + m_thread.CalculateTarget()->GetOpcodeLoadAddress(address)); + SetInitialBreakpoints(); +} + +ThreadPlanRunToAddress::ThreadPlanRunToAddress( + Thread &thread, const std::vector<lldb::addr_t> &addresses, + bool stop_others) + : ThreadPlan(ThreadPlan::eKindRunToAddress, "Run to address plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_stop_others(stop_others), m_addresses(addresses), m_break_ids() { + // Convert all addresses into opcode addresses to make sure we set + // breakpoints at the correct address. + Target &target = thread.GetProcess()->GetTarget(); + std::vector<lldb::addr_t>::iterator pos, end = m_addresses.end(); + for (pos = m_addresses.begin(); pos != end; ++pos) + *pos = target.GetOpcodeLoadAddress(*pos); + + SetInitialBreakpoints(); +} + +void ThreadPlanRunToAddress::SetInitialBreakpoints() { + size_t num_addresses = m_addresses.size(); + m_break_ids.resize(num_addresses); + + for (size_t i = 0; i < num_addresses; i++) { + Breakpoint *breakpoint; + breakpoint = m_thread.CalculateTarget() + ->CreateBreakpoint(m_addresses[i], true, false) + .get(); + if (breakpoint != nullptr) { + if (breakpoint->IsHardware() && !breakpoint->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; + m_break_ids[i] = breakpoint->GetID(); + breakpoint->SetThreadID(m_thread.GetID()); + breakpoint->SetBreakpointKind("run-to-address"); + } + } +} + +ThreadPlanRunToAddress::~ThreadPlanRunToAddress() { + size_t num_break_ids = m_break_ids.size(); + for (size_t i = 0; i < num_break_ids; i++) { + m_thread.CalculateTarget()->RemoveBreakpointByID(m_break_ids[i]); + } + m_could_not_resolve_hw_bp = false; +} + +void ThreadPlanRunToAddress::GetDescription(Stream *s, + lldb::DescriptionLevel level) { + size_t num_addresses = m_addresses.size(); + + if (level == lldb::eDescriptionLevelBrief) { + if (num_addresses == 0) { + s->Printf("run to address with no addresses given."); + return; + } else if (num_addresses == 1) + s->Printf("run to address: "); + else + s->Printf("run to addresses: "); + + for (size_t i = 0; i < num_addresses; i++) { + DumpAddress(s->AsRawOstream(), m_addresses[i], sizeof(addr_t)); + s->Printf(" "); + } + } else { + if (num_addresses == 0) { + s->Printf("run to address with no addresses given."); + return; + } else if (num_addresses == 1) + s->Printf("Run to address: "); + else { + s->Printf("Run to addresses: "); + } + + for (size_t i = 0; i < num_addresses; i++) { + if (num_addresses > 1) { + s->Printf("\n"); + s->Indent(); + } + + DumpAddress(s->AsRawOstream(), m_addresses[i], sizeof(addr_t)); + s->Printf(" using breakpoint: %d - ", m_break_ids[i]); + Breakpoint *breakpoint = + m_thread.CalculateTarget()->GetBreakpointByID(m_break_ids[i]).get(); + if (breakpoint) + breakpoint->Dump(s); + else + s->Printf("but the breakpoint has been deleted."); + } + } +} + +bool ThreadPlanRunToAddress::ValidatePlan(Stream *error) { + if (m_could_not_resolve_hw_bp) { + if (error) + error->Printf("Could not set hardware breakpoint(s)"); + return false; + } + + // If we couldn't set the breakpoint for some reason, then this won't work. + bool all_bps_good = true; + size_t num_break_ids = m_break_ids.size(); + for (size_t i = 0; i < num_break_ids; i++) { + if (m_break_ids[i] == LLDB_INVALID_BREAK_ID) { + all_bps_good = false; + if (error) { + error->Printf("Could not set breakpoint for address: "); + DumpAddress(error->AsRawOstream(), m_addresses[i], sizeof(addr_t)); + error->Printf("\n"); + } + } + } + return all_bps_good; +} + +bool ThreadPlanRunToAddress::DoPlanExplainsStop(Event *event_ptr) { + return AtOurAddress(); +} + +bool ThreadPlanRunToAddress::ShouldStop(Event *event_ptr) { + return AtOurAddress(); +} + +bool ThreadPlanRunToAddress::StopOthers() { return m_stop_others; } + +void ThreadPlanRunToAddress::SetStopOthers(bool new_value) { + m_stop_others = new_value; +} + +StateType ThreadPlanRunToAddress::GetPlanRunState() { return eStateRunning; } + +bool ThreadPlanRunToAddress::WillStop() { return true; } + +bool ThreadPlanRunToAddress::MischiefManaged() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (AtOurAddress()) { + // Remove the breakpoint + size_t num_break_ids = m_break_ids.size(); + + for (size_t i = 0; i < num_break_ids; i++) { + if (m_break_ids[i] != LLDB_INVALID_BREAK_ID) { + m_thread.CalculateTarget()->RemoveBreakpointByID(m_break_ids[i]); + m_break_ids[i] = LLDB_INVALID_BREAK_ID; + } + } + LLDB_LOGF(log, "Completed run to address plan."); + ThreadPlan::MischiefManaged(); + return true; + } else + return false; +} + +bool ThreadPlanRunToAddress::AtOurAddress() { + lldb::addr_t current_address = m_thread.GetRegisterContext()->GetPC(); + bool found_it = false; + size_t num_addresses = m_addresses.size(); + for (size_t i = 0; i < num_addresses; i++) { + if (m_addresses[i] == current_address) { + found_it = true; + break; + } + } + return found_it; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanShouldStopHere.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanShouldStopHere.cpp new file mode 100644 index 00000000000..9599d8197b0 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanShouldStopHere.cpp @@ -0,0 +1,160 @@ +//===-- ThreadPlanShouldStopHere.cpp ----------------------------*- C++ -*-===// +// +// 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/ThreadPlanShouldStopHere.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanShouldStopHere constructor +ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner) + : m_callbacks(), m_baton(nullptr), m_owner(owner), + m_flags(ThreadPlanShouldStopHere::eNone) { + m_callbacks.should_stop_here_callback = + ThreadPlanShouldStopHere::DefaultShouldStopHereCallback; + m_callbacks.step_from_here_callback = + ThreadPlanShouldStopHere::DefaultStepFromHereCallback; +} + +ThreadPlanShouldStopHere::ThreadPlanShouldStopHere( + ThreadPlan *owner, const ThreadPlanShouldStopHereCallbacks *callbacks, + void *baton) + : m_callbacks(), m_baton(), m_owner(owner), + m_flags(ThreadPlanShouldStopHere::eNone) { + SetShouldStopHereCallbacks(callbacks, baton); +} + +ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default; + +bool ThreadPlanShouldStopHere::InvokeShouldStopHereCallback( + FrameComparison operation, Status &status) { + bool should_stop_here = true; + if (m_callbacks.should_stop_here_callback) { + should_stop_here = m_callbacks.should_stop_here_callback( + m_owner, m_flags, operation, status, m_baton); + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) { + lldb::addr_t current_addr = + m_owner->GetThread().GetRegisterContext()->GetPC(0); + + LLDB_LOGF(log, "ShouldStopHere callback returned %u from 0x%" PRIx64 ".", + should_stop_here, current_addr); + } + } + + return should_stop_here; +} + +bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( + ThreadPlan *current_plan, Flags &flags, FrameComparison operation, + Status &status, void *baton) { + bool should_stop_here = true; + StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); + if (!frame) + return true; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if ((operation == eFrameCompareOlder && flags.Test(eStepOutAvoidNoDebug)) || + (operation == eFrameCompareYounger && flags.Test(eStepInAvoidNoDebug)) || + (operation == eFrameCompareSameParent && + flags.Test(eStepInAvoidNoDebug))) { + if (!frame->HasDebugInformation()) { + LLDB_LOGF(log, "Stepping out of frame with no debug info"); + + should_stop_here = false; + } + } + + // Always avoid code with line number 0. + // FIXME: At present the ShouldStop and the StepFromHere calculate this + // independently. If this ever + // becomes expensive (this one isn't) we can try to have this set a state + // that the StepFromHere can use. + if (frame) { + SymbolContext sc; + sc = frame->GetSymbolContext(eSymbolContextLineEntry); + if (sc.line_entry.line == 0) + should_stop_here = false; + } + + return should_stop_here; +} + +ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback( + ThreadPlan *current_plan, Flags &flags, FrameComparison operation, + Status &status, void *baton) { + const bool stop_others = false; + const size_t frame_index = 0; + ThreadPlanSP return_plan_sp; + // If we are stepping through code at line number 0, then we need to step + // over this range. Otherwise we will step out. + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); + if (!frame) + return return_plan_sp; + SymbolContext sc; + sc = frame->GetSymbolContext(eSymbolContextLineEntry | eSymbolContextSymbol); + + if (sc.line_entry.line == 0) { + AddressRange range = sc.line_entry.range; + + // If the whole function is marked line 0 just step out, that's easier & + // faster than continuing to step through it. + bool just_step_out = false; + if (sc.symbol && sc.symbol->ValueIsAddress()) { + Address symbol_end = sc.symbol->GetAddress(); + symbol_end.Slide(sc.symbol->GetByteSize() - 1); + if (range.ContainsFileAddress(sc.symbol->GetAddress()) && + range.ContainsFileAddress(symbol_end)) { + LLDB_LOGF(log, "Stopped in a function with only line 0 lines, just " + "stepping out."); + just_step_out = true; + } + } + if (!just_step_out) { + LLDB_LOGF(log, "ThreadPlanShouldStopHere::DefaultStepFromHereCallback " + "Queueing StepInRange plan to step through line 0 code."); + + return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepInRange( + false, range, sc, nullptr, eOnlyDuringStepping, status, + eLazyBoolCalculate, eLazyBoolNo); + } + } + + if (!return_plan_sp) + return_plan_sp = + current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop( + false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, + frame_index, status, true); + return return_plan_sp; +} + +ThreadPlanSP ThreadPlanShouldStopHere::QueueStepOutFromHerePlan( + lldb_private::Flags &flags, lldb::FrameComparison operation, + Status &status) { + ThreadPlanSP return_plan_sp; + if (m_callbacks.step_from_here_callback) { + return_plan_sp = m_callbacks.step_from_here_callback( + m_owner, flags, operation, status, m_baton); + } + return return_plan_sp; +} + +lldb::ThreadPlanSP ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut( + lldb::FrameComparison operation, Status &status) { + if (!InvokeShouldStopHereCallback(operation, status)) + return QueueStepOutFromHerePlan(m_flags, operation, status); + else + return ThreadPlanSP(); +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanStepInRange.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanStepInRange.cpp new file mode 100644 index 00000000000..ab1f6a21a86 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanStepInRange.cpp @@ -0,0 +1,509 @@ +//===-- ThreadPlanStepInRange.cpp -------------------------------*- C++ -*-===// +// +// 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/ThreadPlanStepInRange.h" +#include "lldb/Core/Architecture.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepThrough.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +uint32_t ThreadPlanStepInRange::s_default_flag_values = + ThreadPlanShouldStopHere::eStepInAvoidNoDebug; + +// ThreadPlanStepInRange: Step through a stack range, either stepping over or +// into based on the value of \a type. + +ThreadPlanStepInRange::ThreadPlanStepInRange( + Thread &thread, const AddressRange &range, + const SymbolContext &addr_context, lldb::RunMode stop_others, + LazyBool step_in_avoids_code_without_debug_info, + LazyBool step_out_avoids_code_without_debug_info) + : ThreadPlanStepRange(ThreadPlan::eKindStepInRange, + "Step Range stepping in", thread, range, addr_context, + stop_others), + ThreadPlanShouldStopHere(this), m_step_past_prologue(true), + m_virtual_step(false) { + SetCallbacks(); + SetFlagsToDefault(); + SetupAvoidNoDebug(step_in_avoids_code_without_debug_info, + step_out_avoids_code_without_debug_info); +} + +ThreadPlanStepInRange::ThreadPlanStepInRange( + Thread &thread, const AddressRange &range, + const SymbolContext &addr_context, const char *step_into_target, + lldb::RunMode stop_others, LazyBool step_in_avoids_code_without_debug_info, + LazyBool step_out_avoids_code_without_debug_info) + : ThreadPlanStepRange(ThreadPlan::eKindStepInRange, + "Step Range stepping in", thread, range, addr_context, + stop_others), + ThreadPlanShouldStopHere(this), m_step_past_prologue(true), + m_virtual_step(false), m_step_into_target(step_into_target) { + SetCallbacks(); + SetFlagsToDefault(); + SetupAvoidNoDebug(step_in_avoids_code_without_debug_info, + step_out_avoids_code_without_debug_info); +} + +ThreadPlanStepInRange::~ThreadPlanStepInRange() = default; + +void ThreadPlanStepInRange::SetupAvoidNoDebug( + LazyBool step_in_avoids_code_without_debug_info, + LazyBool step_out_avoids_code_without_debug_info) { + bool avoid_nodebug = true; + + switch (step_in_avoids_code_without_debug_info) { + case eLazyBoolYes: + avoid_nodebug = true; + break; + case eLazyBoolNo: + avoid_nodebug = false; + break; + case eLazyBoolCalculate: + avoid_nodebug = m_thread.GetStepInAvoidsNoDebug(); + break; + } + if (avoid_nodebug) + GetFlags().Set(ThreadPlanShouldStopHere::eStepInAvoidNoDebug); + else + GetFlags().Clear(ThreadPlanShouldStopHere::eStepInAvoidNoDebug); + + switch (step_out_avoids_code_without_debug_info) { + case eLazyBoolYes: + avoid_nodebug = true; + break; + case eLazyBoolNo: + avoid_nodebug = false; + break; + case eLazyBoolCalculate: + avoid_nodebug = m_thread.GetStepOutAvoidsNoDebug(); + break; + } + if (avoid_nodebug) + GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); + else + GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); +} + +void ThreadPlanStepInRange::GetDescription(Stream *s, + lldb::DescriptionLevel level) { + + auto PrintFailureIfAny = [&]() { + if (m_status.Success()) + return; + s->Printf(" failed (%s)", m_status.AsCString()); + }; + + if (level == lldb::eDescriptionLevelBrief) { + s->Printf("step in"); + PrintFailureIfAny(); + return; + } + + s->Printf("Stepping in"); + bool printed_line_info = false; + if (m_addr_context.line_entry.IsValid()) { + s->Printf(" through line "); + m_addr_context.line_entry.DumpStopContext(s, false); + printed_line_info = true; + } + + const char *step_into_target = m_step_into_target.AsCString(); + if (step_into_target && step_into_target[0] != '\0') + s->Printf(" targeting %s", m_step_into_target.AsCString()); + + if (!printed_line_info || level == eDescriptionLevelVerbose) { + s->Printf(" using ranges:"); + DumpRanges(s); + } + + PrintFailureIfAny(); + + s->PutChar('.'); +} + +bool ThreadPlanStepInRange::ShouldStop(Event *event_ptr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (log) { + StreamString s; + DumpAddress( + s.AsRawOstream(), m_thread.GetRegisterContext()->GetPC(), + m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize()); + LLDB_LOGF(log, "ThreadPlanStepInRange reached %s.", s.GetData()); + } + + if (IsPlanComplete()) + return true; + + m_no_more_plans = false; + if (m_sub_plan_sp && m_sub_plan_sp->IsPlanComplete()) { + if (!m_sub_plan_sp->PlanSucceeded()) { + SetPlanComplete(); + m_no_more_plans = true; + return true; + } else + m_sub_plan_sp.reset(); + } + + if (m_virtual_step) { + // If we've just completed a virtual step, all we need to do is check for a + // ShouldStopHere plan, and otherwise we're done. + // FIXME - This can be both a step in and a step out. Probably should + // record which in the m_virtual_step. + m_sub_plan_sp = + CheckShouldStopHereAndQueueStepOut(eFrameCompareYounger, m_status); + } else { + // Stepping through should be done running other threads in general, since + // we're setting a breakpoint and continuing. So only stop others if we + // are explicitly told to do so. + + bool stop_others = (m_stop_others == lldb::eOnlyThisThread); + + FrameComparison frame_order = CompareCurrentFrameToStartFrame(); + + if (frame_order == eFrameCompareOlder || + frame_order == eFrameCompareSameParent) { + // If we're in an older frame then we should stop. + // + // A caveat to this is if we think the frame is older but we're actually + // in a trampoline. + // I'm going to make the assumption that you wouldn't RETURN to a + // trampoline. So if we are in a trampoline we think the frame is older + // because the trampoline confused the backtracer. + m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough( + m_stack_id, false, stop_others, m_status); + if (!m_sub_plan_sp) { + // Otherwise check the ShouldStopHere for step out: + m_sub_plan_sp = + CheckShouldStopHereAndQueueStepOut(frame_order, m_status); + if (log) { + if (m_sub_plan_sp) + LLDB_LOGF(log, + "ShouldStopHere found plan to step out of this frame."); + else + LLDB_LOGF(log, "ShouldStopHere no plan to step out of this frame."); + } + } else if (log) { + LLDB_LOGF( + log, "Thought I stepped out, but in fact arrived at a trampoline."); + } + } else if (frame_order == eFrameCompareEqual && InSymbol()) { + // If we are not in a place we should step through, we're done. One + // tricky bit here is that some stubs don't push a frame, so we have to + // check both the case of a frame that is younger, or the same as this + // frame. However, if the frame is the same, and we are still in the + // symbol we started in, the we don't need to do this. This first check + // isn't strictly necessary, but it is more efficient. + + // If we're still in the range, keep going, either by running to the next + // branch breakpoint, or by stepping. + if (InRange()) { + SetNextBranchBreakpoint(); + return false; + } + + SetPlanComplete(); + m_no_more_plans = true; + return true; + } + + // If we get to this point, we're not going to use a previously set "next + // branch" breakpoint, so delete it: + ClearNextBranchBreakpoint(); + + // We may have set the plan up above in the FrameIsOlder section: + + if (!m_sub_plan_sp) + m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough( + m_stack_id, false, stop_others, m_status); + + if (log) { + if (m_sub_plan_sp) + LLDB_LOGF(log, "Found a step through plan: %s", + m_sub_plan_sp->GetName()); + else + LLDB_LOGF(log, "No step through plan found."); + } + + // If not, give the "should_stop" callback a chance to push a plan to get + // us out of here. But only do that if we actually have stepped in. + if (!m_sub_plan_sp && frame_order == eFrameCompareYounger) + m_sub_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order, m_status); + + // If we've stepped in and we are going to stop here, check to see if we + // were asked to run past the prologue, and if so do that. + + if (!m_sub_plan_sp && frame_order == eFrameCompareYounger && + m_step_past_prologue) { + lldb::StackFrameSP curr_frame = m_thread.GetStackFrameAtIndex(0); + if (curr_frame) { + size_t bytes_to_skip = 0; + lldb::addr_t curr_addr = m_thread.GetRegisterContext()->GetPC(); + Address func_start_address; + + SymbolContext sc = curr_frame->GetSymbolContext(eSymbolContextFunction | + eSymbolContextSymbol); + + if (sc.function) { + func_start_address = sc.function->GetAddressRange().GetBaseAddress(); + if (curr_addr == + func_start_address.GetLoadAddress( + m_thread.CalculateTarget().get())) + bytes_to_skip = sc.function->GetPrologueByteSize(); + } else if (sc.symbol) { + func_start_address = sc.symbol->GetAddress(); + if (curr_addr == + func_start_address.GetLoadAddress( + m_thread.CalculateTarget().get())) + bytes_to_skip = sc.symbol->GetPrologueByteSize(); + } + + if (bytes_to_skip == 0 && sc.symbol) { + TargetSP target = m_thread.CalculateTarget(); + const Architecture *arch = target->GetArchitecturePlugin(); + if (arch) { + Address curr_sec_addr; + target->GetSectionLoadList().ResolveLoadAddress(curr_addr, + curr_sec_addr); + bytes_to_skip = arch->GetBytesToSkip(*sc.symbol, curr_sec_addr); + } + } + + if (bytes_to_skip != 0) { + func_start_address.Slide(bytes_to_skip); + log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP); + LLDB_LOGF(log, "Pushing past prologue "); + + m_sub_plan_sp = m_thread.QueueThreadPlanForRunToAddress( + false, func_start_address, true, m_status); + } + } + } + } + + if (!m_sub_plan_sp) { + m_no_more_plans = true; + SetPlanComplete(); + return true; + } else { + m_no_more_plans = false; + m_sub_plan_sp->SetPrivate(true); + return false; + } +} + +void ThreadPlanStepInRange::SetAvoidRegexp(const char *name) { + auto name_ref = llvm::StringRef::withNullAsEmpty(name); + if (m_avoid_regexp_up) + *m_avoid_regexp_up = RegularExpression(name_ref); + else + m_avoid_regexp_up.reset(new RegularExpression(name_ref)); +} + +void ThreadPlanStepInRange::SetDefaultFlagValue(uint32_t new_value) { + // TODO: Should we test this for sanity? + ThreadPlanStepInRange::s_default_flag_values = new_value; +} + +bool ThreadPlanStepInRange::FrameMatchesAvoidCriteria() { + StackFrame *frame = GetThread().GetStackFrameAtIndex(0).get(); + + // Check the library list first, as that's cheapest: + bool libraries_say_avoid = false; + + FileSpecList libraries_to_avoid(GetThread().GetLibrariesToAvoid()); + size_t num_libraries = libraries_to_avoid.GetSize(); + if (num_libraries > 0) { + SymbolContext sc(frame->GetSymbolContext(eSymbolContextModule)); + FileSpec frame_library(sc.module_sp->GetFileSpec()); + + if (frame_library) { + for (size_t i = 0; i < num_libraries; i++) { + const FileSpec &file_spec(libraries_to_avoid.GetFileSpecAtIndex(i)); + if (FileSpec::Match(file_spec, frame_library)) { + libraries_say_avoid = true; + break; + } + } + } + } + if (libraries_say_avoid) + return true; + + const RegularExpression *avoid_regexp_to_use = m_avoid_regexp_up.get(); + if (avoid_regexp_to_use == nullptr) + avoid_regexp_to_use = GetThread().GetSymbolsToAvoidRegexp(); + + if (avoid_regexp_to_use != nullptr) { + SymbolContext sc = frame->GetSymbolContext( + eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol); + if (sc.symbol != nullptr) { + const char *frame_function_name = + sc.GetFunctionName(Mangled::ePreferDemangledWithoutArguments) + .GetCString(); + if (frame_function_name) { + llvm::SmallVector<llvm::StringRef, 2> matches; + bool return_value = + avoid_regexp_to_use->Execute(frame_function_name, &matches); + if (return_value && matches.size() > 1) { + std::string match = matches[1].str(); + LLDB_LOGF(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP), + "Stepping out of function \"%s\" because it matches " + "the avoid regexp \"%s\" - match substring: \"%s\".", + frame_function_name, + avoid_regexp_to_use->GetText().str().c_str(), + match.c_str()); + } + return return_value; + } + } + } + return false; +} + +bool ThreadPlanStepInRange::DefaultShouldStopHereCallback( + ThreadPlan *current_plan, Flags &flags, FrameComparison operation, + Status &status, void *baton) { + bool should_stop_here = true; + StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + // First see if the ThreadPlanShouldStopHere default implementation thinks we + // should get out of here: + should_stop_here = ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( + current_plan, flags, operation, status, baton); + if (!should_stop_here) + return false; + + if (should_stop_here && current_plan->GetKind() == eKindStepInRange && + operation == eFrameCompareYounger) { + ThreadPlanStepInRange *step_in_range_plan = + static_cast<ThreadPlanStepInRange *>(current_plan); + if (step_in_range_plan->m_step_into_target) { + SymbolContext sc = frame->GetSymbolContext( + eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol); + if (sc.symbol != nullptr) { + // First try an exact match, since that's cheap with ConstStrings. + // Then do a strstr compare. + if (step_in_range_plan->m_step_into_target == sc.GetFunctionName()) { + should_stop_here = true; + } else { + const char *target_name = + step_in_range_plan->m_step_into_target.AsCString(); + const char *function_name = sc.GetFunctionName().AsCString(); + + if (function_name == nullptr) + should_stop_here = false; + else if (strstr(function_name, target_name) == nullptr) + should_stop_here = false; + } + if (log && !should_stop_here) + LLDB_LOGF(log, + "Stepping out of frame %s which did not match step into " + "target %s.", + sc.GetFunctionName().AsCString(), + step_in_range_plan->m_step_into_target.AsCString()); + } + } + + if (should_stop_here) { + ThreadPlanStepInRange *step_in_range_plan = + static_cast<ThreadPlanStepInRange *>(current_plan); + // Don't log the should_step_out here, it's easier to do it in + // FrameMatchesAvoidCriteria. + should_stop_here = !step_in_range_plan->FrameMatchesAvoidCriteria(); + } + } + + return should_stop_here; +} + +bool ThreadPlanStepInRange::DoPlanExplainsStop(Event *event_ptr) { + // We always explain a stop. Either we've just done a single step, in which + // case we'll do our ordinary processing, or we stopped for some reason that + // isn't handled by our sub-plans, in which case we want to just stop right + // away. In general, we don't want to mark the plan as complete for + // unexplained stops. For instance, if you step in to some code with no debug + // info, so you step out and in the course of that hit a breakpoint, then you + // want to stop & show the user the breakpoint, but not unship the step in + // plan, since you still may want to complete that plan when you continue. + // This is particularly true when doing "step in to target function." + // stepping. + // + // The only variation is that if we are doing "step by running to next + // branch" in which case if we hit our branch breakpoint we don't set the + // plan to complete. + + bool return_value = false; + + if (m_virtual_step) { + return_value = true; + } else { + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + if (stop_info_sp) { + StopReason reason = stop_info_sp->GetStopReason(); + + if (reason == eStopReasonBreakpoint) { + if (NextRangeBreakpointExplainsStop(stop_info_sp)) { + return_value = true; + } + } else if (IsUsuallyUnexplainedStopReason(reason)) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) + log->PutCString("ThreadPlanStepInRange got asked if it explains the " + "stop for some reason other than step."); + return_value = false; + } else { + return_value = true; + } + } else + return_value = true; + } + + return return_value; +} + +bool ThreadPlanStepInRange::DoWillResume(lldb::StateType resume_state, + bool current_plan) { + m_virtual_step = false; + if (resume_state == eStateStepping && current_plan) { + // See if we are about to step over a virtual inlined call. + bool step_without_resume = m_thread.DecrementCurrentInlinedDepth(); + if (step_without_resume) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, + "ThreadPlanStepInRange::DoWillResume: returning false, " + "inline_depth: %d", + m_thread.GetCurrentInlinedDepth()); + SetStopInfo(StopInfo::CreateStopReasonToTrace(m_thread)); + + // FIXME: Maybe it would be better to create a InlineStep stop reason, but + // then + // the whole rest of the world would have to handle that stop reason. + m_virtual_step = true; + } + return !step_without_resume; + } + return true; +} + +bool ThreadPlanStepInRange::IsVirtualStep() { return m_virtual_step; } diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanStepInstruction.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanStepInstruction.cpp new file mode 100644 index 00000000000..afcc9d608b2 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanStepInstruction.cpp @@ -0,0 +1,255 @@ +//===-- ThreadPlanStepInstruction.cpp ---------------------------*- C++ -*-===// +// +// 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/ThreadPlanStepInstruction.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanStepInstruction: Step over the current instruction + +ThreadPlanStepInstruction::ThreadPlanStepInstruction(Thread &thread, + bool step_over, + bool stop_other_threads, + Vote stop_vote, + Vote run_vote) + : ThreadPlan(ThreadPlan::eKindStepInstruction, + "Step over single instruction", thread, stop_vote, run_vote), + m_instruction_addr(0), m_stop_other_threads(stop_other_threads), + m_step_over(step_over) { + m_takes_iteration_count = true; + SetUpState(); +} + +ThreadPlanStepInstruction::~ThreadPlanStepInstruction() = default; + +void ThreadPlanStepInstruction::SetUpState() { + m_instruction_addr = m_thread.GetRegisterContext()->GetPC(0); + StackFrameSP start_frame_sp(m_thread.GetStackFrameAtIndex(0)); + m_stack_id = start_frame_sp->GetStackID(); + + m_start_has_symbol = + start_frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol != nullptr; + + StackFrameSP parent_frame_sp = m_thread.GetStackFrameAtIndex(1); + if (parent_frame_sp) + m_parent_frame_id = parent_frame_sp->GetStackID(); +} + +void ThreadPlanStepInstruction::GetDescription(Stream *s, + lldb::DescriptionLevel level) { + auto PrintFailureIfAny = [&]() { + if (m_status.Success()) + return; + s->Printf(" failed (%s)", m_status.AsCString()); + }; + + if (level == lldb::eDescriptionLevelBrief) { + if (m_step_over) + s->Printf("instruction step over"); + else + s->Printf("instruction step into"); + + PrintFailureIfAny(); + } else { + s->Printf("Stepping one instruction past "); + DumpAddress(s->AsRawOstream(), m_instruction_addr, sizeof(addr_t)); + if (!m_start_has_symbol) + s->Printf(" which has no symbol"); + + if (m_step_over) + s->Printf(" stepping over calls"); + else + s->Printf(" stepping into calls"); + + PrintFailureIfAny(); + } +} + +bool ThreadPlanStepInstruction::ValidatePlan(Stream *error) { + // Since we read the instruction we're stepping over from the thread, this + // plan will always work. + return true; +} + +bool ThreadPlanStepInstruction::DoPlanExplainsStop(Event *event_ptr) { + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + if (stop_info_sp) { + StopReason reason = stop_info_sp->GetStopReason(); + return (reason == eStopReasonTrace || reason == eStopReasonNone); + } + return false; +} + +bool ThreadPlanStepInstruction::IsPlanStale() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + StackID cur_frame_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + if (cur_frame_id == m_stack_id) { + // Set plan Complete when we reach next instruction + uint64_t pc = m_thread.GetRegisterContext()->GetPC(0); + uint32_t max_opcode_size = m_thread.CalculateTarget() + ->GetArchitecture().GetMaximumOpcodeByteSize(); + bool next_instruction_reached = (pc > m_instruction_addr) && + (pc <= m_instruction_addr + max_opcode_size); + if (next_instruction_reached) { + SetPlanComplete(); + } + return (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr); + } else if (cur_frame_id < m_stack_id) { + // If the current frame is younger than the start frame and we are stepping + // over, then we need to continue, but if we are doing just one step, we're + // done. + return !m_step_over; + } else { + if (log) { + LLDB_LOGF(log, + "ThreadPlanStepInstruction::IsPlanStale - Current frame is " + "older than start frame, plan is stale."); + } + return true; + } +} + +bool ThreadPlanStepInstruction::ShouldStop(Event *event_ptr) { + if (m_step_over) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + StackFrameSP cur_frame_sp = m_thread.GetStackFrameAtIndex(0); + if (!cur_frame_sp) { + LLDB_LOGF( + log, + "ThreadPlanStepInstruction couldn't get the 0th frame, stopping."); + SetPlanComplete(); + return true; + } + + StackID cur_frame_zero_id = cur_frame_sp->GetStackID(); + + if (cur_frame_zero_id == m_stack_id || m_stack_id < cur_frame_zero_id) { + if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr) { + if (--m_iteration_count <= 0) { + SetPlanComplete(); + return true; + } else { + // We are still stepping, reset the start pc, and in case we've + // stepped out, reset the current stack id. + SetUpState(); + return false; + } + } else + return false; + } else { + // We've stepped in, step back out again: + StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get(); + if (return_frame) { + if (return_frame->GetStackID() != m_parent_frame_id || + m_start_has_symbol) { + // next-instruction shouldn't step out of inlined functions. But we + // may have stepped into a real function that starts with an inlined + // function, and we do want to step out of that... + + if (cur_frame_sp->IsInlined()) { + StackFrameSP parent_frame_sp = + m_thread.GetFrameWithStackID(m_stack_id); + + if (parent_frame_sp && + parent_frame_sp->GetConcreteFrameIndex() == + cur_frame_sp->GetConcreteFrameIndex()) { + SetPlanComplete(); + if (log) { + LLDB_LOGF(log, + "Frame we stepped into is inlined into the frame " + "we were stepping from, stopping."); + } + return true; + } + } + + if (log) { + StreamString s; + s.PutCString("Stepped in to: "); + addr_t stop_addr = + m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC(); + DumpAddress(s.AsRawOstream(), stop_addr, + m_thread.CalculateTarget() + ->GetArchitecture() + .GetAddressByteSize()); + s.PutCString(" stepping out to: "); + addr_t return_addr = return_frame->GetRegisterContext()->GetPC(); + DumpAddress(s.AsRawOstream(), return_addr, + m_thread.CalculateTarget() + ->GetArchitecture() + .GetAddressByteSize()); + LLDB_LOGF(log, "%s.", s.GetData()); + } + + // StepInstruction should probably have the tri-state RunMode, but + // for now it is safer to run others. + const bool stop_others = false; + m_thread.QueueThreadPlanForStepOutNoShouldStop( + false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 0, + m_status); + return false; + } else { + if (log) { + log->PutCString( + "The stack id we are stepping in changed, but our parent frame " + "did not when stepping from code with no symbols. " + "We are probably just confused about where we are, stopping."); + } + SetPlanComplete(); + return true; + } + } else { + LLDB_LOGF(log, "Could not find previous frame, stopping."); + SetPlanComplete(); + return true; + } + } + } else { + lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC(0); + if (pc_addr != m_instruction_addr) { + if (--m_iteration_count <= 0) { + SetPlanComplete(); + return true; + } else { + // We are still stepping, reset the start pc, and in case we've stepped + // in or out, reset the current stack id. + SetUpState(); + return false; + } + } else + return false; + } +} + +bool ThreadPlanStepInstruction::StopOthers() { return m_stop_other_threads; } + +StateType ThreadPlanStepInstruction::GetPlanRunState() { + return eStateStepping; +} + +bool ThreadPlanStepInstruction::WillStop() { return true; } + +bool ThreadPlanStepInstruction::MischiefManaged() { + if (IsPlanComplete()) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, "Completed single instruction step plan."); + ThreadPlan::MischiefManaged(); + return true; + } else { + return false; + } +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanStepOut.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanStepOut.cpp new file mode 100644 index 00000000000..f15a343aaa3 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanStepOut.cpp @@ -0,0 +1,534 @@ +//===-- ThreadPlanStepOut.cpp -----------------------------------*- C++ -*-===// +// +// 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/ThreadPlanStepOut.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanStepOverRange.h" +#include "lldb/Target/ThreadPlanStepThrough.h" +#include "lldb/Utility/Log.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +uint32_t ThreadPlanStepOut::s_default_flag_values = 0; + +// ThreadPlanStepOut: Step out of the current frame +ThreadPlanStepOut::ThreadPlanStepOut( + Thread &thread, SymbolContext *context, bool first_insn, bool stop_others, + Vote stop_vote, Vote run_vote, uint32_t frame_idx, + LazyBool step_out_avoids_code_without_debug_info, + bool continue_to_next_branch, bool gather_return_value) + : ThreadPlan(ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, + run_vote), + ThreadPlanShouldStopHere(this), m_step_from_insn(LLDB_INVALID_ADDRESS), + m_return_bp_id(LLDB_INVALID_BREAK_ID), + m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others), + m_immediate_step_from_function(nullptr), + m_calculate_return_value(gather_return_value) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + SetFlagsToDefault(); + SetupAvoidNoDebug(step_out_avoids_code_without_debug_info); + + m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0); + + uint32_t return_frame_index = frame_idx + 1; + StackFrameSP return_frame_sp( + m_thread.GetStackFrameAtIndex(return_frame_index)); + StackFrameSP immediate_return_from_sp( + m_thread.GetStackFrameAtIndex(frame_idx)); + + if (!return_frame_sp || !immediate_return_from_sp) + return; // we can't do anything here. ValidatePlan() will return false. + + // While stepping out, behave as-if artificial frames are not present. + while (return_frame_sp->IsArtificial()) { + m_stepped_past_frames.push_back(return_frame_sp); + + ++return_frame_index; + return_frame_sp = m_thread.GetStackFrameAtIndex(return_frame_index); + + // We never expect to see an artificial frame without a regular ancestor. + // If this happens, log the issue and defensively refuse to step out. + if (!return_frame_sp) { + LLDB_LOG(log, "Can't step out of frame with artificial ancestors"); + return; + } + } + + m_step_out_to_id = return_frame_sp->GetStackID(); + m_immediate_step_from_id = immediate_return_from_sp->GetStackID(); + + // If the frame directly below the one we are returning to is inlined, we + // have to be a little more careful. It is non-trivial to determine the real + // "return code address" for an inlined frame, so we have to work our way to + // that frame and then step out. + if (immediate_return_from_sp->IsInlined()) { + if (frame_idx > 0) { + // First queue a plan that gets us to this inlined frame, and when we get + // there we'll queue a second plan that walks us out of this frame. + m_step_out_to_inline_plan_sp = std::make_shared<ThreadPlanStepOut>( + m_thread, nullptr, false, stop_others, eVoteNoOpinion, eVoteNoOpinion, + frame_idx - 1, eLazyBoolNo, continue_to_next_branch); + static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get()) + ->SetShouldStopHereCallbacks(nullptr, nullptr); + m_step_out_to_inline_plan_sp->SetPrivate(true); + } else { + // If we're already at the inlined frame we're stepping through, then + // just do that now. + QueueInlinedStepPlan(false); + } + } else { + // Find the return address and set a breakpoint there: + // FIXME - can we do this more securely if we know first_insn? + + Address return_address(return_frame_sp->GetFrameCodeAddress()); + if (continue_to_next_branch) { + SymbolContext return_address_sc; + AddressRange range; + Address return_address_decr_pc = return_address; + if (return_address_decr_pc.GetOffset() > 0) + return_address_decr_pc.Slide(-1); + + return_address_decr_pc.CalculateSymbolContext( + &return_address_sc, lldb::eSymbolContextLineEntry); + if (return_address_sc.line_entry.IsValid()) { + const bool include_inlined_functions = false; + range = return_address_sc.line_entry.GetSameLineContiguousAddressRange( + include_inlined_functions); + if (range.GetByteSize() > 0) { + return_address = + m_thread.GetProcess()->AdvanceAddressToNextBranchInstruction( + return_address, range); + } + } + } + m_return_addr = + return_address.GetLoadAddress(&m_thread.GetProcess()->GetTarget()); + + if (m_return_addr == LLDB_INVALID_ADDRESS) + return; + + // Perform some additional validation on the return address. + uint32_t permissions = 0; + if (!m_thread.GetProcess()->GetLoadAddressPermissions(m_return_addr, + permissions)) { + m_constructor_errors.Printf("Return address (0x%" PRIx64 + ") permissions not found.", + m_return_addr); + LLDB_LOGF(log, "ThreadPlanStepOut(%p): %s", static_cast<void *>(this), + m_constructor_errors.GetData()); + return; + } else if (!(permissions & ePermissionsExecutable)) { + m_constructor_errors.Printf("Return address (0x%" PRIx64 + ") did not point to executable memory.", + m_return_addr); + LLDB_LOGF(log, "ThreadPlanStepOut(%p): %s", static_cast<void *>(this), + m_constructor_errors.GetData()); + return; + } + + Breakpoint *return_bp = m_thread.CalculateTarget() + ->CreateBreakpoint(m_return_addr, true, false) + .get(); + + if (return_bp != nullptr) { + if (return_bp->IsHardware() && !return_bp->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; + return_bp->SetThreadID(m_thread.GetID()); + m_return_bp_id = return_bp->GetID(); + return_bp->SetBreakpointKind("step-out"); + } + + if (immediate_return_from_sp) { + const SymbolContext &sc = + immediate_return_from_sp->GetSymbolContext(eSymbolContextFunction); + if (sc.function) { + m_immediate_step_from_function = sc.function; + } + } + } +} + +void ThreadPlanStepOut::SetupAvoidNoDebug( + LazyBool step_out_avoids_code_without_debug_info) { + bool avoid_nodebug = true; + switch (step_out_avoids_code_without_debug_info) { + case eLazyBoolYes: + avoid_nodebug = true; + break; + case eLazyBoolNo: + avoid_nodebug = false; + break; + case eLazyBoolCalculate: + avoid_nodebug = m_thread.GetStepOutAvoidsNoDebug(); + break; + } + if (avoid_nodebug) + GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); + else + GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); +} + +void ThreadPlanStepOut::DidPush() { + if (m_step_out_to_inline_plan_sp) + m_thread.QueueThreadPlan(m_step_out_to_inline_plan_sp, false); + else if (m_step_through_inline_plan_sp) + m_thread.QueueThreadPlan(m_step_through_inline_plan_sp, false); +} + +ThreadPlanStepOut::~ThreadPlanStepOut() { + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) + m_thread.CalculateTarget()->RemoveBreakpointByID(m_return_bp_id); +} + +void ThreadPlanStepOut::GetDescription(Stream *s, + lldb::DescriptionLevel level) { + if (level == lldb::eDescriptionLevelBrief) + s->Printf("step out"); + else { + if (m_step_out_to_inline_plan_sp) + s->Printf("Stepping out to inlined frame so we can walk through it."); + else if (m_step_through_inline_plan_sp) + s->Printf("Stepping out by stepping through inlined function."); + else { + s->Printf("Stepping out from "); + Address tmp_address; + if (tmp_address.SetLoadAddress(m_step_from_insn, &GetTarget())) { + tmp_address.Dump(s, &GetThread(), Address::DumpStyleResolvedDescription, + Address::DumpStyleLoadAddress); + } else { + s->Printf("address 0x%" PRIx64 "", (uint64_t)m_step_from_insn); + } + + // FIXME: find some useful way to present the m_return_id, since there may + // be multiple copies of the + // same function on the stack. + + s->Printf(" returning to frame at "); + if (tmp_address.SetLoadAddress(m_return_addr, &GetTarget())) { + tmp_address.Dump(s, &GetThread(), Address::DumpStyleResolvedDescription, + Address::DumpStyleLoadAddress); + } else { + s->Printf("address 0x%" PRIx64 "", (uint64_t)m_return_addr); + } + + if (level == eDescriptionLevelVerbose) + s->Printf(" using breakpoint site %d", m_return_bp_id); + } + } + + s->Printf("\n"); + for (StackFrameSP frame_sp : m_stepped_past_frames) { + s->Printf("Stepped out past: "); + frame_sp->DumpUsingSettingsFormat(s); + } +} + +bool ThreadPlanStepOut::ValidatePlan(Stream *error) { + if (m_step_out_to_inline_plan_sp) + return m_step_out_to_inline_plan_sp->ValidatePlan(error); + + if (m_step_through_inline_plan_sp) + return m_step_through_inline_plan_sp->ValidatePlan(error); + + if (m_could_not_resolve_hw_bp) { + if (error) + error->PutCString( + "Could not create hardware breakpoint for thread plan."); + return false; + } + + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) { + if (error) { + error->PutCString("Could not create return address breakpoint."); + if (m_constructor_errors.GetSize() > 0) { + error->PutCString(" "); + error->PutCString(m_constructor_errors.GetString()); + } + } + return false; + } + + return true; +} + +bool ThreadPlanStepOut::DoPlanExplainsStop(Event *event_ptr) { + // If the step out plan is done, then we just need to step through the + // inlined frame. + if (m_step_out_to_inline_plan_sp) { + return m_step_out_to_inline_plan_sp->MischiefManaged(); + } else if (m_step_through_inline_plan_sp) { + if (m_step_through_inline_plan_sp->MischiefManaged()) { + CalculateReturnValue(); + SetPlanComplete(); + return true; + } else + return false; + } else if (m_step_out_further_plan_sp) { + return m_step_out_further_plan_sp->MischiefManaged(); + } + + // We don't explain signals or breakpoints (breakpoints that handle stepping + // in or out will be handled by a child plan. + + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + if (stop_info_sp) { + StopReason reason = stop_info_sp->GetStopReason(); + if (reason == eStopReasonBreakpoint) { + // If this is OUR breakpoint, we're fine, otherwise we don't know why + // this happened... + BreakpointSiteSP site_sp( + m_thread.GetProcess()->GetBreakpointSiteList().FindByID( + stop_info_sp->GetValue())); + if (site_sp && site_sp->IsBreakpointAtThisSite(m_return_bp_id)) { + bool done; + + StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + + if (m_step_out_to_id == frame_zero_id) + done = true; + else if (m_step_out_to_id < frame_zero_id) { + // Either we stepped past the breakpoint, or the stack ID calculation + // was incorrect and we should probably stop. + done = true; + } else { + done = (m_immediate_step_from_id < frame_zero_id); + } + + if (done) { + if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) { + CalculateReturnValue(); + SetPlanComplete(); + } + } + + // If there was only one owner, then we're done. But if we also hit + // some user breakpoint on our way out, we should mark ourselves as + // done, but also not claim to explain the stop, since it is more + // important to report the user breakpoint than the step out + // completion. + + if (site_sp->GetNumberOfOwners() == 1) + return true; + } + return false; + } else if (IsUsuallyUnexplainedStopReason(reason)) + return false; + else + return true; + } + return true; +} + +bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) { + if (IsPlanComplete()) + return true; + + bool done = false; + if (m_step_out_to_inline_plan_sp) { + if (m_step_out_to_inline_plan_sp->MischiefManaged()) { + // Now step through the inlined stack we are in: + if (QueueInlinedStepPlan(true)) { + // If we can't queue a plan to do this, then just call ourselves done. + m_step_out_to_inline_plan_sp.reset(); + SetPlanComplete(false); + return true; + } else + done = true; + } else + return m_step_out_to_inline_plan_sp->ShouldStop(event_ptr); + } else if (m_step_through_inline_plan_sp) { + if (m_step_through_inline_plan_sp->MischiefManaged()) + done = true; + else + return m_step_through_inline_plan_sp->ShouldStop(event_ptr); + } else if (m_step_out_further_plan_sp) { + if (m_step_out_further_plan_sp->MischiefManaged()) + m_step_out_further_plan_sp.reset(); + else + return m_step_out_further_plan_sp->ShouldStop(event_ptr); + } + + if (!done) { + StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + done = !(frame_zero_id < m_step_out_to_id); + } + + // The normal step out computations think we are done, so all we need to do + // is consult the ShouldStopHere, and we are done. + + if (done) { + if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) { + CalculateReturnValue(); + SetPlanComplete(); + } else { + m_step_out_further_plan_sp = + QueueStepOutFromHerePlan(m_flags, eFrameCompareOlder, m_status); + done = false; + } + } + + return done; +} + +bool ThreadPlanStepOut::StopOthers() { return m_stop_others; } + +StateType ThreadPlanStepOut::GetPlanRunState() { return eStateRunning; } + +bool ThreadPlanStepOut::DoWillResume(StateType resume_state, + bool current_plan) { + if (m_step_out_to_inline_plan_sp || m_step_through_inline_plan_sp) + return true; + + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + + if (current_plan) { + Breakpoint *return_bp = + m_thread.CalculateTarget()->GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != nullptr) + return_bp->SetEnabled(true); + } + return true; +} + +bool ThreadPlanStepOut::WillStop() { + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) { + Breakpoint *return_bp = + m_thread.CalculateTarget()->GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != nullptr) + return_bp->SetEnabled(false); + } + + return true; +} + +bool ThreadPlanStepOut::MischiefManaged() { + if (IsPlanComplete()) { + // Did I reach my breakpoint? If so I'm done. + // + // I also check the stack depth, since if we've blown past the breakpoint + // for some + // reason and we're now stopping for some other reason altogether, then + // we're done with this step out operation. + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) + LLDB_LOGF(log, "Completed step out plan."); + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) { + m_thread.CalculateTarget()->RemoveBreakpointByID(m_return_bp_id); + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + + ThreadPlan::MischiefManaged(); + return true; + } else { + return false; + } +} + +bool ThreadPlanStepOut::QueueInlinedStepPlan(bool queue_now) { + // Now figure out the range of this inlined block, and set up a "step through + // range" plan for that. If we've been provided with a context, then use the + // block in that context. + StackFrameSP immediate_return_from_sp(m_thread.GetStackFrameAtIndex(0)); + if (!immediate_return_from_sp) + return false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) { + StreamString s; + immediate_return_from_sp->Dump(&s, true, false); + LLDB_LOGF(log, "Queuing inlined frame to step past: %s.", s.GetData()); + } + + Block *from_block = immediate_return_from_sp->GetFrameBlock(); + if (from_block) { + Block *inlined_block = from_block->GetContainingInlinedBlock(); + if (inlined_block) { + size_t num_ranges = inlined_block->GetNumRanges(); + AddressRange inline_range; + if (inlined_block->GetRangeAtIndex(0, inline_range)) { + SymbolContext inlined_sc; + inlined_block->CalculateSymbolContext(&inlined_sc); + inlined_sc.target_sp = GetTarget().shared_from_this(); + RunMode run_mode = + m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads; + const LazyBool avoid_no_debug = eLazyBoolNo; + + m_step_through_inline_plan_sp = + std::make_shared<ThreadPlanStepOverRange>( + m_thread, inline_range, inlined_sc, run_mode, avoid_no_debug); + ThreadPlanStepOverRange *step_through_inline_plan_ptr = + static_cast<ThreadPlanStepOverRange *>( + m_step_through_inline_plan_sp.get()); + m_step_through_inline_plan_sp->SetPrivate(true); + + step_through_inline_plan_ptr->SetOkayToDiscard(true); + StreamString errors; + if (!step_through_inline_plan_ptr->ValidatePlan(&errors)) { + // FIXME: Log this failure. + delete step_through_inline_plan_ptr; + return false; + } + + for (size_t i = 1; i < num_ranges; i++) { + if (inlined_block->GetRangeAtIndex(i, inline_range)) + step_through_inline_plan_ptr->AddRange(inline_range); + } + + if (queue_now) + m_thread.QueueThreadPlan(m_step_through_inline_plan_sp, false); + return true; + } + } + } + + return false; +} + +void ThreadPlanStepOut::CalculateReturnValue() { + if (m_return_valobj_sp) + return; + + if (!m_calculate_return_value) + return; + + if (m_immediate_step_from_function != nullptr) { + CompilerType return_compiler_type = + m_immediate_step_from_function->GetCompilerType() + .GetFunctionReturnType(); + if (return_compiler_type) { + lldb::ABISP abi_sp = m_thread.GetProcess()->GetABI(); + if (abi_sp) + m_return_valobj_sp = + abi_sp->GetReturnValueObject(m_thread, return_compiler_type); + } + } +} + +bool ThreadPlanStepOut::IsPlanStale() { + // If we are still lower on the stack than the frame we are returning to, + // then there's something for us to do. Otherwise, we're stale. + + StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + return !(frame_zero_id < m_step_out_to_id); +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp new file mode 100644 index 00000000000..725669b1e9a --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -0,0 +1,185 @@ +//===-- ThreadPlanStepOverBreakpoint.cpp ------------------------*- C++ -*-===// +// +// 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/ThreadPlanStepOverBreakpoint.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanStepOverBreakpoint: Single steps over a breakpoint bp_site_sp at +// the pc. + +ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint(Thread &thread) + : ThreadPlan( + ThreadPlan::eKindStepOverBreakpoint, "Step over breakpoint trap", + thread, eVoteNo, + eVoteNoOpinion), // We need to report the run since this happens + // first in the thread plan stack when stepping over + // a breakpoint + m_breakpoint_addr(LLDB_INVALID_ADDRESS), + m_auto_continue(false), m_reenabled_breakpoint_site(false) + +{ + m_breakpoint_addr = m_thread.GetRegisterContext()->GetPC(); + m_breakpoint_site_id = + m_thread.GetProcess()->GetBreakpointSiteList().FindIDByAddress( + m_breakpoint_addr); +} + +ThreadPlanStepOverBreakpoint::~ThreadPlanStepOverBreakpoint() {} + +void ThreadPlanStepOverBreakpoint::GetDescription( + Stream *s, lldb::DescriptionLevel level) { + s->Printf("Single stepping past breakpoint site %" PRIu64 " at 0x%" PRIx64, + m_breakpoint_site_id, (uint64_t)m_breakpoint_addr); +} + +bool ThreadPlanStepOverBreakpoint::ValidatePlan(Stream *error) { return true; } + +bool ThreadPlanStepOverBreakpoint::DoPlanExplainsStop(Event *event_ptr) { + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + if (stop_info_sp) { + // It's a little surprising that we stop here for a breakpoint hit. + // However, when you single step ONTO a breakpoint we still want to call + // that a breakpoint hit, and trigger the actions, etc. Otherwise you + // would see the + // PC at the breakpoint without having triggered the actions, then you'd + // continue, the PC wouldn't change, + // and you'd see the breakpoint hit, which would be odd. So the lower + // levels fake "step onto breakpoint address" and return that as a + // breakpoint. So our trace step COULD appear as a breakpoint hit if the + // next instruction also contained a breakpoint. + StopReason reason = stop_info_sp->GetStopReason(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, "Step over breakpoint stopped for reason: %s.", + Thread::StopReasonAsCString(reason)); + + switch (reason) { + case eStopReasonTrace: + case eStopReasonNone: + return true; + case eStopReasonBreakpoint: + { + // It's a little surprising that we stop here for a breakpoint hit. + // However, when you single step ONTO a breakpoint we still want to call + // that a breakpoint hit, and trigger the actions, etc. Otherwise you + // would see the PC at the breakpoint without having triggered the + // actions, then you'd continue, the PC wouldn't change, and you'd see + // the breakpoint hit, which would be odd. So the lower levels fake + // "step onto breakpoint address" and return that as a breakpoint hit. + // So our trace step COULD appear as a breakpoint hit if the next + // instruction also contained a breakpoint. We don't want to handle + // that, since we really don't know what to do with breakpoint hits. + // But make sure we don't set ourselves to auto-continue or we'll wrench + // control away from the plans that can deal with this. + // Be careful, however, as we may have "seen a breakpoint under the PC + // because we stopped without changing the PC, in which case we do want + // to re-claim this stop so we'll try again. + lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC(); + + if (pc_addr == m_breakpoint_addr) { + LLDB_LOGF(log, + "Got breakpoint stop reason but pc: 0x%" PRIx64 + "hasn't changed.", + pc_addr); + return true; + } + + SetAutoContinue(false); + return false; + } + default: + return false; + } + } + return false; +} + +bool ThreadPlanStepOverBreakpoint::ShouldStop(Event *event_ptr) { + return !ShouldAutoContinue(event_ptr); +} + +bool ThreadPlanStepOverBreakpoint::StopOthers() { return true; } + +StateType ThreadPlanStepOverBreakpoint::GetPlanRunState() { + return eStateStepping; +} + +bool ThreadPlanStepOverBreakpoint::DoWillResume(StateType resume_state, + bool current_plan) { + if (current_plan) { + BreakpointSiteSP bp_site_sp( + m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress( + m_breakpoint_addr)); + if (bp_site_sp && bp_site_sp->IsEnabled()) { + m_thread.GetProcess()->DisableBreakpointSite(bp_site_sp.get()); + m_reenabled_breakpoint_site = false; + } + } + return true; +} + +bool ThreadPlanStepOverBreakpoint::WillStop() { + ReenableBreakpointSite(); + return true; +} + +void ThreadPlanStepOverBreakpoint::WillPop() { + ReenableBreakpointSite(); +} + +bool ThreadPlanStepOverBreakpoint::MischiefManaged() { + lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC(); + + if (pc_addr == m_breakpoint_addr) { + // If we are still at the PC of our breakpoint, then for some reason we + // didn't get a chance to run. + return false; + } else { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, "Completed step over breakpoint plan."); + // Otherwise, re-enable the breakpoint we were stepping over, and we're + // done. + ReenableBreakpointSite(); + ThreadPlan::MischiefManaged(); + return true; + } +} + +void ThreadPlanStepOverBreakpoint::ReenableBreakpointSite() { + if (!m_reenabled_breakpoint_site) { + m_reenabled_breakpoint_site = true; + BreakpointSiteSP bp_site_sp( + m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress( + m_breakpoint_addr)); + if (bp_site_sp) { + m_thread.GetProcess()->EnableBreakpointSite(bp_site_sp.get()); + } + } +} +void ThreadPlanStepOverBreakpoint::ThreadDestroyed() { + ReenableBreakpointSite(); +} + +void ThreadPlanStepOverBreakpoint::SetAutoContinue(bool do_it) { + m_auto_continue = do_it; +} + +bool ThreadPlanStepOverBreakpoint::ShouldAutoContinue(Event *event_ptr) { + return m_auto_continue; +} + +bool ThreadPlanStepOverBreakpoint::IsPlanStale() { + return m_thread.GetRegisterContext()->GetPC() != m_breakpoint_addr; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanStepOverRange.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanStepOverRange.cpp new file mode 100644 index 00000000000..3dc1967e6d4 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanStepOverRange.cpp @@ -0,0 +1,414 @@ +//===-- ThreadPlanStepOverRange.cpp -----------------------------*- C++ -*-===// +// +// 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/ThreadPlanStepOverRange.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepThrough.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb_private; +using namespace lldb; + +uint32_t ThreadPlanStepOverRange::s_default_flag_values = 0; + +// ThreadPlanStepOverRange: Step through a stack range, either stepping over or +// into based on the value of \a type. + +ThreadPlanStepOverRange::ThreadPlanStepOverRange( + Thread &thread, const AddressRange &range, + const SymbolContext &addr_context, lldb::RunMode stop_others, + LazyBool step_out_avoids_code_without_debug_info) + : ThreadPlanStepRange(ThreadPlan::eKindStepOverRange, + "Step range stepping over", thread, range, + addr_context, stop_others), + ThreadPlanShouldStopHere(this), m_first_resume(true) { + SetFlagsToDefault(); + SetupAvoidNoDebug(step_out_avoids_code_without_debug_info); +} + +ThreadPlanStepOverRange::~ThreadPlanStepOverRange() = default; + +void ThreadPlanStepOverRange::GetDescription(Stream *s, + lldb::DescriptionLevel level) { + auto PrintFailureIfAny = [&]() { + if (m_status.Success()) + return; + s->Printf(" failed (%s)", m_status.AsCString()); + }; + + if (level == lldb::eDescriptionLevelBrief) { + s->Printf("step over"); + PrintFailureIfAny(); + return; + } + + s->Printf("Stepping over"); + bool printed_line_info = false; + if (m_addr_context.line_entry.IsValid()) { + s->Printf(" line "); + m_addr_context.line_entry.DumpStopContext(s, false); + printed_line_info = true; + } + + if (!printed_line_info || level == eDescriptionLevelVerbose) { + s->Printf(" using ranges: "); + DumpRanges(s); + } + + PrintFailureIfAny(); + + s->PutChar('.'); +} + +void ThreadPlanStepOverRange::SetupAvoidNoDebug( + LazyBool step_out_avoids_code_without_debug_info) { + bool avoid_nodebug = true; + switch (step_out_avoids_code_without_debug_info) { + case eLazyBoolYes: + avoid_nodebug = true; + break; + case eLazyBoolNo: + avoid_nodebug = false; + break; + case eLazyBoolCalculate: + avoid_nodebug = m_thread.GetStepOutAvoidsNoDebug(); + break; + } + if (avoid_nodebug) + GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); + else + GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); + // Step Over plans should always avoid no-debug on step in. Seems like you + // shouldn't have to say this, but a tail call looks more like a step in that + // a step out, so we want to catch this case. + GetFlags().Set(ThreadPlanShouldStopHere::eStepInAvoidNoDebug); +} + +bool ThreadPlanStepOverRange::IsEquivalentContext( + const SymbolContext &context) { + // Match as much as is specified in the m_addr_context: This is a fairly + // loose sanity check. Note, sometimes the target doesn't get filled in so I + // left out the target check. And sometimes the module comes in as the .o + // file from the inlined range, so I left that out too... + if (m_addr_context.comp_unit) { + if (m_addr_context.comp_unit != context.comp_unit) + return false; + if (m_addr_context.function) { + if (m_addr_context.function != context.function) + return false; + // It is okay to return to a different block of a straight function, we + // only have to be more careful if returning from one inlined block to + // another. + if (m_addr_context.block->GetInlinedFunctionInfo() == nullptr && + context.block->GetInlinedFunctionInfo() == nullptr) + return true; + return m_addr_context.block == context.block; + } + } + // Fall back to symbol if we have no decision from comp_unit/function/block. + return m_addr_context.symbol && m_addr_context.symbol == context.symbol; +} + +bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (log) { + StreamString s; + DumpAddress( + s.AsRawOstream(), m_thread.GetRegisterContext()->GetPC(), + m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize()); + LLDB_LOGF(log, "ThreadPlanStepOverRange reached %s.", s.GetData()); + } + + // If we're out of the range but in the same frame or in our caller's frame + // then we should stop. When stepping out we only stop others if we are + // forcing running one thread. + bool stop_others = (m_stop_others == lldb::eOnlyThisThread); + ThreadPlanSP new_plan_sp; + FrameComparison frame_order = CompareCurrentFrameToStartFrame(); + + if (frame_order == eFrameCompareOlder) { + // If we're in an older frame then we should stop. + // + // A caveat to this is if we think the frame is older but we're actually in + // a trampoline. + // I'm going to make the assumption that you wouldn't RETURN to a + // trampoline. So if we are in a trampoline we think the frame is older + // because the trampoline confused the backtracer. As below, we step + // through first, and then try to figure out how to get back out again. + + new_plan_sp = m_thread.QueueThreadPlanForStepThrough(m_stack_id, false, + stop_others, m_status); + + if (new_plan_sp && log) + LLDB_LOGF(log, + "Thought I stepped out, but in fact arrived at a trampoline."); + } else if (frame_order == eFrameCompareYounger) { + // Make sure we really are in a new frame. Do that by unwinding and seeing + // if the start function really is our start function... + for (uint32_t i = 1;; ++i) { + StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(i); + if (!older_frame_sp) { + // We can't unwind the next frame we should just get out of here & + // stop... + break; + } + + const SymbolContext &older_context = + older_frame_sp->GetSymbolContext(eSymbolContextEverything); + if (IsEquivalentContext(older_context)) { + new_plan_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop( + false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 0, + m_status, true); + break; + } else { + new_plan_sp = m_thread.QueueThreadPlanForStepThrough( + m_stack_id, false, stop_others, m_status); + // If we found a way through, then we should stop recursing. + if (new_plan_sp) + break; + } + } + } else { + // If we're still in the range, keep going. + if (InRange()) { + SetNextBranchBreakpoint(); + return false; + } + + if (!InSymbol()) { + // This one is a little tricky. Sometimes we may be in a stub or + // something similar, in which case we need to get out of there. But if + // we are in a stub then it's likely going to be hard to get out from + // here. It is probably easiest to step into the stub, and then it will + // be straight-forward to step out. + new_plan_sp = m_thread.QueueThreadPlanForStepThrough( + m_stack_id, false, stop_others, m_status); + } else { + // The current clang (at least through 424) doesn't always get the + // address range for the DW_TAG_inlined_subroutines right, so that when + // you leave the inlined range the line table says you are still in the + // source file of the inlining function. This is bad, because now you + // are missing the stack frame for the function containing the inlining, + // and if you sensibly do "finish" to get out of this function you will + // instead exit the containing function. To work around this, we check + // whether we are still in the source file we started in, and if not + // assume it is an error, and push a plan to get us out of this line and + // back to the containing file. + + if (m_addr_context.line_entry.IsValid()) { + SymbolContext sc; + StackFrameSP frame_sp = m_thread.GetStackFrameAtIndex(0); + sc = frame_sp->GetSymbolContext(eSymbolContextEverything); + if (sc.line_entry.IsValid()) { + if (sc.line_entry.original_file != + m_addr_context.line_entry.original_file && + sc.comp_unit == m_addr_context.comp_unit && + sc.function == m_addr_context.function) { + // Okay, find the next occurrence of this file in the line table: + LineTable *line_table = m_addr_context.comp_unit->GetLineTable(); + if (line_table) { + Address cur_address = frame_sp->GetFrameCodeAddress(); + uint32_t entry_idx; + LineEntry line_entry; + if (line_table->FindLineEntryByAddress(cur_address, line_entry, + &entry_idx)) { + LineEntry next_line_entry; + bool step_past_remaining_inline = false; + if (entry_idx > 0) { + // We require the previous line entry and the current line + // entry come from the same file. The other requirement is + // that the previous line table entry be part of an inlined + // block, we don't want to step past cases where people have + // inlined some code fragment by using #include <source- + // fragment.c> directly. + LineEntry prev_line_entry; + if (line_table->GetLineEntryAtIndex(entry_idx - 1, + prev_line_entry) && + prev_line_entry.original_file == + line_entry.original_file) { + SymbolContext prev_sc; + Address prev_address = + prev_line_entry.range.GetBaseAddress(); + prev_address.CalculateSymbolContext(&prev_sc); + if (prev_sc.block) { + Block *inlined_block = + prev_sc.block->GetContainingInlinedBlock(); + if (inlined_block) { + AddressRange inline_range; + inlined_block->GetRangeContainingAddress(prev_address, + inline_range); + if (!inline_range.ContainsFileAddress(cur_address)) { + + step_past_remaining_inline = true; + } + } + } + } + } + + if (step_past_remaining_inline) { + uint32_t look_ahead_step = 1; + while (line_table->GetLineEntryAtIndex( + entry_idx + look_ahead_step, next_line_entry)) { + // Make sure we haven't wandered out of the function we + // started from... + Address next_line_address = + next_line_entry.range.GetBaseAddress(); + Function *next_line_function = + next_line_address.CalculateSymbolContextFunction(); + if (next_line_function != m_addr_context.function) + break; + + if (next_line_entry.original_file == + m_addr_context.line_entry.original_file) { + const bool abort_other_plans = false; + const RunMode stop_other_threads = RunMode::eAllThreads; + lldb::addr_t cur_pc = m_thread.GetStackFrameAtIndex(0) + ->GetRegisterContext() + ->GetPC(); + AddressRange step_range( + cur_pc, + next_line_address.GetLoadAddress(&GetTarget()) - + cur_pc); + + new_plan_sp = m_thread.QueueThreadPlanForStepOverRange( + abort_other_plans, step_range, sc, stop_other_threads, + m_status); + break; + } + look_ahead_step++; + } + } + } + } + } + } + } + } + } + + // If we get to this point, we're not going to use a previously set "next + // branch" breakpoint, so delete it: + ClearNextBranchBreakpoint(); + + // If we haven't figured out something to do yet, then ask the ShouldStopHere + // callback: + if (!new_plan_sp) { + new_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order, m_status); + } + + if (!new_plan_sp) + m_no_more_plans = true; + else { + // Any new plan will be an implementation plan, so mark it private: + new_plan_sp->SetPrivate(true); + m_no_more_plans = false; + } + + if (!new_plan_sp) { + // For efficiencies sake, we know we're done here so we don't have to do + // this calculation again in MischiefManaged. + SetPlanComplete(m_status.Success()); + return true; + } else + return false; +} + +bool ThreadPlanStepOverRange::DoPlanExplainsStop(Event *event_ptr) { + // For crashes, breakpoint hits, signals, etc, let the base plan (or some + // plan above us) handle the stop. That way the user can see the stop, step + // around, and then when they are done, continue and have their step + // complete. The exception is if we've hit our "run to next branch" + // breakpoint. Note, unlike the step in range plan, we don't mark ourselves + // complete if we hit an unexplained breakpoint/crash. + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + bool return_value; + + if (stop_info_sp) { + StopReason reason = stop_info_sp->GetStopReason(); + + if (reason == eStopReasonTrace) { + return_value = true; + } else if (reason == eStopReasonBreakpoint) { + return_value = NextRangeBreakpointExplainsStop(stop_info_sp); + } else { + if (log) + log->PutCString("ThreadPlanStepInRange got asked if it explains the " + "stop for some reason other than step."); + return_value = false; + } + } else + return_value = true; + + return return_value; +} + +bool ThreadPlanStepOverRange::DoWillResume(lldb::StateType resume_state, + bool current_plan) { + if (resume_state != eStateSuspended && m_first_resume) { + m_first_resume = false; + if (resume_state == eStateStepping && current_plan) { + // See if we are about to step over an inlined call in the middle of the + // inlined stack, if so figure out its extents and reset our range to + // step over that. + bool in_inlined_stack = m_thread.DecrementCurrentInlinedDepth(); + if (in_inlined_stack) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, + "ThreadPlanStepInRange::DoWillResume: adjusting range to " + "the frame at inlined depth %d.", + m_thread.GetCurrentInlinedDepth()); + StackFrameSP stack_sp = m_thread.GetStackFrameAtIndex(0); + if (stack_sp) { + Block *frame_block = stack_sp->GetFrameBlock(); + lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC(); + AddressRange my_range; + if (frame_block->GetRangeContainingLoadAddress( + curr_pc, m_thread.GetProcess()->GetTarget(), my_range)) { + m_address_ranges.clear(); + m_address_ranges.push_back(my_range); + if (log) { + StreamString s; + const InlineFunctionInfo *inline_info = + frame_block->GetInlinedFunctionInfo(); + const char *name; + if (inline_info) + name = + inline_info + ->GetName(frame_block->CalculateSymbolContextFunction() + ->GetLanguage()) + .AsCString(); + else + name = "<unknown-notinlined>"; + + s.Printf( + "Stepping over inlined function \"%s\" in inlined stack: ", + name); + DumpRanges(&s); + log->PutString(s.GetString()); + } + } + } + } + } + } + + return true; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanStepRange.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanStepRange.cpp new file mode 100644 index 00000000000..d1c56165da5 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanStepRange.cpp @@ -0,0 +1,504 @@ +//===-- ThreadPlanStepRange.cpp ---------------------------------*- C++ -*-===// +// +// 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/ThreadPlanStepRange.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanStepRange: Step through a stack range, either stepping over or +// into based on the value of \a type. + +ThreadPlanStepRange::ThreadPlanStepRange(ThreadPlanKind kind, const char *name, + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others, + bool given_ranges_only) + : ThreadPlan(kind, name, thread, eVoteNoOpinion, eVoteNoOpinion), + m_addr_context(addr_context), m_address_ranges(), + m_stop_others(stop_others), m_stack_id(), m_parent_stack_id(), + m_no_more_plans(false), m_first_run_event(true), m_use_fast_step(false), + m_given_ranges_only(given_ranges_only) { + m_use_fast_step = GetTarget().GetUseFastStepping(); + AddRange(range); + m_stack_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + StackFrameSP parent_stack = m_thread.GetStackFrameAtIndex(1); + if (parent_stack) + m_parent_stack_id = parent_stack->GetStackID(); +} + +ThreadPlanStepRange::~ThreadPlanStepRange() { ClearNextBranchBreakpoint(); } + +void ThreadPlanStepRange::DidPush() { + // See if we can find a "next range" breakpoint: + SetNextBranchBreakpoint(); +} + +bool ThreadPlanStepRange::ValidatePlan(Stream *error) { + if (m_could_not_resolve_hw_bp) { + if (error) + error->PutCString( + "Could not create hardware breakpoint for thread plan."); + return false; + } + return true; +} + +Vote ThreadPlanStepRange::ShouldReportStop(Event *event_ptr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + const Vote vote = IsPlanComplete() ? eVoteYes : eVoteNo; + LLDB_LOGF(log, "ThreadPlanStepRange::ShouldReportStop() returning vote %i\n", + vote); + return vote; +} + +void ThreadPlanStepRange::AddRange(const AddressRange &new_range) { + // For now I'm just adding the ranges. At some point we may want to condense + // the ranges if they overlap, though I don't think it is likely to be very + // important. + m_address_ranges.push_back(new_range); + + // Fill the slot for this address range with an empty DisassemblerSP in the + // instruction ranges. I want the indices to match, but I don't want to do + // the work to disassemble this range if I don't step into it. + m_instruction_ranges.push_back(DisassemblerSP()); +} + +void ThreadPlanStepRange::DumpRanges(Stream *s) { + size_t num_ranges = m_address_ranges.size(); + if (num_ranges == 1) { + m_address_ranges[0].Dump(s, m_thread.CalculateTarget().get(), + Address::DumpStyleLoadAddress); + } else { + for (size_t i = 0; i < num_ranges; i++) { + s->Printf(" %" PRIu64 ": ", uint64_t(i)); + m_address_ranges[i].Dump(s, m_thread.CalculateTarget().get(), + Address::DumpStyleLoadAddress); + } + } +} + +bool ThreadPlanStepRange::InRange() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + bool ret_value = false; + + lldb::addr_t pc_load_addr = m_thread.GetRegisterContext()->GetPC(); + + size_t num_ranges = m_address_ranges.size(); + for (size_t i = 0; i < num_ranges; i++) { + ret_value = m_address_ranges[i].ContainsLoadAddress( + pc_load_addr, m_thread.CalculateTarget().get()); + if (ret_value) + break; + } + + if (!ret_value && !m_given_ranges_only) { + // See if we've just stepped to another part of the same line number... + StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get(); + + SymbolContext new_context( + frame->GetSymbolContext(eSymbolContextEverything)); + if (m_addr_context.line_entry.IsValid() && + new_context.line_entry.IsValid()) { + if (m_addr_context.line_entry.original_file == + new_context.line_entry.original_file) { + if (m_addr_context.line_entry.line == new_context.line_entry.line) { + m_addr_context = new_context; + const bool include_inlined_functions = + GetKind() == eKindStepOverRange; + AddRange(m_addr_context.line_entry.GetSameLineContiguousAddressRange( + include_inlined_functions)); + ret_value = true; + if (log) { + StreamString s; + m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(), + true, Address::DumpStyleLoadAddress, + Address::DumpStyleLoadAddress, true); + + LLDB_LOGF( + log, + "Step range plan stepped to another range of same line: %s", + s.GetData()); + } + } else if (new_context.line_entry.line == 0) { + new_context.line_entry.line = m_addr_context.line_entry.line; + m_addr_context = new_context; + const bool include_inlined_functions = + GetKind() == eKindStepOverRange; + AddRange(m_addr_context.line_entry.GetSameLineContiguousAddressRange( + include_inlined_functions)); + ret_value = true; + if (log) { + StreamString s; + m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(), + true, Address::DumpStyleLoadAddress, + Address::DumpStyleLoadAddress, true); + + LLDB_LOGF(log, + "Step range plan stepped to a range at linenumber 0 " + "stepping through that range: %s", + s.GetData()); + } + } else if (new_context.line_entry.range.GetBaseAddress().GetLoadAddress( + m_thread.CalculateTarget().get()) != pc_load_addr) { + // Another thing that sometimes happens here is that we step out of + // one line into the MIDDLE of another line. So far I mostly see + // this due to bugs in the debug information. But we probably don't + // want to be in the middle of a line range, so in that case reset + // the stepping range to the line we've stepped into the middle of + // and continue. + m_addr_context = new_context; + m_address_ranges.clear(); + AddRange(m_addr_context.line_entry.range); + ret_value = true; + if (log) { + StreamString s; + m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(), + true, Address::DumpStyleLoadAddress, + Address::DumpStyleLoadAddress, true); + + LLDB_LOGF(log, + "Step range plan stepped to the middle of new " + "line(%d): %s, continuing to clear this line.", + new_context.line_entry.line, s.GetData()); + } + } + } + } + } + + if (!ret_value && log) + LLDB_LOGF(log, "Step range plan out of range to 0x%" PRIx64, pc_load_addr); + + return ret_value; +} + +bool ThreadPlanStepRange::InSymbol() { + lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC(); + if (m_addr_context.function != nullptr) { + return m_addr_context.function->GetAddressRange().ContainsLoadAddress( + cur_pc, m_thread.CalculateTarget().get()); + } else if (m_addr_context.symbol && m_addr_context.symbol->ValueIsAddress()) { + AddressRange range(m_addr_context.symbol->GetAddressRef(), + m_addr_context.symbol->GetByteSize()); + return range.ContainsLoadAddress(cur_pc, m_thread.CalculateTarget().get()); + } + return false; +} + +// FIXME: This should also handle inlining if we aren't going to do inlining in +// the +// main stack. +// +// Ideally we should remember the whole stack frame list, and then compare that +// to the current list. + +lldb::FrameComparison ThreadPlanStepRange::CompareCurrentFrameToStartFrame() { + FrameComparison frame_order; + + StackID cur_frame_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + + if (cur_frame_id == m_stack_id) { + frame_order = eFrameCompareEqual; + } else if (cur_frame_id < m_stack_id) { + frame_order = eFrameCompareYounger; + } else { + StackFrameSP cur_parent_frame = m_thread.GetStackFrameAtIndex(1); + StackID cur_parent_id; + if (cur_parent_frame) + cur_parent_id = cur_parent_frame->GetStackID(); + if (m_parent_stack_id.IsValid() && cur_parent_id.IsValid() && + m_parent_stack_id == cur_parent_id) + frame_order = eFrameCompareSameParent; + else + frame_order = eFrameCompareOlder; + } + return frame_order; +} + +bool ThreadPlanStepRange::StopOthers() { + switch (m_stop_others) { + case lldb::eOnlyThisThread: + return true; + case lldb::eOnlyDuringStepping: + // If there is a call in the range of the next branch breakpoint, + // then we should always run all threads, since a call can execute + // arbitrary code which might for instance take a lock that's held + // by another thread. + return !m_found_calls; + case lldb::eAllThreads: + return false; + } + llvm_unreachable("Unhandled run mode!"); +} + +InstructionList *ThreadPlanStepRange::GetInstructionsForAddress( + lldb::addr_t addr, size_t &range_index, size_t &insn_offset) { + size_t num_ranges = m_address_ranges.size(); + for (size_t i = 0; i < num_ranges; i++) { + if (m_address_ranges[i].ContainsLoadAddress(addr, &GetTarget())) { + // Some joker added a zero size range to the stepping range... + if (m_address_ranges[i].GetByteSize() == 0) + return nullptr; + + if (!m_instruction_ranges[i]) { + // Disassemble the address range given: + ExecutionContext exe_ctx(m_thread.GetProcess()); + const char *plugin_name = nullptr; + const char *flavor = nullptr; + const bool prefer_file_cache = true; + m_instruction_ranges[i] = Disassembler::DisassembleRange( + GetTarget().GetArchitecture(), plugin_name, flavor, exe_ctx, + m_address_ranges[i], prefer_file_cache); + } + if (!m_instruction_ranges[i]) + return nullptr; + else { + // Find where we are in the instruction list as well. If we aren't at + // an instruction, return nullptr. In this case, we're probably lost, + // and shouldn't try to do anything fancy. + + insn_offset = + m_instruction_ranges[i] + ->GetInstructionList() + .GetIndexOfInstructionAtLoadAddress(addr, GetTarget()); + if (insn_offset == UINT32_MAX) + return nullptr; + else { + range_index = i; + return &m_instruction_ranges[i]->GetInstructionList(); + } + } + } + } + return nullptr; +} + +void ThreadPlanStepRange::ClearNextBranchBreakpoint() { + if (m_next_branch_bp_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, "Removing next branch breakpoint: %d.", + m_next_branch_bp_sp->GetID()); + GetTarget().RemoveBreakpointByID(m_next_branch_bp_sp->GetID()); + m_next_branch_bp_sp.reset(); + m_could_not_resolve_hw_bp = false; + m_found_calls = false; + } +} + +bool ThreadPlanStepRange::SetNextBranchBreakpoint() { + if (m_next_branch_bp_sp) + return true; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + // Stepping through ranges using breakpoints doesn't work yet, but with this + // off we fall back to instruction single stepping. + if (!m_use_fast_step) + return false; + + // clear the m_found_calls, we'll rediscover it for this range. + m_found_calls = false; + + lldb::addr_t cur_addr = GetThread().GetRegisterContext()->GetPC(); + // Find the current address in our address ranges, and fetch the disassembly + // if we haven't already: + size_t pc_index; + size_t range_index; + InstructionList *instructions = + GetInstructionsForAddress(cur_addr, range_index, pc_index); + if (instructions == nullptr) + return false; + else { + Target &target = GetThread().GetProcess()->GetTarget(); + const bool ignore_calls = GetKind() == eKindStepOverRange; + uint32_t branch_index = + instructions->GetIndexOfNextBranchInstruction(pc_index, target, + ignore_calls, + &m_found_calls); + + Address run_to_address; + + // If we didn't find a branch, run to the end of the range. + if (branch_index == UINT32_MAX) { + uint32_t last_index = instructions->GetSize() - 1; + if (last_index - pc_index > 1) { + InstructionSP last_inst = + instructions->GetInstructionAtIndex(last_index); + size_t last_inst_size = last_inst->GetOpcode().GetByteSize(); + run_to_address = last_inst->GetAddress(); + run_to_address.Slide(last_inst_size); + } + } else if (branch_index - pc_index > 1) { + run_to_address = + instructions->GetInstructionAtIndex(branch_index)->GetAddress(); + } + + if (run_to_address.IsValid()) { + const bool is_internal = true; + m_next_branch_bp_sp = + GetTarget().CreateBreakpoint(run_to_address, is_internal, false); + if (m_next_branch_bp_sp) { + + if (m_next_branch_bp_sp->IsHardware() && + !m_next_branch_bp_sp->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; + + if (log) { + lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID; + BreakpointLocationSP bp_loc = + m_next_branch_bp_sp->GetLocationAtIndex(0); + if (bp_loc) { + BreakpointSiteSP bp_site = bp_loc->GetBreakpointSite(); + if (bp_site) { + bp_site_id = bp_site->GetID(); + } + } + LLDB_LOGF(log, + "ThreadPlanStepRange::SetNextBranchBreakpoint - Setting " + "breakpoint %d (site %d) to run to address 0x%" PRIx64, + m_next_branch_bp_sp->GetID(), bp_site_id, + run_to_address.GetLoadAddress( + &m_thread.GetProcess()->GetTarget())); + } + + m_next_branch_bp_sp->SetThreadID(m_thread.GetID()); + m_next_branch_bp_sp->SetBreakpointKind("next-branch-location"); + + return true; + } else + return false; + } + } + return false; +} + +bool ThreadPlanStepRange::NextRangeBreakpointExplainsStop( + lldb::StopInfoSP stop_info_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (!m_next_branch_bp_sp) + return false; + + break_id_t bp_site_id = stop_info_sp->GetValue(); + BreakpointSiteSP bp_site_sp = + m_thread.GetProcess()->GetBreakpointSiteList().FindByID(bp_site_id); + if (!bp_site_sp) + return false; + else if (!bp_site_sp->IsBreakpointAtThisSite(m_next_branch_bp_sp->GetID())) + return false; + else { + // If we've hit the next branch breakpoint, then clear it. + size_t num_owners = bp_site_sp->GetNumberOfOwners(); + bool explains_stop = true; + // If all the owners are internal, then we are probably just stepping over + // this range from multiple threads, or multiple frames, so we want to + // continue. If one is not internal, then we should not explain the stop, + // and let the user breakpoint handle the stop. + for (size_t i = 0; i < num_owners; i++) { + if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) { + explains_stop = false; + break; + } + } + LLDB_LOGF(log, + "ThreadPlanStepRange::NextRangeBreakpointExplainsStop - Hit " + "next range breakpoint which has %" PRIu64 + " owners - explains stop: %u.", + (uint64_t)num_owners, explains_stop); + ClearNextBranchBreakpoint(); + return explains_stop; + } +} + +bool ThreadPlanStepRange::WillStop() { return true; } + +StateType ThreadPlanStepRange::GetPlanRunState() { + if (m_next_branch_bp_sp) + return eStateRunning; + else + return eStateStepping; +} + +bool ThreadPlanStepRange::MischiefManaged() { + // If we have pushed some plans between ShouldStop & MischiefManaged, then + // we're not done... + // I do this check first because we might have stepped somewhere that will + // fool InRange into + // thinking it needs to step past the end of that line. This happens, for + // instance, when stepping over inlined code that is in the middle of the + // current line. + + if (!m_no_more_plans) + return false; + + bool done = true; + if (!IsPlanComplete()) { + if (InRange()) { + done = false; + } else { + FrameComparison frame_order = CompareCurrentFrameToStartFrame(); + done = (frame_order != eFrameCompareOlder) ? m_no_more_plans : true; + } + } + + if (done) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, "Completed step through range plan."); + ClearNextBranchBreakpoint(); + ThreadPlan::MischiefManaged(); + return true; + } else { + return false; + } +} + +bool ThreadPlanStepRange::IsPlanStale() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + FrameComparison frame_order = CompareCurrentFrameToStartFrame(); + + if (frame_order == eFrameCompareOlder) { + if (log) { + LLDB_LOGF(log, "ThreadPlanStepRange::IsPlanStale returning true, we've " + "stepped out."); + } + return true; + } else if (frame_order == eFrameCompareEqual && InSymbol()) { + // If we are not in a place we should step through, we've gotten stale. One + // tricky bit here is that some stubs don't push a frame, so we should. + // check that we are in the same symbol. + if (!InRange()) { + // Set plan Complete when we reach next instruction just after the range + lldb::addr_t addr = m_thread.GetRegisterContext()->GetPC() - 1; + size_t num_ranges = m_address_ranges.size(); + for (size_t i = 0; i < num_ranges; i++) { + bool in_range = m_address_ranges[i].ContainsLoadAddress( + addr, m_thread.CalculateTarget().get()); + if (in_range) { + SetPlanComplete(); + } + } + return true; + } + } + return false; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanStepThrough.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanStepThrough.cpp new file mode 100644 index 00000000000..8c7b180fce2 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanStepThrough.cpp @@ -0,0 +1,266 @@ +//===-- ThreadPlanStepThrough.cpp -------------------------------*- C++ -*-===// +// +// 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/ThreadPlanStepThrough.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanStepThrough: If the current instruction is a trampoline, step +// through it If it is the beginning of the prologue of a function, step +// through that as well. +// FIXME: At present only handles DYLD trampolines. + +ThreadPlanStepThrough::ThreadPlanStepThrough(Thread &thread, + StackID &m_stack_id, + bool stop_others) + : ThreadPlan(ThreadPlan::eKindStepThrough, + "Step through trampolines and prologues", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_start_address(0), m_backstop_bkpt_id(LLDB_INVALID_BREAK_ID), + m_backstop_addr(LLDB_INVALID_ADDRESS), m_return_stack_id(m_stack_id), + m_stop_others(stop_others) { + LookForPlanToStepThroughFromCurrentPC(); + + // If we don't get a valid step through plan, don't bother to set up a + // backstop. + if (m_sub_plan_sp) { + m_start_address = GetThread().GetRegisterContext()->GetPC(0); + + // We are going to return back to the concrete frame 1, we might pass by + // some inlined code that we're in the middle of by doing this, but it's + // easier than trying to figure out where the inlined code might return to. + + StackFrameSP return_frame_sp = m_thread.GetFrameWithStackID(m_stack_id); + + if (return_frame_sp) { + m_backstop_addr = return_frame_sp->GetFrameCodeAddress().GetLoadAddress( + m_thread.CalculateTarget().get()); + Breakpoint *return_bp = + m_thread.GetProcess() + ->GetTarget() + .CreateBreakpoint(m_backstop_addr, true, false) + .get(); + + if (return_bp != nullptr) { + if (return_bp->IsHardware() && !return_bp->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; + return_bp->SetThreadID(m_thread.GetID()); + m_backstop_bkpt_id = return_bp->GetID(); + return_bp->SetBreakpointKind("step-through-backstop"); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) { + LLDB_LOGF(log, "Setting backstop breakpoint %d at address: 0x%" PRIx64, + m_backstop_bkpt_id, m_backstop_addr); + } + } + } +} + +ThreadPlanStepThrough::~ThreadPlanStepThrough() { ClearBackstopBreakpoint(); } + +void ThreadPlanStepThrough::DidPush() { + if (m_sub_plan_sp) + PushPlan(m_sub_plan_sp); +} + +void ThreadPlanStepThrough::LookForPlanToStepThroughFromCurrentPC() { + DynamicLoader *loader = m_thread.GetProcess()->GetDynamicLoader(); + if (loader) + m_sub_plan_sp = + loader->GetStepThroughTrampolinePlan(m_thread, m_stop_others); + + // If the DynamicLoader was unable to provide us with a ThreadPlan, then we + // try the LanguageRuntimes. + if (!m_sub_plan_sp) { + for (LanguageRuntime *runtime : + m_thread.GetProcess()->GetLanguageRuntimes()) { + m_sub_plan_sp = + runtime->GetStepThroughTrampolinePlan(m_thread, m_stop_others); + + if (m_sub_plan_sp) + break; + } + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) { + lldb::addr_t current_address = GetThread().GetRegisterContext()->GetPC(0); + if (m_sub_plan_sp) { + StreamString s; + m_sub_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull); + LLDB_LOGF(log, "Found step through plan from 0x%" PRIx64 ": %s", + current_address, s.GetData()); + } else { + LLDB_LOGF(log, + "Couldn't find step through plan from address 0x%" PRIx64 ".", + current_address); + } + } +} + +void ThreadPlanStepThrough::GetDescription(Stream *s, + lldb::DescriptionLevel level) { + if (level == lldb::eDescriptionLevelBrief) + s->Printf("Step through"); + else { + s->PutCString("Stepping through trampoline code from: "); + DumpAddress(s->AsRawOstream(), m_start_address, sizeof(addr_t)); + if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) { + s->Printf(" with backstop breakpoint ID: %d at address: ", + m_backstop_bkpt_id); + DumpAddress(s->AsRawOstream(), m_backstop_addr, sizeof(addr_t)); + } else + s->PutCString(" unable to set a backstop breakpoint."); + } +} + +bool ThreadPlanStepThrough::ValidatePlan(Stream *error) { + if (m_could_not_resolve_hw_bp) { + if (error) + error->PutCString( + "Could not create hardware breakpoint for thread plan."); + return false; + } + + if (m_backstop_bkpt_id == LLDB_INVALID_BREAK_ID) { + if (error) + error->PutCString("Could not create backstop breakpoint."); + return false; + } + + if (!m_sub_plan_sp.get()) { + if (error) + error->PutCString("Does not have a subplan."); + return false; + } + + return true; +} + +bool ThreadPlanStepThrough::DoPlanExplainsStop(Event *event_ptr) { + // If we have a sub-plan, it will have been asked first if we explain the + // stop, and we won't get asked. The only time we would be the one directly + // asked this question is if we hit our backstop breakpoint. + + return HitOurBackstopBreakpoint(); +} + +bool ThreadPlanStepThrough::ShouldStop(Event *event_ptr) { + // If we've already marked ourselves done, then we're done... + if (IsPlanComplete()) + return true; + + // First, did we hit the backstop breakpoint? + if (HitOurBackstopBreakpoint()) { + SetPlanComplete(true); + return true; + } + + // If we don't have a sub-plan, then we're also done (can't see how we would + // ever get here without a plan, but just in case. + + if (!m_sub_plan_sp) { + SetPlanComplete(); + return true; + } + + // If the current sub plan is not done, we don't want to stop. Actually, we + // probably won't ever get here in this state, since we generally won't get + // asked any questions if out current sub-plan is not done... + if (!m_sub_plan_sp->IsPlanComplete()) + return false; + + // If our current sub plan failed, then let's just run to our backstop. If + // we can't do that then just stop. + if (!m_sub_plan_sp->PlanSucceeded()) { + if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) { + m_sub_plan_sp.reset(); + return false; + } else { + SetPlanComplete(false); + return true; + } + } + + // Next see if there is a specific step through plan at our current pc (these + // might chain, for instance stepping through a dylib trampoline to the objc + // dispatch function...) + LookForPlanToStepThroughFromCurrentPC(); + if (m_sub_plan_sp) { + PushPlan(m_sub_plan_sp); + return false; + } else { + SetPlanComplete(); + return true; + } +} + +bool ThreadPlanStepThrough::StopOthers() { return m_stop_others; } + +StateType ThreadPlanStepThrough::GetPlanRunState() { return eStateRunning; } + +bool ThreadPlanStepThrough::DoWillResume(StateType resume_state, + bool current_plan) { + return true; +} + +bool ThreadPlanStepThrough::WillStop() { return true; } + +void ThreadPlanStepThrough::ClearBackstopBreakpoint() { + if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) { + m_thread.GetProcess()->GetTarget().RemoveBreakpointByID(m_backstop_bkpt_id); + m_backstop_bkpt_id = LLDB_INVALID_BREAK_ID; + m_could_not_resolve_hw_bp = false; + } +} + +bool ThreadPlanStepThrough::MischiefManaged() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (!IsPlanComplete()) { + return false; + } else { + LLDB_LOGF(log, "Completed step through step plan."); + + ClearBackstopBreakpoint(); + ThreadPlan::MischiefManaged(); + return true; + } +} + +bool ThreadPlanStepThrough::HitOurBackstopBreakpoint() { + StopInfoSP stop_info_sp(m_thread.GetStopInfo()); + if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) { + break_id_t stop_value = (break_id_t)stop_info_sp->GetValue(); + BreakpointSiteSP cur_site_sp = + m_thread.GetProcess()->GetBreakpointSiteList().FindByID(stop_value); + if (cur_site_sp && + cur_site_sp->IsBreakpointAtThisSite(m_backstop_bkpt_id)) { + StackID cur_frame_zero_id = + m_thread.GetStackFrameAtIndex(0)->GetStackID(); + + if (cur_frame_zero_id == m_return_stack_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) + log->PutCString("ThreadPlanStepThrough hit backstop breakpoint."); + return true; + } + } + } + return false; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanStepUntil.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanStepUntil.cpp new file mode 100644 index 00000000000..54d27633748 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanStepUntil.cpp @@ -0,0 +1,338 @@ +//===-- ThreadPlanStepUntil.cpp ---------------------------------*- C++ -*-===// +// +// 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/ThreadPlanStepUntil.h" + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Symbol/SymbolContextScope.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanStepUntil: Run until we reach a given line number or step out of +// the current frame + +ThreadPlanStepUntil::ThreadPlanStepUntil(Thread &thread, + lldb::addr_t *address_list, + size_t num_addresses, bool stop_others, + uint32_t frame_idx) + : ThreadPlan(ThreadPlan::eKindStepUntil, "Step until", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_step_from_insn(LLDB_INVALID_ADDRESS), + m_return_bp_id(LLDB_INVALID_BREAK_ID), + m_return_addr(LLDB_INVALID_ADDRESS), m_stepped_out(false), + m_should_stop(false), m_ran_analyze(false), m_explains_stop(false), + m_until_points(), m_stop_others(stop_others) { + // Stash away our "until" addresses: + TargetSP target_sp(m_thread.CalculateTarget()); + + StackFrameSP frame_sp(m_thread.GetStackFrameAtIndex(frame_idx)); + if (frame_sp) { + m_step_from_insn = frame_sp->GetStackID().GetPC(); + lldb::user_id_t thread_id = m_thread.GetID(); + + // Find the return address and set a breakpoint there: + // FIXME - can we do this more securely if we know first_insn? + + StackFrameSP return_frame_sp(m_thread.GetStackFrameAtIndex(frame_idx + 1)); + if (return_frame_sp) { + // TODO: add inline functionality + m_return_addr = return_frame_sp->GetStackID().GetPC(); + Breakpoint *return_bp = + target_sp->CreateBreakpoint(m_return_addr, true, false).get(); + + if (return_bp != nullptr) { + if (return_bp->IsHardware() && !return_bp->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; + return_bp->SetThreadID(thread_id); + m_return_bp_id = return_bp->GetID(); + return_bp->SetBreakpointKind("until-return-backstop"); + } + } + + m_stack_id = frame_sp->GetStackID(); + + // Now set breakpoints on all our return addresses: + for (size_t i = 0; i < num_addresses; i++) { + Breakpoint *until_bp = + target_sp->CreateBreakpoint(address_list[i], true, false).get(); + if (until_bp != nullptr) { + until_bp->SetThreadID(thread_id); + m_until_points[address_list[i]] = until_bp->GetID(); + until_bp->SetBreakpointKind("until-target"); + } else { + m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID; + } + } + } +} + +ThreadPlanStepUntil::~ThreadPlanStepUntil() { Clear(); } + +void ThreadPlanStepUntil::Clear() { + TargetSP target_sp(m_thread.CalculateTarget()); + if (target_sp) { + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) { + target_sp->RemoveBreakpointByID(m_return_bp_id); + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) { + target_sp->RemoveBreakpointByID((*pos).second); + } + } + m_until_points.clear(); + m_could_not_resolve_hw_bp = false; +} + +void ThreadPlanStepUntil::GetDescription(Stream *s, + lldb::DescriptionLevel level) { + if (level == lldb::eDescriptionLevelBrief) { + s->Printf("step until"); + if (m_stepped_out) + s->Printf(" - stepped out"); + } else { + if (m_until_points.size() == 1) + s->Printf("Stepping from address 0x%" PRIx64 " until we reach 0x%" PRIx64 + " using breakpoint %d", + (uint64_t)m_step_from_insn, + (uint64_t)(*m_until_points.begin()).first, + (*m_until_points.begin()).second); + else { + until_collection::iterator pos, end = m_until_points.end(); + s->Printf("Stepping from address 0x%" PRIx64 " until we reach one of:", + (uint64_t)m_step_from_insn); + for (pos = m_until_points.begin(); pos != end; pos++) { + s->Printf("\n\t0x%" PRIx64 " (bp: %d)", (uint64_t)(*pos).first, + (*pos).second); + } + } + s->Printf(" stepped out address is 0x%" PRIx64 ".", + (uint64_t)m_return_addr); + } +} + +bool ThreadPlanStepUntil::ValidatePlan(Stream *error) { + if (m_could_not_resolve_hw_bp) { + if (error) + error->PutCString( + "Could not create hardware breakpoint for thread plan."); + return false; + } else if (m_return_bp_id == LLDB_INVALID_BREAK_ID) { + if (error) + error->PutCString("Could not create return breakpoint."); + return false; + } else { + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) { + if (!LLDB_BREAK_ID_IS_VALID((*pos).second)) + return false; + } + return true; + } +} + +void ThreadPlanStepUntil::AnalyzeStop() { + if (m_ran_analyze) + return; + + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + m_should_stop = true; + m_explains_stop = false; + + if (stop_info_sp) { + StopReason reason = stop_info_sp->GetStopReason(); + + if (reason == eStopReasonBreakpoint) { + // If this is OUR breakpoint, we're fine, otherwise we don't know why + // this happened... + BreakpointSiteSP this_site = + m_thread.GetProcess()->GetBreakpointSiteList().FindByID( + stop_info_sp->GetValue()); + if (!this_site) { + m_explains_stop = false; + return; + } + + if (this_site->IsBreakpointAtThisSite(m_return_bp_id)) { + // If we are at our "step out" breakpoint, and the stack depth has + // shrunk, then this is indeed our stop. If the stack depth has grown, + // then we've hit our step out breakpoint recursively. If we are the + // only breakpoint at that location, then we do explain the stop, and + // we'll just continue. If there was another breakpoint here, then we + // don't explain the stop, but we won't mark ourselves Completed, + // because maybe that breakpoint will continue, and then we'll finish + // the "until". + bool done; + StackID cur_frame_zero_id; + + done = (m_stack_id < cur_frame_zero_id); + + if (done) { + m_stepped_out = true; + SetPlanComplete(); + } else + m_should_stop = false; + + if (this_site->GetNumberOfOwners() == 1) + m_explains_stop = true; + else + m_explains_stop = false; + return; + } else { + // Check if we've hit one of our "until" breakpoints. + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) { + if (this_site->IsBreakpointAtThisSite((*pos).second)) { + // If we're at the right stack depth, then we're done. + + bool done; + StackID frame_zero_id = + m_thread.GetStackFrameAtIndex(0)->GetStackID(); + + if (frame_zero_id == m_stack_id) + done = true; + else if (frame_zero_id < m_stack_id) + done = false; + else { + StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(1); + + // But if we can't even unwind one frame we should just get out + // of here & stop... + if (older_frame_sp) { + const SymbolContext &older_context = + older_frame_sp->GetSymbolContext(eSymbolContextEverything); + SymbolContext stack_context; + m_stack_id.GetSymbolContextScope()->CalculateSymbolContext( + &stack_context); + + done = (older_context == stack_context); + } else + done = false; + } + + if (done) + SetPlanComplete(); + else + m_should_stop = false; + + // Otherwise we've hit this breakpoint recursively. If we're the + // only breakpoint here, then we do explain the stop, and we'll + // continue. If not then we should let higher plans handle this + // stop. + if (this_site->GetNumberOfOwners() == 1) + m_explains_stop = true; + else { + m_should_stop = true; + m_explains_stop = false; + } + return; + } + } + } + // If we get here we haven't hit any of our breakpoints, so let the + // higher plans take care of the stop. + m_explains_stop = false; + return; + } else if (IsUsuallyUnexplainedStopReason(reason)) { + m_explains_stop = false; + } else { + m_explains_stop = true; + } + } +} + +bool ThreadPlanStepUntil::DoPlanExplainsStop(Event *event_ptr) { + // We don't explain signals or breakpoints (breakpoints that handle stepping + // in or out will be handled by a child plan. + AnalyzeStop(); + return m_explains_stop; +} + +bool ThreadPlanStepUntil::ShouldStop(Event *event_ptr) { + // If we've told our self in ExplainsStop that we plan to continue, then do + // so here. Otherwise, as long as this thread has stopped for a reason, we + // will stop. + + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + if (!stop_info_sp || stop_info_sp->GetStopReason() == eStopReasonNone) + return false; + + AnalyzeStop(); + return m_should_stop; +} + +bool ThreadPlanStepUntil::StopOthers() { return m_stop_others; } + +StateType ThreadPlanStepUntil::GetPlanRunState() { return eStateRunning; } + +bool ThreadPlanStepUntil::DoWillResume(StateType resume_state, + bool current_plan) { + if (current_plan) { + TargetSP target_sp(m_thread.CalculateTarget()); + if (target_sp) { + Breakpoint *return_bp = + target_sp->GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != nullptr) + return_bp->SetEnabled(true); + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) { + Breakpoint *until_bp = + target_sp->GetBreakpointByID((*pos).second).get(); + if (until_bp != nullptr) + until_bp->SetEnabled(true); + } + } + } + + m_should_stop = true; + m_ran_analyze = false; + m_explains_stop = false; + return true; +} + +bool ThreadPlanStepUntil::WillStop() { + TargetSP target_sp(m_thread.CalculateTarget()); + if (target_sp) { + Breakpoint *return_bp = target_sp->GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != nullptr) + return_bp->SetEnabled(false); + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) { + Breakpoint *until_bp = target_sp->GetBreakpointByID((*pos).second).get(); + if (until_bp != nullptr) + until_bp->SetEnabled(false); + } + } + return true; +} + +bool ThreadPlanStepUntil::MischiefManaged() { + // I'm letting "PlanExplainsStop" do all the work, and just reporting that + // here. + bool done = false; + if (IsPlanComplete()) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, "Completed step until plan."); + + Clear(); + done = true; + } + if (done) + ThreadPlan::MischiefManaged(); + + return done; +} diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanTracer.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanTracer.cpp new file mode 100644 index 00000000000..b50c1636b7f --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanTracer.cpp @@ -0,0 +1,231 @@ +//===-- ThreadPlanTracer.cpp ------------------------------------*- C++ -*-===// +// +// 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 <cstring> + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/DumpRegisterValue.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +#pragma mark ThreadPlanTracer + +ThreadPlanTracer::ThreadPlanTracer(Thread &thread, lldb::StreamSP &stream_sp) + : m_thread(thread), m_single_step(true), m_enabled(false), + m_stream_sp(stream_sp) {} + +ThreadPlanTracer::ThreadPlanTracer(Thread &thread) + : m_thread(thread), m_single_step(true), m_enabled(false), m_stream_sp() {} + +Stream *ThreadPlanTracer::GetLogStream() { + if (m_stream_sp) + return m_stream_sp.get(); + else { + TargetSP target_sp(m_thread.CalculateTarget()); + if (target_sp) + return &(target_sp->GetDebugger().GetOutputStream()); + } + return nullptr; +} + +void ThreadPlanTracer::Log() { + SymbolContext sc; + bool show_frame_index = false; + bool show_fullpaths = false; + + Stream *stream = GetLogStream(); + if (stream) { + m_thread.GetStackFrameAtIndex(0)->Dump(stream, show_frame_index, + show_fullpaths); + stream->Printf("\n"); + stream->Flush(); + } +} + +bool ThreadPlanTracer::TracerExplainsStop() { + if (m_enabled && m_single_step) { + lldb::StopInfoSP stop_info = m_thread.GetStopInfo(); + return (stop_info->GetStopReason() == eStopReasonTrace); + } else + return false; +} + +#pragma mark ThreadPlanAssemblyTracer + +ThreadPlanAssemblyTracer::ThreadPlanAssemblyTracer(Thread &thread, + lldb::StreamSP &stream_sp) + : ThreadPlanTracer(thread, stream_sp), m_disassembler_sp(), m_intptr_type(), + m_register_values() {} + +ThreadPlanAssemblyTracer::ThreadPlanAssemblyTracer(Thread &thread) + : ThreadPlanTracer(thread), m_disassembler_sp(), m_intptr_type(), + m_register_values() {} + +Disassembler *ThreadPlanAssemblyTracer::GetDisassembler() { + if (!m_disassembler_sp) + m_disassembler_sp = Disassembler::FindPlugin( + m_thread.GetProcess()->GetTarget().GetArchitecture(), nullptr, nullptr); + return m_disassembler_sp.get(); +} + +TypeFromUser ThreadPlanAssemblyTracer::GetIntPointerType() { + if (!m_intptr_type.IsValid()) { + if (auto target_sp = m_thread.CalculateTarget()) { + auto type_system_or_err = + target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR( + lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TYPES), + std::move(err), + "Unable to get integer pointer type from TypeSystem"); + } else { + m_intptr_type = TypeFromUser( + type_system_or_err->GetBuiltinTypeForEncodingAndBitSize( + eEncodingUint, + target_sp->GetArchitecture().GetAddressByteSize() * 8)); + } + } + } + return m_intptr_type; +} + +ThreadPlanAssemblyTracer::~ThreadPlanAssemblyTracer() = default; + +void ThreadPlanAssemblyTracer::TracingStarted() { +} + +void ThreadPlanAssemblyTracer::TracingEnded() { m_register_values.clear(); } + +void ThreadPlanAssemblyTracer::Log() { + Stream *stream = GetLogStream(); + + if (!stream) + return; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); + + lldb::addr_t pc = reg_ctx->GetPC(); + ProcessSP process_sp(m_thread.GetProcess()); + Address pc_addr; + bool addr_valid = false; + uint8_t buffer[16] = {0}; // Must be big enough for any single instruction + addr_valid = process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress( + pc, pc_addr); + + pc_addr.Dump(stream, &m_thread, Address::DumpStyleResolvedDescription, + Address::DumpStyleModuleWithFileAddress); + stream->PutCString(" "); + + Disassembler *disassembler = GetDisassembler(); + if (disassembler) { + Status err; + process_sp->ReadMemory(pc, buffer, sizeof(buffer), err); + + if (err.Success()) { + DataExtractor extractor(buffer, sizeof(buffer), + process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + + bool data_from_file = false; + if (addr_valid) + disassembler->DecodeInstructions(pc_addr, extractor, 0, 1, false, + data_from_file); + else + disassembler->DecodeInstructions(Address(pc), extractor, 0, 1, false, + data_from_file); + + InstructionList &instruction_list = disassembler->GetInstructionList(); + const uint32_t max_opcode_byte_size = + instruction_list.GetMaxOpcocdeByteSize(); + + if (instruction_list.GetSize()) { + const bool show_bytes = true; + const bool show_address = true; + Instruction *instruction = + instruction_list.GetInstructionAtIndex(0).get(); + const FormatEntity::Entry *disassemble_format = + m_thread.GetProcess() + ->GetTarget() + .GetDebugger() + .GetDisassemblyFormat(); + instruction->Dump(stream, max_opcode_byte_size, show_address, + show_bytes, nullptr, nullptr, nullptr, + disassemble_format, 0); + } + } + } + + const ABI *abi = process_sp->GetABI().get(); + TypeFromUser intptr_type = GetIntPointerType(); + + if (abi && intptr_type.IsValid()) { + ValueList value_list; + const int num_args = 1; + + for (int arg_index = 0; arg_index < num_args; ++arg_index) { + Value value; + value.SetValueType(Value::eValueTypeScalar); + value.SetCompilerType(intptr_type); + value_list.PushValue(value); + } + + if (abi->GetArgumentValues(m_thread, value_list)) { + for (int arg_index = 0; arg_index < num_args; ++arg_index) { + stream->Printf( + "\n\targ[%d]=%llx", arg_index, + value_list.GetValueAtIndex(arg_index)->GetScalar().ULongLong()); + + if (arg_index + 1 < num_args) + stream->PutCString(", "); + } + } + } + + if (m_register_values.empty()) { + RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); + m_register_values.resize(reg_ctx->GetRegisterCount()); + } + + RegisterValue reg_value; + for (uint32_t reg_num = 0, num_registers = reg_ctx->GetRegisterCount(); + reg_num < num_registers; ++reg_num) { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num); + if (reg_ctx->ReadRegister(reg_info, reg_value)) { + assert(reg_num < m_register_values.size()); + if (m_register_values[reg_num].GetType() == RegisterValue::eTypeInvalid || + reg_value != m_register_values[reg_num]) { + if (reg_value.GetType() != RegisterValue::eTypeInvalid) { + stream->PutCString("\n\t"); + DumpRegisterValue(reg_value, stream, reg_info, true, false, + eFormatDefault); + } + } + m_register_values[reg_num] = reg_value; + } + } + stream->EOL(); + stream->Flush(); +} diff --git a/gnu/llvm/lldb/source/Target/ThreadSpec.cpp b/gnu/llvm/lldb/source/Target/ThreadSpec.cpp new file mode 100644 index 00000000000..1a733cb551e --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadSpec.cpp @@ -0,0 +1,157 @@ +//===-- ThreadSpec.cpp ------------------------------------------*- C++ -*-===// +// +// 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/ThreadSpec.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/StructuredData.h" + +using namespace lldb; +using namespace lldb_private; + +const char *ThreadSpec::g_option_names[static_cast<uint32_t>( + ThreadSpec::OptionNames::LastOptionName)]{"Index", "ID", "Name", + "QueueName"}; + +ThreadSpec::ThreadSpec() + : m_index(UINT32_MAX), m_tid(LLDB_INVALID_THREAD_ID), m_name(), + m_queue_name() {} + +std::unique_ptr<ThreadSpec> ThreadSpec::CreateFromStructuredData( + const StructuredData::Dictionary &spec_dict, Status &error) { + uint32_t index = UINT32_MAX; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + llvm::StringRef name; + llvm::StringRef queue_name; + + std::unique_ptr<ThreadSpec> thread_spec_up(new ThreadSpec()); + bool success = spec_dict.GetValueForKeyAsInteger( + GetKey(OptionNames::ThreadIndex), index); + if (success) + thread_spec_up->SetIndex(index); + + success = + spec_dict.GetValueForKeyAsInteger(GetKey(OptionNames::ThreadID), tid); + if (success) + thread_spec_up->SetTID(tid); + + success = + spec_dict.GetValueForKeyAsString(GetKey(OptionNames::ThreadName), name); + if (success) + thread_spec_up->SetName(name); + + success = spec_dict.GetValueForKeyAsString(GetKey(OptionNames::ThreadName), + queue_name); + if (success) + thread_spec_up->SetQueueName(queue_name); + + return thread_spec_up; +} + +StructuredData::ObjectSP ThreadSpec::SerializeToStructuredData() { + StructuredData::DictionarySP data_dict_sp(new StructuredData::Dictionary()); + + if (m_index != UINT32_MAX) + data_dict_sp->AddIntegerItem(GetKey(OptionNames::ThreadIndex), m_index); + if (m_tid != LLDB_INVALID_THREAD_ID) + data_dict_sp->AddIntegerItem(GetKey(OptionNames::ThreadID), m_tid); + if (!m_name.empty()) + data_dict_sp->AddStringItem(GetKey(OptionNames::ThreadName), m_name); + if (!m_queue_name.empty()) + data_dict_sp->AddStringItem(GetKey(OptionNames::QueueName), m_queue_name); + + return data_dict_sp; +} + +const char *ThreadSpec::GetName() const { + return m_name.empty() ? nullptr : m_name.c_str(); +} + +const char *ThreadSpec::GetQueueName() const { + return m_queue_name.empty() ? nullptr : m_queue_name.c_str(); +} + +bool ThreadSpec::TIDMatches(Thread &thread) const { + if (m_tid == LLDB_INVALID_THREAD_ID) + return true; + + lldb::tid_t thread_id = thread.GetID(); + return TIDMatches(thread_id); +} + +bool ThreadSpec::IndexMatches(Thread &thread) const { + if (m_index == UINT32_MAX) + return true; + uint32_t index = thread.GetIndexID(); + return IndexMatches(index); +} + +bool ThreadSpec::NameMatches(Thread &thread) const { + if (m_name.empty()) + return true; + + const char *name = thread.GetName(); + return NameMatches(name); +} + +bool ThreadSpec::QueueNameMatches(Thread &thread) const { + if (m_queue_name.empty()) + return true; + + const char *queue_name = thread.GetQueueName(); + return QueueNameMatches(queue_name); +} + +bool ThreadSpec::ThreadPassesBasicTests(Thread &thread) const { + if (!HasSpecification()) + return true; + + if (!TIDMatches(thread)) + return false; + + if (!IndexMatches(thread)) + return false; + + if (!NameMatches(thread)) + return false; + + if (!QueueNameMatches(thread)) + return false; + + return true; +} + +bool ThreadSpec::HasSpecification() const { + return (m_index != UINT32_MAX || m_tid != LLDB_INVALID_THREAD_ID || + !m_name.empty() || !m_queue_name.empty()); +} + +void ThreadSpec::GetDescription(Stream *s, lldb::DescriptionLevel level) const { + if (!HasSpecification()) { + if (level == eDescriptionLevelBrief) { + s->PutCString("thread spec: no "); + } + } else { + if (level == eDescriptionLevelBrief) { + s->PutCString("thread spec: yes "); + } else { + if (GetTID() != LLDB_INVALID_THREAD_ID) + s->Printf("tid: 0x%" PRIx64 " ", GetTID()); + + if (GetIndex() != UINT32_MAX) + s->Printf("index: %d ", GetIndex()); + + const char *name = GetName(); + if (name) + s->Printf("thread name: \"%s\" ", name); + + const char *queue_name = GetQueueName(); + if (queue_name) + s->Printf("queue name: \"%s\" ", queue_name); + } + } +} diff --git a/gnu/llvm/lldb/source/Target/UnixSignals.cpp b/gnu/llvm/lldb/source/Target/UnixSignals.cpp new file mode 100644 index 00000000000..72d9fc0a0a6 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/UnixSignals.cpp @@ -0,0 +1,326 @@ +//===-- UnixSignals.cpp -----------------------------------------*- C++ -*-===// +// +// 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/UnixSignals.h" +#include "Plugins/Process/Utility/FreeBSDSignals.h" +#include "Plugins/Process/Utility/LinuxSignals.h" +#include "Plugins/Process/Utility/MipsLinuxSignals.h" +#include "Plugins/Process/Utility/NetBSDSignals.h" +#include "Plugins/Process/Utility/OpenBSDSignals.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Utility/ArchSpec.h" + +using namespace lldb_private; + +UnixSignals::Signal::Signal(const char *name, bool default_suppress, + bool default_stop, bool default_notify, + const char *description, const char *alias) + : m_name(name), m_alias(alias), m_description(), + m_suppress(default_suppress), m_stop(default_stop), + m_notify(default_notify) { + if (description) + m_description.assign(description); +} + +lldb::UnixSignalsSP UnixSignals::Create(const ArchSpec &arch) { + const auto &triple = arch.GetTriple(); + switch (triple.getOS()) { + case llvm::Triple::Linux: { + switch (triple.getArch()) { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + return std::make_shared<MipsLinuxSignals>(); + default: + return std::make_shared<LinuxSignals>(); + } + } + case llvm::Triple::FreeBSD: + return std::make_shared<FreeBSDSignals>(); + case llvm::Triple::OpenBSD: + return std::make_shared<OpenBSDSignals>(); + case llvm::Triple::NetBSD: + return std::make_shared<NetBSDSignals>(); + default: + return std::make_shared<UnixSignals>(); + } +} + +lldb::UnixSignalsSP UnixSignals::CreateForHost() { + static lldb::UnixSignalsSP s_unix_signals_sp = + Create(HostInfo::GetArchitecture()); + return s_unix_signals_sp; +} + +// UnixSignals constructor +UnixSignals::UnixSignals() { Reset(); } + +UnixSignals::UnixSignals(const UnixSignals &rhs) : m_signals(rhs.m_signals) {} + +UnixSignals::~UnixSignals() = default; + +void UnixSignals::Reset() { + // This builds one standard set of Unix Signals. If yours aren't quite in + // this order, you can either subclass this class, and use Add & Remove to + // change them + // or you can subclass and build them afresh in your constructor; + // + // Note: the signals below are the Darwin signals. Do not change these! + m_signals.clear(); + // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION + // ====== ============ ======== ====== ====== + // =================================================== + AddSignal(1, "SIGHUP", false, true, true, "hangup"); + AddSignal(2, "SIGINT", true, true, true, "interrupt"); + AddSignal(3, "SIGQUIT", false, true, true, "quit"); + AddSignal(4, "SIGILL", false, true, true, "illegal instruction"); + AddSignal(5, "SIGTRAP", true, true, true, + "trace trap (not reset when caught)"); + AddSignal(6, "SIGABRT", false, true, true, "abort()"); + AddSignal(7, "SIGEMT", false, true, true, "pollable event"); + AddSignal(8, "SIGFPE", false, true, true, "floating point exception"); + AddSignal(9, "SIGKILL", false, true, true, "kill"); + AddSignal(10, "SIGBUS", false, true, true, "bus error"); + AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation"); + AddSignal(12, "SIGSYS", false, true, true, "bad argument to system call"); + AddSignal(13, "SIGPIPE", false, false, false, + "write on a pipe with no one to read it"); + AddSignal(14, "SIGALRM", false, false, false, "alarm clock"); + AddSignal(15, "SIGTERM", false, true, true, + "software termination signal from kill"); + AddSignal(16, "SIGURG", false, false, false, + "urgent condition on IO channel"); + AddSignal(17, "SIGSTOP", true, true, true, + "sendable stop signal not from tty"); + AddSignal(18, "SIGTSTP", false, true, true, "stop signal from tty"); + AddSignal(19, "SIGCONT", false, true, true, "continue a stopped process"); + AddSignal(20, "SIGCHLD", false, false, false, + "to parent on child stop or exit"); + AddSignal(21, "SIGTTIN", false, true, true, + "to readers process group upon background tty read"); + AddSignal(22, "SIGTTOU", false, true, true, + "to readers process group upon background tty write"); + AddSignal(23, "SIGIO", false, false, false, "input/output possible signal"); + AddSignal(24, "SIGXCPU", false, true, true, "exceeded CPU time limit"); + AddSignal(25, "SIGXFSZ", false, true, true, "exceeded file size limit"); + AddSignal(26, "SIGVTALRM", false, false, false, "virtual time alarm"); + AddSignal(27, "SIGPROF", false, false, false, "profiling time alarm"); + AddSignal(28, "SIGWINCH", false, false, false, "window size changes"); + AddSignal(29, "SIGINFO", false, true, true, "information request"); + AddSignal(30, "SIGUSR1", false, true, true, "user defined signal 1"); + AddSignal(31, "SIGUSR2", false, true, true, "user defined signal 2"); +} + +void UnixSignals::AddSignal(int signo, const char *name, bool default_suppress, + bool default_stop, bool default_notify, + const char *description, const char *alias) { + Signal new_signal(name, default_suppress, default_stop, default_notify, + description, alias); + m_signals.insert(std::make_pair(signo, new_signal)); + ++m_version; +} + +void UnixSignals::RemoveSignal(int signo) { + collection::iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) + m_signals.erase(pos); + ++m_version; +} + +const char *UnixSignals::GetSignalAsCString(int signo) const { + collection::const_iterator pos = m_signals.find(signo); + if (pos == m_signals.end()) + return nullptr; + else + return pos->second.m_name.GetCString(); +} + +bool UnixSignals::SignalIsValid(int32_t signo) const { + return m_signals.find(signo) != m_signals.end(); +} + +ConstString UnixSignals::GetShortName(ConstString name) const { + if (name) { + const char *signame = name.AsCString(); + return ConstString(signame + 3); // Remove "SIG" from name + } + return name; +} + +int32_t UnixSignals::GetSignalNumberFromName(const char *name) const { + ConstString const_name(name); + + collection::const_iterator pos, end = m_signals.end(); + for (pos = m_signals.begin(); pos != end; pos++) { + if ((const_name == pos->second.m_name) || + (const_name == pos->second.m_alias) || + (const_name == GetShortName(pos->second.m_name)) || + (const_name == GetShortName(pos->second.m_alias))) + return pos->first; + } + + const int32_t signo = + StringConvert::ToSInt32(name, LLDB_INVALID_SIGNAL_NUMBER, 0); + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + return signo; + return LLDB_INVALID_SIGNAL_NUMBER; +} + +int32_t UnixSignals::GetFirstSignalNumber() const { + if (m_signals.empty()) + return LLDB_INVALID_SIGNAL_NUMBER; + + return (*m_signals.begin()).first; +} + +int32_t UnixSignals::GetNextSignalNumber(int32_t current_signal) const { + collection::const_iterator pos = m_signals.find(current_signal); + collection::const_iterator end = m_signals.end(); + if (pos == end) + return LLDB_INVALID_SIGNAL_NUMBER; + else { + pos++; + if (pos == end) + return LLDB_INVALID_SIGNAL_NUMBER; + else + return pos->first; + } +} + +const char *UnixSignals::GetSignalInfo(int32_t signo, bool &should_suppress, + bool &should_stop, + bool &should_notify) const { + collection::const_iterator pos = m_signals.find(signo); + if (pos == m_signals.end()) + return nullptr; + else { + const Signal &signal = pos->second; + should_suppress = signal.m_suppress; + should_stop = signal.m_stop; + should_notify = signal.m_notify; + return signal.m_name.AsCString(""); + } +} + +bool UnixSignals::GetShouldSuppress(int signo) const { + collection::const_iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) + return pos->second.m_suppress; + return false; +} + +bool UnixSignals::SetShouldSuppress(int signo, bool value) { + collection::iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) { + pos->second.m_suppress = value; + ++m_version; + return true; + } + return false; +} + +bool UnixSignals::SetShouldSuppress(const char *signal_name, bool value) { + const int32_t signo = GetSignalNumberFromName(signal_name); + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + return SetShouldSuppress(signo, value); + return false; +} + +bool UnixSignals::GetShouldStop(int signo) const { + collection::const_iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) + return pos->second.m_stop; + return false; +} + +bool UnixSignals::SetShouldStop(int signo, bool value) { + collection::iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) { + pos->second.m_stop = value; + ++m_version; + return true; + } + return false; +} + +bool UnixSignals::SetShouldStop(const char *signal_name, bool value) { + const int32_t signo = GetSignalNumberFromName(signal_name); + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + return SetShouldStop(signo, value); + return false; +} + +bool UnixSignals::GetShouldNotify(int signo) const { + collection::const_iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) + return pos->second.m_notify; + return false; +} + +bool UnixSignals::SetShouldNotify(int signo, bool value) { + collection::iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) { + pos->second.m_notify = value; + ++m_version; + return true; + } + return false; +} + +bool UnixSignals::SetShouldNotify(const char *signal_name, bool value) { + const int32_t signo = GetSignalNumberFromName(signal_name); + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + return SetShouldNotify(signo, value); + return false; +} + +int32_t UnixSignals::GetNumSignals() const { return m_signals.size(); } + +int32_t UnixSignals::GetSignalAtIndex(int32_t index) const { + if (index < 0 || m_signals.size() <= static_cast<size_t>(index)) + return LLDB_INVALID_SIGNAL_NUMBER; + auto it = m_signals.begin(); + std::advance(it, index); + return it->first; +} + +uint64_t UnixSignals::GetVersion() const { return m_version; } + +std::vector<int32_t> +UnixSignals::GetFilteredSignals(llvm::Optional<bool> should_suppress, + llvm::Optional<bool> should_stop, + llvm::Optional<bool> should_notify) { + std::vector<int32_t> result; + for (int32_t signo = GetFirstSignalNumber(); + signo != LLDB_INVALID_SIGNAL_NUMBER; + signo = GetNextSignalNumber(signo)) { + + bool signal_suppress = false; + bool signal_stop = false; + bool signal_notify = false; + GetSignalInfo(signo, signal_suppress, signal_stop, signal_notify); + + // If any of filtering conditions are not met, we move on to the next + // signal. + if (should_suppress.hasValue() && + signal_suppress != should_suppress.getValue()) + continue; + + if (should_stop.hasValue() && signal_stop != should_stop.getValue()) + continue; + + if (should_notify.hasValue() && signal_notify != should_notify.getValue()) + continue; + + result.push_back(signo); + } + + return result; +} diff --git a/gnu/llvm/lldb/source/Target/UnwindAssembly.cpp b/gnu/llvm/lldb/source/Target/UnwindAssembly.cpp new file mode 100644 index 00000000000..d3d8068687c --- /dev/null +++ b/gnu/llvm/lldb/source/Target/UnwindAssembly.cpp @@ -0,0 +1,33 @@ +//===-- UnwindAssembly.cpp --------------------------------------*- C++ -*-===// +// +// 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/UnwindAssembly.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/lldb-private.h" + +using namespace lldb; +using namespace lldb_private; + +UnwindAssemblySP UnwindAssembly::FindPlugin(const ArchSpec &arch) { + UnwindAssemblyCreateInstance create_callback; + + for (uint32_t idx = 0; + (create_callback = PluginManager::GetUnwindAssemblyCreateCallbackAtIndex( + idx)) != nullptr; + ++idx) { + UnwindAssemblySP assembly_profiler_up(create_callback(arch)); + if (assembly_profiler_up) + return assembly_profiler_up; + } + return nullptr; +} + +UnwindAssembly::UnwindAssembly(const ArchSpec &arch) : m_arch(arch) {} + +UnwindAssembly::~UnwindAssembly() = default; |