[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
Thu May 28 09:06:40 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/198630
>From 29b3b3676be9d2b7fdc2ee47d365687a74c82de8 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/3] 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 8026c98328a2c..f03dc4b835112 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 ab740353f2fe9b0d271edb69509db05f739ac39f 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/3] [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 | 236 +++++++++++++++---
1 file changed, 198 insertions(+), 38 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index c4bfa11e9635d..42d6b237c646c 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -485,33 +485,84 @@ DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info,
static std::optional<std::string>
ConvertNtDevicePathToDosPath(llvm::ArrayRef<wchar_t> nt_path) {
- std::array<wchar_t, 512> drive_strings;
- drive_strings[0] = L'\0';
- if (!::GetLogicalDriveStringsW(drive_strings.size(), drive_strings.data()))
+ Log *log = GetLog(WindowsLog::Event);
+
+ // Enumerate every volume on the system, not just lettered drives. In
+ // Windows Server Core containers the volume backing system DLLs is mounted
+ // via the container layer filesystem and has no drive letter, so the
+ // previous implementation (which iterated GetLogicalDriveStringsW) returned
+ // nullopt for kernel32.dll, KernelBase.dll and ucrtbase.dll.
+ std::array<wchar_t, MAX_PATH> volume_name;
+ HANDLE hVolFind =
+ ::FindFirstVolumeW(volume_name.data(), volume_name.size());
+ if (hVolFind == INVALID_HANDLE_VALUE) {
+ LLDB_LOG(log,
+ "ConvertNtDevicePathToDosPath: FindFirstVolumeW failed, error={0}",
+ ::GetLastError());
return std::nullopt;
+ }
+
+ std::optional<std::string> result;
+ do {
+ // FindFirstVolumeW yields names of the form "\\?\Volume{GUID}\".
+ // QueryDosDeviceW expects "Volume{GUID}" (no \\?\ prefix, no trailing \).
+ size_t vlen = ::wcsnlen(volume_name.data(), volume_name.size());
+ if (vlen < 5 || volume_name[vlen - 1] != L'\\')
+ continue;
- std::array<wchar_t, 3> drive = {L"_:"};
- for (const wchar_t *it = drive_strings.data(); *it != L'\0';
- it += wcslen(it) + 1) {
- drive[0] = it[0];
+ volume_name[vlen - 1] = L'\0';
std::array<wchar_t, MAX_PATH> device_name;
- if (!::QueryDosDeviceW(drive.data(), device_name.data(),
- device_name.size()))
+ BOOL ok = ::QueryDosDeviceW(volume_name.data() + 4, device_name.data(),
+ device_name.size());
+ volume_name[vlen - 1] = L'\\';
+ if (!ok)
+ continue;
+
+ size_t device_name_len = ::wcsnlen(device_name.data(), device_name.size());
+ if (device_name_len == 0 || device_name_len >= nt_path.size())
continue;
- size_t device_name_len = wcslen(device_name.data());
- if (device_name_len >= nt_path.size())
+ if (_wcsnicmp(nt_path.data(), device_name.data(), device_name_len) != 0)
continue;
- bool match =
- _wcsnicmp(nt_path.data(), device_name.data(), device_name_len) == 0;
- if (match && nt_path[device_name_len] == L'\\') {
- std::wstring rebuilt_path(drive.data());
- rebuilt_path.append(&nt_path[device_name_len]);
- std::string path_utf8;
- llvm::convertWideToUTF8(rebuilt_path, path_utf8);
- return path_utf8;
+ if (nt_path[device_name_len] != L'\\')
+ continue;
+
+ // Volume matched. Prefer a drive-letter or directory mount point if one
+ // exists, otherwise fall back to the volume's GUID path (which Win32 APIs
+ // can still open via the \\?\Volume{GUID} prefix).
+ std::wstring rebuilt;
+ DWORD names_size = 0;
+ ::GetVolumePathNamesForVolumeNameW(volume_name.data(), nullptr, 0,
+ &names_size);
+ if (names_size > 1) {
+ std::vector<wchar_t> names(names_size);
+ DWORD got_size = 0;
+ if (::GetVolumePathNamesForVolumeNameW(volume_name.data(), names.data(),
+ names_size, &got_size) &&
+ names[0] != L'\0') {
+ rebuilt = std::wstring(names.data());
+ }
}
- }
- return std::nullopt;
+ if (rebuilt.empty())
+ rebuilt = std::wstring(volume_name.data(), vlen);
+ // The mount point / volume name ends with a backslash, and so does the
+ // separator at nt_path[device_name_len], so drop one to avoid doubling.
+ if (!rebuilt.empty() && rebuilt.back() == L'\\')
+ rebuilt.pop_back();
+ rebuilt.append(&nt_path[device_name_len]);
+
+ std::string path_utf8;
+ llvm::convertWideToUTF8(rebuilt, path_utf8);
+ LLDB_LOG(log, "ConvertNtDevicePathToDosPath: matched, result={0}",
+ path_utf8);
+ result = path_utf8;
+ break;
+ } while (
+ ::FindNextVolumeW(hVolFind, volume_name.data(), volume_name.size()));
+
+ ::FindVolumeClose(hVolFind);
+ if (!result)
+ LLDB_LOG(log, "ConvertNtDevicePathToDosPath: no matching volume found");
+ return result;
}
static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) {
@@ -543,10 +594,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),
@@ -555,50 +611,141 @@ static std::optional<std::string> GetFileNameByLoadAddress(HANDLE hProcess,
}
// Fallback: ask the kernel for the file backing the mapping at this address.
- std::array<wchar_t, MAX_PATH + 1> mapped_filename;
- if (!::GetMappedFileNameW(hProcess, base_addr, mapped_filename.data(),
- mapped_filename.size()))
+ // Use a buffer large enough for the maximum NT path length (~32K wchar_t):
+ // GetMappedFileNameW returns 0 with ERROR_MORE_DATA if the buffer is too
+ // small, even when the actual path is well under MAX_PATH.
+ std::vector<wchar_t> mapped_filename(32 * 1024);
+ DWORD mapped_len = ::GetMappedFileNameW(
+ hProcess, base_addr, mapped_filename.data(), mapped_filename.size());
+ LLDB_LOG(log,
+ "GetFileNameByLoadAddress: GetMappedFileNameW(base={0:x}) "
+ "len={1}, error={2}",
+ base_addr, mapped_len, ::GetLastError());
+ if (!mapped_len)
return std::nullopt;
- return ConvertNtDevicePathToDosPath(mapped_filename);
+ 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;
}
@@ -606,6 +753,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);
@@ -632,8 +783,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 8958636d895aeb686f4243a3d32623c66fe6a473 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/3] [ci][windows] enable lldb diagnostic logging and collect
host logs
DIAGNOSTIC infrastructure for the kernel32 LOAD_DLL investigation. Drop
this commit before merging the lldb fix.
* monolithic-windows.sh: pass --channel windows event and --channel lldb
event to dotest via LLDB_TEST_USER_ARGS so each API test writes a
per-test host log capturing WindowsLog::Event output.
* 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/monolithic-windows.sh | 1 +
.ci/utils.sh | 39 +++++++++++++++++++++++++++++++++
.github/workflows/premerge.yaml | 4 +++-
3 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/.ci/monolithic-windows.sh b/.ci/monolithic-windows.sh
index 67cbebb1beb5c..8a11021510631 100755
--- a/.ci/monolithic-windows.sh
+++ b/.ci/monolithic-windows.sh
@@ -57,6 +57,7 @@ cmake -S "${MONOREPO_ROOT}"/llvm -B "${BUILD_DIR}" \
-D CMAKE_MODULE_LINKER_FLAGS="/MANIFEST:NO" \
-D CMAKE_SHARED_LINKER_FLAGS="/MANIFEST:NO" \
-D LLVM_ENABLE_RUNTIMES="${runtimes}" \
+ -D LLDB_TEST_USER_ARGS="--channel;windows event;--channel;lldb event" \
"${runtime_cmake_args[@]}"
start-group "ninja"
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 f03dc4b835112..173efe1ff4c98 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
More information about the lldb-commits
mailing list