diff --git a/source/lib/omnitrace/CMakeLists.txt b/source/lib/omnitrace/CMakeLists.txt index 70aa52c78f..b31267a5cc 100644 --- a/source/lib/omnitrace/CMakeLists.txt +++ b/source/lib/omnitrace/CMakeLists.txt @@ -72,6 +72,7 @@ set(library_sources ${CMAKE_CURRENT_LIST_DIR}/library/state.cpp ${CMAKE_CURRENT_LIST_DIR}/library/thread_data.cpp ${CMAKE_CURRENT_LIST_DIR}/library/timemory.cpp + ${CMAKE_CURRENT_LIST_DIR}/library/tracing.cpp ${CMAKE_CURRENT_LIST_DIR}/library/components/backtrace.cpp ${CMAKE_CURRENT_LIST_DIR}/library/components/fork_gotcha.cpp ${CMAKE_CURRENT_LIST_DIR}/library/components/mpi_gotcha.cpp @@ -102,9 +103,11 @@ set(library_headers ${CMAKE_CURRENT_LIST_DIR}/library/state.hpp ${CMAKE_CURRENT_LIST_DIR}/library/thread_data.hpp ${CMAKE_CURRENT_LIST_DIR}/library/timemory.hpp + ${CMAKE_CURRENT_LIST_DIR}/library/tracing.hpp ${CMAKE_CURRENT_LIST_DIR}/library/utility.hpp ${CMAKE_CURRENT_LIST_DIR}/library/components/fwd.hpp ${CMAKE_CURRENT_LIST_DIR}/library/components/backtrace.hpp + ${CMAKE_CURRENT_LIST_DIR}/library/components/category_region.hpp ${CMAKE_CURRENT_LIST_DIR}/library/components/fork_gotcha.hpp ${CMAKE_CURRENT_LIST_DIR}/library/components/functors.hpp ${CMAKE_CURRENT_LIST_DIR}/library/components/mpi_gotcha.hpp diff --git a/source/lib/omnitrace/library.cpp b/source/lib/omnitrace/library.cpp index 9e29b229a5..e5887add40 100644 --- a/source/lib/omnitrace/library.cpp +++ b/source/lib/omnitrace/library.cpp @@ -41,6 +41,7 @@ #include "library/sampling.hpp" #include "library/thread_data.hpp" #include "library/timemory.hpp" +#include "library/tracing.hpp" #include @@ -49,7 +50,6 @@ #include using namespace omnitrace; -using tim::type_list; //======================================================================================// @@ -64,9 +64,6 @@ struct user_regions using omni_functors = omnitrace::component::functors; using user_functors = omnitrace::component::functors; -TIMEMORY_DEFINE_NAME_TRAIT("host", omni_functors) -TIMEMORY_DEFINE_NAME_TRAIT("user", user_functors) - TIMEMORY_INVOKE_PREINIT(omni_functors) TIMEMORY_INVOKE_PREINIT(user_functors) @@ -74,31 +71,6 @@ TIMEMORY_INVOKE_PREINIT(user_functors) namespace { -using interval_data_instances = thread_data>; - -auto& -get_interval_data(int64_t _tid = threading::get_id()) -{ - static auto& _v = - interval_data_instances::instances(interval_data_instances::construct_on_init{}); - return _v.at(_tid); -} - -auto& -get_timemory_hash_ids(int64_t _tid = threading::get_id()) -{ - static auto _v = std::array{}; - return _v.at(_tid); -} - -auto& -get_timemory_hash_aliases(int64_t _tid = threading::get_id()) -{ - static auto _v = - std::array{}; - return _v.at(_tid); -} - auto ensure_finalization(bool _static_init = false) { @@ -124,13 +96,6 @@ ensure_finalization(bool _static_init = false) return scope::destructor{ []() { omnitrace_finalize_hidden(); } }; } -auto& -get_trace_session() -{ - static std::unique_ptr _session{}; - return _session; -} - auto is_system_backend() { @@ -138,66 +103,28 @@ is_system_backend() return (get_backend() != "inprocess"); } -auto& -get_instrumentation_bundles(int64_t _tid = threading::get_id()) -{ - static thread_local auto& _v = instrumentation_bundles::instances().at(_tid); - return _v; -} - -auto& -get_finalization_functions() -{ - static auto _v = std::vector>{}; - return _v; -} - using Device = critical_trace::Device; using Phase = critical_trace::Phase; } // namespace -//======================================================================================// -/// -/// -/// -//======================================================================================// -namespace -{ -auto& -push_count() -{ - static std::atomic _v{ 0 }; - return _v; -} -auto& -pop_count() -{ - static std::atomic _v{ 0 }; - return _v; -} - -auto _debug_push = tim::get_env("OMNITRACE_DEBUG_PUSH", false) && !get_debug_env(); -auto _debug_pop = tim::get_env("OMNITRACE_DEBUG_POP", false) && !get_debug_env(); -auto _debug_user = - tim::get_env("OMNITRACE_DEBUG_USER_REGIONS", false) && !get_debug_env(); -} // namespace - //======================================================================================// extern "C" void omnitrace_push_trace_hidden(const char* name) { - ++push_count(); + ++tracing::push_count(); // unconditionally return if finalized if(get_state() == State::Finalized) { OMNITRACE_CONDITIONAL_BASIC_PRINT( - _debug_push, "omnitrace_push_trace(%s) called during finalization\n", name); + tracing::debug_push, "omnitrace_push_trace(%s) called during finalization\n", + name); return; } - OMNITRACE_CONDITIONAL_BASIC_PRINT(_debug_push, "omnitrace_push_trace(%s)\n", name); + OMNITRACE_CONDITIONAL_BASIC_PRINT(tracing::debug_push, "omnitrace_push_trace(%s)\n", + name); // the expectation here is that if the state is not active then the call // to omnitrace_init_tooling_hidden will activate all the appropriate @@ -215,7 +142,7 @@ omnitrace_push_trace_hidden(const char* name) static auto _sample_rate = std::max(get_instrumentation_interval(), 1); static thread_local size_t _sample_idx = 0; - auto& _interval = get_interval_data(); + auto& _interval = tracing::get_interval_data(); auto _enabled = (_sample_idx++ % _sample_rate == 0); _interval->emplace_back(_enabled); @@ -242,16 +169,17 @@ omnitrace_push_trace_hidden(const char* name) extern "C" void omnitrace_pop_trace_hidden(const char* name) { - ++pop_count(); + ++tracing::pop_count(); - OMNITRACE_CONDITIONAL_BASIC_PRINT(_debug_pop, "omnitrace_pop_trace(%s)\n", name); + OMNITRACE_CONDITIONAL_BASIC_PRINT(tracing::debug_pop, "omnitrace_pop_trace(%s)\n", + name); // only execute when active if(get_state() == State::Active) { OMNITRACE_DEBUG("omnitrace_pop_trace(%s)\n", name); - auto& _interval_data = get_interval_data(); + auto& _interval_data = tracing::get_interval_data(); if(!_interval_data->empty()) { if(_interval_data->back()) omni_functors::stop(name); @@ -298,11 +226,13 @@ omnitrace_push_region_hidden(const char* name) if(get_state() == State::Finalized) { OMNITRACE_CONDITIONAL_BASIC_PRINT( - _debug_user, "omnitrace_push_region(%s) called during finalization\n", name); + tracing::debug_user, "omnitrace_push_region(%s) called during finalization\n", + name); return; } - OMNITRACE_CONDITIONAL_BASIC_PRINT(_debug_push, "omnitrace_push_region(%s)\n", name); + OMNITRACE_CONDITIONAL_BASIC_PRINT(tracing::debug_push, "omnitrace_push_region(%s)\n", + name); // the expectation here is that if the state is not active then the call // to omnitrace_init_tooling_hidden will activate all the appropriate @@ -625,153 +555,65 @@ omnitrace_init_tooling_hidden() (void) get_perfetto_output_filename(); } - auto _exe = get_exe_name(); - auto _hash_ids = tim::get_hash_ids(); - auto _hash_aliases = tim::get_hash_aliases(); - auto _thread_init = [_exe, _hash_ids, _hash_aliases]() { - static thread_local auto _thread_setup = [_exe]() { - if(threading::get_id() > 0) - threading::set_thread_name( - TIMEMORY_JOIN(" ", "Thread", threading::get_id()).c_str()); - thread_data::construct( - TIMEMORY_JOIN("", _exe, "/thread-", threading::get_id()), - quirk::config{}); - get_interval_data()->reserve(512); - // save the hash maps - get_timemory_hash_ids() = tim::get_hash_ids(); - get_timemory_hash_aliases() = tim::get_hash_aliases(); - return true; - }(); - static thread_local auto _dtor = scope::destructor{ []() { - if(get_state() != State::Finalized) - { - if(get_use_sampling()) sampling::shutdown(); - auto& _thr_bundle = thread_data::instance(); - if(_thr_bundle && _thr_bundle->get() && - _thr_bundle->get()->get_is_running()) - _thr_bundle->stop(); - } - } }; - (void) _thread_setup; - (void) _dtor; - }; - - // separate from _thread_init so that it can be called after the first - // instrumentation on the thread - auto _setup_thread_sampling = []() { - static thread_local auto _v = []() { - auto _use_sampling = get_use_sampling(); - if(_use_sampling) sampling::setup(); - return _use_sampling; - }(); - (void) _v; - }; - - // functors for starting and stopping timemory omni functors - auto _push_timemory = [](const char* name) { - auto& _data = get_instrumentation_bundles(); - // this generates a hash for the raw string array - auto _hash = tim::add_hash_id(tim::string_view_t{ name }); - auto* _bundle = _data.allocator.allocate(1); - _data.bundles.emplace_back(_bundle); - _data.allocator.construct(_bundle, _hash); - _bundle->start(); - }; - - auto _pop_timemory = [](const char* name) { - auto _hash = tim::hash::get_hash_id(tim::string_view_t{ name }); - auto& _data = get_instrumentation_bundles(); - if(_data.bundles.empty()) - { - OMNITRACE_DEBUG("[%s] skipped %s :: empty bundle stack\n", - "omnitrace_pop_trace", name); - return; - } - for(size_t i = _data.bundles.size(); i > 0; --i) - { - auto*& _v = _data.bundles.at(i - 1); - if(_v->get_hash() == _hash) - { - _v->stop(); - _data.allocator.destroy(_v); - _data.allocator.deallocate(_v, 1); - _data.bundles.erase(_data.bundles.begin() + (i - 1)); - break; - } - } - }; - - // functors for starting and stopping perfetto omni functors - auto _push_perfetto = [](auto _category, const char* name) { - using CategoryT = std::decay_t; - uint64_t _ts = comp::wall_clock::record(); - TRACE_EVENT_BEGIN(trait::name::value, perfetto::StaticString(name), - _ts, "begin_ns", _ts); - }; - - auto _pop_perfetto = [](auto _category, const char*) { - using CategoryT = std::decay_t; - uint64_t _ts = comp::wall_clock::record(); - TRACE_EVENT_END(trait::name::value, _ts, "end_ns", _ts); - }; + auto _exe = get_exe_name(); if(get_use_perfetto() && get_use_timemory()) { omni_functors::configure( - [=](const char* name) { - _thread_init(); - _push_perfetto(type_list{}, name); - _push_timemory(name); - _setup_thread_sampling(); + [](const char* name) { + tracing::thread_init(); + tracing::push_perfetto(category::host{}, name); + tracing::push_timemory(name); + tracing::thread_init_sampling(); }, - [=](const char* name) { - _pop_timemory(name); - _pop_perfetto(type_list{}, name); + [](const char* name) { + tracing::pop_timemory(name); + tracing::pop_perfetto(category::host{}, name); }); user_functors::configure( - [=](const char* name) { - _thread_init(); - _push_perfetto(type_list{}, name); - _push_timemory(name); + [](const char* name) { + tracing::thread_init(); + tracing::push_perfetto(category::user{}, name); + tracing::push_timemory(name); }, - [=](const char* name) { - _pop_timemory(name); - _pop_perfetto(type_list{}, name); + [](const char* name) { + tracing::pop_timemory(name); + tracing::pop_perfetto(category::user{}, name); }); } else if(get_use_perfetto()) { omni_functors::configure( - [=](const char* name) { - _thread_init(); - _push_perfetto(type_list{}, name); - _setup_thread_sampling(); + [](const char* name) { + tracing::thread_init(); + tracing::push_perfetto(category::host{}, name); + tracing::thread_init_sampling(); }, - [=](const char* name) { _pop_perfetto(type_list{}, name); }); + [](const char* name) { tracing::pop_perfetto(category::host{}, name); }); user_functors::configure( - [=](const char* name) { - _thread_init(); - _push_perfetto(type_list{}, name); - _setup_thread_sampling(); + [](const char* name) { + tracing::thread_init(); + tracing::push_perfetto(category::user{}, name); + tracing::thread_init_sampling(); }, - [=](const char* name) { _pop_perfetto(type_list{}, name); }); + [](const char* name) { tracing::pop_perfetto(category::user{}, name); }); } else if(get_use_timemory()) { omni_functors::configure( - [=](const char* name) { - _thread_init(); - _push_timemory(name); - _setup_thread_sampling(); + [](const char* name) { + tracing::thread_init(); + tracing::push_timemory(name); + tracing::thread_init_sampling(); }, - [=](const char* name) { _pop_timemory(name); }); + [](const char* name) { tracing::pop_timemory(name); }); user_functors::configure( - [=](const char* name) { - _thread_init(); - _push_timemory(name); - _setup_thread_sampling(); + [](const char* name) { + tracing::thread_init(); + tracing::push_timemory(name); + tracing::thread_init_sampling(); }, - [=](const char* name) { _pop_timemory(name); }); + [](const char* name) { tracing::pop_timemory(name); }); } if(get_use_ompt()) @@ -796,7 +638,7 @@ omnitrace_init_tooling_hidden() PRINT_HERE("%s", "Trace"); }); #endif - auto& tracing_session = get_trace_session(); + auto& tracing_session = tracing::get_trace_session(); tracing_session = perfetto::Tracing::NewTrace(); tracing_session->Setup(cfg); tracing_session->StartBlocking(); @@ -842,8 +684,8 @@ omnitrace_init_hidden(const char* _mode, bool _is_binary_rewrite, const char* _a // always the first (void) get_state(); - (void) push_count(); - (void) pop_count(); + (void) tracing::push_count(); + (void) tracing::pop_count(); OMNITRACE_CONDITIONAL_THROW( get_state() >= State::Init && @@ -853,7 +695,7 @@ omnitrace_init_hidden(const char* _mode, bool _is_binary_rewrite, const char* _a _mode, std::to_string(_is_binary_rewrite).c_str(), _argv0, std::to_string(get_state()).c_str()); - get_finalization_functions().emplace_back([_argv0]() { + tracing::get_finalization_functions().emplace_back([_argv0]() { OMNITRACE_CI_THROW(get_state() != State::Active, "Finalizer function for popping main invoked in non-active " "state :: state = %s\n", @@ -914,13 +756,13 @@ omnitrace_finalize_hidden(void) // some functions called during finalization may alter the push/pop count so we need // to save them here - auto _push_count = push_count().load(); - auto _pop_count = pop_count().load(); + auto _push_count = tracing::push_count().load(); + auto _pop_count = tracing::pop_count().load(); // e.g. omnitrace_pop_trace("main"); if(_push_count > _pop_count) { - for(auto& itr : get_finalization_functions()) + for(auto& itr : tracing::get_finalization_functions()) { itr(); ++_pop_count; @@ -955,12 +797,12 @@ omnitrace_finalize_hidden(void) OMNITRACE_DEBUG_F("Copying over all timemory hash information to main thread...\n"); // copy these over so that all hashes are known - auto& _hzero = get_timemory_hash_ids(0); - auto& _azero = get_timemory_hash_aliases(0); + auto& _hzero = tracing::get_timemory_hash_ids(0); + auto& _azero = tracing::get_timemory_hash_aliases(0); for(size_t i = 1; i < max_supported_threads; ++i) { - auto& _hitr = get_timemory_hash_ids(i); - auto& _aitr = get_timemory_hash_aliases(i); + auto& _hitr = tracing::get_timemory_hash_ids(i); + auto& _aitr = tracing::get_timemory_hash_aliases(i); if(_hzero && _hitr) { for(const auto& itr : *_hitr) @@ -1139,6 +981,11 @@ omnitrace_finalize_hidden(void) bool _perfetto_output_error = false; if(get_use_perfetto() && !is_system_backend()) { + auto& tracing_session = tracing::get_trace_session(); + + OMNITRACE_CI_THROW(tracing_session == nullptr, + "Null pointer to the tracing session"); + if(get_verbose() >= 0) fprintf(stderr, "\n"); if(get_verbose() >= 0 || get_debug()) fprintf(stderr, "[%s][%s]|%i> Flushing perfetto...\n", TIMEMORY_PROJECT_NAME, @@ -1146,9 +993,9 @@ omnitrace_finalize_hidden(void) // Make sure the last event is closed for this example. perfetto::TrackEvent::Flush(); + tracing_session->FlushBlocking(); - auto& tracing_session = get_trace_session(); - OMNITRACE_VERBOSE_F(3, "Stopping the blocking perfetto trace sessions...\n"); + OMNITRACE_VERBOSE_F(3, "Stopping the blocking perfetto trace session...\n"); tracing_session->StopBlocking(); using char_vec_t = std::vector; diff --git a/source/lib/omnitrace/library/common.hpp b/source/lib/omnitrace/library/common.hpp index 9ebe7a0bd3..d87e45b11b 100644 --- a/source/lib/omnitrace/library/common.hpp +++ b/source/lib/omnitrace/library/common.hpp @@ -48,8 +48,9 @@ TIMEMORY_DEFINE_NS_API(api, rocm_smi) namespace omnitrace { -namespace api = tim::api; // NOLINT -} +namespace api = tim::api; // NOLINT +namespace category = tim::category; // NOLINT +} // namespace omnitrace // same sort of functionality as python's " ".join([...]) #if !defined(JOIN) diff --git a/source/lib/omnitrace/library/components/category_region.hpp b/source/lib/omnitrace/library/components/category_region.hpp new file mode 100644 index 0000000000..a7967d57d7 --- /dev/null +++ b/source/lib/omnitrace/library/components/category_region.hpp @@ -0,0 +1,161 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "library/config.hpp" +#include "library/defines.hpp" +#include "library/timemory.hpp" +#include "library/tracing.hpp" + +#include +#include + +namespace omnitrace +{ +namespace audit = ::tim::audit; + +namespace component +{ +// timemory component which calls omnitrace functions +// (used in gotcha wrappers) +template +struct category_region : comp::base, void> +{ + using gotcha_data_t = tim::component::gotcha_data; + + static constexpr auto category_name = trait::name::value; + + static std::string label() { return JOIN('_', "omnitrace", category_name, "region"); } + + template + static void start(const char* name, Args&&...); + + template + static void stop(const char* name, Args&&...); + + template + static void audit(const gotcha_data_t&, audit::incoming, Args&&...); + + template + static void audit(const gotcha_data_t&, audit::outgoing, Args&&...); +}; + +template +template +void +category_region::start(const char* name, Args&&... args) +{ + OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal); + + // unconditionally return if finalized + if(get_state() == State::Finalized) + { + OMNITRACE_CONDITIONAL_BASIC_PRINT( + tracing::debug_user, "omnitrace_push_region(%s) called during finalization\n", + name); + return; + } + + OMNITRACE_CONDITIONAL_BASIC_PRINT(tracing::debug_push, "omnitrace_push_region(%s)\n", + name); + + // the expectation here is that if the state is not active then the call + // to omnitrace_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 && !omnitrace_init_tooling_hidden()) + { + static auto _debug = get_debug_env() || get_debug_init(); + OMNITRACE_CONDITIONAL_BASIC_PRINT( + _debug, "omnitrace_push_region(%s) ignored :: not active. state = %s\n", name, + std::to_string(get_state()).c_str()); + return; + } + + OMNITRACE_DEBUG("[%s] omnitrace_push_region(%s)\n", category_name, name); + + auto _use_timemory = get_use_timemory(); + auto _use_perfetto = get_use_perfetto(); + + if(_use_timemory || _use_perfetto) tracing::thread_init(); + if(_use_perfetto) + { + tracing::push_perfetto(CategoryT{}, name, std::forward(args)...); + } + if(_use_timemory) + { + tracing::push_timemory(name, std::forward(args)...); + } + if(_use_timemory || _use_perfetto) tracing::thread_init_sampling(); +} + +template +template +void +category_region::stop(const char* name, Args&&... args) +{ + OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal); + + // only execute when active + if(get_state() == State::Active) + { + OMNITRACE_CONDITIONAL_PRINT(tracing::debug_pop || get_debug(), + "omnitrace_pop_region(%s)\n", name); + if(get_use_timemory()) + { + tracing::pop_timemory(name, std::forward(args)...); + } + if(get_use_perfetto()) + { + tracing::pop_perfetto(CategoryT{}, name, std::forward(args)...); + } + } + else + { + static auto _debug = get_debug_env(); + OMNITRACE_CONDITIONAL_BASIC_PRINT( + _debug, "omnitrace_pop_region(%s) ignored :: state = %s\n", name, + std::to_string(get_state()).c_str()); + } +} + +template +template +void +category_region::audit(const gotcha_data_t& _data, audit::incoming, + Args&&... _args) +{ + OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal); + start(_data.tool_id.c_str(), "args", JOIN(", ", _args...)); +} + +template +template +void +category_region::audit(const gotcha_data_t& _data, audit::outgoing, + Args&&... _args) +{ + OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal); + stop(_data.tool_id.c_str(), "return", JOIN(", ", _args...)); +} +} // namespace component +} // namespace omnitrace diff --git a/source/lib/omnitrace/library/components/fwd.hpp b/source/lib/omnitrace/library/components/fwd.hpp index 90ffcd4df4..69d1fd5cbe 100644 --- a/source/lib/omnitrace/library/components/fwd.hpp +++ b/source/lib/omnitrace/library/components/fwd.hpp @@ -60,6 +60,30 @@ TIMEMORY_DECLARE_TYPE_TRAIT(name, typename Tp) } \ } +TIMEMORY_DEFINE_NS_API(category, host) +TIMEMORY_DEFINE_NS_API(category, device) +TIMEMORY_DEFINE_NS_API(category, user) +TIMEMORY_DEFINE_NS_API(category, rocm_smi) +TIMEMORY_DEFINE_NS_API(category, kokkos) +TIMEMORY_DEFINE_NS_API(category, mpi) +TIMEMORY_DEFINE_NS_API(category, ompt) +TIMEMORY_DEFINE_NS_API(category, critical_trace) +TIMEMORY_DEFINE_NS_API(category, host_critical_trace) +TIMEMORY_DEFINE_NS_API(category, device_critical_trace) + +TIMEMORY_DEFINE_NAME_TRAIT("host", category::host); +TIMEMORY_DEFINE_NAME_TRAIT("device", category::device); +TIMEMORY_DEFINE_NAME_TRAIT("user", category::user); +TIMEMORY_DEFINE_NAME_TRAIT("rocm_smi", category::rocm_smi); +TIMEMORY_DEFINE_NAME_TRAIT("sampling", category::sampling); +TIMEMORY_DEFINE_NAME_TRAIT("thread_sampling", category::thread_sampling); +TIMEMORY_DEFINE_NAME_TRAIT("kokkos", category::kokkos); +TIMEMORY_DEFINE_NAME_TRAIT("mpi", category::mpi); +TIMEMORY_DEFINE_NAME_TRAIT("ompt", category::ompt); +TIMEMORY_DEFINE_NAME_TRAIT("critical-trace", category::critical_trace); +TIMEMORY_DEFINE_NAME_TRAIT("host-critical-trace", category::host_critical_trace); +TIMEMORY_DEFINE_NAME_TRAIT("device-critical-trace", category::device_critical_trace); + namespace omnitrace { namespace component diff --git a/source/lib/omnitrace/library/components/mpi_gotcha.cpp b/source/lib/omnitrace/library/components/mpi_gotcha.cpp index 91db3a8756..3e571fe8be 100644 --- a/source/lib/omnitrace/library/components/mpi_gotcha.cpp +++ b/source/lib/omnitrace/library/components/mpi_gotcha.cpp @@ -22,7 +22,7 @@ #include "library/components/mpi_gotcha.hpp" #include "library/api.hpp" -#include "library/components/omnitrace.hpp" +#include "library/components/category_region.hpp" #include "library/config.hpp" #include "library/debug.hpp" #include "library/mproc.hpp" @@ -104,8 +104,10 @@ omnitrace_mpi_set_attr() static auto _mpi_fini = [](MPI_Comm, int, void*, void*) { OMNITRACE_DEBUG("MPI Comm attribute finalize\n"); if(mpip_index != std::numeric_limits::max()) - comp::deactivate_mpip, - api::omnitrace>(mpip_index); + comp::deactivate_mpip< + tim::component_tuple< + omnitrace::component::category_region>, + api::omnitrace>(mpip_index); omnitrace_finalize_hidden(); return MPI_SUCCESS; }; @@ -213,8 +215,9 @@ mpi_gotcha::audit(const gotcha_data_t& _data, audit::incoming) OMNITRACE_BASIC_DEBUG_F("%s()\n", _data.tool_id.c_str()); if(mpip_index != std::numeric_limits::max()) - comp::deactivate_mpip, - api::omnitrace>(mpip_index); + comp::deactivate_mpip< + tim::component_tuple>, + api::omnitrace>(mpip_index); #if !defined(TIMEMORY_USE_MPI) && defined(TIMEMORY_USE_MPI_HEADERS) tim::mpi::is_initialized_callback() = []() { return false; }; @@ -266,11 +269,14 @@ mpi_gotcha::audit(const gotcha_data_t& _data, audit::outgoing, int _retval) // use env vars OMNITRACE_MPIP_PERMIT_LIST and OMNITRACE_MPIP_REJECT_LIST // to control the gotcha bindings at runtime - comp::configure_mpip, - api::omnitrace>(); - mpip_index = - comp::activate_mpip, - api::omnitrace>(); + comp::configure_mpip< + tim::component_tuple< + omnitrace::component::category_region>, + api::omnitrace>(); + mpip_index = comp::activate_mpip< + tim::component_tuple< + omnitrace::component::category_region>, + api::omnitrace>(); } auto_lock_t _lk{ type_mutex() }; diff --git a/source/lib/omnitrace/library/config.cpp b/source/lib/omnitrace/library/config.cpp index 99c8f24530..74d78e0d56 100644 --- a/source/lib/omnitrace/library/config.cpp +++ b/source/lib/omnitrace/library/config.cpp @@ -76,7 +76,7 @@ get_setting_name(std::string _v) [&]() { \ auto _ret = _config->insert( \ ENV_NAME, get_setting_name(ENV_NAME), DESCRIPTION, INITIAL_VALUE, \ - std::set{ "custom", "omnitrace", "omnitrace_library", \ + std::set{ "custom", "omnitrace", "libomnitrace", \ __VA_ARGS__ }); \ if(!_ret.second) \ OMNITRACE_PRINT("Warning! Duplicate setting: %s / %s\n", \ @@ -84,7 +84,7 @@ get_setting_name(std::string _v) return _config->find(ENV_NAME)->second; \ }() -// below does not include "omnitrace_library" +// below does not include "libomnitrace" #define OMNITRACE_CONFIG_EXT_SETTING(TYPE, ENV_NAME, DESCRIPTION, INITIAL_VALUE, ...) \ [&]() { \ auto _ret = _config->insert( \ @@ -102,8 +102,7 @@ get_setting_name(std::string _v) [&]() { \ auto _ret = _config->insert( \ ENV_NAME, get_setting_name(ENV_NAME), DESCRIPTION, INITIAL_VALUE, \ - std::set{ "custom", "omnitrace", "omnitrace_library", \ - __VA_ARGS__ }, \ + std::set{ "custom", "omnitrace", "libomnitrace", __VA_ARGS__ }, \ std::vector{ CMD_LINE }); \ if(!_ret.second) \ OMNITRACE_PRINT("Warning! Duplicate setting: %s / %s\n", \ @@ -176,7 +175,7 @@ configure_settings(bool _init) OMNITRACE_CONFIG_EXT_SETTING(bool, "OMNITRACE_DL_VERBOSE", "Verbosity within the omnitrace-dl library", false, - "debugging", "omnitrace_dl_library"); + "debugging", "libomnitrace-dl"); OMNITRACE_CONFIG_SETTING(bool, "OMNITRACE_USE_PERFETTO", "Enable perfetto backend", _default_perfetto_v, "backend", "perfetto"); @@ -399,7 +398,7 @@ configure_settings(bool _init) { auto _categories = itr->second->get_categories(); _categories.emplace("omnitrace"); - _categories.emplace("omnitrace_library"); + _categories.emplace("libomnitrace"); itr->second->set_categories(_categories); } }; diff --git a/source/lib/omnitrace/library/perfetto.hpp b/source/lib/omnitrace/library/perfetto.hpp index 70836699f4..7811f55d34 100644 --- a/source/lib/omnitrace/library/perfetto.hpp +++ b/source/lib/omnitrace/library/perfetto.hpp @@ -37,6 +37,11 @@ perfetto::Category("rocm_smi").SetDescription("Device-level metrics"), \ perfetto::Category("sampling") \ .SetDescription("Metrics derived from sampling"), \ + perfetto::Category("thread_sampling") \ + .SetDescription("Metrics derived from background thread sampling"), \ + perfetto::Category("mpi").SetDescription("MPI regions"), \ + perfetto::Category("kokkos").SetDescription("Kokkos regions"), \ + perfetto::Category("ompt").SetDescription("OpenMP Tools regions"), \ perfetto::Category("critical-trace") \ .SetDescription("Combined critical traces"), \ perfetto::Category("host-critical-trace") \ @@ -51,6 +56,11 @@ perfetto::Category("rocm_smi").SetDescription("Device-level metrics"), \ perfetto::Category("sampling") \ .SetDescription("Metrics derived from sampling"), \ + perfetto::Category("thread_sampling") \ + .SetDescription("Metrics derived from background thread sampling"), \ + perfetto::Category("mpi").SetDescription("MPI regions"), \ + perfetto::Category("kokkos").SetDescription("Kokkos regions"), \ + perfetto::Category("ompt").SetDescription("OpenMP Tools regions"), \ perfetto::Category("critical-trace") \ .SetDescription("Combined critical traces"), \ perfetto::Category("host-critical-trace") \ @@ -136,30 +146,30 @@ struct perfetto_counter_track const char* _category = nullptr, int64_t _mult = 1, bool _incr = false) { + auto& _name_data = get_data().first[_idx]; + auto& _track_data = get_data().second[_idx]; std::vector> _missing = {}; if(config::get_is_continuous_integration()) { - for(const auto& itr : get_data().first[_idx]) + for(const auto& itr : _name_data) { _missing.emplace_back(std::make_tuple(*itr, itr->c_str(), false)); } } - auto& _name = - get_data().first[_idx].emplace_back(std::make_unique(_v)); + auto& _name = _name_data.emplace_back(std::make_unique(_v)); const char* _unit_name = (_units && strlen(_units) > 0) ? _units : nullptr; - get_data().second[_idx].emplace_back(perfetto::CounterTrack{ _name->c_str() } - .set_unit_name(_unit_name) - .set_category(_category) - .set_unit_multiplier(_mult) - .set_is_incremental(_incr)); + _track_data.emplace_back(perfetto::CounterTrack{ _name->c_str() } + .set_unit_name(_unit_name) + .set_category(_category) + .set_unit_multiplier(_mult) + .set_is_incremental(_incr)); if(config::get_is_continuous_integration()) { for(auto& itr : _missing) { - for(const auto& ditr : get_data().first.at(_idx)) + const char* citr = std::get<1>(itr); + for(const auto& ditr : _name_data) { - if(*ditr == _v) continue; - const char* citr = std::get<1>(itr); if(citr == ditr->c_str() && strcmp(citr, ditr->c_str()) == 0) { std::get<2>(itr) = true; @@ -168,9 +178,25 @@ struct perfetto_counter_track } if(!std::get<2>(itr)) { - OMNITRACE_THROW("perfetto_counter_track emplace method for '%s' " - "invalidated C-string '%s'\n", - _v.c_str(), std::get<0>(itr).c_str()); + std::set _prev = {}; + std::set _curr = {}; + for(const auto& eitr : _missing) + _prev.emplace( + static_cast(const_cast(std::get<1>(eitr)))); + for(const auto& eitr : _name_data) + _curr.emplace( + static_cast(const_cast(eitr->c_str()))); + std::stringstream _pss{}; + for(auto&& eitr : _prev) + _pss << " " << std::hex << std::setw(12) << std::left << eitr; + std::stringstream _css{}; + for(auto&& eitr : _curr) + _css << " " << std::hex << std::setw(12) << std::left << eitr; + OMNITRACE_THROW("perfetto_counter_track emplace method for '%s' (%p) " + "invalidated C-string '%s' (%p).\n%8s: %s\n%8s: %s\n", + _v.c_str(), _name->c_str(), std::get<0>(itr).c_str(), + std::get<0>(itr).c_str(), "previous", + _pss.str().c_str(), "current", _css.str().c_str()); } } } diff --git a/source/lib/omnitrace/library/tracing.cpp b/source/lib/omnitrace/library/tracing.cpp new file mode 100644 index 0000000000..9cab24c5c9 --- /dev/null +++ b/source/lib/omnitrace/library/tracing.cpp @@ -0,0 +1,58 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "library/tracing.hpp" + +namespace omnitrace +{ +namespace tracing +{ +std::unique_ptr& +get_trace_session() +{ + static auto _session = std::unique_ptr{}; + return _session; +} + +std::vector>& +get_finalization_functions() +{ + static auto _v = std::vector>{}; + return _v; +} + +tim::hash_map_ptr_t& +get_timemory_hash_ids(int64_t _tid) +{ + static auto _v = std::array{}; + return _v.at(_tid); +} + +tim::hash_alias_ptr_t& +get_timemory_hash_aliases(int64_t _tid) +{ + static auto _v = + std::array{}; + return _v.at(_tid); +} +} // namespace tracing +} // namespace omnitrace diff --git a/source/lib/omnitrace/library/tracing.hpp b/source/lib/omnitrace/library/tracing.hpp new file mode 100644 index 0000000000..3a7d59ee09 --- /dev/null +++ b/source/lib/omnitrace/library/tracing.hpp @@ -0,0 +1,203 @@ +// MIT License +// +// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "library/config.hpp" +#include "library/debug.hpp" +#include "library/defines.hpp" +#include "library/perfetto.hpp" +#include "library/runtime.hpp" +#include "library/sampling.hpp" +#include "library/timemory.hpp" + +namespace omnitrace +{ +namespace tracing +{ +using interval_data_instances = thread_data>; + +std::unique_ptr& +get_trace_session(); + +std::vector>& +get_finalization_functions(); + +tim::hash_map_ptr_t& +get_timemory_hash_ids(int64_t _tid = threading::get_id()); + +tim::hash_alias_ptr_t& +get_timemory_hash_aliases(int64_t _tid = threading::get_id()); + +namespace +{ +bool debug_push = // NOLINT + tim::get_env("OMNITRACE_DEBUG_PUSH", false) && !get_debug_env(); +bool debug_pop = // NOLINT + tim::get_env("OMNITRACE_DEBUG_POP", false) && !get_debug_env(); +bool debug_user = // NOLINT + tim::get_env("OMNITRACE_DEBUG_USER_REGIONS", false) && !get_debug_env(); +} // namespace + +inline auto& +get_interval_data(int64_t _tid = threading::get_id()) +{ + static auto& _v = + interval_data_instances::instances(interval_data_instances::construct_on_init{}); + return _v.at(_tid); +} + +inline auto& +get_instrumentation_bundles(int64_t _tid = threading::get_id()) +{ + static thread_local auto& _v = instrumentation_bundles::instances().at(_tid); + return _v; +} + +inline auto& +push_count() +{ + static std::atomic _v{ 0 }; + return _v; +} + +inline auto& +pop_count() +{ + static std::atomic _v{ 0 }; + return _v; +} + +inline void +thread_init() +{ + static thread_local auto _thread_setup = []() { + auto _exe = get_exe_name(); + if(threading::get_id() > 0) + threading::set_thread_name(JOIN(" ", "Thread", threading::get_id()).c_str()); + thread_data::construct( + JOIN("", _exe, "/thread-", threading::get_id()), + quirk::config{}); + get_interval_data()->reserve(512); + // save the hash maps + get_timemory_hash_ids() = tim::get_hash_ids(); + get_timemory_hash_aliases() = tim::get_hash_aliases(); + return true; + }(); + static thread_local auto _dtor = scope::destructor{ []() { + if(get_state() != State::Finalized) + { + if(get_use_sampling()) sampling::shutdown(); + auto& _thr_bundle = thread_data::instance(); + if(_thr_bundle && _thr_bundle->get() && + _thr_bundle->get()->get_is_running()) + _thr_bundle->stop(); + } + } }; + (void) _thread_setup; + (void) _dtor; +} + +inline void +thread_init_sampling() +{ + static thread_local auto _v = []() { + auto _use_sampling = get_use_sampling(); + if(_use_sampling) sampling::setup(); + return _use_sampling; + }(); + (void) _v; +} + +template +inline void +push_timemory(const char* name, Args&&... args) +{ + auto& _data = tracing::get_instrumentation_bundles(); + // this generates a hash for the raw string array + auto _hash = tim::add_hash_id(tim::string_view_t{ name }); + auto* _bundle = _data.allocator.allocate(1); + _data.bundles.emplace_back(_bundle); + _data.allocator.construct(_bundle, _hash); + _bundle->start(std::forward(args)...); +} + +template +inline void +pop_timemory(const char* name, Args&&... args) +{ + auto _hash = tim::hash::get_hash_id(tim::string_view_t{ name }); + auto& _data = tracing::get_instrumentation_bundles(); + if(_data.bundles.empty()) + { + OMNITRACE_DEBUG("[%s] skipped %s :: empty bundle stack\n", "omnitrace_pop_trace", + name); + return; + } + for(size_t i = _data.bundles.size(); i > 0; --i) + { + auto*& _v = _data.bundles.at(i - 1); + if(_v->get_hash() == _hash) + { + _v->stop(std::forward(args)...); + _data.allocator.destroy(_v); + _data.allocator.deallocate(_v, 1); + _data.bundles.erase(_data.bundles.begin() + (i - 1)); + break; + } + } +} + +template +inline void +push_perfetto(CategoryT, const char* name, Args&&... args) +{ + uint64_t _ts = comp::wall_clock::record(); + TRACE_EVENT_BEGIN(trait::name::value, perfetto::StaticString(name), _ts, + std::forward(args)..., "begin_ns", _ts); +} + +template +inline void +pop_perfetto(CategoryT, const char*, Args&&... args) +{ + uint64_t _ts = comp::wall_clock::record(); + TRACE_EVENT_END(trait::name::value, _ts, std::forward(args)..., + "end_ns", _ts); +} + +template +inline void +push_perfetto_ts(CategoryT, const char* name, uint64_t _ts, Args&&... args) +{ + TRACE_EVENT_BEGIN(trait::name::value, perfetto::StaticString(name), _ts, + std::forward(args)...); +} + +template +inline void +pop_perfetto_ts(CategoryT, const char*, uint64_t _ts, Args&&... args) +{ + TRACE_EVENT_END(trait::name::value, _ts, std::forward(args)...); +} +} // namespace tracing +} // namespace omnitrace