This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new 34f8ea966d8 branch-4.2: [fix](be) Route glibc allocation entry points
through jemalloc #69819 (#70153)
34f8ea966d8 is described below
commit 34f8ea966d8eb8ea924072d01bc62006ae466872
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Nov 5 09:24:07 2026 +0800
branch-4.2: [fix](be) Route glibc allocation entry points through jemalloc
#69819 (#70153)
Cherry-picked from #69819
Co-authored-by: daidai <[email protected]>
---
be/src/runtime/memory/jemalloc_hook.cpp | 41 +++-
be/test/jemalloc_hook/CMakeLists.txt | 58 +++++
be/test/jemalloc_hook/jemalloc_hook_test.cpp | 300 +++++++++++++++++++++++
be/test/jemalloc_hook/jemalloc_hook_test_dso.cpp | 44 ++++
run-be-ut.sh | 32 ++-
5 files changed, 470 insertions(+), 5 deletions(-)
diff --git a/be/src/runtime/memory/jemalloc_hook.cpp
b/be/src/runtime/memory/jemalloc_hook.cpp
index 9cef99757bf..a70abd1f1e1 100644
--- a/be/src/runtime/memory/jemalloc_hook.cpp
+++ b/be/src/runtime/memory/jemalloc_hook.cpp
@@ -15,6 +15,11 @@
// specific language governing permissions and limitations
// under the License.
+#include <unistd.h>
+
+#include <cerrno>
+#include <limits>
+
#include "jemalloc/jemalloc.h"
#ifndef __THROW
@@ -35,7 +40,13 @@ void doris_free(void* p) __THROW {
}
void* doris_realloc(void* p, size_t size) __THROW {
- return jerealloc(p, size);
+ void* ptr = jerealloc(p, size);
+ // jemalloc 5.3.0 can reject an oversized request without setting errno.
+ // realloc(p, 0) may free p and return nullptr, so only nonzero sizes fail.
+ if (ptr == nullptr && size != 0) {
+ errno = ENOMEM;
+ }
+ return ptr;
}
void* doris_calloc(size_t n, size_t size) __THROW {
@@ -55,11 +66,21 @@ void* doris_aligned_alloc(size_t align, size_t size)
__THROW {
}
void* doris_valloc(size_t size) __THROW {
- return jevalloc(size);
+ // Unlike jemalloc's legacy valloc, aligned_alloc keeps zero-size results
+ // page-aligned and sets errno when an allocation fails.
+ return jealigned_alloc(getpagesize(), size);
}
void* doris_pvalloc(size_t size) __THROW {
- return jevalloc(size);
+ const size_t page_size = getpagesize();
+ // pvalloc rounds up to whole pages; SIZE_MAX must fail rather than wrap
+ // to a small allocation when adding the page-size adjustment.
+ if (size > std::numeric_limits<size_t>::max() - (page_size - 1)) {
+ errno = ENOMEM;
+ return nullptr;
+ }
+ const size_t rounded_size = (size + page_size - 1) / page_size * page_size;
+ return jealigned_alloc(page_size, rounded_size);
}
int doris_posix_memalign(void** r, size_t align, size_t size) __THROW {
@@ -77,9 +98,21 @@ void* realloc(void* p, size_t size) __THROW
ALIAS(doris_realloc);
void* calloc(size_t n, size_t size) __THROW ALIAS(doris_calloc);
void cfree(void* ptr) __THROW ALIAS(doris_cfree);
void* memalign(size_t align, size_t size) __THROW ALIAS(doris_memalign);
+#ifdef __GLIBC__
+// Keep glibc's exported allocation entry points paired with the public
jemalloc
+// hooks. For example, before glibc 2.25, dlopened modules allocate dynamic TLS
+// with __libc_memalign and release it through free.
+void* __libc_malloc(size_t size) __THROW ALIAS(doris_malloc);
+void __libc_free(void* ptr) __THROW ALIAS(doris_free);
+void* __libc_calloc(size_t n, size_t size) __THROW ALIAS(doris_calloc);
+void* __libc_realloc(void* ptr, size_t size) __THROW ALIAS(doris_realloc);
+void* __libc_memalign(size_t align, size_t size) __THROW ALIAS(doris_memalign);
+void* __libc_valloc(size_t size) __THROW ALIAS(doris_valloc);
+void* __libc_pvalloc(size_t size) __THROW ALIAS(doris_pvalloc);
+#endif
void* aligned_alloc(size_t align, size_t size) __THROW
ALIAS(doris_aligned_alloc);
void* valloc(size_t size) __THROW ALIAS(doris_valloc);
void* pvalloc(size_t size) __THROW ALIAS(doris_pvalloc);
int posix_memalign(void** r, size_t a, size_t s) __THROW
ALIAS(doris_posix_memalign);
size_t malloc_usable_size(void* ptr) __THROW ALIAS(doris_malloc_usable_size);
-}
\ No newline at end of file
+}
diff --git a/be/test/jemalloc_hook/CMakeLists.txt
b/be/test/jemalloc_hook/CMakeLists.txt
new file mode 100644
index 00000000000..201a4c68405
--- /dev/null
+++ b/be/test/jemalloc_hook/CMakeLists.txt
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This allocator test must not use ASAN: its interceptors would hide missing
+# glibc allocation entry points. Keep it separate from doris_be_test, which
+# run-be-ut.sh builds with USE_JEMALLOC=OFF.
+cmake_minimum_required(VERSION 3.20)
+project(doris_jemalloc_hook_test LANGUAGES CXX)
+
+if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ message(FATAL_ERROR "jemalloc hook tests require Linux/glibc")
+endif()
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+get_filename_component(DORIS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../.."
ABSOLUTE)
+set(THIRDPARTY_DIR "$ENV{DORIS_THIRDPARTY}/installed")
+find_package(Threads REQUIRED)
+
+foreach (LIB_NAME gtest gtest_main jemalloc_doris unwind lzma z)
+ find_library(${LIB_NAME}_LIBRARY NAMES ${LIB_NAME}
+ PATHS "${THIRDPARTY_DIR}/lib" "${THIRDPARTY_DIR}/lib64"
+ NO_DEFAULT_PATH REQUIRED)
+endforeach()
+
+add_library(jemalloc_hook_test_dso SHARED jemalloc_hook_test_dso.cpp)
+target_compile_options(jemalloc_hook_test_dso PRIVATE
-ftls-model=global-dynamic)
+
+add_executable(jemalloc_hook_test
+ jemalloc_hook_test.cpp
+ "${DORIS_SOURCE_DIR}/be/src/runtime/memory/jemalloc_hook.cpp")
+target_include_directories(jemalloc_hook_test PRIVATE
"${THIRDPARTY_DIR}/include")
+target_compile_definitions(jemalloc_hook_test PRIVATE
+ JEMALLOC_HOOK_TEST_DSO="$<TARGET_FILE:jemalloc_hook_test_dso>")
+# Do not enable ENABLE_EXPORTS: verify the hook also works with the production
+# executable's default symbol visibility.
+# libunwind's compressed .debug_frame reader needs zlib's uncompress.
+# Keep its compression archives after unwind in the static link order.
+target_link_libraries(jemalloc_hook_test PRIVATE
+ "${gtest_main_LIBRARY}" "${gtest_LIBRARY}" "${jemalloc_doris_LIBRARY}"
+ "${unwind_LIBRARY}" "${lzma_LIBRARY}" "${z_LIBRARY}"
+ Threads::Threads ${CMAKE_DL_LIBS})
+add_dependencies(jemalloc_hook_test jemalloc_hook_test_dso)
diff --git a/be/test/jemalloc_hook/jemalloc_hook_test.cpp
b/be/test/jemalloc_hook/jemalloc_hook_test.cpp
new file mode 100644
index 00000000000..10eacad09f7
--- /dev/null
+++ b/be/test/jemalloc_hook/jemalloc_hook_test.cpp
@@ -0,0 +1,300 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <dlfcn.h>
+#include <gtest/gtest.h>
+#include <malloc.h>
+#include <unistd.h>
+
+#include <array>
+#include <cerrno>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <thread>
+
+extern "C" void* doris_memalign(size_t alignment, size_t size) noexcept;
+
+namespace doris {
+
+class JemallocHookTest : public testing::Test {
+protected:
+ using AllocationFunction = void* (*)(size_t);
+ using CallocFunction = void* (*)(size_t, size_t);
+ using FreeFunction = void (*)(void*);
+ using ReallocationFunction = void* (*)(void*, size_t);
+ using MemalignFunction = void* (*)(size_t, size_t);
+
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(check_executable_interposition());
+ ASSERT_NO_FATAL_FAILURE(check_legacy_allocation_pair());
+ }
+
+ void check_executable_interposition() {
+ // Anchor the lookup to the linked production hook. A libc allocation
+ // paired with a libc free could otherwise pass without testing the
hook.
+ Dl_info hook_image = {};
+ ASSERT_NE(0, dladdr(reinterpret_cast<void*>(&doris_memalign),
&hook_image));
+ constexpr std::array<const char*, 9> symbol_names = {
+ "__libc_memalign", "malloc", "free",
+ "__libc_malloc", "__libc_free", "__libc_calloc",
+ "__libc_realloc", "__libc_valloc", "__libc_pvalloc"};
+ for (size_t i = 0; i < symbol_names.size(); ++i) {
+ _allocator_symbols[i] = dlsym(RTLD_DEFAULT, symbol_names[i]);
+ ASSERT_NE(nullptr, _allocator_symbols[i]) << symbol_names[i] << ":
" << dlerror();
+ Dl_info symbol_image = {};
+ ASSERT_NE(0, dladdr(_allocator_symbols[i], &symbol_image)) <<
symbol_names[i];
+ ASSERT_EQ(hook_image.dli_fbase, symbol_image.dli_fbase)
+ << symbol_names[i] << " resolved to " <<
symbol_image.dli_fname
+ << " instead of " << hook_image.dli_fname;
+ }
+ _memalign = reinterpret_cast<MemalignFunction>(_allocator_symbols[0]);
+ _libc_malloc =
reinterpret_cast<AllocationFunction>(_allocator_symbols[3]);
+ _libc_free = reinterpret_cast<FreeFunction>(_allocator_symbols[4]);
+ _libc_calloc = reinterpret_cast<CallocFunction>(_allocator_symbols[5]);
+ _libc_realloc =
reinterpret_cast<ReallocationFunction>(_allocator_symbols[6]);
+ _libc_valloc =
reinterpret_cast<AllocationFunction>(_allocator_symbols[7]);
+ _libc_pvalloc =
reinterpret_cast<AllocationFunction>(_allocator_symbols[8]);
+ }
+
+ void check_legacy_allocation_pair() {
+ // Exercise the legacy allocation/free pair even on newer glibc
versions
+ // whose loader no longer uses __libc_memalign for TLS. A subprocess
turns
+ // an allocator-mismatch crash into a test failure instead of aborting
UT.
+ ASSERT_EXIT(
+ {
+ void* ptr = _memalign(16, 32);
+ const int result = (ptr == nullptr);
+ std::free(ptr);
+ std::_Exit(result);
+ },
+ testing::ExitedWithCode(0), "");
+ }
+
+ std::array<void*, 9> _allocator_symbols = {};
+ MemalignFunction _memalign = nullptr;
+ AllocationFunction _libc_malloc = nullptr;
+ FreeFunction _libc_free = nullptr;
+ CallocFunction _libc_calloc = nullptr;
+ ReallocationFunction _libc_realloc = nullptr;
+ AllocationFunction _libc_valloc = nullptr;
+ AllocationFunction _libc_pvalloc = nullptr;
+};
+
+TEST_F(JemallocHookTest, LibcAllocationsCanBeFreedThroughPublicEntry) {
+ for (size_t size : {0, 17, 4097}) {
+ void* ptr = _libc_malloc(size);
+ ASSERT_NE(nullptr, ptr);
+ std::memset(ptr, 0x5a, size);
+ std::free(ptr);
+ }
+}
+
+TEST_F(JemallocHookTest, PublicAllocationsCanBeFreedThroughLibcEntry) {
+ _libc_free(nullptr);
+ for (size_t size : {0, 17, 4097}) {
+ std::unique_ptr<void, decltype(&std::free)> ptr(std::malloc(size),
&std::free);
+ ASSERT_NE(nullptr, ptr.get());
+ std::memset(ptr.get(), 0x5a, size);
+ errno = EDOM;
+ _libc_free(ptr.release());
+ const int free_errno = errno;
+ EXPECT_EQ(EDOM, free_errno);
+ }
+}
+
+TEST_F(JemallocHookTest, LibcCallocInitializesMemoryAndRejectsOverflow) {
+ for (size_t count : {0, 17}) {
+ constexpr size_t element_size = 13;
+ void* ptr = _libc_calloc(count, element_size);
+ ASSERT_NE(nullptr, ptr);
+ for (size_t i = 0; i < count * element_size; ++i) {
+ EXPECT_EQ(0, static_cast<unsigned char*>(ptr)[i]);
+ }
+ std::free(ptr);
+ }
+
+ errno = 0;
+ EXPECT_EQ(nullptr, _libc_calloc(std::numeric_limits<size_t>::max(), 2));
+ EXPECT_EQ(ENOMEM, errno);
+}
+
+TEST_F(JemallocHookTest, ReallocCanCrossLibcAndPublicEntryPoints) {
+ struct AllocationPair {
+ AllocationFunction allocate;
+ ReallocationFunction reallocate;
+ FreeFunction release;
+ };
+ const std::array<AllocationPair, 3> pairs = {{{&std::malloc,
_libc_realloc, &std::free},
+ {_libc_malloc,
&std::realloc, _libc_free},
+ {_libc_malloc,
_libc_realloc, &std::free}}};
+ for (const auto& pair : pairs) {
+ void* ptr = pair.allocate(17);
+ ASSERT_NE(nullptr, ptr);
+ std::memset(ptr, 0x5a, 17);
+ void* resized = pair.reallocate(ptr, 1031);
+ ASSERT_NE(nullptr, resized);
+ for (size_t i = 0; i < 17; ++i) {
+ EXPECT_EQ(0x5a, static_cast<unsigned char*>(resized)[i]);
+ }
+ std::memset(resized, 0x5a, 1031);
+ pair.release(resized);
+ }
+}
+
+TEST_F(JemallocHookTest, LibcReallocFailurePreservesTheOriginalAllocation) {
+ void* ptr = _libc_realloc(nullptr, 17);
+ ASSERT_NE(nullptr, ptr);
+ std::memset(ptr, 0x5a, 17);
+
+ errno = 0;
+ void* resized = _libc_realloc(ptr, std::numeric_limits<size_t>::max());
+ const int allocation_errno = errno;
+ ASSERT_EQ(nullptr, resized);
+ EXPECT_EQ(ENOMEM, allocation_errno);
+ for (size_t i = 0; i < 17; ++i) {
+ EXPECT_EQ(0x5a, static_cast<unsigned char*>(ptr)[i]);
+ }
+ std::free(ptr);
+}
+
+TEST_F(JemallocHookTest, LibcReallocZeroSizeIsNotReportedAsOutOfMemory) {
+ std::unique_ptr<void, decltype(&std::free)> ptr(std::malloc(17),
&std::free);
+ ASSERT_NE(nullptr, ptr.get());
+ errno = 0;
+ void* resized = _libc_realloc(ptr.release(), 0);
+ const int allocation_errno = errno;
+ EXPECT_NE(ENOMEM, allocation_errno);
+ std::free(resized);
+}
+
+TEST_F(JemallocHookTest, LibcPageAlignedAllocationsCanBeFreed) {
+ const long page_size = sysconf(_SC_PAGESIZE);
+ ASSERT_GT(page_size, 0);
+ for (auto allocate : {_libc_valloc, _libc_pvalloc}) {
+ for (size_t size : {size_t {0}, size_t {1},
static_cast<size_t>(page_size) + 1}) {
+ void* ptr = allocate(size);
+ ASSERT_NE(nullptr, ptr);
+ EXPECT_EQ(0, reinterpret_cast<uintptr_t>(ptr) % page_size) <<
"size: " << size;
+ std::memset(ptr, 0x5a, size);
+ std::free(ptr);
+ }
+ errno = 0;
+ void* ptr = allocate(std::numeric_limits<size_t>::max());
+ const int allocation_errno = errno;
+ EXPECT_EQ(nullptr, ptr);
+ EXPECT_EQ(ENOMEM, allocation_errno);
+ }
+}
+
+TEST_F(JemallocHookTest, LibcPvallocRoundsAllocationSizeToPages) {
+ const size_t page_size = getpagesize();
+ for (size_t size : {size_t {0}, size_t {1}, page_size + 1}) {
+ void* ptr = _libc_pvalloc(size);
+ ASSERT_NE(nullptr, ptr);
+ const size_t rounded_size = (size + page_size - 1) / page_size *
page_size;
+ ASSERT_GE(malloc_usable_size(ptr), rounded_size);
+ std::memset(ptr, 0x5a, rounded_size);
+ std::free(ptr);
+ }
+}
+
+TEST_F(JemallocHookTest, AlignedAllocationsCanBeFreed) {
+ for (size_t alignment : {1, 8, 16, 64, 4096}) {
+ for (size_t size : {0, 1, 17, 4097}) {
+ void* ptr = _memalign(alignment, size);
+ ASSERT_NE(nullptr, ptr);
+ EXPECT_EQ(0, reinterpret_cast<uintptr_t>(ptr) % alignment);
+ std::memset(ptr, 0x5a, size);
+ std::free(ptr);
+ }
+ }
+}
+
+TEST_F(JemallocHookTest, AllocationErrorsPreserveMemalignSemantics) {
+ errno = 0;
+ void* ptr = _memalign(3, 16);
+ EXPECT_EQ(nullptr, ptr);
+ EXPECT_EQ(EINVAL, errno);
+ std::free(ptr);
+
+ errno = 0;
+ ptr = _memalign(64, std::numeric_limits<size_t>::max());
+ EXPECT_EQ(nullptr, ptr);
+ EXPECT_EQ(ENOMEM, errno);
+ std::free(ptr);
+}
+
+TEST_F(JemallocHookTest, AllocationsCanBeFreedAfterAllocatingThreadsExit) {
+ std::array<void*, 64> pointers = {};
+ std::array<std::thread, 64> threads;
+ for (size_t i = 0; i < threads.size(); ++i) {
+ threads[i] = std::thread([&, i] { pointers[i] = _memalign(64, 1031);
});
+ }
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ for (void* ptr : pointers) {
+ ASSERT_NE(nullptr, ptr);
+ std::memset(ptr, 0x5a, 1031);
+ std::free(ptr);
+ }
+}
+
+TEST_F(JemallocHookTest, LateLoadedDynamicTlsCanBeFreedAfterThreadsExit) {
+ std::unique_ptr<void, decltype(&dlclose)> handle(
+ dlopen(JEMALLOC_HOOK_TEST_DSO, RTLD_NOW | RTLD_LOCAL), &dlclose);
+ ASSERT_NE(nullptr, handle) << dlerror();
+ using GetAllocatorSymbols = void (*)(void**);
+ auto get_allocator_symbols = reinterpret_cast<GetAllocatorSymbols>(
+ dlsym(handle.get(), "jemalloc_hook_test_allocator_symbols"));
+ ASSERT_NE(nullptr, get_allocator_symbols) << dlerror();
+ std::array<void*, 9> allocator_symbols = {};
+ get_allocator_symbols(allocator_symbols.data());
+ // Check the DSO's actual relocations for every glibc allocation entry
point
+ // before exercising dynamic TLS.
+ ASSERT_EQ(_allocator_symbols, allocator_symbols);
+
+ using TouchTlsFunction = int (*)();
+ auto touch_tls =
+ reinterpret_cast<TouchTlsFunction>(dlsym(handle.get(),
"jemalloc_hook_test_touch_tls"));
+ ASSERT_NE(nullptr, touch_tls) << dlerror();
+
+ // Exceed glibc's default stack cache to exercise TLS deallocation instead
+ // of only keeping exited threads' stacks cached.
+ for (int round = 0; round < 8; ++round) {
+ std::array<int, 64> results = {};
+ std::array<std::thread, 64> threads;
+ for (size_t i = 0; i < threads.size(); ++i) {
+ threads[i] = std::thread([&, i] {
+ for (int call = 0; call < 8; ++call) {
+ results[i] = touch_tls();
+ }
+ });
+ }
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ for (int result : results) {
+ EXPECT_EQ(8, result);
+ }
+ }
+}
+
+} // namespace doris
diff --git a/be/test/jemalloc_hook/jemalloc_hook_test_dso.cpp
b/be/test/jemalloc_hook/jemalloc_hook_test_dso.cpp
new file mode 100644
index 00000000000..21486d3d66e
--- /dev/null
+++ b/be/test/jemalloc_hook/jemalloc_hook_test_dso.cpp
@@ -0,0 +1,44 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <cstdlib>
+
+extern "C" void* __libc_memalign(size_t alignment, size_t size) noexcept;
+extern "C" void* __libc_malloc(size_t size) noexcept;
+extern "C" void __libc_free(void* ptr) noexcept;
+extern "C" void* __libc_calloc(size_t count, size_t size) noexcept;
+extern "C" void* __libc_realloc(void* ptr, size_t size) noexcept;
+extern "C" void* __libc_valloc(size_t size) noexcept;
+extern "C" void* __libc_pvalloc(size_t size) noexcept;
+
+extern "C" void jemalloc_hook_test_allocator_symbols(void** symbols) {
+ symbols[0] = reinterpret_cast<void*>(&__libc_memalign);
+ symbols[1] = reinterpret_cast<void*>(&std::malloc);
+ symbols[2] = reinterpret_cast<void*>(&std::free);
+ symbols[3] = reinterpret_cast<void*>(&__libc_malloc);
+ symbols[4] = reinterpret_cast<void*>(&__libc_free);
+ symbols[5] = reinterpret_cast<void*>(&__libc_calloc);
+ symbols[6] = reinterpret_cast<void*>(&__libc_realloc);
+ symbols[7] = reinterpret_cast<void*>(&__libc_valloc);
+ symbols[8] = reinterpret_cast<void*>(&__libc_pvalloc);
+}
+
+// Access TLS from a late-loaded library, as happens with JNI native libraries.
+extern "C" int jemalloc_hook_test_touch_tls() {
+ static thread_local int tls_values[1024] = {};
+ return ++tls_values[1023];
+}
diff --git a/run-be-ut.sh b/run-be-ut.sh
index 915e877610e..b0b4926c6ca 100755
--- a/run-be-ut.sh
+++ b/run-be-ut.sh
@@ -22,6 +22,7 @@
# Optional options:
# --clean clean and build ut
# --run build and run all ut
+# --jemalloc-hook build standalone jemalloc hook tests without
sanitizers
# --run --filter=xx build and run specified ut
# --gdb debug with gdb, does not take effect if --run is not
specified
# --coverage generate coverage report, does not take effect if
--gdb is specified
@@ -117,6 +118,7 @@ Usage: $0 <options>
Optional options:
--clean clean and build ut
--run build and run all ut
+ --jemalloc-hook build standalone Linux jemalloc hook tests without
sanitizers
--run --filter=xx build and run specified ut
--run --gen_out generate expected check data for test
--run --gen_regression_case generate regression test cases
corrresponding to ut cases for ut cases that support it
@@ -145,7 +147,7 @@ Usage: $0 <options>
exit 1
}
-if ! OPTS="$(getopt -n "$0" -o vhj:f: -l
gen_out,gen_regression_case,coverage,benchmark,run,gdb,clean,filter: -- "$@")";
then
+if ! OPTS="$(getopt -n "$0" -o vhj:f: -l
gen_out,gen_regression_case,coverage,benchmark,run,gdb,clean,jemalloc-hook,filter:
-- "$@")"; then
usage
fi
@@ -153,6 +155,7 @@ eval set -- "${OPTS}"
CLEAN=0
RUN=0
+JEMALLOC_HOOK=0
GDB=0
DENABLE_CLANG_COVERAGE='OFF'
BUILD_AZURE='ON'
@@ -170,6 +173,10 @@ if [[ "$#" != 1 ]]; then
RUN=1
shift
;;
+ --jemalloc-hook)
+ JEMALLOC_HOOK=1
+ shift
+ ;;
--gdb)
GDB=1
shift
@@ -209,6 +216,29 @@ if [[ -z "${PARALLEL}" ]]; then
PARALLEL="$(($(nproc) / 5 + 1))"
fi
+# The regular BE UT uses ASAN and disables jemalloc. Exercise allocator
+# interposition in a separate native executable, before initializing BE's
+# unrelated submodules or configuring the sanitizer build.
+if [[ "${JEMALLOC_HOOK}" -eq 1 ]]; then
+ # These modes belong to the sanitizer BE UT, not the native allocator test.
+ if [[ "${GDB}" -eq 1 || "${DENABLE_CLANG_COVERAGE}" == 'ON' || -n
"${GEN_OUT}${GEN_REGRESSION_CASE}" ]]; then
+ echo "--jemalloc-hook supports --run, --filter, --clean and -j"
+ exit 1
+ fi
+ JEMALLOC_TEST_BUILD_DIR="${DORIS_HOME}/be/ut_build_JEMALLOC"
+ if [[ "${CLEAN}" -eq 1 ]]; then
+ rm -rf "${JEMALLOC_TEST_BUILD_DIR}"
+ fi
+ "${CMAKE_CMD}" -S "${DORIS_HOME}/be/test/jemalloc_hook" \
+ -B "${JEMALLOC_TEST_BUILD_DIR}" -G "${GENERATOR}"
+ "${CMAKE_CMD}" --build "${JEMALLOC_TEST_BUILD_DIR}" --parallel
"${PARALLEL}"
+ if [[ "${RUN}" -eq 1 ]]; then
+ "${JEMALLOC_TEST_BUILD_DIR}/jemalloc_hook_test"
"${FILTER:---gtest_filter=*}" \
+
"--gtest_output=xml:${JEMALLOC_TEST_BUILD_DIR}/gtest_output/jemalloc_hook_test.xml"
+ fi
+ exit 0
+fi
+
CMAKE_BUILD_TYPE="${BUILD_TYPE_UT:-ASAN}"
CMAKE_BUILD_TYPE="$(echo "${CMAKE_BUILD_TYPE}" | awk '{ print(toupper($0)) }')"
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]