brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.2 KiB · b637238 Raw
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