DO NOT LAND fix: Bug fixes for macOS arm64 unit test failures (reference, do not merge) - #18615
Draft
karthikeyann wants to merge 25 commits into
Draft
DO NOT LAND fix: Bug fixes for macOS arm64 unit test failures (reference, do not merge)#18615karthikeyann wants to merge 25 commits into
karthikeyann wants to merge 25 commits into
Conversation
IntervalDayTimeType::valueToString() passed int64_t and int128_t values to the "%d" conversions of a variadic format string. The conversion specifiers of snprintf() are not checked against the argument types, and on AArch64 the variadic argument area is laid out differently, so the wrong bytes were read and the milliseconds field printed garbage. Format through fmt::format(), which is type safe and cannot reproduce this class of defect. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The distinct keys vector can hold the same key more than once, for example when it is wrapped in a dictionary with repeated indices. getKeyChannel() returned the first element that std::unordered_multimap::equal_range() happened to yield, but the order of equivalent elements there is unspecified and differs between standard libraries. Select the largest matching channel explicitly, so that the last channel that set a key is the one visible, and document that contract in the header. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
std::default_random_engine is an implementation defined alias: libstdc++ maps it to minstd_rand0 and libc++ to minstd_rand. The same seed therefore produced a different sketch on different platforms, which broke the reproducibility that the fixed seed configuration is meant to provide. Name std::minstd_rand0 explicitly. It has the same small state the comment asks for and keeps the behaviour libstdc++ builds have today. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The subtraction assigned to result.allocateClocks twice, so the free clocks delta overwrote the allocate clocks delta and result.freeClocks was left at zero. Stats differences are used to report per size class timings, for example through Stats::operator-, so both fields were wrong. Add a unit test for the subtraction, which fails without the fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MemoryArbitrator::Config::toString() iterated the extraConfigs unordered map directly, so the order of the emitted settings depended on the hash implementation and differed between standard libraries. That order shows up in error messages and logs, and made them non reproducible across platforms. Sort the entries by key and update the two tests that encoded the previous hash order. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
release() looked the string up in stringToId_ and erased the returned iterator, guarding the lookup with VELOX_DCHECK. VELOX_DCHECK expands to nothing under NDEBUG, so in a release build a broken invariant erased an end() iterator, which is undefined behaviour and corrupts memory silently. Use VELOX_CHECK with a message naming the id and the string instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cache test fixture called cache_->shutdown() first and only then released the IO executor. Prefetches run on that executor and pin cache entries, so a task that was still in flight dereferenced a cache that had already been shut down, crashing in AsyncDataCacheEntry::setExclusiveToShared(). Join the executor first, so no load is running by the time the cache goes away. The test crashed in 4 of about 14 runs before this change and passed 25 consecutive runs after it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
s2geometry declares nallocx as a function and ships a weak definition that returns the requested size. On Apple platforms a weak symbol cannot be left undefined in Mach-O, so folly builds with FOLLY_HAVE_WEAK_SYMBOLS=0 and instead defines a null function pointer with that name in the global namespace. The linker binds s2's call to folly's data symbol, so the call jumps into __DATA and crashes with SIGBUS. Patch s2 on Apple to use the requested size directly, which is exactly what its own weak implementation does. The patch is applied from both the bundled CMake resolver and the dependency setup script, since either can build s2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The generic Hadoop tarball ships x86-64 native libraries, so the HDFS tests could not load libhdfs on an aarch64 host. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
getDataFromFile() had two defects. It called asFlatVector<StringView>() on the parquet columns, which returns null when a column is not flat and then segfaulted, and it built each StringView over a temporary std::string, so the returned views pointed at freed memory. Decode the columns instead of assuming an encoding, and return owned strings so the callers can build views that stay valid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The benchmark callback captured the input RowVectorPtr and the ExprSet by value. folly keeps registered benchmarks in a global that is destroyed after main() returns, at which point the memory pools backing those vectors are already gone, so the process crashed in AlignedBuffer::freeToPool() during static destruction. Capture raw pointers and keep ownership in main(), where the objects are released while the memory manager is still alive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CDF tests compared results of boost's transcendental routines with EXPECT_EQ. Those results are correct to within a few ULP but are not bit identical across platforms, so the comparisons are not portable. Compare within a tolerance instead. The two cases that pass denormal parameters are only checked where boost is accurate enough to produce the mathematically correct answer; their expected values are left unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
checkUtcToEpoch() treated timegm() as successful unless it both returned -1 and set errno. macOS does not set errno, so out of range calendar values were compared against the -1 sentinel as if it were a real epoch value. Its supported range of negative years is also narrower than glibc's. Recognise -1 as a failure unless the input really is 1969-12-31T23:59:59, and only check the two extreme negative years where the platform can represent them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The buffer test compared the WKT text exactly, so a one ULP difference in a single coordinate failed the comparison. Compare the coordinates numerically with a tolerance and the remaining text exactly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The tests seed std::mt19937 with a fixed value for reproducibility, but drew values through std::uniform_int_distribution and std::uniform_real_distribution. Neither is specified to map engine output to values identically across standard libraries, so the same seed produced different data on libstdc++ and libc++ and the estimated error rates drifted past their thresholds. Draw from the engine directly so every platform sees the same inputs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The second half of the test built digestWeighted but added the weighted values to digest, which already held the first batch, and then compared that digest against the values of the second batch only. The mismatch was hidden by the particular random data libstdc++ generated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The shuffle tests compared against one specific permutation. shuffle() draws through std::shuffle, which uses std::uniform_int_distribution internally, so the permutation for a given seed differs between standard libraries. Check the two properties the function actually guarantees: the same seed produces the same ordering, and the result holds exactly the input elements. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
std::expm1 is accurate to within a ULP but its exact bits differ between platforms, so an equality comparison is not portable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
rebalance() requires at least one recorded row, and SkewedPartitionRebalancerTest asserts that it throws when only processed bytes were added. The fuzz test could draw zero partitions for an iteration and then call rebalance(), violating that contract. Whether it did depended on the standard library's distribution, so it only failed on libc++. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bound was the exact byte total one standard library's vector growth produces. Its purpose is to catch the sketch growing with the number of inserted elements, so leave a small margin and say so. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A single allocate and free pair can take less than one tick of the hardware timestamp counter, whose resolution is far coarser on ARM than the x86 TSC, so the recorded clocks were zero. Repeat the pair so the accumulated time is measurable on any platform. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The remaining filter evaluates to null, and therefore drops the row, whenever 'a' is null, but the test expected every generated row in the result. Whether the randomly generated column contained a null depended on the standard library, so the expectation only held on libstdc++. Build the input explicitly so the expectation is guaranteed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A partial sort task can run to completion before it observes the abort request, in which case it terminates as finished rather than aborted. The test waited for the aborted state only, so it timed out on a state that could never arrive, returned early, and left the merge task running for the teardown check to trip over. Accept either terminal state. The test aborted in 7 of 10 runs before this change and passed 12 consecutive runs after it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The expected message included the text dlerror() appends after the symbol name, which each platform's dynamic loader words differently. Match the prefix Velox produces instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
✅ Deploy Preview for meta-velox canceled.
|
Selective Build Plan
Selective build plan |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
DO NOT MERGE. This PR is intended to serve as a reference for these bug fixes.
There are currently no consumers of the macOS build, so it is not proposed for merging as-is.
Individual fixes can be cherry-picked into their own PRs if wanted.
Summary
All failures below were found by building Velox and running the full unit test suite on a
Mac M3 Pro (macOS, arm64, Apple Clang, Release). Every fix in this PR is verified:
the suite goes from 98 failing tests to 0.
Base commit:
38d78c605b. Intermittent fixes were additionally stress-tested (details in thetables below).
macOS unit tests have never run in CI —
.github/workflows/macos.ymlgates the test stepbehind
if: false, so only the build is validated. That is why these defects went unnoticed.Bugs and root causes
Product defects
type/Type.cpp0 00:00:45.-1982811744int64_tandint128_tpassed to"%d"conversions ofsnprintf. Variadic format specifiers are unchecked, and AArch64 lays out the variadic argument area differently, so the wrong bytes were read. Undefined behaviour.vector/FlatMapVector.cppstd::unordered_multimap::equal_range(), whose order for equivalent elements is unspecified. libstdc++ happened to yield last-inserted-first.common/memory/MemoryAllocator.hSizeClassStats::operator-assigned toresult.allocateClockstwice;result.freeClockswas never set.common/memory/MemoryArbitrator.hConfig::toString()iterated anunordered_map, so emitted order depended on the hash implementation.functions/lib/KllSketch.happrox_percentilereturned different results for the same seedstd::default_random_engineis an implementation-defined alias (minstd_rand0in libstdc++,minstd_randin libc++), breaking the reproducibility the fixed-seed config promises.common/caching/StringIdMap.cpprelease()guarded an erase withVELOX_DCHECK, which compiles to nothing underNDEBUG→erase(end()), undefined behaviour.dwio/dwrf/test/CacheInputTest.cppAsyncDataCacheEntry::setExclusiveToShared()exec/benchmarks/RowContainerSortBenchmark.cppasFlatVector<StringView>()returns null for a non-flat column; and eachStringViewwas built over a temporarystd::string(use-after-free).functions/sparksql/benchmarks/CastBenchmark.cppRowVectorPtr/ExprSetby value; folly holds callbacks in a global destroyed aftermain(), so buffers were freed to an already-destroyed memory pool.Build / packaging
SIGBUSinS2FunctionsTest.cellsnallocxas a function with a weak default. On Mach-O a weak symbol cannot be left undefined, so folly builds withFOLLY_HAVE_WEAK_SYMBOLS=0and definessize_t (*nallocx)(size_t,int) = nullptr— a data symbol — which wins at link time. s2's call jumps into__DATA.scripts/setup-common.shlibhdfson aarch64Tests asserting non-portable behaviour
ProbabilityTestEXPECT_EQon boost transcendental results (differ by 1–8 ULP across libm). Two cases use denormal parameters where Apple's libm is genuinely less accurate.TimestampTesttimegm()settingerrno(macOS does not); macOS also supports a narrower range of negative years.GeometryFunctionsTestST_BufferWKT as exact text; one coordinate differed by 1 ULP.SetDigestTeststd::uniform_*_distribution, whose mapping is implementation-defined.QuantileDigestTestdigestWeightedbut added to and asserted ondigest.ArrayShuffleTeststd::shufflepermutation.ArithmeticTest(spark)expm1.SkewedPartitionBalancerTestrebalance()with zero rows, violating a contract another test asserts.KllSketchTestMemoryAllocatorTestTableScanTestMultiFragmentTestkAborted, but a task can legitimately reach terminalkFinishedfirst.DynamicLinkTestdlerror()wording.Fixes
Product defects
fmt::format— type-safe by construction, removing the whole class of defect (not just this call site).FlatMapVector.h.result.freeClocks. Added a unit test that fails without the fix.std::minstd_rand0explicitly — same small state, and preserves current libstdc++ behaviour so Linux results do not move.VELOX_CHECKwith a diagnostic message naming the id and string.main(), where objects are released while the memory manager is alive.Build / packaging
-aarch64tarball on aarch64 hosts.Tests
-1as failure unless the input really is1969-12-31T23:59:59; check extreme negative years only where representable.strtod, so exponents are handled) and the remaining text exactly.digestWeightedconsistently.O(N)growth.Verification
MultiFragmentTest.abortMergeExchangevelox_dwio_cache_testvelox_sort_benchmark,velox_sparksql_benchmarks_castSizeClassStats::operator-testEnvironment note (not part of this PR)
Two local environment problems accounted for 81 of the original 98 failures and required no
code change:
-DNDEBUGchanges folly's string hash, soF14 heterogeneous lookups (
std::stringvsstd::string_view) always missed.fmt12 vs the pinnedfmt11.Worth knowing for anyone reproducing this: mismatched dependency builds look exactly like
widespread product breakage.
Follow-up worth considering
macos.ymlhasRun Testsbehindif: false. Re-enabling it (after these fixes) would preventthis class of regression from accumulating again.
🤖 Generated with Claude Code