Hollowed out process plug-in to do KDP darwin kernel debugging.



git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@135240 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
new file mode 100644
index 0000000..9ae965e
--- /dev/null
+++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
@@ -0,0 +1,725 @@
+//===-- 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/PluginManager.h"
+#include "lldb/Core/State.h"
+#include "lldb/Host/Host.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)
+{
+    // 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())
+    {
+        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_sp->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;
+    error.SetErrorString ("attaching to a by process name not supported in kdp-remote plug-in");
+    return error;
+}
+
+//----------------------------------------------------------------------
+// Process Control
+//----------------------------------------------------------------------
+Error
+ProcessKDP::DoLaunch (Module* module,
+                      char const *argv[],
+                      char const *envp[],
+                      uint32_t launch_flags,
+                      const char *stdin_path,
+                      const char *stdout_path,
+                      const char *stderr_path,
+                      const char *working_dir)
+{
+    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;
+}
+
+size_t
+ProcessKDP::AttachInputReaderCallback (void *baton, 
+                                       InputReader *reader, 
+                                       lldb::InputReaderAction notification,
+                                       const char *bytes, 
+                                       size_t bytes_len)
+{
+    if (notification == eInputReaderGotToken)
+    {
+//        ProcessKDP *process = (ProcessKDP *)baton;
+//        if (process->m_waiting_for_attach)
+//            process->m_waiting_for_attach = false;
+        reader->SetIsDone(true);
+        return 1;
+    }
+    return 0;
+}
+
+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::DidLaunch()");
+    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;
+    error.SetErrorString ("ProcessKDP::DoResume () is not implemented yet");
+    return error;
+}
+
+uint32_t
+ProcessKDP::UpdateThreadListIfNeeded ()
+{
+    // 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 = %i)", __FUNCTION__, GetID());
+    
+    Mutex::Locker locker (m_thread_list.GetMutex ());
+    // TODO: get the thread list here!
+    const uint32_t stop_id = GetStopID();
+    if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID())
+    {
+        // Update the thread list's stop id immediately so we don't recurse into this function.
+//        ThreadList curr_thread_list (this);
+//        curr_thread_list.SetStopID(stop_id);
+//        
+//        std::vector<lldb::tid_t> thread_ids;
+//        bool sequence_mutex_unavailable = false;
+//        const size_t num_thread_ids = m_comm.GetCurrentThreadIDs (thread_ids, sequence_mutex_unavailable);
+//        if (num_thread_ids > 0)
+//        {
+//            for (size_t i=0; i<num_thread_ids; ++i)
+//            {
+//                tid_t tid = thread_ids[i];
+//                ThreadSP thread_sp (GetThreadList().FindThreadByID (tid, false));
+//                if (!thread_sp)
+//                    thread_sp.reset (new ThreadGDBRemote (*this, tid));
+//                curr_thread_list.AddThread(thread_sp);
+//            }
+//        }
+//        
+//        if (sequence_mutex_unavailable == false)
+//        {
+//            m_thread_list = curr_thread_list;
+//            SetThreadStopInfo (m_last_stop_packet);
+//        }
+    }
+    return GetThreadList().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
+    {
+        // TODO: add the ability to halt a running kernel
+        error.SetErrorString ("halt not supported in kdp-remote plug-in");
+//        if (!m_comm.SendInterrupt (locker, 2, caused_stop, timed_out))
+//        {
+//            if (timed_out)
+//                error.SetErrorString("timed out sending interrupt packet");
+//            else
+//                error.SetErrorString("unknown error sending interrupt packet");
+//        }
+    }
+    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();
+    
+    size_t response_size = m_comm.SendPacket ("D", 1);
+    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
+        {
+            
+            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)
+{
+    error.SetErrorString ("ProcessKDP::DoReadMemory not implemented");
+    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)
+{
+    return EnableSoftwareBreakpoint (bp_site);
+}
+
+Error
+ProcessKDP::DisableBreakpoint (BreakpointSite *bp_site)
+{
+    return DisableSoftwareBreakpoint (bp_site);
+}
+
+Error
+ProcessKDP::EnableWatchpoint (WatchpointLocation *wp)
+{
+    Error error;
+    error.SetErrorString ("watchpoints are not suppported in kdp remote debugging");
+    return error;
+}
+
+Error
+ProcessKDP::DisableWatchpoint (WatchpointLocation *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 = %i) 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 = %i) 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 = %i) 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 = %i) got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID());
+                            done = true;
+                            break;
+                            
+                        default:
+                            if (log)
+                                log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) 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 = %i) listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID());
+                done = true;
+            }
+        }
+    }
+    
+    if (log)
+        log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, arg, process->GetID());
+    
+    process->m_async_thread = LLDB_INVALID_HOST_THREAD;
+    return NULL;
+}
+
+