//===-- ProcessLinux.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 "ProcessLinux.h" #include "ProcessPOSIXLog.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "ProcessMonitor.h" #include "LinuxThread.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------------ // Static functions. ProcessSP ProcessLinux::CreateInstance(Target &target, Listener &listener, const FileSpec *core_file) { return ProcessSP(new ProcessLinux(target, listener, (FileSpec *)core_file)); } void ProcessLinux::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 (ProcessLinux::GetPluginNameStatic(), log_callbacks); ProcessPOSIXLog::RegisterPluginName(GetPluginNameStatic()); } } //------------------------------------------------------------------------------ // Constructors and destructors. ProcessLinux::ProcessLinux(Target& target, Listener &listener, FileSpec *core_file) : ProcessPOSIX(target, listener), m_core_file(core_file), m_stopping_threads(false) { #if 0 // 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. ObjectFile *obj_file = GetTarget().GetExecutableModule()->GetObjectFile(); m_byte_order = obj_file->GetByteOrder(); #else // XXX: Will work only for local processes. m_byte_order = lldb::endian::InlHostByteOrder(); #endif } void ProcessLinux::Terminate() { } lldb_private::ConstString ProcessLinux::GetPluginNameStatic() { static ConstString g_name("linux"); return g_name; } const char * ProcessLinux::GetPluginDescriptionStatic() { return "Process plugin for Linux"; } bool ProcessLinux::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { new_thread_list = old_thread_list; return new_thread_list.GetSize(false) > 0; } //------------------------------------------------------------------------------ // ProcessInterface protocol. lldb_private::ConstString ProcessLinux::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessLinux::GetPluginVersion() { return 1; } void ProcessLinux::GetPluginCommandHelp(const char *command, Stream *strm) { } Error ProcessLinux::ExecutePluginCommand(Args &command, Stream *strm) { return Error(1, eErrorTypeGeneric); } Log * ProcessLinux::EnablePluginLogging(Stream *strm, Args &command) { return NULL; } Error ProcessLinux::DoDetach(bool keep_stopped) { Error error; if (keep_stopped) { // FIXME: If you want to implement keep_stopped, // this would be the place to do it. error.SetErrorString("Detaching with keep_stopped true is not currently supported on Linux."); return error; } 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()); error = m_monitor->Detach(thread->GetID()); } if (error.Success()) SetPrivateState(eStateDetached); return error; } // ProcessPOSIX override Error ProcessLinux::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(); } void ProcessLinux::StopAllThreads(lldb::tid_t stop_tid) { // If a breakpoint occurs while we're stopping threads, we'll get back // here, but we don't want to do it again. Only the MonitorChildProcess // thread calls this function, so we don't need to protect this flag. if (m_stopping_threads) return; m_stopping_threads = true; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (log) log->Printf ("ProcessLinux::%s() stopping all threads", __FUNCTION__); // Walk the thread list and stop the other threads. The thread that caused // the stop should already be marked as stopped before we get here. Mutex::Locker thread_list_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()); assert(thread); lldb::tid_t tid = thread->GetID(); if (!StateIsStoppedState(thread->GetState(), false)) m_monitor->StopThread(tid); } m_stopping_threads = false; if (log) log->Printf ("ProcessLinux::%s() finished", __FUNCTION__); } // ProcessPOSIX override POSIXThread * ProcessLinux::CreateNewPOSIXThread(lldb_private::Process &process, lldb::tid_t tid) { return new LinuxThread(process, tid); } bool ProcessLinux::CanDebug(Target &target, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; /* If core file is specified then let elf-core plugin handle it */ if (m_core_file) return false; return ProcessPOSIX::CanDebug(target, plugin_specified_by_name); } void ProcessLinux::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()); 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); }