[Lldb-commits] [lldb] [lldb][windows] fix long path support in debugger event loop (PR #206114)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Fri Jun 26 13:55:54 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/206114
>From bfd66fba812d6c9f08b9c4a1bf4eeb2d2d2b5942 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 26 Jun 2026 16:44:58 +0100
Subject: [PATCH 1/3] [lldb][windows] fix long path support in debugger event
loop
---
.../Process/Windows/Common/DebuggerThread.cpp | 142 ++++++++++--------
1 file changed, 79 insertions(+), 63 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index ce679d905a734..c109086a67c46 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -548,6 +548,25 @@ ConvertNtDevicePathToDosPath(llvm::ArrayRef<wchar_t> nt_path) {
return std::nullopt;
}
+// Query the file name backing the mapping at `addr` in `process` and convert
+// the resulting NT device path to a DOS path.
+static std::optional<std::string> GetMappedFileDosPath(HANDLE process,
+ LPVOID addr) {
+ std::vector<wchar_t> mapped_filename(MAX_PATH + 1);
+ DWORD mapped_len = 0;
+ while (mapped_filename.size() <= PATHCCH_MAX_CCH) {
+ mapped_len = ::GetMappedFileNameW(process, addr, mapped_filename.data(),
+ mapped_filename.size());
+ if (mapped_len == 0)
+ return std::nullopt;
+ if (mapped_len < mapped_filename.size())
+ break;
+ mapped_filename.resize(mapped_filename.size() * 2);
+ }
+ return ConvertNtDevicePathToDosPath(
+ llvm::ArrayRef<wchar_t>(mapped_filename.data(), mapped_len + 1));
+}
+
static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) {
// Check that file is not empty as we cannot map a file with zero length.
DWORD dwFileSizeHi = 0;
@@ -567,42 +586,29 @@ static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) {
if (!pMem)
return std::nullopt;
- std::array<wchar_t, MAX_PATH + 1> mapped_filename;
- if (!::GetMappedFileNameW(::GetCurrentProcess(), pMem.get(),
- mapped_filename.data(), mapped_filename.size()))
- return std::nullopt;
-
- return ConvertNtDevicePathToDosPath(mapped_filename);
+ return GetMappedFileDosPath(::GetCurrentProcess(), pMem.get());
}
static std::optional<std::string> GetFileNameByLoadAddress(HANDLE process,
LPVOID base_addr) {
- std::array<wchar_t, MAX_PATH + 1> module_filename;
- DWORD len =
- ::GetModuleFileNameExW(process, reinterpret_cast<HMODULE>(base_addr),
- module_filename.data(), module_filename.size());
- if (len > 0 && len < module_filename.size()) {
- std::string path_utf8;
- llvm::convertWideToUTF8(std::wstring(module_filename.data(), len),
- path_utf8);
- return path_utf8;
+ std::vector<wchar_t> module_filename(MAX_PATH + 1);
+ while (module_filename.size() <= PATHCCH_MAX_CCH) {
+ DWORD len =
+ ::GetModuleFileNameExW(process, reinterpret_cast<HMODULE>(base_addr),
+ module_filename.data(), module_filename.size());
+ if (len == 0)
+ break; // Not loaded as a module; fall back to the mapped-file query.
+ if (len < module_filename.size()) {
+ std::string path_utf8;
+ llvm::convertWideToUTF8(std::wstring(module_filename.data(), len),
+ path_utf8);
+ return path_utf8;
+ }
+ module_filename.resize(module_filename.size() * 2);
}
// Fallback: ask the kernel for the file backing the mapping at this address.
- std::vector<wchar_t> mapped_filename(MAX_PATH + 1);
- DWORD mapped_len = 0;
- while (mapped_filename.size() <= PATHCCH_MAX_CCH) {
- mapped_len = ::GetMappedFileNameW(
- process, base_addr, mapped_filename.data(), mapped_filename.size());
- if (mapped_len < mapped_filename.size())
- break;
- if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
- return std::nullopt;
- mapped_filename.resize(mapped_filename.size() * 2);
- }
- std::optional<std::string> dos_path = ConvertNtDevicePathToDosPath(
- llvm::ArrayRef<wchar_t>(mapped_filename.data(), mapped_len + 1));
- return dos_path;
+ return GetMappedFileDosPath(process, base_addr);
}
// Determine how many bytes can be read at `addr` in `process` before crossing
@@ -623,48 +629,58 @@ static SIZE_T BytesReadableAt(HANDLE process, LPCVOID addr) {
static std::optional<std::string> ReadRemotePathStringW(HANDLE process,
LPCVOID addr) {
- SIZE_T to_read = std::min<SIZE_T>((MAX_PATH + 1) * sizeof(wchar_t),
- BytesReadableAt(process, addr));
- to_read &= ~SIZE_T(1); // round down to a wchar_t boundary
- if (to_read < sizeof(wchar_t))
- return std::nullopt;
-
- std::array<wchar_t, MAX_PATH + 1> buf{};
- SIZE_T bytes_read = 0;
- if (!::ReadProcessMemory(process, addr, buf.data(), to_read, &bytes_read))
- return std::nullopt;
+ SIZE_T limit = std::min<SIZE_T>(PATHCCH_MAX_CCH * sizeof(wchar_t),
+ BytesReadableAt(process, addr));
+ std::vector<wchar_t> buf;
+ for (SIZE_T capacity = MAX_PATH * sizeof(wchar_t);; capacity *= 2) {
+ SIZE_T to_read = std::min<SIZE_T>(capacity, limit);
+ to_read &= ~SIZE_T(1); // round down to a wchar_t boundary
+ if (to_read < sizeof(wchar_t))
+ return std::nullopt;
- size_t max_chars = bytes_read / sizeof(wchar_t);
- size_t len = ::wcsnlen(buf.data(), max_chars);
- if (len == max_chars) // no null terminator found
- return std::nullopt;
- if (len == 0) // empty string
- return std::nullopt;
+ buf.resize(to_read / sizeof(wchar_t));
+ SIZE_T bytes_read = 0;
+ if (!::ReadProcessMemory(process, addr, buf.data(), to_read, &bytes_read))
+ return std::nullopt;
- std::string result;
- llvm::convertWideToUTF8(std::wstring(buf.data(), len), result);
- return result;
+ size_t max_chars = bytes_read / sizeof(wchar_t);
+ size_t len = ::wcsnlen(buf.data(), max_chars);
+ if (len < max_chars) { // found the null terminator
+ if (len == 0) // empty string
+ return std::nullopt;
+ std::string result;
+ llvm::convertWideToUTF8(std::wstring(buf.data(), len), result);
+ return result;
+ }
+ if (to_read >= limit) // read everything available without a terminator
+ return std::nullopt;
+ }
}
static std::optional<std::string> ReadRemotePathStringA(HANDLE process,
LPCVOID addr) {
- SIZE_T to_read =
- std::min<SIZE_T>(MAX_PATH + 1, BytesReadableAt(process, addr));
- if (to_read == 0)
- return std::nullopt;
-
- std::array<char, MAX_PATH + 1> buf{};
- SIZE_T bytes_read = 0;
- if (!::ReadProcessMemory(process, addr, buf.data(), to_read, &bytes_read))
- return std::nullopt;
+ SIZE_T limit =
+ std::min<SIZE_T>(PATHCCH_MAX_CCH, BytesReadableAt(process, addr));
+ std::vector<char> buf;
+ for (SIZE_T capacity = MAX_PATH;; capacity *= 2) {
+ SIZE_T to_read = std::min<SIZE_T>(capacity, limit);
+ if (to_read == 0)
+ return std::nullopt;
- size_t len = ::strnlen(buf.data(), bytes_read);
- if (len == bytes_read) // no null terminator found
- return std::nullopt;
- if (len == 0) // empty string
- return std::nullopt;
+ buf.resize(to_read);
+ SIZE_T bytes_read = 0;
+ if (!::ReadProcessMemory(process, addr, buf.data(), to_read, &bytes_read))
+ return std::nullopt;
- return std::string(buf.data(), len);
+ size_t len = ::strnlen(buf.data(), bytes_read);
+ if (len < bytes_read) { // found the null terminator
+ if (len == 0) // empty string
+ return std::nullopt;
+ return std::string(buf.data(), len);
+ }
+ if (to_read >= limit) // read everything available without a terminator
+ return std::nullopt;
+ }
}
// Resolve the LOAD_DLL_DEBUG_INFO::lpImageName field.
>From dce6994fad74aee8c3d8668eef20ac14265281da Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 26 Jun 2026 18:08:07 +0100
Subject: [PATCH 2/3] use string_view
---
lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index c109086a67c46..6471b32377755 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -539,7 +539,7 @@ ConvertNtDevicePathToDosPath(llvm::ArrayRef<wchar_t> nt_path) {
dos_wide.append(nt_path.begin() + dev_len, nt_path.end());
std::string result;
- llvm::convertWideToUTF8(std::wstring(dos_wide.begin(), dos_wide.end()),
+ llvm::convertWideToUTF8(std::wstring_view(dos_wide.data(), dos_wide.size()),
result);
return result;
} while (::FindNextVolumeW(vol_iter, vol_name.data(), vol_name.size()));
@@ -649,7 +649,7 @@ static std::optional<std::string> ReadRemotePathStringW(HANDLE process,
if (len == 0) // empty string
return std::nullopt;
std::string result;
- llvm::convertWideToUTF8(std::wstring(buf.data(), len), result);
+ llvm::convertWideToUTF8(std::wstring_view(buf.data(), len), result);
return result;
}
if (to_read >= limit) // read everything available without a terminator
>From 493db051d39aa2d3c3ec1227b4316ae2e8fd5872 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 26 Jun 2026 21:55:42 +0100
Subject: [PATCH 3/3] convert ALL the cases
---
lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index 6471b32377755..e5e880e725e5d 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -600,7 +600,7 @@ static std::optional<std::string> GetFileNameByLoadAddress(HANDLE process,
break; // Not loaded as a module; fall back to the mapped-file query.
if (len < module_filename.size()) {
std::string path_utf8;
- llvm::convertWideToUTF8(std::wstring(module_filename.data(), len),
+ llvm::convertWideToUTF8(std::wstring_view(module_filename.data(), len),
path_utf8);
return path_utf8;
}
More information about the lldb-commits
mailing list