Index: lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.h =================================================================== --- lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.h +++ lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.h @@ -122,10 +122,12 @@ LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) override; lldb::ProcessSP - Attach(lldb_private::ProcessAttachInfo &attach_info, - lldb_private::Debugger &debugger, - lldb_private::Target *target, - lldb_private::Error &error) override; + DebugProcess(lldb_private::ProcessLaunchInfo &launch_info, lldb_private::Debugger &debugger, + lldb_private::Target *target, lldb_private::Error &error) override; + + lldb::ProcessSP + Attach(lldb_private::ProcessAttachInfo &attach_info, lldb_private::Debugger &debugger, + lldb_private::Target *target, lldb_private::Error &error) override; lldb_private::Error GetFileWithUUID(const lldb_private::FileSpec &platform_file, @@ -145,12 +147,7 @@ void GetStatus(lldb_private::Stream &strm) override; - // Local debugging not yet supported - bool - CanDebugProcess(void) override - { - return false; - } + bool CanDebugProcess() override; // FIXME not sure what the _sigtramp equivalent would be on this platform void Index: lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.cpp =================================================================== --- lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.cpp +++ lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.cpp @@ -526,51 +526,88 @@ return error; } +ProcessSP +PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, Error &error) +{ + // Windows has special considerations that must be followed when launching or attaching to a process. The + // key requirement is that when launching or attaching to a process, you must do it from the same the thread + // that will go into a permanent loop which will then receive debug events from the process. In particular, + // this means we can't use any of LLDB's generic mechanisms to do it for us, because it doesn't have the + // special knowledge required for setting up the background thread or passing the right flags. + // + // Another problem is that that LLDB's standard model for debugging a process is to first launch it, have + // it stop at the entry point, and then attach to it. In Windows this doesn't quite work, you have to + // specify as an argument to CreateProcess() that you're going to debug the process. So we override DebugProcess + // here to handle this. Launch operations go directly to the process plugin, and attach operations almost go + // directly to the process plugin (but we hijack the events first). In essence, we encapsulate all the logic + // of Launching and Attaching in the process plugin, and PlatformWindows::DebugProcess is just a pass-through + // to get to the process plugin. + + if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + { + // This is a process attach. Don't need to launch anything. + ProcessAttachInfo attach_info(launch_info); + return Attach(attach_info, debugger, target, error); + } + else + { + ProcessSP process_sp = target->CreateProcess(launch_info.GetListenerForProcess(debugger), + launch_info.GetProcessPluginName(), + nullptr); + + // We need to launch and attach to the process. + launch_info.GetFlags().Set(eLaunchFlagDebug); + if (process_sp) + error = process_sp->Launch(launch_info); + + return process_sp; + } +} + lldb::ProcessSP PlatformWindows::Attach(ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, Error &error) { + error.Clear(); lldb::ProcessSP process_sp; - if (IsHost()) - { - if (target == NULL) - { - TargetSP new_target_sp; - FileSpec emptyFileSpec; - ArchSpec emptyArchSpec; - - error = debugger.GetTargetList().CreateTarget (debugger, - NULL, - NULL, - false, - NULL, - new_target_sp); - target = new_target_sp.get(); - } - else - error.Clear(); - - if (target && error.Success()) - { - debugger.GetTargetList().SetSelectedTarget(target); - // The Windows platform always currently uses the GDB remote debugger plug-in - // so even when debugging locally we are debugging remotely! - // Just like the darwin plugin. - process_sp = target->CreateProcess (attach_info.GetListenerForProcess(debugger), "gdb-remote", NULL); - - if (process_sp) - error = process_sp->Attach (attach_info); - } - } - else + if (!IsHost()) { if (m_remote_platform_sp) process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error); else error.SetErrorString ("the platform is not currently connected"); + return process_sp; } + + if (target == NULL) + { + TargetSP new_target_sp; + FileSpec emptyFileSpec; + ArchSpec emptyArchSpec; + + error = debugger.GetTargetList().CreateTarget (debugger, + NULL, + NULL, + false, + NULL, + new_target_sp); + target = new_target_sp.get(); + } + + if (!target || error.Fail()) + return process_sp; + + debugger.GetTargetList().SetSelectedTarget(target); + + const char *plugin_name = attach_info.GetProcessPluginName(); + process_sp = target->CreateProcess(attach_info.GetListenerForProcess(debugger), plugin_name, NULL); + + process_sp->HijackProcessEvents(attach_info.GetHijackListener().get()); + if (process_sp) + error = process_sp->Attach (attach_info); + return process_sp; } @@ -687,3 +724,9 @@ << " Build: " << update << '\n'; #endif } + +bool +PlatformWindows::CanDebugProcess() +{ + return true; +} Index: lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h +++ lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h @@ -34,6 +34,7 @@ virtual ~DebuggerThread(); Error DebugLaunch(const ProcessLaunchInfo &launch_info); + Error DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info); HostProcess GetProcess() const @@ -80,8 +81,10 @@ // is finished processing and the debug loop can be // continued. - static lldb::thread_result_t DebuggerThreadRoutine(void *data); - lldb::thread_result_t DebuggerThreadRoutine(const ProcessLaunchInfo &launch_info); + 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); + lldb::thread_result_t DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &launch_info); }; } Index: lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp +++ lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp @@ -22,6 +22,7 @@ #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/ProcessWindowsLog.h" @@ -43,6 +44,19 @@ 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) @@ -64,7 +78,7 @@ Error error; DebugLaunchContext *context = new DebugLaunchContext(this, launch_info); HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", - DebuggerThreadRoutine, context, &error)); + DebuggerThreadLaunchRoutine, context, &error)); if (!error.Success()) { @@ -75,25 +89,53 @@ 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::DebuggerThreadRoutine(void *data) +DebuggerThread::DebuggerThreadLaunchRoutine(void *data) { DebugLaunchContext *context = static_cast(data); - lldb::thread_result_t result = context->m_thread->DebuggerThreadRoutine(context->m_launch_info); + 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(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::DebuggerThreadRoutine(const ProcessLaunchInfo &launch_info) +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 this_ref(shared_from_this()); - WINLOG_IFALL(WINDOWS_LOG_PROCESS, - "DebuggerThread preparing to launch '%s'.", - launch_info.GetExecutableFile().GetPath().c_str()); + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to launch '%s' on background thread.", + launch_info.GetExecutableFile().GetPath().c_str()); Error error; ProcessLauncherWindows launcher; @@ -111,6 +153,31 @@ 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 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) { Index: lldb/trunk/source/Plugins/Process/Windows/DynamicLoaderWindows.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/DynamicLoaderWindows.cpp +++ lldb/trunk/source/Plugins/Process/Windows/DynamicLoaderWindows.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "DynamicLoaderWindows.h" +#include "ProcessWindowsLog.h" #include "lldb/Core/PluginManager.h" #include "lldb/Target/Process.h" @@ -65,6 +66,7 @@ if (should_create) return new DynamicLoaderWindows (process); + return nullptr; } Index: lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h +++ lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h @@ -30,6 +30,7 @@ namespace lldb_private { +class HostProcess; class ProcessWindowsData; } @@ -77,11 +78,14 @@ lldb_private::Error DoDetach(bool keep_stopped) override; lldb_private::Error DoLaunch(lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; + lldb_private::Error DoAttachToProcessWithID(lldb::pid_t pid, + const lldb_private::ProcessAttachInfo &attach_info) override; lldb_private::Error DoResume() override; lldb_private::Error DoDestroy() override; lldb_private::Error DoHalt(bool &caused_stop) override; void DidLaunch() override; + void DidAttach(lldb_private::ArchSpec &arch_spec) override; void RefreshStateAfterStop() override; lldb::addr_t GetImageInfoAddress() override; @@ -116,6 +120,9 @@ void OnDebuggerError(const lldb_private::Error &error, uint32_t type) override; private: + lldb_private::Error WaitForDebuggerConnection(lldb_private::DebuggerThreadSP debugger, + lldb_private::HostProcess &process); + llvm::sys::Mutex m_mutex; // Data for the active debugging session. Index: lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp +++ lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp @@ -62,8 +62,8 @@ class ProcessWindowsData { public: - ProcessWindowsData(const ProcessLaunchInfo &launch_info) - : m_launch_info(launch_info) + ProcessWindowsData(bool stop_at_entry) + : m_stop_at_entry(stop_at_entry) , m_initial_stop_event(nullptr) , m_initial_stop_received(false) { @@ -72,11 +72,11 @@ ~ProcessWindowsData() { ::CloseHandle(m_initial_stop_event); } - ProcessLaunchInfo m_launch_info; lldb_private::Error m_launch_error; lldb_private::DebuggerThreadSP m_debugger; StopInfoSP m_pending_stop_info; HANDLE m_initial_stop_event; + bool m_stop_at_entry; bool m_initial_stop_received; std::map m_new_threads; std::set m_exited_threads; @@ -257,7 +257,8 @@ return result; } - m_session_data.reset(new ProcessWindowsData(launch_info)); + bool stop_at_entry = launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); + m_session_data.reset(new ProcessWindowsData(stop_at_entry)); SetPrivateState(eStateLaunching); DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); @@ -266,42 +267,92 @@ // Kick off the DebugLaunch asynchronously and wait for it to complete. result = debugger->DebugLaunch(launch_info); + if (result.Fail()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", + launch_info.GetExecutableFile().GetPath().c_str(), result.AsCString()); + return result; + } HostProcess process; - if (result.Success()) + Error error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) { - WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch started asynchronous launch of '%s'. Waiting for initial stop.", - launch_info.GetExecutableFile().GetPath().c_str()); - - // Block this function until we receive the initial stop from the process. - if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == WAIT_OBJECT_0) - { - process = debugger->GetProcess(); - if (m_session_data->m_launch_error.Fail()) - result = m_session_data->m_launch_error; - } - else - result.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", + launch_info.GetExecutableFile().GetPath().c_str(), error.AsCString()); + return error; } - if (result.Success()) + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch successfully launched '%s'", + launch_info.GetExecutableFile().GetPath().c_str()); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state + // should already be set to eStateStopped as a result of hitting the initial breakpoint. If + // it was not set, the breakpoint should have already been resumed from and the private state + // should already be eStateRunning. + launch_info.SetProcessID(process.GetProcessId()); + SetID(process.GetProcessId()); + + return result; +} + +Error +ProcessWindows::DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + m_session_data.reset(new ProcessWindowsData(!attach_info.GetContinueOnceAttached())); + + DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); + DebuggerThreadSP debugger(new DebuggerThread(delegate)); + + m_session_data->m_debugger = debugger; + + DWORD process_id = static_cast(pid); + Error error = debugger->DebugAttach(process_id, attach_info); + if (error.Fail()) { - WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch successfully launched '%s'", - launch_info.GetExecutableFile().GetPath().c_str()); + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DoAttachToProcessWithID encountered an error occurred initiating the asynchronous attach. %s", + error.AsCString()); + return error; } - else + + HostProcess process; + error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) { - WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", - launch_info.GetExecutableFile().GetPath().c_str(), result.AsCString()); - return result; + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DoAttachToProcessWithID encountered an error waiting for the debugger to connect. %s", + error.AsCString()); + return error; } - // We've hit the initial stop. The private state should already be set to stopped as a result - // of encountering the breakpoint exception in ProcessWindows::OnDebugException. - launch_info.SetProcessID(process.GetProcessId()); + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoAttachToProcessWithID successfully attached to process with pid=%u", + process_id); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state + // should already be set to eStateStopped as a result of hitting the initial breakpoint. If + // it was not set, the breakpoint should have already been resumed from and the private state + // should already be eStateRunning. SetID(process.GetProcessId()); + return error; +} - return result; +Error +ProcessWindows::WaitForDebuggerConnection(DebuggerThreadSP debugger, HostProcess &process) +{ + Error result; + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection Waiting for loader breakpoint."); + + // Block this function until we receive the initial stop from the process. + if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == WAIT_OBJECT_0) + { + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection hit loader breakpoint, returning."); + + process = debugger->GetProcess(); + return m_session_data->m_launch_error; + } + else + return Error(::GetLastError(), eErrorTypeWin32); } Error @@ -521,11 +572,16 @@ void ProcessWindows::DidLaunch() { + DidAttach(ArchSpec()); +} + +void +ProcessWindows::DidAttach(ArchSpec &arch_spec) +{ llvm::sys::ScopedLock lock(m_mutex); // The initial stop won't broadcast the state change event, so account for that here. - if (m_session_data && GetPrivateState() == eStateStopped && - m_session_data->m_launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) + if (m_session_data && GetPrivateState() == eStateStopped && m_session_data->m_stop_at_entry) RefreshStateAfterStop(); } @@ -557,11 +613,13 @@ ProcessWindows::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) { llvm::sys::ScopedLock lock(m_mutex); + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory attempting to write %u bytes into address 0x%I64x", size, vm_addr); if (!m_session_data) + { + WINERR_IFANY(WINDOWS_LOG_MEMORY, "DoWriteMemory cannot write, there is no active debugger connection."); return 0; - - WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory attempting to write %u bytes into address 0x%I64x", size, vm_addr); + } HostProcess process = m_session_data->m_debugger->GetProcess(); void *addr = reinterpret_cast(vm_addr); @@ -672,20 +730,18 @@ WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger established connected to process %I64u. Image base = 0x%I64x", debugger->GetProcess().GetProcessId(), image_base); - // Either we successfully attached to an existing process, or we successfully launched a new - // process under the debugger. ModuleSP module = GetTarget().GetExecutableModule(); bool load_addr_changed; module->SetLoadAddress(GetTarget(), image_base, false, load_addr_changed); - // Notify the target that the executable module has loaded. This will cause any pending - // breakpoints to be resolved to explicit brekapoint sites. ModuleList loaded_modules; loaded_modules.Append(module); GetTarget().ModulesDidLoad(loaded_modules); + // Add the main executable module to the list of pending module loads. We can't call + // GetTarget().ModulesDidLoad() here because we still haven't returned from DoLaunch() / DoAttach() yet + // so the target may not have set the process instance to `this` yet. llvm::sys::ScopedLock lock(m_mutex); - const HostThreadWindows &wmain_thread = debugger->GetMainThread().GetNativeThread(); m_session_data->m_new_threads[wmain_thread.GetThreadId()] = debugger->GetMainThread(); } @@ -723,9 +779,18 @@ if (!m_session_data->m_initial_stop_received) { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, + "Hit loader breakpoint at address 0x%I64x, setting initial stop event.", + record.GetExceptionAddress()); m_session_data->m_initial_stop_received = true; ::SetEvent(m_session_data->m_initial_stop_event); } + else + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, + "Hit non-loader breakpoint at address 0x%I64x.", + record.GetExceptionAddress()); + } SetPrivateState(eStateStopped); break; case EXCEPTION_SINGLE_STEP: Index: lldb/trunk/source/Target/Process.cpp =================================================================== --- lldb/trunk/source/Target/Process.cpp +++ lldb/trunk/source/Target/Process.cpp @@ -3336,6 +3336,9 @@ switch (state) { + case eStateAttaching: + return eEventActionSuccess; + case eStateRunning: case eStateConnected: return eEventActionRetry; @@ -4457,7 +4460,8 @@ // Only push the input handler if we aren't fowarding events, // as this means the curses GUI is in use... // Or don't push it if we are launching since it will come up stopped. - if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching) + if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching && + new_state != eStateAttaching) PushProcessIOHandler (); m_iohandler_sync.SetValue(true, eBroadcastAlways); }