return 0;
}
+Error
+DebuggerThread::StopDebugging(bool terminate)
+{
+ Error error;
+
+ 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 us to perform the Wait operation.
+ HostProcess process_copy = m_process;
+ lldb::process_t handle = process_copy.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);
+
+ // If we're stuck waiting for an exception to continue, continue it now. But only
+ // AFTER setting the termination event, to make sure that we don't race and enter
+ // another wait for another debug event.
+ if (m_active_exception.get())
+ ContinueAsyncException(ExceptionResult::MaskException);
+
+ // Don't return until the process has exited.
+ if (terminate_suceeded)
+ {
+ DWORD wait_result = ::WaitForSingleObject(handle, 5000);
+ if (wait_result != WAIT_OBJECT_0)
+ terminate_suceeded = false;
+ }
+
+ if (!terminate_suceeded)
+ error.SetError(GetLastError(), eErrorTypeWin32);
+ }
+ else
+ {
+ error.SetErrorString("Detach not yet supported on Windows.");
+ // TODO: Implement detach.
+ }
+ return error;
+}
+
void
DebuggerThread::ContinueAsyncException(ExceptionResult result)
{
- m_exception.SetValue(result, eBroadcastAlways);
+ if (!m_active_exception.get())
+ return;
+
+ 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 exit = false;
- while (!exit && WaitForDebugEvent(&dbe, INFINITE))
+ while (WaitForDebugEvent(&dbe, INFINITE))
{
DWORD continue_status = DBG_CONTINUE;
switch (dbe.dwDebugEventCode)
case EXCEPTION_DEBUG_EVENT:
{
ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId);
- m_exception.SetValue(status, eBroadcastNever);
- m_exception.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, status);
if (status == ExceptionResult::MaskException)
continue_status = DBG_CONTINUE;
break;
case EXIT_PROCESS_DEBUG_EVENT:
continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId);
- exit = true;
+ should_debug = false;
break;
case LOAD_DLL_DEBUG_EVENT:
continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId);
case RIP_EVENT:
continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId);
if (dbe.u.RipInfo.dwType == SLE_ERROR)
- exit = true;
+ should_debug = false;
break;
}
::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
}
+ FreeProcessHandles();
}
ExceptionResult
DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id)
{
bool first_chance = (info.dwFirstChance != 0);
- return m_debug_delegate->OnDebugException(first_chance, ExceptionRecord(info.ExceptionRecord));
+
+ m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord));
+
+ ExceptionResult result = m_debug_delegate->OnDebugException(first_chance, *m_active_exception);
+ m_exception_pred.SetValue(result, eBroadcastNever);
+ m_exception_pred.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, result);
+
+ return result;
}
DWORD
DWORD
DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id)
{
- m_debug_delegate->OnExitProcess(info.dwExitCode);
+ FreeProcessHandles();
- m_process = HostProcess();
- m_main_thread = HostThread();
- ::CloseHandle(m_image_file);
- m_image_file = nullptr;
+ m_debug_delegate->OnExitProcess(info.dwExitCode);
return DBG_CONTINUE;
}
{
return m_main_thread;
}
+ ExceptionRecord *
+ GetActiveException()
+ {
+ return m_active_exception.get();
+ }
+
+ Error StopDebugging(bool terminate);
void ContinueAsyncException(ExceptionResult result);
private:
+ void FreeProcessHandles();
void DebugLoop();
ExceptionResult HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id);
DWORD HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id);
HostThread m_main_thread; // The main thread of the inferior.
HANDLE m_image_file; // The image file of the process being debugged.
- Predicate<ExceptionResult> m_exception; // A predicate which gets signalled when an exception
- // is finished processing and the debug loop can be
- // continued.
+ ExceptionRecordUP m_active_exception; // The current exception waiting to be handled
+
+ Predicate<ExceptionResult> m_exception_pred; // A predicate which gets signalled when an exception
+ // 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);
~ProcessWindowsData() { ::CloseHandle(m_initial_stop_event); }
ProcessLaunchInfo m_launch_info;
- std::shared_ptr<lldb_private::ExceptionRecord> m_active_exception;
lldb_private::Error m_launch_error;
lldb_private::DebuggerThreadSP m_debugger;
StopInfoSP m_pending_stop_info;
Error error;
if (GetPrivateState() == eStateStopped)
{
- if (m_session_data->m_active_exception)
+ if (m_session_data->m_debugger->GetActiveException())
{
// Resume the process and continue processing debug events. Mask the exception so that
// from the process's view, there is no indication that anything happened.
- m_session_data->m_active_exception.reset();
m_session_data->m_debugger->ContinueAsyncException(ExceptionResult::MaskException);
}
Error error;
if (GetPrivateState() != eStateExited && GetPrivateState() != eStateDetached && m_session_data)
{
- // Ends the debugging session and terminates the inferior process.
- DebugActiveProcessStop(m_session_data->m_debugger->GetProcess().GetProcessId());
- SetPrivateState(eStateExited);
+ DebuggerThread &debugger = *m_session_data->m_debugger;
+ error = debugger.StopDebugging(true);
}
+ m_session_data.reset();
return error;
}
{
m_thread_list.RefreshStateAfterStop();
- if (m_session_data->m_active_exception)
- {
- StopInfoSP stop_info;
- ThreadSP stop_thread = m_thread_list.GetSelectedThread();
- RegisterContextSP register_context = stop_thread->GetRegisterContext();
+ ExceptionRecord *active_exception = m_session_data->m_debugger->GetActiveException();
+ if (!active_exception)
+ return;
- ExceptionRecord &exception = *m_session_data->m_active_exception;
- if (exception.GetExceptionCode() == EXCEPTION_BREAKPOINT)
- {
- uint64_t pc = register_context->GetPC();
- BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
- lldb::break_id_t break_id = LLDB_INVALID_BREAK_ID;
- bool should_stop = true;
- if (site)
- {
- should_stop = site->ValidForThisThread(stop_thread.get());
- break_id = site->GetID();
- }
+ StopInfoSP stop_info;
+ ThreadSP stop_thread = m_thread_list.GetSelectedThread();
+ RegisterContextSP register_context = stop_thread->GetRegisterContext();
- stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*stop_thread, break_id, should_stop);
- stop_thread->SetStopInfo(stop_info);
+ if (active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT)
+ {
+ uint64_t pc = register_context->GetPC();
+ BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
+ lldb::break_id_t break_id = LLDB_INVALID_BREAK_ID;
+ bool should_stop = true;
+ if (site)
+ {
+ should_stop = site->ValidForThisThread(stop_thread.get());
+ break_id = site->GetID();
}
+
+ stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*stop_thread, break_id, should_stop);
+ stop_thread->SetStopInfo(stop_info);
}
}
ProcessWindows::OnDebugException(bool first_chance, const ExceptionRecord &record)
{
ExceptionResult result = ExceptionResult::SendToApplication;
- m_session_data->m_active_exception.reset(new ExceptionRecord(record));
switch (record.GetExceptionCode())
{
case EXCEPTION_BREAKPOINT: