From 4a8d8c4e98f1e2bc8a495b99de7693c92296f221 Mon Sep 17 00:00:00 2001 From: Travis Downs Date: Tue, 28 Jul 2026 16:57:12 -0400 Subject: [PATCH 1/4] tests/alloc_test: compare sample vectors with EQUAL_COLLECTIONS The sampled memory profile tests compared std::vector with BOOST_CHECK_EQUAL, which requires an operator<< for the vector itself. Nothing provides one: the test defines operator<< for a single allocation_site, and Boost.Test has no printer for collections, so the only thing that ever satisfied this was the generic vector operator<< in sstring.hh, which sits behind SEASTAR_DEPRECATED_OSTREAM_FORMATTERS and is off by default. The result was that alloc_test did not compile in a heap profiling build: print_helper.hpp:53:39: error: static assertion failed ... Type has to implement operator<< to be printable Use BOOST_CHECK_EQUAL_COLLECTIONS instead, which compares and prints element by element and so only needs the per-allocation_site operator<< that the test already defines. That operator was previously dead code, since no assertion ever passed a lone allocation_site to Boost. This also reports the first differing index rather than dumping both vectors. (cherry picked from commit fcfebbabc70fd92d7a80d1c1a0ff3ea4566c492e) --- tests/unit/alloc_test.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/alloc_test.cc b/tests/unit/alloc_test.cc index 896892dd495..bf568564a68 100644 --- a/tests/unit/alloc_test.cc +++ b/tests/unit/alloc_test.cc @@ -615,20 +615,20 @@ SEASTAR_TEST_CASE(test_sampled_profile_collection_small) auto stats1 = seastar::memory::sampled_memory_profile(); // two back-to-back copies of the sample should have the same value - BOOST_CHECK_EQUAL(stats0, stats1); + BOOST_CHECK_EQUAL_COLLECTIONS(stats0.begin(), stats0.end(), stats1.begin(), stats1.end()); // check that we get the same value from the raw array iterface std::vector stats2(stats0.size()); auto sz2 = seastar::memory::sampled_memory_profile(stats2.data(), stats2.size()); BOOST_CHECK_EQUAL(stats0.size(), sz2); - BOOST_CHECK_EQUAL(stats0, stats2); + BOOST_CHECK_EQUAL_COLLECTIONS(stats0.begin(), stats0.end(), stats2.begin(), stats2.end()); // check with +1 size, we expect to still only get size elements std::vector stats3(stats0.size() + 1); auto sz3 = seastar::memory::sampled_memory_profile(stats3.data(), stats3.size()); BOOST_CHECK_EQUAL(stats0.size(), sz3); stats3.resize(sz3); - BOOST_CHECK_EQUAL(stats0, stats3); + BOOST_CHECK_EQUAL_COLLECTIONS(stats0.begin(), stats0.end(), stats3.begin(), stats3.end()); return stats0; }; From 088a784efdb0f469fa06579acb636f8ef89a25c5 Mon Sep 17 00:00:00 2001 From: Travis Downs Date: Tue, 8 Sep 2026 12:44:26 -0300 Subject: [PATCH 2/4] core/chunked_hash_map: silence the absl warning abseil LTS 20220623, which is what Ubuntu's 24.04 libabsl-dev ships, includes from absl/base/options.h. libstdc++ 16 emits a #warning for that header for C++20 and later: ciso646:49:6: error: " is not a standard header since C++20, use to detect implementation-specific macros" This breaks the build due to warnings-as-errors: disable the warning at the include site. Redpanda-only, this can be squashed into the change which originated absl in our fork in a future rebase. --- include/seastar/core/chunked_hash_map.hh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/seastar/core/chunked_hash_map.hh b/include/seastar/core/chunked_hash_map.hh index 4e0f54ff583..fc83c175fc1 100644 --- a/include/seastar/core/chunked_hash_map.hh +++ b/include/seastar/core/chunked_hash_map.hh @@ -22,7 +22,23 @@ #include +// abseil LTS 20220623 and earlier include from +// absl/base/options.h. libstdc++ 16 warns on that header for C++20 and later, +// and seastar builds with -Werror. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-W#warnings" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcpp" +#endif #include +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + #include #include #include From 898eb4beb1407b97965d050b58f3345f733ca4a2 Mon Sep 17 00:00:00 2001 From: Travis Downs Date: Thu, 30 Jul 2026 13:22:59 -0400 Subject: [PATCH 3/4] tests/alloc: give the sampled profile tests a real sampling margin test_sampled_profile_collection_small and _large each run two identical allocation loops so that there are two distinct call sites, then require sampled_memory_profile() to report exactly 2. Whether a loop is sampled at all is probabilistic: the sampler draws the gap to the next sample from an exponential distribution whose mean is the sampling interval, so a loop that allocates N intervals worth of bytes records nothing with probability e^-N. Both tests sized their loops at only N=5 (500 bytes against a 100 byte interval, and 5000000 bytes against a 1000000 byte interval), which leaves each test failing on about 2*e^-5 = 1.3% of runs with critical check stats.size() == 2 has failed [1 != 2] Give both loops a much wider margin. _small raises count to 1000 for N=50. _large lowers the interval to 200000 instead, for N=25, which keeps the allocation size and the total memory footprint unchanged; it cannot lower it further because sample_size() accounts a sample as max(allocated_size, interval) and a 100000 byte request allocates 131072, so the interval has to stay above that for the neighbouring size == count * sample_rate assertion to hold. Measured over 500 runs of each test: 8/400 and 1/400 failures before, 0/500 and 0/500 after. Closes scylladb/seastar#3580 (cherry picked from commit 3bb2e379f54f6aad55de0de1a6bd465d90012cc9) --- tests/unit/alloc_test.cc | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/unit/alloc_test.cc b/tests/unit/alloc_test.cc index bf568564a68..196a23e73b3 100644 --- a/tests/unit/alloc_test.cc +++ b/tests/unit/alloc_test.cc @@ -591,7 +591,15 @@ SEASTAR_TEST_CASE(test_sampled_profile_collection_small) BOOST_REQUIRE_EQUAL(stats.size(), 0); } - std::size_t count = 100; + // The two loops below are distinct call sites and the assertions require + // both of them to be sampled, so each loop has to cover enough sampling + // intervals that missing one entirely is not plausible. The sampler draws + // the gap to the next sample from an exponential distribution whose mean is + // the sampling interval, so a loop allocating N intervals worth of bytes + // records nothing with probability e^-N. count/2 * 10 bytes against the 100 + // byte interval below is N=50, putting the chance of recording one call + // site instead of two at about 2*e^-50. + std::size_t count = 1000; std::vector ptrs(count); seastar::memory::set_heap_profiling_sampling_rate(100); @@ -668,7 +676,16 @@ SEASTAR_TEST_CASE(test_sampled_profile_collection_large) std::size_t count = 100; std::vector ptrs(count); - seastar::memory::set_heap_profiling_sampling_rate(1000000); + // Both loops have to be sampled, as in + // test_sampled_profile_collection_small: count/2 * 100000 bytes against + // this interval is N=25 intervals per loop, so the chance of recording one + // call site instead of two is about 2*e^-25. The interval also has to stay + // above the 131072 bytes that a 100000 byte request actually allocates, so + // that sample_size() accounts each sample as one full interval and the + // size == count * sample_rate check below holds. + std::size_t sample_rate = 200000; + + seastar::memory::set_heap_profiling_sampling_rate(sample_rate); #ifdef __clang__ #pragma nounroll @@ -690,10 +707,10 @@ SEASTAR_TEST_CASE(test_sampled_profile_collection_large) { auto stats = seastar::memory::sampled_memory_profile(); BOOST_REQUIRE_EQUAL(stats.size(), 2); - BOOST_REQUIRE_EQUAL(stats[0].size, stats[0].count * 1000000); + BOOST_REQUIRE_EQUAL(stats[0].size, stats[0].count * sample_rate); } - seastar::memory::set_heap_profiling_sampling_rate(1000000); + seastar::memory::set_heap_profiling_sampling_rate(sample_rate); for (auto ptr : ptrs) { free((void*)ptr); From b7316da052e8887db7a50669078b82a214947021 Mon Sep 17 00:00:00 2001 From: Travis Downs Date: Tue, 8 Sep 2026 12:59:44 -0300 Subject: [PATCH 4/4] ci: build every job with --heap-profiling We should use --heap-profiling in our CI as that's how we build it on the Redpanda side, and so we should bench/build/test in that context (this is helpful also as upstream does not build much with --heap-profiling so this is our chance to catch more stuff). --- .github/workflows/install-build-env.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/install-build-env.sh b/.github/workflows/install-build-env.sh index ac951fffbfa..33dd757472a 100755 --- a/.github/workflows/install-build-env.sh +++ b/.github/workflows/install-build-env.sh @@ -109,6 +109,7 @@ group "configure.py" --compiler "$CPP" \ --c-compiler "$CC" \ --mode "$MODE" \ + --heap-profiling \ "${cook_args[@]}" \ "${ccache_opt[@]}" \ $OPTIONS \