From 72d0a7d08af513efe26f27e4f069afb59d5ab823 Mon Sep 17 00:00:00 2001 From: "Jonathan R. Madsen" Date: Mon, 25 Apr 2022 17:00:52 -0500 Subject: [PATCH] Code Coverage Support (#46) * Code-coverage support * Examples update - code-coverage example - tweak transpose and parallel-overhead * Coverage output + testing - config::get_setting value(...) - REGULAR_EXPRESSION -> REGEX in cmake func args - coverage.hpp header - coverage JSON - coverage tests * cmake formatting * Library instrumentation w/o main + more - fixed library instrumentation w/o main - use TIMEMORY_PROJECT_NAME in output messages - removed '--driver' option from omnitrace exe - support coverage in trace mode - OMNITRACE_KOKKOS_KERNEL_LOGGER - support multiple calls to omnitrace_set_env after init if already called - support multiple calls to omnitrace_set_mpi after init if same args - support multiple calls to omnitrace_init if same mode - unique_ptr_t for thread_data which calls finalize when thread_data is destroyed - tweaked openmp tests - improved finalization * Replace CI --output-on-failure with -V * Fix to OMNITRACE_DL_INVOKE * omnitrace-exe and testing updates - omnitrace::omnitrace-timemory interface library - support for configs in omnitrace exe - print-{available,instrumented,...} opts no longer exit w/o --simulate - all tests apply --print-instrumented functions - tweaked coverage tests - print-* options print instructions not address range * Remove OMNITRACE_DEBUG_FINALIZE=ON from CI * Python cmake tweaks * Tweak test ordering * Upload CI artifacts if fail or success * CI Python tweaks - Use OMNITRACE_PYTHON_PREFIX and OMNITRACE_PYTHON_ENVS * CI ELFULTILS_DOWNLOAD_VERSION * test tweaks - labels and more coverage tests * tweak to omnitrace --config handling * Update module/function constraint handling + PP - tweak pre-processor definition handling - removed free-standing module_constraint - remove free-standing routine_constraint - remove module_name.find("omnitrace") module constraint - fully handle the output path of omnitrace *-instr files - get_use_code_coverage config option - print-coverage option - coverage_module_functions * use github.job not github.name * Re-enable HSA_ENABLE_INTERRUPT - remove coverage address report [ROCm/rocprofiler-systems commit: 791375bb24828ae29c0da766272b2621f57d4b89] --- .../rocprofiler-systems/.cmake-format.yaml | 39 +- .../.github/workflows/ubuntu-bionic.yml | 16 +- .../workflows/ubuntu-focal-external-rocm.yml | 12 +- .../workflows/ubuntu-focal-external.yml | 16 +- .../.github/workflows/ubuntu-focal.yml | 15 +- .../cmake/MacroUtilities.cmake | 202 ++++++ .../rocprofiler-systems/cmake/Packages.cmake | 7 + .../examples/CMakeLists.txt | 1 + .../examples/code-coverage/CMakeLists.txt | 16 + .../examples/code-coverage/code-coverage.cpp | 88 +++ .../parallel-overhead/parallel-overhead.cpp | 17 +- .../examples/transpose/transpose.cpp | 34 +- .../rocprofiler-systems/external/timemory | 2 +- .../source/bin/omnitrace/CMakeLists.txt | 2 +- .../source/bin/omnitrace/details.cpp | 135 +++- .../bin/omnitrace/function_signature.cpp | 60 +- .../bin/omnitrace/function_signature.hpp | 12 +- .../source/bin/omnitrace/fwd.hpp | 66 +- .../source/bin/omnitrace/info.hpp | 8 +- .../source/bin/omnitrace/module_function.cpp | 262 ++++++- .../source/bin/omnitrace/module_function.hpp | 11 + .../source/bin/omnitrace/omnitrace.cpp | 686 ++++++++---------- .../source/bin/omnitrace/omnitrace.hpp | 104 ++- .../source/bin/tests/CMakeLists.txt | 30 +- .../source/lib/common/invoke.hpp | 3 +- .../source/lib/omnitrace-dl/dl.cpp | 142 +++- .../source/lib/omnitrace-dl/dl.hpp | 5 + .../source/lib/omnitrace/CMakeLists.txt | 6 +- .../source/lib/omnitrace/include/library.hpp | 1 + .../lib/omnitrace/include/library/api.hpp | 16 + .../include/library/components/rocm_smi.hpp | 3 +- .../lib/omnitrace/include/library/config.hpp | 18 + .../omnitrace/include/library/coverage.hpp | 167 +++++ .../include/library/critical_trace.hpp | 2 +- .../lib/omnitrace/include/library/runtime.hpp | 13 +- .../omnitrace/include/library/sampling.hpp | 4 +- .../lib/omnitrace/include/library/state.hpp | 3 +- .../omnitrace/include/library/thread_data.hpp | 47 +- .../source/lib/omnitrace/src/library.cpp | 86 ++- .../source/lib/omnitrace/src/library/api.cpp | 13 + .../src/library/components/backtrace.cpp | 9 +- .../src/library/components/mpi_gotcha.cpp | 2 +- .../src/library/components/pthread_gotcha.cpp | 1 + .../src/library/components/rocm_smi.cpp | 6 +- .../components/roctracer_callbacks.cpp | 20 +- .../lib/omnitrace/src/library/config.cpp | 59 +- .../lib/omnitrace/src/library/coverage.cpp | 424 +++++++++++ .../omnitrace/src/library/critical_trace.cpp | 7 +- .../source/lib/omnitrace/src/library/gpu.cpp | 8 +- .../lib/omnitrace/src/library/kokkosp.cpp | 37 +- .../lib/omnitrace/src/library/runtime.cpp | 4 +- .../lib/omnitrace/src/library/sampling.cpp | 4 +- .../lib/omnitrace/src/library/state.cpp | 1 + .../omnitrace/src/library/thread_sampler.cpp | 1 + .../source/python/CMakeLists.txt | 154 ++-- .../source/python/setup.cfg.in | 1 - .../source/python/setup.py.in | 3 +- .../rocprofiler-systems/tests/CMakeLists.txt | 255 ++++++- 58 files changed, 2572 insertions(+), 794 deletions(-) create mode 100644 projects/rocprofiler-systems/examples/code-coverage/CMakeLists.txt create mode 100644 projects/rocprofiler-systems/examples/code-coverage/code-coverage.cpp create mode 100644 projects/rocprofiler-systems/source/lib/omnitrace/include/library/coverage.hpp create mode 100644 projects/rocprofiler-systems/source/lib/omnitrace/src/library/coverage.cpp diff --git a/projects/rocprofiler-systems/.cmake-format.yaml b/projects/rocprofiler-systems/.cmake-format.yaml index ab649c7bb0..4c15e28863 100644 --- a/projects/rocprofiler-systems/.cmake-format.yaml +++ b/projects/rocprofiler-systems/.cmake-format.yaml @@ -19,6 +19,11 @@ parse: VARIABLES: '*' CONDITION: '*' omnitrace_add_test: + flags: + - SKIP_BASELINE + - SKIP_REWRITE + - SKIP_RUNTIME + - SKIP_SAMPLING kwargs: NAME: '*' TARGET: '*' @@ -32,6 +37,12 @@ parse: ENVIRONMENT: '*' LABELS: '*' PROPERTIES: '*' + RUNTIME_PASS_REGEX: '*' + RUNTIME_FAIL_REGEX: '*' + REWRITE_PASS_REGEX: '*' + REWRITE_FAIL_REGEX: '*' + REWRITE_RUN_PASS_REGEX: '*' + REWRITE_RUN_FAIL_REGEX: '*' omnitrace_target_compile_definitions: kwargs: PUBLIC: '*' @@ -51,9 +62,9 @@ parse: PROPERTIES: '*' ENVIRONMENT: '*' WORKING_DIRECTORY: '*' - PASS_REGULAR_EXPRESSION: '*' - FAIL_REGULAR_EXPRESSION: '*' - SKIP_REGULAR_EXPRESSION: '*' + PASS_REGEX: '*' + FAIL_REGEX: '*' + SKIP_REGEX: '*' omnitrace_add_python_test: flags: - STANDALONE @@ -70,9 +81,9 @@ parse: PROPERTIES: '*' PYTHON_EXECUTABLE: '*' PYTHON_VERSION: '*' - PASS_REGULAR_EXPRESSION: '*' - FAIL_REGULAR_EXPRESSION: '*' - SKIP_REGULAR_EXPRESSION: '*' + PASS_REGEX: '*' + FAIL_REGEX: '*' + SKIP_REGEX: '*' rocm_version_message: flags: - STATUS @@ -105,6 +116,22 @@ parse: PYTHON_VERSION: '*' CXX_STANDARD: '*' VISIBILITY: '*' + omnitrace_directory: + flags: + - MKDIR + - FAIL + kwargs: + PREFIX: '*' + OUTPUT_VARIABLE: '*' + WORKING_DIRECTORY: '*' + PATHS: '*' + omnitrace_check_python_dirs_and_versions: + flags: + - UNSET + - FAIL + kwargs: + RESULT_VARIABLE: '*' + OUTPUT_VARIABLE: '*' override_spec: {} vartags: [] proptags: [] diff --git a/projects/rocprofiler-systems/.github/workflows/ubuntu-bionic.yml b/projects/rocprofiler-systems/.github/workflows/ubuntu-bionic.yml index d3ab21152e..1ee65a7c12 100644 --- a/projects/rocprofiler-systems/.github/workflows/ubuntu-bionic.yml +++ b/projects/rocprofiler-systems/.github/workflows/ubuntu-bionic.yml @@ -9,7 +9,6 @@ on: env: BUILD_TYPE: Release ELFUTILS_DOWNLOAD_VERSION: 0.183 - OMNITRACE_DEBUG_FINALIZE: ON OMNITRACE_VERBOSE: 1 OMNITRACE_CI: ON GIT_DISCOVERY_ACROSS_FILESYSTEM: 1 @@ -81,8 +80,8 @@ jobs: -DOMNITRACE_USE_MPI=${USE_MPI} -DOMNITRACE_USE_HIP=OFF -DOMNITRACE_USE_PYTHON=ON - -DOMNITRACE_PYTHON_VERSIONS="3.6;3.7;3.8;3.9" - -DOMNITRACE_PYTHON_ROOT_DIRS="/opt/conda/envs/py3.6;/opt/conda/envs/py3.7;/opt/conda/envs/py3.8;/opt/conda/envs/py3.9" + -DOMNITRACE_PYTHON_PREFIX=/opt/conda/envs + -DOMNITRACE_PYTHON_ENVS="py3.6;py3.7;py3.8;py3.9" -DLULESH_BUILD_KOKKOS=OFF - name: Build @@ -100,8 +99,8 @@ jobs: cd build && ldd ./omnitrace && ./omnitrace --help && - ctest -V -N -O omnitrace-ctest-${{ env.GITHUB_JOB }}-commands.log && - ctest --output-on-failure --output-log omnitrace-ctest-${{ env.GITHUB_JOB }}.log --stop-on-failure + ctest -V -N -O omnitrace-ctest-${{ github.job }}-commands.log && + ctest -V --output-log omnitrace-ctest-${{ github.job }}.log --stop-on-failure - name: Test Install timeout-minutes: 10 @@ -126,15 +125,18 @@ jobs: omnitrace -e -v 1 -- ls - name: CTest Artifacts + if: success() || failure() uses: actions/upload-artifact@v2 with: - name: ctest-log + name: ctest-${{ github.job }}-log path: | build/*.log - name: Data Artifacts + if: success() || failure() uses: actions/upload-artifact@v2 with: - name: data-files + name: data-${{ github.job }}-files path: | build/omnitrace-tests-output/**/*.txt + build/omnitrace-tests-output/**/*-instr*.json diff --git a/projects/rocprofiler-systems/.github/workflows/ubuntu-focal-external-rocm.yml b/projects/rocprofiler-systems/.github/workflows/ubuntu-focal-external-rocm.yml index 0cf5877258..9211c0d4f7 100644 --- a/projects/rocprofiler-systems/.github/workflows/ubuntu-focal-external-rocm.yml +++ b/projects/rocprofiler-systems/.github/workflows/ubuntu-focal-external-rocm.yml @@ -8,7 +8,6 @@ on: env: BUILD_TYPE: MinSizeRel - OMNITRACE_DEBUG_FINALIZE: ON OMNITRACE_VERBOSE: 1 OMNITRACE_CI: ON OMNITRACE_OUTPUT_PATH: omnitrace-tests-output @@ -84,8 +83,8 @@ jobs: cd build && ldd ./omnitrace && ./omnitrace --help && - ctest -V -N -O omnitrace-ctest-${{ env.GITHUB_JOB }}-commands.log && - ctest --output-on-failure --output-log omnitrace-ctest-${{ env.GITHUB_JOB }}.log --stop-on-failure + ctest -V -N -O omnitrace-ctest-${{ github.job }}-commands.log && + ctest -V --output-log omnitrace-ctest-${{ github.job }}.log --stop-on-failure - name: Test Install timeout-minutes: 10 @@ -108,16 +107,19 @@ jobs: omnitrace -e -v 1 -- ls - name: CTest Artifacts + if: success() || failure() uses: actions/upload-artifact@v2 with: - name: ctest-log + name: ctest-${{ github.job }}-log path: | build/*.log - name: Data Artifacts + if: success() || failure() uses: actions/upload-artifact@v2 with: - name: data-files + name: data-${{ github.job }}-files path: | omnitrace-tests-output/**/*.txt build/omnitrace-tests-output/**/*.txt + build/omnitrace-tests-output/**/*-instr*.json diff --git a/projects/rocprofiler-systems/.github/workflows/ubuntu-focal-external.yml b/projects/rocprofiler-systems/.github/workflows/ubuntu-focal-external.yml index d82522a9f0..03502f6671 100644 --- a/projects/rocprofiler-systems/.github/workflows/ubuntu-focal-external.yml +++ b/projects/rocprofiler-systems/.github/workflows/ubuntu-focal-external.yml @@ -9,7 +9,6 @@ on: env: BUILD_TYPE: RelWithDebInfo ELFUTILS_DOWNLOAD_VERSION: 0.183 - OMNITRACE_DEBUG_FINALIZE: ON OMNITRACE_VERBOSE: 1 OMNITRACE_CI: ON @@ -57,8 +56,8 @@ jobs: -DOMNITRACE_USE_HIP=OFF -DOMNITRACE_USE_OMPT=OFF -DOMNITRACE_USE_PYTHON=ON - -DOMNITRACE_PYTHON_VERSIONS="3.6;3.7;3.8;3.9" - -DOMNITRACE_PYTHON_ROOT_DIRS="/opt/conda/envs/py3.6;/opt/conda/envs/py3.7;/opt/conda/envs/py3.8;/opt/conda/envs/py3.9" + -DOMNITRACE_PYTHON_PREFIX=/opt/conda/envs + -DOMNITRACE_PYTHON_ENVS="py3.6;py3.7;py3.8;py3.9" - name: Build timeout-minutes: 45 @@ -76,8 +75,8 @@ jobs: cd build && ldd ./omnitrace && ./omnitrace --help && - ctest -V -N -O omnitrace-ctest-${{ env.GITHUB_JOB }}-commands.log && - ctest --output-on-failure --output-log omnitrace-ctest-${{ env.GITHUB_JOB }}.log --stop-on-failure + ctest -V -N -O omnitrace-ctest-${{ github.job }}-commands.log && + ctest -V --output-log omnitrace-ctest-${{ github.job }}.log --stop-on-failure - name: Test Install timeout-minutes: 10 @@ -102,15 +101,18 @@ jobs: omnitrace -e -v 1 -- ls - name: CTest Artifacts + if: success() || failure() uses: actions/upload-artifact@v2 with: - name: ctest-log + name: ctest-${{ github.job }}-log path: | build/*.log - name: Data Artifacts + if: success() || failure() uses: actions/upload-artifact@v2 with: - name: data-files + name: data-${{ github.job }}-files path: | build/omnitrace-tests-output/**/*.txt + build/omnitrace-tests-output/**/*-instr*.json diff --git a/projects/rocprofiler-systems/.github/workflows/ubuntu-focal.yml b/projects/rocprofiler-systems/.github/workflows/ubuntu-focal.yml index a6e3e6ac6d..032bfac2d3 100644 --- a/projects/rocprofiler-systems/.github/workflows/ubuntu-focal.yml +++ b/projects/rocprofiler-systems/.github/workflows/ubuntu-focal.yml @@ -8,8 +8,7 @@ on: env: BUILD_TYPE: Release - ELFUTILS_DOWNLOAD_VERSION: 0.183 - OMNITRACE_DEBUG_FINALIZE: ON + ELFUTILS_DOWNLOAD_VERSION: 0.186 OMNITRACE_VERBOSE: 1 OMNITRACE_CI: ON @@ -60,6 +59,7 @@ jobs: -DDYNINST_BUILD_LIBIBERTY=ON -DDYNINST_BUILD_SHARED_LIBS=ON -DDYNINST_BUILD_STATIC_LIBS=OFF + -DDYNINST_ELFUTILS_DOWNLOAD_VERSION=${{ env.ELFUTILS_DOWNLOAD_VERSION }} - name: Build timeout-minutes: 45 @@ -74,8 +74,8 @@ jobs: timeout-minutes: 45 working-directory: ${{ github.workspace }}/build run: - ctest -V -N -O ${{ github.workspace }}/build/omnitrace-ctest-${{ env.GITHUB_JOB }}-commands.log && - ctest --output-on-failure --output-log ${{ github.workspace }}/build/omnitrace-ctest-${{ env.GITHUB_JOB }}.log --stop-on-failure + ctest -V -N -O ${{ github.workspace }}/build/omnitrace-ctest-${{ github.job }}-commands.log && + ctest -V --output-log ${{ github.workspace }}/build/omnitrace-ctest-${{ github.job }}.log --stop-on-failure - name: Test Install timeout-minutes: 10 @@ -100,15 +100,18 @@ jobs: omnitrace -e -v 1 -- ls - name: CTest Artifacts + if: success() || failure() uses: actions/upload-artifact@v2 with: - name: ctest-log + name: ctest-${{ github.job }}-log path: | ${{ github.workspace }}/build/*.log - name: Data Artifacts + if: success() || failure() uses: actions/upload-artifact@v2 with: - name: data-files + name: data-${{ github.job }}-files path: | ${{ github.workspace }}/build/omnitrace-tests-output/**/*.txt + ${{ github.workspace }}/build/omnitrace-tests-output/**/*-instr*.json diff --git a/projects/rocprofiler-systems/cmake/MacroUtilities.cmake b/projects/rocprofiler-systems/cmake/MacroUtilities.cmake index 65a93c2a2c..b709a90e56 100644 --- a/projects/rocprofiler-systems/cmake/MacroUtilities.cmake +++ b/projects/rocprofiler-systems/cmake/MacroUtilities.cmake @@ -556,4 +556,206 @@ function(omnitrace_custom_compilation) endif() endfunction() +function(OMNITRACE_WATCH_FOR_CHANGE _var) + list(LENGTH ARGN _NUM_EXTRA_ARGS) + if(_NUM_EXTRA_ARGS EQUAL 1) + set(_VAR ${ARGN}) + else() + set(_VAR) + endif() + + macro(update_var _VAL) + if(_VAR) + set(${_VAR} + ${_VAL} + PARENT_SCOPE) + endif() + endmacro() + + update_var(OFF) + + set(_omnitrace_watch_var_name OMNITRACE_WATCH_VALUE_${_var}) + if(DEFINED ${_omnitrace_watch_var_name}) + if("${${_var}}" STREQUAL "${${_omnitrace_watch_var_name}}") + return() + else() + omnitrace_message( + STATUS + "${_var} changed :: ${${_omnitrace_watch_var_name}} --> ${${_var}}") + update_var(ON) + endif() + else() + if(NOT "${${_var}}" STREQUAL "") + omnitrace_message(STATUS "${_var} :: ${${_var}}") + update_var(ON) + endif() + endif() + + # store the value for the next run + set(${_omnitrace_watch_var_name} + "${${_var}}" + CACHE INTERNAL "Last value of ${_var}" FORCE) +endfunction() + +function(OMNITRACE_DIRECTORY) + cmake_parse_arguments(F "MKDIR;FAIL;FORCE" "PREFIX;OUTPUT_VARIABLE;WORKING_DIRECTORY" + "PATHS" ${ARGN}) + + if(F_PREFIX AND NOT IS_ABSOLUTE "${F_PREFIX}") + if(F_WORKING_DIRECTORY) + omnitrace_message( + STATUS + "PREFIX was specified as a relative path, using working directory + prefix :: '${F_WORKING_DIRECTORY}/${F_PREFIX}'..." + ) + set(F_PREFIX ${F_WORKING_DIRECTORY}/${F_PREFIX}) + else() + omnitrace_message( + FATAL_ERROR + "PREFIX was specified but it is not an absolute path: ${F_PREFIX}") + endif() + endif() + + if(NOT F_WORKING_DIRECTORY) + set(F_WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + endif() + + foreach(_PATH ${F_PREFIX} ${F_PATHS}) + if(F_PREFIX AND NOT "${_PATH}" STREQUAL "${F_PREFIX}") + # if path is relative, set to prefix + path + if(NOT IS_ABSOLUTE "${_PATH}") + set(_PATH ${F_PREFIX}/${_PATH}) + endif() + list(APPEND _OUTPUT_VAR ${_PATH}) + elseif(NOT F_PREFIX) + list(APPEND _OUTPUT_VAR ${_PATH}) + endif() + + if(NOT EXISTS "${_PATH}" AND F_FAIL) + omnitrace_message(FATAL_ERROR "Directory '${_PATH}' does not exist") + elseif(NOT IS_DIRECTORY "${_PATH}" AND F_FAIL) + omnitrace_message(FATAL_ERROR "'${_PATH}' exists but is not a directory") + elseif(NOT EXISTS "${_PATH}" AND F_MKDIR) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${_PATH} + WORKING_DIRECTORY ${F_WORKING_DIRECTORY}) + elseif( + EXISTS "${_PATH}" + AND NOT IS_DIRECTORY "${_PATH}" + AND F_MKDIR) + if(F_FORCE) + execute_process(COMMAND ${CMAKE_COMMAND} -E rm ${_PATH} + WORKING_DIRECTORY ${F_WORKING_DIRECTORY}) + endif() + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${_PATH} + WORKING_DIRECTORY ${F_WORKING_DIRECTORY}) + endif() + endforeach() + + if(F_OUTPUT_VARIABLE) + set(${F_OUTPUT_VARIABLE} + "${_OUTPUT_VAR}" + PARENT_SCOPE) + endif() +endfunction() + +function(OMNITRACE_CHECK_PYTHON_DIRS_AND_VERSIONS) + cmake_parse_arguments(F "FAIL;UNSET" "RESULT_VARIABLE;OUTPUT_VARIABLE" "" ${ARGN}) + + list(LENGTH OMNITRACE_PYTHON_VERSIONS _NUM_PYTHON_VERSIONS) + list(LENGTH OMNITRACE_PYTHON_ROOT_DIRS _NUM_PYTHON_ROOT_DIRS) + + if(NOT _NUM_PYTHON_VERSIONS EQUAL _NUM_PYTHON_ROOT_DIRS) + set(_RET 1) + else() + set(_RET 0) + if(F_OUTPUT_VARIABLE) + set(${F_OUTPUT_VARIABLE} + ${_NUM_PYTHON_VERSIONS} + PARENT_SCOPE) + endif() + endif() + + if(F_RESULT_VARIABLE) + set(${F_RESULT_VARIABLE} + ${_RET} + PARENT_SCOPE) + endif() + + if(NOT ${_RET} EQUAL 0) + if(F_FAIL) + omnitrace_message( + WARNING + "Error! Number of python versions : ${_NUM_PYTHON_VERSIONS}. VERSIONS :: ${OMNITRACE_PYTHON_VERSIONS}" + ) + omnitrace_message( + WARNING + "Error! Number of python root directories : ${_NUM_PYTHON_ROOT_DIRS}. ROOT DIRS :: ${OMNITRACE_PYTHON_ROOT_DIRS}" + ) + omnitrace_message( + FATAL_ERROR + "Error! Number of python versions != number of python root directories") + elseif(F_UNSET) + unset(OMNITRACE_PYTHON_VERSIONS CACHE) + unset(OMNITRACE_PYTHON_ROOT_DIRS CACHE) + if(F_OUTPUT_VARIABLE) + set(${F_OUTPUT_VARIABLE} 0) + endif() + endif() + endif() +endfunction() + +# ---------------------------------------------------------------------------- +# Console scripts +# +function(OMNITRACE_PYTHON_CONSOLE_SCRIPT SCRIPT_NAME SCRIPT_SUBMODULE) + set(options) + set(args VERSION ROOT_DIR) + set(kwargs) + cmake_parse_arguments(ARG "${options}" "${args}" "${kwargs}" ${ARGN}) + + if(ARG_VERSION AND ARG_ROOT_DIR) + set(Python3_ROOT_DIR "${ARG_ROOT_DIR}") + find_package(Python3 ${ARG_VERSION} EXACT QUIET MODULE COMPONENTS Interpreter) + set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}") + configure_file(${PROJECT_SOURCE_DIR}/cmake/Templates/console-script.in + ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME}-${ARG_VERSION} @ONLY) + + if(CMAKE_INSTALL_PYTHONDIR) + install( + PROGRAMS ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME}-${ARG_VERSION} + DESTINATION ${CMAKE_INSTALL_BINDIR} + OPTIONAL) + endif() + + if(OMNITRACE_BUILD_TESTING OR OMNITRACE_BUILD_PYTHON) + add_test( + NAME ${SCRIPT_NAME}-console-script-test-${ARG_VERSION} + COMMAND ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME}-${ARG_VERSION} --help + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + set_tests_properties( + ${SCRIPT_NAME}-console-script-test-${ARG_VERSION} + PROPERTIES LABELS "python;python-${ARG_VERSION};console-script") + add_test( + NAME ${SCRIPT_NAME}-generic-console-script-test-${ARG_VERSION} + COMMAND ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME} --help + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + set_tests_properties( + ${SCRIPT_NAME}-generic-console-script-test-${ARG_VERSION} + PROPERTIES ENVIRONMENT "PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}" LABELS + "python;python-${ARG_VERSION};console-script") + endif() + else() + set(PYTHON_EXECUTABLE "python3") + + configure_file(${PROJECT_SOURCE_DIR}/cmake/Templates/console-script.in + ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME} @ONLY) + + if(CMAKE_INSTALL_PYTHONDIR) + install( + PROGRAMS ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME} + DESTINATION ${CMAKE_INSTALL_BINDIR} + OPTIONAL) + endif() + endif() +endfunction() + cmake_policy(POP) diff --git a/projects/rocprofiler-systems/cmake/Packages.cmake b/projects/rocprofiler-systems/cmake/Packages.cmake index 46e2f27e84..01e4568109 100644 --- a/projects/rocprofiler-systems/cmake/Packages.cmake +++ b/projects/rocprofiler-systems/cmake/Packages.cmake @@ -23,6 +23,7 @@ omnitrace_add_interface_library(omnitrace-ptl "Enables PTL support (tasking)") omnitrace_add_interface_library(omnitrace-papi "Enable PAPI support") omnitrace_add_interface_library(omnitrace-ompt "Enable OMPT support") omnitrace_add_interface_library(omnitrace-python "Enables Python support") +omnitrace_add_interface_library(omnitrace-timemory "Provides timemory libraries") omnitrace_add_interface_library(omnitrace-timemory-config "CMake interface library applied to all timemory targets") omnitrace_add_interface_library(omnitrace-compile-definitions "Compile definitions") @@ -526,6 +527,12 @@ if(TARGET omnitrace-papi-build) endforeach() endif() +target_link_libraries( + omnitrace-timemory + INTERFACE $ + $ + $) + # ----------------------------------------------------------------------------------------# # # PTL (Parallel Tasking Library) submodule diff --git a/projects/rocprofiler-systems/examples/CMakeLists.txt b/projects/rocprofiler-systems/examples/CMakeLists.txt index aa96b62628..74f8b68b43 100644 --- a/projects/rocprofiler-systems/examples/CMakeLists.txt +++ b/projects/rocprofiler-systems/examples/CMakeLists.txt @@ -7,6 +7,7 @@ set(CMAKE_CXX_VISIBILITY_PRESET "default") add_subdirectory(transpose) add_subdirectory(parallel-overhead) +add_subdirectory(code-coverage) add_subdirectory(user-api) add_subdirectory(openmp) diff --git a/projects/rocprofiler-systems/examples/code-coverage/CMakeLists.txt b/projects/rocprofiler-systems/examples/code-coverage/CMakeLists.txt new file mode 100644 index 0000000000..2ed4c0b70b --- /dev/null +++ b/projects/rocprofiler-systems/examples/code-coverage/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) + +project(omnitrace-code-coverage LANGUAGES CXX) + +set(CMAKE_BUILD_TYPE "RelWithDebInfo") +string(REPLACE " " ";" _FLAGS "${CMAKE_CXX_FLAGS_DEBUG}") + +find_package(Threads REQUIRED) +add_executable(code-coverage code-coverage.cpp) +target_link_libraries(code-coverage Threads::Threads) +target_compile_options(code-coverage PRIVATE ${_FLAGS}) + +if(NOT CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + set_target_properties(code-coverage PROPERTIES RUNTIME_OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}) +endif() diff --git a/projects/rocprofiler-systems/examples/code-coverage/code-coverage.cpp b/projects/rocprofiler-systems/examples/code-coverage/code-coverage.cpp new file mode 100644 index 0000000000..2e3c37c3df --- /dev/null +++ b/projects/rocprofiler-systems/examples/code-coverage/code-coverage.cpp @@ -0,0 +1,88 @@ + +#include +#include +#include +#include +#include +#include + +#define NOINLINE __attribute__((noinline)) + +std::atomic total{ 0 }; + +long +fib(long n) NOINLINE; + +void +run_real(size_t nitr, long) NOINLINE; + +void +run_fake(size_t nitr, long) NOINLINE; + +int +main(int argc, char** argv) +{ + using exec_t = void (*)(size_t, long); + + std::string _name = argv[0]; + auto _pos = _name.find_last_of('/'); + if(_pos != std::string::npos) _name = _name.substr(_pos + 1); + + size_t nthread = std::min(16, std::thread::hardware_concurrency()); + size_t nitr = 5000; + long nfib = 10; + + if(argc > 1) nfib = atol(argv[1]); + if(argc > 2) nthread = atol(argv[2]); + if(argc > 3) nitr = atol(argv[3]); + + exec_t _exec = &run_real; + + // ensure that compiler cannot optimize run_fake away + if(std::getenv("CODE_COVERAGE_USE_FAKE") != nullptr) _exec = &run_fake; + + printf("[%s] Threads: %zu\n[%s] Iterations: %zu\n[%s] fibonacci(%li)...\n", + _name.c_str(), nthread, _name.c_str(), nitr, _name.c_str(), nfib); + + std::vector threads{}; + for(size_t i = 0; i < nthread; ++i) + { + size_t _nitr = ((i % 2) == 1) ? (nitr - (0.1 * nitr)) : (nitr + (0.1 * nitr)); + _nitr = std::max(_nitr, 1); + threads.emplace_back(_exec, _nitr, nfib); + } + + auto _nitr = std::max(nitr - 0.25 * nitr, 1); + (*_exec)(_nitr, nfib - 0.1 * nfib); + for(auto& itr : threads) + itr.join(); + + printf("[%s] fibonacci(%li) x %lu = %li\n", _name.c_str(), nfib, nthread, + total.load()); + + return 0; +} + +long +fib(long n) +{ + return (n < 2) ? n : fib(n - 1) + fib(n - 2); +} + +void +run_real(size_t nitr, long n) +{ + long local = 0; + for(size_t i = 0; i < nitr; ++i) + local += fib(n); + total += local; +} + +void +run_fake(size_t nitr, long n) +{ + long local = 0; + for(size_t i = 0; i < nitr; ++i) + local += fib(n); + total += local; +} diff --git a/projects/rocprofiler-systems/examples/parallel-overhead/parallel-overhead.cpp b/projects/rocprofiler-systems/examples/parallel-overhead/parallel-overhead.cpp index 291401ccc3..19c83f19a6 100644 --- a/projects/rocprofiler-systems/examples/parallel-overhead/parallel-overhead.cpp +++ b/projects/rocprofiler-systems/examples/parallel-overhead/parallel-overhead.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -29,28 +30,36 @@ run(size_t nitr, long n) int main(int argc, char** argv) { + std::string _name = argv[0]; + auto _pos = _name.find_last_of('/'); + if(_pos != std::string::npos) _name = _name.substr(_pos + 1); + size_t nthread = std::min(16, std::thread::hardware_concurrency()); size_t nitr = 50000; long nfib = 10; + if(argc > 1) nfib = atol(argv[1]); if(argc > 2) nthread = atol(argv[2]); if(argc > 3) nitr = atol(argv[3]); - printf("[%s] Threads: %zu\n[%s] Iterations: %zu\n[%s] fibonacci(%li)...\n", argv[0], - nthread, argv[0], nitr, argv[0], nfib); + printf("[%s] Threads: %zu\n[%s] Iterations: %zu\n[%s] fibonacci(%li)...\n", + _name.c_str(), nthread, _name.c_str(), nitr, _name.c_str(), nfib); std::vector threads{}; for(size_t i = 0; i < nthread; ++i) { size_t _nitr = ((i % 2) == 1) ? (nitr - (0.1 * nitr)) : (nitr + (0.1 * nitr)); + _nitr = std::max(_nitr, 1); threads.emplace_back(&run, _nitr, nfib); } - run(nitr - 0.25 * nitr, nfib - 0.1 * nfib); + auto _nitr = std::max(nitr - 0.25 * nitr, 1); + run(_nitr, nfib - 0.1 * nfib); for(auto& itr : threads) itr.join(); - printf("[%s] fibonacci(%li) x %lu = %li\n", argv[0], nfib, nthread, total.load()); + printf("[%s] fibonacci(%li) x %lu = %li\n", _name.c_str(), nfib, nthread, + total.load()); return 0; } diff --git a/projects/rocprofiler-systems/examples/transpose/transpose.cpp b/projects/rocprofiler-systems/examples/transpose/transpose.cpp index 44658d74bd..1b0495599b 100644 --- a/projects/rocprofiler-systems/examples/transpose/transpose.cpp +++ b/projects/rocprofiler-systems/examples/transpose/transpose.cpp @@ -104,33 +104,36 @@ run(int rank, int tid, hipStream_t stream, int argc, char** argv) std::cout << "[" << rank << "][" << tid << "] M: " << M << " N: " << N << std::endl; _lk.unlock(); - size_t size = sizeof(int) * M * N; - int* matrix = new int[size]; + size_t size = sizeof(int) * M * N; + int* inp_matrix = new int[size]; + int* out_matrix = new int[size]; for(size_t i = 0; i < M * N; i++) - matrix[i] = rand() % 1002; + { + inp_matrix[i] = rand() % 1002; + out_matrix[i] = 0; + } int* in = nullptr; int* out = nullptr; - std::chrono::high_resolution_clock::time_point t1, t2; - HIP_API_CALL(hipMalloc(&in, size)); HIP_API_CALL(hipMalloc(&out, size)); - HIP_API_CALL(hipMemset(in, 0, size)); - HIP_API_CALL(hipMemset(out, 0, size)); - HIP_API_CALL(hipMemcpy(in, matrix, size, hipMemcpyHostToDevice)); - HIP_API_CALL(hipDeviceSynchronize()); + HIP_API_CALL(hipMemsetAsync(in, 0, size, stream)); + HIP_API_CALL(hipMemsetAsync(out, 0, size, stream)); + HIP_API_CALL(hipMemcpyAsync(in, inp_matrix, size, hipMemcpyHostToDevice, stream)); + HIP_API_CALL(hipStreamSynchronize(stream)); dim3 grid(M / 32, N / 32, 1); dim3 block(32, 32, 1); // transpose_a - t1 = std::chrono::high_resolution_clock::now(); + auto t1 = std::chrono::high_resolution_clock::now(); for(size_t i = 0; i < nitr; i++) { transpose_a<<>>(in, out, M, N); check_hip_error(); } + auto t2 = std::chrono::high_resolution_clock::now(); HIP_API_CALL(hipStreamSynchronize(stream)); - t2 = std::chrono::high_resolution_clock::now(); + HIP_API_CALL(hipMemcpyAsync(out_matrix, out, size, hipMemcpyDeviceToHost, stream)); double time = std::chrono::duration_cast>(t2 - t1).count(); float GB = (float) size * nitr * 2 / (1 << 30); @@ -142,18 +145,15 @@ run(int rank, int tid, hipStream_t stream, int argc, char** argv) << std::endl; print_lock.unlock(); - HIP_API_CALL(hipDeviceSynchronize()); - - int* out_matrix = new int[size]; - HIP_API_CALL(hipMemcpy(out_matrix, out, size, hipMemcpyDeviceToHost)); + HIP_API_CALL(hipStreamSynchronize(stream)); // cpu_transpose(matrix, out_matrix, M, N); - verify(matrix, out_matrix, M, N); + verify(inp_matrix, out_matrix, M, N); HIP_API_CALL(hipFree(in)); HIP_API_CALL(hipFree(out)); - delete[] matrix; + delete[] inp_matrix; delete[] out_matrix; } diff --git a/projects/rocprofiler-systems/external/timemory b/projects/rocprofiler-systems/external/timemory index 3dbe0d4d74..2389cecd50 160000 --- a/projects/rocprofiler-systems/external/timemory +++ b/projects/rocprofiler-systems/external/timemory @@ -1 +1 @@ -Subproject commit 3dbe0d4d746f923330df9d506ab51e72ab5d38a5 +Subproject commit 2389cecd50d250bcdb89903468ae28da42ddacda diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/CMakeLists.txt b/projects/rocprofiler-systems/source/bin/omnitrace/CMakeLists.txt index 841b93a63f..0cddab6b58 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/bin/omnitrace/CMakeLists.txt @@ -22,9 +22,9 @@ target_link_libraries( omnitrace-exe PRIVATE omnitrace::omnitrace-headers omnitrace::omnitrace-dyninst + omnitrace::omnitrace-timemory omnitrace::omnitrace-compile-options omnitrace::omnitrace-compile-definitions - $ $,omnitrace::omnitrace-sanitizer,>) set_target_properties( diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/details.cpp b/projects/rocprofiler-systems/source/bin/omnitrace/details.cpp index bd08901b72..7cd9abc34b 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/details.cpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/details.cpp @@ -20,6 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include "function_signature.hpp" +#include "fwd.hpp" #include "omnitrace.hpp" static int expect_error = NO_ERROR; @@ -673,11 +675,12 @@ get_func_file_line_info(module_t* module, procedure_t* func) std::vector _params = {}; if(params) { + _params.reserve(params->size()); for(auto* itr : *params) { string_t _name = itr->getType()->getName(); if(_name.empty()) _name = itr->getName(); - _params.push_back(_name); + _params.emplace_back(_name); } } @@ -699,6 +702,136 @@ get_func_file_line_info(module_t* module, procedure_t* func) } } +//======================================================================================// +// +// Gets information (line number, filename, and column number) about +// the instrumented loop and formats it properly. +// +std::map +get_basic_block_file_line_info(module_t* module, procedure_t* func) +{ + std::map _data{}; + if(!func) return _data; + + auto* _cfg = func->getCFG(); + std::set _basic_blocks{}; + _cfg->getAllBasicBlocks(_basic_blocks); + + if(_basic_blocks.empty()) return _data; + + char fname[FUNCNAMELEN + 1]; + char mname[FUNCNAMELEN + 1]; + std::string typeName = {}; + + memset(fname, '\0', FUNCNAMELEN + 1); + memset(mname, '\0', FUNCNAMELEN + 1); + + module->getName(mname, FUNCNAMELEN); + func->getName(fname, FUNCNAMELEN); + + auto* returnType = func->getReturnType(); + + if(returnType) typeName = returnType->getName(); + + auto* params = func->getParams(); + std::vector _params; + if(params) + { + for(auto* itr : *params) + { + string_t _name = itr->getType()->getName(); + if(_name.empty()) _name = itr->getName(); + _params.push_back(_name); + } + } + + for(auto&& itr : _basic_blocks) + { + auto base_addr = itr->getStartAddress(); + auto last_addr = itr->getEndAddress(); + + verbprintf(4, "BB: size = %lu: base_addr = %lu, last_addr = %lu\n", + (unsigned long) (last_addr - base_addr), base_addr, last_addr); + + bpvector_t linesBeg{}; + bpvector_t linesEnd{}; + + string_t filename = mname; + + if(module->getSourceLines(base_addr, linesBeg) && !linesBeg.empty()) + { + int row1 = linesBeg.front().lineNumber(); + int col1 = linesBeg.front().lineOffset(); + + verbprintf(4, "size of linesEnd = %lu\n", (unsigned long) linesEnd.size()); + + if(module->getSourceLines(last_addr, linesEnd) && !linesEnd.empty()) + { + int row2 = linesEnd.back().lineNumber(); + int col2 = linesEnd.back().lineOffset(); + + if(row2 < row1) std::swap(row1, row2); + if(row1 == row2 && col2 < col1) std::swap(col1, col2); + + _data.emplace(itr, + basic_block_signature{ + base_addr, last_addr, + function_signature(typeName, fname, filename, _params, + { row1, row2 }, { col1, col2 }, true, + true, true) }); + } + else + { + _data.emplace(itr, + basic_block_signature{ + base_addr, last_addr, + function_signature(typeName, fname, filename, _params, + { row1, 0 }, { col1, 0 }, true, true, + false) }); + } + } + else + { + _data.emplace(itr, + basic_block_signature{ + base_addr, last_addr, + function_signature(typeName, fname, filename, _params) }); + } + } + + return _data; +} + +//======================================================================================// +// +// We create a new name that embeds the file and line information in the name +// +std::vector +get_source_code(module_t* module, procedure_t* func) +{ + std::vector _lines{}; + if(!module || !func) return _lines; + auto* _cfg = func->getCFG(); + std::set _basic_blocks{}; + _cfg->getAllBasicBlocks(_basic_blocks); + + for(auto&& itr : _basic_blocks) + { + auto _base_addr = itr->getStartAddress(); + auto _last_addr = itr->getEndAddress(); + for(decltype(_base_addr) _addr = _base_addr; _addr <= _last_addr; ++_addr) + { + std::vector _src{}; + if(module->getSourceLines(_addr, _src)) + { + for(auto&& iitr : _src) + _lines.emplace_back(iitr); + } + } + } + return _lines; +} + //======================================================================================// // // Error callback routine. diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/function_signature.cpp b/projects/rocprofiler-systems/source/bin/omnitrace/function_signature.cpp index d6cae8bfae..d29dde8886 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/function_signature.cpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/function_signature.cpp @@ -59,12 +59,14 @@ function_signature::get(function_signature& sig) } string_t -function_signature::get() const +function_signature::get(bool _all, bool _save) const { + if(!_all && _save && !m_signature.empty()) return m_signature; + std::stringstream ss; - if(use_return_info && !m_return.empty()) ss << m_return << " "; + if((_all || use_return_info) && !m_return.empty()) ss << m_return << " "; ss << m_name; - if(use_args_info) ss << m_params; + if(_all || use_args_info) ss << m_params; if(m_loop && m_info_beg) { auto _row_col_str = [](unsigned long _row, unsigned long _col) { @@ -90,10 +92,52 @@ function_signature::get() const else errprintf(1, "loop line info is empty!"); } - if(use_file_info && m_file.length() > 0) ss << " [" << m_file; - if(use_line_info && m_row.first > 0) ss << ":" << m_row.first; - if(use_file_info && m_file.length() > 0) ss << "]"; + if((_all || use_file_info) && m_file.length() > 0) ss << " [" << m_file; + if((_all || use_line_info) && m_row.first > 0) ss << ":" << m_row.first; + if((_all || use_file_info) && m_file.length() > 0) ss << "]"; - m_signature = ss.str(); - return m_signature; + if(_save) m_signature = ss.str(); + return ss.str(); +} + +string_t +function_signature::get_coverage(bool _basic_block) const +{ + std::stringstream ss; + if(!m_return.empty()) ss << m_return << " "; + ss << m_name << m_params; + if(_basic_block && m_loop && m_info_beg) + { + if(m_file.length() > 0) ss << " [" << m_file << "]"; + auto _row_col_str = [](unsigned long _row, unsigned long _col) { + std::stringstream _ss{}; + if(_row == 0 && _col == 0) return std::string{}; + if(_col > 0) + _ss << "{" << _row << "," << _col << "}"; + else + _ss << "{" << _row << "}"; + return _ss.str(); + }; + + auto _rc1 = _row_col_str(m_row.first, m_col.first); + auto _rc2 = _row_col_str(m_row.second, m_col.second); + if(m_info_end && !_rc1.empty() && !_rc2.empty() && _rc1 != _rc2) + ss << " [" << _rc1 << "-" << _rc2 << "]"; + else if(m_info_end && !_rc1.empty() && !_rc2.empty() && _rc1 == _rc2) + ss << " [" << _rc1 << "]"; + else if(m_info_end && !_rc1.empty() && _rc2.empty()) + ss << " [" << _rc1 << "]"; + else if(!m_info_end && !_rc1.empty()) + ss << " [" << _rc1 << "]"; + else + errprintf(1, "loop line info is empty!"); + } + else + { + if(m_file.length() > 0) ss << " [" << m_file; + if(m_row.first > 0) ss << ":" << m_row.first; + if(m_file.length() > 0) ss << "]"; + } + + return ss.str(); } diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/function_signature.hpp b/projects/rocprofiler-systems/source/bin/omnitrace/function_signature.hpp index dc6ea7bbd7..cbbe08ca58 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/function_signature.hpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/function_signature.hpp @@ -41,7 +41,8 @@ struct function_signature bool _info_beg = false, bool _info_end = false); static string_t get(function_signature& sig); - string_t get() const; + string_t get(bool _all = false, bool _save = true) const; + string_t get_coverage(bool _is_basic_block) const; bool m_loop = false; bool m_info_beg = false; @@ -72,3 +73,12 @@ struct function_signature (void) get(); } }; + +struct basic_block_signature +{ + using address_t = Dyninst::Address; + + address_t start_address = {}; + address_t last_address = {}; + function_signature signature = {}; +}; diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/fwd.hpp b/projects/rocprofiler-systems/source/bin/omnitrace/fwd.hpp index 533d0ff032..61921a2d9d 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/fwd.hpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/fwd.hpp @@ -22,7 +22,6 @@ #pragma once -#include #include #include #include @@ -65,6 +64,7 @@ #include #include #include +#include #include #include @@ -78,6 +78,7 @@ #endif struct function_signature; +struct basic_block_signature; struct module_function; template @@ -105,6 +106,7 @@ using snippet_t = BPatch_snippet; using call_expr_t = BPatch_funcCallExpr; using address_space_t = BPatch_addressSpace; using flow_graph_t = BPatch_flowGraph; +using statement_t = BPatch_statement; using basic_block_t = BPatch_basicBlock; using basic_loop_t = BPatch_basicBlockLoop; using procedure_loc_t = BPatch_procedureLocation; @@ -125,6 +127,13 @@ using instruction_t = Dyninst::InstructionAPI::Instruction; void omnitrace_prefork_callback(thread_t* parent, thread_t* child); +enum CodeCoverageMode +{ + CODECOV_NONE = 0, + CODECOV_FUNCTION, + CODECOV_BASIC_BLOCK +}; + //======================================================================================// // // Global Variables @@ -167,21 +176,22 @@ extern string_t prefer_library; // // global variables // -extern patch_pointer_t bpatch; -extern call_expr_t* terminate_expr; -extern snippet_vec_t init_names; -extern snippet_vec_t fini_names; -extern fmodset_t available_module_functions; -extern fmodset_t instrumented_module_functions; -extern fmodset_t overlapping_module_functions; -extern fmodset_t excluded_module_functions; -extern fixed_modset_t fixed_module_functions; -extern regexvec_t func_include; -extern regexvec_t func_exclude; -extern regexvec_t file_include; -extern regexvec_t file_exclude; -extern regexvec_t file_restrict; -extern regexvec_t func_restrict; +extern patch_pointer_t bpatch; +extern call_expr_t* terminate_expr; +extern snippet_vec_t init_names; +extern snippet_vec_t fini_names; +extern fmodset_t available_module_functions; +extern fmodset_t instrumented_module_functions; +extern fmodset_t overlapping_module_functions; +extern fmodset_t excluded_module_functions; +extern fixed_modset_t fixed_module_functions; +extern regexvec_t func_include; +extern regexvec_t func_exclude; +extern regexvec_t file_include; +extern regexvec_t file_exclude; +extern regexvec_t file_restrict; +extern regexvec_t func_restrict; +extern CodeCoverageMode coverage_mode; // //======================================================================================// @@ -230,10 +240,8 @@ consume_parameters(T&&...) extern "C" { bool are_file_include_exclude_lists_empty(); - bool instrument_module(const string_t& file_name); - bool instrument_entity(const string_t& function_name); - bool module_constraint(string_view_t fname); - bool routine_constraint(string_view_t fname); + bool module_constraint(const char* fname); + bool routine_constraint(const char* fname); } //======================================================================================// @@ -248,6 +256,12 @@ function_signature get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* cfGraph, basic_loop_t* loopToInstrument); +std::map +get_basic_block_file_line_info(module_t* module, procedure_t* func); + +std::vector +get_source_code(module_t* module, procedure_t* func); + std::tuple query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc, flow_graph_t* cfGraph = nullptr, basic_loop_t* loopToInstrument = nullptr); @@ -256,11 +270,21 @@ bool query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc, flow_graph_t* cfGraph, basic_loop_t* loopToInstrument, bool allow_traps); +template +bool +insert_instr(address_space_t* mutatee, const bpvector_t& _points, Tp traceFunc, + procedure_loc_t traceLoc, bool allow_traps = instr_traps); + template bool insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc, procedure_loc_t traceLoc, flow_graph_t* cfGraph = nullptr, - basic_loop_t* loopToInstrument = nullptr, bool allow_traps = true); + basic_loop_t* loopToInstrument = nullptr, bool allow_traps = instr_traps); + +template +bool +insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc, + basic_block_t* basicBlock, bool allow_traps = instr_traps); void errorFunc(error_level_t level, int num, const char** params); diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/info.hpp b/projects/rocprofiler-systems/source/bin/omnitrace/info.hpp index 723cff24ef..b29111937a 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/info.hpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/info.hpp @@ -25,6 +25,12 @@ #include "fwd.hpp" #include "module_function.hpp" +#include +#include +#include +#include +#include + static inline void dump_info(std::ostream& _os, const fmodset_t& _data) { @@ -54,7 +60,7 @@ dump_info(const string_t& _label, string_t _oname, const string_t& _ext, namespace cereal = tim::cereal; namespace policy = tim::policy; - _oname += "." + _ext; + _oname = tim::settings::compose_output_filename(_oname, _ext); auto _handle_error = [&]() { std::stringstream _msg{}; _msg << "[dump_info] Error opening '" << _oname << " for output"; diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/module_function.cpp b/projects/rocprofiler-systems/source/bin/omnitrace/module_function.cpp index a4d89da3ef..6b9e47a75c 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/module_function.cpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/module_function.cpp @@ -114,16 +114,28 @@ module_function::write_header(std::ostream& os) bool module_function::should_instrument() const +{ + return should_instrument(false); +} + +bool +module_function::should_coverage_instrument() const +{ + return should_instrument(true); +} + +bool +module_function::should_instrument(bool coverage) const { // hard constraints if(!is_instrumentable()) return false; if(!can_instrument_entry()) return false; - if(!can_instrument_exit()) return false; + if(!coverage && !can_instrument_exit()) return false; if(is_module_constrained()) return false; if(is_routine_constrained()) return false; // should be before user selection - constexpr int absolute_min_instructions = 4; + constexpr int absolute_min_instructions = 2; if(num_instructions < absolute_min_instructions) { messages.emplace_back( @@ -134,8 +146,6 @@ module_function::should_instrument() const // user selection if(is_user_excluded()) return false; - if(is_user_restricted()) return true; - if(is_user_included()) return true; // should be applied before dynamic-callsite check if(is_overlapping_constrained()) return false; @@ -145,6 +155,10 @@ module_function::should_instrument() const // needs to be applied before address range and number of instruction constraints if(is_dynamic_callsite_forced()) return true; + // user selection + if(!file_restrict.empty() || !func_restrict.empty()) return !is_user_restricted(); + if(is_user_included()) return true; + if(is_address_range_constrained()) return false; if(is_num_instructions_constrained()) return false; @@ -194,7 +208,7 @@ module_function::is_user_restricted() const if(!func_restrict.empty()) { - if(check_regex_restrictions(module_name, func_restrict)) + if(check_regex_restrictions(function_name, func_restrict)) { messages.emplace_back(2, "Forcing", "function", "function-restrict-regex"); return false; @@ -282,23 +296,134 @@ module_function::is_overlapping() const bool module_function::is_module_constrained() const { - if(!instrument_module(module_name) || module_constraint(module_name.c_str())) - { - messages.emplace_back(2, "Skipping", "module", "module-constraint"); + auto regex_opts = std::regex_constants::egrep | std::regex_constants::optimize; + auto _report = [&](const string_t& _action, const string_t& _reason, int _lvl) { + messages.emplace_back(_lvl, _action, "module", _reason); return true; - } + }; + + if(module_constraint(function_name.c_str())) return true; + + // always instrument these modules + if(module_name == "DEFAULT_MODULE" || module_name == "LIBRARY_MODULE") + return _report("Skipping", "default module", 2); + + static std::regex ext_regex{ "\\.(s|S)$", regex_opts }; + static std::regex sys_regex{ "^(s|k|e|w)_[A-Za-z_0-9\\-]+\\.(c|C)$", regex_opts }; + static std::regex sys_build_regex{ "^(\\.\\./sysdeps/|/build/)", regex_opts }; + static std::regex dyninst_regex{ "(dyninst|DYNINST|(^|/)RT[[:graph:]]+\\.c$)", + regex_opts }; + static std::regex dependlib_regex{ "^(lib|)(omnitrace|pthread|caliper|gotcha|papi|" + "cupti|TAU|likwid|pfm|nvperf|unwind)", + regex_opts }; + static std::regex core_cmod_regex{ + "^(malloc|(f|)lock|sig|sem)[a-z_]+(|64|_r|_l)\\.c$" + }; + static std::regex core_lib_regex{ + "^(lib|)(c|dl|dw|pthread|tcmalloc|profiler|" + "tbbmalloc|tbbmalloc_proxy|malloc|stdc\\+\\+)(-|\\.)", + regex_opts + }; + static std::regex prefix_regex{ "^(_|\\.[a-zA-Z0-9])", regex_opts }; + + // file extensions that should not be instrumented + if(std::regex_search(module_name, ext_regex)) + return _report("Excluding", "file extension", 3); + + // system modules that should not be instrumented (wastes time) + if(std::regex_search(module_name, sys_regex) || + std::regex_search(module_name, sys_build_regex)) + return _report("Excluding", "system module", 3); + + // dyninst modules that must not be instrumented + if(std::regex_search(module_name, dyninst_regex)) + return _report("Excluding", "dyninst module", 3); + + // modules used by omnitrace and dependent libraries + if(std::regex_search(module_name, core_lib_regex) || + std::regex_search(module_name, core_cmod_regex)) + return _report("Excluding", "core module", 3); + + // modules used by omnitrace and dependent libraries + if(std::regex_search(module_name, dependlib_regex)) + return _report("Excluding", "dependency module", 3); + + // known set of modules whose starting sequence of characters suggest it should not be + // instrumented (wastes time) + if(std::regex_search(module_name, prefix_regex)) + return _report("Excluding", "prefix match", 3); + return false; } bool module_function::is_routine_constrained() const { - if(!instrument_entity(function_name) || !instrument_entity(signature.get()) || - routine_constraint(function_name) || routine_constraint(signature.get())) - { - messages.emplace_back(2, "Skipping", "function", "function-constraint"); + auto regex_opts = std::regex_constants::egrep | std::regex_constants::optimize; + auto _report = [&](const string_t& _action, const string_t& _reason, int _lvl) { + messages.emplace_back(_lvl, _action, "function", _reason); return true; + }; + + if(routine_constraint(function_name.c_str())) return true; + + auto npos = std::string::npos; + if(function_name.find("omnitrace") != npos) + { + return _report("Skipping", "omnitrace-function", 1); } + + if(function_name.find("FunctionInfo") != npos || + function_name.find("_L_lock") != npos || function_name.find("_L_unlock") != npos) + { + return _report("Skipping", "function-constraint", 2); + } + + static std::regex exclude( + "(omnitrace|tim::|N3tim|MPI_Init|MPI_Finalize|dyninst|tm_clones)", regex_opts); + static std::regex exclude_cxx( + "(std::_Sp_counted_base|std::(use|has)_facet|std::locale|::sentry|^std::_|::_(M|" + "S)_|::basic_string[a-zA-Z,<>: ]+::_M_create|::__|::_(Alloc|State)|" + "std::(basic_|)(ifstream|ios|istream|ostream|stream))", + regex_opts); + static std::regex leading("^(_|\\.|frame_dummy|transaction clone|virtual " + "thunk|non-virtual thunk|\\(|targ|kmp_threadprivate_)", + regex_opts); + static std::regex trailing( + "(_|\\.part\\.[0-9]+|\\.constprop\\.[0-9]+|\\.|\\.[0-9]+)$", regex_opts); + static strset_t whole = []() { + auto _v = get_whole_function_names(); + auto _ret = _v; + for(std::string _ext : { "64", "_l", "_r" }) + for(const auto& itr : _v) + _ret.emplace(itr + _ext); + return _ret; + }(); + + // don't instrument the functions when key is found anywhere in function name + if(std::regex_search(function_name, exclude) || + std::regex_search(function_name, exclude_cxx)) + { + return _report("Excluding", "critical", 3); + } + + if(whole.count(function_name) > 0) + { + return _report("Excluding", "critical-whole-match", 3); + } + + // don't instrument the functions when key is found at the start of the function name + if(std::regex_search(function_name, leading)) + { + return _report("Excluding", "recommended-leading-match", 3); + } + + // don't instrument the functions when key is found at the end of the function name + if(std::regex_search(function_name, trailing)) + { + return _report("Excluding", "recommended-trailing-match", 3); + } + return false; } @@ -364,6 +489,8 @@ module_function::is_loop_address_range_constrained() const bool module_function::is_num_instructions_constrained() const { + if(!loop_blocks.empty()) return is_loop_num_instructions_constrained(); + if(num_instructions < min_instructions) { messages.emplace_back(2, "Skipping", "function", "min-instructions"); @@ -373,6 +500,20 @@ module_function::is_num_instructions_constrained() const return false; } +bool +module_function::is_loop_num_instructions_constrained() const +{ + if(loop_blocks.empty()) return false; + + if(num_instructions < min_loop_instructions) + { + messages.emplace_back(2, "Skipping", "function", "min-instructions-loop"); + return true; + } + + return false; +} + bool module_function::can_instrument_entry() const { @@ -459,10 +600,8 @@ module_function::operator()(address_space_t* _addr_space, procedure_t* _entr_tra auto _entr = _trace_entr.get(_entr_trace); auto _exit = _trace_exit.get(_exit_trace); - if(insert_instr(_addr_space, function, _entr, BPatch_entry, nullptr, nullptr, - instr_traps) && - insert_instr(_addr_space, function, _exit, BPatch_exit, nullptr, nullptr, - instr_traps)) + if(insert_instr(_addr_space, function, _entr, BPatch_entry) && + insert_instr(_addr_space, function, _exit, BPatch_exit)) { messages.emplace_back(1, "Instrumenting", "function", "no-constraint"); ++_count.first; @@ -516,3 +655,92 @@ module_function::operator()(address_space_t* _addr_space, procedure_t* _entr_tra return _count; } + +void +module_function::register_source(address_space_t* _addr_space, procedure_t* _entr_trace, + const std::vector& _entr_points) const +{ + switch(coverage_mode) + { + case CODECOV_FUNCTION: + { + auto _name = signature.get_coverage(false); + auto _trace_entr = + omnitrace_call_expr(signature.m_file, signature.m_name, + signature.m_row.first, start_address, _name); + auto _entr = _trace_entr.get(_entr_trace); + + if(insert_instr(_addr_space, _entr_points, _entr, BPatch_entry)) + { + messages.emplace_back(1, "Code Coverage", "function", "no-constraint"); + } + break; + } + case CODECOV_BASIC_BLOCK: + { + for(auto&& itr : get_basic_block_file_line_info(module, function)) + { + auto _start_addr = itr.second.start_address; + auto& _signature = itr.second.signature; + auto _name = _signature.get_coverage(true); + auto _trace_entr = + omnitrace_call_expr(_signature.m_file, _signature.m_name, + _signature.m_row.first, _start_addr, _name); + auto _entr = _trace_entr.get(_entr_trace); + + if(insert_instr(_addr_space, _entr_points, _entr, BPatch_entry)) + { + messages.emplace_back(1, "Code Coverage", "basic_block", + "no-constraint"); + } + } + break; + } + case CODECOV_NONE: break; + } +} + +std::pair +module_function::register_coverage(address_space_t* _addr_space, + procedure_t* _entr_trace) const +{ + std::pair _count = { 0, 0 }; + switch(coverage_mode) + { + case CODECOV_FUNCTION: + { + auto _trace_entr = + omnitrace_call_expr(signature.m_file, signature.m_name, start_address); + auto _entr = _trace_entr.get(_entr_trace); + + if(insert_instr(_addr_space, function, _entr, BPatch_entry)) + { + messages.emplace_back(1, "Code Coverage", "function", "no-constraint"); + ++_count.first; + } + break; + } + case CODECOV_BASIC_BLOCK: + { + for(auto&& itr : get_basic_block_file_line_info(module, function)) + { + auto _start_addr = itr.second.start_address; + auto& _signature = itr.second.signature; + auto _trace_entr = omnitrace_call_expr(_signature.m_file, + _signature.m_name, _start_addr); + auto _entr = _trace_entr.get(_entr_trace); + + if(insert_instr(_addr_space, _entr, BPatch_entry, itr.first)) + { + ++_count.second; + messages.emplace_back(1, "Code Coverage", "basic_block", + "no-constraint"); + } + } + verbprintf(0, "Basic-block code coverage is not available yet\n"); + break; + } + case CODECOV_NONE: break; + } + return _count; +} diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/module_function.hpp b/projects/rocprofiler-systems/source/bin/omnitrace/module_function.hpp index 460882079f..3ec55eed9b 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/module_function.hpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/module_function.hpp @@ -46,12 +46,20 @@ struct module_function module_function(module_t* mod, procedure_t* proc); + // code coverage + void register_source(address_space_t* _addr_space, procedure_t* _entr_trace, + const std::vector&) const; + std::pair register_coverage(address_space_t* _addr_space, + procedure_t* _entr_trace) const; + + // instrumentation std::pair operator()(address_space_t* _addr_space, procedure_t* _entr_trace, procedure_t* _exit_trace) const; // applies logic for all "is_*" and "can_*" checks below bool should_instrument() const; + bool should_coverage_instrument() const; // hard constraints bool is_instrumentable() const; // checks whether can instrument @@ -98,8 +106,10 @@ struct module_function bool is_overlapping() const; // checks if func overlaps private: + bool is_loop_num_instructions_constrained() const; // checks loop instr constraint bool is_loop_address_range_constrained() const; // checks loop addr range constraint bool contains_dynamic_callsites() const; + bool should_instrument(bool _coverage) const; public: template @@ -176,6 +186,7 @@ module_function::serialize(ArchiveT& ar, const unsigned) ar.setNextName("heuristics"); ar.startNode(); ar(cereal::make_nvp("should_instrument", should_instrument()), + cereal::make_nvp("should_coverage_instrument", should_coverage_instrument()), cereal::make_nvp("is_instrumentable", is_instrumentable()), cereal::make_nvp("can_instrument_entry", can_instrument_entry()), cereal::make_nvp("can_instrument_exit", can_instrument_exit()), diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/omnitrace.cpp b/projects/rocprofiler-systems/source/bin/omnitrace/omnitrace.cpp index c6ed6c2340..737a215d80 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/omnitrace.cpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/omnitrace.cpp @@ -23,12 +23,17 @@ #include "omnitrace.hpp" #include "fwd.hpp" +#include +#include + #include #include #include #include +#include #include #include +#include #include #include #include @@ -62,50 +67,53 @@ string_t prefer_library = {}; // // global variables // -patch_pointer_t bpatch = {}; -call_expr_t* terminate_expr = nullptr; -snippet_vec_t init_names = {}; -snippet_vec_t fini_names = {}; -fmodset_t available_module_functions = {}; -fmodset_t instrumented_module_functions = {}; -fmodset_t overlapping_module_functions = {}; -fmodset_t excluded_module_functions = {}; -fixed_modset_t fixed_module_functions = {}; -regexvec_t func_include = {}; -regexvec_t func_exclude = {}; -regexvec_t file_include = {}; -regexvec_t file_exclude = {}; -regexvec_t file_restrict = {}; -regexvec_t func_restrict = {}; +patch_pointer_t bpatch = {}; +call_expr_t* terminate_expr = nullptr; +snippet_vec_t init_names = {}; +snippet_vec_t fini_names = {}; +fmodset_t available_module_functions = {}; +fmodset_t instrumented_module_functions = {}; +fmodset_t coverage_module_functions = {}; +fmodset_t overlapping_module_functions = {}; +fmodset_t excluded_module_functions = {}; +fixed_modset_t fixed_module_functions = {}; +regexvec_t func_include = {}; +regexvec_t func_exclude = {}; +regexvec_t file_include = {}; +regexvec_t file_exclude = {}; +regexvec_t file_restrict = {}; +regexvec_t func_restrict = {}; +CodeCoverageMode coverage_mode = CODECOV_NONE; namespace { -bool binary_rewrite = false; -bool is_attached = false; -bool use_mpi = false; -bool is_static_exe = false; -bool is_driver = false; -bool explicit_dump_and_exit = false; -size_t batch_size = 50; -strset_t extra_libs = {}; -std::vector> hash_ids = {}; -std::map use_stubs = {}; -std::map beg_stubs = {}; -std::map end_stubs = {}; -strvec_t init_stub_names = {}; -strvec_t fini_stub_names = {}; -strset_t used_stub_names = {}; -std::vector env_variables = {}; -std::map beg_expr = {}; -std::map end_expr = {}; -const auto npos_v = string_t::npos; -string_t instr_mode = "trace"; -string_t print_instrumented = {}; -string_t print_excluded = {}; -string_t print_available = {}; -string_t print_overlapping = {}; -strset_t print_formats = { "txt", "json" }; -std::string modfunc_dump_dir = {}; +bool binary_rewrite = false; +bool is_attached = false; +bool use_mpi = false; +bool is_static_exe = false; +bool simulate = false; +size_t batch_size = 50; +strset_t extra_libs = {}; +std::vector> hash_ids = {}; +std::map use_stubs = {}; +std::map beg_stubs = {}; +std::map end_stubs = {}; +strvec_t init_stub_names = {}; +strvec_t fini_stub_names = {}; +strset_t used_stub_names = {}; +strvec_t env_config_variables = {}; +std::vector env_variables = {}; +std::map beg_expr = {}; +std::map end_expr = {}; +const auto npos_v = string_t::npos; +string_t instr_mode = "trace"; +string_t print_coverage = {}; +string_t print_instrumented = {}; +string_t print_excluded = {}; +string_t print_available = {}; +string_t print_overlapping = {}; +strset_t print_formats = { "txt", "json" }; +std::string modfunc_dump_dir = {}; auto regex_opts = std::regex_constants::egrep | std::regex_constants::optimize; std::string @@ -176,6 +184,7 @@ main(int argc, char** argv) fixed_module_functions = { { &available_module_functions, false }, { &instrumented_module_functions, false }, + { &coverage_module_functions, false }, { &excluded_module_functions, false }, { &overlapping_module_functions, false }, }; @@ -218,7 +227,9 @@ main(int argc, char** argv) int k = 0; for(int j = i + 1; j < argc; ++j, ++k) { - copy_str(_cmdv[k], argv[j]); + auto _v = + std::regex_replace(argv[j], std::regex{ "(.*)([ \t\n\r]+)$" }, "$1"); + copy_str(_cmdv[k], _v.c_str()); } mutname = _cmdv[0]; break; @@ -256,9 +267,9 @@ main(int argc, char** argv) << std::endl; } - if(_cmdc > 0) - std::cout << "\n[omnitrace][exe][command]: " << cmd_string(_cmdc, _cmdv) - << "\n\n"; + verbprintf(0, "\n"); + verbprintf(0, "command :: '%s'...\n", cmd_string(_cmdc, _cmdv).c_str()); + verbprintf(0, "\n"); if(_cmdc > 0) cmdv0 = _cmdv[0]; @@ -299,7 +310,7 @@ main(int argc, char** argv) "function lists, e.g. available-instr.txt") .max_count(1) .dtype("bool") - .action([](parser_t& p) { explicit_dump_and_exit = p.get("simulate"); }); + .action([](parser_t& p) { simulate = p.get("simulate"); }); parser .add_argument({ "--print-format" }, "Output format for diagnostic " @@ -317,12 +328,14 @@ main(int argc, char** argv) "function lists, e.g. {print-dir}/available-instr.txt") .count(1) .dtype("string") - .action([](parser_t& p) { modfunc_dump_dir = p.get("print-dir"); }); + .action([](parser_t& p) { + tim::settings::output_path() = p.get("print-dir"); + }); parser .add_argument( { "--print-available" }, "Print the available entities for instrumentation (functions, modules, or " - "module-function pair) to stdout applying regular expressions and exit") + "module-function pair) to stdout after applying regular expressions") .count(1) .choices({ "functions", "modules", "functions+", "pair", "pair+" }) .action( @@ -331,18 +344,27 @@ main(int argc, char** argv) .add_argument( { "--print-instrumented" }, "Print the instrumented entities (functions, modules, or module-function " - "pair) to stdout after applying regular expressions and exit") + "pair) to stdout after applying regular expressions") .count(1) .choices({ "functions", "modules", "functions+", "pair", "pair+" }) .action([](parser_t& p) { print_instrumented = p.get("print-instrumented"); }); + parser + .add_argument({ "--print-coverage" }, + "Print the instrumented coverage entities (functions, modules, or " + "module-function " + "pair) to stdout after applying regular expressions") + .count(1) + .choices({ "functions", "modules", "functions+", "pair", "pair+" }) + .action( + [](parser_t& p) { print_coverage = p.get("print-coverage"); }); parser .add_argument({ "--print-excluded" }, "Print the entities for instrumentation (functions, modules, or " "module-function " "pair) which are excluded from the instrumentation to stdout after " - "applying regular expressions and exit") + "applying regular expressions") .count(1) .choices({ "functions", "modules", "functions+", "pair", "pair+" }) .action( @@ -352,7 +374,7 @@ main(int argc, char** argv) { "--print-overlapping" }, "Print the entities for instrumentation (functions, modules, or " "module-function pair) which overlap other function calls or have multiple " - "entry points to stdout applying regular expressions and exit") + "entry points to stdout after applying regular expressions") .count(1) .choices({ "functions", "modules", "functions+", "pair", "pair+" }) .action([](parser_t& p) { @@ -391,9 +413,13 @@ main(int argc, char** argv) "Instrumentation mode. 'trace' mode instruments the selected " "functions, 'sampling' mode only instruments the main function to " "start and stop the sampler.") - .choices({ "trace", "sampling" }) + .choices({ "trace", "sampling", "coverage" }) .count(1) - .action([](parser_t& p) { instr_mode = p.get("mode"); }); + .action([](parser_t& p) { + instr_mode = p.get("mode"); + if(instr_mode == "coverage" && !p.exists("coverage")) + coverage_mode = CODECOV_FUNCTION; + }); if(_cmdc == 0) { parser @@ -433,23 +459,6 @@ main(int argc, char** argv) "The primary function to instrument around, e.g. 'main'") .count(1) .action([](parser_t& p) { main_fname = p.get("main-function"); }); - /* - parser - .add_argument({ "-s", "--stubs" }, "Instrument with library stubs for LD_PRELOAD") - .dtype("boolean") - .max_count(1) - .action([&inputlib](parser_t& p) { - if(p.get("stubs")) - { - for(auto& itr : inputlib) - itr += "-stubs"; - } - }); - */ - parser.add_argument({ "--driver" }, "Force main or _init/_fini instrumentation") - .dtype("boolean") - .max_count(1) - .action([](parser_t& p) { is_driver = p.get("driver"); }); parser .add_argument({ "--load" }, "Supplemental instrumentation library names w/o extension (e.g. " @@ -471,6 +480,7 @@ main(int argc, char** argv) std::map module_function_map = { { "available_module_functions", &available_module_functions }, { "instrumented_module_functions", &instrumented_module_functions }, + { "coverage_module_functions", &coverage_module_functions }, { "excluded_module_functions", &excluded_module_functions }, { "overlapping_module_functions", &overlapping_module_functions }, }; @@ -545,6 +555,12 @@ main(int argc, char** argv) use_line_info = true; } }); + parser.add_argument() + .names({ "-C", "--config" }) + .dtype("string") + .min_count(1) + .description("Read in a configuration file and encode these values as the " + "defaults in the executable"); parser.add_argument() .names({ "-d", "--default-components" }) .dtype("string") @@ -626,6 +642,18 @@ main(int argc, char** argv) .action([](parser_t& p) { min_loop_address_range = p.get("min-address-range-loop"); }); + parser.add_argument({ "--coverage" }, "Enable recording the code coverage") + .max_count(1) + .choices({ "none", "function", "basic_block" }) + .action([](parser_t& p) { + auto _v = p.get("coverage"); + if(_v == "function" || _v.empty()) + coverage_mode = CODECOV_FUNCTION; + else if(_v == "basic_block") + coverage_mode = CODECOV_BASIC_BLOCK; + else + coverage_mode = CODECOV_NONE; + }); parser .add_argument({ "--dynamic-callsites" }, "Force instrumentation if a function has dynamic callsites (e.g. " @@ -703,6 +731,41 @@ main(int argc, char** argv) return -1; } + if(parser.exists("config")) + { + struct omnitrace_env_config_s + {}; + auto _configs = parser.get("config"); + for(auto&& itr : _configs) + { + auto _settings = tim::settings::push(); + for(auto&& itr : *_settings) + { + itr.second->set_config_updated(false); + itr.second->set_environ_updated(false); + } + _settings->read(itr); + for(auto&& itr : *_settings) + { + if(itr.second && itr.second->get_config_updated()) + { + env_config_variables.emplace_back(TIMEMORY_JOIN( + '=', itr.second->get_env_name(), itr.second->as_string())); + verbprintf(1, "Exporting known config value :: %s\n", + env_config_variables.back().c_str()); + } + } + for(auto&& itr : _settings->get_unknown_configs()) + { + env_config_variables.emplace_back( + TIMEMORY_JOIN('=', itr.first, itr.second)); + verbprintf(1, "Exporting unknown config value :: %s\n", + env_config_variables.back().c_str()); + } + tim::settings::pop(); + } + } + auto _handle_heuristics = [&parser](std::string&& _exists, std::string&& _not_exists, auto& _field, auto _value, std::string&& _msg, bool _cond) { @@ -781,20 +844,17 @@ main(int argc, char** argv) outfile.c_str()); } - if(modfunc_dump_dir.empty()) + if(binary_rewrite) { - modfunc_dump_dir = tim::get_env("OMNITRACE_OUTPUT_PATH", ""); - if(modfunc_dump_dir.empty()) - { - auto _exe_base = (binary_rewrite) ? outfile : std::string{ cmdv0 }; - auto _pos = _exe_base.find_last_of('/'); - if(_pos != std::string::npos && _pos + 1 < _exe_base.length()) - _exe_base = _exe_base.substr(_pos + 1); - modfunc_dump_dir = TIMEMORY_JOIN("-", "omnitrace", _exe_base, "output"); - } + auto* _save = _cmdv[0]; + _cmdv[0] = const_cast(outfile.c_str()); + tim::timemory_init(_cmdc, _cmdv, "omnitrace-"); + _cmdv[0] = _save; + } + else + { + tim::timemory_init(_cmdc, _cmdv, "omnitrace-"); } - - if(verbose_level >= 0) tim::makedir(modfunc_dump_dir); //----------------------------------------------------------------------------------// // @@ -914,9 +974,7 @@ main(int argc, char** argv) // for runtime instrumentation, we need to set this before the process gets created if(!binary_rewrite) { -#if defined(OMNITRACE_USE_ROCTRACER) tim::set_env("HSA_ENABLE_INTERRUPT", "0", 0); -#endif if(_pid >= 0) { verbprintf(-10, "#-------------------------------------------------------" @@ -1053,15 +1111,10 @@ main(int argc, char** argv) std::cout << '\n' << std::endl; } - auto _output_prefix = tim::get_env("OMNITRACE_OUTPUT_PREFIX", ""); - - dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir, - TIMEMORY_JOIN("", _output_prefix, "available-instr")), - available_module_functions, 1, werror, "available-instr", print_formats); - dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir, - TIMEMORY_JOIN("", _output_prefix, "overlapping-instr")), - overlapping_module_functions, 1, werror, "overlapping_module_functions", + dump_info("available-instr", available_module_functions, 1, werror, "available-instr", print_formats); + dump_info("overlapping-instr", overlapping_module_functions, 1, werror, + "overlapping_module_functions", print_formats); //----------------------------------------------------------------------------------// // @@ -1139,8 +1192,8 @@ main(int argc, char** argv) // //----------------------------------------------------------------------------------// - auto* _mutatee_init = find_function(app_image, "_init"); - auto* _mutatee_fini = find_function(app_image, "_fini"); + auto* main_init = find_function(app_image, "_init"); + auto* main_fini = find_function(app_image, "_fini"); auto* main_func = find_function(app_image, main_fname.c_str()); auto* mpi_init_func = find_function(app_image, "MPI_Init", { "MPI_Init_thread" }); auto* mpi_fini_func = find_function(app_image, "MPI_Finalize"); @@ -1168,12 +1221,14 @@ main(int argc, char** argv) verbprintf(0, "Finding instrumentation functions...\n"); - auto* entr_trace = find_function(app_image, "omnitrace_push_trace"); - auto* exit_trace = find_function(app_image, "omnitrace_pop_trace"); - auto* init_func = find_function(app_image, "omnitrace_init"); - auto* fini_func = find_function(app_image, "omnitrace_finalize"); - auto* env_func = find_function(app_image, "omnitrace_set_env"); - auto* mpi_func = find_function(app_image, "omnitrace_set_mpi"); + auto* init_func = find_function(app_image, "omnitrace_init"); + auto* fini_func = find_function(app_image, "omnitrace_finalize"); + auto* env_func = find_function(app_image, "omnitrace_set_env"); + auto* mpi_func = find_function(app_image, "omnitrace_set_mpi"); + auto* entr_trace = find_function(app_image, "omnitrace_push_trace"); + auto* exit_trace = find_function(app_image, "omnitrace_pop_trace"); + auto* reg_src_func = find_function(app_image, "omnitrace_register_source"); + auto* reg_cov_func = find_function(app_image, "omnitrace_register_coverage"); if(!main_func && main_fname == "main") main_func = find_function(app_image, "_main"); @@ -1293,36 +1348,42 @@ main(int argc, char** argv) // //----------------------------------------------------------------------------------// - if(!main_func && is_driver) + if(!main_func) { - errprintf(0, "could not find '%s'\n", main_fname.c_str()); - if(!_mutatee_init || !_mutatee_fini) + if(!main_init && !main_fini) { - errprintf(-1, "could not find '%s' or '%s', aborting\n", "_init", "_fini"); + errprintf(-1, "could not find '%s', '_init' or '_fini', aborting...\n", + main_fname.c_str()); + } + else if(!main_init) + { + errprintf(-1, "could not find '%s' or '_init', aborting...\n", + main_fname.c_str()); + } + else if(!main_fini) + { + errprintf(-1, "could not find '%s' or '_fini', aborting...\n", + main_fname.c_str()); } else { - errprintf(0, "using '%s' and '%s' in lieu of '%s'...", "_init", "_fini", - main_fname.c_str()); + verbprintf(0, "using '%s' and '%s' in lieu of '%s'...", "_init", "_fini", + main_fname.c_str()); } } - else if(!main_func && !is_driver) - { - verbprintf(0, "Warning! No main function and is not driver!\n"); - } using pair_t = std::pair; for(const auto& itr : - { pair_t(main_func, main_fname), pair_t(entr_trace, "omnitrace_push_trace"), + { pair_t(entr_trace, "omnitrace_push_trace"), pair_t(exit_trace, "omnitrace_pop_trace"), pair_t(init_func, "omnitrace_init"), - pair_t(fini_func, "omnitrace_finalize"), - pair_t(env_func, "omnitrace_set_env") }) + pair_t(fini_func, "omnitrace_finalize"), pair_t(env_func, "omnitrace_set_env"), + pair_t(reg_src_func, "omnitrace_register_source"), + pair_t(reg_cov_func, "omnitrace_register_coverage") }) { - if(itr.first == main_func && !is_driver) continue; if(!itr.first) { - errprintf(-1, "could not find required function :: '%s;\n", + errprintf(-1, "could not find required function :: '%s'\n", itr.second.c_str()); } } @@ -1350,8 +1411,8 @@ main(int argc, char** argv) bool has_debug_info = false; check_for_debug_info(has_debug_info, main_func); - check_for_debug_info(has_debug_info, _mutatee_init); - check_for_debug_info(has_debug_info, _mutatee_fini); + check_for_debug_info(has_debug_info, main_init); + check_for_debug_info(has_debug_info, main_fini); //----------------------------------------------------------------------------------// // @@ -1370,18 +1431,15 @@ main(int argc, char** argv) main_exit_points = main_func->findPoint(BPatch_exit); verbprintf(2, "Done\n"); } - else if(is_driver) + else { - if(_mutatee_init) - { - verbprintf(2, "Finding init entry...\n"); - main_entr_points = _mutatee_init->findPoint(BPatch_entry); - } - if(_mutatee_fini) - { - verbprintf(2, "Finding fini exit...\n"); - main_exit_points = _mutatee_fini->findPoint(BPatch_exit); - } + verbprintf(2, "Finding init entry... "); + main_entr_points = main_init->findPoint(BPatch_entry); + verbprintf(2, "Done\n"); + + verbprintf(2, "Finding fini exit... "); + main_exit_points = main_fini->findPoint(BPatch_exit); + verbprintf(2, "Done\n"); } //----------------------------------------------------------------------------------// @@ -1452,9 +1510,12 @@ main(int argc, char** argv) // prioritize the user environment arguments auto env_vars = parser.get("env"); + env_vars.reserve(env_vars.size() + env_config_variables.size()); + for(auto&& itr : env_config_variables) + env_vars.emplace_back(itr); env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_MODE", instr_mode)); -#if defined(OMNITRACE_USE_ROCTRACER) env_vars.emplace_back(TIMEMORY_JOIN('=', "HSA_ENABLE_INTERRUPT", "0")); +#if defined(OMNITRACE_USE_ROCTRACER) && OMNITRACE_USE_ROCTRACER > 0 env_vars.emplace_back(TIMEMORY_JOIN('=', "HSA_TOOLS_LIB", _libname)); #endif env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_MPI_INIT", "OFF")); @@ -1467,6 +1528,8 @@ main(int argc, char** argv) env_vars.emplace_back( TIMEMORY_JOIN('=', "OMNITRACE_USE_MPIP", (binary_rewrite && use_mpi && use_mpip) ? "ON" : "OFF")); + env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_USE_CODE_COVERAGE", + (coverage_mode != CODECOV_NONE) ? "ON" : "OFF")); if(use_mpi) env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_USE_PID", "ON")); for(auto& itr : env_vars) @@ -1508,7 +1571,7 @@ main(int argc, char** argv) if(umpi_call) init_names.emplace_back(umpi_call.get()); if(init_call) init_names.emplace_back(init_call.get()); - if(main_beg_call) init_names.emplace_back(main_beg_call.get()); + if(main_func && main_beg_call) init_names.emplace_back(main_beg_call.get()); for(const auto& itr : end_expr) if(itr.second) fini_names.emplace_back(itr.second.get()); @@ -1524,14 +1587,20 @@ main(int argc, char** argv) { for(const auto& itr : available_module_functions) { + bool _is_not_main = itr.function != main_func && itr.function != main_init && + itr.function != main_fini; if(itr.should_instrument()) { - if(itr.function != main_func && itr.function != _mutatee_init && - itr.function != _mutatee_fini) + if(_is_not_main) _insert_module_function(instrumented_module_functions, itr); } else _insert_module_function(excluded_module_functions, itr); + if(coverage_mode != CODECOV_NONE) + { + if(itr.should_coverage_instrument() && _is_not_main) + _insert_module_function(coverage_module_functions, itr); + } if(itr.is_overlapping()) _insert_module_function(overlapping_module_functions, itr); } @@ -1581,55 +1650,103 @@ main(int argc, char** argv) verbprintf(2, "Beginning instrumentation loop...\n"); verbprintf(1, "\n"); - std::map> _pass_info{}; - const int _pass_verbose_lvl = 2; - for(const auto& itr : instrumented_module_functions) - { - auto _count = itr(addr_space, entr_trace, exit_trace); - _pass_info[itr.module_name].first += _count.first; - _pass_info[itr.module_name].second += _count.second; - - auto _report = [](int _lvl, const string_t& _action, const string_t& _type, - const string_t& _reason, const string_t& _name, - const std::string& _extra = {}) { - static std::map already_reported{}; - auto _key = _type + _action + _reason; - if(already_reported[_key].count(_name) == 0) - { - verbprintf(_lvl, "[%s][%s] %s :: '%s'", _type.c_str(), _action.c_str(), - _reason.c_str(), _name.c_str()); - if(!_extra.empty()) verbprintf_bare(_lvl, " (%s)", _extra.c_str()); - verbprintf_bare(_lvl, "...\n"); - already_reported[_key].insert(_name); - } - }; - - for(const auto& mitr : itr.messages) - _report(std::get<0>(mitr), std::get<1>(mitr), std::get<2>(mitr), - std::get<3>(mitr), - std::get<2>(mitr) == "module" ? itr.module_name : itr.function_name); - } - verbprintf(1, "\n"); - - // report the instrumented - for(auto& itr : _pass_info) - { - auto _valid = (verbose_level > _pass_verbose_lvl || - (itr.second.first + itr.second.second) > 0); - if(_valid) + auto _report_info = [](int _lvl, const string_t& _action, const string_t& _type, + const string_t& _reason, const string_t& _name, + const std::string& _extra = {}) { + static std::map already_reported{}; + auto _key = TIMEMORY_JOIN('_', _type, _action, _reason, _name, _extra); + if(already_reported[_key].count(_name) == 0) { + verbprintf(_lvl, "[%s][%s] %s :: '%s'", _type.c_str(), _action.c_str(), + _reason.c_str(), _name.c_str()); + if(!_extra.empty()) verbprintf_bare(_lvl, " (%s)", _extra.c_str()); + verbprintf_bare(_lvl, "...\n"); + already_reported[_key].insert(_name); + } + }; + + if(instr_mode != "coverage") + { + std::map> _pass_info{}; + const int _pass_verbose_lvl = 1; + for(const auto& itr : instrumented_module_functions) + { + auto _count = itr(addr_space, entr_trace, exit_trace); + _pass_info[itr.module_name].first += _count.first; + _pass_info[itr.module_name].second += _count.second; + + for(const auto& mitr : itr.messages) + _report_info(std::get<0>(mitr), std::get<1>(mitr), std::get<2>(mitr), + std::get<3>(mitr), + std::get<2>(mitr) == "module" ? itr.module_name + : itr.function_name); + } + + // report the trace instrumented functions + for(auto& itr : _pass_info) + { + auto _valid = (verbose_level > _pass_verbose_lvl || + (itr.second.first + itr.second.second) > 0); + if(!_valid) continue; verbprintf(_pass_verbose_lvl, "%4zu instrumented procedures in %s\n", itr.second.first, itr.first.c_str()); _valid = (loop_level_instr && (verbose_level > _pass_verbose_lvl || itr.second.second > 0)); if(_valid) { - verbprintf(_pass_verbose_lvl, "%4zu instrumented loop procedures in %s\n", + verbprintf(_pass_verbose_lvl, "%4zu instrumented loops in procedure %s\n", itr.second.second, itr.first.c_str()); } } } + if(coverage_mode != CODECOV_NONE) + { + std::map> _covr_info{}; + const int _covr_verbose_lvl = 1; + for(const auto& itr : coverage_module_functions) + { + itr.register_source(addr_space, reg_src_func, *main_entr_points); + auto _count = itr.register_coverage(addr_space, reg_cov_func); + _covr_info[itr.module_name].first += _count.first; + _covr_info[itr.module_name].second += _count.second; + + for(const auto& mitr : itr.messages) + _report_info(std::get<0>(mitr), std::get<1>(mitr), std::get<2>(mitr), + std::get<3>(mitr), + std::get<2>(mitr) == "module" ? itr.module_name + : itr.function_name); + } + + // report the coverage instrumented functions + for(auto& itr : _covr_info) + { + auto _valid = (verbose_level > _covr_verbose_lvl || + (itr.second.first + itr.second.second) > 0); + if(!_valid) continue; + switch(coverage_mode) + { + case CODECOV_NONE: + { + break; + } + case CODECOV_FUNCTION: + { + verbprintf(_covr_verbose_lvl, "%4zu coverage functions in %s\n", + itr.second.first, itr.first.c_str()); + break; + } + case CODECOV_BASIC_BLOCK: + { + verbprintf(_covr_verbose_lvl, "%4zu coverage basic blocks in %s\n", + itr.second.second, itr.first.c_str()); + break; + } + } + } + } + verbprintf(1, "\n"); + if(app_thread) { verbprintf(1, "Finalizing insertion set...\n"); @@ -1692,26 +1809,17 @@ main(int argc, char** argv) _insert_module_function(excluded_module_functions, itr); } - bool _dump_and_exit = ((print_available.length() + print_instrumented.length() + - print_overlapping.length() + print_excluded.length()) > 0) || - explicit_dump_and_exit; - - dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir, - TIMEMORY_JOIN("", _output_prefix, "available-instr")), - available_module_functions, 0, werror, "available_module_functions", - print_formats); - dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir, - TIMEMORY_JOIN("", _output_prefix, "instrumented-instr")), - instrumented_module_functions, 0, werror, "instrumented_module_functions", - print_formats); - dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir, - TIMEMORY_JOIN("", _output_prefix, "excluded-instr")), - excluded_module_functions, 0, werror, "excluded_module_functions", - print_formats); - dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir, - TIMEMORY_JOIN("", _output_prefix, "overlapping-instr")), - overlapping_module_functions, 0, werror, "overlapping_module_functions", - print_formats); + dump_info("available-instr", available_module_functions, 0, werror, + "available_module_functions", print_formats); + dump_info("instrumented-instr", instrumented_module_functions, 0, werror, + "instrumented_module_functions", print_formats); + dump_info("excluded-instr", excluded_module_functions, 0, werror, + "excluded_module_functions", print_formats); + if(coverage_mode != CODECOV_NONE) + dump_info("coverage-instr", coverage_module_functions, 0, werror, + "coverage_module_functions", print_formats); + dump_info("overlapping-instr", overlapping_module_functions, 0, werror, + "overlapping_module_functions", print_formats); auto _dump_info = [](const std::string& _label, const string_t& _mode, const fmodset_t& _modset) { @@ -1733,13 +1841,13 @@ main(int argc, char** argv) { for(const auto& itr : _modset) _insert(itr.module_name, TIMEMORY_JOIN("", "[", itr.function_name, "][", - itr.address_range, "]")); + itr.num_instructions, "]")); } else if(_mode == "functions+") { for(const auto& itr : _modset) _insert(itr.module_name, TIMEMORY_JOIN("", "[", itr.signature.get(), "][", - itr.address_range, "]")); + itr.num_instructions, "]")); } else if(_mode == "pair") { @@ -1748,7 +1856,7 @@ main(int argc, char** argv) std::stringstream _ss{}; _ss << std::boolalpha; _ss << "" << itr.module_name << "] --> [" << itr.function_name << "][" - << itr.address_range << "]"; + << itr.num_instructions << "]"; _insert(itr.module_name, _ss.str()); } } @@ -1759,7 +1867,7 @@ main(int argc, char** argv) std::stringstream _ss{}; _ss << std::boolalpha; _ss << "[" << itr.module_name << "] --> [" << itr.signature.get() << "][" - << itr.address_range << "]"; + << itr.num_instructions << "]"; _insert(itr.module_name, _ss.str()); } } @@ -1785,10 +1893,12 @@ main(int argc, char** argv) _dump_info("instrumented", print_instrumented, instrumented_module_functions); if(!print_excluded.empty()) _dump_info("excluded", print_excluded, excluded_module_functions); + if(!print_coverage.empty()) + _dump_info("coverage", print_coverage, coverage_module_functions); if(!print_overlapping.empty()) _dump_info("overlapping", print_overlapping, overlapping_module_functions); - if(_dump_and_exit) exit(EXIT_SUCCESS); + if(simulate) exit(EXIT_SUCCESS); //----------------------------------------------------------------------------------// // @@ -1922,154 +2032,6 @@ main(int argc, char** argv) return code; } -//======================================================================================// - -bool -instrument_module(const string_t& file_name) -{ - auto _report = [&file_name](const string_t& _action, const string_t& _reason, - int _lvl) { - static strset_t already_reported{}; - if(already_reported.count(file_name) == 0) - { - verbprintf(_lvl, "%s module [%s] : '%s'...\n", _action.c_str(), - _reason.c_str(), file_name.c_str()); - already_reported.insert(file_name); - } - }; - - static std::regex ext_regex{ "\\.(s|S)$", regex_opts }; - static std::regex sys_regex{ "^(s|k|e|w)_[A-Za-z_0-9\\-]+\\.(c|C)$", regex_opts }; - static std::regex sys_build_regex{ "^(\\.\\./sysdeps/|/build/)", regex_opts }; - static std::regex dyninst_regex{ "(dyninst|DYNINST|(^|/)RT[[:graph:]]+\\.c$)", - regex_opts }; - static std::regex dependlib_regex{ "^(lib|)(omnitrace|pthread|caliper|gotcha|papi|" - "cupti|TAU|likwid|pfm|nvperf|unwind)", - regex_opts }; - static std::regex core_cmod_regex{ - "^(malloc|(f|)lock|sig|sem)[a-z_]+(|64|_r|_l)\\.c$" - }; - static std::regex core_lib_regex{ - "^(lib|)(c|dl|dw|pthread|tcmalloc|profiler|" - "tbbmalloc|tbbmalloc_proxy|malloc|stdc\\+\\+)(-|\\.)", - regex_opts - }; - static std::regex prefix_regex{ "^(_|\\.[a-zA-Z0-9])", regex_opts }; - - // file extensions that should not be instrumented - if(std::regex_search(file_name, ext_regex)) - { - return (_report("Excluding", "file extension", 3), false); - } - - // system modules that should not be instrumented (wastes time) - if(std::regex_search(file_name, sys_regex) || - std::regex_search(file_name, sys_build_regex)) - { - return (_report("Excluding", "system module", 3), false); - } - - // dyninst modules that must not be instrumented - if(std::regex_search(file_name, dyninst_regex)) - { - return (_report("Excluding", "dyninst module", 3), false); - } - - // modules used by omnitrace and dependent libraries - if(std::regex_search(file_name, core_lib_regex) || - std::regex_search(file_name, core_cmod_regex)) - { - return (_report("Excluding", "core module", 3), false); - } - - // modules used by omnitrace and dependent libraries - if(std::regex_search(file_name, dependlib_regex)) - { - return (_report("Excluding", "dependency module", 3), false); - } - - // known set of modules whose starting sequence of characters suggest it should not be - // instrumented (wastes time) - if(std::regex_search(file_name, prefix_regex)) - { - return (_report("Excluding", "prefix match", 3), false); - } - - _report("Including", "no constraint", 2); - - return true; -} - -//======================================================================================// - -bool -instrument_entity(const string_t& function_name) -{ - auto _report = [&function_name](const string_t& _action, const string_t& _reason, - int _lvl) { - static strset_t already_reported{}; - if(already_reported.count(function_name) == 0) - { - verbprintf(_lvl, "%s function [%s] : '%s'...\n", _action.c_str(), - _reason.c_str(), function_name.c_str()); - already_reported.insert(function_name); - } - }; - - static std::regex exclude( - "(omnitrace|tim::|N3tim|MPI_Init|MPI_Finalize|dyninst|tm_clones)", regex_opts); - static std::regex exclude_cxx( - "(std::_Sp_counted_base|std::(use|has)_facet|std::locale|::sentry|^std::_|::_(M|" - "S)_|::basic_string[a-zA-Z,<>: ]+::_M_create|::__|::_(Alloc|State)|" - "std::(basic_|)(ifstream|ios|istream|ostream|stream))", - regex_opts); - static std::regex leading("^(_|\\.|frame_dummy|transaction clone|virtual " - "thunk|non-virtual thunk|\\(|targ|kmp_threadprivate_)", - regex_opts); - static std::regex trailing( - "(_|\\.part\\.[0-9]+|\\.constprop\\.[0-9]+|\\.|\\.[0-9]+)$", regex_opts); - static strset_t whole = []() { - auto _v = get_whole_function_names(); - auto _ret = _v; - for(std::string _ext : { "64", "_l", "_r" }) - for(const auto& itr : _v) - _ret.emplace(itr + _ext); - return _ret; - }(); - - // don't instrument the functions when key is found anywhere in function name - if(std::regex_search(function_name, exclude) || - std::regex_search(function_name, exclude_cxx)) - { - _report("Excluding", "critical", 3); - return false; - } - - if(whole.count(function_name) > 0) - { - _report("Excluding", "critical", 3); - return false; - } - - // don't instrument the functions when key is found at the start of the function name - if(std::regex_search(function_name, leading)) - { - _report("Excluding", "recommended", 3); - return false; - } - - // don't instrument the functions when key is found at the end of the function name - if(std::regex_search(function_name, trailing)) - { - _report("Excluding", "recommended", 3); - return false; - } - - _report("Including", "no constraint", 2); - - return true; -} - //======================================================================================// // query_instr -- check whether there are one or more instrumentation points // @@ -2151,50 +2113,18 @@ query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc, flow_graph_t* cf // Constraints for instrumentation. Returns true for those modules that // shouldn't be instrumented. bool -module_constraint(string_view_t fname) +module_constraint(const char*) { - // fname is the name of module/file - string_t _fname = string_t{ fname }; - - // never instrumentat any module matching omnitrace - if(_fname.find("omnitrace") != string_t::npos) return true; - - // always instrument these modules - if(_fname == "DEFAULT_MODULE" || _fname == "LIBRARY_MODULE") return false; - - if(instrument_module(_fname)) return false; - - // do not instrument - return true; + return false; } //======================================================================================// // Constraint for routines. The constraint returns true for those routines that // should not be instrumented. bool -routine_constraint(string_view_t fname) +routine_constraint(const char*) { - string_t _fname = string_t{ fname }; - if(_fname.find("omnitrace") != string_t::npos) return true; - - auto npos = std::string::npos; - if(_fname.find("FunctionInfo") != npos || _fname.find("_L_lock") != npos || - _fname.find("_L_unlock") != npos) - return true; // Don't instrument - else - { - // Should the routine fname be instrumented? - if(instrument_entity(string_t(fname))) - { - // Yes it should be instrumented. Return false - return false; - } - else - { - // No. The selective instrumentation file says: don't instrument it - return true; - } - } + return false; } namespace diff --git a/projects/rocprofiler-systems/source/bin/omnitrace/omnitrace.hpp b/projects/rocprofiler-systems/source/bin/omnitrace/omnitrace.hpp index a5fc4a6959..b646d714db 100644 --- a/projects/rocprofiler-systems/source/bin/omnitrace/omnitrace.hpp +++ b/projects/rocprofiler-systems/source/bin/omnitrace/omnitrace.hpp @@ -282,7 +282,42 @@ omnitrace_fork_callback(thread_t* parent, thread_t* child) } // //======================================================================================// -// insert_instr -- generic insert instrumentation function +// insert_instr -- insert instrumentation into a function +// +template +bool +insert_instr(address_space_t* mutatee, const bpvector_t& _points, Tp traceFunc, + procedure_loc_t traceLoc, bool allow_traps) +{ + if(!traceFunc || _points.empty()) return false; + + auto _trace = traceFunc.get(); + auto _traps = std::set{}; + if(!allow_traps) + { + for(const auto& itr : _points) + { + if(itr && itr->usesTrap_NP()) _traps.insert(itr); + } + } + + size_t _n = 0; + for(const auto& itr : _points) + { + if(!itr || _traps.count(itr) > 0) + continue; + else if(traceLoc == BPatch_entry) + mutatee->insertSnippet(*_trace, *itr, BPatch_callBefore, BPatch_firstSnippet); + else + mutatee->insertSnippet(*_trace, *itr); + ++_n; + } + + return (_n > 0); +} +// +//======================================================================================// +// insert_instr -- insert instrumentation into loops // template bool @@ -311,27 +346,6 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc, if(_points == nullptr) return false; if(_points->empty()) return false; - /*if(loop_level_instr) - { - flow_graph_t* flow = funcToInstr->getCFG(); - bpvector_t basicLoop; - flow->getOuterLoops(basicLoop); - for(auto litr = basicLoop.begin(); litr != basicLoop.end(); ++litr) - { - bpvector_t* _tmp; - if(traceLoc == BPatch_entry) - _tmp = cfGraph->findLoopInstPoints(BPatch_locLoopEntry, *litr); - else if(traceLoc == BPatch_exit) - _tmp = cfGraph->findLoopInstPoints(BPatch_locLoopExit, *litr); - if(!_tmp) - continue; - for(auto& itr : *_tmp) - _points->push_back(itr); - } - }*/ - - // verbprintf(0, "Instrumenting |> [ %s ]\n", name.m_name.c_str()); - std::set _traps{}; if(!allow_traps) { @@ -348,9 +362,6 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc, continue; else if(traceLoc == BPatch_entry) mutatee->insertSnippet(*_trace, *itr, BPatch_callBefore, BPatch_firstSnippet); - // else if(traceLoc == BPatch_exit) - // mutatee->insertSnippet(*_trace, *itr, BPatch_callAfter, - // BPatch_firstSnippet); else mutatee->insertSnippet(*_trace, *itr); ++_n; @@ -358,3 +369,46 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc, return (_n > 0); } +// +//======================================================================================// +// insert_instr -- insert instrumentation into basic blocks +// +template +bool +insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc, + basic_block_t* basicBlock, bool allow_traps) +{ + point_t* _point = nullptr; + auto _trace = traceFunc.get(); + + basic_block_t* _bb = basicBlock; + switch(traceLoc) + { + case BPatch_entry: _point = _bb->findEntryPoint(); break; + case BPatch_exit: _point = _bb->findExitPoint(); break; + default: + verbprintf(0, "Warning! trace location type %i not supported\n", + (int) traceLoc); + return false; + } + + if(_point == nullptr) return false; + + if(!allow_traps && _point->usesTrap_NP()) return false; + + switch(traceLoc) + { + case BPatch_entry: + return (mutatee->insertSnippet(*_trace, *_point, BPatch_callBefore, + BPatch_firstSnippet) != nullptr); + case BPatch_exit: return (mutatee->insertSnippet(*_trace, *_point) != nullptr); + default: + { + verbprintf(0, "Warning! trace location type %i not supported\n", + (int) traceLoc); + return false; + } + } + + return false; +} diff --git a/projects/rocprofiler-systems/source/bin/tests/CMakeLists.txt b/projects/rocprofiler-systems/source/bin/tests/CMakeLists.txt index 3fa98fb1ee..927eae60a7 100644 --- a/projects/rocprofiler-systems/source/bin/tests/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/bin/tests/CMakeLists.txt @@ -4,7 +4,7 @@ function(OMNITRACE_ADD_BIN_TEST) TEST "" # options "NAME;TARGET;TIMEOUT;WORKING_DIRECTORY" # single value args - "ARGS;ENVIRONMENT;LABELS;PROPERTIES;PASS_REGULAR_EXPRESSION;FAIL_REGULAR_EXPRESSION;SKIP_REGULAR_EXPRESSION;DEPENDS;COMMAND" # multiple + "ARGS;ENVIRONMENT;LABELS;PROPERTIES;PASS_REGEX;FAIL_REGEX;SKIP_REGEX;DEPENDS;COMMAND" # multiple # value args ${ARGN}) @@ -50,11 +50,11 @@ function(OMNITRACE_ADD_BIN_TEST) LABELS "omnitrace-bin;${TEST_LABELS}" PASS_REGULAR_EXPRESSION - "${TEST_PASS_REGULAR_EXPRESSION}" + "${TEST_PASS_REGEX}" FAIL_REGULAR_EXPRESSION - "${TEST_FAIL_REGULAR_EXPRESSION}" + "${TEST_FAIL_REGEX}" SKIP_REGULAR_EXPRESSION - "${TEST_SKIP_REGULAR_EXPRESSION}" + "${TEST_SKIP_REGEX}" ${TEST_PROPERTIES}) elseif(TARGET ${TEST_TARGET}) add_test( @@ -73,11 +73,11 @@ function(OMNITRACE_ADD_BIN_TEST) LABELS "omnitrace-bin;${TEST_LABELS}" PASS_REGULAR_EXPRESSION - "${TEST_PASS_REGULAR_EXPRESSION}" + "${TEST_PASS_REGEX}" FAIL_REGULAR_EXPRESSION - "${TEST_FAIL_REGULAR_EXPRESSION}" + "${TEST_FAIL_REGEX}" SKIP_REGULAR_EXPRESSION - "${TEST_SKIP_REGULAR_EXPRESSION}" + "${TEST_SKIP_REGEX}" ${TEST_PROPERTIES}) elseif(OMNITRACE_BUILD_TESTING) message(FATAL_ERROR "Error! ${TEST_TARGET} does not exist") @@ -90,7 +90,7 @@ omnitrace_add_bin_test( ARGS --help LABELS omnitrace-exe TIMEOUT 45 - PASS_REGULAR_EXPRESSION + PASS_REGEX ".*\\\[omnitrace\\\] Usage:.*\\\[DEBUG OPTIONS\\\].*\\\[MODE OPTIONS\\\].*\\\[LIBRARY OPTIONS\\\].*\\\[SYMBOL SELECTION OPTIONS\\\].*\\\[RUNTIME OPTIONS\\\].*\\\[GRANULARITY OPTIONS\\\].*\\\[DYNINST OPTIONS\\\].*" ) @@ -115,7 +115,7 @@ omnitrace_add_bin_test( WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/omnitrace-tests-output/omnitrace-exe-simulate-ls TIMEOUT 60 - PASS_REGULAR_EXPRESSION + PASS_REGEX ".*available-instr.json.*available-instr.txt.*available-instr.xml.*excluded-instr.json.*excluded-instr.txt.*excluded-instr.xml.*instrumented-instr.json.*instrumented-instr.txt.*instrumented-instr.xml.*overlapping-instr.json.*overlapping-instr.txt.*overlapping-instr.xml.*" ) @@ -125,7 +125,7 @@ omnitrace_add_bin_test( ARGS --help LABELS omnitrace-avail TIMEOUT 45 - PASS_REGULAR_EXPRESSION + PASS_REGEX ".*\\\[omnitrace-avail\\\] Usage:.*\\\[CATEGORIES\\\].*\\\[VIEW OPTIONS\\\].*\\\[COLUMN OPTIONS\\\].*\\\[WIDTH OPTIONS\\\].*\\\[OUTPUT OPTIONS\\\].*" ) @@ -135,7 +135,7 @@ omnitrace_add_bin_test( ARGS -r wall_clock -C --available LABELS omnitrace-avail TIMEOUT 45 - PASS_REGULAR_EXPRESSION + PASS_REGEX "\\\|[-]+\\\|\n\\\|[ ]+COMPONENT[ ]+\\\|\n\\\|[-]+\\\|\n\\\| (wall_clock)[ ]+\\\|\n\\\| (sampling_wall_clock)[ ]+\\\|\n\\\|[-]+\\\|" ) @@ -145,8 +145,8 @@ omnitrace_add_bin_test( ARGS --categories settings::omnitrace --brief LABELS omnitrace-avail TIMEOUT 45 - PASS_REGULAR_EXPRESSION "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE|OUTPUT_PREFIX)" - FAIL_REGULAR_EXPRESSION + PASS_REGEX "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE|OUTPUT_PREFIX)" + FAIL_REGEX "OMNITRACE_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)" ) @@ -156,6 +156,6 @@ omnitrace_add_bin_test( ARGS --categories settings::timemory --brief LABELS omnitrace-avail TIMEOUT 45 - PASS_REGULAR_EXPRESSION + PASS_REGEX "OMNITRACE_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)" - FAIL_REGULAR_EXPRESSION "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE)") + FAIL_REGEX "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE)") diff --git a/projects/rocprofiler-systems/source/lib/common/invoke.hpp b/projects/rocprofiler-systems/source/lib/common/invoke.hpp index ccd4734e7b..7a3e689906 100644 --- a/projects/rocprofiler-systems/source/lib/common/invoke.hpp +++ b/projects/rocprofiler-systems/source/lib/common/invoke.hpp @@ -62,7 +62,7 @@ get_thread_index() template auto -invoke(const char* _name, int _verbose, FuncT&& _func, Args... _args) +invoke(const char* _name, int _verbose, bool& _toggle, FuncT&& _func, Args... _args) { if(_func) { @@ -78,6 +78,7 @@ invoke(const char* _name, int _verbose, FuncT&& _func, Args... _args) int32_t _lk = get_guard()++; if(_lk == 0) { + _toggle = !_toggle; if(_verbose >= 3) { fflush(stderr); diff --git a/projects/rocprofiler-systems/source/lib/omnitrace-dl/dl.cpp b/projects/rocprofiler-systems/source/lib/omnitrace-dl/dl.cpp index 1d60fdc749..dcd21345bc 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace-dl/dl.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace-dl/dl.cpp @@ -32,6 +32,8 @@ #include "common/invoke.hpp" #include "common/join.hpp" +#include + //--------------------------------------------------------------------------------------// #define OMNITRACE_DLSYM(VARNAME, HANDLE, FUNCNAME) \ @@ -183,6 +185,11 @@ struct OMNITRACE_HIDDEN_API indirect OMNITRACE_DLSYM(omnitrace_pop_trace_f, m_omnihandle, "omnitrace_pop_trace"); OMNITRACE_DLSYM(omnitrace_push_region_f, m_omnihandle, "omnitrace_push_region"); OMNITRACE_DLSYM(omnitrace_pop_region_f, m_omnihandle, "omnitrace_pop_region"); + OMNITRACE_DLSYM(omnitrace_register_source_f, m_omnihandle, + "omnitrace_register_source"); + OMNITRACE_DLSYM(omnitrace_register_coverage_f, m_omnihandle, + "omnitrace_register_coverage"); + #if OMNITRACE_USE_OMPT == 0 _warn_verbose = 5; #endif @@ -231,16 +238,19 @@ struct OMNITRACE_HIDDEN_API indirect } public: - void (*omnitrace_init_library_f)(void) = nullptr; - void (*omnitrace_init_f)(const char*, bool, const char*) = nullptr; - void (*omnitrace_finalize_f)(void) = nullptr; - void (*omnitrace_set_env_f)(const char*, const char*) = nullptr; - void (*omnitrace_set_mpi_f)(bool, bool) = nullptr; - void (*omnitrace_push_trace_f)(const char*) = nullptr; - void (*omnitrace_pop_trace_f)(const char*) = nullptr; - int (*omnitrace_push_region_f)(const char*) = nullptr; - int (*omnitrace_pop_region_f)(const char*) = nullptr; - int (*omnitrace_user_configure_f)(int, void*, void*) = nullptr; + void (*omnitrace_init_library_f)(void) = nullptr; + void (*omnitrace_init_f)(const char*, bool, const char*) = nullptr; + void (*omnitrace_finalize_f)(void) = nullptr; + void (*omnitrace_set_env_f)(const char*, const char*) = nullptr; + void (*omnitrace_set_mpi_f)(bool, bool) = nullptr; + void (*omnitrace_register_source_f)(const char*, const char*, size_t, size_t, + const char*) = nullptr; + void (*omnitrace_register_coverage_f)(const char*, const char*, size_t) = nullptr; + void (*omnitrace_push_trace_f)(const char*) = nullptr; + void (*omnitrace_pop_trace_f)(const char*) = nullptr; + int (*omnitrace_push_region_f)(const char*) = nullptr; + int (*omnitrace_pop_region_f)(const char*) = nullptr; + int (*omnitrace_user_configure_f)(int, void*, void*) = nullptr; ompt_start_tool_result_t* (*ompt_start_tool_f)(unsigned int, const char*); private: @@ -262,18 +272,32 @@ get_indirect() return _v; } +auto& +get_inited() +{ + static bool* _v = new bool{ false }; + return *_v; +} + +auto& +get_finied() +{ + static bool* _v = new bool{ false }; + return *_v; +} + auto& get_active() { - static bool _v = false; - return _v; + static bool* _v = new bool{ false }; + return *_v; } auto& get_enabled() { - static std::atomic _v{ get_env("OMNITRACE_INIT_ENABLED", true) }; - return _v; + static auto* _v = new std::atomic{ get_env("OMNITRACE_INIT_ENABLED", true) }; + return *_v; } auto& @@ -283,19 +307,25 @@ get_thread_enabled() return _v; } -auto& -get_count() -{ - static std::atomic _v{ 0 }; - return _v; -} - auto& get_thread_count() { static thread_local int64_t _v = 0; return _v; } + +auto& +get_thread_status() +{ + static thread_local bool _v = false; + return _v; +} + +// ensure finalization is called +bool _omnitrace_dl_fini = (std::atexit([]() { + if(get_active()) omnitrace_finalize(); + }), + true); } // namespace } // namespace dl } // namespace omnitrace @@ -304,8 +334,21 @@ get_thread_count() #define OMNITRACE_DL_INVOKE(...) \ ::omnitrace::common::invoke(__FUNCTION__, ::omnitrace::dl::_omnitrace_dl_verbose, \ + (::omnitrace::dl::get_thread_status() = false), \ __VA_ARGS__) +#define OMNITRACE_DL_INVOKE_STATUS(STATUS, ...) \ + ::omnitrace::common::invoke(__FUNCTION__, ::omnitrace::dl::_omnitrace_dl_verbose, \ + STATUS, __VA_ARGS__) + +#define OMNITRACE_DL_LOG(LEVEL, ...) \ + if(::omnitrace::dl::_omnitrace_dl_verbose >= LEVEL) \ + { \ + fflush(stderr); \ + fprintf(stderr, "[omnitrace][" OMNITRACE_COMMON_LIBRARY_NAME "] " __VA_ARGS__); \ + fflush(stderr); \ + } + using omnitrace::get_indirect; namespace dl = omnitrace::dl; @@ -318,14 +361,50 @@ extern "C" void omnitrace_init(const char* a, bool b, const char* c) { - OMNITRACE_DL_INVOKE(get_indirect().omnitrace_init_f, a, b, c); - dl::get_active() = true; + if(dl::get_inited() && dl::get_finied()) + { + OMNITRACE_DL_LOG(2, "%s(%s) ignored :: already initialized and finalized\n", + __FUNCTION__, ::omnitrace::join(", ", a, b, c).c_str()); + return; + } + else if(dl::get_inited() && dl::get_active()) + { + OMNITRACE_DL_LOG(2, "%s(%s) ignored :: already initialized and active\n", + __FUNCTION__, ::omnitrace::join(", ", a, b, c).c_str()); + return; + } + + bool _invoked = false; + OMNITRACE_DL_INVOKE_STATUS(_invoked, get_indirect().omnitrace_init_f, a, b, c); + if(_invoked) + { + dl::get_active() = true; + dl::get_inited() = true; + } } void omnitrace_finalize(void) { - dl::get_active() = false; - OMNITRACE_DL_INVOKE(get_indirect().omnitrace_finalize_f); + if(dl::get_inited() && dl::get_finied()) + { + OMNITRACE_DL_LOG(2, "%s() ignored :: already initialized and finalized\n", + __FUNCTION__); + return; + } + else if(dl::get_finied() && !dl::get_active()) + { + OMNITRACE_DL_LOG(2, "%s() ignored :: already finalized but not active\n", + __FUNCTION__); + return; + } + + bool _invoked = false; + OMNITRACE_DL_INVOKE_STATUS(_invoked, get_indirect().omnitrace_finalize_f); + if(_invoked) + { + dl::get_active() = false; + dl::get_finied() = true; + } } void omnitrace_push_trace(const char* name) @@ -391,6 +470,19 @@ extern "C" OMNITRACE_DL_INVOKE(get_indirect().omnitrace_set_mpi_f, a, b); } + void omnitrace_register_source(const char* file, const char* func, size_t line, + size_t address, const char* source) + { + OMNITRACE_DL_INVOKE(get_indirect().omnitrace_register_source_f, file, func, line, + address, source); + } + + void omnitrace_register_coverage(const char* file, const char* func, size_t address) + { + OMNITRACE_DL_INVOKE(get_indirect().omnitrace_register_coverage_f, file, func, + address); + } + int omnitrace_user_start_trace_dl(void) { dl::get_enabled().store(true); diff --git a/projects/rocprofiler-systems/source/lib/omnitrace-dl/dl.hpp b/projects/rocprofiler-systems/source/lib/omnitrace-dl/dl.hpp index f23bc2010c..afea26fef1 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace-dl/dl.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace-dl/dl.hpp @@ -64,6 +64,11 @@ extern "C" void omnitrace_pop_trace(const char* name) OMNITRACE_PUBLIC_API; void omnitrace_push_region(const char*) OMNITRACE_PUBLIC_API; void omnitrace_pop_region(const char*) OMNITRACE_PUBLIC_API; + void omnitrace_register_source(const char* file, const char* func, size_t line, + size_t address, + const char* source) OMNITRACE_PUBLIC_API; + void omnitrace_register_coverage(const char* file, const char* func, + size_t address) OMNITRACE_PUBLIC_API; #if defined(OMNITRACE_DL_SOURCE) && (OMNITRACE_DL_SOURCE > 0) int omnitrace_user_start_trace_dl(void) OMNITRACE_HIDDEN_API; diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/CMakeLists.txt b/projects/rocprofiler-systems/source/lib/omnitrace/CMakeLists.txt index 0a6f009cb0..e98a0bafae 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/lib/omnitrace/CMakeLists.txt @@ -30,9 +30,7 @@ target_link_libraries( $ $ $ - $ - $ - $ + $ $,$,> $,$,> $,omnitrace::omnitrace-sanitizer,>) @@ -53,6 +51,7 @@ set(library_sources ${CMAKE_CURRENT_LIST_DIR}/src/library.cpp ${CMAKE_CURRENT_LIST_DIR}/src/library/api.cpp ${CMAKE_CURRENT_LIST_DIR}/src/library/config.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/library/coverage.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 @@ -79,6 +78,7 @@ set(library_headers ${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/coverage.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 diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library.hpp index e2cae066d9..5ef2d61054 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library.hpp @@ -40,6 +40,7 @@ #include "library/ptl.hpp" #include "library/debug.hpp" #include "library/critical_trace.hpp" +#include "library/runtime.hpp" #include #include diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/api.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/api.hpp index 38e05cbb39..71473de9eb 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/api.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/api.hpp @@ -26,6 +26,8 @@ #include +#include + // forward decl of the API extern "C" { @@ -57,6 +59,15 @@ extern "C" /// stops an instrumentation region (user-defined) int omnitrace_pop_region(const char* name) OMNITRACE_PUBLIC_API; + /// stores source code information + void omnitrace_register_source(const char* file, const char* func, size_t line, + size_t address, + const char* source) OMNITRACE_PUBLIC_API; + + /// increments coverage values + void omnitrace_register_coverage(const char* file, const char* func, + size_t address) OMNITRACE_PUBLIC_API; + // these are the real implementations for internal calling convention void omnitrace_init_library_hidden(void) OMNITRACE_HIDDEN_API; void omnitrace_init_hidden(const char*, bool, const char*) OMNITRACE_HIDDEN_API; @@ -68,4 +79,9 @@ extern "C" void omnitrace_pop_trace_hidden(const char* name) OMNITRACE_HIDDEN_API; void omnitrace_push_region_hidden(const char* name) OMNITRACE_HIDDEN_API; void omnitrace_pop_region_hidden(const char* name) OMNITRACE_HIDDEN_API; + void omnitrace_register_source_hidden(const char* file, const char* func, size_t line, + size_t address, + const char* source) OMNITRACE_HIDDEN_API; + void omnitrace_register_coverage_hidden(const char* file, const char* func, + size_t address) OMNITRACE_HIDDEN_API; } diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/components/rocm_smi.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/components/rocm_smi.hpp index a8bf2c5a70..8a6a6e3f3a 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/components/rocm_smi.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/components/rocm_smi.hpp @@ -31,6 +31,7 @@ #include "library/common.hpp" #include "library/components/fwd.hpp" #include "library/defines.hpp" +#include "library/state.hpp" #include "library/thread_data.hpp" #include @@ -145,7 +146,7 @@ inline void set_state(State) {} } // namespace rocm_smi } // namespace omnitrace -#if defined(OMNITRACE_USE_ROCM_SMI) +#if defined(OMNITRACE_USE_ROCM_SMI) && OMNITRACE_USE_ROCM_SMI > 0 # if !defined(OMNITRACE_EXTERN_COMPONENTS) || \ (defined(OMNITRACE_EXTERN_COMPONENTS) && OMNITRACE_EXTERN_COMPONENTS > 0) diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/config.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/config.hpp index 913ad9ebb7..08924e3049 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/config.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/config.hpp @@ -73,6 +73,18 @@ set_setting_value(const std::string& _name, Tp&& _v) return _setting->second->set(std::forward(_v)); } +template +std::pair +get_setting_value(const std::string& _name) +{ + auto _instance = tim::settings::shared_instance(); + if(!_instance) return std::make_pair(false, Tp{}); + auto _setting = _instance->find(_name); + if(_setting == _instance->end() || !_setting->second) + return std::make_pair(false, Tp{}); + return _setting->second->get(); +} + // // User-configurable settings // @@ -145,9 +157,15 @@ get_use_critical_trace(); bool get_use_kokkosp(); +bool +get_use_kokkosp_kernel_logger(); + bool get_use_ompt(); +bool +get_use_code_coverage(); + bool get_timeline_sampling(); diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/coverage.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/coverage.hpp new file mode 100644 index 0000000000..217db52742 --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/coverage.hpp @@ -0,0 +1,167 @@ +// 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 "timemory/mpl/concepts.hpp" +#include "timemory/tpls/cereal/cereal/cereal.hpp" +#include + +#include +#include +#include + +#if !defined(OMNITRACE_SERIALIZE) +# define OMNITRACE_SERIALIZE(MEMBER_VARIABLE) \ + ar(::tim::cereal::make_nvp(#MEMBER_VARIABLE, MEMBER_VARIABLE)) +#endif + +namespace omnitrace +{ +namespace coverage +{ +void +post_process(); + +//--------------------------------------------------------------------------------------// +// +/// \struct code_coverage +/// \brief Summary information about the code coverage +// +//--------------------------------------------------------------------------------------// + +struct code_coverage +{ + using int_set_t = std::set; + using str_set_t = std::set; + + enum Category + { + STANDARD = 0, + ADDRESS, + MODULE, + FUNCTION + }; + + struct data + { + int_set_t addresses = {}; + str_set_t modules = {}; + str_set_t functions = {}; + + template + void serialize(ArchiveT& ar, const unsigned version); + }; + + double operator()(Category _c = STANDARD) const; + double get(Category _c = STANDARD) const { return (*this)(_c); } + + int_set_t get_uncovered_addresses() const; + str_set_t get_uncovered_modules() const; + str_set_t get_uncovered_functions() const; + + template + void serialize(ArchiveT& ar, const unsigned version); + + size_t count = 0; + size_t size = 0; + data covered = {}; + data possible = {}; +}; +// +template +void +code_coverage::serialize(ArchiveT& ar, const unsigned version) +{ + OMNITRACE_SERIALIZE(count); + OMNITRACE_SERIALIZE(size); + OMNITRACE_SERIALIZE(covered); + OMNITRACE_SERIALIZE(possible); + if constexpr(tim::concepts::is_output_archive::value) + { + ar.setNextName("coverage"); + ar.startNode(); + ar(tim::cereal::make_nvp("total", get(STANDARD))); + ar(tim::cereal::make_nvp("addresses", get(ADDRESS))); + ar(tim::cereal::make_nvp("modules", get(MODULE))); + ar(tim::cereal::make_nvp("functions", get(FUNCTION))); + ar.finishNode(); + } + (void) version; +} +// +template +void +code_coverage::data::serialize(ArchiveT& ar, const unsigned version) +{ + OMNITRACE_SERIALIZE(addresses); + OMNITRACE_SERIALIZE(modules); + OMNITRACE_SERIALIZE(functions); + (void) version; +} + +//--------------------------------------------------------------------------------------// +// +/// \struct coverage_data +/// \brief Detailed information about the code coverage +// +//--------------------------------------------------------------------------------------// + +struct coverage_data +{ + using data_tuple_t = std::tuple; + + template + void serialize(ArchiveT& ar, const unsigned version); + + coverage_data& operator+=(const coverage_data& rhs); + bool operator==(const coverage_data& rhs) const; + bool operator==(const data_tuple_t& rhs) const; + bool operator!=(const coverage_data& rhs) const; + bool operator<(const coverage_data& rhs) const; + bool operator<=(const coverage_data& rhs) const; + bool operator>(const coverage_data& rhs) const; + bool operator>=(const coverage_data& rhs) const; + + size_t count = 0; + size_t address = 0; + size_t line = 0; + std::string module = {}; + std::string function = {}; + std::string source = {}; +}; +// +template +void +coverage_data::serialize(ArchiveT& ar, const unsigned version) +{ + OMNITRACE_SERIALIZE(count); + OMNITRACE_SERIALIZE(line); + OMNITRACE_SERIALIZE(address); + OMNITRACE_SERIALIZE(module); + OMNITRACE_SERIALIZE(function); + OMNITRACE_SERIALIZE(source); + (void) version; +} +// +} // namespace coverage +} // namespace omnitrace diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/critical_trace.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/critical_trace.hpp index 1614aa93e2..9415290bb2 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/critical_trace.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/critical_trace.hpp @@ -217,7 +217,7 @@ using hash_ids = std::unordered_set; uint64_t get_update_frequency(); -std::unique_ptr& +unique_ptr_t& get(int64_t _tid = threading::get_id()); size_t diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/runtime.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/runtime.hpp index 7ce7897610..53e3d4a07d 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/runtime.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/runtime.hpp @@ -30,8 +30,10 @@ #include "library/components/roctracer.hpp" #include "library/defines.hpp" #include "library/state.hpp" +#include "library/thread_data.hpp" #include "library/timemory.hpp" +#include #include #include @@ -48,13 +50,6 @@ using main_bundle_t = using gotcha_bundle_t = tim::lightweight_tuple; -// bundle of components used in instrumentation -using instrumentation_bundle_t = - tim::component_bundle; - -// allocator for instrumentation_bundle_t -using bundle_allocator_t = tim::data::ring_buffer_allocator; - // bundle of components around each thread #if defined(TIMEMORY_RUSAGE_THREAD) && TIMEMORY_RUSAGE_THREAD > 0 using omnitrace_thread_bundle_t = @@ -75,14 +70,14 @@ get_gotcha_bundle(); std::atomic& get_cpu_cid(); -std::unique_ptr>& +unique_ptr_t>& get_cpu_cid_stack(int64_t _tid = threading::get_id(), int64_t _parent = 0); using cpu_cid_data_t = std::tuple; using cpu_cid_pair_t = std::tuple; using cpu_cid_parent_map_t = std::unordered_map; -std::unique_ptr& +unique_ptr_t& get_cpu_cid_parents(int64_t _tid = threading::get_id()); cpu_cid_data_t diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/sampling.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/sampling.hpp index 441492f355..ca8b74ad0f 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/sampling.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/sampling.hpp @@ -53,7 +53,7 @@ using component::sampling_gpu_temp; using component::sampling_percent; using component::sampling_wall_clock; -std::unique_ptr>& +unique_ptr_t>& get_signal_types(int64_t _tid); std::set @@ -70,7 +70,7 @@ using bundle_t = tim::lightweight_tuple; using sampler_t = tim::sampling::sampler; using sampler_instances = thread_data; -std::unique_ptr& +unique_ptr_t& get_sampler(int64_t _tid = threading::get_id()); } // namespace sampling diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/state.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/state.hpp index baacf5c7bd..ed09058db0 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/state.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/state.hpp @@ -39,7 +39,8 @@ enum class State : unsigned short enum class Mode : unsigned short { Trace = 0, - Sampling + Sampling, + Coverage }; } // namespace omnitrace diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/thread_data.hpp b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/thread_data.hpp index d0c7833bff..c4e6483660 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/include/library/thread_data.hpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/include/library/thread_data.hpp @@ -22,8 +22,11 @@ #pragma once +#include "library/api.hpp" +#include "library/common.hpp" +#include "library/config.hpp" #include "library/defines.hpp" -#include "library/runtime.hpp" +#include "library/timemory.hpp" #include #include @@ -37,20 +40,44 @@ namespace omnitrace { +// bundle of components used in instrumentation +using instrumentation_bundle_t = + tim::component_bundle; + +// allocator for instrumentation_bundle_t +using bundle_allocator_t = tim::data::ring_buffer_allocator; + +template +struct thread_deleter; + +// unique ptr type for omnitrace +template +using unique_ptr_t = std::unique_ptr>; + static constexpr size_t max_supported_threads = OMNITRACE_MAX_THREADS; +template +struct thread_deleter +{ + void operator()(Tp* ptr) const + { + if(get_state() != State::Finalized) omnitrace_finalize_hidden(); + delete ptr; + } +}; + template struct thread_data { - using instance_array_t = std::array, MaxThreads>; + using instance_array_t = std::array, MaxThreads>; using construct_on_init = std::true_type; template - static void construct(Args&&...); - static std::unique_ptr& instance(); - static instance_array_t& instances(); + static void construct(Args&&...); + static unique_ptr_t& instance(); + static instance_array_t& instances(); template - static std::unique_ptr& instance(construct_on_init, Args&&...); + static unique_ptr_t& instance(construct_on_init, Args&&...); template static instance_array_t& instances(construct_on_init, Args&&...); @@ -72,14 +99,14 @@ thread_data::construct(Args&&... _args) static auto& _instances = instances(); static thread_local bool _v = [&_args...]() { _instances.at(threading::get_id()) = - std::make_unique(std::forward(_args)...); + unique_ptr_t{ new Tp(std::forward(_args)...) }; return true; }(); (void) _v; } template -std::unique_ptr& +unique_ptr_t& thread_data::instance() { return instances().at(threading::get_id()); @@ -95,7 +122,7 @@ thread_data::instances() template template -std::unique_ptr& +unique_ptr_t& thread_data::instance(construct_on_init, Args&&... _args) { construct(std::forward(_args)...); @@ -110,7 +137,7 @@ thread_data::instances(construct_on_init, Args&&... _args) static auto _v = [&]() { auto _internal = instance_array_t{}; for(size_t i = 0; i < MaxThreads; ++i) - _internal.at(i) = std::make_unique(std::forward(_args)...); + _internal.at(i) = unique_ptr_t{ new Tp(std::forward(_args)...) }; return _internal; }(); return _v; diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library.cpp index 7e35cbe7c8..c73dc0633d 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library.cpp @@ -28,6 +28,7 @@ #include "library/components/mpi_gotcha.hpp" #include "library/components/pthread_gotcha.hpp" #include "library/config.hpp" +#include "library/coverage.hpp" #include "library/critical_trace.hpp" #include "library/debug.hpp" #include "library/defines.hpp" @@ -114,9 +115,7 @@ ensure_finalization(bool _static_init = false) // // see: // https://github.com/ROCm-Developer-Tools/roctracer/issues/22#issuecomment-572814465 -#if defined(OMNITRACE_USE_ROCTRACER) tim::set_env("HSA_ENABLE_INTERRUPT", "0", 0); -#endif } return scope::destructor{ []() { omnitrace_finalize_hidden(); } }; } @@ -346,6 +345,13 @@ omnitrace_pop_region_hidden(const char* name) extern "C" void omnitrace_set_env_hidden(const char* env_name, const char* env_val) { + struct set_env_s // NOLINT + {}; + tim::auto_lock_t _lk{ tim::type_mutex() }; + + static auto _set_envs = std::set{}; + bool _success = _set_envs.emplace(env_name).second; + // just search env to avoid initializing the settings OMNITRACE_CONDITIONAL_PRINT_F(get_debug_init() || get_verbose_env() > 2, "Setting env: %s=%s\n", env_name, env_val); @@ -353,7 +359,7 @@ omnitrace_set_env_hidden(const char* env_name, const char* env_val) tim::set_env(env_name, env_val, 0); OMNITRACE_CONDITIONAL_THROW( - get_state() >= State::Init && + _success && get_state() >= State::Init && (config::get_is_continuous_integration() || get_debug_init()), "omnitrace_set_env(\"%s\", \"%s\") called after omnitrace was initialized. state " "= %s", @@ -375,6 +381,14 @@ std::function _start_gotcha_callback = []() {}; extern "C" void omnitrace_set_mpi_hidden(bool use, bool attached) { + static bool _once = false; + static auto _args = std::make_pair(use, attached); + + // this function may be called multiple times if multiple libraries are instrumented + // we want to guard against multiple calls which with different arguments + if(_once && std::tie(_args.first, _args.second) == std::tie(use, attached)) return; + _once = true; + // just search env to avoid initializing the settings OMNITRACE_CONDITIONAL_PRINT_F(get_debug_init() || get_verbose_env() > 2, "use: %s, attached: %s\n", (use) ? "y" : "n", @@ -478,6 +492,20 @@ omnitrace_init_library_hidden() get_use_sampling() = tim::get_env("OMNITRACE_USE_SAMPLING", true); get_use_critical_trace() = false; } + else if(_mode == Mode::Coverage) + { + for(auto&& itr : + { "USE_SAMPLING", "CRITICAL_TRACE", "USE_ROCTRACER", "USE_ROCM_SMI", + "USE_PERFETTO", "USE_TIMEMORY", "USE_KOKKOSP", "USE_OMPT" }) + { + auto _name = JOIN('_', "OMNITRACE", itr); + if(!config::set_setting_value(_name, false)) + { + OMNITRACE_VERBOSE_F(4, "No configuration setting named '%s'", + _name.c_str()); + } + } + } tim::trait::runtime_enabled::set(get_use_roctracer()); tim::trait::runtime_enabled::set(get_use_roctracer() && @@ -494,7 +522,7 @@ omnitrace_init_library_hidden() tim::set_env("KOKKOS_PROFILE_LIBRARY", "libomnitrace.so", _force); } -#if defined(OMNITRACE_USE_ROCTRACER) +#if defined(OMNITRACE_USE_ROCTRACER) && OMNITRACE_USE_ROCTRACER > 0 tim::set_env("HSA_TOOLS_LIB", "libomnitrace.so", 0); #endif } @@ -814,11 +842,40 @@ omnitrace_init_tooling_hidden() extern "C" void omnitrace_init_hidden(const char* _mode, bool _is_binary_rewrite, const char* _argv0) { + static int _total_count = 0; + static auto _args = std::make_pair(std::string_view{ _mode }, _is_binary_rewrite); + + auto _count = _total_count++; + auto _mode_sv = std::string_view{ _mode }; + // this function may be called multiple times if multiple libraries are instrumented + // we want to guard against multiple calls which with different arguments + if(_count > 0 && + std::tie(_args.first, _args.second) == std::tie(_mode_sv, _is_binary_rewrite)) + return; + + OMNITRACE_CONDITIONAL_THROW( + _count > 0 && + std::tie(_args.first, _args.second) != std::tie(_mode_sv, _is_binary_rewrite), + "\nomnitrace_init(...) called multiple times with different arguments for mode " + "and/or is_binary_rewrite:" + "\n Invocation #1: omnitrace_init(mode=%-8s, is_binary_rewrite=%-5s, ...)" + "\n Invocation #%i: omnitrace_init(mode=%-8s, is_binary_rewrite=%-5s, ...)", + _args.first.data(), std::to_string(_args.second).c_str(), _count + 1, _mode, + std::to_string(_is_binary_rewrite).c_str()); + // always the first (void) get_state(); (void) push_count(); (void) pop_count(); + OMNITRACE_CONDITIONAL_THROW( + get_state() >= State::Init && + (config::get_is_continuous_integration() || get_debug_init()), + "omnitrace_init(mode=%s, is_binary_rewrite=%s, argv0=%s) called after omnitrace " + "was initialized. state = %s", + _mode, std::to_string(_is_binary_rewrite).c_str(), _argv0, + std::to_string(get_state()).c_str()); + get_finalization_functions().emplace_back([_argv0]() { OMNITRACE_CI_THROW(get_state() != State::Active, "Finalizer function for popping main invoked in non-active " @@ -845,6 +902,16 @@ omnitrace_init_hidden(const char* _mode, bool _is_binary_rewrite, const char* _a tim::set_env("OMNITRACE_MODE", _mode, 0); config::is_binary_rewrite() = _is_binary_rewrite; + if(get_mode() == Mode::Coverage) + { + tim::set_env("OMNITRACE_USE_PERFETTO", "OFF", 0); + tim::set_env("OMNITRACE_USE_TIMEMORY", "OFF", 0); + tim::set_env("OMNITRACE_USE_KOKKOSP", "OFF", 0); + tim::set_env("OMNITRACE_USE_SAMPLING", "OFF", 0); + tim::set_env("OMNITRACE_USE_ROCTRACER", "OFF", 0); + tim::set_env("OMNITRACE_USE_ROCM_SMI", "OFF", 0); + } + // set OMNITRACE_USE_SAMPLING to ON by default if mode is sampling tim::set_env("OMNITRACE_USE_SAMPLING", (get_mode() == Mode::Sampling) ? "ON" : "OFF", 0); @@ -1086,8 +1153,8 @@ omnitrace_finalize_hidden(void) { if(get_verbose() >= 0) fprintf(stderr, "\n"); if(get_verbose() >= 0 || get_debug()) - fprintf(stderr, "[%s]|%i> Flushing perfetto...\n", OMNITRACE_FUNCTION, - dmp::rank()); + fprintf(stderr, "[%s][%s]|%i> Flushing perfetto...\n", TIMEMORY_PROJECT_NAME, + OMNITRACE_FUNCTION, dmp::rank()); // Make sure the last event is closed for this example. perfetto::TrackEvent::Flush(); @@ -1108,8 +1175,9 @@ omnitrace_finalize_hidden(void) } // Write the trace into a file. if(get_verbose() >= 0) - fprintf(stderr, "[%s]|%i> Outputting '%s' (%.2f KB / %.2f MB / %.2f GB)... ", - OMNITRACE_FUNCTION, dmp::rank(), + fprintf(stderr, + "[%s][%s]|%i> Outputting '%s' (%.2f KB / %.2f MB / %.2f GB)... ", + TIMEMORY_PROJECT_NAME, OMNITRACE_FUNCTION, dmp::rank(), get_perfetto_output_filename().c_str(), static_cast(trace_data.size()) / units::KB, static_cast(trace_data.size()) / units::MB, @@ -1152,6 +1220,8 @@ omnitrace_finalize_hidden(void) tasking::get_critical_trace_thread_pool().destroy_threadpool(); } + coverage::post_process(); + OMNITRACE_DEBUG_F("Finalizing timemory...\n"); tim::timemory_finalize(); diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/api.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/api.cpp index 09ee76c6bb..b1205daae8 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/api.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/api.cpp @@ -95,3 +95,16 @@ omnitrace_set_mpi(bool use, bool attached) { omnitrace_set_mpi_hidden(use, attached); } + +extern "C" void +omnitrace_register_source(const char* file, const char* func, size_t line, size_t address, + const char* source) +{ + omnitrace_register_source_hidden(file, func, line, address, source); +} + +extern "C" void +omnitrace_register_coverage(const char* file, const char* func, size_t address) +{ + omnitrace_register_coverage_hidden(file, func, address); +} diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/backtrace.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/backtrace.cpp index e552248d11..3db250bdf5 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/backtrace.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/backtrace.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "library/components/fwd.hpp" +#include "library/components/pthread_gotcha.hpp" #include "library/components/rocm_smi.hpp" #include "library/config.hpp" #include "library/debug.hpp" @@ -109,7 +110,7 @@ using papi_vector_instances = thread_data; namespace { -std::unique_ptr& +unique_ptr_t& get_papi_vector(int64_t _tid) { static auto& _v = papi_vector_instances::instances(); @@ -117,14 +118,14 @@ get_papi_vector(int64_t _tid) return _v.at(_tid); } -std::unique_ptr& +unique_ptr_t& get_backtrace_init(int64_t _tid) { static auto& _v = backtrace_init_instances::instances(); return _v.at(_tid); } -std::unique_ptr& +unique_ptr_t& get_sampler_running(int64_t _tid) { static auto& _v = sampler_running_instances::instances(); @@ -345,7 +346,7 @@ backtrace::configure(bool _setup, int64_t _tid) _sampler->set_signals(*_signal_types); _sampler->set_flags(SA_RESTART); _sampler->set_delay(_delay); - _sampler->set_verbose(std::min(_sampler->get_verbose(), 1)); + _sampler->set_verbose(std::min(_sampler->get_verbose(), 2)); _sampler->set_frequency(_prof_freq, { SIGPROF }); _sampler->set_frequency(_alrm_freq, { SIGALRM }); diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/mpi_gotcha.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/mpi_gotcha.cpp index 617eb528c4..b50a143b29 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/mpi_gotcha.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/mpi_gotcha.cpp @@ -73,7 +73,7 @@ mpi_gotcha::configure() mpi_gotcha_t::template configure<1, int, int*, char***, int, int*>( "MPI_Init_thread"); mpi_gotcha_t::template configure<2, int>("MPI_Finalize"); -#if defined(OMNITRACE_USE_MPI_HEADERS) +#if defined(OMNITRACE_USE_MPI_HEADERS) && OMNITRACE_USE_MPI_HEADERS > 0 mpi_gotcha_t::template configure<3, int, comm_t, int*>("MPI_Comm_rank"); mpi_gotcha_t::template configure<4, int, comm_t, int*>("MPI_Comm_size"); #endif diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/pthread_gotcha.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/pthread_gotcha.cpp index 85e957d3e2..4f40a59078 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/pthread_gotcha.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/pthread_gotcha.cpp @@ -25,6 +25,7 @@ #include "library/components/roctracer.hpp" #include "library/config.hpp" #include "library/debug.hpp" +#include "library/runtime.hpp" #include "library/sampling.hpp" #include "library/thread_data.hpp" diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/rocm_smi.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/rocm_smi.cpp index 592f3baf43..13370fa88a 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/rocm_smi.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/rocm_smi.cpp @@ -33,6 +33,7 @@ #include "library/components/rocm_smi.hpp" #include "library/common.hpp" #include "library/components/fwd.hpp" +#include "library/components/pthread_gotcha.hpp" #include "library/config.hpp" #include "library/critical_trace.hpp" #include "library/debug.hpp" @@ -127,7 +128,7 @@ data::print(std::ostream& _os) const namespace { -std::vector*> _bundle_data{}; +std::vector*> _bundle_data{}; } void @@ -139,7 +140,8 @@ config() 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(); + if(!*_bundle_data.at(i)) + *_bundle_data.at(i) = unique_ptr_t{ new bundle_t{} }; } } diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/roctracer_callbacks.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/roctracer_callbacks.cpp index 4d8c9a1fb6..63468585b8 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/roctracer_callbacks.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/components/roctracer_callbacks.cpp @@ -96,9 +96,9 @@ get_clock_skew() _cpu_ave += _cpu_ts / _n; _gpu_ave += _gpu_ts / _n; } - OMNITRACE_BASIC_VERBOSE(1, "CPU timestamp: %li\n", _cpu_ave); - OMNITRACE_BASIC_VERBOSE(1, "HIP timestamp: %li\n", _gpu_ave); - OMNITRACE_BASIC_VERBOSE(0, "CPU/HIP timestamp skew: %li (used: %s)\n", _diff, + OMNITRACE_BASIC_VERBOSE(2, "CPU timestamp: %li\n", _cpu_ave); + OMNITRACE_BASIC_VERBOSE(2, "HIP timestamp: %li\n", _gpu_ave); + OMNITRACE_BASIC_VERBOSE(1, "CPU/HIP timestamp skew: %li (used: %s)\n", _diff, _use ? "yes" : "no"); _diff /= _n; return _diff; @@ -339,8 +339,11 @@ hip_exec_activity_callbacks(int64_t _tid) // ROCTRACER_CALL(roctracer_flush_activity()); tim::auto_lock_t _lk{ get_hip_activity_mutex(_tid) }; auto& _async_ops = get_hip_activity_callbacks(_tid); + if(!_async_ops) return; for(auto& itr : *_async_ops) - itr(); + { + if(itr) itr(); + } _async_ops->clear(); } @@ -734,7 +737,7 @@ extern "C" if(!config::settings_are_configured() && get_state() < State::Active) omnitrace_init_tooling_hidden(); - auto _setup = [=]() { + static auto _setup = [=]() { try { OMNITRACE_CONDITIONAL_BASIC_PRINT_F(get_debug() || get_verbose() > 1, @@ -805,7 +808,7 @@ extern "C" } }; - auto _shutdown = []() { + static auto _shutdown = []() { OMNITRACE_DEBUG_F("roctracer_disable_domain_callback\n"); ROCTRACER_CALL(roctracer_disable_domain_callback(ACTIVITY_DOMAIN_HSA_API)); @@ -816,8 +819,8 @@ extern "C" (void) get_clock_skew(); - comp::roctracer::add_setup("hsa", std::move(_setup)); - comp::roctracer::add_shutdown("hsa", std::move(_shutdown)); + comp::roctracer::add_setup("hsa", _setup); + comp::roctracer::add_shutdown("hsa", _shutdown); rocm_smi::set_state(State::Active); comp::roctracer::setup(); @@ -832,5 +835,6 @@ extern "C" OMNITRACE_DEBUG_F("\n"); rocm_smi::set_state(State::Finalized); comp::roctracer::shutdown(); + omnitrace_finalize_hidden(); } } diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/config.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/config.cpp index 27b8102430..c7a6ad0744 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/config.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/config.cpp @@ -80,6 +80,20 @@ get_setting_name(std::string _v) OMNITRACE_PRINT("Warning! Duplicate setting: %s / %s\n", \ get_setting_name(ENV_NAME).c_str(), ENV_NAME); \ } + +// setting + command line option +#define OMNITRACE_CONFIG_CL_SETTING(TYPE, ENV_NAME, DESCRIPTION, INITIAL_VALUE, \ + CMD_LINE, ...) \ + { \ + auto _ret = _config->insert( \ + ENV_NAME, get_setting_name(ENV_NAME), DESCRIPTION, INITIAL_VALUE, \ + std::set{ "custom", "omnitrace", "omnitrace-library", \ + __VA_ARGS__ }, \ + std::vector{ CMD_LINE }); \ + if(!_ret.second) \ + OMNITRACE_PRINT("Warning! Duplicate setting: %s / %s\n", \ + get_setting_name(ENV_NAME).c_str(), ENV_NAME); \ + } } // namespace inline namespace config @@ -140,12 +154,12 @@ configure_settings(bool _init) !_config->get("OMNITRACE_USE_PERFETTO"), "backend", "timemory", "instrumentation", "sampling"); -#if defined(OMNITRACE_USE_ROCTRACER) +#if defined(OMNITRACE_USE_ROCTRACER) && OMNITRACE_USE_ROCTRACER > 0 OMNITRACE_CONFIG_SETTING(bool, "OMNITRACE_USE_ROCTRACER", "Enable ROCM tracing", true, "backend", "roctracer", "rocm"); #endif -#if defined(OMNITRACE_USE_ROCM_SMI) +#if defined(OMNITRACE_USE_ROCM_SMI) && OMNITRACE_USE_ROCM_SMI > 0 OMNITRACE_CONFIG_SETTING( bool, "OMNITRACE_USE_ROCM_SMI", "Enable sampling GPU power, temp, utilization, and memory usage", true, "backend", @@ -169,12 +183,20 @@ configure_settings(bool _init) "Enable support for Kokkos Tools", false, "kokkos", "backend"); -#if defined(TIMEMORY_USE_OMPT) + OMNITRACE_CONFIG_CL_SETTING( + bool, "OMNITRACE_KOKKOS_KERNEL_LOGGER", "Enables kernel logging", false, + "--omnitrace-kokkos-kernel-logger", "kokkos", "debugging"); + +#if defined(OMNITRACE_USE_OMPT) && OMNITRACE_USE_OMPT > 0 OMNITRACE_CONFIG_SETTING(bool, "OMNITRACE_USE_OMPT", - "Enable support for OpenMP-Tools", true, "openmp", "ompt", + "Enable support for OpenMP-Tools", false, "openmp", "ompt", "backend"); #endif + OMNITRACE_CONFIG_SETTING(bool, "OMNITRACE_USE_CODE_COVERAGE", + "Enable support for code coverage", false, "coverage", + "backend"); + OMNITRACE_CONFIG_SETTING(size_t, "OMNITRACE_INSTRUMENTATION_INTERVAL", "Instrumentation only takes measurements once every N " "function calls (not statistical)", @@ -651,9 +673,12 @@ Mode get_mode() { static auto _v = []() { - auto _mode = tim::get_env_choice("OMNITRACE_MODE", "trace", - { "trace", "sampling" }); - if(_mode == "sampling") return Mode::Sampling; + auto _mode = tim::get_env_choice( + "OMNITRACE_MODE", "trace", { "trace", "sampling", "coverage" }); + if(_mode == "sampling") + return Mode::Sampling; + else if(_mode == "coverage") + return Mode::Coverage; return Mode::Trace; }(); return _v; @@ -741,7 +766,7 @@ get_use_timemory() bool& get_use_roctracer() { -#if defined(OMNITRACE_USE_ROCTRACER) +#if defined(OMNITRACE_USE_ROCTRACER) && OMNITRACE_USE_ROCTRACER > 0 static auto _v = get_config()->find("OMNITRACE_USE_ROCTRACER"); return static_cast&>(*_v->second).get(); #else @@ -753,7 +778,7 @@ get_use_roctracer() bool& get_use_rocm_smi() { -#if defined(OMNITRACE_USE_ROCM_SMI) +#if defined(OMNITRACE_USE_ROCM_SMI) && OMNITRACE_USE_ROCM_SMI > 0 static auto _v = get_config()->find("OMNITRACE_USE_ROCM_SMI"); return static_cast&>(*_v->second).get(); #else @@ -804,6 +829,13 @@ get_use_kokkosp() return static_cast&>(*_v->second).get(); } +bool +get_use_kokkosp_kernel_logger() +{ + static auto _v = get_config()->find("OMNITRACE_KOKKOS_KERNEL_LOGGER"); + return static_cast&>(*_v->second).get(); +} + bool get_use_ompt() { @@ -815,6 +847,13 @@ get_use_ompt() #endif } +bool +get_use_code_coverage() +{ + static auto _v = get_config()->find("OMNITRACE_USE_CODE_COVERAGE"); + return static_cast&>(*_v->second).get(); +} + bool get_critical_trace_debug() { @@ -1001,7 +1040,7 @@ get_thread_sampling_freq() std::string get_rocm_smi_devices() { -#if defined(OMNITRACE_USE_ROCM_SMI) +#if defined(OMNITRACE_USE_ROCM_SMI) && OMNITRACE_USE_ROCM_SMI > 0 static auto _v = get_config()->find("OMNITRACE_ROCM_SMI_DEVICES"); return static_cast&>(*_v->second).get(); #else diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/coverage.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/coverage.cpp new file mode 100644 index 0000000000..c0317c7099 --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/coverage.cpp @@ -0,0 +1,424 @@ +// 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/coverage.hpp" +#include "library/api.hpp" +#include "library/config.hpp" +#include "library/debug.hpp" +#include "library/thread_data.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define OMNITRACE_SERIALIZE(MEMBER_VARIABLE) \ + ar(::tim::cereal::make_nvp(#MEMBER_VARIABLE, MEMBER_VARIABLE)) + +namespace omnitrace +{ +namespace coverage +{ +namespace +{ +template +using uomap_t = std::unordered_map; +// +using coverage_thread_data_type = + uomap_t>>; +// +template +inline std::set +get_uncovered(const std::set& _covered, + const std::set& _possible) +{ + std::set _v{}; + for(auto&& itr : _possible) + { + if(_covered.count(itr) == 0) _v.emplace(itr); + } + return _v; +} +// +template +inline std::vector +get_uncovered(const std::vector& _covered, + const std::vector& _possible) +{ + std::vector _v{}; + for(auto&& itr : _possible) + { + if(!std::any_of(_covered.begin(), _covered.end(), + [itr](auto&& _entry) { return _entry == itr; })) + _v.emplace_back(itr); + } + return _v; +} +// +using coverage_thread_data = + omnitrace::thread_data; +// +auto& +get_code_coverage() +{ + static auto _v = code_coverage{}; + return _v; +} +// +auto& +get_coverage_data() +{ + static auto _v = std::vector{}; + return _v; +} +// +auto& +get_coverage_count(int64_t _tid = tim::threading::get_id()) +{ + static auto& _v = + coverage_thread_data::instances(coverage_thread_data::construct_on_init{}); + return _v.at(_tid); +} +} // namespace + +//--------------------------------------------------------------------------------------// + +double +code_coverage::operator()(Category _c) const +{ + switch(_c) + { + case STANDARD: return static_cast(count) / static_cast(size); + case ADDRESS: + return static_cast(covered.addresses.size()) / + static_cast(possible.addresses.size()); + case MODULE: + return static_cast(covered.modules.size()) / + static_cast(possible.modules.size()); + case FUNCTION: + return static_cast(covered.functions.size()) / + static_cast(possible.functions.size()); + } + return 0.0; +} + +code_coverage::int_set_t +code_coverage::get_uncovered_addresses() const +{ + return get_uncovered(covered.addresses, possible.addresses); +} + +code_coverage::str_set_t +code_coverage::get_uncovered_modules() const +{ + return get_uncovered(covered.modules, possible.modules); +} + +code_coverage::str_set_t +code_coverage::get_uncovered_functions() const +{ + return get_uncovered(covered.functions, possible.functions); +} + +//--------------------------------------------------------------------------------------// + +coverage_data& +coverage_data::operator+=(const coverage_data& rhs) +{ + count += rhs.count; + return *this; +} + +bool +coverage_data::operator==(const coverage_data& rhs) const +{ + return std::tie(module, function, address) == + std::tie(rhs.module, rhs.function, rhs.address); +} + +bool +coverage_data::operator==(const data_tuple_t& rhs) const +{ + return std::tie(module, function, address) == + std::tie(std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs)); +} + +bool +coverage_data::operator!=(const coverage_data& rhs) const +{ + return !(*this == rhs); +} + +bool +coverage_data::operator<(const coverage_data& rhs) const +{ + if(count != rhs.count) return count < rhs.count; + if(module != rhs.module) return module < rhs.module; + if(function != rhs.function) return function < rhs.function; + if(address != rhs.address) return address < rhs.address; + if(line != rhs.line) return line < rhs.line; + return source < rhs.source; +} + +bool +coverage_data::operator<=(const coverage_data& rhs) const +{ + return (*this == rhs || *this < rhs); +} + +bool +coverage_data::operator>(const coverage_data& rhs) const +{ + return (*this != rhs && !(*this < rhs)); +} + +bool +coverage_data::operator>=(const coverage_data& rhs) const +{ + return !(*this < rhs); +} + +//--------------------------------------------------------------------------------------// + +void +post_process() +{ + if(!config::get_use_code_coverage()) return; + + code_coverage& _coverage = get_code_coverage(); + if(_coverage.size == 0) + { + OMNITRACE_VERBOSE_F( + 0, + "Warning! Code coverage enabled but no code coverage data is available!\n"); + return; + } + + using data_tuple_t = coverage_data::data_tuple_t; + auto& _coverage_data = get_coverage_data(); + + auto _find = [&_coverage_data](data_tuple_t&& _v) { + for(auto itr = _coverage_data.begin(); itr != _coverage_data.end(); ++itr) + { + if(*itr == _v) return std::make_pair(itr, true); + } + return std::make_pair(_coverage_data.end(), false); + }; + + auto _data = coverage_thread_data_type{}; + for(size_t i = 0; i < coverage_thread_data::size(); ++i) + { + const auto& _thr_data = *get_coverage_count(i); + for(const auto& file : _thr_data) + { + for(const auto& func : file.second) + { + for(const auto& addr : func.second) + { + _data[file.first][func.first][addr.first] += addr.second; + auto&& _v = _find({ file.first, func.first, addr.first }); + if(_v.second) + { + _v.first->count += addr.second; + } + else + { + OMNITRACE_VERBOSE_F( + 0, "Warning! No matching coverage data for %s :: %s (0x%x)\n", + func.first.data(), file.first.data(), + (unsigned int) addr.first); + } + } + } + } + } + + for(const auto& file : _data) + { + for(const auto& func : file.second) + { + for(const auto& addr : func.second) + { + if(addr.second > 0) + { + _coverage.count += 1; + _coverage.covered.modules.emplace(file.first); + _coverage.covered.functions.emplace(func.first); + _coverage.covered.addresses.emplace(addr.first); + } + } + } + } + + std::sort(_coverage_data.begin(), _coverage_data.end(), + std::greater{}); + + { + auto _tmp = std::decay_t{}; + auto _find_in_tmp = [&_tmp](const auto& _v) { + for(auto itr = _tmp.begin(); itr != _tmp.end(); ++itr) + { + if(itr->source == _v.source && itr->address != _v.address && + itr->count == _v.count) + return std::make_pair(itr, true); + } + return std::make_pair(_tmp.end(), false); + }; + for(auto&& itr : _coverage_data) + { + if(!_find_in_tmp(itr).second) _tmp.emplace_back(itr); + } + std::swap(_coverage_data, _tmp); + } + + OMNITRACE_VERBOSE(0, "code coverage :: %6.2f%s\n", _coverage() * 100.0, "%"); + OMNITRACE_VERBOSE(0, "module coverage :: %6.2f%s\n", + _coverage(code_coverage::MODULE) * 100.0, "%"); + OMNITRACE_VERBOSE(0, "function coverage :: %6.2f%s\n", + _coverage(code_coverage::FUNCTION) * 100.0, "%"); + + if(get_verbose() >= 0) fprintf(stderr, "\n"); + + std::sort(_coverage_data.begin(), _coverage_data.end(), + std::greater{}); + + auto _get_setting = [](const std::string& _v) { + auto&& _b = config::get_setting_value(_v); + OMNITRACE_CI_THROW(!_b.first, "Error! No configuration setting named '%s'", + _v.c_str()); + return (_b.first) ? _b.second : true; + }; + + auto _text_output = _get_setting("OMNITRACE_TEXT_OUTPUT"); + auto _json_output = _get_setting("OMNITRACE_JSON_OUTPUT"); + + if(_text_output) + { + auto _fname = tim::settings::compose_output_filename("coverage", ".txt"); + std::ofstream ofs{}; + if(tim::filepath::open(ofs, _fname)) + { + if(get_verbose() >= 0) + fprintf(stderr, "[%s][coverage]|%i> Outputting '%s'...\n", + TIMEMORY_PROJECT_NAME, dmp::rank(), _fname.c_str()); + for(auto& itr : _coverage_data) + { + // if(get_debug() && get_verbose() >= 2) + if(true) + { + auto _addr = TIMEMORY_JOIN("", "0x", std::hex, itr.address); + ofs << std::setw(8) << itr.count << " " << std::setw(8) << _addr + << " " << itr.source << "\n"; + } + else + { + ofs << std::setw(8) << itr.count << " " << itr.source << "\n"; + } + } + } + else + { + OMNITRACE_THROW("Error opening coverage output file: %s", _fname.c_str()); + } + } + + if(_json_output) + { + auto _fname = tim::settings::compose_output_filename("coverage", ".json"); + std::ofstream ofs{}; + if(tim::filepath::open(ofs, _fname)) + { + if(get_verbose() >= 0) + fprintf(stderr, "[%s][coverage]|%i> Outputting '%s'...\n", + TIMEMORY_PROJECT_NAME, dmp::rank(), _fname.c_str()); + { + namespace cereal = tim::cereal; + auto ar = + tim::policy::output_archive::get( + ofs); + + ar->setNextName("omnitrace"); + ar->startNode(); + ar->setNextName("coverage"); + ar->startNode(); + (*ar)(cereal::make_nvp("summary", _coverage)); + (*ar)(cereal::make_nvp("details", _coverage_data)); + ar->finishNode(); + ar->finishNode(); + } + } + else + { + OMNITRACE_THROW("Error opening coverage output file: %s", _fname.c_str()); + } + } + + if(get_verbose() >= 0) fprintf(stderr, "\n"); +} +} // namespace coverage +} // namespace omnitrace + +//--------------------------------------------------------------------------------------// + +namespace coverage = omnitrace::coverage; + +extern "C" void +omnitrace_register_source_hidden(const char* file, const char* func, size_t line, + size_t address, const char* source) +{ + using coverage_data = coverage::coverage_data; + + OMNITRACE_BASIC_VERBOSE_F(2, "[0x%x] :: %-20s :: %20s:%zu :: %s\n", + (unsigned int) address, func, file, line, source); + + coverage::get_coverage_data().emplace_back( + coverage_data{ size_t{ 0 }, address, line, file, func, + (source && strlen(source) > 0) ? source : func }); + + coverage::get_code_coverage().size += 1; + coverage::get_code_coverage().possible.modules.emplace(file); + coverage::get_code_coverage().possible.functions.emplace(func); + coverage::get_code_coverage().possible.addresses.emplace(address); + + // initialize + for(size_t i = 0; i < coverage::coverage_thread_data::size(); ++i) + (*coverage::get_coverage_count(i))[file][func][address] = 0; +} + +//--------------------------------------------------------------------------------------// + +extern "C" void +omnitrace_register_coverage_hidden(const char* file, const char* func, size_t address) +{ + OMNITRACE_BASIC_VERBOSE_F(3, "[0x%x] %-20s :: %20s\n", (unsigned int) address, func, + file); + coverage::get_coverage_count()->at(file).at(func).at(address) += 1; +} + +//--------------------------------------------------------------------------------------// diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/critical_trace.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/critical_trace.cpp index 4e04855702..91281a8ac6 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/critical_trace.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/critical_trace.cpp @@ -26,6 +26,7 @@ #include "library/defines.hpp" #include "library/perfetto.hpp" #include "library/ptl.hpp" +#include "library/thread_data.hpp" #include #include @@ -491,13 +492,13 @@ get_update_frequency() return get_critical_trace_update_freq(); } -std::unique_ptr& +unique_ptr_t& get(int64_t _tid) { static auto& _v = thread_data::instances(); static thread_local auto _once = [_tid]() { - if(!_v.at(0)) _v.at(0) = std::make_unique(); - if(!_v.at(_tid)) _v.at(_tid) = std::make_unique(); + if(!_v.at(0)) _v.at(0) = unique_ptr_t{ new call_chain{} }; + if(!_v.at(_tid)) _v.at(_tid) = unique_ptr_t{ new call_chain{} }; if(_tid > 0) *_v.at(_tid) = *_v.at(0); return true; }(); diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/gpu.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/gpu.cpp index 598cad5b09..ee040cc941 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/gpu.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/gpu.cpp @@ -22,9 +22,9 @@ #include "library/gpu.hpp" -#if defined(OMNITRACE_USE_ROCM_SMI) +#if defined(OMNITRACE_USE_ROCM_SMI) && OMNITRACE_USE_ROCM_SMI > 0 # include "library/components/rocm_smi.hpp" -#elif defined(OMNITRACE_USE_HIP) +#elif defined(OMNITRACE_USE_HIP) && OMNITRACE_USE_HIP > 0 # if !defined(TIMEMORY_USE_HIP) # define TIMEMORY_USE_HIP 1 # endif @@ -38,11 +38,11 @@ namespace gpu int device_count() { -#if defined(OMNITRACE_USE_ROCM_SMI) +#if defined(OMNITRACE_USE_ROCM_SMI) && OMNITRACE_USE_ROCM_SMI > 0 // store as static since calls after rsmi_shutdown will return zero static auto _v = rocm_smi::device_count(); return _v; -#elif defined(OMNITRACE_USE_HIP) +#elif defined(OMNITRACE_USE_HIP) && OMNITRACE_USE_HIP > 0 return ::tim::hip::device_count(); #else return 0; diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/kokkosp.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/kokkosp.cpp index 7940fb51b6..2bb76ce9e1 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/kokkosp.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/kokkosp.cpp @@ -26,6 +26,7 @@ #include "library/components/omnitrace.hpp" #include "library/config.hpp" +#include "library/debug.hpp" #include @@ -53,26 +54,11 @@ std::string kokkos_banner = //--------------------------------------------------------------------------------------// -bool enable_kernel_logger = false; - -inline void -add_kernel_logger() -{ - static bool _first = true; - if(!_first) return; - _first = false; - using strvec_t = std::vector; - - tim::settings::instance()->insert( - std::string{ "OMNITRACE_KOKKOS_KERNEL_LOGGER" }, std::string{}, - std::string{ "Enables kernel logging" }, enable_kernel_logger, - strvec_t({ "--omnitrace-kokkos-kernel-logger" })); -} - inline void setup_kernel_logger() { - if(tim::settings::debug() || tim::settings::verbose() > 3 || enable_kernel_logger) + if((tim::settings::debug() && tim::settings::verbose() >= 3) || + omnitrace::config::get_use_kokkosp_kernel_logger()) { kokkosp::logger_t::get_initializer() = [](kokkosp::logger_t& _obj) { _obj.initialize(); @@ -98,28 +84,27 @@ extern "C" void kokkosp_init_library(const int loadSeq, const uint64_t interfaceVer, const uint32_t devInfoCount, void* deviceInfo) { - add_kernel_logger(); - tim::consume_parameters(devInfoCount, deviceInfo); - printf("%s\n", kokkos_banner.c_str()); - printf("# KokkosP: omnitrace connector (sequence is %d, version: %llu)\n", - loadSeq, (unsigned long long) interfaceVer); - printf("%s\n", kokkos_banner.c_str()); + OMNITRACE_VERBOSE_F(0, + "Initializing connector (sequence is %d, version: %llu)...", + loadSeq, (unsigned long long) interfaceVer); setup_kernel_logger(); tim::trait::runtime_enabled::set( omnitrace::config::get_use_timemory()); + + if(omnitrace::get_verbose() >= 0) fprintf(stderr, "Done\n"); } void kokkosp_finalize_library() { - printf("%s\n", kokkos_banner.c_str()); - printf("# KokkosP: Finalization of omnitrace connector. Complete.\n"); - printf("%s\n", kokkos_banner.c_str()); + OMNITRACE_VERBOSE_F(0, "Finalizing connector... \n"); kokkosp::cleanup(); + + if(omnitrace::get_verbose() >= 0) fprintf(stderr, "Done\n"); } //----------------------------------------------------------------------------------// diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/runtime.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/runtime.cpp index cada5d8bf4..e2ad1151f2 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/runtime.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/runtime.cpp @@ -57,7 +57,7 @@ get_cpu_cid() return _v; } -std::unique_ptr>& +unique_ptr_t>& get_cpu_cid_stack(int64_t _tid, int64_t _parent) { struct omnitrace_cpu_cid_stack @@ -78,7 +78,7 @@ get_cpu_cid_stack(int64_t _tid, int64_t _parent) (void) _v_copy; } -std::unique_ptr& +unique_ptr_t& get_cpu_cid_parents(int64_t _tid) { struct omnitrace_cpu_cid_stack diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/sampling.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/sampling.cpp index 77c869cb49..0a4b5d6d12 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/sampling.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/sampling.cpp @@ -120,7 +120,7 @@ get_signal_names(Tp&& _v) } } // namespace -std::unique_ptr>& +unique_ptr_t>& get_signal_types(int64_t _tid) { static auto& _v = signal_type_instances::instances(); @@ -178,7 +178,7 @@ unblock_signals(std::set _signals) thread_sigmask(SIG_UNBLOCK, &_v, nullptr); } -std::unique_ptr& +unique_ptr_t& get_sampler(int64_t _tid) { static auto& _v = sampler_instances::instances(); diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/state.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/state.cpp index 572ec09271..067d93bea4 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/state.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/state.cpp @@ -47,6 +47,7 @@ to_string(omnitrace::Mode _v) { case omnitrace::Mode::Trace: return "Trace"; case omnitrace::Mode::Sampling: return "Sampling"; + case omnitrace::Mode::Coverage: return "Coverage"; } return {}; } diff --git a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/thread_sampler.cpp b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/thread_sampler.cpp index 666ecda9c5..4981779b58 100644 --- a/projects/rocprofiler-systems/source/lib/omnitrace/src/library/thread_sampler.cpp +++ b/projects/rocprofiler-systems/source/lib/omnitrace/src/library/thread_sampler.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "library/thread_sampler.hpp" +#include "library/components/pthread_gotcha.hpp" #include "library/components/rocm_smi.hpp" #include "library/config.hpp" #include "library/cpu_freq.hpp" diff --git a/projects/rocprofiler-systems/source/python/CMakeLists.txt b/projects/rocprofiler-systems/source/python/CMakeLists.txt index b9f3e28195..f7f9afdd1c 100644 --- a/projects/rocprofiler-systems/source/python/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/python/CMakeLists.txt @@ -21,10 +21,14 @@ function(OMNITRACE_CONFIGURE_PYTARGET _TARGET _VERSION) ${_TARGET} PROPERTIES PREFIX "" OUTPUT_NAME libpyomnitrace - LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/python/omnitrace - ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/python/omnitrace - RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/python/omnitrace - PDB_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/python/omnitrace + LIBRARY_OUTPUT_DIRECTORY + ${PROJECT_BINARY_DIR}/lib/python/site-packages/omnitrace + ARCHIVE_OUTPUT_DIRECTORY + ${PROJECT_BINARY_DIR}/lib/python/site-packages/omnitrace + RUNTIME_OUTPUT_DIRECTORY + ${PROJECT_BINARY_DIR}/lib/python/site-packages/omnitrace + PDB_OUTPUT_DIRECTORY + ${PROJECT_BINARY_DIR}/lib/python/site-packages/omnitrace INSTALL_RPATH_USE_LINK_PATH ON ${EXTRA_PROPERTIES}) @@ -84,65 +88,33 @@ target_link_libraries( target_compile_definitions(libpyomnitrace-interface INTERFACE OMNITRACE_PYBIND11_SOURCE) -# ---------------------------------------------------------------------------- -# Console scripts -# -function(OMNITRACE_PYTHON_CONSOLE_SCRIPT SCRIPT_NAME SCRIPT_SUBMODULE) - set(options) - set(args VERSION ROOT_DIR) - set(kwargs) - cmake_parse_arguments(ARG "${options}" "${args}" "${kwargs}" ${ARGN}) - - if(ARG_VERSION AND ARG_ROOT_DIR) - set(Python3_ROOT_DIR "${ARG_ROOT_DIR}") - find_package(Python3 ${ARG_VERSION} EXACT QUIET MODULE COMPONENTS Interpreter) - set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}") - configure_file(${PROJECT_SOURCE_DIR}/cmake/Templates/console-script.in - ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME}-${ARG_VERSION} @ONLY) - - if(CMAKE_INSTALL_PYTHONDIR) - install( - PROGRAMS ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME}-${ARG_VERSION} - DESTINATION ${CMAKE_INSTALL_BINDIR} - OPTIONAL) - endif() - - if(OMNITRACE_BUILD_TESTING OR OMNITRACE_BUILD_PYTHON) - add_test( - NAME ${SCRIPT_NAME}-console-script-test-${ARG_VERSION} - COMMAND ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME}-${ARG_VERSION} --help - WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) - set_tests_properties( - ${SCRIPT_NAME}-console-script-test-${ARG_VERSION} - PROPERTIES LABELS "python;python-${ARG_VERSION};console-script") - add_test( - NAME ${SCRIPT_NAME}-generic-console-script-test-${ARG_VERSION} - COMMAND ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME} --help - WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) - set_tests_properties( - ${SCRIPT_NAME}-generic-console-script-test-${ARG_VERSION} - PROPERTIES ENVIRONMENT "PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}" LABELS - "python;python-${ARG_VERSION};console-script") - endif() - else() - set(PYTHON_EXECUTABLE "python3") - - configure_file(${PROJECT_SOURCE_DIR}/cmake/Templates/console-script.in - ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME} @ONLY) - - if(CMAKE_INSTALL_PYTHONDIR) - install( - PROGRAMS ${PROJECT_BINARY_DIR}/bin/${SCRIPT_NAME} - DESTINATION ${CMAKE_INSTALL_BINDIR} - OPTIONAL) - endif() - endif() -endfunction() - include(PyBind11Tools) -# OMNITRACE_PYTHON_ROOT_DIRS=/opt/conda/envs/py36;/opt/conda/envs/py37;/opt/conda/envs/py38;/opt/conda/envs/py39 -# OMNITRACE_PYTHON_VERSIONS=3.6;3.7;3.8;3.9 +omnitrace_watch_for_change(OMNITRACE_PYTHON_ROOT_DIRS _PYTHON_DIRS_CHANGED) +if(_PYTHON_DIRS_CHANGED) + unset(OMNITRACE_PYTHON_VERSION CACHE) + unset(OMNITRACE_PYTHON_VERSIONS CACHE) +else() + foreach(_VAR PREFIX ENVS) + omnitrace_watch_for_change(OMNITRACE_PYTHON_${_VAR} _CHANGED) + if(_CHANGED) + unset(OMNITRACE_PYTHON_ROOT_DIRS CACHE) + unset(OMNITRACE_PYTHON_VERSIONS CACHE) + break() + endif() + endforeach() +endif() + +if(OMNITRACE_PYTHON_PREFIX AND OMNITRACE_PYTHON_ENVS) + omnitrace_directory( + FAIL + PREFIX ${OMNITRACE_PYTHON_PREFIX} + PATHS ${OMNITRACE_PYTHON_ENVS} + OUTPUT_VARIABLE _PYTHON_ROOT_DIRS) + set(OMNITRACE_PYTHON_ROOT_DIRS + "${_PYTHON_ROOT_DIRS}" + CACHE INTERNAL "Root directories for python") +endif() if(NOT OMNITRACE_PYTHON_VERSIONS AND OMNITRACE_PYTHON_VERSION) set(OMNITRACE_PYTHON_VERSIONS "${OMNITRACE_PYTHON_VERSION}") @@ -152,6 +124,7 @@ if(NOT OMNITRACE_PYTHON_VERSIONS AND OMNITRACE_PYTHON_VERSION) "${_PY_ROOT_DIR}" CACHE INTERNAL "" FORCE) endif() + unset(OMNITRACE_PYTHON_VERSION CACHE) elseif( NOT OMNITRACE_PYTHON_VERSIONS AND NOT OMNITRACE_PYTHON_VERSION @@ -182,27 +155,16 @@ elseif( CACHE INTERNAL "" FORCE) endif() -list(LENGTH OMNITRACE_PYTHON_VERSIONS _NUM_PYTHON_VERSIONS) -list(LENGTH OMNITRACE_PYTHON_ROOT_DIRS _NUM_PYTHON_ROOT_DIRS) +omnitrace_watch_for_change(OMNITRACE_PYTHON_ROOT_DIRS) +omnitrace_watch_for_change(OMNITRACE_PYTHON_VERSIONS) -if(NOT _NUM_PYTHON_VERSIONS EQUAL _NUM_PYTHON_ROOT_DIRS) - omnitrace_message( - WARNING - "Error! Number of python versions : ${_NUM_PYTHON_VERSIONS}. VERSIONS :: ${OMNITRACE_PYTHON_VERSIONS}" - ) - omnitrace_message( - WARNING - "Error! Number of python root directories : ${_NUM_PYTHON_ROOT_DIRS}. ROOT DIRS :: ${OMNITRACE_PYTHON_ROOT_DIRS}" - ) - omnitrace_message( - FATAL_ERROR - "Error! Number of python versions != number of python root directories") -endif() +omnitrace_check_python_dirs_and_versions(FAIL) file(GLOB_RECURSE PYTHON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/omnitrace/*.py) foreach(_IN ${PYTHON_FILES}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/omnitrace" - "${PROJECT_BINARY_DIR}/python/omnitrace" _OUT "${_IN}") + "${PROJECT_BINARY_DIR}/lib/python/site-packages/omnitrace" _OUT + "${_IN}") configure_file(${_IN} ${_OUT} @ONLY) install( FILES ${_OUT} @@ -212,12 +174,6 @@ endforeach() omnitrace_python_console_script("omnitrace-python" "omnitrace") -execute_process( - COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_INSTALL_LIBDIR}/python - COMMAND ${CMAKE_COMMAND} -E create_symlink ../../python - ${CMAKE_INSTALL_LIBDIR}/python/site-packages - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) - set(_INDEX 0) foreach(_VERSION ${OMNITRACE_PYTHON_VERSIONS}) # add_library(libpyomnitrace MODULE ${pysources} ${pyheaders}) @@ -237,29 +193,9 @@ foreach(_VERSION ${OMNITRACE_PYTHON_VERSIONS}) math(EXPR _INDEX "${_INDEX} + 1") endforeach() -if(PYTHON_EXECUTABLE AND OFF) - configure_file(${CMAKE_CURRENT_LIST_DIR}/setup.py.in - ${PROJECT_BINARY_DIR}/python/setup.py @ONLY) - configure_file(${CMAKE_CURRENT_LIST_DIR}/setup.cfg.in - ${PROJECT_BINARY_DIR}/python/setup.cfg @ONLY) - configure_file(${CMAKE_CURRENT_LIST_DIR}/pyproject.toml - ${PROJECT_BINARY_DIR}/python/pyproject.toml COPYONLY) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} setup.py dist_info - OUTPUT_VARIABLE _OUT - RESULT_VARIABLE _RET - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/python) - set(_DIST_DIR - ${PROJECT_BINARY_DIR}/python/${PROJECT_NAME}-${PROJECT_VERSION}.dist-info) - if(NOT EXISTS ${_DIST_DIR}) - set(_DIST_DIR ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.dist-info) - endif() - if(EXISTS ${_DIST_DIR} AND IS_DIRECTORY ${_DIST_DIR}) - configure_file(${PROJECT_SOURCE_DIR}/LICENSE ${_DIST_DIR}/LICENSE.txt COPYONLY) - install( - DIRECTORY ${_DIST_DIR} - DESTINATION ${CMAKE_INSTALL_PYTHONDIR} - OPTIONAL) - endif() -endif() +configure_file(${CMAKE_CURRENT_LIST_DIR}/setup.py.in + ${PROJECT_BINARY_DIR}/lib/python/site-packages/setup.py @ONLY) +configure_file(${CMAKE_CURRENT_LIST_DIR}/setup.cfg.in + ${PROJECT_BINARY_DIR}/lib/python/site-packages/setup.cfg @ONLY) +configure_file(${CMAKE_CURRENT_LIST_DIR}/pyproject.toml + ${PROJECT_BINARY_DIR}/lib/python/site-packages/pyproject.toml COPYONLY) diff --git a/projects/rocprofiler-systems/source/python/setup.cfg.in b/projects/rocprofiler-systems/source/python/setup.cfg.in index c66294de91..e1ef902a19 100644 --- a/projects/rocprofiler-systems/source/python/setup.cfg.in +++ b/projects/rocprofiler-systems/source/python/setup.cfg.in @@ -26,7 +26,6 @@ classifiers = Operating System :: Unix Programming Language :: C++ Programming Language :: Python :: 3 - Programming Language :: Python :: @OMNITRACE_PYTHON_VERSION@ Topic :: Software Development :: Libraries :: Python Modules Topic :: Utilities diff --git a/projects/rocprofiler-systems/source/python/setup.py.in b/projects/rocprofiler-systems/source/python/setup.py.in index da0b69f05a..5558677887 100644 --- a/projects/rocprofiler-systems/source/python/setup.py.in +++ b/projects/rocprofiler-systems/source/python/setup.py.in @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + from setuptools import setup setup( @@ -7,5 +9,4 @@ setup( author="AMD Research", url="@PROJECT_HOMEPAGE_URL@", packages=["@PROJECT_NAME@"], - python_requires="=@OMNITRACE_PYTHON_VERSION@", ) diff --git a/projects/rocprofiler-systems/tests/CMakeLists.txt b/projects/rocprofiler-systems/tests/CMakeLists.txt index c2094bbe75..855bac3827 100644 --- a/projects/rocprofiler-systems/tests/CMakeLists.txt +++ b/projects/rocprofiler-systems/tests/CMakeLists.txt @@ -37,7 +37,6 @@ set(_base_environment set(_ompt_environment "OMNITRACE_USE_PERFETTO=ON" "OMNITRACE_USE_TIMEMORY=ON" - "OMNITRACE_USE_SAMPLING=ON" "OMNITRACE_TIME_OUTPUT=OFF" "OMNITRACE_USE_OMPT=ON" "OMNITRACE_CRITICAL_TRACE=OFF" @@ -80,23 +79,33 @@ set(_python_environment "OMNITRACE_USE_PID=OFF" "OMNITRACE_TIMEMORY_COMPONENTS=trip_count" "LD_LIBRARY_PATH=${PROJECT_BINARY_DIR}:${OMNITRACE_DYNINST_API_RT_DIR}:$ENV{LD_LIBRARY_PATH}" - "PYTHONPATH=${CMAKE_BINARY_DIR}/python") + "PYTHONPATH=${PROJECT_BINARY_DIR}/lib/python/site-packages") # -------------------------------------------------------------------------------------- # function(OMNITRACE_ADD_TEST) + foreach(_PREFIX RUNTIME REWRITE REWRITE_RUN) + foreach(_TYPE PASS FAIL SKIP) + list(APPEND _REGEX_OPTS "${_PREFIX}_${_TYPE}_REGEX") + endforeach() + endforeach() + set(_KWARGS REWRITE_ARGS RUNTIME_ARGS RUN_ARGS ENVIRONMENT LABELS PROPERTIES + ${_REGEX_OPTS}) + cmake_parse_arguments( TEST - "SKIP_REWRITE;SKIP_RUNTIME;SKIP_SAMPLING" # options + "SKIP_BASELINE;SKIP_REWRITE;SKIP_RUNTIME;SKIP_SAMPLING" # options "NAME;TARGET;MPI;NUM_PROCS;REWRITE_TIMEOUT;RUNTIME_TIMEOUT" # single value args - "REWRITE_ARGS;RUNTIME_ARGS;RUN_ARGS;ENVIRONMENT;LABELS;PROPERTIES" # multiple - # value args + "${_KWARGS}" # multiple value args ${ARGN}) if("${TEST_MPI}" STREQUAL "") set(TEST_MPI OFF) endif() + list(INSERT TEST_REWRITE_ARGS 0 --print-instrumented functions) + list(INSERT TEST_RUNTIME_ARGS 0 --print-instrumented functions) + if(NOT DEFINED TEST_NUM_PROCS) set(TEST_NUM_PROCS ${NUM_PROCS}) endif() @@ -133,10 +142,12 @@ function(OMNITRACE_ADD_TEST) list(APPEND TEST_ENVIRONMENT "OMNITRACE_USE_PID=OFF") endif() - add_test( - NAME ${TEST_NAME}-baseline - COMMAND ${COMMAND_PREFIX} $ ${TEST_RUN_ARGS} - WORKING_DIRECTORY $) + if(NOT SKIP_BASELINE) + add_test( + NAME ${TEST_NAME}-baseline + COMMAND ${COMMAND_PREFIX} $ ${TEST_RUN_ARGS} + WORKING_DIRECTORY $) + endif() if(NOT TEST_SKIP_REWRITE) add_test( @@ -213,6 +224,9 @@ function(OMNITRACE_ADD_TEST) "OMNITRACE_OUTPUT_PREFIX=${_prefix}") string(REPLACE "-run" "" _labels "${_TEST}") string(REPLACE "-sampling" ";sampling" _labels "${_labels}") + if(TEST_TARGET) + list(APPEND _labels "${TEST_TARGET}") + endif() if("${_TEST}" MATCHES "runtime-instrument") set(_timeout ${TEST_RUNTIME_TIMEOUT}) endif() @@ -223,11 +237,41 @@ function(OMNITRACE_ADD_TEST) list(APPEND _props RUN_SERIAL ON) endif() endif() + + if("${_TEST}" MATCHES "binary-rewrite-run") + set(_REGEX_VAR REWRITE_RUN) + elseif("${_TEST}" MATCHES "runtime-instrument") + set(_REGEX_VAR RUNTIME) + elseif("${_TEST}" MATCHES "binary-rewrite") + set(_REGEX_VAR REWRITE) + else() + set(_REGEX_VAR) + endif() + + foreach(_TYPE PASS FAIL SKIP) + if(_REGEX_VAR) + set(_${_TYPE}_REGEX TEST_${_REGEX_VAR}_${_TYPE}_REGEX) + else() + set(_${_TYPE}_REGEX) + endif() + endforeach() + if(TEST ${TEST_NAME}-${_TEST}) set_tests_properties( ${TEST_NAME}-${_TEST} - PROPERTIES ENVIRONMENT "${_environ}" TIMEOUT ${_timeout} LABELS - "${_labels};${TEST_LABELS}" ${_props}) + PROPERTIES ENVIRONMENT + "${_environ}" + TIMEOUT + ${_timeout} + LABELS + "${_labels};${TEST_LABELS}" + PASS_REGULAR_EXPRESSION + "${${_PASS_REGEX}}" + FAIL_REGULAR_EXPRESSION + "${${_FAIL_REGEX}}" + SKIP_REGULAR_EXPRESSION + "${${_SKIP_REGEX}}" + ${_props}) endif() endforeach() endif() @@ -244,8 +288,8 @@ function(OMNITRACE_ADD_PYTHON_TEST) TEST "STANDALONE" # options "NAME;FILE;TIMEOUT;PYTHON_EXECUTABLE;PYTHON_VERSION" # single value args - "PROFILE_ARGS;RUN_ARGS;ENVIRONMENT;LABELS;PROPERTIES;PASS_REGULAR_EXPRESSION;FAIL_REGULAR_EXPRESSION;SKIP_REGULAR_EXPRESSION;DEPENDS;COMMAND" # multiple - # value args + "PROFILE_ARGS;RUN_ARGS;ENVIRONMENT;LABELS;PROPERTIES;PASS_REGEX;FAIL_REGEX;SKIP_REGEX;DEPENDS;COMMAND" # multiple + # value args ${ARGN}) if(NOT TEST_TIMEOUT) @@ -302,14 +346,14 @@ function(OMNITRACE_ADD_PYTHON_TEST) set(_TEST_PROPERTIES "${TEST_PROPERTIES}") if(NOT "${_TEST}" MATCHES "inverse") # assign pass variable to pass regex - set(_PASS_REGEX TEST_PASS_REGULAR_EXPRESSION) + set(_PASS_REGEX TEST_PASS_REGEX) # assign fail variable to fail regex - set(_FAIL_REGEX TEST_FAIL_REGULAR_EXPRESSION) + set(_FAIL_REGEX TEST_FAIL_REGEX) else() # assign pass variable to fail regex - set(_PASS_REGEX TEST_FAIL_REGULAR_EXPRESSION) + set(_PASS_REGEX TEST_FAIL_REGEX) # assign fail variable to pass regex - set(_FAIL_REGEX TEST_PASS_REGULAR_EXPRESSION) + set(_FAIL_REGEX TEST_PASS_REGEX) # set to will fail list(APPEND _TEST_PROPERTIES WILL_FAIL ON) endif() @@ -329,7 +373,7 @@ function(OMNITRACE_ADD_PYTHON_TEST) FAIL_REGULAR_EXPRESSION "${${_FAIL_REGEX}}" SKIP_REGULAR_EXPRESSION - "${TEST_SKIP_REGULAR_EXPRESSION}" + "${TEST_SKIP_REGEX}" REQUIRED_FILES "${TEST_FILE}" ${_TEST_PROPERTIES}) @@ -350,12 +394,12 @@ omnitrace_add_test( omnitrace_add_test( NAME parallel-overhead TARGET parallel-overhead - REWRITE_ARGS -e -v 2 --min-address-range-loop=64 + REWRITE_ARGS -e -v 2 --min-instructions=8 RUNTIME_ARGS -e -v 1 - --min-address-range-loop=64 + --min-instructions=8 --label file line @@ -367,23 +411,12 @@ omnitrace_add_test( omnitrace_add_test( NAME user-api TARGET user-api - REWRITE_ARGS - -e - -v - 2 - -l - --min-address-range=0 - --min-address-range-loop=0 - --min-instructions=8 - -E - custom_push_region + REWRITE_ARGS -e -v 2 -l --min-instructions=8 -E custom_push_region RUNTIME_ARGS -e -v 1 -l - --min-address-range=0 - --min-address-range-loop=0 --min-instructions=8 -E custom_push_region @@ -400,6 +433,7 @@ omnitrace_add_test( TARGET lulesh MPI ${LULESH_USE_MPI} NUM_PROCS 8 + LABELS "kokkos" REWRITE_ARGS -e -v 2 --label file line return args RUNTIME_ARGS -e @@ -417,10 +451,12 @@ omnitrace_add_test( "${_base_environment};OMNITRACE_CRITICAL_TRACE=OFF;OMNITRACE_USE_KOKKOSP=OFF") omnitrace_add_test( + SKIP_BASELINE NAME lulesh-kokkosp TARGET lulesh MPI ${LULESH_USE_MPI} NUM_PROCS 8 + LABELS "kokkos" REWRITE_ARGS -e -v 2 RUNTIME_ARGS -e @@ -438,10 +474,12 @@ omnitrace_add_test( "${_base_environment};OMNITRACE_CRITICAL_TRACE=OFF;OMNITRACE_USE_KOKKOSP=ON") omnitrace_add_test( + SKIP_BASELINE SKIP_SAMPLING NAME lulesh-perfetto TARGET lulesh MPI ${LULESH_USE_MPI} NUM_PROCS 8 + LABELS "kokkos" REWRITE_ARGS -e -v 2 RUNTIME_ARGS -e @@ -458,10 +496,12 @@ omnitrace_add_test( "${_perfetto_environment};OMNITRACE_CRITICAL_TRACE=OFF;OMNITRACE_USE_KOKKOSP=OFF") omnitrace_add_test( + SKIP_SAMPLING NAME lulesh-timemory TARGET lulesh MPI ${LULESH_USE_MPI} NUM_PROCS 8 + LABELS "kokkos" REWRITE_ARGS -e -v 2 -l --dynamic-callsites --traps --allow-overlapping RUNTIME_ARGS -e @@ -479,22 +519,159 @@ omnitrace_add_test( "${_timemory_environment};OMNITRACE_CRITICAL_TRACE=OFF;OMNITRACE_USE_KOKKOSP=OFF") omnitrace_add_test( + SKIP_SAMPLING NAME openmp-cg TARGET openmp-cg + LABELS "openmp" REWRITE_ARGS -e -v 2 --instrument-loops RUNTIME_ARGS -e -v 1 --label return args REWRITE_TIMEOUT 180 RUNTIME_TIMEOUT 360 - ENVIRONMENT "${_ompt_environment}") + ENVIRONMENT "${_ompt_environment};OMNITRACE_USE_SAMPLING=OFF") omnitrace_add_test( + SKIP_RUNTIME NAME openmp-lu TARGET openmp-lu + LABELS "openmp" REWRITE_ARGS -e -v 2 --instrument-loops - RUNTIME_ARGS -e -v 1 --label return args + RUNTIME_ARGS -e -v 1 --label return args -E ^GOMP REWRITE_TIMEOUT 180 RUNTIME_TIMEOUT 360 - ENVIRONMENT "${_ompt_environment}") + ENVIRONMENT + "${_ompt_environment};OMNITRACE_USE_SAMPLING=ON;OMNITRACE_SAMPLING_FREQ=100") + +omnitrace_add_test( + SKIP_BASELINE SKIP_SAMPLING + NAME code-coverage + TARGET code-coverage + REWRITE_ARGS + -e + -v + 2 + --min-instructions=4 + -E + ^std:: + -M + coverage + --coverage + function + RUNTIME_ARGS + -e + -v + 1 + --min-instructions=4 + -E + ^std:: + --label + file + line + return + args + -M + coverage + --coverage + function + --module-restrict + code.coverage + LABELS "coverage;function-coverage" + RUN_ARGS 10 ${NUM_THREADS} 1000 + ENVIRONMENT "${_base_environment}" + RUNTIME_PASS_REGEX "(\\\[[0-9]+\\\]) code coverage :: 66.67%" + REWRITE_RUN_PASS_REGEX "(\\\[[0-9]+\\\]) code coverage :: 66.67%") + +omnitrace_add_test( + SKIP_BASELINE SKIP_SAMPLING + NAME code-coverage-hybrid + TARGET code-coverage + REWRITE_ARGS -e -v 2 --min-instructions=4 -E ^std:: --coverage function + RUNTIME_ARGS + -e + -v + 1 + --min-instructions=4 + -E + ^std:: + --label + file + line + return + args + --coverage + function + --module-restrict + code.coverage + LABELS "coverage;function-coverage;hybrid-coverage" + RUN_ARGS 10 ${NUM_THREADS} 1000 + ENVIRONMENT "${_base_environment}" + RUNTIME_PASS_REGEX "(\\\[[0-9]+\\\]) code coverage :: 66.67%" + REWRITE_RUN_PASS_REGEX "(\\\[[0-9]+\\\]) code coverage :: 66.67%") + +omnitrace_add_test( + SKIP_BASELINE SKIP_SAMPLING + NAME code-coverage-basic-blocks + TARGET code-coverage + REWRITE_ARGS + -e + -v + 2 + --min-instructions=4 + -E + ^std:: + -M + coverage + --coverage + basic_block + RUNTIME_ARGS + -e + -v + 1 + --min-instructions=4 + -E + ^std:: + --label + file + line + return + args + -M + coverage + --coverage + basic_block + --module-restrict + code.coverage + LABELS "coverage;bb-coverage" + RUN_ARGS 10 ${NUM_THREADS} 1000 + ENVIRONMENT "${_base_environment}" + RUNTIME_PASS_REGEX "(\\\[[0-9]+\\\]) function coverage :: 66.67%" + REWRITE_RUN_PASS_REGEX "(\\\[[0-9]+\\\]) function coverage :: 66.67%") + +omnitrace_add_test( + SKIP_BASELINE SKIP_SAMPLING + NAME code-coverage-basic-blocks-hybrid + TARGET code-coverage + REWRITE_ARGS -e -v 2 --min-instructions=4 -E ^std:: --coverage basic_block + RUNTIME_ARGS + -e + -v + 1 + --min-instructions=4 + -E + ^std:: + --label + file + line + return + args + --coverage + basic_block + --module-restrict + code.coverage + LABELS "coverage;bb-coverage;hybrid-coverage" + RUN_ARGS 10 ${NUM_THREADS} 1000 + ENVIRONMENT "${_base_environment}" + RUNTIME_PASS_REGEX "(\\\[[0-9]+\\\]) function coverage :: 66.67%" + REWRITE_RUN_PASS_REGEX "(\\\[[0-9]+\\\]) function coverage :: 66.67%") # -------------------------------------------------------------------------------------- # # @@ -572,7 +749,7 @@ foreach(_VERSION ${OMNITRACE_PYTHON_VERSIONS}) COMMAND ${OMNITRACE_CAT_COMMAND} PYTHON_VERSION ${_VERSION} FILE omnitrace-tests-output/python-external/${_VERSION}/trip_count.txt - PASS_REGULAR_EXPRESSION + PASS_REGEX "(\\\[compile\\\]).*(\\\| \\\|0>>> \\\[run\\\]\\\[external.py\\\]).*(\\\| \\\|0>>> \\\|_\\\[fib\\\]\\\[external.py\\\]).*(\\\| \\\|0>>> \\\|_\\\[inefficient\\\]\\\[external.py\\\])" DEPENDS python-external-${_VERSION} ENVIRONMENT "${_python_environment}") @@ -582,7 +759,7 @@ foreach(_VERSION ${OMNITRACE_PYTHON_VERSIONS}) COMMAND ${OMNITRACE_CAT_COMMAND} PYTHON_VERSION ${_VERSION} FILE omnitrace-tests-output/python-external-exclude-inefficient/${_VERSION}/trip_count.txt - FAIL_REGULAR_EXPRESSION "(\\\|_inefficient).*(\\\|_sum)" + FAIL_REGEX "(\\\|_inefficient).*(\\\|_sum)" DEPENDS python-external-exclude-inefficient-${_VERSION} ENVIRONMENT "${_python_environment}") @@ -591,7 +768,7 @@ foreach(_VERSION ${OMNITRACE_PYTHON_VERSIONS}) COMMAND ${OMNITRACE_CAT_COMMAND} PYTHON_VERSION ${_VERSION} FILE omnitrace-tests-output/python-builtin/${_VERSION}/trip_count.txt - PASS_REGULAR_EXPRESSION "\\\[inefficient\\\]\\\[builtin.py:14\\\]" + PASS_REGEX "\\\[inefficient\\\]\\\[builtin.py:14\\\]" DEPENDS python-builtin-${_VERSION} ENVIRONMENT "${_python_environment}") @@ -600,7 +777,7 @@ foreach(_VERSION ${OMNITRACE_PYTHON_VERSIONS}) COMMAND ${OMNITRACE_CAT_COMMAND} PYTHON_VERSION ${_VERSION} FILE omnitrace-tests-output/python-source/${_VERSION}/trip_count.txt - PASS_REGULAR_EXPRESSION + PASS_REGEX "(\\\| \\\|0>>> run \\\| 5).*(\\\| \\\|0>>> \\\|_fib \\\| 40).*(\\\| \\\|0>>> \\\|_fib \\\| 5).*(\\\| \\\|0>>> \\\|_inefficient \\\| 5).*(\\\| \\\|0>>> \\\|__sum \\\| 5)" DEPENDS python-source-${_VERSION} ENVIRONMENT "${_python_environment}")