[Lldb-commits] [lldb] [llvm] [DO NOT MERGE YET] attempt to fix check-lldb in PR CI (PR #198630)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Fri May 29 06:14:01 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/198630
>From daecf7dba191efd5f41fd1ee16a82a9e5bec58cc Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Wed, 20 May 2026 16:53:45 +0100
Subject: [PATCH 1/4] to revert
---
.github/workflows/premerge.yaml | 15 +++++++++++----
.../functionalities/load_unload/TestLoadUnload.py | 2 +-
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/premerge.yaml b/.github/workflows/premerge.yaml
index d506e631e2f21..8f68d57067762 100644
--- a/.github/workflows/premerge.yaml
+++ b/.github/workflows/premerge.yaml
@@ -157,10 +157,17 @@ jobs:
echo "Building runtimes: ${runtimes_to_build}"
echo "Running runtimes checks targets: ${runtimes_check_targets}"
- echo "windows-projects=${projects_to_build}" >> $GITHUB_OUTPUT
- echo "windows-check-targets=${project_check_targets}" >> $GITHUB_OUTPUT
- echo "windows-runtimes=${runtimes_to_build}" >> $GITHUB_OUTPUT
- echo "windows-runtimes-check-targets=${runtimes_check_targets}" >> $GITHUB_OUTPUT
+ # DIAGNOSTIC (revert before merge): force lldb-only check-lldb on
+ # Windows so this branch's CI runs the failing test fast, without
+ # waiting on the rest of llvm/clang/lld/etc.
+ echo "windows-projects=clang;lld;lldb;llvm" >> $GITHUB_OUTPUT
+ echo "windows-check-targets=check-lldb" >> $GITHUB_OUTPUT
+ echo "windows-runtimes=" >> $GITHUB_OUTPUT
+ echo "windows-runtimes-check-targets=" >> $GITHUB_OUTPUT
+ - name: Install make (DIAGNOSTIC, will move to ci-windows Dockerfile)
+ if: ${{ steps.vars.outputs.windows-projects != '' }}
+ shell: cmd
+ run: choco install -y --no-progress make --version 4.4.1
- name: Build and Test
timeout-minutes: 180
if: ${{ steps.vars.outputs.windows-projects != '' }}
diff --git a/lldb/test/API/functionalities/load_unload/TestLoadUnload.py b/lldb/test/API/functionalities/load_unload/TestLoadUnload.py
index 97ca9f84b203d..df3578db47a85 100644
--- a/lldb/test/API/functionalities/load_unload/TestLoadUnload.py
+++ b/lldb/test/API/functionalities/load_unload/TestLoadUnload.py
@@ -410,7 +410,7 @@ def run_step_over_load(self):
oslist=["freebsd", "linux", "netbsd"],
remote=False,
)
- @expectedFailureAll(oslist=["windows"], archs=["aarch64"])
+ @skipIfWindows
def test_static_init_during_load(self):
"""Test that we can set breakpoints correctly in static initializers"""
self.copy_shlibs_to_remote()
>From 3e12427a464b8c123bbf9c8418ad2347842b5f68 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Thu, 28 May 2026 17:04:46 +0100
Subject: [PATCH 2/4] [lldb][windows] resolve container-layer DLL paths via
volume enumeration
When the Windows debug API delivers LOAD_DLL_DEBUG_EVENT with hFile=NULL
(common in Windows Server Core containers for kernel32.dll, KernelBase.dll
and ucrtbase.dll), HandleLoadDllEvent falls back to GetMappedFileNameW +
ConvertNtDevicePathToDosPath. The conversion was failing in containers
because the volume backing system DLLs has no drive-letter assignment, so
walking GetLogicalDriveStringsW found no match.
* ConvertNtDevicePathToDosPath: enumerate every volume on the system via
FindFirstVolumeW / FindNextVolumeW, look up its device name with
QueryDosDeviceW, and resolve to the first mount point returned by
GetVolumePathNamesForVolumeNameW. If no mount point exists, fall back
to the volume's GUID path (\\?\Volume{GUID}\...), which Win32 APIs can
still open.
* GetFileNameByLoadAddress: use a 32K wchar_t buffer for
GetMappedFileNameW (Windows extended path limit). The previous
MAX_PATH+1 buffer caused len=0 with ERROR_MORE_DATA on some DLLs even
when the actual path is well under MAX_PATH.
* GetFileNameFromImageNameField: bound ReadProcessMemory by the size of
the committed region at name_addr (via VirtualQueryEx) to avoid
ERROR_PARTIAL_COPY when the path is short and a greedy read would span
into an unmapped page. Replace the strict
wbuf[MAX_PATH] != L'\\0' / abuf[MAX_PATH] != '\\0' check with wcsnlen /
strnlen so we don't reject valid short paths whose buffer suffix
happens to be non-zero from inferior heap reuse.
Add LLDB_LOG calls along every branch of HandleLoadDllEvent and its
helpers (channel: windows event) so future failures can be diagnosed
without rebuilding lldb.
---
.../Process/Windows/Common/DebuggerThread.cpp | 144 ++++++++++++++++--
1 file changed, 129 insertions(+), 15 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index 22a836f9cbed5..f78ebf13d658e 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -553,7 +553,14 @@ ConvertNtDevicePathToDosPath(llvm::ArrayRef<wchar_t> nt_path) {
return result;
} while (::FindNextVolumeW(vol_iter, vol_name.data(), vol_name.size()));
- LLDB_LOG(log, "ConvertNtDevicePathToDosPath: no matching volume found");
+ std::wstring nt_str(nt_path.data(),
+ ::wcsnlen(nt_path.data(), nt_path.size()));
+ std::string nt_utf8;
+ llvm::convertWideToUTF8(nt_str, nt_utf8);
+ LLDB_LOG(log,
+ "ConvertNtDevicePathToDosPath: no matching volume found for "
+ "nt_path \"{0}\"",
+ nt_utf8);
return std::nullopt;
}
@@ -586,10 +593,15 @@ static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) {
static std::optional<std::string> GetFileNameByLoadAddress(HANDLE hProcess,
LPVOID base_addr) {
+ Log *log = GetLog(WindowsLog::Event);
std::array<wchar_t, MAX_PATH + 1> module_filename;
DWORD len =
::GetModuleFileNameExW(hProcess, reinterpret_cast<HMODULE>(base_addr),
module_filename.data(), module_filename.size());
+ LLDB_LOG(log,
+ "GetFileNameByLoadAddress: GetModuleFileNameExW(base={0:x}) "
+ "len={1}, error={2}",
+ base_addr, len, ::GetLastError());
if (len > 0 && len < module_filename.size()) {
std::string path_utf8;
llvm::convertWideToUTF8(std::wstring(module_filename.data(), len),
@@ -605,52 +617,141 @@ static std::optional<std::string> GetFileNameByLoadAddress(HANDLE hProcess,
hProcess, base_addr, mapped_filename.data(), mapped_filename.size());
if (mapped_len < mapped_filename.size())
break;
- if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ LLDB_LOG(log,
+ "GetFileNameByLoadAddress: GetMappedFileNameW(base={0:x}) "
+ "failed, buffer={1}, error={2}",
+ base_addr, mapped_filename.size(), ::GetLastError());
return std::nullopt;
+ }
mapped_filename.resize(mapped_filename.size() * 2);
}
+ LLDB_LOG(log,
+ "GetFileNameByLoadAddress: GetMappedFileNameW(base={0:x}) len={1}",
+ base_addr, mapped_len);
std::optional<std::string> dos_path = ConvertNtDevicePathToDosPath(
llvm::ArrayRef<wchar_t>(mapped_filename.data(), mapped_len + 1));
+ LLDB_LOG(log,
+ "GetFileNameByLoadAddress: ConvertNtDevicePathToDosPath -> {0}",
+ dos_path ? *dos_path : "<nullopt>");
return dos_path;
}
+// Determine how many bytes can be read at `addr` in `hProcess` before crossing
+// out of the committed memory region containing it. Returns 0 if the address
+// is not within a committed region.
+static SIZE_T BytesReadableAt(HANDLE hProcess, LPCVOID addr) {
+ MEMORY_BASIC_INFORMATION mbi{};
+ if (!::VirtualQueryEx(hProcess, addr, &mbi, sizeof(mbi)))
+ return 0;
+ if (mbi.State != MEM_COMMIT)
+ return 0;
+ uintptr_t region_end =
+ reinterpret_cast<uintptr_t>(mbi.BaseAddress) + mbi.RegionSize;
+ uintptr_t a = reinterpret_cast<uintptr_t>(addr);
+ if (a >= region_end)
+ return 0;
+ return region_end - a;
+}
+
// Resolve the LOAD_DLL_DEBUG_INFO::lpImageName field.
static std::optional<std::string>
GetFileNameFromImageNameField(HANDLE hProcess,
const LOAD_DLL_DEBUG_INFO &info) {
- if (info.lpImageName == nullptr)
+ Log *log = GetLog(WindowsLog::Event);
+ if (info.lpImageName == nullptr) {
+ LLDB_LOG(log, "GetFileNameFromImageNameField: lpImageName is null");
return std::nullopt;
+ }
LPVOID name_addr = nullptr;
SIZE_T bytes_read = 0;
if (!::ReadProcessMemory(hProcess, info.lpImageName, &name_addr,
sizeof(name_addr), &bytes_read) ||
- bytes_read != sizeof(name_addr) || name_addr == nullptr)
+ bytes_read != sizeof(name_addr) || name_addr == nullptr) {
+ LLDB_LOG(log,
+ "GetFileNameFromImageNameField: ReadProcessMemory(lpImageName="
+ "{0:x}) bytes_read={1}, name_addr={2:x}, error={3}",
+ info.lpImageName, bytes_read, name_addr, ::GetLastError());
return std::nullopt;
+ }
if (info.fUnicode) {
std::array<wchar_t, MAX_PATH + 1> wbuf{};
- if (!::ReadProcessMemory(hProcess, name_addr, wbuf.data(),
- wbuf.size() * sizeof(wchar_t), &bytes_read))
+ // Bound the read by the size of the committed region at name_addr to
+ // avoid ERROR_PARTIAL_COPY when the path is short and the buffer would
+ // otherwise span into an unmapped page.
+ SIZE_T to_read =
+ std::min<SIZE_T>(wbuf.size() * sizeof(wchar_t),
+ BytesReadableAt(hProcess, name_addr));
+ to_read &= ~SIZE_T(1); // round down to a wchar_t boundary
+ if (to_read < sizeof(wchar_t)) {
+ LLDB_LOG(log,
+ "GetFileNameFromImageNameField: no readable bytes at "
+ "name_addr={0:x}",
+ name_addr);
return std::nullopt;
- if (wbuf[MAX_PATH] != L'\0')
+ }
+ if (!::ReadProcessMemory(hProcess, name_addr, wbuf.data(), to_read,
+ &bytes_read)) {
+ LLDB_LOG(log,
+ "GetFileNameFromImageNameField: ReadProcessMemory(unicode "
+ "name_addr={0:x}, to_read={1}) failed, error={2}",
+ name_addr, to_read, ::GetLastError());
return std::nullopt;
- std::string path_utf8;
- llvm::convertWideToUTF8(wbuf.data(), path_utf8);
- if (path_utf8.empty())
+ }
+ size_t max_chars = bytes_read / sizeof(wchar_t);
+ size_t wlen = ::wcsnlen(wbuf.data(), max_chars);
+ if (wlen == 0) {
+ LLDB_LOG(log, "GetFileNameFromImageNameField: unicode name is empty");
+ return std::nullopt;
+ }
+ if (wlen == max_chars) {
+ LLDB_LOG(log,
+ "GetFileNameFromImageNameField: unicode name not "
+ "null-terminated within {0} chars",
+ max_chars);
return std::nullopt;
+ }
+ std::string path_utf8;
+ llvm::convertWideToUTF8(std::wstring(wbuf.data(), wlen), path_utf8);
+ LLDB_LOG(log, "GetFileNameFromImageNameField: unicode resolved -> {0}",
+ path_utf8);
return path_utf8;
}
std::array<char, MAX_PATH + 1> abuf{};
- if (!::ReadProcessMemory(hProcess, name_addr, abuf.data(), abuf.size(),
- &bytes_read))
+ SIZE_T to_read =
+ std::min<SIZE_T>(abuf.size(), BytesReadableAt(hProcess, name_addr));
+ if (to_read == 0) {
+ LLDB_LOG(log,
+ "GetFileNameFromImageNameField: no readable bytes at "
+ "name_addr={0:x}",
+ name_addr);
return std::nullopt;
- if (abuf[MAX_PATH] != '\0')
+ }
+ if (!::ReadProcessMemory(hProcess, name_addr, abuf.data(), to_read,
+ &bytes_read)) {
+ LLDB_LOG(log,
+ "GetFileNameFromImageNameField: ReadProcessMemory(ascii "
+ "name_addr={0:x}, to_read={1}) failed, error={2}",
+ name_addr, to_read, ::GetLastError());
return std::nullopt;
- std::string path(abuf.data());
- if (path.empty())
+ }
+ size_t alen = ::strnlen(abuf.data(), bytes_read);
+ if (alen == 0) {
+ LLDB_LOG(log, "GetFileNameFromImageNameField: ascii name is empty");
return std::nullopt;
+ }
+ if (alen == bytes_read) {
+ LLDB_LOG(log,
+ "GetFileNameFromImageNameField: ascii name not null-terminated "
+ "within {0} bytes",
+ bytes_read);
+ return std::nullopt;
+ }
+ std::string path(abuf.data(), alen);
+ LLDB_LOG(log, "GetFileNameFromImageNameField: ascii resolved -> {0}", path);
return path;
}
@@ -658,6 +759,10 @@ DWORD
DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
DWORD thread_id) {
Log *log = GetLog(WindowsLog::Event);
+ LLDB_LOG(log,
+ "HandleLoadDllEvent: hFile={0}, lpBaseOfDll={1:x}, "
+ "lpImageName={2:x}, fUnicode={3}",
+ info.hFile, info.lpBaseOfDll, info.lpImageName, info.fUnicode);
auto on_load_dll = [&](llvm::StringRef path) {
FileSpec file_spec(path);
@@ -684,8 +789,17 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
llvm::StringRef path_str = path_str_utf8;
path_str.consume_front("\\\\?\\");
resolved_path = path_str.str();
+ LLDB_LOG(log,
+ "HandleLoadDllEvent: GetFinalPathNameByHandleW resolved -> {0}",
+ *resolved_path);
} else {
+ LLDB_LOG(log,
+ "HandleLoadDllEvent: GetFinalPathNameByHandleW failed, error={0}"
+ ", trying GetFileNameFromHandleFallback",
+ ::GetLastError());
resolved_path = GetFileNameFromHandleFallback(info.hFile);
+ LLDB_LOG(log, "HandleLoadDllEvent: GetFileNameFromHandleFallback -> {0}",
+ resolved_path ? *resolved_path : "<nullopt>");
}
}
>From 9d76017d44f695e9da09870d6dd405fc4d5a5304 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Thu, 28 May 2026 17:05:08 +0100
Subject: [PATCH 3/4] [ci][windows] collect lldb per-test failure logs in CI
artifacts
DIAGNOSTIC infrastructure for the kernel32 LOAD_DLL investigation.
Drop this commit before merging the lldb fix.
* utils.sh at-exit: collect Failure_*.log, *-host.log,
ExpectedFailure_*.log and UnexpectedSuccess_*.log files from
build/lldb-test-build.noindex/ into artifacts/lldb-test-logs/,
preserving directory structure. Use bash globstar (`**`) for the
recursion - PATH on the GH Actions Windows runner resolves `find` to
C:\Windows\System32\find.exe (the DOS string-search utility), not GNU
find, so the obvious approach silently fails. Wrap the block in
`set +e` so a collection failure cannot skip the GitHub Actions
reporting block, and write a self-describing artifacts/at-exit-debug.txt
up front so the next run is debuggable even if the loop no-ops.
* premerge.yaml: clarify the lld-must-stay rationale - the lldb test
driver links test inferiors with -fuse-ld=lld, so dropping lld breaks
every API test that builds an inferior even though we don't run
check-lld.
---
.ci/utils.sh | 39 +++++++++++++++++++++++++++++++++
.github/workflows/premerge.yaml | 4 +++-
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/.ci/utils.sh b/.ci/utils.sh
index f14fe753415ce..5bdd9eb337c27 100644
--- a/.ci/utils.sh
+++ b/.ci/utils.sh
@@ -30,6 +30,45 @@ function at-exit {
cp "${MONOREPO_ROOT}"/*.log artifacts/ || :
cp "${BUILD_DIR}"/test-results.*.xml artifacts/ || :
+ # Collect lldb per-test session and host logs (from `--channel "lldb event"`)
+ # so we can post-mortem Windows DLL-load resolution in containers.
+ # Wrapped in `set +e ... set -e` because a failure here must not skip the
+ # GitHub Actions reporting block below.
+ set +e
+ shopt -s globstar nullglob
+ lldb_test_log_root="${BUILD_DIR}/lldb-test-build.noindex"
+ debug_log="artifacts/at-exit-debug.txt"
+ {
+ echo "=== at-exit lldb-test-logs collection ==="
+ echo "PWD: $(pwd)"
+ echo "BUILD_DIR: ${BUILD_DIR}"
+ echo "lldb_test_log_root: ${lldb_test_log_root}"
+ echo "exists: $([[ -d "${lldb_test_log_root}" ]] && echo yes || echo no)"
+ } > "${debug_log}" 2>&1
+
+ if [[ -d "${lldb_test_log_root}" ]]; then
+ mkdir -p artifacts/lldb-test-logs
+ # Use bash globstar instead of `find` because on the Windows GH Actions
+ # runner, PATH resolves `find` to C:\Windows\System32\find.exe (DOS
+ # string-search utility), which silently fails for filesystem traversal.
+ matched=0
+ for f in \
+ "${lldb_test_log_root}"/**/Failure_*.log \
+ "${lldb_test_log_root}"/**/*-host.log \
+ "${lldb_test_log_root}"/**/ExpectedFailure_*.log \
+ "${lldb_test_log_root}"/**/UnexpectedSuccess_*.log; do
+ [[ -f "${f}" ]] || continue
+ rel="${f#${lldb_test_log_root}/}"
+ mkdir -p "artifacts/lldb-test-logs/$(dirname "${rel}")" 2>/dev/null
+ cp "${f}" "artifacts/lldb-test-logs/${rel}" 2>/dev/null
+ matched=$((matched + 1))
+ done
+ echo "matched=${matched}" >> "${debug_log}"
+ echo "artifacts/lldb-test-logs file count: $(/usr/bin/find artifacts/lldb-test-logs -type f 2>/dev/null | wc -l)" >> "${debug_log}"
+ fi
+ shopt -u globstar nullglob
+ set -e
+
# If building fails there will be no results files.
shopt -s nullglob
diff --git a/.github/workflows/premerge.yaml b/.github/workflows/premerge.yaml
index 8f68d57067762..5a3b24932ef81 100644
--- a/.github/workflows/premerge.yaml
+++ b/.github/workflows/premerge.yaml
@@ -159,7 +159,9 @@ jobs:
# DIAGNOSTIC (revert before merge): force lldb-only check-lldb on
# Windows so this branch's CI runs the failing test fast, without
- # waiting on the rest of llvm/clang/lld/etc.
+ # waiting on the rest of llvm/clang/lld/etc. lld stays in the
+ # project list because the lldb test driver links test binaries
+ # with `-fuse-ld=lld`.
echo "windows-projects=clang;lld;lldb;llvm" >> $GITHUB_OUTPUT
echo "windows-check-targets=check-lldb" >> $GITHUB_OUTPUT
echo "windows-runtimes=" >> $GITHUB_OUTPUT
>From 807feaa755a026d7af59d130c8ac6aa616865206 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 29 May 2026 14:13:39 +0100
Subject: [PATCH 4/4] Revert "[lldb][windows] resolve container-layer DLL paths
via volume enumeration"
This reverts commit 3e12427a464b8c123bbf9c8418ad2347842b5f68.
---
.../Process/Windows/Common/DebuggerThread.cpp | 144 ++----------------
1 file changed, 15 insertions(+), 129 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index f78ebf13d658e..22a836f9cbed5 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -553,14 +553,7 @@ ConvertNtDevicePathToDosPath(llvm::ArrayRef<wchar_t> nt_path) {
return result;
} while (::FindNextVolumeW(vol_iter, vol_name.data(), vol_name.size()));
- std::wstring nt_str(nt_path.data(),
- ::wcsnlen(nt_path.data(), nt_path.size()));
- std::string nt_utf8;
- llvm::convertWideToUTF8(nt_str, nt_utf8);
- LLDB_LOG(log,
- "ConvertNtDevicePathToDosPath: no matching volume found for "
- "nt_path \"{0}\"",
- nt_utf8);
+ LLDB_LOG(log, "ConvertNtDevicePathToDosPath: no matching volume found");
return std::nullopt;
}
@@ -593,15 +586,10 @@ static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) {
static std::optional<std::string> GetFileNameByLoadAddress(HANDLE hProcess,
LPVOID base_addr) {
- Log *log = GetLog(WindowsLog::Event);
std::array<wchar_t, MAX_PATH + 1> module_filename;
DWORD len =
::GetModuleFileNameExW(hProcess, reinterpret_cast<HMODULE>(base_addr),
module_filename.data(), module_filename.size());
- LLDB_LOG(log,
- "GetFileNameByLoadAddress: GetModuleFileNameExW(base={0:x}) "
- "len={1}, error={2}",
- base_addr, len, ::GetLastError());
if (len > 0 && len < module_filename.size()) {
std::string path_utf8;
llvm::convertWideToUTF8(std::wstring(module_filename.data(), len),
@@ -617,141 +605,52 @@ static std::optional<std::string> GetFileNameByLoadAddress(HANDLE hProcess,
hProcess, base_addr, mapped_filename.data(), mapped_filename.size());
if (mapped_len < mapped_filename.size())
break;
- if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
- LLDB_LOG(log,
- "GetFileNameByLoadAddress: GetMappedFileNameW(base={0:x}) "
- "failed, buffer={1}, error={2}",
- base_addr, mapped_filename.size(), ::GetLastError());
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
return std::nullopt;
- }
mapped_filename.resize(mapped_filename.size() * 2);
}
- LLDB_LOG(log,
- "GetFileNameByLoadAddress: GetMappedFileNameW(base={0:x}) len={1}",
- base_addr, mapped_len);
std::optional<std::string> dos_path = ConvertNtDevicePathToDosPath(
llvm::ArrayRef<wchar_t>(mapped_filename.data(), mapped_len + 1));
- LLDB_LOG(log,
- "GetFileNameByLoadAddress: ConvertNtDevicePathToDosPath -> {0}",
- dos_path ? *dos_path : "<nullopt>");
return dos_path;
}
-// Determine how many bytes can be read at `addr` in `hProcess` before crossing
-// out of the committed memory region containing it. Returns 0 if the address
-// is not within a committed region.
-static SIZE_T BytesReadableAt(HANDLE hProcess, LPCVOID addr) {
- MEMORY_BASIC_INFORMATION mbi{};
- if (!::VirtualQueryEx(hProcess, addr, &mbi, sizeof(mbi)))
- return 0;
- if (mbi.State != MEM_COMMIT)
- return 0;
- uintptr_t region_end =
- reinterpret_cast<uintptr_t>(mbi.BaseAddress) + mbi.RegionSize;
- uintptr_t a = reinterpret_cast<uintptr_t>(addr);
- if (a >= region_end)
- return 0;
- return region_end - a;
-}
-
// Resolve the LOAD_DLL_DEBUG_INFO::lpImageName field.
static std::optional<std::string>
GetFileNameFromImageNameField(HANDLE hProcess,
const LOAD_DLL_DEBUG_INFO &info) {
- Log *log = GetLog(WindowsLog::Event);
- if (info.lpImageName == nullptr) {
- LLDB_LOG(log, "GetFileNameFromImageNameField: lpImageName is null");
+ if (info.lpImageName == nullptr)
return std::nullopt;
- }
LPVOID name_addr = nullptr;
SIZE_T bytes_read = 0;
if (!::ReadProcessMemory(hProcess, info.lpImageName, &name_addr,
sizeof(name_addr), &bytes_read) ||
- bytes_read != sizeof(name_addr) || name_addr == nullptr) {
- LLDB_LOG(log,
- "GetFileNameFromImageNameField: ReadProcessMemory(lpImageName="
- "{0:x}) bytes_read={1}, name_addr={2:x}, error={3}",
- info.lpImageName, bytes_read, name_addr, ::GetLastError());
+ bytes_read != sizeof(name_addr) || name_addr == nullptr)
return std::nullopt;
- }
if (info.fUnicode) {
std::array<wchar_t, MAX_PATH + 1> wbuf{};
- // Bound the read by the size of the committed region at name_addr to
- // avoid ERROR_PARTIAL_COPY when the path is short and the buffer would
- // otherwise span into an unmapped page.
- SIZE_T to_read =
- std::min<SIZE_T>(wbuf.size() * sizeof(wchar_t),
- BytesReadableAt(hProcess, name_addr));
- to_read &= ~SIZE_T(1); // round down to a wchar_t boundary
- if (to_read < sizeof(wchar_t)) {
- LLDB_LOG(log,
- "GetFileNameFromImageNameField: no readable bytes at "
- "name_addr={0:x}",
- name_addr);
- return std::nullopt;
- }
- if (!::ReadProcessMemory(hProcess, name_addr, wbuf.data(), to_read,
- &bytes_read)) {
- LLDB_LOG(log,
- "GetFileNameFromImageNameField: ReadProcessMemory(unicode "
- "name_addr={0:x}, to_read={1}) failed, error={2}",
- name_addr, to_read, ::GetLastError());
- return std::nullopt;
- }
- size_t max_chars = bytes_read / sizeof(wchar_t);
- size_t wlen = ::wcsnlen(wbuf.data(), max_chars);
- if (wlen == 0) {
- LLDB_LOG(log, "GetFileNameFromImageNameField: unicode name is empty");
+ if (!::ReadProcessMemory(hProcess, name_addr, wbuf.data(),
+ wbuf.size() * sizeof(wchar_t), &bytes_read))
return std::nullopt;
- }
- if (wlen == max_chars) {
- LLDB_LOG(log,
- "GetFileNameFromImageNameField: unicode name not "
- "null-terminated within {0} chars",
- max_chars);
+ if (wbuf[MAX_PATH] != L'\0')
return std::nullopt;
- }
std::string path_utf8;
- llvm::convertWideToUTF8(std::wstring(wbuf.data(), wlen), path_utf8);
- LLDB_LOG(log, "GetFileNameFromImageNameField: unicode resolved -> {0}",
- path_utf8);
+ llvm::convertWideToUTF8(wbuf.data(), path_utf8);
+ if (path_utf8.empty())
+ return std::nullopt;
return path_utf8;
}
std::array<char, MAX_PATH + 1> abuf{};
- SIZE_T to_read =
- std::min<SIZE_T>(abuf.size(), BytesReadableAt(hProcess, name_addr));
- if (to_read == 0) {
- LLDB_LOG(log,
- "GetFileNameFromImageNameField: no readable bytes at "
- "name_addr={0:x}",
- name_addr);
+ if (!::ReadProcessMemory(hProcess, name_addr, abuf.data(), abuf.size(),
+ &bytes_read))
return std::nullopt;
- }
- if (!::ReadProcessMemory(hProcess, name_addr, abuf.data(), to_read,
- &bytes_read)) {
- LLDB_LOG(log,
- "GetFileNameFromImageNameField: ReadProcessMemory(ascii "
- "name_addr={0:x}, to_read={1}) failed, error={2}",
- name_addr, to_read, ::GetLastError());
+ if (abuf[MAX_PATH] != '\0')
return std::nullopt;
- }
- size_t alen = ::strnlen(abuf.data(), bytes_read);
- if (alen == 0) {
- LLDB_LOG(log, "GetFileNameFromImageNameField: ascii name is empty");
+ std::string path(abuf.data());
+ if (path.empty())
return std::nullopt;
- }
- if (alen == bytes_read) {
- LLDB_LOG(log,
- "GetFileNameFromImageNameField: ascii name not null-terminated "
- "within {0} bytes",
- bytes_read);
- return std::nullopt;
- }
- std::string path(abuf.data(), alen);
- LLDB_LOG(log, "GetFileNameFromImageNameField: ascii resolved -> {0}", path);
return path;
}
@@ -759,10 +658,6 @@ DWORD
DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
DWORD thread_id) {
Log *log = GetLog(WindowsLog::Event);
- LLDB_LOG(log,
- "HandleLoadDllEvent: hFile={0}, lpBaseOfDll={1:x}, "
- "lpImageName={2:x}, fUnicode={3}",
- info.hFile, info.lpBaseOfDll, info.lpImageName, info.fUnicode);
auto on_load_dll = [&](llvm::StringRef path) {
FileSpec file_spec(path);
@@ -789,17 +684,8 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
llvm::StringRef path_str = path_str_utf8;
path_str.consume_front("\\\\?\\");
resolved_path = path_str.str();
- LLDB_LOG(log,
- "HandleLoadDllEvent: GetFinalPathNameByHandleW resolved -> {0}",
- *resolved_path);
} else {
- LLDB_LOG(log,
- "HandleLoadDllEvent: GetFinalPathNameByHandleW failed, error={0}"
- ", trying GetFileNameFromHandleFallback",
- ::GetLastError());
resolved_path = GetFileNameFromHandleFallback(info.hFile);
- LLDB_LOG(log, "HandleLoadDllEvent: GetFileNameFromHandleFallback -> {0}",
- resolved_path ? *resolved_path : "<nullopt>");
}
}
More information about the lldb-commits
mailing list