Load executable module when attaching to process; implement detach from process.

llvm-svn: 240157
This commit is contained in:
Adrian McCarthy 2015-06-19 18:26:53 +00:00
parent 2a20bd1a94
commit a59a7211f0
4 changed files with 164 additions and 40 deletions

View File

@ -63,6 +63,8 @@ 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);
}
@ -153,7 +155,6 @@ DebuggerThread::DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info
else
m_debug_delegate->OnDebuggerError(error, 0);
SetEvent(m_debugging_ended_event);
return 0;
}
@ -193,55 +194,62 @@ DebuggerThread::StopDebugging(bool terminate)
"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)
{
// 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();
// 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())
// 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()))
{
WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION,
"StopDebugging masking active exception");
ContinueAsyncException(ExceptionResult::MaskException);
error.SetError(::GetLastError(), eErrorTypeWin32);
}
}
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid);
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);
}
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
{
error.SetErrorString("Detach not yet supported on Windows.");
// TODO: Implement detach.
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",
"StopDebugging encountered an error while trying to stop process %u. %s",
pid, error.AsCString());
}
return error;
@ -331,6 +339,11 @@ DebuggerThread::DebugLoop()
dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId());
::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
if (m_detached)
{
should_debug = false;
}
}
else
{
@ -344,6 +357,7 @@ DebuggerThread::DebugLoop()
FreeProcessHandles();
WINLOG_IFALL(WINDOWS_LOG_EVENT, "WaitForDebugEvent loop completed, exiting.");
SetEvent(m_debugging_ended_event);
}
ExceptionResult
@ -356,6 +370,18 @@ DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thr
"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);

View File

@ -10,6 +10,7 @@
#ifndef liblldb_Plugins_Process_Windows_DebuggerThread_H_
#define liblldb_Plugins_Process_Windows_DebuggerThread_H_
#include <atomic>
#include <memory>
#include "ForwardDecl.h"
@ -84,6 +85,9 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread>
HANDLE m_debugging_ended_event; // An event which gets signalled by the debugger thread when it
// exits the debugger loop and is detached from the inferior.
std::atomic<DWORD> m_pid_to_detach; // Signals the loop to detach from the process (specified by pid).
bool m_detached; // Indicates we've detached from the inferior process and the debug loop can exit.
static lldb::thread_result_t DebuggerThreadLaunchRoutine(void *data);
lldb::thread_result_t DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info);
static lldb::thread_result_t DebuggerThreadAttachRoutine(void *data);

View File

@ -9,6 +9,7 @@
// Windows includes
#include "lldb/Host/windows/windows.h"
#include <psapi.h>
// C++ Includes
#include <list>
@ -54,6 +55,40 @@ using namespace lldb_private;
#define BOOL_STR(b) ((b) ? "true" : "false")
namespace
{
std::string
GetProcessExecutableName(HANDLE process_handle)
{
std::vector<char> file_name;
DWORD file_name_size = MAX_PATH; // first guess, not an absolute limit
DWORD copied = 0;
do
{
file_name_size *= 2;
file_name.resize(file_name_size);
copied = ::GetModuleFileNameEx(process_handle, NULL, file_name.data(), file_name_size);
} while (copied >= file_name_size);
file_name.resize(copied);
return std::string(file_name.begin(), file_name.end());
}
std::string
GetProcessExecutableName(DWORD pid)
{
std::string file_name;
HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (process_handle != NULL)
{
file_name = GetProcessExecutableName(process_handle);
::CloseHandle(process_handle);
}
return file_name;
}
} // anonymous namespace
namespace lldb_private
{
@ -417,7 +452,49 @@ ProcessWindows::GetPluginVersion()
Error
ProcessWindows::DoDetach(bool keep_stopped)
{
DebuggerThreadSP debugger_thread;
StateType private_state;
{
// Acquire the lock only long enough to get the DebuggerThread.
// StopDebugging() will trigger a call back into ProcessWindows which
// will also acquire the lock. Thus we have to release the lock before
// calling StopDebugging().
llvm::sys::ScopedLock lock(m_mutex);
private_state = GetPrivateState();
if (!m_session_data)
{
WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called while state = %u, but there is no active session.",
private_state);
return Error();
}
debugger_thread = m_session_data->m_debugger;
}
Error error;
if (private_state != eStateExited && private_state != eStateDetached)
{
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called for process %I64u while state = %u. Detaching...",
debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state);
error = debugger_thread->StopDebugging(false);
if (error.Success())
{
SetPrivateState(eStateDetached);
}
// By the time StopDebugging returns, there is no more debugger thread, so
// we can be assured that no other thread will race for the session data.
m_session_data.reset();
}
else
{
WINERR_IFALL(WINDOWS_LOG_PROCESS,
"DoDetach called for process %I64u while state = %u, but cannot destroy in this state.",
debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state);
}
return error;
}
@ -451,9 +528,8 @@ ProcessWindows::DoDestroy()
debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state);
error = debugger_thread->StopDebugging(true);
// By the time StopDebugging returns, there is no more debugger thread, so we can be assured that no other
// thread
// will race for the session data. So it's safe to reset it without holding a lock.
// By the time StopDebugging returns, there is no more debugger thread, so
// we can be assured that no other thread will race for the session data.
m_session_data.reset();
}
else
@ -718,7 +794,8 @@ ProcessWindows::CanDebug(Target &target, bool plugin_specified_by_name)
ModuleSP exe_module_sp(target.GetExecutableModule());
if (exe_module_sp.get())
return exe_module_sp->GetFileSpec().Exists();
return false;
// However, if there is no executable module, we return true since we might be preparing to attach.
return true;
}
void
@ -741,10 +818,32 @@ ProcessWindows::OnDebuggerConnected(lldb::addr_t image_base)
{
DebuggerThreadSP debugger = m_session_data->m_debugger;
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger established connected to process %I64u. Image base = 0x%I64x",
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger connected to process %I64u. Image base = 0x%I64x",
debugger->GetProcess().GetProcessId(), image_base);
ModuleSP module = GetTarget().GetExecutableModule();
if (!module)
{
// During attach, we won't have the executable module, so find it now.
const DWORD pid = debugger->GetProcess().GetProcessId();
const std::string file_name = GetProcessExecutableName(pid);
if (file_name.empty())
{
return;
}
FileSpec executable_file(file_name, true);
ModuleSpec module_spec(executable_file);
Error error;
module = GetTarget().GetSharedModule(module_spec, &error);
if (!module)
{
return;
}
GetTarget().SetExecutableModule(module, false);
}
bool load_addr_changed;
module->SetLoadAddress(GetTarget(), image_base, false, load_addr_changed);
@ -911,4 +1010,4 @@ ProcessWindows::OnDebuggerError(const Error &error, uint32_t type)
error.GetError(), error.AsCString());
return;
}
}
}

View File

@ -92,11 +92,6 @@ public:
bool CanDebug(lldb_private::Target &target, bool plugin_specified_by_name) override;
bool
DetachRequiresHalt() override
{
return true;
}
bool
DestroyRequiresHalt() override
{
return false;