From efb6d766afd455f20af259cd47fb4182e51d0d32 Mon Sep 17 00:00:00 2001 From: "Jonathan R. Madsen" Date: Tue, 23 Nov 2021 02:53:14 -0600 Subject: [PATCH] Reorganization and critical trace support (#17) * Roctracer wall clock integration (#16) * Integrates roctracer values into wall-clock * Fixed scoping + timemory roctracer * Fixed data race in roctracer * Synchronized HIP API on main thread - Cache hip activity callbacks and execute on main thread - Minor updates to transpose * Debugging + MPI + transpose updates * PTL + HSA and timemory + kernel timing - PTL usage fixed HSA + timemory issues bc we could control the thread destruction - Fixed laps counting in roctracer callbacks * Ignore select HIP API types - The ignored API types are ignored because there appears to be a bug which causes the "end" callback to be labeled as begin - hipDeviceEnablePeerAccess - hipImportExternalMemory - hipDestroyExternalMemory * Tweaks to PTL config * Timemory update + pid-prefix w/ mpi headers - %pid%- prefix with mpi headers - timemory submodule update * CMake + critical trace + reorganize library source - clang-tidy tweaks - cmake function updates to use hosttrace_ prefix - update gitignore - cmake HOSTTRACE_MAX_THREADS option - Formatting.cmake - cleaned up MacroUtilities.cmake - PTL submodule + usage - tweak to Findroctracer.cmake - MT transpose - Updated PTL submodule - Updated timemory submodule - fix to hosttrace return value type if type not found - reorganized library source code - support for critical trace * Remove bits/stdint-uintn.h headers * Rename + config + depth + critical path - rename hosttrace_timemory_data to instrumentation_bundles - rename hosttrace_bundle_t to main_bundle_t - rename bundle_t to instrumentation_bundle_t - rework of configuration setup - critical_trace write directly to file option - tweaked depth calculation - updated timemory submodule - improved parallel support in roctracer callbacks - working critical_trace - perfetto device-critical-trace and host-critical-trace categories - made transpose example parallel - made parallel-overhead example a bit uneven - relocated LTO activation * Fixed duplicates in perfetto critical-trace * reworked critical trace support - substantial perf improvement (30-45 min -> 30 sec) - changes to configuration (new and removed options) * Removed "%pid%-" output prefix in mpi_gotcha * Update timemory submodule [ROCm/rocprofiler-systems commit: 752424efc261139d5b4ab76a32ce5c9646030602] --- projects/rocprofiler-systems/.clang-tidy | 1 + .../rocprofiler-systems/.cmake-format.yaml | 2 +- projects/rocprofiler-systems/.gitignore | 3 + projects/rocprofiler-systems/.gitmodules | 3 + projects/rocprofiler-systems/CMakeLists.txt | 131 +- .../cmake/BuildSettings.cmake | 51 +- .../rocprofiler-systems/cmake/Compilers.cmake | 4 +- .../cmake/Formatting.cmake | 60 + .../cmake/MacroUtilities.cmake | 1096 +------ .../cmake/Modules/FindBoost.cmake | 2758 +++++++++++++++++ .../cmake/Modules/Findroctracer.cmake | 4 - .../rocprofiler-systems/cmake/Packages.cmake | 122 +- .../parallel-overhead/parallel-overhead.cpp | 5 +- .../examples/transpose/transpose.cpp | 56 +- projects/rocprofiler-systems/external/PTL | 1 + .../rocprofiler-systems/external/timemory | 2 +- .../rocprofiler-systems/include/hosttrace.hpp | 35 +- .../rocprofiler-systems/include/library.hpp | 353 +-- .../include/library/api.hpp | 44 + .../include/library/common.hpp | 50 + .../include/library/config.hpp | 163 + .../include/library/critical_trace.hpp | 209 ++ .../include/library/debug.hpp | 80 + .../include/library/defines.hpp.in | 42 + .../include/library/dynamic_library.hpp | 71 + .../include/library/fork_gotcha.hpp | 48 + .../include/library/hosttrace_component.hpp | 43 + .../include/library/mpi_gotcha.hpp | 54 + .../include/library/perfetto.hpp | 91 + .../include/library/ptl.hpp | 55 + .../include/{ => library}/roctracer.hpp | 27 + .../include/library/roctracer_callbacks.hpp | 96 + .../include/library/state.hpp | 38 + .../include/library/thread_data.hpp | 126 + .../include/library/timemory.hpp | 63 + .../rocprofiler-systems/scripts/.LICENSE.hpp | 29 + .../rocprofiler-systems/src/hosttrace.cpp | 35 +- .../details.cpp} | 40 +- projects/rocprofiler-systems/src/library.cpp | 338 +- .../src/library/config.cpp | 528 ++++ .../src/library/critical_trace.cpp | 1300 ++++++++ .../src/library/fork_gotcha.cpp | 46 + .../src/library/hosttrace_component.cpp | 50 + .../{libmisc.cpp => library/mpi_gotcha.cpp} | 109 +- .../src/library/perfetto.cpp | 35 + .../rocprofiler-systems/src/library/ptl.cpp | 80 + .../src/library/roctracer.cpp | 283 ++ .../src/library/roctracer_callbacks.cpp | 599 ++++ .../src/library/thread_data.cpp | 36 + .../src/library/timemory.cpp | 31 + .../rocprofiler-systems/src/roctracer.cpp | 645 ---- .../rocprofiler-systems/tests/CMakeLists.txt | 8 +- 52 files changed, 7785 insertions(+), 2394 deletions(-) create mode 100644 projects/rocprofiler-systems/cmake/Formatting.cmake create mode 100644 projects/rocprofiler-systems/cmake/Modules/FindBoost.cmake create mode 160000 projects/rocprofiler-systems/external/PTL create mode 100644 projects/rocprofiler-systems/include/library/api.hpp create mode 100644 projects/rocprofiler-systems/include/library/common.hpp create mode 100644 projects/rocprofiler-systems/include/library/config.hpp create mode 100644 projects/rocprofiler-systems/include/library/critical_trace.hpp create mode 100644 projects/rocprofiler-systems/include/library/debug.hpp create mode 100644 projects/rocprofiler-systems/include/library/defines.hpp.in create mode 100644 projects/rocprofiler-systems/include/library/dynamic_library.hpp create mode 100644 projects/rocprofiler-systems/include/library/fork_gotcha.hpp create mode 100644 projects/rocprofiler-systems/include/library/hosttrace_component.hpp create mode 100644 projects/rocprofiler-systems/include/library/mpi_gotcha.hpp create mode 100644 projects/rocprofiler-systems/include/library/perfetto.hpp create mode 100644 projects/rocprofiler-systems/include/library/ptl.hpp rename projects/rocprofiler-systems/include/{ => library}/roctracer.hpp (55%) create mode 100644 projects/rocprofiler-systems/include/library/roctracer_callbacks.hpp create mode 100644 projects/rocprofiler-systems/include/library/state.hpp create mode 100644 projects/rocprofiler-systems/include/library/thread_data.hpp create mode 100644 projects/rocprofiler-systems/include/library/timemory.hpp create mode 100644 projects/rocprofiler-systems/scripts/.LICENSE.hpp rename projects/rocprofiler-systems/src/{hosttrace-details.cpp => hosttrace/details.cpp} (94%) create mode 100644 projects/rocprofiler-systems/src/library/config.cpp create mode 100644 projects/rocprofiler-systems/src/library/critical_trace.cpp create mode 100644 projects/rocprofiler-systems/src/library/fork_gotcha.cpp create mode 100644 projects/rocprofiler-systems/src/library/hosttrace_component.cpp rename projects/rocprofiler-systems/src/{libmisc.cpp => library/mpi_gotcha.cpp} (54%) create mode 100644 projects/rocprofiler-systems/src/library/perfetto.cpp create mode 100644 projects/rocprofiler-systems/src/library/ptl.cpp create mode 100644 projects/rocprofiler-systems/src/library/roctracer.cpp create mode 100644 projects/rocprofiler-systems/src/library/roctracer_callbacks.cpp create mode 100644 projects/rocprofiler-systems/src/library/thread_data.cpp create mode 100644 projects/rocprofiler-systems/src/library/timemory.cpp delete mode 100644 projects/rocprofiler-systems/src/roctracer.cpp diff --git a/projects/rocprofiler-systems/.clang-tidy b/projects/rocprofiler-systems/.clang-tidy index c5b7cac1ee..ec883837c3 100644 --- a/projects/rocprofiler-systems/.clang-tidy +++ b/projects/rocprofiler-systems/.clang-tidy @@ -18,6 +18,7 @@ modernize-*,\ -modernize-use-using,\ -modernize-use-auto,\ -modernize-concat-nested-namespaces,\ +-modernize-use-nodiscard,\ performance-*,\ readability-*,\ -readability-function-size,\ diff --git a/projects/rocprofiler-systems/.cmake-format.yaml b/projects/rocprofiler-systems/.cmake-format.yaml index 1aa0292819..67c19c1513 100644 --- a/projects/rocprofiler-systems/.cmake-format.yaml +++ b/projects/rocprofiler-systems/.cmake-format.yaml @@ -1,6 +1,6 @@ parse: additional_commands: - checkout_git_submodule: + hosttrace_checkout_git_submodule: flags: - RECURSIVE kwargs: diff --git a/projects/rocprofiler-systems/.gitignore b/projects/rocprofiler-systems/.gitignore index 5d4dfd2c2c..d50c065282 100644 --- a/projects/rocprofiler-systems/.gitignore +++ b/projects/rocprofiler-systems/.gitignore @@ -33,3 +33,6 @@ /build* /.vscode +/.cache +/.clangd +/compile_commands.json diff --git a/projects/rocprofiler-systems/.gitmodules b/projects/rocprofiler-systems/.gitmodules index adc275f04b..6a4abb2132 100644 --- a/projects/rocprofiler-systems/.gitmodules +++ b/projects/rocprofiler-systems/.gitmodules @@ -10,3 +10,6 @@ [submodule "external/dyninst"] path = external/dyninst url = https://github.com/jrmadsen/dyninst.git +[submodule "external/PTL"] + path = external/PTL + url = https://github.com/jrmadsen/PTL.git diff --git a/projects/rocprofiler-systems/CMakeLists.txt b/projects/rocprofiler-systems/CMakeLists.txt index 7ea87ca71c..1b1456ea8b 100644 --- a/projects/rocprofiler-systems/CMakeLists.txt +++ b/projects/rocprofiler-systems/CMakeLists.txt @@ -50,49 +50,124 @@ 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(CMAKE_INSTALL_RPATH_USE_LINK_PATH "Enable rpath to linked libraries" ON) -add_option(HOSTTRACE_USE_CLANG_TIDY "Enable clang-tidy" OFF) -add_option(HOSTTRACE_USE_MPI "Enable MPI support" OFF) -add_option(HOSTTRACE_CUSTOM_DATA_SOURCE "Enable custom data source" OFF) -add_option(HOSTTRACE_USE_ROCTRACER "Enable roctracer support" ON) -add_option(HOSTTRACE_BUILD_DYNINST "Build dyninst from submodule" OFF) -add_option(HOSTTRACE_USE_MPI_HEADERS - "Enable wrapping MPI functions w/o enabling MPI dependency" OFF) +hosttrace_add_feature(CMAKE_CXX_STANDARD "CXX language standard") +hosttrace_add_option(CMAKE_CXX_STANDARD_REQUIRED "Require C++ language standard" ON) +hosttrace_add_option(CMAKE_CXX_EXTENSIONS "Compiler specific language extensions" OFF) +hosttrace_add_option(CMAKE_INSTALL_RPATH_USE_LINK_PATH "Enable rpath to linked libraries" + ON) +hosttrace_add_option(HOSTTRACE_USE_CLANG_TIDY "Enable clang-tidy" OFF) +hosttrace_add_option(HOSTTRACE_USE_MPI "Enable MPI support" OFF) +hosttrace_add_option(HOSTTRACE_CUSTOM_DATA_SOURCE "Enable custom data source" OFF) +hosttrace_add_option(HOSTTRACE_USE_ROCTRACER "Enable roctracer support" ON) +hosttrace_add_option(HOSTTRACE_BUILD_DYNINST "Build dyninst from submodule" OFF) +hosttrace_add_option(HOSTTRACE_USE_MPI_HEADERS + "Enable wrapping MPI functions w/o enabling MPI dependency" OFF) + +include(ProcessorCount) +processorcount(HOSTTRACE_PROCESSOR_COUNT) +math(EXPR HOSTTRACE_THREAD_COUNT "8 * ${HOSTTRACE_PROCESSOR_COUNT}") +set(HOSTTRACE_MAX_THREADS + "${HOSTTRACE_THREAD_COUNT}" + CACHE + STRING + "Maximum number of threads in the host application. Likely only needs to be increased if host app does not use thread-pool but creates many threads" + ) +hosttrace_add_feature( + HOSTTRACE_MAX_THREADS + "Maximum number of total threads supported in the host application (default: 8 * nproc)" + ) # ensure synced set(TIMEMORY_USE_MPI ${HOSTTRACE_USE_MPI} CACHE BOOL "Enable MPI support" FORCE) +# default visibility settings +set(CMAKE_C_VISIBILITY_PRESET "default") +set(CMAKE_CXX_VISIBILITY_PRESET "default") +set(CMAKE_VISIBILITY_INLINES_HIDDEN OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +include(Formatting) # format target include(Packages) # finds third-party libraries +if(HOSTTRACE_USE_ROCTRACER) + find_package(HIP QUIET) + if(HIP_VERSION_MAJOR GREATER_EQUAL 4 AND HIP_VERSION_MINOR GREATER 3) + set(roctracer_kfdwrapper_LIBRARY) + endif() +else() + set(HIP_VERSION "0.0.0") + set(HIP_VERSION_MAJOR 0) + set(HIP_VERSION_MINOR 0) + set(HIP_VERSION_PATCH 0) +endif() + +configure_file(${PROJECT_SOURCE_DIR}/include/library/defines.hpp.in + ${PROJECT_BINARY_DIR}/include/library/defines.hpp @ONLY) + hosttrace_activate_clang_tidy() +# custom visibility settings set(CMAKE_C_VISIBILITY_PRESET "hidden") set(CMAKE_CXX_VISIBILITY_PRESET "hidden") set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) +if(HOSTTRACE_BUILD_LTO) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) +endif() + # ------------------------------------------------------------------------------# # # hosttrace-library target # # ------------------------------------------------------------------------------# -add_library( - hosttrace-library SHARED - ${CMAKE_CURRENT_LIST_DIR}/src/library.cpp ${CMAKE_CURRENT_LIST_DIR}/src/libmisc.cpp - ${CMAKE_CURRENT_LIST_DIR}/include/library.hpp ${perfetto_DIR}/sdk/perfetto.cc) +set(library_sources + ${CMAKE_CURRENT_LIST_DIR}/src/library.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/config.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/critical_trace.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/fork_gotcha.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/hosttrace_component.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/mpi_gotcha.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/perfetto.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/ptl.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/thread_data.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/timemory.cpp + ${perfetto_DIR}/sdk/perfetto.cc) + +set(library_headers + ${CMAKE_CURRENT_LIST_DIR}/include/library.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/api.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/config.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/common.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/critical_trace.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/debug.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/fork_gotcha.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/hosttrace_component.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/mpi_gotcha.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/perfetto.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/ptl.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/state.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/thread_data.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/timemory.hpp + ${perfetto_DIR}/sdk/perfetto.h) + +if(NOT TIMEMORY_USE_PERFETTO) + +endif() + +add_library(hosttrace-library SHARED ${library_sources} ${library_headers}) if(HOSTTRACE_USE_ROCTRACER) target_sources( - hosttrace-library PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/roctracer.hpp - ${CMAKE_CURRENT_LIST_DIR}/src/roctracer.cpp) + hosttrace-library + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/library/roctracer.hpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/roctracer.cpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/roctracer_callbacks.hpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/roctracer_callbacks.cpp) endif() -target_include_directories(hosttrace-library PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include) - target_include_directories(hosttrace-library SYSTEM PRIVATE ${perfetto_DIR}/sdk) target_compile_definitions( @@ -101,10 +176,12 @@ target_compile_definitions( target_link_libraries( hosttrace-library - PRIVATE hosttrace::hosttrace-threading + PRIVATE hosttrace::hosttrace-headers + hosttrace::hosttrace-threading hosttrace::hosttrace-compile-options hosttrace::hosttrace-roctracer hosttrace::hosttrace-mpi + hosttrace::hosttrace-ptl $ $ $ @@ -134,13 +211,13 @@ 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) + ${CMAKE_CURRENT_LIST_DIR}/src/hosttrace/details.cpp) target_link_libraries( hosttrace-exe - PRIVATE hosttrace::hosttrace-dyninst hosttrace::hosttrace-compile-options + PRIVATE hosttrace::hosttrace-headers + hosttrace::hosttrace-dyninst + hosttrace::hosttrace-compile-options $ $,hosttrace::hosttrace-sanitizer,>) @@ -211,3 +288,11 @@ add_subdirectory(tests) # ------------------------------------------------------------------------------# include(ConfigCPack) + +# ------------------------------------------------------------------------------# +# +# config info +# +# ------------------------------------------------------------------------------# + +hosttrace_print_features() diff --git a/projects/rocprofiler-systems/cmake/BuildSettings.cmake b/projects/rocprofiler-systems/cmake/BuildSettings.cmake index 369f81ace6..f3e59323cc 100644 --- a/projects/rocprofiler-systems/cmake/BuildSettings.cmake +++ b/projects/rocprofiler-systems/cmake/BuildSettings.cmake @@ -68,7 +68,7 @@ endif() # ----------------------------------------------------------------------------------------# # extra flags for debug information in debug or optimized binaries # -add_interface_library( +hosttrace_add_interface_library( hosttrace-compile-debuginfo "Attempts to set best flags for more expressive profiling information in debug or optimized binaries" ) @@ -108,7 +108,7 @@ endif() # ----------------------------------------------------------------------------------------# # non-debug optimizations # -add_interface_library(hosttrace-compile-extra "Extra optimization flags") +hosttrace_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" @@ -130,21 +130,16 @@ endif() # 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") +hosttrace_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) + set(HOSTTRACE_BUILD_LTO OFF) else() target_link_options(hosttrace-lto INTERFACE -flto) endif() @@ -161,8 +156,9 @@ 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") +hosttrace_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) @@ -176,15 +172,10 @@ 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") +hosttrace_add_interface_library(hosttrace-develop-options "Adds developer compiler flags") if(HOSTTRACE_BUILD_DEVELOPER) add_target_flag_if_avail( hosttrace-develop-options @@ -195,21 +186,15 @@ 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") +hosttrace_add_interface_library(hosttrace-default-visibility + "Adds -fvisibility=default compiler flag") +hosttrace_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 # @@ -235,9 +220,9 @@ set(HOSTTRACE_SANITIZER_TYPES 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_add_interface_library(hosttrace-sanitizer-compile-options + "Adds compiler flags for sanitizers") +hosttrace_add_interface_library( hosttrace-sanitizer "Adds compiler flags to enable ${HOSTTRACE_SANITIZER_TYPE} sanitizer (-fsanitizer=${HOSTTRACE_SANITIZER_TYPE})" ) @@ -248,8 +233,9 @@ 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})") + hosttrace_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) @@ -273,7 +259,6 @@ if(HOSTTRACE_USE_SANITIZER) endforeach() else() set(HOSTTRACE_USE_SANITIZER OFF) - inform_empty_interface(hosttrace-sanitizer "${HOSTTRACE_SANITIZER_TYPE} sanitizer") endif() if(MSVC) diff --git a/projects/rocprofiler-systems/cmake/Compilers.cmake b/projects/rocprofiler-systems/cmake/Compilers.cmake index 27d90a0568..129c0555a6 100644 --- a/projects/rocprofiler-systems/cmake/Compilers.cmake +++ b/projects/rocprofiler-systems/cmake/Compilers.cmake @@ -31,8 +31,8 @@ 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") +hosttrace_add_interface_library( + ${LIBNAME}-compile-options "Adds the standard set of compiler flags used by timemory") # ----------------------------------------------------------------------------------------# # macro converting string to list diff --git a/projects/rocprofiler-systems/cmake/Formatting.cmake b/projects/rocprofiler-systems/cmake/Formatting.cmake new file mode 100644 index 0000000000..1e14794d66 --- /dev/null +++ b/projects/rocprofiler-systems/cmake/Formatting.cmake @@ -0,0 +1,60 @@ +include_guard(DIRECTORY) + +# ----------------------------------------------------------------------------------------# +# +# Clang Tidy +# +# ----------------------------------------------------------------------------------------# + +# clang-tidy +macro(HOSTTRACE_ACTIVATE_CLANG_TIDY) + if(HOSTTRACE_USE_CLANG_TIDY) + find_program(CLANG_TIDY_COMMAND NAMES clang-tidy) + hosttrace_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() + +# ------------------------------------------------------------------------------# +# +# clang-format target +# +# ------------------------------------------------------------------------------# + +find_program(HOSTTRACE_CLANG_FORMAT_EXE NAMES clang-format-11 clang-format-mp-11 + clang-format) + +if(HOSTTRACE_CLANG_FORMAT_EXE) + file(GLOB_RECURSE sources ${PROJECT_SOURCE_DIR}/src/*.cpp) + file(GLOB_RECURSE headers ${PROJECT_SOURCE_DIR}/include/*.hpp) + file(GLOB_RECURSE examples ${PROJECT_SOURCE_DIR}/examples/*.cpp + ${PROJECT_SOURCE_DIR}/examples/*.hpp) + add_custom_target( + format-hosttrace + ${HOSTTRACE_CLANG_FORMAT_EXE} -i ${sources} ${headers} ${examples} + COMMENT "Running C++ formatter ${HOSTTRACE_CLANG_FORMAT_EXE}...") + if(NOT TARGET format) + add_custom_target(format) + endif() + add_dependencies(format format-hosttrace) +else() + message( + AUTHOR_WARNING + "clang-format could not be found. format build target not available.") +endif() diff --git a/projects/rocprofiler-systems/cmake/MacroUtilities.cmake b/projects/rocprofiler-systems/cmake/MacroUtilities.cmake index f1c69591a6..33a2a7acb7 100644 --- a/projects/rocprofiler-systems/cmake/MacroUtilities.cmake +++ b/projects/rocprofiler-systems/cmake/MacroUtilities.cmake @@ -11,13 +11,6 @@ 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 # ----------------------------------------------------------------------- @@ -99,48 +92,10 @@ macro(HOSTTRACE_RESTORE_VARIABLES _PREFIX) 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) +# function - hosttrace_capitalize - make a string capitalized (first letter is capital) +# usage: capitalize("SHARED" CShared) message(STATUS "-- CShared is \"${CShared}\"") $ -- +# CShared is "Shared" +function(HOSTTRACE_CAPITALIZE str var) # make string lower string(TOLOWER "${str}" str) string(SUBSTRING "${str}" 0 1 _first) @@ -152,92 +107,6 @@ function(CAPITALIZE str var) 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() # @@ -253,150 +122,8 @@ function(ADD_HOSTTRACE_TEST_TARGET) 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() +# macro hosttrace_checkout_git_submodule() # # Run "git submodule update" if a file in a submodule does not exist # @@ -405,7 +132,7 @@ endfunction() # 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) +function(HOSTTRACE_CHECKOUT_GIT_SUBMODULE) # parse args cmake_parse_arguments( CHECKOUT "RECURSIVE" @@ -474,7 +201,7 @@ function(CHECKOUT_GIT_SUBMODULE) 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(STATUS "function(hosttrace_checkout_git_submodule) failed.") message(FATAL_ERROR "Command: \"${_CMD}\"") else() set(_TEST_FILE_EXISTS ON) @@ -514,7 +241,7 @@ function(CHECKOUT_GIT_SUBMODULE) "${GIT_EXECUTABLE} clone -b ${CHECKOUT_REPO_BRANCH} ${CHECKOUT_ADDITIONAL_CMDS} ${CHECKOUT_REPO_URL} ${CHECKOUT_RELATIVE_PATH}" ) - message(STATUS "function(CHECKOUT_GIT_SUBMODULE) failed.") + message(STATUS "function(hosttrace_checkout_git_submodule) failed.") message(FATAL_ERROR "Command: \"${_CMD}\"") else() set(_TEST_FILE_EXISTS ON) @@ -552,47 +279,21 @@ endfunction() # ----------------------------------------------------------------------------------------# # macro to add an interface lib # -macro(ADD_INTERFACE_LIBRARY _TARGET) +macro(HOSTTRACE_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) +function(HOSTTRACE_ADD_RPATH) set(_DIRS) foreach(_ARG ${ARGN}) if(EXISTS "${_ARG}" AND IS_DIRECTORY "${_ARG}") @@ -613,718 +314,12 @@ function(ADD_RPATH) 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) +function(HOSTTRACE_ADD_FEATURE _var _description) set(EXTRA_DESC "") foreach(currentArg ${ARGN}) if(NOT "${currentArg}" STREQUAL "${_var}" @@ -1356,12 +351,12 @@ 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) +function(HOSTTRACE_ADD_OPTION _NAME _MESSAGE _DEFAULT) option(${_NAME} "${_MESSAGE}" ${_DEFAULT}) if("NO_FEATURE" IN_LIST ARGN) mark_as_advanced(${_NAME}) else() - add_feature(${_NAME} "${_MESSAGE}") + hosttrace_add_feature(${_NAME} "${_MESSAGE}") if(HOSTTRACE_BUILD_DOCS) set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_OPTIONS_DOC "${_NAME}` | ${_MESSAGE} |") @@ -1378,7 +373,7 @@ endfunction() # ----------------------------------------------------------------------------------------# # function print_enabled_features() Print enabled features plus their docstrings. # -function(PRINT_ENABLED_FEATURES) +function(HOSTTRACE_PRINT_ENABLED_FEATURES) set(_basemsg "The following features are defined/enabled (+):") set(_currentFeatureText "${_basemsg}") get_property(_features GLOBAL PROPERTY ${PROJECT_NAME}_FEATURES) @@ -1402,7 +397,7 @@ function(PRINT_ENABLED_FEATURES) string(REGEX REPLACE "^${PROJECT_NAME}_USE_" "" _feature_tmp "${_feature}") string(TOLOWER "${_feature_tmp}" _feature_tmp_l) - capitalize("${_feature_tmp}" _feature_tmp_c) + hosttrace_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 "") @@ -1426,7 +421,7 @@ endfunction() # ----------------------------------------------------------------------------------------# # function print_disabled_features() Print disabled features plus their docstrings. # -function(PRINT_DISABLED_FEATURES) +function(HOSTTRACE_PRINT_DISABLED_FEATURES) set(_basemsg "The following features are NOT defined/enabled (-):") set(_currentFeatureText "${_basemsg}") get_property(_features GLOBAL PROPERTY ${PROJECT_NAME}_FEATURES) @@ -1452,65 +447,12 @@ function(PRINT_DISABLED_FEATURES) 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() +function(HOSTTRACE_PRINT_FEATURES) + hosttrace_print_enabled_features() + hosttrace_print_disabled_features() endfunction() # ----------------------------------------------------------------------------------------# diff --git a/projects/rocprofiler-systems/cmake/Modules/FindBoost.cmake b/projects/rocprofiler-systems/cmake/Modules/FindBoost.cmake new file mode 100644 index 0000000000..acf44b8ec9 --- /dev/null +++ b/projects/rocprofiler-systems/cmake/Modules/FindBoost.cmake @@ -0,0 +1,2758 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying file +# Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindBoost +--------- + +Find Boost include dirs and libraries + +Use this module by invoking find_package with the form:: + + find_package(Boost + [version] [EXACT] # Minimum or EXACT version e.g. 1.67.0 + [REQUIRED] # Fail with error if Boost is not found + [COMPONENTS ...] # Boost libraries by their canonical name + # e.g. "date_time" for "libboost_date_time" + [OPTIONAL_COMPONENTS ...] + # Optional Boost libraries by their canonical name) + ) # e.g. "date_time" for "libboost_date_time" + +This module finds headers and requested component libraries OR a CMake +package configuration file provided by a "Boost CMake" build. For the +latter case skip to the "Boost CMake" section below. For the former +case results are reported in variables:: + + Boost_FOUND - True if headers and requested libraries were found + Boost_INCLUDE_DIRS - Boost include directories + Boost_LIBRARY_DIRS - Link directories for Boost libraries + Boost_LIBRARIES - Boost component libraries to be linked + Boost__FOUND - True if component was found ( is upper-case) + Boost__LIBRARY - Libraries to link for component (may include + target_link_libraries debug/optimized keywords) + Boost_VERSION_MACRO - BOOST_VERSION value from boost/version.hpp + Boost_VERSION_STRING - Boost version number in x.y.z format + Boost_VERSION - if CMP0093 NEW => same as Boost_VERSION_STRING + if CMP0093 OLD or unset => same as Boost_VERSION_MACRO + Boost_LIB_VERSION - Version string appended to library filenames + Boost_VERSION_MAJOR - Boost major version number (X in X.y.z) + alias: Boost_MAJOR_VERSION + Boost_VERSION_MINOR - Boost minor version number (Y in x.Y.z) + alias: Boost_MINOR_VERSION + Boost_VERSION_PATCH - Boost subminor version number (Z in x.y.Z) + alias: Boost_SUBMINOR_VERSION + Boost_VERSION_COUNT - Amount of version components (3) + Boost_LIB_DIAGNOSTIC_DEFINITIONS (Windows) + - Pass to add_definitions() to have diagnostic + information about Boost's automatic linking + displayed during compilation + +Note that Boost Python components require a Python version suffix +(Boost 1.67 and later), e.g. ``python36`` or ``python27`` for the +versions built against Python 3.6 and 2.7, respectively. This also +applies to additional components using Python including +``mpi_python`` and ``numpy``. Earlier Boost releases may use +distribution-specific suffixes such as ``2``, ``3`` or ``2.7``. +These may also be used as suffixes, but note that they are not +portable. + +This module reads hints about search locations from variables:: + + BOOST_ROOT - Preferred installation prefix + (or BOOSTROOT) + BOOST_INCLUDEDIR - Preferred include directory e.g. /include + BOOST_LIBRARYDIR - Preferred library directory e.g. /lib + Boost_NO_SYSTEM_PATHS - Set to ON to disable searching in locations not + specified by these hint variables. Default is OFF. + Boost_ADDITIONAL_VERSIONS + - List of Boost versions not known to this module + (Boost install locations may contain the version) + +and saves search results persistently in CMake cache entries:: + + Boost_INCLUDE_DIR - Directory containing Boost headers + Boost_LIBRARY_DIR_RELEASE - Directory containing release Boost libraries + Boost_LIBRARY_DIR_DEBUG - Directory containing debug Boost libraries + Boost__LIBRARY_DEBUG - Component library debug variant + Boost__LIBRARY_RELEASE - Component library release variant + +The following :prop_tgt:`IMPORTED` targets are also defined:: + + Boost::headers - Target for header-only dependencies + (Boost include directory) + alias: Boost::boost + Boost:: - Target for specific component dependency + (shared or static library); is lower- + case + Boost::diagnostic_definitions - interface target to enable diagnostic + information about Boost's automatic linking + during compilation (adds BOOST_LIB_DIAGNOSTIC) + Boost::disable_autolinking - interface target to disable automatic + linking with MSVC (adds BOOST_ALL_NO_LIB) + Boost::dynamic_linking - interface target to enable dynamic linking + linking with MSVC (adds BOOST_ALL_DYN_LINK) + +Implicit dependencies such as ``Boost::filesystem`` requiring +``Boost::system`` will be automatically detected and satisfied, even +if system is not specified when using :command:`find_package` and if +``Boost::system`` is not added to :command:`target_link_libraries`. If using +``Boost::thread``, then ``Threads::Threads`` will also be added automatically. + +It is important to note that the imported targets behave differently +than variables created by this module: multiple calls to +:command:`find_package(Boost)` in the same directory or sub-directories with +different options (e.g. static or shared) will not override the +values of the targets created by the first call. + +Users may set these hints or results as ``CACHE`` entries. Projects +should not read these entries directly but instead use the above +result variables. Note that some hint names start in upper-case +"BOOST". One may specify these as environment variables if they are +not specified as CMake variables or cache entries. + +This module first searches for the ``Boost`` header files using the above +hint variables (excluding ``BOOST_LIBRARYDIR``) and saves the result in +``Boost_INCLUDE_DIR``. Then it searches for requested component libraries +using the above hints (excluding ``BOOST_INCLUDEDIR`` and +``Boost_ADDITIONAL_VERSIONS``), "lib" directories near ``Boost_INCLUDE_DIR``, +and the library name configuration settings below. It saves the +library directories in ``Boost_LIBRARY_DIR_DEBUG`` and +``Boost_LIBRARY_DIR_RELEASE`` and individual library +locations in ``Boost__LIBRARY_DEBUG`` and ``Boost__LIBRARY_RELEASE``. +When one changes settings used by previous searches in the same build +tree (excluding environment variables) this module discards previous +search results affected by the changes and searches again. + +Boost libraries come in many variants encoded in their file name. +Users or projects may tell this module which variant to find by +setting variables:: + + Boost_USE_DEBUG_LIBS - Set to ON or OFF to specify whether to search + and use the debug libraries. Default is ON. + Boost_USE_RELEASE_LIBS - Set to ON or OFF to specify whether to search + and use the release libraries. Default is ON. + Boost_USE_MULTITHREADED - Set to OFF to use the non-multithreaded + libraries ('mt' tag). Default is ON. + Boost_USE_STATIC_LIBS - Set to ON to force the use of the static + libraries. Default is OFF. + Boost_USE_STATIC_RUNTIME - Set to ON or OFF to specify whether to use + libraries linked statically to the C++ runtime + ('s' tag). Default is platform dependent. + Boost_USE_DEBUG_RUNTIME - Set to ON or OFF to specify whether to use + libraries linked to the MS debug C++ runtime + ('g' tag). Default is ON. + Boost_USE_DEBUG_PYTHON - Set to ON to use libraries compiled with a + debug Python build ('y' tag). Default is OFF. + Boost_USE_STLPORT - Set to ON to use libraries compiled with + STLPort ('p' tag). Default is OFF. + Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS + - Set to ON to use libraries compiled with + STLPort deprecated "native iostreams" + ('n' tag). Default is OFF. + Boost_COMPILER - Set to the compiler-specific library suffix + (e.g. "-gcc43"). Default is auto-computed + for the C++ compiler in use. A list may be + used if multiple compatible suffixes should + be tested for, in decreasing order of + preference. + Boost_ARCHITECTURE - Set to the architecture-specific library suffix + (e.g. "-x64"). Default is auto-computed for the + C++ compiler in use. + Boost_THREADAPI - Suffix for "thread" component library name, + such as "pthread" or "win32". Names with + and without this suffix will both be tried. + Boost_NAMESPACE - Alternate namespace used to build boost with + e.g. if set to "myboost", will search for + myboost_thread instead of boost_thread. + +Other variables one may set to control this module are:: + + Boost_DEBUG - Set to ON to enable debug output from FindBoost. + Please enable this before filing any bug report. + Boost_REALPATH - Set to ON to resolve symlinks for discovered + libraries to assist with packaging. For example, + the "system" component library may be resolved to + "/usr/lib/libboost_system.so.1.67.0" instead of + "/usr/lib/libboost_system.so". This does not + affect linking and should not be enabled unless + the user needs this information. + Boost_LIBRARY_DIR - Default value for Boost_LIBRARY_DIR_RELEASE and + Boost_LIBRARY_DIR_DEBUG. + +On Visual Studio and Borland compilers Boost headers request automatic +linking to corresponding libraries. This requires matching libraries +to be linked explicitly or available in the link library search path. +In this case setting ``Boost_USE_STATIC_LIBS`` to ``OFF`` may not achieve +dynamic linking. Boost automatic linking typically requests static +libraries with a few exceptions (such as ``Boost.Python``). Use:: + + add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS}) + +to ask Boost to report information about automatic linking requests. + +Example to find Boost headers only:: + + find_package(Boost 1.36.0) + if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) + add_executable(foo foo.cc) + endif() + +Example to find Boost libraries and use imported targets:: + + find_package(Boost 1.56 REQUIRED COMPONENTS + date_time filesystem iostreams) + add_executable(foo foo.cc) + target_link_libraries(foo Boost::date_time Boost::filesystem + Boost::iostreams) + +Example to find Boost Python 3.6 libraries and use imported targets:: + + find_package(Boost 1.67 REQUIRED COMPONENTS + python36 numpy36) + add_executable(foo foo.cc) + target_link_libraries(foo Boost::python36 Boost::numpy36) + +Example to find Boost headers and some *static* (release only) libraries:: + + set(Boost_USE_STATIC_LIBS ON) # only find static libs + set(Boost_USE_DEBUG_LIBS OFF) # ignore debug libs and + set(Boost_USE_RELEASE_LIBS ON) # only find release libs + set(Boost_USE_MULTITHREADED ON) + set(Boost_USE_STATIC_RUNTIME OFF) + find_package(Boost 1.66.0 COMPONENTS date_time filesystem system ...) + if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) + add_executable(foo foo.cc) + target_link_libraries(foo ${Boost_LIBRARIES}) + endif() + +Boost CMake +^^^^^^^^^^^ + +If Boost was built using the boost-cmake project or from Boost 1.70.0 on +it provides a package configuration file for use with find_package's config mode. +This module looks for the package configuration file called +``BoostConfig.cmake`` or ``boost-config.cmake`` and stores the result in +``CACHE`` entry "Boost_DIR". If found, the package configuration file is loaded +and this module returns with no further action. See documentation of +the Boost CMake package configuration for details on what it provides. + +Set ``Boost_NO_BOOST_CMAKE`` to ``ON``, to disable the search for boost-cmake. +#]=======================================================================] + +# The FPHSA helper provides standard way of reporting final search results to the user +# including the version and component checks. +include(FindPackageHandleStandardArgs) + +# Save project's policies +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) # if IN_LIST +if(NOT CMAKE_VERSION VERSION_LESS 3.17) + cmake_policy(SET CMP0102 NEW) # if mark_as_advanced(non_cache_var) +endif() + +function(_boost_get_existing_target component target_var) + set(names "${component}") + if(component MATCHES "^([a-z_]*)(python|numpy)([1-9])\\.?([0-9])?$") + # handle pythonXY and numpyXY versioned components and also python X.Y, mpi_python + # etc. + list( + APPEND names "${CMAKE_MATCH_1}${CMAKE_MATCH_2}" # python + "${CMAKE_MATCH_1}${CMAKE_MATCH_2}${CMAKE_MATCH_3}" # pythonX + "${CMAKE_MATCH_1}${CMAKE_MATCH_2}${CMAKE_MATCH_3}${CMAKE_MATCH_4}" # pythonXY + ) + endif() + # https://github.com/boost-cmake/boost-cmake uses boost::file_system etc. So handle + # similar constructions of target names + string(TOLOWER "${component}" lower_component) + list(APPEND names "${lower_component}") + foreach(prefix Boost boost) + foreach(name IN LISTS names) + if(TARGET "${prefix}::${name}") + # The target may be an INTERFACE library that wraps around a single other + # target for compatibility. Unwrap this layer so we can extract real + # info. + if("${name}" MATCHES "^(python|numpy|mpi_python)([1-9])([0-9])$") + set(name_nv "${CMAKE_MATCH_1}") + if(TARGET "${prefix}::${name_nv}") + get_property( + type + TARGET "${prefix}::${name}" + PROPERTY TYPE) + if(type STREQUAL "INTERFACE_LIBRARY") + get_property( + lib + TARGET "${prefix}::${name}" + PROPERTY INTERFACE_LINK_LIBRARIES) + if("${lib}" STREQUAL "${prefix}::${name_nv}") + set(${target_var} + "${prefix}::${name_nv}" + PARENT_SCOPE) + return() + endif() + endif() + endif() + endif() + set(${target_var} + "${prefix}::${name}" + PARENT_SCOPE) + return() + endif() + endforeach() + endforeach() + set(${target_var} + "" + PARENT_SCOPE) +endfunction() + +function(_boost_get_canonical_target_name component target_var) + string(TOLOWER "${component}" component) + if(component MATCHES "^([a-z_]*)(python|numpy)([1-9])\\.?([0-9])?$") + # handle pythonXY and numpyXY versioned components and also python X.Y, mpi_python + # etc. + set(${target_var} + "Boost::${CMAKE_MATCH_1}${CMAKE_MATCH_2}" + PARENT_SCOPE) + else() + set(${target_var} + "Boost::${component}" + PARENT_SCOPE) + endif() +endfunction() + +macro(_boost_set_in_parent_scope name value) + # Set a variable in parent scope and make it visibile in current scope + set(${name} + "${value}" + PARENT_SCOPE) + set(${name} "${value}") +endmacro() + +macro(_boost_set_if_unset name value) + if(NOT ${name}) + _boost_set_in_parent_scope(${name} "${value}") + endif() +endmacro() + +macro(_boost_set_cache_if_unset name value) + if(NOT ${name}) + set(${name} + "${value}" + CACHE STRING "" FORCE) + endif() +endmacro() + +macro(_boost_append_include_dir target) + get_target_property(inc "${target}" INTERFACE_INCLUDE_DIRECTORIES) + if(inc) + list(APPEND include_dirs "${inc}") + endif() +endmacro() + +function(_boost_set_legacy_variables_from_config) + # Set legacy variables for compatibility if not set + set(include_dirs "") + set(library_dirs "") + set(libraries "") + # Header targets Boost::headers or Boost::boost + foreach(comp headers boost) + _boost_get_existing_target(${comp} target) + if(TARGET) + _boost_append_include_dir("${target}") + endif() + endforeach() + # Library targets + foreach(comp IN LISTS Boost_FIND_COMPONENTS) + string(TOUPPER ${comp} uppercomp) + # Overwrite if set + _boost_set_in_parent_scope(Boost_${uppercomp}_FOUND "${Boost_${comp}_FOUND}") + if(Boost_${comp}_FOUND) + _boost_get_existing_target(${comp} target) + if(NOT TARGET) + if(Boost_DEBUG OR Boost_VERBOSE) + message( + WARNING + "Could not find imported target for required component '${comp}'. Legacy variables for this component might be missing. Refer to the documentation of your Boost installation for help on variables to use." + ) + endif() + continue() + endif() + _boost_append_include_dir("${target}") + _boost_set_if_unset(Boost_${uppercomp}_LIBRARY "${target}") + _boost_set_if_unset( + Boost_${uppercomp}_LIBRARIES "${target}") # Very old legacy variable + list(APPEND libraries "${target}") + get_property( + type + TARGET "${target}" + PROPERTY TYPE) + if(NOT type STREQUAL "INTERFACE_LIBRARY") + foreach(cfg RELEASE DEBUG) + get_target_property(lib ${target} IMPORTED_LOCATION_${cfg}) + if(lib) + get_filename_component(lib_dir "${lib}" DIRECTORY) + list(APPEND library_dirs ${lib_dir}) + _boost_set_cache_if_unset(Boost_${uppercomp}_LIBRARY_${cfg} + "${lib}") + endif() + endforeach() + elseif(Boost_DEBUG OR Boost_VERBOSE) + # For projects using only the Boost::* targets this warning can be safely + # ignored. + if(NOT Boost_FIND_QUIETLY) + message( + WARNING + "Imported target '${target}' for required component '${comp}' has no artifact. Legacy variables for this component might be missing. Refer to the documentation of your Boost installation for help on variables to use." + ) + endif() + endif() + _boost_get_canonical_target_name("${comp}" canonical_target) + if(NOT TARGET "${canonical_target}") + add_library("${canonical_target}" INTERFACE IMPORTED) + target_link_libraries("${canonical_target}" INTERFACE "${target}") + endif() + endif() + endforeach() + list(REMOVE_DUPLICATES include_dirs) + list(REMOVE_DUPLICATES library_dirs) + _boost_set_if_unset(Boost_INCLUDE_DIRS "${include_dirs}") + _boost_set_if_unset(Boost_LIBRARY_DIRS "${library_dirs}") + _boost_set_if_unset(Boost_LIBRARIES "${libraries}") + _boost_set_if_unset( + Boost_VERSION_STRING + "${Boost_VERSION_MAJOR}.${Boost_VERSION_MINOR}.${Boost_VERSION_PATCH}") + find_path( + Boost_INCLUDE_DIR + NAMES boost/version.hpp boost/config.hpp + HINTS ${Boost_INCLUDE_DIRS} + NO_DEFAULT_PATH) + if(NOT Boost_VERSION_MACRO OR NOT Boost_LIB_VERSION) + set(version_file ${Boost_INCLUDE_DIR}/boost/version.hpp) + if(EXISTS "${version_file}") + file(STRINGS "${version_file}" contents REGEX "#define BOOST_(LIB_)?VERSION ") + if(contents MATCHES "#define BOOST_VERSION ([0-9]+)") + _boost_set_if_unset(Boost_VERSION_MACRO "${CMAKE_MATCH_1}") + endif() + if(contents MATCHES "#define BOOST_LIB_VERSION \"([0-9_]+)\"") + _boost_set_if_unset(Boost_LIB_VERSION "${CMAKE_MATCH_1}") + endif() + endif() + endif() + _boost_set_if_unset(Boost_MAJOR_VERSION ${Boost_VERSION_MAJOR}) + _boost_set_if_unset(Boost_MINOR_VERSION ${Boost_VERSION_MINOR}) + _boost_set_if_unset(Boost_SUBMINOR_VERSION ${Boost_VERSION_PATCH}) + if(WIN32) + _boost_set_if_unset(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC") + endif() + if(NOT TARGET Boost::headers) + add_library(Boost::headers INTERFACE IMPORTED) + target_include_directories(Boost::headers INTERFACE ${Boost_INCLUDE_DIRS}) + endif() + # Legacy targets w/o functionality as all handled by defined targets + foreach(lib diagnostic_definitions disable_autolinking dynamic_linking) + if(NOT TARGET Boost::${lib}) + add_library(Boost::${lib} INTERFACE IMPORTED) + endif() + endforeach() + if(NOT TARGET Boost::boost) + add_library(Boost::boost INTERFACE IMPORTED) + target_link_libraries(Boost::boost INTERFACE Boost::headers) + endif() +endfunction() + +# ------------------------------------------------------------------------------- +# Before we go searching, check whether a boost cmake package is available, unless the +# user specifically asked NOT to search for one. +# +# If Boost_DIR is set, this behaves as any find_package call would. If not, it looks at +# BOOST_ROOT and BOOSTROOT to find Boost. +# +if(NOT Boost_NO_BOOST_CMAKE) + # If Boost_DIR is not set, look for BOOSTROOT and BOOST_ROOT as alternatives, since + # these are more conventional for Boost. + if("$ENV{Boost_DIR}" STREQUAL "") + if(NOT "$ENV{BOOST_ROOT}" STREQUAL "") + set(ENV{Boost_DIR} $ENV{BOOST_ROOT}) + elseif(NOT "$ENV{BOOSTROOT}" STREQUAL "") + set(ENV{Boost_DIR} $ENV{BOOSTROOT}) + endif() + endif() + + # Do the same find_package call but look specifically for the CMake version. Note that + # args are passed in the Boost_FIND_xxxxx variables, so there is no need to delegate + # them to this find_package call. + find_package(Boost QUIET NO_MODULE) + if(DEFINED Boost_DIR) + mark_as_advanced(Boost_DIR) + endif() + + # If we found a boost cmake package, then we're done. Print out what we found. + # Otherwise let the rest of the module try to find it. + if(Boost_FOUND) + # Convert component found variables to standard variables if required Necessary + # for legacy boost-cmake and 1.70 builtin BoostConfig + if(Boost_FIND_COMPONENTS) + # Ignore the meta-component "ALL", introduced by Boost 1.73 + list(REMOVE_ITEM Boost_FIND_COMPONENTS "ALL") + + foreach(_comp IN LISTS Boost_FIND_COMPONENTS) + if(DEFINED Boost_${_comp}_FOUND) + continue() + endif() + string(TOUPPER ${_comp} _uppercomp) + if(DEFINED Boost${_comp}_FOUND) # legacy boost-cmake project + set(Boost_${_comp}_FOUND ${Boost${_comp}_FOUND}) + elseif(DEFINED Boost_${_uppercomp}_FOUND) # Boost 1.70 + set(Boost_${_comp}_FOUND ${Boost_${_uppercomp}_FOUND}) + endif() + endforeach() + endif() + + find_package_handle_standard_args(Boost HANDLE_COMPONENTS CONFIG_MODE) + _boost_set_legacy_variables_from_config() + + # Restore project's policies + cmake_policy(POP) + return() + endif() +endif() + +# ------------------------------------------------------------------------------- +# FindBoost functions & macros +# + +# +# Print debug text if Boost_DEBUG is set. Call example: +# _Boost_DEBUG_PRINT("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "debug +# message") +# +function(_Boost_DEBUG_PRINT file line text) + if(Boost_DEBUG) + message(STATUS "[ ${file}:${line} ] ${text}") + endif() +endfunction() + +# +# _Boost_DEBUG_PRINT_VAR(file line variable_name [ENVIRONMENT] [SOURCE "short explanation +# of origin of var value"]) +# +# ENVIRONMENT - look up environment variable instead of CMake variable +# +# Print variable name and its value if Boost_DEBUG is set. Call example: +# _Boost_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" +# BOOST_ROOT) +# +function(_Boost_DEBUG_PRINT_VAR file line name) + if(Boost_DEBUG) + cmake_parse_arguments(_args "ENVIRONMENT" "SOURCE" "" ${ARGN}) + + unset(source) + if(_args_SOURCE) + set(source " (${_args_SOURCE})") + endif() + + if(_args_ENVIRONMENT) + if(DEFINED ENV{${name}}) + set(value "\"$ENV{${name}}\"") + else() + set(value "") + endif() + set(_name "ENV{${name}}") + else() + if(DEFINED "${name}") + set(value "\"${${name}}\"") + else() + set(value "") + endif() + set(_name "${name}") + endif() + + _boost_debug_print("${file}" "${line}" "${_name} = ${value}${source}") + endif() +endfunction() + +# ######################################################################################## +# +# Check the existence of the libraries. +# +# ######################################################################################## +# This macro was taken directly from the FindQt4.cmake file that is included with the +# CMake distribution. This is NOT my work. All work was done by the original authors of +# the FindQt4.cmake file. Only minor modifications were made to remove references to Qt +# and make this file more generally applicable And ELSE/ENDIF pairs were removed for +# readability. +# ######################################################################################## + +macro(_Boost_ADJUST_LIB_VARS basename) + if(Boost_INCLUDE_DIR) + if(Boost_${basename}_LIBRARY_DEBUG AND Boost_${basename}_LIBRARY_RELEASE) + # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for + # single-config generators, set optimized and debug libraries + get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(_isMultiConfig OR CMAKE_BUILD_TYPE) + set(Boost_${basename}_LIBRARY + optimized ${Boost_${basename}_LIBRARY_RELEASE} debug + ${Boost_${basename}_LIBRARY_DEBUG}) + else() + # For single-config generators where CMAKE_BUILD_TYPE has no value, just + # use the release libraries + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE}) + endif() + # FIXME: This probably should be set for both cases + set(Boost_${basename}_LIBRARIES + optimized ${Boost_${basename}_LIBRARY_RELEASE} debug + ${Boost_${basename}_LIBRARY_DEBUG}) + endif() + + # if only the release version was found, set the debug variable also to the + # release version + if(Boost_${basename}_LIBRARY_RELEASE AND NOT Boost_${basename}_LIBRARY_DEBUG) + set(Boost_${basename}_LIBRARY_DEBUG ${Boost_${basename}_LIBRARY_RELEASE}) + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE}) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE}) + endif() + + # if only the debug version was found, set the release variable also to the debug + # version + if(Boost_${basename}_LIBRARY_DEBUG AND NOT Boost_${basename}_LIBRARY_RELEASE) + set(Boost_${basename}_LIBRARY_RELEASE ${Boost_${basename}_LIBRARY_DEBUG}) + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_DEBUG}) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_DEBUG}) + endif() + + # If the debug & release library ends up being the same, omit the keywords + if("${Boost_${basename}_LIBRARY_RELEASE}" STREQUAL + "${Boost_${basename}_LIBRARY_DEBUG}") + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE}) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE}) + endif() + + if(Boost_${basename}_LIBRARY AND Boost_${basename}_HEADER) + set(Boost_${basename}_FOUND ON) + if("x${basename}" STREQUAL "xTHREAD" AND NOT TARGET Threads::Threads) + string(APPEND Boost_ERROR_REASON_THREAD " (missing dependency: Threads)") + set(Boost_THREAD_FOUND OFF) + endif() + endif() + + endif() + # Make variables changeable to the advanced user + mark_as_advanced(Boost_${basename}_LIBRARY_RELEASE Boost_${basename}_LIBRARY_DEBUG) +endmacro() + +# Detect changes in used variables. Compares the current variable value with the last one. +# In short form: v != v_LAST -> CHANGED = 1 v is defined, v_LAST not +# -> CHANGED = 1 v is not defined, but v_LAST is -> CHANGED = 1 otherwise -> CHANGED = 0 +# CHANGED is returned in variable named ${changed_var} +macro(_Boost_CHANGE_DETECT changed_var) + set(${changed_var} 0) + foreach(v ${ARGN}) + if(DEFINED _Boost_COMPONENTS_SEARCHED) + if(${v}) + if(_${v}_LAST) + string(COMPARE NOTEQUAL "${${v}}" "${_${v}_LAST}" _${v}_CHANGED) + else() + set(_${v}_CHANGED 1) + endif() + elseif(_${v}_LAST) + set(_${v}_CHANGED 1) + endif() + if(_${v}_CHANGED) + set(${changed_var} 1) + endif() + else() + set(_${v}_CHANGED 0) + endif() + endforeach() +endmacro() + +# +# Find the given library (var). Use 'build_type' to support different lib paths for +# RELEASE or DEBUG builds +# +macro(_Boost_FIND_LIBRARY var build_type) + + find_library(${var} ${ARGN}) + + if(${var}) + # If this is the first library found then save Boost_LIBRARY_DIR_[RELEASE,DEBUG]. + if(NOT Boost_LIBRARY_DIR_${build_type}) + get_filename_component(_dir "${${var}}" PATH) + set(Boost_LIBRARY_DIR_${build_type} + "${_dir}" + CACHE PATH "Boost library directory ${build_type}" FORCE) + endif() + elseif(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT) + # Try component-specific hints but do not save Boost_LIBRARY_DIR_[RELEASE,DEBUG]. + find_library(${var} HINTS ${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT} ${ARGN}) + endif() + + # If Boost_LIBRARY_DIR_[RELEASE,DEBUG] is known then search only there. + if(Boost_LIBRARY_DIR_${build_type}) + set(_boost_LIBRARY_SEARCH_DIRS_${build_type} + ${Boost_LIBRARY_DIR_${build_type}} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_LIBRARY_DIR_${build_type}") + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_LIBRARY_SEARCH_DIRS_${build_type}") + endif() +endmacro() + +# ------------------------------------------------------------------------------- + +# Convert CMAKE_CXX_COMPILER_VERSION to boost compiler suffix version. +function(_Boost_COMPILER_DUMPVERSION _OUTPUT_VERSION _OUTPUT_VERSION_MAJOR + _OUTPUT_VERSION_MINOR) + string(REGEX REPLACE "([0-9]+)\\.([0-9]+)(\\.[0-9]+)?" "\\1" + _boost_COMPILER_VERSION_MAJOR "${CMAKE_CXX_COMPILER_VERSION}") + string(REGEX REPLACE "([0-9]+)\\.([0-9]+)(\\.[0-9]+)?" "\\2" + _boost_COMPILER_VERSION_MINOR "${CMAKE_CXX_COMPILER_VERSION}") + + set(_boost_COMPILER_VERSION + "${_boost_COMPILER_VERSION_MAJOR}${_boost_COMPILER_VERSION_MINOR}") + + set(${_OUTPUT_VERSION} + ${_boost_COMPILER_VERSION} + PARENT_SCOPE) + set(${_OUTPUT_VERSION_MAJOR} + ${_boost_COMPILER_VERSION_MAJOR} + PARENT_SCOPE) + set(${_OUTPUT_VERSION_MINOR} + ${_boost_COMPILER_VERSION_MINOR} + PARENT_SCOPE) +endfunction() + +# +# Take a list of libraries with "thread" in it and prepend duplicates with +# "thread_${Boost_THREADAPI}" at the front of the list +# +function(_Boost_PREPEND_LIST_WITH_THREADAPI _output) + set(_orig_libnames ${ARGN}) + string(REPLACE "thread" "thread_${Boost_THREADAPI}" _threadapi_libnames + "${_orig_libnames}") + set(${_output} + ${_threadapi_libnames} ${_orig_libnames} + PARENT_SCOPE) +endfunction() + +# +# If a library is found, replace its cache entry with its REALPATH +# +function(_Boost_SWAP_WITH_REALPATH _library _docstring) + if(${_library}) + get_filename_component(_boost_filepathreal ${${_library}} REALPATH) + unset(${_library} CACHE) + set(${_library} + ${_boost_filepathreal} + CACHE FILEPATH "${_docstring}") + endif() +endfunction() + +function(_Boost_CHECK_SPELLING _var) + if(${_var}) + string(TOUPPER ${_var} _var_UC) + message( + FATAL_ERROR + "ERROR: ${_var} is not the correct spelling. The proper spelling is ${_var_UC}." + ) + endif() +endfunction() + +# Guesses Boost's compiler prefix used in built library names Returns the guess by setting +# the variable pointed to by _ret +function(_Boost_GUESS_COMPILER_PREFIX _ret) + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xIntel") + if(WIN32) + set(_boost_COMPILER "-iw") + else() + set(_boost_COMPILER "-il") + endif() + elseif(GHSMULTI) + set(_boost_COMPILER "-ghs") + elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" + STREQUAL "xMSVC") + if(MSVC_TOOLSET_VERSION GREATER_EQUAL 150) + # Not yet known. + set(_boost_COMPILER "") + elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 140) + # MSVC toolset 14.x versions are forward compatible. + set(_boost_COMPILER "") + foreach( + v + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0) + if(MSVC_TOOLSET_VERSION GREATER_EQUAL 14${v}) + list(APPEND _boost_COMPILER "-vc14${v}") + endif() + endforeach() + elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 80) + set(_boost_COMPILER "-vc${MSVC_TOOLSET_VERSION}") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.10) + set(_boost_COMPILER "-vc71") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13) # Good luck! + set(_boost_COMPILER "-vc7") # yes, this is correct + else() # VS 6.0 Good luck! + set(_boost_COMPILER "-vc6") # yes, this is correct + endif() + + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang") + string(REPLACE "." ";" VERSION_LIST "${CMAKE_CXX_COMPILER_VERSION}") + list(GET VERSION_LIST 0 CLANG_VERSION_MAJOR) + set(_boost_COMPILER "-clangw${CLANG_VERSION_MAJOR};${_boost_COMPILER}") + endif() + elseif(BORLAND) + set(_boost_COMPILER "-bcb") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "SunPro") + set(_boost_COMPILER "-sw") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "XL") + set(_boost_COMPILER "-xlc") + elseif(MINGW) + if(Boost_VERSION_STRING VERSION_LESS 1.34) + set(_boost_COMPILER "-mgw") # no GCC version encoding prior to 1.34 + else() + _boost_compiler_dumpversion( + _boost_COMPILER_VERSION _boost_COMPILER_VERSION_MAJOR + _boost_COMPILER_VERSION_MINOR) + set(_boost_COMPILER "-mgw${_boost_COMPILER_VERSION}") + endif() + elseif(UNIX) + _boost_compiler_dumpversion(_boost_COMPILER_VERSION _boost_COMPILER_VERSION_MAJOR + _boost_COMPILER_VERSION_MINOR) + if(NOT Boost_VERSION_STRING VERSION_LESS 1.69.0) + # From GCC 5 and clang 4, versioning changes and minor becomes patch. For + # those compilers, patch is exclude from compiler tag in Boost 1.69+ library + # naming. + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND _boost_COMPILER_VERSION_MAJOR + VERSION_GREATER 4) + set(_boost_COMPILER_VERSION "${_boost_COMPILER_VERSION_MAJOR}") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + AND _boost_COMPILER_VERSION_MAJOR VERSION_GREATER 3) + set(_boost_COMPILER_VERSION "${_boost_COMPILER_VERSION_MAJOR}") + endif() + endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(Boost_VERSION_STRING VERSION_LESS 1.34) + set(_boost_COMPILER "-gcc") # no GCC version encoding prior to 1.34 + else() + # Determine which version of GCC we have. + if(APPLE) + if(Boost_VERSION_STRING VERSION_LESS 1.36.0) + # In Boost <= 1.35.0, there is no mangled compiler name for the + # macOS/Darwin version of GCC. + set(_boost_COMPILER "") + else() + # In Boost 1.36.0 and newer, the mangled compiler name used on + # macOS/Darwin is "xgcc". + set(_boost_COMPILER "-xgcc${_boost_COMPILER_VERSION}") + endif() + else() + set(_boost_COMPILER "-gcc${_boost_COMPILER_VERSION}") + endif() + endif() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # TODO: Find out any Boost version constraints vs clang support. + set(_boost_COMPILER "-clang${_boost_COMPILER_VERSION}") + endif() + else() + set(_boost_COMPILER "") + endif() + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_COMPILER" SOURCE "guessed") + set(${_ret} + ${_boost_COMPILER} + PARENT_SCOPE) +endfunction() + +# +# Get component dependencies. Requires the dependencies to have been defined for the +# Boost release version. +# +# component - the component to check _ret - list of library dependencies +# +function(_Boost_COMPONENT_DEPENDENCIES component _ret) + # Note: to add a new Boost release, run + # + # % cmake -DBOOST_DIR=/path/to/boost/source -P Utilities/Scripts/BoostScanDeps.cmake + # + # The output may be added in a new block below. If it's the same as the previous + # release, simply update the version range of the block for the previous release. Also + # check if any new components have been added, and add any new components to + # _Boost_COMPONENT_HEADERS. + # + # This information was originally generated by running BoostScanDeps.cmake against + # every boost release to date supported by FindBoost: + # + # % for version in /path/to/boost/sources/* do cmake -DBOOST_DIR=$version -P + # Utilities/Scripts/BoostScanDeps.cmake done + # + # The output was then updated by search and replace with these regexes: + # + # * Strip message(STATUS) prefix dashes s;^-- ;; + # * Indent s;^set(; set(;; + # * Add conditionals s;Scanning /path/to/boost/sources/boost_\(.*\)_\(.*\)_\(.*); + # elseif(NOT Boost_VERSION_STRING VERSION_LESS \1\.\2\.\3 AND Boost_VERSION_STRING + # VERSION_LESS xxxx); + # + # This results in the logic seen below, but will require the xxxx replacing with the + # following Boost release version (or the next minor version to be released, e.g. 1.59 + # was the latest at the time of writing, making 1.60 the next. Identical consecutive + # releases were then merged together by updating the end range of the first block and + # removing the following redundant blocks. + # + # Running the script against all historical releases should be required only if the + # BoostScanDeps.cmake script logic is changed. The addition of a new release should + # only require it to be run against the new release. + + # Handle Python version suffixes + if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9])\$") + set(component "${CMAKE_MATCH_1}") + set(component_python_version "${CMAKE_MATCH_2}") + endif() + + set(_Boost_IMPORTED_TARGETS TRUE) + if(Boost_VERSION_STRING) + if(Boost_VERSION_STRING VERSION_LESS 1.33.0) + if(NOT Boost_FIND_QUIETLY) + message( + WARNING + "Imported targets and dependency information not available for Boost version ${Boost_VERSION_STRING} (all versions older than 1.33)" + ) + endif() + set(_Boost_IMPORTED_TARGETS FALSE) + elseif(Boost_VERSION_STRING VERSION_LESS 1.35.0) + set(_Boost_IOSTREAMS_DEPENDENCIES regex thread) + set(_Boost_REGEX_DEPENDENCIES thread) + set(_Boost_WAVE_DEPENDENCIES filesystem thread) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.36.0) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_WAVE_DEPENDENCIES filesystem system thread) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.38.0) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_WAVE_DEPENDENCIES filesystem system thread) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.43.0) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES filesystem system thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.44.0) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES filesystem system thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.45.0) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l random serialization) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES serialization filesystem system thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.47.0) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.48.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.50.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.53.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l regex random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.54.0) + set(_Boost_ATOMIC_DEPENDENCIES thread chrono system date_time) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l regex random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.55.0) + set(_Boost_ATOMIC_DEPENDENCIES thread chrono system date_time) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread + regex chrono) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l regex random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.56.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread + regex chrono) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l regex random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.59.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread + regex chrono) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.60.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread + regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.61.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread + regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.62.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread + regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.63.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread + regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.65.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_COROUTINE2_DEPENDENCIES context fiber thread chrono system + date_time) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread + regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.67.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread + regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.68.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread + regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.69.0) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_CONTRACT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread + regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono + date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.70.0) + set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time) + set(_Boost_COROUTINE_DEPENDENCIES context) + set(_Boost_FIBER_DEPENDENCIES context) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup filesystem thread regex + chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_THREAD_DEPENDENCIES chrono date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time + atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.72.0) + set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time) + set(_Boost_COROUTINE_DEPENDENCIES context) + set(_Boost_FIBER_DEPENDENCIES context) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup filesystem thread regex + chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_THREAD_DEPENDENCIES chrono date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono) + set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time + atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(Boost_VERSION_STRING VERSION_LESS 1.73.0) + set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time) + set(_Boost_COROUTINE_DEPENDENCIES context) + set(_Boost_FIBER_DEPENDENCIES context) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup filesystem thread regex + chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l chrono atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_THREAD_DEPENDENCIES chrono date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono) + set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time + atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + else() + set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time) + set(_Boost_COROUTINE_DEPENDENCIES context) + set(_Boost_FIBER_DEPENDENCIES context) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup filesystem thread regex + chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f + math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi + serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_THREAD_DEPENDENCIES chrono date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono) + set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time + atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.74.0) + if(NOT Boost_FIND_QUIETLY) + message( + WARNING + "New Boost version may have incorrect or missing dependencies and imported targets" + ) + endif() + endif() + endif() + endif() + + string(TOUPPER ${component} uppercomponent) + set(${_ret} + ${_Boost_${uppercomponent}_DEPENDENCIES} + PARENT_SCOPE) + set(_Boost_IMPORTED_TARGETS + ${_Boost_IMPORTED_TARGETS} + PARENT_SCOPE) + + string(REGEX REPLACE ";" " " _boost_DEPS_STRING + "${_Boost_${uppercomponent}_DEPENDENCIES}") + if(NOT _boost_DEPS_STRING) + set(_boost_DEPS_STRING "(none)") + endif() + # message(STATUS "Dependencies for Boost::${component}: ${_boost_DEPS_STRING}") +endfunction() + +# +# Get component headers. This is the primary header (or headers) for a given component, +# and is used to check that the headers are present as well as the library itself as an +# extra sanity check of the build environment. +# +# component - the component to check _hdrs +# +function(_Boost_COMPONENT_HEADERS component _hdrs) + # Handle Python version suffixes + if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9])\$") + set(component "${CMAKE_MATCH_1}") + set(component_python_version "${CMAKE_MATCH_2}") + endif() + + # Note: new boost components will require adding here. The header must be present in + # all versions of Boost providing a library. + set(_Boost_ATOMIC_HEADERS "boost/atomic.hpp") + set(_Boost_CHRONO_HEADERS "boost/chrono.hpp") + set(_Boost_CONTAINER_HEADERS "boost/container/container_fwd.hpp") + set(_Boost_CONTRACT_HEADERS "boost/contract.hpp") + if(Boost_VERSION_STRING VERSION_LESS 1.61.0) + set(_Boost_CONTEXT_HEADERS "boost/context/all.hpp") + else() + set(_Boost_CONTEXT_HEADERS "boost/context/detail/fcontext.hpp") + endif() + set(_Boost_COROUTINE_HEADERS "boost/coroutine/all.hpp") + set(_Boost_DATE_TIME_HEADERS "boost/date_time/date.hpp") + set(_Boost_EXCEPTION_HEADERS "boost/exception/exception.hpp") + set(_Boost_FIBER_HEADERS "boost/fiber/all.hpp") + set(_Boost_FILESYSTEM_HEADERS "boost/filesystem/path.hpp") + set(_Boost_GRAPH_HEADERS "boost/graph/adjacency_list.hpp") + set(_Boost_GRAPH_PARALLEL_HEADERS "boost/graph/adjacency_list.hpp") + set(_Boost_IOSTREAMS_HEADERS "boost/iostreams/stream.hpp") + set(_Boost_LOCALE_HEADERS "boost/locale.hpp") + set(_Boost_LOG_HEADERS "boost/log/core.hpp") + set(_Boost_LOG_SETUP_HEADERS "boost/log/detail/setup_config.hpp") + set(_Boost_MATH_HEADERS "boost/math_fwd.hpp") + set(_Boost_MATH_C99_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_C99F_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_C99L_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_TR1_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_TR1F_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_TR1L_HEADERS "boost/math/tr1.hpp") + set(_Boost_MPI_HEADERS "boost/mpi.hpp") + set(_Boost_MPI_PYTHON_HEADERS "boost/mpi/python/config.hpp") + set(_Boost_NUMPY_HEADERS "boost/python/numpy.hpp") + set(_Boost_NOWIDE_HEADERS "boost/nowide/cstdlib.hpp") + set(_Boost_PRG_EXEC_MONITOR_HEADERS "boost/test/prg_exec_monitor.hpp") + set(_Boost_PROGRAM_OPTIONS_HEADERS "boost/program_options.hpp") + set(_Boost_PYTHON_HEADERS "boost/python.hpp") + set(_Boost_RANDOM_HEADERS "boost/random.hpp") + set(_Boost_REGEX_HEADERS "boost/regex.hpp") + set(_Boost_SERIALIZATION_HEADERS "boost/serialization/serialization.hpp") + set(_Boost_SIGNALS_HEADERS "boost/signals.hpp") + set(_Boost_STACKTRACE_ADDR2LINE_HEADERS "boost/stacktrace.hpp") + set(_Boost_STACKTRACE_BACKTRACE_HEADERS "boost/stacktrace.hpp") + set(_Boost_STACKTRACE_BASIC_HEADERS "boost/stacktrace.hpp") + set(_Boost_STACKTRACE_NOOP_HEADERS "boost/stacktrace.hpp") + set(_Boost_STACKTRACE_WINDBG_CACHED_HEADERS "boost/stacktrace.hpp") + set(_Boost_STACKTRACE_WINDBG_HEADERS "boost/stacktrace.hpp") + set(_Boost_SYSTEM_HEADERS "boost/system/config.hpp") + set(_Boost_TEST_EXEC_MONITOR_HEADERS "boost/test/test_exec_monitor.hpp") + set(_Boost_THREAD_HEADERS "boost/thread.hpp") + set(_Boost_TIMER_HEADERS "boost/timer.hpp") + set(_Boost_TYPE_ERASURE_HEADERS "boost/type_erasure/config.hpp") + set(_Boost_UNIT_TEST_FRAMEWORK_HEADERS "boost/test/framework.hpp") + set(_Boost_WAVE_HEADERS "boost/wave.hpp") + set(_Boost_WSERIALIZATION_HEADERS "boost/archive/text_wiarchive.hpp") + if(WIN32) + set(_Boost_BZIP2_HEADERS "boost/iostreams/filter/bzip2.hpp") + set(_Boost_ZLIB_HEADERS "boost/iostreams/filter/zlib.hpp") + endif() + + string(TOUPPER ${component} uppercomponent) + set(${_hdrs} + ${_Boost_${uppercomponent}_HEADERS} + PARENT_SCOPE) + + string(REGEX REPLACE ";" " " _boost_HDRS_STRING "${_Boost_${uppercomponent}_HEADERS}") + if(NOT _boost_HDRS_STRING) + set(_boost_HDRS_STRING "(none)") + endif() + # message(STATUS "Headers for Boost::${component}: ${_boost_HDRS_STRING}") +endfunction() + +# +# Determine if any missing dependencies require adding to the component list. +# +# Sets _Boost_${COMPONENT}_DEPENDENCIES for each required component, plus +# _Boost_IMPORTED_TARGETS (TRUE if imported targets should be defined; FALSE if dependency +# information is unavailable). +# +# componentvar - the component list variable name extravar - the indirect dependency list +# variable name +# +function(_Boost_MISSING_DEPENDENCIES componentvar extravar) + # _boost_unprocessed_components - list of components requiring processing + # _boost_processed_components - components already processed (or currently being + # processed) _boost_new_components - new components discovered for future processing + # + list(APPEND _boost_unprocessed_components ${${componentvar}}) + + while(_boost_unprocessed_components) + list(APPEND _boost_processed_components ${_boost_unprocessed_components}) + foreach(component ${_boost_unprocessed_components}) + string(TOUPPER ${component} uppercomponent) + set(${_ret} + ${_Boost_${uppercomponent}_DEPENDENCIES} + PARENT_SCOPE) + _boost_component_dependencies("${component}" + _Boost_${uppercomponent}_DEPENDENCIES) + set(_Boost_${uppercomponent}_DEPENDENCIES + ${_Boost_${uppercomponent}_DEPENDENCIES} + PARENT_SCOPE) + set(_Boost_IMPORTED_TARGETS + ${_Boost_IMPORTED_TARGETS} + PARENT_SCOPE) + foreach(componentdep ${_Boost_${uppercomponent}_DEPENDENCIES}) + if(NOT ("${componentdep}" IN_LIST _boost_processed_components + OR "${componentdep}" IN_LIST _boost_new_components)) + list(APPEND _boost_new_components ${componentdep}) + endif() + endforeach() + endforeach() + set(_boost_unprocessed_components ${_boost_new_components}) + unset(_boost_new_components) + endwhile() + set(_boost_extra_components ${_boost_processed_components}) + if(_boost_extra_components AND ${componentvar}) + list(REMOVE_ITEM _boost_extra_components ${${componentvar}}) + endif() + set(${componentvar} + ${_boost_processed_components} + PARENT_SCOPE) + set(${extravar} + ${_boost_extra_components} + PARENT_SCOPE) +endfunction() + +# +# Some boost libraries may require particular set of compler features. The very first one +# was `boost::fiber` introduced in Boost 1.62. One can check required compiler features of +# it in - `${Boost_ROOT}/libs/fiber/build/Jamfile.v2`; - +# `${Boost_ROOT}/libs/context/build/Jamfile.v2`. +# +# TODO (Re)Check compiler features on (every?) release ??? One may use the following +# command to get the files to check: +# +# $ find . -name Jamfile.v2 | grep build | xargs grep -l cxx1 +# +function(_Boost_COMPILER_FEATURES component _ret) + # Boost >= 1.62 + if(NOT Boost_VERSION_STRING VERSION_LESS 1.62.0) + set(_Boost_FIBER_COMPILER_FEATURES + cxx_alias_templates + cxx_auto_type + cxx_constexpr + cxx_defaulted_functions + cxx_final + cxx_lambdas + cxx_noexcept + cxx_nullptr + cxx_rvalue_references + cxx_thread_local + cxx_variadic_templates) + # Compiler feature for `context` same as for `fiber`. + set(_Boost_CONTEXT_COMPILER_FEATURES ${_Boost_FIBER_COMPILER_FEATURES}) + endif() + + # Boost Contract library available in >= 1.67 + if(NOT Boost_VERSION_STRING VERSION_LESS 1.67.0) + # From `libs/contract/build/boost_contract_build.jam` + set(_Boost_CONTRACT_COMPILER_FEATURES cxx_lambdas cxx_variadic_templates) + endif() + + string(TOUPPER ${component} uppercomponent) + set(${_ret} + ${_Boost_${uppercomponent}_COMPILER_FEATURES} + PARENT_SCOPE) +endfunction() + +# +# Update library search directory hint variable with paths used by prebuilt boost +# binaries. +# +# Prebuilt windows binaries (https://sourceforge.net/projects/boost/files/boost-binaries/) +# have library directories named using MSVC compiler version and architecture. This +# function would append corresponding directories if MSVC is a current compiler, so having +# `BOOST_ROOT` would be enough to specify to find everything. +# +function(_Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS componentlibvar + basedir) + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_arch_suffix 64) + else() + set(_arch_suffix 32) + endif() + if(MSVC_TOOLSET_VERSION GREATER_EQUAL 150) + # Not yet known. + elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 140) + # MSVC toolset 14.x versions are forward compatible. + foreach( + v + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0) + if(MSVC_TOOLSET_VERSION GREATER_EQUAL 14${v}) + list(APPEND ${componentlibvar} + ${basedir}/lib${_arch_suffix}-msvc-14.${v}) + endif() + endforeach() + elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 80) + math(EXPR _toolset_major_version "${MSVC_TOOLSET_VERSION} / 10") + list(APPEND ${componentlibvar} + ${basedir}/lib${_arch_suffix}-msvc-${_toolset_major_version}.0) + endif() + set(${componentlibvar} + ${${componentlibvar}} + PARENT_SCOPE) + endif() +endfunction() + +# +# End functions/macros +# +# ------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- +# main. +# ------------------------------------------------------------------------------- + +# If the user sets Boost_LIBRARY_DIR, use it as the default for both configurations. +if(NOT Boost_LIBRARY_DIR_RELEASE AND Boost_LIBRARY_DIR) + set(Boost_LIBRARY_DIR_RELEASE "${Boost_LIBRARY_DIR}") +endif() +if(NOT Boost_LIBRARY_DIR_DEBUG AND Boost_LIBRARY_DIR) + set(Boost_LIBRARY_DIR_DEBUG "${Boost_LIBRARY_DIR}") +endif() + +if(NOT DEFINED Boost_USE_DEBUG_LIBS) + set(Boost_USE_DEBUG_LIBS TRUE) +endif() +if(NOT DEFINED Boost_USE_RELEASE_LIBS) + set(Boost_USE_RELEASE_LIBS TRUE) +endif() +if(NOT DEFINED Boost_USE_MULTITHREADED) + set(Boost_USE_MULTITHREADED TRUE) +endif() +if(NOT DEFINED Boost_USE_DEBUG_RUNTIME) + set(Boost_USE_DEBUG_RUNTIME TRUE) +endif() + +# Check the version of Boost against the requested version. +if(Boost_FIND_VERSION AND NOT Boost_FIND_VERSION_MINOR) + message( + SEND_ERROR + "When requesting a specific version of Boost, you must provide at least the major and minor version numbers, e.g., 1.34" + ) +endif() + +if(Boost_FIND_VERSION_EXACT) + # The version may appear in a directory with or without the patch level, even when the + # patch level is non-zero. + set(_boost_TEST_VERSIONS + "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}.${Boost_FIND_VERSION_PATCH}" + "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") +else() + # The user has not requested an exact version. Among known versions, find those that + # are acceptable to the user request. + # + # Note: When adding a new Boost release, also update the dependency information in + # _Boost_COMPONENT_DEPENDENCIES and _Boost_COMPONENT_HEADERS. See the instructions at + # the top of _Boost_COMPONENT_DEPENDENCIES. + set(_Boost_KNOWN_VERSIONS + ${Boost_ADDITIONAL_VERSIONS} + "1.72.0" + "1.72" + "1.71.0" + "1.71" + "1.70.0" + "1.70" + "1.69.0" + "1.69" + "1.68.0" + "1.68" + "1.67.0" + "1.67" + "1.66.0" + "1.66" + "1.65.1" + "1.65.0" + "1.65" + "1.64.0" + "1.64" + "1.63.0" + "1.63" + "1.62.0" + "1.62" + "1.61.0" + "1.61" + "1.60.0" + "1.60" + "1.59.0" + "1.59" + "1.58.0" + "1.58" + "1.57.0" + "1.57" + "1.56.0" + "1.56" + "1.55.0" + "1.55" + "1.54.0" + "1.54" + "1.53.0" + "1.53" + "1.52.0" + "1.52" + "1.51.0" + "1.51" + "1.50.0" + "1.50" + "1.49.0" + "1.49" + "1.48.0" + "1.48" + "1.47.0" + "1.47" + "1.46.1" + "1.46.0" + "1.46" + "1.45.0" + "1.45" + "1.44.0" + "1.44" + "1.43.0" + "1.43" + "1.42.0" + "1.42" + "1.41.0" + "1.41" + "1.40.0" + "1.40" + "1.39.0" + "1.39" + "1.38.0" + "1.38" + "1.37.0" + "1.37" + "1.36.1" + "1.36.0" + "1.36" + "1.35.1" + "1.35.0" + "1.35" + "1.34.1" + "1.34.0" + "1.34" + "1.33.1" + "1.33.0" + "1.33") + + set(_boost_TEST_VERSIONS) + if(Boost_FIND_VERSION) + set(_Boost_FIND_VERSION_SHORT + "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") + # Select acceptable versions. + foreach(version ${_Boost_KNOWN_VERSIONS}) + if(NOT "${version}" VERSION_LESS "${Boost_FIND_VERSION}") + # This version is high enough. + list(APPEND _boost_TEST_VERSIONS "${version}") + elseif("${version}.99" VERSION_EQUAL "${_Boost_FIND_VERSION_SHORT}.99") + # This version is a short-form for the requested version with the patch + # level dropped. + list(APPEND _boost_TEST_VERSIONS "${version}") + endif() + endforeach() + else() + # Any version is acceptable. + set(_boost_TEST_VERSIONS "${_Boost_KNOWN_VERSIONS}") + endif() +endif() + +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_TEST_VERSIONS") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_USE_MULTITHREADED") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_USE_STATIC_LIBS") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_USE_STATIC_RUNTIME") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_ADDITIONAL_VERSIONS") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_NO_SYSTEM_PATHS") + +cmake_policy(GET CMP0074 _Boost_CMP0074) +if(NOT "x${_Boost_CMP0074}x" STREQUAL "xNEWx") + _boost_check_spelling(Boost_ROOT) +endif() +unset(_Boost_CMP0074) +_boost_check_spelling(Boost_LIBRARYDIR) +_boost_check_spelling(Boost_INCLUDEDIR) + +# Collect environment variable inputs as hints. Do not consider changes. +foreach(v BOOSTROOT BOOST_ROOT BOOST_INCLUDEDIR BOOST_LIBRARYDIR) + set(_env $ENV{${v}}) + if(_env) + file(TO_CMAKE_PATH "${_env}" _ENV_${v}) + else() + set(_ENV_${v} "") + endif() +endforeach() +if(NOT _ENV_BOOST_ROOT AND _ENV_BOOSTROOT) + set(_ENV_BOOST_ROOT "${_ENV_BOOSTROOT}") +endif() + +# Collect inputs and cached results. Detect changes since the last run. +if(NOT BOOST_ROOT AND BOOSTROOT) + set(BOOST_ROOT "${BOOSTROOT}") +endif() +set(_Boost_VARS_DIR BOOST_ROOT Boost_NO_SYSTEM_PATHS) + +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "BOOST_ROOT") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "BOOST_ROOT" ENVIRONMENT) +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "BOOST_INCLUDEDIR") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "BOOST_INCLUDEDIR" ENVIRONMENT) +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "BOOST_LIBRARYDIR") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "BOOST_LIBRARYDIR" ENVIRONMENT) + +# ------------------------------------------------------------------------ +# Search for Boost include DIR +# ------------------------------------------------------------------------ + +set(_Boost_VARS_INC BOOST_INCLUDEDIR Boost_INCLUDE_DIR Boost_ADDITIONAL_VERSIONS) +_boost_change_detect(_Boost_CHANGE_INCDIR ${_Boost_VARS_DIR} ${_Boost_VARS_INC}) +# Clear Boost_INCLUDE_DIR if it did not change but other input affecting the location did. +# We will find a new one based on the new inputs. +if(_Boost_CHANGE_INCDIR AND NOT _Boost_INCLUDE_DIR_CHANGED) + unset(Boost_INCLUDE_DIR CACHE) +endif() + +if(NOT Boost_INCLUDE_DIR) + set(_boost_INCLUDE_SEARCH_DIRS "") + if(BOOST_INCLUDEDIR) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_INCLUDEDIR}) + elseif(_ENV_BOOST_INCLUDEDIR) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_INCLUDEDIR}) + endif() + + if(BOOST_ROOT) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_ROOT}/include ${BOOST_ROOT}) + elseif(_ENV_BOOST_ROOT) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_ROOT}/include + ${_ENV_BOOST_ROOT}) + endif() + + if(Boost_NO_SYSTEM_PATHS) + list(APPEND _boost_INCLUDE_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH) + else() + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") + foreach(ver ${_boost_TEST_VERSIONS}) + string(REPLACE "." "_" ver "${ver}") + list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS "C:/local/boost_${ver}") + endforeach() + endif() + list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS C:/boost/include C:/boost + /sw/local/include) + endif() + + # Try to find Boost by stepping backwards through the Boost versions we know about. + # Build a list of path suffixes for each version. + set(_boost_PATH_SUFFIXES) + foreach(_boost_VER ${_boost_TEST_VERSIONS}) + # Add in a path suffix, based on the required version, ideally we could read this + # from version.hpp, but for that to work we'd need to know the include dir already + set(_boost_BOOSTIFIED_VERSION) + + # Transform 1.35 => 1_35 and 1.36.0 => 1_36_0 + if(_boost_VER MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(_boost_BOOSTIFIED_VERSION + "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}_${CMAKE_MATCH_3}") + elseif(_boost_VER MATCHES "([0-9]+)\\.([0-9]+)") + set(_boost_BOOSTIFIED_VERSION "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}") + endif() + + list( + APPEND + _boost_PATH_SUFFIXES + "boost-${_boost_BOOSTIFIED_VERSION}" + "boost_${_boost_BOOSTIFIED_VERSION}" + "boost/boost-${_boost_BOOSTIFIED_VERSION}" + "boost/boost_${_boost_BOOSTIFIED_VERSION}") + + endforeach() + + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_INCLUDE_SEARCH_DIRS") + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_PATH_SUFFIXES") + + # Look for a standard boost header file. + find_path( + Boost_INCLUDE_DIR + NAMES boost/config.hpp + HINTS ${_boost_INCLUDE_SEARCH_DIRS} + PATH_SUFFIXES ${_boost_PATH_SUFFIXES}) +endif() + +# ------------------------------------------------------------------------ +# Extract version information from version.hpp +# ------------------------------------------------------------------------ + +if(Boost_INCLUDE_DIR) + _boost_debug_print("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "location of version.hpp: ${Boost_INCLUDE_DIR}/boost/version.hpp") + + # Extract Boost_VERSION_MACRO and Boost_LIB_VERSION from version.hpp + set(Boost_VERSION_MACRO 0) + set(Boost_LIB_VERSION "") + file(STRINGS "${Boost_INCLUDE_DIR}/boost/version.hpp" _boost_VERSION_HPP_CONTENTS + REGEX "#define BOOST_(LIB_)?VERSION ") + if("${_boost_VERSION_HPP_CONTENTS}" MATCHES "#define BOOST_VERSION ([0-9]+)") + set(Boost_VERSION_MACRO "${CMAKE_MATCH_1}") + endif() + if("${_boost_VERSION_HPP_CONTENTS}" MATCHES "#define BOOST_LIB_VERSION \"([0-9_]+)\"") + set(Boost_LIB_VERSION "${CMAKE_MATCH_1}") + endif() + unset(_boost_VERSION_HPP_CONTENTS) + + # Calculate version components + math(EXPR Boost_VERSION_MAJOR "${Boost_VERSION_MACRO} / 100000") + math(EXPR Boost_VERSION_MINOR "${Boost_VERSION_MACRO} / 100 % 1000") + math(EXPR Boost_VERSION_PATCH "${Boost_VERSION_MACRO} % 100") + set(Boost_VERSION_COUNT 3) + + # Define alias variables for backwards compat. + set(Boost_MAJOR_VERSION ${Boost_VERSION_MAJOR}) + set(Boost_MINOR_VERSION ${Boost_VERSION_MINOR}) + set(Boost_SUBMINOR_VERSION ${Boost_VERSION_PATCH}) + + # Define Boost version in x.y.z format + set(Boost_VERSION_STRING + "${Boost_VERSION_MAJOR}.${Boost_VERSION_MINOR}.${Boost_VERSION_PATCH}") + + if(NOT CMAKE_VERSION VERSION_LESS 3.15) + # Define final Boost_VERSION + cmake_policy( + GET CMP0093 _Boost_CMP0093 PARENT_SCOPE # undocumented, do not use outside of + # CMake + ) + if("x${_Boost_CMP0093}x" STREQUAL "xNEWx") + set(Boost_VERSION ${Boost_VERSION_STRING}) + else() + set(Boost_VERSION ${Boost_VERSION_MACRO}) + endif() + unset(_Boost_CMP0093) + endif() + + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_VERSION") + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_VERSION_STRING") + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_VERSION_MACRO") + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_VERSION_MAJOR") + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_VERSION_MINOR") + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_VERSION_PATCH") + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_VERSION_COUNT") +endif() + +# ------------------------------------------------------------------------ +# Prefix initialization +# ------------------------------------------------------------------------ + +set(Boost_LIB_PREFIX "") +if((GHSMULTI AND Boost_USE_STATIC_LIBS) + OR (WIN32 + AND Boost_USE_STATIC_LIBS + AND NOT CYGWIN)) + set(Boost_LIB_PREFIX "lib") +endif() + +if(NOT Boost_NAMESPACE) + set(Boost_NAMESPACE "boost") +endif() + +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_LIB_PREFIX") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Boost_NAMESPACE") + +# ------------------------------------------------------------------------ +# Suffix initialization and compiler suffix detection. +# ------------------------------------------------------------------------ + +set(_Boost_VARS_NAME + Boost_NAMESPACE + Boost_COMPILER + Boost_THREADAPI + Boost_USE_DEBUG_PYTHON + Boost_USE_MULTITHREADED + Boost_USE_STATIC_LIBS + Boost_USE_STATIC_RUNTIME + Boost_USE_STLPORT + Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS) +_boost_change_detect(_Boost_CHANGE_LIBNAME ${_Boost_VARS_NAME}) + +# Setting some more suffixes for the library +if(Boost_COMPILER) + set(_boost_COMPILER ${Boost_COMPILER}) + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_COMPILER" SOURCE "user-specified via Boost_COMPILER") +else() + # Attempt to guess the compiler suffix NOTE: this is not perfect yet, if you + # experience any issues please report them and use the Boost_COMPILER variable to work + # around the problems. + _boost_guess_compiler_prefix(_boost_COMPILER) +endif() + +set(_boost_MULTITHREADED "-mt") +if(NOT Boost_USE_MULTITHREADED) + set(_boost_MULTITHREADED "") +endif() +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_MULTITHREADED") + +# ====================== +# Systematically build up the Boost ABI tag for the 'tagged' and 'versioned' layouts +# http://boost.org/doc/libs/1_66_0/more/getting_started/windows.html#library-naming +# http://boost.org/doc/libs/1_66_0/boost/config/auto_link.hpp +# http://boost.org/doc/libs/1_66_0/tools/build/src/tools/common.jam +# http://boost.org/doc/libs/1_66_0/boostcpp.jam +set(_boost_RELEASE_ABI_TAG "-") +set(_boost_DEBUG_ABI_TAG "-") +# Key Use this library when: s linking statically to the C++ standard library +# and compiler runtime support libraries. +if(Boost_USE_STATIC_RUNTIME) + set(_boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}s") + set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}s") +endif() +# g using debug versions of the standard and runtime support libraries +if(WIN32 AND Boost_USE_DEBUG_RUNTIME) + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" + OR "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang" + OR "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xIntel") + string(APPEND _boost_DEBUG_ABI_TAG "g") + endif() +endif() +# y using special debug build of python +if(Boost_USE_DEBUG_PYTHON) + string(APPEND _boost_DEBUG_ABI_TAG "y") +endif() +# d using a debug version of your code +string(APPEND _boost_DEBUG_ABI_TAG "d") +# p using the STLport standard library rather than the default one supplied with +# your compiler +if(Boost_USE_STLPORT) + string(APPEND _boost_RELEASE_ABI_TAG "p") + string(APPEND _boost_DEBUG_ABI_TAG "p") +endif() +# n using the STLport deprecated "native iostreams" feature removed from the +# documentation in 1.43.0 but still present in boost/config/auto_link.hpp +if(Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS) + string(APPEND _boost_RELEASE_ABI_TAG "n") + string(APPEND _boost_DEBUG_ABI_TAG "n") +endif() + +# -x86 Architecture and address model tag First character is the architecture, then +# word-size, either 32 or 64 Only used in 'versioned' layout, added in Boost 1.66.0 +if(DEFINED Boost_ARCHITECTURE) + set(_boost_ARCHITECTURE_TAG "${Boost_ARCHITECTURE}") + _boost_debug_print_var( + "${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_ARCHITECTURE_TAG" SOURCE "user-specified via Boost_ARCHITECTURE") +else() + set(_boost_ARCHITECTURE_TAG "") + # {CMAKE_CXX_COMPILER_ARCHITECTURE_ID} is not currently set for all compilers + if(NOT "x${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "x" + AND NOT Boost_VERSION_STRING VERSION_LESS 1.66.0) + string(APPEND _boost_ARCHITECTURE_TAG "-") + # This needs to be kept in-sync with the section of CMakePlatformId.h.in inside + # 'defined(_WIN32) && defined(_MSC_VER)' + if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "IA64") + string(APPEND _boost_ARCHITECTURE_TAG "i") + elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "X86" + OR CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "x64") + string(APPEND _boost_ARCHITECTURE_TAG "x") + elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "^ARM") + string(APPEND _boost_ARCHITECTURE_TAG "a") + elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "MIPS") + string(APPEND _boost_ARCHITECTURE_TAG "m") + endif() + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + string(APPEND _boost_ARCHITECTURE_TAG "64") + else() + string(APPEND _boost_ARCHITECTURE_TAG "32") + endif() + endif() + _boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_ARCHITECTURE_TAG" SOURCE "detected") +endif() + +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_RELEASE_ABI_TAG") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_DEBUG_ABI_TAG") + +# ------------------------------------------------------------------------ +# Begin finding boost libraries +# ------------------------------------------------------------------------ + +set(_Boost_VARS_LIB "") +foreach(c DEBUG RELEASE) + set(_Boost_VARS_LIB_${c} BOOST_LIBRARYDIR Boost_LIBRARY_DIR_${c}) + list(APPEND _Boost_VARS_LIB ${_Boost_VARS_LIB_${c}}) + _boost_change_detect(_Boost_CHANGE_LIBDIR_${c} ${_Boost_VARS_DIR} + ${_Boost_VARS_LIB_${c}} Boost_INCLUDE_DIR) + # Clear Boost_LIBRARY_DIR_${c} if it did not change but other input affecting the + # location did. We will find a new one based on the new inputs. + if(_Boost_CHANGE_LIBDIR_${c} AND NOT _Boost_LIBRARY_DIR_${c}_CHANGED) + unset(Boost_LIBRARY_DIR_${c} CACHE) + endif() + + # If Boost_LIBRARY_DIR_[RELEASE,DEBUG] is set, prefer its value. + if(Boost_LIBRARY_DIR_${c}) + set(_boost_LIBRARY_SEARCH_DIRS_${c} ${Boost_LIBRARY_DIR_${c}} NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH) + else() + set(_boost_LIBRARY_SEARCH_DIRS_${c} "") + if(BOOST_LIBRARYDIR) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${BOOST_LIBRARYDIR}) + elseif(_ENV_BOOST_LIBRARYDIR) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${_ENV_BOOST_LIBRARYDIR}) + endif() + + if(BOOST_ROOT) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${BOOST_ROOT}/lib + ${BOOST_ROOT}/stage/lib) + _boost_update_windows_library_search_dirs_with_prebuilt_paths( + _boost_LIBRARY_SEARCH_DIRS_${c} "${BOOST_ROOT}") + elseif(_ENV_BOOST_ROOT) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${_ENV_BOOST_ROOT}/lib + ${_ENV_BOOST_ROOT}/stage/lib) + _boost_update_windows_library_search_dirs_with_prebuilt_paths( + _boost_LIBRARY_SEARCH_DIRS_${c} "${_ENV_BOOST_ROOT}") + endif() + + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${Boost_INCLUDE_DIR}/lib + ${Boost_INCLUDE_DIR}/../lib ${Boost_INCLUDE_DIR}/stage/lib) + _boost_update_windows_library_search_dirs_with_prebuilt_paths( + _boost_LIBRARY_SEARCH_DIRS_${c} "${Boost_INCLUDE_DIR}/..") + _boost_update_windows_library_search_dirs_with_prebuilt_paths( + _boost_LIBRARY_SEARCH_DIRS_${c} "${Boost_INCLUDE_DIR}") + if(Boost_NO_SYSTEM_PATHS) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH) + else() + foreach(ver ${_boost_TEST_VERSIONS}) + string(REPLACE "." "_" ver "${ver}") + _boost_update_windows_library_search_dirs_with_prebuilt_paths( + _boost_LIBRARY_SEARCH_DIRS_${c} "C:/local/boost_${ver}") + endforeach() + _boost_update_windows_library_search_dirs_with_prebuilt_paths( + _boost_LIBRARY_SEARCH_DIRS_${c} "C:/boost") + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} PATHS C:/boost/lib C:/boost + /sw/local/lib) + endif() + endif() +endforeach() + +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_LIBRARY_SEARCH_DIRS_RELEASE") +_boost_debug_print_var("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "_boost_LIBRARY_SEARCH_DIRS_DEBUG") + +# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES +if(Boost_USE_STATIC_LIBS) + set(_boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + if(WIN32) + list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .lib .a) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif() +endif() + +# We want to use the tag inline below without risking double dashes +if(_boost_RELEASE_ABI_TAG) + if(${_boost_RELEASE_ABI_TAG} STREQUAL "-") + set(_boost_RELEASE_ABI_TAG "") + endif() +endif() +if(_boost_DEBUG_ABI_TAG) + if(${_boost_DEBUG_ABI_TAG} STREQUAL "-") + set(_boost_DEBUG_ABI_TAG "") + endif() +endif() + +# The previous behavior of FindBoost when Boost_USE_STATIC_LIBS was enabled on WIN32 was +# to: 1. Search for static libs compiled against a SHARED C++ standard runtime library +# (use if found) 2. Search for static libs compiled against a STATIC C++ standard runtime +# library (use if found) We maintain this behavior since changing it could break people's +# builds. To disable the ambiguous behavior, the user need only set +# Boost_USE_STATIC_RUNTIME either ON or OFF. +set(_boost_STATIC_RUNTIME_WORKAROUND false) +if(WIN32 AND Boost_USE_STATIC_LIBS) + if(NOT DEFINED Boost_USE_STATIC_RUNTIME) + set(_boost_STATIC_RUNTIME_WORKAROUND TRUE) + endif() +endif() + +# On versions < 1.35, remove the System library from the considered list since it wasn't +# added until 1.35. +if(Boost_VERSION_STRING AND Boost_FIND_COMPONENTS) + if(Boost_VERSION_STRING VERSION_LESS 1.35.0) + list(REMOVE_ITEM Boost_FIND_COMPONENTS system) + endif() +endif() + +# Additional components may be required via component dependencies. Add any missing +# components to the list. +_boost_missing_dependencies(Boost_FIND_COMPONENTS _Boost_EXTRA_FIND_COMPONENTS) + +# If thread is required, get the thread libs as a dependency +if("thread" IN_LIST Boost_FIND_COMPONENTS) + if(Boost_FIND_QUIETLY) + set(_Boost_find_quiet QUIET) + else() + set(_Boost_find_quiet "") + endif() + find_package(Threads ${_Boost_find_quiet}) + unset(_Boost_find_quiet) +endif() + +# If the user changed any of our control inputs flush previous results. +if(_Boost_CHANGE_LIBDIR_DEBUG + OR _Boost_CHANGE_LIBDIR_RELEASE + OR _Boost_CHANGE_LIBNAME) + foreach(COMPONENT ${_Boost_COMPONENTS_SEARCHED}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + foreach(c DEBUG RELEASE) + set(_var Boost_${UPPERCOMPONENT}_LIBRARY_${c}) + unset(${_var} CACHE) + set(${_var} "${_var}-NOTFOUND") + endforeach() + endforeach() + set(_Boost_COMPONENTS_SEARCHED "") +endif() + +foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + + set(_boost_docstring_release "Boost ${COMPONENT} library (release)") + set(_boost_docstring_debug "Boost ${COMPONENT} library (debug)") + + # Compute component-specific hints. + set(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT "") + if(${COMPONENT} STREQUAL "mpi" + OR ${COMPONENT} STREQUAL "mpi_python" + OR ${COMPONENT} STREQUAL "graph_parallel") + foreach(lib ${MPI_CXX_LIBRARIES} ${MPI_C_LIBRARIES}) + if(IS_ABSOLUTE "${lib}") + get_filename_component(libdir "${lib}" PATH) + string(REPLACE "\\" "/" libdir "${libdir}") + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT ${libdir}) + endif() + endforeach() + endif() + + # Handle Python version suffixes + unset(COMPONENT_PYTHON_VERSION_MAJOR) + unset(COMPONENT_PYTHON_VERSION_MINOR) + if(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\$") + set(COMPONENT_UNVERSIONED "${CMAKE_MATCH_1}") + set(COMPONENT_PYTHON_VERSION_MAJOR "${CMAKE_MATCH_2}") + elseif(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\\.?([0-9])\$") + set(COMPONENT_UNVERSIONED "${CMAKE_MATCH_1}") + set(COMPONENT_PYTHON_VERSION_MAJOR "${CMAKE_MATCH_2}") + set(COMPONENT_PYTHON_VERSION_MINOR "${CMAKE_MATCH_3}") + endif() + + unset(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME) + if(COMPONENT_PYTHON_VERSION_MINOR) + # Boost >= 1.67 + list( + APPEND + _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME + "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}" + ) + # Debian/Ubuntu (Some versions omit the 2 and/or 3 from the suffix) + list( + APPEND + _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME + "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}" + ) + list( + APPEND + _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME + "${COMPONENT_UNVERSIONED}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}" + ) + # Gentoo + list( + APPEND + _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME + "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}.${COMPONENT_PYTHON_VERSION_MINOR}" + ) + # RPMs + list( + APPEND + _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME + "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}" + ) + endif() + if(COMPONENT_PYTHON_VERSION_MAJOR AND NOT COMPONENT_PYTHON_VERSION_MINOR) + # Boost < 1.67 + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME + "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}") + endif() + + # Consolidate and report component-specific hints. + if(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME) + list(REMOVE_DUPLICATES _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME) + _boost_debug_print( + "${CMAKE_CURRENT_LIST_FILE}" + "${CMAKE_CURRENT_LIST_LINE}" + "Component-specific library search names for ${COMPONENT_NAME}: ${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME}" + ) + endif() + if(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT) + list(REMOVE_DUPLICATES _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT) + _boost_debug_print( + "${CMAKE_CURRENT_LIST_FILE}" + "${CMAKE_CURRENT_LIST_LINE}" + "Component-specific library search paths for ${COMPONENT}: ${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT}" + ) + endif() + + # + # Find headers + # + _boost_component_headers("${COMPONENT}" Boost_${UPPERCOMPONENT}_HEADER_NAME) + # Look for a standard boost header file. + if(Boost_${UPPERCOMPONENT}_HEADER_NAME) + if(EXISTS "${Boost_INCLUDE_DIR}/${Boost_${UPPERCOMPONENT}_HEADER_NAME}") + set(Boost_${UPPERCOMPONENT}_HEADER ON) + else() + set(Boost_${UPPERCOMPONENT}_HEADER OFF) + endif() + else() + set(Boost_${UPPERCOMPONENT}_HEADER ON) + if(NOT Boost_FIND_QUIETLY) + message(WARNING "No header defined for ${COMPONENT}; skipping header check " + "(note: header-only libraries have no designated component)") + endif() + endif() + + # + # Find RELEASE libraries + # + unset(_boost_RELEASE_NAMES) + foreach(component IN LISTS _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME COMPONENT) + foreach(compiler IN LISTS _boost_COMPILER) + list( + APPEND + _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} + ) + endforeach() + list( + APPEND + _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}) + if(_boost_STATIC_RUNTIME_WORKAROUND) + set(_boost_RELEASE_STATIC_ABI_TAG "-s${_boost_RELEASE_ABI_TAG}") + foreach(compiler IN LISTS _boost_COMPILER) + list( + APPEND + _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} + ) + endforeach() + list( + APPEND + _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} + ) + endif() + endforeach() + if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") + _boost_prepend_list_with_threadapi(_boost_RELEASE_NAMES ${_boost_RELEASE_NAMES}) + endif() + _boost_debug_print( + "${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Searching for ${UPPERCOMPONENT}_LIBRARY_RELEASE: ${_boost_RELEASE_NAMES}") + + # if Boost_LIBRARY_DIR_RELEASE is not defined, but Boost_LIBRARY_DIR_DEBUG is, look + # there first for RELEASE libs + if(NOT Boost_LIBRARY_DIR_RELEASE AND Boost_LIBRARY_DIR_DEBUG) + list(INSERT _boost_LIBRARY_SEARCH_DIRS_RELEASE 0 ${Boost_LIBRARY_DIR_DEBUG}) + endif() + + # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing. + string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp + "${_boost_LIBRARY_SEARCH_DIRS_RELEASE}") + + if(Boost_USE_RELEASE_LIBS) + _boost_find_library( + Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE + RELEASE + NAMES + ${_boost_RELEASE_NAMES} + HINTS + ${_boost_LIBRARY_SEARCH_DIRS_tmp} + NAMES_PER_DIR + DOC + "${_boost_docstring_release}") + endif() + + # + # Find DEBUG libraries + # + unset(_boost_DEBUG_NAMES) + foreach(component IN LISTS _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME COMPONENT) + foreach(compiler IN LISTS _boost_COMPILER) + list( + APPEND + _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} + ) + endforeach() + list( + APPEND + _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}) + if(_boost_STATIC_RUNTIME_WORKAROUND) + set(_boost_DEBUG_STATIC_ABI_TAG "-s${_boost_DEBUG_ABI_TAG}") + foreach(compiler IN LISTS _boost_COMPILER) + list( + APPEND + _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} + ) + endforeach() + list( + APPEND + _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} + ) + endif() + endforeach() + if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") + _boost_prepend_list_with_threadapi(_boost_DEBUG_NAMES ${_boost_DEBUG_NAMES}) + endif() + _boost_debug_print( + "${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" + "Searching for ${UPPERCOMPONENT}_LIBRARY_DEBUG: ${_boost_DEBUG_NAMES}") + + # if Boost_LIBRARY_DIR_DEBUG is not defined, but Boost_LIBRARY_DIR_RELEASE is, look + # there first for DEBUG libs + if(NOT Boost_LIBRARY_DIR_DEBUG AND Boost_LIBRARY_DIR_RELEASE) + list(INSERT _boost_LIBRARY_SEARCH_DIRS_DEBUG 0 ${Boost_LIBRARY_DIR_RELEASE}) + endif() + + # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing. + string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp + "${_boost_LIBRARY_SEARCH_DIRS_DEBUG}") + + if(Boost_USE_DEBUG_LIBS) + _boost_find_library( + Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG + DEBUG + NAMES + ${_boost_DEBUG_NAMES} + HINTS + ${_boost_LIBRARY_SEARCH_DIRS_tmp} + NAMES_PER_DIR + DOC + "${_boost_docstring_debug}") + endif() + + if(Boost_REALPATH) + _boost_swap_with_realpath(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE + "${_boost_docstring_release}") + _boost_swap_with_realpath(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG + "${_boost_docstring_debug}") + endif() + + _boost_adjust_lib_vars(${UPPERCOMPONENT}) + + # Check if component requires some compiler features + _boost_compiler_features(${COMPONENT} _Boost_${UPPERCOMPONENT}_COMPILER_FEATURES) + +endforeach() + +# Restore the original find library ordering +if(Boost_USE_STATIC_LIBS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${_boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) +endif() + +# ------------------------------------------------------------------------ +# End finding boost libraries +# ------------------------------------------------------------------------ + +set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) +set(Boost_LIBRARY_DIRS) +if(Boost_LIBRARY_DIR_RELEASE) + list(APPEND Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR_RELEASE}) +endif() +if(Boost_LIBRARY_DIR_DEBUG) + list(APPEND Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR_DEBUG}) +endif() +if(Boost_LIBRARY_DIRS) + list(REMOVE_DUPLICATES Boost_LIBRARY_DIRS) +endif() + +# ------------------------------------------------------------------------ +# Call FPHSA helper, see +# https://cmake.org/cmake/help/latest/module/FindPackageHandleStandardArgs.html +# ------------------------------------------------------------------------ + +# Define aliases as needed by the component handler in the FPHSA helper below +foreach(_comp IN LISTS Boost_FIND_COMPONENTS) + string(TOUPPER ${_comp} _uppercomp) + if(DEFINED Boost_${_uppercomp}_FOUND) + set(Boost_${_comp}_FOUND ${Boost_${_uppercomp}_FOUND}) + endif() +endforeach() + +find_package_handle_standard_args( + Boost + REQUIRED_VARS Boost_INCLUDE_DIR + VERSION_VAR Boost_VERSION_STRING + HANDLE_COMPONENTS) + +if(Boost_FOUND) + if(NOT Boost_LIBRARY_DIRS) + # Compatibility Code for backwards compatibility with CMake 2.4's FindBoost + # module. + + # Look for the boost library path. Note that the user may not have installed any + # libraries so it is quite possible the Boost_LIBRARY_DIRS may not exist. + set(_boost_LIB_DIR ${Boost_INCLUDE_DIR}) + + if("${_boost_LIB_DIR}" MATCHES "boost-[0-9]+") + get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) + endif() + + if("${_boost_LIB_DIR}" MATCHES "/include$") + # Strip off the trailing "/include" in the path. + get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) + endif() + + if(EXISTS "${_boost_LIB_DIR}/lib") + string(APPEND _boost_LIB_DIR /lib) + elseif(EXISTS "${_boost_LIB_DIR}/stage/lib") + string(APPEND _boost_LIB_DIR "/stage/lib") + else() + set(_boost_LIB_DIR "") + endif() + + if(_boost_LIB_DIR AND EXISTS "${_boost_LIB_DIR}") + set(Boost_LIBRARY_DIRS ${_boost_LIB_DIR}) + endif() + + endif() +else() + # Boost headers were not found so no components were found. + foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + set(Boost_${UPPERCOMPONENT}_FOUND 0) + endforeach() +endif() + +# ------------------------------------------------------------------------ +# Add imported targets +# ------------------------------------------------------------------------ + +if(Boost_FOUND) + # The builtin CMake package in Boost 1.70+ introduces a new name for the header-only + # lib, let's provide the same UI in module mode + if(NOT TARGET Boost::headers) + add_library(Boost::headers INTERFACE IMPORTED) + if(Boost_INCLUDE_DIRS) + set_target_properties(Boost::headers PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${Boost_INCLUDE_DIRS}") + endif() + endif() + + # Define the old target name for header-only libraries for backwards compat. + if(NOT TARGET Boost::boost) + add_library(Boost::boost INTERFACE IMPORTED) + set_target_properties(Boost::boost PROPERTIES INTERFACE_LINK_LIBRARIES + Boost::headers) + endif() + + foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + if(_Boost_IMPORTED_TARGETS AND NOT TARGET Boost::${COMPONENT}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + if(Boost_${UPPERCOMPONENT}_FOUND) + if(Boost_USE_STATIC_LIBS) + add_library(Boost::${COMPONENT} STATIC IMPORTED) + else() + # Even if Boost_USE_STATIC_LIBS is OFF, we might have static libraries + # as a result. + add_library(Boost::${COMPONENT} UNKNOWN IMPORTED) + endif() + if(Boost_INCLUDE_DIRS) + set_target_properties( + Boost::${COMPONENT} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${Boost_INCLUDE_DIRS}") + endif() + if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY}") + set_target_properties( + Boost::${COMPONENT} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${Boost_${UPPERCOMPONENT}_LIBRARY}") + endif() + if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE}") + set_property( + TARGET Boost::${COMPONENT} + APPEND + PROPERTY IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties( + Boost::${COMPONENT} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX" + IMPORTED_LOCATION_RELEASE + "${Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE}") + endif() + if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG}") + set_property( + TARGET Boost::${COMPONENT} + APPEND + PROPERTY IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties( + Boost::${COMPONENT} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX" + IMPORTED_LOCATION_DEBUG + "${Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG}") + endif() + if(_Boost_${UPPERCOMPONENT}_DEPENDENCIES) + unset(_Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES) + foreach(dep ${_Boost_${UPPERCOMPONENT}_DEPENDENCIES}) + list(APPEND _Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES + Boost::${dep}) + endforeach() + if(COMPONENT STREQUAL "thread") + list(APPEND _Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES + Threads::Threads) + endif() + set_target_properties( + Boost::${COMPONENT} + PROPERTIES INTERFACE_LINK_LIBRARIES + "${_Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES}") + endif() + if(_Boost_${UPPERCOMPONENT}_COMPILER_FEATURES) + set_target_properties( + Boost::${COMPONENT} + PROPERTIES INTERFACE_COMPILE_FEATURES + "${_Boost_${UPPERCOMPONENT}_COMPILER_FEATURES}") + endif() + endif() + endif() + endforeach() + + # Supply Boost_LIB_DIAGNOSTIC_DEFINITIONS as a convenience target. It will only + # contain any interface definitions on WIN32, but is created on all platforms to keep + # end user code free from platform dependent code. Also provide convenience targets + # to disable autolinking and enable dynamic linking. + if(NOT TARGET Boost::diagnostic_definitions) + add_library(Boost::diagnostic_definitions INTERFACE IMPORTED) + add_library(Boost::disable_autolinking INTERFACE IMPORTED) + add_library(Boost::dynamic_linking INTERFACE IMPORTED) + set_target_properties( + Boost::dynamic_linking PROPERTIES INTERFACE_COMPILE_DEFINITIONS + "BOOST_ALL_DYN_LINK") + endif() + if(WIN32) + # In windows, automatic linking is performed, so you do not have to specify the + # libraries. If you are linking to a dynamic runtime, then you can choose to link + # to either a static or a dynamic Boost library, the default is to do a static + # link. You can alter this for a specific library "whatever" by defining + # BOOST_WHATEVER_DYN_LINK to force Boost library "whatever" to be linked + # dynamically. Alternatively you can force all Boost libraries to dynamic link by + # defining BOOST_ALL_DYN_LINK. + + # This feature can be disabled for Boost library "whatever" by defining + # BOOST_WHATEVER_NO_LIB, or for all of Boost by defining BOOST_ALL_NO_LIB. + + # If you want to observe which libraries are being linked against then defining + # BOOST_LIB_DIAGNOSTIC will cause the auto-linking code to emit a #pragma message + # each time a library is selected for linking. + set(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC") + set_target_properties( + Boost::diagnostic_definitions PROPERTIES INTERFACE_COMPILE_DEFINITIONS + "BOOST_LIB_DIAGNOSTIC") + set_target_properties(Boost::disable_autolinking + PROPERTIES INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB") + endif() +endif() + +# ------------------------------------------------------------------------ +# Finalize +# ------------------------------------------------------------------------ + +# Report Boost_LIBRARIES +set(Boost_LIBRARIES "") +foreach(_comp IN LISTS Boost_FIND_COMPONENTS) + string(TOUPPER ${_comp} _uppercomp) + if(Boost_${_uppercomp}_FOUND) + list(APPEND Boost_LIBRARIES ${Boost_${_uppercomp}_LIBRARY}) + if(_comp STREQUAL "thread") + list(APPEND Boost_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + endif() + endif() +endforeach() + +# Configure display of cache entries in GUI. +foreach(v BOOSTROOT BOOST_ROOT ${_Boost_VARS_INC} ${_Boost_VARS_LIB}) + get_property( + _type + CACHE ${v} + PROPERTY TYPE) + if(_type) + set_property(CACHE ${v} PROPERTY ADVANCED 1) + if("x${_type}" STREQUAL "xUNINITIALIZED") + if("x${v}" STREQUAL "xBoost_ADDITIONAL_VERSIONS") + set_property(CACHE ${v} PROPERTY TYPE STRING) + else() + set_property(CACHE ${v} PROPERTY TYPE PATH) + endif() + endif() + endif() +endforeach() + +# Record last used values of input variables so we can detect on the next run if the user +# changed them. +foreach(v ${_Boost_VARS_INC} ${_Boost_VARS_LIB} ${_Boost_VARS_DIR} ${_Boost_VARS_NAME}) + if(DEFINED ${v}) + set(_${v}_LAST + "${${v}}" + CACHE INTERNAL "Last used ${v} value.") + else() + unset(_${v}_LAST CACHE) + endif() +endforeach() + +# Maintain a persistent list of components requested anywhere since the last flush. +set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}") +list(APPEND _Boost_COMPONENTS_SEARCHED ${Boost_FIND_COMPONENTS}) +list(REMOVE_DUPLICATES _Boost_COMPONENTS_SEARCHED) +list(SORT _Boost_COMPONENTS_SEARCHED) +set(_Boost_COMPONENTS_SEARCHED + "${_Boost_COMPONENTS_SEARCHED}" + CACHE INTERNAL "Components requested for this build tree.") + +# Restore project's policies +cmake_policy(POP) diff --git a/projects/rocprofiler-systems/cmake/Modules/Findroctracer.cmake b/projects/rocprofiler-systems/cmake/Modules/Findroctracer.cmake index 672c1fa913..e085fb62fc 100644 --- a/projects/rocprofiler-systems/cmake/Modules/Findroctracer.cmake +++ b/projects/rocprofiler-systems/cmake/Modules/Findroctracer.cmake @@ -100,10 +100,6 @@ if(roctracer_FOUND) if(roctracer_kfdwrapper_LIBRARY) list(APPEND roctracer_LIBRARIES ${roctracer_kfdwrapper_LIBRARY}) - target_compile_definitions( - roctracer::roctracer - INTERFACE - HOSTTRACE_ROCTRACER_LIBKFDWRAPPER=\"${roctracer_kfdwrapper_LIBRARY}\") target_link_libraries(roctracer::roctracer INTERFACE ${roctracer_kfdwrapper_LIBRARY}) target_link_libraries(roctracer::roctx INTERFACE ${roctracer_kfdwrapper_LIBRARY}) diff --git a/projects/rocprofiler-systems/cmake/Packages.cmake b/projects/rocprofiler-systems/cmake/Packages.cmake index 0ba6b6c340..4bb6b73dbe 100644 --- a/projects/rocprofiler-systems/cmake/Packages.cmake +++ b/projects/rocprofiler-systems/cmake/Packages.cmake @@ -7,14 +7,19 @@ include_guard(DIRECTORY) # # ######################################################################################## -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_add_interface_library( + hosttrace-headers "Provides minimal set of include flags to compile with hosttrace") +hosttrace_add_interface_library(hosttrace-threading "Enables multithreading support") +hosttrace_add_interface_library( hosttrace-dyninst "Provides flags and libraries for Dyninst (dynamic instrumentation)") -add_interface_library(hosttrace-roctracer "Provides flags and libraries for roctracer") -add_interface_library(hosttrace-mpi "Provides MPI or MPI headers") +hosttrace_add_interface_library(hosttrace-roctracer + "Provides flags and libraries for roctracer") +hosttrace_add_interface_library(hosttrace-mpi "Provides MPI or MPI headers") +hosttrace_add_interface_library(hosttrace-ptl "Enables PTL support (tasking)") + +target_include_directories(hosttrace-headers INTERFACE ${PROJECT_SOURCE_DIR}/include + ${PROJECT_BINARY_DIR}/include) # include threading because of rooflines target_link_libraries(hosttrace-headers INTERFACE hosttrace-threading) @@ -80,7 +85,7 @@ endif() # ----------------------------------------------------------------------------------------# if(HOSTTRACE_BUILD_DYNINST) - checkout_git_submodule( + hosttrace_checkout_git_submodule( RELATIVE_PATH external/dyninst WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} REPO_URL https://github.com/jrmadsen/dyninst.git @@ -145,7 +150,7 @@ else() hosttrace-dyninst INTERFACE DYNINST_API_RT="${HOSTTRACE_DYNINST_API_RT}") endif() - add_rpath(${Dyninst_LIBRARIES}) + hosttrace_add_rpath(${Dyninst_LIBRARIES}) target_link_libraries(hosttrace-dyninst INTERFACE Dyninst::Dyninst) else() # updated Dyninst CMake system was not found set(_BOOST_COMPONENTS atomic system thread date_time) @@ -204,7 +209,7 @@ else() endif() endif() - add_rpath(${DYNINST_LIBRARIES} ${Boost_LIBRARIES}) + hosttrace_add_rpath(${DYNINST_LIBRARIES} ${Boost_LIBRARIES}) target_link_libraries(hosttrace-dyninst INTERFACE ${DYNINST_LIBRARIES} ${Boost_LIBRARIES}) foreach( @@ -242,7 +247,7 @@ endif() # ----------------------------------------------------------------------------------------# set(perfetto_DIR ${PROJECT_SOURCE_DIR}/external/perfetto) -checkout_git_submodule( +hosttrace_checkout_git_submodule( RELATIVE_PATH external/perfetto WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} REPO_URL https://android.googlesource.com/platform/external/perfetto @@ -256,7 +261,7 @@ checkout_git_submodule( # ----------------------------------------------------------------------------------------# if(HOSTTRACE_BUILD_DEVICETRACE) - checkout_git_submodule( + hosttrace_checkout_git_submodule( RELATIVE_PATH external/elfio WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} REPO_URL https://github.com/jrmadsen/ELFIO.git @@ -267,63 +272,10 @@ endif() # ----------------------------------------------------------------------------------------# # -# Clang Tidy +# timemory submodule # # ----------------------------------------------------------------------------------------# -# 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() - -# ------------------------------------------------------------------------------# -# -# clang-format target -# -# ------------------------------------------------------------------------------# - -find_program(HOSTTRACE_CLANG_FORMAT_EXE NAMES clang-format-11 clang-format-mp-11 - clang-format) - -if(HOSTTRACE_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 - ${HOSTTRACE_CLANG_FORMAT_EXE} -i ${sources} ${headers} ${examples} - COMMENT "Running ${HOSTTRACE_CLANG_FORMAT_EXE}...") -else() - message( - AUTHOR_WARNING - "clang-format could not be found. format build target not available.") -endif() - -# ----------------------------------------------------------------------------------------# -# configure submodule -# ----------------------------------------------------------------------------------------# - set(TIMEMORY_INSTALL_HEADERS OFF CACHE BOOL "Disable timemory header install") @@ -365,7 +317,7 @@ set(TIMEMORY_TLS_MODEL "global-dynamic" CACHE STRING "Thread-local static model" FORCE) -checkout_git_submodule( +hosttrace_checkout_git_submodule( RELATIVE_PATH external/timemory WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} REPO_URL https://github.com/NERSC/timemory.git @@ -384,3 +336,41 @@ add_subdirectory(external/timemory) hosttrace_restore_variables(BUILD_CONFIG VARIABLES BUILD_SHARED_LIBS BUILD_STATIC_LIBS CMAKE_POSITION_INDEPENDENT_CODE) + +# ----------------------------------------------------------------------------------------# +# +# PTL (Parallel Tasking Library) submodule +# +# ----------------------------------------------------------------------------------------# + +# timemory might provide PTL::ptl-shared +if(NOT TARGET PTL::ptl-shared) + hosttrace_checkout_git_submodule( + RELATIVE_PATH external/PTL + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + REPO_URL https://github.com/jrmadsen/PTL.git + REPO_BRANCH master) + + set(PTL_BUILD_EXAMPLES OFF) + set(PTL_USE_TBB OFF) + set(PTL_USE_GPU OFF) + set(PTL_DEVELOPER_INSTALL OFF) + + hosttrace_save_variables( + BUILD_CONFIG + VARIABLES BUILD_SHARED_LIBS BUILD_STATIC_LIBS CMAKE_POSITION_INDEPENDENT_CODE + CMAKE_CXX_VISIBILITY_PRESET CMAKE_VISIBILITY_INLINES_HIDDEN) + + set(BUILD_SHARED_LIBS ON) + set(BUILD_STATIC_LIBS OFF) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + + add_subdirectory(external/PTL) + hosttrace_restore_variables( + BUILD_CONFIG + VARIABLES BUILD_SHARED_LIBS BUILD_STATIC_LIBS CMAKE_POSITION_INDEPENDENT_CODE + CMAKE_CXX_VISIBILITY_PRESET CMAKE_VISIBILITY_INLINES_HIDDEN) +endif() + +target_link_libraries(hosttrace-ptl INTERFACE PTL::ptl-shared) diff --git a/projects/rocprofiler-systems/examples/parallel-overhead/parallel-overhead.cpp b/projects/rocprofiler-systems/examples/parallel-overhead/parallel-overhead.cpp index 8c7e005a1c..bc08ce8437 100644 --- a/projects/rocprofiler-systems/examples/parallel-overhead/parallel-overhead.cpp +++ b/projects/rocprofiler-systems/examples/parallel-overhead/parallel-overhead.cpp @@ -38,7 +38,10 @@ main(int argc, char** argv) std::vector threads{}; for(size_t i = 0; i < nthread; ++i) - threads.emplace_back(&run, nitr, nfib); + { + size_t _nitr = ((i % 2) == 1) ? (nitr - (0.1 * nitr)) : (nitr + (0.1 * nitr)); + threads.emplace_back(&run, _nitr, nfib); + } for(auto& itr : threads) itr.join(); diff --git a/projects/rocprofiler-systems/examples/transpose/transpose.cpp b/projects/rocprofiler-systems/examples/transpose/transpose.cpp index 3ca1f16373..3946732de1 100644 --- a/projects/rocprofiler-systems/examples/transpose/transpose.cpp +++ b/projects/rocprofiler-systems/examples/transpose/transpose.cpp @@ -29,6 +29,7 @@ THE SOFTWARE. #include #include #include +#include #include #define HIP_API_CALL(CALL) \ @@ -88,8 +89,8 @@ run(int rank, int argc, char** argv) { (void) argc; (void) argv; - unsigned int M = 4960; - unsigned int N = 4960; + unsigned int M = 4960 * 2; + unsigned int N = 4960 * 2; std::cout << "[" << rank << "] M: " << M << " N: " << N << std::endl; size_t size = sizeof(int) * M * N; @@ -102,29 +103,30 @@ run(int rank, int argc, char** argv) 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(); + auto _func = [&](hipStream_t stream) { + for(size_t i = 0; i < times / 2; i++) + { + transpose_a<<>>(in, out, M, N); + check_hip_error(); + } + HIP_API_CALL(hipStreamSynchronize(stream)); + }; + hipStream_t _stream{}; + HIP_API_CALL(hipStreamCreate(&_stream)); + std::thread _t{ _func, _stream }; + _t.join(); + _func(0); HIP_API_CALL(hipDeviceSynchronize()); t2 = std::chrono::high_resolution_clock::now(); double time = @@ -136,14 +138,12 @@ run(int rank, int argc, char** argv) 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); @@ -171,12 +171,32 @@ do_a2a(int rank) int main(int argc, char** argv) { - int rank = 0; + int rank = 0; + int nthreads = 2; + if(argc > 1) nthreads = atoi(argv[1]); + #if defined(USE_MPI) MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); #endif - if(rank == 0) run(rank, argc, argv); + // this is a temporary workaround in hosttrace when HIP + MPI is enabled + int ndevice = 0; + int devid = rank; + HIP_API_CALL(hipGetDeviceCount(&ndevice)); + if(ndevice > 0) + { + devid = rank % ndevice; + HIP_API_CALL(hipSetDevice(devid)); + } + if(rank == devid && rank < ndevice) + { + std::vector _threads{}; + for(int i = 1; i < nthreads; ++i) + _threads.emplace_back(run, rank, argc, argv); + run(rank, argc, argv); + for(auto& itr : _threads) + itr.join(); + } #if defined(USE_MPI) MPI_Barrier(MPI_COMM_WORLD); do_a2a(rank); diff --git a/projects/rocprofiler-systems/external/PTL b/projects/rocprofiler-systems/external/PTL new file mode 160000 index 0000000000..dd1b67829c --- /dev/null +++ b/projects/rocprofiler-systems/external/PTL @@ -0,0 +1 @@ +Subproject commit dd1b67829c9875bd78016d3aad35f22890b59b6c diff --git a/projects/rocprofiler-systems/external/timemory b/projects/rocprofiler-systems/external/timemory index 11183bbdd7..c040fe7022 160000 --- a/projects/rocprofiler-systems/external/timemory +++ b/projects/rocprofiler-systems/external/timemory @@ -1 +1 @@ -Subproject commit 11183bbdd77c58a3165b08d656ff7336ef6b825b +Subproject commit c040fe702285e7b4345d4ccfdb8a08704dbeeb83 diff --git a/projects/rocprofiler-systems/include/hosttrace.hpp b/projects/rocprofiler-systems/include/hosttrace.hpp index 3a53e24aec..0ed8d60b81 100644 --- a/projects/rocprofiler-systems/include/hosttrace.hpp +++ b/projects/rocprofiler-systems/include/hosttrace.hpp @@ -1,27 +1,30 @@ -// 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. +// Copyright (c) 2018 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 +// with 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. +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. // // 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 +// CONTRIBUTORS 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. -// +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH +// THE SOFTWARE. #pragma once @@ -274,7 +277,7 @@ struct function_signature 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_return = {}; string_t m_name = {}; string_t m_params = "()"; string_t m_file = {}; @@ -318,7 +321,7 @@ struct function_signature string_t get() const { std::stringstream ss; - if(use_return_info) ss << m_return << " "; + if(use_return_info && !m_return.empty()) ss << m_return << " "; ss << m_name; if(use_args_info) ss << m_params; if(m_loop && m_info_beg) diff --git a/projects/rocprofiler-systems/include/library.hpp b/projects/rocprofiler-systems/include/library.hpp index 2a727084ea..184c8b91a1 100644 --- a/projects/rocprofiler-systems/include/library.hpp +++ b/projects/rocprofiler-systems/include/library.hpp @@ -1,275 +1,112 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. #pragma once -#if !defined(TIMEMORY_USE_PERFETTO) -# include -# define PERFETTO_CATEGORIES \ - perfetto::Category("host").SetDescription("Host-side function tracing"), \ - perfetto::Category("device").SetDescription("Device-side function tracing") -#else -# define PERFETTO_CATEGORIES \ - perfetto::Category("host").SetDescription("Host-side function tracing"), \ - perfetto::Category("device").SetDescription("Device-side function tracing") -perfetto::Category("timemory") - .SetDescription("Events from the timemory API") -# define TIMEMORY_PERFETTO_CATEGORIES PERFETTO_CATEGORIES -#endif +// this always needs to included first +// clang-format off +#include "library/perfetto.hpp" +// clang-format on + +#include "library/timemory.hpp" +#include "library/roctracer.hpp" +#include "library/api.hpp" +#include "library/fork_gotcha.hpp" +#include "library/mpi_gotcha.hpp" +#include "library/api.hpp" +#include "library/common.hpp" +#include "library/state.hpp" +#include "library/config.hpp" +#include "library/thread_data.hpp" +#include "library/ptl.hpp" +#include "library/debug.hpp" +#include "library/critical_trace.hpp" +#include "timemory/macros/language.hpp" +#include "timemory/utility/utility.hpp" -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include "timemory/api.hpp" -#include "timemory/backends/mpi.hpp" -#include "timemory/backends/process.hpp" -#include "timemory/backends/threading.hpp" -#include "timemory/components.hpp" -#include "timemory/components/gotcha/mpip.hpp" -#include "timemory/components/papi/papi_tuple.hpp" -#include "timemory/config.hpp" -#include "timemory/environment.hpp" -#include "timemory/manager.hpp" -#include "timemory/mpl/apply.hpp" -#include "timemory/operations.hpp" -#include "timemory/runtime.hpp" -#include "timemory/settings.hpp" -#include "timemory/storage.hpp" -#include "timemory/variadic.hpp" - -#include "roctracer.hpp" - -// forward decl of the API -extern "C" +template +inline void +add_critical_trace(int64_t _tid, size_t _cpu_cid, size_t _gpu_cid, size_t _parent_cid, + int64_t _ts_beg, int64_t _ts_val, size_t _hash, uint16_t _depth, + uint16_t _prio = 0) { - void hosttrace_push_trace(const char* name) TIMEMORY_VISIBILITY("default"); - void hosttrace_pop_trace(const char* name) TIMEMORY_VISIBILITY("default"); - void hosttrace_trace_init(const char*, bool, const char*) - TIMEMORY_VISIBILITY("default"); - void hosttrace_trace_finalize(void) TIMEMORY_VISIBILITY("default"); - void hosttrace_trace_set_env(const char* env_name, const char* env_val) - TIMEMORY_VISIBILITY("default"); - void hosttrace_trace_set_mpi(bool use, bool attached) TIMEMORY_VISIBILITY("default"); -} + if(!get_use_critical_trace()) return; -//--------------------------------------------------------------------------------------// + // clang-format off + // these are used to create unique type mutexes + struct critical_insert {}; + struct cpu_cid_stack {}; + // clang-format on -// same sort of functionality as python's " ".join([...]) -#if !defined(JOIN) -# define JOIN(...) tim::mpl::apply::join(__VA_ARGS__) -#endif + using tim::type_mutex; + using auto_lock_t = tim::auto_lock_t; + static constexpr auto num_mutexes = max_supported_threads; + static auto _update_freq = critical_trace::get_update_frequency(); -#define HOSTTRACE_DEBUG(...) \ - if(get_debug()) \ - { \ - fprintf(stderr, __VA_ARGS__); \ - fflush(stderr); \ - } - -//--------------------------------------------------------------------------------------// - -namespace audit = tim::audit; -namespace comp = tim::component; -namespace quirk = tim::quirk; -namespace threading = tim::threading; -namespace scope = tim::scope; -namespace dmp = tim::dmp; -namespace process = tim::process; -namespace units = tim::units; -namespace trait = tim::trait; - -// this is used to wrap fork() -struct fork_gotcha : comp::base -{ - using gotcha_data_t = comp::gotcha_data; - - TIMEMORY_DEFAULT_OBJECT(fork_gotcha) - - // this will get called right before fork - void audit(const gotcha_data_t& _data, audit::incoming); - - // this will get called right after fork with the return value - void audit(const gotcha_data_t& _data, audit::outgoing, pid_t _pid); -}; - -// this is used to wrap MPI_Init and MPI_Init_thread -struct mpi_gotcha : comp::base -{ - using gotcha_data_t = comp::gotcha_data; - - TIMEMORY_DEFAULT_OBJECT(mpi_gotcha) - - // this will get called right before MPI_Init with that functions arguments - void audit(const gotcha_data_t& _data, audit::incoming, int*, char***); - - // this will get called right before MPI_Init_thread with that functions arguments - void audit(const gotcha_data_t& _data, audit::incoming, int*, char***, int, int*); - - // this will get called right after MPI_Init and MPI_Init_thread with the return value - void audit(const gotcha_data_t& _data, audit::outgoing, int _retval); - - // this will get called right before MPI_Finalize - void audit(const gotcha_data_t& _data, audit::incoming); -}; - -// timemory api struct -struct hosttrace : tim::concepts::api -{}; - -// timemory component which calls hosttrace functions -// (used in gotcha wrappers) -struct hosttrace_component : tim::component::base -{ - void start(); - void stop(); - void set_prefix(const char*); - -private: - const char* m_prefix = nullptr; -}; - -using papi_tot_ins = comp::papi_tuple; -using fork_gotcha_t = comp::gotcha<4, tim::component_tuple, hosttrace>; -using mpi_gotcha_t = comp::gotcha<4, tim::component_tuple, hosttrace>; -using hosttrace_bundle_t = - tim::lightweight_tuple; -using hosttrace_thread_bundle_t = - tim::lightweight_tuple; -using bundle_t = - tim::component_bundle; -using bundle_allocator_t = tim::data::ring_buffer_allocator; - -//--------------------------------------------------------------------------------------// - -#if !defined(TIMEMORY_USE_PERFETTO) -PERFETTO_DEFINE_CATEGORIES(PERFETTO_CATEGORIES); -#endif - -#if defined(CUSTOM_DATA_SOURCE) -class CustomDataSource : public perfetto::DataSource -{ -public: - void OnSetup(const SetupArgs&) override + if constexpr(PhaseID != critical_trace::Phase::NONE) { - // Use this callback to apply any custom configuration to your data source - // based on the TraceConfig in SetupArgs. - PRINT_HERE("%s", "setup"); + // unique lock per thread + auto& _mtx = type_mutex(_tid); + auto_lock_t _lk{ _mtx }; + + auto& _critical_trace = critical_trace::get(_tid); + _critical_trace->emplace_back( + critical_trace::entry{ _prio, DevID, PhaseID, _depth, _tid, _cpu_cid, + _gpu_cid, _parent_cid, _ts_beg, _ts_val, _hash }); } - void OnStart(const StartArgs&) override + if constexpr(UpdateStack) { - // This notification can be used to initialize the GPU driver, enable - // counters, etc. StartArgs will contains the DataSourceDescriptor, - // which can be extended. - PRINT_HERE("%s", "start"); + // unique lock per thread + auto& _mtx = type_mutex(_tid); + + if constexpr(PhaseID == critical_trace::Phase::NONE) + { + auto_lock_t _lk{ _mtx }; + get_cpu_cid_stack(_tid)->emplace_back(_cpu_cid); + } + else if constexpr(PhaseID == critical_trace::Phase::BEGIN) + { + auto_lock_t _lk{ _mtx }; + get_cpu_cid_stack(_tid)->emplace_back(_cpu_cid); + } + else if constexpr(PhaseID == critical_trace::Phase::END) + { + auto_lock_t _lk{ _mtx }; + get_cpu_cid_stack(_tid)->pop_back(); + if(_gpu_cid == 0 && _cpu_cid % _update_freq == (_update_freq - 1)) + critical_trace::update(_tid); + } } - void OnStop(const StopArgs&) override - { - // Undo any initialization done in OnStart. - PRINT_HERE("%s", "stop"); - } - - // Data sources can also have per-instance state. - int my_custom_state = 0; -}; - -PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource); -#endif - -//--------------------------------------------------------------------------------------// - -// used for specifying the state of hosttrace -enum class State : unsigned short -{ - DelayedInit = 0, - PreInit, - Active, - Finalized -}; - -bool -get_debug(); - -State& -get_state(); - -std::unique_ptr& -get_main_bundle(); - -bool -get_use_perfetto(); - -bool -get_use_timemory(); - -//--------------------------------------------------------------------------------------// - -template -struct hosttrace_thread_data -{ - static constexpr size_t max_supported_threads = MaxThreads; - using instance_array_t = std::array, max_supported_threads>; - - template - static void construct(Args&&...); - static std::unique_ptr& instance(); - static instance_array_t& instances(); -}; - -template -template -void -hosttrace_thread_data::construct(Args&&... _args) -{ - static thread_local bool _v = [&_args...]() { - instances().at(threading::get_id()) = - std::make_unique(std::forward(_args)...); - return true; - }(); - (void) _v; + tim::consume_parameters(_tid, _cpu_cid, _gpu_cid, _parent_cid, _ts_beg, _ts_val, + _hash, _depth, _prio); } - -template -std::unique_ptr& -hosttrace_thread_data::instance() -{ - return instances().at(threading::get_id()); -} - -template -typename hosttrace_thread_data::instance_array_t& -hosttrace_thread_data::instances() -{ - static auto _v = instance_array_t{}; - return _v; -} - -//--------------------------------------------------------------------------------------// - -// there are currently some strange things that happen with vector so using -// vector and timemory's ring_buffer_allocator to create contiguous memory-page -// aligned instances of the bundle -struct hosttrace_timemory_data -{ - static constexpr size_t max_supported_threads = 1024; - using instance_array_t = std::array; - - bundle_allocator_t allocator{}; - std::vector bundles{}; - - static instance_array_t& instances(); -}; - -//--------------------------------------------------------------------------------------// diff --git a/projects/rocprofiler-systems/include/library/api.hpp b/projects/rocprofiler-systems/include/library/api.hpp new file mode 100644 index 0000000000..1b2040e807 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/api.hpp @@ -0,0 +1,44 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include + +// forward decl of the API +extern "C" +{ + void hosttrace_push_trace(const char* name) TIMEMORY_VISIBILITY("default"); + void hosttrace_pop_trace(const char* name) TIMEMORY_VISIBILITY("default"); + void hosttrace_trace_init(const char*, bool, const char*) + TIMEMORY_VISIBILITY("default"); + void hosttrace_trace_finalize(void) TIMEMORY_VISIBILITY("default"); + void hosttrace_trace_set_env(const char* env_name, const char* env_val) + TIMEMORY_VISIBILITY("default"); + void hosttrace_trace_set_mpi(bool use, bool attached) TIMEMORY_VISIBILITY("default"); +} diff --git a/projects/rocprofiler-systems/include/library/common.hpp b/projects/rocprofiler-systems/include/library/common.hpp new file mode 100644 index 0000000000..bea928be6a --- /dev/null +++ b/projects/rocprofiler-systems/include/library/common.hpp @@ -0,0 +1,50 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// timemory api struct +struct hosttrace : tim::concepts::api +{}; diff --git a/projects/rocprofiler-systems/include/library/config.hpp b/projects/rocprofiler-systems/include/library/config.hpp new file mode 100644 index 0000000000..f744782fbc --- /dev/null +++ b/projects/rocprofiler-systems/include/library/config.hpp @@ -0,0 +1,163 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "library/api.hpp" +#include "library/common.hpp" +#include "library/fork_gotcha.hpp" +#include "library/mpi_gotcha.hpp" +#include "library/roctracer.hpp" +#include "library/state.hpp" +#include "library/timemory.hpp" + +#include + +#include + +// bundle of components around hosttrace_init and hosttrace_finalize +using main_bundle_t = + tim::lightweight_tuple; + +// bundle of components used in instrumentation +using instrumentation_bundle_t = + tim::component_bundle; + +// allocator for instrumentation_bundle_t +using bundle_allocator_t = tim::data::ring_buffer_allocator; + +// bundle of components around each thread +using hosttrace_thread_bundle_t = + tim::lightweight_tuple 0 + comp::peak_rss, +#endif + papi_tot_ins>; + +// +// Initialization routines +// +void +configure_settings(); + +void +print_config_settings(std::ostream& _os, + std::function&& _filter); + +std::string& +get_exe_name(); + +// +// User-configurable settings +// +std::string +get_config_file(); + +bool +get_debug(); + +bool +get_use_perfetto(); + +bool +get_use_timemory(); + +bool& +get_use_pid(); + +bool +get_use_mpip(); + +bool +get_use_critical_trace(); + +bool +get_roctracer_timeline_profile(); + +bool +get_roctracer_flat_profile(); + +bool +get_trace_hsa_api(); + +bool +get_trace_hsa_activity(); + +bool +get_critical_trace_debug(); + +bool +get_critical_trace_serialize_names(); + +size_t +get_perfetto_shmem_size_hint(); + +size_t +get_perfetto_buffer_size(); + +uint64_t +get_critical_trace_update_freq(); + +uint64_t +get_critical_trace_num_threads(); + +std::string +get_trace_hsa_api_types(); + +std::string& +get_backend(); + +std::string +get_perfetto_output_filename(); + +int64_t +get_critical_trace_count(); + +size_t& +get_sample_rate(); + +int64_t +get_critical_trace_per_row(); + +// +// Runtime configuration data +// +State& +get_state(); + +std::unique_ptr& +get_main_bundle(); + +std::atomic& +get_cpu_cid(); + +std::unique_ptr>& +get_cpu_cid_stack(int64_t _tid = threading::get_id()); diff --git a/projects/rocprofiler-systems/include/library/critical_trace.hpp b/projects/rocprofiler-systems/include/library/critical_trace.hpp new file mode 100644 index 0000000000..2437ce513f --- /dev/null +++ b/projects/rocprofiler-systems/include/library/critical_trace.hpp @@ -0,0 +1,209 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "library/config.hpp" +#include "library/thread_data.hpp" +#include "timemory/tpls/cereal/cereal/cereal.hpp" + +#include +#include +#include +#include +#include + +namespace critical_trace +{ +enum class Device : short +{ + NONE = 0, + CPU, + GPU, +}; + +enum class Phase : short +{ + NONE = 0, + BEGIN, + END, + DELTA, +}; + +struct entry +{ + entry() = default; + ~entry() = default; + entry(const entry&) = default; + entry(entry&&) noexcept = default; + entry& operator=(const entry&) = default; + entry& operator=(entry&&) noexcept = default; + + uint16_t priority = 0; // priority value (for sorting) + Device device = Device::CPU; // which device it executed on + Phase phase = Phase::NONE; // start / stop / unspecified + uint16_t depth = 0; // call-stack depth + int64_t tid = 0; // thread id it was registered on + uint64_t cpu_cid = 0; // CPU correlation id + uint64_t gpu_cid = 0; // GPU correlation id + uint64_t parent_cid = 0; // parent CPU correlation id + int64_t begin_ns = 0; // timestamp of start + int64_t end_ns = 0; // timestamp of end + size_t hash = 0; // hash for name + + bool operator==(const entry& rhs) const; + bool operator!=(const entry& rhs) const { return !(*this == rhs); } + bool operator<(const entry& rhs) const; + bool operator>(const entry& rhs) const; + bool operator<=(const entry& rhs) const { return !(*this > rhs); } + bool operator>=(const entry& rhs) const { return !(*this < rhs); } + + entry& operator+=(const entry& rhs); + + size_t get_hash() const; + int64_t get_timestamp() const; + + int64_t get_cost() const; + + bool is_bounded(const entry& rhs) const; + int64_t get_overlap(const entry& rhs) const; + int64_t get_independent(const entry& rhs) const; + + int64_t get_overlap(const entry& rhs, int64_t _tid) const; + int64_t get_independent(const entry& rhs, int64_t _tid) const; + bool is_bounded(const entry& rhs, int64_t _tid) const; + + void write(std::ostream& _os) const; + + static bool is_delta(const entry&, const std::string_view&); + + friend std::ostream& operator<<(std::ostream& _os, const entry& _v) + { + _v.write(_os); + return _os; + } + template + void serialize(Archive& ar, unsigned int); +}; + +template +void +entry::serialize(Archive& ar, unsigned int) +{ + namespace cereal = tim::cereal; + ar(cereal::make_nvp("priority", priority), cereal::make_nvp("device", device), + cereal::make_nvp("phase", phase), cereal::make_nvp("depth", depth), + cereal::make_nvp("tid", tid), cereal::make_nvp("cpu_cid", cpu_cid), + cereal::make_nvp("gpu_cid", gpu_cid), cereal::make_nvp("parent_cid", parent_cid), + cereal::make_nvp("begin_ns", begin_ns), cereal::make_nvp("end_ns", end_ns), + cereal::make_nvp("hash", hash)); + + if(get_critical_trace_serialize_names()) + { + std::string _name{}; + if(hash > 0) _name = tim::demangle(tim::get_hash_identifier(hash)); + ar(cereal::make_nvp("name", _name)); + } +} + +struct call_chain : private std::vector +{ + using base_type = std::vector; + + using base_type::at; + using base_type::back; + using base_type::begin; + using base_type::cbegin; + using base_type::cend; + using base_type::clear; + using base_type::emplace_back; + using base_type::empty; + using base_type::end; + using base_type::erase; + using base_type::front; + using base_type::pop_back; + using base_type::push_back; + using base_type::rbegin; + using base_type::rend; + using base_type::reserve; + using base_type::size; + + size_t get_hash() const; + int64_t get_cost(int64_t _tid = -1) const; + int64_t get_overlap(int64_t _tid = -1) const; + int64_t get_independent(int64_t _tid = -1) const; + static std::vector& get_top_chains(); + + bool operator==(const call_chain& rhs) const; + bool operator!=(const call_chain& rhs) const { return !(*this == rhs); } + friend std::ostream& operator<<(std::ostream& _os, const call_chain& _v) + { + size_t _n = 0; + for(const auto& itr : _v) + _os << " [" << _n++ << "] " << itr << "\n"; + return _os; + } + + template + void serialize(Archive& ar, unsigned int) + { + namespace cereal = tim::cereal; + ar(cereal::make_nvp("call_chain", static_cast(*this))); + } + + template + void generate_perfetto(std::set& _used) const; + + template + bool query(FuncT&&) const; +}; + +using hash_ids = std::unordered_set; + +uint64_t +get_update_frequency(); + +std::unique_ptr& +get(int64_t _tid = threading::get_id()); + +size_t +add_hash_id(const std::string& _label); + +void +add_hash_id(const hash_ids&); + +void +update(int64_t _tid = threading::get_id()); + +void +compute(int64_t _tid = threading::get_id()); + +struct id +{}; + +} // namespace critical_trace diff --git a/projects/rocprofiler-systems/include/library/debug.hpp b/projects/rocprofiler-systems/include/library/debug.hpp new file mode 100644 index 0000000000..c639471c97 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/debug.hpp @@ -0,0 +1,80 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +bool +get_debug(); + +bool +get_critical_trace_debug(); + +#if defined(TIMEMORY_USE_MPI) +# define HOSTTRACE_CONDITIONAL_PRINT(COND, ...) \ + if(COND) \ + { \ + fflush(stderr); \ + tim::auto_lock_t _lk{ tim::type_mutex() }; \ + fprintf(stderr, "[hosttrace][%i][%li] ", static_cast(tim::dmp::rank()), \ + tim::threading::get_id()); \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } +#else +# define HOSTTRACE_CONDITIONAL_PRINT(COND, ...) \ + if(COND) \ + { \ + fflush(stderr); \ + tim::auto_lock_t _lk{ tim::type_mutex() }; \ + fprintf(stderr, "[hosttrace][%i][%li] ", \ + static_cast(tim::process::get_id()), tim::threading::get_id()); \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } +#endif + +#define HOSTTRACE_CONDITIONAL_BASIC_PRINT(COND, ...) \ + if(COND) \ + { \ + fflush(stderr); \ + tim::auto_lock_t _lk{ tim::type_mutex() }; \ + fprintf(stderr, "[hosttrace] "); \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } + +#define HOSTTRACE_DEBUG(...) HOSTTRACE_CONDITIONAL_PRINT(get_debug(), __VA_ARGS__) +#define HOSTTRACE_PRINT(...) HOSTTRACE_CONDITIONAL_PRINT(true, __VA_ARGS__) +#define HOSTTRACE_CT_DEBUG(...) \ + HOSTTRACE_CONDITIONAL_PRINT(get_critical_trace_debug(), __VA_ARGS__) diff --git a/projects/rocprofiler-systems/include/library/defines.hpp.in b/projects/rocprofiler-systems/include/library/defines.hpp.in new file mode 100644 index 0000000000..86fdc4bf62 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/defines.hpp.in @@ -0,0 +1,42 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +// clang-format off +#define HOSTTRACE_HIP_VERSION_STRING "@HIP_VERSION@" +#define HOSTTRACE_HIP_VERSION_MAJOR @HIP_VERSION_MAJOR@ +#define HOSTTRACE_HIP_VERSION_MINOR @HIP_VERSION_MINOR@ +#define HOSTTRACE_HIP_VERSION_PATCH @HIP_VERSION_PATCH@ +// clang-format on + +#if defined(HOSTTRACE_USE_ROCTRACER) +# define HOSTTRACE_ROCTRACER_LIBKFDWRAPPER "@roctracer_kfdwrapper_LIBRARY@" +#else +# define HOSTTRACE_ROCTRACER_LIBKFDWRAPPER "/opt/rocm/roctracer/lib/libkfdwrapper64.so" +#endif diff --git a/projects/rocprofiler-systems/include/library/dynamic_library.hpp b/projects/rocprofiler-systems/include/library/dynamic_library.hpp new file mode 100644 index 0000000000..758b704eec --- /dev/null +++ b/projects/rocprofiler-systems/include/library/dynamic_library.hpp @@ -0,0 +1,71 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "library/debug.hpp" + +#include +#include +#include + +struct dynamic_library +{ + dynamic_library() = delete; + dynamic_library(const dynamic_library&) = delete; + dynamic_library(dynamic_library&&) noexcept = default; + dynamic_library& operator=(const dynamic_library&) = delete; + dynamic_library& operator=(dynamic_library&&) noexcept = default; + + dynamic_library(const char* _env, const char* _fname, + int _flags = (RTLD_NOW | RTLD_GLOBAL), bool _store = false) + : envname{ _env } + , filename{ tim::get_env(_env, _fname, _store) } + , flags{ _flags } + { + if(!filename.empty()) + { + handle = dlopen(filename.c_str(), flags); + if(!handle) + { + HOSTTRACE_DEBUG("%s\n", dlerror()); + } + dlerror(); // Clear any existing error + } + } + + ~dynamic_library() + { + if(handle) dlclose(handle); + } + + std::string envname = {}; + std::string filename = {}; + int flags = 0; + void* handle = nullptr; +}; diff --git a/projects/rocprofiler-systems/include/library/fork_gotcha.hpp b/projects/rocprofiler-systems/include/library/fork_gotcha.hpp new file mode 100644 index 0000000000..427399e549 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/fork_gotcha.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "library/common.hpp" +#include "library/timemory.hpp" + +// this is used to wrap fork() +struct fork_gotcha : comp::base +{ + using gotcha_data_t = comp::gotcha_data; + + TIMEMORY_DEFAULT_OBJECT(fork_gotcha) + + // this will get called right before fork + void audit(const gotcha_data_t& _data, audit::incoming); + + // this will get called right after fork with the return value + void audit(const gotcha_data_t& _data, audit::outgoing, pid_t _pid); +}; + +using fork_gotcha_t = comp::gotcha<4, tim::component_tuple, hosttrace>; diff --git a/projects/rocprofiler-systems/include/library/hosttrace_component.hpp b/projects/rocprofiler-systems/include/library/hosttrace_component.hpp new file mode 100644 index 0000000000..4fcc854375 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/hosttrace_component.hpp @@ -0,0 +1,43 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "library/timemory.hpp" + +// timemory component which calls hosttrace functions +// (used in gotcha wrappers) +struct hosttrace_component : comp::base +{ + void start(); + void stop(); + void set_prefix(const char*); + +private: + const char* m_prefix = nullptr; +}; diff --git a/projects/rocprofiler-systems/include/library/mpi_gotcha.hpp b/projects/rocprofiler-systems/include/library/mpi_gotcha.hpp new file mode 100644 index 0000000000..f2d53404c5 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/mpi_gotcha.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "library/common.hpp" +#include "library/timemory.hpp" + +// this is used to wrap MPI_Init and MPI_Init_thread +struct mpi_gotcha : comp::base +{ + using gotcha_data_t = comp::gotcha_data; + + TIMEMORY_DEFAULT_OBJECT(mpi_gotcha) + + // this will get called right before MPI_Init with that functions arguments + void audit(const gotcha_data_t& _data, audit::incoming, int*, char***); + + // this will get called right before MPI_Init_thread with that functions arguments + void audit(const gotcha_data_t& _data, audit::incoming, int*, char***, int, int*); + + // this will get called right after MPI_Init and MPI_Init_thread with the return value + void audit(const gotcha_data_t& _data, audit::outgoing, int _retval); + + // this will get called right before MPI_Finalize + void audit(const gotcha_data_t& _data, audit::incoming); +}; + +using mpi_gotcha_t = comp::gotcha<4, tim::component_tuple, hosttrace>; diff --git a/projects/rocprofiler-systems/include/library/perfetto.hpp b/projects/rocprofiler-systems/include/library/perfetto.hpp new file mode 100644 index 0000000000..6d981d454d --- /dev/null +++ b/projects/rocprofiler-systems/include/library/perfetto.hpp @@ -0,0 +1,91 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#if defined(PERFETTO_CATEGORIES) +# error "PERFETTO_CATEGORIES is already defined. Please include \"" __FILE__ "\" before including any timemory files" +#endif + +#if !defined(TIMEMORY_USE_PERFETTO) +# include +# define PERFETTO_CATEGORIES \ + perfetto::Category("host").SetDescription("Host-side function tracing"), \ + perfetto::Category("device").SetDescription("Device-side function tracing"), \ + perfetto::Category("host-critical-trace") \ + .SetDescription("Host-side critical traces"), \ + perfetto::Category("device-critical-trace") \ + .SetDescription("Device-side critical traces") +#else +# define PERFETTO_CATEGORIES \ + perfetto::Category("host").SetDescription("Host-side function tracing"), \ + perfetto::Category("device").SetDescription("Device-side function tracing"), \ + perfetto::Category("host-critical-trace") \ + .SetDescription("Host-side critical traces"), \ + perfetto::Category("device-critical-trace") \ + .SetDescription("Device-side critical traces"), \ + perfetto::Category("timemory") \ + .SetDescription("Events from the timemory API") +# define TIMEMORY_PERFETTO_CATEGORIES PERFETTO_CATEGORIES +#endif + +#if !defined(TIMEMORY_USE_PERFETTO) +PERFETTO_DEFINE_CATEGORIES(PERFETTO_CATEGORIES); +#endif + +#if defined(CUSTOM_DATA_SOURCE) +class CustomDataSource : public perfetto::DataSource +{ +public: + void OnSetup(const SetupArgs&) override + { + // Use this callback to apply any custom configuration to your data source + // based on the TraceConfig in SetupArgs. + PRINT_HERE("%s", "setup"); + } + + void OnStart(const StartArgs&) override + { + // This notification can be used to initialize the GPU driver, enable + // counters, etc. StartArgs will contains the DataSourceDescriptor, + // which can be extended. + PRINT_HERE("%s", "start"); + } + + void OnStop(const StopArgs&) override + { + // Undo any initialization done in OnStart. + PRINT_HERE("%s", "stop"); + } + + // Data sources can also have per-instance state. + int my_custom_state = 0; +}; + +PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource); +#endif diff --git a/projects/rocprofiler-systems/include/library/ptl.hpp b/projects/rocprofiler-systems/include/library/ptl.hpp new file mode 100644 index 0000000000..f47a3847c3 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/ptl.hpp @@ -0,0 +1,55 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "PTL/PTL.hh" +#include "timemory/macros/attributes.hpp" + +#include + +namespace tasking +{ +std::mutex& +get_roctracer_mutex(); + +PTL::ThreadPool& +get_roctracer_thread_pool(); + +PTL::TaskGroup& +get_roctracer_task_group(); + +std::mutex& +get_critical_trace_mutex(); + +PTL::ThreadPool& +get_critical_trace_thread_pool(); + +PTL::TaskGroup& +get_critical_trace_task_group(); +} // namespace tasking diff --git a/projects/rocprofiler-systems/include/roctracer.hpp b/projects/rocprofiler-systems/include/library/roctracer.hpp similarity index 55% rename from projects/rocprofiler-systems/include/roctracer.hpp rename to projects/rocprofiler-systems/include/library/roctracer.hpp index 8f26fad32e..0a5b841802 100644 --- a/projects/rocprofiler-systems/include/roctracer.hpp +++ b/projects/rocprofiler-systems/include/library/roctracer.hpp @@ -1,3 +1,30 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. #pragma once diff --git a/projects/rocprofiler-systems/include/library/roctracer_callbacks.hpp b/projects/rocprofiler-systems/include/library/roctracer_callbacks.hpp new file mode 100644 index 0000000000..407d1a62dc --- /dev/null +++ b/projects/rocprofiler-systems/include/library/roctracer_callbacks.hpp @@ -0,0 +1,96 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "library/config.hpp" +#include "library/debug.hpp" +#include "library/dynamic_library.hpp" +#include "library/perfetto.hpp" +#include "library/ptl.hpp" +#include "library/roctracer.hpp" + +#include +#include +#include +#include + +#define AMD_INTERNAL_BUILD 1 +#include +#include + +#include +#include + +// Macro to check ROC-tracer calls status +#define ROCTRACER_CALL(call) \ + do \ + { \ + int err = call; \ + if(err != 0) \ + { \ + std::cerr << roctracer_error_string() << " in: " << #call << std::flush; \ + } \ + } while(0) + +using hsa_timer_t = hsa_rt_utils::Timer; +using timestamp_t = hsa_timer_t::timestamp_t; +using roctracer_bundle_t = tim::component_bundle; +using roctracer_hsa_bundle_t = tim::component_bundle; +using roctracer_functions_t = std::vector>>; + +std::unique_ptr& +get_hsa_timer(); + +// HSA API callback function +void +hsa_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg); + +void +hsa_activity_callback(uint32_t op, activity_record_t* record, void* arg); + +void +hip_exec_activity_callbacks(int64_t _tid); + +// HIP API callback function +void +hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg); + +// Activity tracing callback +void +hip_activity_callback(const char* begin, const char* end, void*); + +bool& +roctracer_is_setup(); + +roctracer_functions_t& +roctracer_setup_routines(); + +roctracer_functions_t& +roctracer_tear_down_routines(); diff --git a/projects/rocprofiler-systems/include/library/state.hpp b/projects/rocprofiler-systems/include/library/state.hpp new file mode 100644 index 0000000000..9dad91df18 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/state.hpp @@ -0,0 +1,38 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +// used for specifying the state of hosttrace +enum class State : unsigned short +{ + DelayedInit = 0, + PreInit, + Active, + Finalized +}; diff --git a/projects/rocprofiler-systems/include/library/thread_data.hpp b/projects/rocprofiler-systems/include/library/thread_data.hpp new file mode 100644 index 0000000000..ff5af56211 --- /dev/null +++ b/projects/rocprofiler-systems/include/library/thread_data.hpp @@ -0,0 +1,126 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include "library/config.hpp" + +#include +#include +#include +#include +#include + +#if !defined(HOSTTRACE_MAX_THREADS) +# define HOSTTRACE_MAX_THREADS 1024 +#endif + +static constexpr size_t max_supported_threads = HOSTTRACE_MAX_THREADS; + +template +struct hosttrace_thread_data +{ + using instance_array_t = std::array, MaxThreads>; + using construct_on_init = std::true_type; + + template + static void construct(Args&&...); + static std::unique_ptr& instance(); + static instance_array_t& instances(); + template + static std::unique_ptr& instance(construct_on_init, Args&&...); + template + static instance_array_t& instances(construct_on_init, Args&&...); +}; + +template +template +void +hosttrace_thread_data::construct(Args&&... _args) +{ + static thread_local bool _v = [&_args...]() { + instances().at(threading::get_id()) = + std::make_unique(std::forward(_args)...); + return true; + }(); + (void) _v; +} + +template +std::unique_ptr& +hosttrace_thread_data::instance() +{ + return instances().at(threading::get_id()); +} + +template +typename hosttrace_thread_data::instance_array_t& +hosttrace_thread_data::instances() +{ + static auto _v = instance_array_t{}; + return _v; +} + +template +template +std::unique_ptr& +hosttrace_thread_data::instance(construct_on_init, Args&&... _args) +{ + construct(std::forward(_args)...); + return instances().at(threading::get_id()); +} + +template +template +typename hosttrace_thread_data::instance_array_t& +hosttrace_thread_data::instances(construct_on_init, Args&&... _args) +{ + static auto _v = [&]() { + auto _internal = instance_array_t{}; + for(size_t i = 0; i < MaxThreads; ++i) + _internal.at(i) = std::make_unique(std::forward(_args)...); + return _internal; + }(); + return _v; +} + +//--------------------------------------------------------------------------------------// + +// there are currently some strange things that happen with +// vector so using vector and +// timemory's ring_buffer_allocator to create contiguous memory-page aligned instances of +// the bundle +struct instrumentation_bundles +{ + using instance_array_t = std::array; + + bundle_allocator_t allocator{}; + std::vector bundles{}; + + static instance_array_t& instances(); +}; diff --git a/projects/rocprofiler-systems/include/library/timemory.hpp b/projects/rocprofiler-systems/include/library/timemory.hpp new file mode 100644 index 0000000000..a33f8fc73f --- /dev/null +++ b/projects/rocprofiler-systems/include/library/timemory.hpp @@ -0,0 +1,63 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace audit = tim::audit; +namespace comp = tim::component; +namespace quirk = tim::quirk; +namespace threading = tim::threading; +namespace scope = tim::scope; +namespace dmp = tim::dmp; +namespace process = tim::process; +namespace units = tim::units; +namespace trait = tim::trait; + +// same sort of functionality as python's " ".join([...]) +#if !defined(JOIN) +# define JOIN(...) tim::mpl::apply::join(__VA_ARGS__) +#endif + +using papi_tot_ins = comp::papi_tuple; diff --git a/projects/rocprofiler-systems/scripts/.LICENSE.hpp b/projects/rocprofiler-systems/scripts/.LICENSE.hpp new file mode 100644 index 0000000000..3028818331 --- /dev/null +++ b/projects/rocprofiler-systems/scripts/.LICENSE.hpp @@ -0,0 +1,29 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#pragma once diff --git a/projects/rocprofiler-systems/src/hosttrace.cpp b/projects/rocprofiler-systems/src/hosttrace.cpp index 7300ea98a9..44773d0750 100644 --- a/projects/rocprofiler-systems/src/hosttrace.cpp +++ b/projects/rocprofiler-systems/src/hosttrace.cpp @@ -1,27 +1,30 @@ -// 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. +// Copyright (c) 2018 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 +// with 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. +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. // // 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 +// CONTRIBUTORS 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. -// +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH +// THE SOFTWARE. #include "hosttrace.hpp" @@ -1183,7 +1186,7 @@ main(int argc, char** argv) auto mpie_fini_args = hosttrace_call_expr("HOSTTRACE_MPI_FINALIZE", "OFF"); auto trace_call_args = hosttrace_call_expr("HOSTTRACE_COMPONENTS", default_components); - auto use_mpi_call_args = hosttrace_call_expr("HOSTTRACE_USE_MPI", "ON"); + auto use_mpi_call_args = hosttrace_call_expr("HOSTTRACE_USE_PID", "ON"); auto use_mpip_call_args = hosttrace_call_expr( "HOSTTRACE_USE_MPIP", (binary_rewrite && use_mpi && use_mpip) ? "ON" : "OFF"); auto none_call_args = hosttrace_call_expr(); @@ -1777,7 +1780,7 @@ main(int argc, char** argv) const auto& outf = outfile; if(outf.find('/') != string_t::npos) { - auto outdir = outf.substr(0, outf.find_last_of('/') - 1); + auto outdir = outf.substr(0, outf.find_last_of('/')); tim::makedir(outdir); } diff --git a/projects/rocprofiler-systems/src/hosttrace-details.cpp b/projects/rocprofiler-systems/src/hosttrace/details.cpp similarity index 94% rename from projects/rocprofiler-systems/src/hosttrace-details.cpp rename to projects/rocprofiler-systems/src/hosttrace/details.cpp index d800182cf2..96bb5b81b1 100644 --- a/projects/rocprofiler-systems/src/hosttrace-details.cpp +++ b/projects/rocprofiler-systems/src/hosttrace/details.cpp @@ -1,26 +1,30 @@ -// 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. +// Copyright (c) 2018 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 +// with 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. +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. // // 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 +// CONTRIBUTORS 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. +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH +// THE SOFTWARE. #include "hosttrace.hpp" @@ -47,7 +51,7 @@ get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* char fname[MUTNAMELEN]; char mname[MUTNAMELEN]; - const char* typeName = nullptr; + std::string typeName = {}; mutatee_module->getName(mname, MUTNAMELEN); @@ -73,8 +77,6 @@ get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* { typeName = returnType->getName(); } - else - typeName = "void"; auto params = f->getParams(); std::vector _params; @@ -148,8 +150,8 @@ get_func_file_line_info(module_t* mutatee_module, procedure_t* f) char fname[MUTNAMELEN]; char mname[MUTNAMELEN]; int row1, col1, row2, col2; - string_t filename; - string_t typeName; + string_t filename = {}; + string_t typeName = {}; mutatee_module->getName(mname, MUTNAMELEN); @@ -164,8 +166,6 @@ get_func_file_line_info(module_t* mutatee_module, procedure_t* f) { typeName = returnType->getName(); } - else - typeName = "void"; auto params = f->getParams(); std::vector _params; diff --git a/projects/rocprofiler-systems/src/library.cpp b/projects/rocprofiler-systems/src/library.cpp index c4ae8f4354..789562a171 100644 --- a/projects/rocprofiler-systems/src/library.cpp +++ b/projects/rocprofiler-systems/src/library.cpp @@ -1,51 +1,39 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. #include "library.hpp" - -bool -get_debug() -{ - static bool _v = tim::get_env("HOSTTRACE_DEBUG", false); - return _v; -} - -State& -get_state() -{ - static State _v{ State::PreInit }; - return _v; -} - -bool -get_use_perfetto() -{ - // if using timemory, default to perfetto being off - static auto _default_v = !tim::get_env("HOSTTRACE_USE_TIMEMORY", false, false); - // explicit env control for using perfetto - static auto _v = tim::get_env("HOSTTRACE_USE_PERFETTO", _default_v); - return _v; -} - -bool -get_use_timemory() -{ - // default to opposite of whether perfetto setting - // to use both timemory and perfetto, both HOSTTRACE_USE_TIMEMORY and - // HOSTTRACE_USE_PERFETTO must be true - static auto _v = tim::get_env("HOSTTRACE_USE_TIMEMORY", !get_use_perfetto()); - return _v; -} - -//--------------------------------------------------------------------------------------// +#include "library/config.hpp" +#include "library/critical_trace.hpp" +#include "library/thread_data.hpp" +#include namespace { -size_t& -get_sample_rate() -{ - static auto _v = tim::get_env("HOSTTRACE_SAMPLE_RATE", 1); - return _v; -} - std::vector& get_sample_data() { @@ -53,17 +41,6 @@ get_sample_data() return _v; } -bool& -get_use_mpi() -{ -#if defined(TIMEMORY_USE_MPI) - static bool _v = tim::get_env("HOSTTRACE_USE_MPI", false); -#else - static bool _v = false; -#endif - return _v; -} - void setup_gotchas() { @@ -83,16 +60,17 @@ setup_gotchas() mpi_gotcha_t::template configure<0, int, int*, char***>("MPI_Init"); mpi_gotcha_t::template configure<1, int, int*, char***, int, int*>( "MPI_Init_thread"); -#if defined(HOSTTRACE_USE_MPI_HEADERS) mpi_gotcha_t::template configure<3, int>("MPI_Finalize"); -#endif }; } auto -ensure_finalization() +ensure_finalization(bool _static_init = false) { - HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); + if(!_static_init) + { + HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); + } return scope::destructor{ []() { hosttrace_trace_finalize(); } }; } @@ -103,45 +81,6 @@ get_trace_session() return _session; } -auto -get_perfetto_output_filename() -{ - static auto _v = []() { - // default name: perfetto-trace..proto or perfetto-trace..proto - auto _default_fname = tim::settings::compose_output_filename( - JOIN('.', "perfetto-trace", (get_use_mpi()) ? "%rank%" : "%pid%"), "proto"); - // have the default display the full path to the output file - return tim::get_env( - "HOSTTRACE_OUTPUT_FILE", - JOIN('/', tim::get_env("PWD", ".", false), _default_fname)); - }(); - - auto _tmp = _v; - auto _replace = [&_tmp](const std::string& _key, auto&& _val) { - auto _pos = _tmp.find(_key); - if(_pos != std::string::npos) - _tmp.replace(_pos, _key.length(), std::to_string(_val())); - }; - _replace("%pid%", []() { return process::get_id(); }); - _replace("%rank%", []() { return tim::mpi::rank(); }); - // backwards compatibility - _replace("%p", []() { return process::get_id(); }); - return _tmp; -} - -auto& -get_backend() -{ - // select inprocess, system, or both (i.e. all) - static auto _v = tim::get_env_choice( - "HOSTTRACE_BACKEND", - tim::get_env("HOSTTRACE_BACKEND_SYSTEM", false, false) - ? "system" // if HOSTTRACE_BACKEND_SYSTEM is true, default to system. - : "inprocess", // Otherwise, default to inprocess - { "inprocess", "system", "all" }); - return _v; -} - auto is_system_backend() { @@ -150,10 +89,10 @@ is_system_backend() } auto& -get_timemory_data() +get_instrumentation_bundles() { static thread_local auto& _v = - hosttrace_timemory_data::instances().at(threading::get_id()); + instrumentation_bundles::instances().at(threading::get_id()); return _v; } @@ -166,6 +105,17 @@ get_functors() return _v; } +auto& +get_cpu_cid_parents() +{ + static thread_local auto _v = + std::unordered_map>{}; + return _v; +} + +using Device = critical_trace::Device; +using Phase = critical_trace::Phase; + bool hosttrace_init_tooling() { @@ -184,26 +134,17 @@ hosttrace_init_tooling() return false; } - // always initialize timemory because gotcha wrappers are always used - tim::settings::flamegraph_output() = false; - tim::settings::cout_output() = false; - tim::settings::file_output() = true; - tim::settings::enable_signal_handler() = true; - tim::settings::collapse_processes() = false; - tim::settings::collapse_threads() = false; - tim::settings::max_thread_bookmarks() = 1; - tim::settings::global_components() = tim::get_env( - "HOSTTRACE_COMPONENTS", "wall_clock", get_use_timemory()); + int _threadpool_verbose = (get_debug()) ? 4 : -1; + tasking::get_roctracer_thread_pool().set_verbose(_threadpool_verbose); + tasking::get_critical_trace_thread_pool().set_verbose(_threadpool_verbose); - // enable timestamp directories when perfetto + mpi is activated - if(get_use_perfetto() && get_use_mpi()) tim::settings::time_output() = true; + // below will effectively do: + // get_cpu_cid_stack(0)->emplace_back(-1); + // plus query some env variables + add_critical_trace(0, -1, 0, 0, 0, 0, 0, 0); - auto _cmd = tim::read_command_line(process::get_id()); - auto _exe = (_cmd.empty()) ? "hosttrace" : _cmd.front(); - auto _pos = _exe.find_last_of('/'); - if(_pos < _exe.length() - 1) _exe = _exe.substr(_pos + 1); - - tim::timemory_init({ _exe }, "hosttrace-"); + // configure the settings + configure_settings(); if(get_sample_rate() < 1) get_sample_rate() = 1; get_sample_data().reserve(512); @@ -218,16 +159,18 @@ hosttrace_init_tooling() if(_comps.size() == 1 && _comps.find(TIMEMORY_WALL_CLOCK) != _comps.end()) { // using wall_clock directly is lower overhead than using it via user_bundle - bundle_t::get_initializer() = [](bundle_t& _bundle) { - _bundle.initialize(); - }; + instrumentation_bundle_t::get_initializer() = + [](instrumentation_bundle_t& _bundle) { + _bundle.initialize(); + }; } else if(!_comps.empty()) { // use user_bundle for other than wall-clock - bundle_t::get_initializer() = [](bundle_t& _bundle) { - _bundle.initialize(); - }; + instrumentation_bundle_t::get_initializer() = + [](instrumentation_bundle_t& _bundle) { + _bundle.initialize(); + }; } else { @@ -252,9 +195,8 @@ hosttrace_init_tooling() if(get_use_perfetto()) { // environment settings - auto shmem_size_hint = - tim::get_env("HOSTTRACE_SHMEM_SIZE_HINT_KB", 40960); - auto buffer_size = tim::get_env("HOSTTRACE_BUFFER_SIZE_KB", 1024000); + auto shmem_size_hint = get_perfetto_shmem_size_hint(); + auto buffer_size = get_perfetto_buffer_size(); auto* buffer_config = cfg.add_buffers(); buffer_config->set_size_kb(buffer_size); @@ -276,6 +218,7 @@ hosttrace_init_tooling() (void) get_perfetto_output_filename(); } + auto _exe = get_exe_name(); static auto _thread_init = [_exe]() { hosttrace_thread_data::construct( TIMEMORY_JOIN("", _exe, "/thread-", threading::get_id()), @@ -285,10 +228,11 @@ hosttrace_init_tooling() } }; (void) _dtor; }; + // functors for starting and stopping timemory static auto _push_timemory = [](const char* name) { _thread_init(); - auto& _data = get_timemory_data(); + auto& _data = get_instrumentation_bundles(); // this generates a hash for the raw string array auto _hash = tim::add_hash_id(tim::string_view_t{ name }); auto* _bundle = _data.allocator.allocate(1); @@ -311,7 +255,7 @@ hosttrace_init_tooling() }; static auto _pop_timemory = [](const char* name) { - auto& _data = get_timemory_data(); + auto& _data = get_instrumentation_bundles(); if(_data.bundles.empty()) { HOSTTRACE_DEBUG("[%s] skipped %s :: empty bundle stack\n", @@ -358,8 +302,22 @@ hosttrace_init_tooling() if(dmp::rank() == 0) { - tim::print_env(std::cerr, - [](const std::string& _v) { return _v.find("HOSTTRACE_") == 0; }); + // generic filter for filtering relevant options + auto _is_hosttrace_option = [](const auto& _v) { +#if !defined(HOSTTRACE_USE_ROCTRACER) + if(_v.find("HOSTTRACE_ROCTRACER_") == 0) return false; +#endif + if(!get_use_critical_trace() && _v.find("HOSTTRACE_CRITICAL_TRACE_") == 0) + return false; + return (_v.find("HOSTTRACE_") == 0) || + ((_v.find("TIMEMORY_") != 0) && (_v.find("SIGNAL_") != 0)); + }; + + tim::print_env(std::cerr, [_is_hosttrace_option](const std::string& _v) { + return _is_hosttrace_option(_v); + }); + + print_config_settings(std::cerr, _is_hosttrace_option); } if(get_use_perfetto() && !is_system_backend()) @@ -421,6 +379,21 @@ extern "C" auto _enabled = (_sample_idx++ % _sample_rate == 0); get_sample_data().emplace_back(_enabled); if(_enabled) get_functors().first(name); + if(get_use_critical_trace()) + { + auto _ts = comp::wall_clock::record(); + auto _cid = get_cpu_cid()++; + uint16_t _depth = (get_cpu_cid_stack()->empty()) + ? get_cpu_cid_stack(0)->size() + : get_cpu_cid_stack()->size() - 1; + auto _parent_cid = (get_cpu_cid_stack()->empty()) + ? get_cpu_cid_stack(0)->back() + : get_cpu_cid_stack()->back(); + get_cpu_cid_parents().emplace(_cid, std::make_tuple(_parent_cid, _depth)); + add_critical_trace( + threading::get_id(), _cid, 0, _parent_cid, _ts, 0, + critical_trace::add_hash_id(name), _depth); + } } void hosttrace_pop_trace(const char* name) @@ -434,6 +407,20 @@ extern "C" if(_sample_data.back()) get_functors().second(name); _sample_data.pop_back(); } + if(get_use_critical_trace()) + { + if(get_cpu_cid_stack() && !get_cpu_cid_stack()->empty()) + { + auto _ts = comp::wall_clock::record(); + auto _cid = get_cpu_cid_stack()->back(); + uint64_t _parent_cid = 0; + uint16_t _depth = 0; + std::tie(_parent_cid, _depth) = get_cpu_cid_parents().at(_cid); + add_critical_trace( + threading::get_id(), _cid, 0, _parent_cid, _ts, _ts, + critical_trace::add_hash_id(name), _depth); + } + } } else { @@ -463,15 +450,19 @@ extern "C" comp::roctracer::tear_down(); #endif + // join extra thread(s) used by roctracer + HOSTTRACE_DEBUG("[%s] waiting for all roctracer tasks to complete...\n", + __FUNCTION__); + tasking::get_roctracer_task_group().join(); + // stop the main bundle and report the high-level metrics if(get_main_bundle()) { get_main_bundle()->stop(); - int64_t _id = (get_use_mpi()) ? dmp::rank() : process::get_id(); - std::stringstream _ss{}; - _ss << "[" << __FUNCTION__ << "][" << _id << "] " << *get_main_bundle() - << "\n"; - std::cerr << _ss.str(); + std::string _msg = JOIN("", *get_main_bundle()); + auto _pos = _msg.find(">>> "); + if(_pos != std::string::npos) _msg = _msg.substr(_pos + 5); + HOSTTRACE_PRINT("%s\n", _msg.c_str()); get_main_bundle().reset(); } @@ -484,14 +475,15 @@ extern "C" if(itr && itr->get() && !itr->get()->get_is_running()) { - std::stringstream _ss{}; - _ss << *itr << "\n"; - std::cerr << _ss.str(); + std::string _msg = JOIN("", *itr); + auto _pos = _msg.find(">>> "); + if(_pos != std::string::npos) _msg = _msg.substr(_pos + 5); + HOSTTRACE_PRINT("%s\n", _msg.c_str()); } } // ensure that all the MT instances are flushed - for(auto& itr : hosttrace_timemory_data::instances()) + for(auto& itr : instrumentation_bundles::instances()) { while(!itr.bundles.empty()) { @@ -503,6 +495,44 @@ extern "C" } } + if(get_use_critical_trace()) + { + // increase the thread-pool size + tasking::get_critical_trace_thread_pool().initialize_threadpool( + get_critical_trace_num_threads()); + + for(size_t i = 0; i < max_supported_threads; ++i) + { + using critical_trace_hash_data = + hosttrace_thread_data; + + if(critical_trace_hash_data::instances().at(i)) + critical_trace::add_hash_id( + *critical_trace_hash_data::instances().at(i)); + } + + for(size_t i = 0; i < max_supported_threads; ++i) + { + using critical_trace_chain_data = + hosttrace_thread_data; + + if(critical_trace_chain_data::instances().at(i)) + critical_trace::update(i); // launch update task + } + + // make sure outstanding hash tasks completed before compute + HOSTTRACE_PRINT("[%s] waiting for all critical trace tasks to complete...\n", + __FUNCTION__); + tasking::get_critical_trace_task_group().join(); + + // launch compute task + HOSTTRACE_PRINT("[%s] launching critical trace compute task...\n", + __FUNCTION__); + critical_trace::compute(); + } + + tasking::get_critical_trace_task_group().join(); + bool _perfetto_output_error = false; if(get_use_perfetto() && !is_system_backend()) { @@ -530,20 +560,27 @@ extern "C" static_cast(trace_data.size()) / units::KB, static_cast(trace_data.size()) / units::MB, static_cast(trace_data.size()) / units::GB); - std::ofstream output{}; - output.open(get_perfetto_output_filename(), std::ios::out | std::ios::binary); - if(!output) + std::ofstream ofs{}; + if(!tim::filepath::open(ofs, get_perfetto_output_filename(), + std::ios::out | std::ios::binary)) { fprintf(stderr, "[%s]> Error opening '%s'...\n", __FUNCTION__, get_perfetto_output_filename().c_str()); _perfetto_output_error = true; } else - output.write(&trace_data[0], trace_data.size()); - output.close(); + ofs.write(&trace_data[0], trace_data.size()); + ofs.close(); } + // these should be destroyed before timemory is finalized, especially the + // roctracer thread-pool + tasking::get_roctracer_thread_pool().destroy_threadpool(); + tasking::get_critical_trace_thread_pool().destroy_threadpool(); + + HOSTTRACE_DEBUG("Finalizing timemory...\n"); tim::timemory_finalize(); + HOSTTRACE_DEBUG("Finalizing timemory... Done\n"); if(_perfetto_output_error) throw std::runtime_error("Unable to create perfetto output file"); @@ -564,20 +601,17 @@ extern "C" { auto& _main_bundle = get_main_bundle(); _main_bundle->start(); -#if defined(TIMEMORY_USE_MPI) - tim::set_env("HOSTTRACE_USE_MPI", "ON", 1); - get_use_mpi() = true; -#endif - get_state() = State::DelayedInit; + get_use_pid() = true; + get_state() = State::DelayedInit; } } } -std::unique_ptr& +std::unique_ptr& get_main_bundle() { static auto _v = - (setup_gotchas(), std::make_unique( + (setup_gotchas(), std::make_unique( "hosttrace", quirk::config{})); return _v; } @@ -587,5 +621,5 @@ namespace // if static objects are destroyed randomly (relatively uncommon behavior) // this might call finalization before perfetto ends the tracing session // but static variable in hosttrace_init_tooling is more likely -auto _ensure_finalization = ensure_finalization(); +auto _ensure_finalization = ensure_finalization(true); } // namespace diff --git a/projects/rocprofiler-systems/src/library/config.cpp b/projects/rocprofiler-systems/src/library/config.cpp new file mode 100644 index 0000000000..fa33eca6aa --- /dev/null +++ b/projects/rocprofiler-systems/src/library/config.cpp @@ -0,0 +1,528 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/config.hpp" +#include "library/debug.hpp" +#include "library/thread_data.hpp" +#include "timemory/backends/dmp.hpp" +#include "timemory/backends/process.hpp" +#include "timemory/settings/types.hpp" +#include "timemory/utility/argparse.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +using settings = tim::settings; + +namespace +{ +auto +get_config() +{ + static auto _once = (configure_settings(), true); + return settings::shared_instance(); + (void) _once; +} + +#define HOSTTRACE_CONFIG_SETTING(TYPE, ENV_NAME, DESCRIPTION, INITIAL_VALUE) \ + _config->insert(ENV_NAME, ENV_NAME, DESCRIPTION, INITIAL_VALUE, \ + std::vector{}) +} // namespace + +void +configure_settings() +{ + static bool _once = false; + if(_once) return; + _once = true; + + static auto _config = settings::shared_instance(); + // auto* _config = settings::instance(); + + // if using timemory, default to perfetto being off + auto _default_perfetto_v = + !tim::get_env("HOSTTRACE_USE_TIMEMORY", false, false); + + auto _default_config_file = + JOIN("/", tim::get_env("HOME", "."), "hosttrace.cfg"); + + auto _system_backend = tim::get_env("HOSTTRACE_BACKEND_SYSTEM", false, false); + + HOSTTRACE_CONFIG_SETTING(std::string, "HOSTTRACE_CONFIG_FILE", + "Configuration file of hosttrace and timemory settings", + _default_config_file); + + HOSTTRACE_CONFIG_SETTING(bool, "HOSTTRACE_DEBUG", "Enable debugging output", + _config->get_debug()); + + auto _hosttrace_debug = _config->get("HOSTTRACE_DEBUG"); + if(_hosttrace_debug) tim::set_env("TIMEMORY_DEBUG_SETTINGS", "1", 0); + + HOSTTRACE_CONFIG_SETTING(bool, "HOSTTRACE_USE_PERFETTO", "Enable perfetto backend", + _default_perfetto_v); + + HOSTTRACE_CONFIG_SETTING(bool, "HOSTTRACE_USE_TIMEMORY", "Enable timemory backend", + !_config->get("HOSTTRACE_USE_PERFETTO")); + + HOSTTRACE_CONFIG_SETTING( + bool, "HOSTTRACE_USE_PID", + "Enable tagging filenames with process identifier (either MPI rank or pid)", + true); + + HOSTTRACE_CONFIG_SETTING( + size_t, "HOSTTRACE_SAMPLE_RATE", + "Counts every function call (N), only record function if (N % == 0)", 1); + + auto _backend = tim::get_env_choice( + "HOSTTRACE_BACKEND", + (_system_backend) + ? "system" // if HOSTTRACE_BACKEND_SYSTEM is true, default to system. + : "inprocess", // Otherwise, default to inprocess + { "inprocess", "system", "all" }, false); + + HOSTTRACE_CONFIG_SETTING(std::string, "HOSTTRACE_BACKEND", + "Specify the perfetto backend to activate. Options are: " + "'inprocess', 'system', or 'all'", + _backend); + + HOSTTRACE_CONFIG_SETTING(bool, "HOSTTRACE_CRITICAL_TRACE", + "Enable generation of the critical trace", false); + + HOSTTRACE_CONFIG_SETTING( + bool, "HOSTTRACE_ROCTRACER_TIMELINE_PROFILE", + "Create unique entries for every kernel with timemory backend", + _config->get_timeline_profile()); + + HOSTTRACE_CONFIG_SETTING( + bool, "HOSTTRACE_ROCTRACER_FLAT_PROFILE", + "Ignore hierarchy in all kernels entries with timemory backend", + _config->get_flat_profile()); + + HOSTTRACE_CONFIG_SETTING(bool, "HOSTTRACE_ROCTRACER_HSA_ACTIVITY", + "Enable HSA activity tracing support", false); + + HOSTTRACE_CONFIG_SETTING(bool, "HOSTTRACE_ROCTRACER_HSA_API", + "Enable HSA API tracing support", false); + + HOSTTRACE_CONFIG_SETTING(std::string, "HOSTTRACE_ROCTRACER_HSA_API_TYPES", + "HSA API type to collect", ""); + + HOSTTRACE_CONFIG_SETTING(bool, "HOSTTRACE_CRITICAL_TRACE_DEBUG", + "Enable debugging for critical trace", _hosttrace_debug); + + HOSTTRACE_CONFIG_SETTING( + bool, "HOSTTRACE_CRITICAL_TRACE_SERIALIZE_NAMES", + "Include names in serialization of critical trace (mainly for debugging)", + _hosttrace_debug); + + HOSTTRACE_CONFIG_SETTING(size_t, "HOSTTRACE_SHMEM_SIZE_HINT_KB", + "Hint for shared-memory buffer size in perfetto (in KB)", + 40960); + + HOSTTRACE_CONFIG_SETTING(size_t, "HOSTTRACE_BUFFER_SIZE_KB", + "Size of perfetto buffer (in KB)", 1024000); + + HOSTTRACE_CONFIG_SETTING(int64_t, "HOSTTRACE_CRITICAL_TRACE_COUNT", + "Number of critical trace to export (0 == all)", 0); + + HOSTTRACE_CONFIG_SETTING(uint64_t, "HOSTTRACE_CRITICAL_TRACE_BUFFER_COUNT", + "Number of critical trace records to store in thread-local " + "memory before submitting to shared buffer", + 2000); + + HOSTTRACE_CONFIG_SETTING( + uint64_t, "HOSTTRACE_CRITICAL_TRACE_NUM_THREADS", + "Number of threads to use when generating the critical trace", + std::min(8, std::thread::hardware_concurrency())); + + HOSTTRACE_CONFIG_SETTING( + int64_t, "HOSTTRACE_CRITICAL_TRACE_PER_ROW", + "How many critical traces per row in perfetto (0 == all in one row)", 0); + + HOSTTRACE_CONFIG_SETTING( + std::string, "HOSTTRACE_COMPONENTS", + "List of components to collect via timemory (see timemory-avail)", "wall_clock"); + + HOSTTRACE_CONFIG_SETTING(std::string, "HOSTTRACE_OUTPUT_FILE", "Perfetto filename", + ""); + + HOSTTRACE_CONFIG_SETTING(bool, "HOSTTRACE_SETTINGS_DESC", + "Provide descriptions when printing settings", false); + + _config->get_flamegraph_output() = false; + _config->get_cout_output() = false; + _config->get_file_output() = true; + _config->get_json_output() = true; + _config->get_tree_output() = true; + _config->get_enable_signal_handler() = true; + _config->get_collapse_processes() = false; + _config->get_collapse_threads() = false; + _config->get_stack_clearing() = false; + _config->get_time_output() = true; + _config->get_timing_precision() = 6; + + for(auto&& itr : + tim::delimit(_config->get("HOSTTRACE_CONFIG_FILE"), ";:")) + { + HOSTTRACE_CONDITIONAL_BASIC_PRINT(true, "Reading config file %s\n", itr.c_str()); + _config->read(itr); + } + + _config->get_global_components() = _config->get("HOSTTRACE_COMPONENTS"); + + // always initialize timemory because gotcha wrappers are always used + auto _cmd = tim::read_command_line(process::get_id()); + auto _exe = (_cmd.empty()) ? "exe" : _cmd.front(); + auto _pos = _exe.find_last_of('/'); + if(_pos < _exe.length() - 1) _exe = _exe.substr(_pos + 1); + get_exe_name() = _exe; + + scope::get_fields()[scope::flat::value] = tim::settings::flat_profile(); + scope::get_fields()[scope::timeline::value] = tim::settings::timeline_profile(); + + bool _found_sep = false; + for(const auto& itr : _cmd) + { + if(itr == "--") _found_sep = true; + } + if(!_found_sep && _cmd.size() > 1) _cmd.insert(_cmd.begin() + 1, "--"); + + using argparser_t = tim::argparse::argument_parser; + argparser_t _parser{ _exe }; + tim::timemory_init(_cmd, _parser, "hosttrace-"); + + settings::suppress_parsing() = true; + settings::suppress_config() = true; + settings::use_output_suffix() = _config->get("HOSTTRACE_USE_PID"); +} + +void +print_config_settings(std::ostream& _os, + std::function&& _filter) +{ + auto _flags = _os.flags(); + + constexpr size_t nfields = 3; + using str_array_t = std::array; + std::vector _data{}; + std::array _widths{}; + _widths.fill(0); + for(const auto& itr : *get_config()) + { + if(_filter(itr.first)) + { + auto _disp = itr.second->get_display(std::ios::boolalpha); + _data.emplace_back(str_array_t{ _disp.at("name"), _disp.at("value"), + _disp.at("description") }); + for(size_t i = 0; i < nfields; ++i) + _widths.at(i) = + std::max(_widths.at(i), _data.back().at(i).length()); + } + } + + std::sort(_data.begin(), _data.end(), [](const auto& lhs, const auto& rhs) { + auto _npos = std::string::npos; + // HOSTTRACE_CONFIG_FILE always first + if(lhs.at(0).find("HOSTTRACE_CONFIG") != _npos) return true; + if(rhs.at(0).find("HOSTTRACE_CONFIG") != _npos) return false; + // HOSTTRACE_USE_* prioritized + auto _lhs_use = lhs.at(0).find("HOSTTRACE_USE_"); + auto _rhs_use = rhs.at(0).find("HOSTTRACE_USE_"); + if(_lhs_use != _rhs_use && _lhs_use < _rhs_use) return true; + if(_lhs_use != _rhs_use && _lhs_use > _rhs_use) return false; + // length sort followed by alphabetical sort + return (lhs.at(0).length() == rhs.at(0).length()) + ? (lhs.at(0) < rhs.at(0)) + : (lhs.at(0).length() < rhs.at(0).length()); + }); + + bool _print_desc = get_debug() || get_config()->get("HOSTTRACE_SETTINGS_DESC"); + + auto tot_width = std::accumulate(_widths.begin(), _widths.end(), 0); + if(!_print_desc) tot_width -= _widths.back() + 4; + + std::stringstream _spacer{}; + _spacer.fill('-'); + _spacer << "#" << std::setw(tot_width + 11) << "" + << "#"; + _os << _spacer.str() << "\n"; + // _os << "# Hosttrace settings:" << std::setw(tot_width - 8) << "#" << "\n"; + for(const auto& itr : _data) + { + _os << "# "; + for(size_t i = 0; i < nfields; ++i) + { + switch(i) + { + case 0: _os << std::left; break; + case 1: _os << std::left; break; + case 2: _os << std::left; break; + } + _os << std::setw(_widths.at(i)) << itr.at(i) << " "; + if(!_print_desc && i == 1) break; + switch(i) + { + case 0: _os << "= "; break; + case 1: _os << "[ "; break; + case 2: _os << "]"; break; + } + } + _os << " #\n"; + } + _os << _spacer.str() << "\n"; + + _os.setf(_flags); +} + +std::string& +get_exe_name() +{ + static std::string _v = {}; + return _v; +} + +std::string +get_config_file() +{ + static auto _v = get_config()->find("HOSTTRACE_CONFIG_FILE"); + return static_cast&>(*_v->second).get(); +} + +bool +get_debug() +{ + static auto _v = get_config()->find("HOSTTRACE_DEBUG"); + return static_cast&>(*_v->second).get(); +} + +bool +get_use_perfetto() +{ + static auto _v = get_config()->find("HOSTTRACE_USE_PERFETTO"); + return static_cast&>(*_v->second).get(); +} + +bool +get_use_timemory() +{ + static auto _v = get_config()->find("HOSTTRACE_USE_TIMEMORY"); + return static_cast&>(*_v->second).get(); +} + +bool& +get_use_pid() +{ + static auto _v = get_config()->find("HOSTTRACE_USE_PID"); + return static_cast&>(*_v->second).get(); +} + +bool +get_use_mpip() +{ + static bool _v = tim::get_env("HOSTTRACE_USE_MPIP", false, false); + return _v; +} + +bool +get_use_critical_trace() +{ + static auto _v = get_config()->find("HOSTTRACE_CRITICAL_TRACE"); + return static_cast&>(*_v->second).get(); +} + +bool +get_critical_trace_debug() +{ + static auto _v = get_config()->find("HOSTTRACE_CRITICAL_TRACE_DEBUG"); + return static_cast&>(*_v->second).get(); +} + +bool +get_critical_trace_serialize_names() +{ + static auto _v = get_config()->find("HOSTTRACE_CRITICAL_TRACE_SERIALIZE_NAMES"); + return static_cast&>(*_v->second).get(); +} + +bool +get_roctracer_timeline_profile() +{ + static auto _v = get_config()->find("HOSTTRACE_ROCTRACER_TIMELINE_PROFILE"); + return static_cast&>(*_v->second).get(); +} + +bool +get_roctracer_flat_profile() +{ + static auto _v = get_config()->find("HOSTTRACE_ROCTRACER_FLAT_PROFILE"); + return static_cast&>(*_v->second).get(); +} + +bool +get_trace_hsa_api() +{ + static auto _v = get_config()->find("HOSTTRACE_ROCTRACER_HSA_API"); + return static_cast&>(*_v->second).get(); +} + +bool +get_trace_hsa_activity() +{ + static auto _v = get_config()->find("HOSTTRACE_ROCTRACER_HSA_ACTIVITY"); + return static_cast&>(*_v->second).get(); +} + +int64_t +get_critical_trace_per_row() +{ + static auto _v = get_config()->find("HOSTTRACE_CRITICAL_TRACE_PER_ROW"); + return static_cast&>(*_v->second).get(); +} + +size_t +get_perfetto_shmem_size_hint() +{ + static auto _v = get_config()->find("HOSTTRACE_SHMEM_SIZE_HINT_KB"); + return static_cast&>(*_v->second).get(); +} + +size_t +get_perfetto_buffer_size() +{ + static auto _v = get_config()->find("HOSTTRACE_BUFFER_SIZE_KB"); + return static_cast&>(*_v->second).get(); +} + +uint64_t +get_critical_trace_update_freq() +{ + static uint64_t _v = + get_config()->get("HOSTTRACE_CRITICAL_TRACE_BUFFER_COUNT"); + return _v; +} + +uint64_t +get_critical_trace_num_threads() +{ + static uint64_t _v = + get_config()->get("HOSTTRACE_CRITICAL_TRACE_NUM_THREADS"); + return _v; +} + +std::string +get_trace_hsa_api_types() +{ + static std::string _v = + get_config()->get("HOSTTRACE_ROCTRACER_HSA_API_TYPES"); + return _v; +} + +std::string& +get_backend() +{ + // select inprocess, system, or both (i.e. all) + static auto _v = get_config()->find("HOSTTRACE_BACKEND"); + return static_cast&>(*_v->second).get(); +} + +std::string +get_perfetto_output_filename() +{ + static auto _v = get_config()->find("HOSTTRACE_OUTPUT_FILE"); + static auto& _t = static_cast&>(*_v->second); + if(_t.get().empty()) + { + // default name: perfetto-trace..proto or perfetto-trace..proto + auto _default_fname = settings::compose_output_filename( + "perfetto-trace", "proto", get_use_pid(), + (tim::dmp::is_initialized()) ? tim::dmp::rank() : process::get_id()); + auto _pid_patch = std::string{ "/" } + std::to_string(tim::process::get_id()) + + "-perfetto-trace"; + auto _dpos = _default_fname.find(_pid_patch); + if(_dpos != std::string::npos) + _default_fname = + _default_fname.replace(_dpos, _pid_patch.length(), "/perfetto-trace"); + // have the default display the full path to the output file + _t.set(tim::get_env( + "HOSTTRACE_OUTPUT_FILE", + JOIN('/', tim::get_env("PWD", ".", false), _default_fname), + false)); + } + return _t.get(); +} + +size_t& +get_sample_rate() +{ + static auto _v = get_config()->find("HOSTTRACE_SAMPLE_RATE"); + return static_cast&>(*_v->second).get(); +} + +int64_t +get_critical_trace_count() +{ + static auto _v = get_config()->find("HOSTTRACE_CRITICAL_TRACE_COUNT"); + return static_cast&>(*_v->second).get(); +} + +State& +get_state() +{ + static State _v{ State::PreInit }; + return _v; +} + +std::atomic& +get_cpu_cid() +{ + static std::atomic _v{ 0 }; + return _v; +} + +std::unique_ptr>& +get_cpu_cid_stack(int64_t _tid) +{ + struct hosttrace_cpu_cid_stack + {}; + using thread_data_t = + hosttrace_thread_data, hosttrace_cpu_cid_stack>; + static auto& _v = thread_data_t::instances(); + static thread_local auto _v_check = [_tid]() { + thread_data_t::construct((_tid > 0) ? *thread_data_t::instances().at(0) + : std::vector{}); + return true; + }(); + return _v.at(_tid); + (void) _v_check; +} diff --git a/projects/rocprofiler-systems/src/library/critical_trace.cpp b/projects/rocprofiler-systems/src/library/critical_trace.cpp new file mode 100644 index 0000000000..0436b242d7 --- /dev/null +++ b/projects/rocprofiler-systems/src/library/critical_trace.cpp @@ -0,0 +1,1300 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/critical_trace.hpp" +#include "PTL/ThreadPool.hh" +#include "library/config.hpp" +#include "library/debug.hpp" +#include "library/perfetto.hpp" +#include "library/ptl.hpp" +#include "timemory/backends/dmp.hpp" +#include "timemory/hash/types.hpp" +#include "timemory/tpls/cereal/cereal/archives/json.hpp" +#include "timemory/tpls/cereal/cereal/cereal.hpp" +#include "timemory/utility/macros.hpp" +#include "timemory/utility/types.hpp" +#include "timemory/utility/utility.hpp" + +#include +#include +#include +#include +#include +#include + +namespace critical_trace +{ +namespace +{ +using call_graph_t = tim::graph; +using call_graph_itr_t = typename call_graph_t::iterator; +using call_graph_sibling_itr_t = typename call_graph_t::sibling_iterator; +using call_graph_preorder_itr_t = typename call_graph_t::pre_order_iterator; + +hash_ids complete_hash_ids{}; +call_chain complete_call_chain{}; +std::mutex complete_call_mutex{}; + +void +update_critical_path(call_chain _chain, int64_t _tid); + +void +compute_critical_trace(); + +void +find_children(PTL::ThreadPool& _tp, call_graph_t& _graph, const call_chain& _chain); + +void +find_sequences(PTL::ThreadPool& _tp, call_graph_t& _graph, + std::vector& _chain); + +void +find_sequences(PTL::ThreadPool& _tp, call_graph_t& _graph, call_graph_itr_t _root, + std::vector& _chain); + +template +void +serialize_graph(ArchiveT& ar, const tim::graph& _graph); + +template +void +serialize_subgraph(ArchiveT& ar, const tim::graph& _graph, + typename tim::graph::iterator _root); + +void +copy_hash_ids() +{ + // make copy to avoid parallel iteration issues + auto _hash_ids = complete_hash_ids; + // ensure all hash ids exist + for(const auto& itr : _hash_ids) + tim::hash::add_hash_id(itr); +} +} // namespace +} // namespace critical_trace + +namespace critical_trace +{ +namespace +{ +template +size_t +get_combined_hash(Arg0&& _zero, Arg1&& _one, Args&&... _args) +{ + size_t _hash = tim::hash::get_combined_hash_id(std::forward(_zero), + std::forward(_one)); + if constexpr(sizeof...(_args) == 0) + { + return _hash; + } + else + { + return get_combined_hash(_hash, std::forward(_args)...); + } +} +} // namespace + +//--------------------------------------------------------------------------------------// +// +// ENTRY +// +//--------------------------------------------------------------------------------------// + +bool +entry::operator==(const entry& rhs) const +{ + return (device == rhs.device && depth == rhs.depth && priority == rhs.priority && + tid == rhs.tid && cpu_cid == rhs.cpu_cid && gpu_cid == rhs.gpu_cid && + hash == rhs.hash); +} + +bool +entry::operator<(const entry& rhs) const +{ + // sort by cpu ids + auto _cpu_eq = (cpu_cid == rhs.cpu_cid); + if(!_cpu_eq) return (cpu_cid < rhs.cpu_cid); + + // sort by gpu ids + if(gpu_cid > 0 && rhs.gpu_cid > 0) + { + auto _gpu_eq = (gpu_cid == rhs.gpu_cid); + if(!_gpu_eq) return (gpu_cid < rhs.gpu_cid); + } + + // sort by parent ids + auto _par_eq = (parent_cid == rhs.parent_cid); + if(!_par_eq) return (parent_cid < rhs.parent_cid); + + // sort by priority + auto _prio_eq = (priority == rhs.priority); + if(!_prio_eq) return (priority < rhs.priority); + + // sort by timestamp (last resort) + return (begin_ns < rhs.begin_ns); +} + +bool +entry::operator>(const entry& rhs) const +{ + return (!(*this < rhs) && begin_ns != rhs.begin_ns && cpu_cid != rhs.cpu_cid && + gpu_cid != rhs.gpu_cid); +} + +entry& +entry::operator+=(const entry& rhs) +{ + if(phase == Phase::BEGIN && rhs.phase == Phase::END) + { + assert(rhs.end_ns >= begin_ns); + end_ns = rhs.end_ns; + phase = Phase::DELTA; + return *this; + } + else + { + HOSTTRACE_PRINT( + "Warning! Incorrect phase. entry::operator+=(entry) is only valid for " + "Phase::BEGIN += Phase::END\n"); + } + return *this; +} + +size_t +entry::get_hash() const +{ + return get_combined_hash(hash, static_cast(device), static_cast(phase), + tid, cpu_cid, gpu_cid, priority); +} + +int64_t +entry::get_timestamp() const +{ + switch(phase) + { + case Phase::BEGIN: return begin_ns; + case Phase::END: return end_ns; + case Phase::DELTA: return (end_ns - begin_ns); + case Phase::NONE: break; + } + return 0; +} + +int64_t +entry::get_cost() const +{ + switch(phase) + { + case Phase::DELTA: return (end_ns - begin_ns); + default: break; + } + return 0; +} + +int64_t +entry::get_overlap(const entry& rhs) const +{ + if(begin_ns >= rhs.end_ns || end_ns >= rhs.begin_ns) // no overlap + return 0; + else if(begin_ns >= rhs.begin_ns && end_ns <= rhs.end_ns) // inclusive to rhs + return get_cost(); + else if(begin_ns <= rhs.begin_ns && end_ns >= rhs.end_ns) // rhs is inclusive + return rhs.get_cost(); + else if(begin_ns <= rhs.begin_ns && end_ns <= rhs.end_ns) // at beginning + return (end_ns - rhs.begin_ns); + else if(begin_ns >= rhs.begin_ns && end_ns >= rhs.end_ns) // at end + return (rhs.end_ns - begin_ns); + else + { + HOSTTRACE_PRINT("Warning! entry::get_overlap(entry, tid) " + "could not determine the overlap :: %s\n", + JOIN("", *this).c_str()); + } + return 0; +} + +int64_t +entry::get_overlap(const entry& rhs, int64_t _tid) const +{ + if(!is_delta(*this, __FUNCTION__)) return 0; + if(!is_delta(rhs, __FUNCTION__)) return 0; + + if(_tid < 0 || (this->tid == _tid && rhs.tid == _tid)) // all threads or same thread + return get_overlap(rhs); + + return 0; +} + +int64_t +entry::get_independent(const entry& rhs) const +{ + if(begin_ns >= rhs.end_ns || end_ns >= rhs.begin_ns) // no overlap + return get_cost(); + else if(begin_ns >= rhs.begin_ns && end_ns <= rhs.end_ns) // inclusive to rhs + return 0; + else if(begin_ns <= rhs.begin_ns && end_ns >= rhs.end_ns) // rhs is inclusive + return get_cost() - rhs.get_cost(); + else if(begin_ns <= rhs.begin_ns && end_ns <= rhs.end_ns) // at beginning + return (rhs.begin_ns - begin_ns); + else if(begin_ns >= rhs.begin_ns && end_ns >= rhs.end_ns) // at end + return (end_ns - rhs.end_ns); + else + { + HOSTTRACE_PRINT("Warning! entry::get_independent(entry, tid) " + "could not determine the overlap :: %s\n", + JOIN("", *this).c_str()); + } + return 0; +} + +int64_t +entry::get_independent(const entry& rhs, int64_t _tid) const +{ + if(!is_delta(*this, __FUNCTION__)) return 0; + if(!is_delta(rhs, __FUNCTION__)) return 0; + + if(_tid < 0 || (this->tid == _tid && rhs.tid == _tid)) // all threads or same thread + return get_independent(rhs); + else if(this->tid == _tid && rhs.tid != _tid) // rhs is on different thread + return get_cost(); + return 0; +} + +bool +entry::is_bounded(const entry& rhs) const +{ + // ignores thread + return !(begin_ns < rhs.begin_ns || end_ns > rhs.end_ns); +} + +bool +entry::is_bounded(const entry& rhs, int64_t _tid) const +{ + if(this->tid == _tid && rhs.tid == _tid) // all threads or same thread + return !(begin_ns < rhs.begin_ns || end_ns > rhs.end_ns); + + return false; +} + +void +entry::write(std::ostream& _os) const +{ + if(device == Device::GPU) + _os << "[GPU][" << cpu_cid << "][" << gpu_cid << "]"; + else + _os << "[CPU][" << cpu_cid << "]"; + _os << " parent: " << static_cast(parent_cid); + _os << ", tid: " << tid; + _os << ", depth: " << depth; + _os << ", priority: " << priority; + if(phase == Phase::DELTA) + { + std::stringstream _cost{}; + _cost << std::setprecision(4) << std::scientific << (get_timestamp() / 1.0e9); + _os << ", cost: [" << std::setw(8) << _cost.str() << " sec]"; + } + else + { + _os << ", phase: "; + if(phase == Phase::BEGIN) + _os << "begin "; + else if(phase == Phase::END) + _os << "end "; + _os << "[" << begin_ns << ":" << end_ns << "]"; + } + _os << ", hash: " << hash << " :: " << tim::demangle(tim::get_hash_identifier(hash)); +} + +bool +entry::is_delta(const entry& _v, const std::string_view& _ctx) +{ + if(_v.phase != Phase::DELTA) + { + HOSTTRACE_CT_DEBUG( + "Warning! Invalid phase for entry. entry::%s requires Phase::DELTA :: %s\n", + _ctx.data(), JOIN("", _v).c_str()); + return true; + } + return false; +} + +//--------------------------------------------------------------------------------------// +// +// CALL CHAIN +// +//--------------------------------------------------------------------------------------// + +bool +call_chain::operator==(const call_chain& rhs) const +{ + if(size() != rhs.size()) return false; + for(size_t i = 0; i < size(); ++i) + if(at(i) != rhs.at(i)) return false; + return true; +} + +size_t +call_chain::get_hash() const +{ + if(empty()) return 0; + int64_t _hash = this->at(0).get_hash(); + for(size_t i = 1; i < this->size(); ++i) + _hash = get_combined_hash(_hash, at(i).get_hash()); + return _hash; +} + +int64_t +call_chain::get_cost(int64_t _tid) const +{ + int64_t _cost = 0; + if(_tid < 0) + { + for(const auto& itr : *this) + _cost += itr.get_cost(); + } + else + { + for(const auto& itr : *this) + { + if(itr.tid == _tid) _cost += itr.get_cost(); + } + } + return _cost; +} + +int64_t +call_chain::get_overlap(int64_t _tid) const +{ + int64_t _cost = 0; + auto itr = this->begin(); + auto nitr = ++this->begin(); + for(; nitr != this->end(); ++nitr, ++itr) + _cost += nitr->get_overlap(*itr, _tid); + return _cost; +} + +int64_t +call_chain::get_independent(int64_t _tid) const +{ + int64_t _cost = 0; + auto itr = this->begin(); + auto nitr = ++this->begin(); + for(; nitr != this->end(); ++nitr, ++itr) + _cost += itr->get_independent(*nitr, _tid); + return _cost; +} + +std::vector& +call_chain::get_top_chains() +{ + static std::vector _v{}; + return _v; +} + +template +bool +call_chain::query(FuncT&& _func) const +{ + for(const auto& itr : *this) + { + if(std::forward(_func)(itr)) return BoolV; + } + return !BoolV; +} + +template <> +void +call_chain::generate_perfetto(std::set&) const +{} + +template <> +void +call_chain::generate_perfetto(std::set& _used) const +{ + static std::set _static_strings{}; + static std::mutex _static_mutex{}; + for(const auto& itr : *this) + { + if(!_used.emplace(itr).second) continue; + std::string _name = tim::demangle(tim::get_hash_identifier(itr.hash)); + _static_mutex.lock(); + auto sitr = _static_strings.emplace(_name); + _static_mutex.unlock(); + TRACE_EVENT_BEGIN("host-critical-trace", + perfetto::StaticString{ sitr.first->c_str() }, + static_cast(itr.begin_ns)); + TRACE_EVENT_END("host-critical-trace", static_cast(itr.end_ns)); + } +} + +template <> +void +call_chain::generate_perfetto(std::set& _used) const +{ + static std::set _static_strings{}; + static std::mutex _static_mutex{}; + for(const auto& itr : *this) + { + if(!_used.emplace(itr).second) continue; + std::string _name = tim::demangle(tim::get_hash_identifier(itr.hash)); + _static_mutex.lock(); + auto sitr = _static_strings.emplace(_name); + _static_mutex.unlock(); + TRACE_EVENT_BEGIN("device-critical-trace", + perfetto::StaticString{ sitr.first->c_str() }, + static_cast(itr.begin_ns)); + TRACE_EVENT_END("device-critical-trace", static_cast(itr.end_ns)); + } +} + +//--------------------------------------------------------------------------------------// +// +// FREE FUNCTIONS +// +//--------------------------------------------------------------------------------------// + +uint64_t +get_update_frequency() +{ + return get_critical_trace_update_freq(); +} + +std::unique_ptr& +get(int64_t _tid) +{ + static auto& _v = hosttrace_thread_data::instances(); + static thread_local auto _once = [_tid]() { + if(!_v.at(0)) _v.at(0) = std::make_unique(); + if(!_v.at(_tid)) _v.at(_tid) = std::make_unique(); + if(_tid > 0) *_v.at(_tid) = *_v.at(0); + return true; + }(); + (void) _once; + return _v.at(_tid); +} + +void +add_hash_id(const hash_ids& _labels) +{ + std::unique_lock _lk{ tasking::get_critical_trace_mutex(), + std::defer_lock }; + if(!_lk.owns_lock()) _lk.lock(); + tasking::get_critical_trace_task_group().run([_labels]() { + static std::mutex _mtx{}; + _mtx.lock(); + for(auto itr : _labels) + complete_hash_ids.emplace(std::move(itr)); + _mtx.unlock(); + }); +} + +size_t +add_hash_id(const std::string& _label) +{ + using critical_trace_hash_data = + hosttrace_thread_data; + + auto _hash = tim::hash::add_hash_id(_label); + if(get_use_critical_trace()) + { + critical_trace_hash_data::construct(); + critical_trace_hash_data::instance()->emplace(_label); + } + return _hash; +} + +void +update(int64_t _tid) +{ + if(!get_use_critical_trace()) return; + std::unique_lock _lk{ tasking::get_critical_trace_mutex(), + std::defer_lock }; + if(!_lk.owns_lock()) _lk.lock(); + call_chain _data{}; + std::swap(_data, *critical_trace::get(_tid)); + tasking::get_critical_trace_task_group().exec(update_critical_path, _data, _tid); +} + +void +compute(int64_t _tid) +{ + update(_tid); + tasking::get_critical_trace_task_group().exec(compute_critical_trace); +} + +//--------------------------------------------------------------------------------------// +// +// HELPER FUNCTIONS +// +//--------------------------------------------------------------------------------------// + +namespace +{ +//--------------------------------------------------------------------------------------// + +std::string +get_perf_name(std::string _func) +{ + const auto _npos = std::string::npos; + auto _pos = std::string::npos; + while((_pos = _func.find('_')) != _npos) + _func = _func.replace(_pos, 1, " "); + if(_func.length() > 0) _func.at(0) = std::toupper(_func.at(0)); + return _func; +} + +//--------------------------------------------------------------------------------------// + +void +save_call_graph(const std::string& _fname, const std::string& _label, + const call_graph_t& _call_graph, bool _msg = false, + std::string _func = {}) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + + using perfstats_t = + tim::lightweight_tuple; + perfstats_t _perf{ get_perf_name(__FUNCTION__) }; + _perf.start(); + + std::stringstream oss{}; + { + namespace cereal = tim::cereal; + auto ar = tim::policy::output_archive::get(oss); + + auto _hash_map = *tim::hash::get_hash_ids(); + for(auto& itr : _hash_map) + itr.second = tim::demangle(itr.second); + ar->setNextName("hosttrace"); + ar->startNode(); + (*ar)(cereal::make_nvp("hash_map", _hash_map)); + ar->setNextName(_label.c_str()); + ar->startNode(); + serialize_graph(*ar, _call_graph); + ar->finishNode(); + ar->finishNode(); + } + + std::ofstream ofs{}; + if(tim::filepath::open(ofs, _fname)) + { + if(_msg) + { + if(_func.empty()) _func = __FUNCTION__; + HOSTTRACE_PRINT("[%s] Outputting '%s'...\n", _func.c_str(), _fname.c_str()); + } + ofs << oss.str() << std::endl; + } + + _perf.stop(); + if(_msg) + { + HOSTTRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); + } +} + +void +save_critical_trace(const std::string& _fname, const std::string& _label, + const std::vector& _cchain, bool _msg = false, + std::string _func = {}) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + + using perfstats_t = + tim::lightweight_tuple; + perfstats_t _perf{ get_perf_name(__FUNCTION__) }; + _perf.start(); + + auto _save = [&](std::ostream& _os) { + namespace cereal = tim::cereal; + auto ar = tim::policy::output_archive::get(_os); + + auto _hash_map = *tim::hash::get_hash_ids(); + for(auto& itr : _hash_map) + itr.second = tim::demangle(itr.second); + ar->setNextName("hosttrace"); + ar->startNode(); + (*ar)(cereal::make_nvp("hash_map", _hash_map), + cereal::make_nvp(_label.c_str(), _cchain)); + ar->finishNode(); + }; + + std::ofstream ofs{}; + if(tim::filepath::open(ofs, _fname)) + { + if(_msg) + { + if(_func.empty()) _func = __FUNCTION__; + HOSTTRACE_PRINT("[%s] Outputting '%s'...\n", _func.c_str(), _fname.c_str()); + } + std::stringstream oss{}; + if(_cchain.size() > 1000) + { + _save(ofs); + } + else + { + _save(oss); + ofs << oss.str() << std::endl; + } + } + + _perf.stop(); + if(_msg) + { + HOSTTRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); + } +} + +void +save_call_chain_text(const std::string& _fname, const call_chain& _call_chain, + bool _msg = false, std::string _func = {}) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + + using perfstats_t = + tim::lightweight_tuple; + perfstats_t _perf{ get_perf_name(__FUNCTION__) }; + _perf.start(); + + std::ofstream ofs{}; + if(tim::filepath::open(ofs, _fname)) + { + if(_msg) + { + if(_func.empty()) _func = __FUNCTION__; + HOSTTRACE_PRINT("[%s] Outputting '%s'...\n", _func.c_str(), _fname.c_str()); + } + ofs << _call_chain << "\n"; + } + + _perf.stop(); + if(_msg) + { + HOSTTRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); + } +} + +void +save_call_chain_json(const std::string& _fname, const std::string& _label, + const call_chain& _call_chain, bool _msg = false, + std::string _func = {}) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + + using perfstats_t = + tim::lightweight_tuple; + perfstats_t _perf{ get_perf_name(__FUNCTION__) }; + _perf.start(); + + auto _save = [&](std::ostream& _os) { + namespace cereal = tim::cereal; + auto ar = tim::policy::output_archive::get(_os); + + auto _hash_map = *tim::hash::get_hash_ids(); + for(auto& itr : _hash_map) + itr.second = tim::demangle(itr.second); + ar->setNextName("hosttrace"); + ar->startNode(); + (*ar)(cereal::make_nvp("hash_map", _hash_map), + cereal::make_nvp(_label.c_str(), _call_chain)); + ar->finishNode(); + }; + + std::ofstream ofs{}; + if(tim::filepath::open(ofs, _fname)) + { + if(_msg) + { + if(_func.empty()) _func = __FUNCTION__; + HOSTTRACE_PRINT("[%s] Outputting '%s'...\n", _func.c_str(), _fname.c_str()); + } + std::stringstream oss{}; + if(_call_chain.size() > 100000) + { + _save(ofs); + } + else + { + _save(oss); + ofs << oss.str() << std::endl; + } + } + + _perf.stop(); + if(_msg) + { + HOSTTRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); + } +} + +void +load_call_chain(const std::string& _fname, const std::string& _label, + call_chain& _call_chain) +{ + std::ifstream ifs{}; + ifs.open(_fname); + if(ifs && ifs.is_open()) + { + namespace cereal = tim::cereal; + auto ar = tim::policy::input_archive::get(ifs); + + ar->setNextName("hosttrace"); + ar->startNode(); + (*ar)(cereal::make_nvp(_label.c_str(), _call_chain)); + ar->finishNode(); + } +} + +template class ContainerT, typename... Args, + typename FuncT = bool (*)(const Tp&, const Tp&)> +inline Tp* +find( + const Tp& _v, ContainerT& _vec, + FuncT&& _func = [](const Tp& _lhs, const Tp& _rhs) { return (_lhs == _rhs); }) +{ + for(auto& itr : _vec) + { + if(std::forward(_func)(_v, itr)) return &itr; + } + return nullptr; +}; + +template +inline entry* +find( + const entry& _v, call_chain& _vec, + FuncT&& _func = [](const entry& _lhs, const entry& _rhs) { return (_lhs == _rhs); }) +{ + return find(_v, reinterpret_cast&>(_vec), + std::forward(_func)); +} + +void +squash_critical_path(call_chain& _targ) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + static auto _strict_equal = [](const entry& _lhs, const entry& _rhs) { + auto _same_phase = (_lhs.phase == _rhs.phase); + bool _phase_check = true; + if(_same_phase) _phase_check = (_lhs.get_timestamp() == _rhs.get_timestamp()); + return (_lhs == _rhs && _lhs.parent_cid == _rhs.parent_cid && _phase_check); + }; + + std::sort(_targ.begin(), _targ.end()); + + call_chain _squashed{}; + for(auto& itr : _targ) + { + if(itr.phase == Phase::DELTA) + { + _squashed.emplace_back(itr); + } + else if(itr.phase == Phase::BEGIN) + { + if(!find(itr, _squashed, _strict_equal)) _squashed.emplace_back(itr); + } + else + { + entry* _match = nullptr; + if((_match = find(itr, _squashed)) != nullptr) + *_match += itr; + else + _squashed.emplace_back(itr); + } + } + + std::swap(_targ, _squashed); + std::sort(_targ.begin(), _targ.end()); +} + +void +combine_critical_path(call_chain& _targ, call_chain _chain) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + call_chain _delta{}; + call_chain _begin{}; + call_chain _end{}; + for(auto& itr : _chain) + { + if(itr.phase == Phase::DELTA) + _delta.emplace_back(itr); + else if(itr.phase == Phase::BEGIN) + _begin.emplace_back(itr); + else if(itr.phase == Phase::END) + { + entry* _match = nullptr; + if((_match = find(itr, _begin)) != nullptr) + *_match += itr; + else + _end.emplace_back(itr); + } + } + + call_chain _combined{}; + _combined.reserve(_delta.size() + _begin.size() + _end.size()); + for(auto& itr : _delta) + _combined.emplace_back(itr); + for(auto& itr : _begin) + _combined.emplace_back(itr); + for(auto& itr : _end) + _combined.emplace_back(itr); + std::sort(_combined.begin(), _combined.end()); + + std::unique_lock _lk{ complete_call_mutex }; + for(auto& itr : _combined) + _targ.emplace_back(itr); + + // squash_critical_path(_targ); +} + +auto +get_indexed(const call_chain& _chain) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + std::map> _indexed = {}; + + // allocate for all cpu correlation ids + for(const auto& itr : _chain) + { + _indexed.emplace(static_cast(itr.cpu_cid), std::vector{}); + _indexed.emplace(static_cast(itr.parent_cid), std::vector{}); + } + + // index based on parent correlation id + for(const auto& itr : _chain) + { + if(itr.depth < 1 && itr.phase == Phase::BEGIN) continue; + _indexed[static_cast(itr.parent_cid)].emplace_back(itr); + } + + for(auto& itr : _indexed) + std::sort(itr.second.begin(), itr.second.end(), + [](const entry& lhs, const entry& rhs) { + // return lhs.cpu_cid < rhs.cpu_cid; + return lhs.begin_ns < rhs.begin_ns; + }); + + return _indexed; +} + +void +find_children(PTL::ThreadPool& _tp, call_graph_t& _graph, const call_chain& _chain) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + + using iterator_t = call_graph_sibling_itr_t; + using itr_entry_vec_t = std::vector>; + using task_group_t = PTL::TaskGroup; + + auto _indexed = get_indexed(_chain); + std::map> _entry_map{}; + + // allocate all entries + HOSTTRACE_CT_DEBUG("[%s] Allocating...\n", __FUNCTION__); + for(const auto& itr : _chain) + { + auto _ins = _entry_map.emplace(itr, std::vector{}); + if(!_ins.second) + { + auto _existing = _ins.first->first; + HOSTTRACE_PRINT("Warning! Duplicate entry for [%s] :: [%s]\n", + JOIN("", _existing).c_str(), JOIN("", itr).c_str()); + } + } + + task_group_t _tg{ &_tp }; + HOSTTRACE_CT_DEBUG("[%s] Parallel mapping...\n", __FUNCTION__); + for(const auto& itr : _chain) + { + _tg.run([&]() { _entry_map[itr] = _indexed.at(itr.cpu_cid); }); + } + _tg.join(); + + std::function _recursive_func; + _recursive_func = [&](iterator_t itr, const entry& _v) { + auto _child = _graph.append_child(itr, _v); + auto _children = std::move(_entry_map[_v]); + _entry_map[_v].clear(); + for(auto&& vitr : _children) + { + _recursive_func(_child, vitr); + } + }; + + // the recursive version of _func + _loop_func has a tendency to overflow the stack + auto _func = [&](iterator_t itr, const entry& _v) { + auto _child = _graph.append_child(itr, _v); + auto _children = std::move(_entry_map[_v]); + _entry_map[_v].clear(); + itr_entry_vec_t _data{}; + for(auto&& vitr : _children) + _data.emplace_back(_child, vitr); + return _data; + }; + + auto _loop_func = [&_func](itr_entry_vec_t& _data) { + auto _inp = _data; + _data.clear(); + for(auto itr : _inp) + { + for(auto&& fitr : _func(itr.first, itr.second)) + _data.emplace_back(std::move(fitr)); + } + // if data is empty return false so we can break out of while loop + return !_data.empty(); + }; + + if(!_indexed.at(-1).empty()) + { + HOSTTRACE_CT_DEBUG("[%s] Setting root (line %i)...\n", __FUNCTION__, __LINE__); + _graph.set_head(_indexed.at(-1).front()); + } + else + { + HOSTTRACE_CT_DEBUG("[%s] Setting root (line %i)...\n", __FUNCTION__, __LINE__); + auto _depth = static_cast(-1); + entry _root{ 0, Device::NONE, Phase::NONE, _depth, 0, 0, 0, 0, 0, 0, 0 }; + _graph.set_head(_root); + } + + iterator_t _root = _graph.begin(); + for(auto&& itr : _entry_map) + { + if(itr.first.depth == _root->depth + 1) + { + HOSTTRACE_CT_DEBUG("[%s] Generating call-graph...\n", __FUNCTION__); + // _recursive_func(_root, itr.first); + itr_entry_vec_t _data = _func(_root, itr.first); + while(_loop_func(_data)) + {} + } + } +} + +void +find_sequences(PTL::ThreadPool& _tp, call_graph_t& _graph, + std::vector& _chain) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + using iterator_t = call_graph_preorder_itr_t; + std::vector _end_nodes{}; + size_t _n = 0; + for(iterator_t itr = _graph.begin(); itr != _graph.end(); ++itr, ++_n) + { + auto _nchild = _graph.number_of_children(itr); + if(_nchild > 0) + { + HOSTTRACE_CT_DEBUG("Skipping node #%zu with %u children :: %s\n", _n, _nchild, + JOIN("", *itr).c_str()); + continue; + } + _end_nodes.emplace_back(itr); + } + HOSTTRACE_CT_DEBUG("Number of end nodes: %zu\n", _end_nodes.size()); + _chain.resize(_end_nodes.size()); + + auto _construct = [&](size_t i) { + auto itr = _end_nodes.at(i); + while(itr != nullptr && _graph.is_valid(itr)) + { + _chain.at(i).emplace_back(*itr); + itr = _graph.parent(itr); + } + std::reverse(_chain.at(i).begin(), _chain.at(i).end()); + }; + + PTL::TaskGroup _tg{ &_tp }; + for(size_t i = 0; i < _end_nodes.size(); ++i) + _tg.run(_construct, i); + _tg.join(); + + std::sort(_chain.begin(), _chain.end(), + [](const call_chain& lhs, const call_chain& rhs) { + return lhs.get_cost() > rhs.get_cost(); + }); +} + +template +void +serialize_graph(ArchiveT& ar, const tim::graph& t) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + + namespace cereal = tim::cereal; + using iterator_t = typename tim::graph::sibling_iterator; + + ar(cereal::make_nvp("graph_nodes", t.size())); + ar.setNextName("graph"); + ar.startNode(); + ar.makeArray(); + for(iterator_t itr = t.begin(); itr != t.end(); ++itr) + serialize_subgraph(ar, t, itr); + ar.finishNode(); +} + +template +void +serialize_subgraph(ArchiveT& ar, const tim::graph& _graph, + typename tim::graph::iterator _root) +{ + using iterator_t = typename tim::graph::sibling_iterator; + + if(_graph.empty()) return; + + ar.setNextName("node"); + ar.startNode(); + ar(*_root); + { + ar.setNextName("children"); + ar.startNode(); + ar.makeArray(); + for(iterator_t itr = _graph.begin(_root); itr != _graph.end(_root); ++itr) + serialize_subgraph(ar, _graph, itr); + ar.finishNode(); + } + ar.finishNode(); +} + +void +update_critical_path(call_chain _chain, int64_t _tid) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + try + { + // remove any data not + auto _diff_tid = [_tid](const entry& _v) { return _v.tid != _tid; }; + _chain.erase(std::remove_if(_chain.begin(), _chain.end(), _diff_tid), + _chain.end()); + combine_critical_path(complete_call_chain, std::move(_chain)); + } catch(const std::exception& e) + { + std::cerr << "Thread exited with exception: " << e.what() << std::endl; + TIMEMORY_CONDITIONAL_DEMANGLED_BACKTRACE(true, 32); + } +} + +template +std::vector +get_top(const std::vector& _chain, size_t _count) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + std::vector _data{}; + _data.reserve(_count); + for(const auto& itr : _chain) + { + if(_data.size() >= _count) break; + if(itr.query<>([](const entry& _v) { return (_v.device == DevT); })) + { + _data.emplace_back(itr); + } + } + return _data; +} + +template +void +generate_perfetto(const std::vector& _data) +{ + HOSTTRACE_CT_DEBUG("[%s]\n", __FUNCTION__); + auto _func = [&](size_t _beg, size_t _end) { + // ensure all hash ids exist + copy_hash_ids(); + std::set _used{}; + for(size_t i = _beg; i < _end; ++i) + { + if(i >= _data.size()) break; + _data.at(i).generate_perfetto(_used); + } + }; + + // run in separate thread(s) so that it ends up in unique row + auto _nrows = get_critical_trace_per_row(); + if(_nrows < 1) _nrows = _data.size(); + + for(size_t i = 0; i < _data.size(); i += _nrows) + { + std::thread{ _func, i, i + _nrows }.join(); + } +} + +void +compute_critical_trace() +{ + static bool _computed = false; + std::unique_lock _lk{ complete_call_mutex }; + + if(_computed) return; + + HOSTTRACE_CT_DEBUG("[%s] Generating critical trace...\n", __FUNCTION__); + + // ensure all hash ids exist + copy_hash_ids(); + + using perfstats_t = + tim::lightweight_tuple; + + perfstats_t _ct_perf{ JOIN("", "[", __FUNCTION__, "]") }; + _ct_perf.start(); + + try + { + PTL::ThreadPool _tp{ get_critical_trace_num_threads(), []() { copy_hash_ids(); }, + false }; + _tp.set_verbose(-1); + PTL::TaskGroup _tg{ &_tp }; + + HOSTTRACE_CT_DEBUG("[%s] initial call chain: %zu entries\n", __FUNCTION__, + complete_call_chain.size()); + + perfstats_t _perf{ get_perf_name(__FUNCTION__) }; + _perf.start(); + + std::sort(complete_call_chain.begin(), complete_call_chain.end()); + + _perf.stop().rekey("Sorting critical trace"); + HOSTTRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); + + _perf.start(); + + squash_critical_path(complete_call_chain); + + HOSTTRACE_CT_DEBUG("[%s] complete call chain: %zu entries\n", __FUNCTION__, + complete_call_chain.size()); + + _perf.stop().rekey("Squash critical path"); + HOSTTRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); + + _tg.run( + [](call_chain _chain, std::string _func) { + save_call_chain_json(tim::settings::compose_output_filename( + "call-chain", ".json", get_use_pid(), + (tim::dmp::is_initialized()) + ? tim::dmp::rank() + : process::get_id()), + "call_chain", _chain, true, std::move(_func)); + }, + complete_call_chain, __FUNCTION__); + + _perf.reset().start(); + + HOSTTRACE_CT_DEBUG("[%s] Finding children...\n", __FUNCTION__); + call_graph_t _graph{}; + find_children(_tp, _graph, complete_call_chain); + + HOSTTRACE_CT_DEBUG("[%s] complete call graph: %zu entries\n", __FUNCTION__, + _graph.size() - 1); + + _perf.stop().rekey("Finding children"); + HOSTTRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); + + _tg.run( + [&](std::string _func) { + save_call_graph(tim::settings::compose_output_filename( + "call-graph", ".json", get_use_pid(), + (tim::dmp::is_initialized()) ? tim::dmp::rank() + : process::get_id()), + "call_graph", _graph, true, std::move(_func)); + }, + __FUNCTION__); + + _tg.join(); + + _perf.reset().start(); + + HOSTTRACE_CT_DEBUG("[%s] Finding sequences...\n", __FUNCTION__); + // find the sequences + std::vector _top{}; + find_sequences(_tp, _graph, _top); + + _perf.stop().rekey("Finding sequences"); + HOSTTRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); + + if(get_critical_trace_count() == 0) + { + HOSTTRACE_CT_DEBUG("[%s] Saving critical trace...\n", __FUNCTION__); + save_critical_trace( + tim::settings::compose_output_filename( + "critical-trace", ".json", get_use_pid(), + (tim::dmp::is_initialized()) ? tim::dmp::rank() : process::get_id()), + "critical_trace", _top, true, __FUNCTION__); + } + else + { + HOSTTRACE_CT_DEBUG("[%s] Getting top CPU functions...\n", __FUNCTION__); + // get the top CPU critical traces + auto _top_cpu = get_top(_top, get_critical_trace_count()); + if(!_top_cpu.empty()) + { + HOSTTRACE_CT_DEBUG("[%s] Generating perfetto CPU critical traces...\n", + __FUNCTION__); + generate_perfetto(_top_cpu); + HOSTTRACE_CT_DEBUG("[%s] Saving CPU critical traces...\n", __FUNCTION__); + save_critical_trace(tim::settings::compose_output_filename( + "critical-trace-cpu", ".json", get_use_pid(), + (tim::dmp::is_initialized()) ? tim::dmp::rank() + : process::get_id()), + "critical_trace", _top_cpu, true, __FUNCTION__); + } + + HOSTTRACE_CT_DEBUG("[%s] Getting top GPU functions...\n", __FUNCTION__); + // get the top GPU critical traces + auto _top_gpu = get_top(_top, get_critical_trace_count()); + if(!_top_gpu.empty()) + { + HOSTTRACE_CT_DEBUG("[%s] Generating perfetto GPU critical traces...\n", + __FUNCTION__); + generate_perfetto(_top_gpu); + HOSTTRACE_CT_DEBUG("[%s] Saving GPU critical traces...\n", __FUNCTION__); + save_critical_trace(tim::settings::compose_output_filename( + "critical-trace-gpu", ".json", get_use_pid(), + (tim::dmp::is_initialized()) ? tim::dmp::rank() + : process::get_id()), + "critical_trace", _top_gpu, true, __FUNCTION__); + } + } + + _tg.join(); + _tp.destroy_threadpool(); + _computed = true; + + } catch(const std::exception& e) + { + HOSTTRACE_PRINT("Thread exited '%s' with exception: %s\n", __FUNCTION__, + e.what()); + TIMEMORY_CONDITIONAL_DEMANGLED_BACKTRACE(true, 32); + } + + _ct_perf.stop(); + auto _ct_msg = JOIN("", _ct_perf); + auto _ct_pos = _ct_msg.find(">>> "); + if(_ct_pos != std::string::npos) _ct_msg = _ct_msg.substr(_ct_pos + 5); + HOSTTRACE_PRINT("%s\n", _ct_msg.c_str()); +} +} // namespace +} // namespace critical_trace diff --git a/projects/rocprofiler-systems/src/library/fork_gotcha.cpp b/projects/rocprofiler-systems/src/library/fork_gotcha.cpp new file mode 100644 index 0000000000..612859bed3 --- /dev/null +++ b/projects/rocprofiler-systems/src/library/fork_gotcha.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/fork_gotcha.hpp" +#include "library/config.hpp" +#include "library/debug.hpp" + +void +fork_gotcha::audit(const gotcha_data_t&, audit::incoming) +{ + HOSTTRACE_DEBUG( + "Warning! Calling fork() within an OpenMPI application using libfabric " + "may result is segmentation fault\n"); + TIMEMORY_CONDITIONAL_DEMANGLED_BACKTRACE(get_debug(), 16); +} + +void +fork_gotcha::audit(const gotcha_data_t& _data, audit::outgoing, pid_t _pid) +{ + HOSTTRACE_DEBUG("%s() return PID %i\n", _data.tool_id.c_str(), (int) _pid); +} diff --git a/projects/rocprofiler-systems/src/library/hosttrace_component.cpp b/projects/rocprofiler-systems/src/library/hosttrace_component.cpp new file mode 100644 index 0000000000..8c460348dc --- /dev/null +++ b/projects/rocprofiler-systems/src/library/hosttrace_component.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/hosttrace_component.hpp" +#include "library/api.hpp" + +void +hosttrace_component::start() +{ + if(m_prefix) hosttrace_push_trace(m_prefix); +} + +void +hosttrace_component::stop() +{ + if(m_prefix) hosttrace_pop_trace(m_prefix); +} + +void +hosttrace_component::set_prefix(const char* _prefix) +{ + m_prefix = _prefix; +} + +TIMEMORY_INITIALIZE_STORAGE(hosttrace_component) diff --git a/projects/rocprofiler-systems/src/libmisc.cpp b/projects/rocprofiler-systems/src/library/mpi_gotcha.cpp similarity index 54% rename from projects/rocprofiler-systems/src/libmisc.cpp rename to projects/rocprofiler-systems/src/library/mpi_gotcha.cpp index bc77ca5be8..6e86e7ae52 100644 --- a/projects/rocprofiler-systems/src/libmisc.cpp +++ b/projects/rocprofiler-systems/src/library/mpi_gotcha.cpp @@ -1,14 +1,40 @@ -#include "library.hpp" +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. -// -// This file contains miscellaneous function definitions related to timemory -// placed in separate file so that, during development, the long compile-times -// arising from compiling timemory's gotcha wrappers are reduced -// +#include "library/mpi_gotcha.hpp" +#include "library/config.hpp" +#include "library/debug.hpp" +#include "library/hosttrace_component.hpp" namespace { -uint64_t mpip_index = std::numeric_limits::max(); +uint64_t mpip_index = std::numeric_limits::max(); +std::string mpi_init_string = {}; // this ensures hosttrace_trace_finalize is called before MPI_Finalize void @@ -19,10 +45,12 @@ hosttrace_mpi_set_attr() return MPI_SUCCESS; }; static auto _mpi_fini = [](MPI_Comm, int, void*, void*) { + HOSTTRACE_DEBUG("MPI Comm attribute finalize\n"); if(mpip_index != std::numeric_limits::max()) comp::deactivate_mpip, hosttrace>( mpip_index); - hosttrace_pop_trace("MPI_Finalize()"); + if(!mpi_init_string.empty()) hosttrace_pop_trace(mpi_init_string.c_str()); + mpi_init_string = {}; hosttrace_trace_finalize(); return MPI_SUCCESS; }; @@ -37,26 +65,15 @@ hosttrace_mpi_set_attr() } } // namespace -void -fork_gotcha::audit(const gotcha_data_t&, audit::incoming) -{ - HOSTTRACE_DEBUG( - "Warning! Calling fork() within an OpenMPI application using libfabric " - "may result is segmentation fault\n"); - TIMEMORY_CONDITIONAL_DEMANGLED_BACKTRACE(get_debug(), 16); -} - -void -fork_gotcha::audit(const gotcha_data_t& _data, audit::outgoing, pid_t _pid) -{ - HOSTTRACE_DEBUG("%s() return PID %i\n", _data.tool_id.c_str(), (int) _pid); -} - void mpi_gotcha::audit(const gotcha_data_t& _data, audit::incoming, int*, char***) { HOSTTRACE_DEBUG("[%s] %s(int*, char***)\n", __FUNCTION__, _data.tool_id.c_str()); - if(get_state() == ::State::DelayedInit) get_state() = ::State::PreInit; + if(get_state() == ::State::DelayedInit) + { + get_state() = ::State::PreInit; + mpi_init_string = _data.tool_id; + } } void @@ -64,7 +81,11 @@ mpi_gotcha::audit(const gotcha_data_t& _data, audit::incoming, int*, char***, in { HOSTTRACE_DEBUG("[%s] %s(int*, char***, int, int*)\n", __FUNCTION__, _data.tool_id.c_str()); - if(get_state() == ::State::DelayedInit) get_state() = ::State::PreInit; + if(get_state() == ::State::DelayedInit) + { + get_state() = ::State::PreInit; + mpi_init_string = _data.tool_id; + } } void @@ -80,7 +101,7 @@ mpi_gotcha::audit(const gotcha_data_t& _data, audit::outgoing, int _retval) // being activated unwaringly during runtime instrumentation because that // will result in double instrumenting the MPI functions (unless the MPI functions // were excluded via a regex expression) - if(tim::get_env("HOSTTRACE_USE_MPIP", false, false)) + if(get_use_mpip()) { HOSTTRACE_DEBUG("[%s] Activating MPI wrappers...\n", __FUNCTION__); comp::configure_mpip, hosttrace>(); @@ -98,39 +119,9 @@ mpi_gotcha::audit(const gotcha_data_t& _data, audit::incoming) if(mpip_index != std::numeric_limits::max()) comp::deactivate_mpip, hosttrace>( mpip_index); - hosttrace_pop_trace("MPI_Finalize()"); + if(!mpi_init_string.empty()) hosttrace_pop_trace(mpi_init_string.c_str()); + mpi_init_string = {}; hosttrace_trace_finalize(); } -void -hosttrace_component::start() -{ - if(m_prefix) hosttrace_push_trace(m_prefix); -} - -void -hosttrace_component::stop() -{ - if(m_prefix) hosttrace_pop_trace(m_prefix); -} - -void -hosttrace_component::set_prefix(const char* _prefix) -{ - m_prefix = _prefix; -} - -hosttrace_timemory_data::instance_array_t& -hosttrace_timemory_data::instances() -{ - static auto _v = instance_array_t{}; - return _v; -} - -PERFETTO_TRACK_EVENT_STATIC_STORAGE(); -TIMEMORY_INITIALIZE_STORAGE(fork_gotcha, mpi_gotcha, comp::wall_clock, - comp::user_global_bundle) - -#if defined(CUSTOM_DATA_SOURCE) -PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource); -#endif +TIMEMORY_INITIALIZE_STORAGE(mpi_gotcha) diff --git a/projects/rocprofiler-systems/src/library/perfetto.cpp b/projects/rocprofiler-systems/src/library/perfetto.cpp new file mode 100644 index 0000000000..e951b868df --- /dev/null +++ b/projects/rocprofiler-systems/src/library/perfetto.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/perfetto.hpp" + +PERFETTO_TRACK_EVENT_STATIC_STORAGE(); + +#if defined(CUSTOM_DATA_SOURCE) +PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource); +#endif diff --git a/projects/rocprofiler-systems/src/library/ptl.cpp b/projects/rocprofiler-systems/src/library/ptl.cpp new file mode 100644 index 0000000000..746480a167 --- /dev/null +++ b/projects/rocprofiler-systems/src/library/ptl.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/ptl.hpp" + +namespace tasking +{ +std::mutex& +get_roctracer_mutex() +{ + static std::mutex _v{}; + return _v; +} + +PTL::ThreadPool& +get_roctracer_thread_pool() +{ + static auto _v = PTL::ThreadPool{ 1 }; + return _v; +} + +PTL::TaskGroup& +get_roctracer_task_group() +{ + static PTL::TaskGroup _v{ &get_roctracer_thread_pool() }; + return _v; +} + +std::mutex& +get_critical_trace_mutex() +{ + static std::mutex _v{}; + return _v; +} + +PTL::ThreadPool& +get_critical_trace_thread_pool() +{ + static auto _v = PTL::ThreadPool{ 1 }; + return _v; +} + +PTL::TaskGroup& +get_critical_trace_task_group() +{ + static PTL::TaskGroup _v{ &get_critical_trace_thread_pool() }; + return _v; +} + +namespace +{ +bool _ptl_initialized = + (get_roctracer_thread_pool(), get_critical_trace_thread_pool(), true); +} +} // namespace tasking diff --git a/projects/rocprofiler-systems/src/library/roctracer.cpp b/projects/rocprofiler-systems/src/library/roctracer.cpp new file mode 100644 index 0000000000..6088cd46de --- /dev/null +++ b/projects/rocprofiler-systems/src/library/roctracer.cpp @@ -0,0 +1,283 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/roctracer.hpp" +#include "library/config.hpp" +#include "library/defines.hpp" +#include "library/roctracer_callbacks.hpp" +#include "library/thread_data.hpp" + +namespace tim +{ +namespace component +{ +void +roctracer::preinit() +{ + HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); + roctracer_data::label() = "roctracer"; + roctracer_data::description() = "ROCm tracer (activity API)"; +} + +bool +roctracer::is_setup() +{ + return roctracer_is_setup(); +} + +void +roctracer::add_setup(const std::string& _lbl, std::function&& _func) +{ + roctracer_setup_routines().emplace_back(_lbl, std::move(_func)); +} + +void +roctracer::add_tear_down(const std::string& _lbl, std::function&& _func) +{ + roctracer_tear_down_routines().emplace_back(_lbl, std::move(_func)); +} + +void +roctracer::remove_setup(const std::string& _lbl) +{ + auto& _data = roctracer_setup_routines(); + for(auto itr = _data.begin(); itr != _data.end(); ++itr) + { + if(itr->first == _lbl) + { + _data.erase(itr); + break; + } + } +} + +void +roctracer::remove_tear_down(const std::string& _lbl) +{ + auto& _data = roctracer_setup_routines(); + for(auto itr = _data.begin(); itr != _data.end(); ++itr) + { + if(itr->first == _lbl) + { + _data.erase(itr); + break; + } + } +} + +void +roctracer::setup() +{ + if(!get_use_timemory() && !get_use_perfetto()) return; + + auto_lock_t _lk{ type_mutex() }; + if(roctracer_is_setup()) return; + roctracer_is_setup() = true; + HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); + + tim::set_env("HSA_TOOLS_LIB", "libhosttrace.so", 0); + + auto _kfdwrapper = dynamic_library{ "HOSTTRACE_ROCTRACER_LIBKFDWRAPPER", + HOSTTRACE_ROCTRACER_LIBKFDWRAPPER }; + + ROCTRACER_CALL(roctracer_set_properties(ACTIVITY_DOMAIN_HIP_API, nullptr)); + + // if(roctracer_default_pool() == nullptr) + { + // Allocating tracing pool + roctracer_properties_t properties{}; + memset(&properties, 0, sizeof(roctracer_properties_t)); + properties.mode = 0x1000; + properties.buffer_size = 0x1000; + properties.buffer_callback_fun = hip_activity_callback; + ROCTRACER_CALL(roctracer_open_pool(&properties)); + } + + // Enable API callbacks, all domains + ROCTRACER_CALL(roctracer_enable_callback(hip_api_callback, nullptr)); + // Enable activity tracing, all domains + ROCTRACER_CALL(roctracer_enable_activity()); + + // callback for HSA + for(auto& itr : roctracer_setup_routines()) + itr.second(); +} + +void +roctracer::tear_down() +{ + auto_lock_t _lk{ type_mutex() }; + if(!roctracer_is_setup()) return; + roctracer_is_setup() = false; + HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); + + // flush all the activity + if(roctracer_default_pool() != nullptr) + { + ROCTRACER_CALL(roctracer_flush_activity()); + } + // flush all buffers + roctracer_flush_buf(); + + // make sure all async operations are executed + for(size_t i = 0; i < max_supported_threads; ++i) + hip_exec_activity_callbacks(i); + + // callback for hsa + for(auto& itr : roctracer_tear_down_routines()) + itr.second(); + + // Disable tracing and closing the pool + ROCTRACER_CALL(roctracer_disable_callback()); + ROCTRACER_CALL(roctracer_disable_activity()); + ROCTRACER_CALL(roctracer_close_pool()); +} + +void +roctracer::start() +{ + if(tracker_type::start() == 0) setup(); +} + +void +roctracer::stop() +{ + if(tracker_type::stop() == 0) tear_down(); +} +} // namespace component +} // namespace tim + +TIMEMORY_INSTANTIATE_EXTERN_COMPONENT(roctracer, false, void) +TIMEMORY_INSTANTIATE_EXTERN_COMPONENT(roctracer_data, true, double) + +// HSA-runtime tool on-load method +extern "C" +{ + bool OnLoad(HsaApiTable* table, uint64_t runtime_version, uint64_t failed_tool_count, + const char* const* failed_tool_names) TIMEMORY_VISIBILITY("default"); + void OnUnload() TIMEMORY_VISIBILITY("default"); + + bool OnLoad(HsaApiTable* table, uint64_t runtime_version, uint64_t failed_tool_count, + const char* const* failed_tool_names) + { + HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); + tim::consume_parameters(table, runtime_version, failed_tool_count, + failed_tool_names); + + // ONLOAD_TRACE_BEG(); + // on_exit(exit_handler, nullptr); + + auto _setup = [=]() { + get_hsa_timer() = + std::make_unique(table->core_->hsa_system_get_info_fn); + + // const char* output_prefix = getenv("ROCP_OUTPUT_DIR"); + const char* output_prefix = nullptr; + + // App begin timestamp begin_ts_file.txt + // begin_ts_file_handle = open_output_file(output_prefix, + // "begin_ts_file.txt"); const timestamp_t app_start_time = + // timer->timestamp_fn_ns(); fprintf(begin_ts_file_handle, "%lu\n", + // app_start_time); + + bool trace_hsa_api = get_trace_hsa_api(); + std::vector hsa_api_vec = + tim::delimit(get_trace_hsa_api_types()); + + // Enable HSA API callbacks/activity + if(trace_hsa_api) + { + // hsa_api_file_handle = open_output_file(output_prefix, + // "hsa_api_trace.txt"); + + // initialize HSA tracing + roctracer_set_properties(ACTIVITY_DOMAIN_HSA_API, (void*) table); + + HOSTTRACE_DEBUG(" HSA-trace("); + if(!hsa_api_vec.empty()) + { + for(const auto& itr : hsa_api_vec) + { + uint32_t cid = HSA_API_ID_NUMBER; + const char* api = itr.c_str(); + ROCTRACER_CALL(roctracer_op_code(ACTIVITY_DOMAIN_HSA_API, api, + &cid, nullptr)); + ROCTRACER_CALL(roctracer_enable_op_callback( + ACTIVITY_DOMAIN_HSA_API, cid, hsa_api_callback, nullptr)); + + HOSTTRACE_DEBUG(" %s", api); + } + } + else + { + ROCTRACER_CALL(roctracer_enable_domain_callback( + ACTIVITY_DOMAIN_HSA_API, hsa_api_callback, nullptr)); + } + HOSTTRACE_DEBUG("\n"); + } + + bool trace_hsa_activity = get_trace_hsa_activity(); + // Enable HSA GPU activity + if(trace_hsa_activity) + { + // initialize HSA tracing + ::roctracer::hsa_ops_properties_t ops_properties{ + table, + reinterpret_cast(hsa_activity_callback), + nullptr, output_prefix + }; + roctracer_set_properties(ACTIVITY_DOMAIN_HSA_OPS, &ops_properties); + + HOSTTRACE_DEBUG(" HSA-activity-trace()\n"); + ROCTRACER_CALL(roctracer_enable_op_activity(ACTIVITY_DOMAIN_HSA_OPS, + HSA_OP_ID_COPY)); + } + }; + + auto _tear_down = []() { + ROCTRACER_CALL(roctracer_disable_domain_callback(ACTIVITY_DOMAIN_HSA_API)); + + ROCTRACER_CALL( + roctracer_disable_op_activity(ACTIVITY_DOMAIN_HSA_OPS, HSA_OP_ID_COPY)); + }; + + if(comp::roctracer::is_setup()) _setup(); + + comp::roctracer::add_setup("hsa", std::move(_setup)); + comp::roctracer::add_tear_down("hsa", std::move(_tear_down)); + + return true; + } + + // HSA-runtime on-unload method + void OnUnload() + { + HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); + // ONLOAD_TRACE(""); + } +} diff --git a/projects/rocprofiler-systems/src/library/roctracer_callbacks.cpp b/projects/rocprofiler-systems/src/library/roctracer_callbacks.cpp new file mode 100644 index 0000000000..fb857f9620 --- /dev/null +++ b/projects/rocprofiler-systems/src/library/roctracer_callbacks.cpp @@ -0,0 +1,599 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/roctracer_callbacks.hpp" +#include "library.hpp" +#include "library/config.hpp" +#include "library/critical_trace.hpp" +#include "library/thread_data.hpp" +#include "timemory/backends/threading.hpp" + +#include + +TIMEMORY_DEFINE_API(roctracer) +namespace api = tim::api; + +std::unordered_set& +get_roctracer_kernels() +{ + static auto _v = std::unordered_set{}; + return _v; +} + +auto& +get_roctracer_hip_data(int64_t _tid = threading::get_id()) +{ + using data_t = std::unordered_map; + using thread_data_t = hosttrace_thread_data; + static auto& _v = thread_data_t::instances(thread_data_t::construct_on_init{}); + return _v.at(_tid); +} + +std::unordered_map& +get_roctracer_key_data() +{ + static auto _v = std::unordered_map{}; + return _v; +} + +std::unordered_map& +get_roctracer_tid_data() +{ + static auto _v = std::unordered_map{}; + return _v; +} + +using cid_tuple_t = std::tuple; +std::unordered_map& +get_roctracer_cid_data() +{ + static auto _v = std::unordered_map{}; + return _v; +} + +auto& +get_hip_activity_callbacks(int64_t _tid = threading::get_id()) +{ + using thread_data_t = + hosttrace_thread_data>, api::roctracer>; + static auto& _v = thread_data_t::instances(thread_data_t::construct_on_init{}); + return _v.at(_tid); +} + +std::unique_ptr& +get_hsa_timer() +{ + static auto _v = std::unique_ptr{}; + return _v; +} + +using hip_activity_mutex_t = std::decay_t; +using key_data_mutex_t = std::decay_t; +using hip_data_mutex_t = std::decay_t; +using cid_data_mutex_t = std::decay_t; + +auto& +get_hip_activity_mutex(int64_t _tid = threading::get_id()) +{ + return tim::type_mutex( + _tid); +} + +// HSA API callback function +void +hsa_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg) +{ + if(get_state() != State::Active || !trait::runtime_enabled::get()) + return; + + (void) arg; + const hsa_api_data_t* data = reinterpret_cast(callback_data); + HOSTTRACE_DEBUG("<%-30s id(%u)\tcorrelation_id(%lu) %s>\n", + roctracer_op_string(domain, cid, 0), cid, data->correlation_id, + (data->phase == ACTIVITY_API_PHASE_ENTER) ? "on-enter" : "on-exit"); + + static thread_local timestamp_t begin_timestamp = 0; + static auto& timer = get_hsa_timer(); + static auto _scope = []() { + auto _v = scope::config{}; + if(get_roctracer_timeline_profile()) _v += scope::timeline{}; + if(get_roctracer_flat_profile()) _v += scope::flat{}; + return _v; + }(); + + if(!timer) return; + + switch(cid) + { + case HSA_API_ID_hsa_init: + case HSA_API_ID_hsa_shut_down: + case HSA_API_ID_hsa_agent_get_exception_policies: + case HSA_API_ID_hsa_agent_get_info: + case HSA_API_ID_hsa_amd_agent_iterate_memory_pools: + case HSA_API_ID_hsa_amd_agent_memory_pool_get_info: + case HSA_API_ID_hsa_amd_coherency_get_type: + case HSA_API_ID_hsa_amd_memory_pool_get_info: + case HSA_API_ID_hsa_amd_pointer_info: + case HSA_API_ID_hsa_amd_pointer_info_set_userdata: + case HSA_API_ID_hsa_amd_profiling_async_copy_enable: + case HSA_API_ID_hsa_amd_profiling_get_async_copy_time: + case HSA_API_ID_hsa_amd_profiling_get_dispatch_time: + case HSA_API_ID_hsa_amd_profiling_set_profiler_enabled: + case HSA_API_ID_hsa_cache_get_info: + case HSA_API_ID_hsa_code_object_get_info: + case HSA_API_ID_hsa_code_object_get_symbol: + case HSA_API_ID_hsa_code_object_get_symbol_from_name: + case HSA_API_ID_hsa_code_object_reader_create_from_memory: + case HSA_API_ID_hsa_code_symbol_get_info: + case HSA_API_ID_hsa_executable_create_alt: + case HSA_API_ID_hsa_executable_freeze: + case HSA_API_ID_hsa_executable_get_info: + case HSA_API_ID_hsa_executable_get_symbol: + case HSA_API_ID_hsa_executable_get_symbol_by_name: + case HSA_API_ID_hsa_executable_symbol_get_info: + case HSA_API_ID_hsa_extension_get_name: + case HSA_API_ID_hsa_ext_image_data_get_info: + case HSA_API_ID_hsa_ext_image_data_get_info_with_layout: + case HSA_API_ID_hsa_ext_image_get_capability: + case HSA_API_ID_hsa_ext_image_get_capability_with_layout: + case HSA_API_ID_hsa_isa_get_exception_policies: + case HSA_API_ID_hsa_isa_get_info: + case HSA_API_ID_hsa_isa_get_info_alt: + case HSA_API_ID_hsa_isa_get_round_method: + case HSA_API_ID_hsa_region_get_info: + case HSA_API_ID_hsa_system_extension_supported: + case HSA_API_ID_hsa_system_get_extension_table: + case HSA_API_ID_hsa_system_get_info: + case HSA_API_ID_hsa_system_get_major_extension_table: + case HSA_API_ID_hsa_wavefront_get_info: break; + default: + { + if(data->phase == ACTIVITY_API_PHASE_ENTER) + { + begin_timestamp = timer->timestamp_fn_ns(); + } + else + { + const auto* _name = roctracer_op_string(domain, cid, 0); + const timestamp_t end_timestamp = (cid == HSA_API_ID_hsa_shut_down) + ? begin_timestamp + : timer->timestamp_fn_ns(); + + if(begin_timestamp > end_timestamp) return; + + if(get_use_perfetto()) + { + TRACE_EVENT_BEGIN("device", perfetto::StaticString{ _name }, + begin_timestamp); + TRACE_EVENT_END("device", end_timestamp); + } + + if(get_use_timemory()) + { + std::unique_lock _lk{ tasking::get_roctracer_mutex() }; + auto _begin_ns = begin_timestamp; + auto _end_ns = end_timestamp; + tasking::get_roctracer_task_group().exec( + [_name, _begin_ns, _end_ns]() { + roctracer_hsa_bundle_t _bundle{ _name, _scope }; + _bundle.start() + .store(std::plus{}, + static_cast(_end_ns - _begin_ns)) + .stop(); + }); + } + // timemory is disabled in this callback because collecting data in this + // thread causes strange segmentation faults + } + } + } +} + +void +hsa_activity_callback(uint32_t op, activity_record_t* record, void* arg) +{ + static const char* copy_op_name = "hsa_async_copy"; + static const char* dispatch_op_name = "hsa_dispatch"; + static const char* barrier_op_name = "hsa_barrier"; + const char** _name = nullptr; + + switch(op) + { + case HSA_OP_ID_DISPATCH: _name = &dispatch_op_name; break; + case HSA_OP_ID_COPY: _name = ©_op_name; break; + case HSA_OP_ID_BARRIER: _name = &barrier_op_name; break; + default: break; + } + + if(!_name) return; + + auto _begin_ns = record->begin_ns; + auto _end_ns = record->end_ns; + static auto _scope = []() { + auto _v = scope::config{}; + if(get_roctracer_timeline_profile()) _v += scope::timeline{}; + if(get_roctracer_flat_profile()) _v += scope::flat{}; + return _v; + }(); + + auto _func = [_begin_ns, _end_ns, _name]() { + if(get_use_perfetto()) + { + TRACE_EVENT_BEGIN("device", perfetto::StaticString{ *_name }, _begin_ns); + TRACE_EVENT_END("device", _end_ns); + } + if(get_use_timemory()) + { + roctracer_hsa_bundle_t _bundle{ *_name, _scope }; + _bundle.start() + .store(std::plus{}, static_cast(_end_ns - _begin_ns)) + .stop(); + } + }; + + std::unique_lock _lk{ tasking::get_roctracer_mutex() }; + tasking::get_roctracer_task_group().exec(_func); + + // timemory is disabled in this callback because collecting data in this thread + // causes strange segmentation faults + tim::consume_parameters(arg); +} + +void +hip_exec_activity_callbacks(int64_t _tid) +{ + // ROCTRACER_CALL(roctracer_flush_activity()); + tim::auto_lock_t _lk{ get_hip_activity_mutex(_tid) }; + auto& _async_ops = get_hip_activity_callbacks(_tid); + for(auto& itr : *_async_ops) + itr(); + _async_ops->clear(); +} + +namespace +{ +thread_local std::unordered_map gpu_cids = {}; +} + +// HIP API callback function +void +hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg) +{ + if(get_state() != State::Active || !trait::runtime_enabled::get()) + return; + + using Device = critical_trace::Device; + using Phase = critical_trace::Phase; + + const char* op_name = roctracer_op_string(domain, cid, 0); + if(op_name == nullptr) op_name = hip_api_name(cid); + if(op_name == nullptr) return; + + const hip_api_data_t* data = reinterpret_cast(callback_data); + HOSTTRACE_DEBUG("<%-30s id(%u)\tcorrelation_id(%lu) %s>\n", op_name, cid, + data->correlation_id, + (data->phase == ACTIVITY_API_PHASE_ENTER) ? "on-enter" : "on-exit"); + + switch(cid) + { + case HIP_API_ID___hipPushCallConfiguration: + case HIP_API_ID___hipPopCallConfiguration: + case HIP_API_ID_hipDeviceEnablePeerAccess: + case HIP_API_ID_hipImportExternalMemory: + case HIP_API_ID_hipDestroyExternalMemory: return; + default: break; + } + + if(data->phase == ACTIVITY_API_PHASE_ENTER) + { + switch(cid) + { + case HIP_API_ID_hipLaunchKernel: + case HIP_API_ID_hipLaunchCooperativeKernel: + { + const char* _name = + hipKernelNameRefByPtr(data->args.hipLaunchKernel.function_address, + data->args.hipLaunchKernel.stream); + if(_name != nullptr) + { + if(get_use_perfetto() || get_use_timemory()) + { + tim::auto_lock_t _lk{ tim::type_mutex() }; + get_roctracer_key_data().emplace(data->correlation_id, _name); + get_roctracer_tid_data().emplace(data->correlation_id, + threading::get_id()); + } + } + break; + } + case HIP_API_ID_hipModuleLaunchKernel: + { + const char* _name = hipKernelNameRef(data->args.hipModuleLaunchKernel.f); + if(_name != nullptr) + { + if(get_use_perfetto() || get_use_timemory()) + { + tim::auto_lock_t _lk{ tim::type_mutex() }; + get_roctracer_key_data().emplace(data->correlation_id, _name); + get_roctracer_tid_data().emplace(data->correlation_id, + threading::get_id()); + } + } + break; + } + default: + { + if(get_use_perfetto() || get_use_timemory()) + { + tim::auto_lock_t _lk{ tim::type_mutex() }; + get_roctracer_key_data().emplace(data->correlation_id, op_name); + get_roctracer_tid_data().emplace(data->correlation_id, + threading::get_id()); + } + } + } + + if(get_use_perfetto()) + { + TRACE_EVENT_BEGIN("device", perfetto::StaticString{ op_name }); + } + if(get_use_timemory()) + { + get_roctracer_hip_data()->emplace( + data->correlation_id, + roctracer_bundle_t{ op_name, quirk::config{} }); + } + if(get_use_critical_trace()) + { + auto _cid = get_cpu_cid()++; + uint16_t _depth = (get_cpu_cid_stack()->empty()) + ? get_cpu_cid_stack(0)->size() + : get_cpu_cid_stack()->size() - 1; + auto _parent_cid = (get_cpu_cid_stack()->empty()) + ? get_cpu_cid_stack(0)->back() + : get_cpu_cid_stack()->back(); + int64_t _ts = comp::wall_clock::record(); + add_critical_trace( + threading::get_id(), _cid, data->correlation_id, _parent_cid, _ts, 0, + critical_trace::add_hash_id(op_name), _depth); + tim::auto_lock_t _lk{ tim::type_mutex() }; + get_roctracer_cid_data().emplace(data->correlation_id, + cid_tuple_t{ _cid, _parent_cid, _depth }); + } + + hip_exec_activity_callbacks(threading::get_id()); + } + else if(data->phase == ACTIVITY_API_PHASE_EXIT) + { + hip_exec_activity_callbacks(threading::get_id()); + + if(get_use_perfetto()) + { + TRACE_EVENT_END("device"); + } + if(get_use_timemory()) + { + auto _stop = [data](int64_t _tid) { + auto& _data = get_roctracer_hip_data(_tid); + auto itr = _data->find(data->correlation_id); + if(itr != get_roctracer_hip_data()->end()) + { + itr->second.stop().pop(); + _data->erase(itr); + return true; + } + return false; + }; + if(!_stop(threading::get_id())) + { + for(size_t i = 0; i < max_supported_threads; ++i) + { + if(_stop(i)) break; + } + } + } + if(get_use_critical_trace()) + { + uint16_t _depth = 0; + uint64_t _cid = 0; + uint64_t _parent_cid = 0; + { + tim::auto_lock_t _lk{ tim::type_mutex() }; + std::tie(_cid, _parent_cid, _depth) = + get_roctracer_cid_data().at(data->correlation_id); + } + int64_t _ts = comp::wall_clock::record(); + add_critical_trace( + threading::get_id(), _cid, data->correlation_id, _parent_cid, _ts, _ts, + critical_trace::add_hash_id(op_name), _depth); + } + } + tim::consume_parameters(arg); +} + +// Activity tracing callback +void +hip_activity_callback(const char* begin, const char* end, void*) +{ + using Device = critical_trace::Device; + using Phase = critical_trace::Phase; + + if(!trait::runtime_enabled::get()) return; + static auto _kernel_names = std::unordered_map{}; + static auto _indexes = std::unordered_map{}; + const roctracer_record_t* record = reinterpret_cast(begin); + const roctracer_record_t* end_record = + reinterpret_cast(end); + + HOSTTRACE_DEBUG("Activity records:\n"); + + while(record < end_record) + { + const char* op_name = + roctracer_op_string(record->domain, record->correlation_id, 0); + if(op_name == nullptr) op_name = hip_api_name(record->correlation_id); + + if(op_name != nullptr) + { + HOSTTRACE_DEBUG("\t%-30s\tcorrelation_id(%6lu) time_ns(%12lu:%12lu) " + "delta_ns(%12lu) device_id(%d) " + "stream_id(%lu)\n", + op_name, record->correlation_id, record->begin_ns, + record->end_ns, (record->end_ns - record->begin_ns), + record->device_id, record->queue_id); + } + + auto _begin_ns = record->begin_ns; + auto _end_ns = record->end_ns; + auto _corr_id = record->correlation_id; + static auto _scope = []() { + auto _v = scope::config{}; + if(get_roctracer_timeline_profile()) _v += scope::timeline{}; + if(get_roctracer_flat_profile()) _v += scope::flat{}; + return _v; + }(); + + auto& _keys = get_roctracer_key_data(); + auto& _cids = get_roctracer_cid_data(); + auto& _tids = get_roctracer_tid_data(); + + int16_t _depth = 0; // depth of kernel launch + int64_t _tid = 0; // thread id + uint64_t _cid = 0; // correlation id + uint64_t _pcid = 0; // parent corr_id + auto _laps = _indexes[_corr_id]++; // see note #1 + const char* _name = nullptr; + bool _found = false; + bool _critical_trace = get_use_critical_trace(); + + { + tim::auto_lock_t _lk{ tim::type_mutex() }; + if(_tids.find(_corr_id) != _tids.end()) + { + _found = true; + _tid = _tids.at(_corr_id); + auto itr = _keys.find(_corr_id); + if(itr != _keys.end()) _name = itr->second; + } + } + + if(_critical_trace) + { + tim::auto_lock_t _lk{ tim::type_mutex() }; + if(_cids.find(_corr_id) != _cids.end()) + std::tie(_cid, _pcid, _depth) = _cids.at(_corr_id); + else + _critical_trace = false; + } + + auto _func = [_critical_trace, _depth, _tid, _cid, _laps, _begin_ns, _end_ns, + _corr_id, _name]() { + // NOTE #1: we get two measurements for 1 kernel so we need to + // tweak the number of laps for the wall-clock component + if(_name != nullptr) + { + if(get_use_perfetto()) + { + if(_kernel_names.find(_name) == _kernel_names.end()) + _kernel_names.emplace(_name, tim::demangle(_name)); + TRACE_EVENT_BEGIN( + "device", + perfetto::StaticString{ _kernel_names.at(_name).c_str() }, + _begin_ns); + TRACE_EVENT_END("device", _end_ns); + } + if(get_use_timemory()) + { + roctracer_bundle_t _bundle{ _name, _scope }; + _bundle.start() + .store(std::plus{}, + static_cast(_end_ns - _begin_ns)) + .stop() + .get([&](comp::wall_clock* wc) { + wc->set_value(_end_ns - _begin_ns); + wc->set_accum(_end_ns - _begin_ns); + if(_laps % 2 == 1) + { + // below is a hack bc we get two measurements for 1 kernel + wc->set_laps(0); + + auto itr = wc->get_iterator(); + if(itr && itr->data().get_laps() == 0) + { + wc->set_is_invalid(true); + itr->data().set_is_invalid(true); + } + } + return wc; + }); + _bundle.pop(); + } + if(_critical_trace) + { + auto _hash = critical_trace::add_hash_id(_name); + uint16_t _prio = _laps + 1; // priority + add_critical_trace( + _tid, _cid, _corr_id, _cid, _begin_ns, _end_ns, _hash, _depth + 1, + _prio); + } + } + }; + + if(_found) + { + auto& _async_ops = get_hip_activity_callbacks(_tid); + tim::auto_lock_t _lk{ get_hip_activity_mutex(_tid) }; + _async_ops->emplace_back(std::move(_func)); + } + + ROCTRACER_CALL(roctracer_next_record(record, &record)); + } +} + +bool& +roctracer_is_setup() +{ + static bool _v = false; + return _v; +} + +using roctracer_functions_t = std::vector>>; + +roctracer_functions_t& +roctracer_setup_routines() +{ + static auto _v = roctracer_functions_t{}; + return _v; +} + +roctracer_functions_t& +roctracer_tear_down_routines() +{ + static auto _v = roctracer_functions_t{}; + return _v; +} diff --git a/projects/rocprofiler-systems/src/library/thread_data.cpp b/projects/rocprofiler-systems/src/library/thread_data.cpp new file mode 100644 index 0000000000..a582ef460d --- /dev/null +++ b/projects/rocprofiler-systems/src/library/thread_data.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/thread_data.hpp" + +instrumentation_bundles::instance_array_t& +instrumentation_bundles::instances() +{ + static auto _v = instance_array_t{}; + return _v; +} diff --git a/projects/rocprofiler-systems/src/library/timemory.cpp b/projects/rocprofiler-systems/src/library/timemory.cpp new file mode 100644 index 0000000000..120dc0e915 --- /dev/null +++ b/projects/rocprofiler-systems/src/library/timemory.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2018 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 +// with 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: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the names of Advanced Micro Devices, Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// 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 +// CONTRIBUTORS 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 WITH +// THE SOFTWARE. + +#include "library/timemory.hpp" + +TIMEMORY_INITIALIZE_STORAGE(comp::wall_clock, comp::user_global_bundle) diff --git a/projects/rocprofiler-systems/src/roctracer.cpp b/projects/rocprofiler-systems/src/roctracer.cpp deleted file mode 100644 index 3908d326f0..0000000000 --- a/projects/rocprofiler-systems/src/roctracer.cpp +++ /dev/null @@ -1,645 +0,0 @@ - -#include "roctracer.hpp" -#include "library.hpp" - -#include -#include -#include -#include -#include - -#define AMD_INTERNAL_BUILD 1 -#include -#include - -#include - -// Macro to check ROC-tracer calls status -#define ROCTRACER_CALL(call) \ - do \ - { \ - int err = call; \ - if(err != 0) \ - { \ - std::cerr << roctracer_error_string() << " in: " << #call << std::flush; \ - } \ - } while(0) - -using roctracer_bundle_t = tim::component_tuple; - -namespace units = tim::units; - -namespace -{ -auto& -get_roctracer_kernels() -{ - static auto _v = std::unordered_set{}; - return _v; -} - -auto& -get_roctracer_hip_data() -{ - static auto _v = std::unordered_map{}; - return _v; -} - -auto& -get_roctracer_key_data() -{ - static auto _v = std::unordered_map{}; - return _v; -} - -using data_type_mutex_t = std::decay_t; -using hsa_timer_t = hsa_rt_utils::Timer; -using timestamp_t = hsa_timer_t::timestamp_t; - -auto& -get_hsa_timer() -{ - static auto _v = std::unique_ptr{}; - return _v; -} - -// HSA API callback function -void -hsa_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg) -{ - if(get_state() != State::Active || !trait::runtime_enabled::get()) - return; - - (void) arg; - static auto _scope = scope::config{} + scope::flat{}; - const hsa_api_data_t* data = reinterpret_cast(callback_data); - HOSTTRACE_DEBUG("<%-30s id(%u)\tcorrelation_id(%lu) %s>\n", - roctracer_op_string(domain, cid, 0), cid, data->correlation_id, - (data->phase == ACTIVITY_API_PHASE_ENTER) ? "on-enter" : "on-exit"); - - static thread_local timestamp_t hsa_begin_timestamp = 0; - static auto& timer = get_hsa_timer(); - - if(!timer) return; - - switch(cid) - { - case HSA_API_ID_hsa_init: - case HSA_API_ID_hsa_shut_down: - case HSA_API_ID_hsa_agent_get_exception_policies: - case HSA_API_ID_hsa_agent_get_info: - case HSA_API_ID_hsa_amd_agent_iterate_memory_pools: - case HSA_API_ID_hsa_amd_agent_memory_pool_get_info: - case HSA_API_ID_hsa_amd_coherency_get_type: - case HSA_API_ID_hsa_amd_memory_pool_get_info: - case HSA_API_ID_hsa_amd_pointer_info: - case HSA_API_ID_hsa_amd_pointer_info_set_userdata: - case HSA_API_ID_hsa_amd_profiling_async_copy_enable: - case HSA_API_ID_hsa_amd_profiling_get_async_copy_time: - case HSA_API_ID_hsa_amd_profiling_get_dispatch_time: - case HSA_API_ID_hsa_amd_profiling_set_profiler_enabled: - case HSA_API_ID_hsa_cache_get_info: - case HSA_API_ID_hsa_code_object_get_info: - case HSA_API_ID_hsa_code_object_get_symbol: - case HSA_API_ID_hsa_code_object_get_symbol_from_name: - case HSA_API_ID_hsa_code_object_reader_create_from_memory: - case HSA_API_ID_hsa_code_symbol_get_info: - case HSA_API_ID_hsa_executable_create_alt: - case HSA_API_ID_hsa_executable_freeze: - case HSA_API_ID_hsa_executable_get_info: - case HSA_API_ID_hsa_executable_get_symbol: - case HSA_API_ID_hsa_executable_get_symbol_by_name: - case HSA_API_ID_hsa_executable_symbol_get_info: - case HSA_API_ID_hsa_extension_get_name: - case HSA_API_ID_hsa_ext_image_data_get_info: - case HSA_API_ID_hsa_ext_image_data_get_info_with_layout: - case HSA_API_ID_hsa_ext_image_get_capability: - case HSA_API_ID_hsa_ext_image_get_capability_with_layout: - case HSA_API_ID_hsa_isa_get_exception_policies: - case HSA_API_ID_hsa_isa_get_info: - case HSA_API_ID_hsa_isa_get_info_alt: - case HSA_API_ID_hsa_isa_get_round_method: - case HSA_API_ID_hsa_region_get_info: - case HSA_API_ID_hsa_system_extension_supported: - case HSA_API_ID_hsa_system_get_extension_table: - case HSA_API_ID_hsa_system_get_info: - case HSA_API_ID_hsa_system_get_major_extension_table: - case HSA_API_ID_hsa_wavefront_get_info: break; - default: - { - if(data->phase == ACTIVITY_API_PHASE_ENTER) - { - hsa_begin_timestamp = timer->timestamp_fn_ns(); - } - else - { - auto _name = roctracer_op_string(domain, cid, 0); - const timestamp_t end_timestamp = (cid == HSA_API_ID_hsa_shut_down) - ? hsa_begin_timestamp - : timer->timestamp_fn_ns(); - - if(get_use_perfetto()) - { - TRACE_EVENT_BEGIN("device", perfetto::StaticString{ _name }, - hsa_begin_timestamp); - TRACE_EVENT_END("device", end_timestamp); - } - - /*if(get_use_timemory()) - { - static auto _scope = scope::flat() + scope::timeline(); - roctracer_bundle_t{ _name, _scope } - .start() - .store(end_timestamp - hsa_begin_timestamp) - .stop(); - }*/ - // timemory is disabled in this callback because collecting data in this - // thread causes strange segmentation faults - } - } - } -} - -void -hsa_activity_callback(uint32_t op, activity_record_t* record, void* arg) -{ - static const char* copy_op_name = "hsa_async_copy"; - static const char* dispatch_op_name = "hsa_dispatch"; - static const char* barrier_op_name = "hsa_barrier"; - const char** _name = nullptr; - - switch(op) - { - case HSA_OP_ID_DISPATCH: _name = &dispatch_op_name; break; - case HSA_OP_ID_COPY: _name = ©_op_name; break; - case HSA_OP_ID_BARRIER: _name = &barrier_op_name; break; - default: break; - } - - if(!_name) return; - - if(get_use_perfetto()) - { - TRACE_EVENT_BEGIN("device", perfetto::StaticString{ *_name }, record->begin_ns); - TRACE_EVENT_END("device", record->end_ns); - } - - // timemory is disabled in this callback because collecting data in this thread - // causes strange segmentation faults - tim::consume_parameters(arg); -} - -// HIP API callback function -void -hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg) -{ - if(get_state() != State::Active || !trait::runtime_enabled::get()) - return; - - static auto _scope = scope::flat() + scope::timeline(); - const hip_api_data_t* data = reinterpret_cast(callback_data); - HOSTTRACE_DEBUG("<%-30s id(%u)\tcorrelation_id(%lu) %s>\n", - roctracer_op_string(domain, cid, 0), cid, data->correlation_id, - (data->phase == ACTIVITY_API_PHASE_ENTER) ? "on-enter" : "on-exit"); - - if(data->phase == ACTIVITY_API_PHASE_ENTER) - { - switch(cid) - { - case HIP_API_ID___hipPushCallConfiguration: - case HIP_API_ID___hipPopCallConfiguration: break; - case HIP_API_ID_hipLaunchKernel: - { - const char* _name = - hipKernelNameRefByPtr(data->args.hipLaunchKernel.function_address, - data->args.hipLaunchKernel.stream); - tim::auto_lock_t _lk{ tim::type_mutex() }; - get_roctracer_kernels().emplace(data->correlation_id); - if(get_use_perfetto()) - { - get_roctracer_key_data().emplace(data->correlation_id, _name); - } - if(get_use_timemory()) - { - get_roctracer_hip_data().emplace(data->correlation_id, - roctracer_bundle_t{ _name, _scope }); - } - break; - } - case HIP_API_ID_hipModuleLaunchKernel: - { - const char* _name = hipKernelNameRef(data->args.hipModuleLaunchKernel.f); - tim::auto_lock_t _lk{ tim::type_mutex() }; - get_roctracer_kernels().emplace(data->correlation_id); - if(get_use_perfetto()) - { - get_roctracer_key_data().emplace(data->correlation_id, _name); - } - if(get_use_timemory()) - { - get_roctracer_hip_data().emplace(data->correlation_id, - roctracer_bundle_t{ _name, _scope }); - } - break; - } - default: - { - tim::auto_lock_t _lk{ tim::type_mutex() }; - const char* _name = roctracer_op_string(domain, cid, 0); - if(get_use_perfetto()) - { - get_roctracer_key_data().emplace(data->correlation_id, _name); - } - if(get_use_timemory()) - { - get_roctracer_hip_data().emplace(data->correlation_id, - roctracer_bundle_t{ _name, _scope }); - } - break; - } - } - } - else if(data->phase == ACTIVITY_API_PHASE_EXIT) - {} - tim::consume_parameters(domain, arg); -} - -// Activity tracing callback -void -hip_activity_callback(const char* begin, const char* end, void*) -{ - if(!trait::runtime_enabled::get()) return; - static auto _kernel_names = std::unordered_map{}; - const roctracer_record_t* record = reinterpret_cast(begin); - const roctracer_record_t* end_record = - reinterpret_cast(end); - std::unordered_set _indexes{}; - - tim::auto_lock_t _lk{ tim::type_mutex() }; - auto& _data = get_roctracer_hip_data(); - auto& _keys = get_roctracer_key_data(); - auto& _kernels = get_roctracer_kernels(); - - HOSTTRACE_DEBUG("Activity records:\n"); - while(record < end_record) - { - HOSTTRACE_DEBUG("\t%-30s\tcorrelation_id(%lu) time_ns(%lu:%lu) device_id(%d) " - "stream_id(%lu)\n", - roctracer_op_string(record->domain, record->correlation_id, 0), - record->correlation_id, record->begin_ns, record->end_ns, - record->device_id, record->queue_id); - - auto _is_kernel = _kernels.find(record->correlation_id) != _kernels.end(); - if(_is_kernel && record->device_id != 0 && record->queue_id != 0) - { - // these are overheads associated with the kernel launch, not kernel runtime - ROCTRACER_CALL(roctracer_next_record(record, &record)); - continue; - } - - auto kitr = - (get_use_perfetto()) ? _keys.find(record->correlation_id) : _keys.end(); - if(kitr != _keys.end()) - { - if(_kernel_names.find(kitr->second) == _kernel_names.end()) - _kernel_names.emplace(kitr->second, tim::demangle(kitr->second)); - TRACE_EVENT_BEGIN( - "device", - perfetto::StaticString{ _kernel_names.at(kitr->second).c_str() }, - record->begin_ns); - TRACE_EVENT_END("device", record->end_ns); - _indexes.emplace(kitr->first); - } - - auto itr = - (get_use_timemory()) ? _data.find(record->correlation_id) : _data.end(); - if(itr != _data.end()) - { - itr->second.start() - .store(std::plus{}, - static_cast(record->end_ns - record->begin_ns)) - .stop(); - _indexes.emplace(itr->first); - } - // code - ROCTRACER_CALL(roctracer_next_record(record, &record)); - } - - if(get_use_perfetto()) - { - for(auto& itr : _indexes) - _keys.erase(itr); - } - - if(get_use_timemory()) - { - for(auto& itr : _indexes) - _data.erase(itr); - } - - HOSTTRACE_DEBUG("[%s] recorded %lu phases\n", __FUNCTION__, - (unsigned long) _indexes.size()); -} - -bool& -roctracer_is_setup() -{ - static bool _v = false; - return _v; -} - -using roctracer_functions_t = std::vector>>; - -auto& -roctracer_setup_routines() -{ - static auto _v = roctracer_functions_t{}; - return _v; -} - -auto& -roctracer_tear_down_routines() -{ - static auto _v = roctracer_functions_t{}; - return _v; -} -} // namespace - -#if !defined(HOSTTRACE_ROCTRACER_LIBKFDWRAPPER) -# define HOSTTRACE_ROCTRACER_LIBKFDWRAPPER "/opt/rocm/roctracer/lib/libkfdwrapper64.so" -#endif - -struct dynamic_library -{ - dynamic_library() = delete; - dynamic_library(const dynamic_library&) = delete; - dynamic_library(dynamic_library&&) noexcept = default; - dynamic_library& operator=(const dynamic_library&) = delete; - dynamic_library& operator=(dynamic_library&&) noexcept = default; - - dynamic_library(const char* _env, const char* _fname, - int _flags = (RTLD_NOW | RTLD_GLOBAL), bool _store = false) - : envname{ _env } - , filename{ tim::get_env(_env, _fname, _store) } - , flags{ _flags } - { - handle = dlopen(filename.c_str(), flags); - if(!handle) fprintf(stderr, "%s\n", dlerror()); - dlerror(); // Clear any existing error - } - - ~dynamic_library() - { - if(handle) dlclose(handle); - } - - std::string envname = {}; - std::string filename = {}; - int flags = 0; - void* handle = nullptr; -}; - -namespace tim -{ -namespace component -{ -void -roctracer::preinit() -{ - HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); - roctracer_data::label() = "roctracer"; - roctracer_data::description() = "ROCm tracer (activity API)"; -} - -bool -roctracer::is_setup() -{ - return roctracer_is_setup(); -} - -void -roctracer::add_setup(const std::string& _lbl, std::function&& _func) -{ - roctracer_setup_routines().emplace_back(_lbl, std::move(_func)); -} - -void -roctracer::add_tear_down(const std::string& _lbl, std::function&& _func) -{ - roctracer_tear_down_routines().emplace_back(_lbl, std::move(_func)); -} - -void -roctracer::remove_setup(const std::string& _lbl) -{ - auto& _data = roctracer_setup_routines(); - for(auto itr = _data.begin(); itr != _data.end(); ++itr) - { - if(itr->first == _lbl) - { - _data.erase(itr); - break; - } - } -} - -void -roctracer::remove_tear_down(const std::string& _lbl) -{ - auto& _data = roctracer_setup_routines(); - for(auto itr = _data.begin(); itr != _data.end(); ++itr) - { - if(itr->first == _lbl) - { - _data.erase(itr); - break; - } - } -} - -void -roctracer::setup() -{ - if(!get_use_timemory() && !get_use_perfetto()) return; - - auto_lock_t _lk{ type_mutex() }; - if(roctracer_is_setup()) return; - roctracer_is_setup() = true; - HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); - - tim::set_env("HSA_TOOLS_LIB", "libhosttrace.so", 0); - - auto _kfdwrapper = dynamic_library{ "HOSTTRACE_ROCTRACER_LIBKFDWRAPPER", - HOSTTRACE_ROCTRACER_LIBKFDWRAPPER }; - - ROCTRACER_CALL(roctracer_set_properties(ACTIVITY_DOMAIN_HIP_API, nullptr)); - - if(roctracer_default_pool() == nullptr) - { - // Allocating tracing pool - roctracer_properties_t properties{}; - properties.buffer_size = 0x1000; - properties.buffer_callback_fun = hip_activity_callback; - ROCTRACER_CALL(roctracer_open_pool(&properties)); - } - - // Enable API callbacks, all domains - ROCTRACER_CALL(roctracer_enable_callback(hip_api_callback, nullptr)); - // Enable activity tracing, all domains - ROCTRACER_CALL(roctracer_enable_activity()); - - // callback for HSA - for(auto& itr : roctracer_setup_routines()) - itr.second(); -} - -void -roctracer::tear_down() -{ - auto_lock_t _lk{ type_mutex() }; - if(!roctracer_is_setup()) return; - roctracer_is_setup() = false; - HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); - - // flush all the activity - if(roctracer_default_pool() != nullptr) - { - ROCTRACER_CALL(roctracer_flush_activity()); - } - // flush all buffers - roctracer_flush_buf(); - - // callback for hsa - for(auto& itr : roctracer_tear_down_routines()) - itr.second(); - - // Disable tracing and closing the pool - ROCTRACER_CALL(roctracer_disable_callback()); - ROCTRACER_CALL(roctracer_disable_activity()); - ROCTRACER_CALL(roctracer_close_pool()); -} - -void -roctracer::start() -{ - if(tracker_type::start() == 0) setup(); -} - -void -roctracer::stop() -{ - if(tracker_type::stop() == 0) tear_down(); -} -} // namespace component -} // namespace tim - -TIMEMORY_INSTANTIATE_EXTERN_COMPONENT(roctracer, false, void) -TIMEMORY_INSTANTIATE_EXTERN_COMPONENT(roctracer_data, true, double) - -// HSA-runtime tool on-load method -extern "C" -{ - bool OnLoad(HsaApiTable* table, uint64_t runtime_version, uint64_t failed_tool_count, - const char* const* failed_tool_names) TIMEMORY_VISIBILITY("default"); - void OnUnload() TIMEMORY_VISIBILITY("default"); - - bool OnLoad(HsaApiTable* table, uint64_t runtime_version, uint64_t failed_tool_count, - const char* const* failed_tool_names) - { - HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); - tim::consume_parameters(table, runtime_version, failed_tool_count, - failed_tool_names); - - // ONLOAD_TRACE_BEG(); - // on_exit(exit_handler, nullptr); - - auto _setup = [=]() { - get_hsa_timer() = - std::make_unique(table->core_->hsa_system_get_info_fn); - - // const char* output_prefix = getenv("ROCP_OUTPUT_DIR"); - const char* output_prefix = nullptr; - - // App begin timestamp begin_ts_file.txt - // begin_ts_file_handle = open_output_file(output_prefix, - // "begin_ts_file.txt"); const timestamp_t app_start_time = - // timer->timestamp_fn_ns(); fprintf(begin_ts_file_handle, "%lu\n", - // app_start_time); - - bool trace_hsa_api = tim::get_env("HOSTTRACE_ROCTRACER_HSA_API", true); - std::vector hsa_api_vec = tim::delimit( - tim::get_env("HOSTTRACE_ROCTRACER_HSA_API_TYPES", "")); - - // Enable HSA API callbacks/activity - if(trace_hsa_api) - { - // hsa_api_file_handle = open_output_file(output_prefix, - // "hsa_api_trace.txt"); - - // initialize HSA tracing - roctracer_set_properties(ACTIVITY_DOMAIN_HSA_API, (void*) table); - - HOSTTRACE_DEBUG(" HSA-trace("); - if(!hsa_api_vec.empty()) - { - for(unsigned i = 0; i < hsa_api_vec.size(); ++i) - { - uint32_t cid = HSA_API_ID_NUMBER; - const char* api = hsa_api_vec[i].c_str(); - ROCTRACER_CALL(roctracer_op_code(ACTIVITY_DOMAIN_HSA_API, api, - &cid, nullptr)); - ROCTRACER_CALL(roctracer_enable_op_callback( - ACTIVITY_DOMAIN_HSA_API, cid, hsa_api_callback, nullptr)); - - HOSTTRACE_DEBUG(" %s", api); - } - } - else - { - ROCTRACER_CALL(roctracer_enable_domain_callback( - ACTIVITY_DOMAIN_HSA_API, hsa_api_callback, nullptr)); - } - HOSTTRACE_DEBUG("\n"); - } - - bool trace_hsa_activity = - tim::get_env("HOSTTRACE_ROCTRACER_HSA_ACTIVITY", true); - // Enable HSA GPU activity - if(trace_hsa_activity) - { - // initialize HSA tracing - roctracer::hsa_ops_properties_t ops_properties{ - table, - reinterpret_cast(hsa_activity_callback), - nullptr, output_prefix - }; - roctracer_set_properties(ACTIVITY_DOMAIN_HSA_OPS, &ops_properties); - - HOSTTRACE_DEBUG(" HSA-activity-trace()\n"); - ROCTRACER_CALL(roctracer_enable_op_activity(ACTIVITY_DOMAIN_HSA_OPS, - HSA_OP_ID_COPY)); - } - }; - - auto _tear_down = []() { - ROCTRACER_CALL(roctracer_disable_domain_callback(ACTIVITY_DOMAIN_HSA_API)); - - ROCTRACER_CALL( - roctracer_disable_op_activity(ACTIVITY_DOMAIN_HSA_OPS, HSA_OP_ID_COPY)); - }; - - if(comp::roctracer::is_setup()) _setup(); - - comp::roctracer::add_setup("hsa", std::move(_setup)); - comp::roctracer::add_tear_down("hsa", std::move(_tear_down)); - - return true; - } - - // HSA-runtime on-unload method - void OnUnload() - { - HOSTTRACE_DEBUG("[%s]\n", __FUNCTION__); - // ONLOAD_TRACE(""); - } -} diff --git a/projects/rocprofiler-systems/tests/CMakeLists.txt b/projects/rocprofiler-systems/tests/CMakeLists.txt index 9a9effde7a..e80334d3e3 100644 --- a/projects/rocprofiler-systems/tests/CMakeLists.txt +++ b/projects/rocprofiler-systems/tests/CMakeLists.txt @@ -3,6 +3,10 @@ if(NOT HOSTTRACE_DYNINST_API_RT_DIR AND HOSTTRACE_DYNINST_API_RT) DIRECTORY) endif() +if(NOT DEFINED NUM_PROCS) + set(NUM_PROCS 2) +endif() + if(HOSTTRACE_BUILD_DYNINST) set(HOSTTRACE_DYNINST_API_RT_DIR "${PROJECT_BINARY_DIR}/external/dyninst/dyninstAPI_RT:${PROJECT_BINARY_DIR}/external/dyninst/dyninstAPI" @@ -16,8 +20,8 @@ set(_test_environment ) if(TARGET transpose) - if(TRANSPOSE_USE_MPI) - set(COMMAND_PREFIX ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 2) + if(TRANSPOSE_USE_MPI AND NUM_PROCS GREATER 0) + set(COMMAND_PREFIX ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${NUM_PROCS}) endif() add_test(