//===-- 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/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"
using namespace lldb;
using namespace lldb_private;
NativeThreadWindows::NativeThreadWindows(NativeProcessWindows &process,
const HostThread &thread)
: NativeThreadProtocol(process, thread.GetNativeThread().GetThreadId()),
m_stop_info(), m_stop_description(), 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 == (DWORD)-1)
return Status(::GetLastError(), eErrorTypeWin32);
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) {
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 == (DWORD)-1)
return Status(::GetLastError(), eErrorTypeWin32);
} while (previous_suspend_count > 1);
m_state = eStateRunning;
}
return Status();
}
std::string NativeThreadWindows::GetName() {
if (!m_name.empty())
return m_name;
// Name is not a property of the Windows thread. Create one with the
// process's.
NativeProcessProtocol &process = GetProcess();
ProcessInstanceInfo process_info;
if (Host::GetProcessInfo(process.GetID(), process_info)) {
std::string process_name(process_info.GetName());
m_name = process_name;
}
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.");
}