From 9ef3800986fb12c1177d751aa62a638c21495460 Mon Sep 17 00:00:00 2001 From: "Jonathan R. Madsen" Date: Fri, 6 Aug 2021 13:08:57 -0500 Subject: [PATCH] Hosttrace via Dyninst - complete with ctest support --- .clang-format | 65 + .clang-tidy | 48 + .gitignore | 1 + CMakeLists.txt | 137 ++ cmake/BuildSettings.cmake | 288 +++ cmake/Compilers.cmake | 594 +++++ cmake/MacroUtilities.cmake | 1526 ++++++++++++ cmake/Packages.cmake | 159 ++ examples/CMakeLists.txt | 6 + examples/transpose/CMakeLists.txt | 59 + examples/transpose/Makefile | 38 + examples/transpose/Makefile.in | 37 + examples/transpose/transpose.cpp | 156 ++ examples/transpose/transpose.new.cpp | 219 ++ include/hosttrace.hpp | 682 ++++++ include/timemory/.clang-format | 65 + include/timemory/api.hpp | 221 ++ include/timemory/api/macros.hpp | 76 + include/timemory/backends/process.hpp | 110 + include/timemory/compat/macros.h | 289 +++ include/timemory/environment.hpp | 33 + include/timemory/environment/declaration.hpp | 268 +++ include/timemory/environment/definition.hpp | 362 +++ include/timemory/environment/macros.hpp | 69 + include/timemory/environment/types.hpp | 103 + include/timemory/general/source_location.hpp | 366 +++ include/timemory/general/types.hpp | 68 + include/timemory/hash/declaration.hpp | 85 + include/timemory/hash/macros.hpp | 43 + include/timemory/hash/types.hpp | 290 +++ include/timemory/macros/attributes.hpp | 193 ++ include/timemory/macros/compiler.hpp | 205 ++ include/timemory/macros/language.hpp | 105 + include/timemory/macros/os.hpp | 118 + include/timemory/mpl/apply.hpp | 684 ++++++ include/timemory/mpl/bits/stl.hpp | 461 ++++ include/timemory/mpl/bits/types.hpp | 297 +++ include/timemory/mpl/concepts.hpp | 647 +++++ include/timemory/mpl/math.hpp | 1244 ++++++++++ include/timemory/mpl/stl.hpp | 371 +++ include/timemory/mpl/types.hpp | 1050 +++++++++ include/timemory/tpls/cereal/cereal.hpp | 45 + .../timemory/tpls/cereal/cereal/access.hpp | 434 ++++ .../timemory/tpls/cereal/cereal/cereal.hpp | 1169 +++++++++ .../tpls/cereal/cereal/details/helpers.hpp | 467 ++++ .../cereal/details/polymorphic_impl_fwd.hpp | 69 + .../cereal/cereal/details/static_object.hpp | 124 + .../tpls/cereal/cereal/details/traits.hpp | 1765 ++++++++++++++ .../timemory/tpls/cereal/cereal/macros.hpp | 143 ++ .../tpls/cereal/cereal/specialize.hpp | 168 ++ .../tpls/cereal/cereal/types/base_class.hpp | 217 ++ .../tpls/cereal/cereal/types/common.hpp | 143 ++ include/timemory/utility/argparse.cpp | 706 ++++++ include/timemory/utility/argparse.hpp | 1124 +++++++++ include/timemory/utility/bits/macros.hpp | 205 ++ include/timemory/utility/macros.hpp | 305 +++ include/timemory/utility/popen.cpp | 391 ++++ include/timemory/utility/popen.hpp | 241 ++ .../timemory/utility/transient_function.hpp | 142 ++ include/timemory/utility/types.hpp | 846 +++++++ include/timemory/utility/utility.cpp | 409 ++++ include/timemory/utility/utility.hpp | 882 +++++++ include/timemory/variadic/macros.hpp | 349 +++ roctrace.cfg | 23 + scripts/clean-timemory.sh | 37 + src/hosttrace-details.cpp | 542 +++++ src/hosttrace.cpp | 2083 +++++++++++++++++ src/library.cpp | 241 ++ tests/CMakeLists.txt | 36 + 69 files changed, 25144 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 CMakeLists.txt create mode 100644 cmake/BuildSettings.cmake create mode 100644 cmake/Compilers.cmake create mode 100644 cmake/MacroUtilities.cmake create mode 100644 cmake/Packages.cmake create mode 100644 examples/CMakeLists.txt create mode 100644 examples/transpose/CMakeLists.txt create mode 100644 examples/transpose/Makefile create mode 100644 examples/transpose/Makefile.in create mode 100644 examples/transpose/transpose.cpp create mode 100644 examples/transpose/transpose.new.cpp create mode 100644 include/hosttrace.hpp create mode 100644 include/timemory/.clang-format create mode 100644 include/timemory/api.hpp create mode 100644 include/timemory/api/macros.hpp create mode 100644 include/timemory/backends/process.hpp create mode 100644 include/timemory/compat/macros.h create mode 100644 include/timemory/environment.hpp create mode 100644 include/timemory/environment/declaration.hpp create mode 100644 include/timemory/environment/definition.hpp create mode 100644 include/timemory/environment/macros.hpp create mode 100644 include/timemory/environment/types.hpp create mode 100644 include/timemory/general/source_location.hpp create mode 100644 include/timemory/general/types.hpp create mode 100644 include/timemory/hash/declaration.hpp create mode 100644 include/timemory/hash/macros.hpp create mode 100644 include/timemory/hash/types.hpp create mode 100644 include/timemory/macros/attributes.hpp create mode 100644 include/timemory/macros/compiler.hpp create mode 100644 include/timemory/macros/language.hpp create mode 100644 include/timemory/macros/os.hpp create mode 100644 include/timemory/mpl/apply.hpp create mode 100644 include/timemory/mpl/bits/stl.hpp create mode 100644 include/timemory/mpl/bits/types.hpp create mode 100644 include/timemory/mpl/concepts.hpp create mode 100644 include/timemory/mpl/math.hpp create mode 100644 include/timemory/mpl/stl.hpp create mode 100644 include/timemory/mpl/types.hpp create mode 100644 include/timemory/tpls/cereal/cereal.hpp create mode 100644 include/timemory/tpls/cereal/cereal/access.hpp create mode 100644 include/timemory/tpls/cereal/cereal/cereal.hpp create mode 100644 include/timemory/tpls/cereal/cereal/details/helpers.hpp create mode 100644 include/timemory/tpls/cereal/cereal/details/polymorphic_impl_fwd.hpp create mode 100644 include/timemory/tpls/cereal/cereal/details/static_object.hpp create mode 100644 include/timemory/tpls/cereal/cereal/details/traits.hpp create mode 100644 include/timemory/tpls/cereal/cereal/macros.hpp create mode 100644 include/timemory/tpls/cereal/cereal/specialize.hpp create mode 100644 include/timemory/tpls/cereal/cereal/types/base_class.hpp create mode 100644 include/timemory/tpls/cereal/cereal/types/common.hpp create mode 100644 include/timemory/utility/argparse.cpp create mode 100644 include/timemory/utility/argparse.hpp create mode 100644 include/timemory/utility/bits/macros.hpp create mode 100644 include/timemory/utility/macros.hpp create mode 100644 include/timemory/utility/popen.cpp create mode 100644 include/timemory/utility/popen.hpp create mode 100644 include/timemory/utility/transient_function.hpp create mode 100644 include/timemory/utility/types.hpp create mode 100644 include/timemory/utility/utility.cpp create mode 100644 include/timemory/utility/utility.hpp create mode 100644 include/timemory/variadic/macros.hpp create mode 100644 roctrace.cfg create mode 100755 scripts/clean-timemory.sh create mode 100644 src/hosttrace-details.cpp create mode 100644 src/hosttrace.cpp create mode 100644 src/library.cpp create mode 100644 tests/CMakeLists.txt diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..0af3d6d017 --- /dev/null +++ b/.clang-format @@ -0,0 +1,65 @@ +# requires clang-tidy version 6.0+ +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: TopLevel +AlwaysBreakAfterReturnType: TopLevel +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BasedOnStyle: Mozilla +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +ColumnLimit: 90 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 0 +ContinuationIndentWidth: 4 +FixNamespaceComments: true +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +PointerAlignment: Left +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000000..c5b7cac1ee --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,48 @@ +--- +Checks: "-*,\ +misc-*,\ +-misc-incorrect-roundings,\ +-misc-macro-parentheses,\ +-misc-misplaced-widening-cast,\ +-misc-static-assert,\ +-misc-no-recursion,\ +-misc-non-private-member-variables-in-classes,\ +modernize-*,\ +-modernize-deprecated-headers,\ +-modernize-raw-string-literal,\ +-modernize-return-braced-init-list,\ +-modernize-use-transparent-functors,\ +-modernize-use-trailing-return-type,\ +-modernize-avoid-c-arrays,\ +-modernize-redundant-void-arg,\ +-modernize-use-using,\ +-modernize-use-auto,\ +-modernize-concat-nested-namespaces,\ +performance-*,\ +readability-*,\ +-readability-function-size,\ +-readability-identifier-naming,\ +-readability-implicit-bool-cast,\ +-readability-inconsistent-declaration-parameter-name,\ +-readability-named-parameter,\ +-readability-magic-numbers,\ +-readability-redundant-declaration,\ +-readability-redundant-member-init,\ +-readability-simplify-boolean-expr,\ +-readability-uppercase-literal-suffix,\ +-readability-braces-around-statements,\ +-readability-avoid-const-params-in-decls,\ +-readability-else-after-return,\ +-readability-isolate-declaration,\ +-readability-redundant-string-cstr,\ +-readability-static-accessed-through-instance,\ +-readability-const-return-type,\ +-readability-redundant-access-specifiers,\ +-readability-function-cognitive-complexity,\ +" +CheckOptions: + - key: readability-braces-around-statements.ShortStatementLines + value: '2' + - key: readability-implicit-bool-conversion.AllowPointerConditions + value: '1' +... diff --git a/.gitignore b/.gitignore index 259148fa18..7f11a65abe 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ *.exe *.out *.app +/build* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..8ff1c90891 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,137 @@ +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND + CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(MSG "") + message(STATUS "Warning! Building from the source directory is not recommended") + message(STATUS "If unintented, please remove 'CMakeCache.txt' and 'CMakeFiles'") + message(STATUS "and build from a separate directory") + message(AUTHOR_WARNING "In-source build") +endif() + +project( + hosttrace + LANGUAGES CXX) + +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) + +if("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE) +endif() + +include(GNUInstallDirs) # install directories +include(MacroUtilities) # various functions and macros +include(Compilers) # compiler identification +include(BuildSettings) # compiler flags + +set(CMAKE_CXX_STANDARD 17 CACHE STRING "CXX language standard") +add_option(CMAKE_CXX_STANDARD_REQUIRED "Require C++ language standard" ON) +add_option(CMAKE_CXX_EXTENSIONS "Compiler specific language extensions" OFF) +add_option(HOSTTRACE_USE_CLANG_TIDY "Enable clang-tidy" OFF) + +include(Packages) # finds third-party libraries + +hosttrace_activate_clang_tidy() + +#------------------------------------------------------------------------------# +# +# hosttrace-library target +# +#------------------------------------------------------------------------------# + +add_library(hosttrace-library SHARED + ${CMAKE_CURRENT_LIST_DIR}/src/library.cpp + ${perfetto_DIR}/sdk/perfetto.cc) + +target_include_directories(hosttrace-library PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/include) + +target_include_directories(hosttrace-library SYSTEM PRIVATE + ${perfetto_DIR}/sdk) + +target_link_libraries(hosttrace-library PRIVATE + hosttrace::hosttrace-threading + $,hosttrace::hosttrace-sanitizer,>) + +set_target_properties(hosttrace-library PROPERTIES + OUTPUT_NAME hosttrace) + +install( + TARGETS hosttrace-library + DESTINATION ${CMAKE_INSTALL_LIBDIR} + OPTIONAL) + +#------------------------------------------------------------------------------# +# +# hosttrace-exe target +# +#------------------------------------------------------------------------------# + +add_executable(hosttrace-exe ${_EXCLUDE} + ${CMAKE_CURRENT_LIST_DIR}/src/hosttrace.cpp + ${CMAKE_CURRENT_LIST_DIR}/include/hosttrace.hpp + ${CMAKE_CURRENT_LIST_DIR}/src/hosttrace-details.cpp) + +target_include_directories(hosttrace-exe PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/include) + +target_link_libraries(hosttrace-exe PRIVATE + hosttrace::hosttrace-dyninst + hosttrace::hosttrace-compile-options) + +set_target_properties(hosttrace-exe PROPERTIES + OUTPUT_NAME hosttrace + INSTALL_RPATH_USE_LINK_PATH ON) + +install( + TARGETS hosttrace-exe + DESTINATION ${CMAKE_INSTALL_BINDIR} + OPTIONAL) + +#------------------------------------------------------------------------------# +# +# clang-format target +# +#------------------------------------------------------------------------------# + +find_program(CLANG_FORMAT_EXE + NAMES + clang-format-12 + clang-format-11 + clang-format-10 + clang-format-9 + clang-format) + +if(CLANG_FORMAT_EXE) + file(GLOB sources + ${PROJECT_SOURCE_DIR}/src/*.cpp) + file(GLOB headers + ${PROJECT_SOURCE_DIR}/include/*.hpp) + file(GLOB_RECURSE examples + ${PROJECT_SOURCE_DIR}/examples/*.cpp + ${PROJECT_SOURCE_DIR}/examples/*.hpp) + add_custom_target(format + ${CLANG_FORMAT_EXE} -i ${sources} ${headers} ${examples} + COMMENT "Running ${CLANG_FORMAT_EXE}...") +else() + message(AUTHOR_WARNING "clang-format could not be found. format build target not available.") +endif() + +#------------------------------------------------------------------------------# +# +# examples +# +#------------------------------------------------------------------------------# + +add_subdirectory(examples) + +#------------------------------------------------------------------------------# +# +# tests +# +#------------------------------------------------------------------------------# + +include(CTest) +enable_testing() + +add_subdirectory(tests) diff --git a/cmake/BuildSettings.cmake b/cmake/BuildSettings.cmake new file mode 100644 index 0000000000..209a64ca82 --- /dev/null +++ b/cmake/BuildSettings.cmake @@ -0,0 +1,288 @@ +# include guard +include_guard(DIRECTORY) + +########################################################################################## +# +# Handles the build settings +# +########################################################################################## + +include(GNUInstallDirs) +include(Compilers) +include(FindPackageHandleStandardArgs) +include(MacroUtilities) + +option(hosttrace_BUILD_DEVELOPER "Extra build flags for development like -Werror" OFF) +option(hosttrace_BUILD_EXTRA_OPTIMIZATIONS "Extra optimization flags" OFF) +option(hosttrace_BUILD_LTO "Build with link-time optimization" OFF) +option(hosttrace_USE_COMPILE_TIMING "" OFF) +option(hosttrace_USE_COVERAGE "" OFF) +option(hosttrace_USE_SANITIZER "" OFF) + +target_compile_definitions(hosttrace-compile-options INTERFACE $<$:DEBUG>) + +set(hosttrace_SANITIZER_TYPE "leak" CACHE STRING "Sanitizer type") + +#----------------------------------------------------------------------------------------# +# dynamic linking and runtime libraries +# +if(CMAKE_DL_LIBS AND NOT "${CMAKE_DL_LIBS}" STREQUAL "dl") + # if cmake provides dl library, use that + set(dl_LIBRARY ${CMAKE_DL_LIBS} CACHE FILEPATH "dynamic linking system library") +endif() + +foreach(_TYPE dl rt dw) + if(NOT ${_TYPE}_LIBRARY) + find_library(${_TYPE}_LIBRARY NAMES ${_TYPE}) + endif() +endforeach() + +find_package_handle_standard_args(dl-library REQUIRED_VARS dl_LIBRARY) +find_package_handle_standard_args(rt-library REQUIRED_VARS rt_LIBRARY) +# find_package_handle_standard_args(dw-library REQUIRED_VARS dw_LIBRARY) + +if(dl_LIBRARY) + target_link_libraries(hosttrace-compile-options INTERFACE ${dl_LIBRARY}) +endif() + +#----------------------------------------------------------------------------------------# +# set the compiler flags +# +add_flag_if_avail( + "-W" + "-Wall" + "-Wno-unknown-pragmas" + "-Wno-unused-function" + "-Wno-ignored-attributes" + "-Wno-attributes" + "-Wno-missing-field-initializers") + +if(WIN32) + # suggested by MSVC for spectre mitigation in rapidjson implementation + add_cxx_flag_if_avail("/Qspectre") +endif() + +if(CMAKE_CXX_COMPILER_IS_CLANG) + add_cxx_flag_if_avail( + "-Wno-mismatched-tags") +endif() + +#----------------------------------------------------------------------------------------# +# extra flags for debug information in debug or optimized binaries +# +add_interface_library(hosttrace-compile-debuginfo + "Attempts to set best flags for more expressive profiling information in debug or optimized binaries") + +add_target_flag_if_avail(hosttrace-compile-debuginfo + "-g" + "-fno-omit-frame-pointer" + "-fno-optimize-sibling-calls") + +if(CMAKE_CUDA_COMPILER_IS_NVIDIA) + add_target_cuda_flag(hosttrace-compile-debuginfo "-lineinfo") +endif() + +target_compile_options(hosttrace-compile-debuginfo INTERFACE + $<$:$<$:-rdynamic>> + $<$:$<$:-rdynamic>>) + +if(NOT APPLE) + target_link_options(hosttrace-compile-debuginfo INTERFACE + $<$:-rdynamic>) +endif() + +if(CMAKE_CUDA_COMPILER_IS_NVIDIA) + target_compile_options(hosttrace-compile-debuginfo INTERFACE + $<$:$<$:-Xcompiler=-rdynamic>>) +endif() + +if(dl_LIBRARY) + target_link_libraries(hosttrace-compile-debuginfo INTERFACE ${dl_LIBRARY}) +endif() + +if(rt_LIBRARY) + target_link_libraries(hosttrace-compile-debuginfo INTERFACE ${rt_LIBRARY}) +endif() + +#----------------------------------------------------------------------------------------# +# non-debug optimizations +# +add_interface_library(hosttrace-compile-extra "Extra optimization flags") +if(NOT hosttrace_USE_COVERAGE) + add_target_flag_if_avail(hosttrace-compile-extra + "-finline-functions" + "-funroll-loops" + "-ftree-vectorize" + "-ftree-loop-optimize" + "-ftree-loop-vectorize") +endif() + +if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" AND hosttrace_BUILD_EXTRA_OPTIMIZATIONS) + target_link_libraries(hosttrace-compile-options INTERFACE + $) + add_flag_if_avail( + "-fno-signaling-nans" + "-fno-trapping-math" + "-fno-signed-zeros" + "-ffinite-math-only" + "-fno-math-errno" + "-fpredictive-commoning" + "-fvariable-expansion-in-unroller") + # add_flag_if_avail("-freciprocal-math" "-fno-signed-zeros" "-mfast-fp") +endif() + +#----------------------------------------------------------------------------------------# +# debug-safe optimizations +# +add_cxx_flag_if_avail("-faligned-new") + +if(hosttrace_BUILD_LTO) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) +endif() + +hosttrace_save_variables(FLTO + VARIABLES CMAKE_CXX_FLAGS) +set(CMAKE_CXX_FLAGS "-flto=thin ${CMAKE_CXX_FLAGS}") + +add_interface_library(hosttrace-lto "Adds link-time-optimization flags") +add_target_flag_if_avail(hosttrace-lto "-flto=thin") +if(NOT cxx_hosttrace_lto_flto_thin) + set(CMAKE_CXX_FLAGS "-flto ${CMAKE_CXX_FLAGS}") + add_target_flag_if_avail(hosttrace-lto "-flto") + if(NOT cxx_hosttrace_lto_flto) + add_disabled_interface(hosttrace-lto) + set(hosttrace_BUILD_LTO OFF) + else() + target_link_options(hosttrace-lto INTERFACE -flto) + endif() +else() + target_link_options(hosttrace-lto INTERFACE -flto=thin) +endif() + +if(hosttrace_BUILD_LTO) + target_link_libraries(hosttrace-compile-options INTERFACE hosttrace::hosttrace-lto) +endif() + +hosttrace_restore_variables(FLTO + VARIABLES CMAKE_CXX_FLAGS) + +#----------------------------------------------------------------------------------------# +# print compilation timing reports (Clang compiler) +# +add_interface_library(hosttrace-compile-timing + "Adds compiler flags which report compilation timing metrics") +if(CMAKE_CXX_COMPILER_IS_CLANG) + add_target_flag_if_avail(hosttrace-compile-timing "-ftime-trace") + if(NOT cxx_hosttrace_compile_timing_ftime_trace) + add_target_flag_if_avail(hosttrace-compile-timing "-ftime-report") + endif() +else() + add_target_flag_if_avail(hosttrace-compile-timing "-ftime-report") +endif() + +if(hosttrace_USE_COMPILE_TIMING) + target_link_libraries(hosttrace-compile-options INTERFACE hosttrace-compile-timing) +endif() + +if(NOT cxx_hosttrace_compile_timing_ftime_report AND NOT cxx_hosttrace_compile_timing_ftime_trace) + add_disabled_interface(hosttrace-compile-timing) +endif() + +#----------------------------------------------------------------------------------------# +# developer build flags +# +add_interface_library(hosttrace-develop-options "Adds developer compiler flags") +if(hosttrace_BUILD_DEVELOPER) + add_target_flag_if_avail(hosttrace-develop-options + # "-Wabi" + "-Wdouble-promotion" + "-Wshadow" + "-Wextra" + "-Wpedantic" + "-Werror" + "/showIncludes") +endif() + +#----------------------------------------------------------------------------------------# +# visibility build flags +# +add_interface_library(hosttrace-default-visibility + "Adds -fvisibility=default compiler flag") +add_interface_library(hosttrace-hidden-visibility + "Adds -fvisibility=hidden compiler flag") + +add_target_flag_if_avail(hosttrace-default-visibility + "-fvisibility=default") +add_target_flag_if_avail(hosttrace-hidden-visibility + "-fvisibility=hidden" "-fvisibility-inlines-hidden") + +foreach(_TYPE default hidden) + if(NOT cxx_hosttrace_${_TYPE}_visibility_fvisibility_${_TYPE}) + add_disabled_interface(hosttrace-${_TYPE}-visibility) + endif() +endforeach() + +#----------------------------------------------------------------------------------------# +# developer build flags +# +if(dl_LIBRARY) + # This instructs the linker to add all symbols, not only used ones, to the dynamic + # symbol table. This option is needed for some uses of dlopen or to allow obtaining + # backtraces from within a program. + add_flag_if_avail("-rdynamic") +endif() + +#----------------------------------------------------------------------------------------# +# sanitizer +# +set(hosttrace_SANITIZER_TYPES address memory thread leak undefined unreachable null bounds alignment) +set_property(CACHE hosttrace_SANITIZER_TYPE PROPERTY STRINGS "${hosttrace_SANITIZER_TYPES}") +add_interface_library(hosttrace-sanitizer-compile-options "Adds compiler flags for sanitizers") +add_interface_library(hosttrace-sanitizer + "Adds compiler flags to enable ${hosttrace_SANITIZER_TYPE} sanitizer (-fsanitizer=${hosttrace_SANITIZER_TYPE})") + +set(COMMON_SANITIZER_FLAGS "-fno-optimize-sibling-calls" "-fno-omit-frame-pointer" "-fno-inline-functions") +add_target_flag(hosttrace-sanitizer-compile-options ${COMMON_SANITIZER_FLAGS}) + +foreach(_TYPE ${hosttrace_SANITIZER_TYPES}) + set(_FLAG "-fsanitize=${_TYPE}") + add_interface_library(hosttrace-${_TYPE}-sanitizer + "Adds compiler flags to enable ${_TYPE} sanitizer (${_FLAG})") + add_target_flag(hosttrace-${_TYPE}-sanitizer ${_FLAG}) + target_link_libraries(hosttrace-${_TYPE}-sanitizer INTERFACE + hosttrace-sanitizer-compile-options) + set_property(TARGET hosttrace-${_TYPE}-sanitizer PROPERTY + INTERFACE_LINK_OPTIONS ${_FLAG} ${COMMON_SANITIZER_FLAGS}) +endforeach() + +unset(_FLAG) +unset(COMMON_SANITIZER_FLAGS) + +if(hosttrace_USE_SANITIZER) + foreach(_TYPE ${hosttrace_SANITIZER_TYPE}) + if(TARGET hosttrace-${_TYPE}-sanitizer) + target_link_libraries(hosttrace-sanitizer INTERFACE hosttrace-${_TYPE}-sanitizer) + else() + message(FATAL_ERROR "Error! Target 'hosttrace-${_TYPE}-sanitizer' does not exist!") + endif() + endforeach() +else() + set(hosttrace_USE_SANITIZER OFF) + inform_empty_interface(hosttrace-sanitizer "${hosttrace_SANITIZER_TYPE} sanitizer") +endif() + +if (MSVC) + # VTune is much more helpful when debug information is included in the + # generated release code. + add_flag_if_avail("/Zi") + add_flag_if_avail("/DEBUG") +endif() + +#----------------------------------------------------------------------------------------# +# user customization +# +get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + +if(NOT APPLE OR "$ENV{CONDA_PYTHON_EXE}" STREQUAL "") + add_user_flags(hosttrace-compile-options "CXX") +endif() diff --git a/cmake/Compilers.cmake b/cmake/Compilers.cmake new file mode 100644 index 0000000000..d1c228888c --- /dev/null +++ b/cmake/Compilers.cmake @@ -0,0 +1,594 @@ +# include guard +include_guard(DIRECTORY) + +########################################################################################## +# +# Compilers +# +########################################################################################## +# +# sets (cached): +# +# CMAKE_C_COMPILER_IS_ +# CMAKE_CXX_COMPILER_IS_ +# +# where TYPE is: +# - GNU +# - CLANG +# - INTEL +# - INTEL_ICC +# - INTEL_ICPC +# - PGI +# - XLC +# - HP_ACC +# - MIPS +# - MSVC +# + +include(CheckCCompilerFlag) +include(CheckCSourceCompiles) +include(CheckCSourceRuns) + +include(CheckCXXCompilerFlag) +include(CheckCXXSourceCompiles) +include(CheckCXXSourceRuns) + +include(CMakeParseArguments) + +include(MacroUtilities) + +if("${LIBNAME}" STREQUAL "") + string(TOLOWER "${PROJECT_NAME}" LIBNAME) +endif() + +add_interface_library(${LIBNAME}-compile-options + "Adds the standard set of compiler flags used by timemory") + +#----------------------------------------------------------------------------------------# +# macro converting string to list +#----------------------------------------------------------------------------------------# +macro(to_list _VAR _STR) + STRING(REPLACE " " " " ${_VAR} "${_STR}") + STRING(REPLACE " " ";" ${_VAR} "${_STR}") +endmacro(to_list _VAR _STR) + + +#----------------------------------------------------------------------------------------# +# macro converting string to list +#----------------------------------------------------------------------------------------# +macro(to_string _VAR _STR) + STRING(REPLACE ";" " " ${_VAR} "${_STR}") +endmacro(to_string _VAR _STR) + + +#----------------------------------------------------------------------------------------# +# Macro to add to string +#----------------------------------------------------------------------------------------# +macro(add _VAR _FLAG) + if(NOT "${_FLAG}" STREQUAL "") + if("${${_VAR}}" STREQUAL "") + set(${_VAR} "${_FLAG}") + else() + set(${_VAR} "${${_VAR}} ${_FLAG}") + endif() + endif() +endmacro() + + +#----------------------------------------------------------------------------------------# +# macro to remove duplicates from string +#----------------------------------------------------------------------------------------# +macro(set_no_duplicates _VAR) + if(NOT "${ARGN}" STREQUAL "") + set(${_VAR} "${ARGN}") + endif() + # remove the duplicates + if(NOT "${${_VAR}}" STREQUAL "") + # create list of flags + to_list(_VAR_LIST "${${_VAR}}") + list(REMOVE_DUPLICATES _VAR_LIST) + to_string(${_VAR} "${_VAR_LIST}") + endif(NOT "${${_VAR}}" STREQUAL "") +endmacro(set_no_duplicates _VAR) + +#----------------------------------------------------------------------------------------# +# call before running check_{c,cxx}_compiler_flag +#----------------------------------------------------------------------------------------# +macro(timemory_begin_flag_check) + if(TIMEMORY_QUIET_CONFIG) + if(NOT DEFINED CMAKE_REQUIRED_QUIET) + set(CMAKE_REQUIRED_QUIET OFF) + endif() + timemory_save_variables(FLAG_CHECK + VARIABLES CMAKE_REQUIRED_QUIET) + set(CMAKE_REQUIRED_QUIET ON) + endif() +endmacro() + +#----------------------------------------------------------------------------------------# +# call after running check_{c,cxx}_compiler_flag +#----------------------------------------------------------------------------------------# +macro(timemory_end_flag_check) + if(TIMEMORY_QUIET_CONFIG) + timemory_restore_variables(FLAG_CHECK + VARIABLES CMAKE_REQUIRED_QUIET) + endif() +endmacro() + +########################################################################################## +# +# C compiler flags +# +########################################################################################## + + +#----------------------------------------------------------------------------------------# +# add C flag to target +#----------------------------------------------------------------------------------------# +macro(ADD_TARGET_C_FLAG _TARG) + string(REPLACE "-" "_" _MAKE_TARG "${_TARG}") + list(APPEND TIMEMORY_MAKE_TARGETS ${_MAKE_TARG}) + + target_compile_options(${_TARG} INTERFACE $<$:${ARGN}>) + list(APPEND ${_MAKE_TARG}_C_FLAGS ${ARGN}) +endmacro() + + +#----------------------------------------------------------------------------------------# +# add C flag w/o check +#----------------------------------------------------------------------------------------# +macro(ADD_C_FLAG FLAG) + set(_TARG ) + set(_LTARG ) + if(NOT "${ARGN}" STREQUAL "") + set(_TARG ${ARGN}) + string(TOLOWER "_${ARGN}" _LTARG) + endif() + if(NOT "${FLAG}" STREQUAL "") + if("${_LTARG}" STREQUAL "") + list(APPEND ${PROJECT_NAME}_C_FLAGS "${FLAG}") + list(APPEND ${PROJECT_NAME}_C_COMPILE_OPTIONS "${FLAG}") + add_target_c_flag(${LIBNAME}-compile-options ${FLAG}) + else() + add_target_c_flag(${_TARG} ${FLAG}) + endif() + endif() + unset(_TARG) + unset(_LTARG) +endmacro() + + +#----------------------------------------------------------------------------------------# +# check C flag +#----------------------------------------------------------------------------------------# +macro(ADD_C_FLAG_IF_AVAIL FLAG) + set(_ENABLE ON) + if(DEFINED TIMEMORY_BUILD_C AND NOT TIMEMORY_BUILD_C) + set(_ENABLE OFF) + endif() + set(_TARG ) + set(_LTARG ) + if(NOT "${ARGN}" STREQUAL "") + set(_TARG ${ARGN}) + string(TOLOWER "_${ARGN}" _LTARG) + endif() + if(NOT "${FLAG}" STREQUAL "") + string(REGEX REPLACE "^/" "c${_LTARG}_" FLAG_NAME "${FLAG}") + string(REGEX REPLACE "^-" "c${_LTARG}_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "-" "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE " " "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "=" "_" FLAG_NAME "${FLAG_NAME}") + if(NOT TIMEMORY_BUILD_C) + set(${FLAG_NAME} ON) + else() + timemory_begin_flag_check() + check_c_compiler_flag("-Werror" c_werror) + if(c_werror) + check_c_compiler_flag("${FLAG} -Werror" ${FLAG_NAME}) + else() + check_c_compiler_flag("${FLAG}" ${FLAG_NAME}) + endif() + timemory_end_flag_check() + if(${FLAG_NAME}) + if("${_LTARG}" STREQUAL "") + list(APPEND ${PROJECT_NAME}_C_FLAGS "${FLAG}") + list(APPEND ${PROJECT_NAME}_C_COMPILE_OPTIONS "${FLAG}") + add_target_c_flag(${LIBNAME}-compile-options ${FLAG}) + else() + add_target_c_flag(${_TARG} ${FLAG}) + endif() + endif() + endif() + endif() + unset(_TARG) + unset(_LTARG) +endmacro() + + +#----------------------------------------------------------------------------------------# +# add C flag to target +#----------------------------------------------------------------------------------------# +macro(ADD_TARGET_C_FLAG_IF_AVAIL _TARG) + foreach(_FLAG ${ARGN}) + add_c_flag_if_avail(${_FLAG} ${_TARG}) + endforeach() +endmacro() + + +########################################################################################## +# +# CXX compiler flags +# +########################################################################################## + + + +#----------------------------------------------------------------------------------------# +# add CXX flag to target +#----------------------------------------------------------------------------------------# +macro(ADD_TARGET_CXX_FLAG _TARG) + string(REPLACE "-" "_" _MAKE_TARG "${_TARG}") + list(APPEND TIMEMORY_MAKE_TARGETS ${_MAKE_TARG}) + + target_compile_options(${_TARG} INTERFACE $<$:${ARGN}>) + list(APPEND ${_MAKE_TARG}_CXX_FLAGS ${ARGN}) + get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + if(CMAKE_CUDA_COMPILER_IS_NVIDIA) + target_compile_options(${_TARG} INTERFACE $<$:-Xcompiler=${ARGN}>) + list(APPEND ${_MAKE_TARG}_CUDA_FLAGS -Xcompiler=${ARGN}) + elseif(CMAKE_CUDA_COMPILER_IS_CLANG) + target_compile_options(${_TARG} INTERFACE $<$:${ARGN}>) + list(APPEND ${_MAKE_TARG}_CUDA_FLAGS ${ARGN}) + endif() +endmacro() + + +#----------------------------------------------------------------------------------------# +# add CXX flag w/o check +#----------------------------------------------------------------------------------------# +macro(ADD_CXX_FLAG FLAG) + set(_TARG ) + set(_LTARG ) + if(NOT "${ARGN}" STREQUAL "") + set(_TARG ${ARGN}) + string(TOLOWER "_${ARGN}" _LTARG) + endif() + if(NOT "${FLAG}" STREQUAL "") + if("${_LTARG}" STREQUAL "") + list(APPEND ${PROJECT_NAME}_CXX_FLAGS "${FLAG}") + list(APPEND ${PROJECT_NAME}_CXX_COMPILE_OPTIONS "${FLAG}") + add_target_cxx_flag(${LIBNAME}-compile-options ${FLAG}) + else() + add_target_cxx_flag(${_TARG} ${FLAG}) + endif() + endif() + unset(_TARG) + unset(_LTARG) +endmacro() + + +#----------------------------------------------------------------------------------------# +# check CXX flag +#----------------------------------------------------------------------------------------# +macro(ADD_CXX_FLAG_IF_AVAIL FLAG) + set(_TARG ) + set(_LTARG ) + if(NOT "${ARGN}" STREQUAL "") + set(_TARG ${ARGN}) + string(TOLOWER "_${ARGN}" _LTARG) + endif() + if(NOT "${FLAG}" STREQUAL "") + string(REGEX REPLACE "^/" "cxx${_LTARG}_" FLAG_NAME "${FLAG}") + string(REGEX REPLACE "^-" "cxx${_LTARG}_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "-" "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE " " "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "=" "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "/" "_" FLAG_NAME "${FLAG_NAME}") + timemory_begin_flag_check() + check_cxx_compiler_flag("-Werror" cxx_werror) + if(cxx_werror) + check_cxx_compiler_flag("${FLAG} -Werror" ${FLAG_NAME}) + else() + check_cxx_compiler_flag("${FLAG}" ${FLAG_NAME}) + endif() + timemory_end_flag_check() + if(${FLAG_NAME}) + if("${_LTARG}" STREQUAL "") + list(APPEND ${PROJECT_NAME}_CXX_FLAGS "${FLAG}") + list(APPEND ${PROJECT_NAME}_CXX_COMPILE_OPTIONS "${FLAG}") + add_target_cxx_flag(${LIBNAME}-compile-options ${FLAG}) + else() + add_target_cxx_flag(${_TARG} ${FLAG}) + endif() + endif() + endif() + unset(_TARG) + unset(_LTARG) +endmacro() + + +#----------------------------------------------------------------------------------------# +# add CXX flag to target +#----------------------------------------------------------------------------------------# +macro(ADD_TARGET_CXX_FLAG_IF_AVAIL _TARG) + foreach(_FLAG ${ARGN}) + add_cxx_flag_if_avail(${_FLAG} ${_TARG}) + endforeach() +endmacro() + + +########################################################################################## +# +# Common +# +########################################################################################## + + +#----------------------------------------------------------------------------------------# +# check C and CXX flag to compile-options w/o checking +#----------------------------------------------------------------------------------------# +macro(ADD_FLAG) + foreach(_ARG ${ARGN}) + ADD_C_FLAG("${_ARG}") + ADD_CXX_FLAG("${_ARG}") + endforeach() +endmacro() + + +#----------------------------------------------------------------------------------------# +# add C and CXX flag w/o checking +#----------------------------------------------------------------------------------------# +macro(ADD_TARGET_FLAG _TARG) + foreach(_ARG ${ARGN}) + ADD_TARGET_C_FLAG(${_TARG} ${_ARG}) + ADD_TARGET_CXX_FLAG(${_TARG} ${_ARG}) + endforeach() +endmacro() + + +#----------------------------------------------------------------------------------------# +# check C and CXX flag +#----------------------------------------------------------------------------------------# +macro(ADD_FLAG_IF_AVAIL) + foreach(_ARG ${ARGN}) + ADD_C_FLAG_IF_AVAIL("${_ARG}") + ADD_CXX_FLAG_IF_AVAIL("${_ARG}") + endforeach() +endmacro() + + +#----------------------------------------------------------------------------------------# +# check C and CXX flag +#----------------------------------------------------------------------------------------# +macro(ADD_TARGET_FLAG_IF_AVAIL _TARG) + foreach(_ARG ${ARGN}) + ADD_TARGET_C_FLAG_IF_AVAIL(${_TARG} ${_ARG}) + ADD_TARGET_CXX_FLAG_IF_AVAIL(${_TARG} ${_ARG}) + endforeach() +endmacro() + + +#----------------------------------------------------------------------------------------# +# check flag +#----------------------------------------------------------------------------------------# +function(TIMEMORY_TARGET_FLAG _TARG_TARGET) + cmake_parse_arguments(_TARG "IF_AVAIL" "MODE" "FLAGS;LANGUAGES" ${ARGN}) + + if(NOT _TARG_MODE) + set(_TARG_MODE INTERFACE) + endif() + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + + if(NOT _TARG_LANGUAGES) + get_property(_TARG_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + endif() + + string(TOLOWER "_${_TARG_TARGET}" _LTARG) + + foreach(_FLAG ${_TARG_FLAGS}) + foreach(_LANG ${_TARG_LANGUAGES}) + if(NOT _TARG_IF_AVAIL) + target_compile_options(${_TARG_TARGET} ${_TARG_MODE} $<$:${_FLAG}>) + continue() + endif() + + if("${_LANG}" STREQUAL "C") + string(REGEX REPLACE "^/" "c${_LTARG}_" FLAG_NAME "${_FLAG}") + string(REGEX REPLACE "^-" "c${_LTARG}_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "-" "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE " " "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "=" "_" FLAG_NAME "${FLAG_NAME}") + timemory_begin_flag_check() + check_c_compiler_flag("-Werror" c_werror) + if(c_werror) + check_c_compiler_flag("${FLAG} -Werror" ${FLAG_NAME}) + else() + check_c_compiler_flag("${FLAG}" ${FLAG_NAME}) + endif() + timemory_end_flag_check() + if(${FLAG_NAME}) + target_compile_options(${_TARG_TARGET} ${_TARG_MODE} + $<$:${_FLAG}>) + endif() + elseif("${_LANG}" STREQUAL "CXX") + string(REGEX REPLACE "^/" "cxx${_LTARG}_" FLAG_NAME "${_FLAG}") + string(REGEX REPLACE "^-" "cxx${_LTARG}_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "-" "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE " " "_" FLAG_NAME "${FLAG_NAME}") + string(REPLACE "=" "_" FLAG_NAME "${FLAG_NAME}") + timemory_begin_flag_check() + check_cxx_compiler_flag("-Werror" cxx_werror) + if(cxx_werror) + check_cxx_compiler_flag("${FLAG} -Werror" ${FLAG_NAME}) + else() + check_cxx_compiler_flag("${FLAG}" ${FLAG_NAME}) + endif() + timemory_end_flag_check() + if(${FLAG_NAME}) + target_compile_options(${_TARG_TARGET} ${_TARG_MODE} + $<$:${_FLAG}>) + if(CMAKE_CUDA_COMPILER_IS_NVIDIA) + target_compile_options(${_TARG_TARGET} ${_TARG_MODE} + $<$:-Xcompiler=${_FLAG}>) + elseif(CMAKE_CUDA_COMPILER_IS_CLANG) + target_compile_options(${_TARG_TARGET} ${_TARG_MODE} + $<$:${_FLAG}>) + endif() + endif() + endif() + endforeach() + endforeach() +endfunction() + + +#----------------------------------------------------------------------------------------# +# add CUDA flag to target +#----------------------------------------------------------------------------------------# +macro(ADD_TARGET_CUDA_FLAG _TARG) + string(REPLACE "-" "_" _MAKE_TARG "${_TARG}") + list(APPEND TIMEMORY_MAKE_TARGETS ${_MAKE_TARG}) + + target_compile_options(${_TARG} INTERFACE $<$:${ARGN}>) + list(APPEND ${_MAKE_TARG}_CUDA_FLAGS ${ARGN}) +endmacro() + +#----------------------------------------------------------------------------------------# +# add to any language +#----------------------------------------------------------------------------------------# +function(ADD_USER_FLAGS _TARGET _LANGUAGE) + + set(_FLAGS ${${_LANGUAGE}FLAGS} $ENV{${_LANGUAGE}FLAGS} + ${${_LANGUAGE}_FLAGS} $ENV{${_LANGUAGE}_FLAGS}) + + string(REPLACE " " ";" _FLAGS "${_FLAGS}") + + set(${PROJECT_NAME}_${_LANGUAGE}_FLAGS + ${${PROJECT_NAME}_${_LANGUAGE}_FLAGS} ${_FLAGS} PARENT_SCOPE) + + set(${PROJECT_NAME}_${_LANGUAGE}_COMPILE_OPTIONS + ${${PROJECT_NAME}_${_LANGUAGE}_COMPILE_OPTIONS} ${_FLAGS} PARENT_SCOPE) + + target_compile_options(${_TARGET} INTERFACE + $<$:${_FLAGS}>) +endfunction() + +#----------------------------------------------------------------------------------------# +# add compiler definition +#----------------------------------------------------------------------------------------# +function(TIMEMORY_TARGET_COMPILE_DEFINITIONS _TARG _VIS) + foreach(_DEF ${ARGN}) + target_compile_definitions(${_TARG} ${_VIS} + $<$:${_DEF}>) + if(CMAKE_CUDA_COMPILER_IS_NVIDIA) + target_compile_definitions(${_TARG} ${_VIS} + $<$:${_DEF}>) + elseif(CMAKE_CUDA_COMPILER_IS_CLANG) + target_compile_definitions(${_TARG} ${_VIS} + $<$:${_DEF}>) + endif() + endforeach() +endfunction() + +#----------------------------------------------------------------------------------------# +# determine compiler types for each language +#----------------------------------------------------------------------------------------# +get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach(LANG C CXX CUDA) + + if(NOT DEFINED CMAKE_${LANG}_COMPILER) + set(CMAKE_${LANG}_COMPILER "") + endif() + + if(NOT DEFINED CMAKE_${LANG}_COMPILER_ID) + set(CMAKE_${LANG}_COMPILER_ID "") + endif() + + function(SET_COMPILER_VAR VAR _BOOL) + set(CMAKE_${LANG}_COMPILER_IS_${VAR} ${_BOOL} CACHE INTERNAL + "CMake ${LANG} compiler identification (${VAR})" FORCE) + mark_as_advanced(CMAKE_${LANG}_COMPILER_IS_${VAR}) + endfunction() + + if(("${LANG}" STREQUAL "C" AND CMAKE_COMPILER_IS_GNUCC) + OR + ("${LANG}" STREQUAL "CXX" AND CMAKE_COMPILER_IS_GNUCXX)) + + # GNU compiler + SET_COMPILER_VAR( GNU 1) + + elseif(CMAKE_${LANG}_COMPILER MATCHES "icc.*") + + # Intel icc compiler + SET_COMPILER_VAR( INTEL 1) + SET_COMPILER_VAR( INTEL_ICC 1) + + elseif(CMAKE_${LANG}_COMPILER MATCHES "icpc.*") + + # Intel icpc compiler + SET_COMPILER_VAR( INTEL 1) + SET_COMPILER_VAR( INTEL_ICPC 1) + + elseif(CMAKE_${LANG}_COMPILER_ID MATCHES "AppleClang") + + # Clang/LLVM compiler + SET_COMPILER_VAR( CLANG 1) + SET_COMPILER_VAR( APPLE_CLANG 1) + + elseif(CMAKE_${LANG}_COMPILER_ID MATCHES "Clang") + + # Clang/LLVM compiler + SET_COMPILER_VAR( CLANG 1) + + elseif(CMAKE_${LANG}_COMPILER_ID MATCHES "PGI") + + # PGI compiler + SET_COMPILER_VAR( PGI 1) + + elseif(CMAKE_${LANG}_COMPILER MATCHES "xlC" AND UNIX) + + # IBM xlC compiler + SET_COMPILER_VAR( XLC 1) + + elseif(CMAKE_${LANG}_COMPILER MATCHES "aCC" AND UNIX) + + # HP aC++ compiler + SET_COMPILER_VAR( HP_ACC 1) + + elseif(CMAKE_${LANG}_COMPILER MATCHES "CC" AND + CMAKE_SYSTEM_NAME MATCHES "IRIX" AND UNIX) + + # IRIX MIPSpro CC Compiler + SET_COMPILER_VAR( MIPS 1) + + elseif(CMAKE_${LANG}_COMPILER_ID MATCHES "Intel") + + SET_COMPILER_VAR( INTEL 1) + + set(CTYPE ICC) + if("${LANG}" STREQUAL "CXX") + set(CTYPE ICPC) + endif() + + SET_COMPILER_VAR( INTEL_${CTYPE} 1) + + elseif(CMAKE_${LANG}_COMPILER MATCHES "MSVC") + + # Windows Visual Studio compiler + SET_COMPILER_VAR( MSVC 1) + + elseif(CMAKE_${LANG}_COMPILER_ID MATCHES "NVIDIA") + + # NVCC + SET_COMPILER_VAR( NVIDIA 1) + + endif() + + # set other to no + foreach(TYPE GNU INTEL INTEL_ICC INTEL_ICPC APPLE_CLANG CLANG PGI XLC HP_ACC MIPS MSVC NVIDIA) + if(NOT DEFINED CMAKE_${LANG}_COMPILER_IS_${TYPE}) + SET_COMPILER_VAR(${TYPE} 0) + endif() + endforeach() + +endforeach() diff --git a/cmake/MacroUtilities.cmake b/cmake/MacroUtilities.cmake new file mode 100644 index 0000000000..2b60617c57 --- /dev/null +++ b/cmake/MacroUtilities.cmake @@ -0,0 +1,1526 @@ +# include guard +include_guard(DIRECTORY) + +# MacroUtilities - useful macros and functions for generic tasks +# + +cmake_policy(PUSH) +cmake_policy(SET CMP0054 NEW) +cmake_policy(SET CMP0057 NEW) + +include(CMakeDependentOption) +include(CMakeParseArguments) + +# Make this file generic by not explicitly defining the name of the project +string(TOUPPER "${PROJECT_NAME}" PROJECT_NAME_UC) +string(TOLOWER "${PROJECT_NAME}" PROJECT_NAME_LC) + +unset(${PROJECT_NAME_UC}_COMPILED_LIBRARIES CACHE) +unset(${PROJECT_NAME_UC}_INTERFACE_LIBRARIES CACHE) + +#----------------------------------------------------------------------- +# message which handles HOSTTRACE_QUIET_CONFIG settings +#----------------------------------------------------------------------- +# +FUNCTION(HOSTTRACE_MESSAGE TYPE) + if(NOT HOSTTRACE_QUIET_CONFIG) + message(${TYPE} "[hosttrace] ${ARGN}") + endif() +ENDFUNCTION() + +#----------------------------------------------------------------------- +# Save a set of variables with the given prefix +#----------------------------------------------------------------------- +MACRO(HOSTTRACE_SAVE_VARIABLES _PREFIX) + # parse args + cmake_parse_arguments(SAVE + "" # options + "CONDITION" # single value args + "VARIABLES" # multiple value args + ${ARGN}) + if(DEFINED SAVE_CONDITION AND NOT "${SAVE_CONDITION}" STREQUAL "") + if(${SAVE_CONDITION}) + foreach(_VAR ${SAVE_VARIABLES}) + if(DEFINED ${_VAR}) + set(${_PREFIX}_${_VAR} "${${_VAR}}") + else() + message(AUTHOR_WARNING "${_VAR} is not defined") + endif() + endforeach() + endif() + else() + foreach(_VAR ${SAVE_VARIABLES}) + if(DEFINED ${_VAR}) + set(${_PREFIX}_${_VAR} "${${_VAR}}") + else() + message(AUTHOR_WARNING "${_VAR} is not defined") + endif() + endforeach() + endif() + unset(SAVE_CONDITION) + unset(SAVE_VARIABLES) +ENDMACRO() + +#----------------------------------------------------------------------- +# Restore a set of variables with the given prefix +#----------------------------------------------------------------------- +MACRO(HOSTTRACE_RESTORE_VARIABLES _PREFIX) + # parse args + cmake_parse_arguments(RESTORE + "" # options + "CONDITION" # single value args + "VARIABLES" # multiple value args + ${ARGN}) + if(DEFINED RESTORE_CONDITION AND NOT "${RESTORE_CONDITION}" STREQUAL "") + if(${RESTORE_CONDITION}) + foreach(_VAR ${RESTORE_VARIABLES}) + if(DEFINED ${_PREFIX}_${_VAR}) + set(${_VAR} ${${_PREFIX}_${_VAR}}) + unset(${_PREFIX}_${_VAR}) + else() + message(AUTHOR_WARNING "${_PREFIX}_${_VAR} is not defined") + endif() + endforeach() + endif() + else() + foreach(_VAR ${RESTORE_VARIABLES}) + if(DEFINED ${_PREFIX}_${_VAR}) + set(${_VAR} ${${_PREFIX}_${_VAR}}) + unset(${_PREFIX}_${_VAR}) + else() + message(AUTHOR_WARNING "${_PREFIX}_${_VAR} is not defined") + endif() + endforeach() + endif() + unset(RESTORE_CONDITION) + unset(RESTORE_VARIABLES) +ENDMACRO() + +#----------------------------------------------------------------------- +# CACHED LIST +#----------------------------------------------------------------------- +# macro set_ifnot( ) +# If variable var is not set, set its value to that provided +# +MACRO(CACHE_LIST _OP _LIST) + set(_TMP_CACHE_LIST ${${_LIST}}) + # apply operation on list + list(${_OP} _TMP_CACHE_LIST ${ARGN}) + # replace list + set(${_LIST} "${_TMP_CACHE_LIST}" CACHE INTERNAL "" FORCE) +ENDMACRO() + + +#----------------------------------------------------------------------- +# CMAKE EXTENSIONS +#----------------------------------------------------------------------- +# macro set_ifnot( ) +# If variable var is not set, set its value to that provided +# +MACRO(SET_IFNOT _var _value) + if(NOT DEFINED ${_var}) + set(${_var} ${_value} ${ARGN}) + endif() +ENDMACRO() + + +#----------------------------------------------------------------------- +# macro safe_remove_duplicates() +# ensures remove_duplicates is only called if list has values +# +MACRO(SAFE_REMOVE_DUPLICATES _list) + if(NOT "${${_list}}" STREQUAL "") + list(REMOVE_DUPLICATES ${_list}) + endif(NOT "${${_list}}" STREQUAL "") +ENDMACRO() + + +#----------------------------------------------------------------------- +# function - capitalize - make a string capitalized (first letter is capital) +# usage: +# capitalize("SHARED" CShared) +# message(STATUS "-- CShared is \"${CShared}\"") +# $ -- CShared is "Shared" +FUNCTION(CAPITALIZE str var) + # make string lower + string(TOLOWER "${str}" str) + string(SUBSTRING "${str}" 0 1 _first) + string(TOUPPER "${_first}" _first) + string(SUBSTRING "${str}" 1 -1 _remainder) + string(CONCAT str "${_first}" "${_remainder}") + set(${var} "${str}" PARENT_SCOPE) +ENDFUNCTION() + + +#----------------------------------------------------------------------- +# macro set_ifnot_match( ) +# If variable var is not set, set its value to that provided +# +MACRO(SET_IFNOT_MATCH VAR APPEND) + if(NOT "${APPEND}" STREQUAL "") + STRING(REGEX MATCH "${APPEND}" _MATCH "${${VAR}}") + if(NOT "${_MATCH}" STREQUAL "") + SET(${VAR} "${${VAR}} ${APPEND}") + endif() + endif() +ENDMACRO() + + +#----------------------------------------------------------------------- +# macro cache_ifnot( ) +# If variable var is not set, set its value to that provided and cache it +# +MACRO(CACHE_IFNOT _var _value _type _doc) + if(NOT ${_var} OR NOT ${CACHE_VARIABLES} MATCHES ${_var}) + set(${_var} ${_value} CACHE ${_type} "${_doc}") + endif() +ENDMACRO() + + +#----------------------------------------------------------------------- +# function add_enabled_interface() +# Mark an interface library as enabled +# +FUNCTION(ADD_ENABLED_INTERFACE _var) + set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_ENABLED_INTERFACES ${_var}) +ENDFUNCTION() + + +#----------------------------------------------------------------------- +# function add_disabled_interface() +# Mark an interface as disabled +# +FUNCTION(ADD_DISABLED_INTERFACE _var) + get_property(_DISABLED GLOBAL PROPERTY ${PROJECT_NAME}_DISABLED_INTERFACES) + if(NOT ${_var} IN_LIST _DISABLED) + set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_DISABLED_INTERFACES ${_var}) + endif() +ENDFUNCTION() + + +#------------------------------------------------------------------------------# +# macro for creating a library target +# +FUNCTION(CREATE_EXECUTABLE) + # for include dirs, compile flags, definitions, etc. --> use INTERFACE libs + # and add them to "LINK_LIBRARIES" + # list of arguments taking multiple values + set(multival_args + HEADERS SOURCES PROPERTIES LINK_LIBRARIES INSTALL_DESTINATION) + + # parse args + cmake_parse_arguments(EXE + "INSTALL;EXCLUDE_FROM_ALL" # options + "TARGET_NAME;" # single value args + "${multival_args}" # multiple value args + ${ARGN}) + + set(_EXCLUDE) + if(EXE_EXCLUDE_FROM_ALL OR HOSTTRACE_BUILD_EXCLUDE_FROM_ALL) + set(_EXCLUDE EXCLUDE_FROM_ALL) + endif() + # create library + add_executable(${EXE_TARGET_NAME} ${_EXCLUDE} ${EXE_SOURCES} ${EXE_HEADERS}) + + # link library + target_link_libraries(${EXE_TARGET_NAME} ${EXE_LINK_LIBRARIES}) + + # target properties + if(NOT "${EXE_PROPERTIES}" STREQUAL "") + set_target_properties(${EXE_TARGET_NAME} PROPERTIES ${EXE_PROPERTIES}) + endif() + + if(EXE_INSTALL AND NOT EXE_INSTALL_DESTINATION) + set(EXE_INSTALL_DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + + # Install the exe + if(EXE_INSTALL_DESTINATION) + install( + TARGETS ${EXE_TARGET_NAME} + DESTINATION ${EXE_INSTALL_DESTINATION} + OPTIONAL) + endif() +ENDFUNCTION() + + +#------------------------------------------------------------------------------# +# function add_hosttrace_test_target() +# +# Creates a target which runs ctest but depends on all the tests being built. +# +FUNCTION(ADD_HOSTTRACE_TEST_TARGET) + if(NOT TARGET hosttrace-test) + add_custom_target(hosttrace-test + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR} --target test + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMENT "Running tests...") + endif() +ENDFUNCTION() + + +#------------------------------------------------------------------------------# +# macro add_googletest() +# +# Adds a unit test and links against googletest. Additional arguments are linked +# against the test. +# +FUNCTION(ADD_HOSTTRACE_GOOGLE_TEST TEST_NAME) + if(NOT HOSTTRACE_BUILD_GOOGLE_TEST) + return() + endif() + set(_OPTS ) + if(NOT HOSTTRACE_BUILD_TESTING) + set(_OPTS EXCLUDE_FROM_ALL) + endif() + add_hosttrace_test_target() + include(GoogleTest) + # list of arguments taking multiple values + set(multival_args SOURCES DEPENDS PROPERTIES DEFINITIONS LINK_LIBRARIES + COMMAND OPTIONS ENVIRONMENT) + # parse args + cmake_parse_arguments(TEST "DISCOVER_TESTS;ADD_TESTS;MPI" "NPROCS;TIMEOUT;TARGET" + "${multival_args}" ${ARGN}) + + if(NOT TARGET google-test-debug-options) + add_library(google-test-debug-options INTERFACE) + target_compile_definitions(google-test-debug-options INTERFACE + $<$:DEBUG> HOSTTRACE_TESTING) + endif() + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR HOSTTRACE_USE_COVERAGE) + # create a pre-processor which relaxes pass/fail when converage is enabled + target_compile_definitions(google-test-debug-options INTERFACE + HOSTTRACE_RELAXED_TESTING) + endif() + list(APPEND TEST_LINK_LIBRARIES google-test-debug-options) + + if(NOT TEST_TARGET) + set(TEST_TARGET ${TEST_NAME}) + endif() + + if(TEST_SOURCES AND NOT TARGET ${TEST_TARGET}) + CREATE_EXECUTABLE(${_OPTS} + TARGET_NAME ${TEST_TARGET} + OUTPUT_NAME ${TEST_TARGET} + SOURCES ${TEST_SOURCES} + LINK_LIBRARIES hosttrace-google-test ${TEST_LINK_LIBRARIES} + PROPERTIES "${TEST_PROPERTIES}") + + target_compile_definitions(${TEST_TARGET} PUBLIC ${TEST_DEFINITIONS}) + + # always add as a dependency if target is built + add_dependencies(hosttrace-test ${TEST_TARGET}) + + if(WIN32) + set_target_properties(${TEST_TARGET} + PROPERTIES + FOLDER tests + RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/outputs/runtime + LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/outputs/runtime + ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/outputs/archive + PDB_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/outputs/runtime) + endif() + endif() + + set(TEST_LAUNCHER) + if(HOSTTRACE_USE_MPI AND TEST_MPI AND MPIEXEC_EXECUTABLE) + if(NOT TEST_NPROCS) + set(TEST_NPROCS 2) + endif() + set(TEST_LAUNCHER ${MPIEXEC_EXECUTABLE} -n ${TEST_NPROCS}) + endif() + + if("${TEST_COMMAND}" STREQUAL "") + set(TEST_COMMAND ${TEST_LAUNCHER} $) + elseif(TEST_LAUNCHER) + set(TEST_COMMAND ${TEST_LAUNCHER} ${TEST_COMMAND}) + endif() + + if(NOT DEFINED CTEST_TEST_TIMEOUT OR "${CTEST_TEST_TIMEOUT}" STREQUAL "") + set(CTEST_TEST_TIMEOUT 180) + endif() + + if(NOT TEST_TIMEOUT) + set(TEST_TIMEOUT ${CTEST_TEST_TIMEOUT}) + endif() + + set(WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}) + if(WIN32) + set(WORKING_DIR $) + endif() + + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR HOSTTRACE_USE_COVERAGE) + if(TEST_ENVIRONMENT) + set(TEST_ENVIRONMENT "HOSTTRACE_DEBUG=ON;HOSTTRACE_VERBOSE=6;${TEST_ENVIRONMENT}") + else() + set(TEST_ENVIRONMENT "HOSTTRACE_DEBUG=ON;HOSTTRACE_VERBOSE=6") + endif() + endif() + + if(TEST_DISCOVER_TESTS) + GTEST_DISCOVER_TESTS(${TEST_TARGET} + TEST_LIST ${TEST_NAME}_TESTS + ${TEST_OPTIONS} + DISCOVERY_TIMEOUT 15 + WORKING_DIRECTORY ${WORKING_DIR}) + SET_TESTS_PROPERTIES(${${TEST_NAME}_TESTS} PROPERTIES + ENVIRONMENT "${TEST_ENVIRONMENT}" + TIMEOUT ${TEST_TIMEOUT}) + elseif(TEST_ADD_TESTS) + GTEST_ADD_TESTS(TARGET ${TEST_TARGET} + TEST_LIST ${TEST_NAME}_TESTS + ${TEST_OPTIONS} + WORKING_DIRECTORY ${WORKING_DIR}) + SET_TESTS_PROPERTIES(${${TEST_NAME}_TESTS} PROPERTIES + ENVIRONMENT "${TEST_ENVIRONMENT}" + TIMEOUT ${TEST_TIMEOUT}) + else() + ADD_TEST( + NAME ${TEST_NAME} + COMMAND ${TEST_COMMAND} + WORKING_DIRECTORY ${WORKING_DIR} + ${TEST_OPTIONS}) + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES + ENVIRONMENT "${TEST_ENVIRONMENT}" + TIMEOUT ${TEST_TIMEOUT}) + endif() + + if(TEST_DEPENDS) + set_property(TEST ${TEST_NAME} APPEND PROPERTY DEPENDS ${TEST_DEPENDS}) + endif() + +ENDFUNCTION() + +#----------------------------------------------------------------------------------------# +# macro CHECKOUT_GIT_SUBMODULE() +# +# Run "git submodule update" if a file in a submodule does not exist +# +# ARGS: +# RECURSIVE (option) -- add "--recursive" flag +# RELATIVE_PATH (one value) -- typically the relative path to submodule +# from PROJECT_SOURCE_DIR +# WORKING_DIRECTORY (one value) -- (default: PROJECT_SOURCE_DIR) +# TEST_FILE (one value) -- file to check for (default: CMakeLists.txt) +# ADDITIONAL_CMDS (many value) -- any addition commands to pass +# +FUNCTION(CHECKOUT_GIT_SUBMODULE) + # parse args + cmake_parse_arguments( + CHECKOUT + "RECURSIVE" + "RELATIVE_PATH;WORKING_DIRECTORY;TEST_FILE;REPO_URL;REPO_BRANCH" + "ADDITIONAL_CMDS" + ${ARGN}) + + if(NOT CHECKOUT_WORKING_DIRECTORY) + set(CHECKOUT_WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + endif() + + if(NOT CHECKOUT_TEST_FILE) + set(CHECKOUT_TEST_FILE "CMakeLists.txt") + endif() + + # default assumption + if(NOT CHECKOUT_REPO_BRANCH) + set(CHECKOUT_REPO_BRANCH "master") + endif() + + find_package(Git) + set(_DIR "${CHECKOUT_WORKING_DIRECTORY}/${CHECKOUT_RELATIVE_PATH}") + # ensure the (possibly empty) directory exists + if(NOT EXISTS "${_DIR}") + if(NOT CHECKOUT_REPO_URL) + message(FATAL_ERROR "submodule directory does not exist") + endif() + endif() + + # if this file exists --> project has been checked out + # if not exists --> not been checked out + set(_TEST_FILE "${_DIR}/${CHECKOUT_TEST_FILE}") + # assuming a .gitmodules file exists + set(_SUBMODULE "${PROJECT_SOURCE_DIR}/.gitmodules") + + set(_TEST_FILE_EXISTS OFF) + if(EXISTS "${_TEST_FILE}" AND NOT IS_DIRECTORY "${_TEST_FILE}") + set(_TEST_FILE_EXISTS ON) + endif() + + if(_TEST_FILE_EXISTS) + return() + endif() + + find_package(Git REQUIRED) + + set(_SUBMODULE_EXISTS OFF) + if(EXISTS "${_SUBMODULE}" AND NOT IS_DIRECTORY "${_SUBMODULE}") + set(_SUBMODULE_EXISTS ON) + endif() + + set(_HAS_REPO_URL OFF) + if(NOT "${CHECKOUT_REPO_URL}" STREQUAL "") + set(_HAS_REPO_URL ON) + endif() + + # if the module has not been checked out + if(NOT _TEST_FILE_EXISTS AND _SUBMODULE_EXISTS) + # perform the checkout + execute_process( + COMMAND + ${GIT_EXECUTABLE} submodule update --init ${_RECURSE} + ${CHECKOUT_ADDITIONAL_CMDS} ${CHECKOUT_RELATIVE_PATH} + WORKING_DIRECTORY + ${CHECKOUT_WORKING_DIRECTORY} + RESULT_VARIABLE RET) + + # check the return code + if(RET GREATER 0) + set(_CMD "${GIT_EXECUTABLE} submodule update --init ${_RECURSE} + ${CHECKOUT_ADDITIONAL_CMDS} ${CHECKOUT_RELATIVE_PATH}") + message(STATUS "function(CHECKOUT_GIT_SUBMODULE) failed.") + message(FATAL_ERROR "Command: \"${_CMD}\"") + else() + set(_TEST_FILE_EXISTS ON) + endif() + endif() + + if(NOT _TEST_FILE_EXISTS AND _HAS_REPO_URL) + message(STATUS "Checking out '${CHECKOUT_REPO_URL}' @ '${CHECKOUT_REPO_BRANCH}'...") + + # remove the existing directory + if(EXISTS "${_DIR}") + execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${_DIR}) + endif() + + # perform the checkout + execute_process( + COMMAND + ${GIT_EXECUTABLE} clone -b ${CHECKOUT_REPO_BRANCH} + ${CHECKOUT_ADDITIONAL_CMDS} + ${CHECKOUT_REPO_URL} ${CHECKOUT_RELATIVE_PATH} + WORKING_DIRECTORY + ${CHECKOUT_WORKING_DIRECTORY} + RESULT_VARIABLE RET) + + # perform the submodule update + if(CHECKOUT_RECURSIVE AND EXISTS "${_DIR}" AND IS_DIRECTORY "${_DIR}") + execute_process( + COMMAND + ${GIT_EXECUTABLE} submodule update --init ${_RECURSE} + WORKING_DIRECTORY + ${_DIR} + RESULT_VARIABLE RET) + endif() + + # check the return code + if(RET GREATER 0) + set(_CMD "${GIT_EXECUTABLE} clone -b ${CHECKOUT_REPO_BRANCH} + ${CHECKOUT_ADDITIONAL_CMDS} ${CHECKOUT_REPO_URL} ${CHECKOUT_RELATIVE_PATH}") + message(STATUS "function(CHECKOUT_GIT_SUBMODULE) failed.") + message(FATAL_ERROR "Command: \"${_CMD}\"") + else() + set(_TEST_FILE_EXISTS ON) + endif() + endif() + + if(NOT EXISTS "${_TEST_FILE}" OR NOT _TEST_FILE_EXISTS) + message(FATAL_ERROR "Error checking out submodule: '${CHECKOUT_RELATIVE_PATH}' to '${_DIR}'") + endif() + +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# try to find a package quietly +# +FUNCTION(HOSTTRACE_TEST_FIND_PACKAGE PACKAGE_NAME OUTPUT_VAR) + cmake_parse_arguments( + PACKAGE "" "" "UNSET" ${ARGN}) + find_package(${PACKAGE_NAME} QUIET ${PACKAGE_UNPARSED_ARGUMENTS}) + if(NOT ${PACKAGE_NAME}_FOUND) + set(${OUTPUT_VAR} OFF PARENT_SCOPE) + else() + set(${OUTPUT_VAR} ON PARENT_SCOPE) + endif() + foreach(_ARG ${PACKAGE_UNSET} FIND_PACKAGE_MESSAGE_DETAILS_${PACKAGE_NAME}) + unset(${_ARG} CACHE) + endforeach() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# macro to add an interface lib +# +MACRO(ADD_INTERFACE_LIBRARY _TARGET) + add_library(${_TARGET} INTERFACE) + add_library(${PROJECT_NAME}::${_TARGET} ALIAS ${_TARGET}) + cache_list(APPEND ${PROJECT_NAME_UC}_INTERFACE_LIBRARIES ${_TARGET}) + install( + TARGETS ${_TARGET} + DESTINATION ${CMAKE_INSTALL_LIBDIR} + EXPORT ${PROJECT_NAME}-library-depends + OPTIONAL) + add_enabled_interface(${_TARGET}) + if(NOT "${ARGN}" STREQUAL "") + set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_INTERFACE_DOC + "${PROJECT_NAME}::${_TARGET}` | ${ARGN} |") + endif() +ENDMACRO() + + +#----------------------------------------------------------------------------------------# +# +# handle empty interface +# +#----------------------------------------------------------------------------------------# + +FUNCTION(INFORM_EMPTY_INTERFACE _TARGET _PACKAGE) + if(NOT TARGET ${_TARGET}) + hosttrace_message(AUTHOR_WARNING "A non-existant target was passed to INFORM_EMPTY_INTERFACE: ${_TARGET}") + endif() + if(NOT ${_TARGET} IN_LIST HOSTTRACE_EMPTY_INTERFACE_LIBRARIES) + hosttrace_message(STATUS + "[interface] ${_PACKAGE} not found/enabled. '${_TARGET}' interface will not provide ${_PACKAGE}...") + set(HOSTTRACE_EMPTY_INTERFACE_LIBRARIES ${HOSTTRACE_EMPTY_INTERFACE_LIBRARIES} ${_TARGET} PARENT_SCOPE) + endif() + add_disabled_interface(${_TARGET}) +endfunction() + +function(ADD_RPATH) + set(_DIRS) + foreach(_ARG ${ARGN}) + if(EXISTS "${_ARG}" AND IS_DIRECTORY "${_ARG}") + list(APPEND _DIRS "${_ARG}") + endif() + get_filename_component(_DIR "${_ARG}" DIRECTORY) + if(EXISTS "${_DIR}" AND IS_DIRECTORY "${_DIR}") + list(APPEND _DIRS "${_DIR}") + endif() + endforeach() + if(_DIRS) + list(REMOVE_DUPLICATES _DIRS) + string(REPLACE ";" ":" _RPATH "${_DIRS}") + # message(STATUS "\n\tRPATH additions: ${_RPATH}\n") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:${_RPATH}" PARENT_SCOPE) + endif() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# macro to build a library of type: shared, static, object +# +FUNCTION(BUILD_LIBRARY) + + # options + set(_options PIC + NO_CACHE_LIST + EXCLUDE_FROM_ALL) + # single-value + set(_onevalue TYPE + OUTPUT_NAME + TARGET_NAME + OUTPUT_DIR + LANGUAGE + LINKER_LANGUAGE) + # multi-value + set(_multival SOURCES + LINK_LIBRARIES + COMPILE_DEFINITIONS + INCLUDE_DIRECTORIES + C_COMPILE_OPTIONS + CXX_COMPILE_OPTIONS + CUDA_COMPILE_OPTIONS + LINK_OPTIONS + EXTRA_PROPERTIES) + + cmake_parse_arguments( + LIBRARY "${_options}" "${_onevalue}" "${_multival}" ${ARGN}) + + if("${LIBRARY_LANGUAGE}" STREQUAL "") + set(LIBRARY_LANGUAGE CXX) + endif() + + if("${LIBRARY_LINKER_LANGUAGE}" STREQUAL "") + set(LIBRARY_LINKER_LANGUAGE CXX) + endif() + + if("${LIBRARY_OUTPUT_DIR}" STREQUAL "") + set(LIBRARY_OUTPUT_DIR ${PROJECT_BINARY_DIR}) + endif() + + set(_EXCLUDE) + if(LIBRARY_EXCLUDE_FROM_ALL OR HOSTTRACE_BUILD_EXCLUDE_FROM_ALL) + set(_EXCLUDE EXCLUDE_FROM_ALL) + endif() + + # handle PIC not specified but global PIC specified + if(NOT LIBRARY_PIC AND CMAKE_POSITION_INDEPENDENT_CODE) + set(LIBRARY_PIC ON) + endif() + + if(NOT "${LIBRARY_TYPE}" STREQUAL "OBJECT") + if(NOT WIN32 AND NOT XCODE) + list(APPEND LIBRARY_EXTRA_PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}) + endif() + + if(NOT WIN32) + set(LIB_PREFIX ) + list(APPEND LIBRARY_EXTRA_PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_DIR} + ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_DIR} + RUNTIME_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_DIR}) + else() + set(LIB_PREFIX lib) + endif() + endif() + + get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + set(_CUDA OFF) + if(CMAKE_CUDA_COMPILER AND "CUDA" IN_LIST LANGUAGES) + set(_CUDA ON) + endif() + + set(_ENABLE_CUSTOM_COMMAND OFF) + + # add the library or sources + if(NOT TARGET ${LIBRARY_TARGET_NAME}) + set(_ENABLE_CUSTOM_COMMAND ON) + add_library(${LIBRARY_TARGET_NAME} ${LIBRARY_TYPE} ${_EXCLUDE} ${LIBRARY_SOURCES}) + add_library(${PROJECT_NAME}::${LIBRARY_TARGET_NAME} ALIAS ${LIBRARY_TARGET_NAME}) + else() + target_sources(${LIBRARY_TARGET_NAME} PRIVATE ${LIBRARY_SOURCES}) + endif() + + # append include directories + target_include_directories(${LIBRARY_TARGET_NAME} + PUBLIC ${LIBRARY_INCLUDE_DIRECTORIES}) + + # compile definitions + hosttrace_target_compile_definitions(${LIBRARY_TARGET_NAME} + PUBLIC ${LIBRARY_COMPILE_DEFINITIONS}) + + # compile flags + target_compile_options(${LIBRARY_TARGET_NAME} PRIVATE + $<$:${LIBRARY_C_COMPILE_OPTIONS}> + $<$:${LIBRARY_CXX_COMPILE_OPTIONS}>) + + # windows + # docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library + # if(MSVC AND NOT "${LIBRARY_TYPE}" STREQUAL "OBJECT") + # set(_MSVC_FLAGS /MD) + # if("${LIBRARY_TYPE}" STREQUAL "STATIC") + # set(_MSVC_FLAGS /MT) + # endif() + # target_compile_options(${LIBRARY_TARGET_NAME} PUBLIC + # $<$:${_MSVC_FLAGS}> + # $<$:${_MSVC_FLAGS}>) + # endif() + + # cuda flags + if(_CUDA) + target_compile_options(${LIBRARY_TARGET_NAME} PRIVATE + $<$:${LIBRARY_CUDA_COMPILE_OPTIONS}>) + endif() + + # link libraries + target_link_libraries(${LIBRARY_TARGET_NAME} + PUBLIC ${LIBRARY_LINK_LIBRARIES} + PRIVATE ${_ANALYSIS_TOOLS} ${_ARCH_LIBRARY}) + + # other properties + if(NOT "${LIBRARY_TYPE}" STREQUAL "OBJECT") + # link options + if(NOT CMAKE_VERSION VERSION_LESS 3.13) + target_link_options(${LIBRARY_TARGET_NAME} PUBLIC ${LIBRARY_LINK_OPTIONS}) + elseif(NOT "${LIBRARY_LINK_OPTIONS}" STREQUAL "") + list(APPEND LIBRARY_EXTRA_PROPERTIES LINK_OPTIONS ${LIBRARY_LINK_OPTIONS}) + endif() + # + set_target_properties( + ${LIBRARY_TARGET_NAME} PROPERTIES + OUTPUT_NAME ${LIB_PREFIX}${LIBRARY_OUTPUT_NAME} + LANGUAGE ${LIBRARY_LANGUAGE} + LINKER_LANGUAGE ${LIBRARY_LINKER_LANGUAGE} + POSITION_INDEPENDENT_CODE ${LIBRARY_PIC} + ${LIBRARY_EXTRA_PROPERTIES}) + else() + set_target_properties( + ${LIBRARY_TARGET_NAME} PROPERTIES + LANGUAGE ${LIBRARY_LANGUAGE} + POSITION_INDEPENDENT_CODE ${LIBRARY_PIC} + ${LIBRARY_EXTRA_PROPERTIES}) + endif() + + set(COMPILED_TYPES "SHARED" "STATIC" "MODULE") + if(NOT LIBRARY_NO_CACHE_LIST) + # add to cached list of compiled libraries + if("${LIBRARY_TYPE}" IN_LIST COMPILED_TYPES) + cache_list(APPEND ${PROJECT_NAME_UC}_COMPILED_LIBRARIES ${LIBRARY_TARGET_NAME}) + endif() + endif() + unset(COMPILED_TYPES) + + set_property(GLOBAL APPEND PROPERTY HOSTTRACE_${LIBRARY_TYPE}_LIBRARIES + ${LIBRARY_TARGET_NAME}) + + if("${LIBRARY_TYPE}" STREQUAL "SHARED" AND _ENABLE_CUSTOM_COMMAND) + set(_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX}) + set(_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) + + get_target_property(_OUTNAME ${LIBRARY_TARGET_NAME} OUTPUT_NAME) + get_target_property(_VERSION ${LIBRARY_TARGET_NAME} VERSION) + get_target_property(_SOVERSION ${LIBRARY_TARGET_NAME} SOVERSION) + + set(_FILENAME ${_PREFIX}${_OUTNAME}${_SUFFIX}) + + get_target_property(_OUTDIR ${LIBRARY_TARGET_NAME} LIBRARY_OUTPUT_DIRECTORY) + if(NOT IS_ABSOLUTE "${_OUTDIR}") + set(_OUTDIR "${PROJECT_BINARY_DIR}/${_OUTDIR}") + endif() + file(RELATIVE_PATH BINARY_RELPATH + "${PROJECT_BINARY_DIR}/hosttrace/libs" "${_OUTDIR}") + + string(REGEX REPLACE "/$" "" BINARY_RELPATH "${BINARY_RELPATH}") + + # build tree + if(WIN32) + # copy if on windows + add_custom_command(TARGET ${LIBRARY_TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + ${PROJECT_BINARY_DIR}/hosttrace/libs + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + else() + # symlink if not windows + add_custom_command(TARGET ${LIBRARY_TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${BINARY_RELPATH}/${_FILENAME} + ${PROJECT_BINARY_DIR}/hosttrace/libs/${_FILENAME} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + endif() + + endif() + +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# finds dependencies +# +function(HOSTTRACE_GET_INTERNAL_DEPENDS VAR LINK) + # set the depends before creating the library so it does not + # link to itself + set(DEPENDS) + foreach(DEP ${ARGN}) + # + if(TARGET ${DEP}-object) + list(APPEND DEPENDS $) + elseif(TARGET ${DEP}-${LINK}) + list(APPEND DEPENDS ${DEP}-${LINK}) + elseif(TARGET ${DEP}) + list(APPEND DEPENDS ${DEP}) + endif() + # + if(TARGET ${DEP}-component-object) + list(APPEND DEPENDS $) + elseif(TARGET ${DEP}-component-${LINK}) + list(APPEND DEPENDS ${DEP}-component-${LINK}) + endif() + # + if(TARGET hosttrace-${DEP}-object) + list(APPEND DEPENDS $) + elseif(TARGET hosttrace-${DEP}-${LINK}) + list(APPEND DEPENDS hosttrace-${DEP}-${LINK}) + endif() + # + if(TARGET hosttrace-${DEP}-component-object) + list(APPEND DEPENDS $) + elseif(TARGET hosttrace-${DEP}-component-${LINK}) + list(APPEND DEPENDS hosttrace-${DEP}-component-${LINK}) + endif() + endforeach() + set(${VAR} "${DEPENDS}" PARENT_SCOPE) +endfunction() + +#----------------------------------------------------------------------------------------# +# finds dependencies +# +function(HOSTTRACE_GET_PROPERTY_DEPENDS VAR LINK) + # set the depends before creating the library so it does not + # link to itself + set(DEPENDS) + foreach(DEP ${ARGN}) + get_property(TMP GLOBAL PROPERTY HOSTTRACE_${LINK}_${DEP}_LIBRARIES) + # message(STATUS "HOSTTRACE_${LINK}_${DEP}_LIBRARIES :: ${TMP}") + foreach(_ENTRY ${TMP}) + if(NOT TARGET ${_ENTRY}) + continue() + endif() + if("${LINK}" STREQUAL "OBJECT") + list(APPEND DEPENDS $) + else() + list(APPEND DEPENDS ${_ENTRY}) + endif() + endforeach() + endforeach() + set(${VAR} "${DEPENDS}" PARENT_SCOPE) +endfunction() + + +#----------------------------------------------------------------------------------------# +# require variable +# +function(CHECK_REQUIRED VAR) + if(NOT DEFINED ${VAR} OR "${${VAR}}" STREQUAL "") + message(FATAL_ERROR "Variable '${VAR}' must be defined and not empty") + endif() +endfunction() + + +#----------------------------------------------------------------------- +# C/C++ development headers +# +FUNCTION(HOSTTRACE_INSTALL_HEADER_FILES) + if(NOT HOSTTRACE_INSTALL_HEADERS) + return() + endif() + foreach(_header ${ARGN}) + if("${_header}" MATCHES "TARGET_OBJECTS") + continue() + endif() + if(NOT EXISTS "${_header}") + hosttrace_message(AUTHOR_WARNING + "Skipping install of non-existant hosttrace header: '${_header}'") + endif() + file(RELATIVE_PATH _relative ${PROJECT_SOURCE_DIR}/source ${_header}) + get_filename_component(_destpath ${_relative} DIRECTORY) + install( + FILES ${_header} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${_destpath} + OPTIONAL) + endforeach() +ENDFUNCTION() + + +#----------------------------------------------------------------------- +# Library installation +# +FUNCTION(HOSTTRACE_INSTALL_LIBRARIES) + cmake_parse_arguments( + LIB "LINK_VERSION;LINK_SOVERSION" "DESTINATION;EXPORT" "TARGETS" ${ARGN}) + + if(NOT LIB_DESTINATION) + set(LIB_DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + if(NOT LIB_EXPORT) + set(LIB_EXPORT ${PROJECT_NAME}-library-depends) + endif() + + set(_ECHO) + if(NOT CMAKE_VERSION VERSION_LESS 3.15) + set(_ECHO COMMAND_ECHO STDOUT) + endif() + + get_property(SHARED_LIBS GLOBAL PROPERTY HOSTTRACE_SHARED_LIBRARIES) + + if(HOSTTRACE_USE_PYTHON) + set(_PYLIB ${CMAKE_INSTALL_PYTHONDIR}/${PROJECT_NAME}/libs) + if(NOT IS_ABSOLUTE "${_PYLIB}") + set(_PYLIB ${CMAKE_INSTALL_PREFIX}/${_PYLIB}) + endif() + endif() + + list(REMOVE_DUPLICATES LIB_TARGETS) + + if (WIN32) + # use the defaults for destination folders on windows; this follows + # conventional windows locations by putting DLLs into bin and LIBs + # into lib + set(_dst) + else() + # set destination to be the one specified by input argument + set(_dst DESTINATION ${LIB_DESTINATION}) + endif() + foreach(_LIB ${LIB_TARGETS}) + # get the list of previously exported libraries + get_property(_PREVIOUS_EXPORTS GLOBAL PROPERTY HOSTTRACE_EXPORTED_LIBRARIES) + + # skip exporting again if already exported + if("${_LIB}" IN_LIST _PREVIOUS_EXPORTS) + continue() + endif() + + # add to list of export targets + set_property(GLOBAL APPEND PROPERTY HOSTTRACE_EXPORTED_LIBRARIES ${_LIB}) + + install( + TARGETS ${_LIB} + ${_dst} + EXPORT ${PROJECT_NAME}-library-depends + OPTIONAL) + + if (WIN32 AND "${_LIB}" IN_LIST SHARED_LIBS) + # for windows install pdb files too + install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) + endif() + + if(HOSTTRACE_USE_PYTHON AND ${_LIB} IN_LIST SHARED_LIBS) + set(_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX}) + set(_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) + + get_target_property(_OUTNAME ${_LIB} OUTPUT_NAME) + get_target_property(_VERSION ${_LIB} VERSION) + get_target_property(_SOVERSION ${_LIB} SOVERSION) + + set(_FILENAME ${_PREFIX}${_OUTNAME}${_SUFFIX}) + set(_VERSION_FILENAME ) + set(_SOVERSION_FILENAME ) + set(_FILE_TYPES _FILENAME) + # + if(_VERSION AND LIB_LINK_VERSION) + if(APPLE) + set(_VERSION_FILENAME ${_PREFIX}${_OUTNAME}.${_VERSION}${_SUFFIX}) + else() + set(_VERSION_FILENAME ${_PREFIX}${_OUTNAME}${_SUFFIX}.${_VERSION}) + endif() + list(APPEND _FILE_TYPES _VERSION_FILENAME) + endif() + # + if(_SOVERSION AND LIB_LINK_SOVERSION) + if(APPLE) + set(_SOVERSION_FILENAME ${_PREFIX}${_OUTNAME}.${_SOVERSION}${_SUFFIX}) + else() + set(_SOVERSION_FILENAME ${_PREFIX}${_OUTNAME}${_SUFFIX}.${_SOVERSION}) + endif() + list(APPEND _FILE_TYPES _SOVERSION_FILENAME) + endif() + + file(RELATIVE_PATH INSTALL_RELPATH "${_PYLIB}" + "${CMAKE_INSTALL_PREFIX}/${LIB_DESTINATION}") + + get_target_property(_OUTDIR ${_LIB} LIBRARY_OUTPUT_DIRECTORY) + if(NOT IS_ABSOLUTE "${_OUTDIR}") + set(_OUTDIR "${PROJECT_BINARY_DIR}/${_OUTDIR}") + endif() + + string(REGEX REPLACE "/$" "" INSTALL_RELPATH "${INSTALL_RELPATH}") + + # install tree + foreach(_FNAME ${_FILE_TYPES}) + if(NOT ${_FNAME}) + continue() + endif() + if(WIN32) + # install a second time if windows + install(TARGETS ${_LIB} DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/${PROJECT_NAME}/libs) + else() + # symlink if not windows + install(CODE + " + EXECUTE_PROCESS( + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${INSTALL_RELPATH}/${${_FNAME}} ${_PYLIB}/${${_FNAME}} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + ${_ECHO}) + " + OPTIONAL) + endif() + endforeach() + endif() + endforeach() +ENDFUNCTION() + + +#----------------------------------------------------------------------- +# Add pre-compiled headers +# +FUNCTION(HOSTTRACE_TARGET_PRECOMPILE_HEADERS _TARG) + cmake_parse_arguments( + HEAD "INSTALL_INTERFACE" "" "FILES" ${ARGN}) + + if(HOSTTRACE_PRECOMPILE_HEADERS) + set(_BINARY_IFACE) + set(_INSTALL_IFACE) + foreach(_HEADER ${HEAD_FILES}) + string(REPLACE "${PROJECT_SOURCE_DIR}/" "" _HEADER "${_HEADER}") + list(APPEND _BINARY_IFACE "${_HEADER}") + string(REPLACE "external/cereal/include/" "" _HEADER "${_HEADER}") + string(REPLACE "source/" "" _HEADER "${_HEADER}") + list(APPEND _INSTALL_IFACE "<${_HEADER}>") + endforeach() + target_precompile_headers(${_TARG} INTERFACE + $) + if(HEAD_INSTALL_INTERFACE) + target_precompile_headers(${_TARG} INTERFACE + $) + endif() + endif() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# macro to build a library of type: shared, static, object +# +FUNCTION(BUILD_INTERMEDIATE_LIBRARY) + + # options + set(_options USE_INTERFACE + USE_CATEGORY + INSTALL_SOURCE + FORCE_OBJECT + FORCE_SHARED + FORCE_STATIC) + # single-value + set(_onevalue NAME + TARGET + CATEGORY + FOLDER + VISIBILITY) + # multi-value + set(_multival HEADERS + SOURCES + DEPENDS + INCLUDES + PROPERTY_DEPENDS + PUBLIC_LINK + PRIVATE_LINK) + + cmake_parse_arguments( + COMP "${_options}" "${_onevalue}" "${_multival}" ${ARGN}) + + check_required(COMP_NAME) + check_required(COMP_TARGET) + check_required(COMP_CATEGORY) + check_required(COMP_FOLDER) + + if(NOT COMP_VISIBILITY) + set(COMP_VISIBILITY default) + endif() + + set(VIS_OPTS "default" "hidden") + if(NOT "${COMP_VISIBILITY}" IN_LIST VIS_OPTS) + message(FATAL_ERROR "${COMP_TARGET} available visibility options: ${VIS_OPTS}") + endif() + + string(TOUPPER "${COMP_NAME}" UPP_COMP) + string(REPLACE "-" "_" UPP_COMP "${UPP_COMP}") + string(TOLOWER "${COMP_CATEGORY}" LC_CATEGORY) + + set(_LIB_TYPES) + set(_LIB_DEFAULT_TYPE) + + # if(HOSTTRACE_BUILD_LTO OR COMP_FORCE_OBJECT) + if(COMP_FORCE_OBJECT) + list(APPEND _LIB_TYPES object) + set(object_OPTIONS PIC TYPE OBJECT) + endif() + + if(_BUILD_STATIC_CXX OR COMP_FORCE_STATIC) + list(APPEND _LIB_TYPES static) + set(static_OPTIONS TYPE STATIC) + set(_LIB_DEFAULT_TYPE static) + endif() + + if(_BUILD_SHARED_CXX OR COMP_FORCE_SHARED) + list(APPEND _LIB_TYPES shared) + set(shared_OPTIONS PIC TYPE SHARED) + set(_LIB_DEFAULT_TYPE shared) + endif() + + set(_SOURCES ${COMP_SOURCES} ${COMP_HEADERS}) + + if(COMP_INSTALL_SOURCE) + set(_SOURCES) + foreach(_SOURCE ${COMP_SOURCES}) + get_filename_component(_SOURCE_NAME "${_SOURCE}" NAME) + get_filename_component(_SOURCE_DIR "${_SOURCE}" DIRECTORY) + get_filename_component(_SOURCE_DIR "${_SOURCE_DIR}" NAME) + if(NOT "${_SOURCE_NAME}" STREQUAL "extern.cpp" AND + NOT "${_SOURCE_DIR}" STREQUAL "extern") + list(APPEND _SOURCES ${_SOURCE}) + else() + message(WARNING "Not installing ${_SOURCE} source") + endif() + endforeach() + hosttrace_install_header_files(${_SOURCES}) + endif() + + foreach(LINK ${_LIB_TYPES}) + + string(TOUPPER "${LINK}" UPP_LINK) + set(TARGET_NAME hosttrace-${COMP_TARGET}-${LINK}) + + if(NOT "${LINK}" STREQUAL OBJECT AND TARGET hosttrace-${COMP_TARGET}-object) + set(_SOURCES $) + endif() + + # set the depends before creating the library so it does not link to itself + hosttrace_get_internal_depends(_DEPENDS ${LINK} ${COMP_DEPENDS} hosttrace-core) + hosttrace_get_property_depends(_PROPERTY_OBJS OBJECT ${COMP_PROPERTY_DEPENDS}) + hosttrace_get_property_depends(_PROPERTY_LINK ${UPP_LINK} ${COMP_PROPERTY_DEPENDS}) + + # must reset this variable + set(DEPENDS) + foreach(_DEP ${_DEPENDS} ${_PROPERTY_OBJS} ${_PROPERTY_LINK}) + if("${_DEP}" MATCHES ".*TARGET_OBJECTS:.*") + list(APPEND _SOURCES ${_DEP}) + else() + list(APPEND DEPENDS ${_DEP}) + endif() + endforeach() + + if(DEPENDS) + list(REMOVE_DUPLICATES DEPENDS) + endif() + + set_property(GLOBAL APPEND PROPERTY HOSTTRACE_HEADERS ${COMP_HEADERS}) + set_property(GLOBAL APPEND PROPERTY HOSTTRACE_SOURCES ${COMP_SOURCES}) + set_property(GLOBAL APPEND PROPERTY HOSTTRACE_${UPP_LINK}_${COMP_CATEGORY}_LIBRARIES + hosttrace-${COMP_TARGET}-${LINK}) + + # message(STATUS "Building ${TARGET_NAME}") + # message(STATUS "[-------] ${TARGET_NAME} :: ${DEPENDS}") + + build_library( + NO_CACHE_LIST + ${${LINK}_OPTIONS} + TARGET_NAME ${TARGET_NAME} + OUTPUT_NAME hosttrace-${COMP_TARGET} + LANGUAGE CXX + LINKER_LANGUAGE ${_LINKER_LANGUAGE} + OUTPUT_DIR ${PROJECT_BINARY_DIR}/${COMP_FOLDER} + SOURCES ${_SOURCES} + CXX_COMPILE_OPTIONS ${${PROJECT_NAME}_CXX_COMPILE_OPTIONS}) + + target_include_directories(${TARGET_NAME} PUBLIC ${COMP_INCLUDES}) + + list(REMOVE_ITEM DEPENDS "${TARGET_NAME}") + set(_DEPS ${DEPENDS}) + set(DEPENDS) + foreach(_DEP ${_DEPS}) + if("${_DEP}" MATCHES "hosttrace::") + list(APPEND DEPENDS ${_DEP}) + else() + list(APPEND DEPENDS "hosttrace::${_DEP}") + endif() + endforeach() + if(NOT "${DEPENDS}" STREQUAL "") + list(REMOVE_DUPLICATES DEPENDS) + endif() + + target_link_libraries(${TARGET_NAME} PUBLIC + hosttrace-external-${LINK} + hosttrace-headers + hosttrace-vector + hosttrace-dmp + ${DEPENDS} + ${COMP_PUBLIC_LINK}) + + target_link_libraries(${TARGET_NAME} PRIVATE + hosttrace-compile-options + hosttrace-develop-options + hosttrace-${COMP_VISIBILITY}-visibility + ${COMP_PRIVATE_LINK}) + + hosttrace_target_compile_definitions(${TARGET_NAME} PRIVATE + HOSTTRACE_SOURCE + HOSTTRACE_${COMP_CATEGORY}_SOURCE + HOSTTRACE_${UPP_COMP}_SOURCE) + + hosttrace_target_compile_definitions(${TARGET_NAME} INTERFACE + HOSTTRACE_USE_${UPP_COMP}_EXTERN) + + if(NOT "${UPP_COMP}" STREQUAL "CORE") + hosttrace_target_compile_definitions(${TARGET_NAME} PUBLIC + HOSTTRACE_USE_CORE_EXTERN) + endif() + + if("${COMP_CATEGORY}" STREQUAL "COMPONENT" OR COMP_USE_CATEGORY) + hosttrace_target_compile_definitions(${TARGET_NAME} INTERFACE + HOSTTRACE_USE_${COMP_CATEGORY}_EXTERN) + endif() + + if("${LINK}" STREQUAL "OBJECT") + if(NOT TARGET hosttrace-${LC_CATEGORY}-${LINK}) + add_interface_library(hosttrace-${LC_CATEGORY}-${LINK}) + endif() + if(NOT "hosttrace-${LC_CATEGORY}-${LINK}" STREQUAL "${TARGET_NAME}") + target_sources(hosttrace-${LC_CATEGORY}-${LINK} INTERFACE + $) + endif() + else() + if(NOT TARGET hosttrace-${LC_CATEGORY}-${LINK}) + add_interface_library(hosttrace-${LC_CATEGORY}-${LINK}) + endif() + + if(NOT "hosttrace-${LC_CATEGORY}-${LINK}" STREQUAL "${TARGET_NAME}") + target_link_libraries(hosttrace-${LC_CATEGORY}-${LINK} INTERFACE ${TARGET_NAME}) + endif() + + hosttrace_install_libraries( + TARGETS ${TARGET_NAME} + DESTINATION ${CMAKE_INSTALL_LIBDIR} + EXPORT ${PROJECT_NAME}-library-depends) + + set_property(GLOBAL APPEND PROPERTY HOSTTRACE_INTERMEDIATE_TARGETS ${TARGET_NAME}) + set_property(GLOBAL APPEND PROPERTY HOSTTRACE_INTERMEDIATE_${UPP_LINK}_TARGETS + ${TARGET_NAME}) + endif() + + endforeach() + + hosttrace_install_header_files(${COMP_HEADERS}) + if(COMP_INSTALL_SOURCE) + hosttrace_install_header_files(${COMP_SOURCES}) + endif() + + if(NOT TARGET hosttrace-${COMP_TARGET}) + add_interface_library(hosttrace-${COMP_TARGET}) + target_link_libraries(hosttrace-${COMP_TARGET} INTERFACE + hosttrace-${COMP_TARGET}-${_LIB_DEFAULT_TYPE}) + endif() + + if(NOT TARGET hosttrace-${LC_CATEGORY}) + add_interface_library(hosttrace-${LC_CATEGORY}) + target_link_libraries(hosttrace-${LC_CATEGORY} INTERFACE + hosttrace-${LC_CATEGORY}-${_LIB_DEFAULT_TYPE}) + endif() + + if(WIN32 AND TARGET hosttrace-${COMP_TARGET}-shared AND TARGET hosttrace-${COMP_TARGET}-static) + add_dependencies(hosttrace-${COMP_TARGET}-shared hosttrace-${COMP_TARGET}-static) + endif() + +ENDFUNCTION() + + +FUNCTION(ADD_CMAKE_DEFINES _VAR) + # parse args + cmake_parse_arguments(DEF "VALUE;QUOTE;DEFAULT" "" "" ${ARGN}) + if(DEF_VALUE) + if(DEF_QUOTE) + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_DEFINES + "${_VAR} \"@${_VAR}@\"") + else() + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_DEFINES "${_VAR} @${_VAR}@") + endif() + if(DEF_DEFAULT) + if(DEF_QUOTE) + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_DEFAULT_CMAKE_DEFINES + "#if !defined(${_VAR})\n#cmakedefine ${_VAR} \"@${_VAR}@\"\n#endif\n") + else() + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_DEFAULT_CMAKE_DEFINES + "#if !defined(${_VAR})\n#cmakedefine ${_VAR} @${_VAR}@\n#endif\n") + endif() + endif() + else() + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_DEFINES "${_VAR}") + endif() +ENDFUNCTION() + +#----------------------------------------------------------------------- +# function add_feature( ) +# Add a project feature, whose activation is specified by the +# existence of the variable , to the list of enabled/disabled +# features, plus a docstring describing the feature +# +FUNCTION(ADD_FEATURE _var _description) + set(EXTRA_DESC "") + foreach(currentArg ${ARGN}) + if(NOT "${currentArg}" STREQUAL "${_var}" AND + NOT "${currentArg}" STREQUAL "${_description}" AND + NOT "${currentArg}" STREQUAL "CMAKE_DEFINE" AND + NOT "${currentArg}" STREQUAL "DOC") + set(EXTRA_DESC "${EXTA_DESC}${currentArg}") + endif() + endforeach() + + set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_FEATURES ${_var}) + set_property(GLOBAL PROPERTY ${_var}_DESCRIPTION "${_description}${EXTRA_DESC}") + + IF("CMAKE_DEFINE" IN_LIST ARGN) + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_DEFINES "${_var} @${_var}@") + IF(HOSTTRACE_BUILD_DOCS) + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_OPTIONS_DOC + "${_var}` | ${_description}${EXTRA_DESC} |") + ENDIF() + ELSEIF("DOC" IN_LIST ARGN AND HOSTTRACE_BUILD_DOCS) + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_OPTIONS_DOC + "${_var}` | ${_description}${EXTRA_DESC} |") + ENDIF() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# function add_option( [NO_FEATURE]) +# Add an option and add as a feature if NO_FEATURE is not provided +# +FUNCTION(ADD_OPTION _NAME _MESSAGE _DEFAULT) + OPTION(${_NAME} "${_MESSAGE}" ${_DEFAULT}) + IF("NO_FEATURE" IN_LIST ARGN) + MARK_AS_ADVANCED(${_NAME}) + ELSE() + ADD_FEATURE(${_NAME} "${_MESSAGE}") + IF(HOSTTRACE_BUILD_DOCS) + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_OPTIONS_DOC + "${_NAME}` | ${_MESSAGE} |") + ENDIF() + ENDIF() + IF("ADVANCED" IN_LIST ARGN) + MARK_AS_ADVANCED(${_NAME}) + ENDIF() + IF("CMAKE_DEFINE" IN_LIST ARGN) + SET_PROPERTY(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_DEFINES ${_NAME}) + ENDIF() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# function print_enabled_features() +# Print enabled features plus their docstrings. +# +FUNCTION(PRINT_ENABLED_FEATURES) + set(_basemsg "The following features are defined/enabled (+):") + set(_currentFeatureText "${_basemsg}") + get_property(_features GLOBAL PROPERTY ${PROJECT_NAME}_FEATURES) + if(NOT "${_features}" STREQUAL "") + list(REMOVE_DUPLICATES _features) + list(SORT _features) + endif() + foreach(_feature ${_features}) + if(${_feature}) + # add feature to text + set(_currentFeatureText "${_currentFeatureText}\n ${_feature}") + # get description + get_property(_desc GLOBAL PROPERTY ${_feature}_DESCRIPTION) + # print description, if not standard ON/OFF, print what is set to + if(_desc) + if(NOT "${${_feature}}" STREQUAL "ON" AND + NOT "${${_feature}}" STREQUAL "TRUE") + set(_currentFeatureText "${_currentFeatureText}: ${_desc} -- [\"${${_feature}}\"]") + else() + string(REGEX REPLACE "^${PROJECT_NAME}_USE_" "" _feature_tmp "${_feature}") + string(TOLOWER "${_feature_tmp}" _feature_tmp_l) + capitalize("${_feature_tmp}" _feature_tmp_c) + foreach(_var _feature _feature_tmp _feature_tmp_l _feature_tmp_c) + set(_ver "${${${_var}}_VERSION}") + if(NOT "${_ver}" STREQUAL "") + set(_desc "${_desc} -- [found version ${_ver}]") + break() + endif() + unset(_ver) + endforeach() + set(_currentFeatureText "${_currentFeatureText}: ${_desc}") + endif() + set(_desc NOTFOUND) + endif() + endif() + endforeach() + + if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}") + message(STATUS "${_currentFeatureText}\n") + endif() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# function print_disabled_features() +# Print disabled features plus their docstrings. +# +FUNCTION(PRINT_DISABLED_FEATURES) + set(_basemsg "The following features are NOT defined/enabled (-):") + set(_currentFeatureText "${_basemsg}") + get_property(_features GLOBAL PROPERTY ${PROJECT_NAME}_FEATURES) + if(NOT "${_features}" STREQUAL "") + list(REMOVE_DUPLICATES _features) + list(SORT _features) + endif() + foreach(_feature ${_features}) + if(NOT ${_feature}) + set(_currentFeatureText "${_currentFeatureText}\n ${_feature}") + + get_property(_desc GLOBAL PROPERTY ${_feature}_DESCRIPTION) + + if(_desc) + set(_currentFeatureText "${_currentFeatureText}: ${_desc}") + set(_desc NOTFOUND) + endif(_desc) + endif() + endforeach(_feature) + + if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}") + message(STATUS "${_currentFeatureText}\n") + endif() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# function print_enabled_interfaces() +# Print enabled INTERFACE libraries plus their docstrings. +# +FUNCTION(PRINT_ENABLED_INTERFACES) + set(_basemsg "The following INTERFACE libraries are enabled:") + set(_currentFeatureText "${_basemsg}") + get_property(_enabled GLOBAL PROPERTY ${PROJECT_NAME}_ENABLED_INTERFACES) + get_property(_disabled GLOBAL PROPERTY ${PROJECT_NAME}_DISABLED_INTERFACES) + if(NOT "${_enabled}" STREQUAL "") + list(REMOVE_DUPLICATES _enabled) + list(SORT _enabled) + endif() + foreach(_LIB ${_disabled}) + if("${_LIB}" IN_LIST _enabled) + list(REMOVE_ITEM _enabled ${_LIB}) + endif() + endforeach() + foreach(_feature ${_enabled}) + # add feature to text + set(_currentFeatureText "${_currentFeatureText}\n hosttrace::${_feature}") + endforeach() + + if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}") + message(STATUS "${_currentFeatureText}\n") + endif() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# function print_disabled_interfaces() +# Print disabled interfaces plus their docstrings. +# +FUNCTION(PRINT_DISABLED_INTERFACES) + set(_basemsg "The following INTERFACE libraries are NOT enabled (empty INTERFACE libraries):") + set(_currentFeatureText "${_basemsg}") + get_property(_disabled GLOBAL PROPERTY ${PROJECT_NAME}_DISABLED_INTERFACES) + if(NOT "${_disabled}" STREQUAL "") + list(REMOVE_DUPLICATES _disabled) + list(SORT _disabled) + endif() + foreach(_feature ${_disabled}) + set(_currentFeatureText "${_currentFeatureText}\n hosttrace::${_feature}") + endforeach(_feature) + + if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}") + message(STATUS "${_currentFeatureText}\n") + endif() +ENDFUNCTION() + + +#----------------------------------------------------------------------------------------# +# function print_features() +# Print all features plus their docstrings. +# +FUNCTION(PRINT_FEATURES) + message(STATUS "") + print_enabled_features() + print_disabled_features() + message(STATUS "") + print_enabled_interfaces() + print_disabled_interfaces() +ENDFUNCTION() + + +cmake_policy(POP) diff --git a/cmake/Packages.cmake b/cmake/Packages.cmake new file mode 100644 index 0000000000..0a211e9e15 --- /dev/null +++ b/cmake/Packages.cmake @@ -0,0 +1,159 @@ +# include guard +include_guard(DIRECTORY) + +########################################################################################## +# +# External Packages are found here +# +########################################################################################## + +add_interface_library(hosttrace-headers + "Provides minimal set of include flags to compile with hosttrace") +add_interface_library(hosttrace-threading + "Enables multithreading support") +add_interface_library(hosttrace-dyninst + "Provides flags and libraries for Dyninst (dynamic instrumentation") + +# include threading because of rooflines +target_link_libraries(hosttrace-headers INTERFACE hosttrace-threading) + +#----------------------------------------------------------------------------------------# +# +# Threading +# +#----------------------------------------------------------------------------------------# + +if(NOT WIN32) + set(CMAKE_THREAD_PREFER_PTHREAD ON) + set(THREADS_PREFER_PTHREAD_FLAG OFF) +endif() + +find_library(pthread_LIBRARY NAMES pthread pthreads) +find_package_handle_standard_args(pthread-library REQUIRED_VARS pthread_LIBRARY) +find_package(Threads ${hosttrace_FIND_QUIETLY} ${hosttrace_FIND_REQUIREMENT}) + +if(Threads_FOUND) + target_link_libraries(hosttrace-threading INTERFACE ${CMAKE_THREAD_LIBS_INIT}) +endif() + +if(pthread_LIBRARY AND NOT WIN32) + target_link_libraries(hosttrace-threading INTERFACE ${pthread_LIBRARY}) +endif() + + +#----------------------------------------------------------------------------------------# +# +# Dyninst +# +#----------------------------------------------------------------------------------------# + +find_package(Dyninst ${hosttrace_FIND_QUIETLY} REQUIRED) +set(_BOOST_COMPONENTS atomic system thread date_time) +set(hosttrace_BOOST_COMPONENTS "${_BOOST_COMPONENTS}" CACHE STRING + "Boost components used by Dyninst in hosttrace") +set(Boost_NO_BOOST_CMAKE ON) +find_package(Boost QUIET REQUIRED + COMPONENTS ${hosttrace_BOOST_COMPONENTS}) + + +# some installs of dyninst don't set this properly +if(EXISTS "${DYNINST_INCLUDE_DIR}" AND NOT DYNINST_HEADER_DIR) + get_filename_component(DYNINST_HEADER_DIR "${DYNINST_INCLUDE_DIR}" REALPATH CACHE) +else() + find_path(DYNINST_HEADER_DIR + NAMES BPatch.h dyninstAPI_RT.h + HINTS ${Dyninst_ROOT_DIR} ${Dyninst_DIR} ${Dyninst_DIR}/../../.. + PATHS ${Dyninst_ROOT_DIR} ${Dyninst_DIR} ${Dyninst_DIR}/../../.. + PATH_SUFFIXES include) +endif() + +# useful for defining the location of the runtime API +find_library(DYNINST_API_RT dyninstAPI_RT + HINTS ${Dyninst_ROOT_DIR} ${Dyninst_DIR} + PATHS ${Dyninst_ROOT_DIR} ${Dyninst_DIR} + PATH_SUFFIXES lib) + +find_path(TBB_INCLUDE_DIR + NAMES tbb/tbb.h +PATH_SUFFIXES include) + +if(TBB_INCLUDE_DIR) + set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR}) +endif() + +if(DYNINST_API_RT) + target_compile_definitions(hosttrace-dyninst INTERFACE + DYNINST_API_RT="${DYNINST_API_RT}") +endif() + +if(Boost_DIR) + get_filename_component(Boost_RPATH_DIR "${Boost_DIR}" DIRECTORY) + get_filename_component(Boost_RPATH_DIR "${Boost_RPATH_DIR}" DIRECTORY) + if(EXISTS "${Boost_RPATH_DIR}" AND IS_DIRECTORY "${Boost_RPATH_DIR}") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:${Boost_RPATH_DIR}") + endif() +endif() + +add_rpath(${DYNINST_LIBRARIES} ${Boost_LIBRARIES}) +target_link_libraries(hosttrace-dyninst INTERFACE + ${DYNINST_LIBRARIES} ${Boost_LIBRARIES}) +foreach(_TARG Dyninst::dyninst Boost::headers Boost::atomic + Boost::system Boost::thread Boost::date_time) + if(TARGET ${_TARG}) + target_link_libraries(hosttrace-dyninst INTERFACE ${_TARG}) + endif() +endforeach() +target_include_directories(hosttrace-dyninst SYSTEM INTERFACE + ${TBB_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} + ${DYNINST_HEADER_DIR}) +target_compile_definitions(hosttrace-dyninst INTERFACE hosttrace_USE_DYNINST) + +if(DYNINST_API_RT) + add_cmake_defines(DYNINST_API_RT VALUE QUOTE DEFAULT) +else() + add_cmake_defines(DYNINST_API_RT VALUE QUOTE) +endif() + +#----------------------------------------------------------------------------------------# +# +# Perfetto +# +#----------------------------------------------------------------------------------------# + +set(perfetto_DIR ${PROJECT_BINARY_DIR}/stuff/hosttrace-dyninst/perfetto) +if(NOT EXISTS "${perfetto_DIR}/.git") + find_package(Git REQUIRED) + execute_process(COMMAND + ${GIT_EXECUTABLE} clone -b v17.0 https://android.googlesource.com/platform/external/perfetto/ ${perfetto_DIR}) +endif() + +#----------------------------------------------------------------------------------------# +# +# Clang Tidy +# +#----------------------------------------------------------------------------------------# + +# clang-tidy +macro(HOSTTRACE_ACTIVATE_CLANG_TIDY) + if(HOSTTRACE_USE_CLANG_TIDY) + find_program(CLANG_TIDY_COMMAND NAMES clang-tidy) + add_feature(CLANG_TIDY_COMMAND "Path to clang-tidy command") + if(NOT CLANG_TIDY_COMMAND) + timemory_message(WARNING "HOSTTRACE_USE_CLANG_TIDY is ON but clang-tidy is not found!") + set(HOSTTRACE_USE_CLANG_TIDY OFF) + else() + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_COMMAND}) + + # Create a preprocessor definition that depends on .clang-tidy content so + # the compile command will change when .clang-tidy changes. This ensures + # that a subsequent build re-runs clang-tidy on all sources even if they + # do not otherwise need to be recompiled. Nothing actually uses this + # definition. We add it to targets on which we run clang-tidy just to + # get the build dependency on the .clang-tidy file. + file(SHA1 ${CMAKE_CURRENT_LIST_DIR}/.clang-tidy clang_tidy_sha1) + set(CLANG_TIDY_DEFINITIONS "CLANG_TIDY_SHA1=${clang_tidy_sha1}") + unset(clang_tidy_sha1) + endif() + endif() +endmacro() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000000..8977a4f474 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) + +project(hosttrace-dyninst-examples + LANGUAGES CXX) + +add_subdirectory(transpose) diff --git a/examples/transpose/CMakeLists.txt b/examples/transpose/CMakeLists.txt new file mode 100644 index 0000000000..a48c93fffa --- /dev/null +++ b/examples/transpose/CMakeLists.txt @@ -0,0 +1,59 @@ + +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) + +# uses make to avoid CMAKE_CXX_COMPILER issues +project(transpose + LANGUAGES CXX) + +find_program(MAKE_EXECUTABLE NAMES make gmake) +find_program(HIPCC_EXECUTABLE NAMES hipcc) + +if(NOT MAKE_EXECUTABLE) + message(AUTHOR_WARNING "make/gmake could not be found. Cannot build transpose target") + return() +endif() + +if(NOT HIPCC_EXECUTABLE) + message(AUTHOR_WARNING "hipcc could not be found. Cannot build transpose target") + return() +endif() + +string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE) +set(transpose_CXX_FLAGS "-W -Wall ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}") + +if(CMAKE_CXX_COMPILER_IS_CLANG AND TARGET hosttrace::hosttrace-compile-options) + set(transpose_CXX_FLAGS "${transpose_CXX_FLAGS} ${hosttrace_CXX_FLAGS}") + get_target_property(COMPILE_OPTS hosttrace::hosttrace-compile-options INTERFACE_COMPILE_OPTIONS) + set(transpose_CXX_FLAGS "${transpose_CXX_FLAGS} ${COMPILE_OPTS}") +endif() + +# remove generator expressions +string(REPLACE "$<" "" transpose_CXX_FLAGS "${transpose_CXX_FLAGS}") +string(REPLACE ">:" ":" transpose_CXX_FLAGS "${transpose_CXX_FLAGS}") +string(REPLACE ">" "" transpose_CXX_FLAGS "${transpose_CXX_FLAGS}") +string(REPLACE "COMPILE_LANGUAGE:CXX:" "" transpose_CXX_FLAGS "${transpose_CXX_FLAGS}") +# remove double spaces +string(REPLACE " " " " transpose_CXX_FLAGS "${transpose_CXX_FLAGS}") +# convert to list +string(REPLACE " " ";" transpose_CXX_FLAGS "${transpose_CXX_FLAGS}") +# remove duplicate args +list(REMOVE_DUPLICATES transpose_CXX_FLAGS) +# make single string +string(REPLACE ";" " " transpose_CXX_FLAGS "${transpose_CXX_FLAGS}") + +# configure files in build directory +configure_file(${PROJECT_SOURCE_DIR}/transpose.cpp + ${CMAKE_BINARY_DIR}/transpose/transpose.cpp COPYONLY) + +configure_file(${PROJECT_SOURCE_DIR}/Makefile.in + ${CMAKE_BINARY_DIR}/transpose/Makefile @ONLY) + +# touch the file when cmake re-runs to ensure rebuilt +execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/transpose/transpose.cpp) + +# target for adding a dependency to libpytimemory target +add_custom_target(transpose ALL + COMMAND ${MAKE_EXECUTABLE} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/transpose + BYPRODUCTS ${CMAKE_BINARY_DIR}/transpose/transpose + SOURCES ${CMAKE_BINARY_DIR}/transpose/transpose.cpp) diff --git a/examples/transpose/Makefile b/examples/transpose/Makefile new file mode 100644 index 0000000000..3c0011ae6b --- /dev/null +++ b/examples/transpose/Makefile @@ -0,0 +1,38 @@ +CXXFLAGS += -std=c++17 -O3 -Wno-unused-result -g +all: transpose + +debug: CXXFLAGS += -ggdb -g +debug: transpose + +transpose: transpose.cpp + hipcc ${CXXFLAGS} -o $@ $^ + +.PHONY: clean + +run: + ./transpose + +run.verbose: + AMD_LOG_LEVEL=3, AMD_LOG_MASK=128 ./transpose + +rd: + rocgdb --args ./transpose + +prof.trace: + rocprof --hip-trace --stats ./transpose + +prof: + rocprof --obj-tracking on -i profile.txt ./transpose + +prof.full: + rocprof --obj-tracking on -i profile.txt -m custom_metric.xml ./transpose + +callgrind: + valgrind --tool=callgrind ./transpose + +callgrind.ann: + callgrind_annotate --inclusive=yes --tree=both callgrind.out.* > callgrind.out + +clean: + rm -f transpose *.o results.* + diff --git a/examples/transpose/Makefile.in b/examples/transpose/Makefile.in new file mode 100644 index 0000000000..71339e7db5 --- /dev/null +++ b/examples/transpose/Makefile.in @@ -0,0 +1,37 @@ +CXXFLAGS += -std=c++@CMAKE_CXX_STANDARD@ @transpose_CXX_FLAGS@ +all: transpose + +debug: CXXFLAGS += -ggdb -g +debug: transpose + +transpose: transpose.cpp + hipcc ${CXXFLAGS} -o $@ $^ + +.PHONY: clean + +run: + ./transpose + +run.verbose: + AMD_LOG_LEVEL=3, AMD_LOG_MASK=128 ./transpose + +rd: + rocgdb --args ./transpose + +prof.trace: + rocprof --hip-trace --stats ./transpose + +prof: + rocprof --obj-tracking on -i profile.txt ./transpose + +prof.full: + rocprof --obj-tracking on -i profile.txt -m custom_metric.xml ./transpose + +callgrind: + valgrind --tool=callgrind ./transpose + +callgrind.ann: + callgrind_annotate --inclusive=yes --tree=both callgrind.out.* > callgrind.out + +clean: + rm -f transpose *.o results.* diff --git a/examples/transpose/transpose.cpp b/examples/transpose/transpose.cpp new file mode 100644 index 0000000000..aea2e28e0f --- /dev/null +++ b/examples/transpose/transpose.cpp @@ -0,0 +1,156 @@ +/* +Copyright (c) 2015-2020 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#include "hip/hip_runtime.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HIP_API_CALL(CALL) \ + { \ + hipError_t error_ = (CALL); \ + if(error_ != hipSuccess) \ + { \ + fprintf(stderr, "%s:%d :: HIP error : %s\n", __FILE__, __LINE__, \ + hipGetErrorString(error_)); \ + exit(EXIT_FAILURE); \ + } \ + } + +void +check_hip_error(void) +{ + hipError_t err = hipGetLastError(); + if(err != hipSuccess) + { + std::cerr << "Error: " << hipGetErrorString(err) << std::endl; + exit(err); + } +} + +void +verify(int* in, int* out, int M, int N) +{ + for(int i = 0; i < 10; i++) + { + int row = rand() % M; + int col = rand() % N; + if(in[row * N + col] != out[col * M + row]) + { + std::cout << "mismatch: " << row << ", " << col << " : " << in[row * N + col] + << " | " << out[col * M + row] << "\n"; + } + } +} + +const unsigned TILE_DIM = 32; +__global__ void +transpose_a(int* in, int* out, int M, int N) +{ + __shared__ int tile[TILE_DIM][TILE_DIM]; + + int idx = (blockIdx.y * blockDim.y + threadIdx.y) * M + blockIdx.x * blockDim.x + + threadIdx.x; + tile[threadIdx.y][threadIdx.x] = in[idx]; + __syncthreads(); + idx = (blockIdx.x * blockDim.x + threadIdx.y) * N + blockIdx.y * blockDim.y + + threadIdx.x; + out[idx] = tile[threadIdx.x][threadIdx.y]; +} + +void +run(int argc, char** argv) +{ + (void) argc; + (void) argv; + unsigned int M = 4960; + unsigned int N = 4960; + + std::cout << "M: " << M << " N: " << N << std::endl; + size_t size = sizeof(int) * M * N; + int* matrix = (int*) malloc(size); + for(size_t i = 0; i < M * N; i++) + matrix[i] = rand() % 1002; + int *in, *out; + + std::chrono::high_resolution_clock::time_point t1, t2; + + HIP_API_CALL(hipMalloc(&in, size)); + HIP_API_CALL(hipMalloc(&out, size)); + check_hip_error(); + HIP_API_CALL(hipMemset(in, 0, size)); + HIP_API_CALL(hipMemset(out, 0, size)); + HIP_API_CALL(hipMemcpy(in, matrix, size, hipMemcpyHostToDevice)); + HIP_API_CALL(hipDeviceSynchronize()); + check_hip_error(); + hipDeviceProp_t props; + HIP_API_CALL(hipGetDeviceProperties(&props, 0)); + + dim3 grid(M / 32, N / 32, 1); + dim3 block(32, 32, 1); // transpose_a + + // warmup + hipLaunchKernelGGL(transpose_a, grid, block, 0, 0, in, out, M, N); + check_hip_error(); + + t1 = std::chrono::high_resolution_clock::now(); + const unsigned times = 10000; + for(size_t i = 0; i < times; i++) + { + hipLaunchKernelGGL(transpose_a, grid, block, 0, 0, in, out, M, N); + check_hip_error(); + HIP_API_CALL(hipDeviceSynchronize()); + } + t2 = std::chrono::high_resolution_clock::now(); + double time = + std::chrono::duration_cast>(t2 - t1).count(); + float GB = (float) size * times * 2 / (1 << 30); + std::cout << "The average performance of transpose is " << GB / time << " GBytes/sec" + << std::endl; + + int* out_matrix = (int*) malloc(size); + HIP_API_CALL(hipMemcpy(out_matrix, out, size, hipMemcpyDeviceToHost)); + check_hip_error(); + + // cpu_transpose(matrix, out_matrix, M, N); + verify(matrix, out_matrix, M, N); + + HIP_API_CALL(hipFree(in)); + HIP_API_CALL(hipFree(out)); + check_hip_error(); + + free(matrix); + free(out_matrix); +} + +int +main(int argc, char** argv) +{ + run(argc, argv); + return 0; +} diff --git a/examples/transpose/transpose.new.cpp b/examples/transpose/transpose.new.cpp new file mode 100644 index 0000000000..786d5d34fb --- /dev/null +++ b/examples/transpose/transpose.new.cpp @@ -0,0 +1,219 @@ +/* +Copyright (c) 2015-2020 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#include "hip/hip_runtime.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void +check_hip_error(void) +{ + hipError_t err = hipGetLastError(); + if(err != hipSuccess) + { + std::cerr << "Error: " << hipGetErrorString(err) << std::endl; + exit(err); + } +} + +__global__ void +transpose_naive(int* in, int* out, int M, int N) +{ + int idx = hipBlockIdx_x * hipBlockDim_x + hipThreadIdx_x; + for(int i = idx; i < M * N; i += hipBlockDim_x * hipGridDim_x) + { + int row = i / N; + int col = i % N; + out[col * M + row] = in[row * N + col]; + } +} + +void +cpu_transpose(int* in, int* out, int M, int N) +{ + for(int i = 0; i < M; i++) + for(int j = 0; j < N; j++) + out[j * M + i] = in[i * N + j]; +} + +void +verify(int* in, int* out, int M, int N) +{ + for(int i = 0; i < 10; i++) + { + int row = rand() % M; + int col = rand() % N; + if(in[row * N + col] != out[col * M + row]) + { + std::cout << "mismatch: " << row << ", " << col << " : " << in[row * N + col] + << " | " << out[col * M + row] << "\n"; + } + } +} +const unsigned TILE_DIM = 32; +__global__ void +transpose_a(int* in, int* out, int M, int N) +{ + int iidx = (blockIdx.x * blockDim.x + threadIdx.x) * N + blockIdx.y * blockDim.y + + threadIdx.y; + int oidx = + (blockIdx.y * blockDim.y + threadIdx.y) * +blockIdx.x * blockDim.x + threadIdx.x; + + out[oidx] = in[iidx]; +} + +const int NUM_ITEM = 8; +__global__ void +transpose_e(int* A, int* B, int n1, int n2) +{ + __shared__ int Cs[64][64 + 1]; + int index; + + index = (blockIdx.y * blockDim.y * NUM_ITEM + threadIdx.y) * n1 + + blockIdx.x * blockDim.x + threadIdx.x; + + for(int i = 0; i < NUM_ITEM; ++i) + { + Cs[threadIdx.y + i * NUM_ITEM][threadIdx.x] = A[index + i * NUM_ITEM * n1]; + } + __syncthreads(); + index = (blockIdx.x * blockDim.x + threadIdx.y) * n2 + + blockIdx.y * blockDim.y * NUM_ITEM + threadIdx.x; + for(int i = 0; i < NUM_ITEM; ++i) + { + B[index + i * NUM_ITEM * n2] = Cs[threadIdx.x][threadIdx.y + i * NUM_ITEM]; + } +} + +__global__ void +transpose_s(int* out, int* in, int n1, int n2) +{ + int x = hipBlockDim_x * hipBlockIdx_x + hipThreadIdx_x; + + int val = in[x]; + + for(int i = 0; i < n1; i++) + { + for(int j = 0; j < n2; j++) + out[i * n2 + j] = __shfl(val, j * n2 + i); + } +} + +int +main(int argc, char** argv) +{ + int nx = 32; + int ny = 32; + if(argc > 1) + nx = atoi(argv[1]); + if(argc > 2) + ny = atoi(argv[2]); + + unsigned int M = 4960; + unsigned int N = 4960; + + if(argc > 3) + M = atoi(argv[3]); + if(argc > 4) + N = atoi(argv[4]); + + std::cout << "M: " << M << " N: " << N << std::endl; + size_t size = sizeof(int) * M * N; + int* matrix = (int*) malloc(size); + for(int i = 0; i < M * N; i++) + matrix[i] = rand() % 1002; + int *in, *out; + + std::chrono::high_resolution_clock::time_point t1, t2; + + hipMalloc(&in, size); + hipMalloc(&out, size); + hipMemset(in, 0, size); + hipMemset(out, 0, size); + check_hip_error(); + hipMemcpy(in, matrix, size, hipMemcpyHostToDevice); + hipDeviceSynchronize(); + check_hip_error(); + hipDeviceProp_t props; + hipGetDeviceProperties(&props, 0); + + dim3 grid(M / nx, N / ny, 1); + dim3 block(nx, ny, 1); // transpose_a + // dim3 grid(M/64, N/64, 1); dim3 block(64, 8, 1); // transpose_e + +#define TRANSPOSE_KERNEL transpose_a + + // warmup + hipLaunchKernelGGL(TRANSPOSE_KERNEL, grid, block, 0, 0, in, out, M, N); + check_hip_error(); + + t1 = std::chrono::high_resolution_clock::now(); + const unsigned times = 10000; + for(int i = 0; i < times; i++) + { + hipLaunchKernelGGL(TRANSPOSE_KERNEL, grid, block, 0, 0, in, out, M, N); + check_hip_error(); + hipDeviceSynchronize(); + check_hip_error(); + } + t2 = std::chrono::high_resolution_clock::now(); + double time = + std::chrono::duration_cast>(t2 - t1).count(); + float GB = (float) size * times * 2 / (1 << 30); + std::cout << "The average performance of transpose is " << GB / time << " GBytes/sec" + << std::endl; + + int* out_matrix = (int*) malloc(size); + hipMemcpy(out_matrix, out, size, hipMemcpyDeviceToHost); + check_hip_error(); + + // cpu_transpose(matrix, out_matrix, M, N); + verify(matrix, out_matrix, M, N); + + hipFree(in); + hipFree(out); + check_hip_error(); + + free(matrix); + free(out_matrix); + + return 0; +} + +/* +dim3 threads(256,1,1); //3D dimensions of a block of threads +dim3 blocks((N+256-1)/256,1,1); //3D dimensions the grid of blocks + +hipLaunchKernelGGL(myKernel, //Kernel name (__global__ void function) + blocks, //Grid dimensions + threads, //Block dimensions + 0, //Bytes of dynamic LDS space (see extra slides) + 0, //Stream (0=NULL stream) + N, a); //Kernel arguments +*/ diff --git a/include/hosttrace.hpp b/include/hosttrace.hpp new file mode 100644 index 0000000000..a49b75ffec --- /dev/null +++ b/include/hosttrace.hpp @@ -0,0 +1,682 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. +// + +#pragma once + +#include "timemory/backends/process.hpp" +#include "timemory/environment.hpp" +#include "timemory/mpl/apply.hpp" +#include "timemory/utility/argparse.hpp" +#include "timemory/utility/macros.hpp" +#include "timemory/utility/popen.hpp" +#include "timemory/variadic/macros.hpp" + +#include "BPatch.h" +#include "BPatch_Vector.h" +#include "BPatch_addressSpace.h" +#include "BPatch_basicBlockLoop.h" +#include "BPatch_callbacks.h" +#include "BPatch_function.h" +#include "BPatch_point.h" +#include "BPatch_process.h" +#include "BPatch_snippet.h" +#include "BPatch_statement.h" + +#include +#include +#include +#include +#include +#include +#include +// +#include +#include +#include +#include +#include + +#define MUTNAMELEN 1024 +#define FUNCNAMELEN 32 * 1024 +#define NO_ERROR -1 +#define TIMEMORY_BIN_DIR "bin" + +#if !defined(PATH_MAX) +# define PATH_MAX std::numeric_limits::max(); +#endif + +struct function_signature; +struct module_function; + +template +using bpvector_t = BPatch_Vector; + +using string_t = std::string; +using stringstream_t = std::stringstream; +using strvec_t = std::vector; +using strset_t = std::set; +using regexvec_t = std::vector; +using fmodset_t = std::set; +using exec_callback_t = BPatchExecCallback; +using exit_callback_t = BPatchExitCallback; +using fork_callback_t = BPatchForkCallback; +using patch_t = BPatch; +using process_t = BPatch_process; +using thread_t = BPatch_thread; +using binary_edit_t = BPatch_binaryEdit; +using image_t = BPatch_image; +using module_t = BPatch_module; +using procedure_t = BPatch_function; +using snippet_t = BPatch_snippet; +using call_expr_t = BPatch_funcCallExpr; +using address_space_t = BPatch_addressSpace; +using flow_graph_t = BPatch_flowGraph; +using basic_loop_t = BPatch_basicBlockLoop; +using procedure_loc_t = BPatch_procedureLocation; +using point_t = BPatch_point; +using local_var_t = BPatch_localVar; +using const_expr_t = BPatch_constExpr; +using error_level_t = BPatchErrorLevel; +using patch_pointer_t = std::shared_ptr; +using snippet_pointer_t = std::shared_ptr; +using call_expr_pointer_t = std::shared_ptr; +using snippet_vec_t = bpvector_t; +using procedure_vec_t = bpvector_t; +using basic_loop_vec_t = bpvector_t; +using snippet_pointer_vec_t = std::vector; + +void +hosttrace_prefork_callback(thread_t* parent, thread_t* child); + +//======================================================================================// +// +// Global Variables +// +//======================================================================================// +// +// boolean settings +// +static bool binary_rewrite = 0; +static bool loop_level_instr = false; +static bool werror = false; +static bool stl_func_instr = false; +static bool cstd_func_instr = false; +static bool use_mpi = false; +static bool is_static_exe = false; +static bool use_return_info = false; +static bool use_args_info = false; +static bool use_file_info = false; +static bool use_line_info = false; +// +// integral settings +// +static bool debug_print = false; +static int expect_error = NO_ERROR; +static int error_print = 0; +static int verbose_level = tim::get_env("TIMEMORY_RUN_VERBOSE", 0); +// +// string settings +// +static string_t main_fname = "main"; +static string_t argv0 = ""; +static string_t cmdv0 = ""; +static string_t default_components = "wall_clock"; +static string_t prefer_library = ""; +// +// global variables +// +static patch_pointer_t bpatch; +static call_expr_t* initialize_expr = nullptr; +static call_expr_t* terminate_expr = nullptr; +static snippet_vec_t init_names; +static snippet_vec_t fini_names; +static fmodset_t available_module_functions; +static fmodset_t instrumented_module_functions; +static regexvec_t func_include; +static regexvec_t func_exclude; +static regexvec_t file_include; +static regexvec_t file_exclude; +static auto regex_opts = std::regex_constants::egrep | std::regex_constants::optimize; +// +//======================================================================================// + +// control debug printf statements +#define dprintf(...) \ + if(debug_print || verbose_level > 0) \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); + +// control verbose printf statements +#define verbprintf(LEVEL, ...) \ + if(verbose_level >= LEVEL) \ + fprintf(stdout, __VA_ARGS__); \ + fflush(stdout); + +//======================================================================================// + +template +void +consume_parameters(T&&...) +{} + +//======================================================================================// + +extern "C" +{ + bool are_file_include_exclude_lists_empty(); + bool process_file_for_instrumentation(const string_t& file_name); + bool instrument_entity(const string_t& function_name); + bool module_constraint(char* fname); + bool routine_constraint(const char* fname); +} + +//======================================================================================// + +function_signature +get_func_file_line_info(module_t* mutatee_module, procedure_t* f); + +function_signature +get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* cfGraph, + basic_loop_t* loopToInstrument); + +template +void +insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc, + procedure_loc_t traceLoc, flow_graph_t* cfGraph = nullptr, + basic_loop_t* loopToInstrument = nullptr); + +void +errorFunc(error_level_t level, int num, const char** params); + +procedure_t* +find_function(image_t* appImage, const string_t& functionName, strset_t = {}); + +void +error_func_real(error_level_t level, int num, const char* const* params); + +void +error_func_fake(error_level_t level, int num, const char* const* params); + +bool +find_func_or_calls(std::vector names, bpvector_t& points, + image_t* appImage, procedure_loc_t loc = BPatch_locEntry); + +bool +find_func_or_calls(const char* name, bpvector_t& points, image_t* image, + procedure_loc_t loc = BPatch_locEntry); + +bool +load_dependent_libraries(address_space_t* bedit, char* bindings); + +bool +c_stdlib_module_constraint(const string_t& file); + +bool +c_stdlib_function_constraint(const string_t& func); + +//======================================================================================// + +inline string_t +get_absolute_path(const char* fname) +{ + char path_save[PATH_MAX]; + char abs_exe_path[PATH_MAX]; + char* p = nullptr; + + if(!(p = strrchr((char*) fname, '/'))) + { + auto ret = getcwd(abs_exe_path, sizeof(abs_exe_path)); + consume_parameters(ret); + } + else + { + auto rets = getcwd(path_save, sizeof(path_save)); + auto retf = chdir(fname); + auto reta = getcwd(abs_exe_path, sizeof(abs_exe_path)); + auto retp = chdir(path_save); + consume_parameters(rets, retf, reta, retp); + } + return string_t(abs_exe_path); +} + +//======================================================================================// + +inline string_t +to_lower(string_t s) +{ + for(auto& itr : s) + itr = tolower(itr); + return s; +} +// +//======================================================================================// +// +struct function_signature +{ + using location_t = std::pair; + + bool m_loop = false; + bool m_info_beg = false; + bool m_info_end = false; + location_t m_row = { 0, 0 }; + location_t m_col = { 0, 0 }; + string_t m_return = "void"; + string_t m_name = ""; + string_t m_params = "()"; + string_t m_file = ""; + mutable string_t m_signature = ""; + + TIMEMORY_DEFAULT_OBJECT(function_signature) + + function_signature(string_t _ret, string_t _name, string_t _file, + location_t _row = { 0, 0 }, location_t _col = { 0, 0 }, + bool _loop = false, bool _info_beg = false, bool _info_end = false) + : m_loop(_loop) + , m_info_beg(_info_beg) + , m_info_end(_info_end) + , m_row(_row) + , m_col(_col) + , m_return(_ret) + , m_name(tim::demangle(_name)) + , m_file(_file) + { + if(m_file.find('/') != string_t::npos) + m_file = m_file.substr(m_file.find_last_of('/') + 1); + } + + function_signature(string_t _ret, string_t _name, string_t _file, + std::vector _params, location_t _row = { 0, 0 }, + location_t _col = { 0, 0 }, bool _loop = false, + bool _info_beg = false, bool _info_end = false) + : function_signature(_ret, _name, _file, _row, _col, _loop, _info_beg, _info_end) + { + std::stringstream ss; + ss << "("; + for(auto& itr : _params) + ss << itr << ", "; + m_params = ss.str(); + m_params = m_params.substr(0, m_params.length() - 2); + m_params += ")"; + } + + static auto get(function_signature& sig) { return sig.get(); } + + string_t get() const + { + std::stringstream ss; + if(use_return_info) + ss << m_return << " "; + ss << m_name; + if(use_args_info) + ss << m_params; + if(m_loop && m_info_beg) + { + if(m_info_end) + { + ss << '/' << "[{" << m_row.first << "," << m_col.first << "}-{" + << m_row.second << "," << m_col.second << "}]"; + } + else + { + ss << "[{" << m_row.first << "," << m_col.first << "}]"; + } + } + else + { + if(use_file_info && m_file.length() > 0) + ss << '/' << m_file; + if(use_line_info && m_row.first > 0) + ss << ":" << m_row.first; + } + + m_signature = ss.str(); + return m_signature; + } +}; +// +//======================================================================================// +// +struct module_function +{ + using width_t = std::array; + + static auto& get_width() + { + static width_t _instance = []() { + width_t _tmp; + _tmp.fill(0); + return _tmp; + }(); + return _instance; + } + + static void reset_width() { get_width().fill(0); } + + static void update_width(const module_function& rhs) + { + get_width()[0] = std::max(get_width()[0], rhs.module.length()); + get_width()[1] = std::max(get_width()[1], rhs.function.length()); + get_width()[2] = std::max(get_width()[2], rhs.signature.get().length()); + } + + module_function(const string_t& _module, const string_t& _func, + const function_signature& _sign) + : module(_module) + , function(_func) + , signature(_sign) + {} + + module_function(module_t* mod, procedure_t* proc) + { + char modname[FUNCNAMELEN]; + char fname[FUNCNAMELEN]; + + mod->getName(modname, FUNCNAMELEN); + proc->getName(fname, FUNCNAMELEN); + + module = modname; + function = fname; + signature = get_func_file_line_info(mod, proc); + } + + friend bool operator<(const module_function& lhs, const module_function& rhs) + { + return (lhs.module == rhs.module) + ? ((lhs.function == rhs.function) + ? (lhs.signature.get() < rhs.signature.get()) + : (lhs.function < rhs.function)) + : (lhs.module < rhs.module); + } + + friend std::ostream& operator<<(std::ostream& os, const module_function& rhs) + { + std::stringstream ss; + + static size_t absolute_max = 80; + auto w0 = std::min(get_width()[0], absolute_max); + auto w1 = std::min(get_width()[1], absolute_max); + auto w2 = std::min(get_width()[2], absolute_max); + + auto _get_str = [](const std::string& _inc) { + if(_inc.length() > absolute_max) + return _inc.substr(0, absolute_max - 3) + "..."; + return _inc; + }; + + ss << std::setw(w0 + 8) << std::left << _get_str(rhs.module) << " " + << std::setw(w1 + 8) << std::left << _get_str(rhs.function) << " " + << std::setw(w2 + 8) << std::left << _get_str(rhs.signature.get()); + os << ss.str(); + return os; + } + + string_t module = ""; + string_t function = ""; + function_signature signature; +}; +// +//======================================================================================// +// +static inline void +dump_info(const string_t& _oname, const fmodset_t& _data, int level) +{ + if(!debug_print && verbose_level < level) + return; + + module_function::reset_width(); + for(const auto& itr : _data) + module_function::update_width(itr); + + std::ofstream ofs(_oname); + if(ofs) + { + verbprintf(level, "Dumping '%s'... ", _oname.c_str()); + for(const auto& itr : _data) + ofs << itr << '\n'; + verbprintf(level, "Done\n"); + } + ofs.close(); + + module_function::reset_width(); +} +// +//======================================================================================// +// +template +snippet_pointer_t +get_snippet(Tp arg) +{ + return snippet_pointer_t(new const_expr_t(arg)); +} +// +//======================================================================================// +// +inline snippet_pointer_t +get_snippet(string_t arg) +{ + return snippet_pointer_t(new const_expr_t(arg.c_str())); +} +// +//======================================================================================// +// +template +snippet_pointer_vec_t +get_snippets(Args&&... args) +{ + snippet_pointer_vec_t _tmp; + TIMEMORY_FOLD_EXPRESSION(_tmp.push_back(get_snippet(std::forward(args)))); + return _tmp; +} +// +//======================================================================================// +// +struct hosttrace_call_expr +{ + using snippet_pointer_t = std::shared_ptr; + + template + hosttrace_call_expr(Args&&... args) + : m_params(get_snippets(std::forward(args)...)) + {} + + snippet_vec_t get_params() + { + snippet_vec_t _ret; + for(auto& itr : m_params) + _ret.push_back(itr.get()); + return _ret; + } + + inline call_expr_pointer_t get(procedure_t* func) + { + return call_expr_pointer_t((func) ? new call_expr_t(*func, get_params()) + : nullptr); + } + +private: + snippet_pointer_vec_t m_params; +}; +// +//======================================================================================// +// +struct hosttrace_snippet_vec +{ + using entry_type = std::vector; + using value_type = std::vector; + + template + void generate(procedure_t* func, Args&&... args) + { + auto _expr = hosttrace_call_expr(std::forward(args)...); + auto _call = _expr.get(func); + if(_call) + { + m_entries.push_back(_expr); + m_data.push_back(_call); + // m_data.push_back(entry_type{ _call, _expr }); + } + } + + void append(snippet_vec_t& _obj) + { + for(auto& itr : m_data) + _obj.push_back(itr.get()); + } + +private: + entry_type m_entries; + value_type m_data; +}; +// +//======================================================================================// +// +static inline address_space_t* +hosttrace_get_address_space(patch_pointer_t _bpatch, int _cmdc, char** _cmdv, + bool _rewrite, int _pid = -1, string_t _name = "") +{ + address_space_t* mutatee = nullptr; + + if(_rewrite) + { + verbprintf(1, "Opening '%s' for binary rewrite... ", _name.c_str()); + fflush(stderr); + if(!_name.empty()) + mutatee = _bpatch->openBinary(_name.c_str(), false); + if(!mutatee) + { + fprintf(stderr, "[hosttrace]> Failed to open binary '%s'\n", _name.c_str()); + throw std::runtime_error("Failed to open binary"); + } + verbprintf(1, "Done\n"); + } + else if(_pid >= 0) + { + verbprintf(1, "Attaching to process %i... ", _pid); + fflush(stderr); + char* _cmdv0 = (_cmdc > 0) ? _cmdv[0] : nullptr; + mutatee = _bpatch->processAttach(_cmdv0, _pid); + if(!mutatee) + { + fprintf(stderr, "[hosttrace]> Failed to connect to process %i\n", (int) _pid); + throw std::runtime_error("Failed to attach to process"); + } + verbprintf(1, "Done\n"); + } + else + { + verbprintf(1, "Creating process '%s'... ", _cmdv[0]); + fflush(stderr); + mutatee = _bpatch->processCreate(_cmdv[0], (const char**) _cmdv, nullptr); + if(!mutatee) + { + std::stringstream ss; + for(int i = 0; i < _cmdc; ++i) + { + if(!_cmdv[i]) + continue; + ss << _cmdv[i] << " "; + } + fprintf(stderr, "[hosttrace]> Failed to create process: '%s'\n", + ss.str().c_str()); + throw std::runtime_error("Failed to create process"); + } + verbprintf(1, "Done\n"); + } + + return mutatee; +} +// +//======================================================================================// +// +TIMEMORY_NOINLINE inline void +hosttrace_thread_exit(thread_t* thread, BPatch_exitType exit_type) +{ + if(!thread) + return; + + BPatch_process* app = thread->getProcess(); + + if(!terminate_expr) + { + fprintf(stderr, "[hosttrace]> continuing execution\n"); + app->continueExecution(); + return; + } + + switch(exit_type) + { + case ExitedNormally: { + fprintf(stderr, "[hosttrace]> Thread exited normally\n"); + break; + } + case ExitedViaSignal: { + fprintf(stderr, "[hosttrace]> Thread terminated unexpectedly\n"); + break; + } + case NoExit: + default: { + fprintf(stderr, "[hosttrace]> %s invoked with NoExit\n", __FUNCTION__); + break; + } + } + + // terminate_expr = nullptr; + thread->oneTimeCode(*terminate_expr); + + fprintf(stderr, "[hosttrace]> continuing execution\n"); + app->continueExecution(); +} +// +//======================================================================================// +// +TIMEMORY_NOINLINE inline void +hosttrace_fork_callback(thread_t* parent, thread_t* child) +{ + if(child) + { + auto* app = child->getProcess(); + if(app) + { + verbprintf(4, "Stopping execution and detaching child fork...\n"); + app->stopExecution(); + app->detach(true); + // app->terminateExecution(); + // app->continueExecution(); + } + } + + if(parent) + { + auto app = parent->getProcess(); + if(app) + { + verbprintf(4, "Continuing execution on parent after fork callback...\n"); + app->continueExecution(); + } + } +} +// +//======================================================================================// +// diff --git a/include/timemory/.clang-format b/include/timemory/.clang-format new file mode 100644 index 0000000000..0af3d6d017 --- /dev/null +++ b/include/timemory/.clang-format @@ -0,0 +1,65 @@ +# requires clang-tidy version 6.0+ +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: TopLevel +AlwaysBreakAfterReturnType: TopLevel +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BasedOnStyle: Mozilla +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +ColumnLimit: 90 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 0 +ContinuationIndentWidth: 4 +FixNamespaceComments: true +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +PointerAlignment: Left +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/include/timemory/api.hpp b/include/timemory/api.hpp new file mode 100644 index 0000000000..3900e5d33e --- /dev/null +++ b/include/timemory/api.hpp @@ -0,0 +1,221 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/api/macros.hpp" +#include "timemory/macros/os.hpp" +#include "timemory/mpl/concepts.hpp" + +#include + +// +// General APIs +// +TIMEMORY_DEFINE_NS_API(project, none) // dummy type +TIMEMORY_DEFINE_NS_API(project, timemory) // provided by timemory API exclusively +TIMEMORY_DEFINE_NS_API(project, python) // provided by timemory python interface +TIMEMORY_DEFINE_NS_API(project, kokkosp) // kokkos profiling API +// +// Device APIs +// +TIMEMORY_DECLARE_NS_API(device, cpu) // collects data on CPU +TIMEMORY_DECLARE_NS_API(device, gpu) // collects data on GPU +// +// Category APIs +// +TIMEMORY_DEFINE_NS_API(category, debugger) // provided debugging utilities +TIMEMORY_DEFINE_NS_API(category, decorator) // decorates external profiler +TIMEMORY_DEFINE_NS_API(category, external) // relies on external package +TIMEMORY_DEFINE_NS_API(category, io) // collects I/O data +TIMEMORY_DEFINE_NS_API(category, logger) // logs generic data or messages +TIMEMORY_DEFINE_NS_API(category, hardware_counter) // collects HW counter data +TIMEMORY_DEFINE_NS_API(category, memory) // collects memory data +TIMEMORY_DEFINE_NS_API(category, resource_usage) // collects resource usage data +TIMEMORY_DEFINE_NS_API(category, timing) // collects timing data +TIMEMORY_DEFINE_NS_API(category, visualization) // related to viz (currently unused) +// +// External Third-party library APIs +// +TIMEMORY_DEFINE_NS_API(tpls, allinea) +TIMEMORY_DEFINE_NS_API(tpls, caliper) +TIMEMORY_DEFINE_NS_API(tpls, craypat) +TIMEMORY_DEFINE_NS_API(tpls, gotcha) +TIMEMORY_DEFINE_NS_API(tpls, gperftools) +TIMEMORY_DEFINE_NS_API(tpls, intel) +TIMEMORY_DEFINE_NS_API(tpls, likwid) +TIMEMORY_DEFINE_NS_API(tpls, nvidia) +TIMEMORY_DEFINE_NS_API(tpls, openmp) +TIMEMORY_DEFINE_NS_API(tpls, rocm) +TIMEMORY_DEFINE_NS_API(tpls, papi) +TIMEMORY_DEFINE_NS_API(tpls, tau) +// +// OS-specific APIs +// +TIMEMORY_DEFINE_NS_API(os, agnostic) +TIMEMORY_DEFINE_NS_API(os, supports_unix) +TIMEMORY_DEFINE_NS_API(os, supports_linux) +TIMEMORY_DEFINE_NS_API(os, supports_darwin) +TIMEMORY_DEFINE_NS_API(os, supports_windows) +// +//--------------------------------------------------------------------------------------// +// +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, project::timemory, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, project::python, true_type) +// +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::debugger, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::decorator, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::external, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::io, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::logger, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::hardware_counter, + true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::resource_usage, + true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::timing, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, category::visualization, + true_type) +// +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::allinea, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::caliper, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::craypat, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::gotcha, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::gperftools, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::intel, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::likwid, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::nvidia, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::openmp, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::papi, true_type) +TIMEMORY_DEFINE_CONCRETE_CONCEPT(is_runtime_configurable, tpls::tau, true_type) +// +namespace tim +{ +namespace api +{ +using native_tag = project::timemory; +} +// +namespace trait +{ +// +#if !defined(TIMEMORY_UNIX) +template <> +struct is_available : false_type +{}; +#endif +// +#if !defined(TIMEMORY_LINUX) +template <> +struct is_available : false_type +{}; +#endif +// +#if !defined(TIMEMORY_MACOS) +template <> +struct is_available : false_type +{}; +#endif +// +#if !defined(TIMEMORY_WINDOWS) +template <> +struct is_available : false_type +{}; +#endif +// +} // namespace trait +// +} // namespace tim +// +//--------------------------------------------------------------------------------------// +// +namespace tim +{ +namespace cereal +{ +class JSONInputArchive; +class XMLInputArchive; +class XMLOutputArchive; +} // namespace cereal +} // namespace tim +// +//--------------------------------------------------------------------------------------// +// +// Default pre-processor settings +// +//--------------------------------------------------------------------------------------// +// + +#if !defined(TIMEMORY_DEFAULT_API) +# define TIMEMORY_DEFAULT_API ::tim::project::timemory +#endif + +#if !defined(TIMEMORY_API) +# define TIMEMORY_API TIMEMORY_DEFAULT_API +#endif + +#if defined(DISABLE_TIMEMORY) || defined(TIMEMORY_DISABLED) +# if !defined(TIMEMORY_DEFAULT_AVAILABLE) +# define TIMEMORY_DEFAULT_AVAILABLE false_type +# endif +#else +# if !defined(TIMEMORY_DEFAULT_AVAILABLE) +# define TIMEMORY_DEFAULT_AVAILABLE true_type +# endif +#endif + +#if !defined(TIMEMORY_DEFAULT_STATISTICS_TYPE) +# if defined(TIMEMORY_USE_STATISTICS) +# define TIMEMORY_DEFAULT_STATISTICS_TYPE true_type +# else +# define TIMEMORY_DEFAULT_STATISTICS_TYPE false_type +# endif +#endif + +#if !defined(TIMEMORY_DEFAULT_PLOTTING) +# define TIMEMORY_DEFAULT_PLOTTING false +#endif + +#if !defined(TIMEMORY_DEFAULT_ENABLED) +# define TIMEMORY_DEFAULT_ENABLED true +#endif + +#if !defined(TIMEMORY_PYTHON_PLOTTER) +# define TIMEMORY_PYTHON_PLOTTER "python" +#endif + +#if !defined(TIMEMORY_DEFAULT_INPUT_ARCHIVE) +# define TIMEMORY_DEFAULT_INPUT_ARCHIVE cereal::JSONInputArchive +#endif + +#if !defined(TIMEMORY_DEFAULT_OUTPUT_ARCHIVE) +# define TIMEMORY_DEFAULT_OUTPUT_ARCHIVE ::tim::type_list<> +#endif + +#if !defined(TIMEMORY_INPUT_ARCHIVE) +# define TIMEMORY_INPUT_ARCHIVE TIMEMORY_DEFAULT_INPUT_ARCHIVE +#endif + +#if !defined(TIMEMORY_OUTPUT_ARCHIVE) +# define TIMEMORY_OUTPUT_ARCHIVE TIMEMORY_DEFAULT_OUTPUT_ARCHIVE +#endif diff --git a/include/timemory/api/macros.hpp b/include/timemory/api/macros.hpp new file mode 100644 index 0000000000..caf6f95c6c --- /dev/null +++ b/include/timemory/api/macros.hpp @@ -0,0 +1,76 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/mpl/concepts.hpp" + +/// \macro TIMEMORY_DECLARE_NS_API(NS, NAME) +/// \brief Declare an API category. APIs are used to designate +/// different project implementations, different external library tools, etc. +/// +#if !defined(TIMEMORY_DECLARE_NS_API) +# define TIMEMORY_DECLARE_NS_API(NS, NAME) \ + namespace tim \ + { \ + namespace NS \ + { \ + struct NAME; \ + } \ + } +#endif + +#if !defined(TIMEMORY_DECLARE_API) +# define TIMEMORY_DECLARE_API(NAME) TIMEMORY_DECLARE_NS_API(api, NAME) +#endif + +// +/// \macro TIMEMORY_DEFINE_NS_API(NS, NAME) +/// \param NS sub-namespace within tim:: +/// \param NAME the name of the API +/// +/// \brief Define an API category within a namespace +/// +#if !defined(TIMEMORY_DEFINE_NS_API) +# define TIMEMORY_DEFINE_NS_API(NS, NAME) \ + namespace tim \ + { \ + namespace NS \ + { \ + struct NAME : public concepts::api \ + {}; \ + } \ + } +#endif + +/// +/// \macro TIMEMORY_DEFINE_API +/// \brief Define an API category. APIs are used to designate +/// different project implementations, different external library tools, etc. +/// Note: this macro inherits from \ref concepts::api instead of specializing +/// is_api<...>, thus allowing specialization from tools downstream +/// +#if !defined(TIMEMORY_DEFINE_API) +# define TIMEMORY_DEFINE_API(NAME) TIMEMORY_DEFINE_NS_API(api, NAME) +#endif diff --git a/include/timemory/backends/process.hpp b/include/timemory/backends/process.hpp new file mode 100644 index 0000000000..d4fd0ecf75 --- /dev/null +++ b/include/timemory/backends/process.hpp @@ -0,0 +1,110 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +/** \file backends/process.hpp + * \headerfile backends/process.hpp "timemory/backends/process.hpp" + * Defines process backend functions + * + */ + +#pragma once + +#include "timemory/macros/os.hpp" +#include "timemory/utility/macros.hpp" + +#include +#include + +#if defined(TIMEMORY_UNIX) +# include +# include +# if defined(TIMEMORY_MACOS) +# include +# include +# endif +#elif defined(TIMEMORY_WINDOWS) +# if !defined(NOMINMAX) +# define NOMINMAX +# endif +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif + +namespace tim +{ +namespace process +{ +// +//--------------------------------------------------------------------------------------// +// +#if defined(TIMEMORY_UNIX) +using id_t = pid_t; +#elif defined(TIMEMORY_WINDOWS) +using id_t = DWORD; +#else +using id_t = int; +#endif +// +//--------------------------------------------------------------------------------------// +// +/// \fn pid_t tim::process::get_id() +/// \brief get the process id of this process +/// +inline id_t +get_id() +{ +#if defined(TIMEMORY_WINDOWS) + static auto instance = GetCurrentProcessId(); +#elif defined(TIMEMORY_UNIX) + static auto instance = getpid(); +#else + static auto instance = 0; +#endif + return instance; +} +// +//--------------------------------------------------------------------------------------// +// +/// \fn pid_t& tim::process::get_target_id() +/// \brief get the target process id of this process (may differ from process::get_id) +/// +inline id_t& +get_target_id() +{ +#if defined(TIMEMORY_WINDOWS) + static auto instance = GetCurrentProcessId(); +#elif defined(TIMEMORY_UNIX) + static auto instance = getpid(); +#else + static auto instance = 0; +#endif + return instance; +} +// +//--------------------------------------------------------------------------------------// +// +} // namespace process +} // namespace tim diff --git a/include/timemory/compat/macros.h b/include/timemory/compat/macros.h new file mode 100644 index 0000000000..c14a3e2a5f --- /dev/null +++ b/include/timemory/compat/macros.h @@ -0,0 +1,289 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#define _TIM_STRINGIZE(X) _TIM_STRINGIZE2(X) +#define _TIM_STRINGIZE2(X) #X +#define _TIM_VAR_NAME_COMBINE(X, Y) X##Y +#define _TIM_VARIABLE(Y) _TIM_VAR_NAME_COMBINE(timemory_variable_, Y) +#define _TIM_SCOPE_DTOR(Y) _TIM_VAR_NAME_COMBINE(timemory_scoped_dtor_, Y) +#define _TIM_TYPEDEF(Y) _TIM_VAR_NAME_COMBINE(timemory_typedef_, Y) +#define _TIM_STORAGE_INIT(Y) _TIM_VAR_NAME_COMBINE(timemory_storage_initializer_, Y) + +#define _TIM_LINESTR _TIM_STRINGIZE(__LINE__) + +#if defined(TIMEMORY_PRETTY_FUNCTION) && !defined(_WINDOWS) +# define _TIM_FUNC __PRETTY_FUNCTION__ +#elif defined(TIMEMORY_PRETTY_FUNCTION) && defined(_WINDOWS) +# define _TIM_FUNC __FUNCSIG__ +#else +# define _TIM_FUNC __FUNCTION__ +#endif + +#if defined(DISABLE_TIMEMORY) || defined(TIMEMORY_DISABLED) +# if !defined(TIMEMORY_SPRINTF) +# define TIMEMORY_SPRINTF(...) +# endif +#else +# if !defined(TIMEMORY_SPRINTF) +# define TIMEMORY_SPRINTF(VAR, LEN, FMT, ...) \ + char VAR[LEN]; \ + snprintf(VAR, LEN, FMT, __VA_ARGS__); +# endif +#endif + +#if !defined(TIMEMORY_CODE) +# if defined(DISABLE_TIMEMORY) || defined(TIMEMORY_DISABLED) +# define TIMEMORY_CODE(...) +# else +# define TIMEMORY_CODE(...) __VA_ARGS__ +# endif +#endif + +//======================================================================================// +// +// Operating System +// +//======================================================================================// + +#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) +# if !defined(_WINDOWS) +# define _WINDOWS 1 +# endif +#elif defined(__APPLE__) || defined(__MACH__) +# if !defined(_MACOS) +# define _MACOS 1 +# endif +# if !defined(_UNIX) +# define _UNIX 1 +# endif +#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) +# if !defined(_LINUX) +# define _LINUX 1 +# endif +# if !defined(_UNIX) +# define _UNIX 1 +# endif +#elif defined(__unix__) || defined(__unix) || defined(unix) +# if !defined(_UNIX) +# define _UNIX 1 +# endif +#endif + +#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) +# if !defined(TIMEMORY_WINDOWS) +# define TIMEMORY_WINDOWS 1 +# endif +#elif defined(__APPLE__) || defined(__MACH__) +# if !defined(TIMEMORY_MACOS) +# define TIMEMORY_MACOS 1 +# endif +# if !defined(TIMEMORY_UNIX) +# define TIMEMORY_UNIX 1 +# endif +#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) +# if !defined(TIMEMORY_LINUX) +# define TIMEMORY_LINUX 1 +# endif +# if !defined(TIMEMORY_UNIX) +# define TIMEMORY_UNIX 1 +# endif +#elif defined(__unix__) || defined(__unix) || defined(unix) +# if !defined(TIMEMORY_UNIX) +# define TIMEMORY_UNIX 1 +# endif +#endif + +//======================================================================================// +// +// General attribute +// +//======================================================================================// + +#if !defined(TIMEMORY_ATTRIBUTE) +# if !defined(_MSC_VER) +# define TIMEMORY_ATTRIBUTE(attr) __attribute__((attr)) +# else +# define TIMEMORY_ATTRIBUTE(attr) __declspec(attr) +# endif +#endif + +//======================================================================================// +// +// Windows DLL settings +// +//======================================================================================// + +#if !defined(TIMEMORY_CDLL) +# if defined(_WINDOWS) +# if defined(TIMEMORY_CDLL_EXPORT) +# define TIMEMORY_CDLL __declspec(dllexport) +# elif defined(TIMEMORY_CDLL_IMPORT) +# define TIMEMORY_CDLL __declspec(dllimport) +# else +# define TIMEMORY_CDLL +# endif +# else +# define TIMEMORY_CDLL +# endif +#endif + +#if !defined(TIMEMORY_DLL) +# if defined(_WINDOWS) +# if defined(TIMEMORY_DLL_EXPORT) +# define TIMEMORY_DLL __declspec(dllexport) +# elif defined(TIMEMORY_DLL_IMPORT) +# define TIMEMORY_DLL __declspec(dllimport) +# else +# define TIMEMORY_DLL +# endif +# else +# define TIMEMORY_DLL +# endif +#endif + +//======================================================================================// +// +// Visibility +// +//======================================================================================// + +#if !defined(TIMEMORY_VISIBILITY) +# if !defined(_MSC_VER) +# define TIMEMORY_VISIBILITY(mode) TIMEMORY_ATTRIBUTE(visibility(mode)) +# else +# define TIMEMORY_VISIBILITY(mode) +# endif +#endif + +#if !defined(TIMEMORY_VISIBLE) +# define TIMEMORY_VISIBLE TIMEMORY_VISIBILITY("default") +#endif + +#if !defined(TIMEMORY_HIDDEN) +# define TIMEMORY_HIDDEN TIMEMORY_VISIBILITY("hidden") +#endif + +//======================================================================================// +// +// Instrumentation +// +//======================================================================================// + +#if !defined(_WINDOWS) +# if !defined(TIMEMORY_NEVER_INSTRUMENT) +# if defined(__clang__) +# define TIMEMORY_NEVER_INSTRUMENT \ + TIMEMORY_ATTRIBUTE(no_instrument_function) \ + TIMEMORY_ATTRIBUTE(xray_never_instrument) +# else +# define TIMEMORY_NEVER_INSTRUMENT TIMEMORY_ATTRIBUTE(no_instrument_function) +# endif +# endif +// +# if !defined(TIMEMORY_INSTRUMENT) +# define TIMEMORY_INSTRUMENT TIMEMORY_ATTRIBUTE(xray_always_instrument) +# endif +#else +# if !defined(TIMEMORY_NEVER_INSTRUMENT) +# define TIMEMORY_NEVER_INSTRUMENT +# endif +// +# if !defined(TIMEMORY_INSTRUMENT) +# define TIMEMORY_INSTRUMENT +# endif +#endif + +//======================================================================================// +// +// Symbol override +// +//======================================================================================// + +#if !defined(TIMEMORY_WEAK_PREFIX) +# if !defined(_MSC_VER) +# if defined(__clang__) && defined(__APPLE__) +# define TIMEMORY_WEAK_PREFIX +# else +# define TIMEMORY_WEAK_PREFIX TIMEMORY_ATTRIBUTE(weak) +# endif +# else +# define TIMEMORY_WEAK_PREFIX +# endif +#endif + +#if !defined(TIMEMORY_WEAK_POSTFIX) +# if !defined(_MSC_VER) +# if defined(__clang__) && defined(__APPLE__) +# define TIMEMORY_WEAK_POSTFIX TIMEMORY_ATTRIBUTE(weak_import) +# else +# define TIMEMORY_WEAK_POSTFIX +# endif +# else +# define TIMEMORY_WEAK_POSTFIX +# endif +#endif + +//======================================================================================// +// +// Library Constructor/Destructor +// +//======================================================================================// + +#if !defined(TIMEMORY_CTOR) +# if !defined(_WINDOWS) +# define TIMEMORY_CTOR TIMEMORY_ATTRIBUTE(constructor) +# else +# define TIMEMORY_CTOR +# endif +#endif +// +//--------------------------------------------------------------------------------------// +// +#if !defined(TIMEMORY_DTOR) +# if !defined(_WINDOWS) +# define TIMEMORY_DTOR TIMEMORY_ATTRIBUTE(destructor) +# else +# define TIMEMORY_DTOR +# endif +#endif + +//======================================================================================// +// +// WINDOWS WARNINGS (apply to C code) +// +//======================================================================================// + +// MSVC compiler +#if defined(_MSC_VER) && _MSC_VER > 0 && !defined(TIMEMORY_MSVC_COMPILER) +# define TIMEMORY_MSVC_COMPILER +#endif + +#if defined(TIMEMORY_MSVC_COMPILER) && !defined(TIMEMORY_MSVC_WARNINGS) + +# pragma warning(disable : 4996) // function may be unsafe +# pragma warning(disable : 5105) // macro produce 'defined' has undefined behavior + +#endif diff --git a/include/timemory/environment.hpp b/include/timemory/environment.hpp new file mode 100644 index 0000000000..45d2157578 --- /dev/null +++ b/include/timemory/environment.hpp @@ -0,0 +1,33 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/environment/declaration.hpp" +#include "timemory/environment/macros.hpp" +#include "timemory/environment/types.hpp" + +#if !defined(TIMEMORY_USE_ENVIRONMENT_EXTERN) +# include "timemory/environment/definition.hpp" +#endif diff --git a/include/timemory/environment/declaration.hpp b/include/timemory/environment/declaration.hpp new file mode 100644 index 0000000000..11f854e048 --- /dev/null +++ b/include/timemory/environment/declaration.hpp @@ -0,0 +1,268 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/environment/macros.hpp" +#include "timemory/environment/types.hpp" +#include "timemory/macros/compiler.hpp" +#include "timemory/macros/os.hpp" +#include "timemory/tpls/cereal/cereal.hpp" +#include "timemory/utility/macros.hpp" +#include "timemory/utility/utility.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace tim +{ +// +//--------------------------------------------------------------------------------------// +// +// environment +// +//--------------------------------------------------------------------------------------// +// +class env_settings +{ +public: + using string_t = std::string; + using env_map_t = std::map; + using env_uomap_t = std::map; + using env_pair_t = std::pair; + using iterator = typename env_map_t::iterator; + using const_iterator = typename env_map_t::const_iterator; + +public: + static env_settings* instance(); + +private: + // if passed a global, insert m_env into it's array + env_settings(env_settings* _global = nullptr, int _id = 0); + + // only the zero id deletes + ~env_settings(); + +public: + template + void insert(const std::string& env_id, Tp val); + + env_map_t get() const; + iterator get(const string_t& _entry) { return m_env->find(_entry); } + const_iterator get(const string_t& _entry) const { return m_env->find(_entry); } + iterator begin() { return m_env->begin(); } + iterator end() { return m_env->end(); } + const_iterator begin() const { return m_env->begin(); } + const_iterator end() const { return m_env->end(); } + + void print(std::ostream&) const; + friend std::ostream& operator<<(std::ostream& os, const env_settings& env) + { + env.print(os); + return os; + } + + //----------------------------------------------------------------------------------// + // serialization + // + template + void serialize(Archive& ar, unsigned int); + + template + static void serialize_environment(Archive& ar) + { + if(instance()) + instance()->serialize(ar, TIMEMORY_GET_CLASS_VERSION(tim::env_settings)); + } + +private: + static std::atomic_bool& lock_flag() + { + static std::atomic_bool _instance(false); + return _instance; + } + + void collapse(); + +private: + int m_id = 0; + env_uomap_t* m_env = new env_uomap_t{}; + std::vector m_env_other; +}; +// +//--------------------------------------------------------------------------------------// +// +template +void +env_settings::insert(const std::string& env_id, Tp val) +{ +#if !defined(TIMEMORY_DISABLE_STORE_ENVIRONMENT) + std::stringstream ss; + ss << std::boolalpha << val; + + auto_lock_t lk(type_mutex(), std::defer_lock); + if(lock_flag().load() && !lk.owns_lock()) + lk.lock(); + + if(m_env && + (m_env->find(env_id) == m_env->end() || m_env->find(env_id)->second != ss.str())) + (*m_env)[env_id] = ss.str(); +#else + consume_parameters(env_id, val); +#endif +} +// +//--------------------------------------------------------------------------------------// +// +template +void +env_settings::serialize(Archive& ar, const unsigned int) +{ + auto _tmp = env_uomap_t{}; + if(!m_env) + m_env = &_tmp; + + collapse(); + + auto_lock_t lk(type_mutex(), std::defer_lock); + lock_flag().store(true); + + if(!lk.owns_lock()) + lk.lock(); + + ar(cereal::make_nvp("environment", *m_env)); + + if(m_env == &_tmp) + m_env = nullptr; + + lock_flag().store(false); +} +// +//--------------------------------------------------------------------------------------// +// +template +Tp +get_env(const std::string& env_id, Tp _default, bool _store) +{ + if(env_id.empty()) + return _default; + + auto* _env_settings = env_settings::instance(); + char* env_var = std::getenv(env_id.c_str()); + if(env_var) + { + std::string str_var = std::string(env_var); + std::stringstream iss{ str_var }; + auto var = Tp{}; + iss >> var; + if(_env_settings && _store) + _env_settings->insert(env_id, var); + return var; + } + // record default value + if(_env_settings && _store) + _env_settings->insert(env_id, _default); + + // return default if not specified in environment + return _default; +} +// +//--------------------------------------------------------------------------------------// +// +template +Tp +get_env_choice(const std::string& env_id, Tp _default, std::set _choices) +{ + assert(!_choices.empty()); + auto _choice = get_env(env_id, _default); + + // check that the choice is valid + if(_choices.find(_choice) == _choices.end()) + { + std::ostringstream _msg{}; + _msg << "Error! Invalid value \"" << _choice << "\" for " << env_id + << ". Valid choices are: "; + std::ostringstream _opts{}; + for(const auto& itr : _choices) + _opts << ", \"" << itr << "\""; + _msg << _opts.str().substr(2); + throw std::runtime_error(_msg.str()); + } + + return _choice; +} +// +//--------------------------------------------------------------------------------------// +// +template +Tp +load_env(const std::string& env_id, Tp _default) +{ + if(env_id.empty()) + return _default; + + auto* _env_settings = env_settings::instance(); + if(!_env_settings) + return _default; + + auto itr = _env_settings->get(env_id); + if(itr != _env_settings->end()) + { + std::stringstream iss{ itr->second }; + auto var = Tp{}; + iss >> var; + return var; + } + + // return default if not specified in environment + return _default; +} +// +//--------------------------------------------------------------------------------------// +// +template +void +set_env(const std::string& env_var, const Tp& _val, int override) +{ + std::stringstream ss_val; + ss_val << _val; +#if defined(TIMEMORY_MACOS) || (defined(TIMEMORY_LINUX) && (_POSIX_C_SOURCE >= 200112L)) + setenv(env_var.c_str(), ss_val.str().c_str(), override); +#elif defined(TIMEMORY_WINDOWS) + auto _curr = get_env(env_var, ""); + if(_curr.empty() || override > 0) + _putenv_s(env_var.c_str(), ss_val.str().c_str()); +#else + consume_parameters(env_var, _val, override, ss_val); +#endif +} +// +//--------------------------------------------------------------------------------------// +// +} // namespace tim diff --git a/include/timemory/environment/definition.hpp b/include/timemory/environment/definition.hpp new file mode 100644 index 0000000000..daa266998e --- /dev/null +++ b/include/timemory/environment/definition.hpp @@ -0,0 +1,362 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +/** + * \file timemory/environment/definition.hpp + * \brief The definitions for the types in environment + */ + +#pragma once + +#include "timemory/environment/declaration.hpp" +#include "timemory/environment/macros.hpp" +#include "timemory/environment/types.hpp" +#include "timemory/utility/transient_function.hpp" +#include "timemory/utility/utility.hpp" + +#include +#include +#include +#include +#include +#include + +namespace tim +{ +// +//--------------------------------------------------------------------------------------// +// +namespace regex_const = std::regex_constants; +// +//--------------------------------------------------------------------------------------// +// +#if !defined(TIMEMORY_USE_ENVIRONMENT_EXTERN) +// +//--------------------------------------------------------------------------------------// +// +// environment +// +//--------------------------------------------------------------------------------------// +// +// if passed a global, insert m_env into it's array +// +TIMEMORY_ENVIRONMENT_LINKAGE(env_settings::env_settings) +(env_settings* _global, int _id) +: m_id(_id) +, m_env(new env_uomap_t) +{ + if(_global && _id != 0) + { + auto_lock_t lk(type_mutex(), std::defer_lock); + lock_flag().store(true); + if(!lk.owns_lock()) + lk.lock(); + _global->m_env_other.emplace_back(m_env); + lock_flag().store(false); + } +} +// +//--------------------------------------------------------------------------------------// +// +// only the zero id deletes +// +TIMEMORY_ENVIRONMENT_LINKAGE(env_settings::~env_settings()) +{ + if(m_id == 0) + { + delete m_env; + for(auto& itr : m_env_other) + delete itr; + } +} +// +//--------------------------------------------------------------------------------------// +// +TIMEMORY_ENVIRONMENT_LINKAGE(env_settings::env_map_t) +env_settings::get() const +{ + auto_lock_t lk(type_mutex(), std::defer_lock); + lock_flag().store(true); + if(!lk.owns_lock()) + lk.lock(); + + auto _tmp = *m_env; + env_map_t _ret; + for(const auto& itr : _tmp) + _ret[itr.first] = itr.second; + + lock_flag().store(false); + return _ret; +} +// +//--------------------------------------------------------------------------------------// +// +TIMEMORY_ENVIRONMENT_LINKAGE(void) +env_settings::print(std::ostream& os) const +{ + if(m_id == 0) + const_cast(*this).collapse(); + + auto _data = get(); + + size_t _w = 35; + for(const auto& itr : _data) + _w = std::max(itr.first.length(), _w); + + std::stringstream filler; + filler.fill('-'); + filler << '#'; + filler << std::setw(88) << ""; + filler << '#'; + + std::stringstream ss; + ss << filler.str() << '\n'; + ss << "# Environment settings:\n"; + for(const auto& itr : _data) + { + ss << "# " << std::setw(_w) << std::right << itr.first << "\t = \t" << std::left + << itr.second << '\n'; + } + ss << filler.str(); + os << ss.str() << '\n'; +} +// +//--------------------------------------------------------------------------------------// +// +// NOLINTNEXTLINE +TIMEMORY_ENVIRONMENT_LINKAGE(void) +env_settings::collapse() +{ + if(m_id != 0) + return; + + auto_lock_t lk(type_mutex(), std::defer_lock); + if(!lk.owns_lock()) + lk.lock(); + + lock_flag().store(true); + + // insert all the other maps into this map + for(size_t i = 0; i < m_env_other.size(); ++i) + { + // get the map instance + auto* itr = m_env_other[i]; + if(itr) + { + // loop over entries + for(const auto& mitr : *m_env_other[i]) + { + auto key = mitr.first; + if(m_env->find(key) != m_env->end()) + { + // if the key already exists and if the entries differ, + // create a new unique entry for the index + if(m_env->find(key)->second != mitr.second) + { + key += string_t("[@") + std::to_string(i + 1) + string_t("]"); + (*m_env)[key] = mitr.second; + } + } + else + { + // if a new entry, insert without unique tag to avoid duplication + // when thread count is very high + (*m_env)[key] = mitr.second; + } + } + } + } + lock_flag().store(false); +} +// +//--------------------------------------------------------------------------------------// +// +// specialization for string since the above will have issues if string includes spaces +// +template <> +TIMEMORY_ENVIRONMENT_LINKAGE(std::string) +get_env(const std::string& env_id, std::string _default, bool _store) +{ + if(env_id.empty()) + return _default; + + auto* _env_settings = env_settings::instance(); + char* env_var = std::getenv(env_id.c_str()); + if(env_var) + { + std::stringstream ss; + ss << env_var; + if(_env_settings && _store) + _env_settings->insert(env_id, ss.str()); + return ss.str(); + } + // record default value + if(_env_settings && _store) + _env_settings->insert(env_id, _default); + + // return default if not specified in environment + return _default; +} +// +//--------------------------------------------------------------------------------------// +// +// overload for boolean +// +template <> +TIMEMORY_ENVIRONMENT_LINKAGE(bool) +get_env(const std::string& env_id, bool _default, bool _store) +{ + if(env_id.empty()) + return _default; + + auto* _env_settings = env_settings::instance(); + char* env_var = std::getenv(env_id.c_str()); + if(env_var) + { + std::string var = std::string(env_var); + bool val = true; + if(var.find_first_not_of("0123456789") == std::string::npos) + { + val = (bool) atoi(var.c_str()); + } + else + { + for(auto& itr : var) + itr = tolower(itr); + for(const auto& itr : { "off", "false", "no", "n", "f", "0" }) + { + if(var == itr) + { + if(_env_settings && _store) + _env_settings->insert(env_id, false); + return false; + } + } + } + if(_env_settings && _store) + _env_settings->insert(env_id, val); + return val; + } + // record default value + if(_env_settings && _store) + _env_settings->insert(env_id, _default); + + // return default if not specified in environment + return _default; +} +// +//--------------------------------------------------------------------------------------// +// +// specialization for string since the above will have issues if string includes spaces +// +template <> +TIMEMORY_ENVIRONMENT_LINKAGE(std::string) +load_env(const std::string& env_id, std::string _default) +{ + if(env_id.empty()) + return _default; + + auto* _env_settings = env_settings::instance(); + auto itr = _env_settings->get(env_id); + if(itr != _env_settings->end()) + return itr->second; + + // return default if not specified in environment + return _default; +} +// +//--------------------------------------------------------------------------------------// +// +// overload for boolean +// +template <> +TIMEMORY_ENVIRONMENT_LINKAGE(bool) +load_env(const std::string& env_id, bool _default) +{ + if(env_id.empty()) + return _default; + + auto* _env_settings = env_settings::instance(); + auto itr = _env_settings->get(env_id); + if(itr != _env_settings->end()) + { + auto val = itr->second; + const auto regex_constants = regex_const::egrep | regex_const::icase; + const std::string pattern = "^(off|false|no|n|f|0)$"; + bool _match = false; + try + { + _match = std::regex_match(val, std::regex(pattern, regex_constants)); + } catch(std::bad_cast&) + { + for(auto& vitr : val) + vitr = tolower(vitr); + for(const auto& vitr : { "off", "false", "no", "n", "f", "0" }) + { + if(val == vitr) + { + _match = true; + break; + } + } + } + return (_match) ? false : true; + } + + // return default if not specified in environment + return _default; +} +// +//--------------------------------------------------------------------------------------// +// +TIMEMORY_ENVIRONMENT_LINKAGE(void) +print_env(std::ostream& os) { os << (*env_settings::instance()); } +// +//--------------------------------------------------------------------------------------// +// +TIMEMORY_ENVIRONMENT_LINKAGE(tim::env_settings*) +env_settings::instance() +{ + static std::atomic _count{ 0 }; + static env_settings _instance{}; + static thread_local int _id = _count++; + static thread_local env_settings* _local = + (_id == 0) ? (&_instance) : new env_settings{ &_instance, _id }; + static thread_local auto _ldtor = scope::destructor{ []() { + if(_local == &_instance || _id == 0) + return; + delete _local; + _local = nullptr; + } }; + return _local; + (void) _ldtor; +} +// +//--------------------------------------------------------------------------------------// +// +#endif // !defined(TIMEMORY_USE_ENVIRONMENT_EXTERN) +// +//--------------------------------------------------------------------------------------// +// +} // namespace tim diff --git a/include/timemory/environment/macros.hpp b/include/timemory/environment/macros.hpp new file mode 100644 index 0000000000..1c3cbe99f5 --- /dev/null +++ b/include/timemory/environment/macros.hpp @@ -0,0 +1,69 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/utility/macros.hpp" + +#if defined(TIMEMORY_CORE_SOURCE) +# define TIMEMORY_ENVIRONMENT_SOURCE +#elif defined(TIMEMORY_USE_CORE_EXTERN) +# define TIMEMORY_USE_ENVIRONMENT_EXTERN +#endif +// +#if defined(TIMEMORY_USE_EXTERN) && !defined(TIMEMORY_USE_ENVIRONMENT_EXTERN) +# define TIMEMORY_USE_ENVIRONMENT_EXTERN +#endif + +#if defined(TIMEMORY_ENVIRONMENT_SOURCE) +# define TIMEMORY_ENVIRONMENT_LINKAGE(...) __VA_ARGS__ +#elif defined(TIMEMORY_USE_ENVIRONMENT_EXTERN) +# define TIMEMORY_ENVIRONMENT_LINKAGE(...) extern __VA_ARGS__ +#else +# define TIMEMORY_ENVIRONMENT_LINKAGE(...) inline __VA_ARGS__ +#endif +// +//--------------------------------------------------------------------------------------// +// +#if !defined(TIMEMORY_ENVIRONMENT_EXTERN_TEMPLATE) +# if defined(TIMEMORY_ENVIRONMENT_SOURCE) +# define TIMEMORY_ENVIRONMENT_EXTERN_TEMPLATE(TYPE) \ + namespace tim \ + { \ + template TYPE get_env(const std::string&, TYPE); \ + template void set_env(const std::string&, const TYPE&, int); \ + } +# elif defined(TIMEMORY_USE_ENVIRONMENT_EXTERN) +# define TIMEMORY_ENVIRONMENT_EXTERN_TEMPLATE(TYPE) \ + namespace tim \ + { \ + extern template TYPE get_env(const std::string&, TYPE); \ + extern template void set_env(const std::string&, const TYPE&, int); \ + } +# endif +#else +# define TIMEMORY_ENVIRONMENT_EXTERN_TEMPLATE(...) +#endif +// +//======================================================================================// diff --git a/include/timemory/environment/types.hpp b/include/timemory/environment/types.hpp new file mode 100644 index 0000000000..588b369e3a --- /dev/null +++ b/include/timemory/environment/types.hpp @@ -0,0 +1,103 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/environment/macros.hpp" +#include "timemory/utility/types.hpp" + +#include +#include +#include +#include + +namespace tim +{ +// +//--------------------------------------------------------------------------------------// +// +// environment +// +//--------------------------------------------------------------------------------------// +// +class env_settings; +// +//--------------------------------------------------------------------------------------// +// +template +Tp +get_env(const std::string& env_id, Tp _default = Tp{}, bool _store = true); +// +//--------------------------------------------------------------------------------------// +// +template <> +std::string +get_env(const std::string& env_id, std::string _default, bool _store); +// +//--------------------------------------------------------------------------------------// +// +template <> +bool +get_env(const std::string& env_id, bool _default, bool _store); +// +//--------------------------------------------------------------------------------------// +// +template +Tp +get_env_choice(const std::string& env_id, Tp _default, std::set _choices); +// +//--------------------------------------------------------------------------------------// +// +template +Tp +load_env(const std::string& env_id, Tp _default = Tp{}); +// +//--------------------------------------------------------------------------------------// +// +template <> +std::string +load_env(const std::string& env_id, std::string _default); +// +//--------------------------------------------------------------------------------------// +// +template <> +bool +load_env(const std::string& env_id, bool _default); +// +//--------------------------------------------------------------------------------------// +// +void +print_env(std::ostream& os = std::cout); +// +//--------------------------------------------------------------------------------------// +// +template +void +set_env(const std::string& env_var, const Tp& _val, int override = 0); +// +//--------------------------------------------------------------------------------------// +// +} // namespace tim + +TIMEMORY_SET_CLASS_VERSION(0, tim::env_settings) diff --git a/include/timemory/general/source_location.hpp b/include/timemory/general/source_location.hpp new file mode 100644 index 0000000000..f8dba4a555 --- /dev/null +++ b/include/timemory/general/source_location.hpp @@ -0,0 +1,366 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/general/types.hpp" +#include "timemory/hash/declaration.hpp" +#include "timemory/mpl/apply.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace tim +{ +namespace impl +{ +template +struct source_location_constant_char; + +template <> +struct source_location_constant_char<> +{ + static constexpr bool value = true; +}; + +template <> +struct source_location_constant_char +{ + static constexpr bool value = true; +}; + +template +struct source_location_constant_char +{ + static constexpr bool value = std::is_same::value; +}; + +template +struct source_location_constant_char +{ + static constexpr bool value = source_location_constant_char::value && + source_location_constant_char::value && + source_location_constant_char::value; +}; + +} // namespace impl + +/// \class tim::source_location +/// \brief Provides source location information and variadic joining of source location +/// tags +class source_location +{ +public: + using join_type = mpl::apply; + using result_type = std::tuple; + +private: + template + static constexpr bool is_constant_char() + { + return impl::source_location_constant_char...>::value; + } + +public: + // + // Public types + // + + //==================================================================================// + // + enum class mode : short + { + blank = 0, + basic = 1, + full = 2, + complete = 3 + }; + + //==================================================================================// + // + struct captured + { + public: + const result_type& get() const { return m_result; } + const std::string& get_id() const { return std::get<0>(m_result); } + const hash_value_t& get_hash() const { return std::get<1>(m_result); } + + explicit captured(result_type _result) + : m_result(std::move(_result)) + {} + + TIMEMORY_DEFAULT_OBJECT(captured) + + private: + friend class source_location; + result_type m_result = result_type("", 0); + + template 0), int> = 0> + captured& set(const source_location& obj, ArgsT&&... _args) + { + switch(obj.m_mode) + { + case mode::blank: + { + auto&& _tmp = join_type::join("", std::forward(_args)...); + m_result = result_type(_tmp, add_hash_id(_tmp)); + break; + } + case mode::basic: + case mode::full: + case mode::complete: + { + auto&& _suffix = join_type::join("", std::forward(_args)...); + if(_suffix.empty()) + { + m_result = result_type(obj.m_prefix, add_hash_id(obj.m_prefix)); + } + else + { + auto _tmp = join_type::join('/', obj.m_prefix.c_str(), _suffix); + m_result = result_type(_tmp, add_hash_id(_tmp)); + } + break; + } + } + return *this; + } + + template = 0> + captured& set(const source_location& obj, ArgsT&&... _args) + { + tim::consume_parameters(std::forward(_args)...); + m_result = result_type(obj.m_prefix, add_hash_id(obj.m_prefix)); + return *this; + } + }; + +public: + // + // Static functions + // + + //==================================================================================// + // + template + static captured get_captured_inline(const mode& _mode, const char* _func, int _line, + const char* _fname, ArgsT&&... _args) + { + source_location _loc{ _mode, _func, _line, _fname, + std::forward(_args)... }; + return _loc.get_captured(std::forward(_args)...); + } + +public: + // + // Constructors, destructors, etc. + // + + //----------------------------------------------------------------------------------// + // + template (), int> = 0> + source_location(const mode& _mode, const char* _func, int _line, const char* _fname, + ArgsT&&...) + : m_mode(_mode) + { + switch(m_mode) + { + case mode::blank: break; + case mode::basic: compute_data(_func); break; + case mode::complete: + case mode::full: + compute_data(_func, _line, _fname, m_mode == mode::full); + break; + } + } + + //----------------------------------------------------------------------------------// + // if a const char* is passed, assume it is constant + // + template (), int> = 0> + source_location(const mode& _mode, const char* _func, int _line, const char* _fname, + ArgsT&&... _args) + : m_mode(_mode) + { + auto _arg = join_type::join("", _args...); + switch(m_mode) + { + case mode::blank: + { + // label and hash + auto&& _label = _arg; + auto&& _hash = add_hash_id(_label); + m_captured = captured(result_type{ _label, _hash }); + break; + } + case mode::basic: + { + compute_data(_func); + auto&& _label = _join(_arg.c_str()); + auto&& _hash = add_hash_id(_label); + m_captured = captured(result_type{ _label, _hash }); + break; + } + case mode::full: + case mode::complete: + { + compute_data(_func, _line, _fname, m_mode == mode::full); + // label and hash + auto&& _label = _join(_arg.c_str()); + auto&& _hash = add_hash_id(_label); + m_captured = captured(result_type{ _label, _hash }); + break; + } + } + } + + //----------------------------------------------------------------------------------// + // + source_location() = delete; + ~source_location() = default; + source_location(const source_location&) = delete; + source_location(source_location&&) = default; + source_location& operator=(const source_location&) = delete; + source_location& operator=(source_location&&) = default; + +protected: + //----------------------------------------------------------------------------------// + // + void compute_data(const char* _func) { m_prefix = _func; } + + //----------------------------------------------------------------------------------// + // + void compute_data(const char* _func, int _line, const char* _fname, bool shorten) + { +#if defined(TIMEMORY_WINDOWS) + static const char delim = '\\'; +#else + static const char delim = '/'; +#endif + std::string _filename(_fname); + if(shorten) + { + if(_filename.find(delim) != std::string::npos) + _filename = _filename.substr(_filename.find_last_of(delim) + 1); + } + + if(_line < 0) + { + if(_filename.length() > 0) + { + m_prefix = join_type::join("", _func, '@', _filename); + } + else + { + m_prefix = _func; + } + } + else + { + if(_filename.length() > 0) + { + m_prefix = join_type::join("", _func, '@', _filename, ':', _line); + } + else + { + m_prefix = join_type::join("", _func, ':', _line); + } + } + } + +public: + //----------------------------------------------------------------------------------// + // + template < + typename... ArgsT, + enable_if_t<(sizeof...(ArgsT) > 0) && !is_constant_char(), int> = 0> + const captured& get_captured(ArgsT&&... _args) + { + return m_captured.set(*this, std::forward(_args)...); + } + + //----------------------------------------------------------------------------------// + // + const captured& get_captured() const { return m_captured; } + + //----------------------------------------------------------------------------------// + // + template < + typename... ArgsT, + enable_if_t<(sizeof...(ArgsT) > 0) && is_constant_char(), int> = 0> + const captured& get_captured(ArgsT&&...) + { + return m_captured; + } + +private: + mode m_mode; + std::string m_prefix = {}; + captured m_captured; + +private: + std::string _join(const char* _arg) + { + return (strcmp(_arg, "") == 0) ? m_prefix + : join_type::join('/', m_prefix.c_str(), _arg); + } +}; +// +namespace internal +{ +namespace +{ +// anonymous namespace to make sure the static instance is local to each .cpp +// making it static also helps ensure this +template +static inline auto& +get_static_source_location(Args&&... args) +{ + static source_location _instance{ std::forward(args)... }; + consume_parameters(std::forward(args)...); + return _instance; +} +} // namespace +} // namespace internal +// +// static function should be local to each .cpp +// +// LineN should be '__LINE__': this ensures that each use in a source file is unique +// CountN should be '__COUNTER__': this ensures that each source file has unique integer +template +static inline auto& +get_static_source_location(Args&&... args) +{ + return internal::get_static_source_location( + std::forward(args)...); + consume_parameters(std::forward(args)...); +} +// +} // namespace tim diff --git a/include/timemory/general/types.hpp b/include/timemory/general/types.hpp new file mode 100644 index 0000000000..daa7ed5fc5 --- /dev/null +++ b/include/timemory/general/types.hpp @@ -0,0 +1,68 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +/** \headerfile "timemory/general/types.hpp" + */ + +#pragma once + +#include + +namespace tim +{ +// +//--------------------------------------------------------------------------------------// +// +class source_location; +// +template +static inline auto& +get_static_source_location(Args&&... args); +// +//--------------------------------------------------------------------------------------// +// +} // namespace tim + +#if !defined(TIMEMORY_SOURCE_LOCATION) + +# define _AUTO_LOCATION_COMBINE(X, Y) X##Y +# define _AUTO_LOCATION(Y) _AUTO_LOCATION_COMBINE(timemory_source_location_, Y) + +# define TIMEMORY_SOURCE_LOCATION(MODE, ...) \ + ::tim::source_location(MODE, __FUNCTION__, __LINE__, __FILE__, __VA_ARGS__) + +# define TIMEMORY_CAPTURE_MODE(MODE_TYPE) ::tim::source_location::mode::MODE_TYPE + +# define TIMEMORY_CAPTURE_ARGS(...) _AUTO_LOCATION(__LINE__).get_captured(__VA_ARGS__) + +# define TIMEMORY_INLINE_SOURCE_LOCATION(MODE, ...) \ + ::tim::get_static_source_location<__LINE__, __COUNTER__>( \ + TIMEMORY_CAPTURE_MODE(MODE), __FUNCTION__, __LINE__, __FILE__, __VA_ARGS__) \ + .get_captured(__VA_ARGS__) + +# define _TIM_STATIC_SRC_LOCATION(MODE, ...) \ + static thread_local auto _AUTO_LOCATION(__LINE__) = \ + TIMEMORY_SOURCE_LOCATION(TIMEMORY_CAPTURE_MODE(MODE), __VA_ARGS__) + +#endif diff --git a/include/timemory/hash/declaration.hpp b/include/timemory/hash/declaration.hpp new file mode 100644 index 0000000000..85185df13b --- /dev/null +++ b/include/timemory/hash/declaration.hpp @@ -0,0 +1,85 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/hash/macros.hpp" +#include "timemory/hash/types.hpp" +#include "timemory/utility/macros.hpp" + +#include +#include +#include + +namespace tim +{ +// +//--------------------------------------------------------------------------------------// +// +template +PairT& +get_shared_ptr_pair() +{ + static auto _master = std::make_shared(); + static std::atomic _count{ 0 }; + static thread_local auto _inst = + PairT{ _master, PtrT{ (_count++ == 0) ? nullptr : new Tp{} } }; + return _inst; +} +// +//--------------------------------------------------------------------------------------// +// +template +PtrT +get_shared_ptr_pair_instance() +{ + static thread_local auto& _pinst = get_shared_ptr_pair(); + static thread_local auto& _inst = _pinst.second ? _pinst.second : _pinst.first; + return _inst; +} +// +//--------------------------------------------------------------------------------------// +// +template +PtrT +get_shared_ptr_pair_main_instance() +{ + static auto& _pinst = get_shared_ptr_pair(); + static auto _inst = _pinst.first; + return _inst; +} +// +//--------------------------------------------------------------------------------------// +// +template +PtrT +get_shared_ptr_lone_instance() +{ + static auto _instance = std::make_shared(); + return _instance; +} +// +//--------------------------------------------------------------------------------------// +// +} // namespace tim diff --git a/include/timemory/hash/macros.hpp b/include/timemory/hash/macros.hpp new file mode 100644 index 0000000000..927c9bb44a --- /dev/null +++ b/include/timemory/hash/macros.hpp @@ -0,0 +1,43 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#if defined(TIMEMORY_CORE_SOURCE) +# define TIMEMORY_HASH_SOURCE +#elif defined(TIMEMORY_USE_CORE_EXTERN) +# define TIMEMORY_USE_HASH_EXTERN +#endif +// +#if defined(TIMEMORY_USE_EXTERN) && !defined(TIMEMORY_USE_HASH_EXTERN) +# define TIMEMORY_USE_HASH_EXTERN +#endif +// +#if defined(TIMEMORY_HASH_SOURCE) +# define TIMEMORY_HASH_LINKAGE(...) __VA_ARGS__ +#elif defined(TIMEMORY_USE_HASH_EXTERN) +# define TIMEMORY_HASH_LINKAGE(...) extern __VA_ARGS__ +#else +# define TIMEMORY_HASH_LINKAGE(...) inline __VA_ARGS__ +#endif diff --git a/include/timemory/hash/types.hpp b/include/timemory/hash/types.hpp new file mode 100644 index 0000000000..2fef34555a --- /dev/null +++ b/include/timemory/hash/types.hpp @@ -0,0 +1,290 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/api.hpp" +#include "timemory/hash/macros.hpp" +#include "timemory/macros/attributes.hpp" +#include "timemory/macros/language.hpp" +#include "timemory/mpl/concepts.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace tim +{ +// +//--------------------------------------------------------------------------------------// +// +// GENERAL PURPOSE TLS DATA +// +//--------------------------------------------------------------------------------------// +// +template , + typename PairT = std::pair> +PairT& +get_shared_ptr_pair(); +// +//--------------------------------------------------------------------------------------// +// +template , + typename PairT = std::pair> +PtrT +get_shared_ptr_pair_instance(); +// +//--------------------------------------------------------------------------------------// +// +template , + typename PairT = std::pair> +PtrT +get_shared_ptr_pair_main_instance(); +// +//--------------------------------------------------------------------------------------// +// +template > +PtrT +get_shared_ptr_lone_instance(); +// +//--------------------------------------------------------------------------------------// +// +// HASH ALIASES +// +//--------------------------------------------------------------------------------------// +// +using hash_value_t = size_t; +using hash_map_t = std::unordered_map; +using hash_alias_map_t = std::unordered_map; +using hash_map_ptr_t = std::shared_ptr; +using hash_map_ptr_pair_t = std::pair; +using hash_alias_ptr_t = std::shared_ptr; +using hash_resolver_t = std::function; +using hash_resolver_vec_t = std::vector; +// +//--------------------------------------------------------------------------------------// +// +// HASH DATA STRUCTURES +// +//--------------------------------------------------------------------------------------// +// +hash_map_ptr_t& +get_hash_ids() TIMEMORY_HOT; +// +//--------------------------------------------------------------------------------------// +// +hash_alias_ptr_t& +get_hash_aliases() TIMEMORY_HOT; +// +//--------------------------------------------------------------------------------------// +// +std::shared_ptr& +get_hash_resolvers() TIMEMORY_HOT; +// +//--------------------------------------------------------------------------------------// +// +// STRING -> HASH CONVERSION +// +//--------------------------------------------------------------------------------------// +// +// declarations +// +template >::value, int> = 0> +TIMEMORY_INLINE hash_value_t + get_hash_id(Tp&& _prefix); +// +template >::value, int> = 0> +TIMEMORY_INLINE hash_value_t + get_hash_id(Tp&& _prefix); +// +TIMEMORY_INLINE hash_value_t + get_combined_hash_id(hash_value_t _lhs, hash_value_t _rhs); +// +template >::value, int> = 0> +TIMEMORY_INLINE hash_value_t + get_combined_hash_id(hash_value_t _lhs, Tp&& _rhs); +// +// get hash of a string type +// +template >::value, int>> +hash_value_t +get_hash_id(Tp&& _prefix) +{ + return std::hash{}(std::forward(_prefix)); +} +// +// get hash of a non-string type +// +template >::value, int>> +hash_value_t +get_hash_id(Tp&& _prefix) +{ + return std::hash>{}(std::forward(_prefix)); +} +// +// combine two existing hashes +// +hash_value_t +get_combined_hash_id(hash_value_t _lhs, hash_value_t _rhs) +{ + return (_lhs ^= _rhs + 0x9e3779b9 + (_lhs << 6) + (_lhs >> 2)); +} +// +// compute the hash and combine it with an existing hash +// +template >::value, int>> +hash_value_t +get_combined_hash_id(hash_value_t _lhs, Tp&& _rhs) +{ + return get_combined_hash_id(_lhs, get_hash_id(std::forward(_rhs))); +} +// +//--------------------------------------------------------------------------------------// +// +hash_value_t +get_hash_id(const hash_alias_ptr_t& _hash_alias, hash_value_t _hash_id) TIMEMORY_HOT; +// +//--------------------------------------------------------------------------------------// +// +/// \fn hash_value_t add_hash_id(hash_map_ptr_t&, const string_view_t&) +/// \brief add an string to the given hash-map (if it doesn't already exist) and return +/// the hash +/// +hash_value_t +add_hash_id(hash_map_ptr_t& _hash_map, const string_view_t& _prefix) TIMEMORY_HOT; +// +inline hash_value_t +add_hash_id(hash_map_ptr_t& _hash_map, const string_view_t& _prefix) +{ + hash_value_t _hash_id = get_hash_id(_prefix); + if(_hash_map && _hash_map->find(_hash_id) == _hash_map->end()) + { + (*_hash_map)[_hash_id] = std::string{ _prefix }; + } + return _hash_id; +} +// +//--------------------------------------------------------------------------------------// +// +/// \fn hash_value_t add_hash_id(const string_view_t&) +/// \brief add an string to the default hash-map (if it doesn't already exist) and return +/// the hash +/// +hash_value_t +add_hash_id(const string_view_t& _prefix) TIMEMORY_HOT; +// +inline hash_value_t +add_hash_id(const string_view_t& _prefix) +{ + return add_hash_id(get_hash_ids(), _prefix); +} +// +//--------------------------------------------------------------------------------------// +// +void +add_hash_id(const hash_map_ptr_t& _hash_map, const hash_alias_ptr_t& _hash_alias, + hash_value_t _hash_id, hash_value_t _alias_hash_id) TIMEMORY_HOT; +// +//--------------------------------------------------------------------------------------// +// +void +add_hash_id(hash_value_t _hash_id, hash_value_t _alias_hash_id) TIMEMORY_HOT; +// +//--------------------------------------------------------------------------------------// +// +// HASH -> STRING CONVERSION +// +//--------------------------------------------------------------------------------------// +// +/// \fn string_view_t get_hash_identifier_fast(hash_value_t) +/// \brief this does not check other threads or aliases. Only call this function when +/// you know that the hash exists on the thread and is not an alias +// +string_view_t +get_hash_identifier_fast(hash_value_t _hash) TIMEMORY_HOT; +// +inline string_view_t +get_hash_identifier_fast(hash_value_t _hash) +{ + auto& _hash_ids = get_hash_ids(); + auto itr = _hash_ids->find(_hash); + if(itr != _hash_ids->end()) + return itr->second; + return ""; +} +// +//--------------------------------------------------------------------------------------// +// +std::string +get_hash_identifier(const hash_map_ptr_t& _hash_map, const hash_alias_ptr_t& _hash_alias, + hash_value_t _hash_id); +// +//--------------------------------------------------------------------------------------// +// +std::string +get_hash_identifier(hash_value_t _hash_id); +// +//--------------------------------------------------------------------------------------// +// +template +size_t +add_hash_resolver(FuncT&& _func) +{ + auto _resolvers = get_hash_resolvers(); + if(!_resolvers) + return 0; + _resolvers->emplace_back(std::forward(_func)); + return _resolvers->size(); +} +// +//--------------------------------------------------------------------------------------// +// +// HASH STRING DEMANGLING +// +//--------------------------------------------------------------------------------------// +// +std::string +demangle_hash_identifier(std::string, char bdelim = '[', char edelim = ']'); +// +//--------------------------------------------------------------------------------------// +// +template +auto +get_demangled_hash_identifier(Args&&... _args) +{ + return demangle_hash_identifier(get_hash_identifier(std::forward(_args)...)); +} +// +//--------------------------------------------------------------------------------------// +// +} // namespace tim diff --git a/include/timemory/macros/attributes.hpp b/include/timemory/macros/attributes.hpp new file mode 100644 index 0000000000..5b4db304fa --- /dev/null +++ b/include/timemory/macros/attributes.hpp @@ -0,0 +1,193 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/macros/compiler.hpp" +#include "timemory/macros/language.hpp" +#include "timemory/macros/os.hpp" + +//======================================================================================// +// +#if !defined(TIMEMORY_ATTRIBUTE) +# if defined(TIMEMORY_MSVC_COMPILER) +# define TIMEMORY_ATTRIBUTE(...) __declspec(__VA_ARGS__) +# else +# define TIMEMORY_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) +# endif +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_ALWAYS_INLINE) +# if defined(TIMEMORY_MSVC_COMPILER) +# define TIMEMORY_ALWAYS_INLINE __forceinline +# else +# define TIMEMORY_ALWAYS_INLINE TIMEMORY_ATTRIBUTE(always_inline) inline +# endif +#endif + +#if !defined(TIMEMORY_INLINE) +# define TIMEMORY_INLINE TIMEMORY_ALWAYS_INLINE +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_NODISCARD) +# if defined(CXX17) +# define TIMEMORY_NODISCARD [[nodiscard]] +# else +# define TIMEMORY_NODISCARD +# endif +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_FLATTEN) +# if !defined(TIMEMORY_MSVC_COMPILER) +# define TIMEMORY_FLATTEN [[gnu::flatten]] +# else +# define TIMEMORY_FLATTEN +# endif +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_HOT) +# if !defined(TIMEMORY_MSVC_COMPILER) +# define TIMEMORY_HOT TIMEMORY_ATTRIBUTE(hot) +# else +# define TIMEMORY_HOT +# endif +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_COLD) +# if !defined(TIMEMORY_MSVC_COMPILER) +# define TIMEMORY_COLD TIMEMORY_ATTRIBUTE(cold) +# else +# define TIMEMORY_COLD +# endif +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_CONST) +# define TIMEMORY_CONST [[gnu::const]] +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_DEPRECATED) +# define TIMEMORY_DEPRECATED(...) [[gnu::deprecated(__VA_ARGS__)]] +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_EXTERN_VISIBLE) +# define TIMEMORY_EXTERN_VISIBLE [[gnu::externally_visible]] +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_HIDDEN) +# if !defined(TIMEMORY_MSVC_COMPILER) +# define TIMEMORY_HIDDEN TIMEMORY_ATTRIBUTE(visibility("hidden")) +# else +# define TIMEMORY_HIDDEN +# endif +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_ALIAS) +# define TIMEMORY_ALIAS(...) [[gnu::alias(__VA_ARGS__)]] +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_NOINLINE) +# define TIMEMORY_NOINLINE TIMEMORY_ATTRIBUTE(noinline) +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_NOCLONE) +# if defined(TIMEMORY_GNU_COMPILER) +# define TIMEMORY_NOCLONE TIMEMORY_ATTRIBUTE(noclone) +# else +# define TIMEMORY_NOCLONE +# endif +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_HOT_INLINE) +# define TIMEMORY_HOT_INLINE TIMEMORY_HOT TIMEMORY_INLINE +#endif + +//======================================================================================// +// +#if !defined(TIMEMORY_DATA_ALIGNMENT) +# define TIMEMORY_DATA_ALIGNMENT 8 +#endif + +#if !defined(TIMEMORY_PACKED_ALIGNMENT) +# if defined(_WIN32) // Windows 32- and 64-bit +# define TIMEMORY_PACKED_ALIGNMENT __declspec(align(TIMEMORY_DATA_ALIGNMENT)) +# elif defined(__GNUC__) // GCC +# define TIMEMORY_PACKED_ALIGNMENT \ + __attribute__((__packed__)) __attribute__((aligned(TIMEMORY_DATA_ALIGNMENT))) +# else // all other compilers +# define TIMEMORY_PACKED_ALIGNMENT +# endif +#endif + +//======================================================================================// +// device decorators +// +#if defined(__CUDACC__) || defined(__HIPCC__) +# define TIMEMORY_LAMBDA __host__ __device__ +# define TIMEMORY_HOST_LAMBDA __host__ +# define TIMEMORY_HOST_FUNCTION __host__ +# define TIMEMORY_DEVICE_LAMBDA __device__ +# define TIMEMORY_DEVICE_FUNCTION __device__ +# define TIMEMORY_GLOBAL_FUNCTION __global__ +# define TIMEMORY_HOST_DEVICE_FUNCTION __host__ __device__ +# define TIMEMORY_DEVICE_INLINE __device__ __inline__ +# define TIMEMORY_GLOBAL_INLINE __global__ __inline__ +# define TIMEMORY_HOST_DEVICE_INLINE __host__ __device__ __inline__ +#else +# define TIMEMORY_LAMBDA +# define TIMEMORY_HOST_LAMBDA +# define TIMEMORY_HOST_FUNCTION +# define TIMEMORY_DEVICE_LAMBDA +# define TIMEMORY_DEVICE_FUNCTION +# define TIMEMORY_GLOBAL_FUNCTION +# define TIMEMORY_HOST_DEVICE_FUNCTION +# define TIMEMORY_DEVICE_INLINE inline +# define TIMEMORY_GLOBAL_INLINE inline +# define TIMEMORY_HOST_DEVICE_INLINE inline +#endif diff --git a/include/timemory/macros/compiler.hpp b/include/timemory/macros/compiler.hpp new file mode 100644 index 0000000000..f101851f3a --- /dev/null +++ b/include/timemory/macros/compiler.hpp @@ -0,0 +1,205 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +#include "timemory/macros/os.hpp" + +//======================================================================================// +// +// Compiler +// +//======================================================================================// + +// clang compiler +#if defined(__clang__) && !defined(TIMEMORY_CLANG_COMPILER) +# define TIMEMORY_CLANG_COMPILER 1 +#endif + +//--------------------------------------------------------------------------------------// + +// GNU compiler +#if defined(__GNUC__) && !defined(TIMEMORY_CLANG_COMPILER) && \ + !defined(TIMEMORY_GNU_COMPILER) +# if(__GNUC__ <= 4 && __GNUC_MINOR__ < 9) +# warning "GCC compilers < 4.9 have been known to have compiler errors" +# define TIMEMORY_GNU_COMPILER 1 +# elif(__GNUC__ >= 4 && __GNUC_MINOR__ >= 9) || __GNUC__ >= 5 +# define TIMEMORY_GNU_COMPILER +# endif +#endif + +//--------------------------------------------------------------------------------------// + +// Intel compiler +#if defined(__INTEL_COMPILER) && !defined(TIMEMORY_INTEL_COMPILER) +# define TIMEMORY_INTEL_COMPILER 1 +# if __INTEL_COMPILER < 1500 +# warning "Intel compilers < 1500 have been known to have compiler errors" +# endif +#endif + +//--------------------------------------------------------------------------------------// + +// nvcc compiler +#if defined(__NVCC__) && !defined(TIMEMORY_NVCC_COMPILER) +# define TIMEMORY_NVCC_COMPILER 1 +#endif + +//--------------------------------------------------------------------------------------// + +// cuda compilation mode +#if defined(__CUDACC__) && !defined(TIMEMORY_CUDACC) +# define TIMEMORY_CUDACC 1 +#endif + +//--------------------------------------------------------------------------------------// + +// hcc or hip-clang compiler +#if(defined(__HCC__) || (defined(__clang__) && defined(__HIP__))) && \ + !defined(TIMEMORY_HIP_COMPILER) +# define TIMEMORY_HIP_COMPILER 1 +#endif + +//--------------------------------------------------------------------------------------// + +// hip compilation mode +#if defined(__HIPCC__) && !defined(TIMEMORY_HIPCC) +# define TIMEMORY_HIPCC 1 +#endif + +//--------------------------------------------------------------------------------------// + +// gpu compilation mode (may be host or device code) +#if(defined(__CUDACC__) || defined(__HIPCC__)) && !defined(TIMEMORY_GPUCC) +# define TIMEMORY_GPUCC 1 +#endif + +//--------------------------------------------------------------------------------------// + +// cuda compilation - device code +#if defined(__CUDA_ARCH__) && !defined(TIMEMORY_CUDA_DEVICE_COMPILE) +# define TIMEMORY_CUDA_DEVICE_COMPILE 1 +#endif + +//--------------------------------------------------------------------------------------// + +// hip compilation - device code +#if defined(__HIP_DEVICE_COMPILE__) && !defined(TIMEMORY_HIP_DEVICE_COMPILE) +# define TIMEMORY_HIP_DEVICE_COMPILE 1 +#endif + +//--------------------------------------------------------------------------------------// + +// gpu compiler - device code +#if(defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)) && \ + !defined(TIMEMORY_GPU_DEVICE_COMPILE) +# define TIMEMORY_GPU_DEVICE_COMPILE 1 +#endif + +//--------------------------------------------------------------------------------------// + +// assume openmp target is enabled +// #if defined(TIMEMORY_CUDACC) && defined(_OPENMP) && !defined(TIMEMORY_OPENMP_TARGET) +// # define TIMEMORY_OPENMP_TARGET 1 +// #endif + +//--------------------------------------------------------------------------------------// + +// MSVC compiler +#if defined(_MSC_VER) && _MSC_VER > 0 && !defined(TIMEMORY_MSVC_COMPILER) +# define TIMEMORY_MSVC_COMPILER 1 +#endif + +//======================================================================================// +// +// Demangling +// +//======================================================================================// + +#if(defined(TIMEMORY_GNU_COMPILER) || defined(TIMEMORY_CLANG_COMPILER) || \ + defined(TIMEMORY_INTEL_COMPILER) || defined(TIMEMORY_NVCC_COMPILER) || \ + defined(TIMEMORY_HIP_COMPILER)) && \ + defined(TIMEMORY_UNIX) +# if !defined(TIMEMORY_ENABLE_DEMANGLE) +# define TIMEMORY_ENABLE_DEMANGLE 1 +# endif +#endif + +//======================================================================================// +// +// WINDOWS WARNINGS +// +//======================================================================================// + +#if defined(TIMEMORY_MSVC_COMPILER) && !defined(TIMEMORY_MSVC_WARNINGS) + +# pragma warning(disable : 4003) // not enough actual params +# pragma warning(disable : 4061) // enum in switch of enum not explicitly handled +# pragma warning(disable : 4068) // unknown pragma +# pragma warning(disable : 4127) // conditional expr is constant +# pragma warning(disable : 4129) // unrecognized char escape +# pragma warning(disable : 4146) // unsigned +# pragma warning(disable : 4217) // locally defined symbol +# pragma warning(disable : 4244) // possible loss of data +# pragma warning(disable : 4251) // needs to have dll-interface to be used +# pragma warning(disable : 4242) // possible loss of data (assignment) +# pragma warning(disable : 4244) // conversion from 'double' to 'LONGLONG' +# pragma warning(disable : 4245) // signed/unsigned mismatch (init conversion) +# pragma warning(disable : 4267) // possible loss of data (implicit conversion) +# pragma warning(disable : 4305) // truncation from 'double' to 'float' +# pragma warning(disable : 4355) // this used in base member init list +# pragma warning(disable : 4365) // signed/unsigned mismatch +# pragma warning(disable : 4464) // relative include path contains '..' +# pragma warning(disable : 4522) // multiple assignment operators specified +# pragma warning(disable : 4625) // copy ctor implicitly defined as deleted +# pragma warning(disable : 4626) // copy assign implicitly defined as deleted +# pragma warning(disable : 4661) // no suitable definition for template inst +# pragma warning(disable : 4700) // uninitialized local variable used +# pragma warning(disable : 4710) // function not inlined +# pragma warning(disable : 4711) // function selected for auto inline expansion +# pragma warning(disable : 4786) // ID truncated to '255' char in debug info +# pragma warning(disable : 4820) // bytes padded after data member +# pragma warning(disable : 4834) // discarding return value with 'nodiscard' +# pragma warning(disable : 4996) // function may be unsafe +# pragma warning(disable : 5219) // implicit conversion; possible loss of data +# pragma warning(disable : 5026) // move ctor implicitly defined as deleted +# pragma warning(disable : 5027) // move assign implicitly defined as deleted +# pragma warning(disable : 26495) // Always initialize member variable + +#endif + +//======================================================================================// +// +// DISABLE WINDOWS MIN/MAX macros +// +//======================================================================================// + +#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) + +# if !defined(NOMINMAX) +# define NOMINMAX +# endif + +#endif diff --git a/include/timemory/macros/language.hpp b/include/timemory/macros/language.hpp new file mode 100644 index 0000000000..74ecb7a752 --- /dev/null +++ b/include/timemory/macros/language.hpp @@ -0,0 +1,105 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +//======================================================================================// +// +// LANGUAGE +// +//======================================================================================// + +// Define C++20 +#if !defined(CXX20) +# if __cplusplus > 201703L // C++20 +# define CXX20 +# endif +#endif + +//--------------------------------------------------------------------------------------// + +// Define C++17 +#if !defined(CXX17) +# if __cplusplus > 201402L // C++17 +# define CXX17 +# endif +#endif + +//--------------------------------------------------------------------------------------// + +// Define C++14 +#if !defined(CXX14) +# if __cplusplus > 201103L // C++14 +# define CXX14 +# endif +#endif + +//--------------------------------------------------------------------------------------// + +#if !defined(CXX14) +# if !defined(TIMEMORY_WINDOWS) +# error "timemory requires __cplusplus > 201103L (C++14)" +# endif +#endif + +//--------------------------------------------------------------------------------------// + +#if !defined(IF_CONSTEXPR) +# if defined(CXX17) +# define IF_CONSTEXPR(...) if constexpr(__VA_ARGS__) +# else +# define IF_CONSTEXPR(...) if(__VA_ARGS__) +# endif +#endif + +//--------------------------------------------------------------------------------------// + +#if defined(CXX17) +# include +// +# if !defined(TIMEMORY_STRING_VIEW) +# define TIMEMORY_STRING_VIEW 1 +# endif +// +namespace tim +{ +using string_view_t = std::string_view; +} +// +#else +// +# include +// +# if !defined(TIMEMORY_STRING_VIEW) +# define TIMEMORY_STRING_VIEW 0 +# endif +// +namespace tim +{ +using string_view_t = std::string; +} +// +#endif + +//--------------------------------------------------------------------------------------// diff --git a/include/timemory/macros/os.hpp b/include/timemory/macros/os.hpp new file mode 100644 index 0000000000..04cd1d997b --- /dev/null +++ b/include/timemory/macros/os.hpp @@ -0,0 +1,118 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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. + +#pragma once + +//======================================================================================// +// +// Operating System +// +//======================================================================================// + +#if defined(__x86_64__) +# if !defined(TIMEMORY_64BIT) +# define TIMEMORY_64BIT 1 +# endif +#else +# if !defined(TIMEMORY_32BIT) +# define TIMEMORY_32BIT 1 +# endif +#endif + +//--------------------------------------------------------------------------------------// +// timemory prefixed os preprocessor definitions +// +#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) +# if !defined(TIMEMORY_WINDOWS) +# define TIMEMORY_WINDOWS 1 +# endif +#elif defined(__APPLE__) || defined(__MACH__) +# if !defined(TIMEMORY_MACOS) +# define TIMEMORY_MACOS 1 +# endif +# if !defined(TIMEMORY_UNIX) +# define TIMEMORY_UNIX 1 +# endif +#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) +# if !defined(TIMEMORY_LINUX) +# define TIMEMORY_LINUX 1 +# endif +# if !defined(TIMEMORY_UNIX) +# define TIMEMORY_UNIX 1 +# endif +#elif defined(__unix__) || defined(__unix) || defined(unix) +# if !defined(TIMEMORY_UNIX) +# define TIMEMORY_UNIX 1 +# endif +#endif + +//--------------------------------------------------------------------------------------// +// non-timemory prefixed os preprocessor definitions (deprecated) +// +#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) +# if !defined(_WINDOWS) +# define _WINDOWS 1 +# endif +#elif defined(__APPLE__) || defined(__MACH__) +# if !defined(_MACOS) +# define _MACOS 1 +# endif +# if !defined(_UNIX) +# define _UNIX 1 +# endif +#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) +# if !defined(_LINUX) +# define _LINUX 1 +# endif +# if !defined(_UNIX) +# define _UNIX 1 +# endif +#elif defined(__unix__) || defined(__unix) || defined(unix) +# if !defined(_UNIX) +# define _UNIX 1 +# endif +#endif + +//--------------------------------------------------------------------------------------// +// this ensures that winnt.h never causes a 64-bit build to fail +// also solves issue with ws2def.h and winsock2.h: +// https://www.zachburlingame.com/2011/05/ +// resolving-redefinition-errors-betwen-ws2def-h-and-winsock-h/ +#if defined(_WINDOWS) +# if !defined(NOMINMAX) +# define NOMINMAX +# endif +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif +# if !defined(WIN32) +# define WIN32 +# endif +# if defined(TIMEMORY_USE_WINSOCK) +# include +# endif +# include +#endif + +//--------------------------------------------------------------------------------------// diff --git a/include/timemory/mpl/apply.hpp b/include/timemory/mpl/apply.hpp new file mode 100644 index 0000000000..a0690fe342 --- /dev/null +++ b/include/timemory/mpl/apply.hpp @@ -0,0 +1,684 @@ +// MIT License +// +// Copyright (c) 2020, The Regents of the University of California, +// through Lawrence Berkeley National Laboratory (subject to receipt of any +// required approvals from the U.S. Dept. of Energy). All rights reserved. +// +// 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 +// 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. + +#pragma once + +#include "timemory/macros/attributes.hpp" +#include "timemory/mpl/math.hpp" +#include "timemory/mpl/stl.hpp" + +#include +#include +#include +#include +#include +#include +#include + +//======================================================================================// + +namespace tim +{ +// clang-format off +namespace device { struct cpu; struct gpu; } // namespace device +// clang-format on + +namespace internal +{ +template +struct apply +{ + //----------------------------------------------------------------------------------// + // invoke a function with a tuple + // + template + static TIMEMORY_HOT_INLINE Ret invoke(Fn&& __f, Tuple&& __t, index_sequence) + { + return __f(std::get(std::forward(__t))...); + } + + //----------------------------------------------------------------------------------// + // prefix with _sep + // + template ::value, char> = 0> + static TIMEMORY_HOT_INLINE Ret join_tail(std::stringstream& _ss, const SepT& _sep, + Arg&& _arg) + { + _ss << _sep << std::forward(_arg); + return _ss.str(); + } + + //----------------------------------------------------------------------------------// + // prefix with _sep + // + template ::value, char> = 0> + static TIMEMORY_HOT_INLINE Ret join_tail(std::stringstream& _ss, const SepT& _sep, + Arg&& _arg, Args&&... __args) + { + _ss << _sep << std::forward(_arg); + return join_tail(_ss, _sep, std::forward(__args)...); + } + + //----------------------------------------------------------------------------------// + // don't prefix + // + template ::value, char> = 0> + static TIMEMORY_HOT_INLINE Ret join(std::stringstream& _ss, const SepT&, Arg&& _arg) + { + _ss << std::forward(_arg); + return _ss.str(); + } + + //----------------------------------------------------------------------------------// + // don't prefix + // + template ::value, char> = 0, + enable_if_t<(sizeof...(Args) > 0), int> = 0> + static TIMEMORY_HOT_INLINE Ret join(std::stringstream& _ss, const SepT& _sep, + Arg&& _arg, Args&&... __args) + { + _ss << std::forward(_arg); + return join_tail(_ss, _sep, std::forward(__args)...); + } + + //----------------------------------------------------------------------------------// +}; + +//======================================================================================// + +template <> +struct apply +{ + using Ret = void; + + //----------------------------------------------------------------------------------// + + template + struct get_index_of; + + template + struct get_index_of> + { + static constexpr int value = 0; + }; + + template + struct get_index_of> + { + static constexpr int value = 0; + }; + + template + struct get_index_of> + { + static constexpr int value = 0; + }; + + template + struct get_index_of> + { + static constexpr int value = 1 + get_index_of>::value; + }; + + template + struct get_index_of> + { + static_assert(sizeof...(Tail) != 0, "Error! Type not found!"); + }; + + //----------------------------------------------------------------------------------// + // invoke a function with a tuple + // + template + static TIMEMORY_HOT_INLINE Ret invoke(Fn&& __f, Tuple&& __t, index_sequence) + { + __f(std::get(std::forward(__t))...); + } + + //----------------------------------------------------------------------------------// + // temporary construction + // + template + static TIMEMORY_HOT_INLINE void construct(Args&&... _args) + { + Type(std::forward(_args)...); + } + + //----------------------------------------------------------------------------------// + // temporary construction + // + template + static TIMEMORY_HOT_INLINE void construct_tuple(std::tuple&& _args, + index_sequence...) + { + construct(std::get(_args)...); + } + + //----------------------------------------------------------------------------------// + + template