diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 172844894b..34bc0a598f 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -216,7 +216,7 @@ jobs: rm -rf elfutils* - name: Install Dyninst - timeout-minutes: 20 + timeout-minutes: 25 run: cmake --version && git submodule update --init external/dyninst && @@ -310,7 +310,7 @@ jobs: echo "LD_LIBRARY_PATH=/opt/omnitrace/lib:/opt/dyninst/lib:${LD_LIBRARY_PATH}" >> $GITHUB_ENV - name: Install Dyninst - timeout-minutes: 20 + timeout-minutes: 25 run: cmake --version && git submodule update --init external/dyninst && @@ -429,7 +429,7 @@ jobs: rm -rf elfutils* - name: Install Dyninst - timeout-minutes: 20 + timeout-minutes: 25 run: cmake --version && git submodule update --init external/dyninst && diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d113a494b..bef032d652 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,186 +130,22 @@ else() set(HIP_VERSION_PATCH 0) endif() -configure_file(${PROJECT_SOURCE_DIR}/include/library/defines.hpp.in - ${PROJECT_BINARY_DIR}/include/library/defines.hpp @ONLY) - omnitrace_activate_clang_tidy() # custom visibility settings if(OMNITRACE_BUILD_HIDDEN_VISIBILITY) - set(CMAKE_C_VISIBILITY_PRESET "hidden") - set(CMAKE_CXX_VISIBILITY_PRESET "hidden") + set(CMAKE_C_VISIBILITY_PRESET "internal") + set(CMAKE_CXX_VISIBILITY_PRESET "internal") set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) endif() -if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF) -endif() - -if(OMNITRACE_BUILD_LTO) - omnitrace_save_variables(LTO VARIABLES CMAKE_INTERPROCEDURAL_OPTIMIZATION) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) -endif() - # ------------------------------------------------------------------------------# # -# omnitrace-library target +# library and executables # # ------------------------------------------------------------------------------# -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/kokkosp.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/gpu.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/perfetto.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/ptl.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/sampling.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/state.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/thread_data.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/timemory.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/components/backtrace.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/components/fork_gotcha.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/components/mpi_gotcha.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/components/omnitrace.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/components/pthread_gotcha.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/gpu.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/perfetto.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/ptl.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/sampling.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 - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/fwd.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/backtrace.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/fork_gotcha.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/mpi_gotcha.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/omnitrace.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/rocm_smi.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/roctracer.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/roctracer_callbacks.hpp - ${CMAKE_CURRENT_LIST_DIR}/include/library/components/pthread_gotcha.hpp - ${perfetto_DIR}/sdk/perfetto.h) - -add_library(omnitrace-library SHARED ${library_sources} ${library_headers}) - -if(OMNITRACE_USE_ROCTRACER) - target_sources( - omnitrace-library - PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/library/components/roctracer.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/library/components/roctracer_callbacks.cpp) -endif() - -if(OMNITRACE_USE_ROCM_SMI) - target_sources(omnitrace-library - PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/library/components/rocm_smi.cpp) -endif() - -target_include_directories(omnitrace-library SYSTEM PRIVATE ${perfetto_DIR}/sdk) - -target_compile_definitions( - omnitrace-library - PRIVATE OMNITRACE_MAX_THREADS=${OMNITRACE_MAX_THREADS} - $,CUSTOM_DATA_SOURCE,>) - -target_link_libraries( - omnitrace-library - PUBLIC $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $,omnitrace::omnitrace-sanitizer,>) - -if(OMNITRACE_DYNINST_API_RT) - get_filename_component(OMNITRACE_DYNINST_API_RT_DIR "${OMNITRACE_DYNINST_API_RT}" - DIRECTORY) -endif() - -set_target_properties( - omnitrace-library - PROPERTIES OUTPUT_NAME omnitrace - INSTALL_RPATH - "\$ORIGIN:\$ORIGIN/timemory/libunwind:\$ORIGIN/dyninst-tpls/libs") - -install( - TARGETS omnitrace-library - DESTINATION ${CMAKE_INSTALL_LIBDIR} - OPTIONAL) - -# ------------------------------------------------------------------------------# -# -# omnitrace-avail target -# -# ------------------------------------------------------------------------------# - -add_executable(omnitrace-avail ${CMAKE_CURRENT_LIST_DIR}/src/avail.cpp - ${CMAKE_CURRENT_LIST_DIR}/include/avail.hpp) - -target_include_directories(omnitrace-avail PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include) -target_compile_definitions(omnitrace-avail PRIVATE OMNITRACE_EXTERN_COMPONENTS=0) -target_link_libraries(omnitrace-avail PRIVATE omnitrace-library) -set_target_properties(omnitrace-avail PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON) - -install( - TARGETS omnitrace-avail - DESTINATION bin - OPTIONAL) - -# ------------------------------------------------------------------------------# -# -# omnitrace-exe target -# -# ------------------------------------------------------------------------------# - -add_executable( - omnitrace-exe - ${_EXCLUDE} ${CMAKE_CURRENT_LIST_DIR}/src/omnitrace.cpp - ${CMAKE_CURRENT_LIST_DIR}/include/omnitrace.hpp - ${CMAKE_CURRENT_LIST_DIR}/src/omnitrace/details.cpp) - -target_link_libraries( - omnitrace-exe - PRIVATE omnitrace::omnitrace-headers - omnitrace::omnitrace-dyninst - omnitrace::omnitrace-compile-options - $ - $,omnitrace::omnitrace-sanitizer,>) - -set_target_properties( - omnitrace-exe - PROPERTIES - OUTPUT_NAME omnitrace - INSTALL_RPATH_USE_LINK_PATH ON - INSTALL_RPATH - "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/timemory/libunwind:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/dyninst-tpls/lib" - ) - -if(CMAKE_BUILD_TYPE MATCHES "^(DEBUG|Debug)") - string(REPLACE " " ";" _FLAGS "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - target_compile_options(omnitrace-exe PRIVATE ${_FLAGS}) -endif() - -install( - TARGETS omnitrace-exe - DESTINATION ${CMAKE_INSTALL_BINDIR} - OPTIONAL) +add_subdirectory(source) # ------------------------------------------------------------------------------# # diff --git a/cmake/Formatting.cmake b/cmake/Formatting.cmake index 444764d109..31a5a6790d 100644 --- a/cmake/Formatting.cmake +++ b/cmake/Formatting.cmake @@ -41,9 +41,9 @@ find_program(OMNITRACE_CLANG_FORMAT_EXE NAMES clang-format-11 clang-format-mp-11 clang-format) if(OMNITRACE_CLANG_FORMAT_EXE) - file(GLOB_RECURSE sources ${PROJECT_SOURCE_DIR}/src/*.cpp) - file(GLOB_RECURSE headers ${PROJECT_SOURCE_DIR}/include/*.hpp - ${PROJECT_SOURCE_DIR}/include/*.hpp.in) + file(GLOB_RECURSE sources ${PROJECT_SOURCE_DIR}/source/*.cpp) + file(GLOB_RECURSE headers ${PROJECT_SOURCE_DIR}/source/*.hpp + ${PROJECT_SOURCE_DIR}/source/*.hpp.in) file(GLOB_RECURSE examples ${PROJECT_SOURCE_DIR}/examples/*.cpp ${PROJECT_SOURCE_DIR}/examples/*.hpp) file(GLOB_RECURSE external ${PROJECT_SOURCE_DIR}/examples/lulesh/external/*.cpp diff --git a/examples/lulesh/cmake/Modules/Utilities.cmake b/examples/lulesh/cmake/Modules/Utilities.cmake index 3a7f55da23..383c9b9e59 100644 --- a/examples/lulesh/cmake/Modules/Utilities.cmake +++ b/examples/lulesh/cmake/Modules/Utilities.cmake @@ -8,9 +8,8 @@ include(CMakeDependentOption) include(CMakeParseArguments) # ----------------------------------------------------------------------- -# function - capitalize - make a string capitalized (first letter is capital) usage: -# capitalize("SHARED" CShared) message(STATUS "-- CShared is \"${CShared}\"") $ -- CShared -# is "Shared" +# function - capitalize - make a string capitalized (first letter is capital) +# function(CAPITALIZE str var) # make string lower string(TOLOWER "${str}" str) @@ -24,7 +23,7 @@ function(CAPITALIZE str var) endfunction() # ----------------------------------------------------------------------------------------# -# macro CHECKOUT_GIT_SUBMODULE() +# function CHECKOUT_GIT_SUBMODULE() # # Run "git submodule update" if a file in a submodule does not exist # @@ -45,7 +44,7 @@ function(CHECKOUT_GIT_SUBMODULE) endif() if(NOT CHECKOUT_TEST_FILE) - set(CHECKOUT_TEST_FILE "Makefile") + set(CHECKOUT_TEST_FILE "CMakeLists.txt") endif() # default assumption @@ -156,160 +155,3 @@ function(CHECKOUT_GIT_SUBMODULE) endif() 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() - -# ----------------------------------------------------------------------- -# function add_feature( ) Add a project feature, whose activation is -# specified by the existence of the variable , to the list of enabled/disabled -# features, plus a docstring describing the feature -# -function(ADD_FEATURE _var _description) - set(EXTRA_DESC "") - foreach(currentArg ${ARGN}) - if(NOT "${currentArg}" STREQUAL "${_var}" AND NOT "${currentArg}" STREQUAL - "${_description}") - set(EXTRA_DESC "${EXTA_DESC}${currentArg}") - endif() - endforeach() - - set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_FEATURES ${_var}) - set_property(GLOBAL PROPERTY ${_var}_DESCRIPTION "${_description}${EXTRA_DESC}") - - if("CMAKE_DEFINE" IN_LIST ARGN) - set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_CMAKE_DEFINES - "${_var} @${_var}@") - endif() -endfunction() - -# ----------------------------------------------------------------------------------------# -# function add_option( [NO_FEATURE]) Add an -# option and add as a feature if NO_FEATURE is not provided -# -function(ADD_OPTION _NAME _MESSAGE _DEFAULT) - option(${_NAME} "${_MESSAGE}" ${_DEFAULT}) - if("NO_FEATURE" IN_LIST ARGN) - mark_as_advanced(${_NAME}) - else() - add_feature(${_NAME} "${_MESSAGE}") - endif() - if("ADVANCED" IN_LIST ARGN) - mark_as_advanced(${_NAME}) - endif() -endfunction() - -# ----------------------------------------------------------------------------------------# -# function print_enabled_features() Print enabled features plus their docstrings. -# -function(PRINT_ENABLED_FEATURES) - set(_basemsg "The following features are defined/enabled (+):") - set(_currentFeatureText "${_basemsg}") - get_property(_features GLOBAL PROPERTY ${PROJECT_NAME}_FEATURES) - if(NOT "${_features}" STREQUAL "") - list(REMOVE_DUPLICATES _features) - list(SORT _features) - endif() - foreach(_feature ${_features}) - if(${_feature}) - # add feature to text - set(_currentFeatureText "${_currentFeatureText}\n ${_feature}") - # get description - get_property(_desc GLOBAL PROPERTY ${_feature}_DESCRIPTION) - # print description, if not standard ON/OFF, print what is set to - if(_desc) - if(NOT "${${_feature}}" STREQUAL "ON" AND NOT "${${_feature}}" STREQUAL - "TRUE") - set(_currentFeatureText - "${_currentFeatureText}: ${_desc} -- [\"${${_feature}}\"]") - else() - string(REGEX REPLACE "^${PROJECT_NAME}_USE_" "" _feature_tmp - "${_feature}") - string(TOLOWER "${_feature_tmp}" _feature_tmp_l) - capitalize("${_feature_tmp}" _feature_tmp_c) - foreach(_var _feature _feature_tmp _feature_tmp_l _feature_tmp_c) - set(_ver "${${${_var}}_VERSION}") - if(NOT "${_ver}" STREQUAL "") - set(_desc "${_desc} -- [found version ${_ver}]") - break() - endif() - unset(_ver) - endforeach() - set(_currentFeatureText "${_currentFeatureText}: ${_desc}") - endif() - set(_desc NOTFOUND) - endif() - endif() - endforeach() - - if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}") - message(STATUS "${_currentFeatureText}\n") - endif() -endfunction() - -# ----------------------------------------------------------------------------------------# -# function print_disabled_features() Print disabled features plus their docstrings. -# -function(PRINT_DISABLED_FEATURES) - set(_basemsg "The following features are NOT defined/enabled (-):") - set(_currentFeatureText "${_basemsg}") - get_property(_features GLOBAL PROPERTY ${PROJECT_NAME}_FEATURES) - if(NOT "${_features}" STREQUAL "") - list(REMOVE_DUPLICATES _features) - list(SORT _features) - endif() - foreach(_feature ${_features}) - if(NOT ${_feature}) - set(_currentFeatureText "${_currentFeatureText}\n ${_feature}") - get_property(_desc GLOBAL PROPERTY ${_feature}_DESCRIPTION) - if(_desc) - set(_currentFeatureText "${_currentFeatureText}: ${_desc}") - set(_desc NOTFOUND) - endif(_desc) - endif() - endforeach(_feature) - - if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}") - message(STATUS "${_currentFeatureText}\n") - endif() -endfunction() - -# ----------------------------------------------------------------------------------------# -# function print_features() Print all features plus their docstrings. -# -function(PRINT_FEATURES) - message(STATUS "") - print_enabled_features() - print_disabled_features() -endfunction() - -# ----------------------------------------------------------------------------------------# -# macro ADD_SUBPROJECT() Does a git submodule update + add_subdirectory -# -macro(ADD_SUBPROJECT PACKAGE_NAME) - # parse args - cmake_parse_arguments(PACKAGE "SUBMODULE" "DIRECTORY" "" ${ARGN}) - if(NOT PACKAGE_DIRECTORY) - set(PACKAGE_DIRECTORY ${PACKAGE_NAME}) - endif() - # if specified in options - if("${PACKAGE_NAME}" IN_LIST PROJECTS) - if(PACKAGE_SUBMODULE) - checkout_git_submodule(RECURSIVE RELATIVE_PATH ${PACKAGE_DIRECTORY}) - endif() - if(NOT EXISTS "${PROJECT_SOURCE_DIR}/${PACKAGE_DIRECTORY}/CMakeLists.txt") - message( - STATUS - "Warning! '${PROJECT_SOURCE_DIR}/${PACKAGE_DIRECTORY}/CMakeLists.txt' does not exist!" - ) - else() - add_subdirectory(${PACKAGE_DIRECTORY}) - endif() - endif() -endmacro() diff --git a/examples/lulesh/external/CMakeLists.txt b/examples/lulesh/external/CMakeLists.txt index 2329f18eb1..4bff89894d 100644 --- a/examples/lulesh/external/CMakeLists.txt +++ b/examples/lulesh/external/CMakeLists.txt @@ -1,4 +1,4 @@ -# include(Utilities) +include(Utilities) option(LULESH_USE_CUDA "Enable Kokkos CUDA backend for lulesh" OFF) option(LULESH_USE_HIP "Enable Kokkos HIP backend for lulesh" OFF) diff --git a/examples/transpose/transpose.cpp b/examples/transpose/transpose.cpp index 098aa02996..1924a8092e 100644 --- a/examples/transpose/transpose.cpp +++ b/examples/transpose/transpose.cpp @@ -120,9 +120,6 @@ run(int rank, int tid, hipStream_t stream, int argc, char** argv) HIP_API_CALL(hipMemcpy(in, matrix, size, hipMemcpyHostToDevice)); HIP_API_CALL(hipDeviceSynchronize()); - hipDeviceProp_t props; - HIP_API_CALL(hipGetDeviceProperties(&props, 0)); - dim3 grid(M / 32, N / 32, 1); dim3 block(32, 32, 1); // transpose_a @@ -189,6 +186,9 @@ main(int argc, char** argv) if(argc > 1) nthreads = atoi(argv[1]); if(argc > 2) nitr = atoi(argv[2]); + printf("[transpose] Number of threads: %i\n", nthreads); + printf("[transpose] Number of iterations: %i\n", nitr); + #if defined(USE_MPI) MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); @@ -200,10 +200,12 @@ main(int argc, char** argv) int ndevice = 0; int devid = rank; HIP_API_CALL(hipGetDeviceCount(&ndevice)); + printf("[transpose] Number of devices found: %i\n", ndevice); if(ndevice > 0) { devid = rank % ndevice; HIP_API_CALL(hipSetDevice(devid)); + printf("[transpose] Rank %i assigned to device %i\n", rank, devid); } if(rank == devid && rank < ndevice) { diff --git a/external/timemory b/external/timemory index 110b907b35..c5c7e73a01 160000 --- a/external/timemory +++ b/external/timemory @@ -1 +1 @@ -Subproject commit 110b907b352d9b51df6b439879ce0439503aa073 +Subproject commit c5c7e73a0164d01bfbdc5b005b936ab20224df73 diff --git a/include/library/debug.hpp b/include/library/debug.hpp deleted file mode 100644 index a671c1333f..0000000000 --- a/include/library/debug.hpp +++ /dev/null @@ -1,140 +0,0 @@ -// MIT License -// -// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include "library/defines.hpp" - -#include -#include -#include -#include - -#include - -namespace omnitrace -{ -inline namespace config -{ -bool -get_debug(); - -bool -get_debug_tid(); - -bool -get_debug_pid(); - -bool -get_critical_trace_debug(); -} // namespace config -} // namespace omnitrace - -#if defined(TIMEMORY_USE_MPI) -# define OMNITRACE_CONDITIONAL_PRINT(COND, ...) \ - if((COND) && get_debug_tid() && get_debug_pid()) \ - { \ - fflush(stderr); \ - tim::auto_lock_t _lk{ tim::type_mutex() }; \ - fprintf(stderr, "[omnitrace][%i][%li] ", static_cast(tim::dmp::rank()), \ - tim::threading::get_id()); \ - fprintf(stderr, __VA_ARGS__); \ - fflush(stderr); \ - } -#else -# define OMNITRACE_CONDITIONAL_PRINT(COND, ...) \ - if((COND) && get_debug_tid() && get_debug_pid()) \ - { \ - fflush(stderr); \ - tim::auto_lock_t _lk{ tim::type_mutex() }; \ - fprintf(stderr, "[omnitrace][%i][%li] ", \ - static_cast(tim::process::get_id()), tim::threading::get_id()); \ - fprintf(stderr, __VA_ARGS__); \ - fflush(stderr); \ - } -#endif - -#define OMNITRACE_CONDITIONAL_BASIC_PRINT(COND, ...) \ - if((COND) && get_debug_tid() && get_debug_pid()) \ - { \ - fflush(stderr); \ - tim::auto_lock_t _lk{ tim::type_mutex() }; \ - fprintf(stderr, "[omnitrace] "); \ - fprintf(stderr, __VA_ARGS__); \ - fflush(stderr); \ - } - -#if defined(TIMEMORY_USE_MPI) -# define OMNITRACE_CONDITIONAL_THROW(COND, ...) \ - if(COND) \ - { \ - char _msg_buffer[2048]; \ - snprintf(_msg_buffer, 2048, "[omnitrace][%i][%li] ", \ - static_cast(tim::dmp::rank()), tim::threading::get_id()); \ - auto len = strlen(_msg_buffer); \ - snprintf(_msg_buffer + len, 2048 - len, __VA_ARGS__); \ - throw std::runtime_error(_msg_buffer); \ - } -#else -# define OMNITRACE_CONDITIONAL_THROW(COND, ...) \ - if(COND) \ - { \ - char _msg_buffer[2048]; \ - snprintf(_msg_buffer, 2048, "[omnitrace][%i][%li] ", \ - static_cast(tim::process::get_id()), \ - tim::threading::get_id()); \ - auto len = strlen(_msg_buffer); \ - snprintf(_msg_buffer + len, 2048 - len, __VA_ARGS__); \ - throw std::runtime_error(_msg_buffer); \ - } -#endif - -#define OMNITRACE_CONDITIONAL_BASIC_THROW(COND, ...) \ - if(COND) \ - { \ - char _msg_buffer[2048]; \ - snprintf(_msg_buffer, 2048, "[omnitrace] " __VA_ARGS__); \ - auto len = strlen(_msg_buffer); \ - snprintf(_msg_buffer + len, 2048 - len, __VA_ARGS__); \ - throw std::runtime_error(_msg_buffer); \ - } - -#define OMNITRACE_DEBUG(...) \ - OMNITRACE_CONDITIONAL_PRINT(::omnitrace::get_debug(), __VA_ARGS__) -#define OMNITRACE_PRINT(...) OMNITRACE_CONDITIONAL_PRINT(true, __VA_ARGS__) -#define OMNITRACE_CT_DEBUG(...) \ - OMNITRACE_CONDITIONAL_PRINT(::omnitrace::get_critical_trace_debug(), __VA_ARGS__) -#define OMNITRACE_BASIC_PRINT(...) OMNITRACE_CONDITIONAL_BASIC_PRINT(true, __VA_ARGS__) - -#define OMNITRACE_THROW(...) OMNITRACE_CONDITIONAL_THROW(true, __VA_ARGS__) -#define OMNITRACE_BASIC_THROW(...) OMNITRACE_CONDITIONAL_BASIC_THROW(true, __VA_ARGS__) - -#include - -namespace std -{ -inline std::string -to_string(bool _v) -{ - return (_v) ? "true" : "false"; -} -} // namespace std diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 0000000000..ddf0cd5d34 --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,14 @@ +if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF) +endif() + +if(OMNITRACE_BUILD_LTO) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) +endif() + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) + +add_subdirectory(lib) +add_subdirectory(bin) diff --git a/source/bin/CMakeLists.txt b/source/bin/CMakeLists.txt new file mode 100644 index 0000000000..4a531b5eb4 --- /dev/null +++ b/source/bin/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(omnitrace-avail) +add_subdirectory(omnitrace-critical-trace) +add_subdirectory(omnitrace) diff --git a/source/bin/omnitrace-avail/CMakeLists.txt b/source/bin/omnitrace-avail/CMakeLists.txt new file mode 100644 index 0000000000..6f6ee5f27a --- /dev/null +++ b/source/bin/omnitrace-avail/CMakeLists.txt @@ -0,0 +1,20 @@ +# ------------------------------------------------------------------------------# +# +# omnitrace-avail target +# +# ------------------------------------------------------------------------------# + +add_executable( + omnitrace-avail + ${CMAKE_CURRENT_LIST_DIR}/avail.cpp ${CMAKE_CURRENT_LIST_DIR}/avail.hpp + $) + +target_include_directories(omnitrace-avail PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include) +target_compile_definitions(omnitrace-avail PRIVATE OMNITRACE_EXTERN_COMPONENTS=0) +target_link_libraries(omnitrace-avail PRIVATE omnitrace::omnitrace-interface-library) +set_target_properties(omnitrace-avail PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON) + +install( + TARGETS omnitrace-avail + DESTINATION bin + OPTIONAL) diff --git a/src/avail.cpp b/source/bin/omnitrace-avail/avail.cpp similarity index 100% rename from src/avail.cpp rename to source/bin/omnitrace-avail/avail.cpp diff --git a/include/avail.hpp b/source/bin/omnitrace-avail/avail.hpp similarity index 100% rename from include/avail.hpp rename to source/bin/omnitrace-avail/avail.hpp diff --git a/source/bin/omnitrace-critical-trace/CMakeLists.txt b/source/bin/omnitrace-critical-trace/CMakeLists.txt new file mode 100644 index 0000000000..a8cdc21cae --- /dev/null +++ b/source/bin/omnitrace-critical-trace/CMakeLists.txt @@ -0,0 +1,23 @@ +# ------------------------------------------------------------------------------# +# +# omnitrace-critical-trace target +# +# ------------------------------------------------------------------------------# + +add_executable( + omnitrace-critical-trace + ${CMAKE_CURRENT_LIST_DIR}/critical-trace.cpp + ${CMAKE_CURRENT_LIST_DIR}/critical-trace.hpp + $) + +target_include_directories(omnitrace-critical-trace + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include) +target_compile_definitions(omnitrace-critical-trace PRIVATE OMNITRACE_EXTERN_COMPONENTS=0) +target_link_libraries(omnitrace-critical-trace + PRIVATE omnitrace::omnitrace-interface-library) +set_target_properties(omnitrace-critical-trace PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON) + +install( + TARGETS omnitrace-critical-trace + DESTINATION bin + OPTIONAL) diff --git a/source/bin/omnitrace-critical-trace/critical-trace.cpp b/source/bin/omnitrace-critical-trace/critical-trace.cpp new file mode 100644 index 0000000000..8989662016 --- /dev/null +++ b/source/bin/omnitrace-critical-trace/critical-trace.cpp @@ -0,0 +1,952 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "critical-trace.hpp" + +#include "library/api.hpp" +#include "library/perfetto.hpp" + +#include + +#include + +namespace config = omnitrace::config; +namespace critical_trace = omnitrace::critical_trace; + +int +main(int argc, char** argv) +{ + omnitrace_init_library(); + + config::set_setting_value("OMNITRACE_CRITICAL_TRACE", true); + // config::set_setting_value("OMNITRACE_CRITICAL_TRACE_DEBUG", true); + config::set_setting_value("OMNITRACE_CRITICAL_TRACE_COUNT", 500); + config::set_setting_value("OMNITRACE_CRITICAL_TRACE_PER_ROW", 100); + config::set_setting_value("OMNITRACE_CRITICAL_TRACE_NUM_THREADS", + std::thread::hardware_concurrency()); + config::set_setting_value("OMNITRACE_CRITICAL_TRACE_SERIALIZE_NAMES", true); + + perfetto::TracingInitArgs args{}; + perfetto::TraceConfig cfg{}; + perfetto::protos::gen::TrackEventConfig track_event_cfg{}; + + auto shmem_size_hint = config::get_perfetto_shmem_size_hint(); + auto buffer_size = config::get_perfetto_buffer_size(); + auto* buffer_config = cfg.add_buffers(); + + buffer_config->set_size_kb(buffer_size); + buffer_config->set_fill_policy( + perfetto::protos::gen::TraceConfig_BufferConfig_FillPolicy_DISCARD); + + auto* ds_cfg = cfg.add_data_sources()->mutable_config(); + ds_cfg->set_name("track_event"); + ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString()); + + args.backends |= perfetto::kInProcessBackend; + args.shmem_size_hint_kb = shmem_size_hint; + + perfetto::Tracing::Initialize(args); + perfetto::TrackEvent::Register(); + + auto tracing_session = perfetto::Tracing::NewTrace(); + tracing_session->Setup(cfg); + tracing_session->StartBlocking(); + + for(int i = 1; i < argc; ++i) + { + critical_trace::complete_call_chain = {}; + OMNITRACE_BASIC_PRINT_F("Loading call-chain %s...\n", argv[i]); + critical_trace::load_call_chain(argv[i], "call_chain", + critical_trace::complete_call_chain); + for(const auto& itr : *tim::get_hash_ids()) + critical_trace::complete_hash_ids.emplace(itr.second); + OMNITRACE_BASIC_PRINT_F("Computing critical trace for %s...\n", argv[i]); + critical_trace::compute_critical_trace(); + } + + // Make sure the last event is closed for this example. + perfetto::TrackEvent::Flush(); + + OMNITRACE_DEBUG_F("Stopping the blocking perfetto trace sessions...\n"); + tracing_session->StopBlocking(); + + OMNITRACE_DEBUG_F("Getting the trace data...\n"); + std::vector trace_data{ tracing_session->ReadTraceBlocking() }; + + if(trace_data.empty()) + { + OMNITRACE_BASIC_PRINT_F( + "> trace data is empty. File '%s' will not be written...\n", + config::get_perfetto_output_filename().c_str()); + } + else + { + // Write the trace into a file. + OMNITRACE_CONDITIONAL_BASIC_PRINT_F( + config::get_verbose() >= 0, + "> Outputting '%s' (%.2f KB / %.2f MB / %.2f GB)... ", + config::get_perfetto_output_filename().c_str(), + static_cast(trace_data.size()) / tim::units::KB, + static_cast(trace_data.size()) / tim::units::MB, + static_cast(trace_data.size()) / tim::units::GB); + + std::ofstream ofs{}; + if(!tim::filepath::open(ofs, config::get_perfetto_output_filename(), + std::ios::out | std::ios::binary)) + { + OMNITRACE_BASIC_PRINT_F("> Error opening '%s'...\n", + config::get_perfetto_output_filename().c_str()); + return EXIT_FAILURE; + } + else + { + // Write the trace into a file. + if(config::get_verbose() >= 0) fprintf(stderr, "Done\n"); + ofs.write(&trace_data[0], trace_data.size()); + } + ofs.close(); + } +} + +namespace omnitrace +{ +namespace critical_trace +{ +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 = {}) +{ + OMNITRACE_CT_DEBUG("\n"); + + 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("omnitrace"); + 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__; + OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose() >= 0, + "[%s] Outputting '%s'...\n", _func.c_str(), + _fname.c_str()); + } + ofs << oss.str() << std::endl; + } + + _perf.stop(); + if(_msg) + { + OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).substr(4).c_str()); + } +} + +void +save_critical_trace(const std::string& _fname, const std::string& _label, + const std::vector& _cchain, bool _msg = false, + std::string _func = {}) +{ + OMNITRACE_CT_DEBUG("\n"); + + 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("omnitrace"); + 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__; + OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose() >= 0, + "[%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) + { + OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).substr(4).c_str()); + } +} + +void +save_call_chain_text(const std::string& _fname, const call_chain& _call_chain, + bool _msg = false, std::string _func = {}) +{ + OMNITRACE_CT_DEBUG("\n"); + + 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__; + OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose() >= 0, + "[%s] Outputting '%s'...\n", _func.c_str(), + _fname.c_str()); + } + ofs << _call_chain << "\n"; + } + + _perf.stop(); + if(_msg) + { + OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).substr(4).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 = {}) +{ + OMNITRACE_CT_DEBUG("\n"); + + 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("omnitrace"); + 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__; + OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose() >= 0, + "[%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) + { + OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).substr(4).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("omnitrace"); + ar->startNode(); + (*ar)(cereal::make_nvp(_label.c_str(), _call_chain)); + ar->finishNode(); + } +} + +auto +get_indexed(const call_chain& _chain) +{ + OMNITRACE_CT_DEBUG("\n"); + 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) +{ + OMNITRACE_CT_DEBUG("\n"); + + 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 + OMNITRACE_CT_DEBUG_F("Allocating...\n"); + for(const auto& itr : _chain) + { + auto _ins = _entry_map.emplace(itr, std::vector{}); + if(!_ins.second) + { + auto _existing = _ins.first->first; + OMNITRACE_BASIC_PRINT("Warning! Duplicate entry for [%s] :: [%s]\n", + JOIN("", _existing).c_str(), JOIN("", itr).c_str()); + } + } + + task_group_t _tg{ &_tp }; + OMNITRACE_CT_DEBUG_F("Parallel mapping...\n"); + 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()) + { + OMNITRACE_CT_DEBUG_F("Setting root (line %i)...\n", __LINE__); + _graph.set_head(_indexed.at(-1).front()); + } + else + { + OMNITRACE_CT_DEBUG_F("Setting root (line %i)...\n", __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) + { + OMNITRACE_CT_DEBUG_F("Generating call-graph...\n"); + // _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) +{ + OMNITRACE_CT_DEBUG("\n"); + /* + using sibling_itr_t = call_graph_sibling_itr_t; + using sibling_vec_t = std::vector; + using sibling_map_t = std::map; + + std::function _no_overlap{}; + _no_overlap = [&](sibling_map_t& _v, sibling_itr_t root) { + sibling_map_t _l{}; + int64_t n = _graph.number_of_children(root); + if(n == 0) return; + + //_graph.sort(sibling_itr_t{ root }, + // [](auto lhs, auto rhs) { return lhs.get_cost() > rhs.get_cost(); }); + + for(int64_t i = 0; i < n; ++i) + { + if(_l.empty()) + { + auto itr = _graph.child(root, i); + _l[itr->tid].emplace_back(itr); + } + else + { + auto itr = _graph.child(root, i); + bool _overlaps = false; + for(auto& litr : _l[itr->tid]) + { + if(litr->device == itr->device && litr->get_overlap(*itr) > 0) + { + _overlaps = true; + break; + } + } + if(!_overlaps) _l[itr->tid].emplace_back(itr); + } + } + for(auto& iitr : _l) + { + for(auto itr : iitr.second) + { + _v[iitr.first].emplace_back(itr); + _no_overlap(_v, itr); + } + } + }; + + std::map _tot{}; + for(sibling_itr_t itr = _graph.begin(); itr != _graph.end(); ++itr) + { + _no_overlap(_tot, itr); + } + + for(const auto& iitr : _tot) + { + call_chain _cc{}; + _cc.emplace_back(*_graph.begin()); + for(const auto& itr : iitr.second) + _cc.emplace_back(*itr); + _chain.emplace_back(_cc); + } + + (void) _tp; + */ + + 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) + { + OMNITRACE_CT_DEBUG("Skipping node #%zu with %u children :: %s\n", _n, _nchild, + JOIN("", *itr).c_str()); + continue; + } + _end_nodes.emplace_back(itr); + } + OMNITRACE_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()); + std::sort( + _chain.at(i).begin(), _chain.at(i).end(), + [](const entry& lhs, const entry& rhs) { return lhs.begin_ns > rhs.end_ns; }); + }; + + 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(); + }); + + /* + std::vector _new_chain{}; + for(auto& itr : _chain) + { + if(itr.empty()) continue; + if(_new_chain.empty()) + { + _new_chain.emplace_back(std::move(itr)); + continue; + } + std::sort(itr.begin(), itr.end(), [](const entry& lhs, const entry& rhs) { + return lhs.get_cost() > rhs.get_cost(); + }); + + call_chain* _append_chain = nullptr; + for(auto& nitr : _new_chain) + { + if(nitr.at(0).tid == itr.at(0).tid && nitr.at(0).get_overlap(itr.at(0)) <= 0) + { + _append_chain = &nitr; + break; + } + } + + if(_append_chain) + { + for(auto& oitr : itr) + _append_chain->emplace_back(oitr); + std::sort(_append_chain->begin(), _append_chain->end(), + [](const entry& lhs, const entry& rhs) { + return lhs.get_cost() > rhs.get_cost(); + }); + } + else + { + _new_chain.emplace_back(std::move(itr)); + } + itr.clear(); + } + + _chain = _new_chain;*/ +} + +template +void +serialize_graph(ArchiveT& ar, const tim::graph& t) +{ + OMNITRACE_CT_DEBUG("\n"); + + 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(); +} + +template +std::vector +get_top(const std::vector& _chain, size_t _count) +{ + OMNITRACE_CT_DEBUG("\n"); + std::vector _data{}; + _data.reserve(_count); + for(const auto& itr : _chain) + { + if(_data.size() >= _count) break; + if(itr.query<>([](const entry& _v) { + return (DevT == Device::ANY) ? true : (_v.device == DevT); + })) + { + _data.emplace_back(itr); + } + } + return _data; +} + +template +void +generate_perfetto(const std::vector& _data) +{ + OMNITRACE_CT_DEBUG("\n"); + + auto _nrows = std::min(get_critical_trace_per_row(), _data.size()); + + // run in separate thread(s) so that it ends up in unique row + if(_nrows < 1) _nrows = _data.size(); + + std::string _dev = (DevT == Device::NONE) ? "" + : (DevT == Device::ANY) ? "CPU + GPU " + : (DevT == Device::CPU) ? "CPU " + : "GPU "; + std::string _cpname = _dev + "CritPath"; + auto _func = [&](size_t _idx, size_t _beg, size_t _end) { + if(DevT != Device::NONE) + { + if(_nrows != 1) + threading::set_thread_name(TIMEMORY_JOIN(" ", _cpname, _idx).c_str()); + else + threading::set_thread_name(_cpname.c_str()); + } + // 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); + } + }; + + for(size_t i = 0; i < _data.size(); i += _nrows) + { + if(DevT == Device::NONE) + _func(i, i, i + _nrows); + else + std::thread{ _func, i, i, i + _nrows }.join(); + } +} + +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) +{ + OMNITRACE_CT_DEBUG("\n"); + 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 +compute_critical_trace() +{ + OMNITRACE_CT_DEBUG_F("Generating critical trace...\n"); + + // ensure all hash ids exist + copy_hash_ids(); + + using perfstats_t = + tim::lightweight_tuple; + + perfstats_t _ct_perf{}; + _ct_perf.start(); + + auto _report_perf = [](auto& _perf, const char* _func, const std::string& _label) { + _perf.stop().rekey(_label); + OMNITRACE_BASIC_PRINT("[%s] %s\n", _func, JOIN("", _perf).substr(5).c_str()); + OMNITRACE_BASIC_PRINT("\n"); + _perf.reset().start(); + }; + + OMNITRACE_BASIC_PRINT("\n"); + + try + { + PTL::ThreadPool _tp{ get_critical_trace_num_threads(), []() { copy_hash_ids(); }, + []() {} }; + _tp.set_verbose(-1); + PTL::TaskGroup _tg{ &_tp }; + + perfstats_t _perf{}; + _perf.start(); + + OMNITRACE_BASIC_PRINT_F("sorting %zu call chain entries\n", + complete_call_chain.size()); + + // sort the complete call chain + std::sort(complete_call_chain.begin(), complete_call_chain.end()); + _report_perf(_perf, __FUNCTION__, "sorting call chain"); + + OMNITRACE_BASIC_PRINT_F("squashing call chain...\n"); + + // squash the critical path (combine start/stop into delta) + squash_critical_path(complete_call_chain); + _report_perf(_perf, __FUNCTION__, "squashing critical path"); + + // generate the perfetto + if(config::get_use_perfetto()) + { + OMNITRACE_BASIC_PRINT_F("generating perfetto for call chain...\n"); + generate_perfetto({ complete_call_chain }); + generate_perfetto({ complete_call_chain }); + generate_perfetto({ complete_call_chain }); + _report_perf(_perf, __FUNCTION__, "perfetto generation"); + } + + OMNITRACE_BASIC_PRINT_F("finding children...\n"); + call_graph_t _graph{}; + find_children(_tp, _graph, complete_call_chain); + _report_perf(_perf, __FUNCTION__, "finding children"); + + // sort the call-graph based on cost + OMNITRACE_BASIC_PRINT_F("sorting %zu call-graph entries...\n", _graph.size() - 1); + _graph.sort([](auto lhs, auto rhs) { return lhs.get_cost() > rhs.get_cost(); }, + [&_tg](auto _f) { _tg.run(_f); }, [&_tg]() { _tg.join(); }); + _report_perf(_perf, __FUNCTION__, "call-graph sort"); + + OMNITRACE_BASIC_PRINT_F("saving call-graph...\n"); + save_call_graph(tim::settings::compose_output_filename("call-graph", ".json"), + "call_graph", _graph, true, __FUNCTION__); + _report_perf(_perf, __FUNCTION__, "saving call-graph"); + + OMNITRACE_BASIC_PRINT_F("finding sequences...\n"); + std::vector _top{}; + find_sequences(_tp, _graph, _top); + _report_perf(_perf, __FUNCTION__, "call-graph sequence search"); + + OMNITRACE_BASIC_PRINT_F("number of sequences found: %zu (%zu)...\n", _top.size(), + (_top.empty()) ? 0 : _top.at(0).size()); + + if(get_critical_trace_count() == 0) + { + OMNITRACE_CT_DEBUG_F("saving critical trace...\n"); + save_critical_trace( + tim::settings::compose_output_filename("critical-trace", ".json"), + "critical_trace", _top, true, __FUNCTION__); + } + else + { + // get the top CPU critical traces + OMNITRACE_BASIC_PRINT_F("getting top CPU functions...\n"); + auto _top_cpu = get_top(_top, get_critical_trace_count()); + + // get the top GPU critical traces + OMNITRACE_BASIC_PRINT_F("getting top GPU functions...\n"); + auto _top_gpu = get_top(_top, get_critical_trace_count()); + + // get the top CPU + GPU critical traces + OMNITRACE_BASIC_PRINT_F("getting top CPU + GPU functions...\n"); + auto _top_any = get_top(_top, get_critical_trace_count()); + + if(!_top_cpu.empty()) + { + OMNITRACE_BASIC_PRINT_F( + "generating %zu perfetto CPU critical traces...\n", _top_cpu.size()); + if(config::get_use_perfetto()) generate_perfetto(_top_cpu); + OMNITRACE_CT_DEBUG_F("saving CPU critical traces...\n"); + save_critical_trace( + tim::settings::compose_output_filename("critical-trace-cpu", ".json"), + "critical_trace", _top_cpu, true, __FUNCTION__); + } + + if(!_top_gpu.empty()) + { + OMNITRACE_BASIC_PRINT_F( + "generating %zu perfetto GPU critical traces...\n", _top_gpu.size()); + if(config::get_use_perfetto()) generate_perfetto(_top_gpu); + OMNITRACE_CT_DEBUG_F("saving GPU critical traces...\n"); + save_critical_trace( + tim::settings::compose_output_filename("critical-trace-gpu", ".json"), + "critical_trace", _top_gpu, true, __FUNCTION__); + } + + if(!_top_any.empty()) + { + OMNITRACE_BASIC_PRINT_F( + "generating %zu perfetto CPU + GPU critical traces...\n", + _top_gpu.size()); + if(config::get_use_perfetto()) generate_perfetto(_top_gpu); + OMNITRACE_CT_DEBUG_F("saving CPU + GPU critical traces...\n"); + save_critical_trace( + tim::settings::compose_output_filename("critical-trace-any", ".json"), + "critical_trace", _top_any, true, __FUNCTION__); + } + } + + _tg.join(); + _tp.destroy_threadpool(); + } catch(std::exception& e) + { + OMNITRACE_BASIC_PRINT("Thread exited '%s' with exception: %s\n", __FUNCTION__, + e.what()); + TIMEMORY_CONDITIONAL_DEMANGLED_BACKTRACE(true, 32); + } + + _report_perf(_ct_perf, __FUNCTION__, "critical trace computation"); +} +} // namespace +} // namespace critical_trace +} // namespace omnitrace diff --git a/source/bin/omnitrace-critical-trace/critical-trace.hpp b/source/bin/omnitrace-critical-trace/critical-trace.hpp new file mode 100644 index 0000000000..ece704bfb8 --- /dev/null +++ b/source/bin/omnitrace-critical-trace/critical-trace.hpp @@ -0,0 +1,113 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "library/config.hpp" +#include "library/critical_trace.hpp" +#include "library/debug.hpp" +#include "library/defines.hpp" +#include "library/perfetto.hpp" +#include "library/ptl.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace omnitrace +{ +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 +load_call_chain(const std::string& _fname, const std::string& _label, + call_chain& _call_chain); + +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 +compute_critical_trace(); + +template +void +generate_perfetto(const std::vector& _data); + +inline 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 omnitrace diff --git a/source/bin/omnitrace/CMakeLists.txt b/source/bin/omnitrace/CMakeLists.txt new file mode 100644 index 0000000000..502ce19586 --- /dev/null +++ b/source/bin/omnitrace/CMakeLists.txt @@ -0,0 +1,37 @@ +# ------------------------------------------------------------------------------# +# +# omnitrace-exe target +# +# ------------------------------------------------------------------------------# + +add_executable( + omnitrace-exe + ${_EXCLUDE} ${CMAKE_CURRENT_LIST_DIR}/omnitrace.cpp + ${CMAKE_CURRENT_LIST_DIR}/omnitrace.hpp ${CMAKE_CURRENT_LIST_DIR}/details.cpp) + +target_link_libraries( + omnitrace-exe + PRIVATE omnitrace::omnitrace-headers + omnitrace::omnitrace-dyninst + omnitrace::omnitrace-compile-options + $ + $,omnitrace::omnitrace-sanitizer,>) + +set_target_properties( + omnitrace-exe + PROPERTIES + OUTPUT_NAME omnitrace + INSTALL_RPATH_USE_LINK_PATH ON + INSTALL_RPATH + "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/timemory/libunwind:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/dyninst-tpls/lib" + ) + +if(CMAKE_BUILD_TYPE MATCHES "^(DEBUG|Debug)") + string(REPLACE " " ";" _FLAGS "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + target_compile_options(omnitrace-exe PRIVATE ${_FLAGS}) +endif() + +install( + TARGETS omnitrace-exe + DESTINATION ${CMAKE_INSTALL_BINDIR} + OPTIONAL) diff --git a/src/omnitrace/details.cpp b/source/bin/omnitrace/details.cpp similarity index 100% rename from src/omnitrace/details.cpp rename to source/bin/omnitrace/details.cpp diff --git a/src/omnitrace.cpp b/source/bin/omnitrace/omnitrace.cpp similarity index 100% rename from src/omnitrace.cpp rename to source/bin/omnitrace/omnitrace.cpp diff --git a/include/omnitrace.hpp b/source/bin/omnitrace/omnitrace.hpp similarity index 100% rename from include/omnitrace.hpp rename to source/bin/omnitrace/omnitrace.hpp diff --git a/source/lib/CMakeLists.txt b/source/lib/CMakeLists.txt new file mode 100644 index 0000000000..a241cff965 --- /dev/null +++ b/source/lib/CMakeLists.txt @@ -0,0 +1,139 @@ +# ------------------------------------------------------------------------------# +# +# omnitrace interface library +# +# ------------------------------------------------------------------------------# + +add_library(omnitrace-interface-library INTERFACE) +add_library(omnitrace::omnitrace-interface-library ALIAS omnitrace-interface-library) + +target_include_directories( + omnitrace-interface-library INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include) +target_include_directories(omnitrace-interface-library SYSTEM + INTERFACE ${perfetto_DIR}/sdk) + +target_compile_definitions( + omnitrace-interface-library + INTERFACE OMNITRACE_MAX_THREADS=${OMNITRACE_MAX_THREADS} + $,CUSTOM_DATA_SOURCE,>) + +target_link_libraries( + omnitrace-interface-library + INTERFACE $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $,omnitrace::omnitrace-sanitizer,>) + +# ------------------------------------------------------------------------------# +# +# omnitrace object library +# +# ------------------------------------------------------------------------------# + +add_library(omnitrace-object-library OBJECT) +add_library(omnitrace::omnitrace-object-library ALIAS omnitrace-object-library) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/library/defines.hpp.in + ${CMAKE_CURRENT_BINARY_DIR}/include/library/defines.hpp @ONLY) + +set(library_sources + ${CMAKE_CURRENT_LIST_DIR}/src/library.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/config.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/cpu_freq.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/critical_trace.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/kokkosp.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/gpu.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/perfetto.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/ptl.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/sampling.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/state.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/thread_data.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/thread_sampler.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/timemory.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/components/backtrace.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/components/fork_gotcha.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/components/mpi_gotcha.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/components/omnitrace.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/components/pthread_gotcha.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/cpu_freq.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/critical_trace.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/debug.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/gpu.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/perfetto.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/ptl.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/sampling.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/state.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/thread_data.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/thread_sampler.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/timemory.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/fwd.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/backtrace.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/fork_gotcha.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/mpi_gotcha.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/omnitrace.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/rocm_smi.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/roctracer.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/roctracer_callbacks.hpp + ${CMAKE_CURRENT_LIST_DIR}/include/library/components/pthread_gotcha.hpp + ${perfetto_DIR}/sdk/perfetto.h) + +target_sources(omnitrace-object-library PRIVATE ${library_sources} ${library_headers}) + +if(OMNITRACE_USE_ROCTRACER) + target_sources( + omnitrace-object-library + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/library/components/roctracer.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/components/roctracer_callbacks.cpp) +endif() + +if(OMNITRACE_USE_ROCM_SMI) + target_sources(omnitrace-object-library + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/library/components/rocm_smi.cpp) +endif() + +target_link_libraries(omnitrace-object-library PRIVATE omnitrace-interface-library) + +if(OMNITRACE_DYNINST_API_RT) + get_filename_component(OMNITRACE_DYNINST_API_RT_DIR "${OMNITRACE_DYNINST_API_RT}" + DIRECTORY) +endif() + +# ------------------------------------------------------------------------------# +# +# omnitrace shared library +# +# ------------------------------------------------------------------------------# + +add_library(omnitrace-library SHARED $) +add_library(omnitrace::omnitrace-library ALIAS omnitrace-library) + +target_link_libraries(omnitrace-library PRIVATE omnitrace-interface-library) + +set_target_properties( + omnitrace-library + PROPERTIES OUTPUT_NAME omnitrace + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + INSTALL_RPATH + "\$ORIGIN:\$ORIGIN/timemory/libunwind:\$ORIGIN/dyninst-tpls/libs") + +install( + TARGETS omnitrace-library + DESTINATION ${CMAKE_INSTALL_LIBDIR} + OPTIONAL) diff --git a/include/library.hpp b/source/lib/include/library.hpp similarity index 100% rename from include/library.hpp rename to source/lib/include/library.hpp diff --git a/include/library/api.hpp b/source/lib/include/library/api.hpp similarity index 95% rename from include/library/api.hpp rename to source/lib/include/library/api.hpp index a2ef6b2e6e..d81505e87a 100644 --- a/include/library/api.hpp +++ b/source/lib/include/library/api.hpp @@ -50,4 +50,7 @@ extern "C" /// stops an instrumentation region void omnitrace_pop_trace(const char* name) TIMEMORY_VISIBILITY("default"); + + /// used by omnitrace-critical-trace + bool omnitrace_init_tooling() TIMEMORY_VISIBILITY("hidden"); } diff --git a/include/library/common.hpp b/source/lib/include/library/common.hpp similarity index 100% rename from include/library/common.hpp rename to source/lib/include/library/common.hpp diff --git a/include/library/components/backtrace.hpp b/source/lib/include/library/components/backtrace.hpp similarity index 100% rename from include/library/components/backtrace.hpp rename to source/lib/include/library/components/backtrace.hpp diff --git a/include/library/components/fork_gotcha.hpp b/source/lib/include/library/components/fork_gotcha.hpp similarity index 100% rename from include/library/components/fork_gotcha.hpp rename to source/lib/include/library/components/fork_gotcha.hpp diff --git a/include/library/components/fwd.hpp b/source/lib/include/library/components/fwd.hpp similarity index 100% rename from include/library/components/fwd.hpp rename to source/lib/include/library/components/fwd.hpp diff --git a/include/library/components/mpi_gotcha.hpp b/source/lib/include/library/components/mpi_gotcha.hpp similarity index 100% rename from include/library/components/mpi_gotcha.hpp rename to source/lib/include/library/components/mpi_gotcha.hpp diff --git a/include/library/components/omnitrace.hpp b/source/lib/include/library/components/omnitrace.hpp similarity index 100% rename from include/library/components/omnitrace.hpp rename to source/lib/include/library/components/omnitrace.hpp diff --git a/include/library/components/pthread_gotcha.hpp b/source/lib/include/library/components/pthread_gotcha.hpp similarity index 100% rename from include/library/components/pthread_gotcha.hpp rename to source/lib/include/library/components/pthread_gotcha.hpp diff --git a/include/library/components/rocm_smi.hpp b/source/lib/include/library/components/rocm_smi.hpp similarity index 85% rename from include/library/components/rocm_smi.hpp rename to source/lib/include/library/components/rocm_smi.hpp index 45720cbb62..a8bf2c5a70 100644 --- a/include/library/components/rocm_smi.hpp +++ b/source/lib/include/library/components/rocm_smi.hpp @@ -51,9 +51,20 @@ namespace rocm_smi void setup(); +void +config(); + +void +sample(); + void shutdown(); +void +post_process(); + +void set_state(State); + uint32_t device_count(); @@ -77,12 +88,6 @@ struct data void sample(uint32_t _dev_id); void print(std::ostream& _os) const; - template , nsec_t>, int> = 0> - static void poll(std::atomic* _state, Tp&& _interval, promise_t*); - - static void set_state(State); - static void poll(std::atomic* _state, nsec_t _interval, promise_t*); static void post_process(uint32_t _dev_id); uint32_t m_dev_id = std::numeric_limits::max(); @@ -100,7 +105,10 @@ struct data private: friend void omnitrace::rocm_smi::setup(); + friend void omnitrace::rocm_smi::config(); + friend void omnitrace::rocm_smi::sample(); friend void omnitrace::rocm_smi::shutdown(); + friend void omnitrace::rocm_smi::post_process(); static size_t device_count; static std::set device_list; @@ -111,28 +119,28 @@ private: static bool shutdown(); }; -template < - typename Tp, - std::enable_if_t, std::chrono::nanoseconds>, int>> -void -data::poll(std::atomic* _state, Tp&& _interval, promise_t* _prom) -{ - poll(_state, std::chrono::duration_cast(_interval), _prom); -} - -using bundle_t = std::deque; -using sampler_instances = thread_data; - #if !defined(OMNITRACE_USE_ROCM_SMI) inline void setup() {} +inline void +config() +{} + +inline void +sample() +{} + inline void shutdown() {} -inline void data::post_process(uint32_t) {} +inline void +post_process() +{} + +inline void set_state(State) {} #endif } // namespace rocm_smi } // namespace omnitrace diff --git a/include/library/components/roctracer.hpp b/source/lib/include/library/components/roctracer.hpp similarity index 100% rename from include/library/components/roctracer.hpp rename to source/lib/include/library/components/roctracer.hpp diff --git a/include/library/components/roctracer_callbacks.hpp b/source/lib/include/library/components/roctracer_callbacks.hpp similarity index 96% rename from include/library/components/roctracer_callbacks.hpp rename to source/lib/include/library/components/roctracer_callbacks.hpp index 4efc1d4b08..cb81cc7e3c 100644 --- a/include/library/components/roctracer_callbacks.hpp +++ b/source/lib/include/library/components/roctracer_callbacks.hpp @@ -54,17 +54,12 @@ namespace omnitrace { -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); diff --git a/include/library/config.hpp b/source/lib/include/library/config.hpp similarity index 98% rename from include/library/config.hpp rename to source/lib/include/library/config.hpp index d3d2c55302..b4f31ae2b6 100644 --- a/include/library/config.hpp +++ b/source/lib/include/library/config.hpp @@ -212,7 +212,7 @@ get_backend(); // make this visible so omnitrace-avail can call it std::string& -get_perfetto_output_filename() TIMEMORY_VISIBILITY("default"); +get_perfetto_output_filename(); int64_t get_critical_trace_count(); @@ -227,7 +227,7 @@ double& get_sampling_delay(); double& -get_rocm_smi_freq(); +get_thread_sampling_freq(); std::string get_rocm_smi_devices(); diff --git a/source/lib/include/library/cpu_freq.hpp b/source/lib/include/library/cpu_freq.hpp new file mode 100644 index 0000000000..616861f0b1 --- /dev/null +++ b/source/lib/include/library/cpu_freq.hpp @@ -0,0 +1,44 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +namespace omnitrace +{ +namespace cpu_freq +{ +void +setup(); + +void +config(); + +void +sample(); + +void +shutdown(); + +void +post_process(); +} // namespace cpu_freq +} // namespace omnitrace diff --git a/include/library/critical_trace.hpp b/source/lib/include/library/critical_trace.hpp similarity index 80% rename from include/library/critical_trace.hpp rename to source/lib/include/library/critical_trace.hpp index 1886177c0a..1614aa93e2 100644 --- a/include/library/critical_trace.hpp +++ b/source/lib/include/library/critical_trace.hpp @@ -26,7 +26,9 @@ #include "library/defines.hpp" #include "library/thread_data.hpp" -#include +#include +#include +#include #include #include @@ -43,6 +45,7 @@ enum class Device : short NONE = 0, CPU, GPU, + ANY, }; enum class Phase : short @@ -106,27 +109,44 @@ struct entry return _os; } template - void serialize(Archive& ar, unsigned int); + void save(Archive& ar, unsigned int) const; + + template + void load(Archive& ar, unsigned int); }; template void -entry::serialize(Archive& ar, unsigned int) +entry::save(Archive& ar, unsigned int) const { namespace cereal = tim::cereal; + std::string _name{}; + if(hash > 0) _name = tim::get_hash_identifier(hash); 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)); + cereal::make_nvp("hash", hash), cereal::make_nvp("name", _name), + cereal::make_nvp("demangled_name", tim::demangle(_name))); +} - 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)); - } +template +void +entry::load(Archive& ar, unsigned int) +{ + namespace cereal = tim::cereal; + std::string _name{}; + std::string _demangled_name{}; + 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), cereal::make_nvp("name", _name), + cereal::make_nvp("demangled_name", _demangled_name)); + + tim::get_hash_ids()->emplace(hash, _name); } struct call_chain : private std::vector @@ -175,12 +195,23 @@ struct call_chain : private std::vector } template - void generate_perfetto(std::set& _used, bool _basic = false) const; + void generate_perfetto(std::set& _used) const; template bool query(FuncT&&) const; }; +template +bool +call_chain::query(FuncT&& _func) const +{ + for(const auto& itr : *this) + { + if(std::forward(_func)(itr)) return BoolV; + } + return !BoolV; +} + using hash_ids = std::unordered_set; uint64_t diff --git a/source/lib/include/library/debug.hpp b/source/lib/include/library/debug.hpp new file mode 100644 index 0000000000..820b030fbd --- /dev/null +++ b/source/lib/include/library/debug.hpp @@ -0,0 +1,218 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "library/defines.hpp" + +#include +#include +#include +#include + +#include + +namespace omnitrace +{ +inline namespace config +{ +bool +get_debug(); + +bool +get_debug_tid(); + +bool +get_debug_pid(); + +bool +get_critical_trace_debug(); +} // namespace config +} // namespace omnitrace + +#if defined(TIMEMORY_USE_MPI) +# define OMNITRACE_PROCESS_IDENTIFIER static_cast(::tim::dmp::rank()) +#elif defined(TIMEMORY_USE_MPI_HEADERS) +# define OMNITRACE_PROCESS_IDENTIFIER \ + (::tim::dmp::is_initialized()) ? static_cast(::tim::dmp::rank()) \ + : static_cast(::tim::process::get_id()) +#else +# define OMNITRACE_PROCESS_IDENTIFIER static_cast(::tim::process::get_id()) +#endif + +#define OMNITRACE_CONDITIONAL_PRINT(COND, ...) \ + if((COND) && ::omnitrace::config::get_debug_tid() && \ + ::omnitrace::config::get_debug_pid()) \ + { \ + fflush(stderr); \ + tim::auto_lock_t _lk{ tim::type_mutex() }; \ + fprintf(stderr, "[omnitrace][%i][%li] ", OMNITRACE_PROCESS_IDENTIFIER, \ + tim::threading::get_id()); \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } + +#define OMNITRACE_CONDITIONAL_BASIC_PRINT(COND, ...) \ + if((COND) && ::omnitrace::config::get_debug_tid() && \ + ::omnitrace::config::get_debug_pid()) \ + { \ + fflush(stderr); \ + tim::auto_lock_t _lk{ tim::type_mutex() }; \ + fprintf(stderr, "[omnitrace] "); \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } + +#define OMNITRACE_CONDITIONAL_PRINT_F(COND, ...) \ + if((COND) && ::omnitrace::config::get_debug_tid() && \ + ::omnitrace::config::get_debug_pid()) \ + { \ + fflush(stderr); \ + tim::auto_lock_t _lk{ tim::type_mutex() }; \ + fprintf(stderr, "[omnitrace][%i][%li][%s] ", OMNITRACE_PROCESS_IDENTIFIER, \ + tim::threading::get_id(), __FUNCTION__); \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } + +#define OMNITRACE_CONDITIONAL_BASIC_PRINT_F(COND, ...) \ + if((COND) && ::omnitrace::config::get_debug_tid() && \ + ::omnitrace::config::get_debug_pid()) \ + { \ + fflush(stderr); \ + tim::auto_lock_t _lk{ tim::type_mutex() }; \ + fprintf(stderr, "[omnitrace][%s] ", __FUNCTION__); \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } + +#define OMNITRACE_CONDITIONAL_THROW(COND, ...) \ + if(COND) \ + { \ + char _msg_buffer[2048]; \ + snprintf(_msg_buffer, 2048, "[omnitrace][%i][%li][%s] ", \ + OMNITRACE_PROCESS_IDENTIFIER, tim::threading::get_id(), __FUNCTION__); \ + auto len = strlen(_msg_buffer); \ + snprintf(_msg_buffer + len, 2048 - len, __VA_ARGS__); \ + throw std::runtime_error(_msg_buffer); \ + } + +#define OMNITRACE_CONDITIONAL_BASIC_THROW(COND, ...) \ + if(COND) \ + { \ + char _msg_buffer[2048]; \ + snprintf(_msg_buffer, 2048, "[omnitrace][%s] ", __FUNCTION__); \ + auto len = strlen(_msg_buffer); \ + snprintf(_msg_buffer + len, 2048 - len, __VA_ARGS__); \ + throw std::runtime_error(_msg_buffer); \ + } + +#define OMNITRACE_STRINGIZE(...) #__VA_ARGS__ +#define OMNITRACE_ESC(...) __VA_ARGS__ + +//--------------------------------------------------------------------------------------// +// +// Debug macros +// +//--------------------------------------------------------------------------------------// +#define OMNITRACE_DEBUG(...) \ + OMNITRACE_CONDITIONAL_PRINT(::omnitrace::get_debug(), __VA_ARGS__) + +#define OMNITRACE_BASIC_DEBUG(...) \ + OMNITRACE_CONDITIONAL_BASIC_PRINT(::omnitrace::get_debug_env(), __VA_ARGS__) + +#define OMNITRACE_DEBUG_F(...) \ + OMNITRACE_CONDITIONAL_PRINT_F(::omnitrace::get_debug(), __VA_ARGS__) + +#define OMNITRACE_BASIC_DEBUG_F(...) \ + OMNITRACE_CONDITIONAL_BASIC_PRINT_F(::omnitrace::get_debug_env(), __VA_ARGS__) + +#define OMNITRACE_CT_DEBUG(...) \ + OMNITRACE_CONDITIONAL_PRINT(::omnitrace::get_critical_trace_debug(), __VA_ARGS__) + +#define OMNITRACE_CT_DEBUG_F(...) \ + OMNITRACE_CONDITIONAL_PRINT_F(::omnitrace::get_critical_trace_debug(), __VA_ARGS__) + +//--------------------------------------------------------------------------------------// +// +// Verbose macros +// +//--------------------------------------------------------------------------------------// +#define OMNITRACE_VERBOSE(LEVEL, ...) \ + OMNITRACE_CONDITIONAL_PRINT( \ + ::omnitrace::get_debug() || ::omnitrace::get_verbose() >= LEVEL, __VA_ARGS__) + +#define OMNITRACE_BASIC_VERBOSE(LEVEL, ...) \ + OMNITRACE_CONDITIONAL_BASIC_PRINT(::omnitrace::get_debug_env() || \ + ::omnitrace::get_verbose_env() >= LEVEL, \ + __VA_ARGS__) + +#define OMNITRACE_VERBOSE_F(LEVEL, ...) \ + OMNITRACE_CONDITIONAL_PRINT_F( \ + ::omnitrace::get_debug() || ::omnitrace::get_verbose() >= LEVEL, __VA_ARGS__) + +#define OMNITRACE_BASIC_VERBOSE_F(LEVEL, ...) \ + OMNITRACE_CONDITIONAL_BASIC_PRINT_F(::omnitrace::get_debug_env() || \ + ::omnitrace::get_verbose_env() >= LEVEL, \ + __VA_ARGS__) + +//--------------------------------------------------------------------------------------// +// +// Basic print macros (basic means it will not provide PID/RANK or TID) and will not +// initialize the settings. +// +//--------------------------------------------------------------------------------------// + +#define OMNITRACE_BASIC_PRINT(...) OMNITRACE_CONDITIONAL_BASIC_PRINT(true, __VA_ARGS__) + +#define OMNITRACE_BASIC_PRINT_F(...) OMNITRACE_CONDITIONAL_BASIC_PRINT(true, __VA_ARGS__) + +//--------------------------------------------------------------------------------------// +// +// Print macros. Will provide PID/RANK and TID (will initialize settings) +// +//--------------------------------------------------------------------------------------// + +#define OMNITRACE_PRINT(...) OMNITRACE_CONDITIONAL_PRINT(true, __VA_ARGS__) + +#define OMNITRACE_PRINT_F(...) OMNITRACE_CONDITIONAL_PRINT_F(true, __VA_ARGS__) + +//--------------------------------------------------------------------------------------// +// +// Throw macros +// +//--------------------------------------------------------------------------------------// + +#define OMNITRACE_THROW(...) OMNITRACE_CONDITIONAL_THROW(true, __VA_ARGS__) + +#define OMNITRACE_BASIC_THROW(...) OMNITRACE_CONDITIONAL_BASIC_THROW(true, __VA_ARGS__) + +#include + +namespace std +{ +inline std::string +to_string(bool _v) +{ + return (_v) ? "true" : "false"; +} +} // namespace std diff --git a/include/library/defines.hpp.in b/source/lib/include/library/defines.hpp.in similarity index 100% rename from include/library/defines.hpp.in rename to source/lib/include/library/defines.hpp.in diff --git a/include/library/dynamic_library.hpp b/source/lib/include/library/dynamic_library.hpp similarity index 100% rename from include/library/dynamic_library.hpp rename to source/lib/include/library/dynamic_library.hpp diff --git a/include/library/gpu.hpp b/source/lib/include/library/gpu.hpp similarity index 100% rename from include/library/gpu.hpp rename to source/lib/include/library/gpu.hpp diff --git a/include/library/perfetto.hpp b/source/lib/include/library/perfetto.hpp similarity index 94% rename from include/library/perfetto.hpp rename to source/lib/include/library/perfetto.hpp index 9239dd18d8..fde493299d 100644 --- a/include/library/perfetto.hpp +++ b/source/lib/include/library/perfetto.hpp @@ -36,6 +36,8 @@ perfetto::Category("rocm_smi").SetDescription("Device-level metrics"), \ perfetto::Category("sampling") \ .SetDescription("Metrics derived from sampling"), \ + perfetto::Category("critical-trace") \ + .SetDescription("Combined critical traces"), \ perfetto::Category("host-critical-trace") \ .SetDescription("Host-side critical traces"), \ perfetto::Category("device-critical-trace") \ @@ -47,6 +49,8 @@ perfetto::Category("rocm_smi").SetDescription("Device-level metrics"), \ perfetto::Category("sampling") \ .SetDescription("Metrics derived from sampling"), \ + perfetto::Category("critical-trace") \ + .SetDescription("Combined critical traces"), \ perfetto::Category("host-critical-trace") \ .SetDescription("Host-side critical traces"), \ perfetto::Category("device-critical-trace") \ diff --git a/include/library/ptl.hpp b/source/lib/include/library/ptl.hpp similarity index 100% rename from include/library/ptl.hpp rename to source/lib/include/library/ptl.hpp diff --git a/include/library/redirect.hpp b/source/lib/include/library/redirect.hpp similarity index 100% rename from include/library/redirect.hpp rename to source/lib/include/library/redirect.hpp diff --git a/include/library/sampling.hpp b/source/lib/include/library/sampling.hpp similarity index 100% rename from include/library/sampling.hpp rename to source/lib/include/library/sampling.hpp diff --git a/include/library/state.hpp b/source/lib/include/library/state.hpp similarity index 100% rename from include/library/state.hpp rename to source/lib/include/library/state.hpp diff --git a/include/library/thread_data.hpp b/source/lib/include/library/thread_data.hpp similarity index 100% rename from include/library/thread_data.hpp rename to source/lib/include/library/thread_data.hpp diff --git a/source/lib/include/library/thread_sampler.hpp b/source/lib/include/library/thread_sampler.hpp new file mode 100644 index 0000000000..a14f391e99 --- /dev/null +++ b/source/lib/include/library/thread_sampler.hpp @@ -0,0 +1,103 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "library/common.hpp" +#include "library/components/fwd.hpp" +#include "library/defines.hpp" +#include "library/state.hpp" +#include "library/thread_data.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace omnitrace +{ +namespace thread_sampler +{ +struct instance +{ + std::function setup = []() {}; + std::function shutdown = []() {}; + std::function config = []() {}; + std::function sample = []() {}; + std::function post_process = []() {}; +}; +// +struct sampler +{ + using msec_t = std::chrono::milliseconds; + using usec_t = std::chrono::microseconds; + using nsec_t = std::chrono::nanoseconds; + using promise_t = std::promise; + using future_t = std::future; + using state_t = State; + + using timestamp_t = int64_t; + + template , nsec_t>, int> = 0> + static void poll(std::atomic* _state, Tp&& _interval, promise_t*); + + static void setup(); + static void shutdown(); + static void post_process(); + static void set_state(state_t); + static void poll(std::atomic* _state, nsec_t _interval, promise_t*); +}; +// +template < + typename Tp, + std::enable_if_t, std::chrono::nanoseconds>, int>> +void +sampler::poll(std::atomic* _state, Tp&& _interval, promise_t* _prom) +{ + poll(_state, std::chrono::duration_cast(_interval), _prom); +} +// +inline void +setup() +{ + sampler::setup(); +} + +inline void +shutdown() +{ + sampler::shutdown(); +} + +inline void +post_process() +{ + sampler::post_process(); +} +// +} // namespace thread_sampler +} // namespace omnitrace diff --git a/include/library/timemory.hpp b/source/lib/include/library/timemory.hpp similarity index 100% rename from include/library/timemory.hpp rename to source/lib/include/library/timemory.hpp diff --git a/src/library.cpp b/source/lib/src/library.cpp similarity index 95% rename from src/library.cpp rename to source/lib/src/library.cpp index 1a5f801b34..0f6aa67395 100644 --- a/src/library.cpp +++ b/source/lib/src/library.cpp @@ -23,7 +23,6 @@ #include "library.hpp" #include "library/components/fork_gotcha.hpp" #include "library/components/mpi_gotcha.hpp" -#include "library/components/rocm_smi.hpp" #include "library/config.hpp" #include "library/critical_trace.hpp" #include "library/debug.hpp" @@ -31,6 +30,7 @@ #include "library/gpu.hpp" #include "library/sampling.hpp" #include "library/thread_data.hpp" +#include "library/thread_sampler.hpp" #include "library/timemory.hpp" #include "timemory/mpl/type_traits.hpp" @@ -112,9 +112,6 @@ get_cpu_cid_parents() using Device = critical_trace::Device; using Phase = critical_trace::Phase; - -bool -omnitrace_init_tooling(); } // namespace //======================================================================================// @@ -133,9 +130,8 @@ omnitrace_push_trace(const char* name) if(get_state() != State::Active && !omnitrace_init_tooling()) { static auto _debug = get_debug_env(); - OMNITRACE_CONDITIONAL_BASIC_PRINT( - _debug, "[%s] %s :: not active and perfetto not initialized\n", __FUNCTION__, - name); + OMNITRACE_CONDITIONAL_BASIC_PRINT_F(_debug, "%s :: not active. state = %s\n", + name, std::to_string(get_state()).c_str()); return; } @@ -342,9 +338,7 @@ omnitrace_init_library() //======================================================================================// -namespace -{ -bool +extern "C" bool omnitrace_init_tooling() { static bool _once = false; @@ -366,22 +360,10 @@ omnitrace_init_tooling() OMNITRACE_DEBUG("[%s]\n", __FUNCTION__); auto _dtor = scope::destructor{ []() { - // if roctracer is not enabled, we cannot rely on OnLoad(...) via HSA tools - // to setup rocm-smi - if constexpr(!trait::is_available::value) - { - try - { - rocm_smi::setup(); - } catch(std::exception& _e) - { - OMNITRACE_PRINT("Exception: %s\n", _e.what()); - } - } - if(get_use_sampling()) { pthread_gotcha::enable_sampling_on_child_threads() = false; + thread_sampler::setup(); sampling::setup(); pthread_gotcha::enable_sampling_on_child_threads() = true; sampling::unblock_signals(); @@ -500,7 +482,8 @@ omnitrace_init_tooling() static auto _push_perfetto = [](const char* name) { _thread_init(); - TRACE_EVENT_BEGIN("host", perfetto::StaticString(name)); + uint64_t _ts = comp::wall_clock::record(); + TRACE_EVENT_BEGIN("host", perfetto::StaticString(name), _ts); }; static auto _pop_timemory = [](const char* name) { @@ -517,7 +500,10 @@ omnitrace_init_tooling() _data.bundles.pop_back(); }; - static auto _pop_perfetto = [](const char*) { TRACE_EVENT_END("host"); }; + static auto _pop_perfetto = [](const char*) { + uint64_t _ts = comp::wall_clock::record(); + TRACE_EVENT_END("host", _ts); + }; if(get_use_perfetto() && get_use_timemory()) { @@ -572,7 +558,6 @@ omnitrace_init_tooling() return true; } -} // namespace //======================================================================================// @@ -590,6 +575,10 @@ omnitrace_init(const char* _mode, bool _is_binary_rewrite, const char* _argv0) tim::set_env("OMNITRACE_MODE", _mode, 0); config::is_binary_rewrite() = _is_binary_rewrite; + // default to KokkosP enabled when sampling, otherwise default to off + tim::set_env("OMNITRACE_USE_KOKKOSP", (get_mode() == Mode::Sampling) ? "ON" : "OFF", + 0); + if(!_set_mpi_called) { _start_gotcha_callback = []() { get_gotcha_bundle()->start(); }; @@ -606,7 +595,12 @@ extern "C" void omnitrace_finalize(void) { // return if not active - if(get_state() != State::Active) return; + if(get_state() != State::Active) + { + OMNITRACE_DEBUG_F("State = %s. Finalization skipped\n", + std::to_string(get_state()).c_str()); + return; + } pthread_gotcha::enable_sampling_on_child_threads() = false; @@ -643,12 +637,7 @@ omnitrace_finalize(void) } pthread_gotcha::shutdown(); - - if(get_use_rocm_smi()) - { - OMNITRACE_DEBUG("[%s] Shutting down rocm-smi sampling...\n", __FUNCTION__); - rocm_smi::shutdown(); - } + thread_sampler::shutdown(); OMNITRACE_DEBUG("[%s] Shutting down roctracer...\n", __FUNCTION__); // ensure that threads running roctracer callbacks shutdown @@ -746,20 +735,7 @@ omnitrace_finalize(void) } } - // ensure that all the MT instances are flushed - if(get_use_rocm_smi()) - { - size_t _ndevice = gpu::device_count(); - OMNITRACE_CONDITIONAL_PRINT( - get_debug() || get_verbose() > 0, - "[%s] Post-processing rocm-smi samples for %zu devices...\n", __FUNCTION__, - _ndevice); - for(size_t i = 0; i < _ndevice; ++i) - { - rocm_smi::data::post_process(i); - rocm_smi::sampler_instances::instances().at(i).reset(); - } - } + thread_sampler::post_process(); if(get_use_critical_trace()) { diff --git a/src/library/components/backtrace.cpp b/source/lib/src/library/components/backtrace.cpp similarity index 100% rename from src/library/components/backtrace.cpp rename to source/lib/src/library/components/backtrace.cpp diff --git a/src/library/components/fork_gotcha.cpp b/source/lib/src/library/components/fork_gotcha.cpp similarity index 100% rename from src/library/components/fork_gotcha.cpp rename to source/lib/src/library/components/fork_gotcha.cpp diff --git a/src/library/components/mpi_gotcha.cpp b/source/lib/src/library/components/mpi_gotcha.cpp similarity index 100% rename from src/library/components/mpi_gotcha.cpp rename to source/lib/src/library/components/mpi_gotcha.cpp diff --git a/src/library/components/omnitrace.cpp b/source/lib/src/library/components/omnitrace.cpp similarity index 100% rename from src/library/components/omnitrace.cpp rename to source/lib/src/library/components/omnitrace.cpp diff --git a/src/library/components/pthread_gotcha.cpp b/source/lib/src/library/components/pthread_gotcha.cpp similarity index 100% rename from src/library/components/pthread_gotcha.cpp rename to source/lib/src/library/components/pthread_gotcha.cpp diff --git a/src/library/components/rocm_smi.cpp b/source/lib/src/library/components/rocm_smi.cpp similarity index 69% rename from src/library/components/rocm_smi.cpp rename to source/lib/src/library/components/rocm_smi.cpp index 0effc359dc..fcbd1ebd6d 100644 --- a/src/library/components/rocm_smi.cpp +++ b/source/lib/src/library/components/rocm_smi.cpp @@ -60,7 +60,9 @@ namespace omnitrace namespace rocm_smi { using tim::type_mutex; -using auto_lock_t = tim::auto_lock_t; +using auto_lock_t = tim::auto_lock_t; +using bundle_t = std::deque; +using sampler_instances = thread_data; namespace { @@ -125,17 +127,51 @@ data::print(std::ostream& _os) const namespace { -struct cpu_freq -{}; -using freq_pair_t = std::pair; -std::vector> cpu_frequencies = {}; +std::vector*> _bundle_data{}; +} -struct cpu_mem -{}; -using cpu_mem_usage_pair_t = std::pair; -std::vector cpu_mem_usage = {}; -} // namespace +void +config() +{ + _bundle_data.resize(data::device_count, nullptr); + for(size_t i = 0; i < data::device_count; ++i) + { + if(data::device_list.count(i) > 0) + { + _bundle_data.at(i) = &sampler_instances::instances().at(i); + if(!*_bundle_data.at(i)) *_bundle_data.at(i) = std::make_unique(); + } + } + data::get_initial().resize(data::device_count); + for(auto itr : data::device_list) + data::get_initial().at(itr).sample(itr); +} + +void +sample() +{ + if(get_rocm_smi_state() != State::Active) return; + + for(auto itr : data::device_list) + { + OMNITRACE_CONDITIONAL_BASIC_PRINT(get_debug(), + "Polling rocm-smi for device %u...\n", itr); + auto& _data = *_bundle_data.at(itr); + if(!_data) continue; + _data->emplace_back(data{ itr }); + OMNITRACE_CONDITIONAL_BASIC_PRINT(get_debug(), " %s\n", + TIMEMORY_JOIN("", _data->back()).c_str()); + } +} + +void +set_state(State _v) +{ + get_rocm_smi_state().store(_v); +} + +/* void data::poll(std::atomic* _state, nsec_t _interval, promise_t* _ready) { @@ -155,71 +191,6 @@ data::poll(std::atomic* _state, nsec_t _interval, promise_t* _ready) } } - auto _ncpu = threading::affinity::hw_concurrency(); - std::vector _cpu_mhz_pos{}; - std::ifstream _ifs{ "/proc/cpuinfo" }; - if(_ifs) - { - for(size_t i = 0; i < _ncpu; ++i) - { - short _n = 0; - std::string _st{}; - while(_ifs && _ifs.good()) - { - std::string _s{}; - _ifs >> _s; - if(!_ifs.good() || !_ifs) break; - - if(_s == "cpu" || _s == "MHz" || _s == ":") - { - ++_n; - _st += _s + " "; - } - else - { - _n = 0; - _st = {}; - } - - if(_n == 3) - { - size_t _pos = _ifs.tellg(); - _cpu_mhz_pos.emplace_back(_pos + 1); - _ifs >> _s; - if(!_ifs.good() || !_ifs) break; - OMNITRACE_CONDITIONAL_BASIC_PRINT(get_debug() || get_verbose() > 1, - "[%zu] %s %s (pos = %zu)\n", i, - _st.c_str(), _s.c_str(), _pos + 1); - break; - } - } - } - } - - cpu_frequencies.resize(_ncpu); - - _ifs = std::ifstream{ "/proc/cpuinfo", std::ifstream::binary }; - auto _read_cpu_freq = [&_cpu_mhz_pos, &_ifs](size_t _idx) { - double _freq = 0; - _ifs.seekg(_cpu_mhz_pos.at(_idx), _ifs.beg); - _ifs >> _freq; - return _freq; - }; - - auto _read_cpu_mem_usage = []() { - cpu_mem_usage.emplace_back(tim::get_clock_real_now(), - tim::get_page_rss()); - }; - - auto _read_cpu_freqs = [&_read_cpu_freq, _ncpu]() { - auto _ts = tim::get_clock_real_now(); - for(size_t i = 0; i < _ncpu; ++i) - { - auto _freq = _read_cpu_freq(i); - cpu_frequencies.at(i).emplace_back(_ts, _freq); - } - }; - OMNITRACE_CONDITIONAL_BASIC_PRINT( get_verbose() > 0 || get_debug(), "Polling rocm-smi for %zu device(s) at an interval of %f seconds...\n", @@ -235,8 +206,6 @@ data::poll(std::atomic* _state, nsec_t _interval, promise_t* _ready) { std::this_thread::sleep_until(_now); if(_state->load() != State::Active) continue; - _read_cpu_mem_usage(); - _read_cpu_freqs(); for(auto itr : device_list) { OMNITRACE_CONDITIONAL_BASIC_PRINT(get_debug(), @@ -254,6 +223,7 @@ data::poll(std::atomic* _state, nsec_t _interval, promise_t* _ready) if(polling_finished) polling_finished->set_value(); } +*/ std::vector& data::get_initial() @@ -262,73 +232,20 @@ data::get_initial() return _v; } -std::unique_ptr& -data::get_thread() -{ - static std::unique_ptr _v; - return _v; -} - -void -data::set_state(State _state) -{ - get_rocm_smi_state().store(_state); -} - bool data::setup() { - perfetto_counter_track::init(); - perfetto_counter_track::init(); perfetto_counter_track::init(); - - // shutdown if already running - shutdown(); - - OMNITRACE_DEBUG("Configuring rocm-smi...\n"); - - auto _freq = get_rocm_smi_freq(); - uint64_t _msec_freq = (1.0 / _freq) * 1.0e3; - - promise_t _prom{}; - auto _fut = _prom.get_future(); - polling_finished = std::make_unique(); - set_state(State::PreInit); - get_thread() = std::make_unique( - &data::poll, &get_rocm_smi_state(), msec_t{ _msec_freq }, &_prom); - - _fut.wait(); return true; } bool data::shutdown() { - auto& _thread = get_thread(); - if(_thread) - { - OMNITRACE_DEBUG("Shutting down rocm-smi...\n"); - set_state(State::Finalized); - if(polling_finished) - { - auto _fut = polling_finished->get_future(); - uint64_t _freq = (1.0 / get_rocm_smi_freq()) * 1.0e3; - _fut.wait_for(msec_t{ 5 * _freq }); - _thread->join(); - } - else - { - uint64_t _freq = (1.0 / get_rocm_smi_freq()) * 1.0e3; - std::this_thread::sleep_for(msec_t{ 5 * _freq }); - pthread_cancel(_thread->native_handle()); - _thread->detach(); - } - _thread = std::unique_ptr{}; - polling_finished = std::unique_ptr{}; - return true; - } - return false; + OMNITRACE_DEBUG("Shutting down rocm-smi...\n"); + set_state(State::Finalized); + return true; } #define GPU_METRIC(COMPONENT, ...) \ @@ -355,50 +272,6 @@ data::post_process(uint32_t _dev_id) using bundle_t = tim::lightweight_tuple; - auto _process_frequencies = [](size_t _idx) { - using counter_track = perfetto_counter_track; - if(!counter_track::exists(_idx)) - { - auto _devname = TIMEMORY_JOIN("", "[CPU ", _idx, "] "); - auto addendum = [&](const char* _v) { return _devname + std::string{ _v }; }; - counter_track::emplace(_idx, addendum("Frequency (S)"), "MHz"); - } - - for(auto& itr : cpu_frequencies.at(_idx)) - { - uint64_t _ts = itr.first; - double _freq = itr.second; - TRACE_COUNTER("sampling", counter_track::at(_idx, 0), _ts, _freq); - } - }; - - auto _process_cpu_mem_usage = []() { - using counter_track = perfetto_counter_track; - if(!counter_track::exists(0)) - { - auto _devname = TIMEMORY_JOIN("", "[CPU] "); - auto addendum = [&](const char* _v) { return _devname + std::string{ _v }; }; - counter_track::emplace(0, addendum("Memory Usage (S)"), "MB"); - } - - for(auto& itr : cpu_mem_usage) - { - uint64_t _ts = itr.first; - double _mem_usage = itr.second; - TRACE_COUNTER("sampling", counter_track::at(0, 0), _ts, - _mem_usage / units::megabyte); - } - }; - - static bool _once = false; - if(!_once) - { - _once = true; - _process_cpu_mem_usage(); - for(size_t i = 0; i < cpu_frequencies.size(); ++i) - _process_frequencies(i); - } - if(device_count < _dev_id) return; auto& _rocm_smi_v = sampler_instances::instances().at(_dev_id); @@ -474,6 +347,7 @@ setup() if(is_initialized() || !get_use_rocm_smi()) return; + auto _enable_samp = pthread_gotcha::enable_sampling_on_child_threads(); pthread_gotcha::enable_sampling_on_child_threads() = false; // assign the data value to determined by rocm-smi @@ -513,8 +387,7 @@ setup() data::setup(); - pthread_gotcha::enable_sampling_on_child_threads() = true; - omnitrace::rocm_smi::data::set_state(State::Active); + pthread_gotcha::enable_sampling_on_child_threads() = _enable_samp; } void @@ -532,6 +405,13 @@ shutdown() is_initialized() = false; } +void +post_process() +{ + for(auto itr : data::device_list) + data::post_process(itr); +} + uint32_t device_count() { diff --git a/src/library/components/roctracer.cpp b/source/lib/src/library/components/roctracer.cpp similarity index 98% rename from src/library/components/roctracer.cpp rename to source/lib/src/library/components/roctracer.cpp index 0d36d111bf..ab5e2ae1c3 100644 --- a/src/library/components/roctracer.cpp +++ b/source/lib/src/library/components/roctracer.cpp @@ -234,9 +234,6 @@ extern "C" "[%s] setting up HSA...\n", __FUNCTION__); - 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; @@ -314,7 +311,7 @@ extern "C" comp::roctracer::add_setup("hsa", std::move(_setup)); comp::roctracer::add_shutdown("hsa", std::move(_shutdown)); - rocm_smi::setup(); + rocm_smi::set_state(State::Active); comp::roctracer::setup(); pthread_gotcha::enable_sampling_on_child_threads() = true; @@ -325,7 +322,7 @@ extern "C" void OnUnload() { OMNITRACE_DEBUG("[%s]\n", __FUNCTION__); - rocm_smi::shutdown(); + rocm_smi::set_state(State::Finalized); comp::roctracer::shutdown(); } } diff --git a/src/library/components/roctracer_callbacks.cpp b/source/lib/src/library/components/roctracer_callbacks.cpp similarity index 89% rename from src/library/components/roctracer_callbacks.cpp rename to source/lib/src/library/components/roctracer_callbacks.cpp index 0b04be6f22..d0d0049156 100644 --- a/src/library/components/roctracer_callbacks.cpp +++ b/source/lib/src/library/components/roctracer_callbacks.cpp @@ -82,13 +82,6 @@ get_hip_activity_callbacks(int64_t _tid = threading::get_id()) 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; @@ -114,17 +107,14 @@ hsa_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* 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 = []() { + static thread_local int64_t begin_timestamp = 0; + 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: @@ -172,22 +162,22 @@ hsa_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* { if(data->phase == ACTIVITY_API_PHASE_ENTER) { - begin_timestamp = timer->timestamp_fn_ns(); + begin_timestamp = comp::wall_clock::record(); } 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(); + const auto* _name = roctracer_op_string(domain, cid, 0); + const auto end_timestamp = (cid == HSA_API_ID_hsa_shut_down) + ? begin_timestamp + : comp::wall_clock::record(); 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); + static_cast(begin_timestamp)); + TRACE_EVENT_END("device", static_cast(end_timestamp)); } if(get_use_timemory()) @@ -247,8 +237,9 @@ hsa_activity_callback(uint32_t op, activity_record_t* record, void* arg) 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); + TRACE_EVENT_BEGIN("device", perfetto::StaticString{ *_name }, + static_cast(_begin_ns)); + TRACE_EVENT_END("device", static_cast(_end_ns)); } if(get_use_timemory()) { @@ -312,6 +303,8 @@ hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* default: break; } + int64_t _ts = comp::wall_clock::record(); + if(data->phase == ACTIVITY_API_PHASE_ENTER) { switch(cid) @@ -349,21 +342,13 @@ hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* } break; } - default: - { - if(get_use_perfetto() || get_use_timemory() || get_use_rocm_smi()) - { - 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()); - } - } + default: break; } if(get_use_perfetto()) { - TRACE_EVENT_BEGIN("device", perfetto::StaticString{ op_name }); + TRACE_EVENT_BEGIN("device", perfetto::StaticString{ op_name }, + static_cast(_ts)); } if(get_use_timemory()) { @@ -388,8 +373,7 @@ hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* 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( + 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() }; @@ -405,7 +389,7 @@ hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* if(get_use_perfetto()) { - TRACE_EVENT_END("device"); + TRACE_EVENT_END("device", static_cast(_ts)); } if(get_use_timemory()) { @@ -438,8 +422,7 @@ hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* std::tie(_cid, _parent_cid, _depth) = get_roctracer_cid_data().at(data->correlation_id); } - int64_t _ts = comp::wall_clock::record(); - add_critical_trace( + add_critical_trace( threading::get_id(), _cid, data->correlation_id, _parent_cid, _ts, _ts, critical_trace::add_hash_id(op_name), _depth); } @@ -471,20 +454,50 @@ hip_activity_callback(const char* begin, const char* end, void*) OMNITRACE_DEBUG("Activity records:\n"); + auto&& _advance_record = [&record]() { + ROCTRACER_CALL(roctracer_next_record(record, &record)); + }; + while(record < end_record) { + // make sure every iteration advances regardless of where return point happens + scope::destructor _next_dtor{ _advance_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); + switch(record->kind) + { + case HIP_API_ID_hipLaunchKernel: + case HIP_API_ID_hipLaunchCooperativeKernel: + case HIP_API_ID_hipModuleLaunchKernel: break; + case HIP_API_ID_hipGetLastError: continue; + default: + { + if(op_name != nullptr && strcmp(op_name, "unknown") != 0 && + strcmp(op_name, "InternalMarker") != 0) + { + OMNITRACE_BASIC_VERBOSE_F(2, "[%s] ignoring callback for %s\n", + __FUNCTION__, op_name); + continue; + } + break; + } + } + + auto _dev_id = record->device_id; + auto _thr_id = record->thread_id; + auto _prc_id = record->process_id; + auto _que_id = record->queue_id; + if(op_name != nullptr) { - OMNITRACE_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); + OMNITRACE_DEBUG( + "\t%-30s\tcorrelation_id(%6lu) time_ns(%12lu:%12lu) " + "delta_ns(%12lu) device_id(%d) stream_id(%lu) proc_id(%u) thr_id(%u)\n", + op_name, record->correlation_id, record->begin_ns, record->end_ns, + (record->end_ns - record->begin_ns), _dev_id, _que_id, _prc_id, _thr_id); } auto _begin_ns = record->begin_ns; @@ -543,8 +556,8 @@ hip_activity_callback(const char* begin, const char* end, void*) TRACE_EVENT_BEGIN( "device", perfetto::StaticString{ _kernel_names.at(_name).c_str() }, - _begin_ns); - TRACE_EVENT_END("device", _end_ns); + static_cast(_begin_ns)); + TRACE_EVENT_END("device", static_cast(_end_ns)); } if(get_use_timemory()) { @@ -589,8 +602,6 @@ hip_activity_callback(const char* begin, const char* end, void*) tim::auto_lock_t _lk{ get_hip_activity_mutex(_tid) }; _async_ops->emplace_back(std::move(_func)); } - - ROCTRACER_CALL(roctracer_next_record(record, &record)); } } diff --git a/src/library/config.cpp b/source/lib/src/library/config.cpp similarity index 97% rename from src/library/config.cpp rename to source/lib/src/library/config.cpp index cff5ab2c99..0557401cd4 100644 --- a/src/library/config.cpp +++ b/source/lib/src/library/config.cpp @@ -127,11 +127,6 @@ configure_settings() OMNITRACE_CONFIG_SETTING(std::string, "OMNITRACE_ROCM_SMI_DEVICES", "Devices to query when OMNITRACE_USE_ROCM_SMI=ON", "all", "backend", "rocm-smi"); - - OMNITRACE_CONFIG_SETTING( - double, "OMNITRACE_ROCM_SMI_FREQ", - "Number of rocm-smi samples per second when OMNITTRACE_USE_ROCM_SMI=ON", 5.0, - "backend", "rocm-smi"); #endif OMNITRACE_CONFIG_SETTING(bool, "OMNITRACE_USE_SAMPLING", @@ -144,7 +139,7 @@ configure_settings() "io"); OMNITRACE_CONFIG_SETTING(bool, "OMNITRACE_USE_KOKKOSP", - "Enable support for Kokkos Tools", true, "kokkos"); + "Enable support for Kokkos Tools", false, "kokkos"); OMNITRACE_CONFIG_SETTING(size_t, "OMNITRACE_INSTRUMENTATION_INTERVAL", "Instrumentation only takes measurements once every N " @@ -335,16 +330,6 @@ configure_settings() if(tim::mpi::is_initialized()) settings::default_process_suffix() = tim::mpi::rank(); #endif OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose_env() > 0, "configuration complete\n"); - -#if defined(OMNITRACE_USE_ROCM_SMI) - auto _rsmi_freq = _config->find("OMNITRACE_ROCM_SMI_FREQ"); - if(_rsmi_freq != _config->end()) - { - double& _rsmi_freq_v = - static_cast&>(*_rsmi_freq->second).get(); - if(_rsmi_freq_v > 1000) _rsmi_freq_v = 1000.; - } -#endif } void @@ -846,17 +831,21 @@ get_critical_trace_count() } double& -get_rocm_smi_freq() +get_thread_sampling_freq() { - static auto _v = get_config()->find("OMNITRACE_ROCM_SMI_FREQ"); - return static_cast&>(*_v->second).get(); + static auto _v = std::min(get_sampling_freq(), 1000.0); + return _v; } std::string get_rocm_smi_devices() { +#if defined(OMNITRACE_USE_ROCM_SMI) static auto _v = get_config()->find("OMNITRACE_ROCM_SMI_DEVICES"); return static_cast&>(*_v->second).get(); +#else + return std::string{}; +#endif } bool diff --git a/source/lib/src/library/cpu_freq.cpp b/source/lib/src/library/cpu_freq.cpp new file mode 100644 index 0000000000..cc7a552993 --- /dev/null +++ b/source/lib/src/library/cpu_freq.cpp @@ -0,0 +1,184 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "library/cpu_freq.hpp" +#include "library/common.hpp" +#include "library/config.hpp" +#include "library/debug.hpp" +#include "library/defines.hpp" +#include "library/perfetto.hpp" +#include "library/timemory.hpp" + +#include +#include +#include + +namespace omnitrace +{ +namespace cpu_freq +{ +namespace +{ +struct cpu_freq +{}; +using freq_pair_t = std::pair; +std::vector> cpu_frequencies = {}; + +struct cpu_mem +{}; +using cpu_mem_usage_pair_t = std::pair; +std::deque cpu_mem_usage = {}; + +int64_t ncpu = threading::affinity::hw_concurrency(); +std::unique_ptr ifs = {}; +std::vector cpu_mhz_pos = {}; + +} // namespace + +void +setup() +{ + perfetto_counter_track::init(); + perfetto_counter_track::init(); +} + +void +config() +{ + auto _ncpu = threading::affinity::hw_concurrency(); + std::vector _cpu_mhz_pos{}; + std::ifstream _ifs{ "/proc/cpuinfo" }; + if(_ifs) + { + for(size_t i = 0; i < _ncpu; ++i) + { + short _n = 0; + std::string _st{}; + while(_ifs && _ifs.good()) + { + std::string _s{}; + _ifs >> _s; + if(!_ifs.good() || !_ifs) break; + + if(_s == "cpu" || _s == "MHz" || _s == ":") + { + ++_n; + _st += _s + " "; + } + else + { + _n = 0; + _st = {}; + } + + if(_n == 3) + { + size_t _pos = _ifs.tellg(); + _cpu_mhz_pos.emplace_back(_pos + 1); + _ifs >> _s; + if(!_ifs.good() || !_ifs) break; + OMNITRACE_CONDITIONAL_BASIC_PRINT(get_debug() || get_verbose() > 1, + "[%zu] %s %s (pos = %zu)\n", i, + _st.c_str(), _s.c_str(), _pos + 1); + break; + } + } + } + } + + _ifs.close(); + + cpu_frequencies.resize(_ncpu); + cpu_mhz_pos = _cpu_mhz_pos; + ifs = std::make_unique("/proc/cpuinfo", std::ifstream::binary); +} + +void +sample() +{ + cpu_mem_usage.emplace_back(tim::get_clock_real_now(), + tim::get_page_rss()); + + if(!ifs) return; + + auto _read_cpu_freq = [](size_t _idx) { + double _freq = 0; + ifs->seekg(cpu_mhz_pos.at(_idx), ifs->beg); + (*ifs) >> _freq; + return _freq; + }; + + auto _ts = tim::get_clock_real_now(); + for(int64_t i = 0; i < ncpu; ++i) + cpu_frequencies.at(i).emplace_back(_ts, _read_cpu_freq(i)); +} + +void +shutdown() +{} + +void +post_process() +{ + OMNITRACE_PRINT("Post-processing %zu cpu freqs and %zu memory usage entries\n", + cpu_frequencies.size(), cpu_mem_usage.size()); + auto _process_frequencies = [](size_t _idx) { + using counter_track = perfetto_counter_track; + if(!counter_track::exists(_idx)) + { + auto _devname = TIMEMORY_JOIN("", "[CPU ", _idx, "] "); + auto addendum = [&](const char* _v) { return _devname + std::string{ _v }; }; + counter_track::emplace(_idx, addendum("Frequency (S)"), "MHz"); + } + + for(auto& itr : cpu_frequencies.at(_idx)) + { + uint64_t _ts = itr.first; + double _freq = itr.second; + TRACE_COUNTER("sampling", counter_track::at(_idx, 0), _ts, _freq); + } + }; + + auto _process_cpu_mem_usage = []() { + using counter_track = perfetto_counter_track; + if(!counter_track::exists(0)) + { + auto _devname = TIMEMORY_JOIN("", "[CPU] "); + auto addendum = [&](const char* _v) { return _devname + std::string{ _v }; }; + counter_track::emplace(0, addendum("Memory Usage (S)"), "MB"); + } + + for(auto& itr : cpu_mem_usage) + { + uint64_t _ts = itr.first; + double _mem_usage = itr.second; + TRACE_COUNTER("sampling", counter_track::at(0, 0), _ts, + _mem_usage / units::megabyte); + } + }; + + _process_cpu_mem_usage(); + for(size_t i = 0; i < cpu_frequencies.size(); ++i) + _process_frequencies(i); +} +} // namespace cpu_freq +} // namespace omnitrace diff --git a/src/library/critical_trace.cpp b/source/lib/src/library/critical_trace.cpp similarity index 51% rename from src/library/critical_trace.cpp rename to source/lib/src/library/critical_trace.cpp index 35dfa24050..2f30c566bd 100644 --- a/src/library/critical_trace.cpp +++ b/source/lib/src/library/critical_trace.cpp @@ -66,26 +66,6 @@ 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() { @@ -417,93 +397,85 @@ call_chain::get_top_chains() return _v; } -template -bool -call_chain::query(FuncT&& _func) const +template <> +void +call_chain::generate_perfetto(std::set& _used) const { for(const auto& itr : *this) { - if(std::forward(_func)(itr)) return BoolV; - } - return !BoolV; -} - -template <> -void -call_chain::generate_perfetto(std::set&, bool) const -{} - -template <> -void -call_chain::generate_perfetto(std::set& _used, bool _basic) const -{ - static std::set _static_strings{}; - static std::mutex _static_mutex{}; - for(const auto& itr : *this) - { - if(_basic) + if(!_used.emplace(itr).second) continue; + if(itr.device == Device::CPU) { - if(itr.device == Device::CPU) - { - TRACE_EVENT_BEGIN("device-critical-trace", "CPU", - static_cast(itr.begin_ns)); - } - else if(itr.device == Device::GPU) - { - TRACE_EVENT_BEGIN("device-critical-trace", "GPU", - static_cast(itr.begin_ns)); - } - TRACE_EVENT_END("device-critical-trace", static_cast(itr.end_ns)); - } - else - { - 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() }, + TRACE_EVENT_BEGIN("device-critical-trace", "CPU", static_cast(itr.begin_ns)); - TRACE_EVENT_END("host-critical-trace", static_cast(itr.end_ns)); } + else if(itr.device == Device::GPU) + { + TRACE_EVENT_BEGIN("device-critical-trace", "GPU", + static_cast(itr.begin_ns)); + } + TRACE_EVENT_END("device-critical-trace", static_cast(itr.end_ns)); } } template <> void -call_chain::generate_perfetto(std::set& _used, bool _basic) const +call_chain::generate_perfetto(std::set& _used) const { static std::set _static_strings{}; static std::mutex _static_mutex{}; for(const auto& itr : *this) { - if(_basic) - { - if(itr.device == Device::CPU) - { - TRACE_EVENT_BEGIN("device-critical-trace", "CPU", - static_cast(itr.begin_ns)); - } - else if(itr.device == Device::GPU) - { - TRACE_EVENT_BEGIN("device-critical-trace", "GPU", - static_cast(itr.begin_ns)); - } - TRACE_EVENT_END("device-critical-trace", static_cast(itr.end_ns)); - } - else - { - 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)); - } + if(!_used.emplace(itr).second) continue; + if(itr.device != Device::CPU) 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; + if(itr.device != Device::GPU) 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)); + } +} + +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("critical-trace", perfetto::StaticString{ sitr.first->c_str() }, + static_cast(itr.begin_ns)); + TRACE_EVENT_END("critical-trace", static_cast(itr.end_ns)); } } @@ -590,8 +562,6 @@ compute(int64_t _tid) namespace { -//--------------------------------------------------------------------------------------// - std::string get_perf_name(std::string _func) { @@ -603,141 +573,6 @@ get_perf_name(std::string _func) 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 = {}) -{ - OMNITRACE_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("omnitrace"); - 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__; - OMNITRACE_CONDITIONAL_PRINT(get_verbose() >= 0, "[%s] Outputting '%s'...\n", - _func.c_str(), _fname.c_str()); - } - ofs << oss.str() << std::endl; - } - - _perf.stop(); - if(_msg) - { - OMNITRACE_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 = {}) -{ - OMNITRACE_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("omnitrace"); - 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__; - OMNITRACE_CONDITIONAL_PRINT(get_verbose() >= 0, "[%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) - { - OMNITRACE_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 = {}) -{ - OMNITRACE_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__; - OMNITRACE_CONDITIONAL_PRINT(get_verbose() >= 0, "[%s] Outputting '%s'...\n", - _func.c_str(), _fname.c_str()); - } - ofs << _call_chain << "\n"; - } - - _perf.stop(); - if(_msg) - { - OMNITRACE_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, @@ -792,24 +627,6 @@ save_call_chain_json(const std::string& _fname, const std::string& _label, } } -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("omnitrace"); - 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* @@ -912,224 +729,16 @@ combine_critical_path(call_chain& _targ, call_chain _chain) // squash_critical_path(_targ); } -auto -get_indexed(const call_chain& _chain) -{ - OMNITRACE_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) -{ - OMNITRACE_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 - OMNITRACE_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; - OMNITRACE_PRINT("Warning! Duplicate entry for [%s] :: [%s]\n", - JOIN("", _existing).c_str(), JOIN("", itr).c_str()); - } - } - - task_group_t _tg{ &_tp }; - OMNITRACE_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()) - { - OMNITRACE_CT_DEBUG("[%s] Setting root (line %i)...\n", __FUNCTION__, __LINE__); - _graph.set_head(_indexed.at(-1).front()); - } - else - { - OMNITRACE_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) - { - OMNITRACE_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) -{ - OMNITRACE_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) - { - OMNITRACE_CT_DEBUG("Skipping node #%zu with %u children :: %s\n", _n, _nchild, - JOIN("", *itr).c_str()); - continue; - } - _end_nodes.emplace_back(itr); - } - OMNITRACE_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) -{ - OMNITRACE_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) +update_critical_path(call_chain _chain, int64_t) { OMNITRACE_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()); + // 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) { @@ -1138,56 +747,6 @@ update_critical_path(call_chain _chain, int64_t _tid) } } -template -std::vector -get_top(const std::vector& _chain, size_t _count) -{ - OMNITRACE_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, bool _basic = false) -{ - OMNITRACE_CT_DEBUG("[%s]\n", __FUNCTION__); - - auto _nrows = std::min(get_critical_trace_per_row(), _data.size()); - - // run in separate thread(s) so that it ends up in unique row - if(_nrows < 1) _nrows = _data.size(); - - auto _func = [&](size_t _idx, size_t _beg, size_t _end) { - if(_nrows != 1) - threading::set_thread_name(TIMEMORY_JOIN(" ", "CriticalPath", _idx).c_str()); - else - threading::set_thread_name("CritialPath"); - // 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, _basic); - } - }; - - for(size_t i = 0; i < _data.size(); i += _nrows) - { - std::thread{ _func, i, i, i + _nrows }.join(); - } -} - void compute_critical_trace() { @@ -1196,7 +755,8 @@ compute_critical_trace() if(_computed) return; - OMNITRACE_CT_DEBUG("[%s] Generating critical trace...\n", __FUNCTION__); + OMNITRACE_CONDITIONAL_PRINT(get_critical_trace_debug() || get_verbose() >= 0, + "[%s] Generating critical trace...\n", __FUNCTION__); // ensure all hash ids exist copy_hash_ids(); @@ -1209,11 +769,6 @@ compute_critical_trace() try { - PTL::ThreadPool _tp{ get_critical_trace_num_threads(), []() { copy_hash_ids(); }, - []() {} }; - _tp.set_verbose(-1); - PTL::TaskGroup _tg{ &_tp }; - OMNITRACE_CT_DEBUG("[%s] initial call chain: %zu entries\n", __FUNCTION__, complete_call_chain.size()); @@ -1225,110 +780,14 @@ compute_critical_trace() _perf.stop().rekey("Sorting critical trace"); OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); - _perf.start(); - - squash_critical_path(complete_call_chain); - - OMNITRACE_CT_DEBUG("[%s] complete call chain: %zu entries\n", __FUNCTION__, - complete_call_chain.size()); - - _perf.stop().rekey("Squash critical path"); - OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); - - _tg.run( - [](call_chain _chain, std::string _func) { // NOLINT - 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(); + save_call_chain_json( + tim::settings::compose_output_filename("call-chain", ".json"), "call_chain", + complete_call_chain, true, __FUNCTION__); - OMNITRACE_CT_DEBUG("[%s] Finding children...\n", __FUNCTION__); - call_graph_t _graph{}; - find_children(_tp, _graph, complete_call_chain); - - OMNITRACE_CT_DEBUG("[%s] complete call graph: %zu entries\n", __FUNCTION__, - _graph.size() - 1); - - _perf.stop().rekey("Finding children"); + _perf.stop().rekey("Save call-chain"); OMNITRACE_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(); - - generate_perfetto({ complete_call_chain }, true); - - OMNITRACE_CT_DEBUG("[%s] Finding sequences...\n", __FUNCTION__); - // find the sequences - std::vector _top{}; - find_sequences(_tp, _graph, _top); - - _perf.stop().rekey("Finding sequences"); - OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).c_str()); - - if(get_critical_trace_count() == 0) - { - OMNITRACE_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 - { - OMNITRACE_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()) - { - OMNITRACE_CT_DEBUG("[%s] Generating perfetto CPU critical traces...\n", - __FUNCTION__); - generate_perfetto(_top_cpu); - OMNITRACE_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__); - } - - OMNITRACE_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()) - { - OMNITRACE_CT_DEBUG("[%s] Generating perfetto GPU critical traces...\n", - __FUNCTION__); - generate_perfetto(_top_gpu); - OMNITRACE_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(std::exception& e) { OMNITRACE_PRINT("Thread exited '%s' with exception: %s\n", __FUNCTION__, diff --git a/src/library/gpu.cpp b/source/lib/src/library/gpu.cpp similarity index 100% rename from src/library/gpu.cpp rename to source/lib/src/library/gpu.cpp diff --git a/src/library/kokkosp.cpp b/source/lib/src/library/kokkosp.cpp similarity index 98% rename from src/library/kokkosp.cpp rename to source/lib/src/library/kokkosp.cpp index e79e1a8231..ba3374c571 100644 --- a/src/library/kokkosp.cpp +++ b/source/lib/src/library/kokkosp.cpp @@ -319,8 +319,4 @@ extern "C" //----------------------------------------------------------------------------------// } -//--------------------------------------------------------------------------------------// - TIMEMORY_INITIALIZE_STORAGE(kokkosp::memory_tracker) - -//--------------------------------------------------------------------------------------// diff --git a/src/library/perfetto.cpp b/source/lib/src/library/perfetto.cpp similarity index 100% rename from src/library/perfetto.cpp rename to source/lib/src/library/perfetto.cpp diff --git a/src/library/ptl.cpp b/source/lib/src/library/ptl.cpp similarity index 100% rename from src/library/ptl.cpp rename to source/lib/src/library/ptl.cpp diff --git a/src/library/sampling.cpp b/source/lib/src/library/sampling.cpp similarity index 100% rename from src/library/sampling.cpp rename to source/lib/src/library/sampling.cpp diff --git a/src/library/state.cpp b/source/lib/src/library/state.cpp similarity index 100% rename from src/library/state.cpp rename to source/lib/src/library/state.cpp diff --git a/src/library/thread_data.cpp b/source/lib/src/library/thread_data.cpp similarity index 100% rename from src/library/thread_data.cpp rename to source/lib/src/library/thread_data.cpp diff --git a/source/lib/src/library/thread_sampler.cpp b/source/lib/src/library/thread_sampler.cpp new file mode 100644 index 0000000000..7ea9a40b5e --- /dev/null +++ b/source/lib/src/library/thread_sampler.cpp @@ -0,0 +1,196 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "library/thread_sampler.hpp" +#include "library/components/rocm_smi.hpp" +#include "library/config.hpp" +#include "library/cpu_freq.hpp" +#include "library/debug.hpp" + +#include +#include + +namespace omnitrace +{ +namespace thread_sampler +{ +namespace +{ +using tim::type_mutex; +using auto_lock_t = tim::auto_lock_t; +using promise_t = std::promise; +std::unique_ptr polling_finished = {}; +std::vector> instances = {}; + +bool& +is_initialized() +{ + static bool _v = false; + return _v; +} + +std::unique_ptr& +get_thread() +{ + static std::unique_ptr _v; + return _v; +} + +std::atomic& +get_sampler_state() +{ + static std::atomic _v{ State::PreInit }; + return _v; +} +} // namespace + +void +sampler::poll(std::atomic* _state, nsec_t _interval, promise_t* _ready) +{ + threading::set_thread_name("omni.sampler"); + + // notify thread started + if(_ready) _ready->set_value(); + + for(auto& itr : instances) + itr->config(); + + OMNITRACE_CONDITIONAL_BASIC_PRINT( + get_verbose() > 0 || get_debug(), + "Thread sampler polling at an interval of %f seconds...\n", + std::chrono::duration_cast>(_interval).count()); + + auto _now = std::chrono::steady_clock::now(); + while(_state && _state->load() != State::Finalized && get_state() != State::Finalized) + { + std::this_thread::sleep_until(_now); + if(_state->load() != State::Active) continue; + for(auto& itr : instances) + itr->sample(); + while(_now < std::chrono::steady_clock::now()) + _now += _interval; + } + + OMNITRACE_CONDITIONAL_BASIC_PRINT(get_debug(), + "Thread sampler polling completed...\n"); + + if(polling_finished) polling_finished->set_value(); +} + +void +sampler::setup() +{ + OMNITRACE_VERBOSE(1, "Setting up background sampler...\n"); + + // shutdown if already running + shutdown(); + + auto _enable_samp = pthread_gotcha::enable_sampling_on_child_threads(); + pthread_gotcha::enable_sampling_on_child_threads() = false; + + if(get_use_rocm_smi()) + { + auto& _rocm_smi = instances.emplace_back(std::make_unique()); + _rocm_smi->setup = []() { rocm_smi::setup(); }; + _rocm_smi->shutdown = []() { rocm_smi::shutdown(); }; + _rocm_smi->post_process = []() { rocm_smi::post_process(); }; + _rocm_smi->config = []() { rocm_smi::config(); }; + _rocm_smi->sample = []() { rocm_smi::sample(); }; + } + + auto& _cpu_freq = instances.emplace_back(std::make_unique()); + _cpu_freq->setup = []() { cpu_freq::setup(); }; + _cpu_freq->shutdown = []() { cpu_freq::shutdown(); }; + _cpu_freq->post_process = []() { cpu_freq::post_process(); }; + _cpu_freq->config = []() { cpu_freq::config(); }; + _cpu_freq->sample = []() { cpu_freq::sample(); }; + + for(auto& itr : instances) + itr->setup(); + + polling_finished = std::make_unique(); + + auto _freq = get_thread_sampling_freq(); + uint64_t _msec_freq = (1.0 / _freq) * 1.0e3; + + promise_t _prom{}; + auto _fut = _prom.get_future(); + polling_finished = std::make_unique(); + + set_state(State::PreInit); + get_thread() = std::make_unique(&poll, &get_sampler_state(), + msec_t{ _msec_freq }, &_prom); + + _fut.wait(); + + pthread_gotcha::enable_sampling_on_child_threads() = _enable_samp; + set_state(State::Active); +} + +void +sampler::shutdown() +{ + for(auto& itr : instances) + itr->shutdown(); + + auto& _thread = get_thread(); + if(_thread) + { + OMNITRACE_VERBOSE(1, "Shutting down background sampler...\n"); + set_state(State::Finalized); + if(polling_finished) + { + auto _fut = polling_finished->get_future(); + uint64_t _freq = (1.0 / get_thread_sampling_freq()) * 1.0e3; + _fut.wait_for(msec_t{ 5 * _freq }); + _thread->join(); + } + else + { + uint64_t _freq = (1.0 / get_thread_sampling_freq()) * 1.0e3; + std::this_thread::sleep_for(msec_t{ 5 * _freq }); + pthread_cancel(_thread->native_handle()); + _thread->detach(); + } + _thread = std::unique_ptr{}; + polling_finished = std::unique_ptr{}; + } + + is_initialized() = false; +} + +void +sampler::post_process() +{ + for(auto& itr : instances) + itr->post_process(); + + instances.clear(); +} + +void +sampler::set_state(state_t _state) +{ + get_sampler_state().store(_state); +} +} // namespace thread_sampler +} // namespace omnitrace diff --git a/src/library/timemory.cpp b/source/lib/src/library/timemory.cpp similarity index 100% rename from src/library/timemory.cpp rename to source/lib/src/library/timemory.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f034325d13..370333dce5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -214,7 +214,7 @@ omnitrace_add_test( REWRITE_ARGS -e -v 2 RUNTIME_ARGS -e -v 1 --label file line return args RUN_ARGS "" - ENVIRONMENT "${_base_environment};OMNITRACE_CRITICAL_TRACE=OFF") + ENVIRONMENT "${_base_environment};OMNITRACE_CRITICAL_TRACE=ON") omnitrace_add_test( NAME transpose-no-save-fpr