This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new b41856ab7e9 [fix](build) Move the macOS toolchain to LLVM 22 so ASAN
works on macOS 26.4+ (#68595)
b41856ab7e9 is described below
commit b41856ab7e9915e3b9ffc76caa96a27baf62aa54
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Wed Sep 30 21:00:34 2026 +0800
[fix](build) Move the macOS toolchain to LLVM 22 so ASAN works on macOS
26.4+ (#68595)
### What problem does this PR solve?
Issue Number: N/A
Related PR: apache/doris-website#4180 (the macOS build guide change that
goes with this PR)
Problem Summary:
**Context.**
On macOS the only entry point for the BE unit tests is `run-be-ut.sh`,
and it defaults to `BUILD_TYPE_UT=ASAN`. Since macOS 26.4 that default
cannot start a single process: every binary linked against llvm.org's
`libclang_rt.asan_osx_dynamic` deadlocks during runtime initialisation,
before `main()`. `env.sh` selects that same toolchain on macOS
(`llvm@20`), so a developer who follows the documented setup gets a
build that hangs rather than one that fails.
How the macOS toolchain gets chosen matters for the rest of this
description:
- **Selection.** `env.sh` writes `custom_env_mac.sh`, which puts `$(brew
--prefix)/opt/<formula>/bin` on `PATH` for every formula in its
`CELLARS` list. Unless `DORIS_CLANG_HOME` is already set, `env.sh` then
takes the first `clang` on `PATH` and derives `CC`/`CXX` from it.
- **Installation.** `CELLARS` only selects; it installs nothing.
Developers install Homebrew formulae by following the macOS build guide,
and each CI workflow installs from its own `brew install` list.
- **Third-party libraries.** `thirdparty/installed` comes from one of
two places:
- the published `doris-thirdparty-prebuilt-darwin-*.tar.xz`, which
`apache/doris-thirdparty`'s `build-target.yml` builds by running
`thirdparty/build-thirdparty.sh` from apache/doris master;
- a local run of that same script. `build.sh` also starts one by itself
when `thirdparty/installed` lacks a library it checks for.
**1. The problem, and what it cost**
*The deadlock.* `sample` on the stalled process shows the whole chain:
```
__malloc_init (libsystem_malloc) <- early libSystem
init calls malloc_default_zone()
`- wrap_malloc_default_zone (asan runtime)
`- AsanInitFromRtl -> AsanInitInternal -> InitializeShadowMemory
`- MemoryRangeIsAvailable -> MemoryMappingLayout::Next ->
get_dyld_hdr()
`- dyld_shared_cache_iterate_text_swift <- macOS 26.4
reimplemented this in Swift
`- _Block_copy -> malloc
`- __sanitizer_mz_malloc (asan's own malloc)
`- AsanInitFromRtl() <- re-enters init
`- StaticSpinMutex::LockSlow <- spins on the
lock it already holds
```
The cause is an OS-side change, not a Doris one. It also does not mean
ASAN is broken on macOS 26: Apple's own clang sanitizer runtime runs
fine on the same host, and only llvm.org's compiler-rt is affected.
Upstream fixed it by weak-importing `_dyld_get_dyld_header` and using
it, when present, instead of walking the shared cache
(llvm/llvm-project#182943, main `2e7d07a`; backport #188913,
release/22.x `7b6514c`). **The fix shipped only in 22.1.8.** 20.1.8 is
the last 20.x release and 21.1.8 the last 21.x, so no version of
`llvm@20` can ever be made to work.
*Why it looks like a hang and not an error.*
- `be/CMakeLists.txt` reaches `storage/index/ann` unconditionally, and
that directory's `cmake-protect` target calls `add_subdirectory()` on
`contrib/openblas`.
- OpenBLAS runs an instrumented `getarch` probe from its **configure**
step (`contrib/openblas/cmake/prebuild.cmake:1513`,
`execute_process(COMMAND .../getarch 0 ...)`), and `execute_process` has
no timeout.
- So every ASAN build stops at `-- Running getarch` and never moves
again. It sits at about 90% CPU and prints no diagnostic.
`run-be-ut.sh` on macOS was therefore unusable at its default setting,
and the failure gave the developer nothing to act on.
*The second obstacle, once the toolchain is bumped.*
- clang 22 added `-Wc2y-extensions` and folds it into `-Wpedantic`.
- `__COUNTER__` only reached the C standard in C2y (WG14 N3457).
`be/src/runtime/runtime_profile.h:73-85` uses it to give two
`SCOPED_TIMER` / `SCOPED_RAW_TIMER` expansions on the same line distinct
names.
- As a result, a clang 22 build of any TU that includes that header
fails under `-Werror`. Three representative unity TUs were enough to hit
it, so the failure is not confined to one module.
*The third obstacle: clang 22 cannot build the third-party libraries.*
The first revision of this PR moved every macOS toolchain reference to
`llvm@22`, including the third-party build. CI failed both macOS
third-party jobs (7m49s, 6m23s) on the first library that exercises a
new clang 22 error:
```
unixODBC 2.3.7 src/SQLBrowseConnectW.c:424:82:
error: incompatible pointer types passing 'SQLSMALLINT *' (aka 'short *')
to parameter of type 'int *' [-Wincompatible-pointer-types]
```
clang 16, clang 20 and Apple clang 21 report this as a warning; clang 22
makes it an error. unixODBC is only the *first* failure: the build
aborts there, so every library after it is untested against clang 22.
The macOS third-party libraries therefore have to stay on `llvm@20`
while the BE moves to `llvm@22`.
Leaving the third-party build alone does not achieve that, because it
sources `env.sh`:
- With `CELLARS` naming `llvm@22`, a machine without `llvm@22` gets a
non-existent directory on `PATH`.
- `command -v clang` then falls through to `/usr/bin/clang` (Apple
clang), which is worse than either explicit choice.
- The job that publishes the prebuilt archive in
`apache/doris-thirdparty` is exactly such a machine: it installs only
`llvm@20` and sets no `DORIS_CLANG_HOME`.
*Two latent defects clang 22 then surfaced.* The bump is not a pure
version change. clang 22's stricter diagnostics stop the build on two
pre-existing bugs, and both are worth fixing on their own merits.
| # | Site | Diagnostic | What is actually wrong |
|---|---|---|---|
| 1 | `be/src/load/group_commit/wal/wal_dirs_info.cpp:98` |
`-Wunused-result` | `LOG(INFO) << "… err: {}", e.what();` — the `,` is
the comma operator, not an argument separator, so the statement is
`(LOG(INFO) << "…{}") , (e.what())`. The `{}` is never substituted, and
`e.what()` is evaluated and then discarded: **the error message has
never been logged**, only the literal `{}`. A repo-wide scan for the
same shape finds exactly this one site (`cloud/src/common/logging.h`
matches only inside macro definitions and is not a bug). |
| 2 | `common/cpp/sync_point.cpp:208,210` | `-Wthread-safety-analysis` |
The function holds `std::unique_lock lock(mutex_)`, then releases and
re-takes the mutex with a raw `mutex_.unlock()` / `mutex_.lock()` pair
around the callback, which bypasses the lock's ownership tracking. If
the callback throws, the re-lock is skipped while `~unique_lock` still
believes it owns the mutex, so the destructor unlocks a mutex that is
not held (UB) and `num_callbacks_running_` is never decremented.
`lock.unlock()` / `lock.lock()` still runs the callback unlocked, as
intended, and keeps the ownership state correct on every path. |
**2. What this PR does, and why it helps**
- **`env.sh`**: the macOS `CELLARS` list moves from `llvm@20` to
`llvm@22`, with a comment recording that 22.1.8 is the minimum and why.
This is the single line that decides which clang a macOS developer
builds the BE with.
- **`thirdparty/build-thirdparty.sh`**: on Darwin, right after sourcing
`env.sh` and next to the existing environment sanitization, the script
unconditionally points `DORIS_CLANG_HOME`, `CC` and `CXX` at `$(brew
--prefix llvm@20)` and puts its `bin/` first on `PATH`. If `llvm@20` is
not installed, it stops with a `brew install llvm@20` hint rather than
falling back to another compiler.
- The override has to come after `env.sh` and has to be unconditional.
`env.sh` sources `custom_env.sh`, which may export `DORIS_CLANG_HOME`
for the BE, and `build.sh` has already exported the BE's `llvm@22`
values before it starts this script.
- Every macOS third-party build runs this script: `build.sh`'s automatic
rebuild, a manual run, the pull request check in `build-thirdparty.yml`,
and the `apache/doris-thirdparty` job that publishes
`doris-thirdparty-prebuilt-darwin-*.tar.xz`. This one place therefore
keeps all of them, and the published archive, on `llvm@20`.
- `.github/workflows/build-thirdparty.yml` is unchanged: its macOS jobs
already install `llvm@20`.
- **`be/CMakeLists.txt`**: add `-Wno-c2y-extensions` for clang 19 and
newer, in its own `add_compile_options` call **after** `-Wpedantic`.
- The position matters: a later `-Wpedantic` turns the group back on.
That is also why passing the flag through `EXTRA_CXX_FLAGS` does not
work: that variable lands near the front of the command line.
- The version gate matters because the `c2y-extensions` group only
exists from clang 19 on. clang rejects an unknown `-Wno-` option like
any other unknown warning option (`-Wunknown-warning-option`, fatal
under `-Werror`), and this file accepts clang 16 and newer.
- For clang 19 and newer the command line is exactly what it was.
- **`.github/workflows/be-ut-mac.yml`**: install `llvm@22` for the BE
and keep `llvm@20` next to it.
- The job builds the BE on top of the downloaded darwin-arm64 prebuilt.
When that archive lacks a library `build.sh` checks for, `build.sh`
rebuilds the third-party libraries from source, and that rebuild now
needs `llvm@20`. The archive falls behind like this whenever master
changes the set of checked libraries before the next archive is
published.
- Both formulae are keg-only, so installing both does not change which
one the BE uses.
- **`be/src/load/group_commit/wal/wal_dirs_info.cpp` and
`common/cpp/sync_point.cpp`**: the two fixes from the table above. Each
is one line, the smallest change that removes the defect rather than
suppressing the warning. No `-Wno-unused-result` /
`-Wno-thread-safety-analysis` is added, because those diagnostics point
at real bugs and should keep firing.
- **apache/doris-website#4180**: the macOS build guide installs
`llvm@22` and `llvm@20` instead of only `llvm@20`. Without it, a
developer who follows the guide after this PR has no `llvm@22`, and the
BE silently builds with Apple clang.
What this PR deliberately does not do is add `llvm@20` to `env.sh`'s
`CELLARS`:
- `CELLARS` installs nothing.
- `build-thirdparty.sh` does not use `PATH` to find `llvm@20`.
- The `CELLARS` loop prepends each entry to `PATH`. An `llvm@20` entry
after `llvm@22` would put the BE back on `llvm@20` and bring the
deadlock back.
**3. The classes, and how they call each other**
```
env.sh CELLARS := llvm@22 (selects a
compiler; installs nothing)
|- generates custom_env_mac.sh: $(brew --prefix)/opt/<cellar>/bin
prepended to PATH
|- DORIS_CLANG_HOME := dirname($(command -v clang))/.. -> CC / CXX /
ASAN_SYMBOLIZER_PATH
'- is sourced by:
|- build.sh, run-be-ut.sh -> BE: llvm@22
'- thirdparty/build-thirdparty.sh:55
'- :100-110 on Darwin: DORIS_CLANG_HOME / CC / CXX := $(brew
--prefix llvm@20)
llvm@20 missing -> exit 1, "brew install llvm@20"
-> third-party
libraries: llvm@20
run by: build.sh (library missing from
thirdparty/installed), manual runs,
build-thirdparty.yml (PR check), doris-thirdparty
build-target.yml (publisher)
brew install lists, i.e. who installs which LLVM:
macOS build guide (apache/doris-website#4180) llvm@22 llvm@20
.github/workflows/be-ut-mac.yml llvm@22 llvm@20
.github/workflows/build-thirdparty.yml llvm@20
apache/doris-thirdparty build-target.yml llvm@20
be/CMakeLists.txt if (COMPILER_CLANG)
|- add_compile_options(-Wpedantic ...) <- enables the c2y
group
'- if (CMAKE_CXX_COMPILER_VERSION >= 19)
add_compile_options(-Wno-c2y-extensions) <- must stay after
the line above
|
be/src/runtime/runtime_profile.h:73-85
'- MACRO_CONCAT(SCOPED_TIMER, __COUNTER__) <- the only
__COUNTER__ use in be/src, be/test
|
be/src/storage/index/ann/cmake-protect/CMakeLists.txt:48
'- add_subdirectory(contrib/openblas)
'- cmake/prebuild.cmake:1513 execute_process(getarch) <- where
the hang surfaced
```
### Release note
None
### Check List (For Author)
- Test: Manual test on macOS 26.5.1 (arm64, dyld-1378), plus CI.
- `clang -fsanitize=address` hello world exits 0 with llvm@22 and still
deadlocks with llvm@20 on the same host.
- `sh run-be-ut.sh` with no environment overrides now selects
`Clang-22.1.8` and clears the `-- Running getarch` point that previously
hung forever: zero FAILED targets, and `doris_be_test` links.
- `sh run-be-ut.sh --run --filter=FormatRoundTest.*` starts the ASAN
binary and passes 8 tests.
- `-Wno-c2y-extensions` gate, tested on a `__COUNTER__` TU under
`-Wpedantic -Werror`: clang 16.0.6 rejects the flag (`unknown warning
option`), which is why it is gated; clang 20.1.8 accepts it; clang
22.1.8 fails without it and passes with it.
- `build-thirdparty.sh` compiler override, checked under `bash -x` with
both `custom_env.sh` and `build.sh` exporting the `llvm@22` values: the
effective `CC`/`CXX` and the first clang on `PATH` are llvm@20. With
`llvm@20` missing, the script exits 1 with the brew hint before building
anything.
- `be-ut-mac.yml`: the "Build BE" step script passes `bash -n` on bash
3.2 and 5.3, and its `cellars` array holds both `llvm@22` and `llvm@20`.
The workflow runs only on pushes to master and on schedule, so this PR
cannot run it.
- `build-support/check-build-hygiene.sh` passes.
- CI: `Build Third Party Libraries (macOS)`, `(macOS-arm64)` and
`(Linux)` pass at `a955357a30f` (run 36542107899), which already
contains the `build-thirdparty.sh` override. The first revision, which
moved these builds to llvm@22, failed both macOS jobs on unixODBC.
- Regression test / unit test: N/A (toolchain, flag and workflow
change).
- Behavior changed: Yes, for macOS builds only.
- The BE builds with llvm@22 instead of llvm@20.
- The third-party libraries, including the published prebuilt, stay on
llvm@20. Building them from source now requires llvm@20 installed next
to llvm@22; without it, the build stops with a `brew install llvm@20`
hint.
- Existing macOS developers need to run `brew install llvm@22`. Without
it, `env.sh` falls back to Apple clang in `/usr/bin` for newly
configured build directories.
- Does this need documentation: Yes, apache/doris-website#4180.
---------
Co-authored-by: Claude Code <[email protected]>
---
.github/workflows/be-ut-mac.yml | 5 +++++
be/CMakeLists.txt | 12 ++++++++++++
be/src/load/group_commit/wal/wal_dirs_info.cpp | 2 +-
common/cpp/sync_point.cpp | 6 +++---
env.sh | 8 +++++++-
thirdparty/build-thirdparty.sh | 22 ++++++++++++++++++++++
6 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/be-ut-mac.yml b/.github/workflows/be-ut-mac.yml
index 201e8d0a640..686335c26c7 100644
--- a/.github/workflows/be-ut-mac.yml
+++ b/.github/workflows/be-ut-mac.yml
@@ -70,6 +70,11 @@ jobs:
'openjdk@11'
'maven'
'node'
+ # llvm@22 builds the BE (env.sh selects it). llvm@20 builds the
+ # third-party libraries: build.sh rebuilds them from source when
the
+ # prebuilt archive below is missing one it checks for, and
+ # thirdparty/build-thirdparty.sh refuses to run on macOS without
llvm@20.
+ 'llvm@22'
'llvm@20'
'libomp'
)
diff --git a/be/CMakeLists.txt b/be/CMakeLists.txt
index 02da8f5dc2e..874426d1dc9 100644
--- a/be/CMakeLists.txt
+++ b/be/CMakeLists.txt
@@ -468,6 +468,18 @@ if (COMPILER_CLANG)
# -Werror failure. The diagnostic carries no
correctness
# signal, so drop it.
-Wno-pass-failed)
+ # clang 22 reports __COUNTER__ under -Wpedantic as a C2y extension
+ # (-Wc2y-extensions). runtime_profile.h expands it to give two
SCOPED_TIMER /
+ # SCOPED_RAW_TIMER uses on the same line distinct names -- a long-standing
+ # clang/gcc/MSVC extension that only reached the standard in C2y, and the
+ # diagnostic carries no correctness signal. The c2y-extensions group only
exists
+ # from clang 19 on, and clang rejects an unknown -Wno- option like any
other
+ # unknown warning option (-Wunknown-warning-option, fatal under -Werror),
so
+ # clang 16-18 must not see the flag. It must also stay after -Wpedantic
above:
+ # a -Wpedantic later on the command line turns the group back on.
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "19")
+ add_compile_options(-Wno-c2y-extensions)
+ endif()
if (USE_LIBCPP)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-stdlib=libc++>)
add_definitions(-DUSE_LIBCPP)
diff --git a/be/src/load/group_commit/wal/wal_dirs_info.cpp
b/be/src/load/group_commit/wal/wal_dirs_info.cpp
index 171f17b43e6..0b0880a1348 100644
--- a/be/src/load/group_commit/wal/wal_dirs_info.cpp
+++ b/be/src/load/group_commit/wal/wal_dirs_info.cpp
@@ -95,7 +95,7 @@ Status WalDirInfo::update_wal_dir_used(size_t used) {
try {
RETURN_IF_ERROR(io::global_local_filesystem()->directory_size(_wal_dir,
&wal_dir_size));
} catch (const std::exception& e) {
- LOG(INFO) << "failed to update wal dir used info, err: {}",
e.what();
+ LOG(INFO) << "failed to update wal dir used info, err: " <<
e.what();
return Status::OK();
}
set_used(wal_dir_size);
diff --git a/common/cpp/sync_point.cpp b/common/cpp/sync_point.cpp
index 209d5dcf0d5..ae9fc35a7c1 100644
--- a/common/cpp/sync_point.cpp
+++ b/common/cpp/sync_point.cpp
@@ -204,10 +204,10 @@ void SyncPoint::Data::process(const std::string& point,
std::vector<std::any>&&
auto callback_pair = callbacks_.find(point);
if (callback_pair != callbacks_.end()) {
num_callbacks_running_++;
- auto callback = callback_pair->second;
- mutex_.unlock();
+ auto callback = callback_pair->second;
+ lock.unlock();
callback(std::move(cb_arg));
- mutex_.lock();
+ lock.lock();
num_callbacks_running_--;
}
cleared_points_.insert(point);
diff --git a/env.sh b/env.sh
index 510b52a224b..2726225bf67 100755
--- a/env.sh
+++ b/env.sh
@@ -69,7 +69,13 @@ CELLARS=(
wget
pcre
maven
- llvm@20
+ # 22.1.8 is the first compiler-rt whose ASAN runtime survives macOS 26.4+:
+ # dyld reimplemented dyld_shared_cache_iterate_text in Swift there and its
+ # _Block_copy allocation re-enters ASAN init, so every -fsanitize=address
+ # binary built with llvm@20 deadlocks before main()
(llvm/llvm-project#182943,
+ # fixed in 22.1.8; no 20.x or 21.x backport exists). That is the BE's
compiler:
+ # thirdparty/build-thirdparty.sh keeps the third-party libraries on
llvm@20.
+ llvm@22
m4
)
for cellar in "\${CELLARS[@]}"; do
diff --git a/thirdparty/build-thirdparty.sh b/thirdparty/build-thirdparty.sh
index 9ac1e01d02d..eee801bd91f 100755
--- a/thirdparty/build-thirdparty.sh
+++ b/thirdparty/build-thirdparty.sh
@@ -87,6 +87,28 @@ unset CMAKE_TOOLCHAIN_FILE \
VCPKG_DEFAULT_TRIPLET \
CONDA_PREFIX
+# The macOS third-party libraries stay on LLVM 20 while env.sh gives the BE
LLVM 22 (the
+# first compiler-rt whose ASAN runtime survives macOS 26.4+). clang 22 turns
+# -Wincompatible-pointer-types into an error and stops at unixODBC 2.3.7
+# (SQLBrowseConnectW.c passes SQLSMALLINT* where int* is expected), and no
package after it
+# has been built with clang 22. Every macOS third-party build comes through
here - the
+# rebuild build.sh starts on its own, a manual run, the pull request check and
the
+# apache/doris-thirdparty job that publishes
doris-thirdparty-prebuilt-darwin-*.tar.xz - so
+# this is the one place that decides their compiler. Like the unset above, it
has to come
+# after env.sh: custom_env.sh may point DORIS_CLANG_HOME at another LLVM for
the BE. CC/CXX
+# carry the compiler and PATH the rest of the LLVM tools, as when env.sh named
llvm@20.
+if [[ "$(uname -s)" == 'Darwin' ]]; then
+ DORIS_CLANG_HOME="$(brew --prefix llvm@20)"
+ if [[ ! -x "${DORIS_CLANG_HOME}/bin/clang" ]]; then
+ echo "The macOS third-party build needs LLVM 20
(${DORIS_CLANG_HOME}/bin/clang is missing): brew install llvm@20" >&2
+ exit 1
+ fi
+ export DORIS_CLANG_HOME
+ export CC="${DORIS_CLANG_HOME}/bin/clang"
+ export CXX="${DORIS_CLANG_HOME}/bin/clang++"
+ export PATH="${DORIS_CLANG_HOME}/bin:${PATH}"
+fi
+
# Check args
usage() {
echo "
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]