blob: be2a557bfa27a8eacbbdb75b1b1494b03f067bdb [file]
//===-- 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/Target/Process.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/State.h"
#include "lldb/lldb-forward.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);
NativeProcessProtocol &process = GetProcess();
const ArchSpec &arch = process.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:
if (log) {
log->Printf("NativeThreadWindows::%s tid %" PRIu64
" in state %s cannot answer stop reason",
__FUNCTION__, 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.");
}