| //===-- NativeThreadWindows.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 "NativeThreadWindows.h" | |
| #include "NativeProcessWindows.h" | |
| #include "lldb/Host/HostThread.h" | |
| #include "lldb/Host/windows/HostThreadWindows.h" | |
| #include "lldb/Host/windows/LazyImport.h" | |
| #include "lldb/Host/windows/windows.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/State.h" | |
| #include <llvm/Support/ConvertUTF.h> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| NativeThreadWindows::NativeThreadWindows(NativeProcessWindows &process, | |
| const HostThread &thread) | |
| : NativeThreadProtocol(process, thread.GetNativeThread().GetThreadId()), | |
| m_host_thread(thread) { | |
| m_reg_context_up = | |
| (NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( | |
| process.GetArchitecture(), *this)); | |
| } | |
| Status NativeThreadWindows::DoStop() { | |
| if (m_state != eStateStopped) { | |
| DWORD previous_suspend_count = | |
| ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); | |
| if (previous_suspend_count == static_cast<DWORD>(-1)) | |
| return Status(::GetLastError(), eErrorTypeWin32); | |
| // Invalidate cached register values on every stop. | |
| GetRegisterContext().InvalidateAllRegisters(); | |
| m_state = eStateStopped; | |
| } | |
| return Status(); | |
| } | |
| Status NativeThreadWindows::DoResume(lldb::StateType resume_state) { | |
| StateType current_state = GetState(); | |
| if (resume_state == current_state) | |
| return Status(); | |
| if (resume_state == eStateStepping) { | |
| Log *log = GetLog(LLDBLog::Thread); | |
| uint32_t flags_index = | |
| GetRegisterContext().ConvertRegisterKindToRegisterNumber( | |
| eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); | |
| uint64_t flags_value = | |
| GetRegisterContext().ReadRegisterAsUnsigned(flags_index, 0); | |
| const ArchSpec &arch = GetProcess().GetArchitecture(); | |
| switch (arch.GetMachine()) { | |
| case llvm::Triple::x86: | |
| case llvm::Triple::x86_64: | |
| flags_value |= 0x100; // Set the trap flag on the CPU | |
| break; | |
| case llvm::Triple::aarch64: | |
| case llvm::Triple::arm: | |
| case llvm::Triple::thumb: | |
| flags_value |= 0x200000; // The SS bit in PState | |
| break; | |
| default: | |
| LLDB_LOG(log, "single stepping unsupported on this architecture"); | |
| break; | |
| } | |
| GetRegisterContext().WriteRegisterFromUnsigned(flags_index, flags_value); | |
| } | |
| if (resume_state == eStateStepping || resume_state == eStateRunning) { | |
| // Clear any stop info left over from a previous stop. | |
| ClearStopInfo(); | |
| DWORD previous_suspend_count = 0; | |
| HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); | |
| do { | |
| // ResumeThread returns -1 on error, or the thread's *previous* suspend | |
| // count on success. This means that the return value is 1 when the thread | |
| // was restarted. Note that DWORD is an unsigned int, so we need to | |
| // explicitly compare with -1. | |
| previous_suspend_count = ::ResumeThread(thread_handle); | |
| if (previous_suspend_count == static_cast<DWORD>(-1)) | |
| return Status(::GetLastError(), eErrorTypeWin32); | |
| } while (previous_suspend_count > 1); | |
| m_state = eStateRunning; | |
| } | |
| return Status(); | |
| } | |
| std::string NativeThreadWindows::GetName() { | |
| Log *log = GetLog(LLDBLog::Thread); | |
| static LazyImport<HRESULT(WINAPI *)(HANDLE, PWSTR *)> | |
| s_get_thread_description{L"Kernel32.dll", "GetThreadDescription"}; | |
| if (!s_get_thread_description) | |
| return m_name; | |
| auto GetThreadDescription = *s_get_thread_description; | |
| PWSTR pszThreadName; | |
| if (SUCCEEDED(GetThreadDescription( | |
| m_host_thread.GetNativeThread().GetSystemHandle(), &pszThreadName))) { | |
| LLDB_LOGF(log, "GetThreadDescription: %ls", pszThreadName); | |
| m_name.clear(); | |
| llvm::convertUTF16ToUTF8String( | |
| llvm::ArrayRef(reinterpret_cast<char *>(pszThreadName), | |
| wcslen(pszThreadName) * sizeof(wchar_t)), | |
| m_name); | |
| ::LocalFree(pszThreadName); | |
| } | |
| return m_name; | |
| } | |
| void NativeThreadWindows::SetStopReason(ThreadStopInfo stop_info, | |
| std::string description) { | |
| m_state = eStateStopped; | |
| m_stop_info = stop_info; | |
| m_stop_description = description; | |
| } | |
| bool NativeThreadWindows::GetStopReason(ThreadStopInfo &stop_info, | |
| std::string &description) { | |
| Log *log = GetLog(LLDBLog::Thread); | |
| switch (m_state) { | |
| case eStateStopped: | |
| case eStateCrashed: | |
| case eStateExited: | |
| case eStateSuspended: | |
| case eStateUnloaded: | |
| stop_info = m_stop_info; | |
| description = m_stop_description; | |
| return true; | |
| case eStateInvalid: | |
| case eStateConnected: | |
| case eStateAttaching: | |
| case eStateLaunching: | |
| case eStateRunning: | |
| case eStateStepping: | |
| case eStateDetached: | |
| LLDB_LOGF(log, "tid %" PRIu64 " in state %s cannot answer stop reason", | |
| GetID(), StateAsCString(m_state)); | |
| return false; | |
| } | |
| llvm_unreachable("unhandled StateType!"); | |
| } | |
| Status NativeThreadWindows::SetWatchpoint(lldb::addr_t addr, size_t size, | |
| uint32_t watch_flags, bool hardware) { | |
| if (!hardware) | |
| return Status::FromErrorString("not implemented"); | |
| if (m_state == eStateLaunching) | |
| return Status(); | |
| Status error = RemoveWatchpoint(addr); | |
| if (error.Fail()) | |
| return error; | |
| uint32_t wp_index = | |
| m_reg_context_up->SetHardwareWatchpoint(addr, size, watch_flags); | |
| if (wp_index == LLDB_INVALID_INDEX32) | |
| return Status::FromErrorString("Setting hardware watchpoint failed."); | |
| m_watchpoint_index_map.insert({addr, wp_index}); | |
| return Status(); | |
| } | |
| Status NativeThreadWindows::RemoveWatchpoint(lldb::addr_t addr) { | |
| auto wp = m_watchpoint_index_map.find(addr); | |
| if (wp == m_watchpoint_index_map.end()) | |
| return Status(); | |
| uint32_t wp_index = wp->second; | |
| m_watchpoint_index_map.erase(wp); | |
| if (m_reg_context_up->ClearHardwareWatchpoint(wp_index)) | |
| return Status(); | |
| return Status::FromErrorString("Clearing hardware watchpoint failed."); | |
| } | |
| Status NativeThreadWindows::SetHardwareBreakpoint(lldb::addr_t addr, | |
| size_t size) { | |
| return Status::FromErrorString("unimplemented."); | |
| } | |
| Status NativeThreadWindows::RemoveHardwareBreakpoint(lldb::addr_t addr) { | |
| return Status::FromErrorString("unimplemented."); | |
| } |