[Lldb-commits] [lldb] r240157 - Load executable module when attaching to process; implement detach from process.
Adrian McCarthy
amccarth at google.com
Fri Jun 19 11:26:53 PDT 2015
Author: amccarth
Date: Fri Jun 19 13:26:53 2015
New Revision: 240157
URL: http://llvm.org/viewvc/llvm-project?rev=240157&view=rev
Log:
Load executable module when attaching to process; implement detach from process.
Modified:
lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp
lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h
lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp
lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h
Modified: lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp?rev=240157&r1=240156&r2=240157&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp Fri Jun 19 13:26:53 2015
@@ -63,6 +63,8 @@ DebuggerThread::DebuggerThread(DebugDele
: 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::DebuggerThreadLaunchRout
else
m_debug_delegate->OnDebuggerError(error, 0);
- SetEvent(m_debugging_ended_event);
return 0;
}
@@ -193,55 +194,62 @@ DebuggerThread::StopDebugging(bool termi
"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())
- {
- WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION,
- "StopDebugging masking active exception");
+ // 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);
- }
+ ContinueAsyncException(ExceptionResult::MaskException);
+ }
- WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid);
+ if (!terminate)
+ {
+ // Indicate that we want to detach.
+ m_pid_to_detach = GetProcess().GetProcessId();
- 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
+ // Force a fresh break so that the detach can happen from the debugger thread.
+ if (!::DebugBreakProcess(GetProcess().GetNativeProcess().GetSystemHandle()))
{
- WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging detach from process %u completed successfully.", pid);
+ 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
{
- 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(con
"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);
Modified: lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h?rev=240157&r1=240156&r2=240157&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h (original)
+++ lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h Fri Jun 19 13:26:53 2015
@@ -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::enabl
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);
Modified: lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp?rev=240157&r1=240156&r2=240157&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp Fri Jun 19 13:26:53 2015
@@ -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,
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
{
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 Er
error.GetError(), error.AsCString());
return;
}
-}
+}
\ No newline at end of file
Modified: lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h?rev=240157&r1=240156&r2=240157&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h (original)
+++ lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h Fri Jun 19 13:26:53 2015
@@ -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;
More information about the lldb-commits
mailing list