-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ThreadSanitizer support #2406
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,11 +182,45 @@ mark_as_advanced ( | |
CMAKE_EXE_LINKER_FLAGS_SANITIZE | ||
CMAKE_SHARED_LINKER_FLAGS_SANITIZE) | ||
|
||
set (CMAKE_CXX_FLAGS_SANITIZETHREAD | ||
"-Os -g" | ||
CACHE | ||
STRING | ||
"Flags used by the C++ compiler during sanitize-thread builds." | ||
FORCE) | ||
|
||
set (CMAKE_C_FLAGS_SANITIZETHREAD | ||
"-Os -g" | ||
CACHE | ||
STRING | ||
"Flags used by the C compiler during sanitize-thread builds." | ||
FORCE) | ||
|
||
set (CMAKE_EXE_LINKER_FLAGS_SANITIZETHREAD | ||
"" | ||
CACHE | ||
STRING | ||
"Flags used for linking binaries during sanitize-thread builds." | ||
FORCE) | ||
|
||
set (CMAKE_SHARED_LINKER_FLAGS_SANITIZETHREAD | ||
"" | ||
CACHE | ||
STRING | ||
"Flags used by the shared libraries linker during sanitize-thread builds." | ||
FORCE) | ||
|
||
mark_as_advanced ( | ||
CMAKE_CXX_FLAGS_SANITIZETHREAD | ||
CMAKE_C_FLAGS_SANITIZETHREAD | ||
CMAKE_EXE_LINKER_FLAGS_SANITIZETHREAD | ||
CMAKE_SHARED_LINKER_FLAGS_SANITIZETHREAD) | ||
|
||
set (CMAKE_BUILD_TYPE | ||
"${CMAKE_BUILD_TYPE}" | ||
CACHE | ||
STRING | ||
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Dev Sanitize." | ||
"Choose the type of build, options are: None Debug Release RelWithDebInfo Dev Sanitize SanitizeThread." | ||
FORCE) | ||
|
||
if (NOT CMAKE_BUILD_TYPE) | ||
|
@@ -356,7 +390,7 @@ endif () | |
|
||
|
||
set (Seastar_TEST_ENVIRONMENT | ||
"ASAN_OPTIONS=${Seastar_ASAN_OPTIONS};UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1;BOOST_TEST_CATCH_SYSTEM_ERRORS=no" | ||
"ASAN_OPTIONS=${Seastar_ASAN_OPTIONS};UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1;TSAN_OPTIONS=halt_on_error=1;BOOST_TEST_CATCH_SYSTEM_ERRORS=no" | ||
CACHE | ||
STRING | ||
"Environment variables for running tests") | ||
|
@@ -377,6 +411,12 @@ set (Seastar_SANITIZE | |
STRING | ||
"Enable ASAN and UBSAN. Can be ON, OFF or DEFAULT (which enables it for Debug and Sanitize)") | ||
|
||
set (Seastar_SANITIZE_THREAD | ||
"DEFAULT" | ||
CACHE | ||
STRING | ||
"Enable TSAN. Can be ON, OFF or DEFAULT (which enables it for SanitizeThread)") | ||
|
||
set (Seastar_DEBUG_SHARED_PTR | ||
"DEFAULT" | ||
CACHE | ||
|
@@ -888,6 +928,26 @@ if (condition) | |
$<${condition}:Sanitizers::undefined_behavior>) | ||
endif () | ||
|
||
tri_state_option (${Seastar_SANITIZE_THREAD} | ||
DEFAULT_BUILD_TYPES "SanitizeThread" | ||
CONDITION condition) | ||
if (condition) | ||
if (NOT ThreadSanitizer_FOUND) | ||
message (FATAL_ERROR "Thread sanitizer not found!") | ||
endif () | ||
set (Seastar_Sanitizers_OPTIONS ${ThreadSanitizer_COMPILER_OPTIONS}) | ||
target_link_libraries (seastar | ||
PUBLIC | ||
$<${condition}:Sanitizers::thread>) | ||
target_compile_definitions(seastar | ||
PUBLIC | ||
SEASTAR_NO_EXCEPTION_HACK) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why make it public? AFAICT, it's not part of the public interface, as this macro definition is only checked by |
||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") | ||
# Disabled because tsan doesn't support std::atomic_thread_fence | ||
list (APPEND Seastar_PRIVATE_CXX_FLAGS -Wno-error=tsan) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you quote the link to the related document and/or the bug report? and why is it a private compiler flag? |
||
endif () | ||
endif () | ||
|
||
# We only need valgrind to find uninitialized memory uses, so disable | ||
# the leak sanitizer. | ||
# To test with valgrind run "ctest -T memcheck" | ||
|
@@ -963,6 +1023,10 @@ if (Sanitizers_FIBER_SUPPORT) | |
list (APPEND Seastar_PRIVATE_COMPILE_DEFINITIONS SEASTAR_HAVE_ASAN_FIBER_SUPPORT) | ||
endif () | ||
|
||
if (ThreadSanitizer_FIBER_SUPPORT) | ||
list (APPEND Seastar_PRIVATE_COMPILE_DEFINITIONS SEASTAR_HAVE_TSAN_FIBER_SUPPORT) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if this definition only applies to ThreadSanitizer, i'd suggest add this definition only if ThreadSanitizer is enabled. so we don't need to check both of them in the C++ code. |
||
endif () | ||
|
||
if (Seastar_ALLOC_PAGE_SIZE) | ||
target_compile_definitions (seastar | ||
PUBLIC SEASTAR_OVERRIDE_ALLOCATOR_PAGE_SIZE=${Seastar_ALLOC_PAGE_SIZE}) | ||
|
@@ -1079,11 +1143,11 @@ foreach (definition | |
SEASTAR_SHUFFLE_TASK_QUEUE) | ||
target_compile_definitions (seastar | ||
PUBLIC | ||
$<$<IN_LIST:$<CONFIG>,Debug;Sanitize>:${definition}>) | ||
$<$<IN_LIST:$<CONFIG>,Debug;Sanitize;SanitizeThread>:${definition}>) | ||
endforeach () | ||
|
||
tri_state_option (${Seastar_DEBUG_SHARED_PTR} | ||
DEFAULT_BUILD_TYPES "Debug" "Sanitize" | ||
DEFAULT_BUILD_TYPES "Debug" "Sanitize" "SanitizeThread" | ||
CONDITION condition) | ||
if (condition) | ||
target_compile_definitions (seastar | ||
|
@@ -1092,7 +1156,7 @@ if (condition) | |
endif () | ||
|
||
tri_state_option (${Seastar_DEBUG_SHARED_PTR} | ||
DEFAULT_BUILD_TYPES "Debug" "Sanitize" | ||
DEFAULT_BUILD_TYPES "Debug" "Sanitize" "SanitizeThread" | ||
CONDITION condition) | ||
if (condition) | ||
target_compile_definitions (seastar | ||
|
@@ -1103,7 +1167,7 @@ endif () | |
include (CheckLibc) | ||
|
||
tri_state_option (${Seastar_STACK_GUARDS} | ||
DEFAULT_BUILD_TYPES "Debug" "Sanitize" "Dev" | ||
DEFAULT_BUILD_TYPES "Debug" "Sanitize" "SanitizeThread" "Dev" | ||
CONDITION condition) | ||
if (condition) | ||
# check for -fstack-clash-protection together with -Werror, because | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,13 @@ if (Sanitizers_UNDEFINED_BEHAVIOR_FOUND) | |
set (Sanitizers_UNDEFINED_BEHAVIOR_COMPILER_OPTIONS "-fsanitize=undefined;-fno-sanitize=vptr") | ||
endif () | ||
|
||
set (CMAKE_REQUIRED_FLAGS -fsanitize=thread) | ||
check_cxx_source_compiles ("int main() {}" Sanitizers_THREAD_FOUND) | ||
|
||
if (Sanitizers_THREAD_FOUND) | ||
set (ThreadSanitizer_COMPILER_OPTIONS -fsanitize=thread) | ||
endif () | ||
|
||
set (Sanitizers_COMPILER_OPTIONS | ||
${Sanitizers_ADDRESS_COMPILER_OPTIONS} | ||
${Sanitizers_UNDEFINED_BEHAVIOR_COMPILER_OPTIONS}) | ||
|
@@ -45,13 +52,21 @@ file (READ ${CMAKE_CURRENT_LIST_DIR}/code_tests/Sanitizers_fiber_test.cc _saniti | |
set (CMAKE_REQUIRED_FLAGS ${Sanitizers_COMPILER_OPTIONS}) | ||
check_cxx_source_compiles ("${_sanitizers_fiber_test_code}" Sanitizers_FIBER_SUPPORT) | ||
|
||
file (READ ${CMAKE_CURRENT_LIST_DIR}/code_tests/ThreadSanitizer_fiber_test.cc _thread_sanitizer_fiber_test_code) | ||
set (CMAKE_REQUIRED_FLAGS ${ThreadSanitizer_COMPILER_OPTIONS}) | ||
check_cxx_source_compiles ("${_thread_sanitizer_fiber_test_code}" ThreadSanitizer_FIBER_SUPPORT) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as explained, i'd suggest define this variable only if ThreadSanitizer is listed in the COMPONENT parameter. |
||
|
||
include (FindPackageHandleStandardArgs) | ||
|
||
find_package_handle_standard_args (Sanitizers | ||
REQUIRED_VARS | ||
Sanitizers_ADDRESS_COMPILER_OPTIONS | ||
Sanitizers_UNDEFINED_BEHAVIOR_COMPILER_OPTIONS) | ||
|
||
find_package_handle_standard_args (ThreadSanitizer | ||
REQUIRED_VARS | ||
ThreadSanitizer_COMPILER_OPTIONS) | ||
Comment on lines
+66
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in general, |
||
|
||
if (Sanitizers_FOUND) | ||
if (NOT (TARGET Sanitizers::address)) | ||
add_library (Sanitizers::address INTERFACE IMPORTED) | ||
|
@@ -71,3 +86,14 @@ if (Sanitizers_FOUND) | |
INTERFACE_LINK_LIBRARIES "${Sanitizers_UNDEFINED_BEHAVIOR_COMPILER_OPTIONS}") | ||
endif () | ||
endif () | ||
|
||
if (ThreadSanitizer_FOUND) | ||
if (NOT (TARGET Sanitizers::thread)) | ||
add_library (Sanitizers::thread INTERFACE IMPORTED) | ||
|
||
set_target_properties (Sanitizers::thread | ||
PROPERTIES | ||
INTERFACE_COMPILE_OPTIONS ${ThreadSanitizer_COMPILER_OPTIONS} | ||
INTERFACE_LINK_LIBRARIES ${ThreadSanitizer_COMPILER_OPTIONS}) | ||
endif () | ||
endif () |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
extern "C" { | ||
void* __tsan_get_current_fiber(); | ||
void* __tsan_create_fiber(unsigned flags); | ||
void __tsan_destroy_fiber(void* fiber); | ||
void __tsan_switch_to_fiber(void* fiber, unsigned flags); | ||
} | ||
|
||
int main() { | ||
void* new_fiber = __tsan_create_fiber(0); | ||
void* main_fiber = __tsan_get_current_fiber(); | ||
__tsan_switch_to_fiber(new_fiber, 0); | ||
__tsan_switch_to_fiber(main_fiber, 0); | ||
__tsan_destroy_fiber(new_fiber); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -169,7 +169,7 @@ def identify_best_standard(cpp_standards, compiler): | |
# For convenience. | ||
tr = seastar_cmake.translate_arg | ||
|
||
MODE_TO_CMAKE_BUILD_TYPE = {'release': 'RelWithDebInfo', 'debug': 'Debug', 'dev': 'Dev', 'sanitize': 'Sanitize' } | ||
MODE_TO_CMAKE_BUILD_TYPE = {'release': 'RelWithDebInfo', 'debug': 'Debug', 'dev': 'Dev', 'sanitize': 'Sanitize', 'sanitize-thread' : 'SanitizeThread'} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'd suggest to add an option to ./configure.py --mode sanitize --sanitizer UndefinedBehavior --sanitizer Thread
./configure.py --mode sanitize --sanitizer Address
# -DCMAKE_BUILD_TYPE is but a convenient way to set a bunch of
# preconfigured settings, but one can fine-tune the build with
# these lower-level options
# just enable these two sanitizers without any predefined configurations
cmake -DCMAKE_BUILD_TYPE=None -DSeastar_SANITIZERS=Address;UndefinedBehavior
# inherit the settings of Sanitize, but customize the enabled Sanitizers
cmake -DCMAKE_BUILD_TYPE=Sanitize -DSeastar_SANITIZERS=Address;UndefinedBehavior |
||
|
||
|
||
def configure_mode(mode): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,6 +71,8 @@ module seastar; | |
#endif | ||
|
||
namespace seastar { | ||
|
||
#ifndef SEASTAR_NO_EXCEPTION_HACK | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
i'd suggest extract this into its own commit. and explain why we want to bring it back. and how this change helps. |
||
using dl_iterate_fn = int (*) (int (*callback) (struct dl_phdr_info *info, size_t size, void *data), void *data); | ||
|
||
[[gnu::no_sanitize_address]] | ||
|
@@ -102,6 +104,9 @@ void init_phdr_cache() { | |
return 0; | ||
}, nullptr); | ||
} | ||
#else | ||
void init_phdr_cache() {} | ||
#endif | ||
|
||
void internal::increase_thrown_exceptions_counter() noexcept { | ||
seastar::engine()._cxx_exceptions++; | ||
|
@@ -124,6 +129,7 @@ void log_exception_trace() noexcept {} | |
|
||
} | ||
|
||
#ifndef SEASTAR_NO_EXCEPTION_HACK | ||
extern "C" | ||
[[gnu::visibility("default")]] | ||
[[gnu::used]] | ||
|
@@ -146,18 +152,16 @@ int dl_iterate_phdr(int (*callback) (struct dl_phdr_info *info, size_t size, voi | |
} | ||
return r; | ||
} | ||
#endif | ||
|
||
#ifndef NO_EXCEPTION_INTERCEPT | ||
extern "C" | ||
[[gnu::visibility("default")]] | ||
[[gnu::used]] | ||
int _Unwind_RaiseException(struct ::_Unwind_Exception *h) { | ||
using throw_fn = int (*)(void *); | ||
static throw_fn org = nullptr; | ||
static throw_fn org = (throw_fn)dlsym (RTLD_NEXT, "_Unwind_RaiseException"); | ||
|
||
if (!org) { | ||
org = (throw_fn)dlsym (RTLD_NEXT, "_Unwind_RaiseException"); | ||
} | ||
if (seastar::local_engine) { | ||
seastar::internal::increase_thrown_exceptions_counter(); | ||
seastar::log_exception_trace(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd prefer keeping using the "sanitize" mode and "Sanitize" config. instead of adding yet another one dedicating for building with TSan enabled, can we just add an option named
Seastar_SANITIZERS
as a list of sanitizers for configuring the build?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tchaikov if you're thinking of enabling this sanitizer by default when sanitizers are enabled - please try to measure the performance impact of this sanitizer on some "typical" (however you define that) Seastar test code. The majority of the Seastar code (and code using Seastar) does not use share data between threads, so although catching every bug is great, I doubt this sanitizer will help catch many bugs so we need some justification to turn it on by
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's not my plan. my plan is to keep the existing behavior.