Skip to content

Commit

Permalink
Implement a framework for live debugging on Windows.
Browse files Browse the repository at this point in the history
When processes are launched for debugging on Windows now, LLDB
will detect changes such as DLL loads and unloads, breakpoints,
thread creation and deletion, etc.

These notifications are not yet propagated to LLDB in a way that
LLDB understands what is happening with the process.  This only
picks up the notifications from the OS in a way that they can be
sent to LLDB with subsequent patches.

Reviewed by: Scott Graham

Differential Revision: http://reviews.llvm.org/D6037

llvm-svn: 221207
  • Loading branch information
Zachary Turner committed Nov 4, 2014
1 parent 06ea7d6 commit 8f21174
Show file tree
Hide file tree
Showing 16 changed files with 1,101 additions and 10 deletions.
1 change: 1 addition & 0 deletions lldb/include/lldb/Host/Predicate.h
Expand Up @@ -11,6 +11,7 @@
#define liblldb_Predicate_h_
#if defined(__cplusplus)

#include "lldb/lldb-defines.h"
#include "lldb/Host/Mutex.h"
#include "lldb/Host/Condition.h"
#include <stdint.h>
Expand Down
3 changes: 0 additions & 3 deletions lldb/include/lldb/Host/windows/HostThreadWindows.h
Expand Up @@ -31,9 +31,6 @@ class HostThreadWindows : public HostNativeThreadBase
virtual void Reset();

lldb::tid_t GetThreadId() const;

protected:
llvm::SmallString<32> m_thread_name;
};
}

Expand Down
5 changes: 5 additions & 0 deletions lldb/source/Plugins/Process/Windows/CMakeLists.txt
Expand Up @@ -4,6 +4,11 @@ include_directories(.)
include_directories(../Utility)

add_lldb_library(lldbPluginProcessWindows
DebugMonitorMessages.cpp
DebugMonitorMessageResults.cpp
DebugOneProcessThread.cpp
DebugProcessLauncher.cpp
DebugDriverThread.cpp
ProcessWindows.cpp
)

247 changes: 247 additions & 0 deletions lldb/source/Plugins/Process/Windows/DebugDriverThread.cpp
@@ -0,0 +1,247 @@
//===-- DebugDriverThread.cpp -----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "DebugDriverThread.h"
#include "DebugMonitorMessages.h"
#include "DebugMonitorMessageResults.h"
#include "DebugOneProcessThread.h"
#include "SlaveMessages.h"

#include "lldb/Core/Log.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Target/Process.h"

#include <vector>

using namespace lldb;
using namespace lldb_private;

DebugDriverThread *DebugDriverThread::m_instance = NULL;

DebugDriverThread::DebugDriverThread()
{
m_monitor_thread = ThreadLauncher::LaunchThread("lldb.plugin.process-windows.monitor-thread", MonitorThread, this, nullptr);
m_shutdown_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
m_monitor_event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
::CreatePipe(&m_monitor_pipe_read, &m_monitor_pipe_write, NULL, 1024);
}

DebugDriverThread::~DebugDriverThread()
{
}

void
DebugDriverThread::Initialize()
{
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
if (log)
log->Printf("DebugDriverThread::Initialize");

m_instance = new DebugDriverThread();
}

void
DebugDriverThread::Teardown()
{
m_instance->Shutdown();

delete m_instance;
m_instance = nullptr;
}

void
DebugDriverThread::Shutdown()
{
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
if (log)
log->Printf("DebugDriverThread::Shutdown");

if (!m_shutdown_event)
return;
::SetEvent(m_shutdown_event);
m_monitor_thread.Join(nullptr);

::CloseHandle(m_shutdown_event);
::CloseHandle(m_monitor_event);
::CloseHandle(m_monitor_pipe_read);
::CloseHandle(m_monitor_pipe_write);

m_shutdown_event = nullptr;
m_monitor_event = nullptr;
m_monitor_pipe_read = nullptr;
m_monitor_pipe_write = nullptr;
}

DebugDriverThread &
DebugDriverThread::GetInstance()
{
return *m_instance;
}

void
DebugDriverThread::PostDebugMessage(const DebugMonitorMessage *message)
{
message->Retain();
if (!::WriteFile(m_monitor_pipe_write, &message, sizeof(message), NULL, NULL))
{
message->Release();
return;
}

::SetEvent(m_monitor_event);
}

const DebugMonitorMessageResult *
DebugDriverThread::HandleMonitorMessage(const DebugMonitorMessage *message)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));

switch (message->GetMessageType())
{
case MonitorMessageType::eLaunchProcess:
{
const auto *launch_message = static_cast<const LaunchProcessMessage *>(message);
return HandleMonitorMessage(launch_message);
}
default:
if (log)
log->Printf("DebugDriverThread received unknown message type %d.", message->GetMessageType());
return nullptr;
}
}

const LaunchProcessMessageResult *
DebugDriverThread::HandleMonitorMessage(const LaunchProcessMessage *launch_message)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
const char *exe = launch_message->GetLaunchInfo().GetExecutableFile().GetPath().c_str();
if (log)
log->Printf("DebugDriverThread launching process '%s'.", exe);

// Create a DebugOneProcessThread which will do the actual creation and enter a debug loop on
// a background thread, only returning after the process has been created on the background
// thread.
std::shared_ptr<DebugOneProcessThread> slave(new DebugOneProcessThread(m_monitor_thread));
const LaunchProcessMessageResult *result = slave->DebugLaunch(launch_message);
if (result && result->GetError().Success())
{
if (log)
log->Printf("DebugDriverThread launched process '%s' with PID %d.", exe, result->GetProcess().GetProcessId());
m_debugged_processes.insert(std::make_pair(result->GetProcess().GetProcessId(), slave));
}
else
{
if (log)
log->Printf("An error occured launching process '%s' -- %s.", exe, result->GetError().AsCString());
}
return result;
}

