607 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			607 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- NativeProcessWindows.cpp ------------------------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "lldb/Host/windows/windows.h"
 | |
| #include <psapi.h>
 | |
| 
 | |
| #include "NativeProcessWindows.h"
 | |
| #include "NativeThreadWindows.h"
 | |
| #include "lldb/Host/FileSystem.h"
 | |
| #include "lldb/Host/HostNativeProcessBase.h"
 | |
| #include "lldb/Host/HostProcess.h"
 | |
| #include "lldb/Host/ProcessLaunchInfo.h"
 | |
| #include "lldb/Host/windows/AutoHandle.h"
 | |
| #include "lldb/Host/windows/HostThreadWindows.h"
 | |
| #include "lldb/Host/windows/ProcessLauncherWindows.h"
 | |
| #include "lldb/Target/MemoryRegionInfo.h"
 | |
| #include "lldb/Target/Process.h"
 | |
| #include "lldb/Utility/State.h"
 | |
| #include "llvm/Support/ConvertUTF.h"
 | |
| #include "llvm/Support/Errc.h"
 | |
| #include "llvm/Support/Error.h"
 | |
| #include "llvm/Support/Format.h"
 | |
| #include "llvm/Support/Threading.h"
 | |
| #include "llvm/Support/raw_ostream.h"
 | |
| 
 | |
| #include "DebuggerThread.h"
 | |
| #include "ExceptionRecord.h"
 | |
| #include "ProcessWindowsLog.h"
 | |
| 
 | |
| #include <tlhelp32.h>
 | |
| 
 | |
| #pragma warning(disable : 4005)
 | |
| #include "winternl.h"
 | |
| #include <ntstatus.h>
 | |
| 
 | |
| using namespace lldb;
 | |
| using namespace lldb_private;
 | |
| using namespace llvm;
 | |
| 
 | |
| namespace lldb_private {
 | |
| 
 | |
| NativeProcessWindows::NativeProcessWindows(ProcessLaunchInfo &launch_info,
 | |
|                                            NativeDelegate &delegate,
 | |
|                                            llvm::Error &E)
 | |
|     : NativeProcessProtocol(LLDB_INVALID_PROCESS_ID,
 | |
|                             launch_info.GetPTY().ReleasePrimaryFileDescriptor(),
 | |
|                             delegate),
 | |
|       ProcessDebugger(), m_arch(launch_info.GetArchitecture()) {
 | |
|   ErrorAsOutParameter EOut(&E);
 | |
|   DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this));
 | |
|   E = LaunchProcess(launch_info, delegate_sp).ToError();
 | |
|   if (E)
 | |
|     return;
 | |
| 
 | |
|   SetID(GetDebuggedProcessId());
 | |
| }
 | |
| 
 | |
| NativeProcessWindows::NativeProcessWindows(lldb::pid_t pid, int terminal_fd,
 | |
|                                            NativeDelegate &delegate,
 | |
|                                            llvm::Error &E)
 | |
|     : NativeProcessProtocol(pid, terminal_fd, delegate), ProcessDebugger() {
 | |
|   ErrorAsOutParameter EOut(&E);
 | |
|   DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this));
 | |
|   ProcessAttachInfo attach_info;
 | |
|   attach_info.SetProcessID(pid);
 | |
|   E = AttachProcess(pid, attach_info, delegate_sp).ToError();
 | |
|   if (E)
 | |
|     return;
 | |
| 
 | |
|   SetID(GetDebuggedProcessId());
 | |
| 
 | |
|   ProcessInstanceInfo info;
 | |
|   if (!Host::GetProcessInfo(pid, info)) {
 | |
|     E = createStringError(inconvertibleErrorCode(),
 | |
|                           "Cannot get process information");
 | |
|     return;
 | |
|   }
 | |
|   m_arch = info.GetArchitecture();
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::Resume(const ResumeActionList &resume_actions) {
 | |
|   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS);
 | |
|   Status error;
 | |
|   llvm::sys::ScopedLock lock(m_mutex);
 | |
| 
 | |
|   StateType state = GetState();
 | |
