diff --git a/.cmake-format b/.cmake-format index 242171f55..ff3057140 100644 --- a/.cmake-format +++ b/.cmake-format @@ -26,14 +26,11 @@ command_case: lower # Format keywords consistently as 'lower' or 'upper' case keyword_case: upper +# enable comment markup parsing and reflow +enable_markup: False + # Setup all the additional commands additional_commands: - header_library: - kwargs: - NAME: "*" - HEADER: "*" - PATH_SUFFIX: "*" - URL: "*" header_library: kwargs: NAME: "*" diff --git a/CMakeLists.txt b/CMakeLists.txt index e500f8ab9..0fb72a9f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,64 @@ if(CI_BUILD) endif() endif(CI_BUILD) +# When using valgrind data race detector or helgrind (both are for debugging issues with threading and data races) +# certain symbols need to be overridden to enable better stack traces. This option enables those extra things +# See https://valgrind.org/docs/manual/drd-manual.html#drd-manual.CXX11 for more details +# +# This is disabled by default and will only be enabled when this option is enabled AND NDEBUG is NOT defined (so compiling in debug mode) +option(USE_VALGRIND "Add extra annotations for better valgrind handling" OFF) +if(USE_VALGRIND) + add_compile_options(-DUSE_VALGRIND) +endif(USE_VALGRIND) + +############################################################################### +### Options for enabling different sanitisers. ### +### ### +### User beware: ### +### Not all sanitisers can be enabled at the same time. ### +############################################################################### +option(USE_ASAN "Enable address sanitization" OFF) +if(USE_ASAN) + add_compile_options(-fsanitize=address -fno-omit-frame-pointer -U_FORTIFY_SOURCE -fno-common) + add_link_options(-fsanitize=address) + link_libraries(asan) +endif(USE_ASAN) + +option(USE_LSAN "Enable leak sanitization" OFF) +if(USE_LSAN) + add_compile_options(-fsanitize=leak -fno-omit-frame-pointer -U_FORTIFY_SOURCE -fno-common) + add_link_options(-fsanitize=leak) + link_libraries(lsan) +endif(USE_LSAN) + +option(USE_TSAN "Enable thread sanitization" OFF) +if(USE_TSAN) + add_compile_options(-fsanitize=thread -fno-omit-frame-pointer -U_FORTIFY_SOURCE -fno-common) + add_link_options(-fsanitize=thread) + link_libraries(tsan) +endif(USE_TSAN) + +option(USE_UBSAN "Enable undefined behaviour sanitization" OFF) +if(USE_UBSAN) + add_compile_options(-fsanitize=undefined -fno-omit-frame-pointer -U_FORTIFY_SOURCE -fno-common) + add_link_options(-fsanitize=undefined) + link_libraries(ubsan) +endif(USE_UBSAN) + +# Option for enabling code profiling. Disabled by default +option(ENABLE_PROFILING "Compile with profiling support enabled.") +if(ENABLE_PROFILING) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + message( + WARNING + "Profiling is enabled but no debugging symbols will be kept in the compiled binaries. This may cause fine-grained profilling data to be lost." + ) + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg -fprofile-arcs") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg -fprofile-arcs") + set(CMAKE_LINKER "${CMAKE_LINKER_FLAGS} -pg -fprofile-arcs") +endif(ENABLE_PROFILING) + # Make the compiler display colours always (even when we build with ninja) if(CMAKE_CXX_COMPILER_ID MATCHES GNU) add_compile_options(-fdiagnostics-color=always) diff --git a/src/PowerPlant.hpp b/src/PowerPlant.hpp index 3c2a670e1..da786f1b8 100644 --- a/src/PowerPlant.hpp +++ b/src/PowerPlant.hpp @@ -19,6 +19,12 @@ #ifndef NUCLEAR_POWERPLANT_HPP #define NUCLEAR_POWERPLANT_HPP +// This file needs to be included in a spot that means it will be included everywhere +// See https://valgrind.org/docs/manual/drd-manual.html#drd-manual.CXX11 +#if defined(USE_VALGRIND) && !defined(NDEBUG) + #include "util/valgrind/valgrind.hpp" +#endif // defined(USE_VALGRIND) && !defined(NDEBUG) + #include #include #include diff --git a/src/util/valgrind/valgrind.cpp b/src/util/valgrind/valgrind.cpp new file mode 100644 index 000000000..54c2ad91c --- /dev/null +++ b/src/util/valgrind/valgrind.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 Alex Biddulph + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if defined(USE_VALGRIND) && !defined(NDEBUG) + // NOLINTBEGIN + // _State_ptr and State are private members od std::thread, this define allows them to not be private + #define _GLIBCXX_THREAD_IMPL 1 + #include + #undef _GLIBCXX_THREAD_IMPL + + #include + +namespace std { +extern "C" { +static void* execute_native_thread_routine(void* __p) { + thread::_State_ptr __t{static_cast(__p)}; + __t->_M_run(); + return nullptr; +} +void thread::_M_start_thread(_State_ptr state, void (*depend)()) { + // Make sure it's not optimized out, not even with LTO. + asm("" : : "rm"(depend)); + + if (!__gthread_active_p()) { + #if __cpp_exceptions + throw system_error(make_error_code(errc::operation_not_permitted), "Enable multithreading to use std::thread"); + #else + __builtin_abort(); + #endif + } + + const int err = __gthread_create(&_M_id._M_thread, &execute_native_thread_routine, state.get()); + if (err) + __throw_system_error(err); + state.release(); +} +} +} // namespace std +// NOLINTEND +#endif // defined(USE_VALGRIND) && !defined(NDEBUG) diff --git a/src/util/valgrind/valgrind.hpp b/src/util/valgrind/valgrind.hpp new file mode 100644 index 000000000..a9613b5df --- /dev/null +++ b/src/util/valgrind/valgrind.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 Alex Biddulph + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +// See https://valgrind.org/docs/manual/drd-manual.html#drd-manual.CXX11 +#if defined(USE_VALGRIND) && !defined(NDEBUG) + #include + #undef _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE + #undef _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER + #define _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(addr) ANNOTATE_HAPPENS_BEFORE(addr) // NOLINT + #define _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(addr) ANNOTATE_HAPPENS_AFTER(addr) // NOLINT +#endif // defined(USE_VALGRIND) && !defined(NDEBUG)