* Addition of basic structure

* Reworked categories

* More causal integration additions

* Causal implementation

* Update examples

* delete virtual_speedup files

* Update perfetto submodule to v31.0

* Update dyninst submodule

* Update timemory submodule

* ElfUtils build for libdw

* OMNITRACE_LIKELY and OMNITRACE_UNLIKELY

* Update common lib join

* Examples updates for causal profiling

* config updates with causal options

- OMNITRACE_CAUSAL_FIXED_LINE
- OMNITRACE_CAUSAL_FIXED_SPEEDUP
- OMNITRACE_CAUSAL_FILE
- OMNITRACE_CAUSAL_BINARY_SCOPE
- OMNITRACE_CAUSAL_SOURCE_SCOPE
- version info in banner
- support increments in parse_numeric_range
- fix occasional deadlock in first call to get_config

* PTL general task group

* Always include PID in debug/verbose messages

* Add blocking/unblocking gotchas to runtime init bundle

* CausalState

* thread_data updates

- generic component_bundle_cache

* Improve handling of causal in category_region

* components updates

- backtrace_causal component
- backtrace::get_data member func
- decrease ignore_depth in backtrace::sample(int)
- handle "omnitrace_main" in backtrace::filter_and_patch(...)
- tweak internal thread state scope for pthread_mutex_gotcha wrappers

* simplify tracing get_instrumentation_bundles usage

* sampling updates

- include backtrace_causal component
- disable backtrace_metrics if using causal and not using perfetto
- disable backtrace and backtrace_timestamp when using causal
- post_process_causal

* causal updates

- more checks in blocking_gotcha and unblocking_gotcha start/stop
- miscellaneous overhaul of data
- experiment update

* Remove virtual speedup

* libomnitrace code_object

* causal-profiling test

* libomnitrace library.cpp updates

- handle causal profiling
- fini_bundle

* Disable causal profiling by default

* Updated causal code and example

- example: three execution variants: cpu + rng, cpu, rng
- example: three instrumentation variants: none, omni, coz
- fix blocking gotcha credit
- rework perform_experiment_impl
- get_eligible_address_ranges
- compute_eligible_lines
- support fixed lines/speedups/functions
- update selected_entry to support function mode
- fix causal::delay
- experiment updates

* omnitrace_progress / omnitrace_user_progress

- with accompanying omnitrace_annotated_progress / omnitrace_user_annotated_progress

* Update timemory submodule

* CausalMode

- mode indicated whether causal predictions source be at line-level or function-level

* code_object, config, runtime, sampling, thread_data

- code_object: address_range
- code_object: basic::line_info serialize(), name(), hash()
- config updates
- two signals for causal sampling
- thread_data init fixes

* pthread updates

- pthread_create_gotcha processes delays
- pthread_mutex_gotcha does not wrap pthread_join in causal mode

* backtrace_causal update

- dynamic delay period stats

* main wrapper uses basename of argv[0]

* update elfio submodule

* perf support (currently unused)

* Fix experiment JSON serialization

- static_vector.hpp (unused)

* causal executable + config options updates

- omnitrace-causal exe simplifies running multiple causal configs
- changed the causal config option names

* Support both throughput and latency points

* process-causal-json.py script

- will be used later for testing

* stable_vector

* Rework thread_data

* Improve omnitrace-causal exe

- better verbosity handling
- correct diagnosis of status for child process
- execvpe when only one iteration (debugging)

* Update timemory submodule

* exe --version

- omnitrace, omnitrace-avail, and omnitrace-sample all support --version on command-line

* OMNITRACE_INTERNAL_API + OMNITRACE_{LIKELY,UNLIKELY}

* omnitrace-causal cmake format

* omnitrace config update

- OMNITRACE_CAUSAL_FILE_CLOBBER

* custom exception

- wraps STL exception and gets stacktrace during construction

* exit_gotcha supports _Exit

* use global construct_on_init + max threads

- add some safety when exceeding max # of threads

* update code_object binary filter

- exclude dyninst and tbbmalloc library

* containers: c_array, static_vector, stable_vector

- moved utility::c_array to container::c_array
- created static_vector: std::vector bound to std::array
- created stable_vector: vector with stable references

* grow thread_data when new thread created

* causal updates

- data: improve compute_eligible_lines to ignore lambdas
- data: use new thread_data
- delay: use new thread_data
- experiment: properly support latency points
- experiment: support file clobber
- experiment: ensure non-zero experiment time
- progress_point: use new thread_data
- backtrace_causal: use new thread_data

* Update causal-profiling tests

* fix omnitrace-causal backslash escaping

* process-causal-json script

* restructure causal implementation

- update verbose messages for omnitrace-causal diagnose_status
- migrated causal implementation in sampling.cpp to causal/sampling.cpp
- OMNITRACE_USE_CAUSAL does not require OMNITRACE_USE_SAMPLING
- added Mode::Causal
- causal sampling uses same signals as regular sampling
- moved tracing::thread_init to implementation file
- combined tracing::thread_init and tracing::thread_init_sampling
- added causal/components folder
- pthread_create_gotcha::wrapper_config
- omnitrace_preload checks OMNITRACE_USE_CAUSAL
  - updates mode accordingly

* update timemory submodule

* update timemory submodule

* causal example updates

- causal for lulesh

* perf code + utility - helpers

- relocated causal perf code
- placement new when generating unique ptr trait for potentially allocating during sampling
- additions to utility header
- removed previously added helpers.hpp

* update timemory submodule

* Default env variables for omnitrace-causal

- activate OMNITRACE_USE_KOKKOSP, etc.

* update stable_vector and static_vector

- static vector can use atomic for size tracking for thread-safe situations

* update causal example header

- CAUSAL_PROGRESS_NAMED
- use CAUSAL_ prefix for some macros

* Tweak lulesh example

- use CAUSAL_PROGRESS instead of CAUSAL_BEGIN and CAUSAL_END

* omnitrace-sample support for causal mode

- set OMNITRACE_USE_SAMPLING to off when OMNITRACE_MODE=causal

* refactor and cleanup code_object

- scope filter
- fixes to address_range

* overhaul causal data + causal config options

- full support for function and line mode
- support static vector of instruction pointers
- improve line info mapping resolution
- remove thread-locality from miscellanous functions where unnecessary
- causal options for {binary,source,function,fileline} exclusion

* causal experiment, sampling, and backtrace updates

- is_selected + unwind address array
- experiment warning about progress points
- increased buffer size for backtrace_casual sampler
- backtrace_causal only stores IP addresses instead of full unwind info

* category_region updates

- minor refactor
- local_category_region::mark

* Update causal tests

* Bump version to 1.8.0

* omnitrace-causal args + CLOBBER -> RESET

- renamed OMNITRACE_CAUSAL_FILE_CLOBBER to OMNITRACE_CAUSAL_FILE_RESET
- updated omnitrace-causal exe to support recently added configuration options
- other miscellaneous tweaks to data.cpp, experiment.cpp, and sampling.cpp

* Refactor causal and code_object

- code_object.hpp and code_object.cpp moved into binary folder
- causal components namespaced into omnitrace::causal::component
- moved sample_data out of backtrace_causal and into own file
- renamed backtrace_causal to causal::component::backtrace

* preload omnitrace_init + OMNITRACE_DEBUG_MARK

- env OMNITRACE_DEBUG_MARK
- fix omnitrace_init call when LD_PRELOAD-ing omnitrace

* Fix fileline support + line-info output names + experiment log

- line-info log files are prefixed with experiment name
- don't print experiment duration when E2E
- account for fileline scope in analysis

* KokkosP: OMNITRACE_KOKKOSP_NAME_LENGTH_MAX

- config option to limit the name of kokkos tool callbacks
- remove [kokkos] from KokkosP names

* Update causal example

- minor tweaks to decrease probability of overlapping regions in binary

* omnitrace-causal update

- prefix N / Ntot in environment printout

* Miscellaneous updates

- causal::finish_experimenting()
- OMNITRACE_CAUSAL_RANDOM_SEED
- KokkosP causal updates
  - exclude some callbacks, make some callbacks unique, etc.
- address_range::operator+=(address_range)
- combine contiguous ranges in binary/analysis.cpp when file, func, line is same and address range is contiguous
- bfd_line_info reads inline info
- wait for perform_experiment_impl to complete
- causal::delay updates
  - delay::process checks if experiment is active
  - uses threading::get_id()
- experiment scales duration up for larger speedup experiments
- line info samples includes excluded lines
- sampler uses CLOCK_REALTIME
- blocking_gotcha updates
  - is no longer fully static
  - adds audit routine which sets the postblock value to zero if try/timed routine fails
- category::host was added to causal_throughput_categories_t
- pthread_create_gotcha sets new threads local parent delay
  - was using internal value, now uses sequent value

* Causal improvements to KokkosP

* Updates to experiment time scaling

- use stats instead of just max

* binary/link_map.{hpp,cpp}

* update process-causal-json.py

* Folded fileline scope into source scope

* Update documentation

- Add documentation for causal profiling
- Replace 'Omnitrace' with 'OmniTrace' everywhere

* Update causal-helpers.cmake + omnitrace-testing.cmake

- split tests/CMakeLists.txt partially into omnitrace-testing.cmake

* omnitrace/causal.h

- OMNITRACE_CAUSAL_PROGRESS
- OMNITRACE_CAUSAL_PROGRESS_NAMED
- OMNITRACE_CAUSAL_BEGIN
- OMNITRACE_CAUSAL_END

* selected_entry + remove default filters for lambdas and operator()

- selected entry stores range and binary load address

* update process-causal-json.py

* format examples/lulesh/CMakeLists.txt

* causal-helpers find_package(Threads)

* OMNITRACE_KOKKOSP_KERNEL_LOGGER

- was OMNITRACE_KOKKOS_KERNEL_LOGGER

* quiet find of coz-profiler

* Fix rocm_smi exception handling

* Update timemory submodule (binutils)

- fix binutls compile error on some systems
- bump binutils to v2.40

* Fix miscellaneous tests

* OMNITRACE_KOKKOSP_PREFIX

* revert rocm_smi handling

* ElfUtils updates

- default to download version 0.188
- add -Wno-error=null-dereference due to GCC 12 compiler error

* Update causal example

* Remove OMNITRACE_VERBOSE from global workflow envs

* Reliable causal test

* disable compilation of causal perf files

* Remove set_current_selection with unwind stack

* update timemory submodule

* fix for segfault on bionic

- locking in TLS dtor was causing segfault

* remove experiment::is_selected(unwind_stack_t)

* update default init of selected_entry

* Fix for when IP is not offset by load address

* Update CMakeLists.txt

* Miscellaneous updates

- OMNITRACE_WARNING_OR_CI_THROW
- OMNITRACE_REQUIRE
- OMNITRACE_PREFER
- fixed issues with no ASLR
-  added load address variable and ipaddr() func to basic/bfd line info
- removed get_basic() from dwarf_line_info
- TIMEMORY_PREFER -> OMNITRACE_PREFER
- removed previously added binary_address and range variables from selected_entry

* Removed superfluous CausalState

* Additional causal tests (lulesh + kokkos)

* filter, prefer, analysis ASLR handling

- removed default filter on cold functions
- fixed OMNITRACE_PREFER
- fixed analysis ASLR handling

* Tweak line-info output

* Removed some superfluous code

- causal/delay
- causal/selected_entry

* Exclude main.cold in function mode

* Update validate-perfetto-proto.py

- account for occasional http errors

* Add sampling test disabling tmp files

* argparser for process-causal-json

- support validation
- support filtering

* Avoid pthread_{lock,unlock} in sampling offload

- use homemade atomic_mutex/atomic_lock since contention will be low and using pthread tools might trigger our wrappers

* Rename process-causal-json.py

- validate-causal-json.py

* rework omnitrace_add_causal_test

- capable of performing validation
- added validation tests

* Fix kokkosp_begin_deep_copy + causal

* Tweak address range in bfd_line_info::read_pc

* Tweak analysis and data IP handling

- look for gaps

* Disable scaling experiment time by speedup

* Revert change in max threads during CI

* binary updates

- significant overhaul of binary analysis implementation
- removed "basic_line_info" and "bfd_line_info" in lieu of "symbol" class
  - symbol class has basic BFD info + vector of inlines + vector of dwarf info

* Updated causal to use new binary analysis

- Fix symbol.cpp includes

* Updated formatting target

- include *.cmake files

* Updated causal tests

- causal tests should be stable now

* Update timemory and dyninst submodules

- TPLs are stripped + built w/o debug info

* Increase tolerance for causal validation speedups

- higher speedups have more variance (increased to +/- 5 from 3)

* Support causal output for MPI

- i.e. tag with MPI rank

* omnitrace-causal launcher argument

* improve experiment sampling output

* causal data updates

- call compute lines once
- fixed filtered cached binary info
- debugging info when experiment fails to start

* Tweaked causal validation tests

* dwarf_entry ranges

* CI updates

- increase max threads to 64

* Tweak causal E2E validation tests

- more threads
- shorter thread runtime
- more iterations

* Fix shadowed variable

* fix symbol read_bfd last PC calculation

* fix maybe-uninitialized warning

* omnitrace-causal launcher update

- only inject "omnitrace-causal --" once
- throw error if no matches found

* Update causal profiling docs for launcher

* fix address range boundaries