|   if (state == eStateStopped || state == eStateCrashed) {
 | |
|     LLDB_LOG(log, "process {0} is in state {1}.  Resuming...",
 | |
|              GetDebuggedProcessId(), state);
 | |
|     LLDB_LOG(log, "resuming {0} threads.", m_threads.size());
 | |
| 
 | |
|     bool failed = false;
 | |
|     for (uint32_t i = 0; i < m_threads.size(); ++i) {
 | |
|       auto thread = static_cast<NativeThreadWindows *>(m_threads[i].get());
 | |
|       const ResumeAction *const action =
 | |
|           resume_actions.GetActionForThread(thread->GetID(), true);
 | |
|       if (action == nullptr)
 | |
|         continue;
 | |
| 
 | |
|       switch (action->state) {
 | |
|       case eStateRunning:
 | |
|       case eStateStepping: {
 | |
|         Status result = thread->DoResume(action->state);
 | |
|         if (result.Fail()) {
 | |
|           failed = true;
 | |
|           LLDB_LOG(log,
 | |
|                    "Trying to resume thread at index {0}, but failed with "
 | |
|                    "error {1}.",
 | |
|                    i, result);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|       case eStateSuspended:
 | |
|       case eStateStopped:
 | |
|         llvm_unreachable("Unexpected state");
 | |
| 
 | |
|       default:
 | |
|         return Status(
 | |
|             "NativeProcessWindows::%s (): unexpected state %s specified "
 | |
|             "for pid %" PRIu64 ", tid %" PRIu64,
 | |
|             __FUNCTION__, StateAsCString(action->state), GetID(),
 | |
|             thread->GetID());
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (failed) {
 | |
|       error.SetErrorString("NativeProcessWindows::DoResume failed");
 | |
|     } else {
 | |
|       SetState(eStateRunning);
 | |
|     }
 | |
| 
 | |
|     // Resume the debug loop.
 | |
|     ExceptionRecordSP active_exception =
 | |
|         m_session_data->m_debugger->GetActiveException().lock();
 | |
|     if (active_exception) {
 | |
|       // 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_debugger->ContinueAsyncException(
 | |
|           ExceptionResult::MaskException);
 | |
|     }
 | |
|   } else {
 | |
|     LLDB_LOG(log, "error: process {0} is in state {1}.  Returning...",
 | |
|              GetDebuggedProcessId(), GetState());
 | |
|   }
 | |
| 
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| NativeThreadWindows *
 | |
| NativeProcessWindows::GetThreadByID(lldb::tid_t thread_id) {
 | |
|   return static_cast<NativeThreadWindows *>(
 | |
|       NativeProcessProtocol::GetThreadByID(thread_id));
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::Halt() {
 | |
|   bool caused_stop = false;
 | |
|   StateType state = GetState();
 | |
|   if (state != eStateStopped)
 | |
|     return HaltProcess(caused_stop);
 | |
|   return Status();
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::Detach() {
 | |
|   Status error;
 | |
|   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS);
 | |
|   StateType state = GetState();
 | |
|   if (state != eStateExited && state != eStateDetached) {
 | |
|     error = DetachProcess();
 | |
|     if (error.Success())
 | |
|       SetState(eStateDetached);
 | |
|     else
 | |
|       LLDB_LOG(log, "Detaching process error: {0}", error);
 | |
|   } else {
 | |
|     error.SetErrorStringWithFormatv("error: process {0} in state = {1}, but "
 | |
|                                     "cannot detach it in this state.",
 | |
|                                     GetID(), state);
 | |
|     LLDB_LOG(log, "error: {0}", error);
 | |
|   }
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::Signal(int signo) {
 | |
|   Status error;
 | |
|   error.SetErrorString("Windows does not support sending signals to processes");
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::Interrupt() { return Halt(); }
 | |
| 
 | |
| Status NativeProcessWindows::Kill() {
 | |
|   StateType state = GetState();
 | |
|   return DestroyProcess(state);
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::IgnoreSignals(llvm::ArrayRef<int> signals) {
 | |
|   return Status();
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::GetMemoryRegionInfo(lldb::addr_t load_addr,
 | |
|                                                  MemoryRegionInfo &range_info) {
 | |
|   return ProcessDebugger::GetMemoryRegionInfo(load_addr, range_info);
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::ReadMemory(lldb::addr_t addr, void *buf,
 | |
|                                         size_t size, size_t &bytes_read) {
 | |
|   return ProcessDebugger::ReadMemory(addr, buf, size, bytes_read);
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::WriteMemory(lldb::addr_t addr, const void *buf,
 | |
|                                          size_t size, size_t &bytes_written) {
 | |
|   return ProcessDebugger::WriteMemory(addr, buf, size, bytes_written);
 | |
| }
 | |
| 
 | |
| llvm::Expected<lldb::addr_t>
 | |
| NativeProcessWindows::AllocateMemory(size_t size, uint32_t permissions) {
 | |
|   lldb::addr_t addr;
 | |
|   Status ST = ProcessDebugger::AllocateMemory(size, permissions, addr);
 | |
|   if (ST.Success())
 | |
|     return addr;
 | |
|   return ST.ToError();
 | |
| }
 | |
| 
 | |
| llvm::Error NativeProcessWindows::DeallocateMemory(lldb::addr_t addr) {
 | |
|   return ProcessDebugger::DeallocateMemory(addr).ToError();
 | |
| }
 | |
| 
 | |
| lldb::addr_t NativeProcessWindows::GetSharedLibraryInfoAddress() { return 0; }
 | |
| 
 | |
| bool NativeProcessWindows::IsAlive() const {
 | |
|   StateType state = GetState();
 | |
|   switch (state) {
 | |
|   case eStateCrashed:
 | |
|   case eStateDetached:
 | |
|   case eStateExited:
 | |
|   case eStateInvalid:
 | |
|   case eStateUnloaded:
 | |
|     return false;
 | |
|   default:
 | |
|     return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void NativeProcessWindows::SetStopReasonForThread(NativeThreadWindows &thread,
 | |
|                                                   lldb::StopReason reason,
 | |
|                                                   std::string description) {
 | |
|   SetCurrentThreadID(thread.GetID());
 | |
| 
 | |
|   ThreadStopInfo stop_info;
 | |
|   stop_info.reason = reason;
 | |
| 
 | |
|   // No signal support on Windows but required to provide a 'valid' signum.
 | |
|   if (reason == StopReason::eStopReasonException) {
 | |
|     stop_info.details.exception.type = 0;
 | |
|     stop_info.details.exception.data_count = 0;
 | |
|   } else {
 | |
|     stop_info.details.signal.signo = SIGTRAP;
 | |
|   }
 | |
| 
 | |
|   thread.SetStopReason(stop_info, description);
 | |
| }
 | |
| 
 | |
| void NativeProcessWindows::StopThread(lldb::tid_t thread_id,
 | |
|                                       lldb::StopReason reason,
 | |
|                                       std::string description) {
 | |
|   NativeThreadWindows *thread = GetThreadByID(thread_id);
 | |
|   if (!thread)
 | |
|     return;
 | |
| 
 | |
|   for (uint32_t i = 0; i < m_threads.size(); ++i) {
 | |
|     auto t = static_cast<NativeThreadWindows *>(m_threads[i].get());
 | |
|     Status error = t->DoStop();
 | |
|     if (error.Fail())
 | |
|       exit(1);
 | |
|   }
 | |
|   SetStopReasonForThread(*thread, reason, description);
 | |
| }
 | |
| 
 | |
| size_t NativeProcessWindows::UpdateThreads() { return m_threads.size(); }
 | |
| 
 | |
| llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
 | |
| NativeProcessWindows::GetAuxvData() const {
 | |
|   // Not available on this target.
 | |
|   return llvm::errc::not_supported;
 | |
| }
 | |
| 
 | |
| bool NativeProcessWindows::FindSoftwareBreakpoint(lldb::addr_t addr) {
 | |
|   auto it = m_software_breakpoints.find(addr);
 | |
|   if (it == m_software_breakpoints.end())
 | |
|     return false;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::SetBreakpoint(lldb::addr_t addr, uint32_t size,
 | |
|                                            bool hardware) {
 | |
|   if (hardware)
 | |
|     return SetHardwareBreakpoint(addr, size);
 | |
|   return SetSoftwareBreakpoint(addr, size);
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::RemoveBreakpoint(lldb::addr_t addr,
 | |
|                                               bool hardware) {
 | |
|   if (hardware)
 | |
|     return RemoveHardwareBreakpoint(addr);
 | |
|   return RemoveSoftwareBreakpoint(addr);
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::CacheLoadedModules() {
 | |
|   Status error;
 | |
|   if (!m_loaded_modules.empty())
 | |
|     return Status();
 | |
| 
 | |
|   // Retrieve loaded modules by a Target/Module free implemenation.
 | |
|   AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetID()));
 | |
|   if (snapshot.IsValid()) {
 | |
|     MODULEENTRY32W me;
 | |
|     me.dwSize = sizeof(MODULEENTRY32W);
 | |
|     if (Module32FirstW(snapshot.get(), &me)) {
 | |
|       do {
 | |
|         std::string path;
 | |
|         if (!llvm::convertWideToUTF8(me.szExePath, path))
 | |
|           continue;
 | |
| 
 | |
|         FileSpec file_spec(path);
 | |
|         FileSystem::Instance().Resolve(file_spec);
 | |
|         m_loaded_modules[file_spec] = (addr_t)me.modBaseAddr;
 | |
|       } while (Module32Next(snapshot.get(), &me));
 | |
|     }
 | |
| 
 | |
|     if (!m_loaded_modules.empty())
 | |
|       return Status();
 | |
|   }
 | |
| 
 | |
|   error.SetError(::GetLastError(), lldb::ErrorType::eErrorTypeWin32);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status NativeProcessWindows::GetLoadedModuleFileSpec(const char *module_path,
 | |
|                                                      FileSpec &file_spec) {
 | |
|   Status error = CacheLoadedModules();
 | |
|   if (error.Fail())
 | |
|     return error;
 | |
| 
 | |
|   FileSpec module_file_spec(module_path);
 | |
|   FileSystem::Instance().Resolve(module_file_spec);
 | |
|   for (auto &it : m_loaded_modules) {
 | |
|     if (it.first == module_file_spec) {
 | |
|       file_spec = it.first;
 | |
|       return Status();
 | |
|     }
 | |
|   }
 | |
|   return Status("Module (%s) not found in process %" PRIu64 "!",
 | |
|                 module_file_spec.GetCString(), GetID());
 | |
| }
 | |
| 
 | |
| Status
 | |
| NativeProcessWindows::GetFileLoadAddress(const llvm::StringRef &file_name,
 | |
|                                          lldb::addr_t &load_addr) {
 | |
|   Status error = CacheLoadedModules();
 | |
|   if (error.Fail())
 | |
|     return error;
 | |
| 
 | |
|   load_addr = LLDB_INVALID_ADDRESS;
 | |
|   FileSpec file_spec(file_name);
 | |
|   FileSystem::Instance().Resolve(file_spec);
 | |
|   for (auto &it : m_loaded_modules) {
 | |
|     if (it.first == file_spec) {
 | |
|       load_addr = it.second;
 | |
|       return Status();
 | |
|     }
 | |
|   }
 | |
|   return Status("Can't get loaded address of file (%s) in process %" PRIu64 "!",
 | |
|                 file_spec.GetCString(), GetID());
 | |
| }
 | |
| 
 | |
| void NativeProcessWindows::OnExitProcess(uint32_t exit_code) {
 | |
|   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS);
 | |
|   LLDB_LOG(log, "Process {0} exited with code {1}", GetID(), exit_code);
 | |
| 
 | |
|   ProcessDebugger::OnExitProcess(exit_code);
 | |
| 
 | |
|   // No signal involved.  It is just an exit event.
 | |
|   WaitStatus wait_status(WaitStatus::Exit, exit_code);
 | |
|   SetExitStatus(wait_status, true);
 | |
| 
 | |
|   // Notify the native delegate.
 | |
|   SetState(eStateExited, true);
 | |
| }
 | |
| 
 | |
| void NativeProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) {
 | |
|   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS);
 | |
|   LLDB_LOG(log, "Debugger connected to process {0}. Image base = {1:x}",
 | |
|            GetDebuggedProcessId(), image_base);
 | |
| 
 | |
|   // This is the earliest chance we can resolve the process ID and
 | |
|   // architecture if we don't know them yet.
 | |
|   if (GetID() == LLDB_INVALID_PROCESS_ID)
 | |
|     SetID(GetDebuggedProcessId());
 | |
| 
 | |
|   if (GetArchitecture().GetMachine() == llvm::Triple::UnknownArch) {
 | |
|     ProcessInstanceInfo process_info;
 | |
|     if (!Host::GetProcessInfo(GetDebuggedProcessId(), process_info)) {
 | |
|       LLDB_LOG(log, "Cannot get process information during debugger connecting "
 | |
|                     "to process");
 | |
|       return;
 | |
|     }
 | |
|     SetArchitecture(process_info.GetArchitecture());
 | |
|   }
 | |
| 
 | |
|   // The very first one shall always be the main thread.
 | |
|   assert(m_threads.empty());
 | |
|   m_threads.push_back(std::make_unique<NativeThreadWindows>(
 | |
|       *this, m_session_data->m_debugger->GetMainThread()));
 | |
| }
 | |
| 
 | |
| ExceptionResult
 | |
| NativeProcessWindows::OnDebugException(bool first_chance,
 | |
|                                        const ExceptionRecord &record) {
 | |
|   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_EXCEPTION);
 | |
|   llvm::sys::ScopedLock lock(m_mutex);
 | |
| 
 | |
|   // Let the debugger establish the internal status.
 | |
|   ProcessDebugger::OnDebugException(first_chance, record);
 | |
| 
 | |
|   static bool initial_stop = false;
 | |
|   if (!first_chance) {
 | |
|     SetState(eStateStopped, false);
 | |
|   }
 | |
| 
 | |
|   ExceptionResult result = ExceptionResult::SendToApplication;
 | |
|   switch (record.GetExceptionCode()) {
 | |
|   case DWORD(STATUS_SINGLE_STEP):
 | |
|   case STATUS_WX86_SINGLE_STEP: {
 | |
|     uint32_t wp_id = LLDB_INVALID_INDEX32;
 | |
|     if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID())) {
 | |
|       NativeRegisterContextWindows ®_ctx = thread->GetRegisterContext();
 | |
|       Status error =
 | |
|           reg_ctx.GetWatchpointHitIndex(wp_id, record.GetExceptionAddress());
 | |
|       if (error.Fail())
 | |
|         LLDB_LOG(log,
 | |
|                  "received error while checking for watchpoint hits, pid = "
 | |
|                  "{0}, error = {1}",
 | |
|                  thread->GetID(), error);
 | |
|       if (wp_id != LLDB_INVALID_INDEX32) {
 | |
|         addr_t wp_addr = reg_ctx.GetWatchpointAddress(wp_id);
 | |
|         addr_t wp_hit_addr = reg_ctx.GetWatchpointHitAddress(wp_id);
 | |
|         std::string desc =
 | |
|             formatv("{0} {1} {2}", wp_addr, wp_id, wp_hit_addr).str();
 | |
|         StopThread(record.GetThreadID(), StopReason::eStopReasonWatchpoint,
 | |
|                    desc);
 | |
|       }
 | |
|     }
 | |
|     if (wp_id == LLDB_INVALID_INDEX32)
 | |
|       StopThread(record.GetThreadID(), StopReason::eStopReasonTrace);
 | |
| 
 | |
|     SetState(eStateStopped, true);
 | |
| 
 | |
|     // Continue the debugger.
 | |
|     return ExceptionResult::MaskException;
 | |
|   }
 | |
|   case DWORD(STATUS_BREAKPOINT):
 | |
|   case STATUS_WX86_BREAKPOINT:
 | |
|     if (FindSoftwareBreakpoint(record.GetExceptionAddress())) {
 | |
|       LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
 | |
|                record.GetExceptionAddress());
 | |
| 
 | |
|       StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint);
 | |
| 
 | |
|       if (NativeThreadWindows *stop_thread =
 | |
|               GetThreadByID(record.GetThreadID())) {
 | |
|         auto ®ister_context = stop_thread->GetRegisterContext();
 | |
|         // The current EIP is AFTER the BP opcode, which is one byte '0xCC'
 | |
|         uint64_t pc = register_context.GetPC() - 1;
 | |
|         register_context.SetPC(pc);
 | |
|       }
 | |
| 
 | |
|       SetState(eStateStopped, true);
 | |
|       return ExceptionResult::MaskException;
 | |
|     }
 | |
| 
 | |
|     if (!initial_stop) {
 | |
|       initial_stop = true;
 | |
|       LLDB_LOG(log,
 | |
|                "Hit loader breakpoint at address {0:x}, setting initial stop "
 | |
|                "event.",
 | |
|                record.GetExceptionAddress());
 | |
| 
 | |
|       // We are required to report the reason for the first stop after
 | |
|       // launching or being attached.
 | |
|       if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID()))
 | |
|         SetStopReasonForThread(*thread, StopReason::eStopReasonBreakpoint);
 | |
| 
 | |
|       // Do not notify the native delegate (e.g. llgs) since at this moment
 | |
|       // the program hasn't returned from Factory::Launch() and the delegate
 | |
|       // might not have an valid native process to operate on.
 | |
|       SetState(eStateStopped, false);
 | |
| 
 | |
|       // Hit the initial stop. Continue the application.
 | |
|       return ExceptionResult::BreakInDebugger;
 | |
|     }
 | |
| 
 | |
|     LLVM_FALLTHROUGH;
 | |
|   default:
 | |
|     LLDB_LOG(log,
 | |
|              "Debugger thread reported exception {0:x} at address {1:x} "
 | |
|              "(first_chance={2})",
 | |
|              record.GetExceptionCode(), record.GetExceptionAddress(),
 | |
|              first_chance);
 | |
| 
 | |
|     {
 | |
|       std::string desc;
 | |
|       llvm::raw_string_ostream desc_stream(desc);
 | |
|       desc_stream << "Exception "
 | |
|                   << llvm::format_hex(record.GetExceptionCode(), 8)
 | |
|                   << " encountered at address "
 | |
|                   << llvm::format_hex(record.GetExceptionAddress(), 8);
 | |
|       StopThread(record.GetThreadID(), StopReason::eStopReasonException,
 | |
|                  desc_stream.str().c_str());
 | |
| 
 | |
|       SetState(eStateStopped, true);
 | |
|     }
 | |
| 
 | |
|     // For non-breakpoints, give the application a chance to handle the
 | |
|     // exception first.
 | |
|     if (first_chance)
 | |
|       result = ExceptionResult::SendToApplication;
 | |
|     else
 | |
|       result = ExceptionResult::BreakInDebugger;
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| void NativeProcessWindows::OnCreateThread(const HostThread &new_thread) {
 | |
|   llvm::sys::ScopedLock lock(m_mutex);
 | |
| 
 | |
|   auto thread = std::make_unique<NativeThreadWindows>(*this, new_thread);
 | |
|   thread->GetRegisterContext().ClearAllHardwareWatchpoints();
 | |
|   for (const auto &pair : GetWatchpointMap()) {
 | |
|     const NativeWatchpoint &wp = pair.second;
 | |
|     thread->SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags,
 | |
|                           wp.m_hardware);
 | |
|   }
 | |
| 
 | |
|   m_threads.push_back(std::move(thread));
 | |
| }
 | |
| 
 | |
| void NativeProcessWindows::OnExitThread(lldb::tid_t thread_id,
 | |
|                                         uint32_t exit_code) {
 | |
|   llvm::sys::ScopedLock lock(m_mutex);
 | |
|   NativeThreadWindows *thread = GetThreadByID(thread_id);
 | |
|   if (!thread)
 | |
|     return;
 | |
| 
 | |
|   for (auto t = m_threads.begin(); t != m_threads.end();) {
 | |
|     if ((*t)->GetID() == thread_id) {
 | |
|       t = m_threads.erase(t);
 | |
|     } else {
 | |
|       ++t;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void NativeProcessWindows::OnLoadDll(const ModuleSpec &module_spec,
 | |
|                                      lldb::addr_t module_addr) {
 | |
|   // Simply invalidate the cached loaded modules.
 | |
|   if (!m_loaded_modules.empty())
 | |
|     m_loaded_modules.clear();
 | |
| }
 | |
| 
 | |
| void NativeProcessWindows::OnUnloadDll(lldb::addr_t module_addr) {
 | |
|   if (!m_loaded_modules.empty())
 | |
|     m_loaded_modules.clear();
 | |
| }
 | |
| 
 | |
| llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
 | |
| NativeProcessWindows::Factory::Launch(
 | |
|     ProcessLaunchInfo &launch_info,
 | |
|     NativeProcessProtocol::NativeDelegate &native_delegate,
 | |
|     MainLoop &mainloop) const {
 | |
|   Error E = Error::success();
 | |
|   auto process_up = std::unique_ptr<NativeProcessWindows>(
 | |
|       new NativeProcessWindows(launch_info, native_delegate, E));
 | |
|   if (E)
 | |
|     return std::move(E);
 | |
|   return std::move(process_up);
 | |
| }
 | |
| 
 | |
| llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
 | |
| NativeProcessWindows::Factory::Attach(
 | |
|     lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
 | |
|     MainLoop &mainloop) const {
 | |
|   Error E = Error::success();
 | |
|   // Set pty master fd invalid since it is not available.
 | |
|   auto process_up = std::unique_ptr<NativeProcessWindows>(
 | |
|       new NativeProcessWindows(pid, -1, native_delegate, E));
 | |
|   if (E)
 | |
|     return std::move(E);
 | |
|   return std::move(process_up);
 | |
| }
 | |
| } // namespace lldb_private
 |