[Lldb-commits] [lldb] [lldb][Windows] Forward OUTPUT_DEBUG_STRING_EVENT through lldb-server (PR #203546)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Fri Jun 12 07:32:53 PDT 2026
https://github.com/charles-zablit created https://github.com/llvm/llvm-project/pull/203546
Hoist `ReadDebugString` into `ProcessDebugger` so both plugins share it, then add
`NativeProcessWindows::OnDebugString` that reads the string, converts UTF-16 to
UTF-8 when needed, and delivers via `NativeDelegate::NewProcessOutput`.
Fixes `Process/Windows/output_debug_string.cpp` on Windows under `LLDB_USE_LLDB_SERVER=1`.
>From 56484d768158327f1dd62b889a438dba2568698c Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 12 Jun 2026 15:28:58 +0100
Subject: [PATCH] [lldb][Windows] Forward OUTPUT_DEBUG_STRING_EVENT through
lldb-server
---
.../Windows/Common/NativeProcessWindows.cpp | 38 ++++++++++++++
.../Windows/Common/NativeProcessWindows.h | 2 +
.../Windows/Common/ProcessDebugger.cpp | 51 +++++++++++++++++++
.../Process/Windows/Common/ProcessDebugger.h | 12 +++++
.../Process/Windows/Common/ProcessWindows.cpp | 51 -------------------
.../Process/Windows/Common/ProcessWindows.h | 4 --
6 files changed, 103 insertions(+), 55 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index f0e88e78e6136..889a2e743c07f 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -713,6 +713,44 @@ void NativeProcessWindows::OnUnloadDll(lldb::addr_t module_addr) {
m_pending_library_events = true;
}
+void NativeProcessWindows::OnDebugString(lldb::addr_t debug_string_addr,
+ bool is_unicode,
+ uint16_t length_lower_word) {
+ Log *log = GetLog(WindowsLog::Process);
+
+ llvm::SmallVector<char, 256> buffer;
+ if (llvm::Error err = ProcessDebugger::ReadDebugString(
+ debug_string_addr, is_unicode, length_lower_word, buffer)) {
+ std::string err_str = llvm::toString(std::move(err));
+ std::string msg =
+ llvm::formatv("Failed to read debug string at {0:x} "
+ "(size & 0xffff={1}, unicode={2}): {3}\n",
+ debug_string_addr, length_lower_word, is_unicode, err_str)
+ .str();
+ LLDB_LOG(log, "{0}", msg);
+ m_delegate.NewProcessOutput(this, llvm::StringRef(msg));
+ return;
+ }
+ if (buffer.empty())
+ return;
+
+ if (is_unicode) {
+ assert(buffer.size() % 2 == 0);
+ llvm::ArrayRef<unsigned short> utf16(
+ reinterpret_cast<const unsigned short *>(buffer.data()),
+ buffer.size() / 2);
+ std::string out;
+ if (!llvm::convertUTF16ToUTF8String(utf16, out)) {
+ LLDB_LOG(log, "Debug string is not valid Utf 16");
+ return;
+ }
+ m_delegate.NewProcessOutput(this, llvm::StringRef(out.data(), out.size()));
+ } else {
+ m_delegate.NewProcessOutput(this,
+ llvm::StringRef(buffer.data(), buffer.size()));
+ }
+}
+
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
NativeProcessWindows::Manager::Launch(
ProcessLaunchInfo &launch_info,
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
index d1c57fc01e06b..e2879bcd803fe 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
@@ -124,6 +124,8 @@ class NativeProcessWindows : public NativeProcessProtocol,
void OnLoadDll(const ModuleSpec &module_spec,
lldb::addr_t module_addr) override;
void OnUnloadDll(lldb::addr_t module_addr) override;
+ void OnDebugString(lldb::addr_t debug_string_addr, bool is_unicode,
+ uint16_t length_lower_word) override;
protected:
NativeThreadWindows *GetThreadByID(lldb::tid_t thread_id);
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp
index 5a2c3bdc87d47..84c74bb1ba919 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp
@@ -555,6 +555,57 @@ void ProcessDebugger::OnDebugString(lldb::addr_t debug_string_addr,
// Do nothing by default
}
+llvm::Error
+ProcessDebugger::ReadDebugString(lldb::addr_t debug_string_addr,
+ bool is_unicode, uint16_t length_lower_word,
+ llvm::SmallVectorImpl<char> &output) {
+ if (is_unicode && length_lower_word % 2 != 0)
+ return llvm::createStringError(
+ "Utf16 string can't have uneven size in bytes");
+
+ const auto is_zero_terminated = [&] {
+ // The zero terminator is always at the end of the buffer.
+ if (is_unicode)
+ return output.size() >= 2 && output.back() == 0 &&
+ output[output.size() - 2] == 0;
+
+ return !output.empty() && output.back() == 0;
+ };
+
+ // Read at most 1 MiB ((1 << 16) * 16 - 1 Bytes) since we don't know the exact
+ // size of the string. We know that `strlen(string) & 0xffff ==
+ // length_lower_word`, so we read in chunks until we reach the terminator:
+ // - 0: `length_lower_word` Bytes
+ // - 1..16: 64 KiB (= 2^16 Bytes)
+ size_t start = length_lower_word == 0 ? 1 : 0;
+ for (size_t i = start; i < 16; ++i) {
+ output.resize_for_overwrite(length_lower_word + i * (1 << 16));
+ size_t chunk_size = i == 0 ? length_lower_word : (1 << 16);
+ lldb::addr_t addr = debug_string_addr + output.size_in_bytes() - chunk_size;
+
+ size_t bytes_read = 0;
+ Status error =
+ ReadMemory(addr, output.end() - chunk_size, chunk_size, bytes_read);
+ if (error.Fail())
+ return error.takeError();
+
+ if (bytes_read != chunk_size) {
+ return llvm::createStringErrorV(
+ "Expected to read {0} bytes, but read {1}", chunk_size, bytes_read);
+ }
+
+ if (is_zero_terminated())
+ break;
+ }
+
+ if (!is_zero_terminated())
+ return llvm::createStringError("String is 1 MiB or larger");
+
+ // Remove null terminator.
+ output.pop_back_n(is_unicode ? 2 : 1);
+ return llvm::Error::success();
+}
+
void ProcessDebugger::OnDebuggerError(const Status &error, uint32_t type) {
llvm::sys::ScopedLock lock(m_mutex);
Log *log = GetLog(WindowsLog::Process);
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h b/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h
index 25c877d5b4f17..10620b44beabf 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h
@@ -14,6 +14,9 @@
#include "lldb/Utility/Status.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-types.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorExtras.h"
#include "llvm/Support/Mutex.h"
#include "ForwardDecl.h"
@@ -82,6 +85,15 @@ class ProcessDebugger {
Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
size_t &bytes_read);
+ /// Read an OUTPUT_DEBUG_STRING_INFO payload from the inferior.
+ /// `length_lower_word` is the OS-supplied low 16 bits of the string length;
+ /// the helper walks 64 KiB chunks (up to 1 MiB) until it finds the NUL
+ /// terminator, then strips it. Shared between ProcessWindows (in-process)
+ /// and NativeProcessWindows (lldb-server).
+ llvm::Error ReadDebugString(lldb::addr_t debug_string_addr, bool is_unicode,
+ uint16_t length_lower_word,
+ llvm::SmallVectorImpl<char> &output);
+
Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
size_t &bytes_written);
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index cd91dd23cb298..1333b6bc80e95 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -839,57 +839,6 @@ void ProcessWindows::OnDebugString(lldb::addr_t debug_string_addr,
}
}
-llvm::Error
-ProcessWindows::ReadDebugString(lldb::addr_t debug_string_addr, bool is_unicode,
- uint16_t length_lower_word,
- llvm::SmallVectorImpl<char> &output) {
- if (is_unicode && length_lower_word % 2 != 0)
- return llvm::createStringError(
- "Utf16 string can't have uneven size in bytes");
-
- const auto is_zero_terminated = [&] {
- // The zero terminator is always at the end of the buffer.
- if (is_unicode)
- return output.size() >= 2 && output.back() == 0 &&
- output[output.size() - 2] == 0;
-
- return !output.empty() && output.back() == 0;
- };
-
- // Read at most 1 MiB ((1 << 16) * 16 - 1 Bytes) since we don't know the exact
- // size of the string. We know that `strlen(string) & 0xffff ==
- // length_lower_word`, so we read in chunks until we reach the terminator:
- // - 0: `length_lower_word` Bytes
- // - 1..16: 64 KiB (= 2^16 Bytes)
- size_t start = length_lower_word == 0 ? 1 : 0;
- for (size_t i = start; i < 16; ++i) {
- output.resize_for_overwrite(length_lower_word + i * (1 << 16));
- size_t chunk_size = i == 0 ? length_lower_word : (1 << 16);
- lldb::addr_t addr = debug_string_addr + output.size_in_bytes() - chunk_size;
-
- Status error;
- size_t bytes_read =
- DoReadMemory(addr, output.end() - chunk_size, chunk_size, error);
- if (error.Fail())
- return error.takeError();
-
- if (bytes_read != chunk_size) {
- return llvm::createStringErrorV(
- "Expected to read {0} bytes, but read {1}", chunk_size, bytes_read);
- }
-
- if (is_zero_terminated())
- break;
- }
-
- if (!is_zero_terminated())
- return llvm::createStringError("String is 1 MiB or larger");
-
- // Remove null terminator.
- output.pop_back_n(is_unicode ? 2 : 1);
- return llvm::Error::success();
-}
-
void ProcessWindows::OnDebuggerError(const Status &error, uint32_t type) {
llvm::sys::ScopedLock lock(m_mutex);
Log *log = GetLog(WindowsLog::Process);
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
index 677ade4971eed..9c6a7528e1b31 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
@@ -121,10 +121,6 @@ class ProcessWindows : public Process, public ProcessDebugger {
MemoryRegionInfo &info) override;
private:
- llvm::Error ReadDebugString(lldb::addr_t debug_string_addr, bool is_unicode,
- uint16_t length_lower_word,
- llvm::SmallVectorImpl<char> &output);
-
struct WatchpointInfo {
uint32_t slot_id;
lldb::addr_t address;
More information about the lldb-commits
mailing list