[ROCm/rocprofiler-systems commit: 9618ddefba]
Этот коммит содержится в:
Jonathan R. Madsen
2023-01-24 18:53:23 -06:00
коммит произвёл GitHub
родитель 234dc4f460
Коммит 3c7e6902e0
163 изменённых файлов: 14418 добавлений и 1257 удалений
+29
Просмотреть файл
@@ -50,6 +50,28 @@ parse:
BASELINE_FAIL_REGEX: '*'
REWRITE_RUN_PASS_REGEX: '*'
REWRITE_RUN_FAIL_REGEX: '*'
omnitrace_add_causal_test:
flags:
- SKIP_BASELINE
kwargs:
NAME: '*'
TARGET: '*'
CAUSAL_TIMEOUT: '*'
CAUSAL_VALIDATE_TIMEOUT: '*'
CAUSAL_MODE: '*'
CAUSAL_ARGS: '*'
CAUSAL_VALIDATE_ARGS: '*'
RUNTIME_ARGS: '*'
RUN_ARGS: '*'
ENVIRONMENT: '*'
LABELS: '*'
PROPERTIES: '*'
CAUSAL_PASS_REGEX: '*'
CAUSAL_FAIL_REGEX: '*'
BASELINE_PASS_REGEX: '*'
BASELINE_FAIL_REGEX: '*'
CAUSAL_VALIDATE_PASS_REGEX: '*'
CAUSAL_VALIDATE_FAIL_REGEX: '*'
omnitrace_target_compile_definitions:
kwargs:
PUBLIC: '*'
@@ -186,6 +208,13 @@ parse:
PATHS: '*'
PATH_SUFFIXES: '*'
DOC: '*'
omnitrace_causal_example_executable:
kwargs:
TAG: '*'
SOURCES: '*'
DEFINITIONS: '*'
LINK_LIBRARIES: '*'
INCLUDE_DIRECTORIES: '*'
override_spec: {}
vartags: []
proptags: []
+1 -2
Просмотреть файл
@@ -18,7 +18,6 @@ concurrency:
cancel-in-progress: true
env:
OMNITRACE_VERBOSE: 1
OMNITRACE_CI: ON
OMNITRACE_TMPDIR: "%env{PWD}%/testing-tmp"
@@ -78,7 +77,7 @@ jobs:
-DOMNITRACE_PYTHON_PREFIX=/opt/conda/envs
-DOMNITRACE_PYTHON_ENVS="py3.6;py3.7;py3.8;py3.9;py3.10"
-DOMNITRACE_CI_MPI_RUN_AS_ROOT=ON
-DOMNITRACE_MAX_THREADS=32
-DOMNITRACE_MAX_THREADS=64
-DOMNITRACE_DISABLE_EXAMPLES="transpose;rccl"
-DOMNITRACE_BUILD_NUMBER=${{ github.run_attempt }}
+1 -2
Просмотреть файл
@@ -20,7 +20,6 @@ concurrency:
env:
BUILD_TYPE: Release
ELFUTILS_DOWNLOAD_VERSION: 0.183
OMNITRACE_VERBOSE: 1
OMNITRACE_CI: ON
GIT_DISCOVERY_ACROSS_FILESYSTEM: 1
OMNITRACE_TMPDIR: "%env{PWD}%/testing-tmp"
@@ -111,7 +110,7 @@ jobs:
-DOMNITRACE_PYTHON_PREFIX=/opt/conda/envs
-DOMNITRACE_PYTHON_ENVS="py3.6;py3.7;py3.8;py3.9;py3.10"
-DLULESH_BUILD_KOKKOS=OFF
-DOMNITRACE_MAX_THREADS=32
-DOMNITRACE_MAX_THREADS=64
-DOMNITRACE_DISABLE_EXAMPLES="transpose;rccl"
-DOMNITRACE_BUILD_NUMBER=${{ github.run_attempt }}
+4 -6
Просмотреть файл
@@ -18,7 +18,6 @@ concurrency:
cancel-in-progress: true
env:
OMNITRACE_VERBOSE: 1
OMNITRACE_CI: ON
OMNITRACE_TMPDIR: "%env{PWD}%/testing-tmp"
@@ -136,7 +135,7 @@ jobs:
-DOMNITRACE_BUILD_STATIC_LIBSTDCXX=${{ matrix.static-libstdcxx }}
-DOMNITRACE_PYTHON_PREFIX=/opt/conda/envs
-DOMNITRACE_PYTHON_ENVS="py3.6;py3.7;py3.8;py3.9;py3.10"
-DOMNITRACE_MAX_THREADS=32
-DOMNITRACE_MAX_THREADS=64
-DOMNITRACE_DISABLE_EXAMPLES="transpose;rccl"
-DOMNITRACE_BUILD_NUMBER=${{ github.run_attempt }}
-DMPI_HEADERS_ALLOW_MPICH=OFF
@@ -316,7 +315,7 @@ jobs:
-DOMNITRACE_BUILD_LTO=OFF
-DOMNITRACE_USE_MPI=OFF
-DOMNITRACE_USE_HIP=ON
-DOMNITRACE_MAX_THREADS=32
-DOMNITRACE_MAX_THREADS=64
-DOMNITRACE_USE_PAPI=OFF
-DOMNITRACE_USE_OMPT=OFF
-DOMNITRACE_USE_PYTHON=ON
@@ -403,7 +402,6 @@ jobs:
env:
ELFUTILS_DOWNLOAD_VERSION: 0.186
OMNITRACE_VERBOSE: 1
steps:
- uses: actions/checkout@v3
@@ -468,7 +466,7 @@ jobs:
-DDYNINST_BUILD_SHARED_LIBS=ON
-DDYNINST_BUILD_STATIC_LIBS=OFF
-DDYNINST_ELFUTILS_DOWNLOAD_VERSION=${{ env.ELFUTILS_DOWNLOAD_VERSION }}
-DOMNITRACE_MAX_THREADS=32
-DOMNITRACE_MAX_THREADS=64
-DOMNITRACE_DISABLE_EXAMPLES="transpose;rccl"
-DOMNITRACE_BUILD_NUMBER=${{ github.run_attempt }}
-DMPI_HEADERS_ALLOW_MPICH=ON
@@ -597,6 +595,6 @@ jobs:
-DOMNITRACE_USE_PAPI=ON
-DOMNITRACE_USE_HIP=OFF
-DOMNITRACE_USE_RCCL=OFF
-DOMNITRACE_MAX_THREADS=32
-DOMNITRACE_MAX_THREADS=64
-DOMNITRACE_DISABLE_EXAMPLES="transpose;rccl"
-DOMNITRACE_BUILD_NUMBER=${{ github.run_attempt }}
+1 -2
Просмотреть файл
@@ -18,7 +18,6 @@ concurrency:
cancel-in-progress: true
env:
OMNITRACE_VERBOSE: 1
OMNITRACE_CI: ON
OMNITRACE_TMPDIR: "%env{PWD}%/testing-tmp"
@@ -163,7 +162,7 @@ jobs:
-DOMNITRACE_PYTHON_PREFIX=/opt/conda/envs
-DOMNITRACE_PYTHON_ENVS="py3.7;py3.8;py3.9;py3.10"
-DOMNITRACE_STRIP_LIBRARIES=${{ matrix.strip }}
-DOMNITRACE_MAX_THREADS=32
-DOMNITRACE_MAX_THREADS=64
-DOMNITRACE_DISABLE_EXAMPLES="transpose;rccl"
-DOMNITRACE_BUILD_NUMBER=${{ github.run_attempt }}
-DUSE_CLANG_OMP=OFF
+3
Просмотреть файл
@@ -32,6 +32,8 @@ such as the memory usage, page-faults, and context-switches, and thread-level me
- Periodic software interrupts per-thread
- Process-level sampling
- Background thread records process-, system- and device-level metrics while the application executes
- Causal profiling
- Quantifies the potential impact of optimizations in parallel codes
- Critical trace generation
### Data Analysis
@@ -41,6 +43,7 @@ such as the memory usage, page-faults, and context-switches, and thread-level me
- Ideal for running at scale
- Comprehensive traces
- Every individual event/measurement
- Application speedup predictions resulting from potential optimizations in functions and lines of code (causal profiling)
- Critical trace analysis (alpha)
### Parallelism API Support
+1 -1
Просмотреть файл
@@ -1 +1 @@
1.7.4
1.8.0
+1 -1
Просмотреть файл
@@ -145,7 +145,7 @@ endif()
# non-debug optimizations
#
omnitrace_add_interface_library(omnitrace-compile-extra "Extra optimization flags")
if(NOT OMNITRACE_USE_COVERAGE)
if(NOT OMNITRACE_USE_COVERAGE AND OMNITRACE_BUILD_EXTRA_OPTIMIZATIONS)
add_target_flag_if_avail(
omnitrace-compile-extra "-finline-functions" "-funroll-loops" "-ftree-vectorize"
"-ftree-loop-optimize" "-ftree-loop-vectorize")
+141
Просмотреть файл
@@ -0,0 +1,141 @@
# ======================================================================================
# elfutils.cmake
#
# Configure elfutils for omnitrace
#
# ----------------------------------------
#
# Accepts the following CMake variables
#
# ElfUtils_ROOT_DIR - Base directory the of elfutils installation
# ElfUtils_INCLUDEDIR - Hint directory that contains the elfutils headers files
# ElfUtils_LIBRARYDIR - Hint directory that contains the elfutils library files
# ElfUtils_MIN_VERSION - Minimum acceptable version of elfutils
#
# Directly exports the following CMake variables
#
# ElfUtils_ROOT_DIR - Computed base directory the of elfutils installation
# ElfUtils_INCLUDE_DIRS - elfutils include directories ElfUtils_LIBRARY_DIRS - Link
# directories for elfutils libraries ElfUtils_LIBRARIES - elfutils library files
#
# NOTE: The exported ElfUtils_ROOT_DIR can be different from the value provided by the
# user in the case that it is determined to build elfutils from source. In such a case,
# ElfUtils_ROOT_DIR will contain the directory of the from-source installation.
#
# See Modules/FindLibElf.cmake and Modules/FindLibDwarf.cmake for details
#
# ======================================================================================
include_guard(GLOBAL)
include(ExternalProject)
# Minimum acceptable version of elfutils NB: We need >=0.178 because libdw isn't
# thread-safe before then
set(_min_version 0.178)
set(ElfUtils_MIN_VERSION
${_min_version}
CACHE STRING "Minimum acceptable elfutils version")
if(${ElfUtils_MIN_VERSION} VERSION_LESS ${_min_version})
omnitrace_message(
FATAL_ERROR
"Requested version ${ElfUtils_MIN_VERSION} is less than minimum supported version (${_min_version})"
)
endif()
# If we didn't find a suitable version on the system, then download one from the web
set(ElfUtils_DOWNLOAD_VERSION
"0.188"
CACHE STRING "Version of elfutils to download and install")
# make sure we are not downloading a version less than minimum
if(${ElfUtils_DOWNLOAD_VERSION} VERSION_LESS ${ElfUtils_MIN_VERSION})
omnitrace_message(
FATAL_ERROR
"elfutils download version is set to ${ElfUtils_DOWNLOAD_VERSION} but elfutils minimum version is set to ${ElfUtils_MIN_VERSION}"
)
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
set(ElfUtils_C_COMPILER
"${CMAKE_C_COMPILER}"
CACHE FILEPATH "C compiler used to compiler ElfUtils")
else()
find_program(
ElfUtils_C_COMPILER
NAMES gcc
PATH_SUFFIXES bin)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(ElfUtils_CXX_COMPILER
"${CMAKE_CXX_COMPILER}"
CACHE FILEPATH "C++ compiler used to compiler ElfUtils")
else()
find_program(
ElfUtils_CXX_COMPILER
NAMES g++
PATH_SUFFIXES bin)
endif()
find_program(
MAKE_COMMAND
NAMES make gmake
PATH_SUFFIXES bin)
if(NOT ElfUtils_C_COMPILER OR NOT ElfUtils_CXX_COMPILER)
omnitrace_message(
FATAL_ERROR
"ElfUtils requires the GNU C and C++ compilers. ElfUtils_C_COMPILER: ${ElfUtils_C_COMPILER}, ElfUtils_CXX_COMPILER: ${ElfUtils_CXX_COMPILER}"
)
endif()
set(_eu_root ${PROJECT_BINARY_DIR}/external/elfutils)
set(_eu_inc_dirs $<BUILD_INTERFACE:${_eu_root}/include>)
set(_eu_lib_dirs $<BUILD_INTERFACE:${_eu_root}/lib>)
set(_eu_libs $<BUILD_INTERFACE:${_eu_root}/lib/libdw${CMAKE_STATIC_LIBRARY_SUFFIX}>
$<BUILD_INTERFACE:${_eu_root}/lib/libelf${CMAKE_STATIC_LIBRARY_SUFFIX}>)
set(_eu_build_byproducts "${_eu_root}/lib/libdw${CMAKE_STATIC_LIBRARY_SUFFIX}"
"${_eu_root}/lib/libelf${CMAKE_STATIC_LIBRARY_SUFFIX}")
externalproject_add(
omnitrace-elfutils-build
PREFIX ${PROJECT_BINARY_DIR}/external/elfutils
URL https://sourceware.org/elfutils/ftp/${ElfUtils_DOWNLOAD_VERSION}/elfutils-${ElfUtils_DOWNLOAD_VERSION}.tar.bz2
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND
${CMAKE_COMMAND} -E env CC=${ElfUtils_C_COMPILER}
CFLAGS=-fPIC\ -O3\ -Wno-error=null-dereference CXX=${ElfUtils_CXX_COMPILER}
CXXFLAGS=-fPIC\ -O3\ -Wno-error=null-dereference
[=[LDFLAGS=-Wl,-rpath='$$ORIGIN']=] <SOURCE_DIR>/configure --enable-install-elfh
--prefix=${_eu_root} --disable-libdebuginfod --disable-debuginfod
--enable-thread-safety
BUILD_COMMAND ${MAKE_COMMAND} install
BUILD_BYPRODUCTS "${_eu_build_byproducts}"
INSTALL_COMMAND "")
# target for re-executing the installation
add_custom_target(
omnitrace-elfutils-install
COMMAND ${MAKE_COMMAND} install
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/external/elfutils/src/ElfUtils-External
COMMENT "Installing ElfUtils...")
# -------------- EXPORT VARIABLES ---------------------------------------------
set(ElfUtils_ROOT_DIR
${_eu_root}
CACHE PATH "Base directory the of elfutils installation" FORCE)
set(ElfUtils_INCLUDE_DIRS
${_eu_inc_dirs}
CACHE PATH "elfutils include directory" FORCE)
set(ElfUtils_LIBRARY_DIRS
${_eu_lib_dirs}
CACHE PATH "elfutils library directory" FORCE)
set(ElfUtils_INCLUDE_DIR
${ElfUtils_INCLUDE_DIRS}
CACHE PATH "elfutils include directory" FORCE)
set(ElfUtils_LIBRARIES
${_eu_libs}
CACHE FILEPATH "elfutils library files" FORCE)
+3
Просмотреть файл
@@ -71,6 +71,9 @@ if(OMNITRACE_CLANG_FORMAT_EXE
${PROJECT_SOURCE_DIR}/source/*CMakeLists.txt
${PROJECT_SOURCE_DIR}/examples/*CMakeLists.txt
${PROJECT_SOURCE_DIR}/tests/*CMakeLists.txt
${PROJECT_SOURCE_DIR}/source/*.cmake
${PROJECT_SOURCE_DIR}/examples/*.cmake
${PROJECT_SOURCE_DIR}/tests/*.cmake
${PROJECT_SOURCE_DIR}/cmake/*.cmake
${PROJECT_SOURCE_DIR}/source/*.cmake)
list(APPEND cmake_files ${PROJECT_SOURCE_DIR}/CMakeLists.txt)
+14
Просмотреть файл
@@ -28,6 +28,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-elfutils "Provides ElfUtils")
omnitrace_add_interface_library(omnitrace-perfetto "Enables Perfetto support")
omnitrace_add_interface_library(omnitrace-timemory "Provides timemory libraries")
omnitrace_add_interface_library(omnitrace-timemory-config
@@ -258,6 +259,19 @@ endif()
omnitrace_target_compile_definitions(
omnitrace-ompt INTERFACE OMNITRACE_USE_OMPT=$<BOOL:${OMNITRACE_USE_OMPT}>)
# ----------------------------------------------------------------------------------------#
#
# ElfUtils
#
# ----------------------------------------------------------------------------------------#
include(ElfUtils)
target_include_directories(omnitrace-elfutils SYSTEM INTERFACE ${ElfUtils_INCLUDE_DIRS})
target_compile_definitions(omnitrace-elfutils INTERFACE ${ElfUtils_DEFINITIONS})
target_link_directories(omnitrace-elfutils INTERFACE ${ElfUtils_LIBRARY_DIRS})
target_link_libraries(omnitrace-elfutils INTERFACE ${ElfUtils_LIBRARIES})
# ----------------------------------------------------------------------------------------#
#
# Dyninst
+8 -1
Просмотреть файл
@@ -18,7 +18,7 @@ set(CMAKE_CXX_CLANG_TIDY)
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME examples)
if(OMNITRACE_BUILD_DEBUG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3 -fno-omit-frame-pointer")
endif()
option(BUILD_SHARED_LIBS "Build dynamic libraries" ON)
@@ -36,6 +36,12 @@ if(OMNITRACE_INSTALL_EXAMPLES)
include(GNUInstallDirs)
endif()
set(OMNITRACE_EXAMPLE_ROOT_DIR
${CMAKE_CURRENT_LIST_DIR}
CACHE INTERNAL "")
# defines function for creating causal profiling exes
include(${CMAKE_CURRENT_LIST_DIR}/causal-helpers.cmake)
add_subdirectory(transpose)
add_subdirectory(parallel-overhead)
add_subdirectory(code-coverage)
@@ -46,3 +52,4 @@ add_subdirectory(python)
add_subdirectory(lulesh)
add_subdirectory(rccl)
add_subdirectory(rewrite-caller)
add_subdirectory(causal)
+104
Просмотреть файл
@@ -0,0 +1,104 @@
#
# function for
#
include_guard(DIRECTORY)
if(NOT TARGET omnitrace::omnitrace-user-library)
find_package(omnitrace REQUIRED COMPONENTS user)
endif()
if(NOT coz-profiler_FOUND)
find_package(coz-profiler QUIET)
endif()
if(NOT TARGET omni-causal-examples)
add_custom_target(omni-causal-examples)
endif()
function(omnitrace_causal_example_executable _NAME)
cmake_parse_arguments(
CAUSAL "" "" "SOURCES;DEFINITIONS;INCLUDE_DIRECTORIES;LINK_LIBRARIES" ${ARGN})
function(omnitrace_causal_example_interface _TARGET)
if(NOT TARGET ${_TARGET})
find_package(Threads REQUIRED)
add_library(${_TARGET} INTERFACE)
target_link_libraries(${_TARGET} INTERFACE Threads::Threads ${CMAKE_DL_LIBS})
endif()
endfunction()
omnitrace_causal_example_interface(omni-causal-example-lib-debug)
omnitrace_causal_example_interface(omni-causal-example-lib-no-debug)
target_compile_options(omni-causal-example-lib-debug
INTERFACE -g3 -fno-omit-frame-pointer)
target_compile_options(omni-causal-example-lib-no-debug INTERFACE -g0)
add_executable(${_NAME} ${CAUSAL_SOURCES})
target_compile_definitions(${_NAME} PRIVATE USE_COZ=0 USE_OMNI=0
${CAUSAL_DEFINITIONS})
target_include_directories(${_NAME} PRIVATE ${OMNITRACE_EXAMPLE_ROOT_DIR}/causal
${CAUSAL_INCLUDE_DIRECTORIES})
target_link_libraries(
${_NAME} PRIVATE ${CAUSAL_LINK_LIBRARIES} omnitrace::omnitrace-user-library
omni-causal-example-lib-debug)
add_executable(${_NAME}-omni ${CAUSAL_SOURCES})
target_compile_definitions(${_NAME}-omni PRIVATE USE_COZ=0 USE_OMNI=1
${CAUSAL_DEFINITIONS})
target_include_directories(${_NAME}-omni PRIVATE ${OMNITRACE_EXAMPLE_ROOT_DIR}/causal
${CAUSAL_INCLUDE_DIRECTORIES})
target_link_libraries(
${_NAME}-omni PRIVATE ${CAUSAL_LINK_LIBRARIES} omnitrace::omnitrace-user-library
omni-causal-example-lib-debug)
add_executable(${_NAME}-ndebug ${CAUSAL_SOURCES})
target_compile_definitions(${_NAME}-ndebug PRIVATE USE_COZ=0 USE_OMNI=0
${CAUSAL_DEFINITIONS})
target_include_directories(
${_NAME}-ndebug PRIVATE ${OMNITRACE_EXAMPLE_ROOT_DIR}/causal
${CAUSAL_INCLUDE_DIRECTORIES})
target_link_libraries(
${_NAME}-ndebug
PRIVATE ${CAUSAL_LINK_LIBRARIES} omnitrace::omnitrace-user-library
omni-causal-example-lib-no-debug)
add_executable(${_NAME}-omni-ndebug ${CAUSAL_SOURCES})
target_compile_definitions(${_NAME}-omni-ndebug PRIVATE USE_COZ=0 USE_OMNI=1
${CAUSAL_DEFINITIONS})
target_include_directories(
${_NAME}-omni-ndebug PRIVATE ${OMNITRACE_EXAMPLE_ROOT_DIR}/causal
${CAUSAL_INCLUDE_DIRECTORIES})
target_link_libraries(
${_NAME}-omni-ndebug
PRIVATE ${CAUSAL_LINK_LIBRARIES} omnitrace::omnitrace-user-library
omni-causal-example-lib-no-debug)
add_dependencies(omni-causal-examples ${_NAME} ${_NAME}-omni ${_NAME}-ndebug
${_NAME}-omni-ndebug)
if(coz-profiler_FOUND)
omnitrace_causal_example_interface(omni-causal-example-lib-coz)
target_compile_options(omni-causal-example-lib-coz
INTERFACE -g3 -gdwarf-3 -fno-omit-frame-pointer)
add_executable(${_NAME}-coz ${CAUSAL_SOURCES})
target_compile_definitions(${_NAME}-coz PRIVATE USE_COZ=1 USE_OMNI=0
${CAUSAL_DEFINITIONS})
target_include_directories(
${_NAME}-coz PRIVATE ${OMNITRACE_EXAMPLE_ROOT_DIR}/causal
${CAUSAL_INCLUDE_DIRECTORIES})
target_link_libraries(${_NAME}-coz PRIVATE ${CAUSAL_LINK_LIBRARIES}
omni-causal-example-lib-coz coz::coz)
add_dependencies(omni-causal-examples ${_NAME}-coz)
endif()
if(OMNITRACE_INSTALL_EXAMPLES)
install(
TARGETS ${_NAME} ${_NAME}-omni ${_NAME}-coz
DESTINATION bin
COMPONENT omnitrace-examples
OPTIONAL)
endif()
endfunction()
+42
Просмотреть файл
@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(omnitrace-causal-example LANGUAGES CXX)
if(OMNITRACE_DISABLE_EXAMPLES)
get_filename_component(_DIR ${CMAKE_CURRENT_LIST_DIR} NAME)
if(${PROJECT_NAME} IN_LIST OMNITRACE_DISABLE_EXAMPLES OR ${_DIR} IN_LIST
OMNITRACE_DISABLE_EXAMPLES)
return()
endif()
endif()
set(CMAKE_BUILD_TYPE "Release")
find_package(Threads REQUIRED)
if(NOT TARGET omnitrace::omnitrace-user-library)
find_package(omnitrace REQUIRED COMPONENTS user)
endif()
add_library(causal-interface-library INTERFACE)
target_compile_options(causal-interface-library INTERFACE -g3 -gdwarf-3
-fno-omit-frame-pointer)
target_link_libraries(causal-interface-library INTERFACE Threads::Threads
${CMAKE_DL_LIBS})
omnitrace_causal_example_executable(
"causal-both"
SOURCES causal.cpp impl.cpp
LINK_LIBRARIES causal-interface-library
DEFINITIONS USE_RNG=1 USE_CPU=1)
omnitrace_causal_example_executable(
"causal-rng"
SOURCES causal.cpp impl.cpp
LINK_LIBRARIES causal-interface-library
DEFINITIONS USE_RNG=1 USE_CPU=0)
omnitrace_causal_example_executable(
"causal-cpu"
SOURCES causal.cpp impl.cpp
LINK_LIBRARIES causal-interface-library
DEFINITIONS USE_RNG=0 USE_CPU=1)
+167
Просмотреть файл
@@ -0,0 +1,167 @@
#include "causal.hpp"
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <mutex>
#include <random>
#include <string>
#include <thread>
#include <unistd.h>
#include <vector>
using mutex_t = std::timed_mutex;
using auto_lock_t = std::unique_lock<mutex_t>;
using clock_type = std::chrono::high_resolution_clock;
using nanosec = std::chrono::nanoseconds;
namespace
{
std::chrono::duration<double, std::milli> t_ms;
std::chrono::duration<double, std::milli> slow_ms;
std::chrono::duration<double, std::milli> fast_ms;
template <typename... Args>
inline void
consume_variables(Args&&...)
{}
} // namespace
template <bool>
bool
rng_func_impl(int64_t n, uint64_t rseed);
template <bool>
bool
cpu_func_impl(int64_t n, int nloop);
void
rng_slow_func(int64_t n, uint64_t rseed) __attribute__((noinline));
void
rng_fast_func(int64_t n, uint64_t rseed) __attribute__((noinline));
void
cpu_slow_func(int64_t n, int nloop) __attribute__((noinline));
void
cpu_fast_func(int64_t n, int nloop) __attribute__((noinline));
#if USE_CPU > 0
# define CPU_SLOW_FUNC(...) cpu_slow_func(__VA_ARGS__)
# define CPU_FAST_FUNC(...) cpu_fast_func(__VA_ARGS__)
#else
# define CPU_SLOW_FUNC(...) consume_variables(__VA_ARGS__)
# define CPU_FAST_FUNC(...) consume_variables(__VA_ARGS__)
#endif
#if USE_RNG > 0
# define RNG_SLOW_FUNC(...) rng_slow_func(__VA_ARGS__)
# define RNG_FAST_FUNC(...) rng_fast_func(__VA_ARGS__)
#else
# define RNG_SLOW_FUNC(...) consume_variables(__VA_ARGS__)
# define RNG_FAST_FUNC(...) consume_variables(__VA_ARGS__)
#endif
int
main(int argc, char** argv)
{
uint64_t rseed = std::random_device{}();
int nitr = 200;
double frac = 70;
int64_t slow_val = 100000000L;
if(argc > 1) frac = std::stod(argv[1]);
if(argc > 2) nitr = std::stoi(argv[2]);
if(argc > 3) rseed = std::stoul(argv[3]);
if(argc > 4) slow_val = std::stol(argv[4]);
int64_t fast_val = (frac / 100.0) * slow_val;
double rfrac = (fast_val / static_cast<double>(slow_val));
if(argc > 5) fast_val = std::stol(argv[5]);
printf("\nIterations: %i, fraction: %6.2f, random seed: %lu :: slow = %zu, "
"fast = %zu, expected ratio = %6.2f\n",
nitr, frac, rseed, slow_val, fast_val, rfrac * 100.0);
auto _t = clock_type::now();
for(int i = 0; i < nitr; ++i)
{
if(i == 0 || i + 1 == nitr || i % (nitr / 5) == 0)
printf("executing iteration: %i\n", i);
//
auto&& _slow_func = [](auto _nsec, auto _seed, auto _nloop) {
auto _t = clock_type::now();
CPU_SLOW_FUNC(_nsec, _nloop);
RNG_SLOW_FUNC(_nsec / 5, _seed);
slow_ms += (clock_type::now() - _t);
};
//
auto&& _fast_func = [](auto _nsec, auto _seed, auto _nloop) {
auto _t = clock_type::now();
CPU_FAST_FUNC(_nsec, _nloop);
RNG_FAST_FUNC(_nsec / 5, _seed);
fast_ms += (clock_type::now() - _t);
};
//
CAUSAL_BEGIN("main_iteration");
//
auto _threads = std::vector<std::thread>{};
_threads.emplace_back(std::move(_slow_func), slow_val, rseed, 10000);
_threads.emplace_back(std::move(_fast_func), fast_val, rseed, 10000);
for(auto& itr : _threads)
itr.join();
CAUSAL_END("main_iteration");
CAUSAL_PROGRESS;
}
t_ms += clock_type::now() - _t;
auto rms = (fast_ms.count() / slow_ms.count());
printf("slow_func() took %10.3f ms\n", slow_ms.count());
printf("fast_func() took %10.3f ms\n", fast_ms.count());
printf("total is %18.3f ms\n", t_ms.count());
printf("ratio is %18.3f %s\n", 100.0 * rms, "%");
printf("rdiff is %18.3f %s\n", 100.0 * (rms - rfrac), "%");
}
//
//
//
void
rng_slow_func(int64_t n, uint64_t rseed)
{
// clang-format off
while(rng_func_impl<false>(n, rseed) != false) {}
// clang-format on
}
//
//
//
void
rng_fast_func(int64_t n, uint64_t rseed)
{
// clang-format off
while(rng_func_impl<true>(n, rseed) != true) {}
// clang-format on
}
//
//
//
void
cpu_slow_func(int64_t n, int nloop)
{
// clang-format off
while(cpu_func_impl<false>(n, nloop) != false) {}
// clang-format on
}
//
//
//
void
cpu_fast_func(int64_t n, int nloop)
{
// clang-format off
while(cpu_func_impl<true>(n, nloop) != true) {}
// clang-format on
}
+46
Просмотреть файл
@@ -0,0 +1,46 @@
// 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
#define CAUSAL_STR2(x) #x
#define CAUSAL_STR(x) CAUSAL_STR2(x)
#define CAUSAL_LABEL __FILE__ ":" CAUSAL_STR(__LINE__)
#if defined(USE_OMNI) && USE_OMNI > 0
# include <omnitrace/causal.h>
# define CAUSAL_PROGRESS OMNITRACE_CAUSAL_PROGRESS
# define CAUSAL_PROGRESS_NAMED(LABEL) OMNITRACE_CAUSAL_PROGRESS_NAMED(LABEL)
# define CAUSAL_BEGIN(LABEL) OMNITRACE_CAUSAL_BEGIN(LABEL)
# define CAUSAL_END(LABEL) OMNITRACE_CAUSAL_END(LABEL)
#elif defined(USE_COZ) && USE_COZ > 0
# include <coz.h>
# define CAUSAL_PROGRESS COZ_PROGRESS_NAMED(CAUSAL_LABEL)
# define CAUSAL_PROGRESS_NAMED(LABEL) COZ_PROGRESS_NAMED(LABEL)
# define CAUSAL_BEGIN(LABEL) COZ_BEGIN(LABEL)
# define CAUSAL_END(LABEL) COZ_END(LABEL)
#else
# define CAUSAL_PROGRESS
# define CAUSAL_PROGRESS_NAMED(LABEL)
# define CAUSAL_BEGIN(LABEL)
# define CAUSAL_END(LABEL)
#endif
+135
Просмотреть файл
@@ -0,0 +1,135 @@
// 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 <chrono>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <mutex>
#include <optional>
#include <random>
#include <stdexcept>
#include <string>
#include <sys/times.h>
#include <thread>
#include <unistd.h>
using mutex_t = std::timed_mutex;
using auto_lock_t = std::unique_lock<mutex_t>;
using clock_type = std::chrono::high_resolution_clock;
using nanosec = std::chrono::nanoseconds;
namespace
{
inline __attribute__((always_inline)) int64_t
clock_tick() noexcept;
template <typename Precision, typename Ret = int64_t>
inline __attribute__((always_inline)) Ret
clock_tick() noexcept;
template <typename Tp = int64_t, typename Precision = std::nano>
inline __attribute__((always_inline)) Tp
get_clock_now(clockid_t clock_id) noexcept;
template <typename Tp = int64_t, typename Precision = std::nano>
inline __attribute__((always_inline)) Tp
get_clock_cpu_now() noexcept;
} // namespace
//
// This implementation works well for Omnitrace
// while COZ makes poor predictions
//
template <bool V>
bool
rng_func_impl(int64_t n, uint64_t rseed)
{
int64_t _n = 0;
auto _rng = std::mt19937_64{ rseed };
auto _dist = std::uniform_int_distribution<int64_t>{ 1, 1 };
// clang-format off
while(_n < n) _n += _dist(_rng);
// clang-format on
return V;
}
template bool rng_func_impl<true>(int64_t, uint64_t);
template bool rng_func_impl<false>(int64_t, uint64_t);
//
// This implementation works well for COZ
// while Omnitrace makes poor predictions
//
template <bool V>
bool
cpu_func_impl(int64_t n, int nloop)
{
auto _t = clock_type::now();
auto _cpu_now = get_clock_cpu_now();
auto _cpu_end = _cpu_now + n;
// clang-format off
while(get_clock_cpu_now() < _cpu_end) { for(volatile int i = 0; i < nloop; ++i) {} }
// clang-format on
return V;
}
template bool
cpu_func_impl<true>(int64_t, int);
template bool
cpu_func_impl<false>(int64_t, int);
namespace
{
int64_t
clock_tick() noexcept
{
static int64_t _val = ::sysconf(_SC_CLK_TCK);
return _val;
}
template <typename Precision, typename Ret>
Ret
clock_tick() noexcept
{
return static_cast<Ret>(Precision::den) / static_cast<Ret>(clock_tick());
}
template <typename Tp, typename Precision>
Tp
get_clock_now(clockid_t clock_id) noexcept
{
constexpr Tp factor = Precision::den / static_cast<Tp>(std::nano::den);
struct timespec ts;
clock_gettime(clock_id, &ts);
return (ts.tv_sec * std::nano::den + ts.tv_nsec) * factor;
}
template <typename Tp, typename Precision>
Tp
get_clock_cpu_now() noexcept
{
return get_clock_now<Tp, Precision>(CLOCK_THREAD_CPUTIME_ID);
}
} // namespace
+7 -7
Просмотреть файл
@@ -12,6 +12,8 @@ if(OMNITRACE_DISABLE_EXAMPLES)
endif()
set(CMAKE_BUILD_TYPE "RelWithDebInfo")
string(REGEX REPLACE " -g(|[0-2]) " " -g3 " CMAKE_CXX_FLAGS_RELWITHDEBINFO
"${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
list(INSERT CMAKE_MODULE_PATH 0 ${PROJECT_SOURCE_DIR}/cmake/Modules)
@@ -63,9 +65,11 @@ endif()
file(GLOB headers ${PROJECT_SOURCE_DIR}/*.h ${PROJECT_SOURCE_DIR}/*.hxx)
file(GLOB sources ${PROJECT_SOURCE_DIR}/*.cc)
add_executable(lulesh ${sources} ${headers})
target_include_directories(lulesh PRIVATE ${PROJECT_SOURCE_DIR}/includes)
target_link_libraries(lulesh PRIVATE Kokkos::kokkos lulesh-mpi)
omnitrace_causal_example_executable(
"lulesh"
SOURCES ${sources} ${headers}
LINK_LIBRARIES Kokkos::kokkos lulesh-mpi
INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/includes)
if(OMNITRACE_INSTALL_EXAMPLES)
if(LULESH_BUILD_KOKKOS)
@@ -76,8 +80,4 @@ if(OMNITRACE_INSTALL_EXAMPLES)
set_target_properties(lulesh PROPERTIES INSTALL_RPATH
"\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}")
endif()
install(
TARGETS lulesh
DESTINATION bin
COMPONENT omnitrace-examples)
endif()
+9
Просмотреть файл
@@ -13,6 +13,8 @@
#include "lulesh.h"
#include "causal.hpp"
static Kokkos::View<Real_t*> buffer;
static size_t buffer_size;
static size_t buffer_offset;
@@ -46,6 +48,7 @@ TimeIncrement(Domain& domain)
if((domain.dtfixed() <= Real_t(0.0)) && (domain.cycle() != Int_t(0)))
{
// CAUSAL_BEGIN("TimeIncrement_Iteration")
Real_t ratio;
Real_t olddt = domain.deltatime();
@@ -86,6 +89,8 @@ TimeIncrement(Domain& domain)
newdt = domain.dtmax();
}
domain.deltatime() = newdt;
CAUSAL_PROGRESS_NAMED("TimeIncrement_Iteration");
// CAUSAL_END("TimeIncrement_Iteration")
}
if((targetdt > domain.deltatime()) &&
@@ -2241,12 +2246,15 @@ main(int argc, char* argv[])
while((locDom.time() < locDom.stoptime()) && (locDom.cycle() < opts.its))
{
Kokkos::Tools::startSection(_time_incrp);
//CAUSAL_BEGIN("Iteration")
TimeIncrement(locDom);
Kokkos::Tools::stopSection(_time_incrp);
Kokkos::Tools::startSection(_leap_frogp);
LagrangeLeapFrog(locDom);
Kokkos::Tools::stopSection(_leap_frogp);
CAUSAL_PROGRESS_NAMED("Iteration")
//CAUSAL_END("Iteration")
if((opts.showProg != 0) && (opts.quiet == 0) && (myRank == 0))
{
@@ -2254,6 +2262,7 @@ main(int argc, char* argv[])
double(locDom.time()), double(locDom.deltatime()));
}
Kokkos::Tools::markEvent("completed_timestep");
CAUSAL_PROGRESS
}
Kokkos::Tools::destroyProfileSection(_time_incrp);
+8 -2
Просмотреть файл
@@ -13,11 +13,17 @@ endif()
set(CMAKE_BUILD_TYPE "Release")
find_package(Threads REQUIRED)
add_library(parallel-overhead-compile-options INTERFACE)
target_compile_options(parallel-overhead-compile-options INTERFACE -g)
add_executable(parallel-overhead parallel-overhead.cpp)
target_link_libraries(parallel-overhead PRIVATE Threads::Threads)
target_link_libraries(parallel-overhead PRIVATE Threads::Threads
parallel-overhead-compile-options)
add_executable(parallel-overhead-locks parallel-overhead.cpp)
target_link_libraries(parallel-overhead-locks PRIVATE Threads::Threads)
target_link_libraries(parallel-overhead-locks PRIVATE Threads::Threads
parallel-overhead-compile-options)
target_compile_definitions(parallel-overhead-locks PRIVATE USE_LOCKS=1)
if(OMNITRACE_INSTALL_EXAMPLES)
Submodule projects/rocprofiler-systems/external/dyninst updated: 5d6b3711d9...e4d2eb36ae
+1 -1
Submodule projects/rocprofiler-systems/external/elfio updated: 637221d267...d00cc32f8b
Submodule projects/rocprofiler-systems/external/perfetto updated: 7e8d6801db...b8da070959
Submodule projects/rocprofiler-systems/external/timemory updated: 04a8679c09...64bf1067a4
+1
Просмотреть файл
@@ -16,6 +16,7 @@ endif()
# executables
add_subdirectory(omnitrace-avail)
add_subdirectory(omnitrace-critical-trace)
add_subdirectory(omnitrace-causal)
add_subdirectory(omnitrace-sample)
add_subdirectory(omnitrace)
+5
Просмотреть файл
@@ -40,6 +40,7 @@
#include <timemory/components/skeletons.hpp>
#include <timemory/mpl/types.hpp>
#include <timemory/timemory.hpp>
#include <timemory/unwind/bfd.hpp>
#include <timemory/utility/types.hpp>
#include <timemory/utility/utility.hpp>
@@ -113,6 +114,7 @@ int gpu_count = 0;
int
main(int argc, char** argv)
{
tim::unwind::set_bfd_verbose(3);
tim::set_env("OMNITRACE_INIT_TOOLING", "OFF", 1);
omnitrace_init_library();
@@ -172,6 +174,9 @@ main(int argc, char** argv)
parser_t parser("omnitrace-avail");
parser.enable_help();
parser.enable_version("omnitrace-avail", "v" OMNITRACE_VERSION_STRING,
OMNITRACE_GIT_DESCRIBE, OMNITRACE_GIT_REVISION);
parser.set_help_width(40);
parser.add_argument({ "--debug" }, "Enable debug messages")
.max_count(1)
+27
Просмотреть файл
@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------#
#
# omnitrace-causal target
#
# ------------------------------------------------------------------------------#
add_executable(
omnitrace-causal
${CMAKE_CURRENT_LIST_DIR}/omnitrace-causal.cpp
${CMAKE_CURRENT_LIST_DIR}/omnitrace-causal.hpp ${CMAKE_CURRENT_LIST_DIR}/impl.cpp)
target_compile_definitions(omnitrace-causal PRIVATE TIMEMORY_CMAKE=1)
target_include_directories(omnitrace-causal PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(
omnitrace-causal
PRIVATE omnitrace::omnitrace-compile-definitions omnitrace::omnitrace-headers
omnitrace::omnitrace-common-library)
set_target_properties(
omnitrace-causal PROPERTIES BUILD_RPATH "\$ORIGIN:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
INSTALL_RPATH "${OMNITRACE_EXE_INSTALL_RPATH}")
omnitrace_strip_target(omnitrace-causal)
install(
TARGETS omnitrace-causal
DESTINATION ${CMAKE_INSTALL_BINDIR}
OPTIONAL)
+985
Просмотреть файл
@@ -0,0 +1,985 @@
// 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 "omnitrace-causal.hpp"
#include "common/defines.h"
#include "common/delimit.hpp"
#include "common/environment.hpp"
#include "common/join.hpp"
#include "common/setup.hpp"
#include <regex>
#include <timemory/environment.hpp>
#include <timemory/log/color.hpp>
#include <timemory/utility/argparse.hpp>
#include <timemory/utility/console.hpp>
#include <timemory/utility/filepath.hpp>
#include <timemory/utility/join.hpp>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <sys/wait.h>
#include <thread>
#include <unistd.h>
#include <vector>
namespace color = ::tim::log::color;
namespace filepath = ::tim::filepath;
namespace console = ::tim::utility::console;
namespace argparse = ::tim::argparse;
using namespace timemory::join;
using tim::get_env;
using tim::log::colorized;
using tim::log::stream;
namespace std
{
std::string
to_string(bool _v)
{
return (_v) ? "true" : "false";
}
} // namespace std
namespace
{
int verbose = 0;
auto updated_envs = std::set<std::string_view>{};
auto original_envs = std::set<std::string>{};
auto child_pids = std::set<pid_t>{};
auto launcher = std::string{};
inline signal_handler&
get_signal_handler(int _sig)
{
static auto _v = std::unordered_map<int, signal_handler>{};
auto itr = _v.emplace(_sig, signal_handler{});
return itr.first->second;
}
void
create_signal_handler(int sig, signal_handler& sh, void (*func)(int))
{
if(sig < 1) return;
sh.m_custom_sigaction.sa_handler = func;
sigemptyset(&sh.m_custom_sigaction.sa_mask);
sh.m_custom_sigaction.sa_flags = SA_RESTART;
if(sigaction(sig, &sh.m_custom_sigaction, &sh.m_original_sigaction) == -1)
{
std::cerr << "Failed to create signal handler for " << sig << std::endl;
}
}
void
forward_signal(int sig)
{
for(auto itr : child_pids)
{
TIMEMORY_PRINTF_WARNING(stderr, "Killing pid=%i with signal %i...\n", itr, sig);
kill(itr, sig);
diagnose_status(itr, wait_pid(itr));
}
signal(sig, SIG_DFL);
kill(getpid(), sig);
}
} // namespace
int
get_verbose()
{
verbose = get_env("OMNITRACE_CAUSAL_VERBOSE",
get_env<int>("OMNITRACE_VERBOSE", verbose, false));
auto _debug =
get_env("OMNITRACE_CAUSAL_DEBUG", get_env<bool>("OMNITRACE_DEBUG", false, false));
if(_debug) verbose += 8;
return verbose;
}
void
forward_signals(const std::set<int>& _signals)
{
for(auto itr : _signals)
create_signal_handler(itr, get_signal_handler(itr), &forward_signal);
}
void
add_child_pid(pid_t _v)
{
child_pids.emplace(_v);
}
void
remove_child_pid(pid_t _v)
{
child_pids.erase(_v);
}
int
wait_pid(pid_t _pid, int _opts)
{
int _status = 0;
pid_t _pid_v = -1;
_opts |= WUNTRACED;
do
{
if((_opts & WNOHANG) > 0)
std::this_thread::sleep_for(std::chrono::milliseconds{ 100 });
_pid_v = waitpid(_pid, &_status, _opts);
} while(_pid <= 0);
return _status;
}
int
diagnose_status(pid_t _pid, int _status)
{
auto _verbose = get_verbose();
if(_verbose >= 3)
{
fflush(stderr);
fflush(stdout);
std::cout << std::flush;
std::cerr << std::flush;
}
bool _normal_exit = (WIFEXITED(_status) > 0);
bool _unhandled_signal = (WIFSIGNALED(_status) > 0);
bool _core_dump = (WCOREDUMP(_status) > 0);
bool _stopped = (WIFSTOPPED(_status) > 0);
int _exit_status = WEXITSTATUS(_status);
int _stop_signal = (_stopped) ? WSTOPSIG(_status) : 0;
int _ec = (_unhandled_signal) ? WTERMSIG(_status) : 0;
if(_verbose >= 4)
{
TIMEMORY_PRINTF_INFO(
stderr,
"diagnosing status for process %i :: status: %i... normal exit: %s, "
"unhandled signal: %s, core dump: %s, stopped: %s, exit status: %i, stop "
"signal: %i, exit code: %i\n",
_pid, _status, std::to_string(_normal_exit).c_str(),
std::to_string(_unhandled_signal).c_str(), std::to_string(_core_dump).c_str(),
std::to_string(_stopped).c_str(), _exit_status, _stop_signal, _ec);
}
else if(_verbose >= 3)
{
TIMEMORY_PRINTF_INFO(stderr,
"diagnosing status for process %i :: status: %i ...\n", _pid,
_status);
}
if(!_normal_exit)
{
if(_ec == 0) _ec = EXIT_FAILURE;
if(_verbose >= 0)
{
TIMEMORY_PRINTF_FATAL(
stderr, "process %i terminated abnormally. exit code: %i\n", _pid, _ec);
}
}
if(_stopped)
{
if(_verbose >= 0)
{
TIMEMORY_PRINTF_FATAL(stderr,
"process %i stopped with signal %i. exit code: %i\n",
_pid, _stop_signal, _ec);
}
}
if(_core_dump)
{
if(_verbose >= 0)
{
TIMEMORY_PRINTF_FATAL(
stderr, "process %i terminated and produced a core dump. exit code: %i\n",
_pid, _ec);
}
}
if(_unhandled_signal)
{
if(_verbose >= 0)
{
TIMEMORY_PRINTF_FATAL(stderr,
"process %i terminated because it received a signal "
"(%i) that was not handled. exit code: %i\n",
_pid, _ec, _ec);
}
}
if(!_normal_exit && _exit_status > 0)
{
if(_verbose >= 0)
{
if(_exit_status == 127)
{
TIMEMORY_PRINTF_FATAL(
stderr, "execv in process %i failed. exit code: %i\n", _pid, _ec);
}
else
{
TIMEMORY_PRINTF_FATAL(
stderr,
"process %i terminated with a non-zero status. exit code: %i\n", _pid,
_ec);
}
}
}
return _ec;
}
std::string
get_realpath(const std::string& _v)
{
auto* _tmp = realpath(_v.c_str(), nullptr);
auto _ret = std::string{ _tmp };
free(_tmp);
return _ret;
}
void
print_command(const std::vector<char*>& _argv, std::string_view _prefix)
{
if(verbose >= 1)
stream(std::cout, color::info())
<< _prefix << "Executing '" << join(array_config{ " " }, _argv) << "'...\n";
std::cerr << color::end() << std::flush;
}
std::vector<char*>
get_initial_environment()
{
std::vector<char*> _env;
if(environ != nullptr)
{
int idx = 0;
while(environ[idx] != nullptr)
{
auto* _v = environ[idx++];
original_envs.emplace(_v);
_env.emplace_back(strdup(_v));
}
}
update_env(_env, "OMNITRACE_MODE", "causal");
update_env(_env, "OMNITRACE_USE_CAUSAL", true);
update_env(_env, "OMNITRACE_USE_SAMPLING", false);
update_env(_env, "OMNITRACE_USE_PERFETTO", false);
update_env(_env, "OMNITRACE_USE_TIMEMORY", false);
update_env(_env, "OMNITRACE_USE_PROCESS_SAMPLING", false);
update_env(_env, "OMNITRACE_CRITICAL_TRACE", false);
return _env;
}
void
prepare_command_for_run(char* _exe, std::vector<char*>& _argv)
{
if(!launcher.empty())
{
bool _injected = false;
auto _new_argv = std::vector<char*>{};
for(auto* itr : _argv)
{
if(!_injected && std::regex_search(itr, std::regex{ launcher }))
{
_new_argv.emplace_back(_exe);
_new_argv.emplace_back(strdup("--"));
_injected = true;
}
_new_argv.emplace_back(itr);
}
if(!_injected)
{
throw std::runtime_error(
join("", "omnitrace-causal was unable to match \"", launcher,
"\" to any arguments on the command line: \"",
join(array_config{ " ", "", "" }, _argv), "\""));
}
std::swap(_argv, _new_argv);
}
}
void
prepare_environment_for_run(std::vector<char*>& _env)
{
if(launcher.empty())
{
update_env(_env, "LD_PRELOAD",
get_realpath(get_internal_libpath("libomnitrace-dl.so")), true);
}
}
std::string
get_internal_libpath(const std::string& _lib)
{
auto _exe = std::string_view{ realpath("/proc/self/exe", nullptr) };
auto _pos = _exe.find_last_of('/');
auto _dir = std::string{ "./" };
if(_pos != std::string_view::npos) _dir = _exe.substr(0, _pos);
return omnitrace::common::join("/", _dir, "..", "lib", _lib);
}
void
print_updated_environment(std::vector<char*> _env, std::string_view _prefix)
{
if(get_verbose() < 0) return;
std::sort(_env.begin(), _env.end(), [](auto* _lhs, auto* _rhs) {
if(!_lhs) return false;
if(!_rhs) return true;
return std::string_view{ _lhs } < std::string_view{ _rhs };
});
std::vector<std::string_view> _updates = {};
std::vector<std::string_view> _general = {};
for(auto* itr : _env)
{
if(itr == nullptr) continue;
auto _is_omni = (std::string_view{ itr }.find("OMNITRACE") == 0);
auto _updated = false;
for(const auto& vitr : updated_envs)
{
if(std::string_view{ itr }.find(vitr) == 0)
{
_updated = true;
break;
}
}
if(_updated)
_updates.emplace_back(itr);
else if(verbose >= 1 && _is_omni)
_general.emplace_back(itr);
}
if(_general.size() + _updates.size() == 0 || verbose < 0) return;
std::cerr << std::endl;
for(auto& itr : _general)
stream(std::cerr, color::source()) << _prefix << itr << "\n";
for(auto& itr : _updates)
stream(std::cerr, color::source()) << _prefix << itr << "\n";
std::cerr << color::end() << std::flush;
}
template <typename Tp>
void
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
bool _append, std::string_view _join_delim)
{
updated_envs.emplace(_env_var);
auto _key = join("", _env_var, "=");
for(auto& itr : _environ)
{
if(!itr) continue;
if(std::string_view{ itr }.find(_key) == 0)
{
if(_append)
{
if(std::string_view{ itr }.find(join("", _env_val)) ==
std::string_view::npos)
{
auto _val = std::string{ itr }.substr(_key.length());
free(itr);
itr = strdup(
join('=', _env_var, join(_join_delim, _env_val, _val)).c_str());
}
}
else
{
free(itr);
itr = strdup(omnitrace::common::join('=', _env_var, _env_val).c_str());
}
return;
}
}
_environ.emplace_back(
strdup(omnitrace::common::join('=', _env_var, _env_val).c_str()));
}
template <typename Tp>
void
add_default_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val)
{
auto _key = join("", _env_var, "=");
for(auto& itr : _environ)
{
if(!itr) continue;
if(std::string_view{ itr }.find(_key) == 0) return;
}
updated_envs.emplace(_env_var);
_environ.emplace_back(
strdup(omnitrace::common::join('=', _env_var, _env_val).c_str()));
}
void
remove_env(std::vector<char*>& _environ, std::string_view _env_var)
{
auto _key = join("", _env_var, "=");
auto _match = [&_key](auto itr) { return std::string_view{ itr }.find(_key) == 0; };
_environ.erase(std::remove_if(_environ.begin(), _environ.end(), _match),
_environ.end());
for(const auto& itr : original_envs)
{
if(std::string_view{ itr }.find(_key) == 0)
_environ.emplace_back(strdup(itr.c_str()));
}
}
std::vector<char*>
parse_args(int argc, char** argv, std::vector<char*>& _env,
std::vector<std::map<std::string_view, std::string>>& _causal_envs)
{
using parser_t = argparse::argument_parser;
using parser_err_t = typename parser_t::result_type;
auto help_check = [](parser_t& p, int _argc, char** _argv) {
std::set<std::string> help_args = { "-h", "--help", "-?" };
return (p.exists("help") || _argc == 1 ||
(_argc > 1 && help_args.find(_argv[1]) != help_args.end()));
};
auto _pec = EXIT_SUCCESS;
auto help_action = [&_pec, argc, argv](parser_t& p) {
if(_pec != EXIT_SUCCESS)
{
std::stringstream msg;
msg << "Error in command:";
for(int i = 0; i < argc; ++i)
msg << " " << argv[i];
msg << "\n\n";
stream(std::cerr, color::fatal()) << msg.str();
std::cerr << std::flush;
}
p.print_help();
exit(_pec);
};
const auto* _desc = R"desc(
Causal profiling usually requires multiple runs to reliably resolve the speedup estimates.
This executable is designed to streamline that process.
For example (assume all commands end with '-- <exe> <args>'):
omnitrace-causal -n 5 -- <exe> # runs <exe> 5x with causal profiling enabled
omnitrace-causal -s 0 5,10,15,20 # runs <exe> 2x with virtual speedups:
# - 0
# - randomly selected from 5, 10, 15, and 20
omnitrace-causal -F func_A func_B func_(A|B) # runs <exe> 3x with the function scope limited to:
# 1. func_A
# 2. func_B
# 3. func_A or func_B
General tips:
- Insert progress points at hotspots in your code or use omnitrace's runtime instrumentation
- Note: binary rewrite will produce a incompatible new binary
- Collect a flat profile via sampling
- E.g., omnitrace-sample -F -- <exe> <args>
- Inspect sampling_wall_clock.txt and sampling_cpu_clock.txt for functions to target
- Run omnitrace-causal in "function" mode first (does not require debug info)
- Run omnitrace-causal in "line" mode when you are targeting one function (requires debug info)
- Preferably, use predictions from the "function" mode to determine which function to target
- Limit the virtual speedups to a smaller pool, e.g., 0,5,10,25,50, to get reliable predictions quicker
- Make use of the binary, source, and function scope to limit the functions/lines selected for experiments
- Note: source scope requires debug info
)desc";
auto parser = parser_t{ basename(argv[0]), _desc };
parser.on_error([](parser_t&, const parser_err_t& _err) {
stream(std::cerr, color::fatal()) << _err << "\n";
exit(EXIT_FAILURE);
});
auto _add_separator = [&](std::string _v, const std::string& _desc) {
parser.add_argument({ "" }, "");
parser
.add_argument({ join("", "[", _v, "]") },
(_desc.empty()) ? _desc : join({ "", "(", ")" }, _desc))
.color(color::info());
parser.add_argument({ "" }, "");
};
parser.enable_help();
parser.enable_version("omnitrace-causal", "v" OMNITRACE_VERSION_STRING,
OMNITRACE_GIT_DESCRIBE, OMNITRACE_GIT_REVISION);
auto _cols = std::get<0>(console::get_columns());
if(_cols > parser.get_help_width() + 8)
parser.set_description_width(
std::min<int>(_cols - parser.get_help_width() - 8, 120));
_add_separator("DEBUG OPTIONS", "");
parser.add_argument({ "--monochrome" }, "Disable colorized output")
.max_count(1)
.dtype("bool")
.action([&](parser_t& p) {
auto _colorized = !p.get<bool>("monochrome");
colorized() = _colorized;
p.set_use_color(_colorized);
update_env(_env, "OMNITRACE_COLORIZED_LOG", (_colorized) ? "1" : "0");
update_env(_env, "COLORIZED_LOG", (_colorized) ? "1" : "0");
});
parser.add_argument({ "--debug" }, "Debug output")
.max_count(1)
.action([&](parser_t& p) {
update_env(_env, "OMNITRACE_DEBUG", p.get<bool>("debug"));
});
parser.add_argument({ "-v", "--verbose" }, "Verbose output")
.count(1)
.action([&](parser_t& p) {
auto _v = p.get<int>("verbose");
verbose = _v;
update_env(_env, "OMNITRACE_VERBOSE", _v);
});
std::string _config_file = {};
std::string _config_folder = "omnitrace-causal-config";
bool _generate_configs = false;
bool _add_defaults = true;
_add_separator("GENERAL OPTIONS", "");
parser.add_argument({ "-c", "--config" }, "Base configuration file")
.min_count(0)
.dtype("filepath")
.action([&](parser_t& p) {
_config_file =
join(array_config{ ":" }, p.get<std::vector<std::string>>("config"));
});
parser
.add_argument(
{ "-l", "--launcher" },
"When running MPI jobs, omnitrace-causal needs to be *before* the executable "
"which launches the MPI processes (i.e. before `mpirun`, `srun`, etc.). Pass "
"the name of the target executable (or a regex for matching to the name of "
"the target) for causal profiling, e.g., `omnitrace-causal -l foo -- mpirun "
"-n 4 foo`. This ensures that the omnitrace library is LD_PRELOADed on the "
"proper target")
.count(1)
.dtype("executable")
.action([&](parser_t& p) { launcher = p.get<std::string>("launcher"); });
parser
.add_argument({ "-g", "--generate-configs" },
"Generate config files instead of passing environment variables "
"directly. If no arguments are provided, the config files will be "
"placed in ${PWD}/omnitrace-causal-config folder")
.min_count(0)
.max_count(1)
.dtype("folder")
.action([&](parser_t& p) {
_generate_configs = true;
auto _dir = p.get<std::string>("generate-configs");
if(!_dir.empty()) _config_folder = std::move(_dir);
if(!filepath::exists(_config_folder)) filepath::makedir(_config_folder);
});
parser
.add_argument({ "--no-defaults" },
"Do not activate default features which are recommended for causal "
"profiling. For example: PID-tagging of output files and "
"timestamped subdirectories are disabled by default. Kokkos tools "
"support is added by default (OMNITRACE_USE_KOKKOSP=ON) because, "
"for Kokkos applications, the Kokkos-Tools callbacks are used for "
"progress points. Activation of OpenMP tools support is similar")
.min_count(0)
.max_count(1)
.dtype("bool")
.action([&](parser_t& p) { _add_defaults = !p.get<bool>("no-defaults"); });
_add_separator("CAUSAL PROFILING OPTIONS (General)",
"These settings will be applied to all causal profiling runs");
parser.add_argument({ "-m", "--mode" }, "Causal profiling mode")
.count(1)
.dtype("string")
.choices({ "function", "line" })
.choice_alias("function", { "func" })
.action([&](parser_t& p) {
update_env(_env, "OMNITRACE_CAUSAL_MODE", p.get<std::string>("mode"));
});
parser
.add_argument({ "-o", "--output-name" },
"Output filename of causal profiling data w/o extension")
.min_count(1)
.dtype("filename")
.action([&](parser_t& p) {
update_env(_env, "OMNITRACE_CAUSAL_FILE", p.get<std::string>("output-name"));
});
bool _reset = false;
parser
.add_argument({ "-r", "--reset" },
"Overwrite any existing experiment results during the first run")
.max_count(1)
.dtype("bool")
.action([&](parser_t& p) { _reset = p.get<bool>("reset"); });
parser
.add_argument({ "-e", "--end-to-end" },
"Single causal experiment for the entire application runtime")
.max_count(1)
.dtype("bool")
.action([&](parser_t& p) {
update_env(_env, "OMNITRACE_CAUSAL_END_TO_END", p.get<bool>("end-to-end"));
});
parser
.add_argument({ "-w", "--wait" },
"Set the wait time (i.e. delay) before starting the first causal "
"experiment (in seconds)")
.count(1)
.dtype("seconds")
.action([&](parser_t& p) {
update_env(_env, "OMNITRACE_CAUSAL_DELAY", p.get<double>("wait"));
});
parser
.add_argument(
{ "-d", "--duration" },
"Set the length of time (in seconds) to perform causal experimentationafter "
"the first experiment is started. Once this amount of time has elapsed, no "
"more causal experiments will be started but any currently running "
"experiment will be allowed to finish.")
.count(1)
.dtype("seconds")
.action([&](parser_t& p) {
update_env(_env, "OMNITRACE_CAUSAL_DURATION", p.get<double>("duration"));
});
int64_t _niterations = 1;
auto _virtual_speedups = std::vector<std::string>{};
auto _function_scopes = std::vector<std::string>{};
auto _binary_scopes = std::vector<std::string>{};
auto _source_scopes = std::vector<std::string>{};
auto _function_excludes = std::vector<std::string>{};
auto _binary_excludes = std::vector<std::string>{};
auto _source_excludes = std::vector<std::string>{};
parser
.add_argument({ "-n", "--iterations" },
"Number of times to repeat the combination of run configurations")
.count(1)
.dtype("int")
.action([&](parser_t& p) { _niterations = p.get<int64_t>("iterations"); });
_add_separator(
"CAUSAL PROFILING OPTIONS (Combinatorial)",
"Each individual argument to these options will multiply the number runs by the "
"number of arguments and the number of iterations. E.g. -n 2 -B \"MAIN\" -F "
"\"foo\" \"bar\" will produce 4 runs: 2 iterations x 1 binary scope x 2 function "
"scopes (MAIN+foo, MAIN+bar, MAIN+foo, MAIN+bar)");
parser
.add_argument({ "-s", "--speedups" },
"Pool of virtual speedups to sample from during experimentation. "
"Each space designates a group and multiple speedups can be "
"grouped together by commas, e.g. -s 0 0,10,20-50 is two groups: "
"group #1 is '0' and group #2 is '0 10 20 25 30 35 40 45 50'")
.min_count(0)
.max_count(-1)
.dtype("integers")
.action([&](parser_t& p) {
_virtual_speedups = p.get<std::vector<std::string>>("speedups");
});
parser
.add_argument({ "-B", "--binary-scope" },
"Restricts causal experiments to the binaries matching the list of "
"regular expressions. Each space designates a group and multiple "
"scopes can be grouped together with a semi-colon")
.min_count(0)
.max_count(-1)
.dtype("integers")
.action([&](parser_t& p) {
_binary_scopes = p.get<std::vector<std::string>>("binary-scope");
});
parser
.add_argument({ "-S", "--source-scope" },
"Restricts causal experiments to the source files or source file + "
"lineno pairs (i.e. <file> or <file>:<line>) matching the list of "
"regular expressions. Each space designates a group and multiple "
"scopes can be grouped together with a semi-colon")
.min_count(0)
.max_count(-1)
.dtype("integers")
.action([&](parser_t& p) {
_source_scopes = p.get<std::vector<std::string>>("source-scope");
});
parser
.add_argument(
{ "-F", "--function-scope" },
"Restricts causal experiments to the functions matching the list of "
"regular expressions. Each space designates a group and multiple "
"scopes can be grouped together with a semi-colon")
.min_count(0)
.max_count(-1)
.dtype("regex-list")
.action([&](parser_t& p) {
_function_scopes = p.get<std::vector<std::string>>("function-scope");
});
parser
.add_argument(
{ "-BE", "--binary-exclude" },
"Excludes causal experiments from being performed on the binaries matching "
"the list of regular expressions. Each space designates a group and multiple "
"excludes can be grouped together with a semi-colon")
.min_count(0)
.max_count(-1)
.dtype("integers")
.action([&](parser_t& p) {
_binary_excludes = p.get<std::vector<std::string>>("binary-exclude");
});
parser
.add_argument(
{ "-SE", "--source-exclude" },
"Excludes causal experiments from being performed on the code from the "
"source files or source file + lineno pair (i.e. <file> or <file>:<line>) "
"matching the list of regular expressions. Each space designates a group and "
"multiple excludes can be grouped together with a semi-colon")
.min_count(0)
.max_count(-1)
.dtype("integers")
.action([&](parser_t& p) {
_source_excludes = p.get<std::vector<std::string>>("source-exclude");
});
parser
.add_argument(
{ "-FE", "--function-exclude" },
"Excludes causal experiments from being performed on the functions matching "
"the list of regular expressions. Each space designates a group and multiple "
"excludes can be grouped together with a semi-colon")
.min_count(0)
.max_count(-1)
.dtype("regex-list")
.action([&](parser_t& p) {
_function_excludes = p.get<std::vector<std::string>>("function-exclude");
});
#if OMNITRACE_HIP_VERSION > 0 && OMNITRACE_HIP_VERSION < 50300
update_env(_env, "HSA_ENABLE_INTERRUPT", 0);
#endif
auto _inpv = std::vector<char*>{};
auto _outv = std::vector<char*>{};
bool _hash = false;
for(int i = 0; i < argc; ++i)
{
if(_hash)
{
_outv.emplace_back(argv[i]);
}
else if(std::string_view{ argv[i] } == "--")
{
_hash = true;
}
else
{
_inpv.emplace_back(argv[i]);
}
}
auto _cerr = parser.parse_args(_inpv.size(), _inpv.data());
if(help_check(parser, argc, argv))
help_action(parser);
else if(_cerr)
throw std::runtime_error(_cerr.what());
if(_niterations < 1) _niterations = 1;
auto _get_size = [](const auto& _v) { return std::max<size_t>(_v.size(), 1); };
auto _causal_envs_tmp = std::vector<std::map<std::string_view, std::string>>{};
auto _fill = [&_causal_envs_tmp](std::string_view _env_var, const auto& _data,
bool _quote) {
if(_data.empty()) return;
if(_causal_envs_tmp.empty()) _causal_envs_tmp.emplace_back();
auto _tmp = _causal_envs_tmp;
_causal_envs_tmp.clear();
_causal_envs_tmp.reserve(_data.size() * _tmp.size());
for(auto ditr : _data)
{
if(_quote)
{
ditr.insert(0, "\"");
ditr += "\"";
}
// duplicate the env, add the env variable, emplace back
for(auto itr : _tmp)
{
itr[_env_var] = ditr;
_causal_envs_tmp.emplace_back(itr);
}
}
};
if(_add_defaults)
{
add_default_env(_env, "OMNITRACE_TIME_OUTPUT", false);
add_default_env(_env, "OMNITRACE_USE_PID", false);
add_default_env(_env, "OMNITRACE_USE_KOKKOSP", true);
#if defined(OMNITRACE_USE_OMPT) && OMNITRACE_USE_OMPT > 0
add_default_env(_env, "OMNITRACE_USE_OMPT", true);
#endif
#if(defined(OMNITRACE_USE_MPI) && OMNITRACE_USE_MPI > 0) || \
(defined(OMNITRACE_USE_MPI_HEADERS) && OMNITRACE_USE_MPI_HEADERS > 0)
add_default_env(_env, "OMNITRACE_USE_MPIP", true);
#endif
#if defined(OMNITRACE_USE_ROCTRACER) && OMNITRACE_USE_ROCTRACER > 0
add_default_env(_env, "OMNITRACE_ROCTRACER_HIP_API", true);
add_default_env(_env, "OMNITRACE_ROCTRACER_HSA_API", true);
#endif
#if defined(OMNITRACE_USE_RCCL) && OMNITRACE_USE_RCCL > 0
add_default_env(_env, "OMNITRACE_USE_RCCLP", true);
#endif
}
_fill("OMNITRACE_CAUSAL_BINARY_EXCLUDE", _binary_excludes, _generate_configs);
_fill("OMNITRACE_CAUSAL_SOURCE_EXCLUDE", _source_excludes, _generate_configs);
_fill("OMNITRACE_CAUSAL_FUNCTION_EXCLUDE", _function_excludes, _generate_configs);
_fill("OMNITRACE_CAUSAL_BINARY_SCOPE", _binary_scopes, _generate_configs);
_fill("OMNITRACE_CAUSAL_SOURCE_SCOPE", _source_scopes, _generate_configs);
_fill("OMNITRACE_CAUSAL_FUNCTION_SCOPE", _function_scopes, _generate_configs);
_fill("OMNITRACE_CAUSAL_FIXED_SPEEDUP", _virtual_speedups, false);
// make sure at least one env exists
if(_causal_envs_tmp.empty()) _causal_envs_tmp.emplace_back();
// duplicate for the number of iterations
_causal_envs.clear();
_causal_envs.reserve(_niterations * _causal_envs_tmp.size());
for(int64_t i = 0; i < _niterations; ++i)
{
for(const auto& itr : _causal_envs_tmp)
_causal_envs.emplace_back(itr);
}
if(_generate_configs)
{
auto _is_omni_cfg = [](std::string_view itr) {
return (itr.find("OMNITRACE") == 0 && itr.find("OMNITRACE_MODE") != 0 &&
itr.find("OMNITRACE_CONFIG_FILE") != 0 &&
itr.find('=') < itr.length());
};
auto _omni_env = std::map<std::string, std::string>{};
for(auto* itr : _env)
{
if(_is_omni_cfg(itr))
{
auto _env_var = std::string{ itr };
auto _pos = _env_var.find('=');
auto _env_val = _env_var.substr(_pos + 1);
_env_var = _env_var.substr(0, _pos);
_omni_env.emplace(_env_var, _env_val);
}
}
_env.erase(std::remove_if(_env.begin(), _env.end(), _is_omni_cfg), _env.end());
_causal_envs_tmp = std::move(_causal_envs);
_causal_envs.clear();
auto _write_config =
[_omni_env](std::ostream& _os,
const std::map<std::string_view, std::string>& _data) {
size_t _width = 0;
for(const auto& itr : _omni_env)
_width = std::max(_width, itr.first.length());
for(const auto& itr : _data)
_width = std::max(_width, itr.first.length());
_os << "# omnitrace common settings\n";
for(const auto& itr : _omni_env)
_os << std::setw(_width + 1) << std::left << itr.first << " = "
<< itr.second << "\n";
_os << "\n# omnitrace causal settings\n";
for(const auto& itr : _data)
_os << std::setw(_width + 1) << std::left << itr.first << " = "
<< itr.second << "\n";
};
int nwidth = (std::log10(_causal_envs_tmp.size()) + 1);
for(size_t i = 0; i < _causal_envs_tmp.size(); ++i)
{
std::stringstream fname{};
fname.fill('0');
fname << _config_folder << "/causal-" << std::setw(nwidth) << i << ".cfg";
std::ofstream _ofs{ fname.str() };
_write_config(_ofs, _causal_envs_tmp.at(i));
auto _cfg_name = (_config_file.empty())
? fname.str()
: join(array_config{ ":" }, _config_file, fname.str());
auto _cfg =
std::map<std::string_view, std::string>{ { "OMNITRACE_CONFIG_FILE",
_cfg_name } };
_causal_envs.emplace_back(_cfg);
}
}
if(_reset)
_causal_envs.front().emplace(std::string_view{ "OMNITRACE_CAUSAL_FILE_RESET" },
std::string{ "true" });
return _outv;
}
// explicit instantiation for usage in omnitrace-causal.cpp
template void
update_env(std::vector<char*>&, std::string_view, const std::string& _env_val,
bool _append, std::string_view);
+136
Просмотреть файл
@@ -0,0 +1,136 @@
// 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 "omnitrace-causal.hpp"
#include <timemory/log/macros.hpp>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string_view>
#include <unistd.h>
int
main(int argc, char** argv)
{
auto _base_env = get_initial_environment();
auto _causal_env = std::vector<std::map<std::string_view, std::string>>{};
bool _has_double_hyphen = false;
for(int i = 1; i < argc; ++i)
{
auto _arg = std::string_view{ argv[i] };
if(_arg == "--" || _arg == "-?" || _arg == "-h" || _arg == "--help" ||
_arg == "--version")
_has_double_hyphen = true;
}
std::vector<char*> _argv = {};
if(_has_double_hyphen)
{
_argv = parse_args(argc, argv, _base_env, _causal_env);
}
else
{
_argv.reserve(argc);
for(int i = 1; i < argc; ++i)
_argv.emplace_back(argv[i]);
_causal_env.resize(1);
}
prepare_command_for_run(argv[0], _argv);
prepare_environment_for_run(_base_env);
if(get_verbose() >= 3)
{
TIMEMORY_PRINTF_INFO(stderr, "causal environments to be executed:\n");
size_t _n = 0;
for(auto& citr : _causal_env)
{
auto _env = _base_env;
for(const auto& eitr : citr)
update_env(_env, eitr.first, eitr.second);
auto _prefix = std::to_string(_n++) + ": ";
print_updated_environment(_env, _prefix);
}
}
if(!_argv.empty())
{
if(_causal_env.size() == 1)
{
auto _env = _base_env;
for(const auto& eitr : _causal_env.front())
update_env(_env, eitr.first, eitr.second);
print_updated_environment(_env, "0: ");
print_command(_argv, "0: ");
_argv.emplace_back(nullptr);
_env.emplace_back(nullptr);
return execvpe(_argv.front(), _argv.data(), _env.data());
}
forward_signals({ SIGINT, SIGTERM, SIGQUIT });
size_t _ncount = 0;
size_t _width = std::log10(_causal_env.size()) + 1;
for(auto& citr : _causal_env)
{
auto _n = _ncount++;
auto _main_pid = getpid();
auto _pid = fork();
if(get_verbose() >= 3)
{
TIMEMORY_PRINTF_INFO(stderr, "process %i returned %i from fork...\n",
getpid(), _pid);
}
if(_pid == 0)
{
auto _prefix = std::stringstream{};
_prefix << std::setw(_width) << std::right << _n << "/"
<< std::setw(_width) << std::left << _causal_env.size() << ": ["
<< _main_pid << " -> " << getpid() << "] ";
auto _env = _base_env;
for(const auto& eitr : citr)
update_env(_env, eitr.first, eitr.second);
print_updated_environment(_env, _prefix.str());
print_command(_argv, _prefix.str());
_argv.emplace_back(nullptr);
_env.emplace_back(nullptr);
return execvpe(_argv.front(), _argv.data(), _env.data());
}
else
{
add_child_pid(_pid);
auto _status = wait_pid(_pid);
auto _ret = diagnose_status(_pid, _status);
remove_child_pid(_pid);
if(_ret != 0) return _ret;
}
}
}
}
+93
Просмотреть файл
@@ -0,0 +1,93 @@
// 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
#define TIMEMORY_PROJECT_NAME "omnitrace-causal"
#include <csignal>
#include <map>
#include <sched.h>
#include <set>
#include <string>
#include <string_view>
#include <vector>
int
get_verbose();
std::string
get_realpath(const std::string&);
void
print_command(const std::vector<char*>& _argv, std::string_view);
void print_updated_environment(std::vector<char*>, std::string_view);
std::vector<char*>
get_initial_environment();
void
prepare_command_for_run(char*, std::vector<char*>&);
void
prepare_environment_for_run(std::vector<char*>&);
std::string
get_internal_libpath(const std::string& _lib);
template <typename Tp>
void
update_env(std::vector<char*>&, std::string_view, Tp&&, bool _append = false,
std::string_view _join_delim = ":");
template <typename Tp>
void
add_default_env(std::vector<char*>&, std::string_view, Tp&&);
void
remove_env(std::vector<char*>&, std::string_view);
std::vector<char*>
parse_args(int argc, char** argv, std::vector<char*>&,
std::vector<std::map<std::string_view, std::string>>&);
using sigaction_t = struct sigaction;
struct signal_handler
{
sigaction_t m_custom_sigaction = {};
sigaction_t m_original_sigaction = {};
};
void
forward_signals(const std::set<int>&);
void add_child_pid(pid_t);
void remove_child_pid(pid_t);
int
wait_pid(pid_t, int = 0);
int
diagnose_status(pid_t, int);
+7 -1
Просмотреть файл
@@ -104,7 +104,9 @@ get_initial_environment()
auto* _omni_libpath =
realpath(get_internal_libpath("libomnitrace.so").c_str(), nullptr);
update_env(_env, "OMNITRACE_USE_SAMPLING", true);
auto _mode = get_env<std::string>("OMNITRACE_MODE", "sampling", false);
update_env(_env, "OMNITRACE_USE_SAMPLING", (_mode != "causal"));
update_env(_env, "OMNITRACE_CRITICAL_TRACE", false);
update_env(_env, "OMNITRACE_USE_PROCESS_SAMPLING", false);
@@ -147,6 +149,8 @@ get_internal_libpath(const std::string& _lib)
void
print_updated_environment(std::vector<char*> _env)
{
if(get_env<int>("OMNITRACE_VERBOSE", 0) < 0) return;
std::sort(_env.begin(), _env.end(), [](auto* _lhs, auto* _rhs) {
if(!_lhs) return false;
if(!_rhs) return true;
@@ -335,6 +339,8 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
};
parser.enable_help();
parser.enable_version("omnitrace-sample", "v" OMNITRACE_VERSION_STRING,
OMNITRACE_GIT_DESCRIBE, OMNITRACE_GIT_REVISION);
auto _cols = std::get<0>(tim::utility::console::get_columns());
if(_cols > parser.get_help_width() + 8)
+2 -1
Просмотреть файл
@@ -36,7 +36,8 @@ main(int argc, char** argv)
for(int i = 1; i < argc; ++i)
{
auto _arg = std::string_view{ argv[i] };
if(_arg == "--" || _arg == "-?" || _arg == "-h" || _arg == "--help")
if(_arg == "--" || _arg == "-?" || _arg == "-h" || _arg == "--help" ||
_arg == "--version")
_has_double_hyphen = true;
}
+8 -4
Просмотреть файл
@@ -21,6 +21,7 @@
// SOFTWARE.
#include "omnitrace.hpp"
#include "common/defines.h"
#include "fwd.hpp"
#include "log.hpp"
@@ -349,10 +350,6 @@ main(int argc, char** argv)
<< std::endl;
}
verbprintf(0, "\n");
verbprintf(0, "command :: '%s'...\n", cmd_string(_cmdc, _cmdv).c_str());
verbprintf(0, "\n");
if(_cmdc > 0) cmdv0 = _cmdv[0];
// now can loop through the options. If the first character is '-', then we know
@@ -364,6 +361,9 @@ main(int argc, char** argv)
string_t extra_help = "-- <CMD> <ARGS>";
parser.enable_help();
parser.enable_version("omnitrace", "v" OMNITRACE_VERSION_STRING,
OMNITRACE_GIT_DESCRIBE, OMNITRACE_GIT_REVISION);
parser.add_argument({ "" }, "");
parser.add_argument({ "[DEBUG OPTIONS]" }, "");
parser.add_argument({ "" }, "");
@@ -875,6 +875,10 @@ main(int argc, char** argv)
return 0;
}
verbprintf(0, "\n");
verbprintf(0, "command :: '%s'...\n", cmd_string(_cmdc, _cmdv).c_str());
verbprintf(0, "\n");
if(err)
{
std::cerr << err << std::endl;
+1
Просмотреть файл
@@ -296,6 +296,7 @@ omnitrace_add_bin_test(
_P
~PERFETTO
~PROCESS_SAMPLING
~KOKKOSP
--csv
--brief
--advanced
+12 -12
Просмотреть файл
@@ -8,11 +8,11 @@
## Overview
> ***[Omnitrace](https://github.com/AMDResearch/omnitrace) is an AMD open source research project and is not supported as part of the ROCm software stack.***
> ***[OmniTrace](https://github.com/AMDResearch/omnitrace) is an AMD open source research project and is not supported as part of the ROCm software stack.***
[Browse Omnitrace source code on Github](https://github.com/AMDResearch/omnitrace)
[Browse OmniTrace source code on Github](https://github.com/AMDResearch/omnitrace)
[Omnitrace](https://github.com/AMDResearch/omnitrace) is designed for both high-level profiling and
[OmniTrace](https://github.com/AMDResearch/omnitrace) is designed for both high-level profiling and
comprehensive tracing of applications running on the CPU or the CPU+GPU via dynamic binary instrumentation,
call-stack sampling, and various other means for determining currently executing function and line information.
@@ -24,17 +24,17 @@ The JSON output files are compatible with the python package [hatchet](https://g
the performance data into pandas dataframes and facilitate multi-run comparisons, filtering, visualization in Jupyter notebooks,
and much more.
[Omnitrace](https://github.com/AMDResearch/omnitrace) has two distinct configuration steps when instrumenting:
[OmniTrace](https://github.com/AMDResearch/omnitrace) has two distinct configuration steps when instrumenting:
1. Configuring which functions and modules are instrumented in the target binaries (i.e. executable and/or libraries)
- [Instrumenting with Omnitrace](instrumenting.md)
- [Instrumenting with OmniTrace](instrumenting.md)
2. Configuring what the instrumentation does happens when the instrumented binaries are executed
- [Customizing Omnitrace Runtime](runtime.md)
- [Customizing OmniTrace Runtime](runtime.md)
## Omnitrace Use Cases
## OmniTrace Use Cases
When analyzing the performance of an application, ***it is always best to NOT assume you know where the performance bottlenecks are***
***and why they are happening.*** Omnitrace is a ***tool for the entire execution of application***. It is the sort of tool which is
***and why they are happening.*** OmniTrace is a ***tool for the entire execution of application***. It is the sort of tool which is
ideal for *characterizing* where optimization would have the greatest impact on the end-to-end execution of the application and/or
viewing what else is happening on the system during a performance bottleneck.
@@ -44,10 +44,10 @@ to 1 microsecond (1000x speed-up) but the original application *never spent time
you will see zero statistically significant speed-up in end-to-end runtime of your application. In other words, it does not matter
how fast or slow the code on GPU is if the application is not bottlenecked waiting on the GPU.
Use Omnitrace to obtain a high-level view of the entire application. Use it to determine where the performance bottlenecks are and
Use OmniTrace to obtain a high-level view of the entire application. Use it to determine where the performance bottlenecks are and
obtain clues to why these bottlenecks are happening. If you want ***extensive*** insight into the execution of individual kernels
on the GPU, AMD Research is working on another tool for this but you should start with the tool which characterizes the
broad picture: Omnitrace.
broad picture: OmniTrace.
With regard to the CPU, Omnitrace does not target any specific vendor, it works just as well with non-AMD CPUs as with AMD CPUs.
With regard to the GPU, Omnitrace is currently restricted to the HIP and HSA APIs and kernels executing on AMD GPUs.
With regard to the CPU, OmniTrace does not target any specific vendor, it works just as well with non-AMD CPUs as with AMD CPUs.
With regard to the GPU, OmniTrace is currently restricted to the HIP and HSA APIs and kernels executing on AMD GPUs.
+480
Просмотреть файл
@@ -0,0 +1,480 @@
# Causal Profiling
```eval_rst
.. toctree::
:glob:
:maxdepth: 3
```
## What is "Causal Profiling"?
> ***If you speed up a given block of code by X%, the application will execute Y% faster***
Causal profiling directs parallel application developers to where they should focus their optimization
efforts by quantifying the potential impact of optimizations. Causal profiling is rooted in the concept
that *software execution speed is relative*: speeding up a block of code by X% is mathematically equivalent
to that block of code running at its current speed if all the other code running slower by X%.
Thus, causal profiling works by performing experiments on blocks of code during program execution which
insert pauses to slow down all other concurrently running code. During post-processing, these experiments
are translated into calculations for the potential impact of speeding up this block of code.
Consider the following C++ code executing `foo` and `bar` concurrently in two different threads
where `foo` is 30% faster than `bar` (ideally):
```cpp
constexpr size_t FOO_N = 7 * 1000000000UL;
constexpr size_t BAR_N = 10 * 1000000000UL;
void foo()
{
for(volatile size_t i = 0; i < FOO_N; ++i) {}
}
void bar()
{
for(volatile size_t i = 0; i < BAR_N; ++i) {}
}
int main()
{
auto _threads = { std::thread{ foo },
std::thread{ bar } };
for(auto& itr : _threads)
itr.join();
}
```
No matter how many optimizations are applied to `foo`, the application will always require the same amount of time
because the end-to-end performance is limited by `bar`. However, a 5% speedup in `bar` will result in the
end-to-end performance improving by 5% and this trend will continue linearly (10% speedup in `bar` yields 10% speedup in
end-to-end performance, and so on) up to 30% speedup, at which point, `bar` executes as fast as `foo`;
any speedup to `bar` beyond 30% will still only yield an end-to-end performance speedup of 30% since the application
will be limited by performance of `foo`, as demonstrated below in the causal profiling visualization:
![foobar-causal-plot](images/causal-foobar.png)
The full details of the causal profiling methodology can be found in the paper [Coz: Finding Code that Counts with Causal Profiling](http://arxiv.org/pdf/1608.03676v1.pdf).
The author's implementation is publicly available on [GitHub](https://github.com/plasma-umass/coz).
## Getting Started
### Progress Points
Causal profiling requires "progress points" to track progress through the code in between samples. Progress points must be triggered deterministically via instrumentation.
This can happen in three different ways:
1. OmniTrace can leverage the callbacks from Kokkos-Tools, OpenMP-Tools, roctracer, etc. and the wrappers around functions for MPI, NUMA, RCCL, etc. to act as progress-points
2. User can leverage the [runtime instrumentation capabilities](instrumenting.md#runtime-instrumentation) to insert progress-points (NOTE: binary rewrite to insert progress-points is not supported)
3. User can leverage the [User API](user_api.md), e.g. `OMNITRACE_CAUSAL_PROGRESS`
Please note with regard to #2, binary rewrite to insert progress-points is not supported: when a rewritten binary is executed, Dyninst translates the instruction pointer address in order
to execute the instrumentation and, as a result, call-stack samples never return instruction pointer addresses in the ranges defined as valid by OmniTrace. Hopefully, a work-around will
be found in the future.
### Key Concepts
| Concept | Setting | Options | Description |
|------------------|-----------------------------------|----------------------------------|--------------------------------------------------------------------------------------------------------------------|
| Mode | `OMNITRACE_CAUSAL_MODE` | `function`, `line` | Select entire function or individual line of code for causal experiments |
| End-to-End | `OMNITRACE_CAUSAL_END_TO_END` | boolean | Perform a single experiment during the entire run (does not require progress-points) |
| Fixed speedup(s) | `OMNITRACE_CAUSAL_FIXED_SPEEDUP` | one or more values from [0, 100] | Virtual speedup or pool of virtual speedups to randomly select |
| Binary scope | `OMNITRACE_CAUSAL_BINARY_SCOPE` | regular expression(s) | Dynamic binaries containing code for experiments |
| Source scope | `OMNITRACE_CAUSAL_SOURCE_SCOPE` | regular expression(s) | `<file>` and/or `<file>:<line>` containing code to include in experiments |
| Function scope | `OMNITRACE_CAUSAL_FUNCTION_SCOPE` | regular expression(s) | Restricts experiments to matching functions (function mode) or lines of code within matching functions (line mode) |
#### Notes
1. Binary scope defaults to `%MAIN%` (executable). Scope can be expanded to include linked libraries
2. `<file>` and `<file>:<line>` support requires debug info (i.e. code was compiled with `-g` or, preferably, `-g3`)
3. Function mode does not require debug info but does not support stripped binaries
### Speedup Prediction Variability and `omnitrace-causal` Executable
Causal profiling typically require executing the application several times in order to adequately sample all the domains of executing code, experiment speedups, etc. and resolve statistical fluctuations.
The `omnitrace-causal` executable is designed to simplify running this procedure:
```console
$ omnitrace-causal --help
[omnitrace-causal] Usage: ./bin/omnitrace-causal [ --help (count: 0, dtype: bool)
--version (count: 0, dtype: bool)
--monochrome (max: 1, dtype: bool)
--debug (max: 1, dtype: bool)
--verbose (count: 1)
--config (min: 0, dtype: filepath)
--launcher (count: 1, dtype: executable)
--generate-configs (min: 0, dtype: folder)
--no-defaults (min: 0, dtype: bool)
--mode (count: 1, dtype: string)
--output-name (min: 1, dtype: filename)
--reset (max: 1, dtype: bool)
--end-to-end (max: 1, dtype: bool)
--wait (count: 1, dtype: seconds)
--duration (count: 1, dtype: seconds)
--iterations (count: 1, dtype: int)
--speedups (min: 0, dtype: integers)
--binary-scope (min: 0, dtype: integers)
--source-scope (min: 0, dtype: integers)
--function-scope (min: 0, dtype: regex-list)
--binary-exclude (min: 0, dtype: integers)
--source-exclude (min: 0, dtype: integers)
--function-exclude (min: 0, dtype: regex-list)
]
Causal profiling usually requires multiple runs to reliably resolve the speedup estimates.
This executable is designed to streamline that process.
For example (assume all commands end with '-- <exe> <args>'):
omnitrace-causal -n 5 -- <exe> # runs <exe> 5x with causal profiling enabled
omnitrace-causal -s 0 5,10,15,20 # runs <exe> 2x with virtual speedups:
# - 0
# - randomly selected from 5, 10, 15, and 20
omnitrace-causal -F func_A func_B func_(A|B) # runs <exe> 3x with the function scope limited to:
# 1. func_A
# 2. func_B
# 3. func_A or func_B
General tips:
- Insert progress points at hotspots in your code or use omnitrace's runtime instrumentation
- Note: binary rewrite will produce a incompatible new binary
- Run omnitrace-causal in "function" mode first (does not require debug info)
- Run omnitrace-causal in "line" mode when you are targeting one function (requires debug info)
- Preferably, use predictions from the "function" mode to determine which function to target
- Limit the virtual speedups to a smaller pool, e.g., 0,5,10,25,50, to get reliable predictions quicker
- Make use of the binary, source, and function scope to limit the functions/lines selected for experiments
- Note: source scope requires debug info
Options:
-h, -?, --help Shows this page
--version Prints the version and exit
[DEBUG OPTIONS]
--monochrome Disable colorized output
--debug Debug output
-v, --verbose Verbose output
[GENERAL OPTIONS]
-c, --config Base configuration file
-l, --launcher When running MPI jobs, omnitrace-causal needs to be *before* the executable which launches the MPI processes (i.e.
before `mpirun`, `srun`, etc.). Pass the name of the target executable (or a regex for matching to the name of the
target) for causal profiling, e.g., `omnitrace-causal -l foo -- mpirun -n 4 foo`. This ensures that the omnitrace
library is LD_PRELOADed on the proper target
-g, --generate-configs Generate config files instead of passing environment variables directly. If no arguments are provided, the config files
will be placed in ${PWD}/omnitrace-causal-config folder
--no-defaults Do not activate default features which are recommended for causal profiling. For example: PID-tagging of output files
and timestamped subdirectories are disabled by default. Kokkos tools support is added by default
(OMNITRACE_USE_KOKKOSP=ON) because, for Kokkos applications, the Kokkos-Tools callbacks are used for progress points.
Activation of OpenMP tools support is similar
[CAUSAL PROFILING OPTIONS (General)]
(These settings will be applied to all causal profiling runs)
-m, --mode [ function (func) | line ]
Causal profiling mode
-o, --output-name Output filename of causal profiling data w/o extension
-r, --reset Overwrite any existing experiment results during the first run
-e, --end-to-end Single causal experiment for the entire application runtime
-w, --wait Set the wait time (i.e. delay) before starting the first causal experiment (in seconds)
-d, --duration Set the length of time (in seconds) to perform causal experimentationafter the first experiment is started. Once this
amount of time has elapsed, no more causal experiments will be started but any currently running experiment will be
allowed to finish.
-n, --iterations Number of times to repeat the combination of run configurations
[CAUSAL PROFILING OPTIONS (Combinatorial)]
(Each individual argument to these options will multiply the number runs by the number of arguments and the number of
iterations. E.g. -n 2 -B "MAIN" -F "foo" "bar" will produce 4 runs: 2 iterations x 1 binary scope x 2 function scopes
(MAIN+foo, MAIN+bar, MAIN+foo, MAIN+bar))
-s, --speedups Pool of virtual speedups to sample from during experimentation. Each space designates a group and multiple speedups can
be grouped together by commas, e.g. -s 0 0,10,20-50 is two groups: group #1 is '0' and group #2 is '0 10 20 25 30 35 40
45 50'
-B, --binary-scope Restricts causal experiments to the binaries matching the list of regular expressions. Each space designates a group
and multiple scopes can be grouped together with a semi-colon
-S, --source-scope Restricts causal experiments to the source files or source file + lineno pairs (i.e. <file> or <file>:<line>) matching
the list of regular expressions. Each space designates a group and multiple scopes can be grouped together with a
semi-colon
-F, --function-scope Restricts causal experiments to the functions matching the list of regular expressions. Each space designates a group
and multiple scopes can be grouped together with a semi-colon
-BE, --binary-exclude Excludes causal experiments from being performed on the binaries matching the list of regular expressions. Each space
designates a group and multiple excludes can be grouped together with a semi-colon
-SE, --source-exclude Excludes causal experiments from being performed on the code from the source files or source file + lineno pair (i.e.
<file> or <file>:<line>) matching the list of regular expressions. Each space designates a group and multiple excludes
can be grouped together with a semi-colon
-FE, --function-exclude Excludes causal experiments from being performed on the functions matching the list of regular expressions. Each space
designates a group and multiple excludes can be grouped together with a semi-colon
```
#### Examples
```bash
#!/bin/bash -e
module load omnitrace
N=20
I=3
# when providing speedups to omnitrace-causal, speedup
# groups are separated by a space so "0,10" results in
# one speedup group where omnitrace samples from
# the speedup set of {0, 10}. Passing "0 10" (without
# quotes to omnitrace-causal multiplies the
# number of runs by 2, where the first half of the
# runs instruct omnitrace to only use 0 as the
# speedup and the second half of the runs instruct
# omnitrace to only use 10 as the speedup.
SPEEDUPS="0,0,0,10,20,30,40,50,50,75,75,75,90,90,90"
# thus, -s ${SPEEDUPS} only multiplies the number
# of runs by 1 whereas -S ${SPEEDUPS_E2E} multiplies
# the number of runs by 15:
# - 3 runs with speedup of 0
# - 1 run for each of the speedups 10, 20, 30, and 40
# - 2 runs with speedup of 50
# - 3 runs with speedup of 75
# - 3 runs with speedup of 90
SPEEDUPS_E2E=$(echo "${SPEEDUPS}" | sed 's/,/ /g')
# 20 iterations in function mode with 1 speedup group
# and source scope set to .cpp files
#
# outputs to files:
# - causal/experiments.func.coz
# - causal/experiments.func.json
#
# total executions: 20
#
omnitrace-causal \
-n ${N} \
-s ${SPEEDUPS} \
-m function \
-o experiments.func \
-S ".*\\.cpp" \
-- \
./causal-omni-cpu "${@}"
# 20 iterations in line mode with 1 speedup group
# and source scope restricted to lines 155 and 165
# in the causal.cpp file.
#
# outputs to files:
# - causal/experiments.line.coz
# - causal/experiments.line.json
#
# total executions: 20
#
omnitrace-causal \
-n ${N} \
-s ${SPEEDUPS} \
-m line \
-o experiments.line \
-S "causal\\.cpp:(155|165)" \
-- \
./causal-omni-cpu "${@}"
# 3 iterations in function mode of 15 singular speedups
# in end-to-end mode with 2 different function scopes
# where one is restricted to "cpu_slow_func" and
# another is restricted to "cpu_fast_func".
#
# outputs to files:
# - causal/experiments.func.e2e.coz
# - causal/experiments.func.e2e.json
#
# total executions: 90
#
omnitrace-causal \
-n ${I} \
-s ${SPEEDUPS_E2E} \
-m func \
-e \
-o experiments.func.e2e \
-F "cpu_slow_func" \
"cpu_fast_func" \
-- \
./causal-omni-cpu "${@}"
# 3 iterations in line mode of 15 singular speedups
# in end-to-end mode with 2 different source scopes
# where one is restricted to line 155 in causal.cpp
# and another is restricted to line 165 in causal.cpp.
#
# outputs to files:
# - causal/experiments.line.e2e.coz
# - causal/experiments.line.e2e.json
#
# total executions: 90
#
omnitrace-causal \
-n ${I} \
-s ${SPEEDUPS_E2E} \
-m line \
-e \
-o experiments.line.e2e \
-S "causal\\.cpp:155" \
"causal\\.cpp:165" \
-- \
./causal-omni-cpu "${@}"
export OMP_NUM_THREADS=8
export OMP_PROC_BIND=spread
export OMP_PLACES=threads
# set number of iterations to 5
N=5
# 5 iterations in function mode of 1 speedup
# group with the source scope restricted
# to files containing "lulesh" in their filename
# and exclude functions which start with "Kokkos::"
# or "std::enable_if".
#
# outputs to files:
# - causal/experiments.func.coz
# - causal/experiments.func.json
#
# total executions: 5
#
# First of 5 executions overwrites any
# existing causal/experiments.func.(coz|json)
# file due to "--reset" argument
#
omnitrace-causal \
--reset \
-n ${N} \
-s ${SPEEDUPS} \
-m func \
-o experiments.func \
-S "lulesh.*" \
-FE "^(Kokkos::|std::enable_if)" \
-- \
./lulesh-omni -i 50 -s 200 -r 20 -b 5 -c 5 -p
# 5 iterations in line mode of 1 speedup
# group with the source scope restricted
# to files containing "lulesh" in their filename
# and exclude functions which start with "exec_range"
# or "execute" and which contain either
# "construct_shared_allocation" or "._omp_fn." in
# the function name.
#
# outputs to files:
# - causal/experiments.line.coz
# - causal/experiments.line.json
#
# total executions: 5
#
# First of 5 executions overwrites any
# existing causal/experiments.line.(coz|json)
# file due to "--reset" argument
#
omnitrace-causal \
--reset \
-n ${N} \
-s ${SPEEDUPS} \
-m line \
-o experiments.line \
-S "lulesh.*" \
-FE "^(exec_range|execute);construct_shared_allocation;\\._omp_fn\\." \
-- \
./lulesh-omni -i 50 -s 200 -r 20 -b 5 -c 5 -p
# 5 iterations in line mode of 1 speedup
# group with the source scope restricted
# to files whose basename is "lulesh.cc"
# for 3 different functions:
# - ApplyMaterialPropertiesForElems
# - CalcHourglassControlForElems
# - CalcVolumeForceForElems
#
# outputs to files:
# - causal/experiments.line.targeted.coz
# - causal/experiments.line.targeted.json
#
# total executions: 15
#
# First of 5 executions overwrites any
# existing causal/experiments.line.(coz|json)
# file due to "--reset" argument
#
omnitrace-causal \
--reset \
-n ${N} \
-s ${SPEEDUPS} \
-m line \
-o experiments.line.targeted \
-F "ApplyMaterialPropertiesForElems" \
"CalcHourglassControlForElems" \
"CalcVolumeForceForElems" \
-S "lulesh\\.cc" \
-- \
./lulesh-omni -i 50 -s 200 -r 20 -b 5 -c 5 -p
```
#### Using `omnitrace-causal` with other launchers (e.g. `mpirun`)
The `omnitrace-causal` executable is intended to assist with application replay and is designed to always be at the start of the command-line (i.e. the primary process).
`omnitrace-causal` typically adds a `LD_PRELOAD` of the OmniTrace libraries into the environment before launching the command in order to inject the functionality
required to start the causal profiling tooling. However, this is problematic when the target application for causal profiling requires another command-line
tool in order to run, e.g. `foo` is the target application but executing `foo` requires `mpirun -n 2 foo`. If one were to simply do `omnitrace-causal -- mpirun -n 2 foo`,
then the causal profiling would be applied to `mpirun` instead of `foo`. `omnitrace-causal` remedies this by providing a command-line option `-l` / `--launcher`
to indicate the target application is using a launcher script/executable. The argument to the command-line option is the name of (or regex for) the target application
on the command-line. When `--launcher` is used, `omnitrace-causal` will generate all the replay configurations and execute them but delay adding the `LD_PRELOAD`, instead it
will inject a call to itself into the command-line right before the target application. This recursive call to itself will inherit the configuration from
parent `omnitrace-causal` executable, insert an `LD_PRELOAD` into the environment, and then invoke an `execv` to replace itself with the new process launched by the target
application.
In other words, the following command:
```console
omnitrace-causal -l foo -n 3 -- mpirun -n 2 foo`
```
Effectively results in:
```console
mpirun -n 2 omnitrace-causal -- foo
mpirun -n 2 omnitrace-causal -- foo
mpirun -n 2 omnitrace-causal -- foo
```
### Visualizing the Causal Output
OmniTrace generates a `causal/experiments.json` and `causal/experiments.coz` in `${OMNITRACE_OUTPUT_PATH}/${OMNITRACE_OUTPUT_PREFIX}`. A standalone GUI for viewing the causal profiling
results in under development but until this is available, visit [plasma-umass.org/coz/](https://plasma-umass.org/coz/) and open the `*.coz` file.
## OmniTrace vs. Coz
This section is intended for readers who are familiar with the [Coz profiler](https://github.com/plasma-umass/coz).
OmniTrace provides several additional features and utilities for causal profiling:
| | [Coz](https://github.com/plasma-umass/coz) | [OmniTrace](https://github.com/AMDResearch/omnitrace) | Notes |
|----------------------|:-------------------------------------------------------------------:|:----------------------------------------------------------:|-------------------------------|
| Debug info | requires debug info in DWARF v3 format (`-gdwarf-3`) | optional, supports any DWARF format version | See Note #1 below |
| Experiment selection | `<file>:<line>` | `<function>` or `<file>:<line>` | See Note #2 below |
| Experiment speedups | Randomly samples b/t 0..100 in increments of 5 or one fixed speedup | Supports specifying smaller subset | Set Note #3 below |
| Scope options | Supports binary and source scopes | Supports binary, source, and function scopes | See Note #4, #5, and #6 below |
| Scope inclusion | Uses `%` as wildcard for binary and source scopes | Full regex support for binary, source, and function scopes | |
| Scope exclusion | Not supported | Supports regexes for excluding binary/source/function | See Note #7 below |
| Call-stack sampling | Linux perf | libunwind | See Note #8 below |
### Notes
1. OmniTrace supports a "function" mode which does not require debug info
2. OmniTrace supports selecting entire range of instruction pointers for a function instead of instruction pointer for one line. In large codes, "function" mode
can resolve in fewer iterations and once a target function is identified, one can switch to line mode and limit the function scope to the target function
3. OmniTrace supports randomly sampling from subsets, e.g. { 0, 0, 5, 10 } where 0% is randomly selected 50% of time and 5% and 10% are randomly selected 25% of the time
4. OmniTrace and COZ have same definition for binary scope: the binaries loaded at runtime (e.g. executable and linked libraries)
5. OmniTrace "source scope" supports both `<file>` and `<file>:<line>` formats in contrast to COZ "source scope" which requires `<file>:<line>` format
6. OmniTrace supports a "function" scope which narrows the functions/lines which are eligible for causal experiments to those within the matching functions
7. OmniTrace supports a second filter on scopes for removing binary/source/function caught by inclusive match, e.g. `BINARY_SCOPE=.*` + `BINARY_EXCLUDE=libmpi.*`
initially includes all binaries but exclude regex removes MPI libraries
+1
Просмотреть файл
@@ -137,6 +137,7 @@ breathe_projects_source = {
"omnitrace/types.h",
"omnitrace/categories.h",
"omnitrace/user.h",
"omnitrace/causal.h",
],
)
}
+4 -1
Просмотреть файл
@@ -8,7 +8,7 @@
## Overview
[Omnitrace](https://github.com/AMDResearch/omnitrace) is designed to be highly extensible. Internally, it leverages the
[OmniTrace](https://github.com/AMDResearch/omnitrace) is designed to be highly extensible. Internally, it leverages the
[timemory performance analysis toolkit](https://github.com/NERSC/timemory) to
manage extensions, resources, data, etc.
@@ -23,6 +23,8 @@ manage extensions, resources, data, etc.
- Periodic software interrupts per-thread
- Process-level sampling
- Background thread records process-, system- and device-level metrics while the application executes
- Causal profiling
- Quantifies the potential impact of optimizations in parallel codes
- Critical trace generation
### Data Analysis
@@ -32,6 +34,7 @@ manage extensions, resources, data, etc.
- Ideal for running at scale
- Comprehensive traces
- Every individual event/measurement
- Application speedup predictions resulting from potential optimizations in functions and lines of code (causal profiling)
- Critical trace analysis (alpha)
### Parallelism API Support
+1 -1
Просмотреть файл
@@ -101,7 +101,7 @@ e.g., omnitrace's meaning of the term "module" when instrumenting Python.
## Data Collection Mode(s)
Omnitrace supports several modes of recording trace and profiling data for your application:
OmniTrace supports several modes of recording trace and profiling data for your application:
| Mode | Descriptions |
|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 27 KiB

+2 -1
Просмотреть файл
@@ -1,4 +1,4 @@
# Welcome to the [Omnitrace](https://github.com/AMDResearch/omnitrace) Documentation!
# Welcome to the [OmniTrace](https://github.com/AMDResearch/omnitrace) Documentation!
```eval_rst
.. toctree::
@@ -14,6 +14,7 @@
runtime
sampling
instrumenting
causal_profiling
critical_trace
output
user_api
+10 -10
Просмотреть файл
@@ -8,7 +8,7 @@
## Operating System
Omnitrace is only supported on Linux.
OmniTrace is only supported on Linux.
- Ubuntu 18.04
- Ubuntu 20.04
@@ -76,16 +76,16 @@ mkdir /opt/omnitrace
./omnitrace-1.0.0-ubuntu-18.04-ROCm-405000-OMPT-PAPI.sh --prefix=/opt/omnitrace --exclude-subdir
```
## Installing Omnitrace from source
## Installing OmniTrace from source
### Build Requirements
Omnitrace needs a GCC compiler with full support for C++17 and CMake v3.16 or higher.
OmniTrace needs a GCC compiler with full support for C++17 and CMake v3.16 or higher.
The Clang compiler may be used in lieu of the GCC compiler if Dyninst is already installed.
- GCC compiler v7+
- Older GCC compilers may be supported but are not tested
- Clang compilers are generally supported for [Omnitrace](https://github.com/AMDResearch/omnitrace) but not Dyninst
- Clang compilers are generally supported for [OmniTrace](https://github.com/AMDResearch/omnitrace) but not Dyninst
- [CMake](https://cmake.org/) v3.16+
> ***If the system installed cmake is too old, installing a new version of cmake can be done through several methods.***
@@ -114,8 +114,8 @@ and Dyninst requires TBB), and the CMake option to build the package alongside o
| Third-Party Library | Minimum Version | Required By | CMake Option |
|---------------------|-----------------|-------------|-------------------------------------------|
| Dyninst | 10.0 | Omnitrace | `OMNITRACE_BUILD_DYNINST` (default: OFF) |
| Libunwind | | Omnitrace | `OMNITRACE_BUILD_LIBUNWIND` (default: ON) |
| Dyninst | 10.0 | OmniTrace | `OMNITRACE_BUILD_DYNINST` (default: OFF) |
| Libunwind | | OmniTrace | `OMNITRACE_BUILD_LIBUNWIND` (default: ON) |
| TBB | 2018.6 | Dyninst | `DYNINST_BUILD_TBB` (default: OFF) |
| ElfUtils | 0.178 | Dyninst | `DYNINST_BUILD_ELFUTILS` (default: OFF) |
| LibIberty | | Dyninst | `DYNINST_BUILD_LIBIBERTY` (default: OFF) |
@@ -144,7 +144,7 @@ and Dyninst requires TBB), and the CMake option to build the package alongside o
### Installing DynInst
#### Building Dyninst alongside Omnitrace
#### Building Dyninst alongside OmniTrace
The easiest way to install Dyninst is to configure omnitrace with `OMNITRACE_BUILD_DYNINST=ON`. Depending on the version of Ubuntu, the apt package manager may have current enough
versions of Dyninst's Boost, TBB, and LibIberty dependencies (i.e. `apt-get install libtbb-dev libiberty-dev libboost-dev`); however, it is possible to request Dyninst to install
@@ -173,7 +173,7 @@ spack load -r dyninst
### Installing omnitrace
Omnitrace has cmake configuration options for supporting MPI (`OMNITRACE_USE_MPI` or `OMNITRACE_USE_MPI_HEADERS`), HIP kernel tracing (`OMNITRACE_USE_ROCTRACER`),
OmniTrace has cmake configuration options for supporting MPI (`OMNITRACE_USE_MPI` or `OMNITRACE_USE_MPI_HEADERS`), HIP kernel tracing (`OMNITRACE_USE_ROCTRACER`),
sampling ROCm devices (`OMNITRACE_USE_ROCM_SMI`), OpenMP-Tools (`OMNITRACE_USE_OMPT`), hardware counters via PAPI (`OMNITRACE_USE_PAPI`), among others.
Various additional features can be enabled via the [`TIMEMORY_USE_*` CMake options](https://timemory.readthedocs.io/en/develop/installation.html#cmake-options).
Any `OMNITRACE_USE_<VAL>` option which has a corresponding `TIMEMORY_USE_<VAL>` option means that the support within timemory for this feature has been integrated
@@ -204,9 +204,9 @@ cmake --build omnitrace-build --target install
source /opt/omnitrace/share/omnitrace/setup-env.sh
```
#### MPI Support within Omnitrace
#### MPI Support within OmniTrace
[Omnitrace](https://github.com/AMDResearch/omnitrace) can have full (`OMNITRACE_USE_MPI=ON`) or partial (`OMNITRACE_USE_MPI_HEADERS=ON`) MPI support.
[OmniTrace](https://github.com/AMDResearch/omnitrace) can have full (`OMNITRACE_USE_MPI=ON`) or partial (`OMNITRACE_USE_MPI_HEADERS=ON`) MPI support.
The only difference between these two modes is whether or not the results collected via timemory and/or perfetto can be aggregated into a single
output file during finalization. When full MPI support is enabled, combining the timemory results always occurs whereas combining the perfetto
results is configurable via the `OMNITRACE_PERFETTO_COMBINE_TRACES` setting.
+2 -2
Просмотреть файл
@@ -311,7 +311,7 @@ omnitrace --simulate -o foo.inst -- foo
### Excluding and Including Modules and Functions
[Omnitrace](https://github.com/AMDResearch/omnitrace) has a set of 6 command-line options which each accept one or more regular expressions for customizing the scope of which module and/or functions are
[OmniTrace](https://github.com/AMDResearch/omnitrace) has a set of 6 command-line options which each accept one or more regular expressions for customizing the scope of which module and/or functions are
instrumented. Multiple regexes per option are treated as an OR operation, e.g. `--module-include libfoo libbar` is effectively that same as `--module-include 'libfoo|libbar'`.
If you would like to force the inclusion of certain modules and/or function without changing any of the heuristics, use the `--module-include` and/or `--function-include` options.
@@ -616,7 +616,7 @@ background system-level thread sampling by default.
Tracing capabilities which do not rely on instrumentation, such as the HIP API and kernel tracing
(which is collected via roctracer), will still be available.
[Omnitrace](https://github.com/AMDResearch/omnitrace)'s sampling capabilities are always available, even in trace mode, but is deactivated by default.
[OmniTrace](https://github.com/AMDResearch/omnitrace)'s sampling capabilities are always available, even in trace mode, but is deactivated by default.
In order to activate sampling in trace mode, simply set `OMNITRACE_USE_SAMPLING=ON` in the environment
or in an omnitrace configuration file.
+2 -1
Просмотреть файл
@@ -116,7 +116,8 @@ WARN_LOGFILE = doc/warnings.log
INPUT = @SOURCE_DIR@/README.md \
@SOURCE_DIR@/source/lib/omnitrace-user/omnitrace/types.h \
@SOURCE_DIR@/source/lib/omnitrace-user/omnitrace/categories.h \
@SOURCE_DIR@/source/lib/omnitrace-user/omnitrace/user.h
@SOURCE_DIR@/source/lib/omnitrace-user/omnitrace/user.h \
@SOURCE_DIR@/source/lib/omnitrace-user/omnitrace/causal.h
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.h \
*.hh \
+3 -3
Просмотреть файл
@@ -1,4 +1,4 @@
# Omnitrace Output
# OmniTrace Output
```eval_rst
.. toctree::
@@ -55,7 +55,7 @@ $ omnitrace -- ./foo
## Metadata
[Omnitrace](https://github.com/AMDResearch/omnitrace) will output a metadata.json file. This metadata file will contain
[OmniTrace](https://github.com/AMDResearch/omnitrace) will output a metadata.json file. This metadata file will contain
information about the settings, environment variables, output files, and info about the system and the run:
- Hardware cache sizes
@@ -237,7 +237,7 @@ information about the settings, environment variables, output files, and info ab
### Core Configuration Settings
> ***See also: [Customizing Omnitrace Runtime](runtime.md)***
> ***See also: [Customizing OmniTrace Runtime](runtime.md)***
| Setting | Value | Description |
|---------------------------|--------------------|---------------------------------------------------------------------------------------------------|
+6 -6
Просмотреть файл
@@ -6,7 +6,7 @@
:maxdepth: 3
```
[Omnitrace](https://github.com/AMDResearch/omnitrace) supports profiling Python code at the source-level and/or the script-level.
[OmniTrace](https://github.com/AMDResearch/omnitrace) supports profiling Python code at the source-level and/or the script-level.
Python support is enabled via the `OMNITRACE_USE_PYTHON` and the `OMNITRACE_PYTHON_VERSIONS="<MAJOR>.<MINOR>` CMake options.
Alternatively, to build multiple python versions, use `OMNITRACE_PYTHON_VERSIONS="<MAJOR>.<MINOR>;[<MAJOR>.<MINOR>]"`,
and `OMNITRACE_PYTHON_ROOT_DIRS="/path/to/version;[/path/to/version]"` instead of `OMNITRACE_PYTHON_VERSION`.
@@ -30,9 +30,9 @@ export PYTHONPATH=/opt/omnitrace/lib/python3.8/site-packages:${PYTHONPATH}
If using either the `share/omnitrace/setup-env.sh` script or the modulefile in `share/modulefiles/omnitrace`, prefixing the `PYTHONPATH`
environment variable is automatically handled.
## Running Omnitrace on a Python Script
## Running OmniTrace on a Python Script
Omnitrace provides an `omnitrace-python` helper bash script which effectively handles ensuring `PYTHONPATH` is properly set and the correct python interpreter is used.
OmniTrace provides an `omnitrace-python` helper bash script which effectively handles ensuring `PYTHONPATH` is properly set and the correct python interpreter is used.
Thus the following are effectively equivalent:
```bash
@@ -57,7 +57,7 @@ optional arguments:
Logging verbosity
-b, --builtin Put 'profile' in the builtins. Use '@profile' to decorate a single function, or 'with profile:' to profile a single section of code.
-c FILE, --config FILE
Omnitrace configuration file
OmniTrace configuration file
-s FILE, --setup FILE
Code to execute before the code to profile
-F [BOOL], --full-filepath [BOOL]
@@ -168,7 +168,7 @@ And executed with `omnitrace-python -b -- ./example.py`, omnitrace would produce
|-----------------------------------------------------------|
```
## Omnitrace Python Source Instrumentation
## OmniTrace Python Source Instrumentation
Starting from the unmodified `example.py` script above, we start by importing the `omnitrace` module:
@@ -232,7 +232,7 @@ The results for both of the source-level instrumentation modes are identical to
> ***When `omnitrace-python` is used without built-ins, the profiling results will likely be cluttered by***
> ***numerous functions called during the importing of more complex modules, e.g. `import numpy`.***
### Omnitrace Python Source Instrumentation Configuration
### OmniTrace Python Source Instrumentation Configuration
Within the Python source code, the profiler can be configured by directly modifying the `omnitrace.profiler.config` data fields.
+7 -7
Просмотреть файл
@@ -1,4 +1,4 @@
# Configuring Omnitrace Runtime
# Configuring OmniTrace Runtime
```eval_rst
.. toctree::
@@ -49,7 +49,7 @@ match to nearly all common expressions for boolean logic: ON, OFF, YES, NO, TRUE
### Exploring Components
[Omnitrace](https://github.com/AMDResearch/omnitrace) uses [timemory](https://github.com/NERSC/timemory) extensively to provide various capabilities and manage
[OmniTrace](https://github.com/AMDResearch/omnitrace) uses [timemory](https://github.com/NERSC/timemory) extensively to provide various capabilities and manage
data and resources. By default, when `OMNITRACE_USE_TIMEMORY=ON`, omnitrace will only collect wall-clock
timing values; however, by modifying the `OMNITRACE_TIMEMORY_COMPONENTS` setting, omnitrace can be configured to
collect hardware counters, CPU-clock timers, memory usage, context-switches, page-faults, network statistics,
@@ -72,7 +72,7 @@ omnitrace-avail --components --available --string --brief
### Exploring Hardware Counters
[Omnitrace](https://github.com/AMDResearch/omnitrace) supports collecting hardware counters via PAPI and ROCm.
[OmniTrace](https://github.com/AMDResearch/omnitrace) supports collecting hardware counters via PAPI and ROCm.
Generally, PAPI is used to collect CPU-based hardware counters and ROCm is used to collect GPU-based hardware
counters; although it is possible to install PAPI with ROCm support and collect GPU-based hardware counters
via PAPI but this is not recommended because CPU hardware counters via PAPI cannot be collected simultaneously.
@@ -143,7 +143,7 @@ OMNITRACE_PAPI_EVENTS = PAPI_TOT_INS perf::CACHE-REFERENCES perf::CACHE-
#### OMNITRACE_ROCM_EVENTS
Omnitrace reads the ROCm events from the `${ROCM_PATH}/lib/rocprofiler/metrics.xml` file. Use the `ROCP_METRICS` environment
OmniTrace reads the ROCm events from the `${ROCM_PATH}/lib/rocprofiler/metrics.xml` file. Use the `ROCP_METRICS` environment
variable to point omnitrace to a different XML metrics file, e.g., `export ROCP_METRICS=${PWD}/custom_metrics.xml`.
`omnitrace-avail -H -c GPU` will show event names with a suffix of `:device=N` where `N` is the device number.
For example, if you have two devices, you will see:
@@ -201,7 +201,7 @@ OMNITRACE_CRITICAL_TRACE_SERIALIZE_NAMES = false
OMNITRACE_DEBUG = false
OMNITRACE_DL_VERBOSE = 0
OMNITRACE_INSTRUMENTATION_INTERVAL = 1
OMNITRACE_KOKKOS_KERNEL_LOGGER = false
OMNITRACE_KOKKOSP_KERNEL_LOGGER = false
OMNITRACE_PAPI_EVENTS = PAPI_TOT_CYC
OMNITRACE_PERFETTO_BACKEND = inprocess
OMNITRACE_PERFETTO_BUFFER_SIZE_KB = 1024000
@@ -302,7 +302,7 @@ $ omnitrace-avail -S -bd
| OMNITRACE_INPUT_PREFIX | Explicitly specify the prefix for in... |
| OMNITRACE_INSTRUMENTATION_INTERVAL | Instrumentation only takes measureme... |
| OMNITRACE_JSON_OUTPUT | Write json output files |
| OMNITRACE_KOKKOS_KERNEL_LOGGER | Enables kernel logging |
| OMNITRACE_KOKKOSP_KERNEL_LOGGER | Enables kernel logging |
| OMNITRACE_MAX_DEPTH | Set the maximum depth of label hiera... |
| OMNITRACE_MAX_THREAD_BOOKMARKS | Maximum number of times a worker thr... |
| OMNITRACE_MAX_WIDTH | Set the maximum width for component ... |
@@ -1161,7 +1161,7 @@ $ omnitrace-avail -H -bd
## Creating a Configuration File
[Omnitrace](https://github.com/AMDResearch/omnitrace) supports 3 configuration file formats: JSON, XML, and plain text.
[OmniTrace](https://github.com/AMDResearch/omnitrace) supports 3 configuration file formats: JSON, XML, and plain text.
Use `omnitrace-avail -G <filename> -F txt json xml` to generate default configuration files of each format and, optionally,
include the `--all` flag for descriptions, etc.
Configuration files are specified via the `OMNITRACE_CONFIG_FILE` environment variable
+1 -1
Просмотреть файл
@@ -192,7 +192,7 @@ are interpreted as belonging to omnitrace and all arguments following the double
application and it's arguments. The double-hyphen is only necessary when passing command line arguments to the target
which also use hyphens. E.g. `omnitrace-sample ls` works but, in order to run `ls -la`, use `omnitrace-sample -- ls -la`.
[Configuring Omnitrace Runtime](runtime.md) establish the precedence of environment variable values over values specified in the configuration files. This enables
[Configuring OmniTrace Runtime](runtime.md) establish the precedence of environment variable values over values specified in the configuration files. This enables
the user to configure the omnitrace runtime to their preferred default behavior in a file such as `~/.omnitrace.cfg` and then easily override
those settings via something like `OMNITRACE_ENABLED=OFF omnitrace-sample -- foo`.
Similarly, the command line arguments passed to `omnitrace-sample` take precedence over environment variables.
+21
Просмотреть файл
@@ -4,6 +4,7 @@
.. doxygenfile:: omnitrace/types.h
.. doxygenfile:: omnitrace/categories.h
.. doxygenfile:: omnitrace/user.h
.. doxygenfile:: omnitrace/causal.h
```
By default, when omnitrace detects any `omnitrace_user_start_*` or `omnitrace_user_stop_*` function, instrumentation
@@ -13,6 +14,26 @@ recorded, regardless of whether whether `omnitrace_user_start_*` or `omnitrace_u
## Example
### Compilation
#### CMake
```cmake
find_package(omnitrace REQUIRED COMPONENTS user)
add_executable(foo foo.cpp)
target_link_libraries(foo PRIVATE omnitrace::omnitrace-user-library)
```
#### General
Assuming omnitrace installed in `/opt/omnitrace`:
```bash
g++ -I/opt/omnitrace foo.cpp -o foo -lomnitrace-user
```
### User API Implementation
```cpp
+3
Просмотреть файл
@@ -71,6 +71,7 @@
#define OMNITRACE_VISIBILITY(MODE) OMNITRACE_ATTRIBUTE(visibility(MODE))
#define OMNITRACE_PUBLIC_API OMNITRACE_VISIBILITY("default")
#define OMNITRACE_HIDDEN_API OMNITRACE_VISIBILITY("hidden")
#define OMNITRACE_INTERNAL_API OMNITRACE_VISIBILITY("internal")
#define OMNITRACE_INLINE OMNITRACE_ATTRIBUTE(always_inline) inline
#define OMNITRACE_NOINLINE OMNITRACE_ATTRIBUTE(noinline)
#define OMNITRACE_HOT OMNITRACE_ATTRIBUTE(hot)
@@ -80,6 +81,8 @@
#define OMNITRACE_WEAK OMNITRACE_ATTRIBUTE(weak)
#define OMNITRACE_PACKED OMNITRACE_ATTRIBUTE(__packed__)
#define OMNITRACE_PACKED_ALIGN(VAL) OMNITRACE_PACKED OMNITRACE_ATTRIBUTE(__aligned__(VAL))
#define OMNITRACE_LIKELY(...) __builtin_expect((__VA_ARGS__), 1)
#define OMNITRACE_UNLIKELY(...) __builtin_expect((__VA_ARGS__), 0)
#if defined(OMNITRACE_CI) && OMNITRACE_CI > 0
# if defined(NDEBUG)
+40
Просмотреть файл
@@ -22,9 +22,13 @@
#pragma once
#include <array>
#include <initializer_list>
#include <ios>
#include <sstream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#if !defined(OMNITRACE_FOLD_EXPRESSION)
@@ -127,6 +131,42 @@ join(QuoteStrings&&, DelimT&& _delim, Args&&... _args)
return (_ret.length() > _len) ? _ret.substr(_len) : std::string{};
}
}
template <typename... Args>
auto
join(std::array<std::string_view, 3>&& _delim, Args&&... _args)
{
return join("", std::get<0>(_delim),
join(std::get<1>(_delim), std::forward<Args>(_args)...),
std::get<2>(_delim));
}
template <typename... Args>
auto
join(QuoteStrings&&, std::array<std::string_view, 3>&& _delim, Args&&... _args)
{
return join(QuoteStrings{}, "", std::get<0>(_delim),
join(std::get<1>(_delim), std::forward<Args>(_args)...),
std::get<2>(_delim));
}
template <typename DelimB, typename DelimT, typename DelimE, typename... Args>
auto
join(std::tuple<DelimB, DelimT, DelimE>&& _delim, Args&&... _args)
{
return join("", std::get<0>(_delim),
join(std::get<1>(_delim), std::forward<Args>(_args)...),
std::get<2>(_delim));
}
template <typename DelimB, typename DelimT, typename DelimE, typename... Args>
auto
join(QuoteStrings&&, std::tuple<DelimB, DelimT, DelimE>&& _delim, Args&&... _args)
{
return join(QuoteStrings{}, "", std::get<0>(_delim),
join(std::get<1>(_delim), std::forward<Args>(_args)...),
std::get<2>(_delim));
}
} // namespace
} // namespace common
} // namespace omnitrace
+41 -3
Просмотреть файл
@@ -266,6 +266,9 @@ struct OMNITRACE_HIDDEN_API indirect
"omnitrace_register_source");
OMNITRACE_DLSYM(omnitrace_register_coverage_f, m_omnihandle,
"omnitrace_register_coverage");
OMNITRACE_DLSYM(omnitrace_progress_f, m_omnihandle, "omnitrace_progress");
OMNITRACE_DLSYM(omnitrace_annotated_progress_f, m_omnihandle,
"omnitrace_annotated_progress");
OMNITRACE_DLSYM(kokkosp_print_help_f, m_omnihandle, "kokkosp_print_help");
OMNITRACE_DLSYM(kokkosp_parse_args_f, m_omnihandle, "kokkosp_parse_args");
@@ -343,8 +346,10 @@ struct OMNITRACE_HIDDEN_API indirect
_cb.stop_thread_trace = &omnitrace_user_stop_thread_trace_dl;
_cb.push_region = &omnitrace_user_push_region_dl;
_cb.pop_region = &omnitrace_user_pop_region_dl;
_cb.progress = &omnitrace_user_progress_dl;
_cb.push_annotated_region = &omnitrace_user_push_annotated_region_dl;
_cb.pop_annotated_region = &omnitrace_user_pop_annotated_region_dl;
_cb.annotated_progress = &omnitrace_user_annotated_progress_dl;
(*omnitrace_user_configure_f)(OMNITRACE_USER_REPLACE_CONFIG, _cb, nullptr);
}
}
@@ -370,6 +375,9 @@ public:
omnitrace_annotation_t*, size_t) = nullptr;
int (*omnitrace_pop_category_region_f)(omnitrace_category_t, const char*,
omnitrace_annotation_t*, size_t) = nullptr;
void (*omnitrace_progress_f)(const char*) = nullptr;
void (*omnitrace_annotated_progress_f)(const char*, omnitrace_annotation_t*,
size_t) = nullptr;
// libomnitrace-user functions
int (*omnitrace_user_configure_f)(int, user_cb_t, user_cb_t*) = nullptr;
@@ -772,6 +780,12 @@ extern "C"
return OMNITRACE_DL_INVOKE(get_indirect().omnitrace_pop_region_f, name);
}
int omnitrace_user_progress_dl(const char* name)
{
OMNITRACE_DL_INVOKE(get_indirect().omnitrace_progress_f, name);
return 0;
}
int omnitrace_user_push_annotated_region_dl(const char* name,
omnitrace_annotation_t* _annotations,
size_t _annotation_count)
@@ -792,6 +806,28 @@ extern "C"
_annotation_count);
}
int omnitrace_user_annotated_progress_dl(const char* name,
omnitrace_annotation_t* _annotations,
size_t _annotation_count)
{
OMNITRACE_DL_INVOKE(get_indirect().omnitrace_annotated_progress_f, name,
_annotations, _annotation_count);
return 0;
}
void omnitrace_progress(const char* _name)
{
return OMNITRACE_DL_INVOKE(get_indirect().omnitrace_progress_f, _name);
}
void omnitrace_annotated_progress(const char* _name,
omnitrace_annotation_t* _annotations,
size_t _annotation_count)
{
return OMNITRACE_DL_INVOKE(get_indirect().omnitrace_annotated_progress_f, _name,
_annotations, _annotation_count);
}
//----------------------------------------------------------------------------------//
//
// KokkosP
@@ -1033,11 +1069,13 @@ omnitrace_preload()
{
// reset_omnitrace_preload();
omnitrace_preinit_library();
auto _causal = get_env("OMNITRACE_USE_CAUSAL", false);
auto _mode = get_env("OMNITRACE_MODE", (_causal) ? "causal" : "sampling");
OMNITRACE_DL_LOG(1, "[%s] invoking %s(%s)\n", __FUNCTION__, "omnitrace_init",
::omnitrace::join(::omnitrace::QuoteStrings{}, ", ", "sampling",
false, "main")
::omnitrace::join(::omnitrace::QuoteStrings{}, ", ", _mode,
false, "omnitrace")
.c_str());
omnitrace_init("sampling", false, "omnitrace");
omnitrace_init(_mode.c_str(), false, nullptr);
omnitrace_init_tooling();
}
+6
Просмотреть файл
@@ -92,6 +92,9 @@ extern "C"
const char* source) OMNITRACE_PUBLIC_API;
void omnitrace_register_coverage(const char* file, const char* func,
size_t address) OMNITRACE_PUBLIC_API;
void omnitrace_progress(const char*) OMNITRACE_PUBLIC_API;
void omnitrace_annotated_progress(const char*, omnitrace_annotation_t*,
size_t) OMNITRACE_PUBLIC_API;
#if defined(OMNITRACE_DL_SOURCE) && (OMNITRACE_DL_SOURCE > 0)
void omnitrace_preinit_library(void) OMNITRACE_HIDDEN_API;
@@ -111,6 +114,9 @@ extern "C"
int omnitrace_user_pop_annotated_region_dl(const char*, omnitrace_annotation_t*,
size_t) OMNITRACE_HIDDEN_API;
int omnitrace_user_progress_dl(const char* name) OMNITRACE_HIDDEN_API;
int omnitrace_user_annotated_progress_dl(const char*, omnitrace_annotation_t*,
size_t) OMNITRACE_HIDDEN_API;
// KokkosP
struct OMNITRACE_HIDDEN_API SpaceHandle
{
+2 -2
Просмотреть файл
@@ -97,11 +97,11 @@ omnitrace_main(int argc, char** argv, char** envp)
const char* mode = getenv("OMNITRACE_MODE");
omnitrace_init(mode ? mode : "sampling", false, argv[0]);
omnitrace_init_tooling();
omnitrace_push_trace("main(int argc, char** argv)");
omnitrace_push_trace(basename(argv[0]));
int ret = main_real(argc, argv, envp);
omnitrace_pop_trace("main(int argc, char** argv)");
omnitrace_pop_trace(basename(argv[0]));
omnitrace_finalize();
return ret;
+2
Просмотреть файл
@@ -17,6 +17,7 @@ target_sources(
omnitrace-user-library
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/user.cpp
${CMAKE_CURRENT_SOURCE_DIR}/omnitrace/user.h
${CMAKE_CURRENT_SOURCE_DIR}/omnitrace/causal.h
${CMAKE_CURRENT_SOURCE_DIR}/omnitrace/types.h)
target_include_directories(
omnitrace-user-library PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
@@ -38,6 +39,7 @@ omnitrace_strip_target(omnitrace-user-library)
install(
FILES ${CMAKE_CURRENT_SOURCE_DIR}/omnitrace/user.h
${CMAKE_CURRENT_SOURCE_DIR}/omnitrace/types.h
${CMAKE_CURRENT_SOURCE_DIR}/omnitrace/causal.h
${CMAKE_CURRENT_SOURCE_DIR}/omnitrace/categories.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/omnitrace)
+86
Просмотреть файл
@@ -0,0 +1,86 @@
// 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.
/** @file causal.h */
#ifndef OMNITRACE_CAUSAL_H_
#define OMNITRACE_CAUSAL_H_
/**
* @defgroup OMNITRACE_CASUAL_GROUP OmniTrace Causal Profiling Defines
*
* @{
*/
#if !defined(OMNITRACE_CAUSAL_ENABLED)
/** Preprocessor switch to enable/disable instrumentation for causal profiling */
# define OMNITRACE_CAUSAL_ENABLED 1
#endif
#if OMNITRACE_CAUSAL_ENABLED > 0
# include <omnitrace/user.h>
# if !defined(OMNITRACE_CAUSAL_LABEL)
/** @cond OMNITRACE_HIDDEN_DEFINES */
# define OMNITRACE_CAUSAL_STR2(x) # x
# define OMNITRACE_CAUSAL_STR(x) OMNITRACE_CAUSAL_STR2(x)
/** @endcond */
/** Default label for a causal progress point */
# define OMNITRACE_CAUSAL_LABEL __FILE__ ":" OMNITRACE_CAUSAL_STR(__LINE__)
# endif
# if !defined(OMNITRACE_CAUSAL_PROGRESS)
/** Adds a throughput progress point with label `<file>:<line>` */
# define OMNITRACE_CAUSAL_PROGRESS omnitrace_user_progress(OMNITRACE_CAUSAL_LABEL);
# endif
# if !defined(OMNITRACE_CAUSAL_PROGRESS_NAMED)
/** Adds a throughput progress point with user defined label. Each instance should use a
* unique label. */
# define OMNITRACE_CAUSAL_PROGRESS_NAMED(LABEL) omnitrace_user_progress(LABEL);
# endif
# if !defined(OMNITRACE_CAUSAL_BEGIN)
/** Starts a latency progress point (region of interest) with user defined label. Each
* instance should use a unique label. */
# define OMNITRACE_CAUSAL_BEGIN(LABEL) omnitrace_user_push_region(LABEL);
# endif
# if !defined(OMNITRACE_CAUSAL_END)
/** End the latency progress point (region of interest) for the matching user defined
* label. */
# define OMNITRACE_CAUSAL_END(LABEL) omnitrace_user_pop_region(LABEL);
# endif
#else
# if !defined(OMNITRACE_CAUSAL_PROGRESS)
# define OMNITRACE_CAUSAL_PROGRESS
# endif
# if !defined(OMNITRACE_CAUSAL_PROGRESS_NAMED)
# define OMNITRACE_CAUSAL_PROGRESS_NAMED(LABEL)
# endif
# if !defined(OMNITRACE_CAUSAL_BEGIN)
# define OMNITRACE_CAUSAL_BEGIN(LABEL)
# endif
# if !defined(OMNITRACE_CAUSAL_END)
# define OMNITRACE_CAUSAL_END(LABEL)
# endif
#endif
/** @} */
#endif // OMNITRACE_CAUSAL_H_
+7 -1
Просмотреть файл
@@ -49,8 +49,10 @@ extern "C"
omnitrace_trace_func_t stop_thread_trace;
omnitrace_region_func_t push_region;
omnitrace_region_func_t pop_region;
omnitrace_region_func_t progress;
omnitrace_annotated_region_func_t push_annotated_region;
omnitrace_annotated_region_func_t pop_annotated_region;
omnitrace_annotated_region_func_t annotated_progress;
/// @var start_trace
/// @brief callback for enabling tracing globally
@@ -64,10 +66,14 @@ extern "C"
/// @brief callback for starting a trace region
/// @var pop_region
/// @brief callback for ending a trace region
/// @var progress
/// @brief callback for marking an causal profiling event
/// @var push_annotated_region
/// @brief callback for starting a trace region + annotations
/// @var pop_annotated_region
/// @brief callback for ending a trace region + annotations
/// @var annotated_progress
/// @brief callback for marking an causal profiling event + annotations
} omnitrace_user_callbacks_t;
/// @enum OMNITRACE_USER_CONFIGURE_MODE
@@ -104,7 +110,7 @@ extern "C"
#ifndef OMNITRACE_USER_CALLBACKS_INIT
# define OMNITRACE_USER_CALLBACKS_INIT \
{ \
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \
}
#endif
+7
Просмотреть файл
@@ -104,6 +104,13 @@ extern "C"
extern int omnitrace_user_pop_annotated_region(const char*, omnitrace_annotation_t*,
size_t) OMNITRACE_PUBLIC_API;
/// mark causal progress
extern int omnitrace_user_progress(const char*) OMNITRACE_PUBLIC_API;
/// mark causal progress with annotations
extern int omnitrace_user_annotated_progress(const char*, omnitrace_annotation_t*,
size_t) OMNITRACE_PUBLIC_API;
/// @fn int omnitrace_user_configure(omnitrace_user_configure_mode_t mode,
/// omnitrace_user_callbacks_t inp,
/// omnitrace_user_callbacks_t* out)
+15
Просмотреть файл
@@ -80,6 +80,11 @@ extern "C"
return invoke(_callbacks.pop_region, id);
}
int omnitrace_user_progress(const char* id)
{
return invoke(_callbacks.progress, id);
}
int omnitrace_user_push_annotated_region(const char* id, annotation_t* _annotations,
size_t _annotation_count)
{
@@ -94,6 +99,12 @@ extern "C"
_annotation_count);
}
int omnitrace_user_annotated_progress(const char* id, annotation_t* _annotations,
size_t _annotation_count)
{
return invoke(_callbacks.annotated_progress, id, _annotations, _annotation_count);
}
int omnitrace_user_configure(omnitrace_user_configure_mode_t mode,
omnitrace_user_callbacks_t inp,
omnitrace_user_callbacks_t* out)
@@ -121,8 +132,10 @@ extern "C"
_update(_v.stop_thread_trace, inp.stop_thread_trace);
_update(_v.push_region, inp.push_region);
_update(_v.pop_region, inp.pop_region);
_update(_v.progress, inp.progress);
_update(_v.push_annotated_region, inp.push_annotated_region);
_update(_v.pop_annotated_region, inp.pop_annotated_region);
_update(_v.annotated_progress, inp.annotated_progress);
_callbacks = _v;
break;
@@ -141,8 +154,10 @@ extern "C"
_update(_v.stop_thread_trace, inp.stop_thread_trace);
_update(_v.push_region, inp.push_region);
_update(_v.pop_region, inp.pop_region);
_update(_v.progress, inp.progress);
_update(_v.push_annotated_region, inp.push_annotated_region);
_update(_v.pop_annotated_region, inp.pop_annotated_region);
_update(_v.annotated_progress, inp.annotated_progress);
_callbacks = _v;
break;
+3 -1
Просмотреть файл
@@ -32,6 +32,7 @@ target_link_libraries(
$<BUILD_INTERFACE:omnitrace::omnitrace-compile-definitions>
$<BUILD_INTERFACE:omnitrace::omnitrace-perfetto>
$<BUILD_INTERFACE:omnitrace::omnitrace-timemory>
$<BUILD_INTERFACE:omnitrace::omnitrace-elfutils>
$<BUILD_INTERFACE:omnitrace::omnitrace-bfd>
$<BUILD_INTERFACE:omnitrace::omnitrace-mpi>
$<BUILD_INTERFACE:omnitrace::omnitrace-ptl>
@@ -57,7 +58,8 @@ add_library(omnitrace::omnitrace-object-library ALIAS omnitrace-object-library)
target_sources(
omnitrace-object-library
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/library.cpp ${CMAKE_CURRENT_LIST_DIR}/regions.cpp
${CMAKE_CURRENT_LIST_DIR}/api.cpp ${CMAKE_CURRENT_LIST_DIR}/api.hpp)
${CMAKE_CURRENT_LIST_DIR}/progress.cpp ${CMAKE_CURRENT_LIST_DIR}/api.cpp
${CMAKE_CURRENT_LIST_DIR}/api.hpp)
add_subdirectory(library)
+13
Просмотреть файл
@@ -100,6 +100,19 @@ omnitrace_pop_category_region(omnitrace_category_t _category, const char* _name,
return 0;
}
extern "C" void
omnitrace_progress(const char* _name)
{
omnitrace_progress_hidden(_name);
}
extern "C" void
omnitrace_annotated_progress(const char* _name, omnitrace_annotation_t* _annotations,
size_t _annotation_count)
{
omnitrace_annotated_progress_hidden(_name, _annotations, _annotation_count);
}
extern "C" void
omnitrace_init_library(void)
{
+10
Просмотреть файл
@@ -86,6 +86,13 @@ extern "C"
void omnitrace_register_coverage(const char* file, const char* func,
size_t address) OMNITRACE_PUBLIC_API;
/// mark causal progress
void omnitrace_progress(const char*) OMNITRACE_PUBLIC_API;
/// mark causal progress with annotations
void omnitrace_annotated_progress(const char*, omnitrace_annotation_t*,
size_t) OMNITRACE_PUBLIC_API;
// these are the real implementations for internal calling convention
void omnitrace_init_library_hidden(void) OMNITRACE_HIDDEN_API;
bool omnitrace_init_tooling_hidden(void) OMNITRACE_HIDDEN_API;
@@ -108,4 +115,7 @@ extern "C"
const char*) OMNITRACE_HIDDEN_API;
void omnitrace_register_coverage_hidden(const char*, const char*,
size_t) OMNITRACE_HIDDEN_API;
void omnitrace_progress_hidden(const char*) OMNITRACE_HIDDEN_API;
void omnitrace_annotated_progress_hidden(const char*, omnitrace_annotation_t*,
size_t) OMNITRACE_HIDDEN_API;
}
+127 -31
Просмотреть файл
@@ -26,12 +26,16 @@
#include "api.hpp"
#include "common/setup.hpp"
#include "library/causal/data.hpp"
#include "library/causal/experiment.hpp"
#include "library/causal/sampling.hpp"
#include "library/components/exit_gotcha.hpp"
#include "library/components/fork_gotcha.hpp"
#include "library/components/fwd.hpp"
#include "library/components/mpi_gotcha.hpp"
#include "library/components/pthread_gotcha.hpp"
#include "library/components/rocprofiler.hpp"
#include "library/concepts.hpp"
#include "library/config.hpp"
#include "library/coverage.hpp"
#include "library/critical_trace.hpp"
@@ -52,6 +56,7 @@
#include "library/utility.hpp"
#include "omnitrace/categories.h" // in omnitrace-user
#include <timemory/process/threading.hpp>
#include <timemory/signals/signal_handlers.hpp>
#include <timemory/signals/types.hpp>
#include <timemory/hash/types.hpp>
@@ -67,6 +72,8 @@
#include <mutex>
#include <string_view>
#include <utility>
#include <cstdlib>
#include <stdexcept>
using namespace omnitrace;
@@ -92,9 +99,39 @@ namespace
auto _timemory_manager = tim::manager::instance();
auto _timemory_settings = tim::settings::shared_instance();
bool
ensure_initialization(bool _offset, int64_t _glob_n, int64_t _offset_n)
{
auto _exit_info = component::exit_gotcha::get_exit_info();
if(_exit_info.is_known && _exit_info.exit_code != EXIT_SUCCESS) return _offset;
auto _tid = utility::get_thread_index();
auto _max_threads = grow_data(_tid + 1);
if(_tid > 0 && _tid < _max_threads)
{
const auto& _info = thread_info::get();
OMNITRACE_BASIC_VERBOSE_F(3,
"thread info: %s, offset: %s, global counter: %li, "
"offset counter: %li, max threads: %li\n",
std::to_string(static_cast<bool>(_info)).c_str(),
std::to_string(_offset).c_str(), _glob_n, _offset_n,
_max_threads);
}
return _offset;
}
auto
ensure_finalization(bool _static_init = false)
{
if(_static_init)
{
auto _idx = threading::add_callback(&ensure_initialization);
if(_idx < 0)
throw exception<std::runtime_error>("failure adding threading callback");
}
const auto& _info = thread_info::init();
const auto& _tid = _info->index_data;
if(_tid)
@@ -149,6 +186,51 @@ is_system_backend()
using Device = critical_trace::Device;
using Phase = critical_trace::Phase;
template <typename... Tp>
struct fini_bundle
{
using data_type = std::tuple<Tp...>;
TIMEMORY_DEFAULT_OBJECT(fini_bundle)
fini_bundle(std::string_view _label)
: m_label{ _label }
{}
template <typename... Args>
void start(Args&&... _args)
{
TIMEMORY_FOLD_EXPRESSION(tim::operation::start<Tp>{}(
std::get<Tp>(m_data), std::forward<Args>(_args)...));
}
template <typename... Args>
void stop(Args&&... _args)
{
TIMEMORY_FOLD_EXPRESSION(tim::operation::stop<Tp>{}(
std::get<Tp>(m_data), std::forward<Args>(_args)...));
}
std::string as_string(bool _print_prefix = true) const
{
std::stringstream _ss;
if(_print_prefix && m_label.length() > 0) _ss << m_label << " : ";
_ss << timemory::join::join(", ", std::get<Tp>(m_data)...);
return _ss.str();
}
std::string_view m_label = {};
data_type m_data = {};
};
template <typename... Tp>
struct fini_bundle<tim::lightweight_tuple<Tp...>>
{
using base_type = fini_bundle<Tp...>;
};
using fini_bundle_t = typename fini_bundle<main_bundle_t>::base_type;
} // namespace
//======================================================================================//
@@ -365,13 +447,21 @@ omnitrace_init_tooling_hidden()
OMNITRACE_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
process_sampler::setup();
}
if(get_use_sampling())
if(get_use_causal())
{
OMNITRACE_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
sampling::setup();
{
OMNITRACE_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
causal::sampling::setup();
}
push_enable_sampling_on_child_threads(get_use_causal());
sampling::unblock_signals();
}
if(get_use_sampling())
else if(get_use_sampling())
{
{
OMNITRACE_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
sampling::setup();
}
push_enable_sampling_on_child_threads(get_use_sampling());
sampling::unblock_signals();
}
@@ -400,6 +490,8 @@ omnitrace_init_tooling_hidden()
tasking::setup();
if(get_use_causal()) causal::start_experimenting();
if(get_use_timemory())
{
comp::user_global_bundle::global_init();
@@ -457,13 +549,14 @@ omnitrace_init_tooling_hidden()
//======================================================================================//
extern "C" void
omnitrace_init_hidden(const char* _mode, bool _is_binary_rewrite, const char* _argv0)
omnitrace_init_hidden(const char* _mode, bool _is_binary_rewrite, const char* _argv0_c)
{
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 };
auto _argv0 = (_argv0_c) ? std::string{ _argv0_c } : config::get_exe_name();
// 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 &&
@@ -495,21 +588,23 @@ omnitrace_init_hidden(const char* _mode, bool _is_binary_rewrite, const char* _a
"called after omnitrace was initialized. state = %s. Mode-based settings "
"(via -M <MODE> passed to omnitrace exe) may not be properly "
"configured.\n",
_mode, std::to_string(_is_binary_rewrite).c_str(), _argv0,
_mode, std::to_string(_is_binary_rewrite).c_str(), _argv0.c_str(),
std::to_string(get_state()).c_str());
}
}
tracing::get_finalization_functions().emplace_back([_argv0]() {
tracing::get_finalization_functions().emplace_back([_argv0_c]() {
OMNITRACE_CI_THROW(get_state() != State::Active,
"Finalizer function for popping main invoked in non-active "
"state :: state = %s\n",
std::to_string(get_state()).c_str());
if(get_state() == State::Active)
{
auto _name = (_argv0_c) ? std::string{ _argv0_c } : config::get_exe_name();
// if main hasn't been popped yet, pop it
OMNITRACE_BASIC_VERBOSE(2, "Running omnitrace_pop_trace(%s)...\n", _argv0);
omnitrace_pop_trace_hidden(_argv0);
OMNITRACE_BASIC_VERBOSE(2, "Running omnitrace_pop_trace(%s)...\n",
_name.c_str());
omnitrace_pop_trace_hidden(_name.c_str());
}
});
@@ -521,7 +616,7 @@ omnitrace_init_hidden(const char* _mode, bool _is_binary_rewrite, const char* _a
OMNITRACE_CONDITIONAL_BASIC_PRINT_F(
get_debug_env() || get_verbose_env() > 2,
"mode: %s | is binary rewrite: %s | command: %s\n", _mode,
(_is_binary_rewrite) ? "y" : "n", _argv0);
(_is_binary_rewrite) ? "y" : "n", _argv0.c_str());
tim::set_env("OMNITRACE_MODE", _mode, 0);
config::is_binary_rewrite() = _is_binary_rewrite;
@@ -561,6 +656,8 @@ omnitrace_finalize_hidden(void)
{
// disable thread id recycling during finalization
threading::recycle_ids() = false;
// disable initialization callback
threading::remove_callback(&ensure_initialization);
set_thread_state(ThreadState::Completed);
@@ -629,13 +726,6 @@ omnitrace_finalize_hidden(void)
tim::signals::enable_signal_detection({ tim::signals::sys_signal::Interrupt },
[](int) {});
std::string _bundle_name = OMNITRACE_FUNCTION;
comp::user_global_bundle _bundle{ _bundle_name.c_str() };
_bundle.clear();
_bundle.insert<comp::wall_clock, comp::cpu_clock, comp::peak_rss, comp::page_rss,
comp::cpu_util>();
_bundle.start();
OMNITRACE_DEBUG_F("Copying over all timemory hash information to main thread...\n");
// copy these over so that all hashes are known
auto& _hzero = tracing::get_timemory_hash_ids(0);
@@ -663,6 +753,9 @@ omnitrace_finalize_hidden(void)
get_main_bundle()->stop();
}
fini_bundle_t _finalization{};
_finalization.start();
if(get_use_rcclp())
{
OMNITRACE_VERBOSE_F(1, "Shutting down RCCLP...\n");
@@ -741,6 +834,12 @@ omnitrace_finalize_hidden(void)
rocprofiler::rocm_cleanup();
}
if(get_use_causal())
{
OMNITRACE_VERBOSE_F(1, "Shutting down causal sampling...\n");
causal::sampling::shutdown();
}
if(get_use_sampling())
{
OMNITRACE_VERBOSE_F(1, "Shutting down sampling...\n");
@@ -764,6 +863,7 @@ omnitrace_finalize_hidden(void)
// if they are still running (e.g. thread-pool still alive), the
// thread-specific data will be wrong if try to stop them from
// the main thread.
auto _thr_verbose = (config::get_use_causal()) ? 1 : 0;
for(auto& itr : thread_data<thread_bundle_t>::instances())
{
if(itr && itr->get<comp::wall_clock>() &&
@@ -772,7 +872,7 @@ omnitrace_finalize_hidden(void)
std::string _msg = JOIN("", *itr);
auto _pos = _msg.find(">>> ");
if(_pos != std::string::npos) _msg = _msg.substr(_pos + 5);
OMNITRACE_VERBOSE_F(0, "%s\n", _msg.c_str());
OMNITRACE_VERBOSE_F(_thr_verbose, "%s\n", _msg.c_str());
}
}
@@ -785,6 +885,12 @@ omnitrace_finalize_hidden(void)
sampling::post_process();
}
if(get_use_causal())
{
OMNITRACE_VERBOSE_F(1, "Finishing the causal experiments...\n");
causal::finish_experimenting();
}
if(get_use_critical_trace() || (get_use_rocm_smi() && get_use_roctracer()))
{
OMNITRACE_VERBOSE_F(1, "Generating the critical trace...\n");
@@ -929,18 +1035,6 @@ omnitrace_finalize_hidden(void)
}
}
_bundle.stop();
auto _get_metric = [](auto* _v, std::string_view _tail) -> std::string {
return (_v) ? JOIN("", *_v, _tail) : std::string{};
};
OMNITRACE_VERBOSE_F(0, "Finalization metrics: %s%s%s%s%s\n",
_get_metric(_bundle.get<comp::wall_clock>(), ", ").c_str(),
_get_metric(_bundle.get<comp::peak_rss>(), ", ").c_str(),
_get_metric(_bundle.get<comp::page_rss>(), ", ").c_str(),
_get_metric(_bundle.get<comp::cpu_clock>(), ", ").c_str(),
_get_metric(_bundle.get<comp::cpu_util>(), "").c_str());
if(_timemory_manager && _timemory_manager != nullptr)
{
_timemory_manager->add_metadata([](auto& ar) {
@@ -967,6 +1061,8 @@ omnitrace_finalize_hidden(void)
"omnitrace", _cfg);
}
_finalization.stop();
if(_perfetto_output_error)
{
OMNITRACE_THROW("Error opening perfetto output file: %s",
@@ -984,7 +1080,7 @@ omnitrace_finalize_hidden(void)
config::finalize();
OMNITRACE_VERBOSE_F(0, "Finalized\n");
OMNITRACE_VERBOSE_F(0, "Finalized: %s\n", _finalization.as_string().c_str());
}
//======================================================================================//
+23 -3
Просмотреть файл
@@ -9,8 +9,10 @@ set(library_sources
${CMAKE_CURRENT_LIST_DIR}/critical_trace.cpp
${CMAKE_CURRENT_LIST_DIR}/debug.cpp
${CMAKE_CURRENT_LIST_DIR}/dynamic_library.cpp
${CMAKE_CURRENT_LIST_DIR}/kokkosp.cpp
${CMAKE_CURRENT_LIST_DIR}/exception.cpp
${CMAKE_CURRENT_LIST_DIR}/gpu.cpp
${CMAKE_CURRENT_LIST_DIR}/kokkosp.cpp
${CMAKE_CURRENT_LIST_DIR}/locking.cpp
${CMAKE_CURRENT_LIST_DIR}/mproc.cpp
${CMAKE_CURRENT_LIST_DIR}/ompt.cpp
${CMAKE_CURRENT_LIST_DIR}/perfetto.cpp
@@ -19,28 +21,30 @@ set(library_sources
${CMAKE_CURRENT_LIST_DIR}/runtime.cpp
${CMAKE_CURRENT_LIST_DIR}/sampling.cpp
${CMAKE_CURRENT_LIST_DIR}/state.cpp
${CMAKE_CURRENT_LIST_DIR}/thread_data.cpp
${CMAKE_CURRENT_LIST_DIR}/thread_deleter.cpp
${CMAKE_CURRENT_LIST_DIR}/thread_info.cpp
${CMAKE_CURRENT_LIST_DIR}/timemory.cpp
${CMAKE_CURRENT_LIST_DIR}/tracing.cpp)
set(library_headers
${CMAKE_CURRENT_LIST_DIR}/categories.hpp
${CMAKE_CURRENT_LIST_DIR}/config.hpp
${CMAKE_CURRENT_LIST_DIR}/common.hpp
${CMAKE_CURRENT_LIST_DIR}/concepts.hpp
${CMAKE_CURRENT_LIST_DIR}/config.hpp
${CMAKE_CURRENT_LIST_DIR}/coverage.hpp
${CMAKE_CURRENT_LIST_DIR}/cpu_freq.hpp
${CMAKE_CURRENT_LIST_DIR}/critical_trace.hpp
${CMAKE_CURRENT_LIST_DIR}/debug.hpp
${CMAKE_CURRENT_LIST_DIR}/dynamic_library.hpp
${CMAKE_CURRENT_LIST_DIR}/gpu.hpp
${CMAKE_CURRENT_LIST_DIR}/locking.hpp
${CMAKE_CURRENT_LIST_DIR}/mproc.hpp
${CMAKE_CURRENT_LIST_DIR}/ompt.hpp
${CMAKE_CURRENT_LIST_DIR}/perfetto.hpp
${CMAKE_CURRENT_LIST_DIR}/process_sampler.hpp
${CMAKE_CURRENT_LIST_DIR}/ptl.hpp
${CMAKE_CURRENT_LIST_DIR}/rcclp.hpp
${CMAKE_CURRENT_LIST_DIR}/redirect.hpp
${CMAKE_CURRENT_LIST_DIR}/rocm.hpp
${CMAKE_CURRENT_LIST_DIR}/rocm_smi.hpp
${CMAKE_CURRENT_LIST_DIR}/rocprofiler.hpp
@@ -49,6 +53,7 @@ set(library_headers
${CMAKE_CURRENT_LIST_DIR}/sampling.hpp
${CMAKE_CURRENT_LIST_DIR}/state.hpp
${CMAKE_CURRENT_LIST_DIR}/thread_data.hpp
${CMAKE_CURRENT_LIST_DIR}/thread_deleter.hpp
${CMAKE_CURRENT_LIST_DIR}/thread_info.hpp
${CMAKE_CURRENT_LIST_DIR}/timemory.hpp
${CMAKE_CURRENT_LIST_DIR}/tracing.hpp
@@ -81,7 +86,22 @@ if(OMNITRACE_USE_ROCM_SMI)
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/rocm_smi.cpp)
endif()
add_subdirectory(binary)
add_subdirectory(causal)
add_subdirectory(components)
add_subdirectory(containers)
add_subdirectory(coverage)
add_subdirectory(rocm)
add_subdirectory(tracing)
set(ndebug_sources
${CMAKE_CURRENT_LIST_DIR}/components/mpi_gotcha.cpp
${CMAKE_CURRENT_LIST_DIR}/components/backtrace_metrics.cpp
${CMAKE_CURRENT_LIST_DIR}/rcclp.cpp
${CMAKE_CURRENT_LIST_DIR}/kokkosp.cpp
${CMAKE_CURRENT_LIST_DIR}/rocm_smi.cpp
${CMAKE_CURRENT_LIST_DIR}/ompt.cpp)
set_source_files_properties(
${ndebug_sources} DIRECTORY ${PROJECT_SOURCE_DIR}/source/lib/omnitrace
PROPERTIES COMPILE_DEFINITIONS NDEBUG COMPILE_OPTIONS "-g0;-O3")
+22
Просмотреть файл
@@ -0,0 +1,22 @@
#
set(binary_sources
${CMAKE_CURRENT_LIST_DIR}/address_multirange.cpp
${CMAKE_CURRENT_LIST_DIR}/address_range.cpp
${CMAKE_CURRENT_LIST_DIR}/analysis.cpp
${CMAKE_CURRENT_LIST_DIR}/dwarf_entry.cpp
${CMAKE_CURRENT_LIST_DIR}/link_map.cpp
${CMAKE_CURRENT_LIST_DIR}/scope_filter.cpp
${CMAKE_CURRENT_LIST_DIR}/symbol.cpp)
set(binary_headers
${CMAKE_CURRENT_LIST_DIR}/address_multirange.hpp
${CMAKE_CURRENT_LIST_DIR}/address_range.hpp
${CMAKE_CURRENT_LIST_DIR}/analysis.hpp
${CMAKE_CURRENT_LIST_DIR}/dwarf_entry.hpp
${CMAKE_CURRENT_LIST_DIR}/fwd.hpp
${CMAKE_CURRENT_LIST_DIR}/binary_info.hpp
${CMAKE_CURRENT_LIST_DIR}/link_map.hpp
${CMAKE_CURRENT_LIST_DIR}/scope_filter.hpp
${CMAKE_CURRENT_LIST_DIR}/symbol.hpp)
target_sources(omnitrace-object-library PRIVATE ${binary_sources} ${binary_headers})
+75
Просмотреть файл
@@ -0,0 +1,75 @@
// 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/binary/address_multirange.hpp"
#include <algorithm>
#include <cstdint>
#include <string>
#include <utility>
namespace omnitrace
{
namespace binary
{
address_multirange&
address_multirange::operator+=(std::pair<coarse, uintptr_t>&& _v)
{
coarse_range = address_range{ std::min(coarse_range.low, _v.second),
std::max(coarse_range.high, _v.second) };
return *this;
}
address_multirange&
address_multirange::operator+=(std::pair<coarse, address_range>&& _v)
{
coarse_range = address_range{ std::min(coarse_range.low, _v.second.low),
std::max(coarse_range.high, _v.second.high) };
return *this;
}
address_multirange&
address_multirange::operator+=(uintptr_t _v)
{
*this += std::make_pair(coarse{}, _v);
for(auto&& itr : m_fine_ranges)
if(itr.contains(_v)) return *this;
m_fine_ranges.emplace(address_range{ _v });
return *this;
}
address_multirange&
address_multirange::operator+=(address_range _v)
{
*this += std::make_pair(coarse{}, _v);
for(auto&& itr : m_fine_ranges)
if(itr.contains(_v)) return *this;
m_fine_ranges.emplace(_v);
return *this;
}
} // namespace binary
} // namespace omnitrace
+76
Просмотреть файл
@@ -0,0 +1,76 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/binary/address_range.hpp"
#include "library/binary/fwd.hpp"
#include <timemory/utility/macros.hpp>
#include <cstdint>
#include <utility>
namespace omnitrace
{
namespace binary
{
struct address_multirange
{
struct coarse
{};
TIMEMORY_DEFAULT_OBJECT(address_multirange)
address_multirange& operator+=(std::pair<coarse, uintptr_t>&&);
address_multirange& operator+=(std::pair<coarse, address_range>&& _v);
address_multirange& operator+=(uintptr_t _v);
address_multirange& operator+=(address_range _v);
template <typename Tp>
bool contains(Tp&& _v) const;
address_range coarse_range = {};
auto size() const { return m_fine_ranges.size(); }
auto empty() const { return m_fine_ranges.empty(); }
auto range_size() const { return coarse_range.size(); }
private:
std::set<address_range> m_fine_ranges = {};
};
template <typename Tp>
OMNITRACE_INLINE bool
address_multirange::contains(Tp&& _v) const
{
using type = concepts::unqualified_type_t<Tp>;
static_assert(std::is_integral<type>::value ||
std::is_same<type, address_range>::value,
"Error! operator+= supports only integrals or address_ranges");
if(!coarse_range.contains(_v)) return false;
return std::any_of(m_fine_ranges.begin(), m_fine_ranges.end(),
[_v](auto&& itr) { return itr.contains(_v); });
}
} // namespace binary
} // namespace omnitrace
+190
Просмотреть файл
@@ -0,0 +1,190 @@
// 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/binary/address_range.hpp"
#include "library/debug.hpp"
namespace omnitrace
{
namespace binary
{
address_range::address_range(uintptr_t _v)
: low{ _v }
, high{ _v }
{}
address_range::address_range(uintptr_t _low, uintptr_t _high)
: low{ _low }
, high{ _high }
{
TIMEMORY_REQUIRE(high >= low)
<< "Error! address_range high must be >= low. low=" << as_hex(low)
<< ", high=" << as_hex(high) << "\n";
}
bool
address_range::is_range() const
{
return (low < high);
}
std::string
address_range::as_string(int _depth) const
{
std::stringstream _ss{};
_ss << std::hex;
_ss << std::setw(2 * _depth) << "";
_ss.fill('0');
_ss << "0x" << std::setw(16) << low << "-"
<< "0x" << std::setw(16) << high;
return _ss.str();
}
uintptr_t
address_range::size() const
{
return (low == high) ? 1 : (high > low) ? (high - low + 1) : (low - high + 1);
}
bool
address_range::is_valid() const
{
return (low <= high && (low + 1) > 1);
}
bool
address_range::contains(uintptr_t _v) const
{
return (is_range()) ? (low <= _v && high > _v) : (_v == low);
}
bool
address_range::contains(address_range _v) const
{
return (*this == _v) || (contains(_v.low) && contains(_v.high));
}
bool
address_range::overlaps(address_range _v) const
{
if(contains(_v)) return false;
int64_t _lhs_diff = (high - low);
int64_t _rhs_diff = (_v.high - _v.low);
int64_t _diff = (std::max(high, _v.high) - std::min(low, _v.low));
return (_diff < (_lhs_diff + _rhs_diff));
}
bool
address_range::contiguous_with(address_range _v) const
{
return (_v.low == high || low == _v.high);
}
bool
address_range::operator==(address_range _v) const
{
// if arg is range and this is not range, call this function with arg
// if(_v.is_range() && !is_range()) return false;
// check if arg is in range
// if(is_range() && !_v.is_range()) return false;
// both are ranges or both are just address
return std::tie(low, high) == std::tie(_v.low, _v.high);
}
bool
address_range::operator<(address_range _v) const
{
if(is_range() && !_v.is_range())
{
return (low == _v.low) ? true : (low < _v.low);
}
else if(!is_range() && _v.is_range())
{
return (low == _v.low) ? false : (low < _v.low);
}
else if(!is_range() && !_v.is_range())
{
return (low < _v.low);
}
return std::tie(low, high) < std::tie(_v.low, _v.high);
// if(_v.low == _v.high && _v.low >= low && _v.low < high) return false;
// return (low == _v.low) ? (high > _v.high) : (low < _v.low);
}
bool
address_range::operator>(address_range _v) const
{
return !(*this < _v) && !(*this == _v);
}
address_range&
address_range::operator+=(uintptr_t _v)
{
if(is_valid())
{
low += _v;
high += _v;
}
else
{
low = _v;
high = _v;
}
return *this;
}
address_range&
address_range::operator-=(uintptr_t _v)
{
if(is_valid())
{
low -= _v;
high -= _v;
}
else
{
low = _v;
high = _v;
}
return *this;
}
address_range&
address_range::operator+=(address_range _v)
{
if(!contiguous_with(_v))
throw exception<std::runtime_error>(
"attempting to add two address ranges that are not contiguous");
low = std::min(low, _v.low);
high = std::max(high, _v.high);
return *this;
}
hash_value_t
address_range::hash() const
{
return (is_range()) ? tim::get_combined_hash_id(hash_value_t{ low }, high)
: hash_value_t{ low };
}
} // namespace binary
} // namespace omnitrace
+104
Просмотреть файл
@@ -0,0 +1,104 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/binary/fwd.hpp"
#include "library/common.hpp"
#include "library/timemory.hpp"
#include <timemory/hash/types.hpp>
#include <timemory/utility/macros.hpp>
#include <cstdint>
#include <limits>
namespace omnitrace
{
namespace binary
{
struct address_range
{
// set to low to max and high to min to support std::min(...)
// and std::max(...) assignment
uintptr_t low = std::numeric_limits<uintptr_t>::max();
uintptr_t high = std::numeric_limits<uintptr_t>::min();
TIMEMORY_DEFAULT_OBJECT(address_range)
explicit address_range(uintptr_t _v);
address_range(uintptr_t _low, uintptr_t _high);
bool contains(uintptr_t) const;
bool contains(address_range) const;
bool overlaps(address_range) const;
bool contiguous_with(address_range) const;
bool operator==(address_range _v) const;
bool operator!=(address_range _v) const { return !(*this == _v); }
bool operator<(address_range _v) const;
bool operator>(address_range _v) const;
address_range& operator+=(uintptr_t);
address_range& operator-=(uintptr_t);
address_range& operator+=(address_range);
bool is_range() const;
hash_value_t hash() const;
std::string as_string(int _depth = 0) const;
bool is_valid() const;
uintptr_t size() const;
explicit operator bool() const { return is_valid(); }
template <typename ArchiveT>
void serialize(ArchiveT& ar, const unsigned)
{
ar(cereal::make_nvp("low", low));
ar(cereal::make_nvp("high", high));
}
};
} // namespace binary
inline binary::address_range
operator+(binary::address_range _lhs, uintptr_t _v)
{
return (_lhs += _v);
}
inline binary::address_range
operator+(uintptr_t _v, binary::address_range _lhs)
{
return (_lhs += _v);
}
} // namespace omnitrace
namespace std
{
template <>
struct hash<::omnitrace::binary::address_range>
{
using address_range_t = ::omnitrace::binary::address_range;
auto operator()(const address_range_t& _v) const { return _v.hash(); }
auto operator()(address_range_t&& _v) const { return _v.hash(); }
};
} // namespace std
+194
Просмотреть файл
@@ -0,0 +1,194 @@
// 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/config.hpp"
#if !defined(TIMEMORY_USE_BFD)
# error "BFD support not enabled"
#endif
#define PACKAGE "omnitrace"
#include <bfd.h>
#include "library/binary/address_range.hpp"
#include "library/binary/analysis.hpp"
#include "library/binary/binary_info.hpp"
#include "library/binary/dwarf_entry.hpp"
#include "library/binary/fwd.hpp"
#include "library/binary/scope_filter.hpp"
#include "library/binary/symbol.hpp"
#include "library/common.hpp"
#include "library/config.hpp"
#include "library/debug.hpp"
#include "library/state.hpp"
#include "library/utility.hpp"
#include <timemory/log/macros.hpp>
#include <timemory/unwind/bfd.hpp>
#include <timemory/unwind/dlinfo.hpp>
#include <timemory/utility/filepath.hpp>
#include <timemory/utility/join.hpp>
#include <timemory/utility/procfs/maps.hpp>
#include <cstdint>
#include <cstdlib>
#include <dlfcn.h>
#include <regex>
#include <set>
#include <stdexcept>
namespace omnitrace
{
namespace binary
{
namespace
{
binary_info
parse_line_info(const std::string& _name)
{
auto _info = binary_info{};
auto& _bfd = _info.bfd;
_bfd = std::make_shared<bfd_file>(_name);
OMNITRACE_VERBOSE(0, "[binary] Reading line info for '%s'...\n", _name.c_str());
if(_bfd && _bfd->is_good())
{
auto& _section_map = _info.sections;
auto _section_set = std::set<asection*>{};
auto _processed = std::set<uintptr_t>{};
for(auto&& itr : _bfd->get_symbols())
{
if(itr.symsize == 0) continue;
auto& _sym = _info.symbols.emplace_back(symbol{ itr });
// if(itr.symsize == 0) continue;
auto* _section = static_cast<asection*>(itr.section);
_section_set.emplace(_section);
_processed.emplace(itr.address);
_info.ranges.emplace_back(
address_range{ itr.address, itr.address + itr.symsize });
_sym.read_bfd(*_bfd);
}
for(auto* itr : _section_set)
{
auto* _section = const_cast<asection*>(itr);
bfd_vma _section_vma = bfd_section_vma(_section);
bfd_size_type _section_len = bfd_section_size(_section);
auto _section_range =
address_range{ _section_vma, _section_vma + _section_len };
_section_map[_section_range] = _section;
}
TIMEMORY_REQUIRE(_section_set.size() == _section_map.size())
<< "section set size (" << _section_set.size() << ") != section map size ("
<< _section_map.size() << ")\n";
_info.debug_info = dwarf_entry::process_dwarf(_bfd->fd, _info.ranges);
for(auto& itr : _info.symbols)
itr.read_dwarf(_info.debug_info);
_info.sort();
}
OMNITRACE_VERBOSE(1, "[binary] Reading line info for '%s'... %zu entries\n",
_bfd->name.c_str(), _info.symbols.size());
return _info;
}
} // namespace
std::vector<binary_info>
get_binary_info(const std::vector<std::string>& _files,
const std::vector<scope_filter>& _filters)
{
auto _satisfies_filter = [&_filters](auto _scope, const std::string& _value) {
for(const auto& itr : _filters) // NOLINT
{
// if the filter is for the specified scope and itr does not satisfy the
// include/exclude mode, return false
if((itr.scope & _scope) == _scope && !itr(_value)) return false;
}
return true;
};
auto _satisfies_binary_filter = [&_satisfies_filter](const std::string& _value) {
return _satisfies_filter(scope_filter::BINARY_FILTER, _value);
};
// filter function used by procfs::get_contiguous_maps
// ensures that we do not process omnitrace/gotcha/libunwind libraries
// and do not process the libraries outside of the binary scope
auto _filter = [&_satisfies_binary_filter](const procfs::maps& _v) {
if(_v.pathname.empty()) return false;
auto _path = filepath::realpath(_v.pathname, nullptr, false);
return (filepath::exists(_path) && _satisfies_binary_filter(_path));
};
auto _data = std::vector<binary_info>{};
_data.reserve(_files.size());
{
auto _exists = std::set<std::string>{};
for(const auto& itr : _files)
{
auto _filename = filepath::realpath(itr, nullptr, false);
if(filepath::exists(_filename) && _satisfies_binary_filter(_filename) &&
_exists.find(_filename) == _exists.end())
{
_data.emplace_back(parse_line_info(_filename));
_exists.emplace(_filename);
}
}
}
// get the memory maps
auto _maps = procfs::get_contiguous_maps(process::get_id(), _filter, true);
for(auto& itr : _data)
{
for(const auto& mitr : _maps)
if(itr.bfd->name == mitr.pathname) itr.mappings.emplace_back(mitr);
}
for(auto& itr : _data)
{
for(const auto& mitr : itr.mappings)
{
auto mrange = address_range{ mitr.load_address, mitr.last_address };
for(auto& sitr : itr.symbols)
{
auto _addr = sitr.address + mitr.load_address;
if(mrange.contains(_addr)) sitr.load_address = mitr.load_address;
}
}
}
for(auto& itr : _data)
itr.sort();
return _data;
}
} // namespace binary
} // namespace omnitrace
+59
Просмотреть файл
@@ -0,0 +1,59 @@
// 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 "common/defines.h"
#include "library/binary/fwd.hpp"
#include "library/common.hpp"
#include "library/defines.hpp"
#include "library/exception.hpp"
#include <timemory/hash/types.hpp>
#include <timemory/mpl/concepts.hpp>
#include <timemory/tpls/cereal/cereal/cereal.hpp>
#include <timemory/unwind/bfd.hpp>
#include <timemory/unwind/types.hpp>
#include <timemory/utility/procfs/maps.hpp>
#include <cstdint>
#include <deque>
#include <map>
#include <memory>
#include <regex>
#include <string>
#include <tuple>
#include <variant>
namespace omnitrace
{
namespace binary
{
namespace procfs = ::tim::procfs; // NOLINT
using bfd_file = ::tim::unwind::bfd_file;
using hash_value_t = ::tim::hash_value_t;
std::vector<binary_info>
get_binary_info(const std::vector<std::string>&, const std::vector<scope_filter>&);
} // namespace binary
} // namespace omnitrace
+76
Просмотреть файл
@@ -0,0 +1,76 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/binary/address_range.hpp"
#include "library/binary/dwarf_entry.hpp"
#include "library/binary/fwd.hpp"
#include "library/binary/symbol.hpp"
#include "library/utility.hpp"
#include <timemory/utility/procfs/maps.hpp>
#include <deque>
#include <memory>
#include <vector>
namespace omnitrace
{
namespace binary
{
struct binary_info
{
std::shared_ptr<bfd_file> bfd = {};
std::vector<procfs::maps> mappings = {};
std::deque<symbol> symbols = {};
std::deque<dwarf_entry> debug_info = {};
std::vector<address_range> ranges = {};
std::unordered_map<address_range, void*> sections = {};
void sort();
template <typename RetT = void>
RetT* find_section(uintptr_t);
};
inline void
binary_info::sort()
{
utility::filter_sort_unique(mappings);
utility::filter_sort_unique(symbols);
utility::filter_sort_unique(ranges);
utility::filter_sort_unique(debug_info);
}
template <typename RetT>
inline RetT*
binary_info::find_section(uintptr_t _addr)
{
for(const auto& sitr : sections)
{
if(sitr.first.contains(_addr)) return static_cast<RetT*>(sitr.second);
}
return nullptr;
}
} // namespace binary
} // namespace omnitrace
+201
Просмотреть файл
@@ -0,0 +1,201 @@
// 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/binary/dwarf_entry.hpp"
#include "library/binary/fwd.hpp"
#include "library/timemory.hpp"
#include "library/utility.hpp"
#include <dwarf.h>
#include <elfutils/libdw.h>
namespace omnitrace
{
namespace binary
{
namespace
{
using utility::combine;
auto
get_dwarf_address_ranges(Dwarf_Die* _die)
{
auto _ranges = std::vector<address_range>{};
if(dwarf_tag(_die) != DW_TAG_compile_unit) return _ranges;
Dwarf_Addr _low_pc;
Dwarf_Addr _high_pc;
dwarf_lowpc(_die, &_low_pc);
dwarf_highpc(_die, &_high_pc);
_ranges.emplace_back(address_range{ _low_pc, _high_pc });
Dwarf_Addr _base_addr;
ptrdiff_t _offset = 0;
do
{
_ranges.emplace_back(address_range{ 0, 0 });
} while((_offset = dwarf_ranges(_die, _offset, &_base_addr, &_ranges.back().low,
&_ranges.back().high)) > 0);
// will always have one extra
_ranges.pop_back();
return _ranges;
}
auto
get_dwarf_entry(Dwarf_Die* _die)
{
auto _line_info = std::deque<dwarf_entry>{};
if(dwarf_tag(_die) != DW_TAG_compile_unit) return _line_info;
Dwarf_Lines* _lines = nullptr;
size_t _num_lines = 0;
if(dwarf_getsrclines(_die, &_lines, &_num_lines) == 0)
{
_line_info.resize(_num_lines);
for(size_t j = 0; j < _num_lines; ++j)
{
auto& itr = _line_info.at(j);
auto* _line = dwarf_onesrcline(_lines, j);
if(_line)
{
int _lineno = 0;
uintptr_t _address = 0;
dwarf_lineno(_line, &_lineno);
dwarf_linecol(_line, &itr.col);
dwarf_linebeginstatement(_line, &itr.begin_statement);
dwarf_lineendsequence(_line, &itr.end_sequence);
dwarf_lineblock(_line, &itr.line_block);
dwarf_lineepiloguebegin(_line, &itr.epilogue_begin);
dwarf_lineprologueend(_line, &itr.prologue_end);
dwarf_lineisa(_line, &itr.isa);
dwarf_linediscriminator(_line, &itr.discriminator);
dwarf_lineaddr(_line, &_address);
itr.address = address_range{ _address };
if(_lineno > 0) itr.line = _lineno;
const auto* _file = dwarf_linesrc(_line, nullptr, nullptr);
if(!_file) _file = dwarf_diename(_die);
itr.file = filepath::realpath(_file, nullptr, false);
}
}
}
return _line_info;
}
} // namespace
bool
dwarf_entry::operator<(const dwarf_entry& _rhs) const
{
return std::tie(address, line, col, discriminator) <
std::tie(_rhs.address, _rhs.line, _rhs.col, _rhs.discriminator);
}
bool
dwarf_entry::operator==(const dwarf_entry& _rhs) const
{
return std::tie(address, line, col, discriminator, vliw_op_index, isa, file) ==
std::tie(_rhs.address, _rhs.line, _rhs.col, _rhs.discriminator,
_rhs.vliw_op_index, _rhs.isa, _rhs.file);
}
bool
dwarf_entry::operator!=(const dwarf_entry& _rhs) const
{
return !(*this == _rhs);
}
bool
dwarf_entry::is_valid() const
{
return (*this != dwarf_entry{} && !file.empty());
}
std::deque<dwarf_entry>
dwarf_entry::process_dwarf(int _fd, std::vector<address_range>& _ranges)
{
auto* _dwarf_v = dwarf_begin(_fd, DWARF_C_READ);
auto _line_info = std::deque<dwarf_entry>{};
size_t cu_header_size = 0;
Dwarf_Off cu_off = 0;
Dwarf_Off next_cu_off = 0;
for(; dwarf_nextcu(_dwarf_v, cu_off, &next_cu_off, &cu_header_size, nullptr, nullptr,
nullptr) == 0;
cu_off = next_cu_off)
{
Dwarf_Off cu_die_off = cu_off + cu_header_size;
Dwarf_Die cu_die;
if(dwarf_offdie(_dwarf_v, cu_die_off, &cu_die) != nullptr)
{
Dwarf_Die* _die = &cu_die;
if(dwarf_tag(_die) == DW_TAG_compile_unit)
{
combine(_line_info, get_dwarf_entry(_die));
combine(_ranges, get_dwarf_address_ranges(_die));
}
}
}
dwarf_end(_dwarf_v);
utility::filter_sort_unique(_line_info);
utility::filter_sort_unique(_ranges);
return _line_info;
}
template <typename ArchiveT>
void
dwarf_entry::serialize(ArchiveT& ar, const unsigned int)
{
#define OMNITRACE_SERIALIZE_MEMBER(MEMBER) ar(::tim::cereal::make_nvp(#MEMBER, MEMBER));
OMNITRACE_SERIALIZE_MEMBER(file)
OMNITRACE_SERIALIZE_MEMBER(line)
OMNITRACE_SERIALIZE_MEMBER(col)
OMNITRACE_SERIALIZE_MEMBER(address)
OMNITRACE_SERIALIZE_MEMBER(discriminator)
// OMNITRACE_SERIALIZE_MEMBER(begin_statement)
// OMNITRACE_SERIALIZE_MEMBER(end_sequence)
// OMNITRACE_SERIALIZE_MEMBER(line_block)
// OMNITRACE_SERIALIZE_MEMBER(prologue_end)
// OMNITRACE_SERIALIZE_MEMBER(epilogue_begin)
// OMNITRACE_SERIALIZE_MEMBER(vliw_op_index)
// OMNITRACE_SERIALIZE_MEMBER(isa)
}
template void
dwarf_entry::serialize<cereal::JSONInputArchive>(cereal::JSONInputArchive&,
const unsigned int);
template void
dwarf_entry::serialize<cereal::MinimalJSONOutputArchive>(
cereal::MinimalJSONOutputArchive&, const unsigned int);
template void
dwarf_entry::serialize<cereal::PrettyJSONOutputArchive>(cereal::PrettyJSONOutputArchive&,
const unsigned int);
} // namespace binary
} // namespace omnitrace
+62
Просмотреть файл
@@ -0,0 +1,62 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/binary/address_range.hpp"
#include "library/binary/fwd.hpp"
namespace omnitrace
{
namespace binary
{
struct dwarf_entry
{
TIMEMORY_DEFAULT_OBJECT(dwarf_entry)
bool begin_statement = false;
bool end_sequence = false;
bool line_block = false;
bool prologue_end = false;
bool epilogue_begin = false;
unsigned int line = 0;
int col = 0;
unsigned int vliw_op_index = 0;
unsigned int isa = 0;
unsigned int discriminator = 0;
address_range address = { 0, 0 };
std::string file = {};
bool is_valid() const;
bool operator<(const dwarf_entry&) const;
bool operator==(const dwarf_entry&) const;
bool operator!=(const dwarf_entry&) const;
explicit operator bool() const { return is_valid(); }
static std::deque<dwarf_entry> process_dwarf(int _fd, std::vector<address_range>&);
template <typename ArchiveT>
void serialize(ArchiveT&, const unsigned int);
};
} // namespace binary
} // namespace omnitrace
+62
Просмотреть файл
@@ -0,0 +1,62 @@
// 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 "common/defines.h"
#include "library/common.hpp"
#include "library/defines.hpp"
#include "library/exception.hpp"
#include <timemory/hash/types.hpp>
#include <timemory/mpl/concepts.hpp>
#include <timemory/tpls/cereal/cereal/cereal.hpp>
#include <timemory/unwind/bfd.hpp>
#include <timemory/unwind/types.hpp>
#include <timemory/utility/procfs/maps.hpp>
#include <cstdint>
#include <deque>
#include <map>
#include <memory>
#include <regex>
#include <string>
#include <tuple>
#include <variant>
namespace omnitrace
{
namespace binary
{
namespace procfs = ::tim::procfs; // NOLINT
using bfd_file = ::tim::unwind::bfd_file;
using hash_value_t = ::tim::hash_value_t;
struct address_range;
struct address_multirange;
struct scope_filter;
struct symbol;
struct dwarf_entry;
struct binary_info;
} // namespace binary
} // namespace omnitrace
+133
Просмотреть файл
@@ -0,0 +1,133 @@
// 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/binary/link_map.hpp"
#include "library/common.hpp"
#include "library/config.hpp"
#include "library/debug.hpp"
#include "library/timemory.hpp"
#include <timemory/utility/filepath.hpp>
#include <cstdint>
#include <dlfcn.h>
#include <link.h>
#include <set>
#include <string>
#include <string_view>
namespace omnitrace
{
namespace binary
{
std::set<link_file>
get_link_map(const char* _lib, const std::string& _exclude_linked_by,
const std::string& _exclude_re)
{
auto _get_chain = [](const char* _name) {
void* _handle = dlopen(_name, RTLD_LAZY | RTLD_NOLOAD);
auto _chain = std::set<std::string>{};
if(_handle)
{
struct link_map* _link_map = nullptr;
dlinfo(_handle, RTLD_DI_LINKMAP, &_link_map);
struct link_map* _next = _link_map;
while(_next)
{
if(_name == nullptr && _next == _link_map &&
std::string_view{ _next->l_name }.empty())
{
// only insert exe name if dlopened the exe and
// empty name is first entry
_chain.emplace(config::get_exe_realpath());
}
else if(!std::string_view{ _next->l_name }.empty())
{
_chain.emplace(_next->l_name);
}
_next = _next->l_next;
}
}
return _chain;
};
auto _full_chain = _get_chain(_lib);
auto _excl_chain = (_exclude_linked_by.empty())
? std::set<std::string>{}
: _get_chain(_exclude_linked_by.c_str());
auto _fini_chain = std::set<link_file>{};
for(const auto& itr : _full_chain)
{
if(_excl_chain.find(itr) == _excl_chain.end())
{
if(_exclude_re.empty() || !std::regex_search(itr, std::regex{ _exclude_re }))
_fini_chain.emplace(itr);
else
_excl_chain.emplace(itr);
}
}
auto _name = (!_lib) ? config::get_exe_realpath() : std::string{ _lib };
for(const auto& itr : _fini_chain)
{
OMNITRACE_VERBOSE(2, "[linkmap][%s]: %s\n", filepath::basename(_name),
itr.real().c_str());
}
for(const auto& itr : _excl_chain)
{
OMNITRACE_VERBOSE(3, "[linkmap][%s]: %s\n", _exclude_linked_by.c_str(),
link_file{ itr }.real().c_str());
}
return _fini_chain;
}
bool
link_file::operator<(const link_file& _rhs) const
{
if(name == _rhs.name) return false;
auto _lhs_base = base();
auto _lhs_real = real();
auto _rhs_base = _rhs.base();
auto _rhs_real = _rhs.real();
if(_lhs_base == _rhs_base || _lhs_real == _rhs_real) return false;
return (_lhs_real < _rhs_real);
}
std::string_view
link_file::base() const
{
return std::string_view{ filepath::basename(name) };
}
std::string
link_file::real() const
{
return filepath::realpath(name, nullptr, false);
}
} // namespace binary
} // namespace omnitrace
+54
Просмотреть файл
@@ -0,0 +1,54 @@
// 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 <cstdint>
#include <set>
#include <string>
#include <string_view>
namespace omnitrace
{
namespace binary
{
struct link_file
{
link_file(std::string_view&& _v)
: name{ _v }
{}
std::string_view base() const;
std::string real() const;
bool operator<(const link_file&) const;
std::string name = {};
};
// default parameters: get the linked binaries for the exe but exclude the linked binaries
// from libomnitrace
std::set<link_file>
get_link_map(const char* _lib = nullptr,
const std::string& _exclude_linked_by = "libomnitrace.so",
const std::string& _exclude_re = "libomnitrace-([a-zA-Z]+)\\.so");
} // namespace binary
} // namespace omnitrace
+46
Просмотреть файл
@@ -0,0 +1,46 @@
// 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/binary/scope_filter.hpp"
#include "library/exception.hpp"
#include <regex>
namespace omnitrace
{
namespace binary
{
bool
scope_filter::operator()(std::string_view _value) const
{
if(mode == FILTER_INCLUDE)
return (expression.empty())
? true
: std::regex_search(_value.data(), std::regex{ expression });
else if(mode == FILTER_EXCLUDE)
return (expression.empty())
? false
: !std::regex_search(_value.data(), std::regex{ expression });
throw exception<std::runtime_error>{ "invalid scope filter mode" };
}
} // namespace binary
} // namespace omnitrace
+75
Просмотреть файл
@@ -0,0 +1,75 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/defines.hpp"
#include <cstdint>
#include <string>
namespace omnitrace
{
namespace binary
{
struct scope_filter
{
enum filter_mode : uint8_t
{
FILTER_INCLUDE = 0,
FILTER_EXCLUDE
};
enum filter_scope : uint8_t
{
UNIVERSAL_FILTER = (1 << 0),
BINARY_FILTER = (1 << 1),
SOURCE_FILTER = (1 << 2),
FUNCTION_FILTER = (1 << 3)
};
filter_mode mode = FILTER_INCLUDE;
filter_scope scope = UNIVERSAL_FILTER;
std::string expression = {};
bool operator()(std::string_view _value) const;
template <typename ContainerT>
static bool satisfies_filter(const ContainerT&, filter_scope,
std::string_view) OMNITRACE_PURE;
};
template <typename ContainerT>
inline bool
scope_filter::satisfies_filter(const ContainerT& _filters, filter_scope _scope,
std::string_view _value)
{
for(const auto& itr : _filters) // NOLINT
{
// if the filter is for the specified scope and itr does not satisfy the
// include/exclude mode, return false
if((itr.scope & _scope) > 0 && !itr(_value)) return false;
}
return true;
}
} // namespace binary
} // namespace omnitrace
+356
Просмотреть файл
@@ -0,0 +1,356 @@
// 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/config.hpp"
#include "library/debug.hpp"
#if !defined(TIMEMORY_USE_BFD)
# error "BFD support not enabled"
#endif
#define PACKAGE "omnitrace"
#define L_LNNO_SIZE 4
#include <bfd.h>
#include <coff/external.h>
#include <coff/internal.h>
#include <cstddef>
#include <cstdio>
#include <dwarf.h>
#include <elf-bfd.h>
#include <elfutils/libdw.h>
#include <libcoff.h>
#include "library/binary/dwarf_entry.hpp"
#include "library/binary/fwd.hpp"
#include "library/binary/scope_filter.hpp"
#include "library/binary/symbol.hpp"
#include "library/timemory.hpp"
#include "library/utility.hpp"
#include <timemory/mpl/concepts.hpp>
namespace omnitrace
{
namespace binary
{
namespace
{
std::vector<inlined_symbol>
read_inliner_info(bfd* _inp)
{
auto _data = std::vector<inlined_symbol>{};
while(true)
{
const char* _file = nullptr;
const char* _func = nullptr;
unsigned int _line = 0;
if(bfd_find_inliner_info(_inp, &_file, &_func, &_line) != 0)
{
if(_file && _func && _line > 0)
_data.emplace_back(inlined_symbol{
_line, filepath::realpath(_file, nullptr, false), _func });
}
else
{
break;
}
}
return _data;
}
} // namespace
symbol::symbol(const base_type& _v)
: base_type{ _v }
, address{ _v.address, _v.address + _v.symsize }
{}
bool
symbol::operator==(const symbol& _rhs) const
{
return std::tie(address, base_type::name) ==
std::tie(_rhs.address, _rhs.base_type::name);
}
bool
symbol::operator<(const symbol& _rhs) const
{
return std::tie(address, base_type::binding, base_type::visibility, base_type::name) <
std::tie(_rhs.address, _rhs.base_type::binding, base_type::visibility,
base_type::name);
}
bool
symbol::operator()(const std::vector<scope_filter>& _filters) const
{
using sf = scope_filter;
// apply filters to the main symbol
return (sf::satisfies_filter(_filters, sf::FUNCTION_FILTER, demangle(func)) &&
(sf::satisfies_filter(_filters, sf::SOURCE_FILTER, file) ||
sf::satisfies_filter(_filters, sf::SOURCE_FILTER, join(':', file, line))));
}
symbol&
symbol::operator+=(const symbol& _rhs)
{
if(address.contiguous_with(_rhs.address) &&
std::tie(line, load_address, func, file) ==
std::tie(_rhs.line, _rhs.load_address, _rhs.func, _rhs.file))
{
address += _rhs.address;
utility::combine(inlines, _rhs.inlines);
utility::combine(dwarf_info, _rhs.dwarf_info);
}
else
{
throw exception<std::runtime_error>("incompatible symbol+=");
}
return *this;
}
symbol::operator bool() const
{
return address.is_valid() && (file.length() + func.length() + line) > 0;
}
size_t
symbol::read_dwarf(const std::deque<dwarf_entry>& _info)
{
for(const auto& itr : _info)
{
if(address.contains(itr.address)) dwarf_info.emplace_back(itr);
}
// make sure the dwarf info is sorted by address (low to high)
std::sort(dwarf_info.begin(), dwarf_info.end(),
[](const dwarf_entry& _lhs, const dwarf_entry& _rhs) {
return _lhs.address < _rhs.address;
});
// helper for getting the end address
auto _get_next_address = [&](auto nitr, uintptr_t _low) {
while(++nitr != dwarf_info.end())
{
if(nitr->address.low > _low)
{
return nitr->address.low;
}
}
// return the end address of the symbol
return address.high;
};
// convert the single addresses into ranges
for(auto itr = dwarf_info.begin(); itr != dwarf_info.end(); ++itr)
{
// if address is already a range, do not update it
if(!itr->address.is_range())
itr->address = address_range{ itr->address.low,
_get_next_address(itr, itr->address.low) };
}
return dwarf_info.size();
}
bool
symbol::read_bfd(bfd_file& _bfd)
{
auto* _section = static_cast<asection*>(section);
bfd_vma _vma = bfd_section_vma(_section);
bfd_size_type _size = bfd_section_size(_section);
auto& _pc = address.low;
auto& _pc_end = address.high;
if(_pc < _vma || _pc >= _vma + _size) return false;
// add one to vma + size because address range is exclusive of last address
if(_pc_end > _vma + _size) _pc_end = (_vma + _size);
auto* _inp = static_cast<bfd*>(_bfd.data);
auto* _syms = reinterpret_cast<asymbol**>(_bfd.syms);
{
const char* _file = nullptr;
const char* _func = nullptr;
unsigned int _line = 0;
unsigned int _discriminator = 0;
// if(bfd_find_nearest_line(_inp, _section, _syms, _pc - _vma, &_file,
// &_func, &_line) != 0)
if(bfd_find_nearest_line_discriminator(_inp, _section, _syms, _pc - _vma, &_file,
&_func, &_line, &_discriminator) != 0)
{
if(_file) file = _file;
if(_func) func = _func;
if(_file && strnlen(_file, 1) > 0)
file = _file;
else if(!_file || strnlen(_file, 1) == 0)
file = bfd_get_filename(_inp);
if(!func.empty())
{
file = filepath::realpath(file, nullptr, false);
line = _line;
inlines = read_inliner_info(_inp);
return true;
}
}
}
return false;
}
symbol
symbol::clone() const
{
auto _sym = symbol{ static_cast<base_type>(*this) };
_sym.line = line;
_sym.load_address = load_address;
_sym.address = address;
_sym.func = func;
_sym.file = file;
return _sym;
}
template <typename Tp>
Tp
symbol::get_inline_symbols(const std::vector<scope_filter>& _filters) const
{
using sf = scope_filter;
using value_type = typename Tp::value_type;
auto _data = Tp{};
for(const auto& itr : inlines)
{
if(sf::satisfies_filter(_filters, sf::FUNCTION_FILTER, demangle(itr.func)) &&
(sf::satisfies_filter(_filters, sf::SOURCE_FILTER, itr.file) ||
sf::satisfies_filter(_filters, sf::SOURCE_FILTER,
join(':', itr.file, itr.line))))
{
if constexpr(concepts::is_unqualified_same<value_type, symbol>::value)
{
auto _sym = clone();
_sym.func = itr.func;
_sym.line = itr.line;
_sym.file = itr.file;
_data.emplace_back(_sym);
}
else if constexpr(concepts::is_unqualified_same<value_type,
inlined_symbol>::value)
{
_data.emplace_back(itr);
}
}
}
return _data;
}
template <typename Tp>
Tp
symbol::get_debug_line_info(const std::vector<scope_filter>& _filters) const
{
using sf = scope_filter;
using value_type = typename Tp::value_type;
auto _data = Tp{};
if(sf::satisfies_filter(_filters, sf::FUNCTION_FILTER, demangle(func)))
{
for(const auto& itr : dwarf_info)
{
if(sf::satisfies_filter(_filters, sf::SOURCE_FILTER, itr.file) ||
sf::satisfies_filter(_filters, sf::SOURCE_FILTER,
join(':', itr.file, itr.line)))
{
if constexpr(concepts::is_unqualified_same<value_type, symbol>::value)
{
auto _sym = clone();
_sym.address = itr.address;
_sym.file = itr.file;
_sym.line = itr.line;
_data.emplace_back(_sym);
}
else if constexpr(concepts::is_unqualified_same<value_type,
dwarf_entry>::value)
{
_data.emplace_back(itr);
}
}
}
}
return _data;
}
template <typename ArchiveT>
void
inlined_symbol::serialize(ArchiveT& ar, const unsigned int)
{
using ::tim::cereal::make_nvp;
ar(make_nvp("func", func), make_nvp("file", file), make_nvp("line", line));
}
template <typename ArchiveT>
void
symbol::serialize(ArchiveT& ar, const unsigned int)
{
using ::tim::cereal::make_nvp;
ar(make_nvp("address", address), make_nvp("load_address", load_address),
make_nvp("line", line), make_nvp("func", func), make_nvp("file", file),
make_nvp("inlines", inlines), make_nvp("dwarf_info", dwarf_info));
if constexpr(concepts::is_output_archive<ArchiveT>::value)
ar(cereal::make_nvp("dfunc", demangle(func)));
}
template void
symbol::serialize<cereal::JSONInputArchive>(cereal::JSONInputArchive&,
const unsigned int);
template void
symbol::serialize<cereal::MinimalJSONOutputArchive>(cereal::MinimalJSONOutputArchive&,
const unsigned int);
template void
symbol::serialize<cereal::PrettyJSONOutputArchive>(cereal::PrettyJSONOutputArchive&,
const unsigned int);
template std::deque<symbol>
symbol::get_inline_symbols<std::deque<symbol>>(
const std::vector<scope_filter>& _filters) const;
template std::vector<inlined_symbol>
symbol::get_inline_symbols<std::vector<inlined_symbol>>(
const std::vector<scope_filter>& _filters) const;
template std::deque<symbol>
symbol::get_debug_line_info<std::deque<symbol>>(
const std::vector<scope_filter>& _filters) const;
template std::vector<dwarf_entry>
symbol::get_debug_line_info<std::vector<dwarf_entry>>(
const std::vector<scope_filter>& _filters) const;
} // namespace binary
} // namespace omnitrace
+96
Просмотреть файл
@@ -0,0 +1,96 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/binary/address_range.hpp"
#include "library/binary/fwd.hpp"
#include <timemory/unwind/bfd.hpp>
#include <cstdint>
#include <deque>
#include <string_view>
#include <vector>
namespace omnitrace
{
namespace binary
{
struct inlined_symbol
{
unsigned int line = 0;
std::string file = {};
std::string func = {};
template <typename ArchiveT>
void serialize(ArchiveT&, const unsigned int);
};
struct symbol : private tim::unwind::bfd_file::symbol
{
using base_type = tim::unwind::bfd_file::symbol;
symbol() = default;
symbol(const base_type& _v);
~symbol() = default;
symbol(const symbol&) = default;
symbol(symbol&&) noexcept = default;
symbol& operator=(const symbol&) = default;
symbol& operator=(symbol&&) noexcept = default;
bool operator==(const symbol&) const;
bool operator<(const symbol&) const;
bool operator()(const std::vector<scope_filter>&) const;
symbol& operator+=(const symbol&);
explicit operator bool() const;
bool read_bfd(bfd_file&);
size_t read_dwarf(const std::deque<dwarf_entry>&);
address_range ipaddr() const { return address + load_address; }
symbol clone() const;
template <typename Tp = std::deque<symbol>>
Tp get_inline_symbols(const std::vector<scope_filter>&) const;
template <typename Tp = std::deque<symbol>>
Tp get_debug_line_info(const std::vector<scope_filter>&) const;
template <typename ArchiveT>
void serialize(ArchiveT&, const unsigned int);
using base_type::binding;
using base_type::section;
using base_type::visibility;
unsigned int line = 0;
uintptr_t load_address = 0;
address_range address = {};
std::string func = {};
std::string file = {};
std::vector<inlined_symbol> inlines = {};
std::vector<dwarf_entry> dwarf_info = {};
};
} // namespace binary
} // namespace omnitrace
+22
Просмотреть файл
@@ -0,0 +1,22 @@
#
set(causal_sources
${CMAKE_CURRENT_LIST_DIR}/data.cpp
${CMAKE_CURRENT_LIST_DIR}/delay.cpp
${CMAKE_CURRENT_LIST_DIR}/experiment.cpp
# ${CMAKE_CURRENT_LIST_DIR}/perf.cpp
${CMAKE_CURRENT_LIST_DIR}/sample_data.cpp
${CMAKE_CURRENT_LIST_DIR}/sampling.cpp
${CMAKE_CURRENT_LIST_DIR}/selected_entry.cpp)
set(causal_headers
${CMAKE_CURRENT_LIST_DIR}/data.hpp
${CMAKE_CURRENT_LIST_DIR}/delay.hpp
${CMAKE_CURRENT_LIST_DIR}/experiment.hpp
# ${CMAKE_CURRENT_LIST_DIR}/perf.hpp
${CMAKE_CURRENT_LIST_DIR}/sample_data.hpp
${CMAKE_CURRENT_LIST_DIR}/sampling.hpp
${CMAKE_CURRENT_LIST_DIR}/selected_entry.hpp)
target_sources(omnitrace-object-library PRIVATE ${causal_sources} ${causal_headers})
add_subdirectory(components)
+16
Просмотреть файл
@@ -0,0 +1,16 @@
#
set(component_sources
${CMAKE_CURRENT_LIST_DIR}/backtrace.cpp
${CMAKE_CURRENT_LIST_DIR}/blocking_gotcha.cpp
${CMAKE_CURRENT_LIST_DIR}/causal_gotcha.cpp
${CMAKE_CURRENT_LIST_DIR}/progress_point.cpp
${CMAKE_CURRENT_LIST_DIR}/unblocking_gotcha.cpp)
set(component_headers
${CMAKE_CURRENT_LIST_DIR}/backtrace.hpp
${CMAKE_CURRENT_LIST_DIR}/blocking_gotcha.hpp
${CMAKE_CURRENT_LIST_DIR}/causal_gotcha.hpp
${CMAKE_CURRENT_LIST_DIR}/progress_point.hpp
${CMAKE_CURRENT_LIST_DIR}/unblocking_gotcha.hpp)
target_sources(omnitrace-object-library PRIVATE ${component_sources} ${component_headers})
+227
Просмотреть файл
@@ -0,0 +1,227 @@
// 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/causal/components/backtrace.hpp"
#include "library/causal/data.hpp"
#include "library/causal/delay.hpp"
#include "library/causal/experiment.hpp"
#include "library/concepts.hpp"
#include "library/config.hpp"
#include "library/debug.hpp"
#include "library/runtime.hpp"
#include "library/state.hpp"
#include "library/thread_data.hpp"
#include "library/thread_info.hpp"
#include "library/tracing.hpp"
#include "library/utility.hpp"
#include <timemory/components/timing/backends.hpp>
#include <timemory/components/timing/wall_clock.hpp>
#include <timemory/mpl/concepts.hpp>
#include <timemory/mpl/types.hpp>
#include <timemory/process/threading.hpp>
#include <timemory/units.hpp>
#include <timemory/utility/backtrace.hpp>
#include <atomic>
#include <ctime>
#include <type_traits>
namespace omnitrace
{
namespace causal
{
namespace component
{
namespace
{
using ::tim::backtrace::get_unw_signal_frame_stack_raw;
auto&
get_delay_statistics()
{
using thread_data_t =
thread_data<identity<tim::statistics<int64_t>>, category::sampling>;
static_assert(
use_placement_new_when_generating_unique_ptr<thread_data_t>::value,
"delay statistics thread data should use placement new to allocate unique_ptr");
static auto& _v = thread_data_t::instance(construct_on_init{});
return _v;
}
auto&
get_in_use()
{
using thread_data_t = thread_data<identity<bool>, category::sampling>;
static_assert(
use_placement_new_when_generating_unique_ptr<thread_data_t>::value,
"sampling is_use thread data should use placement new to allocate unique_ptr");
static auto& _v = thread_data_t::instance(construct_on_init{});
return _v;
}
struct scoped_in_use
{
scoped_in_use(int64_t _tid = utility::get_thread_index())
: value{ get_in_use()->at(_tid) }
{
value = true;
}
~scoped_in_use() { value = false; }
bool& value;
};
auto
is_in_use(int64_t _tid = threading::get_id())
{
return get_in_use()->at(_tid);
}
} // namespace
void
backtrace::start()
{
// do not delete these lines. The thread data needs to be allocated
// before it is called in sampler or else a deadlock will occur when
// the sample interrupts a malloc call
(void) get_delay_statistics();
(void) get_in_use();
}
void
backtrace::stop()
{}
void
backtrace::sample(int _sig)
{
constexpr size_t depth = ::omnitrace::causal::unwind_depth;
constexpr int64_t ignore_depth = ::omnitrace::causal::unwind_offset;
// update the last sample for backtrace signal(s) even when in use
static thread_local int64_t _last_sample = 0;
if(is_in_use())
{
if(_sig == get_realtime_signal()) _last_sample = tracing::now();
return;
}
scoped_in_use _in_use{};
m_index = causal::experiment::get_index();
m_stack = get_unw_signal_frame_stack_raw<depth, ignore_depth>();
// the batch handler timer delivers a signal according to the thread CPU
// clock, ensuring that setting the current selection and processing the
// delays only happens when the thread is active
if(_sig == get_cputime_signal())
{
if(!causal::experiment::is_active())
causal::set_current_selection(m_stack);
else
causal::delay::process();
}
else if(_sig == get_realtime_signal())
{
auto _this_sample = tracing::now();
auto& _period_stat = get_delay_statistics()->at(threading::get_id());
if(_last_sample > 0) _period_stat += (_this_sample - _last_sample);
_last_sample = _this_sample;
if(causal::experiment::is_active() && causal::experiment::is_selected(m_stack))
{
m_selected = true;
causal::experiment::add_selected();
// compute the delay time based on the rate of taking samples,
// unless we have taken less than 10, in which case, we just
// use the pre-computed value.
auto _delay =
(_period_stat.get_count() < 10)
? causal::experiment::get_delay()
: (_period_stat.get_mean() * causal::experiment::get_delay_scaling());
causal::delay::get_local() += _delay;
}
}
else
{
OMNITRACE_THROW("unhandled signal %i\n", _sig);
}
}
template <typename Tp>
Tp
backtrace::get_period(uint64_t _units)
{
using cast_type = std::conditional_t<std::is_floating_point<Tp>::value, Tp, double>;
double _realtime_freq =
(get_use_sampling_realtime()) ? get_sampling_real_freq() : 0.0;
double _cputime_freq = (get_use_sampling_cputime()) ? get_sampling_cpu_freq() : 0.0;
auto _freq = std::max<double>(_realtime_freq, _cputime_freq);
double _period = 1.0 / _freq;
int64_t _period_nsec = static_cast<int64_t>(_period * units::sec) % units::sec;
return static_cast<Tp>(_period_nsec) / static_cast<cast_type>(_units);
}
tim::statistics<int64_t>
backtrace::get_period_stats()
{
scoped_in_use _in_use{};
auto _data = tim::statistics<int64_t>{};
if(!get_delay_statistics()) return _data;
for(size_t i = 0; i < get_delay_statistics()->size(); ++i)
{
scoped_in_use _thr_in_use{ static_cast<int64_t>(i) };
const auto& itr = get_delay_statistics()->at(i);
if(itr.get_count() > 1) _data += itr;
}
return _data;
}
void
backtrace::reset_period_stats()
{
scoped_in_use _in_use{};
for(size_t i = 0; i < get_delay_statistics()->size(); ++i)
{
scoped_in_use _thr_in_use{ static_cast<int64_t>(i) };
get_delay_statistics()->at(i).reset();
}
}
} // namespace component
} // namespace causal
} // namespace omnitrace
#define INSTANTIATE_BT_CAUSAL_PERIOD(TYPE) \
template TYPE omnitrace::causal::component::backtrace::get_period<TYPE>(uint64_t);
INSTANTIATE_BT_CAUSAL_PERIOD(float)
INSTANTIATE_BT_CAUSAL_PERIOD(double)
INSTANTIATE_BT_CAUSAL_PERIOD(int64_t)
INSTANTIATE_BT_CAUSAL_PERIOD(uint64_t)
+91
Просмотреть файл
@@ -0,0 +1,91 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/causal/data.hpp"
#include "library/causal/sample_data.hpp"
#include "library/common.hpp"
#include "library/components/fwd.hpp"
#include "library/defines.hpp"
#include "library/timemory.hpp"
#include <timemory/components/base.hpp>
#include <timemory/macros/language.hpp>
#include <timemory/mpl/concepts.hpp>
#include <timemory/tpls/cereal/cereal/cereal.hpp>
#include <timemory/units.hpp>
#include <timemory/utility/unwind.hpp>
#include <chrono>
#include <cstdint>
namespace omnitrace
{
namespace causal
{
namespace component
{
struct backtrace
: tim::component::empty_base
, tim::concepts::component
{
using value_type = void;
using sample_data_set_t = std::set<sample_data>;
static std::string label() { return "causal::backtrace"; }
static std::string description()
{
return "Causal profiling data collected in backtrace";
}
backtrace() = default;
~backtrace() = default;
backtrace(const backtrace&) = default;
backtrace(backtrace&&) noexcept = default;
backtrace& operator=(const backtrace&) = default;
backtrace& operator=(backtrace&&) noexcept = default;
static void start();
static void stop();
void sample(int = -1);
auto get_selected() const { return m_selected; }
auto get_index() const { return m_index; }
auto get_stack() const { return m_stack; }
template <typename Tp = uint64_t>
static Tp get_period(uint64_t _units = units::nsec);
static tim::statistics<int64_t> get_period_stats();
static void reset_period_stats();
private:
bool m_selected = false;
uint32_t m_index = 0;
causal::unwind_addr_t m_stack = {};
};
} // namespace component
} // namespace causal
} // namespace omnitrace
@@ -0,0 +1,154 @@
// 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/causal/components/blocking_gotcha.hpp"
#include "library/causal/delay.hpp"
#include "library/causal/experiment.hpp"
#include "library/config.hpp"
#include "library/debug.hpp"
#include "library/runtime.hpp"
#include "library/state.hpp"
#include <timemory/components/macros.hpp>
#include <timemory/hash/types.hpp>
#include <csignal>
#include <cstdint>
#include <pthread.h>
#include <stdexcept>
namespace omnitrace
{
namespace causal
{
namespace component
{
std::string
blocking_gotcha::label()
{
return "causal_blocking_gotcha";
}
std::string
blocking_gotcha::description()
{
return "Handles executing all necessary pauses before the thread performs some "
"blocking function";
}
void
blocking_gotcha::preinit()
{
configure();
}
void
blocking_gotcha::configure()
{
blocking_gotcha_t::get_initializer() = []() {
if(!config::get_use_causal()) return;
blocking_gotcha_t::configure(
comp::gotcha_config<0, int, pthread_mutex_t*>{ "pthread_mutex_lock" });
blocking_gotcha_t::configure(
comp::gotcha_config<1, int, pthread_mutex_t*>{ "pthread_mutex_trylock" });
blocking_gotcha_t::configure(
comp::gotcha_config<2, int, pthread_rwlock_t*>{ "pthread_rwlock_wrlock" });
blocking_gotcha_t::configure(
comp::gotcha_config<3, int, pthread_rwlock_t*>{ "pthread_rwlock_trywrlock" });
blocking_gotcha_t::configure(
comp::gotcha_config<4, int, pthread_spinlock_t*>{ "pthread_spin_lock" });
blocking_gotcha_t::configure(
comp::gotcha_config<5, int, pthread_spinlock_t*>{ "pthread_spin_trylock" });
blocking_gotcha_t::configure(
comp::gotcha_config<6, int, pthread_t, void**>{ "pthread_join" });
blocking_gotcha_t::configure(
comp::gotcha_config<7, int, pthread_cond_t*, pthread_mutex_t*>{
"pthread_cond_wait" });
blocking_gotcha_t::configure(
comp::gotcha_config<8, int, pthread_cond_t*, pthread_mutex_t*,
const struct timespec*>{ "pthread_cond_timedwait" });
blocking_gotcha_t::configure(
comp::gotcha_config<9, int, const sigset_t*, int*>{ "sigwait" });
blocking_gotcha_t::configure(
comp::gotcha_config<10, int, const sigset_t*, int*, siginfo_t*>{
"sigwaitinfo" });
blocking_gotcha_t::configure(
comp::gotcha_config<11, int, const sigset_t*, int*, siginfo_t*,
const struct timespec*>{ "sigtimedwait" });
blocking_gotcha_t::configure(
comp::gotcha_config<12, int, const sigset_t*>{ "sigsuspend" });
};
}
void
blocking_gotcha::shutdown()
{
blocking_gotcha_t::disable();
}
void
blocking_gotcha::start()
{
if(causal::experiment::is_active() &&
get_thread_state() == ::omnitrace::ThreadState::Enabled && delay_value == 0)
delay_value = causal::delay::get_global().load();
}
void
blocking_gotcha::audit(const comp::gotcha_data& _data, audit::outgoing, int _ret)
{
// if one of the try/timed functions did not succeed, reset the delay value to zero
if(_ret != 0 && _ret != ETIMEDOUT &&
std::set<size_t>{ 1, 3, 5, 8, 11 }.count(_data.index) > 0)
{
delay_value = 0;
}
}
void
blocking_gotcha::stop()
{
if(delay_value > 0 && causal::experiment::is_active() &&
get_thread_state() == ::omnitrace::ThreadState::Enabled)
{
causal::delay::postblock(delay_value);
delay_value = 0;
}
}
} // namespace component
} // namespace causal
} // namespace omnitrace
TIMEMORY_INVOKE_PREINIT(omnitrace::causal::component::blocking_gotcha)
@@ -0,0 +1,76 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/common.hpp"
#include "library/defines.hpp"
#include "library/timemory.hpp"
#include <timemory/components/gotcha/backends.hpp>
#include <timemory/mpl/macros.hpp>
#include <timemory/utility/types.hpp>
#include <array>
#include <cstddef>
#include <string>
namespace omnitrace
{
namespace causal
{
namespace component
{
using timespec_t = struct timespec;
// this is used to wrap pthread_mutex()
struct blocking_gotcha : comp::base<blocking_gotcha, void>
{
static constexpr size_t gotcha_capacity = 13;
TIMEMORY_DEFAULT_OBJECT(blocking_gotcha)
// string id for component
static std::string label();
static std::string description();
static void preinit();
// generate the gotcha wrappers
static void configure();
static void shutdown();
void start();
void audit(const comp::gotcha_data&, audit::outgoing, int);
void stop();
private:
int64_t delay_value = 0;
};
using blocking_gotcha_t =
comp::gotcha<blocking_gotcha::gotcha_capacity,
tim::lightweight_tuple<blocking_gotcha>, category::causal>;
} // namespace component
} // namespace causal
} // namespace omnitrace
OMNITRACE_DEFINE_CONCRETE_TRAIT(prevent_reentry, causal::component::blocking_gotcha_t,
false_type)
+95
Просмотреть файл
@@ -0,0 +1,95 @@
// 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/causal/components/causal_gotcha.hpp"
#include "library/causal/components/blocking_gotcha.hpp"
#include "library/causal/components/unblocking_gotcha.hpp"
#include "library/config.hpp"
#include <timemory/backends/threading.hpp>
#include <timemory/utility/macros.hpp>
#include <timemory/utility/types.hpp>
#include <array>
#include <vector>
namespace omnitrace
{
namespace causal
{
namespace component
{
namespace
{
using bundle_t = tim::lightweight_tuple<blocking_gotcha_t, unblocking_gotcha_t>;
auto&
get_bundle()
{
static auto _v = std::unique_ptr<bundle_t>{};
if(!_v) _v = std::make_unique<bundle_t>("causal_gotcha");
return _v;
}
bool is_configured = false;
} // namespace
//--------------------------------------------------------------------------------------//
void
causal_gotcha::configure()
{
if(!is_configured)
{
blocking_gotcha::configure();
unblocking_gotcha::configure();
is_configured = true;
}
}
void
causal_gotcha::shutdown()
{
if(is_configured)
{
blocking_gotcha::shutdown();
unblocking_gotcha::shutdown();
is_configured = false;
}
}
void
causal_gotcha::start()
{
configure();
get_bundle()->start();
}
void
causal_gotcha::stop()
{
get_bundle()->stop();
shutdown();
}
} // namespace component
} // namespace causal
} // namespace omnitrace
+54
Просмотреть файл
@@ -0,0 +1,54 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/common.hpp"
#include "library/defines.hpp"
#include "library/timemory.hpp"
#include <cstdint>
#include <future>
namespace omnitrace
{
namespace causal
{
namespace component
{
struct causal_gotcha : tim::component::base<causal_gotcha, void>
{
TIMEMORY_DEFAULT_OBJECT(causal_gotcha)
// string id for component
static std::string label() { return "causal_gotcha"; }
// generate the gotcha wrappers
static void configure();
static void shutdown();
static void start();
static void stop();
};
} // namespace component
} // namespace causal
} // namespace omnitrace
+241
Просмотреть файл
@@ -0,0 +1,241 @@
// 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/causal/components/progress_point.hpp"
#include "library/causal/experiment.hpp"
#include "library/common.hpp"
#include "library/concepts.hpp"
#include "library/debug.hpp"
#include "library/thread_data.hpp"
#include "library/timemory.hpp"
#include <timemory/hash/types.hpp>
#include <timemory/mpl/type_traits.hpp>
#include <timemory/units.hpp>
namespace omnitrace
{
namespace causal
{
namespace component
{
namespace
{
using progress_allocator_t = tim::data::ring_buffer_allocator<progress_point>;
using progress_map_t = std::unordered_map<tim::hash_value_t, progress_point*>;
auto&
get_progress_map()
{
using thread_data_t = thread_data<identity<progress_map_t>>;
static auto& _v = thread_data_t::instance(construct_on_init{});
return _v;
}
progress_map_t&
get_progress_map(int64_t _tid)
{
return get_progress_map()->at(_tid);
}
auto&
get_progress_allocator(int64_t _tid)
{
static auto& _v = thread_data<progress_allocator_t>::instances(construct_on_init{});
return _v.at(_tid);
}
} // namespace
std::unordered_map<tim::hash_value_t, progress_point>
progress_point::get_progress_points()
{
auto _data = std::unordered_map<tim::hash_value_t, progress_point>{};
if(!get_progress_map()) return _data;
for(const auto& titr : *get_progress_map())
{
for(const auto& itr : titr)
{
if(itr.second)
{
auto& ditr = _data[itr.first];
ditr += *itr.second;
ditr.set_hash(itr.second->get_hash());
itr.second->set_value(0);
}
}
}
return _data;
}
std::string
progress_point::label()
{
return "progress_point";
}
std::string
progress_point::description()
{
return "Tracks progress point latency and throughput for causal profiling";
}
void
progress_point::start()
{
++m_arrival;
}
void
progress_point::stop()
{
++m_departure;
}
void
progress_point::mark()
{
++m_delta;
}
void
progress_point::set_value(int64_t _v)
{
m_delta = _v;
m_arrival = _v;
m_departure = _v;
}
progress_point&
progress_point::operator+=(const progress_point& _v)
{
if(this != &_v)
{
m_delta += _v.m_delta;
m_arrival += _v.m_arrival;
m_departure += _v.m_departure;
}
return *this;
}
progress_point&
progress_point::operator-=(const progress_point& _v)
{
if(this != &_v)
{
m_delta -= _v.m_delta;
m_arrival -= _v.m_arrival;
m_departure -= _v.m_departure;
}
return *this;
}
bool
progress_point::is_throughput_point() const
{
return (m_delta != 0);
}
bool
progress_point::is_latency_point() const
{
return (m_arrival != 0 || m_departure != 0);
}
int64_t
progress_point::get_delta() const
{
return m_delta;
}
int64_t
progress_point::get_arrival() const
{
if(!is_latency_point()) return m_arrival;
// when it is a latency point, we want the difference to be greater than zero
return (m_arrival >= m_departure) ? (m_arrival + 1) : m_arrival;
}
int64_t
progress_point::get_departure() const
{
// if(!is_latency_point()) return m_departure;
// return (m_departure <= m_arrival) ? m_departure : (m_departure + 1);
return m_departure;
}
int64_t
progress_point::get_latency_delta() const
{
return (get_arrival() - get_departure());
}
int64_t
progress_point::get_laps() const
{
return std::max(get_delta(), get_latency_delta());
}
void
progress_point::print(std::ostream& os) const
{
os << tim::get_hash_identifier(m_hash) << " :: ";
tim::operation::base_printer<progress_point>(os, *this);
}
} // namespace component
} // namespace causal
} // namespace omnitrace
namespace tim
{
namespace operation
{
namespace causal = omnitrace::causal;
void
push_node<causal::component::progress_point>::operator()(type& _obj, scope::config,
hash_value_t _hash,
int64_t _tid) const
{
auto itr = causal::component::get_progress_map(_tid).emplace(_hash, nullptr);
if(itr.second && !itr.first->second)
{
auto& _alloc = causal::component::get_progress_allocator(_tid);
auto* _val = _alloc->allocate(1);
_alloc->construct(_val);
_val->set_hash(_hash);
itr.first->second = _val;
}
_obj.set_hash(_hash);
_obj.set_iterator(itr.first->second);
}
void
pop_node<causal::component::progress_point>::operator()(type& _obj, int64_t) const
{
auto* itr = _obj.get_iterator();
if(itr && !(_obj.get_is_invalid() || _obj.get_is_running()))
{
*itr += _obj;
}
}
} // namespace operation
} // namespace tim
+157
Просмотреть файл
@@ -0,0 +1,157 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/common.hpp"
#include "library/components/fwd.hpp"
#include "library/defines.hpp"
#include <timemory/components/base.hpp>
#include <timemory/hash/types.hpp>
#include <timemory/mpl/concepts.hpp>
#include <timemory/mpl/type_traits.hpp>
#include <timemory/tpls/cereal/cereal.hpp>
#include <timemory/utility/types.hpp>
#include <cstdint>
#include <unordered_map>
namespace omnitrace
{
namespace causal
{
namespace component
{
struct progress_point : comp::base<progress_point, void>
{
using base_type = comp::base<progress_point, void>;
using value_type = int64_t;
using hash_type = tim::hash_value_t;
using iterator_type = progress_point*;
static std::string label();
static std::string description();
TIMEMORY_DEFAULT_OBJECT(progress_point)
void start();
void stop();
void mark();
void set_value(int64_t);
progress_point& operator+=(const progress_point&);
progress_point& operator-=(const progress_point&);
bool is_throughput_point() const;
bool is_latency_point() const;
void print(std::ostream& os) const;
void set_hash(hash_type _v) { m_hash = _v; }
void set_iterator(iterator_type _v) { m_iterator = _v; }
auto get_iterator() const { return m_iterator; }
auto get_hash() const { return m_hash; }
int64_t get_delta() const;
int64_t get_arrival() const;
int64_t get_departure() const;
int64_t get_latency_delta() const;
int64_t get_laps() const;
template <typename ArchiveT>
void load(ArchiveT& ar, const unsigned)
{
namespace cereal = ::tim::cereal;
auto _name = std::string{};
ar(cereal::make_nvp("name", _name));
ar(cereal::make_nvp("delta", m_delta));
ar(cereal::make_nvp("arrival", m_arrival));
ar(cereal::make_nvp("departure", m_departure));
m_hash = tim::hash::add_hash_id(_name);
}
template <typename ArchiveT>
void save(ArchiveT& ar, const unsigned) const
{
namespace cereal = ::tim::cereal;
ar(cereal::make_nvp("hash", m_hash));
ar(cereal::make_nvp("name", std::string{ tim::get_hash_identifier(m_hash) }));
ar(cereal::make_nvp("delta", m_delta));
ar(cereal::make_nvp("arrival", m_arrival));
ar(cereal::make_nvp("departure", m_departure));
}
static std::unordered_map<tim::hash_value_t, progress_point> get_progress_points();
private:
hash_type m_hash = 0;
int64_t m_delta = 0;
int64_t m_arrival = 0;
int64_t m_departure = 0;
progress_point* m_iterator = nullptr;
};
} // namespace component
} // namespace causal
} // namespace omnitrace
OMNITRACE_DEFINE_CONCRETE_TRAIT(uses_storage, causal::component::progress_point,
false_type)
OMNITRACE_DEFINE_CONCRETE_TRAIT(flat_storage, causal::component::progress_point,
true_type)
OMNITRACE_DEFINE_CONCRETE_TRAIT(uses_timing_units, causal::component::progress_point,
true_type)
OMNITRACE_DEFINE_CONCRETE_TRAIT(is_timing_category, causal::component::progress_point,
true_type)
namespace tim
{
namespace operation
{
template <>
struct push_node<omnitrace::causal::component::progress_point>
{
using type = omnitrace::causal::component::progress_point;
TIMEMORY_DEFAULT_OBJECT(push_node)
push_node(type& _obj, scope::config _scope, hash_value_t _hash,
int64_t _tid = threading::get_id())
{
(*this)(_obj, _scope, _hash, _tid);
}
void operator()(type& _obj, scope::config, hash_value_t _hash,
int64_t _tid = threading::get_id()) const;
};
template <>
struct pop_node<omnitrace::causal::component::progress_point>
{
using type = omnitrace::causal::component::progress_point;
TIMEMORY_DEFAULT_OBJECT(pop_node)
pop_node(type& _obj, int64_t _tid = threading::get_id()) { (*this)(_obj, _tid); }
void operator()(type& _obj, int64_t _tid = threading::get_id()) const;
};
} // namespace operation
} // namespace tim
@@ -0,0 +1,135 @@
// 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/causal/components/unblocking_gotcha.hpp"
#include "library/causal/delay.hpp"
#include "library/causal/experiment.hpp"
#include "library/config.hpp"
#include "library/debug.hpp"
#include "library/runtime.hpp"
#include <timemory/components/macros.hpp>
#include <timemory/hash/types.hpp>
#include <csignal>
#include <cstdint>
#include <pthread.h>
#include <stdexcept>
namespace omnitrace
{
namespace causal
{
namespace component
{
std::string
unblocking_gotcha::label()
{
return "causal_unblocking_gotcha";
}
std::string
unblocking_gotcha::description()
{
return "Handles executing all necessary pauses before the thread performs some "
"blocking function";
}
void
unblocking_gotcha::preinit()
{
configure();
}
void
unblocking_gotcha::configure()
{
unblocking_gotcha_t::get_initializer() = []() {
if(!config::get_use_causal()) return;
unblocking_gotcha_t::configure(
comp::gotcha_config<0, int, pthread_mutex_t*>{ "pthread_mutex_unlock" });
unblocking_gotcha_t::configure(
comp::gotcha_config<1, int, pthread_rwlock_t*>{ "pthread_rwlock_unlock" });
unblocking_gotcha_t::configure(
comp::gotcha_config<2, int, pthread_spinlock_t*>{ "pthread_spin_unlock" });
unblocking_gotcha_t::configure(
comp::gotcha_config<3, int, pthread_barrier_t*>{ "pthread_barrier_wait" });
unblocking_gotcha_t::configure(
comp::gotcha_config<4, int, pthread_cond_t*>{ "pthread_cond_signal" });
unblocking_gotcha_t::configure(
comp::gotcha_config<5, int, pthread_cond_t*>{ "pthread_cond_broadcast" });
unblocking_gotcha_t::configure(
comp::gotcha_config<6, int, pthread_t, int>{ "pthread_kill" });
unblocking_gotcha_t::configure(
comp::gotcha_config<7, void, void*>{ "pthread_exit" });
};
}
void
unblocking_gotcha::shutdown()
{
unblocking_gotcha_t::disable();
}
void
unblocking_gotcha::start()
{
if(causal::experiment::is_active() &&
get_thread_state() == ::omnitrace::ThreadState::Enabled)
causal::delay::process();
}
void
unblocking_gotcha::stop()
{
if(causal::experiment::is_active() &&
get_thread_state() == ::omnitrace::ThreadState::Enabled)
causal::delay::credit();
}
void
unblocking_gotcha::set_data(const comp::gotcha_data& _data)
{
OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal);
auto _hash = tim::add_hash_id(_data.tool_id);
auto&& _ident = tim::get_hash_identifier(_hash);
if(_ident != _data.tool_id)
throw ::omnitrace::exception<std::runtime_error>(
JOIN("", "Error! resolving hash for \"", _data.tool_id, "\" (", _hash,
") returns ", _ident.c_str()));
#if defined(OMNITRACE_CI)
OMNITRACE_VERBOSE_F(3, "data set for '%s'...\n", _data.tool_id.c_str());
#endif
}
} // namespace component
} // namespace causal
} // namespace omnitrace
TIMEMORY_INVOKE_PREINIT(omnitrace::causal::component::unblocking_gotcha)
@@ -0,0 +1,75 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/common.hpp"
#include "library/defines.hpp"
#include "library/timemory.hpp"
#include <timemory/components/gotcha/backends.hpp>
#include <timemory/mpl/macros.hpp>
#include <array>
#include <cstddef>
#include <string>
namespace omnitrace
{
namespace causal
{
namespace component
{
using timespec_t = struct timespec;
// this is used to wrap pthread_mutex()
struct unblocking_gotcha : comp::base<unblocking_gotcha, void>
{
static constexpr size_t gotcha_capacity = 8;
TIMEMORY_DEFAULT_OBJECT(unblocking_gotcha)
// string id for component
static std::string label();
static std::string description();
static void preinit();
// generate the gotcha wrappers
static void configure();
static void shutdown();
static void start();
static void stop();
static void set_data(const comp::gotcha_data&);
};
using unblocking_gotcha_t =
comp::gotcha<unblocking_gotcha::gotcha_capacity,
tim::lightweight_tuple<unblocking_gotcha>, category::causal>;
} // namespace component
} // namespace causal
} // namespace omnitrace
OMNITRACE_DEFINE_CONCRETE_TRAIT(prevent_reentry, causal::component::unblocking_gotcha_t,
false_type)
OMNITRACE_DEFINE_CONCRETE_TRAIT(static_data, causal::component::unblocking_gotcha_t,
true_type)
+949
Просмотреть файл
@@ -0,0 +1,949 @@
// 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/causal/data.hpp"
#include "library/binary/address_multirange.hpp"
#include "library/binary/analysis.hpp"
#include "library/binary/binary_info.hpp"
#include "library/binary/fwd.hpp"
#include "library/binary/link_map.hpp"
#include "library/binary/scope_filter.hpp"
#include "library/causal/delay.hpp"
#include "library/causal/experiment.hpp"
#include "library/causal/sampling.hpp"
#include "library/causal/selected_entry.hpp"
#include "library/config.hpp"
#include "library/debug.hpp"
#include "library/ptl.hpp"
#include "library/runtime.hpp"
#include "library/state.hpp"
#include "library/thread_data.hpp"
#include "library/thread_info.hpp"
#include "library/utility.hpp"
#include <timemory/data/atomic_ring_buffer.hpp>
#include <timemory/hash/types.hpp>
#include <timemory/log/logger.hpp>
#include <timemory/mpl/concepts.hpp>
#include <timemory/units.hpp>
#include <timemory/unwind/dlinfo.hpp>
#include <timemory/unwind/processed_entry.hpp>
#include <timemory/utility/procfs/maps.hpp>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <initializer_list>
#include <random>
#include <regex>
#include <sstream>
#include <thread>
#include <utility>
#include <vector>
namespace omnitrace
{
namespace causal
{
namespace
{
using random_engine_t = std::mt19937_64;
using progress_bundles_t = component_bundle_cache<component::progress_point>;
auto speedup_seeds = std::vector<size_t>{};
auto speedup_divisions = get_env<uint16_t>("OMNITRACE_CAUSAL_SPEEDUP_DIVISIONS", 5);
auto speedup_dist = []() {
size_t _n = std::max<size_t>(1, 100 / speedup_divisions);
std::vector<uint16_t> _v(_n, uint16_t{ 0 });
std::generate(_v.begin(), _v.end(),
[_value = 0]() mutable { return (_value += speedup_divisions); });
// approximately 25% of bins should be zero speedup
size_t _nzero = std::ceil(_v.size() / 4.0);
_v.resize(_v.size() + _nzero, 0);
std::sort(_v.begin(), _v.end());
OMNITRACE_CI_THROW(_v.back() > 100, "Error! last value is too large: %i\n",
(int) _v.back());
return _v;
}();
auto perform_experiment_impl_completed = std::unique_ptr<std::promise<void>>{};
auto num_progress_points = std::atomic<size_t>{ 0 };
template <typename ContextT>
auto&
get_engine()
{
static auto _seed = []() -> hash_value_t {
auto _seed_v =
config::get_setting_value<uint64_t>("OMNITRACE_CAUSAL_RANDOM_SEED").second;
if(_seed_v == 0) _seed_v = std::random_device{}();
return _seed_v;
}();
static thread_local auto _v =
random_engine_t{ tim::get_combined_hash_id(_seed, utility::get_thread_index()) };
return _v;
}
binary::address_multirange&
get_eligible_address_ranges()
{
static auto _v = binary::address_multirange{};
return _v;
}
using sf = binary::scope_filter;
auto
get_filters(std::set<binary::scope_filter::filter_scope> _scopes = {
sf::BINARY_FILTER, sf::SOURCE_FILTER, sf::FUNCTION_FILTER })
{
auto _filters = std::vector<binary::scope_filter>{};
// exclude internal libraries used by omnitrace
if(_scopes.count(sf::BINARY_FILTER) > 0)
_filters.emplace_back(
sf{ sf::FILTER_EXCLUDE, sf::BINARY_FILTER,
"lib(omnitrace[-\\.]|dyninst|tbbmalloc|gotcha\\.|unwind\\.so\\.99)" });
// in function mode, it generally doesn't help to experiment on main function since
// telling the user to "make the main function" faster is literally useless since it
// contains everything that could be made faster
if(config::get_causal_mode() == CausalMode::Function &&
_scopes.count(sf::FUNCTION_FILTER) > 0)
_filters.emplace_back(sf{ sf::FILTER_EXCLUDE, sf::FUNCTION_FILTER,
"( main\\(|^main$|^main\\.cold$)" });
bool _use_default_excludes =
config::get_setting_value<bool>("OMNITRACE_CAUSAL_FUNCTION_EXCLUDE_DEFAULTS")
.second;
if(_use_default_excludes && _scopes.count(sf::FUNCTION_FILTER) > 0)
{
// symbols starting with leading underscore are generally system functions
_filters.emplace_back(sf{ sf::FILTER_EXCLUDE, sf::FUNCTION_FILTER, "^_" });
if(config::get_causal_mode() == CausalMode::Function)
{
// exclude STL implementation functions
_filters.emplace_back(sf{ sf::FILTER_EXCLUDE, sf::FUNCTION_FILTER, "::_M" });
}
}
// in function mode, it generally doesn't help to claim
// "make main function" faster since it contains everything
// that could be made faster
if(config::get_causal_mode() == CausalMode::Function &&
_scopes.count(sf::FUNCTION_FILTER) > 0)
{
_filters.emplace_back(sf{ sf::FILTER_EXCLUDE, sf::FUNCTION_FILTER,
"(^main$|^main.cold$|int main\\()" });
}
using utility::get_regex_or;
auto _source_end_converter = [](const std::string& _v) { return _v + "$"; };
// include handling
{
auto _binary_include = get_regex_or(config::get_causal_binary_scope(), "");
auto _source_include =
get_regex_or(config::get_causal_source_scope(), _source_end_converter, "");
auto _function_include = get_regex_or(config::get_causal_function_scope(), "");
auto _current_include =
std::make_tuple(_binary_include, _source_include, _function_include);
static auto _former_include = decltype(_current_include){};
if(_former_include != _current_include)
{
if(!_binary_include.empty())
OMNITRACE_VERBOSE(0, "[causal] binary scope : %s\n",
_binary_include.c_str());
if(!_source_include.empty())
OMNITRACE_VERBOSE(0, "[causal] source scope : %s\n",
_source_include.c_str());
if(!_function_include.empty())
OMNITRACE_VERBOSE(0, "[causal] function scope : %s\n",
_function_include.c_str());
_former_include = _current_include;
}
if(!_binary_include.empty() && _scopes.count(sf::BINARY_FILTER) > 0)
_filters.emplace_back(
sf{ sf::FILTER_INCLUDE, sf::BINARY_FILTER, _binary_include });
if(!_source_include.empty() && _scopes.count(sf::SOURCE_FILTER) > 0)
_filters.emplace_back(
sf{ sf::FILTER_INCLUDE, sf::SOURCE_FILTER, _source_include });
if(!_function_include.empty() && _scopes.count(sf::FUNCTION_FILTER) > 0)
_filters.emplace_back(
sf{ sf::FILTER_INCLUDE, sf::FUNCTION_FILTER, _function_include });
}
// exclude handling
{
auto _binary_exclude = get_regex_or(config::get_causal_binary_exclude(), "");
auto _source_exclude =
get_regex_or(config::get_causal_source_exclude(), _source_end_converter, "");
auto _function_exclude = get_regex_or(config::get_causal_function_exclude(), "");
auto _current_exclude =
std::make_tuple(_binary_exclude, _source_exclude, _function_exclude);
static auto _former_exclude = decltype(_current_exclude){};
if(_former_exclude != _current_exclude)
{
if(!_binary_exclude.empty())
OMNITRACE_VERBOSE(0, "[causal] binary exclude : %s\n",
_binary_exclude.c_str());
if(!_source_exclude.empty())
OMNITRACE_VERBOSE(0, "[causal] source exclude : %s\n",
_source_exclude.c_str());
if(!_function_exclude.empty())
OMNITRACE_VERBOSE(0, "[causal] function exclude : %s\n",
_function_exclude.c_str());
_former_exclude = _current_exclude;
}
if(!_binary_exclude.empty() && _scopes.count(sf::BINARY_FILTER) > 0)
_filters.emplace_back(
sf{ sf::FILTER_EXCLUDE, sf::BINARY_FILTER, _binary_exclude });
if(!_source_exclude.empty() && _scopes.count(sf::SOURCE_FILTER) > 0)
_filters.emplace_back(
sf{ sf::FILTER_EXCLUDE, sf::SOURCE_FILTER, _source_exclude });
if(!_function_exclude.empty() && _scopes.count(sf::FUNCTION_FILTER) > 0)
_filters.emplace_back(
sf{ sf::FILTER_EXCLUDE, sf::FUNCTION_FILTER, _function_exclude });
}
return _filters;
}
using binary_info_t = std::vector<binary::binary_info>;
std::pair<binary_info_t, binary_info_t>&
get_cached_binary_info()
{
static auto _v = []() {
// get the linked binaries for the exe (excluding ones from libomnitrace)
auto _link_map = binary::get_link_map();
auto _files = std::vector<std::string>{};
_files.reserve(_link_map.size());
for(const auto& itr : _link_map)
_files.emplace_back(itr.real());
auto _discarded = std::vector<binary::binary_info>{};
auto _requested = binary::get_binary_info(_files, get_filters());
return std::make_pair(_requested, _discarded);
}();
return _v;
}
bool
satisfies_filter(const binary::scope_filter::filter_scope& _scope,
const std::string& _value)
{
static auto _filters = get_filters();
return binary::scope_filter::satisfies_filter(_filters, _scope, _value);
}
auto
compute_eligible_lines_impl()
{
const auto& _binary_info = get_cached_binary_info().first;
auto& _filter_info = get_cached_binary_info().second;
auto _filters = get_filters();
auto& _eligible_ar = get_eligible_address_ranges();
for(const auto& litr : _binary_info)
{
for(const auto& ditr : litr.mappings)
{
_eligible_ar +=
std::make_pair(binary::address_multirange::coarse{},
address_range_t{ ditr.load_address, ditr.last_address });
}
for(const auto& ditr : litr.symbols)
{
_eligible_ar += ditr.address + ditr.load_address;
}
auto& _filtered = _filter_info.emplace_back();
_filtered.bfd = litr.bfd;
_filtered.mappings = litr.mappings;
_filtered.ranges = litr.ranges;
_filtered.sections = litr.sections;
for(const auto& ditr : litr.symbols)
{
auto _sym = ditr.clone();
_sym.inlines =
ditr.get_inline_symbols<std::vector<binary::inlined_symbol>>(_filters);
_sym.dwarf_info =
ditr.get_debug_line_info<std::vector<binary::dwarf_entry>>(_filters);
if(ditr(_filters) || (_sym.inlines.size() + _sym.dwarf_info.size()) > 0)
{
_filtered.symbols.emplace_back(_sym);
}
}
for(const auto& ditr : litr.debug_info)
{
if(sf::satisfies_filter(_filters, sf::SOURCE_FILTER, ditr.file) ||
sf::satisfies_filter(_filters, sf::SOURCE_FILTER,
join(':', ditr.file, ditr.line)))
{
_filtered.debug_info.emplace_back(ditr);
}
}
_filtered.sort();
}
OMNITRACE_VERBOSE(
0, "[causal] eligible address ranges: %zu, coarse address range: %zu [%s]\n",
_eligible_ar.size(), _eligible_ar.range_size(),
_eligible_ar.coarse_range.as_string().c_str());
if(_eligible_ar.empty())
{
auto _cfg = settings::compose_filename_config{};
_cfg.subdirectory = "causal/binary-info";
_cfg.use_suffix = config::get_use_pid();
save_line_info(_cfg, config::get_verbose());
}
OMNITRACE_CONDITIONAL_THROW(
_eligible_ar.empty(),
"Error! binary analysis (after filters) resulted in zero eligible instruction "
"pointer addresses for causal experimentation");
}
void
save_maps_info_impl(std::ostream& _ofs)
{
auto _maps_file = join("/", "/proc", process::get_id(), "maps");
auto _ifs = std::ifstream{ _maps_file };
auto _maps = std::stringstream{};
if(_ifs)
{
_maps << _maps_file << "\n";
while(_ifs)
{
std::string _line{};
getline(_ifs, _line);
if(!_line.empty()) _maps << " " << _line << "\n";
}
}
_ofs << _maps.str();
}
void
save_line_info_impl(std::ostream& _ofs,
const std::vector<binary::binary_info>& _binary_data)
{
auto _write_impl = [&_ofs](const binary::binary_info& _data) {
for(const auto& itr : _data.mappings)
{
_ofs << itr.pathname << " [" << as_hex(itr.load_address) << " - "
<< as_hex(itr.last_address) << "]\n";
}
auto _emitted_dwarf_addresses = std::set<uintptr_t>{};
for(const auto& itr : _data.symbols)
{
auto _addr = itr.address;
auto _addr_off = itr.address + itr.load_address;
_ofs << " " << as_hex(_addr_off) << " [" << as_hex(_addr)
<< "] :: " << itr.file;
if(itr.line > 0) _ofs << ":" << itr.line;
if(!itr.func.empty()) _ofs << " [" << tim::demangle(itr.func) << "]";
_ofs << "\n";
for(const auto& ditr : itr.inlines)
{
_ofs << " " << ditr.file << ":" << ditr.line;
if(!ditr.func.empty()) _ofs << " [" << tim::demangle(ditr.func) << "]";
_ofs << "\n";
}
for(const auto& ditr : itr.dwarf_info)
{
_ofs << " " << as_hex(ditr.address) << " :: " << ditr.file << ":"
<< ditr.line;
_ofs << "\n";
_emitted_dwarf_addresses.emplace(ditr.address.low);
}
}
for(const auto& itr : _data.debug_info)
{
if(_emitted_dwarf_addresses.count(itr.address.low) > 0) continue;
_ofs << " " << as_hex(itr.address) << " :: " << itr.file << ":"
<< itr.line;
_ofs << "\n";
}
_ofs << "\n" << std::flush;
};
for(const auto& itr : _binary_data)
_write_impl(itr);
}
void
compute_eligible_lines()
{
static auto _once = std::once_flag{};
std::call_once(_once, compute_eligible_lines_impl);
}
void
perform_experiment_impl(std::shared_ptr<std::promise<void>> _started) // NOLINT
{
using clock_type = std::chrono::high_resolution_clock;
using duration_nsec_t = std::chrono::duration<double, std::nano>;
using duration_sec_t = std::chrono::duration<double, std::ratio<1>>;
const auto& _thr_info = thread_info::init(true);
OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal);
OMNITRACE_CONDITIONAL_THROW(!_thr_info->is_offset,
"Error! causal profiling thread should be offset");
if(!perform_experiment_impl_completed)
perform_experiment_impl_completed = std::make_unique<std::promise<void>>();
perform_experiment_impl_completed->set_value_at_thread_exit();
compute_eligible_lines();
// notify that thread has started
if(_started) _started->set_value();
// pause at least one second to determine sampling rate
// std::this_thread::sleep_for(std::chrono::seconds{ 1 });
if(!config::get_causal_end_to_end())
{
// wait for at least one progress point to start
while(num_progress_points.load(std::memory_order_relaxed) == 0)
{
std::this_thread::sleep_for(std::chrono::milliseconds{ 1 });
}
}
double _delay_sec =
config::get_setting_value<double>("OMNITRACE_CAUSAL_DELAY").second;
double _duration_sec =
config::get_setting_value<double>("OMNITRACE_CAUSAL_DURATION").second;
auto _duration_nsec = duration_nsec_t{ _duration_sec * units::sec };
if(_delay_sec > 0.0)
{
OMNITRACE_VERBOSE(1, "[causal] delaying experimentation for %.2f seconds...\n",
_delay_sec);
uint64_t _delay_nsec = _delay_sec * units::sec;
std::this_thread::sleep_for(std::chrono::nanoseconds{ _delay_nsec });
}
auto _impl_count = 0;
auto _start_time = clock_type::now();
auto _exceeded_duration = [&]() {
if(_duration_sec > 1.0e-3)
{
auto _elapsed = clock_type::now() - _start_time;
if(_elapsed >= _duration_nsec)
{
OMNITRACE_VERBOSE(
1,
"[causal] stopping experimentation after %.2f seconds "
"(elapsed: %.2f seconds)...\n",
_duration_sec,
std::chrono::duration_cast<duration_sec_t>(_elapsed).count());
causal::sampling::post_process();
return true;
}
}
return false;
};
while(get_state() < State::Finalized)
{
auto _impl_no = _impl_count++;
auto _experim = experiment{};
// loop until started or finalized
while(!_experim.start())
{
if(get_state() == State::Finalized)
{
auto _memory = std::stringstream{};
auto _binary = std::stringstream{};
auto _scoped = std::stringstream{};
auto _sample = std::stringstream{};
save_maps_info_impl(_memory);
save_line_info_impl(_binary, get_cached_binary_info().first);
save_line_info_impl(_scoped, get_cached_binary_info().second);
auto _samples = std::map<uintptr_t, size_t>{};
for(const auto& itr : get_samples())
{
for(const auto& iitr : itr.second)
{
_samples[iitr.address] += iitr.count;
}
}
for(const auto& itr : _samples)
{
if(itr.second > 0)
{
auto _linfo = get_line_info(itr.first, true);
// if(_linfo.size() > 1) _linfo.pop_front();
for(const auto& iitr : _linfo)
{
_sample << " " << std::setw(8) << itr.second
<< " :: " << as_hex(itr.first) << " [" << iitr.file
<< ":" << iitr.line << "][" << demangle(iitr.func)
<< "]\n";
}
if(_linfo.empty())
{
_sample << " " << std::setw(8) << itr.second
<< " :: " << as_hex(itr.first) << "\n";
}
}
}
std::cerr << std::flush;
auto _cerr = tim::log::warning_stream(std::cerr);
_cerr << "\nmaps:\n\n" << _memory.str() << "\n";
_cerr << "\nbinary:\n\n" << _binary.str() << "\n";
_cerr << "\nscoped:\n\n" << _scoped.str() << "\n";
_cerr << "\nsample:\n\n" << _sample.str() << "\n";
std::cerr << std::flush;
OMNITRACE_CONDITIONAL_THROW(_impl_no == 0, "experiment never started");
return;
}
}
// wait for the experiment to complete
if(config::get_causal_end_to_end())
{
mark_progress_point(config::get_exe_name(), true);
while(get_state() < State::Finalized)
{
std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds{ 100 });
if(_exceeded_duration()) return;
}
}
else
{
_experim.wait();
}
while(!_experim.stop())
{
if(get_state() == State::Finalized) return;
}
if(_exceeded_duration()) return;
}
}
// thread-safe read/write ring-buffer via atomics
using pc_ring_buffer_t = tim::data_storage::atomic_ring_buffer<uintptr_t>;
// latest_eligible_pcs is an array of unwind_depth size -> samples will
// use lowest indexes for most recent functions address in the call-stack
auto latest_eligible_pc = []() {
auto _arr = std::array<std::unique_ptr<pc_ring_buffer_t>, unwind_depth>{};
for(auto& itr : _arr)
itr = std::make_unique<pc_ring_buffer_t>(units::get_page_size() /
(sizeof(uintptr_t) + 1));
return _arr;
}();
} // namespace
//--------------------------------------------------------------------------------------//
bool
is_eligible_address(uintptr_t _v)
{
return get_eligible_address_ranges().coarse_range.contains(_v);
}
void
save_line_info(const settings::compose_filename_config& _cfg, int _verbose)
{
auto _write = [_verbose](const std::string& ofname, const auto& _data) {
auto _ofs = std::ofstream{};
if(tim::filepath::open(_ofs, ofname))
{
if(_verbose >= 0)
operation::file_output_message<binary::symbol>{}(
ofname, std::string{ "causal_symbol_info" });
save_line_info_impl(_ofs, _data);
save_maps_info_impl(_ofs);
}
else
{
throw ::omnitrace::exception<std::runtime_error>("Error opening " + ofname);
}
};
_write(tim::settings::compose_output_filename(
join('-', config::get_causal_output_filename(), "binary"), "txt", _cfg),
get_cached_binary_info().first);
_write(tim::settings::compose_output_filename(
join('-', config::get_causal_output_filename(), "scoped"), "txt", _cfg),
get_cached_binary_info().second);
}
void
set_current_selection(unwind_addr_t _stack)
{
if(experiment::is_active()) return;
size_t _n = 0;
for(auto itr : _stack)
{
auto& _pcs = latest_eligible_pc.at(_n);
if(_pcs && is_eligible_address(itr))
{
_pcs->write(&itr);
// increment after valid found -> first valid pc for call-stack
++_n;
}
}
}
selected_entry
sample_selection(size_t _nitr, size_t _wait_ns)
{
OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal);
size_t _n = 0;
auto _select_address = [&](auto& _address_vec) {
// this isn't necessary bc of check before calling this lambda but
// kept because of size() - 1 in distribution range
if(OMNITRACE_UNLIKELY(_address_vec.empty()))
{
OMNITRACE_WARNING(0, "no addresses for sample selection...\n");
return selected_entry{};
}
while(!_address_vec.empty())
{
// randomly select an address
auto _dist =
std::uniform_int_distribution<size_t>{ 0, _address_vec.size() - 1 };
auto _idx = _dist(get_engine<selected_entry>());
uintptr_t _addr = _address_vec.at(_idx);
uintptr_t _sym_addr = 0;
uintptr_t _lookup_addr = _addr;
auto _dl_info = unwind::dlinfo::construct(_addr);
_address_vec.erase(_address_vec.begin() + _idx);
if(get_causal_mode() == CausalMode::Function)
_sym_addr = (_dl_info.symbol) ? _dl_info.symbol.address() : _addr;
// lookup the PC line info at either the address or the symbol address
auto linfo = get_line_info(_lookup_addr, false);
// unlikely this will be empty but just in case
if(linfo.empty()) continue;
// debugging for continuous integration
if(OMNITRACE_UNLIKELY(config::get_is_continuous_integration() ||
config::get_debug()))
{
auto _location =
(_dl_info.location)
? filepath::realpath(std::string{ _dl_info.location.name },
nullptr, false)
: std::string{};
for(const auto& itr : linfo)
{
if(OMNITRACE_UNLIKELY(config::get_debug()))
{
OMNITRACE_WARNING(
0, "[%s][%s][%s][%s] %s [%s:%i][%s][%zu]\n",
as_hex(_lookup_addr).c_str(), as_hex(_addr).c_str(),
as_hex(_sym_addr).c_str(),
(_location.empty()) ? "" : _location.data(),
demangle(itr.func).c_str(), itr.file.c_str(), itr.line,
itr.address.as_string().c_str(), itr.address.size());
}
}
}
auto& _linfo_v = (config::get_causal_mode() == CausalMode::Function)
? linfo.front()
: linfo.back();
return selected_entry{ _addr, _sym_addr, _linfo_v };
// return selected_entry{ address_range_t{ _addr },
// address_range_t{ _sym_addr },
// { _linfo_v.second } };
}
return selected_entry{};
};
while(_n++ < _nitr)
{
auto _addresses = std::deque<uintptr_t>{};
for(auto& aitr : latest_eligible_pc)
{
if(OMNITRACE_UNLIKELY(!aitr))
{
OMNITRACE_WARNING(0, "invalid ring buffer...\n");
continue;
}
auto _naddrs = aitr->count();
if(_naddrs == 0) continue;
for(size_t i = 0; i < _naddrs; ++i)
{
uintptr_t _addr = 0;
if(!aitr->is_empty() && aitr->read(&_addr) != nullptr)
{
if(_addr > 0) _addresses.emplace_back(_addr);
}
}
if(!_addresses.empty())
{
auto _selection = _select_address(_addresses);
if(_selection) return _selection;
}
}
std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::nanoseconds{ _wait_ns });
}
return selected_entry{};
}
std::deque<binary::symbol>
get_line_info(uintptr_t _addr, bool _include_discarded)
{
static auto _glob_filters = get_filters({ sf::BINARY_FILTER });
static auto _scope_filters = get_filters();
auto _data = std::deque<binary::symbol>{};
auto _get_line_info = [&](const auto& _info, const auto& _filters) {
// search for exact matches first
for(const binary::binary_info& litr : _info)
{
auto _local_data = std::deque<binary::symbol>{};
for(const auto& ditr : litr.symbols)
{
auto _ipaddr = ditr.ipaddr();
if(!_ipaddr.contains(_addr)) continue;
if(config::get_causal_mode() == CausalMode::Function)
{
// check if the primary symbol satisfy the constraints
if(ditr(_filters)) _local_data.emplace_back(ditr);
// the primary symbol may not satisfy the constraints but the inlined
// functions may
utility::combine(_local_data, ditr.get_inline_symbols(_filters));
}
else if(config::get_causal_mode() == CausalMode::Line)
{
auto _debug_data = std::deque<binary::symbol>{};
for(const auto& itr : ditr.get_debug_line_info(_filters))
{
if(itr.ipaddr().contains(_addr)) _debug_data.emplace_back(itr);
}
utility::combine(_local_data, _debug_data);
}
else
{
throw exception<std::runtime_error>(
join(" ", "Causal mode not supported:",
std::to_string(config::get_causal_mode())));
}
}
if(!_local_data.empty())
{
// combine and only allow first match
utility::combine(_data, _local_data);
break;
}
}
};
if(_include_discarded)
_get_line_info(get_cached_binary_info().first, _glob_filters);
else
_get_line_info(get_cached_binary_info().second, _scope_filters);
return _data;
}
void
push_progress_point(std::string_view _name)
{
if(config::get_causal_end_to_end()) return;
++num_progress_points;
auto _hash = tim::add_hash_id(_name);
auto& _data = progress_bundles_t::instance(utility::get_thread_index());
auto* _bundle = _data.construct(_hash);
_bundle->push();
_bundle->start();
}
void
pop_progress_point(std::string_view _name)
{
if(config::get_causal_end_to_end()) return;
auto& _data = progress_bundles_t::instance(utility::get_thread_index());
if(_data.empty()) return;
if(_name.empty())
{
auto* itr = _data.back();
itr->stop();
itr->pop();
_data.pop_back();
return;
}
else
{
auto _hash = tim::add_hash_id(_name);
for(auto itr = _data.rbegin(); itr != _data.rend(); ++itr)
{
if((*itr)->get_hash() == _hash)
{
(*itr)->stop();
(*itr)->pop();
_data.destroy(itr);
return;
}
}
}
}
void
mark_progress_point(std::string_view _name, bool _force)
{
if(config::get_causal_end_to_end() && !_force) return;
++num_progress_points;
auto _hash = tim::add_hash_id(_name);
auto& _data = progress_bundles_t::instance(utility::get_thread_index());
auto* _bundle = _data.construct(_hash);
_bundle->push();
_bundle->mark();
_bundle->pop();
_data.pop_back();
}
uint16_t
sample_virtual_speedup()
{
if(speedup_dist.empty())
return 0;
else if(speedup_dist.size() == 1)
return speedup_dist.front();
else
{
struct virtual_speedup
{};
auto _dist =
std::uniform_int_distribution<size_t>{ size_t{ 0 }, speedup_dist.size() - 1 };
return speedup_dist.at(_dist(get_engine<virtual_speedup>()));
}
}
void
start_experimenting()
{
OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal);
auto _user_speedup_dist = config::get_causal_fixed_speedup();
if(!_user_speedup_dist.empty())
{
speedup_dist.clear();
for(auto itr : _user_speedup_dist)
{
OMNITRACE_CONDITIONAL_ABORT_F(itr > 100,
"Virtual speedups must be in range [0, 100]. "
"Invalid virtual speedup: %lu\n",
itr);
speedup_dist.emplace_back(static_cast<uint16_t>(itr));
}
}
compute_eligible_lines();
auto _cfg = settings::compose_filename_config{};
_cfg.subdirectory = "causal/binary-info";
_cfg.use_suffix = config::get_use_pid();
save_line_info(_cfg, config::get_verbose());
if(get_state() < State::Finalized)
{
OMNITRACE_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
auto _promise = std::make_shared<std::promise<void>>();
std::thread{ perform_experiment_impl, _promise }.detach();
_promise->get_future().wait_for(std::chrono::seconds{ 2 });
}
}
void
finish_experimenting()
{
if(perform_experiment_impl_completed)
{
perform_experiment_impl_completed->get_future().wait_for(
std::chrono::seconds{ 5 });
perform_experiment_impl_completed.reset();
}
sampling::post_process();
experiment::save_experiments();
}
} // namespace causal
} // namespace omnitrace
+76
Просмотреть файл
@@ -0,0 +1,76 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/binary/analysis.hpp"
#include "library/binary/fwd.hpp"
#include "library/causal/fwd.hpp"
#include "library/containers/c_array.hpp"
#include "library/containers/static_vector.hpp"
#include "library/defines.hpp"
#include "library/thread_data.hpp"
#include "library/utility.hpp"
#include <timemory/hash/types.hpp>
#include <timemory/tpls/cereal/cereal/cereal.hpp>
#include <timemory/utility/procfs/maps.hpp>
#include <timemory/utility/unwind.hpp>
#include <deque>
#include <dlfcn.h>
#include <map>
namespace omnitrace
{
namespace causal
{
void
save_line_info(const settings::compose_filename_config&, int _verbose);
std::deque<binary::symbol>
get_line_info(uintptr_t _addr, bool include_discarded = true);
bool is_eligible_address(uintptr_t);
void set_current_selection(unwind_addr_t);
selected_entry
sample_selection(size_t _nitr = 1000, size_t _wait_ns = 10000);
void push_progress_point(std::string_view);
void pop_progress_point(std::string_view);
void
mark_progress_point(std::string_view, bool force = false);
uint16_t
sample_virtual_speedup();
void
start_experimenting();
void
finish_experimenting();
} // namespace causal
} // namespace omnitrace
+193
Просмотреть файл
@@ -0,0 +1,193 @@
// 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/causal/delay.hpp"
#include "library/causal/experiment.hpp"
#include "library/runtime.hpp"
#include "library/state.hpp"
#include "library/thread_data.hpp"
#include "library/thread_info.hpp"
#include "library/tracing.hpp"
#include "library/utility.hpp"
#include <timemory/components/macros.hpp>
#include <timemory/mpl/concepts.hpp>
#include <timemory/mpl/types.hpp>
#include <timemory/process/threading.hpp>
#include <atomic>
#include <chrono>
#include <random>
namespace omnitrace
{
namespace causal
{
namespace
{
auto&
get_delay_data()
{
using thread_data_t = thread_data<identity<int64_t>, delay>;
static auto& _v = thread_data_t::construct(
construct_on_init{}, []() { return delay::get_global().load(); });
return _v;
}
int64_t
compute_sleep_for_overhead()
{
using random_engine_t = std::mt19937_64;
auto _engine = random_engine_t{ std::random_device{}() };
auto _dist = std::uniform_int_distribution<int64_t>{ 0, 5 };
size_t _ntot = 250;
size_t _nwarm = 50;
auto _stats = tim::statistics<double>{};
for(size_t i = 0; i < _ntot; ++i)
{
auto _val = _dist(_engine);
int64_t _beg = tracing::now();
std::this_thread::sleep_for(std::chrono::nanoseconds{ _val });
int64_t _end = tracing::now();
if(i < _nwarm) continue;
auto _diff = (_end - _beg);
OMNITRACE_CONDITIONAL_THROW(
_diff < _val, "Error! sleep_for(%zu) [nanoseconds] >= %zu", _val, _diff);
_stats += (_diff - _val);
}
OMNITRACE_BASIC_VERBOSE(2,
"[causal] overhead of std::this_thread::sleep_for(...) "
"invocation = %6.3f usec +/- %e\n",
_stats.get_mean() / units::usec,
_stats.get_stddev() / units::usec);
tim::manager::instance()->add_metadata([_stats](auto& ar) {
ar(tim::cereal::make_nvp("causal thread sleep overhead [nsec]", _stats));
});
(void) get_delay_data();
return _stats.get_mean();
}
int64_t sleep_for_overhead = compute_sleep_for_overhead();
} // namespace
void
delay::process()
{
if(!trait::runtime_enabled<delay>::get()) return;
if(get_state() >= ::omnitrace::State::Finalized) return;
if(causal::experiment::is_active())
{
if(get_global() < get_local())
{
auto _diff = (get_local() - get_global());
if(_diff > sleep_for_overhead) get_global() += _diff;
}
else if(get_global() > get_local())
{
auto _beg = tracing::now();
std::this_thread::sleep_for(
std::chrono::nanoseconds{ get_global() - get_local() });
get_local() += (tracing::now() - _beg);
}
}
else
{
get_local() = get_global();
}
}
void
delay::credit()
{
if(!trait::runtime_enabled<delay>::get()) return;
if(get_state() >= ::omnitrace::State::Finalized) return;
auto _diff = get_global() - get_local();
if(_diff > 0)
{
get_local() += _diff;
}
}
void
delay::preblock()
{
if(!trait::runtime_enabled<delay>::get()) return;
if(get_state() >= ::omnitrace::State::Finalized) return;
auto _diff = get_global() - get_local();
if(_diff > 0)
{
get_local() += _diff;
}
}
void
delay::postblock(int64_t _preblock_global_delay_value)
{
if(!trait::runtime_enabled<delay>::get()) return;
if(get_state() >= ::omnitrace::State::Finalized) return;
get_local() += (get_global() - _preblock_global_delay_value);
}
int64_t
delay::sync()
{
auto _v = get_global().load(std::memory_order_seq_cst);
if(get_delay_data()) get_delay_data()->fill(_v);
return _v;
}
std::atomic<int64_t>&
delay::get_global()
{
static auto _v = std::atomic<int64_t>{ 0 };
return _v;
}
int64_t&
delay::get_local(int64_t _tid)
{
auto& _data = get_delay_data();
static thread_local auto _thr_init = []() {
using thread_data_t = thread_data<identity<int64_t>, delay>;
thread_data_t::construct(construct_on_thread{ threading::get_id() },
get_global().load());
return true;
}();
return _data->at(_tid);
(void) _thr_init;
}
uint64_t
delay::compute_total_delay(uint64_t _baseline)
{
return get_global().load() - _baseline;
}
} // namespace causal
} // namespace omnitrace
+62
Просмотреть файл
@@ -0,0 +1,62 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/common.hpp"
#include "library/components/fwd.hpp"
#include "library/defines.hpp"
#include "library/thread_data.hpp"
#include <timemory/components/base.hpp>
#include <timemory/macros/language.hpp>
#include <timemory/mpl/concepts.hpp>
#include <atomic>
#include <cstdint>
namespace omnitrace
{
namespace causal
{
struct delay
: tim::component::empty_base
, tim::concepts::component
{
using value_type = void;
TIMEMORY_DEFAULT_OBJECT(delay)
static void process();
static void credit();
static void preblock();
static void postblock(int64_t);
static int64_t sync();
static std::atomic<int64_t>& get_global();
static int64_t& get_local(int64_t _tid = threading::get_id());
static int64_t get(int64_t _tid = threading::get_id());
static uint64_t compute_total_delay(uint64_t);
};
} // namespace causal
} // namespace omnitrace
+671
Просмотреть файл
@@ -0,0 +1,671 @@
// 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/causal/experiment.hpp"
#include "common/defines.h"
#include "library/causal/components/backtrace.hpp"
#include "library/causal/components/progress_point.hpp"
#include "library/causal/data.hpp"
#include "library/causal/delay.hpp"
#include "library/config.hpp"
#include "library/debug.hpp"
#include "library/state.hpp"
#include "library/thread_data.hpp"
#include "library/thread_info.hpp"
#include "library/tracing.hpp"
#include <string>
#include <timemory/components/timing/backends.hpp>
#include <timemory/hash/types.hpp>
#include <timemory/mpl/policy.hpp>
#include <timemory/tpls/cereal/archives.hpp>
#include <timemory/tpls/cereal/cereal.hpp>
#include <timemory/tpls/cereal/cereal/archives/json.hpp>
#include <timemory/tpls/cereal/types.hpp>
#include <timemory/units.hpp>
#include <chrono>
#include <ratio>
#include <regex>
#include <thread>
#include <vector>
namespace omnitrace
{
namespace causal
{
namespace
{
using backtrace_causal = omnitrace::causal::component::backtrace;
namespace cereal = ::tim::cereal;
auto current_experiment_value = experiment{};
auto current_selected_count = std::atomic<uint64_t>{ 0 };
auto current_experiment = std::atomic<experiment*>{ nullptr };
auto experiment_history = std::vector<experiment>{};
int64_t global_scaling = 1;
int64_t global_scaling_increments = 0;
bool use_exp_speedup_scaling =
get_env<bool>("OMNITRACE_CAUSAL_SCALE_EXPERIMENT_TIME_BY_SPEEDUP", false);
} // namespace
bool
experiment::sample::operator==(const sample& _v) const
{
return std::tie(address, info.line, info.file, info.func, location) ==
std::tie(_v.address, _v.info.line, _v.info.file, _v.info.func, _v.location);
}
bool
experiment::sample::operator<(const sample& _v) const
{
if(info.line > 0 && _v.info.line > 0)
{
return std::tie(info.line, info.file) == std::tie(_v.info.line, _v.info.file);
}
else if((info.line + _v.info.line) > 0)
{
return std::tie(info.file, location, info.line) <
std::tie(_v.info.file, _v.location, _v.info.line);
}
return (location < _v.location);
}
const auto&
experiment::sample::operator+=(const sample& _v) const
{
if(*this == _v && this != &_v) count += _v.count;
return *this;
}
template <typename ArchiveT>
void
experiment::sample::serialize(ArchiveT& ar, const unsigned)
{
namespace cereal = ::tim::cereal;
ar(cereal::make_nvp("location", location), cereal::make_nvp("count", count),
cereal::make_nvp("info", info));
}
template <typename ArchiveT>
void
experiment::record::serialize(ArchiveT& ar, const unsigned)
{
namespace cereal = ::tim::cereal;
ar(cereal::make_nvp("startup_time", startup),
cereal::make_nvp("experiments", experiments),
cereal::make_nvp("runtime", runtime));
auto _samples = std::vector<sample>{};
if constexpr(concepts::is_input_archive<ArchiveT>::value)
{
ar(cereal::make_nvp("samples", _samples));
for(auto& itr : _samples)
samples.emplace(std::move(itr));
}
else
{
ar(cereal::make_nvp("samples", samples));
}
}
template <typename ArchiveT>
void
experiment::serialize(ArchiveT& ar, const unsigned)
{
namespace cereal = ::tim::cereal;
ar(cereal::make_nvp("index", index),
cereal::make_nvp("virtual_speedup", virtual_speedup),
cereal::make_nvp("sampling_period", sampling_period),
cereal::make_nvp("start_time", start_time), cereal::make_nvp("end_time", end_time),
cereal::make_nvp("experiment_time", experiment_time),
cereal::make_nvp("batch_size", batch_size), cereal::make_nvp("duration", duration),
cereal::make_nvp("scaling_factor", scaling_factor),
cereal::make_nvp("selected", selected),
cereal::make_nvp("sample_delay", sample_delay),
cereal::make_nvp("delay_scaling", delay_scaling),
cereal::make_nvp("total_delay", total_delay),
cereal::make_nvp("global_delay", global_delay),
cereal::make_nvp("selection", selection));
if constexpr(concepts::is_input_archive<ArchiveT>::value)
{
auto _ppts = std::vector<component::progress_point>{};
init_progress.clear();
fini_progress.clear();
ar(cereal::make_nvp("progress_points", _ppts));
for(auto itr : _ppts)
fini_progress.emplace(itr.get_hash(), itr);
}
else
{
auto _ppts = std::vector<component::progress_point>{};
{
auto ppts = fini_progress;
for(auto& pitr : ppts)
pitr.second.set_hash(pitr.first);
for(auto pitr : init_progress)
ppts[pitr.first] -= pitr.second;
_ppts.reserve(ppts.size());
for(auto& pitr : ppts)
_ppts.emplace_back(pitr.second);
}
ar(cereal::make_nvp("progress_points", _ppts));
}
ar(cereal::make_nvp("period_stats", period_stats));
}
std::string
experiment::label()
{
return "causal_experiment";
}
std::string
experiment::description()
{
return "Records an experiment for causal profiling";
}
const std::atomic<experiment*>&
experiment::get_current_experiment()
{
return current_experiment;
}
bool
experiment::start()
{
if(running && tracing::now() < start_time + experiment_time) return false;
selection = sample_selection();
if(!selection) return false;
// sampling period in nanoseconds
sampling_period = backtrace_causal::get_period(units::nsec);
// adjust for the real sampling period
period_stats = causal::component::backtrace::get_period_stats();
if(period_stats.get_count() > 10) sampling_period = period_stats.get_mean();
// experiment time is scaled up for longer speedups
index = experiment_history.size() + 1;
virtual_speedup = sample_virtual_speedup();
delay_scaling = virtual_speedup / 100.0;
if(use_exp_speedup_scaling) scaling_factor *= (1.0 + delay_scaling);
experiment_time = global_scaling * scaling_factor * sampling_period * batch_size;
sample_delay = sampling_period * delay_scaling;
total_delay = delay::sync();
init_progress = component::progress_point::get_progress_points();
start_time = tracing::now();
OMNITRACE_VERBOSE(0, "Starting causal experiment #%-3u: %s\n", index,
as_string().c_str());
current_experiment_value = *this;
current_selected_count.store(0);
current_experiment.store(this);
return true;
}
bool
experiment::wait() const
{
auto _now = tracing::now();
auto _wait = experiment_time - (_now - start_time);
auto _end = _now + _wait;
auto _incr = std::min<uint64_t>(_wait / 100, 1000000);
while(tracing::now() < _end && get_state() < State::Finalized)
{
std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::nanoseconds{ _incr });
}
return (tracing::now() >= _end);
}
bool
experiment::stop()
{
auto _now = tracing::now();
if(_now < start_time + experiment_time) return false;
current_experiment.store(nullptr);
selected = current_selected_count.load();
running = false;
end_time = _now;
experiment_time = (end_time - start_time);
global_delay = delay::compute_total_delay(0);
total_delay = (global_delay - total_delay);
duration = (experiment_time > total_delay) ? (experiment_time - total_delay) : 0;
fini_progress = component::progress_point::get_progress_points();
period_stats = causal::component::backtrace::get_period_stats();
// sync data
delay::sync();
// for larger speedups, we increased the experiment time, so we want to artificially
// increase num by the same factor. E.g. 10 throughput points at speedup 50 should
// really look like 15
double _scale_num = 1.0 + ((use_exp_speedup_scaling) ? delay_scaling : 0.0);
auto _prog_stats = tim::statistics<int64_t>{};
for(auto fitr : fini_progress)
{
auto _pt = fitr.second - init_progress[fitr.first];
int64_t _num =
std::max<int64_t>({ _pt.get_laps(), _pt.get_arrival(), _pt.get_departure() });
if(_num > 0) _prog_stats += (_num * _scale_num);
}
auto _mean = (_prog_stats.get_count() > 0) ? _prog_stats.get_mean() : 0;
auto _high = (_prog_stats.get_count() > 0) ? _prog_stats.get_max() : 0;
if(_high < 5)
{
global_scaling *= 2;
++global_scaling_increments; // keep track of how many successive increments have
// been performed
}
else if(_mean > 10 && global_scaling > 1)
{
global_scaling /= 2;
global_scaling_increments = 0;
}
if(OMNITRACE_UNLIKELY(global_scaling_increments >= 5))
{
OMNITRACE_WARNING(
0,
"Warning! causal experimentation hasn't seen at least 5 progress points "
"in the last %li experiments. Progress points are necessary for measuring "
"the effect of the virtual speed-up. Please visit "
"https://amdresearch.github.io/omnitrace/ for documentation on progress "
"points and how to add them\n",
global_scaling_increments);
}
if(_high > 0) experiment_history.emplace_back(*this);
std::this_thread::sleep_for(std::chrono::nanoseconds{ sampling_period * batch_size });
return true;
}
std::string
experiment::as_string() const
{
std::stringstream _ss{};
auto _dur = static_cast<double>(experiment_time) / static_cast<double>(units::sec);
_ss << std::boolalpha << "speed-up: " << std::setw(3) << virtual_speedup
<< "%, period: " << std::setw(4) << std::fixed << std::setprecision(2)
<< (sampling_period / static_cast<double>(units::msec)) << " msec";
if(!config::get_causal_end_to_end())
_ss << ", duration: " << std::setw(5) << std::fixed << std::setprecision(3)
<< _dur << " sec";
_ss << " :: experiment: " << as_hex(selection.address) << " ";
//_ss << " [" << selection.info.ipaddr().as_string() << "]";
if(selection.symbol_address > 0 && selection.address != selection.symbol_address)
_ss << "(symbol@" << as_hex(selection.symbol_address) << ") ";
if(!selection.symbol.file.empty() && selection.symbol.line > 0)
_ss << "[" << filepath::basename(selection.symbol.file) << ":"
<< selection.symbol.line << "]";
auto _patch = [](std::string _v) {
auto _pos = std::string::npos;
using strpair_t = std::pair<std::string_view, std::string>;
for(const auto& itr :
{ strpair_t{
"::basic_string<char, std::char_traits<char>, std::allocator<char> > ",
"::string" },
strpair_t{ "::__cxx11::", "::" } })
{
while((_pos = _v.find(itr.first)) != std::string::npos)
_v = _v.replace(_pos, itr.first.length(), itr.second);
}
return _v;
};
auto _func = _patch(demangle(selection.symbol.func));
_ss << "['" << _func << "']";
return _ss.str();
}
// in nanoseconds
uint64_t
experiment::get_delay()
{
if(!current_experiment.load()) return 0;
return current_experiment_value.sample_delay;
}
double
experiment::get_delay_scaling()
{
if(!current_experiment.load()) return 0;
return current_experiment_value.delay_scaling;
}
uint32_t
experiment::get_index()
{
if(!is_active()) return 0;
return current_experiment_value.index;
}
bool
experiment::is_active()
{
return (current_experiment.load(std::memory_order_relaxed) != nullptr);
}
bool
experiment::is_selected(unwind_addr_t _stack)
{
if(is_active())
{
for(auto itr : _stack)
if(current_experiment_value.selection.contains(itr)) return true;
}
return false;
}
void
experiment::add_selected()
{
if(current_experiment.load() == nullptr) return;
++current_selected_count;
}
std::vector<experiment>
experiment::get_experiments()
{
return experiment_history;
}
void
experiment::save_experiments()
{
auto _cfg = settings::compose_filename_config{};
_cfg.subdirectory = "causal";
_cfg.use_suffix = config::get_use_pid();
save_experiments(config::get_causal_output_filename(), _cfg);
}
void // NOLINTNEXTLINE
experiment::save_experiments(std::string _fname_base, const filename_config_t& _cfg)
{
const auto& _info0 = thread_info::get(0, InternalTID);
// if(experiment_history.size() > 1)
// experiment_history.erase(experiment_history.begin());
auto current_record = record{};
current_record.startup = _info0->lifetime.first;
// update experiments
{
for(auto& itr : experiment_history)
{
if(itr.duration == 0 || itr.experiment_time == 0) continue;
current_record.experiments.emplace_back(std::move(itr));
}
experiment_history.clear();
}
// update runtime value
{
uint64_t _beg_runtime = std::numeric_limits<uint64_t>::max();
uint64_t _end_runtime = std::numeric_limits<uint64_t>::min();
for(auto& itr : current_record.experiments)
{
if(itr.duration == 0) continue;
if(itr.experiment_time == 0) continue;
_beg_runtime = std::min<uint64_t>(_beg_runtime, itr.start_time);
_end_runtime = std::max<uint64_t>(_end_runtime, itr.end_time);
}
current_record.runtime = (_end_runtime - _beg_runtime);
}
// update sample data
{
auto _add_sample = [&current_record](sample&& _v) {
auto fitr = current_record.samples.find(_v);
if(fitr != current_record.samples.end())
*fitr += _v;
else
current_record.samples.emplace(std::move(_v));
};
auto _total_samples = std::map<uintptr_t, size_t>{};
for(const auto& itr : get_samples())
{
for(const auto& sitr : itr.second)
{
_total_samples[sitr.address] += sitr.count;
}
}
auto _binfo_cfg = settings::compose_filename_config{};
_binfo_cfg.subdirectory = "causal/binary-info";
_binfo_cfg.use_suffix = config::get_use_pid();
save_line_info(_binfo_cfg, config::get_verbose());
for(const auto& itr : _total_samples)
{
auto _addr = itr.first;
auto _count = itr.second;
if(_count > 0)
{
auto _linfo = get_line_info(_addr, true);
for(const auto& iitr : _linfo)
{
auto _name = (iitr.line > 0) ? join(":", iitr.file, iitr.line)
: demangle(iitr.func);
_name = join(" :: ", as_hex(_addr), _name);
_add_sample(sample{ _count, _addr, _name, iitr });
}
if(_linfo.empty() && config::get_debug())
{
_add_sample(
sample{ _count, _addr, as_hex(_addr), sample::line_info{} });
}
}
}
}
bool _causal_output_reset =
config::get_setting_value<bool>("OMNITRACE_CAUSAL_FILE_RESET").second;
// if(current_record.experiments.empty()) return;
{
auto _saved_experiments = (_causal_output_reset)
? std::vector<experiment::record>{}
: load_experiments(_fname_base, _cfg, false);
_saved_experiments.emplace_back(current_record);
std::stringstream oss{};
{
auto ar =
tim::policy::output_archive<cereal::PrettyJSONOutputArchive>::get(oss);
ar->setNextName("omnitrace");
ar->startNode();
ar->setNextName("causal");
ar->startNode();
(*ar)(cereal::make_nvp("records", _saved_experiments));
ar->finishNode();
ar->finishNode();
}
auto _fname = tim::settings::compose_output_filename(_fname_base, "json", _cfg);
auto ofs = std::ofstream{};
if(tim::filepath::open(ofs, _fname))
{
if(get_verbose() >= 0)
operation::file_output_message<experiment>{}(
_fname, std::string{ "causal_experiments" });
ofs << oss.str() << "\n";
}
else
{
OMNITRACE_THROW("Error opening causal experiments output file: %s",
_fname.c_str());
}
}
auto _fname = tim::settings::compose_output_filename(_fname_base, "coz", _cfg);
// read in existing data
auto _existing = std::stringstream{};
if(!_causal_output_reset)
{
std::ifstream ifs{ _fname };
if(ifs)
{
while(ifs && ifs.good())
{
std::string _line;
std::getline(ifs, _line);
_existing << _line << "\n";
}
}
}
std::ofstream ofs{};
ofs.setf(std::ios::fixed);
if(tim::filepath::open(ofs, _fname))
{
if(get_verbose() >= 0)
operation::file_output_message<experiment>{}(
_fname, std::string{ "causal_experiments" });
ofs << _existing.str();
ofs << "startup\ttime=" << current_record.startup << "\n";
for(auto& itr : current_record.experiments)
{
auto& _selection = itr.selection;
auto& _line_info = _selection.symbol;
std::string _name = (_selection.symbol_address > 0)
? _line_info.func
: join(":", _line_info.file, _line_info.line);
OMNITRACE_CONDITIONAL_THROW(
_name.empty(),
"Error! causal experiment selection has no name: address=%s, file=%s, "
"line=%u, func=%s",
as_hex(_line_info.address).c_str(), _line_info.file.c_str(),
_line_info.line, _line_info.func.c_str());
ofs << "experiment\tselected=" << demangle(_name)
<< "\tspeedup=" << std::setprecision(2)
<< static_cast<double>(itr.virtual_speedup / 100.0)
<< "\tduration=" << itr.duration << "\tselected-samples=" << itr.selected
<< "\n";
auto ppts = itr.fini_progress;
for(auto pitr : itr.init_progress)
ppts[pitr.first] -= pitr.second;
for(auto pitr : ppts)
{
// if(pitr.second.get_laps() == 0) continue;
if(get_causal_end_to_end() && pitr.second.get_laps() > 1) continue;
if(pitr.second.is_throughput_point() && pitr.second.get_delta() != 0)
{
ofs << "throughput-point\tname="
<< tim::demangle(tim::get_hash_identifier(pitr.first))
<< "\tdelta=" << pitr.second.get_delta() << "\n";
if(get_causal_end_to_end()) break;
}
if(pitr.second.is_latency_point())
{
if(get_causal_end_to_end()) continue;
auto _delta = std::max<int64_t>(pitr.second.get_latency_delta(), 1);
ofs << "latency-point\tname="
<< tim::demangle(tim::get_hash_identifier(pitr.first))
<< "\tarrivals=" << pitr.second.get_arrival()
<< "\tdepartures=" << pitr.second.get_departure()
<< "\tdifference=" << _delta << "\n";
}
}
}
ofs << "runtime\ttime=" << current_record.runtime << "\n";
for(const auto& itr : current_record.samples)
{
ofs << "samples\tlocation=" << itr.location << "\tcount=" << itr.count;
if(config::get_debug()) ofs << "\taddress=" << as_hex(itr.address);
ofs << "\n";
}
}
else
{
OMNITRACE_THROW("Error opening causal experiments output file: %s",
_fname.c_str());
}
}
std::vector<experiment::record>
experiment::load_experiments(bool _throw_on_error)
{
auto _cfg = settings::compose_filename_config{};
_cfg.subdirectory = "causal";
_cfg.use_suffix = config::get_use_pid();
return load_experiments(config::get_causal_output_filename(), _cfg, _throw_on_error);
}
std::vector<experiment::record>
experiment::load_experiments(std::string _fname, const filename_config_t& _cfg,
bool _throw_on_error)
{
_fname = tim::settings::compose_input_filename(_fname, "json", _cfg);
auto ifs = std::ifstream{};
auto _data = std::vector<experiment::record>{};
if(tim::filepath::open(ifs, _fname))
{
auto ar = tim::policy::input_archive<cereal::JSONInputArchive>::get(ifs);
ar->setNextName("omnitrace");
ar->startNode();
ar->setNextName("causal");
ar->startNode();
(*ar)(cereal::make_nvp("records", _data));
ar->finishNode();
ar->finishNode();
}
else
{
if(_throw_on_error)
{
OMNITRACE_THROW("Error opening causal experiments input file: %s",
_fname.c_str());
}
}
return _data;
}
} // namespace causal
} // namespace omnitrace
+139
Просмотреть файл
@@ -0,0 +1,139 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/binary/dwarf_entry.hpp"
#include "library/binary/symbol.hpp"
#include "library/causal/components/backtrace.hpp"
#include "library/causal/components/progress_point.hpp"
#include "library/causal/data.hpp"
#include "library/causal/sample_data.hpp"
#include "library/causal/selected_entry.hpp"
#include "library/containers/c_array.hpp"
#include "library/defines.hpp"
#include "library/utility.hpp"
#include <timemory/hash/types.hpp>
#include <timemory/mpl/concepts.hpp>
#include <timemory/tpls/cereal/cereal.hpp>
#include <timemory/tpls/cereal/cereal/cereal.hpp>
#include <timemory/utility/unwind.hpp>
#include <atomic>
#include <cstdint>
#include <unordered_map>
namespace omnitrace
{
namespace causal
{
using hash_value_t = ::tim::hash_value_t;
struct experiment
{
using progress_points_t =
std::unordered_map<tim::hash_value_t, component::progress_point>;
using experiments_t = std::vector<experiment>;
using filename_config_t = settings::compose_filename_config;
using sample_dataset_t = std::set<sample_data>;
using period_stats_t = tim::statistics<int64_t>;
struct sample
{
using line_info = binary::symbol;
mutable uint64_t count = 0;
uintptr_t address = 0;
std::string location = {};
line_info info = {};
bool operator==(const sample&) const;
bool operator<(const sample&) const;
const auto& operator+=(const sample&) const;
template <typename ArchiveT>
void serialize(ArchiveT& ar, const unsigned);
};
struct record
{
int64_t startup = 0;
uint64_t runtime = 0;
std::vector<experiment> experiments = {};
std::set<sample> samples = {};
template <typename ArchiveT>
void serialize(ArchiveT& ar, const unsigned);
};
static std::string label();
static std::string description();
static const std::atomic<experiment*>& get_current_experiment();
TIMEMORY_DEFAULT_OBJECT(experiment)
bool start();
bool wait() const; // returns false if interrupted
bool stop();
std::string as_string() const;
template <typename ArchiveT>
void serialize(ArchiveT& ar, const unsigned version);
// in nanoseconds
static uint64_t get_delay();
static double get_delay_scaling();
static uint32_t get_index();
static bool is_active();
static bool is_selected(unwind_addr_t);
static void add_selected();
static experiments_t get_experiments();
static void save_experiments();
static void save_experiments(std::string, const filename_config_t&);
static std::vector<record> load_experiments(bool _throw_on_err = true);
static std::vector<record> load_experiments(std::string, const filename_config_t&,
bool = true);
bool running = false;
uint16_t virtual_speedup = 0; /// 0-100 in multiples of 5
uint32_t index = 0; /// experiment number
uint64_t sampling_period = 0; /// period b/t samples [nsec]
uint64_t start_time = 0; /// start of experiment [nsec]
uint64_t end_time = 0; /// end of experiment [nsec]
uint64_t experiment_time = 0; /// how long the experiment ran [nsec]
uint64_t duration = 0; /// runtime - delays [nsec]
uint64_t batch_size = 10; /// batch factor for experiment/cooloff
uint64_t scaling_factor = 50; /// scaling factor for experiment time
uint64_t sample_delay = 0; /// how long to delay [nsec]
uint64_t total_delay = 0; /// total delays [nsec]
uint64_t selected = 0; /// num times selected line sampled
uint64_t global_delay = 0;
double delay_scaling = 0.0; /// virtual_speedup / 100.
selected_entry selection = {}; /// which line was selected
progress_points_t init_progress = {}; /// progress points at start
progress_points_t fini_progress = {}; /// progress points at end
period_stats_t period_stats = {}; /// stats for sampling period
};
} // namespace causal
} // namespace omnitrace
+52
Просмотреть файл
@@ -0,0 +1,52 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include "library/binary/fwd.hpp"
#include "library/containers/static_vector.hpp"
#include "library/defines.hpp"
#include <timemory/hash/types.hpp>
#include <timemory/unwind/stack.hpp>
#include <timemory/utility/procfs/maps.hpp>
#include <cstddef>
#include <cstdint>
#include <map>
#include <utility>
namespace omnitrace
{
namespace unwind = ::tim::unwind;
namespace causal
{
static constexpr size_t unwind_depth = 8;
static constexpr size_t unwind_offset = 0;
using unwind_stack_t = unwind::stack<unwind_depth>;
using unwind_addr_t = container::static_vector<uintptr_t, unwind_depth>;
using hash_value_t = tim::hash_value_t;
struct selected_entry;
} // namespace causal
} // namespace omnitrace

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше