-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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 CMake options to build using sanitizers #1053
Conversation
@rsgowman Rich, could you please take a look and check that what I'm doing is reasonable with regards to CMake? A couple of notes:
|
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.
If you could check that sanitizers work on Linux, it would be awesome.
$ cmake -DASAN=ON ..
-- The C compiler identification is GNU 7.3.0
-- The CXX compiler identification is GNU 7.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found ZLIB: /usr/lib/x86_64-linux-gnu/libz.so (found version "1.2.8")
-- Configuring done
-- Generating done
CMake Warning:
Manually-specified variables were not used by the project:
ASAN
I think that's superbuild related. We need to pass it down into the subproject like this:
diff --git a/cmake/external/firestore.cmake b/cmake/external/firestore.cmake
index 94f7caee..65c43f84 100644
--- a/cmake/external/firestore.cmake
+++ b/cmake/external/firestore.cmake
@@ -32,6 +32,7 @@ ExternalProject_Add(
CMAKE_ARGS
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX:PATH=${FIREBASE_INSTALL_DIR}
+ -DASAN:BOOL=${ASAN}
BUILD_ALWAYS ON
With that specified, the warning goes away. (Repeat for TSAN/UBSAN).
That said, everything compiles fine for me, so if there's a problem with autoid_test.cc, my compiler doesn't see it. (I also tried against the autoid_test.cc from HEAD^, i.e. right before you reverted it.)
Firestore/CMakeLists.txt
Outdated
option(TSAN "Build with Thread sanitizer (mutually exculsive with ASAN)" OFF) | ||
option(UBSAN "Build with Undefined behavior sanitizer" OFF) | ||
|
||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") |
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.
cmake/CompilerSetup.cmake defines CLANG and GNU with these values, so you could simplify to just if (CLANG OR GNU)
. But I don't mind this as is.
(Note that the include of CompilerSetup is below.)
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.
Hmm, I can't move CompilerSetup
here, because it sets compiler options that don't play nice with Abseil. Conversely, it seems that setting sanitizer options below would affect how the dependencies are compiled, and I thought it's best to compile as much stuff with sanitizers enabled as possible. I might be wrong on this, though.
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.
We could also factor out compiler detection from CompilerSetup
into a separate CMake module. Alternatively, we could make the core logic of setting up the compile a function within CompilerSetup
. That way we can source the file wherever we please and then specifically invoke our compiler settings in the location that makes the best sense.
An extension of that would be to move this setup into there too since this is largely compiler setup stuff anyway,
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.
We could also factor out compiler detection from CompilerSetup into a separate CMake module
It seems to me that it's the least intrusive way, I can do it in one of the follow-ups.
Firestore/CMakeLists.txt
Outdated
@@ -15,6 +15,53 @@ | |||
cmake_minimum_required(VERSION 2.8.11) | |||
project(firestore C CXX) | |||
|
|||
option(ASAN "Build with Address sanitizer (mutually exculsive with TSAN)" OFF) |
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.
All your changes here might make more sense in cmake/CompilerOptions.cmake, though they would then apply across all of the repo. (As long as everything defaults to off, maybe that's ok?)
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'm okay with that, let's postpone until the next round of review.
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.
It's better not to hide the options we support since this file serves as documentation for people trying to figure out how to configure this build to their purposes.
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.
@wilhuff Do you think that it would be worthwhile to have these options in the Firebase build as well? If that were the case, I thought about splitting the sanitizer logic from here into ./cmake/Sanitizers.cmake
and including that in both projects. It's a good point that it would make these options non-obvious, so perhaps it would be okay to have the declaration of sanitizer options twice, once for each project. What do you think?
Right, I always run just |
@rsgowman Rich, I think that passing the options the way you describe it (in I run CMake from |
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'm broadly in favor of this change though I think we could afford to
- factor this out of the FIrestore specific build
- cut down on the validation contortion the code is going through
Firestore/CMakeLists.txt
Outdated
option(TSAN "Build with Thread sanitizer (mutually exculsive with ASAN)" OFF) | ||
option(UBSAN "Build with Undefined behavior sanitizer" OFF) | ||
|
||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") |
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.
We could also factor out compiler detection from CompilerSetup
into a separate CMake module. Alternatively, we could make the core logic of setting up the compile a function within CompilerSetup
. That way we can source the file wherever we please and then specifically invoke our compiler settings in the location that makes the best sense.
An extension of that would be to move this setup into there too since this is largely compiler setup stuff anyway,
Firestore/CMakeLists.txt
Outdated
# - it requires all dependencies to be compiled with msan enabled (see | ||
# https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo); | ||
# - AppleClang doesn't support it. | ||
option(TSAN "Build with Thread sanitizer (mutually exculsive with ASAN)" OFF) |
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.
Conventionally these kinds of options are WITH_FOO
. For example, see other CMake files that do it:: https://github.com/facebook/rocksdb/blob/master/CMakeLists.txt#L258. This is not universal though, so feel free to ignore.
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.
Done. I like that this prefix makes them grouped together in the cache.
Firestore/CMakeLists.txt
Outdated
@@ -15,6 +15,53 @@ | |||
cmake_minimum_required(VERSION 2.8.11) | |||
project(firestore C CXX) | |||
|
|||
option(ASAN "Build with Address sanitizer (mutually exculsive with TSAN)" OFF) |
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.
It's better not to hide the options we support since this file serves as documentation for people trying to figure out how to configure this build to their purposes.
Firestore/CMakeLists.txt
Outdated
message(FATAL_ERROR "Only Clang and GCC support sanitizers.") | ||
endif() | ||
|
||
function(add_to_compile_and_link_flags flag) |
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.
If you make this a macro instead of a function you can avoid the PARENT_SCOPE
messiness.
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.
Done.
Firestore/CMakeLists.txt
Outdated
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} ${flag}" PARENT_SCOPE) | ||
endfunction() | ||
|
||
if (HAVE_SANITIZERS AND USE_SANITIZERS) |
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.
This logic seems more complicated than it needs to be. It seems like you could just have
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(ASAN)
// add flags
endif()
if(TSAN)
if(ASAN OR UBSAN)
message(FATAL_ERROR "Cannot combine tsan with other santizers")
endif()
// add flags
endif()
if(UBSAN)
// add flags
endif()
else()
if(ASAN OR TSAN OR UBSAN)
message(FATAL_ERROR "Only Clang and GCC support sanitizers")
endif()
endif()
This eliminates HAVE_SANITIZERS
and USE_SANITIZERS
and all these extra blocks that check conditions you're effectively duplicating in here through nesting.
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.
Done. The intention behind those named variables was to make things clearer, but I think the shorter version is at least as readable.
Firestore/CMakeLists.txt
Outdated
|
||
function(add_to_compile_and_link_flags flag) | ||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE) | ||
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} ${flag}" PARENT_SCOPE) |
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.
Elsehwere I've seen this specified as CMAKE_EXE_FLAGS since we only need to specify these extra flags during compilation and final linking into our tests.
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.
Oh, it looks like I'm using a non-existent CMake variable. Changed to CMAKE_EXE_LINKER_FLAGS
.
The build actually works without specifying the linker flag at all. If linking is invoked via the frontend call (e.g., via clang
, not ld
), then perhaps the CXX flag is bypassed to the linker automatically. Anyway, perhaps I should drop the linker part altogether, since it appears to be extraneous.
Firestore/CMakeLists.txt
Outdated
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") | ||
|
||
if (TSAN) | ||
add_to_compile_and_link_flags("-fsanitize=thread") |
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.
Doesn't tsan also need us to specify -pie
on final linking and -fPIC
during compilation?
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.
Hmm, apparently not. The most relevant thing I could find is this thread, which doesn't contain a definitive answer, but has this quote from the Clang docs:
Non-position-independent executables are not supported. Therefore, the
fsanitize=thread
flag will cause Clang to act as though the-fPIE
flag had been supplied if compiling without-fPIC
, and as though the-pie
flag had been supplied if linking an executable.
I couldn't find the same stated explicitly for GCC. However, the wiki page for TSan only has the following instructions:
Simply compile your program with -fsanitize=thread and link it with -fsanitize=thread. To get a reasonable performance add -O2. Use -g to get file names and line numbers in the warning messages.
(Let me know if you think -g
should be added)
@var-const : Using your instructions, I got the following. (I might have got it before too? Unsure. The tests actually pass, despite the errors, therefore masking them.):
Can we adjust so that these cause the test to fail? Otherwise, I'll never notice. :) |
Followup to my comment: with properly bad code (code I was testing with was only a little bad), things fail spectacularly. :) We'll still have the issue of the asan (?) only outputting warnings (as displayed in my previous comment). You mentioned that turning them into errors causes an immediate failure, so you'd only see one of them (instead of all of them) making it more difficult to find/fix. Maybe once we're in a happy state (ie no errors) we can enable immediate failure, since (assuming quick test cycles) there'd only ever be the error that you just immediately introduced? |
@rsgowman Just to write up our offline discussion for posterity: ASan is designed to fail upon encountering the first issue. My impression is that it's more of a limitation than a deliberate decision. From the wiki page:
UBSan, on the other hand, produces warnings by default (the warning you mentioned in your comment about misaligned pointer comes from UBSan), though it's possible to make UBSan fail using |
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.
This is likely worth committing as-is. We can refactor this later if it turns out that more projects within Firebase want it.
Example run: `cmake -DWITH_ASAN=ON ..` (from Firestore build folder)
Supported sanitizers:
Sample run:
cmake -DASAN=ON ..
(note that CMake caches variables values).Note that this only applies sanitizers to Firestore build, not the whole Firebase build. If I read the docs correctly, this is explicitly okay for ASan:
Not sure about the other two, but empirically they seem to work correctly.
The next step would be to add these to the Travis build. Note that only ASan and UBSan can work together, and that
-fsanitize
option requires rebuilding. Also note: