//===-- ThreadMachCore.cpp ------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include #include #include #include "RegisterContextUnifiedCore.h" #include "ThreadMachCore.h" #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Host/SafeMachO.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/AppleArm64ExceptionClass.h" #include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Unwind.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StructuredData.h" #include "ProcessMachCore.h" //#include "RegisterContextKDP_arm.h" //#include "RegisterContextKDP_i386.h" //#include "RegisterContextKDP_x86_64.h" using namespace lldb; using namespace lldb_private; // Thread Registers ThreadMachCore::ThreadMachCore(Process &process, lldb::tid_t tid, uint32_t objfile_lc_thread_idx) : Thread(process, tid), m_thread_name(), m_dispatch_queue_name(), m_thread_dispatch_qaddr(LLDB_INVALID_ADDRESS), m_thread_reg_ctx_sp(), m_objfile_lc_thread_idx(objfile_lc_thread_idx) {} ThreadMachCore::~ThreadMachCore() { DestroyThread(); } const char *ThreadMachCore::GetName() { if (m_thread_name.empty()) return nullptr; return m_thread_name.c_str(); } void ThreadMachCore::RefreshStateAfterStop() { // Invalidate all registers in our register context. We don't set "force" to // true because the stop reply packet might have had some register values // that were expedited and these will already be copied into the register // context by the time this function gets called. The KDPRegisterContext // class has been made smart enough to detect when it needs to invalidate // which registers are valid by putting hooks in the register read and // register supply functions where they check the process stop ID and do the // right thing. const bool force = false; GetRegisterContext()->InvalidateIfNeeded(force); } bool ThreadMachCore::ThreadIDIsValid(lldb::tid_t thread) { return thread != 0; } lldb::RegisterContextSP ThreadMachCore::GetRegisterContext() { if (!m_reg_context_sp) m_reg_context_sp = CreateRegisterContextForFrame(nullptr); return m_reg_context_sp; } lldb::RegisterContextSP ThreadMachCore::CreateRegisterContextForFrame(StackFrame *frame) { uint32_t concrete_frame_idx = 0; if (frame) concrete_frame_idx = frame->GetConcreteFrameIndex(); if (concrete_frame_idx > 0) return GetUnwinder().CreateRegisterContextForFrame(frame); if (m_thread_reg_ctx_sp) return m_thread_reg_ctx_sp; ProcessSP process_sp(GetProcess()); assert(process_sp); ObjectFile *core_objfile = static_cast(process_sp.get())->GetCoreObjectFile(); if (!core_objfile) return {}; RegisterContextSP core_thread_regctx_sp = core_objfile->GetThreadContextAtIndex(m_objfile_lc_thread_idx, *this); if (!core_thread_regctx_sp) return {}; StructuredData::ObjectSP process_md_sp = core_objfile->GetCorefileProcessMetadata(); StructuredData::ObjectSP thread_md_sp; if (process_md_sp && process_md_sp->GetAsDictionary() && process_md_sp->GetAsDictionary()->HasKey("threads")) { StructuredData::Array *threads = process_md_sp->GetAsDictionary() ->GetValueForKey("threads") ->GetAsArray(); if (threads && threads->GetSize() == core_objfile->GetNumThreadContexts()) { StructuredData::ObjectSP thread_sp = threads->GetItemAtIndex(m_objfile_lc_thread_idx); if (thread_sp && thread_sp->GetAsDictionary()) thread_md_sp = thread_sp; } } m_thread_reg_ctx_sp = std::make_shared( *this, concrete_frame_idx, core_thread_regctx_sp, thread_md_sp); return m_thread_reg_ctx_sp; } static bool IsCrashExceptionClass(AppleArm64ExceptionClass EC) { switch (EC) { case AppleArm64ExceptionClass::ESR_EC_UNCATEGORIZED: case AppleArm64ExceptionClass::ESR_EC_SVC_32: case AppleArm64ExceptionClass::ESR_EC_SVC_64: // In the ARM exception model, a process takes an exception when asking the // kernel to service a system call. Don't treat this like a crash. return false; default: return true; } } bool ThreadMachCore::CalculateStopInfo() { ProcessSP process_sp(GetProcess()); if (process_sp) { StopInfoSP stop_info; RegisterContextSP reg_ctx_sp = GetRegisterContext(); if (reg_ctx_sp) { Target &target = process_sp->GetTarget(); const ArchSpec arch_spec = target.GetArchitecture(); const uint32_t cputype = arch_spec.GetMachOCPUType(); if (cputype == llvm::MachO::CPU_TYPE_ARM64 || cputype == llvm::MachO::CPU_TYPE_ARM64_32) { const RegisterInfo *esr_info = reg_ctx_sp->GetRegisterInfoByName("esr"); const RegisterInfo *far_info = reg_ctx_sp->GetRegisterInfoByName("far"); RegisterValue esr, far; if (reg_ctx_sp->ReadRegister(esr_info, esr) && reg_ctx_sp->ReadRegister(far_info, far)) { const uint32_t esr_val = esr.GetAsUInt32(); const AppleArm64ExceptionClass exception_class = getAppleArm64ExceptionClass(esr_val); if (IsCrashExceptionClass(exception_class)) { StreamString S; S.Printf("%s (fault address: 0x%" PRIx64 ")", toString(exception_class), far.GetAsUInt64()); stop_info = StopInfo::CreateStopReasonWithException(*this, S.GetData()); } } } } // Set a stop reason for crashing threads only so that they get selected // preferentially. if (stop_info) SetStopInfo(stop_info); return true; } return false; }