diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp b/gnu/llvm/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp new file mode 100644 index 00000000000..0a49f96f54a --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp @@ -0,0 +1,627 @@ +//===-- FreeBSDThread.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 <errno.h> +#include <pthread.h> +#include <pthread_np.h> +#include <stdlib.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <sys/user.h> + +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/State.h" + +#include "FreeBSDThread.h" +#include "POSIXStopInfo.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" +#include "Plugins/Process/Utility/UnwindLLDB.h" +#include "ProcessFreeBSD.h" +#include "ProcessMonitor.h" +#include "RegisterContextPOSIXProcessMonitor_arm.h" +#include "RegisterContextPOSIXProcessMonitor_arm64.h" +#include "RegisterContextPOSIXProcessMonitor_mips64.h" +#include "RegisterContextPOSIXProcessMonitor_powerpc.h" +#include "RegisterContextPOSIXProcessMonitor_x86.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Utility/State.h" +#include "llvm/ADT/SmallString.h" + +using namespace lldb; +using namespace lldb_private; + +FreeBSDThread::FreeBSDThread(Process &process, lldb::tid_t tid) + : Thread(process, tid), m_frame_up(), m_breakpoint(), + m_thread_name_valid(false), m_thread_name(), m_posix_thread(nullptr) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + LLDB_LOGV(log, "tid = {0}", tid); + + // Set the current watchpoints for this thread. + Target &target = GetProcess()->GetTarget(); + const WatchpointList &wp_list = target.GetWatchpointList(); + size_t wp_size = wp_list.GetSize(); + + for (uint32_t wp_idx = 0; wp_idx < wp_size; wp_idx++) { + lldb::WatchpointSP wp = wp_list.GetByIndex(wp_idx); + if (wp.get() && wp->IsEnabled()) { + // This watchpoint as been enabled; obviously this "new" thread has been + // created since that watchpoint was enabled. Since the + // POSIXBreakpointProtocol has yet to be initialized, its + // m_watchpoints_initialized member will be FALSE. Attempting to read + // the debug status register to determine if a watchpoint has been hit + // would result in the zeroing of that register. Since the active debug + // registers would have been cloned when this thread was created, simply + // force the m_watchpoints_initized member to TRUE and avoid resetting + // dr6 and dr7. + GetPOSIXBreakpointProtocol()->ForceWatchpointsInitialized(); + } + } +} + +FreeBSDThread::~FreeBSDThread() { DestroyThread(); } + +ProcessMonitor &FreeBSDThread::GetMonitor() { + ProcessSP base = GetProcess(); + ProcessFreeBSD &process = static_cast<ProcessFreeBSD &>(*base); + return process.GetMonitor(); +} + +void FreeBSDThread::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. if (StateIsStoppedState(GetState()) + { + const bool force = false; + GetRegisterContext()->InvalidateIfNeeded(force); + } +} + +const char *FreeBSDThread::GetInfo() { return nullptr; } + +void FreeBSDThread::SetName(const char *name) { + m_thread_name_valid = (name && name[0]); + if (m_thread_name_valid) + m_thread_name.assign(name); + else + m_thread_name.clear(); +} + +const char *FreeBSDThread::GetName() { + if (!m_thread_name_valid) { + m_thread_name.clear(); + int pid = GetProcess()->GetID(); + + struct kinfo_proc *kp = nullptr, *nkp; + size_t len = 0; + int error; + int ctl[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, + pid}; + + while (1) { + error = sysctl(ctl, 4, kp, &len, nullptr, 0); + if (kp == nullptr || (error != 0 && errno == ENOMEM)) { + // Add extra space in case threads are added before next call. + len += sizeof(*kp) + len / 10; + nkp = (struct kinfo_proc *)realloc(kp, len); + if (nkp == nullptr) { + free(kp); + return nullptr; + } + kp = nkp; + continue; + } + if (error != 0) + len = 0; + break; + } + + for (size_t i = 0; i < len / sizeof(*kp); i++) { + if (kp[i].ki_tid == (lwpid_t)GetID()) { + m_thread_name.append(kp[i].ki_tdname, + kp[i].ki_tdname + strlen(kp[i].ki_tdname)); + break; + } + } + free(kp); + m_thread_name_valid = true; + } + + if (m_thread_name.empty()) + return nullptr; + return m_thread_name.c_str(); +} + +lldb::RegisterContextSP FreeBSDThread::GetRegisterContext() { + if (!m_reg_context_sp) { + m_posix_thread = nullptr; + + RegisterInfoInterface *reg_interface = nullptr; + const ArchSpec &target_arch = GetProcess()->GetTarget().GetArchitecture(); + + assert(target_arch.GetTriple().getOS() == llvm::Triple::FreeBSD); + switch (target_arch.GetMachine()) { + case llvm::Triple::aarch64: + reg_interface = new RegisterInfoPOSIX_arm64(target_arch); + break; + case llvm::Triple::arm: + reg_interface = new RegisterInfoPOSIX_arm(target_arch); + break; + case llvm::Triple::ppc: +#ifndef __powerpc64__ + reg_interface = new RegisterContextFreeBSD_powerpc32(target_arch); + break; +#endif + case llvm::Triple::ppc64: + reg_interface = new RegisterContextFreeBSD_powerpc64(target_arch); + break; + case llvm::Triple::mips64: + reg_interface = new RegisterContextFreeBSD_mips64(target_arch); + break; + case llvm::Triple::x86: + reg_interface = new RegisterContextFreeBSD_i386(target_arch); + break; + case llvm::Triple::x86_64: + reg_interface = new RegisterContextFreeBSD_x86_64(target_arch); + break; + default: + llvm_unreachable("CPU not supported"); + } + + switch (target_arch.GetMachine()) { + case llvm::Triple::aarch64: { + RegisterContextPOSIXProcessMonitor_arm64 *reg_ctx = + new RegisterContextPOSIXProcessMonitor_arm64(*this, 0, reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } + case llvm::Triple::arm: { + RegisterContextPOSIXProcessMonitor_arm *reg_ctx = + new RegisterContextPOSIXProcessMonitor_arm(*this, 0, reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } + case llvm::Triple::mips64: { + RegisterContextPOSIXProcessMonitor_mips64 *reg_ctx = + new RegisterContextPOSIXProcessMonitor_mips64(*this, 0, + reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } + case llvm::Triple::ppc: + case llvm::Triple::ppc64: { + RegisterContextPOSIXProcessMonitor_powerpc *reg_ctx = + new RegisterContextPOSIXProcessMonitor_powerpc(*this, 0, + reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } + case llvm::Triple::x86: + case llvm::Triple::x86_64: { + RegisterContextPOSIXProcessMonitor_x86_64 *reg_ctx = + new RegisterContextPOSIXProcessMonitor_x86_64(*this, 0, + reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } + default: + break; + } + } + return m_reg_context_sp; +} + +lldb::RegisterContextSP +FreeBSDThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) { + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + LLDB_LOGV(log, "called"); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) + reg_ctx_sp = GetRegisterContext(); + else { + assert(GetUnwinder()); + reg_ctx_sp = GetUnwinder()->CreateRegisterContextForFrame(frame); + } + + return reg_ctx_sp; +} + +lldb::addr_t FreeBSDThread::GetThreadPointer() { + ProcessMonitor &monitor = GetMonitor(); + addr_t addr; + if (monitor.ReadThreadPointer(GetID(), addr)) + return addr; + else + return LLDB_INVALID_ADDRESS; +} + +bool FreeBSDThread::CalculateStopInfo() { + SetStopInfo(m_stop_info_sp); + return true; +} + +Unwind *FreeBSDThread::GetUnwinder() { + if (!m_unwinder_up) + m_unwinder_up.reset(new UnwindLLDB(*this)); + + return m_unwinder_up.get(); +} + +void FreeBSDThread::DidStop() { + // Don't set the thread state to stopped unless we really stopped. +} + +void FreeBSDThread::WillResume(lldb::StateType resume_state) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + LLDB_LOGF(log, "tid %lu resume_state = %s", GetID(), + lldb_private::StateAsCString(resume_state)); + ProcessSP process_sp(GetProcess()); + ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(process_sp.get()); + int signo = GetResumeSignal(); + bool signo_valid = process->GetUnixSignals()->SignalIsValid(signo); + + switch (resume_state) { + case eStateSuspended: + case eStateStopped: + process->m_suspend_tids.push_back(GetID()); + break; + case eStateRunning: + process->m_run_tids.push_back(GetID()); + if (signo_valid) + process->m_resume_signo = signo; + break; + case eStateStepping: + process->m_step_tids.push_back(GetID()); + if (signo_valid) + process->m_resume_signo = signo; + break; + default: + break; + } +} + +bool FreeBSDThread::Resume() { + lldb::StateType resume_state = GetResumeState(); + ProcessMonitor &monitor = GetMonitor(); + bool status; + + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + LLDB_LOGF(log, "FreeBSDThread::%s (), resume_state = %s", __FUNCTION__, + StateAsCString(resume_state)); + + switch (resume_state) { + default: + assert(false && "Unexpected state for resume!"); + status = false; + break; + + case lldb::eStateRunning: + SetState(resume_state); + status = monitor.Resume(GetID(), GetResumeSignal()); + break; + + case lldb::eStateStepping: + SetState(resume_state); + status = monitor.SingleStep(GetID(), GetResumeSignal()); + break; + case lldb::eStateStopped: + case lldb::eStateSuspended: + status = true; + break; + } + + return status; +} + +void FreeBSDThread::Notify(const ProcessMessage &message) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + LLDB_LOGF(log, "FreeBSDThread::%s () message kind = '%s' for tid %" PRIu64, + __FUNCTION__, message.PrintKind(), GetID()); + + switch (message.GetKind()) { + default: + assert(false && "Unexpected message kind!"); + break; + + case ProcessMessage::eExitMessage: + // Nothing to be done. + break; + + case ProcessMessage::eLimboMessage: + LimboNotify(message); + break; + + case ProcessMessage::eCrashMessage: + case ProcessMessage::eSignalMessage: + SignalNotify(message); + break; + + case ProcessMessage::eSignalDeliveredMessage: + SignalDeliveredNotify(message); + break; + + case ProcessMessage::eTraceMessage: + TraceNotify(message); + break; + + case ProcessMessage::eBreakpointMessage: + BreakNotify(message); + break; + + case ProcessMessage::eWatchpointMessage: + WatchNotify(message); + break; + + case ProcessMessage::eExecMessage: + ExecNotify(message); + break; + } +} + +bool FreeBSDThread::EnableHardwareWatchpoint(Watchpoint *wp) { + bool wp_set = false; + if (wp) { + addr_t wp_addr = wp->GetLoadAddress(); + size_t wp_size = wp->GetByteSize(); + bool wp_read = wp->WatchpointRead(); + bool wp_write = wp->WatchpointWrite(); + uint32_t wp_hw_index = wp->GetHardwareIndex(); + POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol(); + if (reg_ctx) + wp_set = reg_ctx->SetHardwareWatchpointWithIndex( + wp_addr, wp_size, wp_read, wp_write, wp_hw_index); + } + return wp_set; +} + +bool FreeBSDThread::DisableHardwareWatchpoint(Watchpoint *wp) { + bool result = false; + if (wp) { + lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext(); + if (reg_ctx_sp.get()) + result = reg_ctx_sp->ClearHardwareWatchpoint(wp->GetHardwareIndex()); + } + return result; +} + +uint32_t FreeBSDThread::NumSupportedHardwareWatchpoints() { + lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext(); + if (reg_ctx_sp.get()) + return reg_ctx_sp->NumSupportedHardwareWatchpoints(); + return 0; +} + +uint32_t FreeBSDThread::FindVacantWatchpointIndex() { + uint32_t hw_index = LLDB_INVALID_INDEX32; + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + uint32_t wp_idx; + POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol(); + if (reg_ctx) { + for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) { + if (reg_ctx->IsWatchpointVacant(wp_idx)) { + hw_index = wp_idx; + break; + } + } + } + return hw_index; +} + +void FreeBSDThread::BreakNotify(const ProcessMessage &message) { + bool status; + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + + assert(GetRegisterContext()); + status = GetPOSIXBreakpointProtocol()->UpdateAfterBreakpoint(); + assert(status && "Breakpoint update failed!"); + + // With our register state restored, resolve the breakpoint object + // corresponding to our current PC. + assert(GetRegisterContext()); + lldb::addr_t pc = GetRegisterContext()->GetPC(); + LLDB_LOGF(log, "FreeBSDThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc); + lldb::BreakpointSiteSP bp_site( + GetProcess()->GetBreakpointSiteList().FindByAddress(pc)); + + // If the breakpoint is for this thread, then we'll report the hit, but if it + // is for another thread, we create a stop reason with should_stop=false. If + // there is no breakpoint location, then report an invalid stop reason. We + // don't need to worry about stepping over the breakpoint here, that will be + // taken care of when the thread resumes and notices that there's a + // breakpoint under the pc. + if (bp_site) { + lldb::break_id_t bp_id = bp_site->GetID(); + // If we have an operating system plug-in, we might have set a thread + // specific breakpoint using the operating system thread ID, so we can't + // make any assumptions about the thread ID so we must always report the + // breakpoint regardless of the thread. + if (bp_site->ValidForThisThread(this) || + GetProcess()->GetOperatingSystem() != nullptr) + SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id)); + else { + const bool should_stop = false; + SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id, + should_stop)); + } + } else + SetStopInfo(StopInfoSP()); +} + +void FreeBSDThread::WatchNotify(const ProcessMessage &message) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + + lldb::addr_t halt_addr = message.GetHWAddress(); + LLDB_LOGF(log, + "FreeBSDThread::%s () Hardware Watchpoint Address = 0x%8.8" PRIx64, + __FUNCTION__, halt_addr); + + POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol(); + if (reg_ctx) { + uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints(); + uint32_t wp_idx; + for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) { + if (reg_ctx->IsWatchpointHit(wp_idx)) { + // Clear the watchpoint hit here + reg_ctx->ClearWatchpointHits(); + break; + } + } + + if (wp_idx == num_hw_wps) + return; + + Target &target = GetProcess()->GetTarget(); + lldb::addr_t wp_monitor_addr = reg_ctx->GetWatchpointAddress(wp_idx); + const WatchpointList &wp_list = target.GetWatchpointList(); + lldb::WatchpointSP wp_sp = wp_list.FindByAddress(wp_monitor_addr); + + assert(wp_sp.get() && "No watchpoint found"); + SetStopInfo( + StopInfo::CreateStopReasonWithWatchpointID(*this, wp_sp->GetID())); + } +} + +void FreeBSDThread::TraceNotify(const ProcessMessage &message) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + + // Try to resolve the breakpoint object corresponding to the current PC. + assert(GetRegisterContext()); + lldb::addr_t pc = GetRegisterContext()->GetPC(); + LLDB_LOGF(log, "FreeBSDThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc); + lldb::BreakpointSiteSP bp_site( + GetProcess()->GetBreakpointSiteList().FindByAddress(pc)); + + // If the current pc is a breakpoint site then set the StopInfo to + // Breakpoint. Otherwise, set the StopInfo to Watchpoint or Trace. If we have + // an operating system plug-in, we might have set a thread specific + // breakpoint using the operating system thread ID, so we can't make any + // assumptions about the thread ID so we must always report the breakpoint + // regardless of the thread. + if (bp_site && (bp_site->ValidForThisThread(this) || + GetProcess()->GetOperatingSystem() != nullptr)) + SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID( + *this, bp_site->GetID())); + else { + POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol(); + if (reg_ctx) { + uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints(); + uint32_t wp_idx; + for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) { + if (reg_ctx->IsWatchpointHit(wp_idx)) { + WatchNotify(message); + return; + } + } + } + SetStopInfo(StopInfo::CreateStopReasonToTrace(*this)); + } +} + +void FreeBSDThread::LimboNotify(const ProcessMessage &message) { + SetStopInfo(lldb::StopInfoSP(new POSIXLimboStopInfo(*this))); +} + +void FreeBSDThread::SignalNotify(const ProcessMessage &message) { + int signo = message.GetSignal(); + if (message.GetKind() == ProcessMessage::eCrashMessage) { + std::string stop_description = GetCrashReasonString( + message.GetCrashReason(), message.GetFaultAddress()); + SetStopInfo(StopInfo::CreateStopReasonWithSignal( + *this, signo, stop_description.c_str())); + } else { + SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, signo)); + } +} + +void FreeBSDThread::SignalDeliveredNotify(const ProcessMessage &message) { + int signo = message.GetSignal(); + SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, signo)); +} + +unsigned FreeBSDThread::GetRegisterIndexFromOffset(unsigned offset) { + unsigned reg = LLDB_INVALID_REGNUM; + ArchSpec arch = HostInfo::GetArchitecture(); + + switch (arch.GetMachine()) { + default: + llvm_unreachable("CPU type not supported!"); + break; + + case llvm::Triple::aarch64: + case llvm::Triple::arm: + case llvm::Triple::mips64: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + case llvm::Triple::x86: + case llvm::Triple::x86_64: { + POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol(); + reg = reg_ctx->GetRegisterIndexFromOffset(offset); + } break; + } + return reg; +} + +void FreeBSDThread::ExecNotify(const ProcessMessage &message) { + SetStopInfo(StopInfo::CreateStopReasonWithExec(*this)); +} + +const char *FreeBSDThread::GetRegisterName(unsigned reg) { + const char *name = nullptr; + ArchSpec arch = HostInfo::GetArchitecture(); + + switch (arch.GetMachine()) { + default: + assert(false && "CPU type not supported!"); + break; + + case llvm::Triple::aarch64: + case llvm::Triple::arm: + case llvm::Triple::mips64: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + case llvm::Triple::x86: + case llvm::Triple::x86_64: + name = GetRegisterContext()->GetRegisterName(reg); + break; + } + return name; +} + +const char *FreeBSDThread::GetRegisterNameFromOffset(unsigned offset) { + return GetRegisterName(GetRegisterIndexFromOffset(offset)); +} |