Greg Clayton 982c9762a2 Modified all Process::Launch() calls to use a ProcessLaunchInfo structure
on internal only (public API hasn't changed) to simplify the paramter list
to the launch calls down into just one argument. Also all of the argument,
envronment and stdio things are now handled in a much more centralized fashion.

llvm-svn: 143656
2011-11-03 21:22:33 +00:00

807 lines
25 KiB
C++

//===-- ProcessKDP.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>
#include <stdlib.h>
// C++ Includes
// Other libraries and framework includes
#include "lldb/Core/ConnectionFileDescriptor.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/State.h"
#include "lldb/Host/Host.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
// Project includes
#include "ProcessKDP.h"
#include "ProcessKDPLog.h"
#include "ThreadKDP.h"
#include "StopInfoMachException.h"
using namespace lldb;
using namespace lldb_private;
const char *
ProcessKDP::GetPluginNameStatic()
{
return "kdp-remote";
}
const char *
ProcessKDP::GetPluginDescriptionStatic()
{
return "KDP Remote protocol based debugging plug-in for darwin kernel debugging.";
}
void
ProcessKDP::Terminate()
{
PluginManager::UnregisterPlugin (ProcessKDP::CreateInstance);
}
Process*
ProcessKDP::CreateInstance (Target &target, Listener &listener)
{
return new ProcessKDP (target, listener);
}
bool
ProcessKDP::CanDebug(Target &target, bool plugin_specified_by_name)
{
if (plugin_specified_by_name)
return true;
// For now we are just making sure the file exists for a given module
Module *exe_module = target.GetExecutableModulePointer();
if (exe_module)
{
const llvm::Triple &triple_ref = target.GetArchitecture().GetTriple();
if (triple_ref.getOS() == llvm::Triple::Darwin &&
triple_ref.getVendor() == llvm::Triple::Apple)
{
ObjectFile *exe_objfile = exe_module->GetObjectFile();
if (exe_objfile->GetType() == ObjectFile::eTypeExecutable &&
exe_objfile->GetStrata() == ObjectFile::eStrataKernel)
return true;
}
}
return false;
}
//----------------------------------------------------------------------
// ProcessKDP constructor
//----------------------------------------------------------------------
ProcessKDP::ProcessKDP(Target& target, Listener &listener) :
Process (target, listener),
m_comm("lldb.process.kdp-remote.communication"),
m_async_broadcaster ("lldb.process.kdp-remote.async-broadcaster"),
m_async_thread (LLDB_INVALID_HOST_THREAD)
{
// m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit");
// m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue");
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
ProcessKDP::~ProcessKDP()
{
Clear();
}
//----------------------------------------------------------------------
// PluginInterface
//----------------------------------------------------------------------
const char *
ProcessKDP::GetPluginName()
{
return "Process debugging plug-in that uses the Darwin KDP remote protocol";
}
const char *
ProcessKDP::GetShortPluginName()
{
return GetPluginNameStatic();
}
uint32_t
ProcessKDP::GetPluginVersion()
{
return 1;
}
Error
ProcessKDP::WillLaunch (Module* module)
{
Error error;
error.SetErrorString ("launching not supported in kdp-remote plug-in");
return error;
}
Error
ProcessKDP::WillAttachToProcessWithID (lldb::pid_t pid)
{
Error error;
error.SetErrorString ("attaching to a by process ID not supported in kdp-remote plug-in");
return error;
}
Error
ProcessKDP::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch)
{
Error error;
error.SetErrorString ("attaching to a by process name not supported in kdp-remote plug-in");
return error;
}
Error
ProcessKDP::DoConnectRemote (const char *remote_url)
{
// TODO: fill in the remote connection to the remote KDP here!
Error error;
if (remote_url == NULL || remote_url[0] == '\0')
remote_url = "udp://localhost:41139";
std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
if (conn_ap.get())
{
// Only try once for now.
// TODO: check if we should be retrying?
const uint32_t max_retry_count = 1;
for (uint32_t retry_count = 0; retry_count < max_retry_count; ++retry_count)
{
if (conn_ap->Connect(remote_url, &error) == eConnectionStatusSuccess)
break;
usleep (100000);
}
}
if (conn_ap->IsConnected())
{
const uint16_t reply_port = conn_ap->GetReadPort ();
if (reply_port != 0)
{
m_comm.SetConnection(conn_ap.release());
if (m_comm.SendRequestReattach(reply_port))
{
if (m_comm.SendRequestConnect(reply_port, reply_port, "Greetings from LLDB..."))
{
m_comm.GetVersion();
uint32_t cpu = m_comm.GetCPUType();
uint32_t sub = m_comm.GetCPUSubtype();
ArchSpec kernel_arch;
kernel_arch.SetArchitecture(eArchTypeMachO, cpu, sub);
m_target.SetArchitecture(kernel_arch);
SetID (1);
GetThreadList ();
SetPrivateState (eStateStopped);
StreamSP async_strm_sp(m_target.GetDebugger().GetAsyncOutputStream());
if (async_strm_sp)
{
const char *cstr;
if ((cstr = m_comm.GetKernelVersion ()) != NULL)
{
async_strm_sp->Printf ("Version: %s\n", cstr);
async_strm_sp->Flush();
}
// if ((cstr = m_comm.GetImagePath ()) != NULL)
// {
// async_strm_sp->Printf ("Image Path: %s\n", cstr);
// async_strm_sp->Flush();
// }
}
}
}
else
{
error.SetErrorString("KDP reattach failed");
}
}
else
{
error.SetErrorString("invalid reply port from UDP connection");
}
}
else
{
if (error.Success())
error.SetErrorStringWithFormat ("failed to connect to '%s'", remote_url);
}
if (error.Fail())
m_comm.Disconnect();
return error;
}
//----------------------------------------------------------------------
// Process Control
//----------------------------------------------------------------------
Error
ProcessKDP::DoLaunch (Module *exe_module,
const ProcessLaunchInfo &launch_info)
{
Error error;
error.SetErrorString ("launching not supported in kdp-remote plug-in");
return error;
}
Error
ProcessKDP::DoAttachToProcessWithID (lldb::pid_t attach_pid)
{
Error error;
error.SetErrorString ("attach to process by ID is not suppported in kdp remote debugging");
return error;
}
Error
ProcessKDP::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch)
{
Error error;
error.SetErrorString ("attach to process by name is not suppported in kdp remote debugging");
return error;
}
void
ProcessKDP::DidAttach ()
{
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS));
if (log)
log->Printf ("ProcessKDP::DidAttach()");
if (GetID() != LLDB_INVALID_PROCESS_ID)
{
// TODO: figure out the register context that we will use
}
}
Error
ProcessKDP::WillResume ()
{
return Error();
}
Error
ProcessKDP::DoResume ()
{
Error error;
if (!m_comm.SendRequestResume ())
error.SetErrorString ("KDP resume failed");
return error;
}
uint32_t
ProcessKDP::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list)
{
// locker will keep a mutex locked until it goes out of scope
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_THREAD));
if (log && log->GetMask().Test(KDP_LOG_VERBOSE))
log->Printf ("ProcessKDP::%s (pid = %llu)", __FUNCTION__, GetID());
// We currently are making only one thread per core and we
// actually don't know about actual threads. Eventually we
// want to get the thread list from memory and note which
// threads are on CPU as those are the only ones that we
// will be able to resume.
const uint32_t cpu_mask = m_comm.GetCPUMask();
for (uint32_t cpu_mask_bit = 1; cpu_mask_bit & cpu_mask; cpu_mask_bit <<= 1)
{
lldb::tid_t tid = cpu_mask_bit;
ThreadSP thread_sp (old_thread_list.FindThreadByID (tid, false));
if (!thread_sp)
thread_sp.reset(new ThreadKDP (*this, tid));
new_thread_list.AddThread(thread_sp);
}
return new_thread_list.GetSize(false);
}
StateType
ProcessKDP::SetThreadStopInfo (StringExtractor& stop_packet)
{
// TODO: figure out why we stopped given the packet that tells us we stopped...
return eStateStopped;
}
void
ProcessKDP::RefreshStateAfterStop ()
{
// Let all threads recover from stopping and do any clean up based
// on the previous thread state (if any).
m_thread_list.RefreshStateAfterStop();
//SetThreadStopInfo (m_last_stop_packet);
}
Error
ProcessKDP::DoHalt (bool &caused_stop)
{
Error error;
// bool timed_out = false;
Mutex::Locker locker;
if (m_public_state.GetValue() == eStateAttaching)
{
// We are being asked to halt during an attach. We need to just close
// our file handle and debugserver will go away, and we can be done...
m_comm.Disconnect();
}
else
{
if (!m_comm.SendRequestSuspend ())
error.SetErrorString ("KDP halt failed");
}
return error;
}
Error
ProcessKDP::InterruptIfRunning (bool discard_thread_plans,
bool catch_stop_event,
EventSP &stop_event_sp)
{
Error error;
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS));
bool paused_private_state_thread = false;
const bool is_running = m_comm.IsRunning();
if (log)
log->Printf ("ProcessKDP::InterruptIfRunning(discard_thread_plans=%i, catch_stop_event=%i) is_running=%i",
discard_thread_plans,
catch_stop_event,
is_running);
if (discard_thread_plans)
{
if (log)
log->Printf ("ProcessKDP::InterruptIfRunning() discarding all thread plans");
m_thread_list.DiscardThreadPlans();
}
if (is_running)
{
if (catch_stop_event)
{
if (log)
log->Printf ("ProcessKDP::InterruptIfRunning() pausing private state thread");
PausePrivateStateThread();
paused_private_state_thread = true;
}
bool timed_out = false;
// bool sent_interrupt = false;
Mutex::Locker locker;
// TODO: implement halt in CommunicationKDP
// if (!m_comm.SendInterrupt (locker, 1, sent_interrupt, timed_out))
// {
// if (timed_out)
// error.SetErrorString("timed out sending interrupt packet");
// else
// error.SetErrorString("unknown error sending interrupt packet");
// if (paused_private_state_thread)
// ResumePrivateStateThread();
// return error;
// }
if (catch_stop_event)
{
// LISTEN HERE
TimeValue timeout_time;
timeout_time = TimeValue::Now();
timeout_time.OffsetWithSeconds(5);
StateType state = WaitForStateChangedEventsPrivate (&timeout_time, stop_event_sp);
timed_out = state == eStateInvalid;
if (log)
log->Printf ("ProcessKDP::InterruptIfRunning() catch stop event: state = %s, timed-out=%i", StateAsCString(state), timed_out);
if (timed_out)
error.SetErrorString("unable to verify target stopped");
}
if (paused_private_state_thread)
{
if (log)
log->Printf ("ProcessKDP::InterruptIfRunning() resuming private state thread");
ResumePrivateStateThread();
}
}
return error;
}
Error
ProcessKDP::WillDetach ()
{
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS));
if (log)
log->Printf ("ProcessKDP::WillDetach()");
bool discard_thread_plans = true;
bool catch_stop_event = true;
EventSP event_sp;
return InterruptIfRunning (discard_thread_plans, catch_stop_event, event_sp);
}
Error
ProcessKDP::DoDetach()
{
Error error;
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS));
if (log)
log->Printf ("ProcessKDP::DoDetach()");
DisableAllBreakpointSites ();
m_thread_list.DiscardThreadPlans();
if (m_comm.IsConnected())
{
m_comm.SendRequestDisconnect();
size_t response_size = m_comm.Disconnect ();
if (log)
{
if (response_size)
log->PutCString ("ProcessKDP::DoDetach() detach packet sent successfully");
else
log->PutCString ("ProcessKDP::DoDetach() detach packet send failed");
}
}
// Sleep for one second to let the process get all detached...
StopAsyncThread ();
m_comm.StopReadThread();
m_comm.Disconnect(); // Disconnect from the debug server.
SetPrivateState (eStateDetached);
ResumePrivateStateThread();
//KillDebugserverProcess ();
return error;
}
Error
ProcessKDP::DoDestroy ()
{
Error error;
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS));
if (log)
log->Printf ("ProcessKDP::DoDestroy()");
// Interrupt if our inferior is running...
if (m_comm.IsConnected())
{
if (m_public_state.GetValue() == eStateAttaching)
{
// We are being asked to halt during an attach. We need to just close
// our file handle and debugserver will go away, and we can be done...
m_comm.Disconnect();
}
else
{
DisableAllBreakpointSites ();
m_comm.SendRequestDisconnect();
StringExtractor response;
// TODO: Send kill packet?
SetExitStatus(SIGABRT, NULL);
}
}
StopAsyncThread ();
m_comm.StopReadThread();
m_comm.Disconnect(); // Disconnect from the debug server.
return error;
}
//------------------------------------------------------------------
// Process Queries
//------------------------------------------------------------------
bool
ProcessKDP::IsAlive ()
{
return m_comm.IsConnected() && m_private_state.GetValue() != eStateExited;
}
//------------------------------------------------------------------
// Process Memory
//------------------------------------------------------------------
size_t
ProcessKDP::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error)
{
if (m_comm.IsConnected())
return m_comm.SendRequestReadMemory (addr, buf, size, error);
error.SetErrorString ("not connected");
return 0;
}
size_t
ProcessKDP::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error)
{
error.SetErrorString ("ProcessKDP::DoReadMemory not implemented");
return 0;
}
lldb::addr_t
ProcessKDP::DoAllocateMemory (size_t size, uint32_t permissions, Error &error)
{
error.SetErrorString ("memory allocation not suppported in kdp remote debugging");
return LLDB_INVALID_ADDRESS;
}
Error
ProcessKDP::DoDeallocateMemory (lldb::addr_t addr)
{
Error error;
error.SetErrorString ("memory deallocation not suppported in kdp remote debugging");
return error;
}
Error
ProcessKDP::EnableBreakpoint (BreakpointSite *bp_site)
{
if (m_comm.LocalBreakpointsAreSupported ())
{
Error error;
if (!bp_site->IsEnabled())
{
if (m_comm.SendRequestBreakpoint(true, bp_site->GetLoadAddress()))
{
bp_site->SetEnabled(true);
bp_site->SetType (BreakpointSite::eExternal);
}
else
{
error.SetErrorString ("KDP set breakpoint failed");
}
}
return error;
}
return EnableSoftwareBreakpoint (bp_site);
}
Error
ProcessKDP::DisableBreakpoint (BreakpointSite *bp_site)
{
if (m_comm.LocalBreakpointsAreSupported ())
{
Error error;
if (bp_site->IsEnabled())
{
BreakpointSite::Type bp_type = bp_site->GetType();
if (bp_type == BreakpointSite::eExternal)
{
if (m_comm.SendRequestBreakpoint(false, bp_site->GetLoadAddress()))
bp_site->SetEnabled(false);
else
error.SetErrorString ("KDP remove breakpoint failed");
}
else
{
error = DisableSoftwareBreakpoint (bp_site);
}
}
return error;
}
return DisableSoftwareBreakpoint (bp_site);
}
Error
ProcessKDP::EnableWatchpoint (Watchpoint *wp)
{
Error error;
error.SetErrorString ("watchpoints are not suppported in kdp remote debugging");
return error;
}
Error
ProcessKDP::DisableWatchpoint (Watchpoint *wp)
{
Error error;
error.SetErrorString ("watchpoints are not suppported in kdp remote debugging");
return error;
}
void
ProcessKDP::Clear()
{
Mutex::Locker locker (m_thread_list.GetMutex ());
m_thread_list.Clear();
}
Error
ProcessKDP::DoSignal (int signo)
{
Error error;
error.SetErrorString ("sending signals is not suppported in kdp remote debugging");
return error;
}
void
ProcessKDP::Initialize()
{
static bool g_initialized = false;
if (g_initialized == false)
{
g_initialized = true;
PluginManager::RegisterPlugin (GetPluginNameStatic(),
GetPluginDescriptionStatic(),
CreateInstance);
Log::Callbacks log_callbacks = {
ProcessKDPLog::DisableLog,
ProcessKDPLog::EnableLog,
ProcessKDPLog::ListLogCategories
};
Log::RegisterLogChannel (ProcessKDP::GetPluginNameStatic(), log_callbacks);
}
}
bool
ProcessKDP::StartAsyncThread ()
{
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS));
if (log)
log->Printf ("ProcessKDP::%s ()", __FUNCTION__);
// Create a thread that watches our internal state and controls which
// events make it to clients (into the DCProcess event queue).
m_async_thread = Host::ThreadCreate ("<lldb.process.kdp-remote.async>", ProcessKDP::AsyncThread, this, NULL);
return IS_VALID_LLDB_HOST_THREAD(m_async_thread);
}
void
ProcessKDP::StopAsyncThread ()
{
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS));
if (log)
log->Printf ("ProcessKDP::%s ()", __FUNCTION__);
m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit);
// Stop the stdio thread
if (IS_VALID_LLDB_HOST_THREAD(m_async_thread))
{
Host::ThreadJoin (m_async_thread, NULL, NULL);
}
}
void *
ProcessKDP::AsyncThread (void *arg)
{
ProcessKDP *process = (ProcessKDP*) arg;
LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS));
if (log)
log->Printf ("ProcessKDP::%s (arg = %p, pid = %llu) thread starting...", __FUNCTION__, arg, process->GetID());
Listener listener ("ProcessKDP::AsyncThread");
EventSP event_sp;
const uint32_t desired_event_mask = eBroadcastBitAsyncContinue |
eBroadcastBitAsyncThreadShouldExit;
if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask)
{
listener.StartListeningForEvents (&process->m_comm, Communication::eBroadcastBitReadThreadDidExit);
bool done = false;
while (!done)
{
if (log)
log->Printf ("ProcessKDP::%s (arg = %p, pid = %llu) listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID());
if (listener.WaitForEvent (NULL, event_sp))
{
const uint32_t event_type = event_sp->GetType();
if (event_sp->BroadcasterIs (&process->m_async_broadcaster))
{
if (log)
log->Printf ("ProcessKDP::%s (arg = %p, pid = %llu) Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type);
switch (event_type)
{
case eBroadcastBitAsyncContinue:
{
const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get());
if (continue_packet)
{
// TODO: do continue support here
// const char *continue_cstr = (const char *)continue_packet->GetBytes ();
// const size_t continue_cstr_len = continue_packet->GetByteSize ();
// if (log)
// log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr);
//
// if (::strstr (continue_cstr, "vAttach") == NULL)
// process->SetPrivateState(eStateRunning);
// StringExtractor response;
// StateType stop_state = process->GetCommunication().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response);
//
// switch (stop_state)
// {
// case eStateStopped:
// case eStateCrashed:
// case eStateSuspended:
// process->m_last_stop_packet = response;
// process->SetPrivateState (stop_state);
// break;
//
// case eStateExited:
// process->m_last_stop_packet = response;
// response.SetFilePos(1);
// process->SetExitStatus(response.GetHexU8(), NULL);
// done = true;
// break;
//
// case eStateInvalid:
// process->SetExitStatus(-1, "lost connection");
// break;
//
// default:
// process->SetPrivateState (stop_state);
// break;
// }
}
}
break;
case eBroadcastBitAsyncThreadShouldExit:
if (log)
log->Printf ("ProcessKDP::%s (arg = %p, pid = %llu) got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID());
done = true;
break;
default:
if (log)
log->Printf ("ProcessKDP::%s (arg = %p, pid = %llu) got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type);
done = true;
break;
}
}
else if (event_sp->BroadcasterIs (&process->m_comm))
{
if (event_type & Communication::eBroadcastBitReadThreadDidExit)
{
process->SetExitStatus (-1, "lost connection");
done = true;
}
}
}
else
{
if (log)
log->Printf ("ProcessKDP::%s (arg = %p, pid = %llu) listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID());
done = true;
}
}
}
if (log)
log->Printf ("ProcessKDP::%s (arg = %p, pid = %llu) thread exiting...", __FUNCTION__, arg, process->GetID());
process->m_async_thread = LLDB_INVALID_HOST_THREAD;
return NULL;
}