diff options
author | Adrian McCarthy <amccarth@google.com> | 2015-08-24 16:00:51 +0000 |
---|---|---|
committer | Adrian McCarthy <amccarth@google.com> | 2015-08-24 16:00:51 +0000 |
commit | 27785dd5300825e60d5ff7edf5a2bada85004b58 (patch) | |
tree | c5a03e2890b6331638b2f2a55861bce8dd57e465 /lldb/source/Plugins/Process/Windows/Live/DebuggerThread.cpp | |
parent | 550dfe79ca8c3a2ec5fbf2e34ad40fe66eb7c09f (diff) | |
download | bcm5719-llvm-27785dd5300825e60d5ff7edf5a2bada85004b58.tar.gz bcm5719-llvm-27785dd5300825e60d5ff7edf5a2bada85004b58.zip |
Reorg code to allow Windows Process Plugins to share some common code.
Differential Revision: http://reviews.llvm.org/D12252
llvm-svn: 245850
Diffstat (limited to 'lldb/source/Plugins/Process/Windows/Live/DebuggerThread.cpp')
-rw-r--r-- | lldb/source/Plugins/Process/Windows/Live/DebuggerThread.cpp | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Windows/Live/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Live/DebuggerThread.cpp new file mode 100644 index 00000000000..e65310d1c8d --- /dev/null +++ b/lldb/source/Plugins/Process/Windows/Live/DebuggerThread.cpp @@ -0,0 +1,533 @@ +//===-- DebuggerThread.DebuggerThread --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "IDebugDelegate.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/HostProcessWindows.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/Process.h" + +#include "Plugins/Process/Windows/Live/ProcessWindowsLog.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +struct DebugLaunchContext +{ + DebugLaunchContext(DebuggerThread *thread, const ProcessLaunchInfo &launch_info) + : m_thread(thread) + , m_launch_info(launch_info) + { + } + DebuggerThread *m_thread; + ProcessLaunchInfo m_launch_info; +}; + +struct DebugAttachContext +{ + DebugAttachContext(DebuggerThread *thread, lldb::pid_t pid, const ProcessAttachInfo &attach_info) + : m_thread(thread) + , m_pid(pid) + , m_attach_info(attach_info) + { + } + DebuggerThread *m_thread; + lldb::pid_t m_pid; + ProcessAttachInfo m_attach_info; +}; +} + +DebuggerThread::DebuggerThread(DebugDelegateSP debug_delegate) + : m_debug_delegate(debug_delegate) + , m_image_file(nullptr) + , m_debugging_ended_event(nullptr) + , m_pid_to_detach(0) + , m_detached(false) +{ + m_debugging_ended_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); +} + +DebuggerThread::~DebuggerThread() +{ + ::CloseHandle(m_debugging_ended_event); +} + +Error +DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info) +{ + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DebuggerThread::DebugLaunch launching '%s'", launch_info.GetExecutableFile().GetPath().c_str()); + + Error error; + DebugLaunchContext *context = new DebugLaunchContext(this, launch_info); + HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", + DebuggerThreadLaunchRoutine, context, &error)); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DebugLaunch couldn't launch debugger thread. %s", error.AsCString()); + } + + return error; +} + +Error +DebuggerThread::DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread::DebugAttach attaching to '%u'", (DWORD)pid); + + Error error; + DebugAttachContext *context = new DebugAttachContext(this, pid, attach_info); + HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", + DebuggerThreadAttachRoutine, context, &error)); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DebugAttach couldn't attach to process '%u'. %s", (DWORD)pid, + error.AsCString()); + } + + return error; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(void *data) +{ + DebugLaunchContext *context = static_cast<DebugLaunchContext *>(data); + lldb::thread_result_t result = context->m_thread->DebuggerThreadLaunchRoutine(context->m_launch_info); + delete context; + return result; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadAttachRoutine(void *data) +{ + DebugAttachContext *context = static_cast<DebugAttachContext *>(data); + lldb::thread_result_t result = + context->m_thread->DebuggerThreadAttachRoutine(context->m_pid, context->m_attach_info); + delete context; + return result; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info) +{ + // Grab a shared_ptr reference to this so that we know it won't get deleted until after the + // thread routine has exited. + std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to launch '%s' on background thread.", + launch_info.GetExecutableFile().GetPath().c_str()); + + Error error; + ProcessLauncherWindows launcher; + HostProcess process(launcher.LaunchProcess(launch_info, error)); + // If we couldn't create the process, notify waiters immediately. Otherwise enter the debug + // loop and wait until we get the create process debug notification. Note that if the process + // was created successfully, we can throw away the process handle we got from CreateProcess + // because Windows will give us another (potentially more useful?) handle when it sends us the + // CREATE_PROCESS_DEBUG_EVENT. + if (error.Success()) + DebugLoop(); + else + m_debug_delegate->OnDebuggerError(error, 0); + + return 0; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + // Grab a shared_ptr reference to this so that we know it won't get deleted until after the + // thread routine has exited. + std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to attach to process '%u' on background thread.", + (DWORD)pid); + + if (!DebugActiveProcess((DWORD)pid)) + { + Error error(::GetLastError(), eErrorTypeWin32); + m_debug_delegate->OnDebuggerError(error, 0); + return 0; + } + + // The attach was successful, enter the debug loop. From here on out, this is no different than + // a create process operation, so all the same comments in DebugLaunch should apply from this + // point out. + DebugLoop(); + + return 0; +} + +Error +DebuggerThread::StopDebugging(bool terminate) +{ + Error error; + + lldb::pid_t pid = m_process.GetProcessId(); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging('%s') called (inferior=%I64u).", + (terminate ? "true" : "false"), pid); + + // Make a copy of the process, since the termination sequence will reset + // DebuggerThread's internal copy and it needs to remain open for the Wait operation. + HostProcess process_copy = m_process; + lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle(); + + if (terminate) + { + // Initiate the termination before continuing the exception, so that the next debug + // event we get is the exit process event, and not some other event. + BOOL terminate_suceeded = TerminateProcess(handle, 0); + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging called TerminateProcess(0x%p, 0) (inferior=%I64u), success='%s'", + handle, pid, (terminate_suceeded ? "true" : "false")); + } + + // If we're stuck waiting for an exception to continue (e.g. the user is at a breakpoint + // messing around in the debugger), continue it now. But only AFTER calling TerminateProcess + // to make sure that the very next call to WaitForDebugEvent is an exit process event. + if (m_active_exception.get()) + { + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, + "StopDebugging masking active exception"); + + ContinueAsyncException(ExceptionResult::MaskException); + } + + if (!terminate) + { + // Indicate that we want to detach. + m_pid_to_detach = GetProcess().GetProcessId(); + + // Force a fresh break so that the detach can happen from the debugger thread. + if (!::DebugBreakProcess(GetProcess().GetNativeProcess().GetSystemHandle())) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + } + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid); + + DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000); + if (wait_result != WAIT_OBJECT_0) + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging WaitForSingleObject(0x%p, 5000) returned %u", + m_debugging_ended_event, wait_result); + } + else + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging detach from process %u completed successfully.", pid); + } + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging encountered an error while trying to stop process %u. %s", + pid, error.AsCString()); + } + return error; +} + +void +DebuggerThread::ContinueAsyncException(ExceptionResult result) +{ + if (!m_active_exception.get()) + return; + + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, + "ContinueAsyncException called for inferior process %I64u, broadcasting.", + m_process.GetProcessId()); + + m_active_exception.reset(); + m_exception_pred.SetValue(result, eBroadcastAlways); +} + +void +DebuggerThread::FreeProcessHandles() +{ + m_process = HostProcess(); + m_main_thread = HostThread(); + if (m_image_file) + { + ::CloseHandle(m_image_file); + m_image_file = nullptr; + } +} + +void +DebuggerThread::DebugLoop() +{ + DEBUG_EVENT dbe = {0}; + bool should_debug = true; + WINLOG_IFALL(WINDOWS_LOG_EVENT, "Entering WaitForDebugEvent loop"); + while (should_debug) + { + WINLOGD_IFALL(WINDOWS_LOG_EVENT, "Calling WaitForDebugEvent"); + BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE); + if (wait_result) + { + DWORD continue_status = DBG_CONTINUE; + switch (dbe.dwDebugEventCode) + { + case EXCEPTION_DEBUG_EVENT: + { + ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId); + + if (status == ExceptionResult::MaskException) + continue_status = DBG_CONTINUE; + else if (status == ExceptionResult::SendToApplication) + continue_status = DBG_EXCEPTION_NOT_HANDLED; + break; + } + case CREATE_THREAD_DEBUG_EVENT: + continue_status = HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId); + break; + case CREATE_PROCESS_DEBUG_EVENT: + continue_status = HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId); + break; + case EXIT_THREAD_DEBUG_EVENT: + continue_status = HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId); + break; + case EXIT_PROCESS_DEBUG_EVENT: + continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId); + should_debug = false; + break; + case LOAD_DLL_DEBUG_EVENT: + continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId); + break; + case UNLOAD_DLL_DEBUG_EVENT: + continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId); + break; + case OUTPUT_DEBUG_STRING_EVENT: + continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId); + break; + case RIP_EVENT: + continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId); + if (dbe.u.RipInfo.dwType == SLE_ERROR) + should_debug = false; + break; + } + + WINLOGD_IFALL(WINDOWS_LOG_EVENT, "DebugLoop calling ContinueDebugEvent(%u, %u, %u) on thread %u.", + dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId()); + + ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status); + + if (m_detached) + { + should_debug = false; + } + } + else + { + WINERR_IFALL(WINDOWS_LOG_EVENT, + "DebugLoop returned FALSE from WaitForDebugEvent. Error = %u", + ::GetCurrentThreadId(), ::GetLastError()); + + should_debug = false; + } + } + FreeProcessHandles(); + + WINLOG_IFALL(WINDOWS_LOG_EVENT, "WaitForDebugEvent loop completed, exiting."); + SetEvent(m_debugging_ended_event); +} + +ExceptionResult +DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id) +{ + bool first_chance = (info.dwFirstChance != 0); + + m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord, thread_id)); + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION, + "HandleExceptionEvent encountered %s chance exception 0x%x on thread 0x%x", + first_chance ? "first" : "second", info.ExceptionRecord.ExceptionCode, thread_id); + + if (m_pid_to_detach != 0 && m_active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT) { + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_PROCESS, + "Breakpoint exception is cue to detach from process 0x%x", + m_pid_to_detach); + if (::DebugActiveProcessStop(m_pid_to_detach)) { + m_detached = true; + return ExceptionResult::MaskException; + } else { + WINLOG_IFANY(WINDOWS_LOG_PROCESS, "Failed to detach, treating as a regular breakpoint"); + } + } + + ExceptionResult result = m_debug_delegate->OnDebugException(first_chance, + *m_active_exception); + m_exception_pred.SetValue(result, eBroadcastNever); + + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, + "DebuggerThread::HandleExceptionEvent waiting for ExceptionPred != BreakInDebugger"); + + m_exception_pred.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, result); + + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, + "DebuggerThread::HandleExceptionEvent got ExceptionPred = %u", + m_exception_pred.GetValue()); + + return result; +} + +DWORD +DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleCreateThreadEvent Thread 0x%x spawned in process %I64u", + thread_id, m_process.GetProcessId()); + HostThread thread(info.hThread); + thread.GetNativeThread().SetOwnsHandle(false); + m_debug_delegate->OnCreateThread(thread); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ + uint32_t process_id = ::GetProcessId(info.hProcess); + + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_PROCESS, "HandleCreateProcessEvent process %u spawned", process_id); + + std::string thread_name; + llvm::raw_string_ostream name_stream(thread_name); + name_stream << "lldb.plugin.process-windows.slave[" << process_id << "]"; + name_stream.flush(); + ThisThread::SetName(thread_name.c_str()); + + // info.hProcess and info.hThread are closed automatically by Windows when + // EXIT_PROCESS_DEBUG_EVENT is received. + m_process = HostProcess(info.hProcess); + ((HostProcessWindows &)m_process.GetNativeProcess()).SetOwnsHandle(false); + m_main_thread = HostThread(info.hThread); + m_main_thread.GetNativeThread().SetOwnsHandle(false); + m_image_file = info.hFile; + + lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfImage); + m_debug_delegate->OnDebuggerConnected(load_addr); + + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleExitThreadEvent Thread %u exited with code %u in process %I64u", + thread_id, info.dwExitCode, m_process.GetProcessId()); + m_debug_delegate->OnExitThread(thread_id, info.dwExitCode); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleExitProcessEvent process %I64u exited with code %u", + m_process.GetProcessId(), info.dwExitCode); + + m_debug_delegate->OnExitProcess(info.dwExitCode); + + FreeProcessHandles(); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread_id) +{ + if (info.hFile == nullptr) + { + // Not sure what this is, so just ignore it. + WINWARN_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent has a NULL file handle, returning...", + m_process.GetProcessId()); + return DBG_CONTINUE; + } + + std::vector<char> buffer(1); + DWORD required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS); + if (required_size > 0) + { + buffer.resize(required_size + 1); + required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], required_size + 1, VOLUME_NAME_DOS); + llvm::StringRef path_str(&buffer[0]); + const char *path = path_str.data(); + if (path_str.startswith("\\\\?\\")) + path += 4; + + FileSpec file_spec(path, false); + ModuleSpec module_spec(file_spec); + lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll); + + WINLOG_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent DLL '%s' loaded at address 0x%p...", + m_process.GetProcessId(), path, info.lpBaseOfDll); + + m_debug_delegate->OnLoadDll(module_spec, load_addr); + } + else + { + WINERR_IFALL(WINDOWS_LOG_EVENT, + "Inferior %I64u - HandleLoadDllEvent Error %u occurred calling GetFinalPathNameByHandle", + m_process.GetProcessId(), ::GetLastError()); + } + // Windows does not automatically close info.hFile, so we need to do it. + ::CloseHandle(info.hFile); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFALL(WINDOWS_LOG_EVENT, + "HandleUnloadDllEvent process %I64u unloading DLL at addr 0x%p.", + m_process.GetProcessId(), info.lpBaseOfDll); + + m_debug_delegate->OnUnloadDll(reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll)); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD thread_id) +{ + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) +{ + WINERR_IFALL(WINDOWS_LOG_EVENT, + "HandleRipEvent encountered error %u (type=%u) in process %I64u thread %u", + info.dwError, info.dwType, m_process.GetProcessId(), thread_id); + + Error error(info.dwError, eErrorTypeWin32); + m_debug_delegate->OnDebuggerError(error, info.dwType); + + return DBG_CONTINUE; +} |