blob: efcb02a70f2c07ea3f82d4dfd621568a95546f0e [file] [log] [blame]
//===-- 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 <errno.h>
// 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<POSIXThread*>(
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)
{
LinuxThread *thread = static_cast<LinuxThread*>(
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<POSIXThread*>(
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<POSIXThread*>(
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);
}