583 lines · cpp
1//===-- DebuggerThread.cpp ------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "DebuggerThread.h"10#include "ExceptionRecord.h"11#include "IDebugDelegate.h"12 13#include "lldb/Core/ModuleSpec.h"14#include "lldb/Host/ProcessLaunchInfo.h"15#include "lldb/Host/ThreadLauncher.h"16#include "lldb/Host/windows/AutoHandle.h"17#include "lldb/Host/windows/HostProcessWindows.h"18#include "lldb/Host/windows/HostThreadWindows.h"19#include "lldb/Host/windows/ProcessLauncherWindows.h"20#include "lldb/Target/Process.h"21#include "lldb/Utility/FileSpec.h"22#include "lldb/Utility/Log.h"23#include "lldb/Utility/Predicate.h"24#include "lldb/Utility/Status.h"25 26#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h"27 28#include "llvm/ADT/STLExtras.h"29#include "llvm/Support/ConvertUTF.h"30#include "llvm/Support/Threading.h"31#include "llvm/Support/raw_ostream.h"32 33#include <optional>34#include <psapi.h>35 36#ifndef STATUS_WX86_BREAKPOINT37#define STATUS_WX86_BREAKPOINT 0x4000001FL // For WOW6438#endif39 40using namespace lldb;41using namespace lldb_private;42 43DebuggerThread::DebuggerThread(DebugDelegateSP debug_delegate)44 : m_debug_delegate(debug_delegate), m_pid_to_detach(0),45 m_is_shutting_down(false) {46 m_debugging_ended_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);47}48 49DebuggerThread::~DebuggerThread() { ::CloseHandle(m_debugging_ended_event); }50 51Status DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info) {52 Log *log = GetLog(WindowsLog::Process);53 LLDB_LOG(log, "launching '{0}'", launch_info.GetExecutableFile().GetPath());54 55 Status result;56 llvm::Expected<HostThread> secondary_thread = ThreadLauncher::LaunchThread(57 "lldb.plugin.process-windows.secondary[?]",58 [this, launch_info] { return DebuggerThreadLaunchRoutine(launch_info); });59 if (!secondary_thread) {60 result = Status::FromError(secondary_thread.takeError());61 LLDB_LOG(log, "couldn't launch debugger thread. {0}", result);62 }63 64 return result;65}66 67Status DebuggerThread::DebugAttach(lldb::pid_t pid,68 const ProcessAttachInfo &attach_info) {69 Log *log = GetLog(WindowsLog::Process);70 LLDB_LOG(log, "attaching to '{0}'", pid);71 72 Status result;73 llvm::Expected<HostThread> secondary_thread = ThreadLauncher::LaunchThread(74 "lldb.plugin.process-windows.secondary[?]", [this, pid, attach_info] {75 return DebuggerThreadAttachRoutine(pid, attach_info);76 });77 if (!secondary_thread) {78 result = Status::FromError(secondary_thread.takeError());79 LLDB_LOG(log, "couldn't attach to process '{0}'. {1}", pid, result);80 }81 82 return result;83}84 85lldb::thread_result_t DebuggerThread::DebuggerThreadLaunchRoutine(86 const ProcessLaunchInfo &launch_info) {87 // Grab a shared_ptr reference to this so that we know it won't get deleted88 // until after the thread routine has exited.89 std::shared_ptr<DebuggerThread> this_ref(shared_from_this());90 91 Log *log = GetLog(WindowsLog::Process);92 LLDB_LOG(log, "preparing to launch '{0}' on background thread.",93 launch_info.GetExecutableFile().GetPath());94 95 Status error;96 ProcessLauncherWindows launcher;97 HostProcess process(launcher.LaunchProcess(launch_info, error));98 // If we couldn't create the process, notify waiters immediately. Otherwise99 // enter the debug loop and wait until we get the create process debug100 // notification. Note that if the process was created successfully, we can101 // throw away the process handle we got from CreateProcess because Windows102 // will give us another (potentially more useful?) handle when it sends us103 // the CREATE_PROCESS_DEBUG_EVENT.104 if (error.Success())105 DebugLoop();106 else107 m_debug_delegate->OnDebuggerError(error, 0);108 109 return {};110}111 112lldb::thread_result_t DebuggerThread::DebuggerThreadAttachRoutine(113 lldb::pid_t pid, const ProcessAttachInfo &attach_info) {114 // Grab a shared_ptr reference to this so that we know it won't get deleted115 // until after the thread routine has exited.116 std::shared_ptr<DebuggerThread> this_ref(shared_from_this());117 118 Log *log = GetLog(WindowsLog::Process);119 LLDB_LOG(log, "preparing to attach to process '{0}' on background thread.",120 pid);121 122 if (!DebugActiveProcess((DWORD)pid)) {123 Status error(::GetLastError(), eErrorTypeWin32);124 m_debug_delegate->OnDebuggerError(error, 0);125 return {};126 }127 128 // The attach was successful, enter the debug loop. From here on out, this129 // is no different than a create process operation, so all the same comments130 // in DebugLaunch should apply from this point out.131 DebugLoop();132 133 return {};134}135 136Status DebuggerThread::StopDebugging(bool terminate) {137 Status error;138 139 lldb::pid_t pid = m_process.GetProcessId();140 141 Log *log = GetLog(WindowsLog::Process);142 LLDB_LOG(log, "terminate = {0}, inferior={1}.", terminate, pid);143 144 // Set m_is_shutting_down to true if it was false. Return if it was already145 // true.146 bool expected = false;147 if (!m_is_shutting_down.compare_exchange_strong(expected, true))148 return error;149 150 // Make a copy of the process, since the termination sequence will reset151 // DebuggerThread's internal copy and it needs to remain open for the Wait152 // operation.153 HostProcess process_copy = m_process;154 lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle();155 156 if (terminate) {157 if (handle != nullptr && handle != LLDB_INVALID_PROCESS) {158 // Initiate the termination before continuing the exception, so that the159 // next debug event we get is the exit process event, and not some other160 // event.161 BOOL terminate_suceeded = TerminateProcess(handle, 0);162 LLDB_LOG(log,163 "calling TerminateProcess({0}, 0) (inferior={1}), success={2}",164 handle, pid, terminate_suceeded);165 } else {166 LLDB_LOG(log,167 "NOT calling TerminateProcess because the inferior is not valid "168 "({0}, 0) (inferior={1})",169 handle, pid);170 }171 }172 173 // If we're stuck waiting for an exception to continue (e.g. the user is at a174 // breakpoint messing around in the debugger), continue it now. But only175 // AFTER calling TerminateProcess to make sure that the very next call to176 // WaitForDebugEvent is an exit process event.177 if (m_active_exception.get()) {178 LLDB_LOG(log, "masking active exception");179 ContinueAsyncException(ExceptionResult::MaskException);180 }181 182 if (!terminate) {183 // Indicate that we want to detach.184 m_pid_to_detach = GetProcess().GetProcessId();185 186 // Force a fresh break so that the detach can happen from the debugger187 // thread.188 if (!::DebugBreakProcess(189 GetProcess().GetNativeProcess().GetSystemHandle())) {190 error = Status(::GetLastError(), eErrorTypeWin32);191 }192 }193 194 LLDB_LOG(log, "waiting for detach from process {0} to complete.", pid);195 196 DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000);197 if (wait_result != WAIT_OBJECT_0) {198 error = Status(GetLastError(), eErrorTypeWin32);199 LLDB_LOG(log, "error: WaitForSingleObject({0}, 5000) returned {1}",200 m_debugging_ended_event, wait_result);201 } else202 LLDB_LOG(log, "detach from process {0} completed successfully.", pid);203 204 if (!error.Success()) {205 LLDB_LOG(log, "encountered an error while trying to stop process {0}. {1}",206 pid, error);207 }208 return error;209}210 211void DebuggerThread::ContinueAsyncException(ExceptionResult result) {212 if (!m_active_exception.get())213 return;214 215 Log *log = GetLog(WindowsLog::Process | WindowsLog::Exception);216 LLDB_LOG(log, "broadcasting for inferior process {0}.",217 m_process.GetProcessId());218 219 m_active_exception.reset();220 m_exception_pred.SetValue(result, eBroadcastAlways);221}222 223void DebuggerThread::FreeProcessHandles() {224 m_process = HostProcess();225 m_main_thread = HostThread();226 if (m_image_file) {227 ::CloseHandle(m_image_file);228 m_image_file = nullptr;229 }230}231 232void DebuggerThread::DebugLoop() {233 Log *log = GetLog(WindowsLog::Event);234 DEBUG_EVENT dbe = {};235 bool should_debug = true;236 LLDB_LOGV(log, "Entering WaitForDebugEvent loop");237 while (should_debug) {238 LLDB_LOGV(log, "Calling WaitForDebugEvent");239 BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE);240 if (wait_result) {241 DWORD continue_status = DBG_CONTINUE;242 bool shutting_down = m_is_shutting_down;243 switch (dbe.dwDebugEventCode) {244 default:245 llvm_unreachable("Unhandle debug event code!");246 case EXCEPTION_DEBUG_EVENT: {247 ExceptionResult status = HandleExceptionEvent(248 dbe.u.Exception, dbe.dwThreadId, shutting_down);249 250 if (status == ExceptionResult::MaskException)251 continue_status = DBG_CONTINUE;252 else if (status == ExceptionResult::SendToApplication)253 continue_status = DBG_EXCEPTION_NOT_HANDLED;254 255 break;256 }257 case CREATE_THREAD_DEBUG_EVENT:258 continue_status =259 HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId);260 break;261 case CREATE_PROCESS_DEBUG_EVENT:262 continue_status =263 HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId);264 break;265 case EXIT_THREAD_DEBUG_EVENT:266 continue_status =267 HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId);268 break;269 case EXIT_PROCESS_DEBUG_EVENT:270 continue_status =271 HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId);272 should_debug = false;273 break;274 case LOAD_DLL_DEBUG_EVENT:275 continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId);276 break;277 case UNLOAD_DLL_DEBUG_EVENT:278 continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId);279 break;280 case OUTPUT_DEBUG_STRING_EVENT:281 continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId);282 break;283 case RIP_EVENT:284 continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId);285 if (dbe.u.RipInfo.dwType == SLE_ERROR)286 should_debug = false;287 break;288 }289 290 LLDB_LOGV(log, "calling ContinueDebugEvent({0}, {1}, {2}) on thread {3}.",291 dbe.dwProcessId, dbe.dwThreadId, continue_status,292 ::GetCurrentThreadId());293 294 ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);295 296 // We have to DebugActiveProcessStop after ContinueDebugEvent, otherwise297 // the target process will crash298 if (shutting_down) {299 // A breakpoint that occurs while `m_pid_to_detach` is non-zero is a300 // magic exception that we use simply to wake up the DebuggerThread so301 // that we can close out the debug loop.302 if (m_pid_to_detach != 0 &&303 (dbe.u.Exception.ExceptionRecord.ExceptionCode ==304 EXCEPTION_BREAKPOINT ||305 dbe.u.Exception.ExceptionRecord.ExceptionCode ==306 STATUS_WX86_BREAKPOINT)) {307 LLDB_LOG(log,308 "Breakpoint exception is cue to detach from process {0:x}",309 m_pid_to_detach.load());310 311 // detaching with leaving breakpoint exception event on the queue may312 // cause target process to crash so process events as possible since313 // target threads are running at this time, there is possibility to314 // have some breakpoint exception between last WaitForDebugEvent and315 // DebugActiveProcessStop but ignore for now.316 while (WaitForDebugEvent(&dbe, 0)) {317 continue_status = DBG_CONTINUE;318 if (dbe.dwDebugEventCode == EXCEPTION_DEBUG_EVENT &&319 !(dbe.u.Exception.ExceptionRecord.ExceptionCode ==320 EXCEPTION_BREAKPOINT ||321 dbe.u.Exception.ExceptionRecord.ExceptionCode ==322 STATUS_WX86_BREAKPOINT ||323 dbe.u.Exception.ExceptionRecord.ExceptionCode ==324 EXCEPTION_SINGLE_STEP))325 continue_status = DBG_EXCEPTION_NOT_HANDLED;326 ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId,327 continue_status);328 }329 330 ::DebugActiveProcessStop(m_pid_to_detach);331 m_detached = true;332 }333 }334 335 if (m_detached) {336 should_debug = false;337 }338 } else {339 LLDB_LOG(log, "returned FALSE from WaitForDebugEvent. Error = {0}",340 ::GetLastError());341 342 should_debug = false;343 }344 }345 FreeProcessHandles();346 347 LLDB_LOG(log, "WaitForDebugEvent loop completed, exiting.");348 ::SetEvent(m_debugging_ended_event);349}350 351ExceptionResult352DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info,353 DWORD thread_id, bool shutting_down) {354 Log *log = GetLog(WindowsLog::Event | WindowsLog::Exception);355 if (shutting_down) {356 bool is_breakpoint =357 (info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT ||358 info.ExceptionRecord.ExceptionCode == STATUS_WX86_BREAKPOINT);359 360 // Don't perform any blocking operations while we're shutting down. That361 // will cause TerminateProcess -> WaitForSingleObject to time out.362 // We should not send breakpoint exceptions to the application.363 return is_breakpoint ? ExceptionResult::MaskException364 : ExceptionResult::SendToApplication;365 }366 367 bool first_chance = (info.dwFirstChance != 0);368 369 m_active_exception.reset(370 new ExceptionRecord(info.ExceptionRecord, thread_id));371 LLDB_LOG(log, "encountered {0} chance exception {1:x} on thread {2:x}",372 first_chance ? "first" : "second",373 info.ExceptionRecord.ExceptionCode, thread_id);374 375 ExceptionResult result =376 m_debug_delegate->OnDebugException(first_chance, *m_active_exception);377 m_exception_pred.SetValue(result, eBroadcastNever);378 379 LLDB_LOG(log, "waiting for ExceptionPred != BreakInDebugger");380 result = *m_exception_pred.WaitForValueNotEqualTo(381 ExceptionResult::BreakInDebugger);382 383 LLDB_LOG(log, "got ExceptionPred = {0}", (int)m_exception_pred.GetValue());384 return result;385}386 387DWORD388DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info,389 DWORD thread_id) {390 Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);391 LLDB_LOG(log, "Thread {0} spawned in process {1}", thread_id,392 m_process.GetProcessId());393 HostThread thread(info.hThread);394 thread.GetNativeThread().SetOwnsHandle(false);395 m_debug_delegate->OnCreateThread(thread);396 return DBG_CONTINUE;397}398 399DWORD400DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info,401 DWORD thread_id) {402 Log *log = GetLog(WindowsLog::Event | WindowsLog::Process);403 uint32_t process_id = ::GetProcessId(info.hProcess);404 405 LLDB_LOG(log, "process {0} spawned", process_id);406 407 std::string thread_name;408 llvm::raw_string_ostream name_stream(thread_name);409 name_stream << "lldb.plugin.process-windows.secondary[" << process_id << "]";410 llvm::set_thread_name(thread_name);411 412 // info.hProcess and info.hThread are closed automatically by Windows when413 // EXIT_PROCESS_DEBUG_EVENT is received.414 m_process = HostProcess(info.hProcess);415 ((HostProcessWindows &)m_process.GetNativeProcess()).SetOwnsHandle(false);416 m_main_thread = HostThread(info.hThread);417 m_main_thread.GetNativeThread().SetOwnsHandle(false);418 m_image_file = info.hFile;419 420 lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfImage);421 m_debug_delegate->OnDebuggerConnected(load_addr);422 423 return DBG_CONTINUE;424}425 426DWORD427DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info,428 DWORD thread_id) {429 Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);430 LLDB_LOG(log, "Thread {0} exited with code {1} in process {2}", thread_id,431 info.dwExitCode, m_process.GetProcessId());432 m_debug_delegate->OnExitThread(thread_id, info.dwExitCode);433 return DBG_CONTINUE;434}435 436DWORD437DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info,438 DWORD thread_id) {439 Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);440 LLDB_LOG(log, "process {0} exited with code {1}", m_process.GetProcessId(),441 info.dwExitCode);442 443 m_debug_delegate->OnExitProcess(info.dwExitCode);444 445 return DBG_CONTINUE;446}447 448static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) {449 // Check that file is not empty as we cannot map a file with zero length.450 DWORD dwFileSizeHi = 0;451 DWORD dwFileSizeLo = ::GetFileSize(hFile, &dwFileSizeHi);452 if (dwFileSizeLo == 0 && dwFileSizeHi == 0)453 return std::nullopt;454 455 AutoHandle filemap(456 ::CreateFileMappingW(hFile, nullptr, PAGE_READONLY, 0, 1, NULL), nullptr);457 if (!filemap.IsValid())458 return std::nullopt;459 460 auto view_deleter = [](void *pMem) { ::UnmapViewOfFile(pMem); };461 std::unique_ptr<void, decltype(view_deleter)> pMem(462 ::MapViewOfFile(filemap.get(), FILE_MAP_READ, 0, 0, 1), view_deleter);463 if (!pMem)464 return std::nullopt;465 466 std::array<wchar_t, MAX_PATH + 1> mapped_filename;467 if (!::GetMappedFileNameW(::GetCurrentProcess(), pMem.get(),468 mapped_filename.data(), mapped_filename.size()))469 return std::nullopt;470 471 // A series of null-terminated strings, plus an additional null character472 std::array<wchar_t, 512> drive_strings;473 drive_strings[0] = L'\0';474 if (!::GetLogicalDriveStringsW(drive_strings.size(), drive_strings.data()))475 return std::nullopt;476 477 std::array<wchar_t, 3> drive = {L"_:"};478 for (const wchar_t *it = drive_strings.data(); *it != L'\0';479 it += wcslen(it) + 1) {480 // Copy the drive letter to the template string481 drive[0] = it[0];482 std::array<wchar_t, MAX_PATH> device_name;483 if (::QueryDosDeviceW(drive.data(), device_name.data(),484 device_name.size())) {485 size_t device_name_len = wcslen(device_name.data());486 if (device_name_len < mapped_filename.size()) {487 bool match = _wcsnicmp(mapped_filename.data(), device_name.data(),488 device_name_len) == 0;489 if (match && mapped_filename[device_name_len] == L'\\') {490 // Replace device path with its drive letter491 std::wstring rebuilt_path(drive.data());492 rebuilt_path.append(&mapped_filename[device_name_len]);493 std::string path_utf8;494 llvm::convertWideToUTF8(rebuilt_path, path_utf8);495 return path_utf8;496 }497 }498 }499 }500 return std::nullopt;501}502 503DWORD504DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,505 DWORD thread_id) {506 Log *log = GetLog(WindowsLog::Event);507 if (info.hFile == nullptr) {508 // Not sure what this is, so just ignore it.509 LLDB_LOG(log, "Warning: Inferior {0} has a NULL file handle, returning...",510 m_process.GetProcessId());511 return DBG_CONTINUE;512 }513 514 auto on_load_dll = [&](llvm::StringRef path) {515 FileSpec file_spec(path);516 ModuleSpec module_spec(file_spec);517 lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll);518 519 LLDB_LOG(log, "Inferior {0} - DLL '{1}' loaded at address {2:x}...",520 m_process.GetProcessId(), path, info.lpBaseOfDll);521 522 m_debug_delegate->OnLoadDll(module_spec, load_addr);523 };524 525 std::vector<wchar_t> buffer(1);526 DWORD required_size =527 GetFinalPathNameByHandleW(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS);528 if (required_size > 0) {529 buffer.resize(required_size + 1);530 required_size = GetFinalPathNameByHandleW(info.hFile, &buffer[0],531 required_size, VOLUME_NAME_DOS);532 std::string path_str_utf8;533 llvm::convertWideToUTF8(buffer.data(), path_str_utf8);534 llvm::StringRef path_str = path_str_utf8;535 const char *path = path_str.data();536 if (path_str.starts_with("\\\\?\\"))537 path += 4;538 539 on_load_dll(path);540 } else if (std::optional<std::string> path =541 GetFileNameFromHandleFallback(info.hFile)) {542 on_load_dll(*path);543 } else {544 LLDB_LOG(545 log,546 "Inferior {0} - Error {1} occurred calling GetFinalPathNameByHandle",547 m_process.GetProcessId(), ::GetLastError());548 }549 // Windows does not automatically close info.hFile, so we need to do it.550 ::CloseHandle(info.hFile);551 return DBG_CONTINUE;552}553 554DWORD555DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info,556 DWORD thread_id) {557 Log *log = GetLog(WindowsLog::Event);558 LLDB_LOG(log, "process {0} unloading DLL at addr {1:x}.",559 m_process.GetProcessId(), info.lpBaseOfDll);560 561 m_debug_delegate->OnUnloadDll(562 reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll));563 return DBG_CONTINUE;564}565 566DWORD567DebuggerThread::HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info,568 DWORD thread_id) {569 return DBG_CONTINUE;570}571 572DWORD573DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) {574 Log *log = GetLog(WindowsLog::Event);575 LLDB_LOG(log, "encountered error {0} (type={1}) in process {2} thread {3}",576 info.dwError, info.dwType, m_process.GetProcessId(), thread_id);577 578 Status error(info.dwError, eErrorTypeWin32);579 m_debug_delegate->OnDebuggerError(error, info.dwType);580 581 return DBG_CONTINUE;582}583