void
DebugDriverThread::HandleSlaveEvent(const SlaveMessageProcessExited &message)
{
lldb::pid_t pid = message.GetProcess().GetProcessId();

m_debugged_processes.erase(pid);

Process::SetProcessExitStatus(nullptr, pid, true, 0, message.GetExitCode());
}

void
DebugDriverThread::HandleSlaveEvent(const SlaveMessageRipEvent &message)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));

lldb::pid_t pid = message.GetProcess().GetProcessId();
m_debugged_processes.erase(pid);

if (log)
{
log->Printf("An error was encountered while debugging process %d. Debugging has been terminated. Error = s.", pid,
message.GetError().AsCString());
}
}

bool
DebugDriverThread::ProcessMonitorMessages()
{
DWORD bytes_available = 0;
if (!PeekNamedPipe(m_monitor_pipe_read, NULL, 0, NULL, &bytes_available, NULL))
{
// There's some kind of error with the named pipe. Fail out and stop monitoring.
return false;
}

if (bytes_available <= 0)
{
// There's no data available, but the operation succeeded.
return true;
}

int count = bytes_available / sizeof(DebugMonitorMessage *);
std::vector<DebugMonitorMessage *> messages(count);
if (!::ReadFile(m_monitor_pipe_read, &messages[0], bytes_available, NULL, NULL))
return false;

for (DebugMonitorMessage *message : messages)
{
const DebugMonitorMessageResult *result = HandleMonitorMessage(message);
message->CompleteMessage(result);
message->Release();
}
return true;
}

lldb::thread_result_t
DebugDriverThread::MonitorThread(void *data)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
if (log)
log->Printf("ProcessWindows DebugDriverThread starting up.");

DebugDriverThread *monitor_thread = static_cast<DebugDriverThread *>(data);
const int kMonitorEventIndex = 0;
const int kShutdownEventIndex = 1;

Error error;
HANDLE events[kShutdownEventIndex + 1];
events[kMonitorEventIndex] = monitor_thread->m_monitor_event;
events[kShutdownEventIndex] = monitor_thread->m_shutdown_event;

while (true)
{
bool exit = false;
// See if any new processes are ready for debug monitoring.
DWORD result = WaitForMultipleObjectsEx(llvm::array_lengthof(events), events, FALSE, 1000, TRUE);
switch (result)
{
case WAIT_OBJECT_0 + kMonitorEventIndex:
// LLDB is telling us to do something. Process pending messages in our queue.
monitor_thread->ProcessMonitorMessages();
break;
case WAIT_OBJECT_0 + kShutdownEventIndex:
error.SetErrorString("Shutdown event received.");
exit = true;
break;
case WAIT_TIMEOUT:
case WAIT_IO_COMPLETION:
break;
default:
error.SetError(GetLastError(), eErrorTypeWin32);
exit = true;
break;
}
if (exit)
break;
}

if (log)
log->Printf("ProcessWindows Debug monitor thread exiting. %s", error.AsCString());
return 0;
}
79 changes: 79 additions & 0 deletions lldb/source/Plugins/Process/Windows/DebugDriverThread.h
@@ -0,0 +1,79 @@
//===-- DebugDriverThread.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_Plugins_Process_Windows_DebugDriverThread_H_
#define liblldb_Plugins_Process_Windows_DebugDriverThread_H_

#include "lldb/Host/HostThread.h"
#include "lldb/Host/windows/windows.h"
#include "lldb/lldb-types.h"

#include <map>

class ProcessWindows;

namespace lldb_private
{
class DebugMonitorMessage;
class DebugMonitorMessageResult;
class DebugOneProcessThread;
class LaunchProcessMessage;
class LaunchProcessMessageResult;

class SlaveMessageProcessExited;
class SlaveMessageRipEvent;

//----------------------------------------------------------------------
// DebugDriverThread
//
// Runs a background thread that pumps a queue from the application to tell the
// debugger to do different things like launching processes, attaching to
// processes, etc.
//----------------------------------------------------------------------
class DebugDriverThread
{
friend class DebugOneProcessThread;

public:
virtual ~DebugDriverThread();

static void Initialize();
static void Teardown();
static DebugDriverThread &GetInstance();

void PostDebugMessage(const DebugMonitorMessage *message);

private:
DebugDriverThread();

void Shutdown();

bool ProcessMonitorMessages();
const DebugMonitorMessageResult *HandleMonitorMessage(const DebugMonitorMessage *message);
const LaunchProcessMessageResult *HandleMonitorMessage(const LaunchProcessMessage *launch_message);

// Slave message handlers. These are invoked by the
void HandleSlaveEvent(const SlaveMessageProcessExited &message);
void HandleSlaveEvent(const SlaveMessageRipEvent &message);

static DebugDriverThread *m_instance;

std::map<lldb::pid_t, std::shared_ptr<DebugOneProcessThread>> m_debugged_processes;

HANDLE m_monitor_event;
HANDLE m_shutdown_event;
HANDLE m_monitor_pipe_read;
HANDLE m_monitor_pipe_write;
lldb_private::HostThread m_monitor_thread;

static lldb::thread_result_t MonitorThread(void *data);
};
}

#endif

0 comments on commit 8f21174

Please sign in to comment.