Rename Omnitrace to ROCm Systems Profiler (#4)
The Omnitrace program is being renamed.
Full name: "ROCm Systems Profiler"
Package name: "rocprofiler-systems"
Binary / Library names: "rocprof-sys-*"
---------
Co-authored-by: Xuan Chen <xuchen@amd.com>
Signed-off-by: David Galiffi <David.Galiffi@amd.com>
[ROCm/rocprofiler-systems commit: d07bf508a9]
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# rocprofiler-systems object library
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_library(rocprofiler-systems-object-library OBJECT)
|
||||
add_library(rocprofiler-systems::rocprofiler-systems-object-library ALIAS
|
||||
rocprofiler-systems-object-library)
|
||||
|
||||
target_sources(
|
||||
rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/library.cpp ${CMAKE_CURRENT_LIST_DIR}/regions.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/progress.cpp ${CMAKE_CURRENT_LIST_DIR}/api.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/timeout.cpp ${CMAKE_CURRENT_LIST_DIR}/api.hpp)
|
||||
|
||||
add_subdirectory(library)
|
||||
|
||||
target_link_libraries(rocprofiler-systems-object-library
|
||||
PRIVATE rocprofiler-systems::rocprofiler-systems-interface-library)
|
||||
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# rocprofiler-systems static library
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_library(rocprofiler-systems-static-library STATIC
|
||||
$<TARGET_OBJECTS:rocprofiler-systems-object-library>)
|
||||
add_library(rocprofiler-systems::librocprofiler-systems-static ALIAS
|
||||
rocprofiler-systems-static-library)
|
||||
|
||||
target_link_libraries(
|
||||
rocprofiler-systems-static-library
|
||||
PRIVATE rocprofiler-systems::rocprofiler-systems-interface-library
|
||||
rocprofiler-systems::rocprofiler-systems-core
|
||||
rocprofiler-systems::rocprofiler-systems-binary)
|
||||
|
||||
set_target_properties(rocprofiler-systems-static-library PROPERTIES OUTPUT_NAME
|
||||
${BINARY_NAME_PREFIX})
|
||||
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# rocprofiler-systems shared library
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_library(rocprofiler-systems-shared-library SHARED
|
||||
$<TARGET_OBJECTS:rocprofiler-systems-object-library>)
|
||||
add_library(rocprofiler-systems::librocprofiler-systems-shared ALIAS
|
||||
rocprofiler-systems-shared-library)
|
||||
add_library(rocprofiler-systems::rocprofiler-systems-library ALIAS
|
||||
rocprofiler-systems-shared-library)
|
||||
|
||||
target_link_libraries(
|
||||
rocprofiler-systems-shared-library
|
||||
PRIVATE rocprofiler-systems::rocprofiler-systems-interface-library
|
||||
rocprofiler-systems::rocprofiler-systems-core
|
||||
rocprofiler-systems::rocprofiler-systems-binary)
|
||||
|
||||
set_target_properties(
|
||||
rocprofiler-systems-shared-library
|
||||
PROPERTIES OUTPUT_NAME ${BINARY_NAME_PREFIX}
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||
INSTALL_RPATH "${ROCPROFSYS_LIB_INSTALL_RPATH}")
|
||||
|
||||
rocprofiler_systems_strip_target(rocprofiler-systems-shared-library)
|
||||
|
||||
install(
|
||||
TARGETS rocprofiler-systems-shared-library
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
OPTIONAL)
|
||||
@@ -0,0 +1,169 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "api.hpp"
|
||||
#include "core/debug.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_push_trace(const char* _name)
|
||||
{
|
||||
rocprofsys_push_trace_hidden(_name);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_pop_trace(const char* _name)
|
||||
{
|
||||
rocprofsys_pop_trace_hidden(_name);
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
rocprofsys_push_region(const char* _name)
|
||||
{
|
||||
try
|
||||
{
|
||||
rocprofsys_push_region_hidden(_name);
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
ROCPROFSYS_WARNING_F(1, "Exception caught: %s\n", _e.what());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
rocprofsys_pop_region(const char* _name)
|
||||
{
|
||||
try
|
||||
{
|
||||
rocprofsys_pop_region_hidden(_name);
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
ROCPROFSYS_WARNING_F(1, "Exception caught: %s\n", _e.what());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
rocprofsys_push_category_region(rocprofsys_category_t _category, const char* _name,
|
||||
rocprofsys_annotation_t* _annotations,
|
||||
size_t _annotation_count)
|
||||
{
|
||||
try
|
||||
{
|
||||
rocprofsys_push_category_region_hidden(_category, _name, _annotations,
|
||||
_annotation_count);
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
ROCPROFSYS_WARNING_F(1, "Exception caught: %s\n", _e.what());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
rocprofsys_pop_category_region(rocprofsys_category_t _category, const char* _name,
|
||||
rocprofsys_annotation_t* _annotations,
|
||||
size_t _annotation_count)
|
||||
{
|
||||
try
|
||||
{
|
||||
rocprofsys_pop_category_region_hidden(_category, _name, _annotations,
|
||||
_annotation_count);
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
ROCPROFSYS_WARNING_F(1, "Exception caught: %s\n", _e.what());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_progress(const char* _name)
|
||||
{
|
||||
rocprofsys_progress_hidden(_name);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_annotated_progress(const char* _name, rocprofsys_annotation_t* _annotations,
|
||||
size_t _annotation_count)
|
||||
{
|
||||
rocprofsys_annotated_progress_hidden(_name, _annotations, _annotation_count);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_init_library(void)
|
||||
{
|
||||
rocprofsys_init_library_hidden();
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_init_tooling(void)
|
||||
{
|
||||
rocprofsys_init_tooling_hidden();
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_init(const char* _mode, bool _rewrite, const char* _arg0)
|
||||
{
|
||||
rocprofsys_init_hidden(_mode, _rewrite, _arg0);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_finalize(void)
|
||||
{
|
||||
rocprofsys_finalize_hidden();
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_reset_preload(void)
|
||||
{
|
||||
rocprofsys_reset_preload_hidden();
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_set_env(const char* env_name, const char* env_val)
|
||||
{
|
||||
rocprofsys_set_env_hidden(env_name, env_val);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_set_mpi(bool use, bool attached)
|
||||
{
|
||||
rocprofsys_set_mpi_hidden(use, attached);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_register_source(const char* file, const char* func, size_t line,
|
||||
size_t address, const char* source)
|
||||
{
|
||||
rocprofsys_register_source_hidden(file, func, line, address, source);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_register_coverage(const char* file, const char* func, size_t address)
|
||||
{
|
||||
rocprofsys_register_coverage_hidden(file, func, address);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/defines.hpp"
|
||||
#include "rocprofiler-systems/categories.h" // in rocprof-sys-user
|
||||
|
||||
#include <timemory/compat/macros.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// forward decl of the API
|
||||
extern "C"
|
||||
{
|
||||
/// handles configuration logic
|
||||
void rocprofsys_init_library(void) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// handles configuration logic
|
||||
void rocprofsys_init_tooling(void) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// starts gotcha wrappers
|
||||
void rocprofsys_init(const char*, bool, const char*) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// shuts down all tooling and generates output
|
||||
void rocprofsys_finalize(void) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// remove librocprof-sys from LD_PRELOAD
|
||||
void rocprofsys_reset_preload(void) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// sets an environment variable
|
||||
void rocprofsys_set_env(const char*, const char*) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// sets whether MPI should be used
|
||||
void rocprofsys_set_mpi(bool, bool) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// starts an instrumentation region
|
||||
void rocprofsys_push_trace(const char*) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// stops an instrumentation region
|
||||
void rocprofsys_pop_trace(const char*) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// starts an instrumentation region (user-defined)
|
||||
int rocprofsys_push_region(const char*) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// stops an instrumentation region (user-defined)
|
||||
int rocprofsys_pop_region(const char*) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// starts an instrumentation region in a user-defined category and (optionally)
|
||||
/// adds annotations to the perfetto trace.
|
||||
int rocprofsys_push_category_region(rocprofsys_category_t, const char*,
|
||||
rocprofsys_annotation_t*,
|
||||
size_t) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// stops an instrumentation region in a user-defined category and (optionally)
|
||||
/// adds annotations to the perfetto trace.
|
||||
int rocprofsys_pop_category_region(rocprofsys_category_t, const char*,
|
||||
rocprofsys_annotation_t*,
|
||||
size_t) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// stores source code information
|
||||
void rocprofsys_register_source(const char* file, const char* func, size_t line,
|
||||
size_t address,
|
||||
const char* source) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// increments coverage values
|
||||
void rocprofsys_register_coverage(const char* file, const char* func,
|
||||
size_t address) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// mark causal progress
|
||||
void rocprofsys_progress(const char*) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
/// mark causal progress with annotations
|
||||
void rocprofsys_annotated_progress(const char*, rocprofsys_annotation_t*,
|
||||
size_t) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
// these are the real implementations for internal calling convention
|
||||
void rocprofsys_init_library_hidden(void) ROCPROFSYS_HIDDEN_API;
|
||||
bool rocprofsys_init_tooling_hidden(void) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_init_hidden(const char*, bool, const char*) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_finalize_hidden(void) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_reset_preload_hidden(void) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_set_env_hidden(const char*, const char*) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_set_mpi_hidden(bool, bool) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_push_trace_hidden(const char*) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_pop_trace_hidden(const char*) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_push_region_hidden(const char*) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_pop_region_hidden(const char*) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_push_category_region_hidden(rocprofsys_category_t, const char*,
|
||||
rocprofsys_annotation_t*,
|
||||
size_t) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_pop_category_region_hidden(rocprofsys_category_t, const char*,
|
||||
rocprofsys_annotation_t*,
|
||||
size_t) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_register_source_hidden(const char*, const char*, size_t, size_t,
|
||||
const char*) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_register_coverage_hidden(const char*, const char*,
|
||||
size_t) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_progress_hidden(const char*) ROCPROFSYS_HIDDEN_API;
|
||||
void rocprofsys_annotated_progress_hidden(const char*, rocprofsys_annotation_t*,
|
||||
size_t) ROCPROFSYS_HIDDEN_API;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
#
|
||||
set(library_sources
|
||||
${CMAKE_CURRENT_LIST_DIR}/coverage.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/cpu_freq.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/kokkosp.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ompt.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/perf.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/process_sampler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ptl.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/runtime.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/sampling.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/thread_deleter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/thread_info.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/tracing.cpp)
|
||||
|
||||
set(library_headers
|
||||
${CMAKE_CURRENT_LIST_DIR}/coverage.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/cpu_freq.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ompt.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/process_sampler.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/perf.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ptl.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rcclp.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocm.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocm_smi.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprofiler.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/roctracer.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/runtime.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/sampling.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}/tracing.hpp)
|
||||
|
||||
target_sources(rocprofiler-systems-object-library PRIVATE ${library_sources}
|
||||
${library_headers})
|
||||
|
||||
if(ROCPROFSYS_USE_ROCTRACER OR ROCPROFSYS_USE_ROCPROFILER)
|
||||
target_sources(rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/rocm.cpp)
|
||||
endif()
|
||||
|
||||
if(ROCPROFSYS_USE_ROCTRACER)
|
||||
target_sources(rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/roctracer.cpp)
|
||||
endif()
|
||||
|
||||
if(ROCPROFSYS_USE_RCCL)
|
||||
target_sources(rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/rcclp.cpp)
|
||||
endif()
|
||||
|
||||
if(ROCPROFSYS_USE_ROCPROFILER)
|
||||
target_sources(
|
||||
rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/rocprofiler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprofiler.hpp)
|
||||
endif()
|
||||
|
||||
if(ROCPROFSYS_USE_ROCM_SMI)
|
||||
target_sources(rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/rocm_smi.cpp)
|
||||
endif()
|
||||
|
||||
add_subdirectory(causal)
|
||||
add_subdirectory(components)
|
||||
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/rocprof-sys
|
||||
PROPERTIES COMPILE_DEFINITIONS NDEBUG COMPILE_OPTIONS "-g0;-O3")
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
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}/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}/sample_data.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/sampling.hpp ${CMAKE_CURRENT_LIST_DIR}/selected_entry.hpp)
|
||||
|
||||
target_sources(rocprofiler-systems-object-library PRIVATE ${causal_sources}
|
||||
${causal_headers})
|
||||
|
||||
add_subdirectory(components)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#
|
||||
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(rocprofiler-systems-object-library PRIVATE ${component_sources}
|
||||
${component_headers})
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/concepts.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/causal/data.hpp"
|
||||
#include "library/causal/delay.hpp"
|
||||
#include "library/causal/experiment.hpp"
|
||||
#include "library/perf.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
#include "library/tracing.hpp"
|
||||
|
||||
#include <timemory/components/timing/backends.hpp>
|
||||
#include <timemory/components/timing/wall_clock.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/type_traits.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 <execinfo.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using ::tim::backtrace::get_unw_signal_frame_stack_raw;
|
||||
|
||||
int realtime_signal = 0;
|
||||
int cputime_signal = 0;
|
||||
int overflow_signal = 0;
|
||||
|
||||
void
|
||||
generic_global_init()
|
||||
{
|
||||
// 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
|
||||
if(realtime_signal + cputime_signal + overflow_signal == 0)
|
||||
{
|
||||
realtime_signal = get_sampling_realtime_signal();
|
||||
cputime_signal = get_sampling_cputime_signal();
|
||||
overflow_signal = get_sampling_overflow_signal();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
overflow::global_init()
|
||||
{
|
||||
// do not delete these lines.
|
||||
generic_global_init();
|
||||
}
|
||||
|
||||
void
|
||||
backtrace::global_init()
|
||||
{
|
||||
// do not delete these lines.
|
||||
generic_global_init();
|
||||
}
|
||||
|
||||
void
|
||||
overflow::sample(int _sig)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
static thread_local const auto& _tinfo = thread_info::get();
|
||||
auto _tid = _tinfo->index_data->sequent_value;
|
||||
auto& _perf_event = perf::get_instance(_tid);
|
||||
|
||||
if(!_perf_event) return;
|
||||
|
||||
m_index = causal::experiment::get_index();
|
||||
|
||||
_perf_event->stop();
|
||||
|
||||
for(auto itr : *_perf_event)
|
||||
{
|
||||
if(itr.is_sample())
|
||||
{
|
||||
auto _sample_ip = itr.get_ip();
|
||||
auto _data = callchain_t{};
|
||||
_data.emplace_back(_sample_ip);
|
||||
for(auto ditr : itr.get_callchain())
|
||||
{
|
||||
if(ditr != _sample_ip) _data.emplace_back(ditr);
|
||||
if(_data.size() == _data.capacity()) break;
|
||||
}
|
||||
|
||||
if(causal::experiment::is_active() && causal::experiment::is_selected(_data))
|
||||
{
|
||||
++m_selected;
|
||||
causal::experiment::add_selected();
|
||||
causal::delay::get_local() += causal::experiment::get_delay();
|
||||
}
|
||||
else if(!causal::experiment::is_active())
|
||||
{
|
||||
causal::set_current_selection(_data);
|
||||
}
|
||||
|
||||
m_stack.emplace_back(_data);
|
||||
}
|
||||
}
|
||||
|
||||
_perf_event->start();
|
||||
|
||||
if(_sig == cputime_signal) causal::delay::process();
|
||||
}
|
||||
|
||||
void
|
||||
backtrace::sample(int _sig)
|
||||
{
|
||||
constexpr size_t depth = ::rocprofsys::causal::unwind_depth;
|
||||
constexpr int64_t ignore_depth = ::rocprofsys::causal::unwind_offset;
|
||||
constexpr size_t select_init = std::numeric_limits<size_t>::max();
|
||||
constexpr size_t select_ival = 5; // interval at which realtime signal contributes
|
||||
|
||||
// update the last sample for backtrace signal(s) even when in use
|
||||
static thread_local size_t _protect_flag = 0;
|
||||
|
||||
// the select_count is initialized to max so that realtime signal does
|
||||
// not initially set the current selection
|
||||
static thread_local size_t _select_count = select_init;
|
||||
static thread_local size_t _select_zeros = 0;
|
||||
|
||||
if((_protect_flag & 1) == 1 ||
|
||||
ROCPROFSYS_UNLIKELY(!trait::runtime_enabled<causal::component::backtrace>::get()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
++_protect_flag;
|
||||
// on RedHat, the unw_step within get_unw_signal_frame_stack_raw involves a mutex lock
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
m_index = causal::experiment::get_index();
|
||||
m_stack = get_unw_signal_frame_stack_raw<depth, ignore_depth>();
|
||||
|
||||
auto _set_current_selection = [](auto _stack) {
|
||||
// save the former selection count
|
||||
auto _former_count = _select_count;
|
||||
// get the current selection count
|
||||
_select_count = causal::set_current_selection(_stack);
|
||||
// if the selection count was reduced, reset select zeros.
|
||||
// this typically means that a new experiment was started
|
||||
if(_former_count > _select_count) _select_zeros = 0;
|
||||
// if no PCs were selected, increment the select zeros.
|
||||
// if the cputime signal has not selected a PC in select_ival iterations,
|
||||
// then the realtime signal will start contributing to the current
|
||||
// selection. We generally want only the cputime signal to contribute
|
||||
// because those PCs are in-use (since the thread CPU clock in increasing)
|
||||
if(_select_count == 0) ++_select_zeros;
|
||||
};
|
||||
|
||||
// the batch handler timer delivers a signal according to the thread CPU
|
||||
// clock, ensuring that setting the current selection is preferred when the thread
|
||||
// is active and processing the delays happens only when the thread is active
|
||||
if(_sig == cputime_signal)
|
||||
{
|
||||
if(causal::experiment::is_active())
|
||||
causal::delay::process();
|
||||
else
|
||||
_set_current_selection(m_stack);
|
||||
}
|
||||
else if(_sig == realtime_signal)
|
||||
{
|
||||
if(causal::experiment::is_active() && causal::experiment::is_selected(m_stack))
|
||||
{
|
||||
m_selected = true;
|
||||
causal::experiment::add_selected();
|
||||
causal::delay::get_local() += causal::experiment::get_delay();
|
||||
}
|
||||
else if(!causal::experiment::is_active())
|
||||
{
|
||||
// if no PCs have been selected after at least "select_ival" call-stacks via
|
||||
// the cputime signal, then contribute the call-stack via the realtime signal.
|
||||
// This can be particularly relevant in end-to-end runs targeting a particular
|
||||
// line/function since it is possible that the line/function is situated such
|
||||
// the cputime signal is never delivered when executing the particular
|
||||
// line/function... despite the line/function executing in between the
|
||||
// the cputime signals. This is rare but has been observed
|
||||
//
|
||||
if(_select_count == 0 && _select_zeros >= select_ival)
|
||||
_set_current_selection(m_stack);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_THROW("unhandled signal %i\n", _sig);
|
||||
}
|
||||
|
||||
++_protect_flag;
|
||||
}
|
||||
|
||||
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 _period = 1.0 / 1000.0;
|
||||
int64_t _period_nsec = static_cast<int64_t>(_period * units::sec) % units::sec;
|
||||
return static_cast<Tp>(_period_nsec) / static_cast<cast_type>(_units);
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
|
||||
#define INSTANTIATE_BT_CAUSAL_PERIOD(TYPE) \
|
||||
template TYPE rocprofsys::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)
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/causal/data.hpp"
|
||||
#include "library/causal/fwd.hpp"
|
||||
#include "library/perf.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 rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct overflow : comp::empty_base
|
||||
{
|
||||
static constexpr auto alt_stack_size = perf::perf_event::max_batch_size;
|
||||
|
||||
using value_type = void;
|
||||
using callchain_t = container::static_vector<uintptr_t, unwind_depth>;
|
||||
using alt_stack_t = container::static_vector<callchain_t, alt_stack_size>;
|
||||
|
||||
static std::string label() { return "causal::overflow"; }
|
||||
static void global_init();
|
||||
|
||||
void sample(int = -1);
|
||||
|
||||
auto get_selected() const { return m_selected; }
|
||||
auto get_index() const { return m_index; }
|
||||
const auto& get_stack() const { return m_stack; }
|
||||
|
||||
private:
|
||||
int32_t m_selected = 0;
|
||||
uint32_t m_index = 0;
|
||||
alt_stack_t m_stack = {};
|
||||
};
|
||||
|
||||
struct backtrace : comp::empty_base
|
||||
{
|
||||
using value_type = void;
|
||||
using callchain_t = container::static_vector<uint64_t, unwind_depth>;
|
||||
|
||||
static std::string label() { return "causal::backtrace"; }
|
||||
static void global_init();
|
||||
|
||||
backtrace() = default;
|
||||
~backtrace() = default;
|
||||
backtrace(const backtrace&) = default;
|
||||
backtrace(backtrace&&) noexcept = default;
|
||||
|
||||
backtrace& operator=(const backtrace&) = default;
|
||||
backtrace& operator=(backtrace&&) noexcept = default;
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
bool m_selected = false;
|
||||
uint32_t m_index = 0;
|
||||
causal::unwind_addr_t m_stack = {};
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/causal/components/causal_gotcha.hpp"
|
||||
#include "library/causal/delay.hpp"
|
||||
#include "library/causal/experiment.hpp"
|
||||
#include "library/causal/sampling.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/hash/types.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <pthread.h>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#pragma weak pthread_join
|
||||
#pragma weak pthread_mutex_lock
|
||||
#pragma weak pthread_spin_lock
|
||||
#pragma weak pthread_cond_wait
|
||||
#pragma weak pthread_rwlock_rdlock
|
||||
#pragma weak pthread_rwlock_wrlock
|
||||
#pragma weak pthread_tryjoin_np
|
||||
#pragma weak pthread_timedjoin_np
|
||||
#pragma weak pthread_cond_timedwait
|
||||
#pragma weak pthread_rwlock_timedrdlock
|
||||
#pragma weak pthread_rwlock_timedwrlock
|
||||
#pragma weak pthread_mutex_trylock
|
||||
#pragma weak pthread_spin_trylock
|
||||
#pragma weak pthread_rwlock_trywrlock
|
||||
#pragma weak sigwait
|
||||
#pragma weak sigwaitinfo
|
||||
#pragma weak sigtimedwait
|
||||
#pragma weak sigsuspend
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
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;
|
||||
|
||||
// postblock(true)
|
||||
// - pthread_join
|
||||
// - pthread_mutex_lock
|
||||
// - pthread_cond_wait
|
||||
// - pthread_barrier_wait
|
||||
// - pthread_rwlock_rdlock
|
||||
// - pthread_rwlock_wrlock
|
||||
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 0, pthread_join);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 1, pthread_mutex_lock);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 2, pthread_spin_lock);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 3, pthread_cond_wait);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 4, pthread_rwlock_rdlock);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 5, pthread_rwlock_wrlock);
|
||||
|
||||
// postblock(result == 0)
|
||||
// - pthread_tryjoin_np
|
||||
// - pthread_timedjoin_np
|
||||
// - pthread_cond_timedwait
|
||||
// - pthread_rwlock_timedrdlock
|
||||
// - pthread_rwlock_timedwrlock
|
||||
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 6, pthread_tryjoin_np);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 7, pthread_timedjoin_np);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 8, pthread_cond_timedwait);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 9, pthread_rwlock_timedrdlock);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 10, pthread_rwlock_timedwrlock);
|
||||
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 11, pthread_mutex_trylock);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 12, pthread_spin_trylock);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 13, pthread_rwlock_trywrlock);
|
||||
|
||||
// postblock(...)
|
||||
// - sigwait
|
||||
// - sigwaitinfo
|
||||
// - sigtimedwait
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 14, sigwait);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 15, sigwaitinfo);
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 16, sigtimedwait);
|
||||
|
||||
// other
|
||||
TIMEMORY_C_GOTCHA(blocking_gotcha_t, 17, sigsuspend);
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
blocking_gotcha::shutdown()
|
||||
{
|
||||
blocking_gotcha_t::disable();
|
||||
}
|
||||
|
||||
template <size_t Idx, typename Ret, typename... Args>
|
||||
std::enable_if_t<(Idx <= blocking_gotcha::indexes::maybe_post_block_max_idx), Ret>
|
||||
blocking_gotcha::operator()(gotcha_index<Idx>, Ret (*_func)(Args...),
|
||||
Args... _args) const noexcept
|
||||
{
|
||||
int64_t _delay_value = causal::delay::get_global().load(std::memory_order_relaxed);
|
||||
|
||||
causal::sampling::block_backtrace_samples();
|
||||
auto _ret = (*_func)(_args...);
|
||||
causal::sampling::unblock_backtrace_samples();
|
||||
|
||||
if(get_thread_state() < ::rocprofsys::ThreadState::Internal)
|
||||
{
|
||||
if constexpr(Idx >= always_post_block_min_idx && Idx <= always_post_block_max_idx)
|
||||
{
|
||||
causal::delay::postblock(_delay_value);
|
||||
}
|
||||
else if constexpr(Idx >= maybe_post_block_min_idx &&
|
||||
Idx <= maybe_post_block_max_idx)
|
||||
{
|
||||
if(_ret == 0) causal::delay::postblock(_delay_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(Idx > maybe_post_block_max_idx, "Error! bad overload");
|
||||
}
|
||||
}
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
int
|
||||
blocking_gotcha::operator()(gotcha_index<sigwait_idx>, int (*)(const sigset_t*, int*),
|
||||
const sigset_t* _set_v, int* _sig) const noexcept
|
||||
{
|
||||
auto _active = get_thread_state() < ::rocprofsys::ThreadState::Internal;
|
||||
|
||||
sigset_t _set = *_set_v;
|
||||
causal_gotcha::remove_signals(&_set);
|
||||
siginfo_t _info;
|
||||
|
||||
int64_t _delay_value = (_active) ? causal::delay::get_global().load() : 0;
|
||||
|
||||
auto* _data = blocking_gotcha_t::at(16);
|
||||
auto f_sigwaitinfo = reinterpret_cast<decltype(&sigwaitinfo)>(_data->wrappee);
|
||||
|
||||
causal::sampling::block_backtrace_samples();
|
||||
auto _ret = (*f_sigwaitinfo)(&_set, &_info);
|
||||
causal::sampling::unblock_backtrace_samples();
|
||||
|
||||
// Woken up by another thread if the call did not fail and this is waking process
|
||||
if(_active && _ret != -1 && _info.si_pid == process::get_id())
|
||||
causal::delay::postblock(_delay_value);
|
||||
|
||||
if(_ret == -1)
|
||||
return errno; // If there was an error, return the error code
|
||||
else
|
||||
*_sig = _ret; // sig is declared as non-null so skip check
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
blocking_gotcha::operator()(gotcha_index<sigwaitinfo_idx>,
|
||||
int (*_func)(const sigset_t*, siginfo_t*),
|
||||
const sigset_t* _set_v, siginfo_t* _info_v) const noexcept
|
||||
{
|
||||
auto _active = get_thread_state() < ::rocprofsys::ThreadState::Internal;
|
||||
|
||||
sigset_t _set = *_set_v;
|
||||
causal_gotcha::remove_signals(&_set);
|
||||
siginfo_t _info;
|
||||
|
||||
int64_t _delay_value = (_active) ? causal::delay::get_global().load() : 0;
|
||||
|
||||
causal::sampling::block_backtrace_samples();
|
||||
auto _ret = (*_func)(&_set, &_info);
|
||||
causal::sampling::unblock_backtrace_samples();
|
||||
|
||||
// Woken up by another thread if the call did not fail and this is waking process
|
||||
if(_active && _ret > 0 && _info.si_pid == process::get_id())
|
||||
causal::delay::postblock(_delay_value);
|
||||
|
||||
if(_ret > 0 && _info_v) *_info_v = _info;
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
int
|
||||
blocking_gotcha::operator()(gotcha_index<sigtimedwait_idx>,
|
||||
int (*_func)(const sigset_t*, siginfo_t*,
|
||||
const struct timespec*),
|
||||
const sigset_t* _set_v, siginfo_t* _info_v,
|
||||
const struct timespec* _wait_v) const noexcept
|
||||
{
|
||||
auto _active = get_thread_state() < ::rocprofsys::ThreadState::Internal;
|
||||
|
||||
sigset_t _set = *_set_v;
|
||||
causal_gotcha::remove_signals(&_set);
|
||||
siginfo_t _info;
|
||||
|
||||
int64_t _delay_value = (_active) ? causal::delay::get_global().load() : 0;
|
||||
|
||||
causal::sampling::block_backtrace_samples();
|
||||
auto _ret = (*_func)(&_set, &_info, _wait_v);
|
||||
causal::sampling::unblock_backtrace_samples();
|
||||
|
||||
// Woken up by another thread if the call did not fail and this is waking process
|
||||
if(_active && _ret > 0 && _info.si_pid == process::get_id())
|
||||
causal::delay::postblock(_delay_value);
|
||||
|
||||
if(_ret > 0 && _info_v) *_info_v = _info;
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
int
|
||||
blocking_gotcha::operator()(gotcha_index<sigsuspend_idx>, int (*)(const sigset_t*),
|
||||
const sigset_t* _set_v) const noexcept
|
||||
{
|
||||
auto _old_set = sigset_t{};
|
||||
int _sig = 0;
|
||||
::sigprocmask(SIG_SETMASK, _set_v, &_old_set);
|
||||
// sigwait is wrapped so no need to block/unblock signals
|
||||
auto _ret = ::sigwait(_set_v, &_sig);
|
||||
::sigprocmask(SIG_SETMASK, &_old_set, nullptr);
|
||||
|
||||
return _ret;
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_INVOKE_PREINIT(rocprofsys::causal::component::blocking_gotcha)
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/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 rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct blocking_gotcha : comp::base<blocking_gotcha, void>
|
||||
{
|
||||
static constexpr size_t gotcha_capacity = 19;
|
||||
|
||||
template <size_t Idx>
|
||||
using gotcha_index = std::integral_constant<size_t, Idx>;
|
||||
|
||||
enum indexes
|
||||
{
|
||||
always_post_block_min_idx = 0,
|
||||
always_post_block_max_idx = 5,
|
||||
maybe_post_block_min_idx = 6,
|
||||
maybe_post_block_max_idx = 13,
|
||||
sigwait_idx = 14,
|
||||
sigwaitinfo_idx = 15,
|
||||
sigtimedwait_idx = 16,
|
||||
sigsuspend_idx = 17,
|
||||
indexes_max = gotcha_capacity - 1,
|
||||
};
|
||||
|
||||
ROCPROFSYS_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();
|
||||
|
||||
template <size_t Idx, typename Ret, typename... Args>
|
||||
std::enable_if_t<(Idx <= maybe_post_block_max_idx), Ret> operator()(
|
||||
gotcha_index<Idx>, Ret (*)(Args...), Args...) const noexcept;
|
||||
|
||||
int operator()(gotcha_index<sigwait_idx>, int (*)(const sigset_t*, int*),
|
||||
const sigset_t*, int*) const noexcept;
|
||||
|
||||
int operator()(gotcha_index<sigwaitinfo_idx>, int (*)(const sigset_t*, siginfo_t*),
|
||||
const sigset_t*, siginfo_t*) const noexcept;
|
||||
|
||||
int operator()(gotcha_index<sigtimedwait_idx>,
|
||||
int (*)(const sigset_t*, siginfo_t*, const struct timespec*),
|
||||
const sigset_t*, siginfo_t*, const struct timespec*) const noexcept;
|
||||
|
||||
int operator()(gotcha_index<sigsuspend_idx>, int (*)(const sigset_t*),
|
||||
const sigset_t*) const noexcept;
|
||||
};
|
||||
|
||||
using blocking_gotcha_t =
|
||||
comp::gotcha<blocking_gotcha::gotcha_capacity, tim::type_list<>, blocking_gotcha>;
|
||||
} // namespace component
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(prevent_reentry, causal::component::blocking_gotcha_t,
|
||||
false_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(static_data, causal::component::blocking_gotcha_t,
|
||||
false_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(fast_gotcha, causal::component::blocking_gotcha_t,
|
||||
true_type)
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/config.hpp"
|
||||
#include "library/causal/components/blocking_gotcha.hpp"
|
||||
#include "library/causal/components/unblocking_gotcha.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/signals/signal_mask.hpp>
|
||||
#include <timemory/utility/macros.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
const auto&
|
||||
sampling_signals()
|
||||
{
|
||||
static auto _v = get_sampling_signals();
|
||||
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();
|
||||
}
|
||||
|
||||
void
|
||||
causal_gotcha::remove_signals(sigset_t* _set)
|
||||
{
|
||||
for(auto _sig : sampling_signals())
|
||||
{
|
||||
if(sigismember(_set, _sig) != 0) sigdelset(_set, _sig);
|
||||
}
|
||||
|
||||
if(sigismember(_set, SIGSEGV) != 0) sigdelset(_set, SIGSEGV);
|
||||
|
||||
if(sigismember(_set, SIGABRT) != 0) sigdelset(_set, SIGABRT);
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct causal_gotcha : tim::component::base<causal_gotcha, void>
|
||||
{
|
||||
ROCPROFSYS_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();
|
||||
|
||||
static void remove_signals(sigset_t*);
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/concepts.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/causal/experiment.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/hash/types.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
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)
|
||||
{
|
||||
return thread_data<progress_allocator_t>::instance(construct_on_thread{ _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 rocprofsys
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace operation
|
||||
{
|
||||
namespace causal = rocprofsys::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-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/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 rocprofsys
|
||||
{
|
||||
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();
|
||||
|
||||
ROCPROFSYS_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 rocprofsys
|
||||
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(uses_storage, causal::component::progress_point,
|
||||
false_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(flat_storage, causal::component::progress_point,
|
||||
true_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(uses_timing_units, causal::component::progress_point,
|
||||
true_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(is_timing_category, causal::component::progress_point,
|
||||
true_type)
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace operation
|
||||
{
|
||||
template <>
|
||||
struct push_node<rocprofsys::causal::component::progress_point>
|
||||
{
|
||||
using type = rocprofsys::causal::component::progress_point;
|
||||
|
||||
ROCPROFSYS_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<rocprofsys::causal::component::progress_point>
|
||||
{
|
||||
using type = rocprofsys::causal::component::progress_point;
|
||||
|
||||
ROCPROFSYS_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
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/causal/components/causal_gotcha.hpp"
|
||||
#include "library/causal/delay.hpp"
|
||||
#include "library/causal/experiment.hpp"
|
||||
#include "library/causal/sampling.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/hash/types.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <pthread.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#pragma weak pthread_mutex_unlock
|
||||
#pragma weak pthread_spin_unlock
|
||||
#pragma weak pthread_cond_signal
|
||||
#pragma weak pthread_cond_broadcast
|
||||
#pragma weak pthread_kill
|
||||
#pragma weak pthread_sigqueue
|
||||
#pragma weak pthread_barrier_wait
|
||||
#pragma weak kill
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
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;
|
||||
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 0, pthread_mutex_unlock);
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 1, pthread_spin_unlock);
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 2, pthread_rwlock_unlock);
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 3, pthread_cond_signal);
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 4, pthread_cond_broadcast);
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 5, pthread_kill);
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 6, pthread_sigqueue);
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 7, pthread_barrier_wait);
|
||||
TIMEMORY_C_GOTCHA(unblocking_gotcha_t, 8, kill);
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
unblocking_gotcha::shutdown()
|
||||
{
|
||||
unblocking_gotcha_t::disable();
|
||||
}
|
||||
|
||||
template <size_t Idx, typename Ret, typename... Args>
|
||||
std::enable_if_t<(Idx < unblocking_gotcha::indexes::kill_idx), Ret>
|
||||
unblocking_gotcha::operator()(gotcha_index<Idx>, Ret (*_func)(Args...),
|
||||
Args... _args) const noexcept
|
||||
{
|
||||
auto _active = get_thread_state() < ::rocprofsys::ThreadState::Internal;
|
||||
|
||||
if(_active)
|
||||
{
|
||||
causal::delay::process();
|
||||
|
||||
if constexpr(Idx == pthread_barrier_wait_idx)
|
||||
{
|
||||
int64_t _delay_value = (_active) ? causal::delay::get_global().load() : 0;
|
||||
|
||||
causal::sampling::block_backtrace_samples();
|
||||
auto _ret = (*_func)(_args...);
|
||||
causal::sampling::unblock_backtrace_samples();
|
||||
|
||||
causal::delay::postblock(_delay_value);
|
||||
return _ret;
|
||||
}
|
||||
}
|
||||
|
||||
return (*_func)(_args...);
|
||||
}
|
||||
|
||||
int
|
||||
unblocking_gotcha::operator()(gotcha_index<kill_idx>, int (*_func)(pid_t, int),
|
||||
pid_t _pid, int _sig) const noexcept
|
||||
{
|
||||
auto _active = get_thread_state() < ::rocprofsys::ThreadState::Internal;
|
||||
|
||||
if(_active && _pid == process::get_id()) causal::delay::process();
|
||||
|
||||
causal::sampling::block_backtrace_samples();
|
||||
auto _ret = (*_func)(_pid, _sig);
|
||||
causal::sampling::unblock_backtrace_samples();
|
||||
|
||||
return _ret;
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_INVOKE_PREINIT(rocprofsys::causal::component::unblocking_gotcha)
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <timemory/components/gotcha/backends.hpp>
|
||||
#include <timemory/mpl/macros.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct unblocking_gotcha : comp::base<unblocking_gotcha, void>
|
||||
{
|
||||
static constexpr size_t gotcha_capacity = 9;
|
||||
|
||||
enum indexes
|
||||
{
|
||||
pthread_barrier_wait_idx = 7,
|
||||
kill_idx = 8,
|
||||
indexes_max = gotcha_capacity,
|
||||
};
|
||||
|
||||
template <size_t Idx>
|
||||
using gotcha_index = std::integral_constant<size_t, Idx>;
|
||||
|
||||
ROCPROFSYS_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();
|
||||
|
||||
template <size_t Idx, typename Ret, typename... Args>
|
||||
std::enable_if_t<(Idx < kill_idx), Ret> operator()(gotcha_index<Idx>,
|
||||
Ret (*)(Args...),
|
||||
Args...) const noexcept;
|
||||
|
||||
int operator()(gotcha_index<kill_idx>, int (*)(pid_t, int), pid_t,
|
||||
int) const noexcept;
|
||||
};
|
||||
|
||||
using unblocking_gotcha_t =
|
||||
comp::gotcha<unblocking_gotcha::gotcha_capacity, tim::type_list<>, unblocking_gotcha>;
|
||||
} // namespace component
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(prevent_reentry, causal::component::unblocking_gotcha_t,
|
||||
false_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(static_data, causal::component::unblocking_gotcha_t,
|
||||
false_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(fast_gotcha, causal::component::unblocking_gotcha_t,
|
||||
true_type)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "binary/analysis.hpp"
|
||||
#include "core/binary/fwd.hpp"
|
||||
#include "core/containers/c_array.hpp"
|
||||
#include "core/containers/static_vector.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/causal/fwd.hpp"
|
||||
#include "library/thread_data.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 rocprofsys
|
||||
{
|
||||
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);
|
||||
|
||||
size_t set_current_selection(unwind_addr_t);
|
||||
size_t set_current_selection(container::c_array<uint64_t>);
|
||||
|
||||
void
|
||||
reset_sample_selection();
|
||||
|
||||
selected_entry
|
||||
sample_selection(size_t _nitr = 1000, size_t _wait_ns = 100000);
|
||||
|
||||
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 rocprofsys
|
||||
@@ -0,0 +1,191 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/state.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/causal/components/causal_gotcha.hpp"
|
||||
#include "library/causal/experiment.hpp"
|
||||
#include "library/causal/sampling.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
#include "library/tracing.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 rocprofsys
|
||||
{
|
||||
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);
|
||||
ROCPROFSYS_CONDITIONAL_THROW(
|
||||
_diff < _val, "Error! sleep_for(%zu) [nanoseconds] >= %zu", _val, _diff);
|
||||
_stats += (_diff - _val);
|
||||
}
|
||||
|
||||
ROCPROFSYS_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 = 0;
|
||||
} // namespace
|
||||
|
||||
void
|
||||
delay::setup()
|
||||
{
|
||||
static std::once_flag _once{};
|
||||
std::call_once(_once, []() { sleep_for_overhead = compute_sleep_for_overhead(); });
|
||||
}
|
||||
|
||||
void
|
||||
delay::process()
|
||||
{
|
||||
if(causal::experiment::is_active())
|
||||
{
|
||||
if(get_global() < get_local())
|
||||
{
|
||||
get_global() += (get_local() - get_global());
|
||||
}
|
||||
else if(get_global() > get_local())
|
||||
{
|
||||
::rocprofsys::causal::sampling::pause();
|
||||
auto _beg = tracing::now();
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::nanoseconds{ get_global() - get_local() });
|
||||
get_local() += (tracing::now() - _beg);
|
||||
::rocprofsys::causal::sampling::resume();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
get_local() = get_global();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
delay::credit()
|
||||
{
|
||||
auto _diff = get_global() - get_local();
|
||||
if(_diff > 0)
|
||||
{
|
||||
get_local() += _diff;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
delay::preblock()
|
||||
{
|
||||
auto _diff = get_global() - get_local();
|
||||
if(_diff > 0)
|
||||
{
|
||||
get_local() += _diff;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
delay::postblock(int64_t _preblock_global_delay_value)
|
||||
{
|
||||
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 rocprofsys
|
||||
@@ -0,0 +1,61 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/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 rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
struct delay : comp::empty_base
|
||||
{
|
||||
using value_type = void;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(delay)
|
||||
|
||||
static void setup();
|
||||
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 rocprofsys
|
||||
@@ -0,0 +1,715 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "binary/analysis.hpp"
|
||||
#include "binary/dwarf_entry.hpp"
|
||||
#include "binary/symbol.hpp"
|
||||
#include "common/defines.h"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/state.hpp"
|
||||
#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/causal/sample_data.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
#include "library/tracing.hpp"
|
||||
|
||||
#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 <timemory/unwind/dlinfo.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <ratio>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using backtrace_causal = rocprofsys::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>("ROCPROFSYS_CAUSAL_SCALE_EXPERIMENT_TIME_BY_SPEEDUP", false);
|
||||
} // namespace
|
||||
|
||||
experiment::sample::sample(const base_type& _b, uint64_t _c)
|
||||
: base_type{ _b }
|
||||
, count{ _c }
|
||||
{
|
||||
if(lineinfo)
|
||||
{
|
||||
for(const auto& itr : lineinfo.lines)
|
||||
{
|
||||
if(itr.inlined)
|
||||
inlines.emplace_back(
|
||||
binary::inlined_symbol{ itr.line, itr.location, itr.name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
experiment::sample::operator==(const sample& _v) const
|
||||
{
|
||||
return base_type::operator==(_v);
|
||||
}
|
||||
|
||||
bool
|
||||
experiment::sample::operator<(const sample& _v) const
|
||||
{
|
||||
return base_type::operator<(_v);
|
||||
}
|
||||
|
||||
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;
|
||||
using cereal::make_nvp;
|
||||
|
||||
ar(cereal::make_nvp("count", count));
|
||||
if constexpr(concepts::is_output_archive<ArchiveT>::value)
|
||||
{
|
||||
ar(cereal::make_nvp("location", get_identifier()));
|
||||
}
|
||||
|
||||
ar.setNextName("info");
|
||||
ar.startNode();
|
||||
ar(make_nvp("address", address), make_nvp("line", lineno), make_nvp("file", location),
|
||||
make_nvp("func", name));
|
||||
|
||||
if constexpr(concepts::is_output_archive<ArchiveT>::value)
|
||||
{
|
||||
ar(cereal::make_nvp("dfunc", demangle(name)),
|
||||
cereal::make_nvp("dwarf_info", std::vector<binary::dwarf_entry>{}));
|
||||
}
|
||||
ar(cereal::make_nvp("inlines", inlines));
|
||||
ar.finishNode();
|
||||
|
||||
ar(cereal::make_nvp("dlinfo", info));
|
||||
}
|
||||
|
||||
std::string
|
||||
experiment::sample::get_identifier() const
|
||||
{
|
||||
return (lineno > 0 && !location.empty()) ? join(":", location, lineno)
|
||||
: demangle(name);
|
||||
}
|
||||
|
||||
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_back(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));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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();
|
||||
|
||||
ROCPROFSYS_VERBOSE(0, "Starting causal experiment #%-3u: %s\n", index,
|
||||
as_string().c_str());
|
||||
|
||||
if(get_state() < State::Finalized)
|
||||
{
|
||||
current_experiment_value = *this;
|
||||
current_selected_count.store(0);
|
||||
current_experiment.store(this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// sync data
|
||||
delay::sync();
|
||||
|
||||
auto _prog_stats = tim::statistics<double>{};
|
||||
auto _prog_vals = std::vector<int64_t>{};
|
||||
_prog_vals.reserve(fini_progress.size());
|
||||
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_vals.emplace_back(_num);
|
||||
}
|
||||
std::sort(_prog_vals.begin(), _prog_vals.end());
|
||||
for(auto itr : _prog_vals)
|
||||
_prog_stats += itr;
|
||||
|
||||
auto _nvals = _prog_vals.size();
|
||||
auto _medi = (_nvals > 2) ? _prog_vals.at(_nvals / 2) : _prog_vals.front();
|
||||
auto _mean = (_nvals > 0) ? _prog_stats.get_mean() : 0;
|
||||
auto _high = (_nvals > 0) ? _prog_stats.get_max() : 0;
|
||||
auto _lowv = (_nvals > 0) ? _prog_stats.get_min() : 0;
|
||||
|
||||
if(_lowv <= 3 && (_mean < 5 || _medi < 5))
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(2,
|
||||
"[progress points] increasing experiment time :: low: %6.3f, "
|
||||
"high: %6.3f, mean: %6.3f, median: %zi\n",
|
||||
_lowv, _high, _mean, _medi);
|
||||
global_scaling *= 2;
|
||||
++global_scaling_increments; // keep track of how many successive increments have
|
||||
// been performed
|
||||
}
|
||||
else if(_mean > 10 && _lowv >= 8 && global_scaling > 1)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(2,
|
||||
"[progress points] decreasing experiment time :: low: %6.3f, "
|
||||
"high: %6.3f, mean: %6.3f, median: %zi\n",
|
||||
_lowv, _high, _mean, _medi);
|
||||
global_scaling /= 2;
|
||||
global_scaling_increments = 0;
|
||||
}
|
||||
|
||||
if(ROCPROFSYS_UNLIKELY(global_scaling_increments >= 5))
|
||||
{
|
||||
ROCPROFSYS_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://rocm.docs.amd.com/projects/rocprofiler-systems/en/latest/ 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{ 5 * 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) << " ";
|
||||
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(uint64_t _addr)
|
||||
{
|
||||
return (is_active() && current_experiment_value.selection.contains(_addr));
|
||||
}
|
||||
|
||||
bool
|
||||
experiment::is_selected(unwind_addr_t _stack)
|
||||
{
|
||||
if(is_active())
|
||||
{
|
||||
for(auto itr : _stack)
|
||||
if(itr > 0 && current_experiment_value.selection.contains(itr)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
experiment::is_selected(container::c_array<uint64_t> _stack)
|
||||
{
|
||||
if(is_active())
|
||||
{
|
||||
for(auto itr : _stack)
|
||||
if(itr > 0 && 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);
|
||||
|
||||
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 = [¤t_record](sample&& _v) {
|
||||
current_record.samples.emplace_back(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;
|
||||
}
|
||||
}
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(1, "Processing line info for %zu sampled addresses...\n",
|
||||
_total_samples.size());
|
||||
|
||||
for(const auto& itr : _total_samples)
|
||||
{
|
||||
auto _entry = binary::lookup_ipaddr_entry<true>(itr.first);
|
||||
if(_entry) _add_sample(sample{ *_entry, itr.second });
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
bool _causal_output_reset =
|
||||
config::get_setting_value<bool>("ROCPROFSYS_CAUSAL_FILE_RESET").value_or(false);
|
||||
|
||||
{
|
||||
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("rocprofsys");
|
||||
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
|
||||
{
|
||||
ROCPROFSYS_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);
|
||||
|
||||
ROCPROFSYS_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.get_identifier()
|
||||
<< "\tcount=" << itr.count;
|
||||
if(config::get_debug()) ofs << "\taddress=" << as_hex(itr.address);
|
||||
ofs << "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_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("rocprofsys");
|
||||
ar->startNode();
|
||||
ar->setNextName("causal");
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("records", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_throw_on_error)
|
||||
{
|
||||
ROCPROFSYS_THROW("Error opening causal experiments input file: %s",
|
||||
_fname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return _data;
|
||||
}
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,148 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "binary/dwarf_entry.hpp"
|
||||
#include "binary/symbol.hpp"
|
||||
#include "core/containers/c_array.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/causal/components/backtrace.hpp"
|
||||
#include "library/causal/components/progress_point.hpp"
|
||||
#include "library/causal/data.hpp"
|
||||
#include "library/causal/selected_entry.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/unwind/types.hpp>
|
||||
#include <timemory/utility/unwind.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
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 period_stats_t = tim::statistics<int64_t>;
|
||||
|
||||
struct sample : unwind::processed_entry
|
||||
{
|
||||
using base_type = unwind::processed_entry;
|
||||
|
||||
sample() = default;
|
||||
sample(const base_type&, uint64_t);
|
||||
|
||||
mutable uint64_t count = 0;
|
||||
std::vector<binary::inlined_symbol> inlines = {};
|
||||
|
||||
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);
|
||||
|
||||
std::string get_identifier() const;
|
||||
};
|
||||
|
||||
struct record
|
||||
{
|
||||
int64_t startup = 0;
|
||||
uint64_t runtime = 0;
|
||||
std::vector<experiment> experiments = {};
|
||||
std::vector<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();
|
||||
|
||||
ROCPROFSYS_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(uint64_t);
|
||||
static bool is_selected(unwind_addr_t);
|
||||
static bool is_selected(container::c_array<uint64_t>);
|
||||
static void add_selected();
|
||||
static experiments_t get_experiments();
|
||||
|
||||
template <size_t N>
|
||||
static bool is_selected(std::array<uint64_t, N> _v)
|
||||
{
|
||||
return is_selected(container::c_array<uint64_t>{ _v.data(), _v.size() });
|
||||
}
|
||||
|
||||
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 = 100; /// 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
|
||||
};
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,53 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/binary/fwd.hpp"
|
||||
#include "core/containers/static_vector.hpp"
|
||||
#include "core/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 rocprofsys
|
||||
{
|
||||
namespace unwind = ::tim::unwind;
|
||||
|
||||
namespace causal
|
||||
{
|
||||
static constexpr size_t unwind_depth = ROCPROFSYS_MAX_UNWIND_DEPTH;
|
||||
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 rocprofsys
|
||||
@@ -0,0 +1,84 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/sample_data.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto samples = std::map<uint32_t, std::map<uintptr_t, uint64_t>>{};
|
||||
}
|
||||
|
||||
std::vector<sample_data>
|
||||
get_samples(uint32_t _index)
|
||||
{
|
||||
auto _data = std::vector<sample_data>{};
|
||||
_data.reserve(samples.at(_index).size());
|
||||
for(const auto& itr : samples.at(_index))
|
||||
{
|
||||
_data.emplace_back(sample_data{ itr.first, itr.second });
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::vector<sample_data>>
|
||||
get_samples()
|
||||
{
|
||||
auto _data = std::map<uint32_t, std::vector<sample_data>>{};
|
||||
|
||||
for(const auto& itr : samples)
|
||||
{
|
||||
_data[itr.first] = get_samples(itr.first);
|
||||
}
|
||||
|
||||
return _data;
|
||||
}
|
||||
|
||||
void
|
||||
add_sample(uint32_t _index, uintptr_t _addr, uint64_t _count)
|
||||
{
|
||||
samples[_index][_addr] += _count;
|
||||
}
|
||||
|
||||
void
|
||||
add_samples(uint32_t _index, const std::vector<uintptr_t>& _v)
|
||||
{
|
||||
for(const auto& itr : _v)
|
||||
add_sample(_index, itr);
|
||||
}
|
||||
|
||||
void
|
||||
add_samples(uint32_t _index, const std::map<uintptr_t, uint64_t>& _v)
|
||||
{
|
||||
for(const auto& itr : _v)
|
||||
add_sample(_index, itr.first, itr.second);
|
||||
}
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,67 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
struct sample_data
|
||||
{
|
||||
uintptr_t address = 0x0;
|
||||
mutable uint64_t count = 0;
|
||||
|
||||
bool operator==(sample_data _v) const { return (address == _v.address); }
|
||||
bool operator!=(sample_data _v) const { return !(*this == _v); }
|
||||
bool operator<(sample_data _v) const { return (address < _v.address); }
|
||||
bool operator>(sample_data _v) const { return (address > _v.address); }
|
||||
bool operator<=(sample_data _v) const { return (address <= _v.address); }
|
||||
bool operator>=(sample_data _v) const { return (address >= _v.address); }
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& ar, const unsigned)
|
||||
{
|
||||
ar(cereal::make_nvp("address", address), cereal::make_nvp("count", count));
|
||||
}
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::vector<sample_data>>
|
||||
get_samples();
|
||||
|
||||
void
|
||||
add_samples(uint32_t, const std::vector<uintptr_t>&);
|
||||
|
||||
std::vector<sample_data> get_samples(uint32_t);
|
||||
|
||||
void add_sample(uint32_t, uintptr_t, uint64_t = 1);
|
||||
|
||||
void
|
||||
add_samples(uint32_t, const std::map<uintptr_t, uint64_t>&);
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,647 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/sampling.hpp"
|
||||
#include "binary/analysis.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/concepts.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/locking.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/causal/components/backtrace.hpp"
|
||||
#include "library/causal/data.hpp"
|
||||
#include "library/causal/sample_data.hpp"
|
||||
#include "library/perf.hpp"
|
||||
#include "library/ptl.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/sampling.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <timemory/macros.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/sampling/allocator.hpp>
|
||||
#include <timemory/sampling/overflow.hpp>
|
||||
#include <timemory/sampling/sampler.hpp>
|
||||
#include <timemory/sampling/timer.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/utility/backtrace.hpp>
|
||||
#include <timemory/variadic.hpp>
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace sampling
|
||||
{
|
||||
using ::tim::sampling::dynamic;
|
||||
using ::tim::sampling::overflow;
|
||||
using ::tim::sampling::timer;
|
||||
|
||||
using causal_bundle_t =
|
||||
tim::lightweight_tuple<causal::component::overflow, causal::component::backtrace>;
|
||||
using causal_sampler_t = tim::sampling::sampler<causal_bundle_t, dynamic>;
|
||||
using backtrace_enabled = trait::runtime_enabled<component::backtrace>;
|
||||
using overflow_enabled = trait::runtime_enabled<component::overflow>;
|
||||
} // namespace sampling
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(prevent_reentry, causal::sampling::causal_sampler_t,
|
||||
std::true_type)
|
||||
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(provide_backtrace, causal::sampling::causal_sampler_t,
|
||||
std::false_type)
|
||||
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(buffer_size, causal::sampling::causal_sampler_t,
|
||||
TIMEMORY_ESC(std::integral_constant<size_t, 4096>))
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace sampling
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using causal_sampler_allocator_t = typename causal_sampler_t::allocator_t;
|
||||
using causal_sampler_bundle_t = typename causal_sampler_t::bundle_type;
|
||||
using causal_sampler_buffer_t = tim::data_storage::ring_buffer<causal_sampler_bundle_t>;
|
||||
|
||||
struct causal_sampling
|
||||
{};
|
||||
|
||||
std::set<int>
|
||||
configure(bool _setup, int64_t _tid = threading::get_id());
|
||||
|
||||
std::shared_ptr<causal_sampler_allocator_t>&
|
||||
get_causal_sampler_allocator(bool _construct)
|
||||
{
|
||||
static auto _v = std::shared_ptr<causal_sampler_allocator_t>{};
|
||||
if(!_v && _construct) _v = std::make_shared<causal_sampler_allocator_t>();
|
||||
return _v;
|
||||
}
|
||||
|
||||
auto&
|
||||
get_causal_sampler_signals()
|
||||
{
|
||||
using thread_data_t = thread_data<identity<std::set<int>>, causal_sampling>;
|
||||
static auto& _v = thread_data_t::instance(construct_on_init{});
|
||||
return _v;
|
||||
}
|
||||
|
||||
auto&
|
||||
get_causal_sampler_running()
|
||||
{
|
||||
using thread_data_t = thread_data<identity<bool>, causal_sampling>;
|
||||
static auto& _v = thread_data_t::instance(construct_on_init{});
|
||||
return _v;
|
||||
}
|
||||
|
||||
auto&
|
||||
get_causal_samplers()
|
||||
{
|
||||
using thread_data_t =
|
||||
thread_data<identity<std::unique_ptr<causal_sampler_t>>, causal_sampling>;
|
||||
static auto& _v = thread_data_t::instance(construct_on_init{});
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::set<int>&
|
||||
get_causal_sampler_signals(int64_t _tid)
|
||||
{
|
||||
auto& _data = get_causal_sampler_signals();
|
||||
if(static_cast<size_t>(_tid) >= _data->size())
|
||||
_data->resize(_tid + 1, std::set<int>{});
|
||||
return _data->at(_tid);
|
||||
}
|
||||
|
||||
bool&
|
||||
get_causal_sampler_running(int64_t _tid)
|
||||
{
|
||||
auto& _data = get_causal_sampler_running();
|
||||
if(static_cast<size_t>(_tid) >= _data->size()) _data->resize(_tid + 1, false);
|
||||
return _data->at(_tid);
|
||||
}
|
||||
|
||||
auto&
|
||||
get_causal_sampler(int64_t _tid)
|
||||
{
|
||||
auto& _data = get_causal_samplers();
|
||||
if(static_cast<size_t>(_tid) >= _data->size()) _data->resize(_tid + 1);
|
||||
return _data->at(_tid);
|
||||
}
|
||||
|
||||
void
|
||||
causal_offload_buffer(int64_t, causal_sampler_buffer_t&& _buf)
|
||||
{
|
||||
auto _data = std::move(_buf);
|
||||
auto _processed = std::map<uint32_t, std::map<uintptr_t, uint64_t>>{};
|
||||
while(!_data.is_empty())
|
||||
{
|
||||
auto _bundle = causal_sampler_bundle_t{};
|
||||
_data.read(&_bundle);
|
||||
|
||||
const auto* _bt_causal = _bundle.get<causal::component::backtrace>();
|
||||
if(_bt_causal)
|
||||
{
|
||||
auto _stack = _bt_causal->get_stack();
|
||||
|
||||
for(auto itr : _stack)
|
||||
{
|
||||
if(itr > 0) _processed[_bt_causal->get_index()][itr] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const auto* _of_causal = _bundle.get<causal::component::overflow>();
|
||||
if(_of_causal)
|
||||
{
|
||||
const auto& _stack = _of_causal->get_stack();
|
||||
|
||||
for(const auto& ditr : _stack)
|
||||
{
|
||||
for(auto aitr : ditr)
|
||||
{
|
||||
if(aitr > 0) _processed[_of_causal->get_index()][aitr] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_data.destroy();
|
||||
|
||||
if(!_processed.empty())
|
||||
{
|
||||
static auto _mutex = locking::atomic_mutex{};
|
||||
auto _lk = locking::atomic_lock{ _mutex };
|
||||
for(const auto& itr : _processed)
|
||||
{
|
||||
add_samples(itr.first, itr.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::set<int>
|
||||
configure(bool _setup, int64_t _tid)
|
||||
{
|
||||
const auto& _info = thread_info::get(_tid, SequentTID);
|
||||
auto& _causal = get_causal_sampler(_tid);
|
||||
auto& _causal_perf = perf::get_instance(_tid);
|
||||
auto& _running = get_causal_sampler_running(_tid);
|
||||
auto& _signal_types = get_causal_sampler_signals(_tid);
|
||||
|
||||
ROCPROFSYS_CONDITIONAL_THROW(get_use_sampling(),
|
||||
"Internal error! configuring causal profiling not "
|
||||
"permitted when sampling is enabled");
|
||||
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
|
||||
if(_setup && _signal_types.empty()) _signal_types = get_sampling_signals(_tid);
|
||||
|
||||
// initialize
|
||||
if(_setup)
|
||||
{
|
||||
using global_init_mode = operation::mode_constant<operation::init_mode::global>;
|
||||
using thread_init_mode = operation::mode_constant<operation::init_mode::thread>;
|
||||
// initialize backtrace
|
||||
operation::init<component::backtrace>{}(global_init_mode{});
|
||||
operation::init<component::backtrace>{}(thread_init_mode{});
|
||||
// initialize overflow
|
||||
operation::init<component::overflow>{}(global_init_mode{});
|
||||
operation::init<component::overflow>{}(thread_init_mode{});
|
||||
}
|
||||
|
||||
if(_setup && !_causal && !_running && !_signal_types.empty())
|
||||
{
|
||||
auto _verbose = std::min<int>(get_verbose() - 2, 2);
|
||||
if(get_debug_sampling()) _verbose = 2;
|
||||
|
||||
// if this thread has an offset ID, that means it was created internally
|
||||
// and is probably here bc it called a function which was instrumented.
|
||||
// thus we should not start a sampler for it
|
||||
if(_tid > 0 && _info && _info->is_offset) return std::set<int>{};
|
||||
// if the thread state is disabled or completed, return
|
||||
if(_info && _info->index_data->sequent_value == _tid &&
|
||||
get_thread_state() == ThreadState::Disabled)
|
||||
return std::set<int>{};
|
||||
|
||||
(void) get_debug_sampling(); // make sure query in sampler does not allocate
|
||||
assert(_tid == threading::get_id());
|
||||
|
||||
auto _causal_alloc = get_causal_sampler_allocator(true);
|
||||
_causal = std::make_unique<causal_sampler_t>(_causal_alloc, "rocprofsys", _tid,
|
||||
_verbose);
|
||||
|
||||
auto _activate_perf_backend = [&_causal, &_causal_perf, &_info, &_tid]() {
|
||||
_causal_perf = std::make_unique<perf::perf_event>();
|
||||
auto _open_error =
|
||||
_causal_perf->open(1000.0, 10, _info->index_data->system_value);
|
||||
if(_open_error)
|
||||
{
|
||||
_causal_perf.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
overflow_enabled::set(true);
|
||||
overflow_enabled::set(scope::thread_scope{}, true);
|
||||
backtrace_enabled::set(false);
|
||||
backtrace_enabled::set(scope::thread_scope{}, false);
|
||||
_causal->configure(overflow{ get_sampling_overflow_signal(),
|
||||
[](int, pid_t, long, int64_t) {
|
||||
// perf::get_instance(_idx)->set_ready_signal(_sig);
|
||||
return true;
|
||||
},
|
||||
[](int, pid_t, long, int64_t _idx) {
|
||||
return perf::get_instance(_idx)->start();
|
||||
},
|
||||
[](int, pid_t, long, int64_t _idx) {
|
||||
return perf::get_instance(_idx)->stop();
|
||||
},
|
||||
_tid, threading::get_sys_tid() });
|
||||
if(_tid == 0) ROCPROFSYS_VERBOSE(1, "causal profiling backend: perf\n");
|
||||
}
|
||||
|
||||
return _open_error;
|
||||
};
|
||||
|
||||
auto _activate_timer_backend = [&_causal, &_tid]() {
|
||||
backtrace_enabled::set(true);
|
||||
backtrace_enabled::set(scope::thread_scope{}, true);
|
||||
overflow_enabled::set(false);
|
||||
overflow_enabled::set(scope::thread_scope{}, false);
|
||||
_causal->configure(timer{ get_sampling_realtime_signal(), CLOCK_REALTIME,
|
||||
SIGEV_THREAD_ID, 1000.0, 1.0e-6, _tid,
|
||||
threading::get_sys_tid() });
|
||||
if(_tid == 0) ROCPROFSYS_VERBOSE(1, "causal profiling backend: timer\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
TIMEMORY_REQUIRE(_causal) << "nullptr to causal profiling instance";
|
||||
|
||||
_causal->set_flags(SA_RESTART);
|
||||
_causal->set_verbose(_verbose);
|
||||
_causal->set_offload(&causal_offload_buffer);
|
||||
|
||||
if(get_causal_backend() == CausalBackend::Perf)
|
||||
{
|
||||
auto _perf_error = _activate_perf_backend();
|
||||
ROCPROFSYS_REQUIRE(!_perf_error)
|
||||
<< "perf backend for causal profiling failed to activate: "
|
||||
<< *_perf_error << "\n";
|
||||
}
|
||||
else if(get_causal_backend() == CausalBackend::Timer)
|
||||
{
|
||||
ROCPROFSYS_REQUIRE(_activate_timer_backend())
|
||||
<< "timer backend for causal profiling failed to activate\n";
|
||||
}
|
||||
else if(get_causal_backend() == CausalBackend::Auto)
|
||||
{
|
||||
auto _perf_error = _activate_perf_backend();
|
||||
if(!_perf_error)
|
||||
{
|
||||
config::set_setting_value("ROCPROFSYS_CAUSAL_BACKEND",
|
||||
std::string{ "perf" });
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_WARNING_F(
|
||||
0, "perf backend for causal profiling failed to activate: %s\n",
|
||||
_perf_error->c_str());
|
||||
|
||||
ROCPROFSYS_REQUIRE(_activate_timer_backend())
|
||||
<< "timer backend for causal profiling failed to activate\n";
|
||||
|
||||
config::set_setting_value("ROCPROFSYS_CAUSAL_BACKEND",
|
||||
std::string{ "timer" });
|
||||
}
|
||||
}
|
||||
|
||||
_causal->configure(timer{ get_sampling_cputime_signal(), CLOCK_THREAD_CPUTIME_ID,
|
||||
SIGEV_THREAD_ID, 1000.0, 1.0e-6, _tid,
|
||||
threading::get_sys_tid() });
|
||||
|
||||
_running = true;
|
||||
_causal->start();
|
||||
}
|
||||
else if(!_setup && _causal && _running)
|
||||
{
|
||||
ROCPROFSYS_DEBUG("Destroying causal sampler for thread %lu...\n", _tid);
|
||||
_running = false;
|
||||
|
||||
if(_tid == threading::get_id() && !_signal_types.empty())
|
||||
block_signals(_signal_types);
|
||||
|
||||
if(_tid == 0)
|
||||
{
|
||||
block_samples();
|
||||
|
||||
// this propagates to all threads
|
||||
_causal->ignore(_signal_types);
|
||||
|
||||
for(int64_t i = 1; i < ROCPROFSYS_MAX_THREADS; ++i)
|
||||
{
|
||||
if(get_causal_sampler(i))
|
||||
{
|
||||
get_causal_sampler(i)->stop();
|
||||
get_causal_sampler(i)->reset();
|
||||
}
|
||||
|
||||
if(perf::get_instance(i))
|
||||
{
|
||||
perf::get_instance(i).reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_causal->stop();
|
||||
_causal->reset();
|
||||
|
||||
if(_causal_perf)
|
||||
{
|
||||
_causal_perf.reset();
|
||||
}
|
||||
|
||||
ROCPROFSYS_DEBUG("Causal sampler destroyed for thread %lu\n", _tid);
|
||||
}
|
||||
|
||||
return _signal_types;
|
||||
}
|
||||
|
||||
void
|
||||
post_process_causal(int64_t _tid, const std::vector<causal_bundle_t>& _data);
|
||||
} // namespace
|
||||
|
||||
std::set<int>
|
||||
get_signal_types(int64_t _tid)
|
||||
{
|
||||
return (get_causal_sampler_signals()) ? get_causal_sampler_signals(_tid)
|
||||
: std::set<int>{};
|
||||
}
|
||||
|
||||
std::set<int>
|
||||
setup()
|
||||
{
|
||||
if(!get_use_causal()) return std::set<int>{};
|
||||
return configure(true);
|
||||
}
|
||||
|
||||
std::set<int>
|
||||
shutdown()
|
||||
{
|
||||
auto _v = configure(false);
|
||||
return _v;
|
||||
}
|
||||
|
||||
void
|
||||
block_samples()
|
||||
{
|
||||
trait::runtime_enabled<causal_sampler_t>::set(false);
|
||||
trait::runtime_enabled<causal::component::backtrace>::set(false);
|
||||
}
|
||||
|
||||
void
|
||||
unblock_samples()
|
||||
{
|
||||
trait::runtime_enabled<causal::component::backtrace>::set(true);
|
||||
trait::runtime_enabled<causal_sampler_t>::set(true);
|
||||
}
|
||||
|
||||
void
|
||||
block_backtrace_samples()
|
||||
{
|
||||
pause(scope::thread_scope{});
|
||||
}
|
||||
|
||||
void
|
||||
unblock_backtrace_samples()
|
||||
{
|
||||
resume(scope::thread_scope{});
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::optional<bool> _process_paused = {};
|
||||
thread_local std::optional<bool> _thread_paused = {};
|
||||
namespace signals = ::tim::signals;
|
||||
|
||||
const auto&
|
||||
sampling_signals()
|
||||
{
|
||||
static thread_local auto _v = get_signal_types(threading::get_id());
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename ScopeT>
|
||||
void pause(ScopeT)
|
||||
{
|
||||
static_assert(
|
||||
tim::is_one_of<ScopeT,
|
||||
type_list<scope::thread_scope, scope::process_scope>>::value,
|
||||
"Unsupported scope");
|
||||
|
||||
if constexpr(std::is_same<ScopeT, scope::thread_scope>::value)
|
||||
{
|
||||
if(!_thread_paused) _thread_paused = false;
|
||||
|
||||
bool _paused_v = *_thread_paused;
|
||||
if(!_paused_v)
|
||||
{
|
||||
auto& _causal_perf = perf::get_instance(threading::get_id());
|
||||
if(_causal_perf) _causal_perf->stop();
|
||||
signals::block_signals(sampling_signals(), signals::sigmask_scope::thread);
|
||||
_thread_paused = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!_process_paused) _process_paused = false;
|
||||
|
||||
bool _paused_v = *_process_paused;
|
||||
if(!_paused_v)
|
||||
{
|
||||
for(auto i = 0; i < ROCPROFSYS_MAX_THREADS; ++i)
|
||||
{
|
||||
auto& _causal_perf = perf::get_instance(i);
|
||||
if(_causal_perf) _causal_perf->stop();
|
||||
}
|
||||
signals::block_signals(sampling_signals(), signals::sigmask_scope::process);
|
||||
_process_paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ScopeT>
|
||||
void resume(ScopeT)
|
||||
{
|
||||
static_assert(
|
||||
tim::is_one_of<ScopeT,
|
||||
type_list<scope::thread_scope, scope::process_scope>>::value,
|
||||
"Unsupported scope");
|
||||
|
||||
if constexpr(std::is_same<ScopeT, scope::thread_scope>::value)
|
||||
{
|
||||
if(!_thread_paused) _thread_paused = true;
|
||||
|
||||
bool _paused_v = *_thread_paused;
|
||||
if(_paused_v)
|
||||
{
|
||||
auto& _causal_perf = perf::get_instance(threading::get_id());
|
||||
if(_causal_perf) _causal_perf->start();
|
||||
signals::unblock_signals(sampling_signals(), signals::sigmask_scope::thread);
|
||||
_thread_paused = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!_process_paused) _process_paused = true;
|
||||
|
||||
bool _paused_v = *_process_paused;
|
||||
if(_paused_v)
|
||||
{
|
||||
for(auto i = 0; i < ROCPROFSYS_MAX_THREADS; ++i)
|
||||
{
|
||||
auto& _causal_perf = perf::get_instance(i);
|
||||
if(_causal_perf) _causal_perf->start();
|
||||
}
|
||||
signals::unblock_signals(sampling_signals(), signals::sigmask_scope::process);
|
||||
_process_paused = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void pause<scope::thread_scope>(scope::thread_scope);
|
||||
template void pause<scope::process_scope>(scope::process_scope);
|
||||
|
||||
template void resume<scope::thread_scope>(scope::thread_scope);
|
||||
template void resume<scope::process_scope>(scope::process_scope);
|
||||
|
||||
void
|
||||
block_signals(std::set<int> _signals)
|
||||
{
|
||||
if(_signals.empty()) _signals = get_signal_types(threading::get_id());
|
||||
if(_signals.empty()) return;
|
||||
|
||||
::rocprofsys::sampling::block_signals(_signals);
|
||||
}
|
||||
|
||||
void
|
||||
unblock_signals(std::set<int> _signals)
|
||||
{
|
||||
if(_signals.empty()) _signals = get_signal_types(threading::get_id());
|
||||
if(_signals.empty()) return;
|
||||
|
||||
::rocprofsys::sampling::unblock_signals(_signals);
|
||||
}
|
||||
|
||||
void
|
||||
post_process()
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
ROCPROFSYS_VERBOSE(2 || get_debug_sampling(),
|
||||
"Stopping causal sampling components...\n");
|
||||
|
||||
block_samples();
|
||||
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
{
|
||||
auto& _causal = get_causal_sampler(i);
|
||||
if(_causal) _causal->stop();
|
||||
auto& _causal_perf = perf::get_instance(i);
|
||||
if(_causal_perf) _causal_perf->stop();
|
||||
}
|
||||
|
||||
configure(false, 0);
|
||||
|
||||
auto _allocator = get_causal_sampler_allocator(false);
|
||||
if(_allocator) _allocator->flush();
|
||||
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
{
|
||||
auto& _causal = get_causal_sampler(i);
|
||||
auto _causal_data =
|
||||
(_causal) ? _causal->get_data() : std::vector<sampling::causal_bundle_t>{};
|
||||
|
||||
if(!_causal_data.empty()) post_process_causal(i, _causal_data);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
{
|
||||
get_causal_sampler(i).reset();
|
||||
|
||||
auto& _causal_perf = perf::get_instance(i);
|
||||
if(_causal_perf)
|
||||
{
|
||||
_causal_perf.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if(_allocator) _allocator.reset();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void
|
||||
post_process_causal(int64_t, const std::vector<causal_bundle_t>& _data)
|
||||
{
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
const auto* _bt_causal = itr.get<causal::component::backtrace>();
|
||||
if(_bt_causal)
|
||||
{
|
||||
auto _stack = _bt_causal->get_stack();
|
||||
for(auto&& ditr : _stack)
|
||||
{
|
||||
if(ditr > 0) add_sample(_bt_causal->get_index(), ditr);
|
||||
}
|
||||
}
|
||||
|
||||
const auto* _of_causal = itr.get<causal::component::overflow>();
|
||||
if(_of_causal)
|
||||
{
|
||||
const auto& _stack = _of_causal->get_stack();
|
||||
|
||||
for(const auto& ditr : _stack)
|
||||
{
|
||||
for(auto aitr : ditr)
|
||||
{
|
||||
if(aitr > 0) add_sample(_of_causal->get_index(), aitr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // namespace sampling
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,74 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/concepts.hpp"
|
||||
#include "core/defines.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
namespace sampling
|
||||
{
|
||||
std::set<int>
|
||||
get_signal_types(int64_t _tid);
|
||||
|
||||
void
|
||||
block_samples();
|
||||
|
||||
void
|
||||
unblock_samples();
|
||||
|
||||
void
|
||||
block_backtrace_samples();
|
||||
|
||||
void
|
||||
unblock_backtrace_samples();
|
||||
|
||||
template <typename Tp = tim::scope::thread_scope>
|
||||
void pause(Tp = {});
|
||||
|
||||
template <typename Tp = tim::scope::thread_scope>
|
||||
void resume(Tp = {});
|
||||
|
||||
void block_signals(std::set<int> = {});
|
||||
|
||||
void unblock_signals(std::set<int> = {});
|
||||
|
||||
std::set<int>
|
||||
setup();
|
||||
|
||||
std::set<int>
|
||||
shutdown();
|
||||
|
||||
void
|
||||
post_process();
|
||||
} // namespace sampling
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,52 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/selected_entry.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
template <typename ArchiveT>
|
||||
void
|
||||
selected_entry::serialize(ArchiveT& ar, const unsigned int)
|
||||
{
|
||||
using ::tim::cereal::make_nvp;
|
||||
ar(make_nvp("address", address), make_nvp("symbol_address", symbol_address),
|
||||
make_nvp("info", symbol));
|
||||
}
|
||||
|
||||
template void
|
||||
selected_entry::serialize<cereal::JSONInputArchive>(cereal::JSONInputArchive&,
|
||||
const unsigned int);
|
||||
|
||||
template void
|
||||
selected_entry::serialize<cereal::MinimalJSONOutputArchive>(
|
||||
cereal::MinimalJSONOutputArchive&, const unsigned int);
|
||||
|
||||
template void
|
||||
selected_entry::serialize<cereal::PrettyJSONOutputArchive>(
|
||||
cereal::PrettyJSONOutputArchive&, const unsigned int);
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,70 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "binary/dwarf_entry.hpp"
|
||||
#include "binary/symbol.hpp"
|
||||
#include "core/binary/fwd.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "library/causal/fwd.hpp"
|
||||
|
||||
#include <timemory/hash/types.hpp>
|
||||
#include <timemory/unwind/dlinfo.hpp>
|
||||
#include <timemory/unwind/stack.hpp>
|
||||
#include <timemory/utility/macros.hpp>
|
||||
#include <timemory/utility/procfs/maps.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <dlfcn.h>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace causal
|
||||
{
|
||||
struct selected_entry
|
||||
{
|
||||
ROCPROFSYS_DEFAULT_OBJECT(selected_entry)
|
||||
|
||||
uintptr_t address = 0x0;
|
||||
uintptr_t symbol_address = 0x0;
|
||||
binary::symbol symbol = {};
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT&, const unsigned int);
|
||||
|
||||
bool contains(uintptr_t) const;
|
||||
explicit operator bool() const { return (address > 0 && symbol.address); }
|
||||
};
|
||||
|
||||
inline bool
|
||||
selected_entry::contains(uintptr_t _v) const
|
||||
{
|
||||
return (_v == address || (symbol_address > 0 && _v == symbol_address) ||
|
||||
symbol.ipaddr().contains(_v));
|
||||
}
|
||||
} // namespace causal
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,53 @@
|
||||
#
|
||||
set(component_sources
|
||||
${CMAKE_CURRENT_LIST_DIR}/backtrace.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/backtrace_metrics.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/backtrace_timestamp.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/callchain.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/comm_data.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/cpu_freq.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/exit_gotcha.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/fork_gotcha.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mpi_gotcha.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/numa_gotcha.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pthread_gotcha.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pthread_create_gotcha.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pthread_mutex_gotcha.cpp)
|
||||
|
||||
set(component_headers
|
||||
${CMAKE_CURRENT_LIST_DIR}/backtrace.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/backtrace_metrics.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/backtrace_timestamp.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/callchain.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/category_region.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/comm_data.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/cpu_freq.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ensure_storage.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/exit_gotcha.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/fork_gotcha.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mpi_gotcha.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/numa_gotcha.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rcclp.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprofiler.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/roctracer.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pthread_gotcha.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pthread_create_gotcha.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pthread_mutex_gotcha.hpp)
|
||||
|
||||
target_sources(rocprofiler-systems-object-library PRIVATE ${component_sources}
|
||||
${component_headers})
|
||||
|
||||
if(ROCPROFSYS_USE_ROCPROFILER)
|
||||
target_sources(rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/rocprofiler.cpp)
|
||||
endif()
|
||||
|
||||
if(ROCPROFSYS_USE_ROCTRACER)
|
||||
target_sources(rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/roctracer.cpp)
|
||||
endif()
|
||||
|
||||
if(ROCPROFSYS_USE_RCCL)
|
||||
target_sources(rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/rcclp.cpp)
|
||||
endif()
|
||||
@@ -0,0 +1,209 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/components/ensure_storage.hpp"
|
||||
#include "library/ptl.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/sampling.hpp"
|
||||
|
||||
#include <timemory/backends/papi.hpp>
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/components/data_tracker/components.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/components/papi/extern.hpp>
|
||||
#include <timemory/components/papi/papi_array.hpp>
|
||||
#include <timemory/components/papi/papi_vector.hpp>
|
||||
#include <timemory/components/rusage/components.hpp>
|
||||
#include <timemory/components/rusage/types.hpp>
|
||||
#include <timemory/components/timing/backends.hpp>
|
||||
#include <timemory/components/trip_count/extern.hpp>
|
||||
#include <timemory/macros.hpp>
|
||||
#include <timemory/math.hpp>
|
||||
#include <timemory/mpl.hpp>
|
||||
#include <timemory/mpl/quirks.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/operations.hpp>
|
||||
#include <timemory/storage.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/utility/backtrace.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
#include <timemory/variadic.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <initializer_list>
|
||||
#include <mutex>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
std::vector<backtrace::entry_type>
|
||||
backtrace::get() const
|
||||
{
|
||||
std::vector<entry_type> _v = {};
|
||||
if(size() == 0) return _v;
|
||||
|
||||
{
|
||||
static auto _cache = cache_type{ get_sampling_include_inlines() };
|
||||
auto_lock_t _lk{ type_mutex<backtrace>() };
|
||||
_v = m_data.get(&_cache, false);
|
||||
}
|
||||
|
||||
// put the bottom of the call-stack on top
|
||||
std::reverse(_v.begin(), _v.end());
|
||||
//
|
||||
auto _known_excludes =
|
||||
std::set<std::string>{ "funlockfile", "killpg", "__restore_rt" };
|
||||
// remove some known functions which are by-products of interrupts
|
||||
while(!_v.empty() && _known_excludes.find(_v.back().name) != _known_excludes.end())
|
||||
_v.pop_back();
|
||||
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::string
|
||||
backtrace::label()
|
||||
{
|
||||
return "backtrace";
|
||||
}
|
||||
|
||||
std::string
|
||||
backtrace::description()
|
||||
{
|
||||
return "Records backtrace data";
|
||||
}
|
||||
|
||||
std::vector<backtrace::entry_type>
|
||||
backtrace::filter_and_patch(const std::vector<entry_type>& _data)
|
||||
{
|
||||
// check whether the call-stack entry should be used. -1 means break, 0 means continue
|
||||
auto _use_label = [](std::string_view _lbl) -> short {
|
||||
// debugging feature
|
||||
bool _keep_internal = get_sampling_keep_internal();
|
||||
const auto _npos = std::string::npos;
|
||||
if(_keep_internal) return 1;
|
||||
if(_lbl.find("rocprofsys_main") != _npos) return 0;
|
||||
if(_lbl.find("rocprofsys::") != _npos) return 0;
|
||||
if(_lbl.find("tim::openmp::") != _npos) return -1;
|
||||
if(_lbl.find("tim::") != _npos) return 0;
|
||||
if(_lbl.find("DYNINST_") != _npos) return 0;
|
||||
if(_lbl.find("rocprofsys_") != _npos) return -1;
|
||||
if(_lbl.find("rocprofiler_") != _npos) return -1;
|
||||
if(_lbl.find("roctracer_") != _npos) return -1;
|
||||
if(_lbl.find("perfetto::") != _npos) return -1;
|
||||
if(_lbl.find("protozero::") == 0) return -1;
|
||||
if(_lbl.find("gotcha_") != _npos) return -1;
|
||||
return 1;
|
||||
};
|
||||
|
||||
static bool _keep_suffix = tim::get_env<bool>(
|
||||
"ROCPROFSYS_SAMPLING_KEEP_DYNINST_SUFFIX", get_debug_sampling());
|
||||
|
||||
// in the dyninst binary rewrite runtime, instrumented functions are appended with
|
||||
// "_dyninst", i.e. "main" will show up as "main_dyninst" in the backtrace.
|
||||
auto _patch_label = [](std::string_view _lbl) -> std::string {
|
||||
// debugging feature
|
||||
if(_keep_suffix) return std::string{ _lbl };
|
||||
const std::string _dyninst{ "_dyninst" };
|
||||
auto _pos = _lbl.find(_dyninst);
|
||||
if(_pos == std::string::npos) return std::string{ _lbl };
|
||||
return std::string{ _lbl }.replace(_pos, _dyninst.length(), "");
|
||||
};
|
||||
|
||||
auto _ret = std::vector<entry_type>{};
|
||||
_ret.reserve(_data.size());
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
auto _name = tim::demangle(_patch_label(itr.name));
|
||||
auto _use = _use_label(_name);
|
||||
if(_use == -1) break;
|
||||
if(_use == 0) continue;
|
||||
auto _v = itr;
|
||||
_v.name = _name;
|
||||
_ret.emplace_back(_v);
|
||||
}
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
void
|
||||
backtrace::start()
|
||||
{}
|
||||
|
||||
void
|
||||
backtrace::stop()
|
||||
{}
|
||||
|
||||
bool
|
||||
backtrace::empty() const
|
||||
{
|
||||
return (size() == 0);
|
||||
}
|
||||
|
||||
size_t
|
||||
backtrace::size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
void
|
||||
backtrace::sample(int signo)
|
||||
{
|
||||
if(signo == get_sampling_overflow_signal()) return;
|
||||
|
||||
// on RedHat, the unw_step within get_unw_stack involves a mutex lock
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
using namespace tim::backtrace;
|
||||
constexpr bool with_signal_frame = false;
|
||||
constexpr size_t ignore_depth = 3;
|
||||
// ignore depth based on:
|
||||
// 1. this frame
|
||||
// 2. tim::sampling::sampler<...>::sample(...) [always inline]
|
||||
// 3. tim::sampling::sampler<...>::execute(...)
|
||||
// 4a. funlockfile [common but not explicitly in call-stack]
|
||||
// 4b. __resume_rt [common but not explicitly in call-stack]
|
||||
// 4c. killpg [common but not explicitly in call-stack]
|
||||
m_data = get_unw_stack<stack_depth, ignore_depth, with_signal_frame>();
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_INITIALIZE_STORAGE(rocprofsys::component::backtrace)
|
||||
@@ -0,0 +1,86 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/components/base/declaration.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/unwind/cache.hpp>
|
||||
#include <timemory/unwind/processed_entry.hpp>
|
||||
#include <timemory/unwind/stack.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct backtrace : comp::empty_base
|
||||
{
|
||||
static constexpr size_t stack_depth = ROCPROFSYS_MAX_UNWIND_DEPTH;
|
||||
|
||||
using data_t = tim::unwind::stack<stack_depth>;
|
||||
using cache_type = typename data_t::cache_type;
|
||||
using entry_type = tim::unwind::processed_entry;
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
using value_type = void;
|
||||
using system_clock = std::chrono::system_clock;
|
||||
using system_time_point = typename system_clock::time_point;
|
||||
|
||||
static std::string label();
|
||||
static std::string description();
|
||||
|
||||
backtrace() = default;
|
||||
~backtrace() = default;
|
||||
backtrace(const backtrace&) = default;
|
||||
backtrace(backtrace&&) noexcept = default;
|
||||
|
||||
backtrace& operator=(const backtrace&) = default;
|
||||
backtrace& operator=(backtrace&&) noexcept = default;
|
||||
|
||||
static std::vector<entry_type> filter_and_patch(const std::vector<entry_type>&);
|
||||
|
||||
static void start();
|
||||
static void stop();
|
||||
|
||||
void sample(int = -1);
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
std::vector<entry_type> get() const;
|
||||
data_t get_data() const { return m_data; }
|
||||
|
||||
private:
|
||||
data_t m_data = {};
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/backtrace_metrics.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "library/components/ensure_storage.hpp"
|
||||
#include "library/ptl.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
#include "library/tracing.hpp"
|
||||
|
||||
#include <timemory/backends/papi.hpp>
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/components/data_tracker/components.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/components/papi/extern.hpp>
|
||||
#include <timemory/components/papi/papi_array.hpp>
|
||||
#include <timemory/components/papi/papi_vector.hpp>
|
||||
#include <timemory/components/rusage/components.hpp>
|
||||
#include <timemory/components/rusage/types.hpp>
|
||||
#include <timemory/components/timing/backends.hpp>
|
||||
#include <timemory/components/trip_count/extern.hpp>
|
||||
#include <timemory/macros.hpp>
|
||||
#include <timemory/math.hpp>
|
||||
#include <timemory/mpl.hpp>
|
||||
#include <timemory/mpl/quirks.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/operations.hpp>
|
||||
#include <timemory/storage.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/utility/backtrace.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
#include <timemory/variadic.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <initializer_list>
|
||||
#include <mutex>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
namespace tracing
|
||||
{
|
||||
using namespace ::rocprofsys::tracing;
|
||||
}
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
using hw_counters = typename backtrace_metrics::hw_counters;
|
||||
using signal_type_instances = thread_data<std::set<int>, category::sampling>;
|
||||
using backtrace_metrics_init_instances =
|
||||
thread_data<backtrace_metrics, category::sampling>;
|
||||
using sampler_running_instances = thread_data<bool, category::sampling>;
|
||||
using papi_vector_instances = thread_data<hw_counters, category::sampling>;
|
||||
using papi_label_instances = thread_data<std::vector<std::string>, category::sampling>;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct perfetto_rusage
|
||||
{};
|
||||
|
||||
unique_ptr_t<std::vector<std::string>>&
|
||||
get_papi_labels(int64_t _tid)
|
||||
{
|
||||
return papi_label_instances::instance(construct_on_thread{ _tid });
|
||||
}
|
||||
|
||||
unique_ptr_t<hw_counters>&
|
||||
get_papi_vector(int64_t _tid)
|
||||
{
|
||||
return papi_vector_instances::instance(construct_on_thread{ _tid });
|
||||
}
|
||||
|
||||
unique_ptr_t<backtrace_metrics>&
|
||||
get_backtrace_metrics_init(int64_t _tid)
|
||||
{
|
||||
return backtrace_metrics_init_instances::instance(construct_on_thread{ _tid });
|
||||
}
|
||||
|
||||
unique_ptr_t<bool>&
|
||||
get_sampler_running(int64_t _tid)
|
||||
{
|
||||
return sampler_running_instances::instance(construct_on_thread{ _tid }, false);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string
|
||||
backtrace_metrics::label()
|
||||
{
|
||||
return "backtrace_metrics";
|
||||
}
|
||||
|
||||
std::string
|
||||
backtrace_metrics::description()
|
||||
{
|
||||
return "Records sampling data";
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
backtrace_metrics::get_hw_counter_labels(int64_t _tid)
|
||||
{
|
||||
auto& _v = get_papi_labels(_tid);
|
||||
return (_v) ? *_v : std::vector<std::string>{};
|
||||
}
|
||||
|
||||
void
|
||||
backtrace_metrics::start()
|
||||
{}
|
||||
|
||||
void
|
||||
backtrace_metrics::stop()
|
||||
{}
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename... Tp>
|
||||
auto get_enabled(tim::type_list<Tp...>)
|
||||
{
|
||||
constexpr size_t N = sizeof...(Tp);
|
||||
auto _v = std::bitset<N>{};
|
||||
size_t _n = 0;
|
||||
(_v.set(_n++, trait::runtime_enabled<Tp>::get()), ...);
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
void
|
||||
backtrace_metrics::sample(int)
|
||||
{
|
||||
if(!get_enabled(type_list<category::process_sampling, backtrace_metrics>{}).all())
|
||||
{
|
||||
m_valid.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_valid = get_enabled(categories_t{});
|
||||
|
||||
// return if everything is disabled
|
||||
if(!m_valid.any()) return;
|
||||
|
||||
auto _cache = tim::rusage_cache{ RUSAGE_THREAD };
|
||||
m_cpu = tim::get_clock_thread_now<int64_t, std::nano>();
|
||||
m_mem_peak = _cache.get_peak_rss();
|
||||
m_ctx_swch = _cache.get_num_priority_context_switch() +
|
||||
_cache.get_num_voluntary_context_switch();
|
||||
m_page_flt = _cache.get_num_major_page_faults() + _cache.get_num_minor_page_faults();
|
||||
|
||||
if constexpr(tim::trait::is_available<hw_counters>::value)
|
||||
{
|
||||
constexpr auto hw_counters_idx = tim::index_of<hw_counters, categories_t>::value;
|
||||
constexpr auto hw_category_idx =
|
||||
tim::index_of<category::thread_hardware_counter, categories_t>::value;
|
||||
|
||||
auto _tid = threading::get_id();
|
||||
if(m_valid.test(hw_category_idx) && m_valid.test(hw_counters_idx))
|
||||
{
|
||||
assert(get_papi_vector(_tid).get() != nullptr);
|
||||
m_hw_counter = get_papi_vector(_tid)->record();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
backtrace_metrics::configure(bool _setup, int64_t _tid)
|
||||
{
|
||||
auto& _running = get_sampler_running(_tid);
|
||||
bool _is_running = (!_running) ? false : *_running;
|
||||
|
||||
ensure_storage<comp::trip_count, sampling_wall_clock, sampling_cpu_clock, hw_counters,
|
||||
sampling_percent>{}();
|
||||
|
||||
if(_setup && !_is_running)
|
||||
{
|
||||
(void) get_debug_sampling(); // make sure query in sampler does not allocate
|
||||
assert(_tid == threading::get_id());
|
||||
|
||||
if constexpr(tim::trait::is_available<hw_counters>::value)
|
||||
{
|
||||
perfetto_counter_track<hw_counters>::init();
|
||||
ROCPROFSYS_DEBUG("HW COUNTER: starting...\n");
|
||||
if(get_papi_vector(_tid))
|
||||
{
|
||||
get_papi_vector(_tid)->start();
|
||||
*get_papi_labels(_tid) = get_papi_vector(_tid)->get_config()->labels;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!_setup && _is_running)
|
||||
{
|
||||
ROCPROFSYS_DEBUG("Destroying sampler for thread %lu...\n", _tid);
|
||||
*_running = false;
|
||||
|
||||
if constexpr(tim::trait::is_available<hw_counters>::value)
|
||||
{
|
||||
if(_tid == threading::get_id())
|
||||
{
|
||||
if(get_papi_vector(_tid)) get_papi_vector(_tid)->stop();
|
||||
ROCPROFSYS_DEBUG("HW COUNTER: stopped...\n");
|
||||
}
|
||||
}
|
||||
ROCPROFSYS_DEBUG("Sampler destroyed for thread %lu\n", _tid);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
backtrace_metrics::init_perfetto(int64_t _tid, valid_array_t _valid)
|
||||
{
|
||||
auto _hw_cnt_labels = *get_papi_labels(_tid);
|
||||
auto _tid_name = JOIN("", '[', _tid, ']');
|
||||
|
||||
if(!perfetto_counter_track<perfetto_rusage>::exists(_tid))
|
||||
{
|
||||
if(get_valid(category::thread_cpu_time{}, _valid))
|
||||
perfetto_counter_track<perfetto_rusage>::emplace(
|
||||
_tid, JOIN(' ', "Thread CPU time", _tid_name, "(S)"), "sec");
|
||||
if(get_valid(category::thread_peak_memory{}, _valid))
|
||||
perfetto_counter_track<perfetto_rusage>::emplace(
|
||||
_tid, JOIN(' ', "Thread Peak Memory Usage", _tid_name, "(S)"), "MB");
|
||||
if(get_valid(category::thread_context_switch{}, _valid))
|
||||
perfetto_counter_track<perfetto_rusage>::emplace(
|
||||
_tid, JOIN(' ', "Thread Context Switches", _tid_name, "(S)"));
|
||||
if(get_valid(category::thread_page_fault{}, _valid))
|
||||
perfetto_counter_track<perfetto_rusage>::emplace(
|
||||
_tid, JOIN(' ', "Thread Page Faults", _tid_name, "(S)"));
|
||||
}
|
||||
|
||||
if(!perfetto_counter_track<hw_counters>::exists(_tid) &&
|
||||
get_valid(type_list<hw_counters>{}, _valid) &&
|
||||
get_valid(category::thread_hardware_counter{}, _valid))
|
||||
{
|
||||
for(auto& itr : _hw_cnt_labels)
|
||||
{
|
||||
std::string _desc = tim::papi::get_event_info(itr).short_descr;
|
||||
if(_desc.empty()) _desc = itr;
|
||||
ROCPROFSYS_CI_THROW(_desc.empty(), "Empty description for %s\n", itr.c_str());
|
||||
perfetto_counter_track<hw_counters>::emplace(
|
||||
_tid, JOIN(' ', "Thread", _desc, _tid_name, "(S)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
backtrace_metrics::fini_perfetto(int64_t _tid, valid_array_t _valid)
|
||||
{
|
||||
auto _hw_cnt_labels = *get_papi_labels(_tid);
|
||||
const auto& _thread_info = thread_info::get(_tid, SequentTID);
|
||||
|
||||
ROCPROFSYS_CI_THROW(!_thread_info, "Error! missing thread info for tid=%li\n", _tid);
|
||||
if(!_thread_info) return;
|
||||
|
||||
uint64_t _ts = _thread_info->get_stop();
|
||||
uint64_t _rusage_idx = 0;
|
||||
|
||||
if(get_valid(category::thread_cpu_time{}, _valid))
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_cpu_time>::value,
|
||||
perfetto_counter_track<perfetto_rusage>::at(_tid, _rusage_idx++),
|
||||
_ts, 0);
|
||||
}
|
||||
|
||||
if(get_valid(category::thread_peak_memory{}, _valid))
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_peak_memory>::value,
|
||||
perfetto_counter_track<perfetto_rusage>::at(_tid, _rusage_idx++),
|
||||
_ts, 0);
|
||||
}
|
||||
|
||||
if(get_valid(category::thread_context_switch{}, _valid))
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_context_switch>::value,
|
||||
perfetto_counter_track<perfetto_rusage>::at(_tid, _rusage_idx++),
|
||||
_ts, 0);
|
||||
}
|
||||
|
||||
if(get_valid(category::thread_page_fault{}, _valid))
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_page_fault>::value,
|
||||
perfetto_counter_track<perfetto_rusage>::at(_tid, _rusage_idx++),
|
||||
_ts, 0);
|
||||
}
|
||||
|
||||
if(get_valid(type_list<hw_counters>{}, _valid) &&
|
||||
get_valid(category::thread_hardware_counter{}, _valid))
|
||||
{
|
||||
for(size_t i = 0; i < perfetto_counter_track<hw_counters>::size(_tid); ++i)
|
||||
{
|
||||
if(i < _hw_cnt_labels.size())
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_hardware_counter>::value,
|
||||
perfetto_counter_track<hw_counters>::at(_tid, i), _ts, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backtrace_metrics&
|
||||
backtrace_metrics::operator-=(const backtrace_metrics& _rhs)
|
||||
{
|
||||
auto& _lhs = *this;
|
||||
|
||||
if(_lhs(category::thread_cpu_time{}))
|
||||
{
|
||||
_lhs.m_cpu -= _rhs.m_cpu;
|
||||
}
|
||||
|
||||
if(_lhs(category::thread_peak_memory{}))
|
||||
{
|
||||
_lhs.m_mem_peak -= _rhs.m_mem_peak;
|
||||
}
|
||||
|
||||
if(_lhs(category::thread_context_switch{}))
|
||||
{
|
||||
_lhs.m_ctx_swch -= _rhs.m_ctx_swch;
|
||||
}
|
||||
|
||||
if(_lhs(category::thread_page_fault{}))
|
||||
{
|
||||
_lhs.m_page_flt -= _rhs.m_page_flt;
|
||||
}
|
||||
|
||||
if(_lhs(type_list<hw_counters>{}) && _lhs(category::thread_hardware_counter{}))
|
||||
{
|
||||
for(size_t i = 0; i < _lhs.m_hw_counter.size(); ++i)
|
||||
_lhs.m_hw_counter.at(i) -= _rhs.m_hw_counter.at(i);
|
||||
}
|
||||
|
||||
return _lhs;
|
||||
}
|
||||
|
||||
void
|
||||
backtrace_metrics::post_process_perfetto(int64_t _tid, uint64_t _ts) const
|
||||
{
|
||||
uint64_t _rusage_idx = 0;
|
||||
|
||||
if((*this)(category::thread_cpu_time{}))
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_cpu_time>::value,
|
||||
perfetto_counter_track<perfetto_rusage>::at(_tid, _rusage_idx++),
|
||||
_ts, m_cpu / units::sec);
|
||||
}
|
||||
|
||||
if((*this)(category::thread_peak_memory{}))
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_peak_memory>::value,
|
||||
perfetto_counter_track<perfetto_rusage>::at(_tid, _rusage_idx++),
|
||||
_ts, m_mem_peak / units::megabyte);
|
||||
}
|
||||
|
||||
if((*this)(category::thread_context_switch{}))
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_context_switch>::value,
|
||||
perfetto_counter_track<perfetto_rusage>::at(_tid, _rusage_idx++),
|
||||
_ts, m_ctx_swch);
|
||||
}
|
||||
|
||||
if((*this)(category::thread_page_fault{}))
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_page_fault>::value,
|
||||
perfetto_counter_track<perfetto_rusage>::at(_tid, _rusage_idx++),
|
||||
_ts, m_page_flt);
|
||||
}
|
||||
|
||||
if((*this)(type_list<hw_counters>{}) && (*this)(category::thread_hardware_counter{}))
|
||||
{
|
||||
for(size_t i = 0; i < perfetto_counter_track<hw_counters>::size(_tid); ++i)
|
||||
{
|
||||
if(i < m_hw_counter.size())
|
||||
{
|
||||
TRACE_COUNTER(trait::name<category::thread_hardware_counter>::value,
|
||||
perfetto_counter_track<hw_counters>::at(_tid, i), _ts,
|
||||
m_hw_counter.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_wall_clock>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_cpu_clock>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_fraction>), true,
|
||||
double)
|
||||
|
||||
TIMEMORY_INITIALIZE_STORAGE(rocprofsys::component::backtrace_metrics)
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/components/backtrace.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/components/base.hpp>
|
||||
#include <timemory/components/papi/papi_array.hpp>
|
||||
#include <timemory/components/papi/types.hpp>
|
||||
#include <timemory/macros/language.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/utility/type_list.hpp>
|
||||
#include <timemory/variadic/types.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
template <typename... Tp>
|
||||
using type_list = ::tim::type_list<Tp...>;
|
||||
|
||||
namespace component
|
||||
{
|
||||
struct backtrace_metrics : comp::empty_base
|
||||
{
|
||||
static constexpr size_t num_hw_counters = TIMEMORY_PAPI_ARRAY_SIZE;
|
||||
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
using value_type = void;
|
||||
using hw_counters = tim::component::papi_array<num_hw_counters>;
|
||||
using hw_counter_data_t = typename hw_counters::value_type;
|
||||
using system_clock = std::chrono::system_clock;
|
||||
using system_time_point = typename system_clock::time_point;
|
||||
|
||||
using categories_t =
|
||||
type_list<category::thread_cpu_time, category::thread_peak_memory,
|
||||
category::thread_context_switch, category::thread_page_fault,
|
||||
category::thread_hardware_counter, hw_counters>;
|
||||
static constexpr size_t num_categories = std::tuple_size<categories_t>::value;
|
||||
using valid_array_t = std::bitset<num_categories>;
|
||||
|
||||
static std::string label();
|
||||
static std::string description();
|
||||
|
||||
backtrace_metrics() = default;
|
||||
~backtrace_metrics() = default;
|
||||
backtrace_metrics(const backtrace_metrics&) = default;
|
||||
backtrace_metrics(backtrace_metrics&&) noexcept = default;
|
||||
|
||||
backtrace_metrics& operator=(const backtrace_metrics&) = default;
|
||||
backtrace_metrics& operator=(backtrace_metrics&&) noexcept = default;
|
||||
|
||||
static void configure(bool, int64_t _tid = threading::get_id());
|
||||
static void init_perfetto(int64_t _tid, valid_array_t);
|
||||
static void fini_perfetto(int64_t _tid, valid_array_t);
|
||||
static std::vector<std::string> get_hw_counter_labels(int64_t);
|
||||
|
||||
template <typename Tp>
|
||||
static bool get_valid(Tp, valid_array_t);
|
||||
|
||||
template <typename Tp>
|
||||
static bool get_valid(type_list<Tp>, valid_array_t);
|
||||
|
||||
static void start();
|
||||
static void stop();
|
||||
void sample(int = -1);
|
||||
void post_process(int64_t _tid, const backtrace* _bt,
|
||||
const backtrace_metrics* _last) const;
|
||||
|
||||
explicit operator bool() const { return m_valid.any(); }
|
||||
|
||||
template <typename Tp>
|
||||
bool operator()(Tp) const;
|
||||
|
||||
template <typename Tp>
|
||||
bool operator()(type_list<Tp>) const;
|
||||
|
||||
auto get_valid() const { return m_valid; }
|
||||
auto get_cpu_timestamp() const { return m_cpu; }
|
||||
auto get_peak_memory() const { return m_mem_peak; }
|
||||
auto get_context_switches() const { return m_ctx_swch; }
|
||||
auto get_page_faults() const { return m_page_flt; }
|
||||
const auto& get_hw_counters() const { return m_hw_counter; }
|
||||
|
||||
void post_process_perfetto(int64_t _tid, uint64_t _ts) const;
|
||||
|
||||
backtrace_metrics& operator-=(const backtrace_metrics&);
|
||||
|
||||
friend backtrace_metrics operator-(backtrace_metrics _lhs,
|
||||
const backtrace_metrics& _rhs)
|
||||
{
|
||||
return (_lhs -= _rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
valid_array_t m_valid = {};
|
||||
int64_t m_cpu = 0;
|
||||
int64_t m_mem_peak = 0;
|
||||
int64_t m_ctx_swch = 0;
|
||||
int64_t m_page_flt = 0;
|
||||
hw_counter_data_t m_hw_counter = {};
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
backtrace_metrics::get_valid(type_list<Tp>, valid_array_t _valid)
|
||||
{
|
||||
constexpr auto idx = tim::index_of<Tp, categories_t>::value;
|
||||
return _valid.test(idx);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
bool backtrace_metrics::operator()(type_list<Tp>) const
|
||||
{
|
||||
static_assert(!concepts::is_type_listing<Tp>::value,
|
||||
"Error! invalid call with tuple");
|
||||
|
||||
constexpr auto idx = tim::index_of<Tp, categories_t>::value;
|
||||
return m_valid.test(idx);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
backtrace_metrics::get_valid(Tp, valid_array_t _valid)
|
||||
{
|
||||
return get_valid(type_list<Tp>{}, _valid);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
bool backtrace_metrics::operator()(Tp) const
|
||||
{
|
||||
return (*this)(type_list<Tp>{});
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
#if !defined(ROCPROFSYS_EXTERN_COMPONENTS) || \
|
||||
(defined(ROCPROFSYS_EXTERN_COMPONENTS) && ROCPROFSYS_EXTERN_COMPONENTS > 0)
|
||||
|
||||
# include <timemory/operations.hpp>
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_wall_clock>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_cpu_clock>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_fraction>), true,
|
||||
double)
|
||||
|
||||
#endif
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/backtrace_timestamp.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <timemory/components/timing/backends.hpp>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
bool
|
||||
backtrace_timestamp::operator<(const backtrace_timestamp& rhs) const
|
||||
{
|
||||
return std::tie(m_tid, m_real) < std::tie(rhs.m_tid, rhs.m_real);
|
||||
}
|
||||
|
||||
bool
|
||||
backtrace_timestamp::is_valid() const
|
||||
{
|
||||
const auto& _info = thread_info::get(m_tid, SequentTID);
|
||||
return (_info) ? _info->is_valid_time(m_real) : false;
|
||||
}
|
||||
|
||||
void
|
||||
backtrace_timestamp::sample(int)
|
||||
{
|
||||
m_tid = tim::threading::get_id();
|
||||
m_real = tim::get_clock_real_now<uint64_t, std::nano>();
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_INITIALIZE_STORAGE(rocprofsys::component::backtrace_timestamp)
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <timemory/components/base.hpp>
|
||||
#include <timemory/macros/language.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct backtrace_timestamp : comp::empty_base
|
||||
{
|
||||
using value_type = void;
|
||||
|
||||
static std::string label() { return "backtrace_timestamp"; }
|
||||
static std::string description() { return "Timestamp for backtrace"; }
|
||||
|
||||
backtrace_timestamp() = default;
|
||||
~backtrace_timestamp() = default;
|
||||
backtrace_timestamp(const backtrace_timestamp&) = default;
|
||||
backtrace_timestamp(backtrace_timestamp&&) noexcept = default;
|
||||
|
||||
backtrace_timestamp& operator=(const backtrace_timestamp&) = default;
|
||||
backtrace_timestamp& operator=(backtrace_timestamp&&) noexcept = default;
|
||||
|
||||
bool operator<(const backtrace_timestamp& rhs) const;
|
||||
|
||||
static void start() {}
|
||||
static void stop() {}
|
||||
|
||||
void sample(int = -1);
|
||||
|
||||
auto get_tid() const { return m_tid; }
|
||||
auto get_timestamp() const { return m_real; }
|
||||
bool is_valid() const;
|
||||
|
||||
private:
|
||||
int64_t m_tid = 0;
|
||||
uint64_t m_real = 0;
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,219 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "callchain.hpp"
|
||||
#include "binary/analysis.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/components/ensure_storage.hpp"
|
||||
#include "library/perf.hpp"
|
||||
#include "library/ptl.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/sampling.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <timemory/backends/papi.hpp>
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/components/data_tracker/components.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/components/papi/extern.hpp>
|
||||
#include <timemory/components/papi/papi_array.hpp>
|
||||
#include <timemory/components/papi/papi_vector.hpp>
|
||||
#include <timemory/components/rusage/components.hpp>
|
||||
#include <timemory/components/rusage/types.hpp>
|
||||
#include <timemory/components/timing/backends.hpp>
|
||||
#include <timemory/components/trip_count/extern.hpp>
|
||||
#include <timemory/macros.hpp>
|
||||
#include <timemory/math.hpp>
|
||||
#include <timemory/mpl.hpp>
|
||||
#include <timemory/mpl/quirks.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/operations.hpp>
|
||||
#include <timemory/storage.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/unwind/entry.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
#include <timemory/variadic.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <initializer_list>
|
||||
#include <mutex>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
bool
|
||||
callchain::record::operator<(const record& rhs) const
|
||||
{
|
||||
return timestamp < rhs.timestamp;
|
||||
}
|
||||
|
||||
std::vector<callchain::ts_entry_vec_t>
|
||||
callchain::get() const
|
||||
{
|
||||
std::vector<ts_entry_vec_t> _v = {};
|
||||
if(size() == 0) return _v;
|
||||
|
||||
_v.reserve(size());
|
||||
auto _data = m_data;
|
||||
std::sort(_data.begin(), _data.end());
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
auto _v2 = ts_entry_vec_t{ itr.timestamp, {} };
|
||||
for(auto iitr : itr.data)
|
||||
{
|
||||
auto _entry = binary::lookup_ipaddr_entry<true>(iitr);
|
||||
if(_entry) _v2.second.emplace_back(*_entry);
|
||||
}
|
||||
|
||||
if(!_v2.second.empty())
|
||||
{
|
||||
// put the bottom of the call-stack on top
|
||||
std::reverse(_v2.second.begin(), _v2.second.end());
|
||||
_v.emplace_back(std::move(_v2));
|
||||
}
|
||||
}
|
||||
|
||||
auto _known_excludes =
|
||||
std::set<std::string>{ "funlockfile", "killpg", "__restore_rt" };
|
||||
// remove some known functions which are by-products of interrupts
|
||||
for(auto& itr : _v)
|
||||
{
|
||||
while(!itr.second.empty() &&
|
||||
_known_excludes.find(itr.second.back().name) != _known_excludes.end())
|
||||
itr.second.pop_back();
|
||||
}
|
||||
|
||||
std::sort(_v.begin(), _v.end(),
|
||||
[](const auto& _lhs, const auto& _rhs) { return _lhs.first < _rhs.first; });
|
||||
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::string
|
||||
callchain::label()
|
||||
{
|
||||
return "callchain";
|
||||
}
|
||||
|
||||
std::string
|
||||
callchain::description()
|
||||
{
|
||||
return "Records callchain data";
|
||||
}
|
||||
|
||||
std::vector<callchain::ts_entry_vec_t>
|
||||
callchain::filter_and_patch(const std::vector<ts_entry_vec_t>& _data)
|
||||
{
|
||||
auto _ret = std::vector<ts_entry_vec_t>{};
|
||||
_ret.reserve(_data.size());
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
auto _v = backtrace::filter_and_patch(itr.second);
|
||||
if(!_v.empty()) _ret.emplace_back(ts_entry_vec_t{ itr.first, std::move(_v) });
|
||||
}
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
void
|
||||
callchain::start()
|
||||
{}
|
||||
|
||||
void
|
||||
callchain::stop()
|
||||
{}
|
||||
|
||||
bool
|
||||
callchain::empty() const
|
||||
{
|
||||
return (size() == 0);
|
||||
}
|
||||
|
||||
size_t
|
||||
callchain::size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
void
|
||||
callchain::sample(int signo)
|
||||
{
|
||||
if(signo != get_sampling_overflow_signal()) return;
|
||||
|
||||
// on RedHat, the unw_step within get_unw_stack involves a mutex lock
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
static thread_local const auto& _tinfo = thread_info::get();
|
||||
auto _tid = _tinfo->index_data->sequent_value;
|
||||
auto& _perf_event = perf::get_instance(_tid);
|
||||
|
||||
if(!_perf_event) return;
|
||||
|
||||
_perf_event->stop();
|
||||
|
||||
for(auto itr : *_perf_event)
|
||||
{
|
||||
if(itr.is_sample())
|
||||
{
|
||||
auto _ip = itr.get_ip();
|
||||
auto _data = record{};
|
||||
_data.timestamp = itr.get_time();
|
||||
_data.data.emplace_back(_ip);
|
||||
bool _skip_ip = true;
|
||||
for(auto ditr : itr.get_callchain())
|
||||
{
|
||||
// skip the first instance of current IP but allow after that since this
|
||||
// might be a recursive call
|
||||
if(ditr == _ip && _skip_ip)
|
||||
_skip_ip = false;
|
||||
else
|
||||
_data.data.emplace_back(ditr);
|
||||
if(_data.data.size() == _data.data.capacity()) break;
|
||||
}
|
||||
if(!_data.data.empty()) m_data.emplace_back(_data);
|
||||
}
|
||||
}
|
||||
|
||||
_perf_event->start();
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_INITIALIZE_STORAGE(rocprofsys::component::callchain)
|
||||
@@ -0,0 +1,95 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/containers/static_vector.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/components/base/declaration.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/unwind/cache.hpp>
|
||||
#include <timemory/unwind/processed_entry.hpp>
|
||||
#include <timemory/unwind/stack.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct callchain : comp::empty_base
|
||||
{
|
||||
static constexpr size_t stack_depth = ROCPROFSYS_MAX_UNWIND_DEPTH;
|
||||
|
||||
struct record
|
||||
{
|
||||
uint64_t timestamp = 0;
|
||||
container::static_vector<uintptr_t, stack_depth> data = {};
|
||||
|
||||
bool operator<(const record& rhs) const;
|
||||
};
|
||||
|
||||
using cache_type = tim::unwind::cache;
|
||||
using entry_type = tim::unwind::processed_entry;
|
||||
using value_type = void;
|
||||
using data_t = container::static_vector<record, 64>;
|
||||
using entry_vec_t = std::vector<entry_type>;
|
||||
using ts_entry_vec_t = std::pair<uint64_t, entry_vec_t>;
|
||||
|
||||
static std::string label();
|
||||
static std::string description();
|
||||
|
||||
callchain() = default;
|
||||
~callchain() = default;
|
||||
callchain(const callchain&) = default;
|
||||
callchain(callchain&&) noexcept = default;
|
||||
|
||||
callchain& operator=(const callchain&) = default;
|
||||
callchain& operator=(callchain&&) noexcept = default;
|
||||
|
||||
static std::vector<ts_entry_vec_t> filter_and_patch(
|
||||
const std::vector<ts_entry_vec_t>&);
|
||||
|
||||
static void start();
|
||||
static void stop();
|
||||
|
||||
void sample(int = -1);
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
std::vector<ts_entry_vec_t> get() const;
|
||||
data_t get_data() const { return m_data; }
|
||||
|
||||
private:
|
||||
data_t m_data = {};
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/config.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/causal/data.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/tracing.hpp"
|
||||
#include "library/tracing/annotation.hpp"
|
||||
|
||||
#include <timemory/components/gotcha/backends.hpp>
|
||||
#include <timemory/hash/types.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace quirk
|
||||
{
|
||||
struct causal : concepts::quirk_type
|
||||
{};
|
||||
|
||||
struct perfetto : concepts::quirk_type
|
||||
{};
|
||||
|
||||
struct timemory : concepts::quirk_type
|
||||
{};
|
||||
} // namespace quirk
|
||||
} // namespace tim
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
using tim::is_one_of;
|
||||
using tim::type_list;
|
||||
|
||||
// these categories increment push/pop counts, which are used for sanity checks since
|
||||
// they should ALWAYS be popped if they were pushed
|
||||
using tracing_count_categories_t =
|
||||
type_list<category::host, category::mpi, category::pthread, category::rocm_hip,
|
||||
category::rocm_hsa, category::rocm_rccl>;
|
||||
|
||||
// convert these categories to throughput points
|
||||
using causal_throughput_categories_t =
|
||||
type_list<category::host, category::kokkos, category::ompt, category::rocm_hip,
|
||||
category::rocm_hsa, category::rocm_rccl, category::rocm_roctx>;
|
||||
|
||||
// define this outside of category region functions so that the
|
||||
// static thread_local is global instead of per-template instantiation
|
||||
inline ThreadState
|
||||
get_thread_status()
|
||||
{
|
||||
static thread_local auto _thread_init_once = std::once_flag{};
|
||||
std::call_once(_thread_init_once, tracing::thread_init);
|
||||
|
||||
return get_thread_state();
|
||||
}
|
||||
|
||||
// timemory component which calls rocprof-sys functions
|
||||
// (used in gotcha wrappers)
|
||||
template <typename CategoryT>
|
||||
struct category_region : comp::base<category_region<CategoryT>, void>
|
||||
{
|
||||
using gotcha_data_t = tim::component::gotcha_data;
|
||||
|
||||
static constexpr auto category_name = trait::name<CategoryT>::value;
|
||||
|
||||
static std::string label()
|
||||
{
|
||||
return JOIN('_', "rocprofsys", category_name, "region");
|
||||
}
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
static void start(std::string_view name, Args&&...);
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
static void stop(std::string_view name, Args&&...);
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
static void mark(std::string_view name, Args&&...);
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
static void audit(const gotcha_data_t&, audit::incoming, Args&&...);
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
static void audit(const gotcha_data_t&, audit::outgoing, Args&&...);
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
static void audit(std::string_view, audit::incoming, Args&&...);
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
static void audit(std::string_view, audit::outgoing, Args&&...);
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
static void audit(quirk::config<OptsT...>, Args&&...);
|
||||
};
|
||||
|
||||
template <typename CategoryT>
|
||||
template <typename... OptsT, typename... Args>
|
||||
void
|
||||
category_region<CategoryT>::start(std::string_view name, Args&&... args)
|
||||
{
|
||||
// skip if category is disabled
|
||||
if(tracing::category_push_disabled<CategoryT>()) return;
|
||||
|
||||
// unconditionally return if thread is disabled or finalized
|
||||
if(get_thread_state() == ThreadState::Disabled) return;
|
||||
if(get_state() >= State::Finalized) return;
|
||||
|
||||
if(name.empty()) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
// the expectation here is that if the state is not active then the call
|
||||
// to rocprofsys_init_tooling_hidden will activate all the appropriate
|
||||
// tooling one time and as it exits set it to active and return true.
|
||||
if(get_state() != State::Active && !rocprofsys_init_tooling_hidden()) return;
|
||||
|
||||
if(get_thread_status() == ThreadState::Disabled) return;
|
||||
|
||||
constexpr bool _ct_use_timemory =
|
||||
(sizeof...(OptsT) == 0 || is_one_of<quirk::timemory, type_list<OptsT...>>::value);
|
||||
|
||||
constexpr bool _ct_use_perfetto =
|
||||
(sizeof...(OptsT) == 0 || is_one_of<quirk::perfetto, type_list<OptsT...>>::value);
|
||||
|
||||
constexpr bool _ct_use_causal =
|
||||
(sizeof...(OptsT) == 0 || is_one_of<quirk::causal, type_list<OptsT...>>::value);
|
||||
|
||||
ROCPROFSYS_CONDITIONAL_PRINT(
|
||||
tracing::debug_push,
|
||||
"[%s][PID=%i][state=%s][thread_state=%s] rocprofsys_push_region(%s)\n",
|
||||
category_name, process::get_id(), std::to_string(get_state()).c_str(),
|
||||
std::to_string(get_thread_state()).c_str(), name.data());
|
||||
|
||||
if constexpr(is_one_of<CategoryT, tracing_count_categories_t>::value)
|
||||
{
|
||||
++tracing::push_count();
|
||||
}
|
||||
|
||||
auto _hash = tim::add_hash_id(name);
|
||||
name = tim::get_hash_identifier_fast(_hash);
|
||||
|
||||
if constexpr(_ct_use_causal)
|
||||
{
|
||||
if constexpr(!is_one_of<CategoryT, causal_throughput_categories_t>::value)
|
||||
{
|
||||
if(get_use_causal()) causal::push_progress_point(name);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(_ct_use_timemory)
|
||||
{
|
||||
if(get_use_timemory())
|
||||
{
|
||||
tracing::push_timemory(CategoryT{}, name, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(_ct_use_perfetto)
|
||||
{
|
||||
if(get_use_perfetto())
|
||||
{
|
||||
tracing::push_perfetto(CategoryT{}, name.data(), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CategoryT>
|
||||
template <typename... OptsT, typename... Args>
|
||||
void
|
||||
category_region<CategoryT>::stop(std::string_view name, Args&&... args)
|
||||
{
|
||||
// skip if category is disabled
|
||||
if(tracing::category_pop_disabled<CategoryT>()) return;
|
||||
|
||||
if(get_thread_state() == ThreadState::Disabled) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
constexpr bool _ct_use_timemory =
|
||||
(sizeof...(OptsT) == 0 || is_one_of<quirk::timemory, type_list<OptsT...>>::value);
|
||||
|
||||
constexpr bool _ct_use_perfetto =
|
||||
(sizeof...(OptsT) == 0 || is_one_of<quirk::perfetto, type_list<OptsT...>>::value);
|
||||
|
||||
constexpr bool _ct_use_causal =
|
||||
(sizeof...(OptsT) == 0 || is_one_of<quirk::causal, type_list<OptsT...>>::value);
|
||||
|
||||
ROCPROFSYS_CONDITIONAL_PRINT(
|
||||
tracing::debug_pop,
|
||||
"[%s][PID=%i][state=%s][thread_state=%s] rocprofsys_pop_region(%s)\n",
|
||||
category_name, process::get_id(), std::to_string(get_state()).c_str(),
|
||||
std::to_string(get_thread_state()).c_str(), name.data());
|
||||
|
||||
// only execute when active
|
||||
if(get_state() == State::Active)
|
||||
{
|
||||
if constexpr(is_one_of<CategoryT, tracing_count_categories_t>::value)
|
||||
{
|
||||
++tracing::pop_count();
|
||||
}
|
||||
|
||||
if constexpr(_ct_use_perfetto)
|
||||
{
|
||||
if(get_use_perfetto())
|
||||
{
|
||||
tracing::pop_perfetto(CategoryT{}, name.data(),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(_ct_use_timemory)
|
||||
{
|
||||
if(get_use_timemory())
|
||||
{
|
||||
tracing::pop_timemory(CategoryT{}, name, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(_ct_use_causal)
|
||||
{
|
||||
if constexpr(is_one_of<CategoryT, causal_throughput_categories_t>::value)
|
||||
{
|
||||
if(get_use_causal()) causal::mark_progress_point(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(get_use_causal()) causal::pop_progress_point(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static auto _debug = get_debug_env();
|
||||
ROCPROFSYS_CONDITIONAL_BASIC_PRINT(
|
||||
_debug, "[%s] rocprofsys_pop_region(%s) ignored :: state = %s\n",
|
||||
category_name, name.data(), std::to_string(get_state()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CategoryT>
|
||||
template <typename... OptsT, typename... Args>
|
||||
void
|
||||
category_region<CategoryT>::mark(std::string_view name, Args&&...)
|
||||
{
|
||||
constexpr bool _ct_use_causal =
|
||||
(sizeof...(OptsT) == 0 || is_one_of<quirk::causal, type_list<OptsT...>>::value);
|
||||
|
||||
if constexpr(!_ct_use_causal) return;
|
||||
|
||||
// skip if category is disabled
|
||||
if(tracing::category_mark_disabled<CategoryT>()) return;
|
||||
|
||||
// the expectation here is that if the state is not active then the call
|
||||
// to rocprofsys_init_tooling_hidden will activate all the appropriate
|
||||
// tooling one time and as it exits set it to active and return true.
|
||||
if(get_state() != State::Active && !rocprofsys_init_tooling_hidden()) return;
|
||||
|
||||
// unconditionally return if thread is disabled or finalized
|
||||
if(get_thread_state() >= ThreadState::Completed) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
if(get_use_causal())
|
||||
{
|
||||
ROCPROFSYS_CONDITIONAL_PRINT(
|
||||
tracing::debug_mark,
|
||||
"[%s][PID=%i][state=%s][thread_state=%s] rocprofsys_progress(%s)\n",
|
||||
category_name, process::get_id(), std::to_string(get_state()).c_str(),
|
||||
std::to_string(get_thread_state()).c_str(), name.data());
|
||||
|
||||
causal::mark_progress_point(name);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CategoryT>
|
||||
template <typename... OptsT, typename... Args>
|
||||
void
|
||||
category_region<CategoryT>::audit(const gotcha_data_t& _data, audit::incoming,
|
||||
Args&&... _args)
|
||||
{
|
||||
start<OptsT...>(_data.tool_id.c_str(), [&](::perfetto::EventContext ctx) {
|
||||
if(config::get_perfetto_annotations())
|
||||
{
|
||||
int64_t _n = 0;
|
||||
ROCPROFSYS_FOLD_EXPRESSION(tracing::add_perfetto_annotation(
|
||||
ctx, tim::try_demangle<std::remove_reference_t<Args>>(), _args, _n++));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename CategoryT>
|
||||
template <typename... OptsT, typename... Args>
|
||||
void
|
||||
category_region<CategoryT>::audit(const gotcha_data_t& _data, audit::outgoing,
|
||||
Args&&... _args)
|
||||
{
|
||||
stop<OptsT...>(_data.tool_id.c_str(), [&](::perfetto::EventContext ctx) {
|
||||
if(config::get_perfetto_annotations())
|
||||
tracing::add_perfetto_annotation(ctx, "return", JOIN(", ", _args...));
|
||||
});
|
||||
}
|
||||
|
||||
template <typename CategoryT>
|
||||
template <typename... OptsT, typename... Args>
|
||||
void
|
||||
category_region<CategoryT>::audit(std::string_view _name, audit::incoming,
|
||||
Args&&... _args)
|
||||
{
|
||||
start<OptsT...>(_name.data(), [&](::perfetto::EventContext ctx) {
|
||||
if(config::get_perfetto_annotations())
|
||||
{
|
||||
int64_t _n = 0;
|
||||
ROCPROFSYS_FOLD_EXPRESSION(tracing::add_perfetto_annotation(
|
||||
ctx, tim::try_demangle<std::remove_reference_t<Args>>(), _args, _n++));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename CategoryT>
|
||||
template <typename... OptsT, typename... Args>
|
||||
void
|
||||
category_region<CategoryT>::audit(std::string_view _name, audit::outgoing,
|
||||
Args&&... _args)
|
||||
{
|
||||
stop<OptsT...>(_name.data(), [&](::perfetto::EventContext ctx) {
|
||||
if(config::get_perfetto_annotations())
|
||||
tracing::add_perfetto_annotation(ctx, "return", JOIN(", ", _args...));
|
||||
});
|
||||
}
|
||||
|
||||
template <typename CategoryT>
|
||||
template <typename... OptsT, typename... Args>
|
||||
void
|
||||
category_region<CategoryT>::audit(quirk::config<OptsT...>, Args&&... _args)
|
||||
{
|
||||
audit<OptsT...>(std::forward<Args>(_args)...);
|
||||
}
|
||||
|
||||
template <typename CategoryT>
|
||||
struct local_category_region : comp::base<local_category_region<CategoryT>, void>
|
||||
{
|
||||
using impl_type = category_region<CategoryT>;
|
||||
|
||||
static constexpr auto category_name = impl_type::category_name;
|
||||
static std::string label() { return impl_type::label(); }
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
auto start(Args&&... args)
|
||||
{
|
||||
if(m_prefix.empty()) return;
|
||||
return impl_type::template start<OptsT...>(m_prefix, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
auto stop(Args&&... args)
|
||||
{
|
||||
if(m_prefix.empty()) return;
|
||||
return impl_type::template stop<OptsT...>(m_prefix, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
auto mark(Args&&... args)
|
||||
{
|
||||
if(m_prefix.empty()) return;
|
||||
return impl_type::template mark<OptsT...>(m_prefix, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
auto audit(Args&&... args)
|
||||
-> decltype(impl_type::template audit<OptsT...>(std::declval<std::string_view>(),
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
if(m_prefix.empty()) return;
|
||||
return impl_type::template audit<OptsT...>(m_prefix, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... OptsT, typename... Args>
|
||||
auto audit(quirk::config<OptsT...>, Args&&... args)
|
||||
{
|
||||
if(m_prefix.empty()) return;
|
||||
return impl_type::template audit<OptsT...>(quirk::config<OptsT...>{}, m_prefix,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void set_prefix(std::string_view _v) { m_prefix = _v; }
|
||||
|
||||
private:
|
||||
std::string_view m_prefix = {};
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,425 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/comm_data.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "library/tracing.hpp"
|
||||
|
||||
#include <timemory/backends/mpi.hpp>
|
||||
#include <timemory/manager.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/utility/locking.hpp>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename Tp, typename... Args>
|
||||
void
|
||||
write_perfetto_counter_track(uint64_t _val)
|
||||
{
|
||||
using counter_track = rocprofsys::perfetto_counter_track<Tp>;
|
||||
|
||||
if(rocprofsys::get_use_perfetto() &&
|
||||
rocprofsys::get_state() == rocprofsys::State::Active)
|
||||
{
|
||||
auto _emplace = [](const size_t _idx) {
|
||||
if(!counter_track::exists(_idx))
|
||||
{
|
||||
std::string _label = (_idx > 0)
|
||||
? JOIN(" ", Tp::label, JOIN("", '[', _idx, ']'))
|
||||
: Tp::label;
|
||||
counter_track::emplace(_idx, _label, "bytes");
|
||||
}
|
||||
};
|
||||
|
||||
const size_t _idx = 0;
|
||||
static std::once_flag _once{};
|
||||
std::call_once(_once, _emplace, _idx);
|
||||
|
||||
static std::mutex _mutex{};
|
||||
static uint64_t value = 0;
|
||||
uint64_t _now = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> _lk{ _mutex };
|
||||
_now = rocprofsys::tracing::now<uint64_t>();
|
||||
_val = (value += _val);
|
||||
}
|
||||
|
||||
TRACE_COUNTER(Tp::value, counter_track::at(_idx, 0), _now, _val);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
comm_data::preinit()
|
||||
{
|
||||
configure();
|
||||
}
|
||||
|
||||
void
|
||||
comm_data::global_finalize()
|
||||
{
|
||||
configure();
|
||||
}
|
||||
|
||||
void
|
||||
comm_data::configure()
|
||||
{
|
||||
static bool _once = false;
|
||||
if(_once) return;
|
||||
_once = true;
|
||||
|
||||
comm_data_tracker_t::label() = "comm_data";
|
||||
comm_data_tracker_t::description() = "Tracks MPI/RCCL communication data sizes";
|
||||
comm_data_tracker_t::display_unit() = "MB";
|
||||
comm_data_tracker_t::unit() = units::megabyte;
|
||||
|
||||
auto _fmt_flags = comm_data_tracker_t::get_format_flags();
|
||||
_fmt_flags &= (std::ios_base::fixed & std::ios_base::scientific);
|
||||
_fmt_flags |= (std::ios_base::scientific);
|
||||
comm_data_tracker_t::set_precision(3);
|
||||
comm_data_tracker_t::set_format_flags(_fmt_flags);
|
||||
}
|
||||
|
||||
#if defined(ROCPROFSYS_USE_MPI)
|
||||
// MPI_Send
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, int count,
|
||||
MPI_Datatype datatype, int dst, int tag, MPI_Comm)
|
||||
{
|
||||
int _size = mpi_type_size(datatype);
|
||||
if(_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_send>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _a{ _name };
|
||||
add(_a, count * _size);
|
||||
tracker_t _b{ JOIN('/', _name, JOIN('=', "dst", dst)) };
|
||||
add(_b, count * _size);
|
||||
add(JOIN('/', _name, JOIN('=', "dst", dst), JOIN('=', "tag", tag)), count * _size);
|
||||
}
|
||||
|
||||
// MPI_Recv
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, void*, int count,
|
||||
MPI_Datatype datatype, int dst, int tag, MPI_Comm, MPI_Status*)
|
||||
{
|
||||
int _size = mpi_type_size(datatype);
|
||||
if(_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_recv>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _a{ _name };
|
||||
add(_a, count * _size);
|
||||
tracker_t _b{ JOIN('/', _name, JOIN('=', "dst", dst)) };
|
||||
add(_b, count * _size);
|
||||
add(JOIN('/', _name, JOIN('=', "dst", dst), JOIN('=', "tag", tag)), count * _size);
|
||||
}
|
||||
|
||||
// MPI_Isend
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, int count,
|
||||
MPI_Datatype datatype, int dst, int tag, MPI_Comm, MPI_Request*)
|
||||
{
|
||||
int _size = mpi_type_size(datatype);
|
||||
if(_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_send>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _a{ _name };
|
||||
add(_a, count * _size);
|
||||
tracker_t _b{ JOIN('/', _name, JOIN('=', "dst", dst)) };
|
||||
add(_b, count * _size);
|
||||
add(JOIN('/', _name, JOIN('=', "dst", dst), JOIN('=', "tag", tag)), count * _size);
|
||||
}
|
||||
|
||||
// MPI_Irecv
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, void*, int count,
|
||||
MPI_Datatype datatype, int dst, int tag, MPI_Comm, MPI_Request*)
|
||||
{
|
||||
int _size = mpi_type_size(datatype);
|
||||
if(_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_recv>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _a{ _name };
|
||||
add(_a, count * _size);
|
||||
tracker_t _b{ JOIN('/', _name, JOIN('=', "dst", dst)) };
|
||||
add(_b, count * _size);
|
||||
add(JOIN('/', _name, JOIN('=', "dst", dst), JOIN('=', "tag", tag)), count * _size);
|
||||
}
|
||||
|
||||
// MPI_Bcast
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, void*, int count,
|
||||
MPI_Datatype datatype, int root, MPI_Comm)
|
||||
{
|
||||
int _size = mpi_type_size(datatype);
|
||||
if(_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_send>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _t{ _name };
|
||||
add(_t, count * _size);
|
||||
add(JOIN('/', _name, JOIN('=', "root", root)), count * _size);
|
||||
}
|
||||
|
||||
// MPI_Allreduce
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, void*, int count,
|
||||
MPI_Datatype datatype, MPI_Op, MPI_Comm)
|
||||
{
|
||||
int _size = mpi_type_size(datatype);
|
||||
if(_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_recv>(count * _size);
|
||||
write_perfetto_counter_track<mpi_send>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
add(_data, count * _size);
|
||||
}
|
||||
|
||||
// MPI_Sendrecv
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, int sendcount,
|
||||
MPI_Datatype sendtype, int dst, int sendtag, void*, int recvcount,
|
||||
MPI_Datatype recvtype, int src, int recvtag, MPI_Comm, MPI_Status*)
|
||||
{
|
||||
int _send_size = mpi_type_size(sendtype);
|
||||
int _recv_size = mpi_type_size(recvtype);
|
||||
if(_send_size == 0 || _recv_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_send>(sendcount * _send_size);
|
||||
write_perfetto_counter_track<mpi_recv>(recvcount * _recv_size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _t{ _name };
|
||||
add(_t, sendcount * _send_size + recvcount * _recv_size);
|
||||
{
|
||||
tracker_t _b{ JOIN('/', _name, "send") };
|
||||
add(_b, sendcount * _send_size);
|
||||
tracker_t _c{ JOIN('/', _name, JOIN('=', "send", dst)) };
|
||||
add(_b, sendcount * _send_size);
|
||||
add(JOIN('/', _name, "send", JOIN('=', "tag", sendtag)), sendcount * _send_size);
|
||||
add(JOIN('/', _name, JOIN('=', "send", dst), JOIN('=', "tag", sendtag)),
|
||||
sendcount * _send_size);
|
||||
}
|
||||
{
|
||||
tracker_t _b{ JOIN('/', _name, "recv") };
|
||||
add(_b, recvcount * _recv_size);
|
||||
tracker_t _c{ JOIN('/', _name, JOIN('=', "recv", src)) };
|
||||
add(_b, recvcount * _recv_size);
|
||||
add(JOIN('/', _name, "recv", JOIN('=', "tag", recvtag)), recvcount * _recv_size);
|
||||
add(JOIN('/', _name, JOIN('=', "recv", src), JOIN('=', "tag", recvtag)),
|
||||
recvcount * _recv_size);
|
||||
}
|
||||
}
|
||||
|
||||
// MPI_Gather
|
||||
// MPI_Scatter
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, int sendcount,
|
||||
MPI_Datatype sendtype, void*, int recvcount, MPI_Datatype recvtype,
|
||||
int root, MPI_Comm)
|
||||
{
|
||||
int _send_size = mpi_type_size(sendtype);
|
||||
int _recv_size = mpi_type_size(recvtype);
|
||||
if(_send_size == 0 || _recv_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_send>(sendcount * _send_size);
|
||||
write_perfetto_counter_track<mpi_recv>(recvcount * _recv_size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _t{ _name };
|
||||
add(_t, sendcount * _send_size + recvcount * _recv_size);
|
||||
tracker_t _r(JOIN('/', _name, JOIN('=', "root", root)));
|
||||
add(_r, sendcount * _send_size + recvcount * _recv_size);
|
||||
add(JOIN('/', _name, JOIN('=', "root", root), "send"), sendcount * _send_size);
|
||||
add(JOIN('/', _name, JOIN('=', "root", root), "recv"), recvcount * _recv_size);
|
||||
}
|
||||
|
||||
// MPI_Alltoall
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, int sendcount,
|
||||
MPI_Datatype sendtype, void*, int recvcount, MPI_Datatype recvtype,
|
||||
MPI_Comm)
|
||||
{
|
||||
int _send_size = mpi_type_size(sendtype);
|
||||
int _recv_size = mpi_type_size(recvtype);
|
||||
if(_send_size == 0 || _recv_size == 0) return;
|
||||
|
||||
write_perfetto_counter_track<mpi_send>(sendcount * _send_size);
|
||||
write_perfetto_counter_track<mpi_recv>(recvcount * _recv_size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _t{ _name };
|
||||
add(_t, sendcount * _send_size + recvcount * _recv_size);
|
||||
add(JOIN('/', _name, "send"), sendcount * _send_size);
|
||||
add(JOIN('/', _name, "recv"), recvcount * _recv_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ROCPROFSYS_USE_RCCL)
|
||||
// ncclReduce
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, const void*,
|
||||
size_t count, ncclDataType_t datatype, ncclRedOp_t, int root, ncclComm_t,
|
||||
hipStream_t)
|
||||
{
|
||||
int _size = rccl_type_size(datatype);
|
||||
if(_size <= 0) return;
|
||||
|
||||
write_perfetto_counter_track<rccl_recv>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _t{ _name };
|
||||
add(_t, count * _size);
|
||||
add(JOIN('/', _name, JOIN('=', "root", root)), count * _size);
|
||||
}
|
||||
|
||||
// ncclSend
|
||||
// ncclGather
|
||||
// ncclBcast
|
||||
// ncclRecv
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, size_t count,
|
||||
ncclDataType_t datatype, int peer, ncclComm_t, hipStream_t)
|
||||
{
|
||||
int _size = rccl_type_size(datatype);
|
||||
if(_size <= 0) return;
|
||||
|
||||
static auto _send_types = std::unordered_set<std::string>{ "ncclSend", "ncclBcast" };
|
||||
static auto _recv_types = std::unordered_set<std::string>{ "ncclGather", "ncclRecv" };
|
||||
|
||||
if(_send_types.count(_data.tool_id) > 0)
|
||||
{
|
||||
write_perfetto_counter_track<rccl_send>(count * _size);
|
||||
}
|
||||
else if(_recv_types.count(_data.tool_id) > 0)
|
||||
{
|
||||
write_perfetto_counter_track<rccl_recv>(count * _size);
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_CI_THROW(true, "RCCL function not handled: %s", _data.tool_id.c_str());
|
||||
}
|
||||
|
||||
write_perfetto_counter_track<rccl_recv>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
std::string _label = "root";
|
||||
if(_name.find("Send") != std::string::npos) _label = "peer";
|
||||
|
||||
tracker_t _t{ _name };
|
||||
add(_t, count * _size);
|
||||
add(JOIN('/', _name, JOIN('=', _label, peer)), count * _size);
|
||||
}
|
||||
|
||||
// ncclBroadcast
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, const void*,
|
||||
size_t count, ncclDataType_t datatype, int root, ncclComm_t, hipStream_t)
|
||||
{
|
||||
int _size = rccl_type_size(datatype);
|
||||
if(_size <= 0) return;
|
||||
|
||||
write_perfetto_counter_track<rccl_send>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
auto _name = std::string_view{ _data.tool_id };
|
||||
tracker_t _t{ _name };
|
||||
add(_t, count * _size);
|
||||
add(JOIN('/', _data.tool_id, JOIN('=', "root", root)), count * _size);
|
||||
}
|
||||
|
||||
// ncclAllReduce
|
||||
// ncclReduceScatter
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, const void*,
|
||||
size_t count, ncclDataType_t datatype, ncclRedOp_t, ncclComm_t,
|
||||
hipStream_t)
|
||||
{
|
||||
int _size = rccl_type_size(datatype);
|
||||
if(_size <= 0) return;
|
||||
|
||||
static auto _recv_types = std::unordered_set<std::string>{ "ncclAllReduce" };
|
||||
static auto _send_types = std::unordered_set<std::string>{ "ncclReduceScatter" };
|
||||
|
||||
if(_send_types.count(_data.tool_id) > 0)
|
||||
{
|
||||
write_perfetto_counter_track<rccl_send>(count * _size);
|
||||
}
|
||||
else if(_recv_types.count(_data.tool_id) > 0)
|
||||
{
|
||||
write_perfetto_counter_track<rccl_recv>(count * _size);
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_CI_THROW(true, "RCCL function not handled: %s", _data.tool_id.c_str());
|
||||
}
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
add(_data, count * _size);
|
||||
}
|
||||
|
||||
// ncclAllGather
|
||||
void
|
||||
comm_data::audit(const gotcha_data& _data, audit::incoming, const void*, const void*,
|
||||
size_t count, ncclDataType_t datatype, ncclComm_t, hipStream_t)
|
||||
{
|
||||
int _size = rccl_type_size(datatype);
|
||||
if(_size <= 0) return;
|
||||
|
||||
write_perfetto_counter_track<rccl_recv>(count * _size);
|
||||
|
||||
if(!rocprofsys::get_use_timemory()) return;
|
||||
add(_data, count * _size);
|
||||
}
|
||||
#endif
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<float, tim::project::rocprofsys>), true, float)
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(comm_data, false, void)
|
||||
@@ -0,0 +1,244 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "common/join.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/rccl.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/components/category_region.hpp"
|
||||
|
||||
#include <timemory/api/macros.hpp>
|
||||
#include <timemory/components/gotcha/backends.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/operations/types/set.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#if defined(ROCPROFSYS_USE_MPI)
|
||||
# include <mpi.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
ROCPROFSYS_COMPONENT_ALIAS(comm_data_tracker_t,
|
||||
::tim::component::data_tracker<float, project::rocprofsys>)
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
using gotcha_data = ::tim::component::gotcha_data;
|
||||
|
||||
struct comm_data : base<comm_data, void>
|
||||
{
|
||||
using value_type = void;
|
||||
using this_type = comm_data;
|
||||
using base_type = base<this_type, value_type>;
|
||||
using tracker_t = tim::auto_tuple<comm_data_tracker_t>;
|
||||
using data_type = float;
|
||||
|
||||
struct mpi_recv
|
||||
{
|
||||
static constexpr auto value = "comm_data";
|
||||
static constexpr auto label = "MPI Comm Recv";
|
||||
};
|
||||
|
||||
struct mpi_send
|
||||
{
|
||||
static constexpr auto value = "comm_data";
|
||||
static constexpr auto label = "MPI Comm Send";
|
||||
};
|
||||
|
||||
struct rccl_recv
|
||||
{
|
||||
static constexpr auto value = "comm_data";
|
||||
static constexpr auto label = "RCCL Comm Recv";
|
||||
};
|
||||
|
||||
struct rccl_send
|
||||
{
|
||||
static constexpr auto value = "comm_data";
|
||||
static constexpr auto label = "RCCL Comm Send";
|
||||
};
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(comm_data)
|
||||
|
||||
static void preinit();
|
||||
static void configure();
|
||||
static void global_finalize();
|
||||
static void start() {}
|
||||
static void stop() {}
|
||||
|
||||
#if defined(ROCPROFSYS_USE_MPI)
|
||||
static int mpi_type_size(MPI_Datatype _datatype)
|
||||
{
|
||||
int _size = 0;
|
||||
PMPI_Type_size(_datatype, &_size);
|
||||
return _size;
|
||||
}
|
||||
|
||||
// MPI_Send
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*, int count,
|
||||
MPI_Datatype datatype, int dst, int tag, MPI_Comm);
|
||||
|
||||
// MPI_Recv
|
||||
static void audit(const gotcha_data& _data, audit::incoming, void*, int count,
|
||||
MPI_Datatype datatype, int dst, int tag, MPI_Comm, MPI_Status*);
|
||||
|
||||
// MPI_Isend
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*, int count,
|
||||
MPI_Datatype datatype, int dst, int tag, MPI_Comm, MPI_Request*);
|
||||
|
||||
// MPI_Irecv
|
||||
static void audit(const gotcha_data& _data, audit::incoming, void*, int count,
|
||||
MPI_Datatype datatype, int dst, int tag, MPI_Comm, MPI_Request*);
|
||||
|
||||
// MPI_Bcast
|
||||
static void audit(const gotcha_data& _data, audit::incoming, void*, int count,
|
||||
MPI_Datatype datatype, int root, MPI_Comm);
|
||||
|
||||
// MPI_Allreduce
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*, void*,
|
||||
int count, MPI_Datatype datatype, MPI_Op, MPI_Comm);
|
||||
|
||||
// MPI_Sendrecv
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*,
|
||||
int sendcount, MPI_Datatype sendtype, int, int sendtag, void*,
|
||||
int recvcount, MPI_Datatype recvtype, int, int recvtag, MPI_Comm,
|
||||
MPI_Status*);
|
||||
|
||||
// MPI_Gather
|
||||
// MPI_Scatter
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*,
|
||||
int sendcount, MPI_Datatype sendtype, void*, int recvcount,
|
||||
MPI_Datatype recvtype, int root, MPI_Comm);
|
||||
|
||||
// MPI_Alltoall
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*,
|
||||
int sendcount, MPI_Datatype sendtype, void*, int recvcount,
|
||||
MPI_Datatype recvtype, MPI_Comm);
|
||||
#endif
|
||||
|
||||
#if defined(ROCPROFSYS_USE_RCCL)
|
||||
static auto rccl_type_size(ncclDataType_t datatype)
|
||||
{
|
||||
switch(datatype)
|
||||
{
|
||||
case ncclInt8:
|
||||
case ncclUint8: return 1;
|
||||
case ncclFloat16: return 2;
|
||||
case ncclInt32:
|
||||
case ncclUint32:
|
||||
case ncclFloat32: return 4;
|
||||
case ncclInt64:
|
||||
case ncclUint64:
|
||||
case ncclFloat64: return 8;
|
||||
default: return 0;
|
||||
};
|
||||
}
|
||||
|
||||
// ncclReduce
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*, const void*,
|
||||
size_t count, ncclDataType_t datatype, ncclRedOp_t, int root,
|
||||
ncclComm_t, hipStream_t);
|
||||
|
||||
// ncclSend
|
||||
// ncclGather
|
||||
// ncclBcast
|
||||
// ncclRecv
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*,
|
||||
size_t count, ncclDataType_t datatype, int peer, ncclComm_t,
|
||||
hipStream_t);
|
||||
|
||||
// ncclBroadcast
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*, const void*,
|
||||
size_t count, ncclDataType_t datatype, int root, ncclComm_t,
|
||||
hipStream_t);
|
||||
|
||||
// ncclAllReduce
|
||||
// ncclReduceScatter
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*, const void*,
|
||||
size_t count, ncclDataType_t datatype, ncclRedOp_t, ncclComm_t,
|
||||
hipStream_t);
|
||||
|
||||
// ncclAllGather
|
||||
// ncclAlltoAll
|
||||
static void audit(const gotcha_data& _data, audit::incoming, const void*, const void*,
|
||||
size_t count, ncclDataType_t datatype, ncclComm_t, hipStream_t);
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
static auto& add(tracker_t& _t, data_type value)
|
||||
{
|
||||
if(rocprofsys::get_state() != rocprofsys::State::Active)
|
||||
{
|
||||
_t.invoke<operation::set_is_invalid>(true);
|
||||
return _t;
|
||||
}
|
||||
_t.store(std::plus<data_type>{}, value);
|
||||
return _t;
|
||||
}
|
||||
|
||||
static auto add(const gotcha_data& _data, data_type value)
|
||||
{
|
||||
tracker_t _t{ std::string_view{ _data.tool_id.c_str() } };
|
||||
return add(_t, value);
|
||||
}
|
||||
|
||||
static auto add(std::string&& _name, data_type value)
|
||||
{
|
||||
tracker_t _t{ _name };
|
||||
return add(_t, value);
|
||||
}
|
||||
|
||||
static auto add(std::string_view _name, data_type value)
|
||||
{
|
||||
tracker_t _t{ _name };
|
||||
return add(_t, value);
|
||||
}
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
#if !defined(ROCPROFSYS_EXTERN_COMPONENTS) || \
|
||||
(defined(ROCPROFSYS_EXTERN_COMPONENTS) && ROCPROFSYS_EXTERN_COMPONENTS > 0)
|
||||
|
||||
# include <timemory/components/base.hpp>
|
||||
# include <timemory/components/data_tracker/components.hpp>
|
||||
# include <timemory/operations.hpp>
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<float, tim::project::rocprofsys>), true, float)
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(comm_data, false, void)
|
||||
#endif
|
||||
@@ -0,0 +1,222 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/cpu_freq.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/components/rusage/backends.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/utility/procfs/cpuinfo.hpp>
|
||||
#include <timemory/utility/type_list.hpp>
|
||||
|
||||
namespace cpuinfo = tim::procfs::cpuinfo;
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
cpu_freq::cpu_id_set_t&
|
||||
cpu_freq::get_enabled_cpus()
|
||||
{
|
||||
static auto _v = cpu_id_set_t{};
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::string
|
||||
cpu_freq::label()
|
||||
{
|
||||
return "cpu_freq";
|
||||
}
|
||||
|
||||
std::string
|
||||
cpu_freq::description()
|
||||
{
|
||||
return "Records the current CPU frequencies";
|
||||
}
|
||||
|
||||
int64_t
|
||||
cpu_freq::unit()
|
||||
{
|
||||
return tim::units::MHz;
|
||||
}
|
||||
|
||||
std::string
|
||||
cpu_freq::display_unit()
|
||||
{
|
||||
return tim::units::freq_repr(unit());
|
||||
}
|
||||
|
||||
void
|
||||
cpu_freq::configure()
|
||||
{
|
||||
auto _ncpu = cpuinfo::freq::size();
|
||||
auto _enabled_freqs = std::set<uint64_t>{};
|
||||
|
||||
auto _enabled_val = get_sampling_cpus();
|
||||
for(auto& itr : _enabled_val)
|
||||
itr = tolower(itr);
|
||||
if(_enabled_val == "off")
|
||||
_enabled_val = "none";
|
||||
else if(_enabled_val == "on")
|
||||
_enabled_val = "all";
|
||||
if(_enabled_val != "none" && _enabled_val != "all")
|
||||
{
|
||||
auto _enabled = tim::delimit(_enabled_val, ",; \t");
|
||||
if(_enabled.empty())
|
||||
{
|
||||
for(size_t i = 0; i < _ncpu; ++i)
|
||||
_enabled_freqs.emplace(i);
|
||||
}
|
||||
for(auto&& _v : _enabled)
|
||||
{
|
||||
if(_v.find_first_not_of("0123456789-") != std::string::npos)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(
|
||||
0,
|
||||
"Invalid CPU specification. Only numerical values (e.g., 0) or "
|
||||
"ranges (e.g., 0-7) are permitted. Ignoring %s...",
|
||||
_v.c_str());
|
||||
continue;
|
||||
}
|
||||
if(_v.find('-') != std::string::npos)
|
||||
{
|
||||
auto _vv = tim::delimit(_v, "-");
|
||||
ROCPROFSYS_CONDITIONAL_THROW(
|
||||
_vv.size() != 2,
|
||||
"Invalid CPU range specification: %s. Required format N-M, e.g. 0-4",
|
||||
_v.c_str());
|
||||
for(size_t i = std::stoull(_vv.at(0)); i <= std::stoull(_vv.at(1)); ++i)
|
||||
_enabled_freqs.emplace(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
_enabled_freqs.emplace(std::stoull(_v));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(_enabled_val == "all")
|
||||
{
|
||||
for(size_t i = 0; i < _ncpu; ++i)
|
||||
_enabled_freqs.emplace(i);
|
||||
}
|
||||
else if(_enabled_val == "none")
|
||||
{
|
||||
_enabled_freqs.clear();
|
||||
}
|
||||
|
||||
for(auto itr : _enabled_freqs)
|
||||
{
|
||||
if(itr < cpuinfo::freq::size())
|
||||
_enabled_freqs.emplace(itr);
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(
|
||||
0, "[cpu_freq::config] Warning! Removing invalid cpu %zu...\n", itr);
|
||||
}
|
||||
}
|
||||
|
||||
if(!cpuinfo::freq{})
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(0, "[cpu_freq::config] Warning! CPU frequencies are disabled "
|
||||
":: unable to open /proc/cpuinfo");
|
||||
_enabled_freqs.clear();
|
||||
}
|
||||
|
||||
ROCPROFSYS_CI_FAIL(!cpuinfo::freq{},
|
||||
"[cpu_freq::config] CPU frequencies are disabled "
|
||||
":: unable to open /proc/cpuinfo");
|
||||
|
||||
get_enabled_cpus() = _enabled_freqs;
|
||||
}
|
||||
|
||||
std::string
|
||||
cpu_freq::as_string() const
|
||||
{
|
||||
return tim::operation::base_printer<cpu_freq>{}(std::stringstream{}, *this).str();
|
||||
}
|
||||
|
||||
cpu_freq::value_type
|
||||
cpu_freq::record()
|
||||
{
|
||||
auto& enabled_cpu_freqs = get_enabled_cpus();
|
||||
|
||||
std::vector<uint64_t> _freqs{};
|
||||
if(!enabled_cpu_freqs.empty())
|
||||
{
|
||||
_freqs.reserve(enabled_cpu_freqs.size());
|
||||
auto&& _freq = cpuinfo::freq{};
|
||||
for(const auto& itr : enabled_cpu_freqs)
|
||||
{
|
||||
_freqs.emplace_back(_freq(itr) * tim::units::MHz);
|
||||
}
|
||||
}
|
||||
|
||||
return _freqs;
|
||||
}
|
||||
|
||||
void
|
||||
cpu_freq::start()
|
||||
{
|
||||
value = record();
|
||||
}
|
||||
|
||||
void
|
||||
cpu_freq::stop()
|
||||
{
|
||||
using namespace tim::stl;
|
||||
value = (record() - value);
|
||||
}
|
||||
|
||||
cpu_freq&
|
||||
cpu_freq::sample()
|
||||
{
|
||||
value = record();
|
||||
return *this;
|
||||
}
|
||||
|
||||
float
|
||||
cpu_freq::at(size_t _idx, int64_t _unit) const
|
||||
{
|
||||
return (value.at(_idx) / static_cast<float>(_unit));
|
||||
}
|
||||
|
||||
std::vector<float>
|
||||
cpu_freq::get(int64_t _unit) const
|
||||
{
|
||||
std::vector<float> _v{};
|
||||
_v.reserve(value.size());
|
||||
for(const auto& itr : value)
|
||||
_v.emplace_back(itr / static_cast<float>(_unit));
|
||||
return _v;
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_INITIALIZE_STORAGE(rocprofsys::component::cpu_freq)
|
||||
@@ -0,0 +1,111 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct cpu_freq
|
||||
: comp::empty_base
|
||||
, tim::component::base_format<cpu_freq>
|
||||
, tim::component::base_data<std::vector<uint64_t>, 1>
|
||||
{
|
||||
using base_type = comp::empty_base;
|
||||
using this_type = cpu_freq;
|
||||
using value_type = std::vector<uint64_t>;
|
||||
using storage_type = tim::storage<cpu_freq, value_type>;
|
||||
using cpu_id_set_t = std::set<uint64_t>;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(cpu_freq)
|
||||
|
||||
// string id for component
|
||||
static std::string label();
|
||||
static std::string description();
|
||||
static int64_t unit();
|
||||
static std::string display_unit();
|
||||
|
||||
static void configure();
|
||||
static cpu_id_set_t& get_enabled_cpus();
|
||||
static value_type record();
|
||||
|
||||
// this will get called right before fork
|
||||
void start();
|
||||
void stop();
|
||||
cpu_freq& sample();
|
||||
std::string as_string() const;
|
||||
|
||||
float at(size_t _idx, int64_t _unit = unit()) const;
|
||||
std::vector<float> get(int64_t _unit = unit()) const;
|
||||
|
||||
public:
|
||||
static auto get_label() { return label(); }
|
||||
static auto get_description() { return description(); }
|
||||
static auto get_unit() { return unit(); }
|
||||
static auto get_display_unit() { return display_unit(); }
|
||||
static int64_t get_laps() { return 0; }
|
||||
static storage_type* get_storage() { return nullptr; }
|
||||
|
||||
auto get_display() const { return as_string(); }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& _os, const cpu_freq& _v)
|
||||
{
|
||||
return (_os << _v.as_string());
|
||||
}
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& _ar, const unsigned _version)
|
||||
{
|
||||
if constexpr(tim::concepts::is_output_archive<ArchiveT>::value)
|
||||
operation::serialization<cpu_freq>{}(*this, _ar, _version);
|
||||
else
|
||||
_ar(tim::cereal::make_nvp("value", value));
|
||||
(void) _version;
|
||||
}
|
||||
|
||||
this_type& operator+=(const this_type& _rhs)
|
||||
{
|
||||
using namespace tim::stl;
|
||||
value += _rhs.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
this_type& operator-=(const this_type& _rhs)
|
||||
{
|
||||
using namespace tim::stl;
|
||||
value -= _rhs.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
using tim::component::base_data<value_type, 1>::value;
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/defines.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/operations/types.hpp>
|
||||
#include <timemory/utility/macros.hpp>
|
||||
#include <timemory/utility/type_list.hpp>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename... Tp>
|
||||
struct ensure_storage
|
||||
{
|
||||
ROCPROFSYS_DEFAULT_OBJECT(ensure_storage)
|
||||
|
||||
void operator()() const { ROCPROFSYS_FOLD_EXPRESSION((*this)(tim::type_list<Tp>{})); }
|
||||
|
||||
private:
|
||||
template <typename Up, std::enable_if_t<tim::trait::is_available<Up>::value, int> = 0>
|
||||
void operator()(tim::type_list<Up>) const
|
||||
{
|
||||
using namespace tim;
|
||||
static thread_local auto _storage = operation::get_storage<Up>{}();
|
||||
static thread_local auto _tid = threading::get_id();
|
||||
static thread_local auto _dtor =
|
||||
scope::destructor{ []() { operation::set_storage<Up>{}(nullptr, _tid); } };
|
||||
|
||||
tim::operation::set_storage<Up>{}(_storage, _tid);
|
||||
if(_tid == 0 && !_storage) tim::trait::runtime_enabled<Up>::set(false);
|
||||
}
|
||||
|
||||
template <typename Up,
|
||||
std::enable_if_t<!tim::trait::is_available<Up>::value, long> = 0>
|
||||
void operator()(tim::type_list<Up>) const
|
||||
{
|
||||
tim::trait::runtime_enabled<Up>::set(false);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/exit_gotcha.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/process/threading.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
void
|
||||
exit_gotcha::configure()
|
||||
{
|
||||
exit_gotcha_t::get_initializer() = []() {
|
||||
exit_gotcha_t::configure<0, void>("abort");
|
||||
exit_gotcha_t::configure<1, void, int>("exit");
|
||||
exit_gotcha_t::configure<2, void, int>("quick_exit");
|
||||
exit_gotcha_t::configure<3, void, int>("_Exit");
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
auto _exit_info = exit_gotcha::exit_info{};
|
||||
|
||||
template <typename FuncT, typename... Args>
|
||||
void
|
||||
invoke_exit_gotcha(const exit_gotcha::gotcha_data& _data, FuncT _func, Args... _args)
|
||||
{
|
||||
threading::clear_callbacks();
|
||||
|
||||
if(get_state() < State::Finalized)
|
||||
{
|
||||
if(config::settings_are_configured())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(0, "finalizing %s before calling %s(%s)...\n",
|
||||
get_exe_name().c_str(), _data.tool_id.c_str(),
|
||||
JOIN(", ", _args...).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(0, "finalizing %s before calling %s(%s)...\n",
|
||||
get_exe_name().c_str(), _data.tool_id.c_str(),
|
||||
JOIN(", ", _args...).c_str());
|
||||
}
|
||||
|
||||
rocprofsys_finalize();
|
||||
}
|
||||
|
||||
if(config::settings_are_configured())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(0, "calling %s(%s) in %s...\n", _data.tool_id.c_str(),
|
||||
JOIN(", ", _args...).c_str(), get_exe_name().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(0, "calling %s(%s) in %s...\n", _data.tool_id.c_str(),
|
||||
JOIN(", ", _args...).c_str(), get_exe_name().c_str());
|
||||
}
|
||||
|
||||
if(_exit_info.is_known && _exit_info.exit_code != 0)
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(0, "%s exiting with non-zero exit code: %i...\n",
|
||||
get_exe_name().c_str(), _exit_info.exit_code);
|
||||
}
|
||||
|
||||
(*_func)(_args...);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// exit
|
||||
// quick_exit
|
||||
void
|
||||
exit_gotcha::operator()(const gotcha_data& _data, exit_func_t _func, int _ec) const
|
||||
{
|
||||
_exit_info = { true, _data.tool_id.find("quick") != std::string::npos, _ec };
|
||||
invoke_exit_gotcha(_data, _func, _ec);
|
||||
}
|
||||
|
||||
// abort
|
||||
void
|
||||
exit_gotcha::operator()(const gotcha_data& _data, abort_func_t _func) const
|
||||
{
|
||||
_exit_info = { true, false, SIGABRT };
|
||||
invoke_exit_gotcha(_data, _func);
|
||||
}
|
||||
|
||||
exit_gotcha::exit_info
|
||||
exit_gotcha::get_exit_info()
|
||||
{
|
||||
return _exit_info;
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <timemory/components/base.hpp>
|
||||
#include <timemory/components/gotcha/backends.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct exit_gotcha : tim::component::base<exit_gotcha, void>
|
||||
{
|
||||
static constexpr size_t gotcha_capacity = 4;
|
||||
|
||||
using gotcha_data = tim::component::gotcha_data;
|
||||
using exit_func_t = void (*)(int);
|
||||
using abort_func_t = void (*)();
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(exit_gotcha)
|
||||
|
||||
// string id for component
|
||||
static std::string label() { return "exit_gotcha"; }
|
||||
|
||||
// generate the gotcha wrappers
|
||||
static void configure();
|
||||
static void shutdown();
|
||||
|
||||
static inline void start() {}
|
||||
static inline void stop() {}
|
||||
|
||||
// exit / _Exit / quick_exit
|
||||
void operator()(const gotcha_data&, exit_func_t, int) const;
|
||||
// abort
|
||||
void operator()(const gotcha_data&, abort_func_t) const;
|
||||
|
||||
struct exit_info
|
||||
{
|
||||
bool is_known = false;
|
||||
bool is_quick = false;
|
||||
int exit_code = EXIT_SUCCESS;
|
||||
};
|
||||
static exit_info get_exit_info();
|
||||
};
|
||||
} // namespace component
|
||||
|
||||
using exit_gotcha_t = tim::component::gotcha<component::exit_gotcha::gotcha_capacity,
|
||||
std::tuple<>, component::exit_gotcha>;
|
||||
} // namespace rocprofsys
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "api.hpp"
|
||||
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "core/perfetto_fwd.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/components/fork_gotcha.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/sampling.hpp"
|
||||
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/process/process.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// these are used to prevent handlers from executing multiple times
|
||||
bool prefork_lock = false;
|
||||
bool postfork_parent_lock = false;
|
||||
bool postfork_child_lock = false;
|
||||
|
||||
// this does a quick exit (no cleanup) on child processes
|
||||
// because perfetto has a tendency to access memory it
|
||||
// shouldn't during cleanup
|
||||
void
|
||||
child_exit(int _ec, void*)
|
||||
{
|
||||
std::quick_exit(_ec);
|
||||
}
|
||||
|
||||
void
|
||||
prefork_setup()
|
||||
{
|
||||
if(prefork_lock) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
|
||||
if(get_state() < State::Active && !config::settings_are_configured())
|
||||
rocprofsys_init_library_hidden();
|
||||
|
||||
tim::set_env("ROCPROFSYS_PRELOAD", "0", 1);
|
||||
tim::set_env("ROCPROFSYS_ROOT_PROCESS", process::get_id(), 0);
|
||||
rocprofsys_reset_preload_hidden();
|
||||
ROCPROFSYS_BASIC_VERBOSE(0, "fork() called on PID %i (rank: %i), TID %li\n",
|
||||
process::get_id(), dmp::rank(), threading::get_id());
|
||||
ROCPROFSYS_BASIC_DEBUG(
|
||||
"Warning! Calling fork() within an OpenMPI application using libfabric "
|
||||
"may result is segmentation fault\n");
|
||||
TIMEMORY_CONDITIONAL_DEMANGLED_BACKTRACE(get_debug_env(), 16);
|
||||
|
||||
if(config::get_use_sampling()) sampling::block_samples();
|
||||
|
||||
rocprofsys::categories::disable_categories(config::get_enabled_categories());
|
||||
|
||||
// prevent re-entry until post-fork routines have been called
|
||||
prefork_lock = true;
|
||||
postfork_parent_lock = false;
|
||||
postfork_child_lock = false;
|
||||
}
|
||||
|
||||
void
|
||||
postfork_parent()
|
||||
{
|
||||
if(postfork_parent_lock) return;
|
||||
|
||||
rocprofsys::categories::enable_categories(config::get_enabled_categories());
|
||||
|
||||
if(config::get_use_sampling()) sampling::unblock_samples();
|
||||
|
||||
// prevent re-entry until prefork has been called
|
||||
postfork_parent_lock = true;
|
||||
prefork_lock = false;
|
||||
}
|
||||
|
||||
void
|
||||
postfork_child()
|
||||
{
|
||||
if(postfork_child_lock) return;
|
||||
|
||||
ROCPROFSYS_REQUIRE(is_child_process())
|
||||
<< "Error! child process " << process::get_id()
|
||||
<< " believes it is the root process " << get_root_process_id() << "\n";
|
||||
|
||||
settings::enabled() = false;
|
||||
settings::verbose() = -127;
|
||||
settings::debug() = false;
|
||||
rocprofsys::sampling::shutdown();
|
||||
rocprofsys::categories::shutdown();
|
||||
set_thread_state(::rocprofsys::ThreadState::Disabled);
|
||||
|
||||
rocprofsys::get_perfetto_session(process::get_parent_id()).release();
|
||||
|
||||
// register these exit handlers to avoid cleaning up resources
|
||||
on_exit(&child_exit, nullptr);
|
||||
std::atexit([]() { child_exit(EXIT_SUCCESS, nullptr); });
|
||||
|
||||
// prevent re-entry until prefork has been called
|
||||
postfork_child_lock = true;
|
||||
prefork_lock = false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
fork_gotcha::configure()
|
||||
{
|
||||
fork_gotcha_t::get_initializer() = []() {
|
||||
TIMEMORY_C_GOTCHA(fork_gotcha_t, 0, fork);
|
||||
};
|
||||
|
||||
// registering the pthread_atfork and gotcha means that we might execute twice
|
||||
// handlers twice, hence the locks
|
||||
pthread_atfork(&prefork_setup, &postfork_parent, &postfork_child);
|
||||
}
|
||||
|
||||
pid_t
|
||||
fork_gotcha::operator()(const gotcha_data_t&, pid_t (*_real_fork)()) const
|
||||
{
|
||||
prefork_setup();
|
||||
|
||||
auto _pid = (*_real_fork)();
|
||||
|
||||
if(_pid != 0)
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(0, "fork() called on PID %i created PID %i\n", getpid(),
|
||||
_pid);
|
||||
|
||||
postfork_parent();
|
||||
}
|
||||
else
|
||||
{
|
||||
postfork_child();
|
||||
}
|
||||
|
||||
if(!settings::use_output_suffix())
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(
|
||||
0, "Application which make calls to fork() should enable using an process "
|
||||
"identifier output suffix (i.e. set ROCPROFSYS_USE_PID=ON)\n");
|
||||
}
|
||||
|
||||
return _pid;
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
// this is used to wrap fork()
|
||||
struct fork_gotcha : comp::base<fork_gotcha, void>
|
||||
{
|
||||
static constexpr size_t gotcha_capacity = 1;
|
||||
|
||||
using gotcha_data_t = comp::gotcha_data;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(fork_gotcha)
|
||||
|
||||
// string id for component
|
||||
static std::string label() { return "fork_gotcha"; }
|
||||
|
||||
// generate the gotcha wrappers
|
||||
static void configure();
|
||||
|
||||
// this will get called right before fork
|
||||
pid_t operator()(const gotcha_data_t&, pid_t (*)()) const;
|
||||
|
||||
// silence SFINAE disabled for rocprofsys::fork_gotcha warnings
|
||||
static inline void start() {}
|
||||
static inline void stop() {}
|
||||
};
|
||||
} // namespace component
|
||||
|
||||
using fork_gotcha_t = comp::gotcha<component::fork_gotcha::gotcha_capacity, std::tuple<>,
|
||||
component::fork_gotcha>;
|
||||
} // namespace rocprofsys
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/mpi_gotcha.hpp"
|
||||
#include "api.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/mproc.hpp"
|
||||
#include "library/components/category_region.hpp"
|
||||
#include "library/components/comm_data.hpp"
|
||||
|
||||
#include <timemory/backends/mpi.hpp>
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/signals/signal_mask.hpp>
|
||||
#include <timemory/utility/locking.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using mpip_bundle_t =
|
||||
tim::component_tuple<category_region<category::mpi>, comp::comm_data>;
|
||||
|
||||
struct comm_rank_data
|
||||
{
|
||||
int rank = -1;
|
||||
int size = -1;
|
||||
uintptr_t comm = mpi_gotcha::null_comm();
|
||||
|
||||
auto updated() const
|
||||
{
|
||||
return comm != mpi_gotcha::null_comm() && rank >= 0 && size > 0;
|
||||
};
|
||||
|
||||
friend bool operator==(const comm_rank_data& _lhs, const comm_rank_data& _rhs)
|
||||
{
|
||||
auto _lupd = _lhs.updated();
|
||||
auto _rupd = _rhs.updated();
|
||||
return std::tie(_lupd, _lhs.rank, _lhs.size, _lhs.comm) ==
|
||||
std::tie(_rupd, _rhs.rank, _rhs.size, _rhs.comm);
|
||||
}
|
||||
|
||||
friend bool operator!=(const comm_rank_data& _lhs, const comm_rank_data& _rhs)
|
||||
{
|
||||
return !(_lhs == _rhs);
|
||||
}
|
||||
|
||||
friend bool operator>(const comm_rank_data& _lhs, const comm_rank_data& _rhs)
|
||||
{
|
||||
ROCPROFSYS_CI_THROW(!_lhs.updated() && !_rhs.updated(),
|
||||
"Error! comparing rank data that is not updated");
|
||||
|
||||
if(_lhs.updated() && !_rhs.updated()) return true;
|
||||
if(!_lhs.updated() && _rhs.updated()) return false;
|
||||
|
||||
if(_lhs.size != _rhs.size) return _lhs.size > _rhs.size;
|
||||
if(_lhs.rank != _rhs.rank) return _lhs.rank > _rhs.rank;
|
||||
|
||||
// lesser comm is greater
|
||||
return _lhs.comm < _rhs.comm;
|
||||
}
|
||||
|
||||
friend bool operator<(const comm_rank_data& _lhs, const comm_rank_data& _rhs)
|
||||
{
|
||||
return (_lhs != _rhs && !(_lhs > _rhs));
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t mpip_index = std::numeric_limits<uint64_t>::max();
|
||||
auto last_comm_record = comm_rank_data{};
|
||||
auto mproc_comm_record = comm_rank_data{};
|
||||
auto mpi_comm_records = std::map<uintptr_t, comm_rank_data>{};
|
||||
|
||||
using tim::auto_lock_t;
|
||||
using tim::type_mutex;
|
||||
|
||||
#if defined(TIMEMORY_USE_MPI)
|
||||
int
|
||||
rocprofsys_mpi_copy(MPI_Comm, int, void*, void*, void*, int*)
|
||||
{
|
||||
return MPI_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
rocprofsys_mpi_fini(MPI_Comm, int, void*, void*)
|
||||
{
|
||||
ROCPROFSYS_DEBUG("MPI Comm attribute finalize\n");
|
||||
auto _blocked = get_sampling_signals();
|
||||
if(!_blocked.empty())
|
||||
tim::signals::block_signals(_blocked, tim::signals::sigmask_scope::process);
|
||||
if(mpip_index != std::numeric_limits<uint64_t>::max())
|
||||
comp::deactivate_mpip<mpip_bundle_t, project::rocprofsys>(mpip_index);
|
||||
if(is_root_process()) rocprofsys_finalize_hidden();
|
||||
return MPI_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
// this ensures rocprofsys_finalize is called before MPI_Finalize
|
||||
void
|
||||
rocprofsys_mpi_set_attr()
|
||||
{
|
||||
#if defined(TIMEMORY_USE_MPI)
|
||||
auto _blocked = get_sampling_signals();
|
||||
if(!_blocked.empty())
|
||||
tim::signals::block_signals(_blocked, tim::signals::sigmask_scope::process);
|
||||
|
||||
int _comm_key = -1;
|
||||
if(PMPI_Comm_create_keyval(&rocprofsys_mpi_copy, &rocprofsys_mpi_fini, &_comm_key,
|
||||
nullptr) == MPI_SUCCESS)
|
||||
PMPI_Comm_set_attr(MPI_COMM_SELF, _comm_key, nullptr);
|
||||
|
||||
if(!_blocked.empty())
|
||||
tim::signals::unblock_signals(_blocked, tim::signals::sigmask_scope::process);
|
||||
#endif
|
||||
}
|
||||
|
||||
using strset_t = std::set<std::string>;
|
||||
auto permit_bindings = strset_t{};
|
||||
auto reject_bindings = strset_t{};
|
||||
} // namespace
|
||||
|
||||
void
|
||||
mpi_gotcha::configure()
|
||||
{
|
||||
// don't emit warnings for missing MPI functions unless debug or verbosity >= 3
|
||||
if(get_verbose_env() < 3 && !get_debug_env())
|
||||
{
|
||||
for(size_t i = 0; i < mpi_gotcha_t::capacity(); ++i)
|
||||
{
|
||||
auto* itr = mpi_gotcha_t::at(i);
|
||||
if(itr) itr->verbose = -1;
|
||||
}
|
||||
}
|
||||
|
||||
mpi_gotcha_t::get_initializer() = []() {
|
||||
mpi_gotcha_t::template configure<0, int, int*, char***>("MPI_Init");
|
||||
mpi_gotcha_t::template configure<1, int, int*, char***, int, int*>(
|
||||
"MPI_Init_thread");
|
||||
mpi_gotcha_t::template configure<2, int>("MPI_Finalize");
|
||||
reject_bindings.emplace("MPI_Init");
|
||||
reject_bindings.emplace("MPI_Init_thread");
|
||||
reject_bindings.emplace("MPI_Finalize");
|
||||
#if defined(ROCPROFSYS_USE_MPI_HEADERS) && ROCPROFSYS_USE_MPI_HEADERS > 0
|
||||
mpi_gotcha_t::template configure<3, int, comm_t, int*>("MPI_Comm_rank");
|
||||
mpi_gotcha_t::template configure<4, int, comm_t, int*>("MPI_Comm_size");
|
||||
reject_bindings.emplace("MPI_Comm_rank");
|
||||
reject_bindings.emplace("MPI_Comm_size");
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
mpi_gotcha::shutdown()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
bool
|
||||
mpi_gotcha::update()
|
||||
{
|
||||
auto_lock_t _lk{ type_mutex<mpi_gotcha>(), std::defer_lock };
|
||||
if(!_lk.owns_lock()) _lk.lock();
|
||||
|
||||
comm_rank_data _rank_data = mproc_comm_record;
|
||||
for(const auto& itr : mpi_comm_records)
|
||||
{
|
||||
// skip null comms
|
||||
if(itr.first == null_comm()) continue;
|
||||
// if currently have null comm, replace
|
||||
else if(_rank_data.comm == null_comm())
|
||||
_rank_data = itr.second;
|
||||
// if
|
||||
else if(itr.second > _rank_data)
|
||||
_rank_data = itr.second;
|
||||
}
|
||||
|
||||
if(_rank_data.updated() && _rank_data != last_comm_record)
|
||||
{
|
||||
auto _rank = _rank_data.rank;
|
||||
auto _size = _rank_data.size;
|
||||
|
||||
tim::mpi::set_rank(_rank);
|
||||
tim::mpi::set_size(_size);
|
||||
tim::settings::default_process_suffix() = _rank;
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE(0, "[pid=%i] MPI rank: %i (%i), MPI size: %i (%i)\n",
|
||||
process::get_id(), tim::mpi::rank(), _rank,
|
||||
tim::mpi::size(), _size);
|
||||
last_comm_record = _rank_data;
|
||||
config::get_use_pid() = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
mpi_gotcha::disable_comm_intercept()
|
||||
{
|
||||
#if defined(ROCPROFSYS_USE_MPI_HEADERS) && ROCPROFSYS_USE_MPI_HEADERS > 0
|
||||
mpi_gotcha_t::revert<3>();
|
||||
mpi_gotcha_t::revert<4>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
mpi_gotcha::audit(const gotcha_data_t& _data, audit::incoming, int*, char***)
|
||||
{
|
||||
ROCPROFSYS_BASIC_DEBUG_F("%s(int*, char***)\n", _data.tool_id.c_str());
|
||||
|
||||
rocprofsys_push_trace_hidden(_data.tool_id.c_str());
|
||||
#if !defined(TIMEMORY_USE_MPI) && defined(TIMEMORY_USE_MPI_HEADERS)
|
||||
tim::mpi::is_initialized_callback() = []() { return true; };
|
||||
tim::mpi::is_finalized() = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
mpi_gotcha::audit(const gotcha_data_t& _data, audit::incoming, int*, char***, int, int*)
|
||||
{
|
||||
ROCPROFSYS_BASIC_DEBUG_F("%s(int*, char***, int, int*)\n", _data.tool_id.c_str());
|
||||
|
||||
rocprofsys_push_trace_hidden(_data.tool_id.c_str());
|
||||
#if !defined(TIMEMORY_USE_MPI) && defined(TIMEMORY_USE_MPI_HEADERS)
|
||||
tim::mpi::is_initialized_callback() = []() { return true; };
|
||||
tim::mpi::is_finalized() = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
mpi_gotcha::audit(const gotcha_data_t& _data, audit::incoming)
|
||||
{
|
||||
ROCPROFSYS_BASIC_DEBUG_F("%s()\n", _data.tool_id.c_str());
|
||||
|
||||
auto _blocked = get_sampling_signals();
|
||||
if(!_blocked.empty())
|
||||
tim::signals::block_signals(_blocked, tim::signals::sigmask_scope::process);
|
||||
|
||||
if(mpip_index != std::numeric_limits<uint64_t>::max())
|
||||
comp::deactivate_mpip<mpip_bundle_t, project::rocprofsys>(mpip_index);
|
||||
|
||||
#if !defined(TIMEMORY_USE_MPI) && defined(TIMEMORY_USE_MPI_HEADERS)
|
||||
tim::mpi::is_initialized_callback() = []() { return false; };
|
||||
tim::mpi::is_finalized() = true;
|
||||
#else
|
||||
if(is_root_process() && rocprofsys::get_state() < rocprofsys::State::Finalized)
|
||||
rocprofsys_finalize_hidden();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
mpi_gotcha::audit(const gotcha_data_t& _data, audit::incoming, comm_t _comm, int* _val)
|
||||
{
|
||||
ROCPROFSYS_BASIC_DEBUG_F("%s()\n", _data.tool_id.c_str());
|
||||
|
||||
rocprofsys_push_trace_hidden(_data.tool_id.c_str());
|
||||
if(_data.tool_id == "MPI_Comm_rank")
|
||||
{
|
||||
m_comm_val = (uintptr_t) _comm; // NOLINT
|
||||
m_rank_ptr = _val;
|
||||
}
|
||||
else if(_data.tool_id == "MPI_Comm_size")
|
||||
{
|
||||
m_comm_val = (uintptr_t) _comm; // NOLINT
|
||||
m_size_ptr = _val;
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_BASIC_PRINT_F("%s(<comm>, %p) :: unexpected function wrapper\n",
|
||||
_data.tool_id.c_str(), static_cast<void*>(_val));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mpi_gotcha::audit(const gotcha_data_t& _data, audit::outgoing, int _retval)
|
||||
{
|
||||
ROCPROFSYS_BASIC_DEBUG_F("%s() returned %i\n", _data.tool_id.c_str(), (int) _retval);
|
||||
|
||||
if(!settings::use_output_suffix()) settings::use_output_suffix() = true;
|
||||
|
||||
if(_retval == tim::mpi::success_v && _data.tool_id.find("MPI_Init") == 0)
|
||||
{
|
||||
rocprofsys_mpi_set_attr();
|
||||
// rocprof-sys will set this environement variable to true in binary rewrite mode
|
||||
// when it detects MPI. Hides this env variable from the user to avoid this
|
||||
// being activated unwaringly during runtime instrumentation because that
|
||||
// will result in double instrumenting the MPI functions (unless the MPI functions
|
||||
// were excluded via a regex expression)
|
||||
if(get_use_mpip())
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(2, "Activating MPI wrappers...\n");
|
||||
|
||||
// use env vars ROCPROFSYS_MPIP_PERMIT_LIST and ROCPROFSYS_MPIP_REJECT_LIST
|
||||
// to control the gotcha bindings at runtime
|
||||
comp::configure_mpip<mpip_bundle_t, project::rocprofsys>(permit_bindings,
|
||||
reject_bindings);
|
||||
mpip_index = comp::activate_mpip<mpip_bundle_t, project::rocprofsys>();
|
||||
}
|
||||
|
||||
auto_lock_t _lk{ type_mutex<mpi_gotcha>() };
|
||||
if(!mproc_comm_record.updated())
|
||||
{
|
||||
auto _pid = getpid();
|
||||
auto _ppid = getppid();
|
||||
auto _size = mproc::get_concurrent_processes(_ppid).size();
|
||||
if(_size > 0)
|
||||
{
|
||||
mproc_comm_record.comm = _ppid;
|
||||
mproc_comm_record.size = m_size = _size;
|
||||
auto _rank = mproc::get_process_index(_pid, _ppid);
|
||||
if(_rank >= 0) mproc_comm_record.rank = m_rank = _rank;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(_retval == tim::mpi::success_v && _data.tool_id.find("MPI_Comm_") == 0)
|
||||
{
|
||||
auto_lock_t _lk{ type_mutex<mpi_gotcha>() };
|
||||
if(m_comm_val != null_comm())
|
||||
{
|
||||
auto& _comm_entry = mpi_comm_records[m_comm_val];
|
||||
_comm_entry.comm = m_comm_val;
|
||||
|
||||
auto _get_rank = [&]() {
|
||||
return (m_rank_ptr) ? std::max<int>(*m_rank_ptr, m_rank) : m_rank;
|
||||
};
|
||||
|
||||
auto _get_size = [&]() {
|
||||
return (m_size_ptr) ? std::max<int>(*m_size_ptr, m_size)
|
||||
: std::max<int>(m_size, _get_rank() + 1);
|
||||
};
|
||||
|
||||
if(_data.tool_id == "MPI_Comm_rank" || _data.tool_id == "MPI_Comm_size")
|
||||
{
|
||||
_comm_entry.rank = m_rank = std::max<int>(_comm_entry.rank, _get_rank());
|
||||
_comm_entry.size = m_size = std::max<int>(_comm_entry.size, _get_size());
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(
|
||||
0, "%s() returned %i :: unexpected function wrapper\n",
|
||||
_data.tool_id.c_str(), (int) _retval);
|
||||
}
|
||||
|
||||
if(_comm_entry.updated())
|
||||
{
|
||||
static thread_local int _num_updates = 0;
|
||||
static int _disable_after =
|
||||
tim::get_env<int>("ROCPROFSYS_MPI_MAX_COMM_UPDATES", 4);
|
||||
if(_num_updates++ < _disable_after) update();
|
||||
}
|
||||
}
|
||||
}
|
||||
rocprofsys_pop_trace_hidden(_data.tool_id.c_str());
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_INITIALIZE_STORAGE(rocprofsys::component::mpi_gotcha)
|
||||
@@ -0,0 +1,85 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
// this is used to wrap MPI_Init and MPI_Init_thread
|
||||
struct mpi_gotcha : comp::base<mpi_gotcha, void>
|
||||
{
|
||||
using comm_t = tim::mpi::comm_t;
|
||||
using gotcha_data_t = comp::gotcha_data;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(mpi_gotcha)
|
||||
|
||||
// string id for component
|
||||
static std::string label() { return "mpi_gotcha"; }
|
||||
|
||||
// generate the gotcha wrappers
|
||||
static void configure();
|
||||
static void shutdown();
|
||||
|
||||
// called right before MPI_Init with that functions arguments
|
||||
static void audit(const gotcha_data_t& _data, audit::incoming, int*, char***);
|
||||
|
||||
// called right before MPI_Init_thread with that functions arguments
|
||||
static void audit(const gotcha_data_t& _data, audit::incoming, int*, char***, int,
|
||||
int*);
|
||||
|
||||
// called right before MPI_Finalize
|
||||
static void audit(const gotcha_data_t& _data, audit::incoming);
|
||||
|
||||
// called right before MPI_Comm_{rank,size} with that functions arguments
|
||||
void audit(const gotcha_data_t& _data, audit::incoming, comm_t, int*);
|
||||
|
||||
// called right after MPI_{Init,Init_thread,Comm_rank,Comm_size} with the return value
|
||||
void audit(const gotcha_data_t& _data, audit::outgoing, int _retval);
|
||||
|
||||
// without these you will get a verbosity level 1 warning
|
||||
static void start() {}
|
||||
static void stop() {}
|
||||
|
||||
static bool update();
|
||||
static uintptr_t null_comm() { return std::numeric_limits<uintptr_t>::max(); }
|
||||
static void disable_comm_intercept();
|
||||
|
||||
private:
|
||||
int m_rank = 0;
|
||||
int m_size = 1;
|
||||
int* m_rank_ptr = nullptr;
|
||||
int* m_size_ptr = nullptr;
|
||||
uintptr_t m_comm_val = null_comm();
|
||||
};
|
||||
} // namespace component
|
||||
|
||||
using mpi_gotcha_t =
|
||||
comp::gotcha<5, tim::component_tuple<component::mpi_gotcha>, project::rocprofsys>;
|
||||
} // namespace rocprofsys
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/numa_gotcha.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/components/category_region.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
// int
|
||||
// numa_migrate_pages(int pid, struct bitmask* from, struct bitmask* to);
|
||||
|
||||
// int
|
||||
// numa_move_pages(int pid, unsigned long count, void** pages, const int* nodes, int*
|
||||
// status,
|
||||
// int flags);
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto&
|
||||
get_numa_gotcha()
|
||||
{
|
||||
static auto _v = tim::lightweight_tuple<numa_gotcha_t>{};
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
numa_gotcha::configure()
|
||||
{
|
||||
// don't emit warnings for missing MPI functions unless debug or verbosity >= 3
|
||||
if(get_verbose_env() < 3 && !get_debug_env())
|
||||
{
|
||||
for(size_t i = 0; i < numa_gotcha_t::capacity(); ++i)
|
||||
{
|
||||
auto* itr = numa_gotcha_t::at(i);
|
||||
if(itr) itr->verbose = -1;
|
||||
}
|
||||
}
|
||||
|
||||
numa_gotcha_t::get_initializer() = []() {
|
||||
numa_gotcha_t::configure<0, long, void*, unsigned long, int, const unsigned long*,
|
||||
unsigned long, unsigned>("mbind");
|
||||
numa_gotcha_t::configure<1, long, int, unsigned long, const unsigned long*,
|
||||
const unsigned long*>("migrate_pages");
|
||||
numa_gotcha_t::configure<2, long, int, unsigned long, void**, const int*, int*,
|
||||
int>("move_pages");
|
||||
numa_gotcha_t::configure<3, int, int, struct bitmask*, struct bitmask*>(
|
||||
"numa_migrate_pages");
|
||||
numa_gotcha_t::configure<4, int, int, unsigned long, void**, const int*, int*,
|
||||
int>("numa_move_pages");
|
||||
numa_gotcha_t::configure<5, void*, size_t>("numa_alloc");
|
||||
numa_gotcha_t::configure<6, void*, size_t>("numa_alloc_local");
|
||||
numa_gotcha_t::configure<7, void*, size_t>("numa_alloc_interleaved");
|
||||
numa_gotcha_t::configure<8, void*, size_t, int>("numa_alloc_onnode");
|
||||
numa_gotcha_t::configure<9, void*, void*, size_t, size_t>("numa_realloc");
|
||||
numa_gotcha_t::configure<10, void, void*, size_t>("numa_free");
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::shutdown()
|
||||
{
|
||||
numa_gotcha_t::disable();
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::start()
|
||||
{
|
||||
if(!get_numa_gotcha().get<numa_gotcha_t>()->get_is_running())
|
||||
{
|
||||
configure();
|
||||
get_numa_gotcha().start();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::stop()
|
||||
{
|
||||
// get_numa_gotcha().stop();
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::incoming, void* start,
|
||||
unsigned long len, int mode, const unsigned long* nmask,
|
||||
unsigned long maxnode, unsigned flags)
|
||||
{
|
||||
category_region<category::numa>::start(std::string_view{ _data.tool_id }, "start",
|
||||
start, "len", len, "mode", mode, "nmask",
|
||||
nmask, "maxnode", maxnode, "flags", flags);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::incoming, int pid,
|
||||
unsigned long maxnode, const unsigned long* frommask,
|
||||
const unsigned long* tomask)
|
||||
{
|
||||
category_region<category::numa>::start(std::string_view{ _data.tool_id }, "pid", pid,
|
||||
"maxnode", maxnode, "frommask", frommask,
|
||||
"tomask", tomask);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::incoming, int pid,
|
||||
unsigned long count, void** pages, const int* nodes, int* status,
|
||||
int flags)
|
||||
{
|
||||
category_region<category::numa>::start(std::string_view{ _data.tool_id }, "pid", pid,
|
||||
"count", count, "pages", pages, "nodes", nodes,
|
||||
"status", status, "flags", flags);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::incoming, int pid,
|
||||
struct bitmask* from, struct bitmask* to)
|
||||
{
|
||||
category_region<category::numa>::start(std::string_view{ _data.tool_id }, "pid", pid,
|
||||
"from", JOIN("", from).c_str(), "to",
|
||||
JOIN("", to).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::incoming, size_t _size)
|
||||
{
|
||||
category_region<category::numa>::start(std::string_view{ _data.tool_id }, "size",
|
||||
_size);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::incoming, size_t _size, int _node)
|
||||
{
|
||||
category_region<category::numa>::start(std::string_view{ _data.tool_id }, "size",
|
||||
_size, "node", _node);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::incoming, void* _addr, size_t _size)
|
||||
{
|
||||
category_region<category::numa>::start(std::string_view{ _data.tool_id }, "address",
|
||||
_addr, "size", _size);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::incoming, void* _old_addr,
|
||||
size_t _old_size, size_t _new_size)
|
||||
{
|
||||
category_region<category::numa>::start(std::string_view{ _data.tool_id },
|
||||
"old_address", _old_addr, "old_size",
|
||||
_old_size, "new_size", _new_size);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::outgoing, int ret)
|
||||
{
|
||||
category_region<category::numa>::stop(std::string_view{ _data.tool_id }, "return",
|
||||
ret);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::outgoing, long ret)
|
||||
{
|
||||
category_region<category::numa>::stop(std::string_view{ _data.tool_id }, "return",
|
||||
ret);
|
||||
}
|
||||
|
||||
void
|
||||
numa_gotcha::audit(const gotcha_data& _data, audit::outgoing, void* ret)
|
||||
{
|
||||
category_region<category::numa>::stop(std::string_view{ _data.tool_id }, "return",
|
||||
ret);
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_STORAGE_INITIALIZER(rocprofsys::component::numa_gotcha)
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <timemory/components/base.hpp>
|
||||
#include <timemory/components/gotcha/backends.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct numa_gotcha : tim::component::base<numa_gotcha, void>
|
||||
{
|
||||
static constexpr size_t gotcha_capacity = 11;
|
||||
|
||||
using gotcha_data = tim::component::gotcha_data;
|
||||
using exit_func_t = void (*)(int);
|
||||
using abort_func_t = void (*)();
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(numa_gotcha)
|
||||
|
||||
// string id for component
|
||||
static std::string label() { return "numa_gotcha"; }
|
||||
|
||||
// generate the gotcha wrappers
|
||||
static void configure();
|
||||
static void shutdown();
|
||||
|
||||
static void start();
|
||||
static void stop();
|
||||
|
||||
static void audit(const gotcha_data&, audit::incoming, void* start, unsigned long len,
|
||||
int mode, const unsigned long* nmask, unsigned long maxnode,
|
||||
unsigned flags);
|
||||
static void audit(const gotcha_data&, audit::incoming, int pid, unsigned long maxnode,
|
||||
const unsigned long* frommask, const unsigned long* tomask);
|
||||
static void audit(const gotcha_data&, audit::incoming, int pid, unsigned long count,
|
||||
void** pages, const int* nodes, int* status, int flags);
|
||||
static void audit(const gotcha_data&, audit::incoming, int pid, struct bitmask* from,
|
||||
struct bitmask* to);
|
||||
static void audit(const gotcha_data&, audit::incoming, size_t);
|
||||
static void audit(const gotcha_data&, audit::incoming, size_t, int);
|
||||
static void audit(const gotcha_data&, audit::incoming, void*, size_t);
|
||||
static void audit(const gotcha_data&, audit::incoming, void*, size_t, size_t);
|
||||
static void audit(const gotcha_data&, audit::outgoing, int);
|
||||
static void audit(const gotcha_data&, audit::outgoing, long);
|
||||
static void audit(const gotcha_data&, audit::outgoing, void*);
|
||||
};
|
||||
} // namespace component
|
||||
|
||||
using numa_bundle_t = tim::component_bundle<category::numa, component::numa_gotcha>;
|
||||
using numa_gotcha_t = tim::component::gotcha<component::numa_gotcha::gotcha_capacity,
|
||||
numa_bundle_t, category::numa>;
|
||||
} // namespace rocprofsys
|
||||
+623
@@ -0,0 +1,623 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/pthread_create_gotcha.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/locking.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/causal/delay.hpp"
|
||||
#include "library/components/category_region.hpp"
|
||||
#include "library/components/roctracer.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/sampling.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
#include "library/tracing.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/components/timing/wall_clock.hpp>
|
||||
#include <timemory/hash/types.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/sampling/allocator.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <csignal>
|
||||
#include <ostream>
|
||||
#include <pthread.h>
|
||||
#include <utility>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace sampling
|
||||
{
|
||||
std::set<int>
|
||||
setup();
|
||||
std::set<int>
|
||||
shutdown();
|
||||
} // namespace sampling
|
||||
|
||||
namespace component
|
||||
{
|
||||
using bundle_t = tim::lightweight_tuple<comp::wall_clock, comp::roctracer_data>;
|
||||
using category_region_t = tim::lightweight_tuple<category_region<category::pthread>>;
|
||||
|
||||
namespace
|
||||
{
|
||||
auto* is_shutdown = new bool{ false }; // intentional data leak
|
||||
auto* bundles = new std::map<int64_t, std::shared_ptr<bundle_t>>{};
|
||||
auto* bundles_mutex = new std::mutex{};
|
||||
auto bundles_dtor = scope::destructor{ []() {
|
||||
pthread_create_gotcha::shutdown();
|
||||
delete bundles;
|
||||
delete bundles_mutex;
|
||||
bundles = nullptr;
|
||||
bundles_mutex = nullptr;
|
||||
} };
|
||||
|
||||
template <typename... Args>
|
||||
inline void
|
||||
start_bundle(bundle_t& _bundle, int64_t _tid, Args&&... _args)
|
||||
{
|
||||
if(!get_use_timemory() && !get_use_perfetto()) return;
|
||||
trait::runtime_enabled<comp::roctracer_data>::set(get_use_roctracer());
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(3, "starting bundle '%s' in thread %li...\n",
|
||||
_bundle.key().c_str(), _tid);
|
||||
if constexpr(sizeof...(Args) > 0)
|
||||
{
|
||||
const char* _name = nullptr;
|
||||
if(tim::get_hash_identifier(_bundle.hash(), _name) && _name != nullptr)
|
||||
{
|
||||
category_region_t{}.audit(quirk::config<quirk::perfetto>{},
|
||||
std::string_view{ _name }, _args...);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tim::consume_parameters(_args...);
|
||||
}
|
||||
if(get_use_timemory())
|
||||
{
|
||||
_bundle.push(_tid);
|
||||
_bundle.start();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void
|
||||
stop_bundle(bundle_t& _bundle, int64_t _tid, Args&&... _args)
|
||||
{
|
||||
if(!get_use_timemory() && !get_use_perfetto()) return;
|
||||
|
||||
auto _main_manager = tim::manager::master_instance();
|
||||
auto _this_manager = tim::manager::instance();
|
||||
if(!_main_manager || !_this_manager || _main_manager->is_finalized() ||
|
||||
_this_manager->is_finalized())
|
||||
return;
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(3, "stopping bundle '%s' in thread %li...\n",
|
||||
_bundle.key().c_str(), _tid);
|
||||
if(get_use_timemory())
|
||||
{
|
||||
auto _wc = *_bundle.get<comp::wall_clock>();
|
||||
_wc.stop();
|
||||
// update roctracer_data
|
||||
_bundle.store(std::plus<double>{}, _wc.get() * _wc.unit());
|
||||
// stop all
|
||||
_bundle.stop();
|
||||
// exclude popping wall-clock
|
||||
_bundle.pop(_tid);
|
||||
}
|
||||
if constexpr(sizeof...(Args) > 0)
|
||||
{
|
||||
const char* _name = nullptr;
|
||||
if(tim::get_hash_identifier(_bundle.hash(), _name) && _name != nullptr)
|
||||
{
|
||||
category_region_t{}.audit(quirk::config<quirk::perfetto>{},
|
||||
std::string_view{ _name }, _args...);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tim::consume_parameters(_args...);
|
||||
}
|
||||
}
|
||||
|
||||
using native_handle_set_t = std::set<pthread_create_gotcha::native_handle_t>;
|
||||
|
||||
auto native_handles = native_handle_set_t{};
|
||||
auto internal_native_handles = native_handle_set_t{};
|
||||
auto native_handles_mutex = locking::atomic_mutex{};
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
pthread_create_gotcha::wrapper::wrapper(routine_t _routine, void* _arg,
|
||||
wrapper_config _config)
|
||||
: m_routine{ _routine }
|
||||
, m_arg{ _arg }
|
||||
, m_config{ std::move(_config) }
|
||||
{}
|
||||
|
||||
void*
|
||||
pthread_create_gotcha::wrapper::operator()() const
|
||||
{
|
||||
using thread_bundle_data_t = thread_data<thread_bundle_t>;
|
||||
|
||||
if(is_shutdown && *is_shutdown)
|
||||
{
|
||||
if(m_config.promise) m_config.promise->set_value();
|
||||
// execute the original function
|
||||
return m_routine(m_arg);
|
||||
}
|
||||
|
||||
push_thread_state(ThreadState::Internal);
|
||||
|
||||
int64_t _tid = -1;
|
||||
void* _ret = nullptr;
|
||||
auto _is_sampling = false;
|
||||
auto _bundle = std::shared_ptr<bundle_t>{};
|
||||
auto _signals = std::set<int>{};
|
||||
auto _coverage = (get_mode() == Mode::Coverage);
|
||||
const auto& _parent_info = thread_info::get(m_config.parent_tid, InternalTID);
|
||||
const auto& _info = thread_info::init(m_config.offset);
|
||||
auto _dtor = [&]() {
|
||||
set_thread_state(ThreadState::Internal);
|
||||
if(_is_sampling)
|
||||
{
|
||||
if(m_config.enable_causal)
|
||||
{
|
||||
causal::sampling::block_signals(_signals);
|
||||
causal::sampling::shutdown();
|
||||
}
|
||||
else if(m_config.enable_sampling)
|
||||
{
|
||||
sampling::block_signals(_signals);
|
||||
sampling::shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
if(_tid >= 0)
|
||||
{
|
||||
auto _active = (get_state() == ::rocprofsys::State::Active &&
|
||||
bundles != nullptr && bundles_mutex != nullptr);
|
||||
if(!_active) return;
|
||||
thread_info::set_stop(comp::wall_clock::record());
|
||||
auto& _thr_bundle = thread_bundle_data_t::instance();
|
||||
if(_thr_bundle && _thr_bundle->get<comp::wall_clock>() &&
|
||||
_thr_bundle->get<comp::wall_clock>()->get_is_running())
|
||||
_thr_bundle->stop();
|
||||
if(_bundle) stop_bundle(*_bundle, _tid);
|
||||
pthread_create_gotcha::shutdown(_tid);
|
||||
ROCPROFSYS_BASIC_VERBOSE(
|
||||
1, "[PID=%i][rank=%i] Thread %s (parent: %s) exited\n", process::get_id(),
|
||||
dmp::rank(), _info->index_data->as_string().c_str(),
|
||||
_parent_info->index_data->as_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
auto _active = (get_state() == ::rocprofsys::State::Active && bundles != nullptr &&
|
||||
bundles_mutex != nullptr);
|
||||
|
||||
if(m_config.offset)
|
||||
{
|
||||
auto _lk = locking::atomic_lock{ native_handles_mutex };
|
||||
internal_native_handles.emplace(pthread_self());
|
||||
}
|
||||
|
||||
if(_active && !_coverage && !m_config.offset)
|
||||
{
|
||||
_tid = _info->index_data->sequent_value;
|
||||
ROCPROFSYS_BASIC_VERBOSE(1, "[PID=%i][rank=%i] Thread %s (parent: %s) created\n",
|
||||
process::get_id(), dmp::rank(),
|
||||
_info->index_data->as_string().c_str(),
|
||||
_parent_info->index_data->as_string().c_str());
|
||||
threading::set_thread_name(TIMEMORY_JOIN(" ", "Thread", _tid).c_str());
|
||||
auto _manager = tim::manager::instance();
|
||||
if(_manager) _manager->initialize();
|
||||
if(!thread_bundle_data_t::get()->at(_tid))
|
||||
{
|
||||
thread_data<thread_bundle_t>::construct(
|
||||
TIMEMORY_JOIN('/', "rocprofsys/process", process::get_id(), "thread",
|
||||
_tid),
|
||||
quirk::config<quirk::auto_start>{});
|
||||
thread_bundle_data_t::get()->at(_tid)->start();
|
||||
}
|
||||
if(bundles && bundles_mutex)
|
||||
{
|
||||
std::unique_lock<std::mutex> _lk{ *bundles_mutex };
|
||||
_bundle = bundles->emplace(_tid, std::make_shared<bundle_t>("start_thread"))
|
||||
.first->second;
|
||||
}
|
||||
if(_bundle) start_bundle(*_bundle, _tid);
|
||||
get_cpu_cid_stack(_tid, m_config.parent_tid);
|
||||
if(m_config.enable_causal)
|
||||
{
|
||||
// children inherit the parent delay data
|
||||
if(_parent_info && _parent_info->index_data)
|
||||
causal::delay::get_local(_tid) =
|
||||
causal::delay::get_local(_parent_info->index_data->sequent_value);
|
||||
_is_sampling = true;
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
_signals = causal::sampling::setup();
|
||||
causal::sampling::unblock_signals();
|
||||
}
|
||||
else if(m_config.enable_sampling)
|
||||
{
|
||||
_is_sampling = true;
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
_signals = sampling::setup();
|
||||
sampling::unblock_signals();
|
||||
}
|
||||
}
|
||||
else if(m_config.offset)
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(
|
||||
2,
|
||||
"[PID=%i][rank=%i] Thread %s (parent: %s) created [started by rocprof-sys]\n",
|
||||
process::get_id(), dmp::rank(), _info->index_data->as_string().c_str(),
|
||||
_parent_info->index_data->as_string().c_str());
|
||||
}
|
||||
|
||||
// notify the wrapper that all internal work is completed
|
||||
if(m_config.promise) m_config.promise->set_value();
|
||||
|
||||
// Internal -> Enabled
|
||||
pop_thread_state();
|
||||
|
||||
push_thread_state(ThreadState::Enabled);
|
||||
|
||||
// execute the original function
|
||||
_ret = m_routine(m_arg);
|
||||
|
||||
if(get_state() < ::rocprofsys::State::Finalized)
|
||||
{
|
||||
pop_thread_state();
|
||||
|
||||
// execute the destructor actions
|
||||
_dtor();
|
||||
|
||||
set_thread_state(ThreadState::Completed);
|
||||
}
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
void*
|
||||
pthread_create_gotcha::wrapper::wrap(void* _arg)
|
||||
{
|
||||
if(_arg == nullptr) return nullptr;
|
||||
|
||||
auto _self = pthread_self();
|
||||
|
||||
// convert the argument
|
||||
wrapper* _wrapper = static_cast<wrapper*>(_arg);
|
||||
|
||||
// store the handle
|
||||
{
|
||||
auto _lk = locking::atomic_lock{ native_handles_mutex };
|
||||
native_handles.emplace(_self);
|
||||
}
|
||||
|
||||
static thread_local auto _remover = scope::destructor{ []() {
|
||||
if(get_state() >= rocprofsys::State::Finalized) return;
|
||||
// remove the handle even if original function aborts
|
||||
auto _lk = locking::atomic_lock{ native_handles_mutex };
|
||||
native_handles.erase(pthread_self());
|
||||
} };
|
||||
(void) _remover;
|
||||
|
||||
// execute the original function
|
||||
void* _ret = (*_wrapper)();
|
||||
|
||||
// remove the handle
|
||||
if(::pthread_equal(_self, pthread_self()) == 0)
|
||||
{
|
||||
auto _lk = locking::atomic_lock{ native_handles_mutex };
|
||||
native_handles.erase(_self);
|
||||
}
|
||||
|
||||
// eliminate memory leak
|
||||
if(_ret != _arg) delete _wrapper;
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const auto shutdown_signal_v = SIGRTMAX - 1;
|
||||
|
||||
size_t shutdown_signals_delivered = 0;
|
||||
|
||||
void
|
||||
pthread_create_gotcha_shutdown_handler(int)
|
||||
{
|
||||
pthread_create_gotcha::shutdown(threading::get_id());
|
||||
++shutdown_signals_delivered;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
pthread_create_gotcha::configure()
|
||||
{
|
||||
pthread_create_gotcha_t::get_initializer() = []() {
|
||||
if(!tim::settings::enabled()) return;
|
||||
pthread_create_gotcha_t::template configure<
|
||||
0, int, pthread_t*, const pthread_attr_t*, void* (*) (void*), void*>(
|
||||
"pthread_create");
|
||||
};
|
||||
|
||||
tim::hash::add_hash_id("start_thread");
|
||||
}
|
||||
|
||||
void
|
||||
pthread_create_gotcha::shutdown()
|
||||
{
|
||||
if(is_shutdown)
|
||||
{
|
||||
if(*is_shutdown) return;
|
||||
*is_shutdown = true;
|
||||
}
|
||||
|
||||
if(!bundles_mutex || !bundles) return;
|
||||
|
||||
unsigned long _ndangling = 0;
|
||||
|
||||
for(const auto& itr : *bundles)
|
||||
{
|
||||
if(itr.second) ++_ndangling;
|
||||
}
|
||||
|
||||
tracing::copy_timemory_hash_ids();
|
||||
|
||||
// enable the signal handler for when the timeout is reached
|
||||
struct sigaction _action = {};
|
||||
struct sigaction _former = {};
|
||||
|
||||
memset(&_action, 0, sizeof(_action));
|
||||
memset(&_former, 0, sizeof(_former));
|
||||
sigemptyset(&_action.sa_mask);
|
||||
sigemptyset(&_former.sa_mask);
|
||||
|
||||
_action.sa_flags = SA_RESTART;
|
||||
_action.sa_handler = pthread_create_gotcha_shutdown_handler;
|
||||
// activate signal handler
|
||||
sigaction(shutdown_signal_v, &_action, &_former);
|
||||
|
||||
size_t _expected_shutdown_signals_delivered = 0;
|
||||
{
|
||||
auto _lk = locking::atomic_lock{ native_handles_mutex };
|
||||
for(auto itr : native_handles)
|
||||
{
|
||||
// skip sending signals to internal threads
|
||||
if(internal_native_handles.count(itr) != 0) continue;
|
||||
if(pthread_equal(pthread_self(), itr) == 0 && pthread_equal(itr, itr) != 0)
|
||||
{
|
||||
::pthread_kill(itr, shutdown_signal_v);
|
||||
++_expected_shutdown_signals_delivered;
|
||||
}
|
||||
}
|
||||
|
||||
auto _nattempt = 0U;
|
||||
constexpr auto nmax_attempt = 20U;
|
||||
while(shutdown_signals_delivered < _expected_shutdown_signals_delivered &&
|
||||
_nattempt++ < nmax_attempt)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{ 50 });
|
||||
}
|
||||
|
||||
ROCPROFSYS_CI_BASIC_FAIL(
|
||||
shutdown_signals_delivered != _expected_shutdown_signals_delivered,
|
||||
"Number of signals delivered (%zu) != expected number of signals delievered "
|
||||
"(%zu)",
|
||||
shutdown_signals_delivered, _expected_shutdown_signals_delivered);
|
||||
}
|
||||
|
||||
// restore existing signal handler
|
||||
sigaction(shutdown_signal_v, &_former, nullptr);
|
||||
|
||||
// subtract the bundles that had signals delivered
|
||||
_ndangling -= shutdown_signals_delivered;
|
||||
|
||||
// stop any remaining dangling bundles on this thread
|
||||
std::unique_lock<std::mutex> _lk{ *bundles_mutex };
|
||||
for(auto itr : *bundles)
|
||||
{
|
||||
if(itr.second)
|
||||
{
|
||||
stop_bundle(*itr.second, itr.first);
|
||||
++_ndangling;
|
||||
}
|
||||
itr.second.reset();
|
||||
}
|
||||
|
||||
bundles->clear();
|
||||
|
||||
if(config::settings_are_configured())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(2 && _ndangling > 0,
|
||||
"[pthread_create_gotcha] cleaned up %lu dangling bundles\n",
|
||||
_ndangling);
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(
|
||||
2 && _ndangling > 0,
|
||||
"[pthread_create_gotcha] cleaned up %lu dangling bundles\n", _ndangling);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pthread_create_gotcha::shutdown(int64_t _tid)
|
||||
{
|
||||
if(_tid == 0) shutdown();
|
||||
|
||||
if(is_shutdown && *is_shutdown) return;
|
||||
|
||||
if(!bundles_mutex || !bundles) return;
|
||||
|
||||
std::unique_lock<std::mutex> _lk{ *bundles_mutex };
|
||||
auto itr = bundles->find(_tid);
|
||||
if(itr != bundles->end())
|
||||
{
|
||||
if(itr->second) stop_bundle(*itr->second, itr->first);
|
||||
itr->second.reset();
|
||||
bundles->erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pthread_create_gotcha::set_data(wrappee_t _v)
|
||||
{
|
||||
m_wrappee = _v;
|
||||
}
|
||||
|
||||
std::set<pthread_create_gotcha::native_handle_t>
|
||||
pthread_create_gotcha::get_native_handles()
|
||||
{
|
||||
auto _v = native_handles;
|
||||
return _v;
|
||||
}
|
||||
|
||||
// pthread_create
|
||||
int
|
||||
pthread_create_gotcha::operator()(pthread_t* thread, const pthread_attr_t* attr,
|
||||
void* (*func)(void*), void* arg) const
|
||||
{
|
||||
auto _tid = utility::get_thread_index();
|
||||
auto _thr_state = get_thread_state();
|
||||
auto _glob_state = get_state();
|
||||
auto _mode = get_mode();
|
||||
auto _disabled = (_thr_state == ThreadState::Disabled);
|
||||
auto _enabled = (_thr_state == ThreadState::Enabled);
|
||||
auto _bundle = std::optional<bundle_t>{};
|
||||
auto _sample_child = sampling_enabled_on_child_threads();
|
||||
auto _active = (_glob_state == ::rocprofsys::State::Active && !_disabled);
|
||||
const auto& _info = thread_info::init(!_active || !_sample_child || _disabled);
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
auto _coverage = (_mode == Mode::Coverage);
|
||||
auto _use_sampling = config::get_use_sampling();
|
||||
auto _use_causal = config::get_use_causal();
|
||||
auto _offset = (!_enabled || !_active || _info->is_offset);
|
||||
auto _use_bundle = (_active && !_coverage && !_offset);
|
||||
auto _enable_sampling =
|
||||
(_use_sampling && _sample_child && _active && !_coverage && !_offset);
|
||||
auto _enable_causal =
|
||||
(_use_causal && _sample_child && _active && !_coverage && !_offset);
|
||||
|
||||
static bool debug_threading_get_id =
|
||||
get_env<bool>(TIMEMORY_SETTINGS_PREFIX "DEBUG_THREADING_GET_ID", false);
|
||||
|
||||
auto _verbose = (debug_threading_get_id) ? 0 : 3;
|
||||
ROCPROFSYS_VERBOSE(
|
||||
_verbose,
|
||||
"Creating new thread :: global_state=%s, thread_state=%s, mode=%s, active=%s, "
|
||||
"coverage=%s, use_causal=%s, use_sampling=%s, sample_children=%s, tid=%li, "
|
||||
"use_bundle=%s, enable_causal=%s, enable_sampling=%s, thread_info=(%s)...\n",
|
||||
std::to_string(_glob_state).c_str(), std::to_string(_thr_state).c_str(),
|
||||
std::to_string(_mode).c_str(), std::to_string(_active).c_str(),
|
||||
std::to_string(_coverage).c_str(), std::to_string(_use_causal).c_str(),
|
||||
std::to_string(_use_sampling).c_str(), std::to_string(_sample_child).c_str(),
|
||||
_tid, std::to_string(_use_bundle).c_str(), std::to_string(_enable_causal).c_str(),
|
||||
std::to_string(_enable_sampling).c_str(), JOIN("", *_info).c_str());
|
||||
|
||||
if(debug_threading_get_id)
|
||||
{
|
||||
timemory_print_demangled_backtrace<8>(std::cerr, std::string{},
|
||||
std::string{ "threading::get_id() [id=" } +
|
||||
std::to_string(_tid) +
|
||||
std::string{ "]" },
|
||||
std::string{ " " }, false);
|
||||
}
|
||||
|
||||
if(_active && !_disabled && !_info->is_offset)
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE(2, "[PID=%i][rank=%i] Starting new thread on %s...\n",
|
||||
process::get_id(), dmp::rank(),
|
||||
_info->index_data->as_string().c_str());
|
||||
}
|
||||
|
||||
// ensure that cpu cid stack exists on the parent thread if active
|
||||
if(_active && !_coverage)
|
||||
{
|
||||
ROCPROFSYS_DEBUG("blocking signals...\n");
|
||||
get_cpu_cid_stack();
|
||||
}
|
||||
|
||||
set_thread_state(ThreadState::Disabled);
|
||||
auto _blocked = get_sampling_signals();
|
||||
auto _promise = (_active) ? std::make_shared<std::promise<void>>() : promise_t{};
|
||||
auto _config =
|
||||
wrapper_config{ _enable_causal, _enable_sampling, _offset, _tid, _promise };
|
||||
auto* _wrap = new wrapper{ func, arg, _config };
|
||||
set_thread_state(ThreadState::Internal);
|
||||
|
||||
// block the signals in entire process
|
||||
if(_enable_sampling && !_blocked.empty())
|
||||
{
|
||||
ROCPROFSYS_DEBUG("blocking signals...\n");
|
||||
tim::signals::block_signals(_blocked, tim::signals::sigmask_scope::process);
|
||||
}
|
||||
|
||||
if(_use_bundle)
|
||||
{
|
||||
_bundle = bundle_t{ "pthread_create" };
|
||||
start_bundle(*_bundle, _info->index_data->sequent_value, audit::incoming{},
|
||||
thread, attr, func, arg);
|
||||
}
|
||||
|
||||
// threads must process their delays before creating a new thread
|
||||
causal::delay::process();
|
||||
|
||||
// create the thread
|
||||
auto _ret = (*m_wrappee)(thread, attr, &wrapper::wrap, static_cast<void*>(_wrap));
|
||||
|
||||
// wait for thread to set promise
|
||||
if(_promise)
|
||||
{
|
||||
ROCPROFSYS_DEBUG("waiting for child to signal it is setup...\n");
|
||||
_promise->get_future().wait_for(std::chrono::milliseconds{ 500 });
|
||||
}
|
||||
|
||||
if(_use_bundle)
|
||||
stop_bundle(*_bundle, _info->index_data->sequent_value, audit::outgoing{}, _ret);
|
||||
|
||||
// unblock the signals in the entire process
|
||||
if(_enable_sampling && !_blocked.empty())
|
||||
{
|
||||
ROCPROFSYS_DEBUG("unblocking signals...\n");
|
||||
tim::signals::unblock_signals(_blocked, tim::signals::sigmask_scope::process);
|
||||
}
|
||||
|
||||
ROCPROFSYS_DEBUG("returning success...\n");
|
||||
return _ret;
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
TIMEMORY_INITIALIZE_STORAGE(component::roctracer_data)
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
struct pthread_gotcha;
|
||||
|
||||
namespace component
|
||||
{
|
||||
struct pthread_create_gotcha : tim::component::base<pthread_create_gotcha, void>
|
||||
{
|
||||
static constexpr size_t gotcha_capacity = 1;
|
||||
|
||||
using routine_t = void* (*) (void*);
|
||||
using wrappee_t = int (*)(pthread_t*, const pthread_attr_t*, routine_t, void*);
|
||||
using promise_t = std::shared_ptr<std::promise<void>>;
|
||||
using native_handle_t = std::thread::native_handle_type;
|
||||
|
||||
struct wrapper_config
|
||||
{
|
||||
bool enable_causal = false;
|
||||
bool enable_sampling = false;
|
||||
bool offset = false;
|
||||
int64_t parent_tid = 0;
|
||||
promise_t promise = {};
|
||||
};
|
||||
|
||||
struct wrapper
|
||||
{
|
||||
wrapper(routine_t _routine, void* _arg, wrapper_config _cfg);
|
||||
void* operator()() const;
|
||||
|
||||
static void* wrap(void* _arg);
|
||||
|
||||
private:
|
||||
routine_t m_routine = nullptr;
|
||||
void* m_arg = nullptr;
|
||||
wrapper_config m_config = {};
|
||||
};
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(pthread_create_gotcha)
|
||||
|
||||
// string id for component
|
||||
static std::string label() { return "pthread_create_gotcha"; }
|
||||
|
||||
// generate the gotcha wrappers
|
||||
static void configure();
|
||||
static void shutdown();
|
||||
static void shutdown(int64_t);
|
||||
|
||||
// pthread_create
|
||||
int operator()(pthread_t* thread, const pthread_attr_t* attr,
|
||||
void* (*start_routine)(void*), void* arg) const;
|
||||
|
||||
void set_data(wrappee_t);
|
||||
|
||||
private:
|
||||
friend struct ::rocprofsys::pthread_gotcha;
|
||||
|
||||
static std::set<native_handle_t> get_native_handles();
|
||||
|
||||
wrappee_t m_wrappee = &pthread_create;
|
||||
};
|
||||
|
||||
using pthread_create_gotcha_t =
|
||||
tim::component::gotcha<pthread_create_gotcha::gotcha_capacity, std::tuple<>,
|
||||
pthread_create_gotcha>;
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/pthread_gotcha.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/components/pthread_create_gotcha.hpp"
|
||||
#include "library/components/pthread_mutex_gotcha.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/utility/macros.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace operation
|
||||
{
|
||||
template <>
|
||||
struct stop<rocprofsys::component::pthread_create_gotcha_t>
|
||||
{
|
||||
using type = rocprofsys::component::pthread_create_gotcha_t;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(stop)
|
||||
|
||||
template <typename... Args>
|
||||
explicit stop(type&, Args&&...)
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(type&, Args&&...)
|
||||
{}
|
||||
};
|
||||
} // namespace operation
|
||||
} // namespace tim
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using bundle_t = tim::lightweight_tuple<component::pthread_create_gotcha_t,
|
||||
component::pthread_mutex_gotcha_t>;
|
||||
|
||||
auto&
|
||||
get_bundle()
|
||||
{
|
||||
static auto _v = std::unique_ptr<bundle_t>{};
|
||||
if(!_v) _v = std::make_unique<bundle_t>("pthread_gotcha");
|
||||
return _v;
|
||||
}
|
||||
|
||||
bool is_configured = false;
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
void
|
||||
pthread_gotcha::configure()
|
||||
{
|
||||
if(!is_configured)
|
||||
{
|
||||
::rocprofsys::component::pthread_create_gotcha::configure();
|
||||
::rocprofsys::component::pthread_mutex_gotcha::configure();
|
||||
is_configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pthread_gotcha::shutdown()
|
||||
{
|
||||
if(is_configured)
|
||||
{
|
||||
::rocprofsys::component::pthread_mutex_gotcha::shutdown();
|
||||
::rocprofsys::component::pthread_create_gotcha::shutdown();
|
||||
is_configured = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pthread_gotcha::start()
|
||||
{
|
||||
configure();
|
||||
get_bundle()->start();
|
||||
}
|
||||
|
||||
void
|
||||
pthread_gotcha::stop()
|
||||
{
|
||||
get_bundle()->stop();
|
||||
}
|
||||
|
||||
std::set<pthread_gotcha::native_handle_t>
|
||||
pthread_gotcha::get_native_handles()
|
||||
{
|
||||
return ::rocprofsys::component::pthread_create_gotcha::get_native_handles();
|
||||
}
|
||||
} // namespace rocprofsys
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
struct pthread_gotcha : tim::component::base<pthread_gotcha, void>
|
||||
{
|
||||
using native_handle_t = std::thread::native_handle_type;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(pthread_gotcha)
|
||||
|
||||
// string id for component
|
||||
static std::string label() { return "pthread_gotcha"; }
|
||||
|
||||
// generate the gotcha wrappers
|
||||
static void configure();
|
||||
static void shutdown();
|
||||
|
||||
static void start();
|
||||
static void stop();
|
||||
|
||||
static std::set<native_handle_t> get_native_handles();
|
||||
};
|
||||
} // namespace rocprofsys
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/pthread_mutex_gotcha.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/components/category_region.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/utility/signals.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <pthread.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
pthread_mutex_gotcha::hash_array_t&
|
||||
pthread_mutex_gotcha::get_hashes()
|
||||
{
|
||||
// theoretically, this private function will NEVER be called until it
|
||||
// is called by a gotcha wrapper, which means the tool ids should be
|
||||
// fully populated. If that fails to be the case for some reason,
|
||||
// we could see weird results.
|
||||
static auto _v = []() {
|
||||
const auto& _data = pthread_mutex_gotcha_t::get_gotcha_data();
|
||||
auto _init = hash_array_t{};
|
||||
auto _skip = std::set<size_t>{};
|
||||
if(!config::get_trace_thread_locks())
|
||||
{
|
||||
for(size_t i = 0; i < 3; ++i)
|
||||
_skip.emplace(i);
|
||||
}
|
||||
if(!config::get_trace_thread_rwlocks())
|
||||
{
|
||||
for(size_t i = 3; i < 8; ++i)
|
||||
_skip.emplace(i);
|
||||
}
|
||||
if(!config::get_trace_thread_spin_locks())
|
||||
{
|
||||
for(size_t i = 9; i < 12; ++i)
|
||||
_skip.emplace(i);
|
||||
}
|
||||
if(!config::get_trace_thread_barriers()) _skip.emplace(8);
|
||||
if(!config::get_trace_thread_join()) _skip.emplace(12);
|
||||
for(size_t i = 0; i < gotcha_capacity; ++i)
|
||||
{
|
||||
auto&& _id = _data.at(i).tool_id;
|
||||
if(!_id.empty())
|
||||
_init.at(i) = tim::add_hash_id(_id.c_str());
|
||||
else
|
||||
{
|
||||
if(_skip.count(i) > 0) continue;
|
||||
ROCPROFSYS_VERBOSE(
|
||||
1,
|
||||
"WARNING!!! pthread_mutex_gotcha tool id at index %zu was empty!\n",
|
||||
i);
|
||||
}
|
||||
ROCPROFSYS_CI_FAIL(
|
||||
_id.empty() || _init.at(i) == 0,
|
||||
"pthread_mutex_gotcha tool id at index %zu has no hash value\n", i);
|
||||
}
|
||||
return _init;
|
||||
}();
|
||||
return _v;
|
||||
}
|
||||
|
||||
void
|
||||
pthread_mutex_gotcha::configure()
|
||||
{
|
||||
pthread_mutex_gotcha_t::get_initializer() = []() {
|
||||
if(!tim::settings::enabled() || get_use_causal()) return;
|
||||
|
||||
if(config::get_trace_thread_locks())
|
||||
{
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<0, int, pthread_mutex_t*>{ "pthread_mutex_lock" });
|
||||
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<1, int, pthread_mutex_t*>{ "pthread_mutex_unlock" });
|
||||
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<2, int, pthread_mutex_t*>{ "pthread_mutex_trylock" });
|
||||
}
|
||||
|
||||
if(config::get_trace_thread_rwlocks())
|
||||
{
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<3, int, pthread_rwlock_t*>{
|
||||
"pthread_rwlock_rdlock" });
|
||||
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<4, int, pthread_rwlock_t*>{
|
||||
"pthread_rwlock_wrlock" });
|
||||
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<5, int, pthread_rwlock_t*>{
|
||||
"pthread_rwlock_tryrdlock" });
|
||||
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<6, int, pthread_rwlock_t*>{
|
||||
"pthread_rwlock_trywrlock" });
|
||||
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<7, int, pthread_rwlock_t*>{
|
||||
"pthread_rwlock_unlock" });
|
||||
}
|
||||
|
||||
if(config::get_trace_thread_barriers())
|
||||
{
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<8, int, pthread_barrier_t*>{
|
||||
"pthread_barrier_wait" });
|
||||
}
|
||||
|
||||
if(config::get_trace_thread_spin_locks())
|
||||
{
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<9, int, pthread_spinlock_t*>{ "pthread_spin_lock" });
|
||||
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<10, int, pthread_spinlock_t*>{
|
||||
"pthread_spin_trylock" });
|
||||
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<11, int, pthread_spinlock_t*>{
|
||||
"pthread_spin_unlock" });
|
||||
}
|
||||
|
||||
if(config::get_trace_thread_join())
|
||||
{
|
||||
pthread_mutex_gotcha_t::configure(
|
||||
comp::gotcha_config<12, int, pthread_t, void**>{ "pthread_join" });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
pthread_mutex_gotcha::shutdown()
|
||||
{
|
||||
pthread_mutex_gotcha_t::disable();
|
||||
}
|
||||
|
||||
pthread_mutex_gotcha::pthread_mutex_gotcha(const gotcha_data_t& _data)
|
||||
: m_data{ &_data }
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
auto
|
||||
pthread_mutex_gotcha::operator()(uintptr_t&&, int (*_callee)(Args...),
|
||||
Args... _args) const
|
||||
{
|
||||
using bundle_t = category_region<category::pthread>;
|
||||
|
||||
if(is_disabled() || m_protect)
|
||||
{
|
||||
if(!_callee)
|
||||
{
|
||||
if(m_data)
|
||||
{
|
||||
ROCPROFSYS_PRINT("Warning! nullptr to %s\n", m_data->tool_id.c_str());
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
return (*_callee)(_args...);
|
||||
}
|
||||
|
||||
struct local_dtor
|
||||
{
|
||||
explicit local_dtor(bool& _v)
|
||||
: _protect{ _v }
|
||||
{}
|
||||
~local_dtor() { _protect = false; }
|
||||
bool& _protect;
|
||||
} _dtor{ m_protect = true };
|
||||
|
||||
bundle_t::audit(std::string_view{ m_data->tool_id }, audit::incoming{}, _args...);
|
||||
auto _ret = (*_callee)(_args...);
|
||||
bundle_t::audit(std::string_view{ m_data->tool_id }, audit::outgoing{}, _ret);
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
int
|
||||
pthread_mutex_gotcha::operator()(int (*_callee)(pthread_mutex_t*),
|
||||
pthread_mutex_t* _mutex) const
|
||||
{
|
||||
if(get_state() != ::rocprofsys::State::Active || m_protect) return (*_callee)(_mutex);
|
||||
return (*this)(reinterpret_cast<uintptr_t>(_mutex), _callee, _mutex);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_mutex_gotcha::operator()(int (*_callee)(pthread_spinlock_t*),
|
||||
pthread_spinlock_t* _lock) const
|
||||
{
|
||||
if(get_state() != ::rocprofsys::State::Active || m_protect) return (*_callee)(_lock);
|
||||
return (*this)(reinterpret_cast<uintptr_t>(_lock), _callee, _lock);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_mutex_gotcha::operator()(int (*_callee)(pthread_rwlock_t*),
|
||||
pthread_rwlock_t* _lock) const
|
||||
{
|
||||
if(get_state() != ::rocprofsys::State::Active || m_protect) return (*_callee)(_lock);
|
||||
return (*this)(reinterpret_cast<uintptr_t>(_lock), _callee, _lock);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_mutex_gotcha::operator()(int (*_callee)(pthread_barrier_t*),
|
||||
pthread_barrier_t* _barrier) const
|
||||
{
|
||||
if(get_state() != ::rocprofsys::State::Active || m_protect)
|
||||
return (*_callee)(_barrier);
|
||||
return (*this)(reinterpret_cast<uintptr_t>(_barrier), _callee, _barrier);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_mutex_gotcha::operator()(int (*_callee)(pthread_t, void**), pthread_t _thr,
|
||||
void** _tinfo) const
|
||||
{
|
||||
if(get_state() != ::rocprofsys::State::Active || m_protect)
|
||||
return (*_callee)(_thr, _tinfo);
|
||||
return (*this)(static_cast<uintptr_t>(threading::get_id()), _callee, _thr, _tinfo);
|
||||
}
|
||||
|
||||
bool
|
||||
pthread_mutex_gotcha::is_disabled()
|
||||
{
|
||||
static thread_local const auto& _info = thread_info::get();
|
||||
return (!_info || _info->is_offset || get_state() != ::rocprofsys::State::Active ||
|
||||
get_thread_state() != ThreadState::Enabled);
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace policy
|
||||
{
|
||||
template <size_t N>
|
||||
pthread_mutex_gotcha&
|
||||
static_data<pthread_mutex_gotcha, pthread_mutex_gotcha_t>::operator()(
|
||||
std::integral_constant<size_t, N>, const component::gotcha_data& _data) const
|
||||
{
|
||||
using thread_data_t =
|
||||
rocprofsys::thread_data<pthread_mutex_gotcha, std::integral_constant<size_t, N>>;
|
||||
static thread_local auto& _v =
|
||||
thread_data_t::instance(rocprofsys::construct_on_thread{}, _data);
|
||||
return *_v;
|
||||
}
|
||||
} // namespace policy
|
||||
} // namespace tim
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <timemory/components/gotcha/backends.hpp>
|
||||
#include <timemory/mpl/macros.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
// this is used to wrap pthread_mutex()
|
||||
struct pthread_mutex_gotcha : comp::base<pthread_mutex_gotcha, void>
|
||||
{
|
||||
static constexpr size_t gotcha_capacity = 13;
|
||||
using hash_array_t = std::array<size_t, gotcha_capacity>;
|
||||
using gotcha_data_t = comp::gotcha_data;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(pthread_mutex_gotcha)
|
||||
|
||||
explicit pthread_mutex_gotcha(const gotcha_data_t&);
|
||||
|
||||
// string id for component
|
||||
static std::string label() { return "pthread_mutex_gotcha"; }
|
||||
|
||||
// generate the gotcha wrappers
|
||||
static void configure();
|
||||
static void shutdown();
|
||||
|
||||
int operator()(int (*)(pthread_mutex_t*), pthread_mutex_t*) const;
|
||||
int operator()(int (*)(pthread_spinlock_t*), pthread_spinlock_t*) const;
|
||||
int operator()(int (*)(pthread_rwlock_t*), pthread_rwlock_t*) const;
|
||||
int operator()(int (*)(pthread_barrier_t*), pthread_barrier_t*) const;
|
||||
int operator()(int (*)(pthread_t, void**), pthread_t, void**) const;
|
||||
|
||||
private:
|
||||
static bool is_disabled();
|
||||
static hash_array_t& get_hashes();
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(uintptr_t&&, int (*)(Args...), Args...) const;
|
||||
|
||||
mutable bool m_protect = false;
|
||||
const gotcha_data_t* m_data = nullptr;
|
||||
};
|
||||
|
||||
using pthread_mutex_gotcha_t = comp::gotcha<pthread_mutex_gotcha::gotcha_capacity,
|
||||
std::tuple<>, pthread_mutex_gotcha>;
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(fast_gotcha, component::pthread_mutex_gotcha_t,
|
||||
true_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(static_data, component::pthread_mutex_gotcha_t,
|
||||
true_type)
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace policy
|
||||
{
|
||||
using pthread_mutex_gotcha = ::rocprofsys::component::pthread_mutex_gotcha;
|
||||
using pthread_mutex_gotcha_t = ::rocprofsys::component::pthread_mutex_gotcha_t;
|
||||
|
||||
template <>
|
||||
struct static_data<pthread_mutex_gotcha, pthread_mutex_gotcha_t> : std::true_type
|
||||
{
|
||||
template <size_t N>
|
||||
pthread_mutex_gotcha& operator()(std::integral_constant<size_t, N>,
|
||||
const component::gotcha_data& _data) const;
|
||||
};
|
||||
} // namespace policy
|
||||
} // namespace tim
|
||||
@@ -0,0 +1,195 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/rcclp.hpp"
|
||||
#include "library/rcclp.hpp"
|
||||
|
||||
#include <timemory/manager.hpp>
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& _os, const ncclUniqueId& _v)
|
||||
{
|
||||
for(auto itr : _v.internal)
|
||||
_os << itr;
|
||||
return _os;
|
||||
}
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
uint64_t
|
||||
activate_rcclp()
|
||||
{
|
||||
using handle_t = tim::component::rcclp_handle;
|
||||
|
||||
static auto _handle = std::shared_ptr<handle_t>{};
|
||||
|
||||
if(!_handle.get())
|
||||
{
|
||||
_handle = std::make_shared<handle_t>();
|
||||
_handle->start();
|
||||
|
||||
auto cleanup_functor = [=]() {
|
||||
if(_handle)
|
||||
{
|
||||
_handle->stop();
|
||||
_handle.reset();
|
||||
}
|
||||
};
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "timemory-rcclp-" << demangle<rccl_toolset_t>() << "-"
|
||||
<< demangle<category::rocm_rccl>();
|
||||
tim::manager::instance()->add_cleanup(ss.str(), cleanup_functor);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
uint64_t
|
||||
deactivate_rcclp(uint64_t id)
|
||||
{
|
||||
if(id > 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "timemory-rcclp-" << demangle<rccl_toolset_t>() << "-"
|
||||
<< demangle<category::rocm_rccl>();
|
||||
tim::manager::instance()->cleanup(ss.str());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
void
|
||||
configure_rcclp(const std::set<std::string>& permit, const std::set<std::string>& reject)
|
||||
{
|
||||
static bool is_initialized = false;
|
||||
if(!is_initialized)
|
||||
{
|
||||
// generate the gotcha wrappers
|
||||
rcclp_gotcha_t::get_initializer() = []() {
|
||||
// TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 0, ncclGetVersion);
|
||||
// TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 1, ncclGetUniqueId);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 2, ncclCommInitRank);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 3, ncclCommInitAll);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 4, ncclCommDestroy);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 5, ncclCommCount);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 6, ncclCommCuDevice);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 7, ncclCommUserRank);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 8, ncclReduce);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 9, ncclBcast);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 10, ncclBroadcast);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 11, ncclAllReduce);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 12, ncclReduceScatter);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 13, ncclAllGather);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 14, ncclGroupStart);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 15, ncclGroupEnd);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 16, ncclSend);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 17, ncclRecv);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 18, ncclGather);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 19, ncclScatter);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 20, ncclAllToAll);
|
||||
TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 21, ncclAllToAllv);
|
||||
// TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 22, ncclRedOpCreatePreMulSum);
|
||||
// TIMEMORY_C_GOTCHA(rcclp_gotcha_t, 23, ncclRedOpDestroy);
|
||||
};
|
||||
|
||||
// provide environment variable for suppressing wrappers
|
||||
rcclp_gotcha_t::get_reject_list() = [reject]() {
|
||||
auto _reject = reject;
|
||||
// check environment
|
||||
auto reject_list =
|
||||
tim::get_env<std::string>("ROCPROFSYS_RCCLP_REJECT_LIST", "");
|
||||
// add environment setting
|
||||
for(const auto& itr : tim::delimit(reject_list))
|
||||
_reject.insert(itr);
|
||||
return _reject;
|
||||
};
|
||||
|
||||
// provide environment variable for selecting wrappers
|
||||
rcclp_gotcha_t::get_permit_list() = [permit]() {
|
||||
auto _permit = permit;
|
||||
// check environment
|
||||
auto permit_list =
|
||||
tim::get_env<std::string>("ROCPROFSYS_RCCLP_PERMIT_LIST", "");
|
||||
// add environment setting
|
||||
for(const auto& itr : tim::delimit(permit_list))
|
||||
_permit.insert(itr);
|
||||
return _permit;
|
||||
};
|
||||
|
||||
is_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rcclp_handle::start()
|
||||
{
|
||||
if(get_tool_count()++ == 0)
|
||||
{
|
||||
get_tool_instance() = std::make_shared<rcclp_tuple_t>("timemory_rcclp");
|
||||
get_tool_instance()->start();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rcclp_handle::stop()
|
||||
{
|
||||
auto idx = --get_tool_count();
|
||||
if(get_tool_instance().get())
|
||||
{
|
||||
get_tool_instance()->stop();
|
||||
if(idx == 0) get_tool_instance().reset();
|
||||
}
|
||||
}
|
||||
|
||||
rcclp_handle::persistent_data&
|
||||
rcclp_handle::get_persistent_data()
|
||||
{
|
||||
static persistent_data _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
std::atomic<short>&
|
||||
rcclp_handle::get_configured()
|
||||
{
|
||||
return get_persistent_data().m_configured;
|
||||
}
|
||||
|
||||
rcclp_handle::toolset_ptr_t&
|
||||
rcclp_handle::get_tool_instance()
|
||||
{
|
||||
return get_persistent_data().m_tool;
|
||||
}
|
||||
|
||||
std::atomic<int64_t>&
|
||||
rcclp_handle::get_tool_count()
|
||||
{
|
||||
return get_persistent_data().m_count;
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,106 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/rccl.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/components/category_region.hpp"
|
||||
#include "library/components/comm_data.hpp"
|
||||
|
||||
#include <timemory/api/macros.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#if !defined(ROCPROFSYS_NUM_RCCLP_WRAPPERS)
|
||||
# define ROCPROFSYS_NUM_RCCLP_WRAPPERS 25
|
||||
#endif
|
||||
|
||||
ROCPROFSYS_COMPONENT_ALIAS(
|
||||
rccl_toolset_t,
|
||||
::tim::component_bundle<category::rocm_rccl,
|
||||
rocprofsys::component::category_region<category::rocm_rccl>,
|
||||
comm_data>)
|
||||
ROCPROFSYS_COMPONENT_ALIAS(rcclp_gotcha_t,
|
||||
::tim::component::gotcha<ROCPROFSYS_NUM_RCCLP_WRAPPERS,
|
||||
rccl_toolset_t, category::rocm_rccl>)
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_RCCL)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(is_available, component::rcclp_gotcha_t, false_type)
|
||||
#endif
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
uint64_t
|
||||
activate_rcclp();
|
||||
|
||||
uint64_t
|
||||
deactivate_rcclp(uint64_t id);
|
||||
|
||||
void
|
||||
configure_rcclp(const std::set<std::string>& permit = {},
|
||||
const std::set<std::string>& reject = {});
|
||||
|
||||
struct rcclp_handle : base<rcclp_handle, void>
|
||||
{
|
||||
static constexpr size_t rcclp_wrapper_count = ROCPROFSYS_NUM_RCCLP_WRAPPERS;
|
||||
|
||||
using value_type = void;
|
||||
using this_type = rcclp_handle;
|
||||
using base_type = base<this_type, value_type>;
|
||||
|
||||
using rcclp_tuple_t = tim::component_tuple<rcclp_gotcha_t>;
|
||||
using toolset_ptr_t = std::shared_ptr<rcclp_tuple_t>;
|
||||
|
||||
static std::string label() { return "rcclp_handle"; }
|
||||
static std::string description() { return "Handle for activating NCCL wrappers"; }
|
||||
static void get() {}
|
||||
static void start();
|
||||
static void stop();
|
||||
static int get_count() { return get_tool_count().load(); }
|
||||
|
||||
private:
|
||||
struct persistent_data
|
||||
{
|
||||
std::atomic<short> m_configured{ 0 };
|
||||
std::atomic<int64_t> m_count{ 0 };
|
||||
toolset_ptr_t m_tool = toolset_ptr_t{};
|
||||
};
|
||||
|
||||
static persistent_data& get_persistent_data();
|
||||
static std::atomic<short>& get_configured();
|
||||
static toolset_ptr_t& get_tool_instance();
|
||||
static std::atomic<int64_t>& get_tool_count();
|
||||
};
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/rocprofiler.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/dynamic_library.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "core/redirect.hpp"
|
||||
#include "library/rocprofiler.hpp"
|
||||
#include "library/sampling.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/storage/types.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
#include <timemory/variadic/functional.hpp>
|
||||
#include <timemory/variadic/lightweight_tuple.hpp>
|
||||
|
||||
#include <rocprofiler.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto&
|
||||
rocprofiler_activity_count()
|
||||
{
|
||||
static std::atomic<int64_t> _v{ 0 };
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
unique_ptr_t<rocm_data_t>&
|
||||
rocm_data(int64_t _tid)
|
||||
{
|
||||
using thread_data_t = thread_data<rocm_data_t, rocm_event>;
|
||||
return thread_data_t::instance(construct_on_thread{ _tid });
|
||||
}
|
||||
|
||||
rocm_event::rocm_event(uint32_t _dev, uint32_t _thr, uint32_t _queue,
|
||||
std::string _event_name, rocm_metric_type _begin,
|
||||
rocm_metric_type _end, uint32_t _feature_count, void* _features_v)
|
||||
: device_id{ _dev }
|
||||
, thread_id{ _thr }
|
||||
, queue_id{ _queue }
|
||||
, entry{ _begin }
|
||||
, exit{ _end }
|
||||
, name(std::move(_event_name))
|
||||
{
|
||||
feature_values.reserve(_feature_count);
|
||||
feature_names.reserve(_feature_count);
|
||||
auto* _features = static_cast<rocprofiler_feature_t*>(_features_v);
|
||||
for(uint32_t i = 0; i < _feature_count; ++i)
|
||||
{
|
||||
const rocprofiler_feature_t* p = &_features[i];
|
||||
feature_names.emplace_back(i);
|
||||
switch(p->data.kind)
|
||||
{
|
||||
// Output metrics results
|
||||
case ROCPROFILER_DATA_KIND_UNINIT: break;
|
||||
case ROCPROFILER_DATA_KIND_BYTES:
|
||||
feature_values.emplace_back(
|
||||
rocm_feature_value{ p->data.result_bytes.size });
|
||||
break;
|
||||
case ROCPROFILER_DATA_KIND_INT32:
|
||||
feature_values.emplace_back(rocm_feature_value{ p->data.result_int32 });
|
||||
break;
|
||||
case ROCPROFILER_DATA_KIND_FLOAT:
|
||||
feature_values.emplace_back(rocm_feature_value{ p->data.result_float });
|
||||
break;
|
||||
case ROCPROFILER_DATA_KIND_DOUBLE:
|
||||
feature_values.emplace_back(rocm_feature_value{ p->data.result_double });
|
||||
break;
|
||||
case ROCPROFILER_DATA_KIND_INT64:
|
||||
feature_values.emplace_back(rocm_feature_value{ p->data.result_int64 });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
rocm_event::as_string() const
|
||||
{
|
||||
std::stringstream _ss{};
|
||||
_ss << name << ", device: " << device_id << ", queue: " << queue_id
|
||||
<< ", thread: " << thread_id << ", entry: " << entry << ", exit = " << exit;
|
||||
_ss.precision(3);
|
||||
_ss << std::fixed;
|
||||
for(size_t i = 0; i < feature_names.size(); ++i)
|
||||
{
|
||||
auto _name = rocprofsys::rocprofiler::get_data_labels().at(device_id).at(
|
||||
feature_names.at(i));
|
||||
_ss << ", " << _name << " = ";
|
||||
auto _as_string = [&_ss](auto&& itr) { _ss << std::setw(4) << itr; };
|
||||
std::visit(_as_string, feature_values.at(i));
|
||||
}
|
||||
return _ss.str();
|
||||
}
|
||||
|
||||
void
|
||||
rocprofiler::preinit()
|
||||
{
|
||||
rocprofiler_data::label() = "rocprofiler";
|
||||
rocprofiler_data::description() = "ROCm hardware counters";
|
||||
}
|
||||
|
||||
void
|
||||
rocprofiler::start()
|
||||
{
|
||||
if(tracker_type::start() == 0) setup();
|
||||
}
|
||||
|
||||
void
|
||||
rocprofiler::stop()
|
||||
{
|
||||
if(tracker_type::stop() == 0) shutdown();
|
||||
}
|
||||
|
||||
bool
|
||||
rocprofiler::is_setup()
|
||||
{
|
||||
return rocprofsys::rocprofiler::is_setup();
|
||||
}
|
||||
|
||||
void
|
||||
rocprofiler::add_setup(const std::string&, std::function<void()>&&)
|
||||
{}
|
||||
|
||||
void
|
||||
rocprofiler::add_shutdown(const std::string&, std::function<void()>&&)
|
||||
{}
|
||||
|
||||
void
|
||||
rocprofiler::remove_setup(const std::string&)
|
||||
{}
|
||||
|
||||
void
|
||||
rocprofiler::remove_shutdown(const std::string&)
|
||||
{}
|
||||
|
||||
void
|
||||
rocprofiler::setup()
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(1, "rocprofiler is setup\n");
|
||||
}
|
||||
|
||||
void
|
||||
rocprofiler::shutdown()
|
||||
{
|
||||
rocprofsys::rocprofiler::post_process();
|
||||
rocprofsys::rocprofiler::rocm_cleanup();
|
||||
ROCPROFSYS_VERBOSE_F(1, "rocprofiler is shutdown\n");
|
||||
}
|
||||
|
||||
scope::transient_destructor
|
||||
rocprofiler::protect_flush_activity()
|
||||
{
|
||||
return scope::transient_destructor([]() { --rocprofiler_activity_count(); },
|
||||
[]() { ++rocprofiler_activity_count(); });
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(rocprofiler, false, void)
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(rocprofiler_data, true,
|
||||
tim::component::rocprofiler_value)
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/api.hpp>
|
||||
#include <timemory/backends/hardware_counters.hpp>
|
||||
#include <timemory/components/base.hpp>
|
||||
#include <timemory/components/data_tracker/components.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/enum.h>
|
||||
#include <timemory/macros.hpp>
|
||||
#include <timemory/macros/os.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/macros.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/utility/transient_function.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
using rocm_metric_type = unsigned long long;
|
||||
using rocm_info_entry = ::tim::hardware_counters::info;
|
||||
using rocm_feature_value = std::variant<uint32_t, float, uint64_t, double>;
|
||||
|
||||
struct rocm_counter
|
||||
{
|
||||
std::array<rocm_metric_type, ROCPROFSYS_ROCM_MAX_COUNTERS> counters;
|
||||
};
|
||||
|
||||
struct rocm_event
|
||||
{
|
||||
using value_type = rocm_feature_value;
|
||||
|
||||
uint32_t device_id = 0;
|
||||
uint32_t thread_id = 0;
|
||||
uint32_t queue_id = 0;
|
||||
rocm_metric_type entry = 0;
|
||||
rocm_metric_type exit = 0;
|
||||
std::string name = {};
|
||||
std::vector<size_t> feature_names = {};
|
||||
std::vector<rocm_feature_value> feature_values = {};
|
||||
|
||||
rocm_event() = default;
|
||||
rocm_event(uint32_t _dev, uint32_t _thr, uint32_t _queue, std::string _event_name,
|
||||
rocm_metric_type begin, rocm_metric_type end, uint32_t _feature_count,
|
||||
void* _features);
|
||||
|
||||
std::string as_string() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& _os, const rocm_event& _v)
|
||||
{
|
||||
return (_os << _v.as_string());
|
||||
}
|
||||
|
||||
friend bool operator<(const rocm_event& _lhs, const rocm_event& _rhs)
|
||||
{
|
||||
return std::tie(_lhs.device_id, _lhs.queue_id, _lhs.entry, _lhs.thread_id) <
|
||||
std::tie(_rhs.device_id, _rhs.queue_id, _rhs.entry, _rhs.thread_id);
|
||||
}
|
||||
};
|
||||
|
||||
using rocm_data_t = std::vector<rocm_event>;
|
||||
using rocm_data_tracker = data_tracker<rocm_feature_value, rocm_event>;
|
||||
|
||||
rocprofsys::unique_ptr_t<rocm_data_t>&
|
||||
rocm_data(int64_t _tid = threading::get_id());
|
||||
|
||||
using rocprofiler_value = typename rocm_event::value_type;
|
||||
using rocprofiler_data = data_tracker<rocprofiler_value, rocprofiler>;
|
||||
|
||||
struct rocprofiler
|
||||
: base<rocprofiler, void>
|
||||
, private policy::instance_tracker<rocprofiler, false>
|
||||
{
|
||||
using value_type = void;
|
||||
using base_type = base<rocprofiler, void>;
|
||||
using tracker_type = policy::instance_tracker<rocprofiler, false>;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(rocprofiler)
|
||||
|
||||
static void preinit();
|
||||
static void global_init() { setup(); }
|
||||
static void global_finalize() { shutdown(); }
|
||||
|
||||
static bool is_setup();
|
||||
static void setup();
|
||||
static void shutdown();
|
||||
static void add_setup(const std::string&, std::function<void()>&&);
|
||||
static void add_shutdown(const std::string&, std::function<void()>&&);
|
||||
static void remove_setup(const std::string&);
|
||||
static void remove_shutdown(const std::string&);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
// this function protects rocprofiler_flush_activty from being called
|
||||
// when rocprof-sys exits during a callback
|
||||
[[nodiscard]] static scope::transient_destructor protect_flush_activity();
|
||||
};
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_ROCPROFILER)
|
||||
inline void
|
||||
rocprofiler::setup()
|
||||
{}
|
||||
|
||||
inline void
|
||||
rocprofiler::shutdown()
|
||||
{}
|
||||
|
||||
inline bool
|
||||
rocprofiler::is_setup()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
using ::rocprofsys::component::rocm_data_tracker;
|
||||
using ::rocprofsys::component::rocm_feature_value;
|
||||
using ::rocprofsys::component::rocprofiler_data;
|
||||
using ::rocprofsys::component::rocprofiler_value;
|
||||
} // namespace component
|
||||
} // namespace tim
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace operation
|
||||
{
|
||||
template <>
|
||||
struct set_storage<component::rocm_data_tracker>
|
||||
{
|
||||
using T = component::rocm_data_tracker;
|
||||
static constexpr size_t max_threads = 4096;
|
||||
using type = T;
|
||||
using storage_array_t = std::array<storage<type>*, max_threads>;
|
||||
friend struct get_storage<component::rocm_data_tracker>;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(set_storage)
|
||||
|
||||
auto operator()(storage<type>*, size_t) const {}
|
||||
auto operator()(type&, size_t) const {}
|
||||
auto operator()(storage<type>* _v) const { get().fill(_v); }
|
||||
|
||||
private:
|
||||
static storage_array_t& get()
|
||||
{
|
||||
static storage_array_t _v = { nullptr };
|
||||
return _v;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_storage<component::rocm_data_tracker>
|
||||
{
|
||||
using type = component::rocm_data_tracker;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(get_storage)
|
||||
|
||||
auto operator()(const type&) const
|
||||
{
|
||||
return operation::set_storage<type>::get().at(0);
|
||||
}
|
||||
|
||||
auto operator()() const
|
||||
{
|
||||
type _obj{};
|
||||
return (*this)(_obj);
|
||||
}
|
||||
|
||||
auto operator()(size_t _idx) const
|
||||
{
|
||||
return operation::set_storage<type>::get().at(_idx);
|
||||
}
|
||||
|
||||
auto operator()(type&, size_t _idx) const { return (*this)(_idx); }
|
||||
};
|
||||
} // namespace operation
|
||||
} // namespace tim
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_ROCPROFILER)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(is_available, component::rocprofiler_data, false_type)
|
||||
#endif
|
||||
|
||||
TIMEMORY_SET_COMPONENT_API(component::rocprofiler_data, project::timemory,
|
||||
category::timing, os::supports_unix)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(is_timing_category, component::rocprofiler_data,
|
||||
false_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(uses_timing_units, component::rocprofiler_data,
|
||||
false_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(report_units, component::rocprofiler_data, false_type)
|
||||
TIMEMORY_STATISTICS_TYPE(component::rocprofiler_data, component::rocprofiler_value)
|
||||
TIMEMORY_STATISTICS_TYPE(component::rocm_data_tracker, component::rocm_feature_value)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(report_units, component::rocm_data_tracker, false_type)
|
||||
|
||||
#if !defined(ROCPROFSYS_EXTERN_COMPONENTS) || \
|
||||
(defined(ROCPROFSYS_EXTERN_COMPONENTS) && ROCPROFSYS_EXTERN_COMPONENTS > 0)
|
||||
|
||||
# include <timemory/operations.hpp>
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(rocprofiler, false, void)
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(rocprofiler_data, true, double)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,396 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/components/roctracer.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/dynamic_library.hpp"
|
||||
#include "core/redirect.hpp"
|
||||
#include "library/roctracer.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <roctracer.h>
|
||||
|
||||
#define HIP_PROF_HIP_API_STRING 1
|
||||
|
||||
#include <roctracer_ext.h>
|
||||
#include <roctracer_hip.h>
|
||||
|
||||
#if ROCPROFSYS_HIP_VERSION < 50300
|
||||
# include <roctracer_hcc.h>
|
||||
#endif
|
||||
|
||||
#define AMD_INTERNAL_BUILD 1
|
||||
#include <roctracer_hsa.h>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto&
|
||||
roctracer_activity_count()
|
||||
{
|
||||
static std::atomic<int64_t> _v{ 0 };
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
roctracer::preinit()
|
||||
{
|
||||
roctracer_data::label() = "roctracer";
|
||||
roctracer_data::description() = "ROCm tracer (activity API)";
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::start()
|
||||
{
|
||||
if(tracker_type::start() == 0) setup(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::stop()
|
||||
{
|
||||
if(tracker_type::stop() == 0) shutdown();
|
||||
}
|
||||
|
||||
bool
|
||||
roctracer::is_setup()
|
||||
{
|
||||
return roctracer_is_setup();
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::add_setup(const std::string& _lbl, std::function<void()>&& _func)
|
||||
{
|
||||
roctracer_setup_routines().emplace_back(_lbl, std::move(_func));
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::add_shutdown(const std::string& _lbl, std::function<void()>&& _func)
|
||||
{
|
||||
roctracer_shutdown_routines().emplace_back(_lbl, std::move(_func));
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::remove_setup(const std::string& _lbl)
|
||||
{
|
||||
auto& _data = roctracer_setup_routines();
|
||||
for(auto itr = _data.begin(); itr != _data.end(); ++itr)
|
||||
{
|
||||
if(itr->first == _lbl)
|
||||
{
|
||||
_data.erase(itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::remove_shutdown(const std::string& _lbl)
|
||||
{
|
||||
auto& _data = roctracer_setup_routines();
|
||||
for(auto itr = _data.begin(); itr != _data.end(); ++itr)
|
||||
{
|
||||
if(itr->first == _lbl)
|
||||
{
|
||||
_data.erase(itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::setup(void* table, bool on_load_trace)
|
||||
{
|
||||
if(!get_use_roctracer()) return;
|
||||
|
||||
auto_lock_t _lk{ type_mutex<roctracer>() };
|
||||
if(roctracer_is_setup()) return;
|
||||
roctracer_is_setup() = true;
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(1, "setting up roctracer...\n");
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
|
||||
dynamic_library _amdhip64{ "ROCPROFSYS_ROCTRACER_LIBAMDHIP64",
|
||||
find_library_path("libamdhip64.so",
|
||||
{ "ROCPROFSYS_ROCM_PATH", "ROCM_PATH" },
|
||||
{ ROCPROFSYS_DEFAULT_ROCM_PATH }) };
|
||||
|
||||
#if ROCPROFSYS_HIP_VERSION_MAJOR == 4 && ROCPROFSYS_HIP_VERSION_MINOR < 4
|
||||
dynamic_library _kfdwrapper{
|
||||
"ROCPROFSYS_ROCTRACER_LIBKFDWRAPPER",
|
||||
find_library_path("libkfdwrapper64.so", { "ROCPROFSYS_ROCM_PATH", "ROCM_PATH" },
|
||||
{ ROCPROFSYS_DEFAULT_ROCM_PATH },
|
||||
{ "roctracer/lib", "roctracer/lib64", "lib", "lib64" })
|
||||
};
|
||||
#endif
|
||||
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_set_properties(ACTIVITY_DOMAIN_HIP_API, nullptr));
|
||||
|
||||
// Allocating tracing pool
|
||||
roctracer_properties_t properties{};
|
||||
memset(&properties, 0, sizeof(roctracer_properties_t));
|
||||
// properties.mode = 0x1000;
|
||||
properties.buffer_size = 0x100;
|
||||
properties.buffer_callback_fun = hip_activity_callback;
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_open_pool(&properties));
|
||||
|
||||
#if ROCPROFSYS_HIP_VERSION_MAJOR == 4 && ROCPROFSYS_HIP_VERSION_MINOR >= 4
|
||||
// HIP 4.5.0 has an invalid warning
|
||||
redirect _rd{ std::cerr, "roctracer_enable_callback(), get_op_end(), invalid domain "
|
||||
"ID(4) in: roctracer_enable_callback(hip_api_callback, "
|
||||
"nullptr)roctracer_enable_activity_expl(), get_op_end(), "
|
||||
"invalid domain ID(4) in: roctracer_enable_activity()" };
|
||||
#endif
|
||||
|
||||
if(get_trace_hip_api())
|
||||
{
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_enable_domain_callback(
|
||||
ACTIVITY_DOMAIN_HIP_API, hip_api_callback, nullptr));
|
||||
}
|
||||
|
||||
if(get_use_roctx())
|
||||
{
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_enable_domain_callback(
|
||||
ACTIVITY_DOMAIN_ROCTX, roctx_api_callback, nullptr));
|
||||
}
|
||||
|
||||
if(get_trace_hip_activity())
|
||||
{
|
||||
// Enable HIP activity tracing
|
||||
ROCPROFSYS_ROCTRACER_CALL(
|
||||
roctracer_enable_domain_activity(ACTIVITY_DOMAIN_HIP_OPS));
|
||||
}
|
||||
|
||||
if(table != nullptr)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(1 || on_load_trace, "[OnLoad] setting up HSA...\n");
|
||||
|
||||
bool trace_hsa_api = get_trace_hsa_api();
|
||||
|
||||
// Enable HSA API callbacks/activity
|
||||
if(trace_hsa_api)
|
||||
{
|
||||
std::vector<std::string> hsa_api_vec =
|
||||
tim::delimit(get_trace_hsa_api_types());
|
||||
|
||||
// initialize HSA tracing
|
||||
roctracer_set_properties(
|
||||
static_cast<activity_domain_t>(ACTIVITY_DOMAIN_HSA_API), (void*) table);
|
||||
|
||||
if(!hsa_api_vec.empty())
|
||||
{
|
||||
for(const auto& itr : hsa_api_vec)
|
||||
{
|
||||
uint32_t cid = HSA_API_ID_NUMBER;
|
||||
const char* api = itr.c_str();
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_op_code(
|
||||
static_cast<activity_domain_t>(ACTIVITY_DOMAIN_HSA_API), api,
|
||||
&cid, nullptr));
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_enable_op_callback(
|
||||
static_cast<activity_domain_t>(ACTIVITY_DOMAIN_HSA_API), cid,
|
||||
hsa_api_callback, nullptr));
|
||||
|
||||
ROCPROFSYS_VERBOSE(1 || on_load_trace, " HSA-trace(%s)", api);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(1 || on_load_trace, " HSA-trace()\n");
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_enable_domain_callback(
|
||||
static_cast<activity_domain_t>(ACTIVITY_DOMAIN_HSA_API),
|
||||
hsa_api_callback, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
bool trace_hsa_activity = get_trace_hsa_activity();
|
||||
// Enable HSA GPU activity
|
||||
if(trace_hsa_activity)
|
||||
{
|
||||
#if ROCPROFSYS_HIP_VERSION < 50300
|
||||
using namespace roctracer;
|
||||
// initialize HSA tracing
|
||||
const char* output_prefix = nullptr;
|
||||
hsa_ops_properties_t ops_properties{
|
||||
table, reinterpret_cast<activity_async_callback_t>(hsa_activity_callback),
|
||||
nullptr, output_prefix
|
||||
};
|
||||
#elif ROCPROFSYS_HIP_VERSION < 50301
|
||||
hsa_ops_properties_t ops_properties;
|
||||
ops_properties.table = table;
|
||||
ops_properties.reserved1[0] = reinterpret_cast<void*>(&hsa_activity_callback);
|
||||
ops_properties.reserved1[1] = nullptr;
|
||||
ops_properties.reserved1[2] = nullptr;
|
||||
#else
|
||||
hsa_ops_properties_t ops_properties{
|
||||
table, reinterpret_cast<void*>(&hsa_activity_callback), nullptr, nullptr
|
||||
};
|
||||
#endif
|
||||
roctracer_set_properties(
|
||||
static_cast<activity_domain_t>(ACTIVITY_DOMAIN_HSA_OPS), &ops_properties);
|
||||
|
||||
ROCPROFSYS_VERBOSE(1 || on_load_trace, " HSA-activity-trace()\n");
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_enable_op_activity(
|
||||
static_cast<activity_domain_t>(ACTIVITY_DOMAIN_HSA_OPS), HSA_OP_ID_COPY));
|
||||
}
|
||||
}
|
||||
|
||||
// callback for HSA
|
||||
for(auto& itr : roctracer_setup_routines())
|
||||
itr.second();
|
||||
|
||||
// make sure all async callbacks are allocated
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
hip_exec_activity_callbacks(i);
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(1, "roctracer is setup\n");
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::flush()
|
||||
{
|
||||
auto wait_for_activity_flush_completion = []() {
|
||||
uint16_t nitr = 0;
|
||||
while(roctracer_activity_count() > 0 && nitr++ < 10)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{ 100 });
|
||||
};
|
||||
|
||||
// a flush may already be happening
|
||||
wait_for_activity_flush_completion();
|
||||
|
||||
if(roctracer_activity_count() == 0)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(2, "executing roctracer_flush_activity()...\n");
|
||||
ROCPROFSYS_ROCTRACER_CALL(roctracer_flush_activity());
|
||||
// wait to make sure flush completes
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{ 100 });
|
||||
wait_for_activity_flush_completion();
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_CI_FAIL(true,
|
||||
"roctracer_activity_count() != 0 (== %li). "
|
||||
"roctracer::shutdown() most likely called during abort",
|
||||
roctracer_activity_count().load());
|
||||
}
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(2, "executing hip_exec_activity_callbacks(0..%zu)\n",
|
||||
thread_info::get_peak_num_threads());
|
||||
// make sure all async operations are executed
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
hip_exec_activity_callbacks(i);
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(2, "roctracer flush completed\n");
|
||||
}
|
||||
|
||||
void
|
||||
roctracer::shutdown()
|
||||
{
|
||||
auto_lock_t _lk{ type_mutex<roctracer>() };
|
||||
if(!roctracer_is_setup()) return;
|
||||
|
||||
roctracer_is_setup() = false;
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(1, "shutting down roctracer...\n");
|
||||
|
||||
// callback for hsa
|
||||
ROCPROFSYS_VERBOSE_F(2, "executing %zu roctracer_shutdown_routines...\n",
|
||||
roctracer_shutdown_routines().size());
|
||||
for(auto& itr : roctracer_shutdown_routines())
|
||||
itr.second();
|
||||
|
||||
#if ROCPROFSYS_HIP_VERSION_MAJOR == 4 && ROCPROFSYS_HIP_VERSION_MINOR >= 4
|
||||
ROCPROFSYS_DEBUG_F("redirecting roctracer warnings\n");
|
||||
// HIP 4.5.0 has an invalid warning
|
||||
redirect _rd{
|
||||
std::cerr, "roctracer_disable_callback(), get_op_end(), invalid domain ID(4) "
|
||||
"in: roctracer_disable_callback()roctracer_disable_activity(), "
|
||||
"get_op_end(), invalid domain ID(4) in: roctracer_disable_activity()"
|
||||
};
|
||||
#endif
|
||||
|
||||
if(get_trace_hip_api())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(
|
||||
2,
|
||||
"executing roctracer_disable_domain_callback(ACTIVITY_DOMAIN_HIP_API)...\n");
|
||||
ROCPROFSYS_ROCTRACER_CALL(
|
||||
roctracer_disable_domain_callback(ACTIVITY_DOMAIN_HIP_API));
|
||||
}
|
||||
|
||||
if(get_use_roctx())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(
|
||||
2, "executing roctracer_disable_domain_activity(ACTIVITY_DOMAIN_ROCTX)...\n");
|
||||
ROCPROFSYS_ROCTRACER_CALL(
|
||||
roctracer_disable_domain_callback(ACTIVITY_DOMAIN_ROCTX));
|
||||
}
|
||||
|
||||
if(get_trace_hip_activity())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(
|
||||
2,
|
||||
"executing roctracer_disable_domain_activity(ACTIVITY_DOMAIN_HIP_OPS)...\n");
|
||||
ROCPROFSYS_ROCTRACER_CALL(
|
||||
roctracer_disable_domain_activity(ACTIVITY_DOMAIN_HIP_OPS));
|
||||
}
|
||||
|
||||
if(get_trace_hsa_api())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(
|
||||
2,
|
||||
"executing roctracer_disable_domain_activity(ACTIVITY_DOMAIN_HSA_API)...\n");
|
||||
ROCPROFSYS_ROCTRACER_CALL(
|
||||
roctracer_disable_domain_callback(ACTIVITY_DOMAIN_HSA_API));
|
||||
}
|
||||
|
||||
if(get_trace_hsa_api())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(
|
||||
2, "executing roctracer_disable_op_activity(ACTIVITY_DOMAIN_HSA_OPS, "
|
||||
"HSA_OP_ID_COPY)...\n");
|
||||
ROCPROFSYS_ROCTRACER_CALL(
|
||||
roctracer_disable_op_activity(ACTIVITY_DOMAIN_HSA_OPS, HSA_OP_ID_COPY));
|
||||
}
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(1, "roctracer is shutdown\n");
|
||||
}
|
||||
|
||||
scope::transient_destructor
|
||||
roctracer::protect_flush_activity()
|
||||
{
|
||||
return scope::transient_destructor([]() { --roctracer_activity_count(); },
|
||||
[]() { ++roctracer_activity_count(); });
|
||||
}
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(roctracer, false, void)
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(roctracer_data, true, double)
|
||||
@@ -0,0 +1,117 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
|
||||
#include <timemory/api.hpp>
|
||||
#include <timemory/components/base.hpp>
|
||||
#include <timemory/components/data_tracker/components.hpp>
|
||||
#include <timemory/components/macros.hpp>
|
||||
#include <timemory/enum.h>
|
||||
#include <timemory/macros/os.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/utility/transient_function.hpp>
|
||||
|
||||
ROCPROFSYS_COMPONENT_ALIAS(roctracer_data,
|
||||
::tim::component::data_tracker<double, roctracer>)
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace component
|
||||
{
|
||||
struct roctracer
|
||||
: base<roctracer, void>
|
||||
, private policy::instance_tracker<roctracer, false>
|
||||
{
|
||||
using value_type = void;
|
||||
using base_type = base<roctracer, void>;
|
||||
using tracker_type = policy::instance_tracker<roctracer, false>;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(roctracer)
|
||||
|
||||
static void preinit();
|
||||
static void global_finalize() { shutdown(); }
|
||||
|
||||
static bool is_setup();
|
||||
static void setup(void* hsa_api_table, bool on_load_trace = false);
|
||||
static void flush();
|
||||
static void shutdown();
|
||||
static void add_setup(const std::string&, std::function<void()>&&);
|
||||
static void add_shutdown(const std::string&, std::function<void()>&&);
|
||||
static void remove_setup(const std::string&);
|
||||
static void remove_shutdown(const std::string&);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
// this function protects roctracer_flush_activty from being called
|
||||
// when rocprof-sys exits during a callback
|
||||
[[nodiscard]] static scope::transient_destructor protect_flush_activity();
|
||||
};
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_ROCTRACER)
|
||||
inline void
|
||||
roctracer::setup(void*, bool)
|
||||
{}
|
||||
|
||||
inline void
|
||||
roctracer::flush()
|
||||
{}
|
||||
|
||||
inline void
|
||||
roctracer::shutdown()
|
||||
{}
|
||||
|
||||
inline bool
|
||||
roctracer::is_setup()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
} // namespace component
|
||||
} // namespace rocprofsys
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_ROCTRACER)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(is_available, component::roctracer_data, false_type)
|
||||
#endif
|
||||
|
||||
TIMEMORY_SET_COMPONENT_API(rocprofsys::component::roctracer_data, project::timemory,
|
||||
category::timing, os::supports_unix)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(is_timing_category, component::roctracer_data, true_type)
|
||||
ROCPROFSYS_DEFINE_CONCRETE_TRAIT(uses_timing_units, component::roctracer_data, true_type)
|
||||
|
||||
#if defined(ROCPROFSYS_USE_ROCTRACER) && ROCPROFSYS_USE_ROCTRACER > 0
|
||||
# if !defined(ROCPROFSYS_EXTERN_COMPONENTS) || \
|
||||
(defined(ROCPROFSYS_EXTERN_COMPONENTS) && ROCPROFSYS_EXTERN_COMPONENTS > 0)
|
||||
|
||||
# include <timemory/operations.hpp>
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(roctracer, false, void)
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(roctracer_data, true, double)
|
||||
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,347 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "library/coverage.hpp"
|
||||
#include "api.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "library/coverage/impl.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/tpls/cereal/cereal.hpp>
|
||||
#include <timemory/utility/popen.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#define ROCPROFSYS_SERIALIZE(MEMBER_VARIABLE) \
|
||||
ar(::tim::cereal::make_nvp(#MEMBER_VARIABLE, MEMBER_VARIABLE))
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace coverage
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename... Tp>
|
||||
using uomap_t = std::unordered_map<Tp...>;
|
||||
//
|
||||
using coverage_thread_data_type =
|
||||
uomap_t<std::string_view, uomap_t<std::string_view, std::map<size_t, size_t>>>;
|
||||
//
|
||||
using coverage_data_vector = std::vector<coverage_data>;
|
||||
//
|
||||
using coverage_data_map =
|
||||
uomap_t<std::string_view,
|
||||
uomap_t<std::string_view, std::map<size_t, coverage_data_vector::iterator>>>;
|
||||
//
|
||||
using coverage_thread_data =
|
||||
rocprofsys::thread_data<coverage_thread_data_type, code_coverage>;
|
||||
//
|
||||
auto&
|
||||
get_code_coverage()
|
||||
{
|
||||
static auto _v = code_coverage{};
|
||||
return _v;
|
||||
}
|
||||
//
|
||||
auto&
|
||||
get_post_processed()
|
||||
{
|
||||
static auto* _v = new bool{ false };
|
||||
return *_v;
|
||||
}
|
||||
//
|
||||
auto&
|
||||
get_coverage_data()
|
||||
{
|
||||
static auto _v = coverage_data_vector{};
|
||||
return _v;
|
||||
}
|
||||
//
|
||||
auto&
|
||||
get_coverage_count(int64_t _tid = tim::threading::get_id())
|
||||
{
|
||||
return coverage_thread_data::instance(construct_on_thread{ _tid });
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
void
|
||||
post_process()
|
||||
{
|
||||
using data_tuple_t = coverage_data::data_tuple_t;
|
||||
|
||||
if(get_post_processed()) return;
|
||||
get_post_processed() = true;
|
||||
|
||||
if(!config::get_use_code_coverage()) return;
|
||||
|
||||
auto& _coverage = get_code_coverage();
|
||||
auto& _coverage_data = get_coverage_data();
|
||||
|
||||
if(_coverage.size == 0)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(
|
||||
0,
|
||||
"Warning! Code coverage enabled but no code coverage data is available!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto _data = coverage_thread_data_type{};
|
||||
{
|
||||
auto _coverage_map = coverage_data_map{};
|
||||
auto _find = [&_coverage_data, &_coverage_map](data_tuple_t&& _v) {
|
||||
auto& _cache = _coverage_map[std::get<0>(_v)][std::get<1>(_v)];
|
||||
auto mitr = _cache.find(std::get<2>(_v));
|
||||
if(mitr != _cache.end()) return std::make_pair(mitr->second, true);
|
||||
|
||||
for(auto itr = _coverage_data.begin(); itr != _coverage_data.end(); ++itr)
|
||||
{
|
||||
if(*itr == _v)
|
||||
{
|
||||
_cache[std::get<2>(_v)] = itr;
|
||||
return std::make_pair(itr, true);
|
||||
}
|
||||
}
|
||||
return std::make_pair(_coverage_data.end(), false);
|
||||
};
|
||||
|
||||
for(size_t i = 0; i < coverage_thread_data::size(); ++i)
|
||||
{
|
||||
const auto& _thr_data = *get_coverage_count(i);
|
||||
for(const auto& file : _thr_data)
|
||||
{
|
||||
for(const auto& func : file.second)
|
||||
{
|
||||
for(const auto& addr : func.second)
|
||||
{
|
||||
_data[file.first][func.first][addr.first] += addr.second;
|
||||
auto&& _v = _find({ file.first, func.first, addr.first });
|
||||
if(_v.second)
|
||||
{
|
||||
_v.first->count += addr.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(0,
|
||||
"Warning! No matching coverage data for "
|
||||
"%s :: %s (0x%x)\n",
|
||||
func.first.data(), file.first.data(),
|
||||
(unsigned int) addr.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(const auto& file : _data)
|
||||
{
|
||||
for(const auto& func : file.second)
|
||||
{
|
||||
for(const auto& addr : func.second)
|
||||
{
|
||||
if(addr.second > 0)
|
||||
{
|
||||
_coverage.count += 1;
|
||||
_coverage.covered.modules.emplace(file.first);
|
||||
_coverage.covered.functions.emplace(func.first);
|
||||
_coverage.covered.addresses.emplace(addr.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(_coverage_data.begin(), _coverage_data.end(),
|
||||
std::greater<coverage_data>{});
|
||||
|
||||
{
|
||||
auto _tmp_map = coverage_data_map{};
|
||||
auto _tmp = std::decay_t<decltype(_coverage_data)>{};
|
||||
auto _find_in_tmp = [&_tmp, &_tmp_map](const auto& _v) {
|
||||
auto& _cache = _tmp_map[_v.module][_v.function];
|
||||
auto mitr = _cache.find(_v.address);
|
||||
if(mitr != _cache.end()) return std::make_pair(mitr->second, true);
|
||||
|
||||
for(auto titr = _tmp.begin(); titr != _tmp.end(); ++titr)
|
||||
{
|
||||
if(titr->source == _v.source && titr->address != _v.address &&
|
||||
titr->count == _v.count)
|
||||
{
|
||||
_cache[_v.address] = titr;
|
||||
return std::make_pair(titr, true);
|
||||
}
|
||||
}
|
||||
return std::make_pair(_tmp.end(), false);
|
||||
};
|
||||
for(auto&& itr : _coverage_data)
|
||||
{
|
||||
if(!_find_in_tmp(itr).second) _tmp.emplace_back(itr);
|
||||
}
|
||||
std::swap(_coverage_data, _tmp);
|
||||
}
|
||||
|
||||
ROCPROFSYS_VERBOSE(0, "code coverage :: %6.2f%s\n", _coverage() * 100.0, "%");
|
||||
ROCPROFSYS_VERBOSE(0, "module coverage :: %6.2f%s\n",
|
||||
_coverage(code_coverage::MODULE) * 100.0, "%");
|
||||
ROCPROFSYS_VERBOSE(0, "function coverage :: %6.2f%s\n",
|
||||
_coverage(code_coverage::FUNCTION) * 100.0, "%");
|
||||
|
||||
if(get_verbose() >= 0) fprintf(stderr, "\n");
|
||||
|
||||
std::sort(_coverage_data.begin(), _coverage_data.end(),
|
||||
std::greater<coverage_data>{});
|
||||
|
||||
auto _get_setting = [](const std::string& _v) {
|
||||
auto&& _b = config::get_setting_value<bool>(_v);
|
||||
ROCPROFSYS_CI_THROW(!_b, "Error! No configuration setting named '%s'",
|
||||
_v.c_str());
|
||||
return _b.value_or(true);
|
||||
};
|
||||
|
||||
auto _text_output = _get_setting("ROCPROFSYS_TEXT_OUTPUT");
|
||||
auto _json_output = _get_setting("ROCPROFSYS_JSON_OUTPUT");
|
||||
|
||||
if(_text_output)
|
||||
{
|
||||
auto _fname = tim::settings::compose_output_filename("coverage", ".txt");
|
||||
std::ofstream ofs{};
|
||||
if(tim::filepath::open(ofs, _fname))
|
||||
{
|
||||
if(get_verbose() >= 0)
|
||||
operation::file_output_message<code_coverage>{}(
|
||||
_fname, std::string{ "coverage" });
|
||||
for(auto& itr : _coverage_data)
|
||||
{
|
||||
// if(get_debug() && get_verbose() >= 2)
|
||||
if(true)
|
||||
{
|
||||
auto _addr = TIMEMORY_JOIN("", "0x", std::hex, itr.address);
|
||||
ofs << std::setw(8) << itr.count << " " << std::setw(8) << _addr
|
||||
<< " " << itr.source << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
ofs << std::setw(8) << itr.count << " " << itr.source << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_THROW("Error opening coverage output file: %s", _fname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if(_json_output)
|
||||
{
|
||||
std::stringstream oss{};
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
auto ar =
|
||||
tim::policy::output_archive<cereal::PrettyJSONOutputArchive>::get(oss);
|
||||
|
||||
ar->setNextName("rocprofsys");
|
||||
ar->startNode();
|
||||
ar->setNextName("coverage");
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("summary", _coverage));
|
||||
(*ar)(cereal::make_nvp("details", _coverage_data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
auto _fname = tim::settings::compose_output_filename("coverage", ".json");
|
||||
std::ofstream ofs{};
|
||||
if(tim::filepath::open(ofs, _fname))
|
||||
{
|
||||
if(get_verbose() >= 0)
|
||||
operation::file_output_message<code_coverage>{}(
|
||||
_fname, std::string{ "coverage" });
|
||||
ofs << oss.str() << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_THROW("Error opening coverage output file: %s", _fname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if(get_verbose() >= 0) fprintf(stderr, "\n");
|
||||
}
|
||||
} // namespace coverage
|
||||
} // namespace rocprofsys
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
namespace coverage = rocprofsys::coverage;
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_register_source_hidden(const char* file, const char* func, size_t line,
|
||||
size_t address, const char* source)
|
||||
{
|
||||
if(coverage::get_post_processed()) return;
|
||||
|
||||
using coverage_data = coverage::coverage_data;
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(4, "[0x%x] :: %-20s :: %20s:%zu :: %s\n",
|
||||
(unsigned int) address, func, file, line, source);
|
||||
|
||||
coverage::get_coverage_data().emplace_back(
|
||||
coverage_data{ size_t{ 0 }, address, line, file, func,
|
||||
(source && strlen(source) > 0) ? source : func });
|
||||
|
||||
coverage::get_code_coverage().size += 1;
|
||||
coverage::get_code_coverage().possible.modules.emplace(file);
|
||||
coverage::get_code_coverage().possible.functions.emplace(func);
|
||||
coverage::get_code_coverage().possible.addresses.emplace(address);
|
||||
|
||||
// initialize
|
||||
for(size_t i = 0; i < coverage::coverage_thread_data::size(); ++i)
|
||||
{
|
||||
(*coverage::get_coverage_count(i))[file][func].emplace(address, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
extern "C" void
|
||||
rocprofsys_register_coverage_hidden(const char* file, const char* func, size_t address)
|
||||
{
|
||||
if(coverage::get_post_processed()) return;
|
||||
if(rocprofsys::get_state() < rocprofsys::State::Active &&
|
||||
!rocprofsys_init_tooling_hidden())
|
||||
return;
|
||||
else if(rocprofsys::get_state() >= rocprofsys::State::Finalized)
|
||||
return;
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(3, "[0x%x] %-20s :: %20s\n", (unsigned int) address, func,
|
||||
file);
|
||||
(*coverage::get_coverage_count())[file][func][address] += 1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
@@ -0,0 +1,173 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/tpls/cereal/cereal.hpp>
|
||||
#include <timemory/tpls/cereal/cereal/cereal.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#if !defined(ROCPROFSYS_SERIALIZE)
|
||||
# define ROCPROFSYS_SERIALIZE(MEMBER_VARIABLE) \
|
||||
ar(::tim::cereal::make_nvp(#MEMBER_VARIABLE, MEMBER_VARIABLE))
|
||||
#endif
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace coverage
|
||||
{
|
||||
#if !defined(ROCPROFSYS_PYBIND11_SOURCE) || ROCPROFSYS_PYBIND11_SOURCE == 0
|
||||
void
|
||||
post_process();
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//
|
||||
/// \struct code_coverage
|
||||
/// \brief Summary information about the code coverage
|
||||
//
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
struct code_coverage
|
||||
{
|
||||
using int_set_t = std::set<size_t>;
|
||||
using str_set_t = std::set<std::string>;
|
||||
|
||||
enum Category
|
||||
{
|
||||
STANDARD = 0,
|
||||
ADDRESS,
|
||||
MODULE,
|
||||
FUNCTION
|
||||
};
|
||||
|
||||
struct data
|
||||
{
|
||||
int_set_t addresses = {};
|
||||
str_set_t modules = {};
|
||||
str_set_t functions = {};
|
||||
|
||||
data& operator+=(const data& rhs);
|
||||
data operator+(const data& rhs) const;
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& ar, const unsigned version);
|
||||
};
|
||||
|
||||
double operator()(Category _c = STANDARD) const;
|
||||
double get(Category _c = STANDARD) const { return (*this)(_c); }
|
||||
|
||||
int_set_t get_uncovered_addresses() const;
|
||||
str_set_t get_uncovered_modules() const;
|
||||
str_set_t get_uncovered_functions() const;
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& ar, const unsigned version);
|
||||
|
||||
size_t count = 0;
|
||||
size_t size = 0;
|
||||
data covered = {};
|
||||
data possible = {};
|
||||
};
|
||||
//
|
||||
template <typename ArchiveT>
|
||||
void
|
||||
code_coverage::serialize(ArchiveT& ar, const unsigned version)
|
||||
{
|
||||
ROCPROFSYS_SERIALIZE(count);
|
||||
ROCPROFSYS_SERIALIZE(size);
|
||||
ROCPROFSYS_SERIALIZE(covered);
|
||||
ROCPROFSYS_SERIALIZE(possible);
|
||||
if constexpr(tim::concepts::is_output_archive<ArchiveT>::value)
|
||||
{
|
||||
ar.setNextName("coverage");
|
||||
ar.startNode();
|
||||
ar(tim::cereal::make_nvp("total", get(STANDARD)));
|
||||
ar(tim::cereal::make_nvp("addresses", get(ADDRESS)));
|
||||
ar(tim::cereal::make_nvp("modules", get(MODULE)));
|
||||
ar(tim::cereal::make_nvp("functions", get(FUNCTION)));
|
||||
ar.finishNode();
|
||||
}
|
||||
(void) version;
|
||||
}
|
||||
//
|
||||
template <typename ArchiveT>
|
||||
void
|
||||
code_coverage::data::serialize(ArchiveT& ar, const unsigned version)
|
||||
{
|
||||
ROCPROFSYS_SERIALIZE(addresses);
|
||||
ROCPROFSYS_SERIALIZE(modules);
|
||||
ROCPROFSYS_SERIALIZE(functions);
|
||||
(void) version;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//
|
||||
/// \struct coverage_data
|
||||
/// \brief Detailed information about the code coverage
|
||||
//
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
struct coverage_data
|
||||
{
|
||||
using data_tuple_t = std::tuple<std::string_view, std::string_view, size_t>;
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& ar, const unsigned version);
|
||||
|
||||
coverage_data& operator+=(const coverage_data& rhs);
|
||||
coverage_data operator+(const coverage_data& rhs) const;
|
||||
bool operator==(const coverage_data& rhs) const;
|
||||
bool operator==(const data_tuple_t& rhs) const;
|
||||
bool operator!=(const coverage_data& rhs) const;
|
||||
bool operator<(const coverage_data& rhs) const;
|
||||
bool operator<=(const coverage_data& rhs) const;
|
||||
bool operator>(const coverage_data& rhs) const;
|
||||
bool operator>=(const coverage_data& rhs) const;
|
||||
|
||||
size_t count = 0;
|
||||
size_t address = 0;
|
||||
size_t line = 0;
|
||||
std::string module = {};
|
||||
std::string function = {};
|
||||
std::string source = {};
|
||||
};
|
||||
//
|
||||
template <typename ArchiveT>
|
||||
void
|
||||
coverage_data::serialize(ArchiveT& ar, const unsigned version)
|
||||
{
|
||||
ROCPROFSYS_SERIALIZE(count);
|
||||
ROCPROFSYS_SERIALIZE(line);
|
||||
ROCPROFSYS_SERIALIZE(address);
|
||||
ROCPROFSYS_SERIALIZE(module);
|
||||
ROCPROFSYS_SERIALIZE(function);
|
||||
ROCPROFSYS_SERIALIZE(source);
|
||||
(void) version;
|
||||
}
|
||||
//
|
||||
} // namespace coverage
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,6 @@
|
||||
#
|
||||
set(coverage_sources)
|
||||
set(coverage_headers ${CMAKE_CURRENT_LIST_DIR}/impl.hpp)
|
||||
|
||||
target_sources(rocprofiler-systems-object-library PRIVATE ${coverage_sources}
|
||||
${coverage_headers})
|
||||
@@ -0,0 +1,194 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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
|
||||
|
||||
// IMPORTANT NOTE:
|
||||
// this may be a header file but will lead to ODR violations if included
|
||||
// more than once. The reason it is in a header file is so that the python
|
||||
// bindings can also build these symbols. The absence of inline is intentional!
|
||||
|
||||
#include "library/coverage.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace coverage
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename Tp, typename... Args>
|
||||
inline std::set<Tp, Args...>
|
||||
get_uncovered(const std::set<Tp, Args...>& _covered,
|
||||
const std::set<Tp, Args...>& _possible)
|
||||
{
|
||||
std::set<Tp, Args...> _v{};
|
||||
for(auto&& itr : _possible)
|
||||
{
|
||||
if(_covered.count(itr) == 0) _v.emplace(itr);
|
||||
}
|
||||
return _v;
|
||||
}
|
||||
//
|
||||
template <typename Tp, typename... Args>
|
||||
inline std::vector<Tp, Args...>
|
||||
get_uncovered(const std::vector<Tp, Args...>& _covered,
|
||||
const std::vector<Tp, Args...>& _possible)
|
||||
{
|
||||
std::vector<Tp, Args...> _v{};
|
||||
for(auto&& itr : _possible)
|
||||
{
|
||||
if(!std::any_of(_covered.begin(), _covered.end(),
|
||||
[itr](auto&& _entry) { return _entry == itr; }))
|
||||
_v.emplace_back(itr);
|
||||
}
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
code_coverage::data&
|
||||
code_coverage::data::operator+=(const data& rhs)
|
||||
{
|
||||
for(auto&& itr : rhs.addresses)
|
||||
addresses.emplace(itr);
|
||||
for(auto&& itr : rhs.modules)
|
||||
modules.emplace(itr);
|
||||
for(auto&& itr : rhs.modules)
|
||||
modules.emplace(itr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
code_coverage::data
|
||||
code_coverage::data::operator+(const data& rhs) const
|
||||
{
|
||||
return data{ *this } += rhs;
|
||||
}
|
||||
|
||||
double
|
||||
code_coverage::operator()(Category _c) const
|
||||
{
|
||||
switch(_c)
|
||||
{
|
||||
case STANDARD: return static_cast<double>(count) / static_cast<double>(size);
|
||||
case ADDRESS:
|
||||
return static_cast<double>(covered.addresses.size()) /
|
||||
static_cast<double>(possible.addresses.size());
|
||||
case MODULE:
|
||||
return static_cast<double>(covered.modules.size()) /
|
||||
static_cast<double>(possible.modules.size());
|
||||
case FUNCTION:
|
||||
return static_cast<double>(covered.functions.size()) /
|
||||
static_cast<double>(possible.functions.size());
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
code_coverage::int_set_t
|
||||
code_coverage::get_uncovered_addresses() const
|
||||
{
|
||||
return get_uncovered(covered.addresses, possible.addresses);
|
||||
}
|
||||
|
||||
code_coverage::str_set_t
|
||||
code_coverage::get_uncovered_modules() const
|
||||
{
|
||||
return get_uncovered(covered.modules, possible.modules);
|
||||
}
|
||||
|
||||
code_coverage::str_set_t
|
||||
code_coverage::get_uncovered_functions() const
|
||||
{
|
||||
return get_uncovered(covered.functions, possible.functions);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
coverage_data&
|
||||
coverage_data::operator+=(const coverage_data& rhs)
|
||||
{
|
||||
count += rhs.count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
coverage_data
|
||||
coverage_data::operator+(const coverage_data& rhs) const
|
||||
{
|
||||
return coverage_data{ *this } += rhs;
|
||||
}
|
||||
|
||||
bool
|
||||
coverage_data::operator==(const coverage_data& rhs) const
|
||||
{
|
||||
return std::tie(module, function, address) ==
|
||||
std::tie(rhs.module, rhs.function, rhs.address);
|
||||
}
|
||||
|
||||
bool
|
||||
coverage_data::operator==(const data_tuple_t& rhs) const
|
||||
{
|
||||
return std::tie(module, function, address) ==
|
||||
std::tie(std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs));
|
||||
}
|
||||
|
||||
bool
|
||||
coverage_data::operator!=(const coverage_data& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
coverage_data::operator<(const coverage_data& rhs) const
|
||||
{
|
||||
if(count != rhs.count) return count < rhs.count;
|
||||
if(module != rhs.module) return module < rhs.module;
|
||||
if(function != rhs.function) return function < rhs.function;
|
||||
if(address != rhs.address) return address < rhs.address;
|
||||
if(line != rhs.line) return line < rhs.line;
|
||||
return source < rhs.source;
|
||||
}
|
||||
|
||||
bool
|
||||
coverage_data::operator<=(const coverage_data& rhs) const
|
||||
{
|
||||
return (*this == rhs || *this < rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
coverage_data::operator>(const coverage_data& rhs) const
|
||||
{
|
||||
return (*this != rhs && !(*this < rhs));
|
||||
}
|
||||
|
||||
bool
|
||||
coverage_data::operator>=(const coverage_data& rhs) const
|
||||
{
|
||||
return !(*this < rhs);
|
||||
}
|
||||
//
|
||||
} // namespace coverage
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,254 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "library/cpu_freq.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/components/cpu_freq.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <timemory/components/rusage/backends.hpp>
|
||||
#include <timemory/mpl/types.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/utility/procfs/cpuinfo.hpp>
|
||||
#include <timemory/utility/type_list.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <sys/resource.h>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace cpu_freq
|
||||
{
|
||||
template <typename... Tp>
|
||||
using type_list = tim::type_list<Tp...>;
|
||||
|
||||
namespace
|
||||
{
|
||||
using cpu_data_tuple_t = std::tuple<size_t, int64_t, int64_t, int64_t, int64_t, int64_t,
|
||||
int64_t, int64_t, component::cpu_freq>;
|
||||
std::deque<cpu_data_tuple_t> data = {};
|
||||
|
||||
template <typename... Types>
|
||||
void init_perfetto_counter_tracks(type_list<Types...>)
|
||||
{
|
||||
(perfetto_counter_track<Types>::init(), ...);
|
||||
}
|
||||
} // namespace
|
||||
} // namespace cpu_freq
|
||||
} // namespace rocprofsys
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace cpu_freq
|
||||
{
|
||||
void
|
||||
setup()
|
||||
{
|
||||
init_perfetto_counter_tracks(
|
||||
type_list<category::cpu_freq, category::process_page, category::process_virt,
|
||||
category::process_peak, category::process_context_switch,
|
||||
category::process_page_fault, category::process_user_mode_time,
|
||||
category::process_kernel_mode_time>{});
|
||||
}
|
||||
|
||||
void
|
||||
config()
|
||||
{
|
||||
component::cpu_freq::configure();
|
||||
}
|
||||
|
||||
void
|
||||
sample()
|
||||
{
|
||||
auto _ts = tim::get_clock_real_now<size_t, std::nano>();
|
||||
|
||||
auto _rcache = tim::rusage_cache{ RUSAGE_SELF };
|
||||
auto _freqs = component::cpu_freq{}.sample();
|
||||
|
||||
// user and kernel mode times are in microseconds
|
||||
data.emplace_back(
|
||||
_ts, tim::get_page_rss(), tim::get_virt_mem(), _rcache.get_peak_rss(),
|
||||
_rcache.get_num_priority_context_switch() +
|
||||
_rcache.get_num_voluntary_context_switch(),
|
||||
_rcache.get_num_major_page_faults() + _rcache.get_num_minor_page_faults(),
|
||||
_rcache.get_user_mode_time() * 1000, _rcache.get_kernel_mode_time() * 1000,
|
||||
std::move(_freqs));
|
||||
}
|
||||
|
||||
void
|
||||
shutdown()
|
||||
{}
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename... Types, size_t N = sizeof...(Types)>
|
||||
void
|
||||
config_perfetto_counter_tracks(type_list<Types...>, std::array<const char*, N> _labels,
|
||||
std::array<const char*, N> _units)
|
||||
{
|
||||
static_assert(sizeof...(Types) == N,
|
||||
"Error! Number of types != number of labels/units");
|
||||
|
||||
auto _config = [&](auto _t) {
|
||||
using type = std::decay_t<decltype(_t)>;
|
||||
using track = perfetto_counter_track<type>;
|
||||
constexpr auto _idx = tim::index_of<type, type_list<Types...>>::value;
|
||||
if(!track::exists(0))
|
||||
{
|
||||
auto addendum = [&](const char* _v) { return JOIN(" ", "CPU", _v, "(S)"); };
|
||||
track::emplace(0, addendum(_labels.at(_idx)), _units.at(_idx));
|
||||
}
|
||||
};
|
||||
|
||||
(_config(Types{}), ...);
|
||||
}
|
||||
|
||||
struct index
|
||||
{
|
||||
size_t value = 0;
|
||||
};
|
||||
|
||||
template <typename Tp, typename... Args>
|
||||
void
|
||||
write_perfetto_counter_track(Args... _args)
|
||||
{
|
||||
using track = perfetto_counter_track<Tp>;
|
||||
TRACE_COUNTER(trait::name<Tp>::value, track::at(0, 0), _args...);
|
||||
}
|
||||
|
||||
template <typename Tp, typename... Args>
|
||||
void
|
||||
write_perfetto_counter_track(index&& _idx, Args... _args)
|
||||
{
|
||||
using track = perfetto_counter_track<Tp>;
|
||||
TRACE_COUNTER(trait::name<Tp>::value, track::at(_idx.value, 0), _args...);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
post_process()
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(1,
|
||||
"Post-processing %zu cpu frequency and memory usage entries...\n",
|
||||
data.size());
|
||||
auto _process_frequencies = [](size_t _idx, size_t _offset) {
|
||||
using freq_track = perfetto_counter_track<category::cpu_freq>;
|
||||
|
||||
const auto& _thread_info = thread_info::get(0, InternalTID);
|
||||
ROCPROFSYS_CI_THROW(!_thread_info, "Missing thread info for thread 0");
|
||||
if(!_thread_info) return;
|
||||
|
||||
if(!freq_track::exists(_idx))
|
||||
{
|
||||
auto addendum = [&](const char* _v) {
|
||||
return JOIN(" ", "CPU", _v, JOIN("", '[', _idx, ']'), "(S)");
|
||||
};
|
||||
freq_track::emplace(_idx, addendum("Frequency"), "MHz");
|
||||
}
|
||||
|
||||
for(auto& itr : data)
|
||||
{
|
||||
uint64_t _ts = std::get<0>(itr);
|
||||
double _freq = static_cast<double>(std::get<8>(itr).at(_offset));
|
||||
if(!_thread_info->is_valid_time(_ts)) continue;
|
||||
write_perfetto_counter_track<category::cpu_freq>(index{ _idx }, _ts, _freq);
|
||||
}
|
||||
|
||||
auto _end_ts = _thread_info->get_stop();
|
||||
write_perfetto_counter_track<category::cpu_freq>(index{ _idx }, _end_ts, 0);
|
||||
};
|
||||
|
||||
auto _process_cpu_rusage = []() {
|
||||
config_perfetto_counter_tracks(
|
||||
type_list<category::process_page, category::process_virt,
|
||||
category::process_peak, category::process_context_switch,
|
||||
category::process_page_fault, category::process_user_mode_time,
|
||||
category::process_kernel_mode_time>{},
|
||||
{ "Memory Usage", "Virtual Memory Usage", "Peak Memory", "Context Switches",
|
||||
"Page Faults", "User Time", "Kernel Time" },
|
||||
{ "MB", "MB", "MB", "", "", "sec", "sec" });
|
||||
|
||||
const auto& _thread_info = thread_info::get(0, InternalTID);
|
||||
ROCPROFSYS_CI_THROW(!_thread_info, "Missing thread info for thread 0");
|
||||
if(!_thread_info) return;
|
||||
|
||||
for(auto& itr : data)
|
||||
{
|
||||
uint64_t _ts = std::get<0>(itr);
|
||||
if(!_thread_info->is_valid_time(_ts)) continue;
|
||||
|
||||
double _page = std::get<1>(itr);
|
||||
double _virt = std::get<2>(itr);
|
||||
double _peak = std::get<3>(itr);
|
||||
uint64_t _cntx = std::get<4>(itr);
|
||||
uint64_t _flts = std::get<5>(itr);
|
||||
double _user = std::get<6>(itr);
|
||||
double _kern = std::get<7>(itr);
|
||||
write_perfetto_counter_track<category::process_page>(_ts,
|
||||
_page / units::megabyte);
|
||||
write_perfetto_counter_track<category::process_virt>(_ts,
|
||||
_virt / units::megabyte);
|
||||
write_perfetto_counter_track<category::process_peak>(_ts,
|
||||
_peak / units::megabyte);
|
||||
write_perfetto_counter_track<category::process_context_switch>(_ts, _cntx);
|
||||
write_perfetto_counter_track<category::process_page_fault>(_ts, _flts);
|
||||
write_perfetto_counter_track<category::process_user_mode_time>(
|
||||
_ts, _user / units::sec);
|
||||
write_perfetto_counter_track<category::process_kernel_mode_time>(
|
||||
_ts, _kern / units::sec);
|
||||
}
|
||||
|
||||
auto _end_ts = _thread_info->get_stop();
|
||||
write_perfetto_counter_track<category::process_page>(_end_ts, 0.0);
|
||||
write_perfetto_counter_track<category::process_virt>(_end_ts, 0.0);
|
||||
write_perfetto_counter_track<category::process_peak>(_end_ts, 0.0);
|
||||
write_perfetto_counter_track<category::process_context_switch>(_end_ts, 0);
|
||||
write_perfetto_counter_track<category::process_page_fault>(_end_ts, 0);
|
||||
write_perfetto_counter_track<category::process_user_mode_time>(_end_ts, 0.0);
|
||||
write_perfetto_counter_track<category::process_kernel_mode_time>(_end_ts, 0.0);
|
||||
};
|
||||
|
||||
_process_cpu_rusage();
|
||||
|
||||
auto& enabled_cpu_freqs = component::cpu_freq::get_enabled_cpus();
|
||||
for(auto itr = enabled_cpu_freqs.begin(); itr != enabled_cpu_freqs.end(); ++itr)
|
||||
{
|
||||
auto _idx = *itr;
|
||||
auto _offset = std::distance(enabled_cpu_freqs.begin(), itr);
|
||||
_process_frequencies(_idx, _offset);
|
||||
}
|
||||
enabled_cpu_freqs.clear();
|
||||
}
|
||||
} // namespace cpu_freq
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,44 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace cpu_freq
|
||||
{
|
||||
void
|
||||
setup();
|
||||
|
||||
void
|
||||
config();
|
||||
|
||||
void
|
||||
sample();
|
||||
|
||||
void
|
||||
shutdown();
|
||||
|
||||
void
|
||||
post_process();
|
||||
} // namespace cpu_freq
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,589 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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.
|
||||
|
||||
#define TIMEMORY_KOKKOSP_POSTFIX ROCPROFSYS_PUBLIC_API
|
||||
|
||||
#include "api.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "library/components/category_region.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
|
||||
#include <timemory/api/kokkosp.hpp>
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/hash/types.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/utility/procfs/maps.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace kokkosp = ::tim::kokkosp;
|
||||
namespace category = ::tim::category;
|
||||
namespace comp = ::rocprofsys::component;
|
||||
|
||||
using kokkosp_region = comp::local_category_region<category::kokkos>;
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
namespace tim
|
||||
{
|
||||
template <>
|
||||
inline auto
|
||||
invoke_preinit<kokkosp::memory_tracker>(long)
|
||||
{
|
||||
kokkosp::memory_tracker::label() = "kokkos_memory";
|
||||
kokkosp::memory_tracker::description() = "Kokkos Memory tracker";
|
||||
}
|
||||
} // namespace tim
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string kokkos_banner =
|
||||
"#---------------------------------------------------------------------------#";
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline void
|
||||
setup_kernel_logger()
|
||||
{
|
||||
if((tim::settings::debug() && tim::settings::verbose() >= 3) ||
|
||||
rocprofsys::config::get_use_kokkosp_kernel_logger())
|
||||
{
|
||||
kokkosp::logger_t::get_initializer() = [](kokkosp::logger_t& _obj) {
|
||||
_obj.initialize<kokkosp::kernel_logger>();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace
|
||||
{
|
||||
bool _standalone_initialized = false;
|
||||
bool _kp_deep_copy = false;
|
||||
size_t _name_len_limit = 0;
|
||||
std::string _kp_prefix = {};
|
||||
std::vector<std::string> _initialize_arguments = {};
|
||||
|
||||
template <typename Tp>
|
||||
void
|
||||
set_invalid_id(Tp* _v)
|
||||
{
|
||||
constexpr bool is32 = std::is_same<Tp, uint32_t>::value;
|
||||
constexpr bool is64 = std::is_same<Tp, uint64_t>::value;
|
||||
static_assert(is32 || is64, "only support uint32_t or uint64_t");
|
||||
|
||||
*_v = std::numeric_limits<Tp>::max();
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
is_invalid_id(Tp _v)
|
||||
{
|
||||
constexpr bool is32 = std::is_same<Tp, uint32_t>::value;
|
||||
constexpr bool is64 = std::is_same<Tp, uint64_t>::value;
|
||||
static_assert(is32 || is64, "only support uint32_t or uint64_t");
|
||||
|
||||
return (_v == std::numeric_limits<Tp>::max());
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
auto
|
||||
strlength(Tp&& _v)
|
||||
{
|
||||
using type = ::tim::concepts::unqualified_type_t<Tp>;
|
||||
if constexpr(std::is_same<type, std::string_view>::value ||
|
||||
std::is_same<type, std::string>::value)
|
||||
return _v.length();
|
||||
else
|
||||
return strnlen(_v, std::max<size_t>(_name_len_limit, 1));
|
||||
}
|
||||
|
||||
template <typename Arg, typename... Args>
|
||||
bool
|
||||
violates_name_rules(Arg&& _arg, Args&&... _args)
|
||||
{
|
||||
// for causal profiling we only consider callbacks which are explicitly named
|
||||
if(rocprofsys::config::get_use_causal() &&
|
||||
(std::string_view{ _arg }.find("Kokkos::") == 0 ||
|
||||
std::string_view{ _arg }.find("Space::") != std::string_view::npos))
|
||||
return true;
|
||||
|
||||
size_t _len =
|
||||
(strlength(std::forward<Arg>(_arg)) + ... + strlength(std::forward<Args>(_args)));
|
||||
|
||||
// ignore labels without names
|
||||
if(_len == 0)
|
||||
return true;
|
||||
else if(_name_len_limit == 0)
|
||||
return false;
|
||||
|
||||
return (_len >= _name_len_limit);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
extern "C"
|
||||
{
|
||||
struct Kokkos_Tools_ToolSettings
|
||||
{
|
||||
bool requires_global_fencing;
|
||||
bool padding[255];
|
||||
};
|
||||
|
||||
void kokkosp_request_tool_settings(const uint32_t,
|
||||
Kokkos_Tools_ToolSettings*) ROCPROFSYS_PUBLIC_API;
|
||||
void kokkosp_dual_view_sync(const char*, const void* const,
|
||||
bool) ROCPROFSYS_PUBLIC_API;
|
||||
void kokkosp_dual_view_modify(const char*, const void* const,
|
||||
bool) ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
void kokkosp_print_help(char*) {}
|
||||
|
||||
void kokkosp_parse_args(int argc, char** argv)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
if(!rocprofsys::config::settings_are_configured() &&
|
||||
rocprofsys::get_state() < rocprofsys::State::Active)
|
||||
{
|
||||
_standalone_initialized = true;
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(0, "Parsing arguments...\n");
|
||||
std::string _command_line = {};
|
||||
for(int i = 0; i < argc; ++i)
|
||||
{
|
||||
_initialize_arguments.emplace_back(argv[i]);
|
||||
_command_line.append(" ").append(argv[i]);
|
||||
}
|
||||
if(_command_line.length() > 1) _command_line = _command_line.substr(1);
|
||||
tim::set_env("ROCPROFSYS_COMMAND_LINE", _command_line, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void kokkosp_declare_metadata(const char* key, const char* value)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
tim::manager::add_metadata(key, value);
|
||||
}
|
||||
|
||||
void kokkosp_request_tool_settings(const uint32_t _version,
|
||||
Kokkos_Tools_ToolSettings* _settings)
|
||||
{
|
||||
if(_version > 0) _settings->requires_global_fencing = false;
|
||||
}
|
||||
|
||||
void kokkosp_init_library(const int loadSeq, const uint64_t interfaceVer,
|
||||
const uint32_t devInfoCount, void* deviceInfo)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
tim::consume_parameters(devInfoCount, deviceInfo);
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(
|
||||
0,
|
||||
"Initializing rocprof-sys kokkos connector (sequence %d, version: %llu)... ",
|
||||
loadSeq, (unsigned long long) interfaceVer);
|
||||
|
||||
if(_standalone_initialized ||
|
||||
(!rocprofsys::config::settings_are_configured() &&
|
||||
rocprofsys::get_state() < rocprofsys::State::Active))
|
||||
{
|
||||
auto _kokkos_profile_lib =
|
||||
tim::get_env<std::string>("KOKKOS_PROFILE_LIBRARY");
|
||||
if(_kokkos_profile_lib.find("librocprof-sys.so") != std::string::npos)
|
||||
{
|
||||
auto _maps = tim::procfs::read_maps(tim::process::get_id());
|
||||
auto _libs = std::set<std::string>{};
|
||||
for(auto& itr : _maps)
|
||||
{
|
||||
auto&& _path = itr.pathname;
|
||||
if(!_path.empty() && _path.at(0) != '[' &&
|
||||
rocprofsys::filepath::exists(_path))
|
||||
_libs.emplace(_path);
|
||||
}
|
||||
for(const auto& itr : _libs)
|
||||
{
|
||||
if(itr.find("librocprof-sys-dl.so") != std::string::npos)
|
||||
{
|
||||
std::stringstream _libs_str{};
|
||||
for(const auto& litr : _libs)
|
||||
_libs_str << " " << litr << "\n";
|
||||
ROCPROFSYS_ABORT(
|
||||
"%s was invoked with librocprof-sys.so as the "
|
||||
"KOKKOS_PROFILE_LIBRARY.\n"
|
||||
"However, librocprof-sys-dl.so has already been loaded by "
|
||||
"the process.\nTo avoid duplicate collections culminating "
|
||||
"is an error, please set KOKKOS_PROFILE_LIBRARY=%s.\nLoaded "
|
||||
"libraries:\n%s",
|
||||
__FUNCTION__, itr.c_str(), _libs_str.str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(0, "Initializing rocprof-sys (standalone)... ");
|
||||
auto _mode = tim::get_env<std::string>("ROCPROFSYS_MODE", "trace");
|
||||
auto _arg0 = (_initialize_arguments.empty()) ? std::string{ "unknown" }
|
||||
: _initialize_arguments.at(0);
|
||||
|
||||
_standalone_initialized = true;
|
||||
rocprofsys_set_mpi_hidden(false, false);
|
||||
rocprofsys_init_hidden(_mode.c_str(), false, _arg0.c_str());
|
||||
rocprofsys_push_trace_hidden("kokkos_main");
|
||||
}
|
||||
|
||||
setup_kernel_logger();
|
||||
|
||||
tim::trait::runtime_enabled<kokkosp::memory_tracker>::set(
|
||||
rocprofsys::config::get_use_timemory());
|
||||
|
||||
if(rocprofsys::get_verbose() >= 0)
|
||||
{
|
||||
fprintf(stderr, "%sDone\n%s", tim::log::color::info(),
|
||||
tim::log::color::end());
|
||||
}
|
||||
|
||||
_name_len_limit = rocprofsys::config::get_setting_value<int64_t>(
|
||||
"ROCPROFSYS_KOKKOSP_NAME_LENGTH_MAX")
|
||||
.value_or(_name_len_limit);
|
||||
_kp_prefix = rocprofsys::config::get_setting_value<std::string>(
|
||||
"ROCPROFSYS_KOKKOSP_PREFIX")
|
||||
.value_or(_kp_prefix);
|
||||
|
||||
_kp_deep_copy =
|
||||
rocprofsys::config::get_setting_value<bool>("ROCPROFSYS_KOKKOSP_DEEP_COPY")
|
||||
.value_or(_kp_deep_copy);
|
||||
}
|
||||
|
||||
void kokkosp_finalize_library()
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
if(_standalone_initialized)
|
||||
{
|
||||
rocprofsys_pop_trace_hidden("kokkos_main");
|
||||
ROCPROFSYS_VERBOSE_F(
|
||||
0, "Finalizing kokkos rocprof-sys connector (standalone)...\n");
|
||||
rocprofsys_finalize_hidden();
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(0, "Finalizing kokkos rocprof-sys connector... ");
|
||||
kokkosp::cleanup();
|
||||
if(rocprofsys::get_verbose() >= 0) fprintf(stderr, "Done\n");
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_begin_parallel_for(const char* name, uint32_t devid, uint64_t* kernid)
|
||||
{
|
||||
if(violates_name_rules(name)) return set_invalid_id(kernid);
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
auto pname = (devid > std::numeric_limits<uint16_t>::max()) // junk device number
|
||||
? JOIN(" ", _kp_prefix, name, "[for]")
|
||||
: JOIN(" ", _kp_prefix, name, JOIN("", "[for][dev", devid, ']'));
|
||||
*kernid = kokkosp::get_unique_id();
|
||||
kokkosp::logger_t{}.mark(1, __FUNCTION__, name, *kernid);
|
||||
kokkosp::create_profiler<kokkosp_region>(pname, *kernid);
|
||||
kokkosp::start_profiler<kokkosp_region>(*kernid);
|
||||
}
|
||||
|
||||
void kokkosp_end_parallel_for(uint64_t kernid)
|
||||
{
|
||||
if(is_invalid_id(kernid)) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(-1, __FUNCTION__, kernid);
|
||||
kokkosp::stop_profiler<kokkosp_region>(kernid);
|
||||
kokkosp::destroy_profiler<kokkosp_region>(kernid);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_begin_parallel_reduce(const char* name, uint32_t devid, uint64_t* kernid)
|
||||
{
|
||||
if(violates_name_rules(name)) return set_invalid_id(kernid);
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
auto pname =
|
||||
(devid > std::numeric_limits<uint16_t>::max()) // junk device number
|
||||
? JOIN(" ", _kp_prefix, name, "[reduce]")
|
||||
: JOIN(" ", _kp_prefix, name, JOIN("", "[reduce][dev", devid, ']'));
|
||||
*kernid = kokkosp::get_unique_id();
|
||||
kokkosp::logger_t{}.mark(1, __FUNCTION__, name, *kernid);
|
||||
kokkosp::create_profiler<kokkosp_region>(pname, *kernid);
|
||||
kokkosp::start_profiler<kokkosp_region>(*kernid);
|
||||
}
|
||||
|
||||
void kokkosp_end_parallel_reduce(uint64_t kernid)
|
||||
{
|
||||
if(is_invalid_id(kernid)) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(-1, __FUNCTION__, kernid);
|
||||
kokkosp::stop_profiler<kokkosp_region>(kernid);
|
||||
kokkosp::destroy_profiler<kokkosp_region>(kernid);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_begin_parallel_scan(const char* name, uint32_t devid, uint64_t* kernid)
|
||||
{
|
||||
if(violates_name_rules(name)) return set_invalid_id(kernid);
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
auto pname =
|
||||
(devid > std::numeric_limits<uint16_t>::max()) // junk device number
|
||||
? JOIN(" ", _kp_prefix, name, "[scan]")
|
||||
: JOIN(" ", _kp_prefix, name, JOIN("", "[scan][dev", devid, ']'));
|
||||
*kernid = kokkosp::get_unique_id();
|
||||
kokkosp::logger_t{}.mark(1, __FUNCTION__, name, *kernid);
|
||||
kokkosp::create_profiler<kokkosp_region>(pname, *kernid);
|
||||
kokkosp::start_profiler<kokkosp_region>(*kernid);
|
||||
}
|
||||
|
||||
void kokkosp_end_parallel_scan(uint64_t kernid)
|
||||
{
|
||||
if(is_invalid_id(kernid)) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(-1, __FUNCTION__, kernid);
|
||||
kokkosp::stop_profiler<kokkosp_region>(kernid);
|
||||
kokkosp::destroy_profiler<kokkosp_region>(kernid);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_begin_fence(const char* name, uint32_t devid, uint64_t* kernid)
|
||||
{
|
||||
if(violates_name_rules(name)) return set_invalid_id(kernid);
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
auto pname =
|
||||
(devid > std::numeric_limits<uint16_t>::max()) // junk device number
|
||||
? JOIN(" ", _kp_prefix, name, "[fence]")
|
||||
: JOIN(" ", _kp_prefix, name, JOIN("", "[fence][dev", devid, ']'));
|
||||
*kernid = kokkosp::get_unique_id();
|
||||
kokkosp::logger_t{}.mark(1, __FUNCTION__, name, *kernid);
|
||||
kokkosp::create_profiler<kokkosp_region>(pname, *kernid);
|
||||
kokkosp::start_profiler<kokkosp_region>(*kernid);
|
||||
}
|
||||
|
||||
void kokkosp_end_fence(uint64_t kernid)
|
||||
{
|
||||
if(is_invalid_id(kernid)) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(-1, __FUNCTION__, kernid);
|
||||
kokkosp::stop_profiler<kokkosp_region>(kernid);
|
||||
kokkosp::destroy_profiler<kokkosp_region>(kernid);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_push_profile_region(const char* name)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(1, __FUNCTION__, name);
|
||||
kokkosp::get_profiler_stack<kokkosp_region>()
|
||||
.emplace_back(kokkosp::profiler_t<kokkosp_region>(name))
|
||||
.start();
|
||||
}
|
||||
|
||||
void kokkosp_pop_profile_region()
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(-1, __FUNCTION__);
|
||||
if(kokkosp::get_profiler_stack<kokkosp_region>().empty()) return;
|
||||
kokkosp::get_profiler_stack<kokkosp_region>().back().stop();
|
||||
kokkosp::get_profiler_stack<kokkosp_region>().pop_back();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_create_profile_section(const char* name, uint32_t* secid)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
*secid = kokkosp::get_unique_id();
|
||||
auto pname = std::string{ name };
|
||||
kokkosp::create_profiler<kokkosp_region>(name, *secid);
|
||||
}
|
||||
|
||||
void kokkosp_destroy_profile_section(uint32_t secid)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::destroy_profiler<kokkosp_region>(secid);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_start_profile_section(uint32_t secid)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(1, __FUNCTION__, secid);
|
||||
kokkosp::start_profiler<kokkosp_region>(secid);
|
||||
}
|
||||
|
||||
void kokkosp_stop_profile_section(uint32_t secid)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(-1, __FUNCTION__, secid);
|
||||
kokkosp::stop_profiler<kokkosp_region>(secid);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_allocate_data(const SpaceHandle space, const char* label,
|
||||
const void* const ptr, const uint64_t size)
|
||||
{
|
||||
if(violates_name_rules(label)) return;
|
||||
if(rocprofsys::config::get_use_causal()) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(0, __FUNCTION__, space.name, label,
|
||||
JOIN("", '[', ptr, ']'), size);
|
||||
auto pname =
|
||||
JOIN(" ", _kp_prefix, label, JOIN("", '[', space.name, "][allocate]"));
|
||||
kokkosp::profiler_alloc_t<>{ pname }.store(std::plus<int64_t>{}, size);
|
||||
kokkosp::profiler_t<kokkosp_region>{ pname }.mark();
|
||||
}
|
||||
|
||||
void kokkosp_deallocate_data(const SpaceHandle space, const char* label,
|
||||
const void* const ptr, const uint64_t size)
|
||||
{
|
||||
if(violates_name_rules(label)) return;
|
||||
if(rocprofsys::config::get_use_causal()) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(0, __FUNCTION__, space.name, label,
|
||||
JOIN("", '[', ptr, ']'), size);
|
||||
auto pname =
|
||||
JOIN(" ", _kp_prefix, label, JOIN("", '[', space.name, "][deallocate]"));
|
||||
kokkosp::profiler_alloc_t<>{ pname }.store(std::plus<int64_t>{}, size);
|
||||
kokkosp::profiler_t<kokkosp_region>{ pname }.mark();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_begin_deep_copy(SpaceHandle dst_handle, const char* dst_name,
|
||||
const void* dst_ptr, SpaceHandle src_handle,
|
||||
const char* src_name, const void* src_ptr, uint64_t size)
|
||||
{
|
||||
if(!_kp_deep_copy || rocprofsys::config::get_use_causal()) return;
|
||||
if(violates_name_rules(dst_name, src_name)) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(1, __FUNCTION__, dst_handle.name, dst_name,
|
||||
JOIN("", '[', dst_ptr, ']'), src_handle.name, src_name,
|
||||
JOIN("", '[', src_ptr, ']'), size);
|
||||
|
||||
auto name = JOIN(" ", _kp_prefix, JOIN('=', dst_handle.name, dst_name), "<-",
|
||||
JOIN('=', src_handle.name, src_name), "[deep_copy]");
|
||||
|
||||
auto& _data = kokkosp::get_profiler_stack<kokkosp_region>();
|
||||
_data.emplace_back(name);
|
||||
_data.back().audit(dst_handle, dst_name, dst_ptr, src_handle, src_name, src_ptr,
|
||||
size);
|
||||
_data.back().start();
|
||||
_data.back().store(tim::mpl::piecewise_select<kokkosp::memory_tracker>{},
|
||||
std::plus<int64_t>{}, size);
|
||||
}
|
||||
|
||||
void kokkosp_end_deep_copy()
|
||||
{
|
||||
if(!_kp_deep_copy || rocprofsys::config::get_use_causal()) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
kokkosp::logger_t{}.mark(-1, __FUNCTION__);
|
||||
auto& _data = kokkosp::get_profiler_stack<kokkosp_region>();
|
||||
if(_data.empty()) return;
|
||||
_data.back().store(tim::mpl::piecewise_select<kokkosp::memory_tracker>{},
|
||||
std::minus<int64_t>{}, 0);
|
||||
_data.back().stop();
|
||||
_data.pop_back();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_profile_event(const char* name)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
auto _name = tim::get_hash_identifier_fast(tim::add_hash_id(name));
|
||||
kokkosp::profiler_t<kokkosp_region>{ _name }.mark();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
void kokkosp_dual_view_sync(const char* label, const void* const, bool is_device)
|
||||
{
|
||||
if(violates_name_rules(label)) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
if(rocprofsys::config::get_use_perfetto())
|
||||
{
|
||||
auto _name = tim::get_hash_identifier_fast(
|
||||
tim::add_hash_id(JOIN(" ", _kp_prefix, label, "[dual_view_sync]")));
|
||||
TRACE_EVENT_INSTANT("user", ::perfetto::StaticString{ _name.data() },
|
||||
"target", (is_device) ? "device" : "host");
|
||||
}
|
||||
else if(rocprofsys::config::get_use_causal())
|
||||
{
|
||||
auto _name = tim::get_hash_identifier_fast(tim::add_hash_id(JOIN(
|
||||
"", label, " [dual_view_sync][", (is_device) ? "device" : "host", "]")));
|
||||
kokkosp::profiler_t<kokkosp_region>{ _name }.mark();
|
||||
}
|
||||
}
|
||||
|
||||
void kokkosp_dual_view_modify(const char* label, const void* const, bool is_device)
|
||||
{
|
||||
if(violates_name_rules(label)) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
if(rocprofsys::config::get_use_perfetto())
|
||||
{
|
||||
auto _name = tim::get_hash_identifier_fast(
|
||||
tim::add_hash_id(JOIN(" ", _kp_prefix, label, "[dual_view_modify]")));
|
||||
TRACE_EVENT_INSTANT("user", ::perfetto::StaticString{ _name.data() },
|
||||
"target", (is_device) ? "device" : "host");
|
||||
}
|
||||
else if(rocprofsys::config::get_use_causal())
|
||||
{
|
||||
auto _name = tim::get_hash_identifier_fast(
|
||||
tim::add_hash_id(JOIN(" ", _kp_prefix, label, "[dual_view_modify][",
|
||||
(is_device) ? "device" : "host", "]")));
|
||||
kokkosp::profiler_t<kokkosp_region>{ _name }.mark();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
}
|
||||
|
||||
TIMEMORY_INITIALIZE_STORAGE(kokkosp::memory_tracker)
|
||||
@@ -0,0 +1,175 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "api.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
|
||||
#include <timemory/defines.h>
|
||||
|
||||
#if defined(ROCPROFSYS_USE_OMPT) && ROCPROFSYS_USE_OMPT > 0
|
||||
|
||||
# include "core/components/fwd.hpp"
|
||||
# include "library/components/category_region.hpp"
|
||||
|
||||
# include <timemory/components/ompt.hpp>
|
||||
# include <timemory/components/ompt/extern.hpp>
|
||||
# include <timemory/mpl/type_traits.hpp>
|
||||
# include <timemory/timemory.hpp>
|
||||
|
||||
# include <memory>
|
||||
|
||||
using api_t = TIMEMORY_API;
|
||||
using ompt_handle_t = tim::component::ompt_handle<api_t>;
|
||||
using ompt_context_t = tim::openmp::context_handler<api_t>;
|
||||
using ompt_toolset_t = typename ompt_handle_t::toolset_type;
|
||||
using ompt_bundle_t = tim::component_tuple<ompt_handle_t>;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
ompt_start_tool_result_t* ompt_start_tool(unsigned int,
|
||||
const char*) ROCPROFSYS_PUBLIC_API;
|
||||
}
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace ompt
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::unique_ptr<ompt_bundle_t> f_bundle = {};
|
||||
bool _init_toolset_off = (trait::runtime_enabled<ompt_toolset_t>::set(false),
|
||||
trait::runtime_enabled<ompt_context_t>::set(false), true);
|
||||
tim::ompt::finalize_tool_func_t f_finalize = nullptr;
|
||||
} // namespace
|
||||
|
||||
void
|
||||
setup()
|
||||
{
|
||||
if(!tim::settings::enabled()) return;
|
||||
trait::runtime_enabled<ompt_toolset_t>::set(true);
|
||||
trait::runtime_enabled<ompt_context_t>::set(true);
|
||||
comp::user_ompt_bundle::global_init();
|
||||
comp::user_ompt_bundle::reset();
|
||||
tim::auto_lock_t lk{ tim::type_mutex<ompt_handle_t>() };
|
||||
comp::user_ompt_bundle::configure<component::local_category_region<category::ompt>>();
|
||||
f_bundle = std::make_unique<ompt_bundle_t>("rocprofsys/ompt",
|
||||
quirk::config<quirk::auto_start>{});
|
||||
}
|
||||
|
||||
void
|
||||
shutdown()
|
||||
{
|
||||
static bool _protect = false;
|
||||
if(_protect) return;
|
||||
_protect = true;
|
||||
if(f_bundle)
|
||||
{
|
||||
if(tim::manager::instance()) tim::manager::instance()->cleanup("rocprofsys-ompt");
|
||||
f_bundle->stop();
|
||||
ompt_context_t::cleanup();
|
||||
trait::runtime_enabled<ompt_toolset_t>::set(false);
|
||||
trait::runtime_enabled<ompt_context_t>::set(false);
|
||||
comp::user_ompt_bundle::reset();
|
||||
pthread_gotcha::shutdown();
|
||||
// call the OMPT finalize callback
|
||||
if(f_finalize) (*f_finalize)();
|
||||
}
|
||||
f_bundle.reset();
|
||||
_protect = false;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool&
|
||||
use_tool()
|
||||
{
|
||||
static bool _v = false;
|
||||
return _v;
|
||||
}
|
||||
|
||||
int
|
||||
tool_initialize(ompt_function_lookup_t lookup, int initial_device_num,
|
||||
ompt_data_t* tool_data)
|
||||
{
|
||||
if(!rocprofsys::settings_are_configured())
|
||||
{
|
||||
ROCPROFSYS_BASIC_WARNING(
|
||||
0,
|
||||
"[%s] invoked before rocprof-sys was initialized. In instrumentation mode, "
|
||||
"settings exported to the environment have not been propagated yet...\n",
|
||||
__FUNCTION__);
|
||||
rocprofsys::configure_settings();
|
||||
}
|
||||
|
||||
use_tool() = rocprofsys::config::get_use_ompt();
|
||||
if(use_tool())
|
||||
{
|
||||
TIMEMORY_PRINTF(stderr, "OpenMP-tools configuring for initial device %i\n\n",
|
||||
initial_device_num);
|
||||
f_finalize = tim::ompt::configure<TIMEMORY_OMPT_API_TAG>(
|
||||
lookup, initial_device_num, tool_data);
|
||||
}
|
||||
return 1; // success
|
||||
}
|
||||
|
||||
void
|
||||
tool_finalize(ompt_data_t*)
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
} // namespace
|
||||
} // namespace ompt
|
||||
} // namespace rocprofsys
|
||||
|
||||
extern "C" ompt_start_tool_result_t*
|
||||
ompt_start_tool(unsigned int omp_version, const char* runtime_version)
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(0, "OpenMP version: %u, runtime version: %s\n",
|
||||
omp_version, runtime_version);
|
||||
ROCPROFSYS_METADATA("OMP_VERSION", omp_version);
|
||||
ROCPROFSYS_METADATA("OMP_RUNTIME_VERSION", runtime_version);
|
||||
|
||||
static auto* data = new ompt_start_tool_result_t{ &rocprofsys::ompt::tool_initialize,
|
||||
&rocprofsys::ompt::tool_finalize,
|
||||
{ 0 } };
|
||||
return data;
|
||||
}
|
||||
|
||||
#else
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace ompt
|
||||
{
|
||||
void
|
||||
setup()
|
||||
{}
|
||||
|
||||
void
|
||||
shutdown()
|
||||
{}
|
||||
} // namespace ompt
|
||||
} // namespace rocprofsys
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace ompt
|
||||
{
|
||||
void
|
||||
setup();
|
||||
|
||||
void
|
||||
shutdown();
|
||||
} // namespace ompt
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,666 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/perf.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/locking.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/log/logger.hpp>
|
||||
#include <timemory/log/macros.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
|
||||
#include <asm/unistd.h>
|
||||
#include <ctime>
|
||||
#include <fcntl.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <mutex>
|
||||
#include <poll.h>
|
||||
#include <regex>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(ROCPROFSYS_RETURN_ERROR_MSG)
|
||||
# define ROCPROFSYS_RETURN_ERROR_MSG(COND, ...) \
|
||||
if((COND)) \
|
||||
{ \
|
||||
auto _msg_ss = std::stringstream{}; \
|
||||
_msg_ss << __VA_ARGS__; \
|
||||
return std::optional<std::string>{ _msg_ss.str() }; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(ROCPROFSYS_FATAL)
|
||||
# define ROCPROFSYS_FATAL TIMEMORY_FATAL
|
||||
#endif
|
||||
|
||||
#if !defined(ROCPROFSYS_ASSERT)
|
||||
# define ROCPROFSYS_ASSERT(COND) (COND) ? ::tim::log::base() : TIMEMORY_FATAL
|
||||
#endif
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace perf
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct SizeParams
|
||||
{
|
||||
const size_t num_pages = 2;
|
||||
const size_t page = units::get_page_size();
|
||||
const size_t data = num_pages * page;
|
||||
const size_t mmap = data + page;
|
||||
};
|
||||
const SizeParams sizes = {};
|
||||
} // namespace
|
||||
|
||||
long
|
||||
perf_event_open(struct perf_event_attr* hw_event, pid_t _pid, int _cpu, int group_fd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return syscall(__NR_perf_event_open, hw_event, _pid, _cpu, group_fd, flags);
|
||||
}
|
||||
|
||||
/// Move constructor
|
||||
perf_event::perf_event(perf_event&& rhs) noexcept
|
||||
{
|
||||
// Release resources if the current perf_event is initialized and not equal to this
|
||||
// one
|
||||
if(m_fd != -1 && m_fd != rhs.m_fd)
|
||||
{
|
||||
::close(m_fd);
|
||||
ROCPROFSYS_VERBOSE(1, "Closed perf event fd %li\n", m_fd);
|
||||
}
|
||||
|
||||
if(m_mapping != nullptr && m_mapping != rhs.m_mapping) munmap(m_mapping, sizes.mmap);
|
||||
|
||||
// take rhs perf event's file descriptor and replace it with -1
|
||||
m_fd = rhs.m_fd;
|
||||
rhs.m_fd = -1;
|
||||
|
||||
// take rhs perf_event's mapping and replace it with nullptr
|
||||
m_mapping = rhs.m_mapping;
|
||||
rhs.m_mapping = nullptr;
|
||||
|
||||
// Copy over the sample type and read format
|
||||
m_sample_type = rhs.m_sample_type;
|
||||
m_read_format = rhs.m_read_format;
|
||||
}
|
||||
|
||||
/// Close the perf_event file descriptor and unmap the ring buffer
|
||||
perf_event::~perf_event() { close(); }
|
||||
|
||||
/// Move assignment
|
||||
perf_event&
|
||||
perf_event::operator=(perf_event&& rhs) noexcept
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
if(&rhs == this) return *this;
|
||||
|
||||
// Release resources if the current perf_event is initialized and not equal to this
|
||||
// one
|
||||
if(m_fd != -1 && m_fd != rhs.m_fd) ::close(m_fd);
|
||||
if(m_mapping != nullptr && m_mapping != rhs.m_mapping) munmap(m_mapping, sizes.mmap);
|
||||
|
||||
// take rhs perf event's file descriptor and replace it with -1
|
||||
m_fd = rhs.m_fd;
|
||||
rhs.m_fd = -1;
|
||||
|
||||
// take rhs perf_event's mapping and replace it with nullptr
|
||||
m_mapping = rhs.m_mapping;
|
||||
rhs.m_mapping = nullptr;
|
||||
|
||||
// Copy over the sample type and read format
|
||||
m_sample_type = rhs.m_sample_type;
|
||||
m_read_format = rhs.m_read_format;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Open a perf_event file and map it (if sampling is enabled)
|
||||
std::optional<std::string>
|
||||
perf_event::open(struct perf_event_attr& _pe, pid_t _pid, int _cpu)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
m_sample_type = _pe.sample_type;
|
||||
m_read_format = _pe.read_format;
|
||||
m_batch_size = _pe.wakeup_events;
|
||||
|
||||
// Set some mandatory fields
|
||||
_pe.size = sizeof(struct perf_event_attr);
|
||||
_pe.disabled = 1;
|
||||
|
||||
// Open the file
|
||||
m_fd = perf_event_open(&_pe, _pid, _cpu, -1, 0);
|
||||
if(m_fd == -1)
|
||||
{
|
||||
std::string path = "/proc/sys/kernel/perf_event_paranoid";
|
||||
|
||||
auto file = std::ifstream{ path.c_str() };
|
||||
|
||||
ROCPROFSYS_RETURN_ERROR_MSG(!file,
|
||||
"Failed to open " << path << ": " << strerror(errno));
|
||||
|
||||
int value = 4;
|
||||
file >> value;
|
||||
|
||||
ROCPROFSYS_RETURN_ERROR_MSG(file.bad(), "Failed to read from "
|
||||
<< path << ": " << strerror(errno));
|
||||
|
||||
ROCPROFSYS_RETURN_ERROR_MSG(
|
||||
true, "Failed to open perf event. Consider tweaking "
|
||||
<< path << " to 2 or less "
|
||||
<< "(current value is " << value << "), "
|
||||
<< "or run rocprof-sys as a privileged user (with CAP_SYS_ADMIN).");
|
||||
}
|
||||
|
||||
// If sampling, map the perf event file
|
||||
if(_pe.sample_type != 0 && _pe.sample_period != 0)
|
||||
{
|
||||
void* ring_buffer =
|
||||
mmap(nullptr, sizes.mmap, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
|
||||
|
||||
ROCPROFSYS_RETURN_ERROR_MSG(
|
||||
ring_buffer == MAP_FAILED,
|
||||
"Mapping perf_event ring buffer failed. Make sure the current user has "
|
||||
"permission to invoke the perf tool, and that the program being profiled "
|
||||
"does not use an excessive number of threads (>1000)");
|
||||
|
||||
m_mapping = reinterpret_cast<struct perf_event_mmap_page*>(ring_buffer);
|
||||
}
|
||||
|
||||
return std::optional<std::string>{};
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
perf_event::open(double _freq, uint32_t _batch_size, pid_t _pid, int _cpu)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
uint64_t _period = (1.0 / _freq) * units::sec;
|
||||
struct perf_event_attr _pe;
|
||||
|
||||
if(_batch_size > 0)
|
||||
m_batch_size = _batch_size;
|
||||
else
|
||||
_batch_size = m_batch_size;
|
||||
|
||||
memset(&_pe, 0, sizeof(_pe));
|
||||
_pe.type = PERF_TYPE_SOFTWARE;
|
||||
_pe.config = PERF_COUNT_SW_TASK_CLOCK;
|
||||
_pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_CALLCHAIN;
|
||||
_pe.sample_period = _period;
|
||||
_pe.wakeup_events = _batch_size;
|
||||
_pe.exclude_idle = 1;
|
||||
_pe.exclude_kernel = 1;
|
||||
_pe.disabled = 1;
|
||||
// potential additions
|
||||
_pe.inherit = 0;
|
||||
_pe.exclude_hv = 1;
|
||||
_pe.exclude_callchain_kernel = 1;
|
||||
_pe.use_clockid = 1;
|
||||
_pe.clockid = CLOCK_REALTIME;
|
||||
// _pe.precise_ip = 0;
|
||||
// _pe.exclusive = 1;
|
||||
// _pe.pinned = 1;
|
||||
|
||||
return open(_pe, _pid, _cpu);
|
||||
}
|
||||
|
||||
/// Read event count
|
||||
long
|
||||
perf_event::get_fileno() const
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
/// Read event count
|
||||
uint64_t
|
||||
perf_event::get_count() const
|
||||
{
|
||||
uint64_t count;
|
||||
ROCPROFSYS_REQUIRE(read(m_fd, &count, sizeof(uint64_t)) == sizeof(uint64_t))
|
||||
<< "Failed to read event count from perf_event file";
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Start counting events
|
||||
bool
|
||||
perf_event::start() const
|
||||
{
|
||||
if(m_fd != -1)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
ROCPROFSYS_REQUIRE(ioctl(m_fd, PERF_EVENT_IOC_ENABLE, 0) != -1)
|
||||
<< "Failed to start perf event: " << strerror(errno);
|
||||
}
|
||||
return (m_fd != -1);
|
||||
}
|
||||
|
||||
/// Stop counting events
|
||||
bool
|
||||
perf_event::stop() const
|
||||
{
|
||||
if(m_fd != -1)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
ROCPROFSYS_REQUIRE(ioctl(m_fd, PERF_EVENT_IOC_DISABLE, 0) != -1)
|
||||
<< "Failed to stop perf event: " << strerror(errno) << " (" << m_fd << ")";
|
||||
}
|
||||
return (m_fd != -1);
|
||||
}
|
||||
|
||||
bool
|
||||
perf_event::is_open() const
|
||||
{
|
||||
return (m_fd != -1);
|
||||
}
|
||||
|
||||
void
|
||||
perf_event::close()
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
stop();
|
||||
|
||||
if(m_fd != -1)
|
||||
{
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
if(m_mapping != nullptr)
|
||||
{
|
||||
munmap(m_mapping, sizes.mmap);
|
||||
m_mapping = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
perf_event::set_ready_signal(int sig) const
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
// Set the perf_event file to async
|
||||
ROCPROFSYS_REQUIRE(fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL, 0) | O_ASYNC) != -1)
|
||||
<< "failed to set perf_event file to async mode";
|
||||
|
||||
// Set the notification signal for the perf file
|
||||
ROCPROFSYS_REQUIRE(fcntl(m_fd, F_SETSIG, sig) != -1)
|
||||
<< "failed to set perf_event file signal";
|
||||
|
||||
// Set the current thread as the owner of the file (to target signal delivery)
|
||||
ROCPROFSYS_REQUIRE(fcntl(m_fd, F_SETOWN, gettid()) != -1)
|
||||
<< "failed to set the owner of the perf_event file";
|
||||
}
|
||||
|
||||
void
|
||||
perf_event::iterator::next()
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
struct perf_event_header _hdr;
|
||||
|
||||
// Copy out the record header
|
||||
perf_event::copy_from_ring_buffer(m_mapping, m_index, &_hdr,
|
||||
sizeof(struct perf_event_header));
|
||||
|
||||
// Advance to the next record
|
||||
m_index += _hdr.size;
|
||||
}
|
||||
|
||||
perf_event::iterator::iterator(perf_event& _source, struct perf_event_mmap_page* _mapping)
|
||||
: m_source{ _source }
|
||||
, m_mapping{ _mapping }
|
||||
{
|
||||
if(_mapping != nullptr)
|
||||
{
|
||||
m_index = _mapping->data_tail;
|
||||
m_head = _mapping->data_head;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_index = 0;
|
||||
m_head = 0;
|
||||
}
|
||||
}
|
||||
|
||||
perf_event::iterator::~iterator()
|
||||
{
|
||||
if(m_mapping != nullptr)
|
||||
{
|
||||
m_mapping->data_tail = m_index;
|
||||
}
|
||||
}
|
||||
|
||||
perf_event::iterator&
|
||||
perf_event::iterator::operator++()
|
||||
{
|
||||
next();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
perf_event::iterator::operator!=(const iterator& other) const
|
||||
{
|
||||
return has_data() != other.has_data();
|
||||
}
|
||||
|
||||
perf_event::record
|
||||
perf_event::iterator::get()
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
// Copy out the record header
|
||||
perf_event::copy_from_ring_buffer(m_mapping, m_index, _buf,
|
||||
sizeof(struct perf_event_header));
|
||||
|
||||
// Get a pointer to the header
|
||||
struct perf_event_header* header = reinterpret_cast<struct perf_event_header*>(_buf);
|
||||
|
||||
// Copy out the entire record
|
||||
perf_event::copy_from_ring_buffer(m_mapping, m_index, _buf, header->size);
|
||||
|
||||
return perf_event::record(&m_source, header);
|
||||
}
|
||||
|
||||
bool
|
||||
perf_event::iterator::has_data() const
|
||||
{
|
||||
// If there is no ring buffer, there is no data
|
||||
if(m_mapping == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there isn't enough data in the ring buffer to hold a header, there is no data
|
||||
if(m_index + sizeof(struct perf_event_header) >= m_head)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct perf_event_header _hdr;
|
||||
perf_event::copy_from_ring_buffer(m_mapping, m_index, &_hdr,
|
||||
sizeof(struct perf_event_header));
|
||||
|
||||
// If the first record is larger than the available data, nothing can be read
|
||||
if(m_index + _hdr.size > m_head)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
perf_event::copy_from_ring_buffer(struct perf_event_mmap_page* _mapping, ptrdiff_t _index,
|
||||
void* _dest, size_t _nbytes)
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
uintptr_t _base = reinterpret_cast<uintptr_t>(_mapping) + sizes.page;
|
||||
size_t _beg_idx = _index % sizes.data;
|
||||
size_t _end_idx = _beg_idx + _nbytes;
|
||||
|
||||
if(_end_idx <= sizes.data)
|
||||
{
|
||||
memcpy(_dest, reinterpret_cast<void*>(_base + _beg_idx), _nbytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t _chunk_size2 = _end_idx - sizes.data;
|
||||
size_t _chunk_size1 = _nbytes - _chunk_size2;
|
||||
|
||||
void* _dest2 =
|
||||
reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(_dest) + _chunk_size1);
|
||||
|
||||
memcpy(_dest, reinterpret_cast<void*>(_base + _beg_idx), _chunk_size1);
|
||||
memcpy(_dest2, reinterpret_cast<void*>(_base), _chunk_size2);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
perf_event::record::get_ip() const
|
||||
{
|
||||
ROCPROFSYS_ASSERT(is_sample() && m_source != nullptr &&
|
||||
m_source->is_sampling(sample::ip))
|
||||
<< "Record does not have an ip field (" << is_sample() << "|" << m_source << ")";
|
||||
return *locate_field<sample::ip, uint64_t*>();
|
||||
}
|
||||
|
||||
uint64_t
|
||||
perf_event::record::get_pid() const
|
||||
{
|
||||
ROCPROFSYS_ASSERT(is_sample() && m_source != nullptr &&
|
||||
m_source->is_sampling(sample::pid_tid))
|
||||
<< "Record does not have a `pid` field (" << is_sample() << "|" << m_source
|
||||
<< ")";
|
||||
return locate_field<sample::pid_tid, uint32_t*>()[0];
|
||||
}
|
||||
|
||||
uint64_t
|
||||
perf_event::record::get_tid() const
|
||||
{
|
||||
ROCPROFSYS_ASSERT(is_sample() && m_source != nullptr &&
|
||||
m_source->is_sampling(sample::pid_tid))
|
||||
<< "Record does not have a `tid` field (" << is_sample() << "|" << m_source
|
||||
<< ")";
|
||||
return locate_field<sample::pid_tid, uint32_t*>()[1];
|
||||
}
|
||||
|
||||
uint64_t
|
||||
perf_event::record::get_time() const
|
||||
{
|
||||
ROCPROFSYS_ASSERT(is_sample() && m_source != nullptr &&
|
||||
m_source->is_sampling(sample::time))
|
||||
<< "Record does not have a 'time' field (" << is_sample() << "|" << m_source
|
||||
<< ")";
|
||||
return *locate_field<sample::time, uint64_t*>();
|
||||
}
|
||||
|
||||
uint64_t
|
||||
perf_event::record::get_period() const
|
||||
{
|
||||
ROCPROFSYS_ASSERT(is_sample() && m_source != nullptr &&
|
||||
m_source->is_sampling(sample::period))
|
||||
<< "Record does not have a 'period' field (" << is_sample() << "|" << m_source
|
||||
<< ")";
|
||||
return *locate_field<sample::period, uint64_t*>();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
perf_event::record::get_cpu() const
|
||||
{
|
||||
ROCPROFSYS_ASSERT(is_sample() && m_source != nullptr &&
|
||||
m_source->is_sampling(sample::cpu))
|
||||
<< "Record does not have a 'cpu' field (" << is_sample() << "|" << m_source
|
||||
<< ")";
|
||||
return *locate_field<sample::cpu, uint32_t*>();
|
||||
}
|
||||
|
||||
container::c_array<uint64_t>
|
||||
perf_event::record::get_callchain() const
|
||||
{
|
||||
ROCPROFSYS_ASSERT(is_sample() && m_source != nullptr &&
|
||||
m_source->is_sampling(sample::callchain))
|
||||
<< "Record does not have a callchain field (" << is_sample() << "|" << m_source
|
||||
<< ")";
|
||||
|
||||
uint64_t* _base = locate_field<sample::callchain, uint64_t*>();
|
||||
uint64_t _size = *_base;
|
||||
// Advance the callchain array pointer past the size
|
||||
++_base;
|
||||
return container::wrap_c_array(_base, _size);
|
||||
}
|
||||
|
||||
template <sample SampleT, typename Tp>
|
||||
Tp
|
||||
perf_event::record::locate_field() const
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
uintptr_t p =
|
||||
reinterpret_cast<uintptr_t>(m_header) + sizeof(struct perf_event_header);
|
||||
|
||||
// Walk through the fields in the sample structure. Once the requested field is
|
||||
// reached, return. Skip past any unrequested fields that are included in the sample
|
||||
// type
|
||||
|
||||
// ip
|
||||
if constexpr(SampleT == sample::ip) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::ip)) p += sizeof(uint64_t);
|
||||
|
||||
// pid, tid
|
||||
if constexpr(SampleT == sample::pid_tid) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::pid_tid))
|
||||
p += sizeof(uint32_t) + sizeof(uint32_t);
|
||||
|
||||
// time
|
||||
if constexpr(SampleT == sample::time) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::time)) p += sizeof(uint64_t);
|
||||
|
||||
// addr
|
||||
if constexpr(SampleT == sample::addr) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::addr)) p += sizeof(uint64_t);
|
||||
|
||||
// id
|
||||
if constexpr(SampleT == sample::id) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::id)) p += sizeof(uint64_t);
|
||||
|
||||
// stream_id
|
||||
if constexpr(SampleT == sample::stream_id) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::stream_id))
|
||||
p += sizeof(uint64_t);
|
||||
|
||||
// cpu
|
||||
if constexpr(SampleT == sample::cpu) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::cpu))
|
||||
p += sizeof(uint32_t) + sizeof(uint32_t);
|
||||
|
||||
// period
|
||||
if constexpr(SampleT == sample::period) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::period))
|
||||
p += sizeof(uint64_t);
|
||||
|
||||
// value
|
||||
if constexpr(SampleT == sample::read) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::read))
|
||||
{
|
||||
uint64_t read_format = m_source->get_read_format();
|
||||
if(read_format & PERF_FORMAT_GROUP)
|
||||
{
|
||||
// Get the number of values in the read format structure
|
||||
uint64_t nr = *reinterpret_cast<uint64_t*>(p);
|
||||
// The default size of each entry is a u64
|
||||
size_t sz = sizeof(uint64_t);
|
||||
// If requested, the id will be included with each value
|
||||
if(read_format & PERF_FORMAT_ID) sz += sizeof(uint64_t);
|
||||
// Skip over the entry count, and each entry
|
||||
p += sizeof(uint64_t) + nr * sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip over the value
|
||||
p += sizeof(uint64_t);
|
||||
// Skip over the id, if included
|
||||
if(read_format & PERF_FORMAT_ID) p += sizeof(uint64_t);
|
||||
}
|
||||
|
||||
// Skip over the time_enabled field
|
||||
if(read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) p += sizeof(uint64_t);
|
||||
// Skip over the time_running field
|
||||
if(read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) p += sizeof(uint64_t);
|
||||
}
|
||||
|
||||
// callchain
|
||||
if constexpr(SampleT == sample::callchain) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::callchain))
|
||||
{
|
||||
uint64_t nr = *reinterpret_cast<uint64_t*>(p);
|
||||
p += sizeof(uint64_t) + (nr * sizeof(uint64_t));
|
||||
}
|
||||
|
||||
// raw
|
||||
if constexpr(SampleT == sample::raw) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::raw))
|
||||
{
|
||||
uint32_t raw_size = *reinterpret_cast<uint32_t*>(p);
|
||||
p += sizeof(uint32_t) + raw_size;
|
||||
}
|
||||
|
||||
// branch_stack
|
||||
if constexpr(SampleT == sample::branch_stack) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::branch_stack))
|
||||
ROCPROFSYS_FATAL << "Branch stack sampling is not supported";
|
||||
|
||||
// regs
|
||||
if constexpr(SampleT == sample::regs) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::regs))
|
||||
ROCPROFSYS_FATAL << "Register sampling is not supported";
|
||||
|
||||
// stack
|
||||
if constexpr(SampleT == sample::stack) return reinterpret_cast<Tp>(p);
|
||||
if(m_source != nullptr && m_source->is_sampling(sample::stack))
|
||||
ROCPROFSYS_FATAL << "Stack sampling is not supported";
|
||||
|
||||
// end
|
||||
if constexpr(SampleT == sample::last) return reinterpret_cast<Tp>(p);
|
||||
|
||||
ROCPROFSYS_FATAL << "Unsupported sample field requested!";
|
||||
|
||||
if constexpr(std::is_pointer<Tp>::value)
|
||||
return nullptr;
|
||||
else
|
||||
return Tp{};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
inline auto&
|
||||
get_instances()
|
||||
{
|
||||
using thread_data_t = thread_data<identity<std::unique_ptr<perf_event>>, perf_event>;
|
||||
static auto& _v = thread_data_t::instance(construct_on_init{});
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<perf_event>&
|
||||
get_instance(int64_t _tid)
|
||||
{
|
||||
auto& _data = get_instances();
|
||||
if(static_cast<size_t>(_tid) >= _data->size())
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
_data->resize(_tid + 1);
|
||||
}
|
||||
return _data->at(_tid);
|
||||
}
|
||||
} // namespace perf
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,212 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/containers/c_array.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/locking.hpp"
|
||||
#include "core/perf.hpp"
|
||||
|
||||
#include <timemory/backends/papi.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <linux/perf_event.h>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace perf
|
||||
{
|
||||
struct perf_event
|
||||
{
|
||||
static constexpr uint32_t max_batch_size = 32;
|
||||
|
||||
struct record;
|
||||
struct sample_record;
|
||||
class iterator;
|
||||
|
||||
/// Default constructor
|
||||
perf_event() = default;
|
||||
/// Move constructor
|
||||
perf_event(perf_event&& other) noexcept;
|
||||
|
||||
/// Close the perf event file and unmap the ring buffer
|
||||
~perf_event();
|
||||
|
||||
/// Move assignment is supported
|
||||
perf_event& operator=(perf_event&& other) noexcept;
|
||||
|
||||
perf_event(const perf_event&) = delete;
|
||||
perf_event& operator=(const perf_event&) = delete;
|
||||
|
||||
/// Open a perf_event file using the given options structure
|
||||
std::optional<std::string> open(struct perf_event_attr& pe, pid_t pid = 0,
|
||||
int cpu = -1);
|
||||
std::optional<std::string> open(double, uint32_t = 0, pid_t pid = 0, int cpu = -1);
|
||||
|
||||
/// Return file descriptor
|
||||
long get_fileno() const;
|
||||
|
||||
/// Read event count
|
||||
uint64_t get_count() const;
|
||||
|
||||
/// Get the batch size
|
||||
uint32_t get_batch_size() const { return m_batch_size; }
|
||||
|
||||
/// Start counting events and collecting samples
|
||||
bool start() const;
|
||||
|
||||
/// Stop counting events
|
||||
bool stop() const;
|
||||
|
||||
/// Check if counting events and collecting samples
|
||||
bool is_open() const;
|
||||
|
||||
/// Close the perf_event file and unmap the ring buffer
|
||||
void close();
|
||||
|
||||
/// Configure the perf_event file to deliver a signal when samples are ready to be
|
||||
/// processed
|
||||
void set_ready_signal(int sig) const;
|
||||
|
||||
/// Check if this perf_event was configured to collect a type of sample data
|
||||
inline bool is_sampling(sample s) const
|
||||
{
|
||||
return (m_sample_type & static_cast<uint64_t>(s)) != 0u;
|
||||
}
|
||||
|
||||
/// Get the configuration for this perf_event's read format
|
||||
inline uint64_t get_read_format() const { return m_read_format; }
|
||||
|
||||
/// A generic record type
|
||||
struct record
|
||||
{
|
||||
friend class perf_event::iterator;
|
||||
|
||||
record() = default;
|
||||
~record() = default;
|
||||
record(const record&) = default;
|
||||
record(record&&) noexcept = default;
|
||||
record& operator=(const record&) = default;
|
||||
record& operator=(record&&) noexcept = default;
|
||||
|
||||
bool is_valid() const { return (m_source != nullptr && m_header != nullptr); }
|
||||
operator bool() const { return is_valid(); }
|
||||
|
||||
record_type get_type() const { return static_cast<record_type>(m_header->type); }
|
||||
|
||||
inline bool is_mmap() const { return get_type() == record_type::mmap; }
|
||||
inline bool is_lost() const { return get_type() == record_type::lost; }
|
||||
inline bool is_comm() const { return get_type() == record_type::comm; }
|
||||
inline bool is_exit() const { return get_type() == record_type::exit; }
|
||||
inline bool is_throttle() const { return get_type() == record_type::throttle; }
|
||||
inline bool is_unthrottle() const
|
||||
{
|
||||
return get_type() == record_type::unthrottle;
|
||||
}
|
||||
inline bool is_fork() const { return get_type() == record_type::fork; }
|
||||
inline bool is_read() const { return get_type() == record_type::read; }
|
||||
inline bool is_sample() const { return get_type() == record_type::sample; }
|
||||
inline bool is_mmap2() const { return get_type() == record_type::mmap2; }
|
||||
|
||||
uint64_t get_ip() const;
|
||||
uint64_t get_pid() const;
|
||||
uint64_t get_tid() const;
|
||||
uint64_t get_time() const;
|
||||
uint64_t get_period() const;
|
||||
uint32_t get_cpu() const;
|
||||
container::c_array<uint64_t> get_callchain() const;
|
||||
|
||||
private:
|
||||
record(const perf_event* source, struct perf_event_header* header)
|
||||
: m_source(source)
|
||||
, m_header(header)
|
||||
{}
|
||||
|
||||
template <sample SampleT, typename Tp = void*>
|
||||
Tp locate_field() const;
|
||||
|
||||
const perf_event* m_source = nullptr;
|
||||
struct perf_event_header* m_header = nullptr;
|
||||
};
|
||||
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
iterator(perf_event& source, struct perf_event_mmap_page* mapping);
|
||||
~iterator();
|
||||
|
||||
void next();
|
||||
record get();
|
||||
bool has_data() const;
|
||||
|
||||
iterator& operator++();
|
||||
record operator*() { return get(); }
|
||||
bool operator!=(const iterator& other) const;
|
||||
|
||||
private:
|
||||
perf_event& m_source;
|
||||
size_t m_index = 0;
|
||||
size_t m_head = 0;
|
||||
struct perf_event_mmap_page* m_mapping = nullptr;
|
||||
|
||||
// Buffer to hold the current record. Just a hack until records play nice with the
|
||||
// ring buffer
|
||||
uint8_t _buf[4096];
|
||||
};
|
||||
|
||||
/// Get an iterator to the beginning of the memory mapped ring buffer
|
||||
iterator begin() { return iterator(*this, m_mapping); }
|
||||
|
||||
/// Get an iterator to the end of the memory mapped ring buffer
|
||||
iterator end() { return iterator(*this, nullptr); }
|
||||
|
||||
private:
|
||||
// Copy data out of the mmap ring buffer
|
||||
static void copy_from_ring_buffer(struct perf_event_mmap_page* mapping,
|
||||
ptrdiff_t index, void* dest, size_t bytes);
|
||||
|
||||
uint32_t m_batch_size = 10;
|
||||
|
||||
/// File descriptor for the perf event
|
||||
long m_fd = -1;
|
||||
|
||||
/// Memory mapped perf event region
|
||||
struct perf_event_mmap_page* m_mapping = nullptr;
|
||||
|
||||
/// The sample type from this perf_event's configuration
|
||||
uint64_t m_sample_type = 0;
|
||||
/// The read format from this perf event's configuration
|
||||
uint64_t m_read_format = 0;
|
||||
};
|
||||
|
||||
/// provides thread-local instance of perf_event
|
||||
std::unique_ptr<perf_event>&
|
||||
get_instance(int64_t _tid);
|
||||
} // namespace perf
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,241 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/process_sampler.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "library/cpu_freq.hpp"
|
||||
#include "library/rocm_smi.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace process_sampler
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using promise_t = std::promise<void>;
|
||||
std::unique_ptr<promise_t> polling_finished = {};
|
||||
std::vector<std::unique_ptr<instance>> instances = {};
|
||||
|
||||
bool&
|
||||
is_initialized()
|
||||
{
|
||||
static bool _v = false;
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::thread>&
|
||||
get_thread()
|
||||
{
|
||||
static std::unique_ptr<std::thread> _v;
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::atomic<State>&
|
||||
get_sampler_state()
|
||||
{
|
||||
static std::atomic<State> _v{ State::PreInit };
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::atomic<bool>&
|
||||
get_sampler_is_sampling()
|
||||
{
|
||||
static std::atomic<bool> _v{ false };
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
sampler::poll(std::atomic<State>* _state, nsec_t _interval, promise_t* _ready)
|
||||
{
|
||||
threading::offset_this_id(true);
|
||||
threading::set_thread_name("omni.sampler");
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
// notify thread started
|
||||
if(_ready) _ready->set_value();
|
||||
|
||||
for(auto& itr : instances)
|
||||
itr->config();
|
||||
|
||||
ROCPROFSYS_VERBOSE(
|
||||
1, "Background process sampling polling at an interval of %f seconds...\n",
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(_interval).count());
|
||||
|
||||
auto _duration = config::get_process_sampling_duration();
|
||||
if(_duration < 0.0) _duration = config::get_sampling_duration();
|
||||
bool _has_duration = (_duration > 0.0);
|
||||
|
||||
auto _now = std::chrono::steady_clock::now();
|
||||
auto _end =
|
||||
_now + std::chrono::nanoseconds{ static_cast<uint64_t>(_duration * units::sec) };
|
||||
while(_state && _state->load() < State::Finalized && get_state() < State::Finalized)
|
||||
{
|
||||
std::this_thread::sleep_until(_now);
|
||||
if(_state->load() != State::Active) continue;
|
||||
if(get_state() >= State::Finalized) break;
|
||||
if(get_state() != State::Active) continue;
|
||||
get_sampler_is_sampling().store(true);
|
||||
for(auto& itr : instances)
|
||||
itr->sample();
|
||||
get_sampler_is_sampling().store(false);
|
||||
if(_has_duration && _now >= _end) break;
|
||||
_now = std::chrono::steady_clock::now() + _interval;
|
||||
}
|
||||
|
||||
// ensure this is always false
|
||||
get_sampler_is_sampling().store(false);
|
||||
|
||||
if(_has_duration && _now >= _end && get_state() < State::Finalized)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(
|
||||
1,
|
||||
"Background process sampling duration of %f seconds has elapsed. "
|
||||
"Shutting down process sampling...\n",
|
||||
_duration);
|
||||
}
|
||||
|
||||
ROCPROFSYS_CONDITIONAL_BASIC_PRINT(get_debug(),
|
||||
"Thread sampler polling completed...\n");
|
||||
|
||||
if(polling_finished) polling_finished->set_value();
|
||||
}
|
||||
|
||||
void
|
||||
sampler::setup()
|
||||
{
|
||||
if(!get_use_process_sampling())
|
||||
{
|
||||
ROCPROFSYS_DEBUG("Background sampler is disabled...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ROCPROFSYS_VERBOSE(1, "Setting up background sampler...\n");
|
||||
|
||||
// shutdown if already running
|
||||
shutdown();
|
||||
|
||||
if(get_use_rocm_smi())
|
||||
{
|
||||
auto& _rocm_smi = instances.emplace_back(std::make_unique<instance>());
|
||||
_rocm_smi->setup = []() { rocm_smi::setup(); };
|
||||
_rocm_smi->shutdown = []() { rocm_smi::shutdown(); };
|
||||
_rocm_smi->post_process = []() { rocm_smi::post_process(); };
|
||||
_rocm_smi->config = []() { rocm_smi::config(); };
|
||||
_rocm_smi->sample = []() { rocm_smi::sample(); };
|
||||
}
|
||||
|
||||
auto& _cpu_freq = instances.emplace_back(std::make_unique<instance>());
|
||||
_cpu_freq->setup = []() { cpu_freq::setup(); };
|
||||
_cpu_freq->shutdown = []() { cpu_freq::shutdown(); };
|
||||
_cpu_freq->post_process = []() { cpu_freq::post_process(); };
|
||||
_cpu_freq->config = []() { cpu_freq::config(); };
|
||||
_cpu_freq->sample = []() { cpu_freq::sample(); };
|
||||
|
||||
for(auto& itr : instances)
|
||||
itr->setup();
|
||||
|
||||
polling_finished = std::make_unique<promise_t>();
|
||||
|
||||
auto _freq = get_process_sampling_freq();
|
||||
uint64_t _msec_freq = (1.0 / _freq) * 1.0e3;
|
||||
|
||||
polling_finished = std::make_unique<promise_t>();
|
||||
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
|
||||
set_state(State::PreInit);
|
||||
get_thread() = std::make_unique<std::thread>(&poll<msec_t>, &get_sampler_state(),
|
||||
msec_t{ _msec_freq }, nullptr);
|
||||
|
||||
set_state(State::Active);
|
||||
}
|
||||
|
||||
void
|
||||
sampler::shutdown()
|
||||
{
|
||||
// set the local sampler state to finalized
|
||||
set_state(State::Finalized);
|
||||
|
||||
// shutdown all components
|
||||
for(auto& itr : instances)
|
||||
itr->shutdown();
|
||||
|
||||
auto& _thread = get_thread();
|
||||
if(_thread)
|
||||
{
|
||||
size_t _nitr = 0;
|
||||
constexpr size_t _nitr_max = 100;
|
||||
uint64_t _freq = (1.0 / get_process_sampling_freq()) * 1.0e3;
|
||||
|
||||
// wait until the sampler is no longer sampling
|
||||
std::this_thread::sleep_for(msec_t{ _freq });
|
||||
while(get_sampler_is_sampling().load())
|
||||
{
|
||||
if(_nitr++ > _nitr_max) break;
|
||||
}
|
||||
|
||||
// during CI, throw an error if polling_finished is not valid
|
||||
ROCPROFSYS_CI_THROW(!polling_finished, "polling_finished is not valid\n");
|
||||
if(polling_finished)
|
||||
{
|
||||
// wait for the thread to finish
|
||||
auto _fut = polling_finished->get_future();
|
||||
_fut.wait_for(msec_t{ 10 * _freq });
|
||||
_thread->join();
|
||||
}
|
||||
else
|
||||
{
|
||||
// cancel the thread and detach
|
||||
std::this_thread::sleep_for(msec_t{ 10 * _freq });
|
||||
pthread_cancel(_thread->native_handle());
|
||||
_thread->detach();
|
||||
}
|
||||
_thread = std::unique_ptr<std::thread>{ nullptr };
|
||||
polling_finished = std::unique_ptr<promise_t>{};
|
||||
}
|
||||
|
||||
is_initialized() = false;
|
||||
}
|
||||
|
||||
void
|
||||
sampler::post_process()
|
||||
{
|
||||
for(auto& itr : instances)
|
||||
itr->post_process();
|
||||
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
void
|
||||
sampler::set_state(state_t _state)
|
||||
{
|
||||
get_sampler_state().store(_state);
|
||||
}
|
||||
} // namespace process_sampler
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,103 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace process_sampler
|
||||
{
|
||||
struct instance
|
||||
{
|
||||
std::function<void()> setup = []() {};
|
||||
std::function<void()> shutdown = []() {};
|
||||
std::function<void()> config = []() {};
|
||||
std::function<void()> sample = []() {};
|
||||
std::function<void()> post_process = []() {};
|
||||
};
|
||||
//
|
||||
struct sampler
|
||||
{
|
||||
using msec_t = std::chrono::milliseconds;
|
||||
using usec_t = std::chrono::microseconds;
|
||||
using nsec_t = std::chrono::nanoseconds;
|
||||
using promise_t = std::promise<void>;
|
||||
using future_t = std::future<void>;
|
||||
using state_t = State;
|
||||
|
||||
using timestamp_t = int64_t;
|
||||
|
||||
template <typename Tp = nsec_t,
|
||||
std::enable_if_t<!std::is_same_v<std::decay_t<Tp>, nsec_t>, int> = 0>
|
||||
static void poll(std::atomic<state_t>* _state, Tp&& _interval, promise_t*);
|
||||
|
||||
static void setup();
|
||||
static void shutdown();
|
||||
static void post_process();
|
||||
static void set_state(state_t);
|
||||
static void poll(std::atomic<state_t>* _state, nsec_t _interval, promise_t*);
|
||||
};
|
||||
//
|
||||
template <
|
||||
typename Tp,
|
||||
std::enable_if_t<!std::is_same_v<std::decay_t<Tp>, std::chrono::nanoseconds>, int>>
|
||||
void
|
||||
sampler::poll(std::atomic<state_t>* _state, Tp&& _interval, promise_t* _prom)
|
||||
{
|
||||
poll(_state, std::chrono::duration_cast<nsec_t>(_interval), _prom);
|
||||
}
|
||||
//
|
||||
inline void
|
||||
setup()
|
||||
{
|
||||
sampler::setup();
|
||||
}
|
||||
|
||||
inline void
|
||||
shutdown()
|
||||
{
|
||||
sampler::shutdown();
|
||||
}
|
||||
|
||||
inline void
|
||||
post_process()
|
||||
{
|
||||
sampler::post_process();
|
||||
}
|
||||
//
|
||||
} // namespace process_sampler
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,234 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/ptl.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/sampling.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <PTL/ThreadPool.hh>
|
||||
#include <PTL/UserTaskQueue.hh>
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/utility/declaration.hpp>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace tasking
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto _thread_pool_cfg = []() {
|
||||
int64_t _nthreads = 0;
|
||||
if(config::settings_are_configured())
|
||||
{
|
||||
_nthreads = config::get_thread_pool_size();
|
||||
}
|
||||
else
|
||||
{
|
||||
const int64_t _max_threads = std::thread::hardware_concurrency() / 2;
|
||||
const int64_t _min_threads = 1;
|
||||
|
||||
_nthreads = get_env<int64_t>("ROCPROFSYS_THREAD_POOL_SIZE", -1, false);
|
||||
if(_nthreads == -1)
|
||||
{
|
||||
_nthreads = 4;
|
||||
if(_nthreads > _max_threads) _nthreads = _max_threads;
|
||||
if(_nthreads < _min_threads) _nthreads = _min_threads;
|
||||
|
||||
tim::set_env("ROCPROFSYS_THREAD_POOL_SIZE", _nthreads, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static char buffer[sizeof(PTL::UserTaskQueue)];
|
||||
static auto* _task_queue = new((void*) buffer) PTL::UserTaskQueue(_nthreads);
|
||||
|
||||
PTL::ThreadPool::Config _v{};
|
||||
_v.init = true;
|
||||
_v.use_affinity = false;
|
||||
_v.use_tbb = false;
|
||||
_v.verbose = -1;
|
||||
_v.initializer = []() {
|
||||
thread_info::init(true);
|
||||
threading::set_thread_name(
|
||||
JOIN('.', "ptl", PTL::Threading::GetThreadId()).c_str());
|
||||
set_thread_state(ThreadState::Disabled);
|
||||
sampling::block_signals();
|
||||
};
|
||||
_v.finalizer = []() {};
|
||||
_v.priority = 5;
|
||||
_v.pool_size = _nthreads;
|
||||
_v.task_queue = _task_queue;
|
||||
return _v;
|
||||
};
|
||||
|
||||
auto&
|
||||
get_thread_pool_state()
|
||||
{
|
||||
static auto _v = State::PreInit;
|
||||
return _v;
|
||||
}
|
||||
|
||||
PTL::ThreadPool&
|
||||
get_thread_pool()
|
||||
{
|
||||
static auto _cfg = _thread_pool_cfg();
|
||||
static auto* _v =
|
||||
(get_thread_pool_state() = State::Active, new PTL::ThreadPool{ _cfg });
|
||||
return *_v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace general
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto&
|
||||
get_thread_pool_state()
|
||||
{
|
||||
static auto _v = State::PreInit;
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
} // namespace general
|
||||
|
||||
namespace roctracer
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto&
|
||||
get_thread_pool_state()
|
||||
{
|
||||
static auto _v = State::PreInit;
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
} // namespace roctracer
|
||||
|
||||
void
|
||||
setup()
|
||||
{
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
(void) get_thread_pool();
|
||||
}
|
||||
|
||||
void
|
||||
join()
|
||||
{
|
||||
if(roctracer::get_thread_pool_state() == State::Active)
|
||||
{
|
||||
ROCPROFSYS_DEBUG_F("waiting for all roctracer tasks to complete...\n");
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
roctracer::get_task_group(i).join();
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_DEBUG_F("roctracer thread-pool is not active...\n");
|
||||
}
|
||||
|
||||
if(general::get_thread_pool_state() == State::Active)
|
||||
{
|
||||
ROCPROFSYS_DEBUG_F("waiting for all general tasks to complete...\n");
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
general::get_task_group(i).join();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shutdown()
|
||||
{
|
||||
if(roctracer::get_thread_pool_state() == State::Active)
|
||||
{
|
||||
ROCPROFSYS_DEBUG_F("Waiting on completion of roctracer tasks...\n");
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
{
|
||||
roctracer::get_task_group(i).join();
|
||||
roctracer::get_task_group(i).clear();
|
||||
roctracer::get_task_group(i).set_pool(nullptr);
|
||||
}
|
||||
roctracer::get_thread_pool_state() = State::Finalized;
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_DEBUG_F("roctracer thread-pool is not active...\n");
|
||||
}
|
||||
|
||||
if(general::get_thread_pool_state() == State::Active)
|
||||
{
|
||||
ROCPROFSYS_DEBUG_F("Waiting on completion of general tasks...\n");
|
||||
for(size_t i = 0; i < thread_info::get_peak_num_threads(); ++i)
|
||||
{
|
||||
general::get_task_group(i).join();
|
||||
general::get_task_group(i).clear();
|
||||
general::get_task_group(i).set_pool(nullptr);
|
||||
}
|
||||
general::get_thread_pool_state() = State::Finalized;
|
||||
}
|
||||
|
||||
if(get_thread_pool_state() == State::Active)
|
||||
{
|
||||
ROCPROFSYS_DEBUG_F("Destroying the rocprof-sys thread pool...\n");
|
||||
get_thread_pool().destroy_threadpool();
|
||||
get_thread_pool_state() = State::Finalized;
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFSYS_DEBUG_F("thread-pool is not active...\n");
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
initialize_threadpool(size_t _v)
|
||||
{
|
||||
return get_thread_pool().initialize_threadpool(_v);
|
||||
}
|
||||
|
||||
PTL::TaskGroup<void>&
|
||||
general::get_task_group(int64_t _tid)
|
||||
{
|
||||
struct local
|
||||
{};
|
||||
using thread_data_t = thread_data<PTL::TaskGroup<void>, local>;
|
||||
static thread_local auto& _v =
|
||||
thread_data_t::instance(construct_on_thread{ _tid }, &tasking::get_thread_pool());
|
||||
return *_v;
|
||||
}
|
||||
|
||||
PTL::TaskGroup<void>&
|
||||
roctracer::get_task_group(int64_t _tid)
|
||||
{
|
||||
struct local
|
||||
{};
|
||||
using thread_data_t = thread_data<PTL::TaskGroup<void>, local>;
|
||||
static thread_local auto& _v = (roctracer::get_thread_pool_state() = State::Active,
|
||||
thread_data_t::instance(construct_on_thread{ _tid },
|
||||
&tasking::get_thread_pool()));
|
||||
return *_v;
|
||||
}
|
||||
} // namespace tasking
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,71 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/defines.hpp"
|
||||
#include "core/utility.hpp"
|
||||
|
||||
#include <PTL/PTL.hh>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace tasking
|
||||
{
|
||||
void
|
||||
setup();
|
||||
|
||||
void
|
||||
join();
|
||||
|
||||
void
|
||||
shutdown();
|
||||
|
||||
size_t initialize_threadpool(size_t);
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//
|
||||
// general
|
||||
//
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
namespace general
|
||||
{
|
||||
PTL::TaskGroup<void>&
|
||||
get_task_group(int64_t _tid = utility::get_thread_index());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//
|
||||
// roctracer
|
||||
//
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
namespace roctracer
|
||||
{
|
||||
PTL::TaskGroup<void>&
|
||||
get_task_group(int64_t _tid = utility::get_thread_index());
|
||||
} // namespace roctracer
|
||||
} // namespace tasking
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,87 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "library/components/rcclp.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/dynamic_library.hpp"
|
||||
#include "core/rccl.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/components/category_region.hpp"
|
||||
|
||||
#include <timemory/timemory.hpp>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
{
|
||||
uint64_t global_id = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace rcclp
|
||||
{
|
||||
void
|
||||
configure()
|
||||
{}
|
||||
|
||||
void
|
||||
setup()
|
||||
{
|
||||
configure();
|
||||
|
||||
// make sure the symbols are loaded to be wrapped
|
||||
dynamic_library _librccl{
|
||||
"ROCPROFSYS_RCCL_LIBRARY", "librccl.so", RTLD_NOW | RTLD_GLOBAL, true, true, true
|
||||
};
|
||||
|
||||
auto _use_data = tim::get_env("ROCPROFSYS_RCCLP_COMM_DATA", get_use_timemory());
|
||||
if(!get_use_timemory())
|
||||
{
|
||||
trait::runtime_enabled<component::comm_data>::set(false);
|
||||
trait::runtime_enabled<component::comm_data_tracker_t>::set(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
trait::runtime_enabled<component::comm_data>::set(_use_data);
|
||||
trait::runtime_enabled<component::comm_data_tracker_t>::set(_use_data);
|
||||
}
|
||||
|
||||
component::configure_rcclp();
|
||||
global_id = component::activate_rcclp();
|
||||
}
|
||||
|
||||
void
|
||||
shutdown()
|
||||
{
|
||||
if(global_id < std::numeric_limits<uint64_t>::max())
|
||||
component::deactivate_rcclp(global_id);
|
||||
}
|
||||
} // namespace rcclp
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,57 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/defines.hpp"
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace rcclp
|
||||
{
|
||||
void
|
||||
configure();
|
||||
|
||||
void
|
||||
setup();
|
||||
|
||||
void
|
||||
shutdown();
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_RCCL) || \
|
||||
(defined(ROCPROFSYS_USE_RCCL) && ROCPROFSYS_USE_RCCL == 0)
|
||||
inline void
|
||||
configure()
|
||||
{}
|
||||
|
||||
inline void
|
||||
setup()
|
||||
{}
|
||||
|
||||
inline void
|
||||
shutdown()
|
||||
{}
|
||||
#endif
|
||||
} // namespace rcclp
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,253 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/rocm.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/dynamic_library.hpp"
|
||||
#include "core/gpu.hpp"
|
||||
#include "library/components/rocprofiler.hpp"
|
||||
#include "library/components/roctracer.hpp"
|
||||
#include "library/rocm/hsa_rsrc_factory.hpp"
|
||||
#include "library/rocm_smi.hpp"
|
||||
#include "library/rocprofiler.hpp"
|
||||
#include "library/roctracer.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/tracing.hpp"
|
||||
|
||||
#include <timemory/backends/cpu.hpp>
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
|
||||
#if defined(ROCPROFSYS_USE_ROCPROFILER) && ROCPROFSYS_USE_ROCPROFILER > 0
|
||||
# include <rocprofiler.h>
|
||||
#endif
|
||||
|
||||
using namespace rocprofsys;
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace rocm
|
||||
{
|
||||
std::mutex rocm_mutex = {};
|
||||
bool is_loaded = false;
|
||||
bool on_load_trace = (get_env<int>("ROCP_ONLOAD_TRACE", 0) > 0);
|
||||
} // namespace rocm
|
||||
} // namespace rocprofsys
|
||||
|
||||
#if defined(ROCPROFSYS_USE_ROCPROFILER) && ROCPROFSYS_USE_ROCPROFILER > 0
|
||||
std::ostream&
|
||||
operator<<(std::ostream& _os, const rocprofiler_settings_t& _v)
|
||||
{
|
||||
# define ROCPROF_SETTING_FIELD_STR(NAME) JOIN('=', # NAME, _v.NAME)
|
||||
|
||||
_os << JOIN(
|
||||
", ", ROCPROF_SETTING_FIELD_STR(intercept_mode),
|
||||
ROCPROF_SETTING_FIELD_STR(code_obj_tracking),
|
||||
ROCPROF_SETTING_FIELD_STR(memcopy_tracking),
|
||||
ROCPROF_SETTING_FIELD_STR(trace_size), ROCPROF_SETTING_FIELD_STR(trace_local),
|
||||
ROCPROF_SETTING_FIELD_STR(timeout), ROCPROF_SETTING_FIELD_STR(timestamp_on),
|
||||
ROCPROF_SETTING_FIELD_STR(hsa_intercepting),
|
||||
ROCPROF_SETTING_FIELD_STR(k_concurrent), ROCPROF_SETTING_FIELD_STR(opt_mode),
|
||||
ROCPROF_SETTING_FIELD_STR(obj_dumping));
|
||||
return _os;
|
||||
}
|
||||
#endif
|
||||
|
||||
// HSA-runtime tool on-load method
|
||||
extern "C"
|
||||
{
|
||||
#if defined(ROCPROFSYS_USE_ROCPROFILER) && ROCPROFSYS_USE_ROCPROFILER > 0
|
||||
void OnUnloadTool()
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(2 || rocm::on_load_trace, "Unloading...\n");
|
||||
|
||||
rocm::lock_t _lk{ rocm::rocm_mutex, std::defer_lock };
|
||||
if(!_lk.owns_lock()) _lk.lock();
|
||||
|
||||
if(!rocm::is_loaded)
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(1 || rocm::on_load_trace,
|
||||
"rocprofiler is not loaded\n");
|
||||
return;
|
||||
}
|
||||
rocm::is_loaded = false;
|
||||
|
||||
_lk.unlock();
|
||||
|
||||
// stop_top_level_timer_if_necessary();
|
||||
// Final resources cleanup
|
||||
rocprofsys::rocprofiler::rocm_cleanup();
|
||||
}
|
||||
|
||||
void OnLoadToolProp(rocprofiler_settings_t* settings)
|
||||
{
|
||||
using ::rocprofiler::util::HsaRsrcFactory;
|
||||
|
||||
if(!config::get_use_rocprofiler() || config::get_rocm_events().empty()) return;
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(2 || rocm::on_load_trace, "Loading...\n");
|
||||
|
||||
rocm::lock_t _lk{ rocm::rocm_mutex, std::defer_lock };
|
||||
if(!_lk.owns_lock()) _lk.lock();
|
||||
|
||||
if(rocm::is_loaded)
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(1 || rocm::on_load_trace,
|
||||
"rocprofiler is already loaded\n");
|
||||
return;
|
||||
}
|
||||
rocm::is_loaded = true;
|
||||
|
||||
_lk.unlock();
|
||||
|
||||
// Enable timestamping
|
||||
settings->timestamp_on = 1;
|
||||
settings->intercept_mode = 1;
|
||||
settings->hsa_intercepting = 1;
|
||||
settings->k_concurrent = 0;
|
||||
settings->obj_dumping = 0;
|
||||
// settings->code_obj_tracking = 0;
|
||||
// settings->memcopy_tracking = 0;
|
||||
// settings->trace_local = 1;
|
||||
// settings->opt_mode = 1;
|
||||
// settings->trace_size = 0;
|
||||
// settings->timeout = 0;
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(1 || rocm::on_load_trace, "rocprofiler settings: %s\n",
|
||||
JOIN("", *settings).c_str());
|
||||
|
||||
// Initialize profiling
|
||||
rocprofsys::rocprofiler::rocm_initialize();
|
||||
HsaRsrcFactory::Instance().PrintGpuAgents("ROCm");
|
||||
}
|
||||
#endif
|
||||
|
||||
bool OnLoad(HsaApiTable* table, uint64_t runtime_version, uint64_t failed_tool_count,
|
||||
const char* const* failed_tool_names)
|
||||
{
|
||||
tim::consume_parameters(table, runtime_version, failed_tool_count,
|
||||
failed_tool_names);
|
||||
|
||||
static bool _once = false;
|
||||
if(_once) return true;
|
||||
_once = true;
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(2 || rocm::on_load_trace, "Loading...\n");
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
|
||||
if(!tim::get_env("ROCPROFSYS_INIT_TOOLING", true)) return true;
|
||||
if(!tim::settings::enabled()) return true;
|
||||
|
||||
roctracer_is_init() = true;
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(1 || rocm::on_load_trace, "Loading ROCm tooling...\n");
|
||||
|
||||
if(!config::settings_are_configured() && get_state() < State::Active)
|
||||
rocprofsys_init_tooling_hidden();
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
#if ROCPROFSYS_HIP_VERSION < 50300
|
||||
ROCPROFSYS_VERBOSE_F(1 || rocm::on_load_trace,
|
||||
"Computing the roctracer clock skew...\n");
|
||||
(void) rocprofsys::get_clock_skew();
|
||||
#endif
|
||||
|
||||
if(get_use_process_sampling() && get_use_rocm_smi())
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(1 || rocm::on_load_trace,
|
||||
"Setting rocm_smi state to active...\n");
|
||||
rocm_smi::set_state(State::Active);
|
||||
}
|
||||
|
||||
comp::roctracer::setup(static_cast<void*>(table), rocm::on_load_trace);
|
||||
|
||||
#if defined(ROCPROFSYS_USE_ROCPROFILER) && ROCPROFSYS_USE_ROCPROFILER > 0
|
||||
bool _force_rocprofiler_init =
|
||||
tim::get_env("ROCPROFSYS_FORCE_ROCPROFILER_INIT", false, false);
|
||||
#else
|
||||
bool _force_rocprofiler_init = false;
|
||||
#endif
|
||||
|
||||
bool _success = true;
|
||||
bool _is_empty =
|
||||
(config::settings_are_configured() && config::get_rocm_events().empty());
|
||||
if(_force_rocprofiler_init || (get_use_rocprofiler() && !_is_empty))
|
||||
{
|
||||
#if ROCPROFSYS_HIP_VERSION < 50500
|
||||
auto _rocprof = dynamic_library{
|
||||
"ROCPROFSYS_ROCPROFILER_LIBRARY",
|
||||
find_library_path(
|
||||
"librocprofiler64.so", { "ROCPROFSYS_ROCM_PATH", "ROCM_PATH" },
|
||||
{ ROCPROFSYS_DEFAULT_ROCM_PATH },
|
||||
{ "lib", "lib64", "rocprofiler/lib", "rocprofiler/lib64" }),
|
||||
(RTLD_LAZY | RTLD_GLOBAL), false
|
||||
};
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(1 || rocm::on_load_trace,
|
||||
"Loading rocprofiler library (%s=%s)...\n",
|
||||
_rocprof.envname.c_str(), _rocprof.filename.c_str());
|
||||
_rocprof.open();
|
||||
|
||||
on_load_t _rocprof_load = nullptr;
|
||||
_success = _rocprof.invoke("OnLoad", _rocprof_load, table, runtime_version,
|
||||
failed_tool_count, failed_tool_names);
|
||||
ROCPROFSYS_CONDITIONAL_PRINT_F(!_success,
|
||||
"Warning! Invoking rocprofiler's OnLoad "
|
||||
"failed! ROCPROFSYS_ROCPROFILER_LIBRARY=%s\n",
|
||||
_rocprof.filename.c_str());
|
||||
ROCPROFSYS_CI_THROW(!_success,
|
||||
"Warning! Invoking rocprofiler's OnLoad "
|
||||
"failed! ROCPROFSYS_ROCPROFILER_LIBRARY=%s\n",
|
||||
_rocprof.filename.c_str());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
using ::rocprofiler::util::HsaRsrcFactory;
|
||||
|
||||
HsaRsrcFactory::Instance().PrintGpuAgents("ROCm");
|
||||
}
|
||||
|
||||
gpu::add_hip_device_metadata();
|
||||
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(2 || rocm::on_load_trace, "Loading... %s\n",
|
||||
(_success) ? "Done" : "Failed");
|
||||
return _success;
|
||||
}
|
||||
|
||||
// HSA-runtime on-unload method
|
||||
void OnUnload()
|
||||
{
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(2 || rocm::on_load_trace, "Unloading...\n");
|
||||
rocprofsys_finalize_hidden();
|
||||
ROCPROFSYS_BASIC_VERBOSE_F(2 || rocm::on_load_trace, "Unloading... Done\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/defines.hpp"
|
||||
|
||||
#if defined(ROCPROFSYS_USE_ROCPROFILER) && ROCPROFSYS_USE_ROCPROFILER > 0
|
||||
# include <rocprofiler.h>
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace rocm
|
||||
{
|
||||
using lock_t = std::unique_lock<std::mutex>;
|
||||
|
||||
extern std::mutex rocm_mutex;
|
||||
extern bool is_loaded;
|
||||
} // namespace rocm
|
||||
} // namespace rocprofsys
|
||||
|
||||
extern "C"
|
||||
{
|
||||
struct HsaApiTable;
|
||||
using on_load_t = bool (*)(HsaApiTable*, uint64_t, uint64_t, const char* const*);
|
||||
|
||||
bool OnLoad(HsaApiTable* table, uint64_t runtime_version, uint64_t failed_tool_count,
|
||||
const char* const* failed_tool_names) ROCPROFSYS_PUBLIC_API;
|
||||
void OnUnload() ROCPROFSYS_PUBLIC_API;
|
||||
|
||||
#if defined(ROCPROFSYS_USE_ROCPROFILER) && ROCPROFSYS_USE_ROCPROFILER > 0
|
||||
void OnLoadToolProp(rocprofiler_settings_t* settings) ROCPROFSYS_PUBLIC_API;
|
||||
void OnUnloadTool() ROCPROFSYS_PUBLIC_API;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#
|
||||
if(ROCPROFSYS_USE_ROCPROFILER OR ROCPROFSYS_USE_ROCTRACER)
|
||||
target_sources(
|
||||
rocprofiler-systems-object-library
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/hsa_rsrc_factory.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/hsa_rsrc_factory.cpp)
|
||||
endif()
|
||||
+1027
File diff suppressed because it is too large
Load Diff
+582
@@ -0,0 +1,582 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/exception.hpp"
|
||||
|
||||
#define AMD_INTERNAL_BUILD 1
|
||||
|
||||
#include <hsa.h>
|
||||
#include <hsa_api_trace.h>
|
||||
#include <hsa_ext_amd.h>
|
||||
#include <hsa_ext_finalize.h>
|
||||
#include <hsa_ven_amd_aqlprofile.h>
|
||||
#include <hsa_ven_amd_loader.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define HSA_ARGUMENT_ALIGN_BYTES 16
|
||||
#define HSA_QUEUE_ALIGN_BYTES 64
|
||||
#define HSA_PACKET_ALIGN_BYTES 64
|
||||
#define HSA_MESSAGE_LENGTH 4096
|
||||
|
||||
#define CHECK_STATUS(msg, status) \
|
||||
do \
|
||||
{ \
|
||||
if((status) != HSA_STATUS_SUCCESS) \
|
||||
{ \
|
||||
const char* emsg = 0; \
|
||||
hsa_status_string(status, &emsg); \
|
||||
char _buffer[HSA_MESSAGE_LENGTH]; \
|
||||
snprintf(_buffer, HSA_MESSAGE_LENGTH - 1, "%s: %s", msg, \
|
||||
emsg ? emsg : "<unknown error>"); \
|
||||
throw ::rocprofsys::exception<std::runtime_error>(_buffer); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define CHECK_ITER_STATUS(msg, status) \
|
||||
do \
|
||||
{ \
|
||||
if((status) != HSA_STATUS_INFO_BREAK) \
|
||||
{ \
|
||||
const char* emsg = 0; \
|
||||
hsa_status_string(status, &emsg); \
|
||||
char _buffer[HSA_MESSAGE_LENGTH]; \
|
||||
snprintf(_buffer, HSA_MESSAGE_LENGTH - 1, "%s: %s", msg, \
|
||||
emsg ? emsg : "<unknown error>"); \
|
||||
throw ::rocprofsys::exception<std::runtime_error>(_buffer); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
static const size_t MEM_PAGE_BYTES = 0x1000;
|
||||
static const size_t MEM_PAGE_MASK = MEM_PAGE_BYTES - 1;
|
||||
typedef decltype(hsa_agent_t::handle) hsa_agent_handle_t;
|
||||
|
||||
struct hsa_pfn_t
|
||||
{
|
||||
decltype(::hsa_init)* hsa_init;
|
||||
decltype(::hsa_shut_down)* hsa_shut_down;
|
||||
decltype(::hsa_agent_get_info)* hsa_agent_get_info;
|
||||
decltype(::hsa_iterate_agents)* hsa_iterate_agents;
|
||||
|
||||
decltype(::hsa_queue_create)* hsa_queue_create;
|
||||
decltype(::hsa_queue_destroy)* hsa_queue_destroy;
|
||||
decltype(::hsa_queue_load_read_index_relaxed)* hsa_queue_load_read_index_relaxed;
|
||||
decltype(::hsa_queue_load_write_index_relaxed)* hsa_queue_load_write_index_relaxed;
|
||||
decltype(
|
||||
::hsa_queue_add_write_index_scacq_screl)* hsa_queue_add_write_index_scacq_screl;
|
||||
|
||||
decltype(::hsa_signal_create)* hsa_signal_create;
|
||||
decltype(::hsa_signal_destroy)* hsa_signal_destroy;
|
||||
decltype(::hsa_signal_load_relaxed)* hsa_signal_load_relaxed;
|
||||
decltype(::hsa_signal_store_relaxed)* hsa_signal_store_relaxed;
|
||||
decltype(::hsa_signal_wait_scacquire)* hsa_signal_wait_scacquire;
|
||||
decltype(::hsa_signal_store_screlease)* hsa_signal_store_screlease;
|
||||
|
||||
decltype(::hsa_code_object_reader_create_from_file)*
|
||||
hsa_code_object_reader_create_from_file;
|
||||
decltype(::hsa_executable_create_alt)* hsa_executable_create_alt;
|
||||
decltype(
|
||||
::hsa_executable_load_agent_code_object)* hsa_executable_load_agent_code_object;
|
||||
decltype(::hsa_executable_freeze)* hsa_executable_freeze;
|
||||
decltype(::hsa_executable_destroy)* hsa_executable_destroy;
|
||||
decltype(::hsa_executable_get_symbol)* hsa_executable_get_symbol;
|
||||
decltype(::hsa_executable_symbol_get_info)* hsa_executable_symbol_get_info;
|
||||
decltype(::hsa_executable_iterate_symbols)* hsa_executable_iterate_symbols;
|
||||
|
||||
decltype(::hsa_system_get_info)* hsa_system_get_info;
|
||||
decltype(
|
||||
::hsa_system_get_major_extension_table)* hsa_system_get_major_extension_table;
|
||||
|
||||
decltype(::hsa_amd_agent_iterate_memory_pools)* hsa_amd_agent_iterate_memory_pools;
|
||||
decltype(::hsa_amd_memory_pool_get_info)* hsa_amd_memory_pool_get_info;
|
||||
decltype(::hsa_amd_memory_pool_allocate)* hsa_amd_memory_pool_allocate;
|
||||
decltype(::hsa_amd_agents_allow_access)* hsa_amd_agents_allow_access;
|
||||
decltype(::hsa_amd_memory_async_copy)* hsa_amd_memory_async_copy;
|
||||
|
||||
decltype(::hsa_amd_signal_async_handler)* hsa_amd_signal_async_handler;
|
||||
decltype(
|
||||
::hsa_amd_profiling_set_profiler_enabled)* hsa_amd_profiling_set_profiler_enabled;
|
||||
decltype(
|
||||
::hsa_amd_profiling_get_async_copy_time)* hsa_amd_profiling_get_async_copy_time;
|
||||
decltype(::hsa_amd_profiling_get_dispatch_time)* hsa_amd_profiling_get_dispatch_time;
|
||||
};
|
||||
|
||||
// Encapsulates information about a Hsa Agent such as its
|
||||
// handle, name, max queue size, max wavefront size, etc.
|
||||
struct AgentInfo
|
||||
{
|
||||
// Handle of Agent
|
||||
hsa_agent_t dev_id;
|
||||
|
||||
// Agent type - Cpu = 0, Gpu = 1 or Dsp = 2
|
||||
uint32_t dev_type;
|
||||
|
||||
// APU flag
|
||||
bool is_apu;
|
||||
|
||||
// Agent system index
|
||||
uint32_t dev_index;
|
||||
|
||||
// GFXIP name
|
||||
char gfxip[64];
|
||||
|
||||
// Name of Agent whose length is less than 64
|
||||
char name[64];
|
||||
|
||||
// Max size of Wavefront size
|
||||
uint32_t max_wave_size;
|
||||
|
||||
// Max size of Queue buffer
|
||||
uint32_t max_queue_size;
|
||||
|
||||
// Hsail profile supported by agent
|
||||
hsa_profile_t profile;
|
||||
|
||||
// CPU/GPU/kern-arg memory pools
|
||||
hsa_amd_memory_pool_t cpu_pool;
|
||||
hsa_amd_memory_pool_t gpu_pool;
|
||||
hsa_amd_memory_pool_t kern_arg_pool;
|
||||
|
||||
// The number of compute unit available in the agent.
|
||||
uint32_t cu_num;
|
||||
|
||||
// Maximum number of waves possible in a Compute Unit.
|
||||
uint32_t waves_per_cu;
|
||||
|
||||
// Number of SIMD's per compute unit CU
|
||||
uint32_t simds_per_cu;
|
||||
|
||||
// Number of Shader Engines (SE) in Gpu
|
||||
uint32_t se_num;
|
||||
|
||||
// Number of Shader Arrays Per Shader Engines in Gpu
|
||||
uint32_t shader_arrays_per_se;
|
||||
|
||||
// SGPR/VGPR/LDS block sizes
|
||||
uint32_t sgpr_block_dflt;
|
||||
uint32_t sgpr_block_size;
|
||||
uint32_t vgpr_block_size;
|
||||
static const uint32_t lds_block_size = 128 * 4;
|
||||
};
|
||||
|
||||
// HSA timer class
|
||||
// Provides current HSA timestampa and system-clock/ns conversion API
|
||||
class HsaTimer
|
||||
{
|
||||
public:
|
||||
typedef uint64_t timestamp_t;
|
||||
static const timestamp_t TIMESTAMP_MAX = UINT64_MAX;
|
||||
typedef long double freq_t;
|
||||
|
||||
enum time_id_t
|
||||
{
|
||||
TIME_ID_CLOCK_REALTIME = 0,
|
||||
TIME_ID_CLOCK_REALTIME_COARSE = 1,
|
||||
TIME_ID_CLOCK_MONOTONIC = 2,
|
||||
TIME_ID_CLOCK_MONOTONIC_COARSE = 3,
|
||||
TIME_ID_CLOCK_MONOTONIC_RAW = 4,
|
||||
TIME_ID_NUMBER
|
||||
};
|
||||
|
||||
HsaTimer(const hsa_pfn_t* hsa_api)
|
||||
: hsa_api_(hsa_api)
|
||||
{
|
||||
timestamp_t sysclock_hz = 0;
|
||||
hsa_status_t status = hsa_api_->hsa_system_get_info(
|
||||
HSA_SYSTEM_INFO_TIMESTAMP_FREQUENCY, &sysclock_hz);
|
||||
CHECK_STATUS("hsa_system_get_info(HSA_SYSTEM_INFO_TIMESTAMP_FREQUENCY)", status);
|
||||
sysclock_factor_ = (freq_t) 1000000000 / (freq_t) sysclock_hz;
|
||||
}
|
||||
|
||||
// Methods for system-clock/ns conversion
|
||||
timestamp_t sysclock_to_ns(const timestamp_t& sysclock) const
|
||||
{
|
||||
return timestamp_t((freq_t) sysclock * sysclock_factor_);
|
||||
}
|
||||
timestamp_t ns_to_sysclock(const timestamp_t& time) const
|
||||
{
|
||||
return timestamp_t((freq_t) time / sysclock_factor_);
|
||||
}
|
||||
|
||||
// Method for timespec/ns conversion
|
||||
static timestamp_t timespec_to_ns(const timespec& time)
|
||||
{
|
||||
return ((timestamp_t) time.tv_sec * 1000000000) + time.tv_nsec;
|
||||
}
|
||||
|
||||
// Return timestamp in 'ns'
|
||||
timestamp_t timestamp_ns() const
|
||||
{
|
||||
timestamp_t sysclock;
|
||||
hsa_status_t status =
|
||||
hsa_api_->hsa_system_get_info(HSA_SYSTEM_INFO_TIMESTAMP, &sysclock);
|
||||
CHECK_STATUS("hsa_system_get_info(HSA_SYSTEM_INFO_TIMESTAMP)", status);
|
||||
return sysclock_to_ns(sysclock);
|
||||
}
|
||||
|
||||
// Return time in 'ns'
|
||||
timestamp_t clocktime_ns(clockid_t clock_id) const
|
||||
{
|
||||
timespec time;
|
||||
clock_gettime(clock_id, &time);
|
||||
return timespec_to_ns(time);
|
||||
}
|
||||
|
||||
// Return pair of correlated values of profiling timestamp and time with
|
||||
// correlation error for a given time ID and number of iterations
|
||||
void correlated_pair_ns(time_id_t time_id, uint32_t iters, timestamp_t* timestamp_v,
|
||||
timestamp_t* time_v, timestamp_t* error_v)
|
||||
{
|
||||
clockid_t clock_id = 0;
|
||||
switch(time_id)
|
||||
{
|
||||
case TIME_ID_CLOCK_REALTIME: clock_id = CLOCK_REALTIME; break;
|
||||
case TIME_ID_CLOCK_REALTIME_COARSE: clock_id = CLOCK_REALTIME_COARSE; break;
|
||||
case TIME_ID_CLOCK_MONOTONIC: clock_id = CLOCK_MONOTONIC; break;
|
||||
case TIME_ID_CLOCK_MONOTONIC_COARSE: clock_id = CLOCK_MONOTONIC_COARSE; break;
|
||||
case TIME_ID_CLOCK_MONOTONIC_RAW: clock_id = CLOCK_MONOTONIC_RAW; break;
|
||||
default: CHECK_STATUS("internal error: invalid time_id", HSA_STATUS_ERROR);
|
||||
}
|
||||
|
||||
std::vector<timestamp_t> ts_vec(iters);
|
||||
std::vector<timespec> tm_vec(iters);
|
||||
const uint32_t steps = iters - 1;
|
||||
|
||||
for(uint32_t i = 0; i < iters; ++i)
|
||||
{
|
||||
hsa_api_->hsa_system_get_info(HSA_SYSTEM_INFO_TIMESTAMP, &ts_vec[i]);
|
||||
clock_gettime(clock_id, &tm_vec[i]);
|
||||
}
|
||||
|
||||
const timestamp_t ts_base = sysclock_to_ns(ts_vec.front());
|
||||
const timestamp_t tm_base = timespec_to_ns(tm_vec.front());
|
||||
const timestamp_t error = (ts_vec.back() - ts_vec.front()) / (2 * steps);
|
||||
|
||||
timestamp_t ts_accum = 0;
|
||||
timestamp_t tm_accum = 0;
|
||||
for(uint32_t i = 0; i < iters; ++i)
|
||||
{
|
||||
ts_accum += (ts_vec[i] - ts_base);
|
||||
tm_accum += (timespec_to_ns(tm_vec[i]) - tm_base);
|
||||
}
|
||||
|
||||
*timestamp_v = (ts_accum / iters) + ts_base + error;
|
||||
*time_v = (tm_accum / iters) + tm_base;
|
||||
*error_v = error;
|
||||
}
|
||||
|
||||
private:
|
||||
// Timestamp frequency factor
|
||||
freq_t sysclock_factor_;
|
||||
// HSA API table
|
||||
const hsa_pfn_t* const hsa_api_;
|
||||
};
|
||||
|
||||
class HsaRsrcFactory
|
||||
{
|
||||
public:
|
||||
static const size_t CMD_SLOT_SIZE_B = 0x40;
|
||||
typedef std::recursive_mutex mutex_t;
|
||||
typedef HsaTimer::timestamp_t timestamp_t;
|
||||
|
||||
static HsaRsrcFactory* Create(bool initialize_hsa = true)
|
||||
{
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
HsaRsrcFactory* obj = instance_.load(std::memory_order_relaxed);
|
||||
if(obj == nullptr)
|
||||
{
|
||||
obj = new HsaRsrcFactory(initialize_hsa);
|
||||
instance_.store(obj, std::memory_order_release);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static HsaRsrcFactory& Instance()
|
||||
{
|
||||
HsaRsrcFactory* obj = instance_.load(std::memory_order_acquire);
|
||||
if(obj == nullptr) obj = Create(false);
|
||||
hsa_status_t status = (obj != nullptr) ? HSA_STATUS_SUCCESS : HSA_STATUS_ERROR;
|
||||
CHECK_STATUS("HsaRsrcFactory::Instance() failed", status);
|
||||
return *obj;
|
||||
}
|
||||
|
||||
static void Destroy()
|
||||
{
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
if(instance_) delete instance_.load();
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
// Return system agent info
|
||||
const AgentInfo* GetAgentInfo(const hsa_agent_t agent);
|
||||
|
||||
// Get the count of Hsa Gpu Agents available on the platform
|
||||
// @return uint32_t Number of Gpu agents on platform
|
||||
uint32_t GetCountOfGpuAgents();
|
||||
|
||||
// Get the count of Hsa Cpu Agents available on the platform
|
||||
// @return uint32_t Number of Cpu agents on platform
|
||||
uint32_t GetCountOfCpuAgents();
|
||||
|
||||
// Get the AgentInfo handle of a Gpu device
|
||||
// @param idx Gpu Agent at specified index
|
||||
// @param agent_info Output parameter updated with AgentInfo
|
||||
// @return bool true if successful, false otherwise
|
||||
bool GetGpuAgentInfo(uint32_t idx, const AgentInfo** agent_info);
|
||||
|
||||
// Get the AgentInfo handle of a Cpu device
|
||||
// @param idx Cpu Agent at specified index
|
||||
// @param agent_info Output parameter updated with AgentInfo
|
||||
// @return bool true if successful, false otherwise
|
||||
bool GetCpuAgentInfo(uint32_t idx, const AgentInfo** agent_info);
|
||||
|
||||
// Create a Queue object and return its handle. The queue object is expected
|
||||
// to support user requested number of Aql dispatch packets.
|
||||
// @param agent_info Gpu Agent on which to create a queue object
|
||||
// @param num_Pkts Number of packets to be held by queue
|
||||
// @param queue Output parameter updated with handle of queue object
|
||||
// @return bool true if successful, false otherwise
|
||||
bool CreateQueue(const AgentInfo* agent_info, uint32_t num_pkts, hsa_queue_t** queue);
|
||||
|
||||
// Create a Signal object and return its handle.
|
||||
// @param value Initial value of signal object
|
||||
// @param signal Output parameter updated with handle of signal object
|
||||
// @return bool true if successful, false otherwise
|
||||
bool CreateSignal(uint32_t value, hsa_signal_t* signal);
|
||||
|
||||
// Allocate local GPU memory
|
||||
// @param agent_info Agent from whose memory region to allocate
|
||||
// @param size Size of memory in terms of bytes
|
||||
// @return uint8_t* Pointer to buffer, null if allocation fails.
|
||||
uint8_t* AllocateLocalMemory(const AgentInfo* agent_info, size_t size);
|
||||
|
||||
// Allocate memory tp pass kernel parameters
|
||||
// Memory is alocated accessible for all CPU agents and for GPU given by AgentInfo
|
||||
// parameter.
|
||||
// @param agent_info Agent from whose memory region to allocate
|
||||
// @param size Size of memory in terms of bytes
|
||||
// @return uint8_t* Pointer to buffer, null if allocation fails.
|
||||
uint8_t* AllocateKernArgMemory(const AgentInfo* agent_info, size_t size);
|
||||
|
||||
// Allocate system memory accessible from both CPU and GPU
|
||||
// Memory is alocated accessible to all CPU agents and AgentInfo parameter is ignored.
|
||||
// @param agent_info Agent from whose memory region to allocate
|
||||
// @param size Size of memory in terms of bytes
|
||||
// @return uint8_t* Pointer to buffer, null if allocation fails.
|
||||
uint8_t* AllocateSysMemory(const AgentInfo* agent_info, size_t size);
|
||||
|
||||
// Allocate memory for command buffer.
|
||||
// @param agent_info Agent from whose memory region to allocate
|
||||
// @param size Size of memory in terms of bytes
|
||||
// @return uint8_t* Pointer to buffer, null if allocation fails.
|
||||
uint8_t* AllocateCmdMemory(const AgentInfo* agent_info, size_t size);
|
||||
|
||||
// Wait signal
|
||||
hsa_signal_value_t SignalWait(const hsa_signal_t& signal,
|
||||
const hsa_signal_value_t& signal_value) const;
|
||||
|
||||
// Wait signal with signal value restore
|
||||
void SignalWaitRestore(const hsa_signal_t& signal,
|
||||
const hsa_signal_value_t& signal_value) const;
|
||||
|
||||
// Copy data from GPU to host memory
|
||||
bool Memcpy(const hsa_agent_t& agent, void* dst, const void* src, size_t size);
|
||||
bool Memcpy(const AgentInfo* agent_info, void* dst, const void* src, size_t size);
|
||||
|
||||
// Memory free method
|
||||
static bool FreeMemory(void* ptr);
|
||||
|
||||
// Loads an Assembled Brig file and Finalizes it into Device Isa
|
||||
// @param agent_info Gpu device for which to finalize
|
||||
// @param brig_path File path of the Assembled Brig file
|
||||
// @param kernel_name Name of the kernel to finalize
|
||||
// @param code_desc Handle of finalized Code Descriptor that could
|
||||
// be used to submit for execution
|
||||
// @return true if successful, false otherwise
|
||||
bool LoadAndFinalize(const AgentInfo* agent_info, const char* brig_path,
|
||||
const char* kernel_name, hsa_executable_t* hsa_exec,
|
||||
hsa_executable_symbol_t* code_desc);
|
||||
|
||||
// Print the various fields of Hsa Gpu Agents
|
||||
bool PrintGpuAgents(const std::string& header);
|
||||
|
||||
// Utils for submitting AQL packet to a given queue
|
||||
static void* GetSlotPointer(hsa_queue_t* queue, const uint64_t& idx);
|
||||
static void* GetReadPointer(hsa_queue_t* queue);
|
||||
static uint64_t Submit(hsa_queue_t* queue, const void* packet);
|
||||
static uint64_t Submit(hsa_queue_t* queue, const void* packet, size_t size_bytes);
|
||||
|
||||
// Enable executables loading tracking
|
||||
static bool IsExecutableTracking() { return executable_tracking_on_; }
|
||||
static void EnableExecutableTracking(HsaApiTable* table);
|
||||
static const char* GetKernelNameRef(uint64_t addr);
|
||||
|
||||
// Initialize HSA API table
|
||||
void static InitHsaApiTable(HsaApiTable* table);
|
||||
static const hsa_pfn_t* HsaApi() { return &hsa_api_; }
|
||||
|
||||
// Return AqlProfile API table
|
||||
typedef hsa_ven_amd_aqlprofile_pfn_t aqlprofile_pfn_t;
|
||||
const aqlprofile_pfn_t* AqlProfileApi() const { return &aqlprofile_api_; }
|
||||
|
||||
// Return Loader API table
|
||||
const hsa_ven_amd_loader_1_00_pfn_t* LoaderApi() const { return &loader_api_; }
|
||||
|
||||
// Methods for system-clock/ns conversion and timestamp in 'ns'
|
||||
timestamp_t SysclockToNs(const timestamp_t& sysclock) const
|
||||
{
|
||||
return timer_->sysclock_to_ns(sysclock);
|
||||
}
|
||||
timestamp_t NsToSysclock(const timestamp_t& time) const
|
||||
{
|
||||
return timer_->ns_to_sysclock(time);
|
||||
}
|
||||
timestamp_t TimestampNs() const { return timer_->timestamp_ns(); }
|
||||
|
||||
timestamp_t GetSysTimeout() const { return timeout_; }
|
||||
static timestamp_t GetTimeoutNs() { return timeout_ns_; }
|
||||
static void SetTimeoutNs(const timestamp_t& time)
|
||||
{
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
timeout_ns_ = time;
|
||||
if(instance_ != nullptr)
|
||||
Instance().timeout_ = Instance().timer_->ns_to_sysclock(time);
|
||||
}
|
||||
|
||||
void CorrelateTime(HsaTimer::time_id_t time_id, uint32_t iters)
|
||||
{
|
||||
timestamp_t timestamp_v = 0;
|
||||
timestamp_t time_v = 0;
|
||||
timestamp_t error_v = 0;
|
||||
timer_->correlated_pair_ns(time_id, iters, ×tamp_v, &time_v, &error_v);
|
||||
time_shift_[time_id] = time_v - timestamp_v;
|
||||
time_error_[time_id] = error_v;
|
||||
}
|
||||
|
||||
hsa_status_t GetTimeVal(uint32_t time_id, uint64_t time_stamp, uint64_t* time_value)
|
||||
{
|
||||
if(time_id >= HsaTimer::TIME_ID_NUMBER) return HSA_STATUS_ERROR;
|
||||
*time_value = time_stamp + time_shift_[time_id];
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
hsa_status_t GetTimeErr(uint32_t time_id, uint64_t* err)
|
||||
{
|
||||
*err = time_error_[time_id];
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
private:
|
||||
// System agents iterating callback
|
||||
static hsa_status_t GetHsaAgentsCallback(hsa_agent_t agent, void* data);
|
||||
|
||||
// Callback function to find and bind kernarg region of an agent
|
||||
static hsa_status_t FindMemRegionsCallback(hsa_region_t region, void* data);
|
||||
|
||||
// Load AQL profile HSA extension library directly
|
||||
static hsa_status_t LoadAqlProfileLib(aqlprofile_pfn_t* api);
|
||||
|
||||
// Constructor of the class. Will initialize the Hsa Runtime and
|
||||
// query the system topology to get the list of Cpu and Gpu devices
|
||||
explicit HsaRsrcFactory(bool initialize_hsa);
|
||||
|
||||
// Destructor of the class
|
||||
~HsaRsrcFactory();
|
||||
|
||||
// Add an instance of AgentInfo representing a Hsa Gpu agent
|
||||
const AgentInfo* AddAgentInfo(const hsa_agent_t agent);
|
||||
|
||||
// To mmap command buffer memory
|
||||
static const bool CMD_MEMORY_MMAP = false;
|
||||
|
||||
// HSA was initialized
|
||||
const bool initialize_hsa_;
|
||||
|
||||
static std::atomic<HsaRsrcFactory*> instance_;
|
||||
static mutex_t mutex_;
|
||||
|
||||
// Used to maintain a list of Hsa Gpu Agent Info
|
||||
std::vector<const AgentInfo*> gpu_list_;
|
||||
std::vector<hsa_agent_t> gpu_agents_;
|
||||
|
||||
// Used to maintain a list of Hsa Cpu Agent Info
|
||||
std::vector<const AgentInfo*> cpu_list_;
|
||||
std::vector<hsa_agent_t> cpu_agents_;
|
||||
|
||||
// System agents map
|
||||
std::map<hsa_agent_handle_t, const AgentInfo*> agent_map_;
|
||||
|
||||
// Executables loading tracking
|
||||
typedef std::map<uint64_t, const char*> symbols_map_t;
|
||||
static symbols_map_t* symbols_map_;
|
||||
static bool executable_tracking_on_;
|
||||
static void* to_dump_code_obj_;
|
||||
static hsa_status_t hsa_executable_freeze_interceptor(hsa_executable_t executable,
|
||||
const char* options);
|
||||
static hsa_status_t hsa_executable_destroy_interceptor(hsa_executable_t executable);
|
||||
static hsa_status_t executable_symbols_cb(hsa_executable_t exec,
|
||||
hsa_executable_symbol_t symbol, void* data);
|
||||
|
||||
// HSA runtime API table
|
||||
static hsa_pfn_t hsa_api_;
|
||||
|
||||
// AqlProfile API table
|
||||
aqlprofile_pfn_t aqlprofile_api_;
|
||||
|
||||
// Loader API table
|
||||
hsa_ven_amd_loader_1_00_pfn_t loader_api_;
|
||||
|
||||
// System timeout, ns
|
||||
static timestamp_t timeout_ns_;
|
||||
// System timeout, sysclock
|
||||
timestamp_t timeout_;
|
||||
|
||||
// HSA timer
|
||||
HsaTimer* timer_;
|
||||
|
||||
// Time shift array to support time conversion
|
||||
timestamp_t time_shift_[HsaTimer::TIME_ID_NUMBER];
|
||||
timestamp_t time_error_[HsaTimer::TIME_ID_NUMBER];
|
||||
|
||||
// CPU/kern-arg memory pools
|
||||
hsa_amd_memory_pool_t* cpu_pool_;
|
||||
hsa_amd_memory_pool_t* kern_arg_pool_;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,492 @@
|
||||
// Copyright (c) 2018-2024 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// with the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimers.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimers in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the names of Advanced Micro Devices, Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this Software without specific prior written permission.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
|
||||
// THE SOFTWARE.
|
||||
|
||||
#if defined(NDEBUG)
|
||||
# undef NDEBUG
|
||||
#endif
|
||||
|
||||
#include "library/rocm_smi.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/gpu.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/runtime.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/components/timing/backends.hpp>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/units.hpp>
|
||||
#include <timemory/utility/delimit.hpp>
|
||||
#include <timemory/utility/locking.hpp>
|
||||
|
||||
#include <rocm_smi/rocm_smi.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <sys/resource.h>
|
||||
#include <thread>
|
||||
|
||||
#define ROCPROFSYS_ROCM_SMI_CALL(...) \
|
||||
::rocprofsys::rocm_smi::check_error(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace rocm_smi
|
||||
{
|
||||
using bundle_t = std::deque<data>;
|
||||
using sampler_instances = thread_data<bundle_t, category::rocm_smi>;
|
||||
|
||||
namespace
|
||||
{
|
||||
auto&
|
||||
get_settings(uint32_t _dev_id)
|
||||
{
|
||||
static auto _v = std::unordered_map<uint32_t, rocm_smi::settings>{};
|
||||
return _v[_dev_id];
|
||||
}
|
||||
|
||||
bool&
|
||||
is_initialized()
|
||||
{
|
||||
static bool _v = false;
|
||||
return _v;
|
||||
}
|
||||
|
||||
void
|
||||
check_error(const char* _file, int _line, rsmi_status_t _code, bool* _option = nullptr)
|
||||
{
|
||||
if(_code == RSMI_STATUS_SUCCESS)
|
||||
return;
|
||||
else if(_code == RSMI_STATUS_NOT_SUPPORTED && _option)
|
||||
{
|
||||
*_option = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const char* _msg = nullptr;
|
||||
auto _err = rsmi_status_string(_code, &_msg);
|
||||
if(_err != RSMI_STATUS_SUCCESS)
|
||||
ROCPROFSYS_THROW("rsmi_status_string failed. No error message available. "
|
||||
"Error code %i originated at %s:%i\n",
|
||||
static_cast<int>(_code), _file, _line);
|
||||
ROCPROFSYS_THROW("[%s:%i] Error code %i :: %s", _file, _line, static_cast<int>(_code),
|
||||
_msg);
|
||||
}
|
||||
|
||||
std::atomic<State>&
|
||||
get_state()
|
||||
{
|
||||
static std::atomic<State> _v{ State::PreInit };
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
size_t data::device_count = 0;
|
||||
std::set<uint32_t> data::device_list = {};
|
||||
std::unique_ptr<data::promise_t> data::polling_finished = {};
|
||||
|
||||
data::data(uint32_t _dev_id) { sample(_dev_id); }
|
||||
|
||||
void
|
||||
data::sample(uint32_t _dev_id)
|
||||
{
|
||||
auto _ts = tim::get_clock_real_now<size_t, std::nano>();
|
||||
assert(_ts < std::numeric_limits<int64_t>::max());
|
||||
|
||||
auto _state = get_state().load();
|
||||
|
||||
if(_state != State::Active) return;
|
||||
|
||||
m_dev_id = _dev_id;
|
||||
m_ts = _ts;
|
||||
|
||||
#define ROCPROFSYS_RSMI_GET(OPTION, FUNCTION, ...) \
|
||||
if(OPTION) \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
ROCPROFSYS_ROCM_SMI_CALL(FUNCTION(__VA_ARGS__), &OPTION); \
|
||||
} catch(std::runtime_error & _e) \
|
||||
{ \
|
||||
ROCPROFSYS_VERBOSE_F( \
|
||||
0, "[%s] Exception: %s. Disabling future samples from rocm-smi...\n", \
|
||||
#FUNCTION, _e.what()); \
|
||||
get_state().store(State::Disabled); \
|
||||
} \
|
||||
}
|
||||
|
||||
ROCPROFSYS_RSMI_GET(get_settings(m_dev_id).busy, rsmi_dev_busy_percent_get, _dev_id,
|
||||
&m_busy_perc);
|
||||
ROCPROFSYS_RSMI_GET(get_settings(m_dev_id).temp, rsmi_dev_temp_metric_get, _dev_id,
|
||||
RSMI_TEMP_TYPE_EDGE, RSMI_TEMP_CURRENT, &m_temp);
|
||||
ROCPROFSYS_RSMI_GET(get_settings(m_dev_id).power, rsmi_dev_power_ave_get, _dev_id, 0,
|
||||
&m_power);
|
||||
ROCPROFSYS_RSMI_GET(get_settings(m_dev_id).mem_usage, rsmi_dev_memory_usage_get,
|
||||
_dev_id, RSMI_MEM_TYPE_VRAM, &m_mem_usage);
|
||||
|
||||
#undef ROCPROFSYS_RSMI_GET
|
||||
}
|
||||
|
||||
void
|
||||
data::print(std::ostream& _os) const
|
||||
{
|
||||
std::stringstream _ss{};
|
||||
_ss << "device: " << m_dev_id << ", busy = " << m_busy_perc << "%, temp = " << m_temp
|
||||
<< ", power = " << m_power << ", memory usage = " << m_mem_usage;
|
||||
_os << _ss.str();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<unique_ptr_t<bundle_t>*> _bundle_data{};
|
||||
}
|
||||
|
||||
void
|
||||
config()
|
||||
{
|
||||
_bundle_data.resize(data::device_count, nullptr);
|
||||
for(size_t i = 0; i < data::device_count; ++i)
|
||||
{
|
||||
if(data::device_list.count(i) > 0)
|
||||
{
|
||||
_bundle_data.at(i) = &sampler_instances::get()->at(i);
|
||||
if(!*_bundle_data.at(i))
|
||||
*_bundle_data.at(i) = unique_ptr_t<bundle_t>{ new bundle_t{} };
|
||||
}
|
||||
}
|
||||
|
||||
data::get_initial().resize(data::device_count);
|
||||
for(auto itr : data::device_list)
|
||||
data::get_initial().at(itr).sample(itr);
|
||||
}
|
||||
|
||||
void
|
||||
sample()
|
||||
{
|
||||
for(auto itr : data::device_list)
|
||||
{
|
||||
if(rocm_smi::get_state() != State::Active) continue;
|
||||
ROCPROFSYS_DEBUG_F("Polling rocm-smi for device %u...\n", itr);
|
||||
auto& _data = *_bundle_data.at(itr);
|
||||
if(!_data) continue;
|
||||
_data->emplace_back(data{ itr });
|
||||
ROCPROFSYS_DEBUG_F(" %s\n", TIMEMORY_JOIN("", _data->back()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_state(State _v)
|
||||
{
|
||||
rocm_smi::get_state().store(_v);
|
||||
}
|
||||
|
||||
std::vector<data>&
|
||||
data::get_initial()
|
||||
{
|
||||
static std::vector<data> _v{};
|
||||
return _v;
|
||||
}
|
||||
|
||||
bool
|
||||
data::setup()
|
||||
{
|
||||
perfetto_counter_track<data>::init();
|
||||
rocm_smi::set_state(State::PreInit);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
data::shutdown()
|
||||
{
|
||||
ROCPROFSYS_DEBUG("Shutting down rocm-smi...\n");
|
||||
rocm_smi::set_state(State::Finalized);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define GPU_METRIC(COMPONENT, ...) \
|
||||
if constexpr(tim::trait::is_available<COMPONENT>::value) \
|
||||
{ \
|
||||
auto* _val = _v.get<COMPONENT>(); \
|
||||
if(_val) \
|
||||
{ \
|
||||
_val->set_value(itr.__VA_ARGS__); \
|
||||
_val->set_accum(itr.__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
void
|
||||
data::post_process(uint32_t _dev_id)
|
||||
{
|
||||
using component::sampling_gpu_busy;
|
||||
using component::sampling_gpu_memory;
|
||||
using component::sampling_gpu_power;
|
||||
using component::sampling_gpu_temp;
|
||||
|
||||
if(device_count < _dev_id) return;
|
||||
|
||||
auto& _rocm_smi_v = sampler_instances::get()->at(_dev_id);
|
||||
auto _rocm_smi = (_rocm_smi_v) ? *_rocm_smi_v : std::deque<rocm_smi::data>{};
|
||||
const auto& _thread_info = thread_info::get(0, InternalTID);
|
||||
|
||||
ROCPROFSYS_VERBOSE(1, "Post-processing %zu rocm-smi samples from device %u\n",
|
||||
_rocm_smi.size(), _dev_id);
|
||||
|
||||
ROCPROFSYS_CI_THROW(!_thread_info, "Missing thread info for thread 0");
|
||||
if(!_thread_info) return;
|
||||
|
||||
auto _settings = get_settings(_dev_id);
|
||||
|
||||
auto _process_perfetto = [&]() {
|
||||
auto _idx = std::array<uint64_t, 4>{};
|
||||
{
|
||||
_idx.fill(_idx.size());
|
||||
uint64_t nidx = 0;
|
||||
if(_settings.busy) _idx.at(0) = nidx++;
|
||||
if(_settings.temp) _idx.at(1) = nidx++;
|
||||
if(_settings.power) _idx.at(2) = nidx++;
|
||||
if(_settings.mem_usage) _idx.at(3) = nidx++;
|
||||
}
|
||||
|
||||
for(auto& itr : _rocm_smi)
|
||||
{
|
||||
using counter_track = perfetto_counter_track<data>;
|
||||
if(itr.m_dev_id != _dev_id) continue;
|
||||
if(!counter_track::exists(_dev_id))
|
||||
{
|
||||
auto addendum = [&](const char* _v) {
|
||||
return JOIN(" ", "GPU", _v, JOIN("", '[', _dev_id, ']'), "(S)");
|
||||
};
|
||||
|
||||
if(_settings.busy) counter_track::emplace(_dev_id, addendum("Busy"), "%");
|
||||
if(_settings.temp)
|
||||
counter_track::emplace(_dev_id, addendum("Temperature"), "deg C");
|
||||
if(_settings.power)
|
||||
counter_track::emplace(_dev_id, addendum("Power"), "watts");
|
||||
if(_settings.mem_usage)
|
||||
counter_track::emplace(_dev_id, addendum("Memory Usage"),
|
||||
"megabytes");
|
||||
}
|
||||
uint64_t _ts = itr.m_ts;
|
||||
if(!_thread_info->is_valid_time(_ts)) continue;
|
||||
|
||||
double _busy = itr.m_busy_perc;
|
||||
double _temp = itr.m_temp / 1.0e3;
|
||||
double _power = itr.m_power / 1.0e6;
|
||||
double _usage = itr.m_mem_usage / static_cast<double>(units::megabyte);
|
||||
|
||||
if(_settings.busy)
|
||||
TRACE_COUNTER("device_busy", counter_track::at(_dev_id, _idx.at(0)), _ts,
|
||||
_busy);
|
||||
if(_settings.temp)
|
||||
TRACE_COUNTER("device_temp", counter_track::at(_dev_id, _idx.at(1)), _ts,
|
||||
_temp);
|
||||
if(_settings.power)
|
||||
TRACE_COUNTER("device_power", counter_track::at(_dev_id, _idx.at(2)), _ts,
|
||||
_power);
|
||||
if(_settings.mem_usage)
|
||||
TRACE_COUNTER("device_memory_usage",
|
||||
counter_track::at(_dev_id, _idx.at(3)), _ts, _usage);
|
||||
}
|
||||
};
|
||||
|
||||
if(get_use_perfetto()) _process_perfetto();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
void
|
||||
setup()
|
||||
{
|
||||
auto_lock_t _lk{ type_mutex<category::rocm_smi>() };
|
||||
|
||||
if(is_initialized() || !get_use_rocm_smi()) return;
|
||||
|
||||
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
|
||||
|
||||
// assign the data value to determined by rocm-smi
|
||||
data::device_count = device_count();
|
||||
|
||||
auto _devices_v = get_sampling_gpus();
|
||||
for(auto& itr : _devices_v)
|
||||
itr = tolower(itr);
|
||||
if(_devices_v == "off")
|
||||
_devices_v = "none";
|
||||
else if(_devices_v == "on")
|
||||
_devices_v = "all";
|
||||
bool _all_devices = _devices_v.find("all") != std::string::npos || _devices_v.empty();
|
||||
bool _no_devices = _devices_v.find("none") != std::string::npos;
|
||||
|
||||
std::set<uint32_t> _devices = {};
|
||||
auto _emplace = [&_devices](auto idx) {
|
||||
if(idx < data::device_count) _devices.emplace(idx);
|
||||
};
|
||||
|
||||
if(_all_devices)
|
||||
{
|
||||
for(uint32_t i = 0; i < data::device_count; ++i)
|
||||
_emplace(i);
|
||||
}
|
||||
else if(!_no_devices)
|
||||
{
|
||||
auto _enabled = tim::delimit(_devices_v, ",; \t");
|
||||
for(auto&& itr : _enabled)
|
||||
{
|
||||
if(itr.find_first_not_of("0123456789-") != std::string::npos)
|
||||
{
|
||||
ROCPROFSYS_THROW("Invalid GPU specification: '%s'. Only numerical values "
|
||||
"(e.g., 0) or ranges (e.g., 0-7) are permitted.",
|
||||
itr.c_str());
|
||||
}
|
||||
|
||||
if(itr.find('-') != std::string::npos)
|
||||
{
|
||||
auto _v = tim::delimit(itr, "-");
|
||||
ROCPROFSYS_CONDITIONAL_THROW(_v.size() != 2,
|
||||
"Invalid GPU range specification: '%s'. "
|
||||
"Required format N-M, e.g. 0-4",
|
||||
itr.c_str());
|
||||
for(auto i = std::stoul(_v.at(0)); i < std::stoul(_v.at(1)); ++i)
|
||||
_emplace(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
_emplace(std::stoul(itr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data::device_list = _devices;
|
||||
|
||||
auto _metrics = get_setting_value<std::string>("ROCPROFSYS_ROCM_SMI_METRICS");
|
||||
|
||||
try
|
||||
{
|
||||
for(auto itr : _devices)
|
||||
{
|
||||
uint16_t dev_id = 0;
|
||||
ROCPROFSYS_ROCM_SMI_CALL(rsmi_dev_id_get(itr, &dev_id));
|
||||
// dev_id holds the device ID of device i, upon a successful call
|
||||
|
||||
if(_metrics && !_metrics->empty())
|
||||
{
|
||||
using key_pair_t = std::pair<std::string_view, bool&>;
|
||||
const auto supported = std::unordered_map<std::string_view, bool&>{
|
||||
key_pair_t{ "busy", get_settings(dev_id).busy },
|
||||
key_pair_t{ "temp", get_settings(dev_id).temp },
|
||||
key_pair_t{ "power", get_settings(dev_id).power },
|
||||
key_pair_t{ "mem_usage", get_settings(dev_id).mem_usage },
|
||||
};
|
||||
|
||||
get_settings(dev_id) = { false, false, false, false };
|
||||
for(const auto& metric : tim::delimit(*_metrics, ",;:\t\n "))
|
||||
{
|
||||
auto iitr = supported.find(metric);
|
||||
if(iitr == supported.end())
|
||||
ROCPROFSYS_FAIL_F("unsupported rocm-smi metric: %s\n",
|
||||
metric.c_str());
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(1, "Enabling rocm-smi metric '%s'\n",
|
||||
metric.c_str());
|
||||
iitr->second = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_initialized() = true;
|
||||
|
||||
data::setup();
|
||||
} catch(std::runtime_error& _e)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(0, "Exception thrown when initializing rocm-smi: %s\n",
|
||||
_e.what());
|
||||
data::device_list = {};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shutdown()
|
||||
{
|
||||
auto_lock_t _lk{ type_mutex<category::rocm_smi>() };
|
||||
|
||||
if(!is_initialized()) return;
|
||||
|
||||
try
|
||||
{
|
||||
if(data::shutdown())
|
||||
{
|
||||
ROCPROFSYS_ROCM_SMI_CALL(rsmi_shut_down());
|
||||
}
|
||||
} catch(std::runtime_error& _e)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE(0, "Exception thrown when shutting down rocm-smi: %s\n",
|
||||
_e.what());
|
||||
}
|
||||
|
||||
is_initialized() = false;
|
||||
}
|
||||
|
||||
void
|
||||
post_process()
|
||||
{
|
||||
for(auto itr : data::device_list)
|
||||
data::post_process(itr);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
device_count()
|
||||
{
|
||||
return gpu::rsmi_device_count();
|
||||
}
|
||||
} // namespace rocm_smi
|
||||
} // namespace rocprofsys
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_gpu_busy>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_gpu_temp>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_gpu_power>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_INSTANTIATE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_gpu_memory>), true,
|
||||
double)
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) 2018-2024 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// with the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimers.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimers in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the names of Advanced Micro Devices, Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this Software without specific prior written permission.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
|
||||
// THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <future>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace rocm_smi
|
||||
{
|
||||
void
|
||||
setup();
|
||||
|
||||
void
|
||||
config();
|
||||
|
||||
void
|
||||
sample();
|
||||
|
||||
void
|
||||
shutdown();
|
||||
|
||||
void
|
||||
post_process();
|
||||
|
||||
void set_state(State);
|
||||
|
||||
uint32_t
|
||||
device_count();
|
||||
|
||||
struct settings
|
||||
{
|
||||
bool busy = true;
|
||||
bool temp = true;
|
||||
bool power = true;
|
||||
bool mem_usage = true;
|
||||
};
|
||||
|
||||
struct data
|
||||
{
|
||||
using msec_t = std::chrono::milliseconds;
|
||||
using usec_t = std::chrono::microseconds;
|
||||
using nsec_t = std::chrono::nanoseconds;
|
||||
using promise_t = std::promise<void>;
|
||||
|
||||
using timestamp_t = int64_t;
|
||||
using power_t = uint64_t;
|
||||
using busy_perc_t = uint32_t;
|
||||
using mem_usage_t = uint64_t;
|
||||
using temp_t = int64_t;
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(data)
|
||||
|
||||
explicit data(uint32_t _dev_id);
|
||||
|
||||
void sample(uint32_t _dev_id);
|
||||
void print(std::ostream& _os) const;
|
||||
|
||||
static void post_process(uint32_t _dev_id);
|
||||
|
||||
uint32_t m_dev_id = std::numeric_limits<uint32_t>::max();
|
||||
timestamp_t m_ts = 0;
|
||||
busy_perc_t m_busy_perc = 0;
|
||||
temp_t m_temp = 0;
|
||||
power_t m_power = 0;
|
||||
mem_usage_t m_mem_usage = 0;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& _os, const data& _v)
|
||||
{
|
||||
_v.print(_os);
|
||||
return _os;
|
||||
}
|
||||
|
||||
private:
|
||||
friend void rocprofsys::rocm_smi::setup();
|
||||
friend void rocprofsys::rocm_smi::config();
|
||||
friend void rocprofsys::rocm_smi::sample();
|
||||
friend void rocprofsys::rocm_smi::shutdown();
|
||||
friend void rocprofsys::rocm_smi::post_process();
|
||||
|
||||
static size_t device_count;
|
||||
static std::set<uint32_t> device_list;
|
||||
static std::unique_ptr<promise_t> polling_finished;
|
||||
static std::vector<data>& get_initial();
|
||||
static std::unique_ptr<std::thread>& get_thread();
|
||||
static bool setup();
|
||||
static bool shutdown();
|
||||
};
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_ROCM_SMI)
|
||||
inline void
|
||||
setup()
|
||||
{}
|
||||
|
||||
inline void
|
||||
config()
|
||||
{}
|
||||
|
||||
inline void
|
||||
sample()
|
||||
{}
|
||||
|
||||
inline void
|
||||
shutdown()
|
||||
{}
|
||||
|
||||
inline void
|
||||
post_process()
|
||||
{}
|
||||
|
||||
inline void set_state(State) {}
|
||||
#endif
|
||||
} // namespace rocm_smi
|
||||
} // namespace rocprofsys
|
||||
|
||||
#if defined(ROCPROFSYS_USE_ROCM_SMI) && ROCPROFSYS_USE_ROCM_SMI > 0
|
||||
# if !defined(ROCPROFSYS_EXTERN_COMPONENTS) || \
|
||||
(defined(ROCPROFSYS_EXTERN_COMPONENTS) && ROCPROFSYS_EXTERN_COMPONENTS > 0)
|
||||
|
||||
# include <timemory/components/base.hpp>
|
||||
# include <timemory/components/data_tracker/components.hpp>
|
||||
# include <timemory/operations.hpp>
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_gpu_busy>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_gpu_temp>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_gpu_power>), true,
|
||||
double)
|
||||
|
||||
ROCPROFSYS_DECLARE_EXTERN_COMPONENT(
|
||||
TIMEMORY_ESC(data_tracker<double, rocprofsys::component::backtrace_gpu_memory>), true,
|
||||
double)
|
||||
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,834 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/rocprofiler.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/gpu.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "library/rocm.hpp"
|
||||
#include "library/rocm/hsa_rsrc_factory.hpp"
|
||||
|
||||
#include <timemory/backends/hardware_counters.hpp>
|
||||
#include <timemory/manager.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/storage/types.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <rocprofiler.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <dlfcn.h>
|
||||
#include <hsa.h>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using ::rocprofiler::util::AgentInfo;
|
||||
using ::rocprofiler::util::HsaRsrcFactory;
|
||||
|
||||
auto&
|
||||
get_event_names()
|
||||
{
|
||||
static auto _v = std::map<uint32_t, std::vector<rocprofiler_feature_t>>{};
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Error handler
|
||||
void
|
||||
fatal(const std::string& msg)
|
||||
{
|
||||
ROCPROFSYS_PRINT_F("\n");
|
||||
ROCPROFSYS_PRINT_F("%s\n", msg.c_str());
|
||||
abort();
|
||||
}
|
||||
|
||||
// Check returned HSA API status
|
||||
const char*
|
||||
rocm_error_string(hsa_status_t _status)
|
||||
{
|
||||
const char* _err_string = nullptr;
|
||||
if(_status != HSA_STATUS_SUCCESS) rocprofiler_error_string(&_err_string);
|
||||
return _err_string;
|
||||
}
|
||||
|
||||
// Check returned HSA API status
|
||||
bool
|
||||
rocm_check_status(hsa_status_t _status, const std::set<hsa_status_t>& _nonfatal = {})
|
||||
{
|
||||
if(_status != HSA_STATUS_SUCCESS)
|
||||
{
|
||||
if(_nonfatal.count(_status) == 0)
|
||||
fatal(JOIN(" :: ", "ERROR", rocm_error_string(_status)));
|
||||
|
||||
ROCPROFSYS_PRINT_F("Warning! %s\n", rocm_error_string(_status));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Context stored entry type
|
||||
struct context_entry_t
|
||||
{
|
||||
bool valid;
|
||||
hsa_agent_t agent;
|
||||
rocprofiler_group_t group;
|
||||
rocprofiler_callback_data_t data;
|
||||
};
|
||||
|
||||
// Context callback arg
|
||||
struct callbacks_arg_t
|
||||
{
|
||||
rocprofiler_pool_t** pools;
|
||||
};
|
||||
|
||||
// Handler callback arg
|
||||
struct handler_arg_t
|
||||
{
|
||||
rocprofiler_feature_t* features;
|
||||
unsigned feature_count;
|
||||
};
|
||||
|
||||
bool&
|
||||
is_setup()
|
||||
{
|
||||
static bool _v = false;
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::vector<std::string_view>>
|
||||
get_data_labels()
|
||||
{
|
||||
auto _v = std::map<uint32_t, std::vector<std::string_view>>{};
|
||||
for(const auto& itr : get_event_names())
|
||||
{
|
||||
_v[itr.first] = {};
|
||||
for(auto vitr : itr.second)
|
||||
_v[itr.first].emplace_back(std::string_view{ vitr.name });
|
||||
}
|
||||
return _v;
|
||||
}
|
||||
|
||||
// Dump stored context entry
|
||||
void
|
||||
rocm_dump_context_entry(context_entry_t* entry, rocprofiler_feature_t* features,
|
||||
unsigned feature_count)
|
||||
{
|
||||
volatile std::atomic<bool>* valid =
|
||||
reinterpret_cast<std::atomic<bool>*>(&entry->valid);
|
||||
while(valid->load() == false)
|
||||
sched_yield();
|
||||
|
||||
const rocprofiler_dispatch_record_t* record = entry->data.record;
|
||||
|
||||
if(!record) return; // there is nothing to do here.
|
||||
|
||||
auto _queue_id = entry->data.queue_id;
|
||||
auto _thread_id = entry->data.thread_id;
|
||||
auto _dev_id = HsaRsrcFactory::Instance().GetAgentInfo(entry->agent)->dev_index;
|
||||
auto _kernel_name = std::string{ entry->data.kernel_name };
|
||||
auto _pos = _kernel_name.find_last_of(')');
|
||||
if(_pos != std::string::npos) _kernel_name = _kernel_name.substr(0, _pos + 1);
|
||||
|
||||
rocprofiler_group_t& group = entry->group;
|
||||
if(group.context == nullptr)
|
||||
{
|
||||
fatal("context is nullptr\n");
|
||||
}
|
||||
|
||||
if(feature_count > 0)
|
||||
{
|
||||
rocm_check_status(rocprofiler_group_get_data(&group));
|
||||
rocm_check_status(rocprofiler_get_metrics(group.context));
|
||||
}
|
||||
|
||||
auto _evt =
|
||||
component::rocm_event{ _dev_id, _thread_id, _queue_id, _kernel_name,
|
||||
record->begin, record->end, feature_count, features };
|
||||
|
||||
component::rocm_data()->emplace_back(_evt);
|
||||
}
|
||||
|
||||
// Profiling completion handler
|
||||
// Dump and delete the context entry
|
||||
// Return true if the context was dumped successfully
|
||||
bool
|
||||
rocm_context_handler(const rocprofiler_pool_entry_t* entry, void* arg)
|
||||
{
|
||||
// Context entry
|
||||
context_entry_t* ctx_entry = reinterpret_cast<context_entry_t*>(entry->payload);
|
||||
handler_arg_t* handler_arg = reinterpret_cast<handler_arg_t*>(arg);
|
||||
|
||||
// rocm::lock_t _lk{ rocm::rocm_mutex, std::defer_lock };
|
||||
// if(!_lk.owns_lock()) _lk.lock();
|
||||
|
||||
rocm_dump_context_entry(ctx_entry, handler_arg->features, handler_arg->feature_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Kernel disoatch callback
|
||||
hsa_status_t
|
||||
rocm_dispatch_callback(const rocprofiler_callback_data_t* callback_data, void* arg,
|
||||
rocprofiler_group_t* group)
|
||||
{
|
||||
// Passed tool data
|
||||
hsa_agent_t agent = callback_data->agent;
|
||||
|
||||
// Open profiling context
|
||||
const unsigned gpu_id = HsaRsrcFactory::Instance().GetAgentInfo(agent)->dev_index;
|
||||
callbacks_arg_t* callbacks_arg = reinterpret_cast<callbacks_arg_t*>(arg);
|
||||
rocprofiler_pool_t* pool = callbacks_arg->pools[gpu_id];
|
||||
rocprofiler_pool_entry_t pool_entry{};
|
||||
rocm_check_status(rocprofiler_pool_fetch(pool, &pool_entry));
|
||||
// Profiling context entry
|
||||
rocprofiler_t* context = pool_entry.context;
|
||||
context_entry_t* entry = reinterpret_cast<context_entry_t*>(pool_entry.payload);
|
||||
|
||||
// Get group[0]
|
||||
rocm_check_status(rocprofiler_get_group(context, 0, group));
|
||||
|
||||
// Fill profiling context entry
|
||||
entry->agent = agent;
|
||||
entry->group = *group;
|
||||
entry->data = *callback_data;
|
||||
entry->data.kernel_name = strdup(callback_data->kernel_name);
|
||||
reinterpret_cast<std::atomic<bool>*>(&entry->valid)->store(true);
|
||||
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
unsigned
|
||||
metrics_input(unsigned _device, rocprofiler_feature_t** ret)
|
||||
{
|
||||
// Profiling feature objects
|
||||
auto _events = tim::delimit(config::get_rocm_events(), ", ;\t\n");
|
||||
std::vector<std::string> _features = {};
|
||||
auto _this_device = JOIN("", ":device=", _device);
|
||||
for(auto itr : _events)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(3, "Processing feature '%s' for device %u...\n", itr.c_str(),
|
||||
_device);
|
||||
auto _pos = itr.find(":device=");
|
||||
if(_pos != std::string::npos)
|
||||
{
|
||||
if(itr.find(_this_device) != std::string::npos)
|
||||
{
|
||||
_features.emplace_back(itr.substr(0, _pos));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_features.emplace_back(itr);
|
||||
}
|
||||
}
|
||||
const unsigned feature_count = _features.size();
|
||||
rocprofiler_feature_t* features = new rocprofiler_feature_t[feature_count];
|
||||
memset(features, 0, feature_count * sizeof(rocprofiler_feature_t));
|
||||
|
||||
// PMC events
|
||||
for(unsigned i = 0; i < feature_count; ++i)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(3, "Adding feature '%s' for device %u...\n",
|
||||
_features.at(i).c_str(), _device);
|
||||
features[i].kind = ROCPROFILER_FEATURE_KIND_METRIC;
|
||||
features[i].name = strdup(_features.at(i).c_str());
|
||||
features[i].parameters = nullptr;
|
||||
features[i].parameter_count = 0;
|
||||
}
|
||||
|
||||
*ret = features;
|
||||
return feature_count;
|
||||
}
|
||||
|
||||
using info_data = std::vector<component::rocm_info_entry>;
|
||||
|
||||
hsa_status_t
|
||||
info_data_callback(const rocprofiler_info_data_t info, void* arg)
|
||||
{
|
||||
using qualifier_t = tim::hardware_counters::qualifier;
|
||||
using qualifier_vec_t = std::vector<qualifier_t>;
|
||||
auto* _data = static_cast<info_data*>(arg);
|
||||
auto _dev_index = info.agent_index;
|
||||
|
||||
switch(info.kind)
|
||||
{
|
||||
case ROCPROFILER_INFO_KIND_METRIC:
|
||||
{
|
||||
auto _device_qualifier_sym = JOIN("", ":device=", _dev_index);
|
||||
auto _device_qualifier =
|
||||
tim::hardware_counters::qualifier{ true, static_cast<int>(_dev_index),
|
||||
_device_qualifier_sym,
|
||||
JOIN(" ", "Device", _dev_index) };
|
||||
auto _long_desc = std::string{ info.metric.description };
|
||||
auto _units = std::string{};
|
||||
auto _pysym = std::string{};
|
||||
if(info.metric.expr != nullptr)
|
||||
{
|
||||
auto _sym = JOIN("", info.metric.name, _device_qualifier_sym);
|
||||
auto _short_desc = JOIN("", "Derived counter: ", info.metric.expr);
|
||||
_data->emplace_back(component::rocm_info_entry(
|
||||
true, tim::hardware_counters::api::rocm, _data->size(), 0, _sym,
|
||||
_pysym, _short_desc, _long_desc, _units,
|
||||
qualifier_vec_t{ _device_qualifier }));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(info.metric.instances == 1)
|
||||
{
|
||||
auto _sym = JOIN("", info.metric.name, _device_qualifier_sym);
|
||||
auto _short_desc =
|
||||
JOIN("", info.metric.name, " on device ", _dev_index);
|
||||
_data->emplace_back(component::rocm_info_entry(
|
||||
true, tim::hardware_counters::api::rocm, _data->size(), 0, _sym,
|
||||
_pysym, _short_desc, _long_desc, _units,
|
||||
qualifier_vec_t{ _device_qualifier }));
|
||||
}
|
||||
else
|
||||
{
|
||||
for(uint32_t i = 0; i < info.metric.instances; ++i)
|
||||
{
|
||||
auto _instance_qualifier_sym = JOIN("", '[', i, ']');
|
||||
auto _instance_qualifier =
|
||||
tim::hardware_counters::qualifier{ true, static_cast<int>(i),
|
||||
_instance_qualifier_sym,
|
||||
JOIN(" ", "Instance", i) };
|
||||
auto _sym = JOIN("", info.metric.name, _instance_qualifier_sym,
|
||||
_device_qualifier_sym);
|
||||
auto _short_desc = JOIN("", info.metric.name, " instance ", i,
|
||||
" on device ", _dev_index);
|
||||
_data->emplace_back(component::rocm_info_entry(
|
||||
true, tim::hardware_counters::api::rocm, _data->size(), 0,
|
||||
_sym, _pysym, _short_desc, _long_desc, _units,
|
||||
qualifier_vec_t{ _device_qualifier, _instance_qualifier }));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: printf("wrong info kind %u\n", info.kind); return HSA_STATUS_ERROR;
|
||||
}
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
std::vector<component::rocm_info_entry>
|
||||
rocm_metrics()
|
||||
{
|
||||
std::vector<component::rocm_info_entry> _data = {};
|
||||
try
|
||||
{
|
||||
(void) HsaRsrcFactory::Instance();
|
||||
} catch(std::runtime_error& _e)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(0, "%s\n", _e.what());
|
||||
return _data;
|
||||
}
|
||||
|
||||
// Available GPU agents
|
||||
const unsigned gpu_count = HsaRsrcFactory::Instance().GetCountOfGpuAgents();
|
||||
|
||||
std::vector<AgentInfo*> _gpu_agents(gpu_count, nullptr);
|
||||
for(unsigned i = 0; i < gpu_count; ++i)
|
||||
{
|
||||
const AgentInfo* _agent = _gpu_agents[i];
|
||||
const AgentInfo** _agent_p = &_agent;
|
||||
HsaRsrcFactory::Instance().GetGpuAgentInfo(i, _agent_p);
|
||||
|
||||
if(!rocm_check_status(rocprofiler_iterate_info(
|
||||
&_agent->dev_id, ROCPROFILER_INFO_KIND_METRIC,
|
||||
info_data_callback, reinterpret_cast<void*>(&_data)),
|
||||
{ HSA_STATUS_ERROR_NOT_INITIALIZED }))
|
||||
{
|
||||
ROCPROFSYS_WARNING_F(-1, "rocprofiler_iterate_info failed for gpu agent %u\n",
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
if(gpu_count > 0 && _data.empty())
|
||||
{
|
||||
if(!rocm_check_status(rocprofiler_iterate_info(
|
||||
nullptr, ROCPROFILER_INFO_KIND_METRIC,
|
||||
info_data_callback, reinterpret_cast<void*>(&_data)),
|
||||
{ HSA_STATUS_ERROR_NOT_INITIALIZED }))
|
||||
{
|
||||
ROCPROFSYS_WARNING_F(
|
||||
-1, "rocprofiler_iterate_info failed for %i gpu agents\n", gpu_count);
|
||||
}
|
||||
}
|
||||
|
||||
auto _settings = tim::settings::shared_instance();
|
||||
if(_settings)
|
||||
{
|
||||
auto ritr = _settings->find("ROCPROFSYS_ROCM_EVENTS");
|
||||
if(ritr != _settings->end())
|
||||
{
|
||||
auto _rocm_events = ritr->second;
|
||||
if(_rocm_events->get_choices().empty())
|
||||
{
|
||||
std::vector<std::string> _choices = {};
|
||||
_choices.reserve(_data.size());
|
||||
for(auto itr : _data)
|
||||
{
|
||||
if(!itr.symbol().empty()) _choices.emplace_back(itr.symbol());
|
||||
}
|
||||
_rocm_events->set_choices(_choices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _data;
|
||||
}
|
||||
|
||||
void
|
||||
rocm_initialize()
|
||||
{
|
||||
// Available GPU agents
|
||||
const unsigned gpu_count = HsaRsrcFactory::Instance().GetCountOfGpuAgents();
|
||||
|
||||
(void) rocm_metrics();
|
||||
|
||||
// Adding dispatch observer
|
||||
callbacks_arg_t* callbacks_arg = new callbacks_arg_t{};
|
||||
callbacks_arg->pools = new rocprofiler_pool_t*[gpu_count];
|
||||
for(unsigned gpu_id = 0; gpu_id < gpu_count; gpu_id++)
|
||||
{
|
||||
// Getting profiling features
|
||||
rocprofiler_feature_t* features = nullptr;
|
||||
unsigned feature_count = metrics_input(gpu_id, &features);
|
||||
|
||||
if(features)
|
||||
{
|
||||
get_event_names()[gpu_id].clear();
|
||||
get_event_names()[gpu_id].reserve(feature_count);
|
||||
for(unsigned i = 0; i < feature_count; ++i)
|
||||
get_event_names().at(gpu_id).emplace_back(features[i]);
|
||||
}
|
||||
|
||||
// Handler arg
|
||||
handler_arg_t* handler_arg = new handler_arg_t{};
|
||||
handler_arg->features = features;
|
||||
handler_arg->feature_count = feature_count;
|
||||
|
||||
// Context properties
|
||||
rocprofiler_pool_properties_t properties{};
|
||||
properties.num_entries = 100;
|
||||
properties.payload_bytes = sizeof(context_entry_t);
|
||||
properties.handler = rocm_context_handler;
|
||||
properties.handler_arg = handler_arg;
|
||||
|
||||
// Getting GPU device info
|
||||
const AgentInfo* agent_info = nullptr;
|
||||
if(HsaRsrcFactory::Instance().GetGpuAgentInfo(gpu_id, &agent_info) == false)
|
||||
{
|
||||
fprintf(stderr, "GetGpuAgentInfo failed\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Open profiling pool
|
||||
rocprofiler_pool_t* pool = nullptr;
|
||||
uint32_t mode = 0; // ROCPROFILER_MODE_SINGLEGROUP
|
||||
rocm_check_status(rocprofiler_pool_open(agent_info->dev_id, features,
|
||||
feature_count, &pool, mode, &properties));
|
||||
callbacks_arg->pools[gpu_id] = pool;
|
||||
}
|
||||
|
||||
rocprofiler_queue_callbacks_t callbacks_ptrs{};
|
||||
callbacks_ptrs.dispatch = rocm_dispatch_callback;
|
||||
int err = rocprofiler_set_queue_callbacks(callbacks_ptrs, callbacks_arg);
|
||||
ROCPROFSYS_VERBOSE_F(3, "err=%d, rocprofiler_set_queue_callbacks\n", err);
|
||||
|
||||
is_setup() = true;
|
||||
}
|
||||
|
||||
void
|
||||
rocm_cleanup()
|
||||
{
|
||||
// Unregister dispatch callback
|
||||
rocm_check_status(rocprofiler_remove_queue_callbacks());
|
||||
// close profiling pool
|
||||
// rocm_check_status(rocprofiler_pool_flush(pool));
|
||||
// rocm_check_status(rocprofiler_pool_close(pool));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using rocm_event = component::rocm_event;
|
||||
using rocm_data_t = component::rocm_data_t;
|
||||
using rocm_metric_type = component::rocm_metric_type;
|
||||
using rocm_feature_value = component::rocm_feature_value;
|
||||
using rocm_data_tracker = component::rocm_data_tracker;
|
||||
|
||||
void
|
||||
post_process_perfetto()
|
||||
{
|
||||
using counter_track = perfetto_counter_track<rocm_event>;
|
||||
|
||||
static bool _once = false;
|
||||
if(_once) return;
|
||||
|
||||
auto _data = rocm_data_t{};
|
||||
auto _device_data = std::map<uint32_t, std::vector<rocm_event*>>{};
|
||||
auto _device_fields = std::map<uint32_t, std::vector<std::string_view>>{};
|
||||
auto _device_range = std::map<uint32_t, std::set<rocm_metric_type>>{};
|
||||
|
||||
for(size_t i = 0; i < ROCPROFSYS_MAX_THREADS; ++i)
|
||||
{
|
||||
auto& _v = component::rocm_data(i);
|
||||
if(_v)
|
||||
{
|
||||
_data.reserve(_data.size() + _v->size());
|
||||
for(auto& itr : *_v)
|
||||
_data.emplace_back(itr);
|
||||
}
|
||||
}
|
||||
|
||||
if(_data.empty()) return;
|
||||
_once = true;
|
||||
|
||||
std::sort(_data.begin(), _data.end());
|
||||
|
||||
auto _get_events = [](std::vector<rocm_event*>& _inp, rocm_metric_type _ts) {
|
||||
auto _v = std::vector<rocm_event*>{};
|
||||
for(const auto& itr : _inp)
|
||||
{
|
||||
if(_ts >= itr->entry && _ts <= itr->exit) _v.emplace_back(itr);
|
||||
if(_ts > itr->exit) break;
|
||||
}
|
||||
return _v;
|
||||
};
|
||||
|
||||
{
|
||||
auto _device_time = std::map<uint32_t, std::set<rocm_metric_type>>{};
|
||||
for(auto& itr : _data)
|
||||
{
|
||||
_device_data[itr.device_id].emplace_back(&itr);
|
||||
_device_time[itr.device_id].emplace(itr.entry);
|
||||
_device_time[itr.device_id].emplace(itr.exit);
|
||||
auto _dev_id = itr.device_id;
|
||||
if(get_use_perfetto() && !counter_track::exists(_dev_id))
|
||||
{
|
||||
auto addendum = [&](auto&& _v) {
|
||||
return JOIN(" ", "Device", _v, JOIN("", '[', _dev_id, ']'));
|
||||
};
|
||||
for(auto nitr : itr.feature_names)
|
||||
{
|
||||
auto _name = get_data_labels().at(itr.device_id).at(nitr);
|
||||
counter_track::emplace(_dev_id, addendum(_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& ditr : _device_time)
|
||||
{
|
||||
for(auto itr = ditr.second.begin(); itr != ditr.second.end(); ++itr)
|
||||
{
|
||||
auto _next = std::next(itr);
|
||||
if(_next == ditr.second.end()) continue;
|
||||
_device_range[ditr.first].emplace(((*_next / 2) + (*itr / 2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& ditr : _device_range)
|
||||
{
|
||||
auto _dev_id = ditr.first;
|
||||
auto _values = std::vector<rocm_feature_value>{};
|
||||
auto _ts_sorted_data = _device_data[_dev_id];
|
||||
std::sort(_ts_sorted_data.begin(), _ts_sorted_data.end(),
|
||||
[](auto* _l, auto* _r) { return _l->exit < _r->exit; });
|
||||
for(const auto& itr : ditr.second)
|
||||
{
|
||||
auto _v = _get_events(_ts_sorted_data, itr);
|
||||
uint64_t _ts = itr;
|
||||
for(auto* vitr : _v)
|
||||
{
|
||||
size_t _n = vitr->feature_values.size();
|
||||
if(_values.empty())
|
||||
{
|
||||
_values.reserve(_n);
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
_values.emplace_back(vitr->feature_values.at(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
auto _plus = [](auto& _lhs, auto&& _rhs) { _lhs += _rhs; };
|
||||
std::visit(_plus, _values.at(i), vitr->feature_values.at(i));
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _values.size(); ++i)
|
||||
{
|
||||
auto _trace_counter = [_dev_id, i, _ts](auto&& _val) {
|
||||
TRACE_COUNTER("kernel_hardware_counter",
|
||||
counter_track::at(_dev_id, i), _ts, _val);
|
||||
};
|
||||
std::visit(_trace_counter, _values.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
post_process_timemory()
|
||||
{
|
||||
static bool _once = false;
|
||||
if(_once) return;
|
||||
|
||||
auto _data = rocm_data_t{};
|
||||
auto _device_data = std::map<uint32_t, std::vector<rocm_event*>>{};
|
||||
auto _device_fields = std::map<uint32_t, std::vector<std::string_view>>{};
|
||||
auto _device_range = std::map<uint32_t, std::set<rocm_metric_type>>{};
|
||||
|
||||
for(size_t i = 0; i < ROCPROFSYS_MAX_THREADS; ++i)
|
||||
{
|
||||
auto& _v = component::rocm_data(i);
|
||||
if(_v)
|
||||
{
|
||||
_data.reserve(_data.size() + _v->size());
|
||||
for(auto& itr : *_v)
|
||||
_data.emplace_back(itr);
|
||||
}
|
||||
}
|
||||
|
||||
if(_data.empty()) return;
|
||||
_once = true;
|
||||
|
||||
std::sort(_data.begin(), _data.end());
|
||||
|
||||
for(auto& itr : _data)
|
||||
{
|
||||
_device_data[itr.device_id].emplace_back(&itr);
|
||||
}
|
||||
|
||||
for(auto& itr : _device_data)
|
||||
{
|
||||
// sort according to when it exited
|
||||
std::sort(itr.second.begin(), itr.second.end(),
|
||||
[](auto* _lhs, auto* _rhs) { return _lhs->exit < _rhs->exit; });
|
||||
}
|
||||
|
||||
using storage_type = typename rocm_data_tracker::storage_type;
|
||||
using bundle_type = tim::lightweight_tuple<rocm_data_tracker>;
|
||||
|
||||
auto _info = rocm_metrics();
|
||||
static auto _get_description = [&_info](std::string_view _v) {
|
||||
for(auto& itr : _info)
|
||||
{
|
||||
if(itr.symbol().find(_v) == 0 || itr.short_description().find(_v) == 0)
|
||||
{
|
||||
return itr.long_description();
|
||||
}
|
||||
}
|
||||
return std::string{};
|
||||
};
|
||||
|
||||
struct local_event
|
||||
{
|
||||
rocm_event* parent = nullptr;
|
||||
mutable std::vector<local_event> children = {};
|
||||
|
||||
ROCPROFSYS_DEFAULT_OBJECT(local_event)
|
||||
|
||||
explicit local_event(rocm_event* _v)
|
||||
: parent{ _v }
|
||||
{}
|
||||
|
||||
bool operator()(rocm_event* _v)
|
||||
{
|
||||
if(!parent) return false;
|
||||
if(_v->device_id != parent->device_id) return false;
|
||||
if(_v->entry > parent->entry && _v->exit <= parent->exit)
|
||||
{
|
||||
children.emplace_back(_v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator<(const local_event& _v) const
|
||||
{
|
||||
if(!parent && _v.parent) return true;
|
||||
if(parent && !_v.parent) return false;
|
||||
return *parent < *_v.parent;
|
||||
}
|
||||
|
||||
void operator()(int64_t _index, scope::config _scope) const
|
||||
{
|
||||
if(!parent) return;
|
||||
bundle_type _bundle{ parent->name, _scope };
|
||||
_bundle.push(parent->queue_id)
|
||||
.start()
|
||||
.store(parent->feature_values.at(_index));
|
||||
|
||||
std::sort(children.begin(), children.end());
|
||||
for(const auto& itr : children)
|
||||
itr(_index, _scope);
|
||||
|
||||
_bundle.stop().pop(parent->queue_id);
|
||||
}
|
||||
};
|
||||
|
||||
struct local_storage
|
||||
{
|
||||
int64_t index = 0;
|
||||
std::string metric_name = {};
|
||||
std::string metric_description = {};
|
||||
std::unique_ptr<storage_type> storage = {};
|
||||
|
||||
local_storage(uint32_t _devid, size_t _idx, std::string_view _name)
|
||||
: index{ static_cast<int64_t>(_idx) }
|
||||
, metric_name{ _name }
|
||||
, metric_description{ _get_description(metric_name) }
|
||||
{
|
||||
auto _metric_name = std::string{ _name };
|
||||
_metric_name = std::regex_replace(
|
||||
_metric_name, std::regex{ "(.*)\\[([0-9]+)\\]" }, "$1_$2");
|
||||
storage = std::make_unique<storage_type>(
|
||||
tim::standalone_storage{}, index,
|
||||
JOIN('-', "rocprof", "device", _devid, _metric_name));
|
||||
}
|
||||
|
||||
void operator()(const local_event& _event, scope::config _scope) const
|
||||
{
|
||||
operation::set_storage<rocm_data_tracker>{}(storage.get());
|
||||
_event(index, _scope);
|
||||
}
|
||||
|
||||
void write() const
|
||||
{
|
||||
rocm_data_tracker::label() = metric_name;
|
||||
rocm_data_tracker::description() = metric_description;
|
||||
storage->write();
|
||||
}
|
||||
};
|
||||
|
||||
auto _local_data = std::map<uint32_t, std::vector<local_event>>{};
|
||||
auto _scope = scope::get_default();
|
||||
|
||||
for(auto& ditr : _device_data)
|
||||
{
|
||||
ROCPROFSYS_VERBOSE_F(1, "Post-processing %zu entries for device %u...\n",
|
||||
ditr.second.size(), ditr.first);
|
||||
auto _storage = std::vector<local_storage>{};
|
||||
for(auto& itr : ditr.second)
|
||||
{
|
||||
auto _n = itr->feature_names.size();
|
||||
if(_n > _storage.size())
|
||||
{
|
||||
_storage.reserve(_n);
|
||||
for(size_t i = _storage.size(); i < _n; ++i)
|
||||
_storage.emplace_back(
|
||||
ditr.first, i,
|
||||
get_data_labels().at(ditr.first).at(itr->feature_names.at(i)));
|
||||
}
|
||||
}
|
||||
|
||||
auto& _local = _local_data[ditr.first];
|
||||
_local.reserve(ditr.second.size());
|
||||
double _avg = 0.0;
|
||||
for(auto& itr : ditr.second)
|
||||
{
|
||||
if(_local.empty() || itr->entry >= _local.back().parent->exit)
|
||||
{
|
||||
_local.emplace_back(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t _n = 0;
|
||||
bool _found = false;
|
||||
for(auto litr = _local.rbegin(); litr != _local.rend(); ++litr)
|
||||
{
|
||||
++_n;
|
||||
if((*litr)(itr))
|
||||
{
|
||||
_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!_found) _local.emplace_back(itr);
|
||||
_avg += _n;
|
||||
}
|
||||
}
|
||||
|
||||
ROCPROFSYS_VERBOSE_F(3, "Average # of iterations before match: %.1f\n",
|
||||
_avg / ditr.second.size() * 100.0);
|
||||
|
||||
for(auto& sitr : _storage)
|
||||
{
|
||||
for(auto& itr : _local)
|
||||
sitr(itr, _scope);
|
||||
}
|
||||
|
||||
for(auto& itr : _storage)
|
||||
itr.write();
|
||||
}
|
||||
|
||||
tim::trait::runtime_enabled<rocprofsys::rocprofiler::rocm_data_tracker>::set(false);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
post_process()
|
||||
{
|
||||
if(get_use_perfetto()) post_process_perfetto();
|
||||
|
||||
if(get_use_timemory())
|
||||
{
|
||||
auto _manager = tim::manager::master_instance();
|
||||
if(_manager)
|
||||
{
|
||||
_manager->add_cleanup("rocprofiler", &post_process_timemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
post_process_timemory();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace rocprofiler
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,88 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/components/rocprofiler.hpp"
|
||||
|
||||
#include <timemory/backends/hardware_counters.hpp>
|
||||
#include <timemory/macros.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/macros.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace rocprofiler
|
||||
{
|
||||
std::map<uint32_t, std::vector<std::string_view>>
|
||||
get_data_labels();
|
||||
|
||||
void
|
||||
rocm_initialize();
|
||||
|
||||
void
|
||||
rocm_cleanup();
|
||||
|
||||
bool&
|
||||
is_setup();
|
||||
|
||||
void
|
||||
post_process();
|
||||
|
||||
std::vector<component::rocm_info_entry>
|
||||
rocm_metrics();
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_ROCPROFILER) || ROCPROFSYS_USE_ROCPROFILER == 0
|
||||
inline void
|
||||
post_process()
|
||||
{}
|
||||
|
||||
inline void
|
||||
rocm_cleanup()
|
||||
{}
|
||||
|
||||
inline std::vector<component::rocm_info_entry>
|
||||
rocm_metrics()
|
||||
{
|
||||
return std::vector<component::rocm_info_entry>{};
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace rocprofiler
|
||||
} // namespace rocprofsys
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/hip_runtime.hpp"
|
||||
#include "core/perfetto.hpp"
|
||||
#include "library/components/roctracer.hpp"
|
||||
#include "library/ptl.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
// Macro to check ROC-tracer calls status
|
||||
#define ROCPROFSYS_ROCTRACER_CALL(call) \
|
||||
{ \
|
||||
ROCPROFSYS_DEBUG_F(#call); \
|
||||
int err = call; \
|
||||
if(err != 0) \
|
||||
{ \
|
||||
ROCPROFSYS_PRINT_F("%s in: %s\n", roctracer_error_string(), #call); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
using roctracer_hip_bundle_t =
|
||||
tim::component_bundle<category::rocm_hip, comp::roctracer_data, comp::wall_clock>;
|
||||
using roctracer_hsa_bundle_t =
|
||||
tim::component_bundle<category::rocm_hsa, comp::roctracer_data>;
|
||||
using roctracer_functions_t = std::vector<std::pair<std::string, std::function<void()>>>;
|
||||
|
||||
// HSA API callback function
|
||||
void
|
||||
hsa_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg);
|
||||
|
||||
void
|
||||
hsa_activity_callback(uint32_t op, const void* record, void* arg);
|
||||
|
||||
void
|
||||
hip_exec_activity_callbacks(int64_t _tid);
|
||||
|
||||
// HIP API callback function
|
||||
void
|
||||
hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg);
|
||||
|
||||
void
|
||||
roctx_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg);
|
||||
|
||||
// Activity tracing callback
|
||||
void
|
||||
hip_activity_callback(const char* begin, const char* end, void*);
|
||||
|
||||
bool&
|
||||
roctracer_is_init();
|
||||
|
||||
bool&
|
||||
roctracer_is_setup();
|
||||
|
||||
int64_t
|
||||
get_clock_skew();
|
||||
|
||||
roctracer_functions_t&
|
||||
roctracer_setup_routines();
|
||||
|
||||
roctracer_functions_t&
|
||||
roctracer_shutdown_routines();
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,290 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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/runtime.hpp"
|
||||
#include "api.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <timemory/backends/dmp.hpp>
|
||||
#include <timemory/backends/mpi.hpp>
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/components/rusage/backends.hpp>
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/process/process.hpp>
|
||||
#include <timemory/sampling/allocator.hpp>
|
||||
#include <timemory/settings.hpp>
|
||||
#include <timemory/settings/types.hpp>
|
||||
#include <timemory/utility/argparse.hpp>
|
||||
#include <timemory/utility/declaration.hpp>
|
||||
#include <timemory/utility/signals.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto root_process_id =
|
||||
get_env<pid_t>("ROCPROFSYS_ROOT_PROCESS", process::get_id(), false);
|
||||
|
||||
auto&
|
||||
get_sampling_on_child_threads_history(int64_t _idx = utility::get_thread_index())
|
||||
{
|
||||
static auto _v = utility::get_filled_array<ROCPROFSYS_MAX_THREADS>(
|
||||
[]() { return utility::get_reserved_vector<bool>(32); });
|
||||
|
||||
if(_idx >= ROCPROFSYS_MAX_THREADS)
|
||||
{
|
||||
static thread_local auto _tl_v = utility::get_reserved_vector<bool>(32);
|
||||
return _tl_v;
|
||||
}
|
||||
|
||||
return _v.at(_idx);
|
||||
}
|
||||
|
||||
bool&
|
||||
sampling_on_child_threads()
|
||||
{
|
||||
static const auto& _thr_info = thread_info::get();
|
||||
// if the thread is offset, disable by default
|
||||
// if the global state is not active or the thread state is not enabled, disable by
|
||||
// default if there is no history, disable by default (first thread) otherwise,
|
||||
// inherit the last state
|
||||
static thread_local bool _v =
|
||||
(_thr_info) ? !_thr_info->is_offset
|
||||
: (get_state() != State::Active || get_thread_state() != ThreadState::Enabled)
|
||||
? false
|
||||
: (get_sampling_on_child_threads_history().empty()
|
||||
? false
|
||||
: get_sampling_on_child_threads_history().back());
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::atomic<uint64_t>&
|
||||
get_cpu_cid()
|
||||
{
|
||||
static std::atomic<uint64_t> _v{ 0 };
|
||||
return _v;
|
||||
}
|
||||
|
||||
unique_ptr_t<std::vector<uint64_t>>&
|
||||
get_cpu_cid_stack(int64_t _tid, int64_t _parent)
|
||||
{
|
||||
struct rocprofsys_cpu_cid_stack
|
||||
{};
|
||||
using init_data_t = thread_data<bool, rocprofsys_cpu_cid_stack>;
|
||||
using thread_data_t = thread_data<std::vector<uint64_t>, rocprofsys_cpu_cid_stack>;
|
||||
|
||||
auto& _v_tid = thread_data_t::instance(construct_on_thread{ _tid });
|
||||
auto& _b_tid = init_data_t::instance(construct_on_thread{ _tid }, false);
|
||||
|
||||
if(_b_tid && !(*_b_tid))
|
||||
{
|
||||
*_b_tid = true;
|
||||
auto _parent_tid = _parent;
|
||||
auto& _p_tid = thread_data_t::instance(construct_on_thread{ _parent_tid });
|
||||
// if tid != parent and there is not a valid pointer for the provided parent
|
||||
// thread id set it to zero since that will always be valid
|
||||
if(_tid != _parent_tid && !_p_tid) _parent_tid = 0;
|
||||
// copy over the thread ids from the parent if tid != parent
|
||||
if(_tid != _parent_tid) *_v_tid = *_p_tid;
|
||||
}
|
||||
return _v_tid;
|
||||
}
|
||||
|
||||
unique_ptr_t<cpu_cid_parent_map_t>&
|
||||
get_cpu_cid_parents(int64_t _tid)
|
||||
{
|
||||
struct rocprofsys_cpu_cid_stack
|
||||
{};
|
||||
using thread_data_t = thread_data<cpu_cid_parent_map_t, rocprofsys_cpu_cid_stack>;
|
||||
return thread_data_t::instance(construct_on_thread{ _tid }, cpu_cid_parent_map_t{});
|
||||
}
|
||||
|
||||
std::tuple<uint64_t, uint64_t, uint32_t>
|
||||
create_cpu_cid_entry(int64_t _tid)
|
||||
{
|
||||
using tim::auto_lock_t;
|
||||
|
||||
ROCPROFSYS_SCOPED_THREAD_STATE(ThreadState::Internal);
|
||||
|
||||
// unique lock for _tid
|
||||
auto& _mtx = get_cpu_cid_stack_lock(_tid);
|
||||
auto_lock_t _lk{ _mtx, std::defer_lock };
|
||||
if(!_lk.owns_lock()) _lk.lock();
|
||||
|
||||
int64_t _p_idx = (get_cpu_cid_stack(_tid)->empty()) ? 0 : _tid;
|
||||
|
||||
auto& _p_mtx = get_cpu_cid_stack_lock(_p_idx);
|
||||
auto_lock_t _p_lk{ _p_mtx, std::defer_lock };
|
||||
if(!_p_lk.owns_lock()) _p_lk.lock();
|
||||
|
||||
auto&& _cid = get_cpu_cid()++;
|
||||
// auto&& _parent_cid = get_cpu_cid_stack(_p_idx)->back();
|
||||
uint64_t _parent_cid = 0;
|
||||
auto& cid_stack = get_cpu_cid_stack(_p_idx);
|
||||
|
||||
if(!cid_stack->empty())
|
||||
{
|
||||
_parent_cid = cid_stack->back();
|
||||
}
|
||||
|
||||
uint32_t&& _depth = get_cpu_cid_stack(_p_idx)->size() - ((_p_idx == _tid) ? 1 : 0);
|
||||
|
||||
get_cpu_cid_parents(_tid)->emplace(_cid, std::make_tuple(_parent_cid, _depth));
|
||||
return std::make_tuple(_cid, _parent_cid, _depth);
|
||||
}
|
||||
|
||||
cpu_cid_pair_t
|
||||
get_cpu_cid_entry(uint64_t _cid, int64_t _tid)
|
||||
{
|
||||
return get_cpu_cid_parents(_tid)->at(_cid);
|
||||
}
|
||||
|
||||
tim::mutex_t&
|
||||
get_cpu_cid_stack_lock(int64_t _tid)
|
||||
{
|
||||
struct cpu_cid_stack_s
|
||||
{};
|
||||
return tim::type_mutex<cpu_cid_stack_s, project::rocprofsys, max_supported_threads>(
|
||||
_tid);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void
|
||||
setup_gotchas()
|
||||
{
|
||||
static bool _initialized = false;
|
||||
if(_initialized) return;
|
||||
_initialized = true;
|
||||
|
||||
ROCPROFSYS_BASIC_DEBUG(
|
||||
"Configuring gotcha wrapper around fork, MPI_Init, and MPI_Init_thread\n");
|
||||
|
||||
component::mpi_gotcha::configure();
|
||||
component::exit_gotcha::configure();
|
||||
component::fork_gotcha::configure();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<main_bundle_t>&
|
||||
get_main_bundle()
|
||||
{
|
||||
static auto _v = []() {
|
||||
auto _self = RUSAGE_SELF;
|
||||
std::swap(_self, tim::get_rusage_type());
|
||||
auto _tmp = std::make_unique<main_bundle_t>(
|
||||
JOIN('/', "rocprofsys/process", process::get_id()),
|
||||
quirk::config<quirk::auto_start>{});
|
||||
std::swap(_self, tim::get_rusage_type());
|
||||
return _tmp;
|
||||
}();
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::unique_ptr<init_bundle_t>&
|
||||
get_init_bundle()
|
||||
{
|
||||
static auto _v = std::make_unique<init_bundle_t>(
|
||||
JOIN('/', "rocprofsys/process", process::get_id()));
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::unique_ptr<preinit_bundle_t>&
|
||||
get_preinit_bundle()
|
||||
{
|
||||
static auto _v =
|
||||
(setup_gotchas(), std::make_unique<preinit_bundle_t>(
|
||||
JOIN('/', "rocprofsys/process", process::get_id()),
|
||||
quirk::config<quirk::auto_start>{}));
|
||||
return _v;
|
||||
}
|
||||
|
||||
bool
|
||||
sampling_enabled_on_child_threads()
|
||||
{
|
||||
return sampling_on_child_threads();
|
||||
}
|
||||
|
||||
bool
|
||||
push_enable_sampling_on_child_threads(bool _v)
|
||||
{
|
||||
bool _last = sampling_on_child_threads();
|
||||
sampling_on_child_threads() = _v;
|
||||
auto& _hist = get_sampling_on_child_threads_history();
|
||||
_hist.emplace_back(_last);
|
||||
return _last;
|
||||
}
|
||||
|
||||
bool
|
||||
pop_enable_sampling_on_child_threads()
|
||||
{
|
||||
auto& _hist = get_sampling_on_child_threads_history();
|
||||
if(!_hist.empty())
|
||||
{
|
||||
bool _restored = _hist.back();
|
||||
_hist.pop_back();
|
||||
sampling_on_child_threads() = _restored;
|
||||
}
|
||||
return sampling_on_child_threads();
|
||||
}
|
||||
|
||||
void
|
||||
set_sampling_on_all_future_threads(bool _v)
|
||||
{
|
||||
for(size_t i = 0; i < max_supported_threads; ++i)
|
||||
get_sampling_on_child_threads_history(i).emplace_back(_v);
|
||||
}
|
||||
|
||||
pid_t
|
||||
get_root_process_id()
|
||||
{
|
||||
return root_process_id;
|
||||
}
|
||||
|
||||
bool
|
||||
is_root_process()
|
||||
{
|
||||
return (root_process_id == process::get_id());
|
||||
}
|
||||
|
||||
bool
|
||||
is_child_process()
|
||||
{
|
||||
return (root_process_id != process::get_id());
|
||||
}
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,140 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "api.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/causal/components/causal_gotcha.hpp"
|
||||
#include "library/components/exit_gotcha.hpp"
|
||||
#include "library/components/fork_gotcha.hpp"
|
||||
#include "library/components/mpi_gotcha.hpp"
|
||||
#include "library/components/numa_gotcha.hpp"
|
||||
#include "library/components/pthread_gotcha.hpp"
|
||||
#include "library/components/roctracer.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/macros/language.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
// started during preinit phase
|
||||
using preinit_bundle_t =
|
||||
tim::lightweight_tuple<exit_gotcha_t, fork_gotcha_t, mpi_gotcha_t>;
|
||||
|
||||
// started during init phase
|
||||
using init_bundle_t = tim::lightweight_tuple<causal::component::causal_gotcha,
|
||||
pthread_gotcha, component::numa_gotcha>;
|
||||
|
||||
// bundle of components around rocprofsys_init and rocprofsys_finalize
|
||||
using main_bundle_t =
|
||||
tim::lightweight_tuple<comp::wall_clock, comp::peak_rss, comp::page_rss,
|
||||
comp::cpu_clock, comp::cpu_util>;
|
||||
|
||||
// bundle of components around each thread
|
||||
#if defined(TIMEMORY_RUSAGE_THREAD) && TIMEMORY_RUSAGE_THREAD > 0
|
||||
using thread_bundle_t = tim::lightweight_tuple<comp::wall_clock, comp::thread_cpu_clock,
|
||||
comp::thread_cpu_util, comp::peak_rss>;
|
||||
#else
|
||||
using thread_bundle_t = tim::lightweight_tuple<comp::wall_clock, comp::thread_cpu_clock,
|
||||
comp::thread_cpu_util>;
|
||||
#endif
|
||||
|
||||
std::unique_ptr<main_bundle_t>&
|
||||
get_main_bundle();
|
||||
|
||||
std::unique_ptr<init_bundle_t>&
|
||||
get_init_bundle();
|
||||
|
||||
std::unique_ptr<preinit_bundle_t>&
|
||||
get_preinit_bundle();
|
||||
|
||||
std::atomic<uint64_t>&
|
||||
get_cpu_cid() TIMEMORY_HOT;
|
||||
|
||||
unique_ptr_t<std::vector<uint64_t>>&
|
||||
get_cpu_cid_stack(int64_t _tid = threading::get_id(), int64_t _parent = 0) TIMEMORY_HOT;
|
||||
|
||||
using cpu_cid_data_t = std::tuple<uint64_t, uint64_t, uint32_t>;
|
||||
using cpu_cid_pair_t = std::tuple<uint64_t, uint32_t>;
|
||||
using cpu_cid_parent_map_t = std::unordered_map<uint64_t, cpu_cid_pair_t>;
|
||||
|
||||
unique_ptr_t<cpu_cid_parent_map_t>&
|
||||
get_cpu_cid_parents(int64_t _tid = threading::get_id()) TIMEMORY_HOT;
|
||||
|
||||
cpu_cid_data_t
|
||||
create_cpu_cid_entry(int64_t _tid = threading::get_id()) TIMEMORY_HOT;
|
||||
|
||||
cpu_cid_pair_t
|
||||
get_cpu_cid_entry(uint64_t _cid, int64_t _tid = threading::get_id()) TIMEMORY_HOT;
|
||||
|
||||
tim::mutex_t&
|
||||
get_cpu_cid_stack_lock(int64_t _tid = threading::get_id()) TIMEMORY_HOT;
|
||||
|
||||
// query current value
|
||||
bool
|
||||
sampling_enabled_on_child_threads();
|
||||
|
||||
// use this to disable sampling in a region (e.g. right before thread creation)
|
||||
bool
|
||||
push_enable_sampling_on_child_threads(bool _v);
|
||||
|
||||
// use this to restore previous setting
|
||||
bool
|
||||
pop_enable_sampling_on_child_threads();
|
||||
|
||||
// make sure every newly created thead starts with this value
|
||||
void
|
||||
set_sampling_on_all_future_threads(bool _v);
|
||||
|
||||
struct scoped_child_sampling
|
||||
{
|
||||
scoped_child_sampling(bool _v) { push_enable_sampling_on_child_threads(_v); }
|
||||
~scoped_child_sampling() { pop_enable_sampling_on_child_threads(); }
|
||||
};
|
||||
|
||||
pid_t
|
||||
get_root_process_id();
|
||||
|
||||
bool
|
||||
is_root_process();
|
||||
|
||||
bool
|
||||
is_child_process();
|
||||
} // namespace rocprofsys
|
||||
|
||||
#define ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(VALUE) \
|
||||
::rocprofsys::scoped_child_sampling ROCPROFSYS_VARIABLE(_scoped_child_sampling_, \
|
||||
__LINE__) \
|
||||
{ \
|
||||
VALUE \
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/components/fwd.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "library/components/backtrace.hpp"
|
||||
#include "library/components/backtrace_metrics.hpp"
|
||||
#include "library/components/backtrace_timestamp.hpp"
|
||||
#include "library/components/callchain.hpp"
|
||||
#include "library/thread_data.hpp"
|
||||
|
||||
#include <timemory/macros/language.hpp>
|
||||
#include <timemory/variadic/types.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
namespace sampling
|
||||
{
|
||||
unique_ptr_t<std::set<int>>&
|
||||
get_signal_types(int64_t _tid);
|
||||
|
||||
std::set<int>
|
||||
setup();
|
||||
|
||||
std::set<int>
|
||||
shutdown();
|
||||
|
||||
void
|
||||
block_samples();
|
||||
|
||||
void
|
||||
unblock_samples();
|
||||
|
||||
void block_signals(std::set<int> = {});
|
||||
|
||||
void unblock_signals(std::set<int> = {});
|
||||
|
||||
void
|
||||
post_process();
|
||||
} // namespace sampling
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,687 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/common.hpp"
|
||||
#include "core/concepts.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/containers/stable_vector.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/defines.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/thread_deleter.hpp"
|
||||
|
||||
#include <timemory/utility/macros.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
// bundle of components used in instrumentation
|
||||
using instrumentation_bundle_t =
|
||||
tim::component_bundle<project::rocprofsys, comp::wall_clock*,
|
||||
comp::user_global_bundle*>;
|
||||
|
||||
// allocator for instrumentation_bundle_t
|
||||
using bundle_allocator_t = tim::data::ring_buffer_allocator<instrumentation_bundle_t>;
|
||||
|
||||
using grow_functor_t = int64_t (*)(int64_t);
|
||||
|
||||
inline auto&
|
||||
grow_functors()
|
||||
{
|
||||
static auto _v = container::stable_vector<grow_functor_t>{};
|
||||
return _v;
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
struct base_thread_data
|
||||
{
|
||||
base_thread_data()
|
||||
{
|
||||
auto _func = [](int64_t _sz) -> int64_t {
|
||||
decltype(auto) _v = Tp::private_instance();
|
||||
if(_v && _v->capacity() < static_cast<size_t>(_sz + 1))
|
||||
{
|
||||
_v->reserve(_v->capacity() + 1);
|
||||
_v->resize(_v->capacity());
|
||||
}
|
||||
return (_v) ? _v->capacity() : 0;
|
||||
};
|
||||
grow_functors().emplace_back(std::move(_func));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Tp, typename Tag = void, size_t MaxThreads = max_supported_threads>
|
||||
struct thread_data;
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
struct use_placement_new_when_generating_unique_ptr<thread_data<Tp, Tag, MaxThreads>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
struct use_placement_new_when_generating_unique_ptr<
|
||||
thread_data<std::optional<Tp>, Tag, MaxThreads>> : std::true_type
|
||||
{};
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
struct use_placement_new_when_generating_unique_ptr<
|
||||
thread_data<identity<Tp>, Tag, MaxThreads>> : std::true_type
|
||||
{};
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
struct thread_data : base_thread_data<thread_data<Tp, Tag, MaxThreads>>
|
||||
{
|
||||
using this_type = thread_data<Tp, Tag, MaxThreads>;
|
||||
using value_type = unique_ptr_t<Tp>;
|
||||
using array_type =
|
||||
container::stable_vector<value_type, MaxThreads, container::cacheline_align_v>;
|
||||
using functor_type = std::function<value_type()>;
|
||||
|
||||
template <typename... Args>
|
||||
static void construct(construct_on_thread&&, Args&&...);
|
||||
static value_type& instance();
|
||||
template <typename... Args>
|
||||
static value_type& instance(construct_on_thread&&, Args&&...);
|
||||
|
||||
template <typename... Args>
|
||||
static void construct(Args&&... args)
|
||||
{
|
||||
construct(construct_on_thread{}, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static value_type& instance(Args&&... args)
|
||||
{
|
||||
return instance(construct_on_thread{}, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static size_t size() { return private_instance()->m_data.size(); }
|
||||
|
||||
decltype(auto) data() { return m_data; }
|
||||
decltype(auto) data() const { return m_data; }
|
||||
|
||||
decltype(auto) begin() { return m_data.begin(); }
|
||||
decltype(auto) end() { return m_data.end(); }
|
||||
|
||||
decltype(auto) begin() const { return m_data.begin(); }
|
||||
decltype(auto) end() const { return m_data.end(); }
|
||||
|
||||
decltype(auto) at(size_t _idx) { return m_data.at(_idx); }
|
||||
decltype(auto) at(size_t _idx) const { return m_data.at(_idx); }
|
||||
|
||||
decltype(auto) operator[](size_t _idx) { return m_data[_idx]; }
|
||||
decltype(auto) operator[](size_t _idx) const { return m_data[_idx]; }
|
||||
|
||||
decltype(auto) reserve(size_t _n) { return m_data.reserve(_n); }
|
||||
decltype(auto) capacity() const { return m_data.capacity(); }
|
||||
decltype(auto) empty() const { return m_data.empty(); }
|
||||
|
||||
void resize(size_t _n) { container::resize(m_data, _n, m_init()); }
|
||||
|
||||
template <typename Up>
|
||||
void resize(size_t _n, Up&& _v)
|
||||
{
|
||||
static_assert(std::is_assignable<value_type, Up>::value,
|
||||
"value is not assignable to optional<Tp>");
|
||||
container::resize(m_data, _n, std::forward<Up>(_v));
|
||||
}
|
||||
|
||||
static array_type* get()
|
||||
{
|
||||
return (private_instance()) ? &private_instance()->m_data : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct base_thread_data<this_type>;
|
||||
static unique_ptr_t<this_type>& private_instance();
|
||||
static array_type& instances();
|
||||
|
||||
template <typename... Args>
|
||||
static array_type& instances(construct_on_init, Args&&...);
|
||||
|
||||
array_type m_data = array_type(MaxThreads);
|
||||
functor_type m_init = []() { return value_type{}; };
|
||||
};
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
unique_ptr_t<thread_data<Tp, Tag, MaxThreads>>&
|
||||
thread_data<Tp, Tag, MaxThreads>::private_instance()
|
||||
{
|
||||
static auto _v = unique_ptr_t<this_type>{ new this_type{} };
|
||||
return _v;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
void
|
||||
thread_data<Tp, Tag, MaxThreads>::construct(construct_on_thread&& _t, Args&&... _args)
|
||||
{
|
||||
// construct outside of lambda to prevent data-race
|
||||
static auto& _instances = instances();
|
||||
if(!_instances.at(_t.index))
|
||||
_instances.at(_t.index) =
|
||||
utility::generate<value_type>{}(std::forward<Args>(_args)...);
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
unique_ptr_t<Tp>&
|
||||
thread_data<Tp, Tag, MaxThreads>::instance()
|
||||
{
|
||||
return instances().at(threading::get_id());
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
typename thread_data<Tp, Tag, MaxThreads>::array_type&
|
||||
thread_data<Tp, Tag, MaxThreads>::instances()
|
||||
{
|
||||
return private_instance()->m_data;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
unique_ptr_t<Tp>&
|
||||
thread_data<Tp, Tag, MaxThreads>::instance(construct_on_thread&& _t, Args&&... _args)
|
||||
{
|
||||
construct(construct_on_thread{ _t }, std::forward<Args>(_args)...);
|
||||
return instances().at(_t.index);
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
typename thread_data<Tp, Tag, MaxThreads>::array_type&
|
||||
thread_data<Tp, Tag, MaxThreads>::instances(construct_on_init, Args&&... _args)
|
||||
{
|
||||
static auto& _v = [&]() -> array_type& {
|
||||
auto& _internal = instances();
|
||||
for(size_t i = 0; i < MaxThreads; ++i)
|
||||
_internal.at(i) =
|
||||
utility::generate<value_type>{}(std::forward<Args>(_args)...);
|
||||
private_instance()->m_init = [_args...]() {
|
||||
return utility::generate<value_type>{}(_args...);
|
||||
};
|
||||
return _internal;
|
||||
}();
|
||||
return _v;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//
|
||||
// thread_data with std::optional
|
||||
//
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
struct thread_data<std::optional<Tp>, Tag, MaxThreads>
|
||||
: base_thread_data<thread_data<std::optional<Tp>, Tag, MaxThreads>>
|
||||
{
|
||||
using this_type = thread_data<std::optional<Tp>, Tag, MaxThreads>;
|
||||
using value_type = std::optional<Tp>;
|
||||
using functor_type = std::function<value_type()>;
|
||||
using array_type =
|
||||
container::stable_vector<value_type, MaxThreads, container::cacheline_align_v>;
|
||||
|
||||
thread_data() = default;
|
||||
~thread_data() = default;
|
||||
|
||||
explicit thread_data(functor_type&& _init)
|
||||
: m_init{ std::move(_init) }
|
||||
{}
|
||||
|
||||
thread_data(const thread_data&) = default;
|
||||
thread_data(thread_data&&) noexcept = default;
|
||||
|
||||
thread_data& operator=(const thread_data&) = default;
|
||||
thread_data& operator=(thread_data&&) noexcept = default;
|
||||
|
||||
static unique_ptr_t<this_type>& instance();
|
||||
|
||||
template <typename... Args>
|
||||
static unique_ptr_t<this_type>& instance(construct_on_init, Args&&...);
|
||||
|
||||
template <typename... Args>
|
||||
static value_type& instance(construct_on_thread&&, Args&&...);
|
||||
|
||||
template <typename... Args>
|
||||
static unique_ptr_t<this_type>& construct(construct_on_init, Args&&...);
|
||||
|
||||
template <typename... Args>
|
||||
static value_type& construct(construct_on_thread&&, Args&&...);
|
||||
|
||||
size_t size() { return m_data.size(); }
|
||||
|
||||
decltype(auto) data() { return m_data; }
|
||||
decltype(auto) data() const { return m_data; }
|
||||
|
||||
decltype(auto) begin() { return m_data.begin(); }
|
||||
decltype(auto) end() { return m_data.end(); }
|
||||
|
||||
decltype(auto) begin() const { return m_data.begin(); }
|
||||
decltype(auto) end() const { return m_data.end(); }
|
||||
|
||||
decltype(auto) at(size_t _idx) { return m_data.at(_idx); }
|
||||
decltype(auto) at(size_t _idx) const { return m_data.at(_idx); }
|
||||
|
||||
decltype(auto) operator[](size_t _idx) { return m_data[_idx]; }
|
||||
decltype(auto) operator[](size_t _idx) const { return m_data[_idx]; }
|
||||
|
||||
decltype(auto) reserve(size_t _n) { return m_data.reserve(_n); }
|
||||
decltype(auto) capacity() const { return m_data.capacity(); }
|
||||
decltype(auto) empty() const { return m_data.empty(); }
|
||||
|
||||
void resize(size_t _n) { container::resize(m_data, _n, m_init()); }
|
||||
|
||||
template <typename Up>
|
||||
void resize(size_t _n, Up&& _v)
|
||||
{
|
||||
static_assert(std::is_assignable<value_type, Up>::value,
|
||||
"value is not assignable to optional<Tp>");
|
||||
container::resize(m_data, _n, std::forward<Up>(_v));
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct base_thread_data<this_type>;
|
||||
static decltype(auto) private_instance() { return instance(); }
|
||||
|
||||
array_type m_data = {};
|
||||
functor_type m_init = []() { return value_type{}; };
|
||||
};
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
unique_ptr_t<thread_data<std::optional<Tp>, Tag, MaxThreads>>&
|
||||
thread_data<std::optional<Tp>, Tag, MaxThreads>::instance()
|
||||
{
|
||||
static auto _v = unique_ptr_t<this_type>{};
|
||||
return _v;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
unique_ptr_t<thread_data<std::optional<Tp>, Tag, MaxThreads>>&
|
||||
thread_data<std::optional<Tp>, Tag, MaxThreads>::instance(construct_on_init,
|
||||
Args&&... _args)
|
||||
{
|
||||
static auto& _v = [&]() -> unique_ptr_t<this_type>& {
|
||||
auto& _ref = instance();
|
||||
if(!_ref)
|
||||
_ref = utility::generate<unique_ptr_t<this_type>>{}(
|
||||
std::forward<Args>(_args)...);
|
||||
if(_ref->size() < MaxThreads) _ref->resize(MaxThreads);
|
||||
return _ref;
|
||||
}();
|
||||
return _v;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
unique_ptr_t<thread_data<std::optional<Tp>, Tag, MaxThreads>>&
|
||||
thread_data<std::optional<Tp>, Tag, MaxThreads>::construct(construct_on_init,
|
||||
Args&&... _args)
|
||||
{
|
||||
// construct outside of lambda to prevent data-race
|
||||
static auto& _ref = instance(construct_on_init{});
|
||||
static auto _v = [&]() {
|
||||
if(_ref)
|
||||
{
|
||||
for(auto& itr : *_ref)
|
||||
itr = utility::generate<value_type>{}(std::forward<Args>(_args)...);
|
||||
}
|
||||
return (_ref != nullptr);
|
||||
}();
|
||||
return _ref;
|
||||
(void) _v;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
std::optional<Tp>&
|
||||
thread_data<std::optional<Tp>, Tag, MaxThreads>::construct(construct_on_thread&& _t,
|
||||
Args&&... _args)
|
||||
{
|
||||
// construct outside of lambda to prevent data-race
|
||||
static auto& _instance = instance(construct_on_init{});
|
||||
static auto _constructed =
|
||||
container::stable_vector<bool, MaxThreads, container::cacheline_align_v>{};
|
||||
static auto _grow = []() {
|
||||
container::resize(_constructed, MaxThreads, false);
|
||||
grow_functors().emplace_back([](int64_t _n) -> int64_t {
|
||||
if(static_cast<size_t>(_n) >= _constructed.size())
|
||||
{
|
||||
_constructed.reserve(_constructed.capacity() + 1);
|
||||
container::resize(_constructed, _constructed.capacity(), false);
|
||||
}
|
||||
return _constructed.size();
|
||||
});
|
||||
return true;
|
||||
}();
|
||||
|
||||
if(!_constructed.at(_t.index))
|
||||
_constructed.at(_t.index) =
|
||||
(_instance->at(_t.index) =
|
||||
utility::generate<value_type>{}(std::forward<Args>(_args)...),
|
||||
true);
|
||||
|
||||
return _instance->at(_t.index);
|
||||
|
||||
(void) _grow;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
std::optional<Tp>&
|
||||
thread_data<std::optional<Tp>, Tag, MaxThreads>::instance(construct_on_thread&& _t,
|
||||
Args&&... _args)
|
||||
{
|
||||
construct(construct_on_thread{ _t }, std::forward<Args>(_args)...);
|
||||
return instance()->at(_t.index);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//
|
||||
// thread_data with raw data (no pointer)
|
||||
//
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
struct thread_data<identity<Tp>, Tag, MaxThreads>
|
||||
: base_thread_data<thread_data<identity<Tp>, Tag, MaxThreads>>
|
||||
{
|
||||
using this_type = thread_data<identity<Tp>, Tag, MaxThreads>;
|
||||
using value_type = Tp;
|
||||
using array_type =
|
||||
container::stable_vector<value_type, MaxThreads, container::cacheline_align_v>;
|
||||
using functor_type = std::function<value_type()>;
|
||||
|
||||
thread_data() = default;
|
||||
~thread_data() = default;
|
||||
|
||||
explicit thread_data(functor_type&& _init)
|
||||
: m_init{ std::move(_init) }
|
||||
{}
|
||||
|
||||
thread_data(const thread_data&) = default;
|
||||
thread_data(thread_data&&) noexcept = default;
|
||||
|
||||
thread_data& operator=(const thread_data&) = default;
|
||||
thread_data& operator=(thread_data&&) noexcept = default;
|
||||
|
||||
static unique_ptr_t<this_type>& instance();
|
||||
|
||||
template <typename... Args>
|
||||
static unique_ptr_t<this_type>& instance(construct_on_init, Args&&...);
|
||||
|
||||
template <typename... Args>
|
||||
static value_type& instance(construct_on_thread&&, Args&&...);
|
||||
|
||||
template <typename... Args>
|
||||
static unique_ptr_t<this_type>& construct(construct_on_init, Args&&...);
|
||||
|
||||
template <typename... Args>
|
||||
static value_type& construct(construct_on_thread&&, Args&&...);
|
||||
|
||||
size_t size() { return m_data.size(); }
|
||||
|
||||
decltype(auto) data() { return m_data; }
|
||||
decltype(auto) data() const { return m_data; }
|
||||
|
||||
decltype(auto) begin() { return m_data.begin(); }
|
||||
decltype(auto) end() { return m_data.end(); }
|
||||
|
||||
decltype(auto) begin() const { return m_data.begin(); }
|
||||
decltype(auto) end() const { return m_data.end(); }
|
||||
|
||||
decltype(auto) at(size_t _idx) { return m_data.at(_idx); }
|
||||
decltype(auto) at(size_t _idx) const { return m_data.at(_idx); }
|
||||
|
||||
decltype(auto) operator[](size_t _idx) { return m_data[_idx]; }
|
||||
decltype(auto) operator[](size_t _idx) const { return m_data[_idx]; }
|
||||
|
||||
decltype(auto) reserve(size_t _n) { return m_data.reserve(_n); }
|
||||
decltype(auto) capacity() const { return m_data.capacity(); }
|
||||
decltype(auto) empty() const { return m_data.empty(); }
|
||||
|
||||
void resize(size_t _n) { container::resize(m_data, _n, m_init()); }
|
||||
void resize(size_t _n, value_type&& _v) { container::resize(m_data, _n, _v); }
|
||||
|
||||
void fill(value_type _v)
|
||||
{
|
||||
for(auto& itr : m_data)
|
||||
itr = _v;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct base_thread_data<this_type>;
|
||||
static decltype(auto) private_instance() { return instance(); }
|
||||
|
||||
array_type m_data = {};
|
||||
functor_type m_init = []() { return value_type{}; };
|
||||
};
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
unique_ptr_t<thread_data<identity<Tp>, Tag, MaxThreads>>&
|
||||
thread_data<identity<Tp>, Tag, MaxThreads>::instance()
|
||||
{
|
||||
static auto _v = unique_ptr_t<this_type>{};
|
||||
return _v;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
unique_ptr_t<thread_data<identity<Tp>, Tag, MaxThreads>>&
|
||||
thread_data<identity<Tp>, Tag, MaxThreads>::instance(construct_on_init, Args&&... _args)
|
||||
{
|
||||
static auto& _v = [&]() -> unique_ptr_t<this_type>& {
|
||||
auto& _ref = instance();
|
||||
if(!_ref)
|
||||
_ref = utility::generate<unique_ptr_t<this_type>>{}(
|
||||
std::forward<Args>(_args)...);
|
||||
if(_ref->size() < MaxThreads) _ref->resize(MaxThreads);
|
||||
return _ref;
|
||||
}();
|
||||
return _v;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
unique_ptr_t<thread_data<identity<Tp>, Tag, MaxThreads>>&
|
||||
thread_data<identity<Tp>, Tag, MaxThreads>::construct(construct_on_init, Args&&... _args)
|
||||
{
|
||||
// construct outside of lambda to prevent data-race
|
||||
static auto& _ref = instance(construct_on_init{});
|
||||
static auto _v = [&]() {
|
||||
if(_ref)
|
||||
{
|
||||
for(auto& itr : *_ref)
|
||||
itr = utility::generate<value_type>{}(std::forward<Args>(_args)...);
|
||||
}
|
||||
return (_ref != nullptr);
|
||||
}();
|
||||
return _ref;
|
||||
(void) _v;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
Tp&
|
||||
thread_data<identity<Tp>, Tag, MaxThreads>::construct(construct_on_thread&& _t,
|
||||
Args&&... _args)
|
||||
{
|
||||
// construct outside of lambda to prevent data-race
|
||||
static auto& _instance = instance(construct_on_init{});
|
||||
static auto _constructed =
|
||||
container::stable_vector<bool, MaxThreads, container::cacheline_align_v>{};
|
||||
static auto _grow = []() {
|
||||
container::resize(_constructed, MaxThreads, false);
|
||||
grow_functors().emplace_back([](int64_t _n) -> int64_t {
|
||||
if(static_cast<size_t>(_n) >= _constructed.size())
|
||||
{
|
||||
_constructed.reserve(_constructed.capacity() + 1);
|
||||
container::resize(_constructed, _constructed.capacity(), false);
|
||||
}
|
||||
return _constructed.size();
|
||||
});
|
||||
return true;
|
||||
}();
|
||||
|
||||
if(!_constructed.at(_t.index))
|
||||
_constructed.at(_t.index) =
|
||||
(_instance->at(_t.index) =
|
||||
utility::generate<value_type>{}(std::forward<Args>(_args)...),
|
||||
true);
|
||||
|
||||
return _instance->at(_t.index);
|
||||
(void) _grow;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Tag, size_t MaxThreads>
|
||||
template <typename... Args>
|
||||
Tp&
|
||||
thread_data<identity<Tp>, Tag, MaxThreads>::instance(construct_on_thread&& _t,
|
||||
Args&&... _args)
|
||||
{
|
||||
construct(construct_on_thread{ _t }, std::forward<Args>(_args)...);
|
||||
return instance()->at(_t.index);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
// there are currently some strange things that happen with
|
||||
// vector<instrumentation_bundle_t> so using vector<instrumentation_bundle_t*> and
|
||||
// timemory's ring_buffer_allocator to create contiguous memory-page aligned instances of
|
||||
// the bundle
|
||||
template <typename... Tp>
|
||||
struct component_bundle_cache_impl
|
||||
{
|
||||
using this_type = component_bundle_cache_impl<Tp...>;
|
||||
using bundle_type = tim::component_bundle<project::rocprofsys, Tp...>;
|
||||
using allocator_type = tim::data::ring_buffer_allocator<bundle_type>;
|
||||
using array_type = std::vector<bundle_type*>;
|
||||
|
||||
using iterator = typename array_type::iterator;
|
||||
using const_iterator = typename array_type::const_iterator;
|
||||
using reverse_iterator = typename array_type::reverse_iterator;
|
||||
|
||||
component_bundle_cache_impl() = default;
|
||||
~component_bundle_cache_impl() = default;
|
||||
|
||||
component_bundle_cache_impl(const component_bundle_cache_impl&) = delete;
|
||||
component_bundle_cache_impl(component_bundle_cache_impl&&) noexcept = delete;
|
||||
|
||||
component_bundle_cache_impl& operator=(const component_bundle_cache_impl&) = delete;
|
||||
component_bundle_cache_impl& operator=(component_bundle_cache_impl&&) noexcept =
|
||||
delete;
|
||||
|
||||
bool empty() const { return m_bundles.empty(); }
|
||||
|
||||
auto& front() { return m_bundles.front(); }
|
||||
auto& front() const { return m_bundles.front(); }
|
||||
|
||||
auto& back() { return m_bundles.back(); }
|
||||
auto& back() const { return m_bundles.back(); }
|
||||
|
||||
auto begin() { return m_bundles.begin(); }
|
||||
auto end() { return m_bundles.end(); }
|
||||
|
||||
auto rbegin() { return m_bundles.rbegin(); }
|
||||
auto rend() { return m_bundles.rend(); }
|
||||
|
||||
auto begin() const { return m_bundles.begin(); }
|
||||
auto end() const { return m_bundles.end(); }
|
||||
|
||||
auto size() const { return m_bundles.size(); }
|
||||
|
||||
auto& at(size_t _idx) { return m_bundles.at(_idx); }
|
||||
const auto& at(size_t _idx) const { return m_bundles.at(_idx); }
|
||||
|
||||
template <typename... Args>
|
||||
bundle_type* construct(Args&&... args)
|
||||
{
|
||||
bundle_type* _v = m_allocator.allocate(1);
|
||||
m_allocator.construct(_v, std::forward<Args>(args)...);
|
||||
return m_bundles.emplace_back(_v);
|
||||
}
|
||||
|
||||
void destroy(bundle_type* _v, size_t _idx)
|
||||
{
|
||||
m_allocator.destroy(_v);
|
||||
m_allocator.deallocate(_v, 1);
|
||||
m_bundles.erase(m_bundles.begin() + _idx);
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
bundle_type* _v = m_bundles.back();
|
||||
m_allocator.destroy(_v);
|
||||
m_allocator.deallocate(_v, 1);
|
||||
m_bundles.pop_back();
|
||||
}
|
||||
|
||||
template <typename IterT>
|
||||
void destroy(IterT _v)
|
||||
{
|
||||
iterator itr = begin();
|
||||
if constexpr(std::is_same<IterT, reverse_iterator>::value)
|
||||
{
|
||||
if(_v == rend()) return;
|
||||
std::advance(itr, std::distance(rbegin(), _v));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_v == end()) return;
|
||||
itr = _v;
|
||||
}
|
||||
m_allocator.destroy(*itr);
|
||||
m_allocator.deallocate(*itr, 1);
|
||||
m_bundles.erase(itr);
|
||||
}
|
||||
|
||||
private:
|
||||
allocator_type m_allocator = {};
|
||||
array_type m_bundles = {};
|
||||
};
|
||||
|
||||
template <typename... Tp>
|
||||
struct component_bundle_cache_impl<tim::component_bundle<project::rocprofsys, Tp...>>
|
||||
: component_bundle_cache_impl<Tp...>
|
||||
{
|
||||
using base_type = component_bundle_cache_impl<Tp...>;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename... Tp>
|
||||
using component_bundle_cache = thread_data<component_bundle_cache_impl<Tp...>>;
|
||||
using instrumentation_bundles = component_bundle_cache<instrumentation_bundle_t>;
|
||||
|
||||
extern template struct component_bundle_cache_impl<instrumentation_bundle_t>;
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,58 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "library/thread_deleter.hpp"
|
||||
#include "api.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "library/components/pthread_create_gotcha.hpp"
|
||||
#include "library/thread_info.hpp"
|
||||
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/components/timing/backends.hpp>
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
template struct component_bundle_cache_impl<instrumentation_bundle_t>;
|
||||
|
||||
void
|
||||
thread_deleter<void>::operator()() const
|
||||
{
|
||||
// called after thread info is deleted
|
||||
if(!thread_info::exists()) return;
|
||||
|
||||
const auto& _info = thread_info::get();
|
||||
if(_info && _info->index_data)
|
||||
{
|
||||
auto _tid = _info->index_data->sequent_value;
|
||||
|
||||
component::pthread_create_gotcha::shutdown(_tid);
|
||||
set_thread_state(ThreadState::Completed);
|
||||
if(get_state() < State::Finalized && _tid == 0) rocprofsys_finalize_hidden();
|
||||
}
|
||||
else
|
||||
{
|
||||
set_thread_state(ThreadState::Completed);
|
||||
}
|
||||
}
|
||||
|
||||
template struct thread_deleter<void>;
|
||||
} // namespace rocprofsys
|
||||
@@ -0,0 +1,52 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2024 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 "core/concepts.hpp"
|
||||
#include "core/defines.hpp"
|
||||
|
||||
namespace rocprofsys
|
||||
{
|
||||
template <>
|
||||
struct thread_deleter<void>
|
||||
{
|
||||
void operator()() const;
|
||||
};
|
||||
|
||||
extern template struct thread_deleter<void>;
|
||||
|
||||
template <typename Tp>
|
||||
struct thread_deleter
|
||||
{
|
||||
void operator()(Tp* ptr) const
|
||||
{
|
||||
constexpr bool delete_pointer =
|
||||
(use_placement_new_when_generating_unique_ptr<Tp>::value == false);
|
||||
|
||||
thread_deleter<void>{}();
|
||||
if constexpr(delete_pointer) delete ptr;
|
||||
|
||||
(void) ptr;
|
||||
}
|
||||
};
|
||||
} // namespace rocprofsys
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user