Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp (nonexistent) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp (revision 263368) @@ -0,0 +1,69 @@ +//===-- FreeBSDThread.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/State.h" + +// Project includes +#include "FreeBSDThread.h" +#include "ProcessFreeBSD.h" +#include "ProcessPOSIXLog.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +FreeBSDThread::FreeBSDThread(Process &process, lldb::tid_t tid) + : POSIXThread(process, tid) +{ +} + +FreeBSDThread::~FreeBSDThread() +{ +} + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + +void +FreeBSDThread::WillResume(lldb::StateType resume_state) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf("tid %lu resume_state = %s", GetID(), + lldb_private::StateAsCString(resume_state)); + ProcessSP process_sp(GetProcess()); + ProcessFreeBSD *process = static_cast(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; + } +} Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h (nonexistent) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h (revision 263368) @@ -0,0 +1,39 @@ +//===-- FreeBSDThread.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FreeBSDThread_H_ +#define liblldb_FreeBSDThread_H_ + +// Other libraries and framework includes +#include "POSIXThread.h" + +//------------------------------------------------------------------------------ +// @class FreeBSDThread +// @brief Abstraction of a FreeBSD thread. +class FreeBSDThread + : public POSIXThread +{ +public: + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + FreeBSDThread(lldb_private::Process &process, lldb::tid_t tid); + + virtual ~FreeBSDThread(); + + //-------------------------------------------------------------------------- + // FreeBSDThread internal API. + + // POSIXThread override + virtual void + WillResume(lldb::StateType resume_state); +}; + +#endif // #ifndef liblldb_FreeBSDThread_H_ Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp (revision 263367) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp (revision 263368) @@ -1,168 +1,275 @@ //===-- ProcessFreeBSD.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include // C++ Includes // Other libraries and framework includes #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Target.h" #include "ProcessFreeBSD.h" #include "ProcessPOSIXLog.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "ProcessMonitor.h" -#include "POSIXThread.h" +#include "FreeBSDThread.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------------ // Static functions. lldb::ProcessSP ProcessFreeBSD::CreateInstance(Target& target, Listener &listener, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) process_sp.reset(new ProcessFreeBSD (target, listener)); return process_sp; } void ProcessFreeBSD::Initialize() { static bool g_initialized = false; if (!g_initialized) { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); Log::Callbacks log_callbacks = { ProcessPOSIXLog::DisableLog, ProcessPOSIXLog::EnableLog, ProcessPOSIXLog::ListLogCategories }; Log::RegisterLogChannel (ProcessFreeBSD::GetPluginNameStatic(), log_callbacks); ProcessPOSIXLog::RegisterPluginName(GetPluginNameStatic()); g_initialized = true; } } lldb_private::ConstString ProcessFreeBSD::GetPluginNameStatic() { static ConstString g_name("freebsd"); return g_name; } const char * ProcessFreeBSD::GetPluginDescriptionStatic() { return "Process plugin for FreeBSD"; } //------------------------------------------------------------------------------ // ProcessInterface protocol. lldb_private::ConstString ProcessFreeBSD::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessFreeBSD::GetPluginVersion() { return 1; } void ProcessFreeBSD::GetPluginCommandHelp(const char *command, Stream *strm) { } Error ProcessFreeBSD::ExecutePluginCommand(Args &command, Stream *strm) { return Error(1, eErrorTypeGeneric); } Log * ProcessFreeBSD::EnablePluginLogging(Stream *strm, Args &command) { return NULL; } //------------------------------------------------------------------------------ // Constructors and destructors. ProcessFreeBSD::ProcessFreeBSD(Target& target, Listener &listener) : ProcessPOSIX(target, listener) { } void ProcessFreeBSD::Terminate() { } Error ProcessFreeBSD::DoDetach(bool keep_stopped) { Error error; if (keep_stopped) { error.SetErrorString("Detaching with keep_stopped true is not currently supported on FreeBSD."); return error; } error = m_monitor->Detach(GetID()); if (error.Success()) SetPrivateState(eStateDetached); return error; } +Error +ProcessFreeBSD::DoResume() +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + // FreeBSD's ptrace() uses 0 to indicate "no signal is to be sent." + int resume_signal = 0; + + SetPrivateState(eStateRunning); + + Mutex::Locker lock(m_thread_list.GetMutex()); + bool do_step = false; + + for (tid_collection::const_iterator t_pos = m_run_tids.begin(), t_end = m_run_tids.end(); t_pos != t_end; ++t_pos) + { + m_monitor->ThreadSuspend(*t_pos, false); + } + for (tid_collection::const_iterator t_pos = m_step_tids.begin(), t_end = m_step_tids.end(); t_pos != t_end; ++t_pos) + { + m_monitor->ThreadSuspend(*t_pos, false); + do_step = true; + } + for (tid_collection::const_iterator t_pos = m_suspend_tids.begin(), t_end = m_suspend_tids.end(); t_pos != t_end; ++t_pos) + { + m_monitor->ThreadSuspend(*t_pos, true); + // XXX Cannot PT_CONTINUE properly with suspended threads. + do_step = true; + } + + if (log) + log->Printf("process %lu resuming (%s)", GetID(), do_step ? "step" : "continue"); + if (do_step) + m_monitor->SingleStep(GetID(), resume_signal); + else + m_monitor->Resume(GetID(), resume_signal); + + return Error(); +} + bool ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { - Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); - if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) - log->Printf ("ProcessFreeBSD::%s() (pid = %" PRIu64 ")", __FUNCTION__, GetID()); + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log) + log->Printf("ProcessFreeBSD::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); - bool has_updated = false; - const lldb::pid_t pid = GetID(); - // Update the process thread list with this new thread. - // FIXME: We should be using tid, not pid. - assert(m_monitor); - ThreadSP thread_sp (old_thread_list.FindThreadByID (pid, false)); - if (!thread_sp) { - ProcessSP me = this->shared_from_this(); - thread_sp.reset(new POSIXThread(*me, pid)); - has_updated = true; + std::vector tds; + if (!GetMonitor().GetCurrentThreadIDs(tds)) + { + return false; } - if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) - log->Printf ("ProcessFreeBSD::%s() updated tid = %" PRIu64, __FUNCTION__, pid); + ThreadList old_thread_list_copy(old_thread_list); + for (size_t i = 0; i < tds.size(); ++i) + { + tid_t tid = tds[i]; + ThreadSP thread_sp (old_thread_list_copy.RemoveThreadByID(tid, false)); + if (!thread_sp) + { + thread_sp.reset(new FreeBSDThread(*this, tid)); + if (log) + log->Printf("ProcessFreeBSD::%s new tid = %" PRIu64, __FUNCTION__, tid); + } + else + { + if (log) + log->Printf("ProcessFreeBSD::%s existing tid = %" PRIu64, __FUNCTION__, tid); + } + new_thread_list.AddThread(thread_sp); + } + for (size_t i = 0; i < old_thread_list_copy.GetSize(false); ++i) + { + ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false)); + if (old_thread_sp) + { + if (log) + log->Printf("ProcessFreeBSD::%s remove tid", __FUNCTION__); + } + } - new_thread_list.AddThread(thread_sp); + return true; +} - return has_updated; // the list has been updated +Error +ProcessFreeBSD::WillResume() +{ + m_suspend_tids.clear(); + m_run_tids.clear(); + m_step_tids.clear(); + return ProcessPOSIX::WillResume(); } + +void +ProcessFreeBSD::SendMessage(const ProcessMessage &message) +{ + Mutex::Locker lock(m_message_mutex); + + switch (message.GetKind()) + { + case ProcessMessage::eInvalidMessage: + return; + + case ProcessMessage::eAttachMessage: + SetPrivateState(eStateStopped); + return; + + case ProcessMessage::eLimboMessage: + case ProcessMessage::eExitMessage: + m_exit_status = message.GetExitStatus(); + SetExitStatus(m_exit_status, NULL); + break; + + case ProcessMessage::eSignalMessage: + case ProcessMessage::eSignalDeliveredMessage: + case ProcessMessage::eBreakpointMessage: + case ProcessMessage::eTraceMessage: + case ProcessMessage::eWatchpointMessage: + case ProcessMessage::eCrashMessage: + SetPrivateState(eStateStopped); + break; + + case ProcessMessage::eNewThreadMessage: + assert(0 && "eNewThreadMessage unexpected on FreeBSD"); + break; + + case ProcessMessage::eExecMessage: + SetPrivateState(eStateStopped); + break; + } + + m_message_queue.push(message); +} + Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h (revision 263367) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h (revision 263368) @@ -1,85 +1,104 @@ //===-- ProcessFreeBSD.h ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ProcessFreeBSD_H_ #define liblldb_ProcessFreeBSD_H_ // C Includes // C++ Includes #include // Other libraries and framework includes #include "lldb/Target/Process.h" #include "lldb/Target/ThreadList.h" #include "ProcessMessage.h" #include "ProcessPOSIX.h" class ProcessMonitor; class ProcessFreeBSD : public ProcessPOSIX { public: //------------------------------------------------------------------ // Static functions. //------------------------------------------------------------------ static lldb::ProcessSP CreateInstance(lldb_private::Target& target, lldb_private::Listener &listener, const lldb_private::FileSpec *crash_file_path); static void Initialize(); static void Terminate(); static lldb_private::ConstString GetPluginNameStatic(); static const char * GetPluginDescriptionStatic(); //------------------------------------------------------------------ // Constructors and destructors //------------------------------------------------------------------ ProcessFreeBSD(lldb_private::Target& target, lldb_private::Listener &listener); virtual lldb_private::Error DoDetach(bool keep_stopped); virtual bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list); + virtual lldb_private::Error + DoResume(); + + virtual lldb_private::Error + WillResume(); + + virtual void + SendMessage(const ProcessMessage &message); + //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ virtual lldb_private::ConstString GetPluginName(); virtual uint32_t GetPluginVersion(); virtual void GetPluginCommandHelp(const char *command, lldb_private::Stream *strm); virtual lldb_private::Error ExecutePluginCommand(lldb_private::Args &command, lldb_private::Stream *strm); virtual lldb_private::Log * EnablePluginLogging(lldb_private::Stream *strm, lldb_private::Args &command); + +protected: + friend class FreeBSDThread; + + typedef std::vector tid_collection; + tid_collection m_suspend_tids; + tid_collection m_run_tids; + tid_collection m_step_tids; + + int m_resume_signo; }; #endif // liblldb_MacOSXProcess_H_ Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp (revision 263367) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp (revision 263368) @@ -1,1603 +1,1660 @@ //===-- ProcessMonitor.cpp ------------------------------------ -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include #include #include #include #include #include #include #include #include #include // C++ Includes // Other libraries and framework includes #include "lldb/Core/Error.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Host/Host.h" #include "lldb/Target/Thread.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Utility/PseudoTerminal.h" #include "POSIXThread.h" #include "ProcessFreeBSD.h" #include "ProcessPOSIXLog.h" #include "ProcessMonitor.h" extern "C" { extern char ** environ; } using namespace lldb; using namespace lldb_private; // We disable the tracing of ptrace calls for integration builds to // avoid the additional indirection and checks. #ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION // Wrapper for ptrace to catch errors and log calls. const char * Get_PT_IO_OP(int op) { switch (op) { case PIOD_READ_D: return "READ_D"; case PIOD_WRITE_D: return "WRITE_D"; case PIOD_READ_I: return "READ_I"; case PIOD_WRITE_I: return "WRITE_I"; default: return "Unknown op"; } } // Wrapper for ptrace to catch errors and log calls. // Note that ptrace sets errno on error because -1 is reserved as a valid result. extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, const char* reqName, const char* file, int line) { long int result; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (log) { log->Printf("ptrace(%s, %" PRIu64 ", %p, %x) called from file %s line %d", reqName, pid, addr, data, file, line); if (req == PT_IO) { struct ptrace_io_desc *pi = (struct ptrace_io_desc *) addr; log->Printf("PT_IO: op=%s offs=%zx size=%zu", Get_PT_IO_OP(pi->piod_op), (size_t)pi->piod_offs, pi->piod_len); } } //PtraceDisplayBytes(req, data); errno = 0; result = ptrace(req, pid, (caddr_t) addr, data); //PtraceDisplayBytes(req, data); if (log && errno != 0) { const char* str; switch (errno) { case ESRCH: str = "ESRCH"; break; case EINVAL: str = "EINVAL"; break; case EBUSY: str = "EBUSY"; break; case EPERM: str = "EPERM"; break; default: str = ""; } log->Printf("ptrace() failed; errno=%d (%s)", errno, str); } #ifdef __amd64__ if (log) { if (req == PT_GETREGS) { struct reg *r = (struct reg *) addr; log->Printf("PT_GETREGS: ip=0x%lx", r->r_rip); log->Printf("PT_GETREGS: sp=0x%lx", r->r_rsp); log->Printf("PT_GETREGS: bp=0x%lx", r->r_rbp); log->Printf("PT_GETREGS: ax=0x%lx", r->r_rax); } } #endif return result; } // Wrapper for ptrace when logging is not required. // Sets errno to 0 prior to calling ptrace. extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data) { long result = 0; errno = 0; result = ptrace(req, pid, (caddr_t)addr, data); return result; } #define PTRACE(req, pid, addr, data) \ PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__) #else PtraceWrapper((req), (pid), (addr), (data)) #endif //------------------------------------------------------------------------------ // Static implementations of ProcessMonitor::ReadMemory and // ProcessMonitor::WriteMemory. This enables mutual recursion between these // functions without needed to go thru the thread funnel. static size_t DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf, size_t size, Error &error) { struct ptrace_io_desc pi_desc; pi_desc.piod_op = PIOD_READ_D; pi_desc.piod_offs = (void *)vm_addr; pi_desc.piod_addr = buf; pi_desc.piod_len = size; if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) error.SetErrorToErrno(); return pi_desc.piod_len; } static size_t DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) { struct ptrace_io_desc pi_desc; pi_desc.piod_op = PIOD_WRITE_D; pi_desc.piod_offs = (void *)vm_addr; pi_desc.piod_addr = (void *)buf; pi_desc.piod_len = size; if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) error.SetErrorToErrno(); return pi_desc.piod_len; } // Simple helper function to ensure flags are enabled on the given file // descriptor. static bool EnsureFDFlags(int fd, int flags, Error &error) { int status; if ((status = fcntl(fd, F_GETFL)) == -1) { error.SetErrorToErrno(); return false; } if (fcntl(fd, F_SETFL, status | flags) == -1) { error.SetErrorToErrno(); return false; } return true; } //------------------------------------------------------------------------------ /// @class Operation /// @brief Represents a ProcessMonitor operation. /// /// Under FreeBSD, it is not possible to ptrace() from any other thread but the /// one that spawned or attached to the process from the start. Therefore, when /// a ProcessMonitor is asked to deliver or change the state of an inferior /// process the operation must be "funneled" to a specific thread to perform the /// task. The Operation class provides an abstract base for all services the /// ProcessMonitor must perform via the single virtual function Execute, thus /// encapsulating the code that needs to run in the privileged context. class Operation { public: virtual ~Operation() {} virtual void Execute(ProcessMonitor *monitor) = 0; }; //------------------------------------------------------------------------------ /// @class ReadOperation /// @brief Implements ProcessMonitor::ReadMemory. class ReadOperation : public Operation { public: ReadOperation(lldb::addr_t addr, void *buff, size_t size, Error &error, size_t &result) : m_addr(addr), m_buff(buff), m_size(size), m_error(error), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::addr_t m_addr; void *m_buff; size_t m_size; Error &m_error; size_t &m_result; }; void ReadOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error); } //------------------------------------------------------------------------------ /// @class WriteOperation /// @brief Implements ProcessMonitor::WriteMemory. class WriteOperation : public Operation { public: WriteOperation(lldb::addr_t addr, const void *buff, size_t size, Error &error, size_t &result) : m_addr(addr), m_buff(buff), m_size(size), m_error(error), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::addr_t m_addr; const void *m_buff; size_t m_size; Error &m_error; size_t &m_result; }; void WriteOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error); } //------------------------------------------------------------------------------ /// @class ReadRegOperation /// @brief Implements ProcessMonitor::ReadRegisterValue. class ReadRegOperation : public Operation { public: ReadRegOperation(lldb::tid_t tid, unsigned offset, unsigned size, RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_size(size), m_value(value), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; unsigned m_size; RegisterValue &m_value; bool &m_result; }; void ReadRegOperation::Execute(ProcessMonitor *monitor) { struct reg regs; int rc; if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0)) < 0) { m_result = false; } else { if (m_size == sizeof(uintptr_t)) m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); else memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } //------------------------------------------------------------------------------ /// @class WriteRegOperation /// @brief Implements ProcessMonitor::WriteRegisterValue. class WriteRegOperation : public Operation { public: WriteRegOperation(lldb::tid_t tid, unsigned offset, const RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_value(value), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; const RegisterValue &m_value; bool &m_result; }; void WriteRegOperation::Execute(ProcessMonitor *monitor) { struct reg regs; if (PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0) < 0) { m_result = false; return; } *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); if (PTRACE(PT_SETREGS, m_tid, (caddr_t)®s, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadGPROperation /// @brief Implements ProcessMonitor::ReadGPR. class ReadGPROperation : public Operation { public: ReadGPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void ReadGPROperation::Execute(ProcessMonitor *monitor) { int rc; errno = 0; rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)m_buf, 0); if (errno != 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadFPROperation /// @brief Implements ProcessMonitor::ReadFPR. class ReadFPROperation : public Operation { public: ReadFPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void ReadFPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_GETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class WriteGPROperation /// @brief Implements ProcessMonitor::WriteGPR. class WriteGPROperation : public Operation { public: WriteGPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void WriteGPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_SETREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class WriteFPROperation /// @brief Implements ProcessMonitor::WriteFPR. class WriteFPROperation : public Operation { public: WriteFPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void WriteFPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_SETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ResumeOperation /// @brief Implements ProcessMonitor::Resume. class ResumeOperation : public Operation { public: ResumeOperation(uint32_t signo, bool &result) : m_signo(signo), m_result(result) { } void Execute(ProcessMonitor *monitor); private: uint32_t m_signo; bool &m_result; }; void ResumeOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); int data = 0; if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; if (PTRACE(PT_CONTINUE, pid, (caddr_t)1, data)) { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (log) log->Printf ("ResumeOperation (%" PRIu64 ") failed: %s", pid, strerror(errno)); m_result = false; } else m_result = true; } //------------------------------------------------------------------------------ /// @class SingleStepOperation /// @brief Implements ProcessMonitor::SingleStep. class SingleStepOperation : public Operation { public: SingleStepOperation(uint32_t signo, bool &result) : m_signo(signo), m_result(result) { } void Execute(ProcessMonitor *monitor); private: uint32_t m_signo; bool &m_result; }; void SingleStepOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); int data = 0; if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; if (PTRACE(PT_STEP, pid, NULL, data)) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class LwpInfoOperation /// @brief Implements ProcessMonitor::GetLwpInfo. class LwpInfoOperation : public Operation { public: LwpInfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err) : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_info; bool &m_result; int &m_err; }; void LwpInfoOperation::Execute(ProcessMonitor *monitor) { struct ptrace_lwpinfo plwp; if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) { m_result = false; m_err = errno; } else { memcpy(m_info, &plwp, sizeof(plwp)); m_result = true; } } //------------------------------------------------------------------------------ +/// @class ThreadSuspendOperation +/// @brief Implements ProcessMonitor::ThreadSuspend. +class ThreadSuspendOperation : public Operation +{ +public: + ThreadSuspendOperation(lldb::tid_t tid, bool suspend, bool &result) + : m_tid(tid), m_suspend(suspend), m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + bool m_suspend; + bool &m_result; +} ; + +void +ThreadSuspendOperation::Execute(ProcessMonitor *monitor) +{ + m_result = !PTRACE(m_suspend ? PT_SUSPEND : PT_RESUME, m_tid, NULL, 0); +} + + + +//------------------------------------------------------------------------------ /// @class EventMessageOperation /// @brief Implements ProcessMonitor::GetEventMessage. class EventMessageOperation : public Operation { public: EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result) : m_tid(tid), m_message(message), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned long *m_message; bool &m_result; }; void EventMessageOperation::Execute(ProcessMonitor *monitor) { struct ptrace_lwpinfo plwp; if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) m_result = false; else { if (plwp.pl_flags & PL_FLAG_FORKED) { *m_message = plwp.pl_child_pid; m_result = true; } else m_result = false; } } //------------------------------------------------------------------------------ /// @class KillOperation /// @brief Implements ProcessMonitor::BringProcessIntoLimbo. class KillOperation : public Operation { public: KillOperation(bool &result) : m_result(result) { } void Execute(ProcessMonitor *monitor); private: bool &m_result; }; void KillOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); if (PTRACE(PT_KILL, pid, NULL, 0)) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class DetachOperation /// @brief Implements ProcessMonitor::BringProcessIntoLimbo. class DetachOperation : public Operation { public: DetachOperation(Error &result) : m_error(result) { } void Execute(ProcessMonitor *monitor); private: Error &m_error; }; void DetachOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); if (PTRACE(PT_DETACH, pid, NULL, 0) < 0) m_error.SetErrorToErrno(); } ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor) : m_monitor(monitor) { sem_init(&m_semaphore, 0, 0); } ProcessMonitor::OperationArgs::~OperationArgs() { sem_destroy(&m_semaphore); } ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor, lldb_private::Module *module, char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_dir) : OperationArgs(monitor), m_module(module), m_argv(argv), m_envp(envp), m_stdin_path(stdin_path), m_stdout_path(stdout_path), m_stderr_path(stderr_path), m_working_dir(working_dir) { } ProcessMonitor::LaunchArgs::~LaunchArgs() { } ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid) : OperationArgs(monitor), m_pid(pid) { } ProcessMonitor::AttachArgs::~AttachArgs() { } //------------------------------------------------------------------------------ /// The basic design of the ProcessMonitor is built around two threads. /// /// One thread (@see SignalThread) simply blocks on a call to waitpid() looking /// for changes in the debugee state. When a change is detected a /// ProcessMessage is sent to the associated ProcessFreeBSD instance. This thread /// "drives" state changes in the debugger. /// /// The second thread (@see OperationThread) is responsible for two things 1) /// launching or attaching to the inferior process, and then 2) servicing /// operations such as register reads/writes, stepping, etc. See the comments /// on the Operation class for more info as to why this is needed. ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, Module *module, const char *argv[], const char *envp[], const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_dir, lldb_private::Error &error) : m_process(static_cast(process)), m_operation_thread(LLDB_INVALID_HOST_THREAD), m_monitor_thread(LLDB_INVALID_HOST_THREAD), m_pid(LLDB_INVALID_PROCESS_ID), m_terminal_fd(-1), m_operation(0) { std::unique_ptr args(new LaunchArgs(this, module, argv, envp, stdin_path, stdout_path, stderr_path, working_dir)); sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); StartLaunchOpThread(args.get(), error); if (!error.Success()) return; WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait(&args->m_semaphore)) { if (errno == EINTR) goto WAIT_AGAIN; else { error.SetErrorToErrno(); return; } } // Check that the launch was a success. if (!args->m_error.Success()) { StopOpThread(); error = args->m_error; return; } // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( ProcessMonitor::MonitorCallback, this, GetPID(), true); if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) { error.SetErrorToGenericError(); error.SetErrorString("Process launch failed."); return; } } ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, lldb::pid_t pid, lldb_private::Error &error) : m_process(static_cast(process)), m_operation_thread(LLDB_INVALID_HOST_THREAD), m_monitor_thread(LLDB_INVALID_HOST_THREAD), m_pid(pid), m_terminal_fd(-1), m_operation(0) { sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); std::unique_ptr args(new AttachArgs(this, pid)); StartAttachOpThread(args.get(), error); if (!error.Success()) return; WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait(&args->m_semaphore)) { if (errno == EINTR) goto WAIT_AGAIN; else { error.SetErrorToErrno(); return; } } // Check that the attach was a success. if (!args->m_error.Success()) { StopOpThread(); error = args->m_error; return; } // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( ProcessMonitor::MonitorCallback, this, GetPID(), true); if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) { error.SetErrorToGenericError(); error.SetErrorString("Process attach failed."); return; } } ProcessMonitor::~ProcessMonitor() { StopMonitor(); } //------------------------------------------------------------------------------ // Thread setup and tear down. void ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Error &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) return; m_operation_thread = Host::ThreadCreate(g_thread_name, LaunchOpThread, args, &error); } void * ProcessMonitor::LaunchOpThread(void *arg) { LaunchArgs *args = static_cast(arg); if (!Launch(args)) { sem_post(&args->m_semaphore); return NULL; } ServeOperation(args); return NULL; } bool ProcessMonitor::Launch(LaunchArgs *args) { ProcessMonitor *monitor = args->m_monitor; ProcessFreeBSD &process = monitor->GetProcess(); const char **argv = args->m_argv; const char **envp = args->m_envp; const char *stdin_path = args->m_stdin_path; const char *stdout_path = args->m_stdout_path; const char *stderr_path = args->m_stderr_path; const char *working_dir = args->m_working_dir; lldb_utility::PseudoTerminal terminal; const size_t err_len = 1024; char err_str[err_len]; lldb::pid_t pid; // Propagate the environment if one is not supplied. if (envp == NULL || envp[0] == NULL) envp = const_cast(environ); if ((pid = terminal.Fork(err_str, err_len)) == -1) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Process fork failed."); goto FINISH; } // Recognized child exit status codes. enum { ePtraceFailed = 1, eDupStdinFailed, eDupStdoutFailed, eDupStderrFailed, eChdirFailed, eExecFailed }; // Child process. if (pid == 0) { // Trace this process. if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0) exit(ePtraceFailed); // Do not inherit setgid powers. setgid(getgid()); // Let us have our own process group. setpgid(0, 0); // Dup file descriptors if needed. // // FIXME: If two or more of the paths are the same we needlessly open // the same file multiple times. if (stdin_path != NULL && stdin_path[0]) if (!DupDescriptor(stdin_path, STDIN_FILENO, O_RDONLY)) exit(eDupStdinFailed); if (stdout_path != NULL && stdout_path[0]) if (!DupDescriptor(stdout_path, STDOUT_FILENO, O_WRONLY | O_CREAT)) exit(eDupStdoutFailed); if (stderr_path != NULL && stderr_path[0]) if (!DupDescriptor(stderr_path, STDERR_FILENO, O_WRONLY | O_CREAT)) exit(eDupStderrFailed); // Change working directory if (working_dir != NULL && working_dir[0]) if (0 != ::chdir(working_dir)) exit(eChdirFailed); // Execute. We should never return. execve(argv[0], const_cast(argv), const_cast(envp)); exit(eExecFailed); } // Wait for the child process to to trap on its call to execve. ::pid_t wpid; int status; if ((wpid = waitpid(pid, &status, 0)) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } else if (WIFEXITED(status)) { // open, dup or execve likely failed for some reason. args->m_error.SetErrorToGenericError(); switch (WEXITSTATUS(status)) { case ePtraceFailed: args->m_error.SetErrorString("Child ptrace failed."); break; case eDupStdinFailed: args->m_error.SetErrorString("Child open stdin failed."); break; case eDupStdoutFailed: args->m_error.SetErrorString("Child open stdout failed."); break; case eDupStderrFailed: args->m_error.SetErrorString("Child open stderr failed."); break; case eChdirFailed: args->m_error.SetErrorString("Child failed to set working directory."); break; case eExecFailed: args->m_error.SetErrorString("Child exec failed."); break; default: args->m_error.SetErrorString("Child returned unknown exit status."); break; } goto FINISH; } assert(WIFSTOPPED(status) && wpid == (::pid_t)pid && "Could not sync with inferior process."); #ifdef notyet // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } #endif // Release the master terminal descriptor and pass it off to the // ProcessMonitor instance. Similarly stash the inferior pid. monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); monitor->m_pid = pid; // Set the terminal fd to be in non blocking mode (it simplifies the // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking // descriptor to read from). if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error)) goto FINISH; process.SendMessage(ProcessMessage::Attach(pid)); FINISH: return args->m_error.Success(); } void ProcessMonitor::StartAttachOpThread(AttachArgs *args, lldb_private::Error &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) return; m_operation_thread = Host::ThreadCreate(g_thread_name, AttachOpThread, args, &error); } void * ProcessMonitor::AttachOpThread(void *arg) { AttachArgs *args = static_cast(arg); if (!Attach(args)) return NULL; ServeOperation(args); return NULL; } bool ProcessMonitor::Attach(AttachArgs *args) { lldb::pid_t pid = args->m_pid; ProcessMonitor *monitor = args->m_monitor; ProcessFreeBSD &process = monitor->GetProcess(); if (pid <= 1) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Attaching to process 1 is not allowed."); goto FINISH; } // Attach to the requested process. if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } int status; if ((status = waitpid(pid, NULL, 0)) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } process.SendMessage(ProcessMessage::Attach(pid)); FINISH: return args->m_error.Success(); } +size_t +ProcessMonitor::GetCurrentThreadIDs(std::vector&thread_ids) +{ + lwpid_t *tids; + int tdcnt; + + thread_ids.clear(); + + tdcnt = PTRACE(PT_GETNUMLWPS, m_pid, NULL, 0); + if (tdcnt <= 0) + return 0; + tids = (lwpid_t *)malloc(tdcnt * sizeof(*tids)); + if (tids == NULL) + return 0; + if (PTRACE(PT_GETLWPLIST, m_pid, (void *)tids, tdcnt) < 0) { + free(tids); + return 0; + } + thread_ids = std::vector(tids, tids + tdcnt); + free(tids); + return thread_ids.size(); +} + bool ProcessMonitor::MonitorCallback(void *callback_baton, lldb::pid_t pid, bool exited, int signal, int status) { ProcessMessage message; ProcessMonitor *monitor = static_cast(callback_baton); ProcessFreeBSD *process = monitor->m_process; assert(process); bool stop_monitoring; struct ptrace_lwpinfo plwp; int ptrace_err; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (exited) { if (log) log->Printf ("ProcessMonitor::%s() got exit signal, tid = %" PRIu64, __FUNCTION__, pid); message = ProcessMessage::Exit(pid, status); process->SendMessage(message); return pid == process->GetID(); } if (!monitor->GetLwpInfo(pid, &plwp, ptrace_err)) stop_monitoring = true; // pid is gone. Bail. else { switch (plwp.pl_siginfo.si_signo) { case SIGTRAP: - message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, pid); + message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, plwp.pl_lwpid); break; default: - message = MonitorSignal(monitor, &plwp.pl_siginfo, pid); + message = MonitorSignal(monitor, &plwp.pl_siginfo, plwp.pl_lwpid); break; } process->SendMessage(message); stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage; } return stop_monitoring; } ProcessMessage ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, - const siginfo_t *info, lldb::pid_t pid) + const siginfo_t *info, lldb::tid_t tid) { ProcessMessage message; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); assert(monitor); assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!"); switch (info->si_code) { default: assert(false && "Unexpected SIGTRAP code!"); break; case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */): { // The inferior process is about to exit. Maintain the process in a // state of "limbo" until we are explicitly commanded to detach, // destroy, resume, etc. unsigned long data = 0; - if (!monitor->GetEventMessage(pid, &data)) + if (!monitor->GetEventMessage(tid, &data)) data = -1; if (log) - log->Printf ("ProcessMonitor::%s() received exit? event, data = %lx, pid = %" PRIu64, __FUNCTION__, data, pid); - message = ProcessMessage::Limbo(pid, (data >> 8)); + log->Printf ("ProcessMonitor::%s() received exit? event, data = %lx, tid = %" PRIu64, __FUNCTION__, data, tid); + message = ProcessMessage::Limbo(tid, (data >> 8)); break; } case 0: case TRAP_TRACE: if (log) - log->Printf ("ProcessMonitor::%s() received trace event, pid = %" PRIu64, __FUNCTION__, pid); - message = ProcessMessage::Trace(pid); + log->Printf ("ProcessMonitor::%s() received trace event, tid = %" PRIu64, __FUNCTION__, tid); + message = ProcessMessage::Trace(tid); break; case SI_KERNEL: case TRAP_BRKPT: if (log) - log->Printf ("ProcessMonitor::%s() received breakpoint event, pid = %" PRIu64, __FUNCTION__, pid); - message = ProcessMessage::Break(pid); + log->Printf ("ProcessMonitor::%s() received breakpoint event, tid = %" PRIu64, __FUNCTION__, tid); + message = ProcessMessage::Break(tid); break; } return message; } ProcessMessage ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, - const siginfo_t *info, lldb::pid_t pid) + const siginfo_t *info, lldb::tid_t tid) { ProcessMessage message; int signo = info->si_signo; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); // POSIX says that process behaviour is undefined after it ignores a SIGFPE, // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a // kill(2) or raise(3). Similarly for tgkill(2) on FreeBSD. // // IOW, user generated signals never generate what we consider to be a // "crash". // // Similarly, ACK signals generated by this monitor. if (info->si_code == SI_USER) { if (log) log->Printf ("ProcessMonitor::%s() received signal %s with code %s, pid = %d", __FUNCTION__, monitor->m_process->GetUnixSignals().GetSignalAsCString (signo), "SI_USER", info->si_pid); if (info->si_pid == getpid()) - return ProcessMessage::SignalDelivered(pid, signo); + return ProcessMessage::SignalDelivered(tid, signo); else - return ProcessMessage::Signal(pid, signo); + return ProcessMessage::Signal(tid, signo); } if (log) log->Printf ("ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals().GetSignalAsCString (signo)); if (signo == SIGSEGV) { lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info); - return ProcessMessage::Crash(pid, reason, signo, fault_addr); + return ProcessMessage::Crash(tid, reason, signo, fault_addr); } if (signo == SIGILL) { lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info); - return ProcessMessage::Crash(pid, reason, signo, fault_addr); + return ProcessMessage::Crash(tid, reason, signo, fault_addr); } if (signo == SIGFPE) { lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info); - return ProcessMessage::Crash(pid, reason, signo, fault_addr); + return ProcessMessage::Crash(tid, reason, signo, fault_addr); } if (signo == SIGBUS) { lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info); - return ProcessMessage::Crash(pid, reason, signo, fault_addr); + return ProcessMessage::Crash(tid, reason, signo, fault_addr); } // Everything else is "normal" and does not require any special action on // our part. - return ProcessMessage::Signal(pid, signo); + return ProcessMessage::Signal(tid, signo); } ProcessMessage::CrashReason ProcessMonitor::GetCrashReasonForSIGSEGV(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGSEGV); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGSEGV"); break; case SEGV_MAPERR: reason = ProcessMessage::eInvalidAddress; break; case SEGV_ACCERR: reason = ProcessMessage::ePrivilegedAddress; break; } return reason; } ProcessMessage::CrashReason ProcessMonitor::GetCrashReasonForSIGILL(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGILL); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGILL"); break; case ILL_ILLOPC: reason = ProcessMessage::eIllegalOpcode; break; case ILL_ILLOPN: reason = ProcessMessage::eIllegalOperand; break; case ILL_ILLADR: reason = ProcessMessage::eIllegalAddressingMode; break; case ILL_ILLTRP: reason = ProcessMessage::eIllegalTrap; break; case ILL_PRVOPC: reason = ProcessMessage::ePrivilegedOpcode; break; case ILL_PRVREG: reason = ProcessMessage::ePrivilegedRegister; break; case ILL_COPROC: reason = ProcessMessage::eCoprocessorError; break; case ILL_BADSTK: reason = ProcessMessage::eInternalStackError; break; } return reason; } ProcessMessage::CrashReason ProcessMonitor::GetCrashReasonForSIGFPE(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGFPE); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGFPE"); break; case FPE_INTDIV: reason = ProcessMessage::eIntegerDivideByZero; break; case FPE_INTOVF: reason = ProcessMessage::eIntegerOverflow; break; case FPE_FLTDIV: reason = ProcessMessage::eFloatDivideByZero; break; case FPE_FLTOVF: reason = ProcessMessage::eFloatOverflow; break; case FPE_FLTUND: reason = ProcessMessage::eFloatUnderflow; break; case FPE_FLTRES: reason = ProcessMessage::eFloatInexactResult; break; case FPE_FLTINV: reason = ProcessMessage::eFloatInvalidOperation; break; case FPE_FLTSUB: reason = ProcessMessage::eFloatSubscriptRange; break; } return reason; } ProcessMessage::CrashReason ProcessMonitor::GetCrashReasonForSIGBUS(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGBUS); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGBUS"); break; case BUS_ADRALN: reason = ProcessMessage::eIllegalAlignment; break; case BUS_ADRERR: reason = ProcessMessage::eIllegalAddress; break; case BUS_OBJERR: reason = ProcessMessage::eHardwareError; break; } return reason; } void ProcessMonitor::ServeOperation(OperationArgs *args) { ProcessMonitor *monitor = args->m_monitor; // We are finised with the arguments and are ready to go. Sync with the // parent thread and start serving operations on the inferior. sem_post(&args->m_semaphore); for (;;) { // wait for next pending operation sem_wait(&monitor->m_operation_pending); monitor->m_operation->Execute(monitor); // notify calling thread that operation is complete sem_post(&monitor->m_operation_done); } } void ProcessMonitor::DoOperation(Operation *op) { Mutex::Locker lock(m_operation_mutex); m_operation = op; // notify operation thread that an operation is ready to be processed sem_post(&m_operation_pending); // wait for operation to complete sem_wait(&m_operation_done); } size_t ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Error &error) { size_t result; ReadOperation op(vm_addr, buf, size, error, result); DoOperation(&op); return result; } size_t ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error) { size_t result; WriteOperation op(vm_addr, buf, size, error, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char* reg_name, unsigned size, RegisterValue &value) { bool result; ReadRegOperation op(tid, offset, size, value, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char* reg_name, const RegisterValue &value) { bool result; WriteRegOperation op(tid, offset, value, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; ReadGPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; ReadFPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { return false; } bool ProcessMonitor::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; WriteGPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; WriteFPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { return false; } bool ProcessMonitor::ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value) { return false; } bool ProcessMonitor::Resume(lldb::tid_t unused, uint32_t signo) { bool result; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (log) log->Printf ("ProcessMonitor::%s() resuming pid %" PRIu64 " with signal %s", __FUNCTION__, GetPID(), m_process->GetUnixSignals().GetSignalAsCString (signo)); ResumeOperation op(signo, result); DoOperation(&op); if (log) log->Printf ("ProcessMonitor::%s() resuming result = %s", __FUNCTION__, result ? "true" : "false"); return result; } bool ProcessMonitor::SingleStep(lldb::tid_t unused, uint32_t signo) { bool result; SingleStepOperation op(signo, result); DoOperation(&op); return result; } bool ProcessMonitor::BringProcessIntoLimbo() { bool result; KillOperation op(result); DoOperation(&op); return result; } bool ProcessMonitor::GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &ptrace_err) { bool result; LwpInfoOperation op(tid, lwpinfo, result, ptrace_err); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::ThreadSuspend(lldb::tid_t tid, bool suspend) +{ + bool result; + ThreadSuspendOperation op(tid, suspend, result); DoOperation(&op); return result; } bool ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) { bool result; EventMessageOperation op(tid, message, result); DoOperation(&op); return result; } lldb_private::Error ProcessMonitor::Detach(lldb::tid_t tid) { lldb_private::Error error; if (tid != LLDB_INVALID_THREAD_ID) { DetachOperation op(error); DoOperation(&op); } return error; } bool ProcessMonitor::DupDescriptor(const char *path, int fd, int flags) { int target_fd = open(path, flags, 0666); if (target_fd == -1) return false; return (dup2(target_fd, fd) == -1) ? false : true; } void ProcessMonitor::StopMonitoringChildProcess() { lldb::thread_result_t thread_result; if (IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) { Host::ThreadCancel(m_monitor_thread, NULL); Host::ThreadJoin(m_monitor_thread, &thread_result, NULL); m_monitor_thread = LLDB_INVALID_HOST_THREAD; } } void ProcessMonitor::StopMonitor() { StopMonitoringChildProcess(); StopOpThread(); sem_destroy(&m_operation_pending); sem_destroy(&m_operation_done); // Note: ProcessPOSIX passes the m_terminal_fd file descriptor to // Process::SetSTDIOFileDescriptor, which in turn transfers ownership of // the descriptor to a ConnectionFileDescriptor object. Consequently // even though still has the file descriptor, we shouldn't close it here. } // FIXME: On Linux, when a new thread is created, we receive to notifications, // (1) a SIGTRAP|PTRACE_EVENT_CLONE from the main process thread with the // child thread id as additional information, and (2) a SIGSTOP|SI_USER from // the new child thread indicating that it has is stopped because we attached. // We have no guarantee of the order in which these arrive, but we need both // before we are ready to proceed. We currently keep a list of threads which // have sent the initial SIGSTOP|SI_USER event. Then when we receive the // SIGTRAP|PTRACE_EVENT_CLONE notification, if the initial stop has not occurred // we call ProcessMonitor::WaitForInitialTIDStop() to wait for it. // // Right now, the above logic is in ProcessPOSIX, so we need a definition of // this function in the FreeBSD ProcessMonitor implementation even if it isn't // logically needed. // // We really should figure out what actually happens on FreeBSD and move the // Linux-specific logic out of ProcessPOSIX as needed. bool ProcessMonitor::WaitForInitialTIDStop(lldb::tid_t tid) { return true; } void ProcessMonitor::StopOpThread() { lldb::thread_result_t result; if (!IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) return; Host::ThreadCancel(m_operation_thread, NULL); Host::ThreadJoin(m_operation_thread, &result, NULL); m_operation_thread = LLDB_INVALID_HOST_THREAD; } Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h (revision 263367) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h (revision 263368) @@ -1,329 +1,337 @@ //===-- ProcessMonitor.h -------------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ProcessMonitor_H_ #define liblldb_ProcessMonitor_H_ // C Includes #include #include // C++ Includes // Other libraries and framework includes #include "lldb/lldb-types.h" #include "lldb/Host/Mutex.h" namespace lldb_private { class Error; class Module; class Scalar; } // End lldb_private namespace. class ProcessFreeBSD; class Operation; /// @class ProcessMonitor /// @brief Manages communication with the inferior (debugee) process. /// /// Upon construction, this class prepares and launches an inferior process for /// debugging. /// /// Changes in the inferior process state are propagated to the associated /// ProcessFreeBSD instance by calling ProcessFreeBSD::SendMessage with the /// appropriate ProcessMessage events. /// /// A purposely minimal set of operations are provided to interrogate and change /// the inferior process state. class ProcessMonitor { public: /// Launches an inferior process ready for debugging. Forms the /// implementation of Process::DoLaunch. ProcessMonitor(ProcessPOSIX *process, lldb_private::Module *module, char const *argv[], char const *envp[], const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_dir, lldb_private::Error &error); ProcessMonitor(ProcessPOSIX *process, lldb::pid_t pid, lldb_private::Error &error); ~ProcessMonitor(); /// Provides the process number of debugee. lldb::pid_t GetPID() const { return m_pid; } /// Returns the process associated with this ProcessMonitor. ProcessFreeBSD & GetProcess() { return *m_process; } /// Returns a file descriptor to the controlling terminal of the inferior /// process. /// /// Reads from this file descriptor yield both the standard output and /// standard error of this debugee. Even if stderr and stdout were /// redirected on launch it may still happen that data is available on this /// descriptor (if the inferior process opens /dev/tty, for example). /// /// If this monitor was attached to an existing process this method returns /// -1. int GetTerminalFD() const { return m_terminal_fd; } /// Reads @p size bytes from address @vm_adder in the inferior process /// address space. /// /// This method is provided to implement Process::DoReadMemory. size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, lldb_private::Error &error); /// Writes @p size bytes from address @p vm_adder in the inferior process /// address space. /// /// This method is provided to implement Process::DoWriteMemory. size_t WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error); /// Reads the contents from the register identified by the given (architecture /// dependent) offset. /// /// This method is provided for use by RegisterContextFreeBSD derivatives. /// FIXME: The FreeBSD implementation of this function should use tid in order /// to enable support for debugging threaded programs. bool ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size, lldb_private::RegisterValue &value); /// Writes the given value to the register identified by the given /// (architecture dependent) offset. /// /// This method is provided for use by RegisterContextFreeBSD derivatives. /// FIXME: The FreeBSD implementation of this function should use tid in order /// to enable support for debugging threaded programs. bool WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, const lldb_private::RegisterValue &value); /// Reads all general purpose registers into the specified buffer. /// FIXME: The FreeBSD implementation of this function should use tid in order /// to enable support for debugging threaded programs. bool ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size); /// Reads all floating point registers into the specified buffer. /// FIXME: The FreeBSD implementation of this function should use tid in order /// to enable support for debugging threaded programs. bool ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size); /// Reads the specified register set into the specified buffer. /// /// This method is provided for use by RegisterContextFreeBSD derivatives. /// FIXME: The FreeBSD implementation of this function should use tid in order /// to enable support for debugging threaded programs. bool ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset); /// Writes all general purpose registers into the specified buffer. /// FIXME: The FreeBSD implementation of this function should use tid in order /// to enable support for debugging threaded programs. bool WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size); /// Writes all floating point registers into the specified buffer. /// FIXME: The FreeBSD implementation of this function should use tid in order /// to enable support for debugging threaded programs. bool WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size); /// Writes the specified register set into the specified buffer. /// /// This method is provided for use by RegisterContextFreeBSD derivatives. /// FIXME: The FreeBSD implementation of this function should use tid in order /// to enable support for debugging threaded programs. bool WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset); /// Reads the value of the thread-specific pointer for a given thread ID. bool ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value); + /// Returns current thread IDs in process + size_t + GetCurrentThreadIDs(std::vector &thread_ids); + /// Writes a ptrace_lwpinfo structure corresponding to the given thread ID /// to the memory region pointed to by @p lwpinfo. bool GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &error_no); + + /// Suspends or unsuspends a thread prior to process resume or step. + bool + ThreadSuspend(lldb::tid_t tid, bool suspend); /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) /// corresponding to the given thread IDto the memory pointed to by @p /// message. bool GetEventMessage(lldb::tid_t tid, unsigned long *message); /// Resumes the process. If @p signo is anything but /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the process. bool Resume(lldb::tid_t unused, uint32_t signo); /// Single steps the process. If @p signo is anything but /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the process. bool SingleStep(lldb::tid_t unused, uint32_t signo); /// Sends the inferior process a PTRACE_KILL signal. The inferior will /// still exists and can be interrogated. Once resumed it will exit as /// though it received a SIGKILL. bool BringProcessIntoLimbo(); lldb_private::Error Detach(lldb::tid_t tid); void StopMonitor(); // Waits for the initial stop message from a new thread. bool WaitForInitialTIDStop(lldb::tid_t tid); private: ProcessFreeBSD *m_process; lldb::thread_t m_operation_thread; lldb::thread_t m_monitor_thread; lldb::pid_t m_pid; int m_terminal_fd; // current operation which must be executed on the privileged thread Operation *m_operation; lldb_private::Mutex m_operation_mutex; // semaphores notified when Operation is ready to be processed and when // the operation is complete. sem_t m_operation_pending; sem_t m_operation_done; struct OperationArgs { OperationArgs(ProcessMonitor *monitor); ~OperationArgs(); ProcessMonitor *m_monitor; // The monitor performing the attach. sem_t m_semaphore; // Posted to once operation complete. lldb_private::Error m_error; // Set if process operation failed. }; /// @class LauchArgs /// /// @brief Simple structure to pass data to the thread responsible for /// launching a child process. struct LaunchArgs : OperationArgs { LaunchArgs(ProcessMonitor *monitor, lldb_private::Module *module, char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_dir); ~LaunchArgs(); lldb_private::Module *m_module; // The executable image to launch. char const **m_argv; // Process arguments. char const **m_envp; // Process environment. const char *m_stdin_path; // Redirect stdin or NULL. const char *m_stdout_path; // Redirect stdout or NULL. const char *m_stderr_path; // Redirect stderr or NULL. const char *m_working_dir; // Working directory or NULL. }; void StartLaunchOpThread(LaunchArgs *args, lldb_private::Error &error); static void * LaunchOpThread(void *arg); static bool Launch(LaunchArgs *args); struct AttachArgs : OperationArgs { AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid); ~AttachArgs(); lldb::pid_t m_pid; // pid of the process to be attached. }; void StartAttachOpThread(AttachArgs *args, lldb_private::Error &error); static void * AttachOpThread(void *args); static bool Attach(AttachArgs *args); static void ServeOperation(OperationArgs *args); static bool DupDescriptor(const char *path, int fd, int flags); static bool MonitorCallback(void *callback_baton, lldb::pid_t pid, bool exited, int signal, int status); static ProcessMessage MonitorSIGTRAP(ProcessMonitor *monitor, const siginfo_t *info, lldb::pid_t pid); static ProcessMessage MonitorSignal(ProcessMonitor *monitor, const siginfo_t *info, lldb::pid_t pid); static ProcessMessage::CrashReason GetCrashReasonForSIGSEGV(const siginfo_t *info); static ProcessMessage::CrashReason GetCrashReasonForSIGILL(const siginfo_t *info); static ProcessMessage::CrashReason GetCrashReasonForSIGFPE(const siginfo_t *info); static ProcessMessage::CrashReason GetCrashReasonForSIGBUS(const siginfo_t *info); void DoOperation(Operation *op); /// Stops the child monitor thread. void StopMonitoringChildProcess(); /// Stops the operation thread used to attach/launch a process. void StopOpThread(); }; #endif // #ifndef liblldb_ProcessMonitor_H_ Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp (revision 263367) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp (revision 263368) @@ -1,624 +1,589 @@ //===-- POSIXThread.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" // C Includes #include // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadSpec.h" #include "POSIXStopInfo.h" #include "POSIXThread.h" #include "ProcessPOSIX.h" #include "ProcessPOSIXLog.h" #include "ProcessMonitor.h" #include "RegisterContextPOSIXProcessMonitor_mips64.h" #include "RegisterContextPOSIXProcessMonitor_x86.h" #include "RegisterContextLinux_i386.h" #include "RegisterContextLinux_x86_64.h" #include "RegisterContextFreeBSD_i386.h" #include "RegisterContextFreeBSD_mips64.h" #include "RegisterContextFreeBSD_x86_64.h" #include "UnwindLLDB.h" using namespace lldb; using namespace lldb_private; POSIXThread::POSIXThread(Process &process, lldb::tid_t tid) : Thread(process, tid), m_frame_ap (), m_breakpoint (), m_thread_name_valid (false), m_thread_name (), m_posix_thread(NULL) { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("POSIXThread::%s (tid = %" PRIi64 ")", __FUNCTION__, 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()) { assert(EnableHardwareWatchpoint(wp.get())); } } } POSIXThread::~POSIXThread() { DestroyThread(); } ProcessMonitor & POSIXThread::GetMonitor() { ProcessSP base = GetProcess(); ProcessPOSIX &process = static_cast(*base); return process.GetMonitor(); } +// Overridden by FreeBSDThread; this is used only on Linux. void POSIXThread::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); } // FIXME: This should probably happen somewhere else. SetResumeState(eStateRunning); Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); if (log) log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to running", __FUNCTION__, GetID()); } const char * POSIXThread::GetInfo() { return NULL; } void POSIXThread::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 * POSIXThread::GetName () { if (!m_thread_name_valid) { SetName(Host::GetThreadName(GetProcess()->GetID(), GetID()).c_str()); m_thread_name_valid = true; } if (m_thread_name.empty()) return NULL; return m_thread_name.c_str(); } lldb::RegisterContextSP POSIXThread::GetRegisterContext() { if (!m_reg_context_sp) { m_posix_thread = NULL; RegisterInfoInterface *reg_interface = NULL; const ArchSpec &target_arch = GetProcess()->GetTarget().GetArchitecture(); switch (target_arch.GetCore()) { case ArchSpec::eCore_mips64: { RegisterInfoInterface *reg_interface = NULL; switch (target_arch.GetTriple().getOS()) { case llvm::Triple::FreeBSD: reg_interface = new RegisterContextFreeBSD_mips64(target_arch); break; default: assert(false && "OS not supported"); break; } if (reg_interface) { 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 ArchSpec::eCore_x86_32_i386: case ArchSpec::eCore_x86_32_i486: case ArchSpec::eCore_x86_32_i486sx: case ArchSpec::eCore_x86_64_x86_64: { switch (target_arch.GetTriple().getOS()) { case llvm::Triple::FreeBSD: reg_interface = new RegisterContextFreeBSD_x86_64(target_arch); break; case llvm::Triple::Linux: reg_interface = new RegisterContextLinux_x86_64(target_arch); break; default: assert(false && "OS not supported"); break; } if (reg_interface) { 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: assert(false && "CPU type not supported!"); break; } } return m_reg_context_sp; } lldb::RegisterContextSP POSIXThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) { lldb::RegisterContextSP reg_ctx_sp; uint32_t concrete_frame_idx = 0; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("POSIXThread::%s ()", __FUNCTION__); 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 POSIXThread::GetThreadPointer () { ProcessMonitor &monitor = GetMonitor(); addr_t addr; if (monitor.ReadThreadPointer (GetID(), addr)) return addr; else return LLDB_INVALID_ADDRESS; } bool POSIXThread::CalculateStopInfo() { SetStopInfo (m_stop_info_sp); return true; } Unwind * POSIXThread::GetUnwinder() { if (m_unwinder_ap.get() == NULL) m_unwinder_ap.reset(new UnwindLLDB(*this)); return m_unwinder_ap.get(); } +// Overridden by FreeBSDThread; this is used only on Linux. void POSIXThread::WillResume(lldb::StateType resume_state) { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); if (log) log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to %s", __FUNCTION__, GetID(), StateAsCString(resume_state)); // TODO: the line below shouldn't really be done, but // the POSIXThread might rely on this so I will leave this in for now SetResumeState(resume_state); } void POSIXThread::DidStop() { // Don't set the thread state to stopped unless we really stopped. -} - -bool -POSIXThread::Resume() -{ - lldb::StateType resume_state = GetResumeState(); - ProcessMonitor &monitor = GetMonitor(); - bool status; - - Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); - if (log) - log->Printf ("POSIXThread::%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 POSIXThread::Notify(const ProcessMessage &message) { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); if (log) log->Printf ("POSIXThread::%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::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::eCrashMessage: CrashNotify(message); break; case ProcessMessage::eNewThreadMessage: ThreadNotify(message); break; case ProcessMessage::eExecMessage: ExecNotify(message); break; } } bool POSIXThread::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 POSIXThread::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 POSIXThread::NumSupportedHardwareWatchpoints() { lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext(); if (reg_ctx_sp.get()) return reg_ctx_sp->NumSupportedHardwareWatchpoints(); return 0; } uint32_t POSIXThread::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 POSIXThread::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(); if (log) log->Printf ("POSIXThread::%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 (bp_site->ValidForThisThread(this)) SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id)); else { const bool should_stop = false; SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id, should_stop)); } } else SetStopInfo(StopInfoSP()); } void POSIXThread::WatchNotify(const ProcessMessage &message) { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); lldb::addr_t halt_addr = message.GetHWAddress(); if (log) log->Printf ("POSIXThread::%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 POSIXThread::TraceNotify(const ProcessMessage &message) { SetStopInfo (StopInfo::CreateStopReasonToTrace(*this)); } void POSIXThread::LimboNotify(const ProcessMessage &message) { SetStopInfo (lldb::StopInfoSP(new POSIXLimboStopInfo(*this))); } void POSIXThread::SignalNotify(const ProcessMessage &message) { int signo = message.GetSignal(); SetStopInfo (StopInfo::CreateStopReasonWithSignal(*this, signo)); SetResumeSignal(signo); } void POSIXThread::SignalDeliveredNotify(const ProcessMessage &message) { int signo = message.GetSignal(); SetStopInfo (StopInfo::CreateStopReasonWithSignal(*this, signo)); SetResumeSignal(signo); } void POSIXThread::CrashNotify(const ProcessMessage &message) { // FIXME: Update stop reason as per bugzilla 14598 int signo = message.GetSignal(); assert(message.GetKind() == ProcessMessage::eCrashMessage); Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); if (log) log->Printf ("POSIXThread::%s () signo = %i, reason = '%s'", __FUNCTION__, signo, message.PrintCrashReason()); SetStopInfo (lldb::StopInfoSP(new POSIXCrashStopInfo(*this, signo, message.GetCrashReason(), message.GetFaultAddress()))); SetResumeSignal(signo); } void POSIXThread::ThreadNotify(const ProcessMessage &message) { SetStopInfo (lldb::StopInfoSP(new POSIXNewThreadStopInfo(*this))); } unsigned POSIXThread::GetRegisterIndexFromOffset(unsigned offset) { unsigned reg = LLDB_INVALID_REGNUM; ArchSpec arch = Host::GetArchitecture(); switch (arch.GetCore()) { default: llvm_unreachable("CPU type not supported!"); break; case ArchSpec::eCore_mips64: case ArchSpec::eCore_x86_32_i386: case ArchSpec::eCore_x86_32_i486: case ArchSpec::eCore_x86_32_i486sx: case ArchSpec::eCore_x86_64_x86_64: { POSIXBreakpointProtocol* reg_ctx = GetPOSIXBreakpointProtocol(); reg = reg_ctx->GetRegisterIndexFromOffset(offset); } break; } return reg; } void POSIXThread::ExecNotify(const ProcessMessage &message) { SetStopInfo (StopInfo::CreateStopReasonWithExec(*this)); } const char * POSIXThread::GetRegisterName(unsigned reg) { const char * name = nullptr; ArchSpec arch = Host::GetArchitecture(); switch (arch.GetCore()) { default: assert(false && "CPU type not supported!"); break; case ArchSpec::eCore_mips64: case ArchSpec::eCore_x86_32_i386: case ArchSpec::eCore_x86_32_i486: case ArchSpec::eCore_x86_32_i486sx: case ArchSpec::eCore_x86_64_x86_64: name = GetRegisterContext()->GetRegisterName(reg); break; } return name; } const char * POSIXThread::GetRegisterNameFromOffset(unsigned offset) { return GetRegisterName(GetRegisterIndexFromOffset(offset)); } Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/POSIXThread.h =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/POSIXThread.h (revision 263367) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/POSIXThread.h (revision 263368) @@ -1,135 +1,133 @@ //===-- POSIXThread.h -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_POSIXThread_H_ #define liblldb_POSIXThread_H_ // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "lldb/Target/Thread.h" #include "RegisterContextPOSIX.h" class ProcessMessage; class ProcessMonitor; class POSIXBreakpointProtocol; //------------------------------------------------------------------------------ // @class POSIXThread // @brief Abstraction of a POSIX thread. class POSIXThread : public lldb_private::Thread { public: POSIXThread(lldb_private::Process &process, lldb::tid_t tid); virtual ~POSIXThread(); void RefreshStateAfterStop(); virtual void WillResume(lldb::StateType resume_state); // This notifies the thread when a private stop occurs. virtual void DidStop (); const char * GetInfo(); void SetName (const char *name); const char * GetName (); virtual lldb::RegisterContextSP GetRegisterContext(); virtual lldb::RegisterContextSP CreateRegisterContextForFrame (lldb_private::StackFrame *frame); virtual lldb::addr_t GetThreadPointer (); //-------------------------------------------------------------------------- // These functions provide a mapping from the register offset // back to the register index or name for use in debugging or log // output. unsigned GetRegisterIndexFromOffset(unsigned offset); const char * GetRegisterName(unsigned reg); const char * GetRegisterNameFromOffset(unsigned offset); //-------------------------------------------------------------------------- // These methods form a specialized interface to POSIX threads. // - bool Resume(); - void Notify(const ProcessMessage &message); //-------------------------------------------------------------------------- // These methods provide an interface to watchpoints // bool EnableHardwareWatchpoint(lldb_private::Watchpoint *wp); bool DisableHardwareWatchpoint(lldb_private::Watchpoint *wp); uint32_t NumSupportedHardwareWatchpoints(); uint32_t FindVacantWatchpointIndex(); protected: POSIXBreakpointProtocol * GetPOSIXBreakpointProtocol () { if (!m_reg_context_sp) m_reg_context_sp = GetRegisterContext(); return m_posix_thread; } std::unique_ptr m_frame_ap; lldb::BreakpointSiteSP m_breakpoint; bool m_thread_name_valid; std::string m_thread_name; POSIXBreakpointProtocol *m_posix_thread; ProcessMonitor & GetMonitor(); virtual bool CalculateStopInfo(); void BreakNotify(const ProcessMessage &message); void WatchNotify(const ProcessMessage &message); virtual void TraceNotify(const ProcessMessage &message); void LimboNotify(const ProcessMessage &message); void SignalNotify(const ProcessMessage &message); void SignalDeliveredNotify(const ProcessMessage &message); void CrashNotify(const ProcessMessage &message); void ThreadNotify(const ProcessMessage &message); void ExitNotify(const ProcessMessage &message); void ExecNotify(const ProcessMessage &message); lldb_private::Unwind * GetUnwinder(); }; #endif // #ifndef liblldb_POSIXThread_H_ Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp (revision 263367) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp (revision 263368) @@ -1,942 +1,816 @@ //===-- ProcessPOSIX.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" // C Includes #include // C++ Includes // Other libraries and framework includes #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Target.h" #include "ProcessPOSIX.h" #include "ProcessPOSIXLog.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "ProcessMonitor.h" #include "POSIXThread.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------------ // Static functions. #if 0 Process* ProcessPOSIX::CreateInstance(Target& target, Listener &listener) { return new ProcessPOSIX(target, listener); } void ProcessPOSIX::Initialize() { static bool g_initialized = false; if (!g_initialized) { g_initialized = true; PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); Log::Callbacks log_callbacks = { ProcessPOSIXLog::DisableLog, ProcessPOSIXLog::EnableLog, ProcessPOSIXLog::ListLogCategories }; Log::RegisterLogChannel (ProcessPOSIX::GetPluginNameStatic(), log_callbacks); } } #endif //------------------------------------------------------------------------------ // Constructors and destructors. ProcessPOSIX::ProcessPOSIX(Target& target, Listener &listener) : Process(target, listener), m_byte_order(lldb::endian::InlHostByteOrder()), m_monitor(NULL), m_module(NULL), m_message_mutex (Mutex::eMutexTypeRecursive), m_exit_now(false), m_seen_initial_stop() { // FIXME: Putting this code in the ctor and saving the byte order in a // member variable is a hack to avoid const qual issues in GetByteOrder. lldb::ModuleSP module = GetTarget().GetExecutableModule(); if (module && module->GetObjectFile()) m_byte_order = module->GetObjectFile()->GetByteOrder(); } ProcessPOSIX::~ProcessPOSIX() { delete m_monitor; } //------------------------------------------------------------------------------ // Process protocol. void ProcessPOSIX::Finalize() { Process::Finalize(); if (m_monitor) m_monitor->StopMonitor(); } bool ProcessPOSIX::CanDebug(Target &target, bool plugin_specified_by_name) { // For now we are just making sure the file exists for a given module ModuleSP exe_module_sp(target.GetExecutableModule()); if (exe_module_sp.get()) return exe_module_sp->GetFileSpec().Exists(); // If there is no executable module, we return true since we might be preparing to attach. return true; } Error ProcessPOSIX::DoAttachToProcessWithID(lldb::pid_t pid) { Error error; assert(m_monitor == NULL); Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("ProcessPOSIX::%s(pid = %" PRIi64 ")", __FUNCTION__, GetID()); m_monitor = new ProcessMonitor(this, pid, error); if (!error.Success()) return error; PlatformSP platform_sp (m_target.GetPlatform ()); assert (platform_sp.get()); if (!platform_sp) return error; // FIXME: Detatch? // Find out what we can about this process ProcessInstanceInfo process_info; platform_sp->GetProcessInfo (pid, process_info); // Resolve the executable module ModuleSP exe_module_sp; FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); error = platform_sp->ResolveExecutable(process_info.GetExecutableFile(), m_target.GetArchitecture(), exe_module_sp, executable_search_paths.GetSize() ? &executable_search_paths : NULL); if (!error.Success()) return error; // Fix the target architecture if necessary const ArchSpec &module_arch = exe_module_sp->GetArchitecture(); if (module_arch.IsValid() && !m_target.GetArchitecture().IsExactMatch(module_arch)) m_target.SetArchitecture(module_arch); // Initialize the target module list m_target.SetExecutableModule (exe_module_sp, true); SetSTDIOFileDescriptor(m_monitor->GetTerminalFD()); SetID(pid); return error; } Error ProcessPOSIX::DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info) { return DoAttachToProcessWithID(pid); } Error ProcessPOSIX::WillLaunch(Module* module) { Error error; return error; } const char * ProcessPOSIX::GetFilePath( const lldb_private::ProcessLaunchInfo::FileAction *file_action, const char *default_path) { const char *pts_name = "/dev/pts/"; const char *path = NULL; if (file_action) { if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) path = file_action->GetPath(); // By default the stdio paths passed in will be pseudo-terminal // (/dev/pts). If so, convert to using a different default path // instead to redirect I/O to the debugger console. This should // also handle user overrides to /dev/null or a different file. if (::strncmp(path, pts_name, ::strlen(pts_name)) == 0) path = default_path; } return path; } Error ProcessPOSIX::DoLaunch (Module *module, const ProcessLaunchInfo &launch_info) { Error error; assert(m_monitor == NULL); const char* working_dir = launch_info.GetWorkingDirectory(); if (working_dir) { FileSpec WorkingDir(working_dir, true); if (!WorkingDir || WorkingDir.GetFileType() != FileSpec::eFileTypeDirectory) { error.SetErrorStringWithFormat("No such file or directory: %s", working_dir); return error; } } SetPrivateState(eStateLaunching); const lldb_private::ProcessLaunchInfo::FileAction *file_action; // Default of NULL will mean to use existing open file descriptors const char *stdin_path = NULL; const char *stdout_path = NULL; const char *stderr_path = NULL; file_action = launch_info.GetFileActionForFD (STDIN_FILENO); stdin_path = GetFilePath(file_action, stdin_path); file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); stdout_path = GetFilePath(file_action, stdout_path); file_action = launch_info.GetFileActionForFD (STDERR_FILENO); stderr_path = GetFilePath(file_action, stderr_path); m_monitor = new ProcessMonitor (this, module, launch_info.GetArguments().GetConstArgumentVector(), launch_info.GetEnvironmentEntries().GetConstArgumentVector(), stdin_path, stdout_path, stderr_path, working_dir, error); m_module = module; if (!error.Success()) return error; SetSTDIOFileDescriptor(m_monitor->GetTerminalFD()); SetID(m_monitor->GetPID()); return error; } void ProcessPOSIX::DidLaunch() { } -Error -ProcessPOSIX::DoResume() -{ - StateType state = GetPrivateState(); - - assert(state == eStateStopped); - - SetPrivateState(eStateRunning); - - bool did_resume = false; - - Mutex::Locker lock(m_thread_list.GetMutex()); - - uint32_t thread_count = m_thread_list.GetSize(false); - for (uint32_t i = 0; i < thread_count; ++i) - { - POSIXThread *thread = static_cast( - m_thread_list.GetThreadAtIndex(i, false).get()); - did_resume = thread->Resume() || did_resume; - } - assert(did_resume && "Process resume failed!"); - - return Error(); -} - addr_t ProcessPOSIX::GetImageInfoAddress() { Target *target = &GetTarget(); ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); Address addr = obj_file->GetImageInfoAddress(target); if (addr.IsValid()) return addr.GetLoadAddress(target); return LLDB_INVALID_ADDRESS; } Error ProcessPOSIX::DoHalt(bool &caused_stop) { Error error; if (IsStopped()) { caused_stop = false; } else if (kill(GetID(), SIGSTOP)) { caused_stop = false; error.SetErrorToErrno(); } else { caused_stop = true; } return error; } Error ProcessPOSIX::DoSignal(int signal) { Error error; if (kill(GetID(), signal)) error.SetErrorToErrno(); return error; } Error ProcessPOSIX::DoDestroy() { Error error; if (!HasExited()) { // Drive the exit event to completion (do not keep the inferior in // limbo). m_exit_now = true; if ((m_monitor == NULL || kill(m_monitor->GetPID(), SIGKILL)) && error.Success()) { error.SetErrorToErrno(); return error; } SetPrivateState(eStateExited); } return error; } void ProcessPOSIX::DoDidExec() { Target *target = &GetTarget(); if (target) { PlatformSP platform_sp (target->GetPlatform()); assert (platform_sp.get()); if (platform_sp) { ProcessInstanceInfo process_info; platform_sp->GetProcessInfo(GetID(), process_info); ModuleSP exe_module_sp; FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); Error error = platform_sp->ResolveExecutable(process_info.GetExecutableFile(), target->GetArchitecture(), exe_module_sp, executable_search_paths.GetSize() ? &executable_search_paths : NULL); if (!error.Success()) return; target->SetExecutableModule(exe_module_sp, true); } } -} - -void -ProcessPOSIX::SendMessage(const ProcessMessage &message) -{ - Mutex::Locker lock(m_message_mutex); - - Mutex::Locker thread_lock(m_thread_list.GetMutex()); - - POSIXThread *thread = static_cast( - m_thread_list.FindThreadByID(message.GetTID(), false).get()); - - switch (message.GetKind()) - { - case ProcessMessage::eInvalidMessage: - return; - - case ProcessMessage::eAttachMessage: - SetPrivateState(eStateStopped); - return; - - case ProcessMessage::eLimboMessage: - assert(thread); - thread->SetState(eStateStopped); - if (message.GetTID() == GetID()) - { - m_exit_status = message.GetExitStatus(); - if (m_exit_now) - { - SetPrivateState(eStateExited); - m_monitor->Detach(GetID()); - } - else - { - StopAllThreads(message.GetTID()); - SetPrivateState(eStateStopped); - } - } - else - { - StopAllThreads(message.GetTID()); - SetPrivateState(eStateStopped); - } - break; - - case ProcessMessage::eExitMessage: - assert(thread); - thread->SetState(eStateExited); - // FIXME: I'm not sure we need to do this. - if (message.GetTID() == GetID()) - { - m_exit_status = message.GetExitStatus(); - SetExitStatus(m_exit_status, NULL); - } - else if (!IsAThreadRunning()) - SetPrivateState(eStateStopped); - break; - - case ProcessMessage::eSignalMessage: - case ProcessMessage::eSignalDeliveredMessage: - if (message.GetSignal() == SIGSTOP && - AddThreadForInitialStopIfNeeded(message.GetTID())) - return; - // Intentional fall-through - - case ProcessMessage::eBreakpointMessage: - case ProcessMessage::eTraceMessage: - case ProcessMessage::eWatchpointMessage: - case ProcessMessage::eCrashMessage: - assert(thread); - thread->SetState(eStateStopped); - StopAllThreads(message.GetTID()); - SetPrivateState(eStateStopped); - break; - - case ProcessMessage::eNewThreadMessage: - { - lldb::tid_t new_tid = message.GetChildTID(); - if (WaitingForInitialStop(new_tid)) - { - m_monitor->WaitForInitialTIDStop(new_tid); - } - assert(thread); - thread->SetState(eStateStopped); - StopAllThreads(message.GetTID()); - SetPrivateState(eStateStopped); - break; - } - - case ProcessMessage::eExecMessage: - { - assert(thread); - thread->SetState(eStateStopped); - StopAllThreads(message.GetTID()); - SetPrivateState(eStateStopped); - break; - } - } - - - m_message_queue.push(message); } void ProcessPOSIX::StopAllThreads(lldb::tid_t stop_tid) { // FIXME: Will this work the same way on FreeBSD and Linux? } bool ProcessPOSIX::AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid) { bool added_to_set = false; ThreadStopSet::iterator it = m_seen_initial_stop.find(stop_tid); if (it == m_seen_initial_stop.end()) { m_seen_initial_stop.insert(stop_tid); added_to_set = true; } return added_to_set; } bool ProcessPOSIX::WaitingForInitialStop(lldb::tid_t stop_tid) { return (m_seen_initial_stop.find(stop_tid) == m_seen_initial_stop.end()); } POSIXThread * ProcessPOSIX::CreateNewPOSIXThread(lldb_private::Process &process, lldb::tid_t tid) { return new POSIXThread(process, tid); } void ProcessPOSIX::RefreshStateAfterStop() { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("ProcessPOSIX::%s(), message_queue size = %d", __FUNCTION__, (int)m_message_queue.size()); Mutex::Locker lock(m_message_mutex); // This method used to only handle one message. Changing it to loop allows // it to handle the case where we hit a breakpoint while handling a different // breakpoint. while (!m_message_queue.empty()) { ProcessMessage &message = m_message_queue.front(); // Resolve the thread this message corresponds to and pass it along. lldb::tid_t tid = message.GetTID(); if (log) log->Printf ("ProcessPOSIX::%s(), message_queue size = %d, pid = %" PRIi64, __FUNCTION__, (int)m_message_queue.size(), tid); if (message.GetKind() == ProcessMessage::eNewThreadMessage) { if (log) log->Printf ("ProcessPOSIX::%s() adding thread, tid = %" PRIi64, __FUNCTION__, message.GetChildTID()); lldb::tid_t child_tid = message.GetChildTID(); ThreadSP thread_sp; thread_sp.reset(CreateNewPOSIXThread(*this, child_tid)); Mutex::Locker lock(m_thread_list.GetMutex()); m_thread_list.AddThread(thread_sp); } m_thread_list.RefreshStateAfterStop(); POSIXThread *thread = static_cast( GetThreadList().FindThreadByID(tid, false).get()); if (thread) thread->Notify(message); if (message.GetKind() == ProcessMessage::eExitMessage) { // FIXME: We should tell the user about this, but the limbo message is probably better for that. if (log) log->Printf ("ProcessPOSIX::%s() removing thread, tid = %" PRIi64, __FUNCTION__, tid); Mutex::Locker lock(m_thread_list.GetMutex()); ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false); thread_sp.reset(); m_seen_initial_stop.erase(tid); } m_message_queue.pop(); } } bool ProcessPOSIX::IsAlive() { StateType state = GetPrivateState(); return state != eStateDetached && state != eStateExited && state != eStateInvalid && state != eStateUnloaded; } size_t ProcessPOSIX::DoReadMemory(addr_t vm_addr, void *buf, size_t size, Error &error) { assert(m_monitor); return m_monitor->ReadMemory(vm_addr, buf, size, error); } size_t ProcessPOSIX::DoWriteMemory(addr_t vm_addr, const void *buf, size_t size, Error &error) { assert(m_monitor); return m_monitor->WriteMemory(vm_addr, buf, size, error); } addr_t ProcessPOSIX::DoAllocateMemory(size_t size, uint32_t permissions, Error &error) { addr_t allocated_addr = LLDB_INVALID_ADDRESS; unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; if (InferiorCallMmap(this, allocated_addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { m_addr_to_mmap_size[allocated_addr] = size; error.Clear(); } else { allocated_addr = LLDB_INVALID_ADDRESS; error.SetErrorStringWithFormat("unable to allocate %zu bytes of memory with permissions %s", size, GetPermissionsAsCString (permissions)); } return allocated_addr; } Error ProcessPOSIX::DoDeallocateMemory(lldb::addr_t addr) { Error error; MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); if (pos != m_addr_to_mmap_size.end() && InferiorCallMunmap(this, addr, pos->second)) m_addr_to_mmap_size.erase (pos); else error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); return error; } addr_t ProcessPOSIX::ResolveIndirectFunction(const Address *address, Error &error) { addr_t function_addr = LLDB_INVALID_ADDRESS; if (address == NULL) { error.SetErrorStringWithFormat("unable to determine direct function call for NULL address"); } else if (!InferiorCall(this, address, function_addr)) { function_addr = LLDB_INVALID_ADDRESS; error.SetErrorStringWithFormat("unable to determine direct function call for indirect function %s", address->CalculateSymbolContextSymbol()->GetName().AsCString()); } return function_addr; } size_t ProcessPOSIX::GetSoftwareBreakpointTrapOpcode(BreakpointSite* bp_site) { static const uint8_t g_i386_opcode[] = { 0xCC }; ArchSpec arch = GetTarget().GetArchitecture(); const uint8_t *opcode = NULL; size_t opcode_size = 0; switch (arch.GetCore()) { default: assert(false && "CPU type not supported!"); break; case ArchSpec::eCore_x86_32_i386: case ArchSpec::eCore_x86_64_x86_64: opcode = g_i386_opcode; opcode_size = sizeof(g_i386_opcode); break; } bp_site->SetTrapOpcode(opcode, opcode_size); return opcode_size; } Error ProcessPOSIX::EnableBreakpointSite(BreakpointSite *bp_site) { return EnableSoftwareBreakpoint(bp_site); } Error ProcessPOSIX::DisableBreakpointSite(BreakpointSite *bp_site) { return DisableSoftwareBreakpoint(bp_site); } Error ProcessPOSIX::EnableWatchpoint(Watchpoint *wp, bool notify) { Error error; if (wp) { user_id_t watchID = wp->GetID(); addr_t addr = wp->GetLoadAddress(); Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); if (log) log->Printf ("ProcessPOSIX::EnableWatchpoint(watchID = %" PRIu64 ")", watchID); if (wp->IsEnabled()) { if (log) log->Printf("ProcessPOSIX::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr); return error; } // Try to find a vacant watchpoint slot in the inferiors' main thread uint32_t wp_hw_index = LLDB_INVALID_INDEX32; Mutex::Locker lock(m_thread_list.GetMutex()); POSIXThread *thread = static_cast( m_thread_list.GetThreadAtIndex(0, false).get()); if (thread) wp_hw_index = thread->FindVacantWatchpointIndex(); if (wp_hw_index == LLDB_INVALID_INDEX32) { error.SetErrorString("Setting hardware watchpoint failed."); } else { wp->SetHardwareIndex(wp_hw_index); bool wp_enabled = true; uint32_t thread_count = m_thread_list.GetSize(false); for (uint32_t i = 0; i < thread_count; ++i) { thread = static_cast( m_thread_list.GetThreadAtIndex(i, false).get()); if (thread) wp_enabled &= thread->EnableHardwareWatchpoint(wp); else wp_enabled = false; } if (wp_enabled) { wp->SetEnabled(true, notify); return error; } else { // Watchpoint enabling failed on at least one // of the threads so roll back all of them DisableWatchpoint(wp, false); error.SetErrorString("Setting hardware watchpoint failed"); } } } else error.SetErrorString("Watchpoint argument was NULL."); return error; } Error ProcessPOSIX::DisableWatchpoint(Watchpoint *wp, bool notify) { Error error; if (wp) { user_id_t watchID = wp->GetID(); addr_t addr = wp->GetLoadAddress(); Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); if (log) log->Printf("ProcessPOSIX::DisableWatchpoint(watchID = %" PRIu64 ")", watchID); if (!wp->IsEnabled()) { if (log) log->Printf("ProcessPOSIX::DisableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already disabled.", watchID, (uint64_t)addr); // This is needed (for now) to keep watchpoints disabled correctly wp->SetEnabled(false, notify); return error; } if (wp->IsHardware()) { bool wp_disabled = true; Mutex::Locker lock(m_thread_list.GetMutex()); uint32_t thread_count = m_thread_list.GetSize(false); for (uint32_t i = 0; i < thread_count; ++i) { POSIXThread *thread = static_cast( m_thread_list.GetThreadAtIndex(i, false).get()); if (thread) wp_disabled &= thread->DisableHardwareWatchpoint(wp); else wp_disabled = false; } if (wp_disabled) { wp->SetHardwareIndex(LLDB_INVALID_INDEX32); wp->SetEnabled(false, notify); return error; } else error.SetErrorString("Disabling hardware watchpoint failed"); } } else error.SetErrorString("Watchpoint argument was NULL."); return error; } Error ProcessPOSIX::GetWatchpointSupportInfo(uint32_t &num) { Error error; Mutex::Locker lock(m_thread_list.GetMutex()); POSIXThread *thread = static_cast( m_thread_list.GetThreadAtIndex(0, false).get()); if (thread) num = thread->NumSupportedHardwareWatchpoints(); else error.SetErrorString("Process does not exist."); return error; } Error ProcessPOSIX::GetWatchpointSupportInfo(uint32_t &num, bool &after) { Error error = GetWatchpointSupportInfo(num); // Watchpoints trigger and halt the inferior after // the corresponding instruction has been executed. after = true; return error; } uint32_t ProcessPOSIX::UpdateThreadListIfNeeded() { Mutex::Locker lock(m_thread_list.GetMutex()); // Do not allow recursive updates. return m_thread_list.GetSize(false); } bool ProcessPOSIX::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("ProcessPOSIX::%s() (pid = %" PRIi64 ")", __FUNCTION__, GetID()); bool has_updated = false; // Update the process thread list with this new thread. // FIXME: We should be using tid, not pid. assert(m_monitor); ThreadSP thread_sp (old_thread_list.FindThreadByID (GetID(), false)); if (!thread_sp) { thread_sp.reset(CreateNewPOSIXThread(*this, GetID())); has_updated = true; } if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("ProcessPOSIX::%s() updated pid = %" PRIi64, __FUNCTION__, GetID()); new_thread_list.AddThread(thread_sp); return has_updated; // the list has been updated } ByteOrder ProcessPOSIX::GetByteOrder() const { // FIXME: We should be able to extract this value directly. See comment in // ProcessPOSIX(). return m_byte_order; } size_t ProcessPOSIX::PutSTDIN(const char *buf, size_t len, Error &error) { ssize_t status; if ((status = write(m_monitor->GetTerminalFD(), buf, len)) < 0) { error.SetErrorToErrno(); return 0; } return status; } UnixSignals & ProcessPOSIX::GetUnixSignals() { return m_signals; } //------------------------------------------------------------------------------ // Utility functions. bool ProcessPOSIX::HasExited() { switch (GetPrivateState()) { default: break; case eStateDetached: case eStateExited: return true; } return false; } bool ProcessPOSIX::IsStopped() { switch (GetPrivateState()) { default: break; case eStateStopped: case eStateCrashed: case eStateSuspended: return true; } return false; } bool ProcessPOSIX::IsAThreadRunning() { bool is_running = false; Mutex::Locker lock(m_thread_list.GetMutex()); uint32_t thread_count = m_thread_list.GetSize(false); for (uint32_t i = 0; i < thread_count; ++i) { POSIXThread *thread = static_cast( m_thread_list.GetThreadAtIndex(i, false).get()); StateType thread_state = thread->GetState(); if (thread_state == eStateRunning || thread_state == eStateStepping) { is_running = true; break; } } return is_running; } Index: stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h =================================================================== --- stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h (revision 263367) +++ stable/10/contrib/llvm/tools/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h (revision 263368) @@ -1,217 +1,218 @@ //===-- ProcessPOSIX.h ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ProcessPOSIX_H_ #define liblldb_ProcessPOSIX_H_ // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "lldb/Target/Process.h" #include "lldb/Target/UnixSignals.h" #include "ProcessMessage.h" class ProcessMonitor; class POSIXThread; class ProcessPOSIX : public lldb_private::Process { public: //------------------------------------------------------------------ // Constructors and destructors //------------------------------------------------------------------ ProcessPOSIX(lldb_private::Target& target, lldb_private::Listener &listener); virtual ~ProcessPOSIX(); //------------------------------------------------------------------ // Process protocol. //------------------------------------------------------------------ virtual void Finalize(); virtual bool CanDebug(lldb_private::Target &target, bool plugin_specified_by_name); virtual lldb_private::Error WillLaunch(lldb_private::Module *module); virtual lldb_private::Error DoAttachToProcessWithID(lldb::pid_t pid); virtual lldb_private::Error DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); virtual lldb_private::Error DoLaunch (lldb_private::Module *exe_module, const lldb_private::ProcessLaunchInfo &launch_info); virtual void DidLaunch(); virtual lldb_private::Error - DoResume(); + DoResume() = 0; virtual lldb_private::Error DoHalt(bool &caused_stop); virtual lldb_private::Error DoDetach(bool keep_stopped) = 0; virtual lldb_private::Error DoSignal(int signal); virtual lldb_private::Error DoDestroy(); virtual void DoDidExec(); virtual void RefreshStateAfterStop(); virtual bool IsAlive(); virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, lldb_private::Error &error); virtual size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error); virtual lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, lldb_private::Error &error); virtual lldb_private::Error DoDeallocateMemory(lldb::addr_t ptr); virtual lldb::addr_t ResolveIndirectFunction(const lldb_private::Address *address, lldb_private::Error &error); virtual size_t GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site); virtual lldb_private::Error EnableBreakpointSite(lldb_private::BreakpointSite *bp_site); virtual lldb_private::Error DisableBreakpointSite(lldb_private::BreakpointSite *bp_site); virtual lldb_private::Error EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true); virtual lldb_private::Error DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true); virtual lldb_private::Error GetWatchpointSupportInfo(uint32_t &num); virtual lldb_private::Error GetWatchpointSupportInfo(uint32_t &num, bool &after); virtual uint32_t UpdateThreadListIfNeeded(); virtual bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) = 0; virtual lldb::ByteOrder GetByteOrder() const; virtual lldb::addr_t GetImageInfoAddress(); virtual size_t PutSTDIN(const char *buf, size_t len, lldb_private::Error &error); //-------------------------------------------------------------------------- // ProcessPOSIX internal API. /// Registers the given message with this process. - void SendMessage(const ProcessMessage &message); + virtual void + SendMessage(const ProcessMessage &message) = 0; ProcessMonitor & GetMonitor() { assert(m_monitor); return *m_monitor; } lldb_private::UnixSignals & GetUnixSignals(); const char * GetFilePath(const lldb_private::ProcessLaunchInfo::FileAction *file_action, const char *default_path); /// Stops all threads in the process. /// The \p stop_tid parameter indicates the thread which initiated the stop. virtual void StopAllThreads(lldb::tid_t stop_tid); /// Adds the thread to the list of threads for which we have received the initial stopping signal. /// The \p stop_tid paramter indicates the thread which the stop happened for. bool AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid); bool WaitingForInitialStop(lldb::tid_t stop_tid); virtual POSIXThread * CreateNewPOSIXThread(lldb_private::Process &process, lldb::tid_t tid); protected: /// Target byte order. lldb::ByteOrder m_byte_order; /// Process monitor; ProcessMonitor *m_monitor; /// The module we are executing. lldb_private::Module *m_module; /// Message queue notifying this instance of inferior process state changes. lldb_private::Mutex m_message_mutex; std::queue m_message_queue; /// Drive any exit events to completion. bool m_exit_now; /// OS-specific signal set. lldb_private::UnixSignals m_signals; /// Returns true if the process has exited. bool HasExited(); /// Returns true if the process is stopped. bool IsStopped(); /// Returns true if at least one running is currently running bool IsAThreadRunning(); typedef std::map MMapMap; MMapMap m_addr_to_mmap_size; typedef std::set ThreadStopSet; /// Every thread begins with a stop signal. This keeps track /// of the threads for which we have received the stop signal. ThreadStopSet m_seen_initial_stop; }; #endif // liblldb_MacOSXProcess_H_ Index: stable/10/lib/clang/liblldbPluginProcessFreeBSD/Makefile =================================================================== --- stable/10/lib/clang/liblldbPluginProcessFreeBSD/Makefile (revision 263367) +++ stable/10/lib/clang/liblldbPluginProcessFreeBSD/Makefile (revision 263368) @@ -1,20 +1,21 @@ # $FreeBSD$ .include LIB= lldbPluginProcessFreeBSD # include_directories(.) CFLAGS+=-I${.CURDIR}/../../../contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD CFLAGS+=-I${.CURDIR}/../../../contrib/llvm/tools/lldb/source/Plugins/Process/POSIX SRCDIR= tools/lldb/source/Plugins/Process/FreeBSD -SRCS= ProcessFreeBSD.cpp \ +SRCS= FreeBSDThread.cpp \ + ProcessFreeBSD.cpp \ ProcessMonitor.cpp TGHDRS= DiagnosticCommonKinds \ DeclNodes \ StmtNodes \ CommentCommandList .include "../lldb.lib.mk" Index: stable/10 =================================================================== --- stable/10 (revision 263367) +++ stable/10 (revision 263368) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r258892