jemalloc: use a safe prof_backtrace hook (system backtrace) on macOS instead of the crashing default

This commit is contained in:
Raúl Marín
2026-07-09 18:39:05 +02:00
parent 74614e068a
commit 222f16662c
2 changed files with 23 additions and 12 deletions
+3 -12
View File
@@ -203,10 +203,9 @@ target_compile_options(_jemalloc PRIVATE ${WITHOUT_COVERAGE_FLAGS_LIST})
target_compile_definitions(_jemalloc PRIVATE -DJEMALLOC_PROF=1)
# jemalloc provides several unwind flavors for the profiler backtrace:
# jemalloc provides support two unwind flavors:
# - JEMALLOC_PROF_LIBUNWIND - unw_backtrace() - gnu libunwind (compatible with llvm libunwind)
# - JEMALLOC_PROF_LIBGCC - _Unwind_Backtrace() - the original HP libunwind and the one coming with gcc / g++ / libstdc++.
# - JEMALLOC_PROF_GCC - __builtin_return_address() - a plain frame-pointer walk, no DWARF/unwinder.
#
# But for JEMALLOC_PROF_LIBGCC it also calls _Unwind_Backtrace() during
# bootstraping of jemalloc, which may lead to deadlock, if the dlsym will do
@@ -216,16 +215,8 @@ target_compile_definitions(_jemalloc PRIVATE -DJEMALLOC_PROF=1)
#
# And since ClickHouse unwind already supports unw_backtrace() we can safely
# switch to it to avoid this deadlock.
if (OS_DARWIN)
# On macOS the profiler samples inside the malloc hook and unwinds the current stack, which on
# arm64 includes frame-pointer-less libsystem frames (e.g. the malloc-zone shims). llvm libunwind's
# DWARF stepper null-derefs on those, crashing the server. Use the frame-pointer backtrace instead
# (ClickHouse builds with -fno-omit-frame-pointer); it needs no unwinder and cannot fault that way.
target_compile_definitions (_jemalloc PRIVATE -DJEMALLOC_PROF_GCC=1)
else ()
target_compile_definitions (_jemalloc PRIVATE -DJEMALLOC_PROF_LIBUNWIND=1)
target_link_libraries (_jemalloc PRIVATE unwind)
endif ()
target_compile_definitions (_jemalloc PRIVATE -DJEMALLOC_PROF_LIBUNWIND=1)
target_link_libraries (_jemalloc PRIVATE unwind)
# for RTLD_NEXT
target_compile_options(_jemalloc PRIVATE -D_GNU_SOURCE)
+20
View File
@@ -10,6 +10,10 @@
# include <Common/TraceSender.h>
# include <Common/logger_useful.h>
# if defined(OS_DARWIN)
# include <execinfo.h>
# endif
# define STRINGIFY_HELPER(x) #x
# define STRINGIFY(x) STRINGIFY_HELPER(x)
@@ -176,6 +180,18 @@ void setLastFlushProfile(const char * filename)
last_flush_profile = std::string_view{last_flush_profile_buffer.data(), last_flush_profile_size};
}
#if defined(OS_DARWIN)
/// jemalloc's built-in profiler backtraces crash inside the malloc hook on macOS: the libunwind DWARF
/// stepper null-derefs, and the __builtin_frame_address walk faults on frames without a set-up frame
/// pointer. The system backtrace() validates the frame-pointer chain (and does not allocate), so it is
/// safe to call here and is what we use for the profiler elsewhere. Installed via the prof_backtrace hook.
void jemallocProfBacktrace(void ** vec, unsigned * len, unsigned max_len)
{
int size = ::backtrace(vec, static_cast<int>(max_len));
*len = size > 0 ? static_cast<unsigned>(size) : 0;
}
#endif
}
void setCollectLocalProfileSamplesInTraceLog(bool value)
@@ -208,6 +224,10 @@ void setup(
setValue("experimental.hooks.prof_sample", &jemallocAllocationTracker);
setValue("experimental.hooks.prof_sample_free", &jemallocDeallocationTracker);
setValue("experimental.hooks.prof_dump", &setLastFlushProfile);
#if defined(OS_DARWIN)
/// Replace jemalloc's default (crash-prone on macOS) profiler backtrace with the safe system one.
setValue("experimental.hooks.prof_backtrace", &jemallocProfBacktrace);
#endif
}
void verifySetup(