| //===-- TargetThreadWindows.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/HostInfo.h" | |
| #include "lldb/Host/windows/LazyImport.h" | |
| #include "lldb/Target/Unwind.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "ProcessWindows.h" | |
| #include "TargetThreadWindows.h" | |
| #include "lldb/Host/windows/HostThreadWindows.h" | |
| #include <llvm/Support/ConvertUTF.h> | |
| #if defined(__x86_64__) || defined(_M_AMD64) | |
| #include "x64/RegisterContextWindows_x64.h" | |
| #elif defined(__i386__) || defined(_M_IX86) | |
| #include "x86/RegisterContextWindows_x86.h" | |
| #elif defined(__aarch64__) || defined(_M_ARM64) | |
| #include "arm64/RegisterContextWindows_arm64.h" | |
| #elif defined(__arm__) || defined(_M_ARM) | |
| #include "arm/RegisterContextWindows_arm.h" | |
| #endif | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| TargetThreadWindows::TargetThreadWindows(ProcessWindows &process, | |
| const HostThread &thread) | |
| : Thread(process, thread.GetNativeThread().GetThreadId()), | |
| m_host_thread(thread) {} | |
| TargetThreadWindows::~TargetThreadWindows() { DestroyThread(); } | |
| void TargetThreadWindows::RefreshStateAfterStop() { | |
| ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); | |
| GetRegisterContext()->InvalidateIfNeeded(false); | |
| } | |
| void TargetThreadWindows::WillResume(lldb::StateType resume_state) {} | |
| StructuredData::ObjectSP TargetThreadWindows::FetchThreadExtendedInfo() { | |
| struct ThreadBasicInformation { | |
| LONG ExitStatus; | |
| PVOID TebBaseAddress; | |
| struct { | |
| HANDLE UniqueProcess; | |
| HANDLE UniqueThread; | |
| } ClientId; | |
| ULONG_PTR AffinityMask; | |
| LONG Priority; | |
| LONG BasePriority; | |
| }; | |
| using NtQueryInformationThreadFn = | |
| LONG(WINAPI *)(HANDLE ThreadHandle, ULONG ThreadInformationClass, | |
| PVOID ThreadInformation, ULONG ThreadInformationLength, | |
| PULONG ReturnLength); | |
| static LazyImport<NtQueryInformationThreadFn> s_query_information_thread{ | |
| L"Kernel32.dll", "NtQueryInformationThread"}; | |
| if (!s_query_information_thread) | |
| return StructuredData::ObjectSP(); | |
| auto NtQueryInformationThread = *s_query_information_thread; | |
| HANDLE handle = m_host_thread.GetNativeThread().GetSystemHandle(); | |
| if (!handle || handle == INVALID_HANDLE_VALUE) | |
| return StructuredData::ObjectSP(); | |
| ThreadBasicInformation info = {}; | |
| // ThreadInformationClass 0 is ThreadBasicInformation. | |
| if (NtQueryInformationThread(handle, 0, &info, sizeof(info), nullptr) < 0) | |
| return StructuredData::ObjectSP(); | |
| if (!info.TebBaseAddress) | |
| return StructuredData::ObjectSP(); | |
| auto dict = std::make_shared<StructuredData::Dictionary>(); | |
| dict->AddIntegerItem("teb_address", | |
| reinterpret_cast<uint64_t>(info.TebBaseAddress)); | |
| return dict; | |
| } | |
| void TargetThreadWindows::DidStop() {} | |
| RegisterContextSP TargetThreadWindows::GetRegisterContext() { | |
| if (!m_reg_context_sp) | |
| m_reg_context_sp = CreateRegisterContextForFrame(nullptr); | |
| return m_reg_context_sp; | |
| } | |
| RegisterContextSP | |
| TargetThreadWindows::CreateRegisterContextForFrame(StackFrame *frame) { | |
| RegisterContextSP reg_ctx_sp; | |
| uint32_t concrete_frame_idx = 0; | |
| Log *log = GetLog(LLDBLog::Thread); | |
| if (frame) | |
| concrete_frame_idx = frame->GetConcreteFrameIndex(); | |
| if (concrete_frame_idx == 0) { | |
| if (!m_thread_reg_ctx_sp) { | |
| ArchSpec arch = HostInfo::GetArchitecture(); | |
| switch (arch.GetMachine()) { | |
| case llvm::Triple::arm: | |
| case llvm::Triple::thumb: | |
| #if defined(__arm__) || defined(_M_ARM) | |
| m_thread_reg_ctx_sp.reset( | |
| new RegisterContextWindows_arm(*this, concrete_frame_idx)); | |
| #else | |
| LLDB_LOG(log, "debugging foreign targets is currently unsupported"); | |
| #endif | |
| break; | |
| case llvm::Triple::aarch64: | |
| #if defined(__aarch64__) || defined(_M_ARM64) | |
| m_thread_reg_ctx_sp.reset( | |
| new RegisterContextWindows_arm64(*this, concrete_frame_idx)); | |
| #else | |
| LLDB_LOG(log, "debugging foreign targets is currently unsupported"); | |
| #endif | |
| break; | |
| case llvm::Triple::x86: | |
| #if defined(__i386__) || defined(_M_IX86) | |
| m_thread_reg_ctx_sp.reset( | |
| new RegisterContextWindows_x86(*this, concrete_frame_idx)); | |
| #else | |
| LLDB_LOG(log, "debugging foreign targets is currently unsupported"); | |
| #endif | |
| break; | |
| case llvm::Triple::x86_64: | |
| #if defined(__x86_64__) || defined(_M_AMD64) | |
| m_thread_reg_ctx_sp.reset( | |
| new RegisterContextWindows_x64(*this, concrete_frame_idx)); | |
| #else | |
| LLDB_LOG(log, "debugging foreign targets is currently unsupported"); | |
| #endif | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| reg_ctx_sp = m_thread_reg_ctx_sp; | |
| } else { | |
| reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame); | |
| } | |
| return reg_ctx_sp; | |
| } | |
| bool TargetThreadWindows::CalculateStopInfo() { | |
| SetStopInfo(m_stop_info_sp); | |
| return true; | |
| } | |
| Status TargetThreadWindows::DoResume() { | |
| StateType resume_state = GetTemporaryResumeState(); | |
| 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); | |
| ProcessSP process = GetProcess(); | |
| const ArchSpec &arch = process->GetTarget().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) { | |
| 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); | |
| } | |
| return Status(); | |
| } | |
| const char *TargetThreadWindows::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.c_str(); | |
| 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.c_str(); | |
| } |