
`llvm::convertUTF16ToUTF8String` opens with an assertion that the output
container is empty:
3bdf9a0880/llvm/lib/Support/ConvertUTFWrapper.cpp (L83-L84)
It's not clear to me why this function requires the output container to
be empty instead of just overwriting it, but the callsite in
`TargetThreadWindows::GetName` may reuse the container without clearing
it out first, resulting in an assertion failure:
```
# Child-SP RetAddr Call Site
00 000000d2`44b8ea48 00007ff8`beefc12e ntdll!NtTerminateProcess+0x14
01 000000d2`44b8ea50 00007ff8`bcf518ab ntdll!RtlExitUserProcess+0x11e
02 000000d2`44b8ea80 00007ff8`bc0e0143 KERNEL32!ExitProcessImplementation+0xb
03 000000d2`44b8eab0 00007ff8`bc0e4c49 ucrtbase!common_exit+0xc7
04 000000d2`44b8eb10 00007ff8`bc102ae6 ucrtbase!abort+0x69
05 000000d2`44b8eb40 00007ff8`bc102cc1 ucrtbase!common_assert_to_stderr<wchar_t>+0x6e
06 000000d2`44b8eb80 00007fff`b8e27a80 ucrtbase!wassert+0x71
07 000000d2`44b8ebb0 00007fff`b8b821e1 liblldb!llvm::convertUTF16ToUTF8String+0x30 [D:\r\_work\swift-build\swift-build\SourceCache\llvm-project\llvm\lib\Support\ConvertUTFWrapper.cpp @ 88]
08 000000d2`44b8ec30 00007fff`b83e9aa2 liblldb!lldb_private::TargetThreadWindows::GetName+0x1b1 [D:\r\_work\swift-build\swift-build\SourceCache\llvm-project\lldb\source\Plugins\Process\Windows\Common\TargetThreadWindows.cpp @ 198]
09 000000d2`44b8eca0 00007ff7`2a3c3c14 liblldb!lldb::SBThread::GetName+0x102 [D:\r\_work\swift-build\swift-build\SourceCache\llvm-project\lldb\source\API\SBThread.cpp @ 432]
0a 000000d2`44b8ed70 00007ff7`2a3a5ac6 lldb_dap!lldb_dap::CreateThread+0x1f4 [S:\SourceCache\llvm-project\lldb\tools\lldb-dap\JSONUtils.cpp @ 877]
0b 000000d2`44b8ef10 00007ff7`2a3b0ab5 lldb_dap!`anonymous namespace'::request_threads+0xa6 [S:\SourceCache\llvm-project\lldb\tools\lldb-dap\lldb-dap.cpp @ 3906]
0c 000000d2`44b8f010 00007ff7`2a3b0fe8 lldb_dap!lldb_dap::DAP::HandleObject+0x1c5 [S:\SourceCache\llvm-project\lldb\tools\lldb-dap\DAP.cpp @ 796]
0d 000000d2`44b8f130 00007ff7`2a3a8b96 lldb_dap!lldb_dap::DAP::Loop+0x78 [S:\SourceCache\llvm-project\lldb\tools\lldb-dap\DAP.cpp @ 812]
0e 000000d2`44b8f1d0 00007ff7`2a4b5fbc lldb_dap!main+0x1096 [S:\SourceCache\llvm-project\lldb\tools\lldb-dap\lldb-dap.cpp @ 5319]
0f (Inline Function) --------`-------- lldb_dap!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
10 000000d2`44b8fb80 00007ff8`bcf3e8d7 lldb_dap!__scrt_common_main_seh+0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
11 000000d2`44b8fbc0 00007ff8`beefbf6c KERNEL32!BaseThreadInitThunk+0x17
12 000000d2`44b8fbf0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
```
This stack trace was captured from the lldb distributed in the Swift
toolchain. The issue is easy to reproduce by resuming from a breakpoint
twice in VS Code.
I've verified that clearing out the container here fixes the assertion
failure.
205 lines
6.6 KiB
C++
205 lines
6.6 KiB
C++
//===-- 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/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;
|
|
|
|
using GetThreadDescriptionFunctionPtr =
|
|
HRESULT(WINAPI *)(HANDLE hThread, PWSTR *ppszThreadDescription);
|
|
|
|
TargetThreadWindows::TargetThreadWindows(ProcessWindows &process,
|
|
const HostThread &thread)
|
|
: Thread(process, thread.GetNativeThread().GetThreadId()),
|
|
m_thread_reg_ctx_sp(), m_host_thread(thread) {}
|
|
|
|
TargetThreadWindows::~TargetThreadWindows() { DestroyThread(); }
|
|
|
|
void TargetThreadWindows::RefreshStateAfterStop() {
|
|
::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle());
|
|
SetState(eStateStopped);
|
|
GetRegisterContext()->InvalidateIfNeeded(false);
|
|
}
|
|
|
|
void TargetThreadWindows::WillResume(lldb::StateType resume_state) {}
|
|
|
|
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 == (DWORD)-1)
|
|
return Status(::GetLastError(), eErrorTypeWin32);
|
|
|
|
} while (previous_suspend_count > 1);
|
|
}
|
|
|
|
return Status();
|
|
}
|
|
|
|
const char *TargetThreadWindows::GetName() {
|
|
Log *log = GetLog(LLDBLog::Thread);
|
|
static GetThreadDescriptionFunctionPtr GetThreadDescription = []() {
|
|
HMODULE hModule = ::LoadLibraryW(L"Kernel32.dll");
|
|
return hModule
|
|
? reinterpret_cast<GetThreadDescriptionFunctionPtr>(
|
|
(void *)::GetProcAddress(hModule, "GetThreadDescription"))
|
|
: nullptr;
|
|
}();
|
|
LLDB_LOGF(log, "GetProcAddress: %p",
|
|
reinterpret_cast<void *>(GetThreadDescription));
|
|
if (!GetThreadDescription)
|
|
return m_name.c_str();
|
|
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();
|
|
}
|