Add 'projects/rocprofiler-systems/' from commit '92e1d84c72c9321d79a1866e0090fae0215e6557'
git-subtree-dir: projects/rocprofiler-systems git-subtree-mainline:ee9e74df21git-subtree-split:92e1d84c72
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# executable RPATH
|
||||
|
||||
set(ROCPROFSYS_EXE_INSTALL_RPATH
|
||||
"\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}"
|
||||
)
|
||||
|
||||
# executables
|
||||
add_subdirectory(rocprof-sys-avail)
|
||||
add_subdirectory(rocprof-sys-causal)
|
||||
add_subdirectory(rocprof-sys-sample)
|
||||
add_subdirectory(rocprof-sys-instrument)
|
||||
add_subdirectory(rocprof-sys-run)
|
||||
|
||||
# tests
|
||||
if(ROCPROFSYS_BUILD_TESTING OR "$ENV{ROCPROFSYS_CI}" MATCHES "[1-9]+|ON|on|y|yes")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
@@ -0,0 +1,49 @@
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# rocprofiler-systems-avail target
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_executable(rocprofiler-systems-avail)
|
||||
|
||||
target_sources(
|
||||
rocprofiler-systems-avail
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/avail.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/avail.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/common.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/common.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/component_categories.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/defines.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/enumerated_list.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/generate_config.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/generate_config.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/get_availability.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/get_categories.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/info_type.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/info_type.hpp
|
||||
)
|
||||
|
||||
target_include_directories(rocprofiler-systems-avail PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_compile_definitions(
|
||||
rocprofiler-systems-avail
|
||||
PRIVATE ROCPROFSYS_EXTERN_COMPONENTS=0
|
||||
)
|
||||
target_link_libraries(
|
||||
rocprofiler-systems-avail
|
||||
PRIVATE
|
||||
rocprofiler-systems::rocprofiler-systems-compile-definitions
|
||||
rocprofiler-systems::rocprofiler-systems-interface-library
|
||||
rocprofiler-systems::librocprofiler-systems-static
|
||||
)
|
||||
set_target_properties(
|
||||
rocprofiler-systems-avail
|
||||
PROPERTIES
|
||||
BUILD_RPATH "\$ORIGIN:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
|
||||
INSTALL_RPATH "${ROCPROFSYS_EXE_INSTALL_RPATH}"
|
||||
OUTPUT_NAME ${BINARY_NAME_PREFIX}-avail
|
||||
)
|
||||
|
||||
rocprofiler_systems_strip_target(rocprofiler-systems-avail)
|
||||
|
||||
install(TARGETS rocprofiler-systems-avail DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
File diff ditekan karena terlalu besar
Load Diff
@@ -0,0 +1,329 @@
|
||||
// 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 "defines.hpp"
|
||||
|
||||
#include <timemory/settings/macros.hpp>
|
||||
#include <timemory/tpls/cereal/archives.hpp>
|
||||
#include <timemory/tpls/cereal/cereal/external/base64.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if !defined(TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME)
|
||||
# define TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME epilogue
|
||||
#endif
|
||||
|
||||
#if !defined(TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME)
|
||||
# define TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME prologue
|
||||
#endif
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace cereal
|
||||
{
|
||||
class SettingsTextArchive
|
||||
: public OutputArchive<SettingsTextArchive>
|
||||
, public traits::TextArchive
|
||||
{
|
||||
public:
|
||||
using width_type = std::vector<uint64_t>;
|
||||
using value_type = std::string;
|
||||
using entry_type = std::map<std::string, value_type>;
|
||||
using array_type = std::vector<entry_type>;
|
||||
using unique_set = std::set<std::string>;
|
||||
using int_stack = std::stack<uint32_t>;
|
||||
|
||||
public:
|
||||
//! Construct, outputting to the provided stream
|
||||
/// \param stream The array of output data
|
||||
SettingsTextArchive(array_type& stream, unique_set exclude)
|
||||
: OutputArchive<SettingsTextArchive>(this)
|
||||
, output_stream(&stream)
|
||||
, exclude_stream(std::move(exclude))
|
||||
{
|
||||
name_counter.push(0);
|
||||
}
|
||||
|
||||
~SettingsTextArchive() override = default;
|
||||
|
||||
void saveBinaryValue(const void* data, size_t size, const char* name = nullptr)
|
||||
{
|
||||
setNextName(name);
|
||||
writeName();
|
||||
|
||||
auto base64string =
|
||||
base64::encode(reinterpret_cast<const unsigned char*>(data), size);
|
||||
saveValue(base64string);
|
||||
}
|
||||
|
||||
void startNode() { name_counter.push(0); }
|
||||
|
||||
void finishNode() { name_counter.pop(); }
|
||||
|
||||
//! Sets the name for the next node created with startNode
|
||||
void setNextName(const char* name)
|
||||
{
|
||||
if(exclude_stream.count(name) > 0) return;
|
||||
|
||||
if((current_entry != nullptr) && value_keys.count(name) > 0)
|
||||
{
|
||||
current_entry->insert({ name, "" });
|
||||
current_value = &((*current_entry)[name]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(value_keys.count(name) > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
current_value = nullptr;
|
||||
output_stream->push_back(entry_type{});
|
||||
current_entry = &(output_stream->back());
|
||||
|
||||
current_entry->insert({ "identifier", name });
|
||||
std::string func = name;
|
||||
const std::string prefix = TIMEMORY_SETTINGS_PREFIX;
|
||||
func = func.erase(0, prefix.length());
|
||||
std::transform(func.begin(), func.end(), func.begin(),
|
||||
[](char& c) { return tolower(c); });
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "settings::" << func << "()";
|
||||
current_entry->insert({ "static_accessor", ss.str() });
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "settings::instance()->get_" << func << "()";
|
||||
current_entry->insert({ "member_accessor", ss.str() });
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "settings." << func;
|
||||
current_entry->insert({ "python_accessor", ss.str() });
|
||||
}
|
||||
}
|
||||
|
||||
void setNextType(const char*) {}
|
||||
|
||||
public:
|
||||
template <typename Tp>
|
||||
inline void saveValue(Tp _val)
|
||||
{
|
||||
std::stringstream ssval;
|
||||
ssval << std::boolalpha << _val;
|
||||
if(current_value)
|
||||
{
|
||||
*current_value = ssval.str();
|
||||
}
|
||||
}
|
||||
|
||||
void writeName() {}
|
||||
|
||||
void makeArray() {}
|
||||
|
||||
private:
|
||||
value_type* current_value = nullptr;
|
||||
entry_type* current_entry = nullptr;
|
||||
array_type* output_stream = nullptr;
|
||||
unique_set exclude_stream = {};
|
||||
int_stack name_counter;
|
||||
unique_set value_keys = { "name", "value", "description", "count",
|
||||
"environ", "max_count", "cmdline", "data_type",
|
||||
"initial", "categories" };
|
||||
};
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// prologue and epilogue functions
|
||||
//
|
||||
//======================================================================================//
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Prologue for NVPs for settings archive
|
||||
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive&, const NameValuePair<T>&)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Epilogue for NVPs for settings archive
|
||||
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive&, const NameValuePair<T>&)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Prologue for deferred data for settings archive
|
||||
/*! Do nothing for the defer wrapper */
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive&, const DeferredData<T>&)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Epilogue for deferred for settings archive
|
||||
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive&, const DeferredData<T>&)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Prologue for SizeTags for settings archive
|
||||
/*! SizeTags are ignored */
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive& ar, const SizeTag<T>&)
|
||||
{
|
||||
ar.makeArray();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Epilogue for SizeTags for settings archive
|
||||
/*! SizeTags are ignored */
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive&, const SizeTag<T>&)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Prologue for all other types for settings archive
|
||||
/*! Starts a new node, named either automatically or by some NVP,
|
||||
that may be given data by the type about to be archived*/
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive& ar, const T&)
|
||||
{
|
||||
ar.startNode();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Epilogue for all other types other for settings archive
|
||||
/*! Finishes the node created in the prologue*/
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive& ar, const T&)
|
||||
{
|
||||
ar.finishNode();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Prologue for arithmetic types for settings archive
|
||||
inline void
|
||||
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive&, const std::nullptr_t&)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Epilogue for arithmetic types for settings archive
|
||||
inline void
|
||||
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive&, const std::nullptr_t&)
|
||||
{}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Common serialization functions
|
||||
//
|
||||
//======================================================================================//
|
||||
|
||||
//! Serializing NVP types
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive& ar, const NameValuePair<T>& t)
|
||||
{
|
||||
ar.setNextName(t.name);
|
||||
if(std::is_same<T, std::string>::value)
|
||||
{
|
||||
ar.setNextType("string");
|
||||
}
|
||||
else
|
||||
{
|
||||
ar.setNextType(tim::demangle<T>().c_str());
|
||||
}
|
||||
ar(t.value);
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename Alloc>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(
|
||||
SettingsTextArchive& ar,
|
||||
const NameValuePair<std::basic_string<CharT, Traits, Alloc>>& t)
|
||||
{
|
||||
ar.setNextName(t.name);
|
||||
ar.setNextType("string");
|
||||
ar(t.value);
|
||||
}
|
||||
|
||||
//! Saving for nullptr
|
||||
inline void
|
||||
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive&, const std::nullptr_t&)
|
||||
{}
|
||||
|
||||
//! Saving for arithmetic
|
||||
template <typename T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive& ar, const T& t)
|
||||
{
|
||||
if(std::is_same<T, std::string>::value) ar.setNextType("string");
|
||||
ar.saveValue(t);
|
||||
}
|
||||
|
||||
//! saving string
|
||||
template <typename CharT, typename Traits, typename Alloc>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive& ar,
|
||||
const std::basic_string<CharT, Traits, Alloc>& str)
|
||||
{
|
||||
ar.setNextType("string");
|
||||
ar.saveValue(str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//! Saving SizeTags
|
||||
template <typename T>
|
||||
inline void
|
||||
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive&, const SizeTag<T>&)
|
||||
{
|
||||
// nothing to do here, we don't explicitly save the size
|
||||
}
|
||||
|
||||
} // namespace cereal
|
||||
} // namespace tim
|
||||
|
||||
// register archives for polymorphic support
|
||||
TIMEMORY_CEREAL_REGISTER_ARCHIVE(SettingsTextArchive)
|
||||
@@ -0,0 +1,398 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "common.hpp"
|
||||
|
||||
#include <timemory/mpl/apply.hpp>
|
||||
#include <timemory/settings/settings.hpp>
|
||||
#include <timemory/variadic/macros.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
using settings = ::tim::settings;
|
||||
|
||||
std::string global_delim = std::string{ "|" };
|
||||
bool csv = false;
|
||||
bool markdown = false;
|
||||
bool alphabetical = false;
|
||||
bool available_only = false;
|
||||
bool all_info = false;
|
||||
bool force_brief = false;
|
||||
bool case_insensitive = false;
|
||||
bool regex_hl = false;
|
||||
bool expand_keys = false;
|
||||
bool force_config = false;
|
||||
bool print_advanced = false;
|
||||
int32_t max_width = 0;
|
||||
int32_t num_cols = 0;
|
||||
int32_t min_width = 40;
|
||||
int32_t padding = 4;
|
||||
str_vec_t regex_keys = {};
|
||||
str_vec_t category_regex_keys = {};
|
||||
str_set_t category_view = {};
|
||||
std::stringstream lerr{};
|
||||
|
||||
bool debug_msg = tim::get_env<bool>("ROCPROFSYS_DEBUG_AVAIL", settings::debug());
|
||||
int32_t verbose_level =
|
||||
tim::get_env<int32_t>("ROCPROFSYS_VERBOSE_AVAIL", settings::verbose());
|
||||
|
||||
// explicit setting names to exclude
|
||||
std::set<std::string> settings_exclude = {
|
||||
"ROCPROFSYS_ENVIRONMENT",
|
||||
"ROCPROFSYS_COMMAND_LINE",
|
||||
"cereal_class_version",
|
||||
"settings",
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
namespace
|
||||
{
|
||||
const auto&
|
||||
get_regex_constants()
|
||||
{
|
||||
static auto _constants = []() {
|
||||
auto _v = regex_const::egrep | regex_const::optimize;
|
||||
if(case_insensitive) _v |= regex_const::icase;
|
||||
return _v;
|
||||
}();
|
||||
return _constants;
|
||||
}
|
||||
|
||||
const auto&
|
||||
get_regex_pattern()
|
||||
{
|
||||
static auto _pattern = []() {
|
||||
std::array<std::string, 2> _v{};
|
||||
for(const auto& itr : regex_keys)
|
||||
{
|
||||
if(itr.empty()) continue;
|
||||
std::string _local_pattern = {};
|
||||
if(itr.at(0) == '~')
|
||||
{
|
||||
_local_pattern = itr.substr(1);
|
||||
_v.at(1) += "|" + _local_pattern;
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_pattern = itr;
|
||||
_v.at(0) += "|" + _local_pattern;
|
||||
}
|
||||
lerr << "Adding regex key: '" << _local_pattern << "'...\n";
|
||||
}
|
||||
for(auto& itr : _v)
|
||||
if(!itr.empty()) itr = itr.substr(1);
|
||||
|
||||
return _v;
|
||||
}();
|
||||
return _pattern;
|
||||
}
|
||||
|
||||
auto
|
||||
get_regex()
|
||||
{
|
||||
static auto _rc = std::array<std::regex, 2>{
|
||||
std::regex(get_regex_pattern().at(0), get_regex_constants()),
|
||||
std::regex(get_regex_pattern().at(1), get_regex_constants())
|
||||
};
|
||||
return _rc;
|
||||
}
|
||||
|
||||
bool
|
||||
regex_match(const std::string& _line)
|
||||
{
|
||||
if(get_regex_pattern().at(0).empty() && get_regex_pattern().at(1).empty())
|
||||
return true;
|
||||
|
||||
static size_t lerr_width = 0;
|
||||
lerr_width = std::max<size_t>(lerr_width, _line.length());
|
||||
std::stringstream _line_ss;
|
||||
_line_ss << "'" << _line << "'";
|
||||
|
||||
if(!get_regex_pattern().at(1).empty())
|
||||
{
|
||||
if(std::regex_match(_line, get_regex().at(1)))
|
||||
{
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " matched negating pattern '" << get_regex_pattern().at(1)
|
||||
<< "'...\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(std::regex_search(_line, get_regex().at(1)))
|
||||
{
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " found negating pattern '" << get_regex_pattern().at(1) << "'...\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!get_regex_pattern().at(0).empty())
|
||||
{
|
||||
if(std::regex_match(_line, get_regex().at(0)))
|
||||
{
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " matched pattern '" << get_regex_pattern().at(0) << "'...\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
if(std::regex_search(_line, get_regex().at(0)))
|
||||
{
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " found pattern '" << get_regex_pattern().at(0) << "'...\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str() << " missing pattern '"
|
||||
<< get_regex_pattern().at(0) << "'...\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string
|
||||
regex_replace(const std::string& _line)
|
||||
{
|
||||
#if defined(TIMEMORY_UNIX)
|
||||
if(get_regex_pattern().empty()) return _line;
|
||||
if(regex_match(_line))
|
||||
return std::regex_replace(_line, get_regex().at(0), "\33[01;04;36;40m$&\33[0m");
|
||||
#endif
|
||||
return _line;
|
||||
}
|
||||
|
||||
const auto&
|
||||
get_category_regex_pattern()
|
||||
{
|
||||
static auto _pattern = []() {
|
||||
std::array<std::string, 2> _v{};
|
||||
for(const auto& itr : category_regex_keys)
|
||||
{
|
||||
if(itr.empty()) continue;
|
||||
std::string _local_pattern = {};
|
||||
if(itr.at(0) == '~')
|
||||
{
|
||||
_local_pattern = itr.substr(1);
|
||||
_v.at(1) += "|" + _local_pattern;
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_pattern = itr;
|
||||
_v.at(0) += "|" + _local_pattern;
|
||||
}
|
||||
lerr << "Adding category regex key: '" << _local_pattern << "'...\n";
|
||||
}
|
||||
for(auto& itr : _v)
|
||||
if(!itr.empty()) itr = itr.substr(1);
|
||||
|
||||
return _v;
|
||||
}();
|
||||
return _pattern;
|
||||
}
|
||||
|
||||
auto
|
||||
get_category_regex()
|
||||
{
|
||||
static auto _rc = std::array<std::regex, 2>{
|
||||
std::regex(get_category_regex_pattern().at(0), get_regex_constants()),
|
||||
std::regex(get_category_regex_pattern().at(1), get_regex_constants())
|
||||
};
|
||||
return _rc;
|
||||
}
|
||||
|
||||
bool
|
||||
category_regex_match(const std::string& _line)
|
||||
{
|
||||
if(get_category_regex_pattern().at(0).empty() &&
|
||||
get_category_regex_pattern().at(1).empty())
|
||||
return true;
|
||||
|
||||
static size_t lerr_width = 0;
|
||||
lerr_width = std::max<size_t>(lerr_width, _line.length());
|
||||
std::stringstream _line_ss;
|
||||
_line_ss << "'" << _line << "'";
|
||||
|
||||
if(!get_category_regex_pattern().at(1).empty())
|
||||
{
|
||||
if(std::regex_match(_line, get_category_regex().at(1)))
|
||||
{
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " matched negating category pattern '"
|
||||
<< get_category_regex_pattern().at(1) << "'...\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(std::regex_search(_line, get_category_regex().at(1)))
|
||||
{
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " found negating category pattern '"
|
||||
<< get_category_regex_pattern().at(1) << "'...\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!get_category_regex_pattern().at(0).empty())
|
||||
{
|
||||
if(std::regex_match(_line, get_category_regex().at(0)))
|
||||
{
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " matched category pattern '" << get_category_regex_pattern().at(0)
|
||||
<< "'...\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
if(std::regex_search(_line, get_category_regex().at(0)))
|
||||
{
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " found category pattern '" << get_category_regex_pattern().at(0)
|
||||
<< "'...\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
lerr << std::left << std::setw(lerr_width) << _line_ss.str()
|
||||
<< " missing category pattern '" << get_category_regex_pattern().at(0)
|
||||
<< "'...\n";
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
bool
|
||||
is_selected(const std::string& _line)
|
||||
{
|
||||
return regex_match(_line);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
bool
|
||||
is_category_selected(const std::string& _line)
|
||||
{
|
||||
return category_regex_match(_line);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
std::string
|
||||
hl_selected(const std::string& _line)
|
||||
{
|
||||
return (regex_hl) ? regex_replace(_line) : _line;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
void
|
||||
process_categories(parser_t& p, const str_set_t& _category_options)
|
||||
{
|
||||
category_view = p.get<str_set_t>("categories");
|
||||
std::vector<std::function<void()>> _shorthand_patches{};
|
||||
for(const auto& itr : category_view)
|
||||
{
|
||||
auto _is_shorthand = [&_shorthand_patches, &_category_options,
|
||||
itr](const std::string& _prefix) {
|
||||
auto _opt = TIMEMORY_JOIN("::", _prefix, itr);
|
||||
if(_category_options.count(_opt) > 0)
|
||||
{
|
||||
_shorthand_patches.emplace_back([itr, _opt]() {
|
||||
category_view.erase(itr);
|
||||
category_view.emplace(_opt);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if(_category_options.count(itr) == 0)
|
||||
{
|
||||
if(!_is_shorthand("component") && !_is_shorthand("settings") &&
|
||||
!_is_shorthand("hw_counters"))
|
||||
throw std::runtime_error(
|
||||
itr + " is not a valid category. Use --list-categories to view "
|
||||
"valid categories");
|
||||
}
|
||||
}
|
||||
for(auto&& itr : _shorthand_patches)
|
||||
itr();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
bool
|
||||
exclude_setting(const std::string& _v)
|
||||
{
|
||||
if(settings_exclude.find(_v) != settings_exclude.end()) return true;
|
||||
auto itr = settings::instance()->find(_v, false);
|
||||
if(itr == settings::instance()->end()) return true;
|
||||
return itr->second->get_hidden();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
void
|
||||
dump_log()
|
||||
{
|
||||
if(debug_msg)
|
||||
{
|
||||
std::cerr << lerr.str() << std::flush;
|
||||
lerr = std::stringstream{};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dump_log_abort(int _v)
|
||||
{
|
||||
fprintf(stderr, "\n[rocprof-sys-avail] Exiting with signal %i...\n", _v);
|
||||
debug_msg = true;
|
||||
dump_log();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
std::string
|
||||
remove(std::string inp, const std::set<std::string>& entries)
|
||||
{
|
||||
for(const auto& itr : entries)
|
||||
{
|
||||
auto idx = inp.find(itr);
|
||||
while(idx != std::string::npos)
|
||||
{
|
||||
inp.erase(idx, itr.length());
|
||||
idx = inp.find(itr);
|
||||
}
|
||||
}
|
||||
return inp;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
bool
|
||||
file_exists(const std::string& _fname)
|
||||
{
|
||||
struct stat _buffer;
|
||||
if(stat(_fname.c_str(), &_buffer) == 0)
|
||||
return (S_ISREG(_buffer.st_mode) != 0 || S_ISLNK(_buffer.st_mode) != 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
@@ -0,0 +1,198 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "defines.hpp"
|
||||
|
||||
#include <timemory/components/types.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/settings/types.hpp>
|
||||
#include <timemory/utility/argparse.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/type_list.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// namespaces
|
||||
|
||||
namespace regex_const = ::std::regex_constants; // NOLINT
|
||||
namespace comp = ::tim::component; // NOLINT
|
||||
using settings = ::tim::settings; // NOLINT
|
||||
using tim::demangle; // NOLINT
|
||||
using tim::type_list; // NOLINT
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// aliases
|
||||
|
||||
template <typename Tp, size_t N>
|
||||
using array_t = ::std::array<Tp, N>;
|
||||
using string_t = ::std::string;
|
||||
using stringstream_t = ::std::stringstream;
|
||||
using str_vec_t = ::std::vector<string_t>;
|
||||
using str_set_t = ::std::set<string_t>;
|
||||
using info_type_base = ::std::tuple<string_t, bool, str_vec_t>;
|
||||
using parser_t = ::tim::argparse::argument_parser;
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// enums
|
||||
|
||||
enum : int
|
||||
{
|
||||
VAL = 0,
|
||||
ENUM = 1,
|
||||
LANG = 2,
|
||||
CID = 3,
|
||||
FNAME = 4,
|
||||
DESC = 5,
|
||||
CATEGORY = 6,
|
||||
TOTAL = 7
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// variables
|
||||
|
||||
constexpr size_t num_component_options = 7;
|
||||
constexpr size_t num_settings_options = 4;
|
||||
constexpr size_t num_hw_counter_options = 5;
|
||||
constexpr size_t num_dump_config_options = TOTAL;
|
||||
|
||||
extern std::string global_delim;
|
||||
extern bool csv;
|
||||
extern bool markdown;
|
||||
extern bool alphabetical;
|
||||
extern bool available_only;
|
||||
extern bool all_info;
|
||||
extern bool force_brief;
|
||||
extern bool debug_msg;
|
||||
extern bool case_insensitive;
|
||||
extern bool regex_hl;
|
||||
extern bool expand_keys;
|
||||
extern bool force_config;
|
||||
extern bool print_advanced;
|
||||
extern int32_t max_width;
|
||||
extern int32_t num_cols;
|
||||
extern int32_t min_width;
|
||||
extern int32_t padding;
|
||||
extern int32_t verbose_level;
|
||||
extern str_vec_t regex_keys;
|
||||
extern str_vec_t category_regex_keys;
|
||||
extern str_set_t category_view;
|
||||
extern std::stringstream lerr;
|
||||
|
||||
// explicit setting names to exclude
|
||||
extern std::set<std::string> settings_exclude;
|
||||
|
||||
// exclude some timemory settings which are not relevant to rocprof-sys
|
||||
// exact matches, e.g. ROCPROFSYS_BANNER
|
||||
extern std::string settings_rexclude_exact;
|
||||
|
||||
// leading matches, e.g. ROCPROFSYS_MPI_[A-Z_]+
|
||||
extern std::string settings_rexclude_begin;
|
||||
|
||||
constexpr size_t max_error_message_buffer_length = 4096;
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// functions
|
||||
|
||||
bool
|
||||
is_selected(const std::string& line);
|
||||
|
||||
bool
|
||||
is_category_selected(const std::string& _line);
|
||||
|
||||
std::string
|
||||
hl_selected(const std::string& line);
|
||||
|
||||
void
|
||||
process_categories(parser_t&, const str_set_t&);
|
||||
|
||||
bool
|
||||
exclude_setting(const std::string&);
|
||||
|
||||
void
|
||||
dump_log();
|
||||
|
||||
void
|
||||
dump_log_abort(int _v);
|
||||
|
||||
std::string
|
||||
remove(std::string inp, const std::set<std::string>& entries);
|
||||
|
||||
bool
|
||||
file_exists(const std::string&);
|
||||
|
||||
// control debug printf statements
|
||||
#define errprintf(LEVEL, ...) \
|
||||
{ \
|
||||
if(LEVEL < verbose_level) \
|
||||
{ \
|
||||
if(debug_msg || verbose_level >= LEVEL) \
|
||||
{ \
|
||||
fprintf(stderr, "%s", tim::log::color::fatal()); \
|
||||
fprintf(stderr, "[rocprof-sys][avail] Error! " __VA_ARGS__); \
|
||||
fprintf(stderr, "%s", tim::log::color::end()); \
|
||||
} \
|
||||
char _buff[max_error_message_buffer_length]; \
|
||||
snprintf(_buff, max_error_message_buffer_length, \
|
||||
"[rocprof-sys][avail] Error! " __VA_ARGS__); \
|
||||
throw std::runtime_error(std::string{ _buff }); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if(debug_msg || verbose_level >= LEVEL) \
|
||||
{ \
|
||||
fprintf(stderr, "%s", tim::log::color::warning()); \
|
||||
fprintf(stderr, "[rocprof-sys][avail] Warning! " __VA_ARGS__); \
|
||||
fprintf(stderr, "%s", tim::log::color::end()); \
|
||||
} \
|
||||
} \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
// control verbose printf statements
|
||||
#define verbprintf(LEVEL, ...) \
|
||||
{ \
|
||||
if(debug_msg || verbose_level >= LEVEL) \
|
||||
{ \
|
||||
fprintf(stderr, "%s", tim::log::color::info()); \
|
||||
fprintf(stderr, "[rocprof-sys][avail] " __VA_ARGS__); \
|
||||
fprintf(stderr, "%s", tim::log::color::end()); \
|
||||
} \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
#define verbprintf_bare(LEVEL, ...) \
|
||||
{ \
|
||||
if(debug_msg || verbose_level >= LEVEL) \
|
||||
{ \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} \
|
||||
fflush(stderr); \
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "defines.hpp"
|
||||
|
||||
#include <timemory/components/types.hpp>
|
||||
#include <timemory/enum.h>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
template <typename Type = void>
|
||||
struct component_categories;
|
||||
|
||||
template <typename Type>
|
||||
struct component_categories
|
||||
{
|
||||
template <typename... Tp>
|
||||
void operator()(std::set<std::string>& _v, type_list<Tp...>) const
|
||||
{
|
||||
//
|
||||
auto _cleanup = [](std::string _type, const std::string& _pattern) {
|
||||
auto _pos = std::string::npos;
|
||||
while((_pos = _type.find(_pattern)) != std::string::npos)
|
||||
_type = _type.erase(_pos, _pattern.length());
|
||||
return _type;
|
||||
};
|
||||
(void) _cleanup; // unused but set if sizeof...(Tp) == 0
|
||||
|
||||
TIMEMORY_FOLD_EXPRESSION(_v.emplace(TIMEMORY_JOIN(
|
||||
"::", "component", _cleanup(tim::try_demangle<Tp>(), "tim::"))));
|
||||
}
|
||||
|
||||
void operator()(std::set<std::string>& _v) const
|
||||
{
|
||||
if constexpr(!tim::concepts::is_placeholder<Type>::value)
|
||||
(*this)(_v, tim::trait::component_apis_t<Type>{});
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct component_categories<void>
|
||||
{
|
||||
template <size_t... Idx>
|
||||
void operator()(std::set<std::string>& _v, std::index_sequence<Idx...>) const
|
||||
{
|
||||
TIMEMORY_FOLD_EXPRESSION(component_categories<comp::enumerator_t<Idx>>{}(_v));
|
||||
}
|
||||
|
||||
void operator()(std::set<std::string>& _v) const
|
||||
{
|
||||
(*this)(_v, std::make_index_sequence<TIMEMORY_COMPONENTS_END>{});
|
||||
}
|
||||
|
||||
auto operator()() const
|
||||
{
|
||||
std::set<std::string> _categories{};
|
||||
(*this)(_categories);
|
||||
return _categories;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define TIMEMORY_DISABLE_BANNER
|
||||
#define TIMEMORY_DISABLE_COMPONENT_STORAGE_INIT
|
||||
|
||||
#include "common/defines.h"
|
||||
#include "core/config.hpp"
|
||||
#include "core/defines.hpp"
|
||||
@@ -0,0 +1,53 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "defines.hpp"
|
||||
|
||||
#include <timemory/components/properties.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/utility/type_list.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
template <typename T, typename I>
|
||||
struct enumerated_list;
|
||||
|
||||
template <template <typename...> class TupT, typename... T>
|
||||
struct enumerated_list<TupT<T...>, std::index_sequence<>>
|
||||
{
|
||||
using type = type_list<T...>;
|
||||
};
|
||||
|
||||
template <template <typename...> class TupT, size_t I, typename... T, size_t... Idx>
|
||||
struct enumerated_list<TupT<T...>, std::index_sequence<I, Idx...>>
|
||||
{
|
||||
using Tp = tim::component::enumerator_t<I>;
|
||||
static constexpr bool is_nothing = tim::concepts::is_placeholder<Tp>::value;
|
||||
using type = typename enumerated_list<
|
||||
std::conditional_t<is_nothing, type_list<T...>, type_list<T..., Tp>>,
|
||||
std::index_sequence<Idx...>>::type;
|
||||
};
|
||||
@@ -0,0 +1,473 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "generate_config.hpp"
|
||||
#include "common.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "info_type.hpp"
|
||||
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/policy.hpp>
|
||||
#include <timemory/settings.hpp>
|
||||
#include <timemory/settings/types.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/cereal/archives/xml.hpp>
|
||||
#include <timemory/tpls/cereal/cereal/cereal.hpp>
|
||||
#include <timemory/utility/filepath.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace cereal = ::tim::cereal;
|
||||
namespace filepath = ::tim::filepath;
|
||||
using settings = ::tim::settings;
|
||||
using ::tim::tsettings;
|
||||
using ::tim::type_list;
|
||||
using ::tim::policy::output_archive;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct custom_setting_serializer
|
||||
{
|
||||
static std::array<bool, TOTAL> options;
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
ignore_setting(const Tp& _v)
|
||||
{
|
||||
if(_v->get_hidden()) return true;
|
||||
if(exclude_setting(_v->get_env_name())) return true;
|
||||
if(_v->get_config_updated() || _v->get_environ_updated()) return false;
|
||||
if(!is_selected(_v->get_env_name()) && !is_selected(_v->get_name())) return true;
|
||||
if(available_only && !_v->get_enabled()) return true;
|
||||
if(!category_view.empty())
|
||||
{
|
||||
bool _found = false;
|
||||
for(auto& itr : _v->get_categories())
|
||||
{
|
||||
if(category_view.count(itr) > 0 ||
|
||||
category_view.count(TIMEMORY_JOIN("::", "settings", itr)) > 0)
|
||||
{
|
||||
_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!_found) return true;
|
||||
}
|
||||
if(category_view.count("deprecated") == 0 &&
|
||||
category_view.count("settings::deprecated") == 0 &&
|
||||
_v->get_categories().count("deprecated") > 0)
|
||||
return true;
|
||||
if(!print_advanced && category_view.count("advanced") == 0 &&
|
||||
category_view.count("settings::advanced") == 0 &&
|
||||
_v->get_categories().count("advanced") > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::array<bool, TOTAL> custom_setting_serializer::options = { false };
|
||||
|
||||
namespace tim
|
||||
{
|
||||
namespace operation
|
||||
{
|
||||
template <typename Tp>
|
||||
struct setting_serialization<Tp, custom_setting_serializer>
|
||||
{
|
||||
template <typename ArchiveT>
|
||||
void operator()(ArchiveT&, const char*, const Tp&) const
|
||||
{}
|
||||
|
||||
template <typename ArchiveT>
|
||||
void operator()(ArchiveT&, const char*, Tp&&) const
|
||||
{}
|
||||
};
|
||||
//
|
||||
template <typename Tp>
|
||||
struct setting_serialization<tsettings<Tp>, custom_setting_serializer>
|
||||
{
|
||||
using value_type = tsettings<Tp>;
|
||||
|
||||
template <typename ArchiveT>
|
||||
void operator()(ArchiveT& _ar, value_type& _val) const
|
||||
{
|
||||
static_assert(concepts::is_output_archive<ArchiveT>::value,
|
||||
"Requires an output archive");
|
||||
|
||||
if(ignore_setting(&_val)) return;
|
||||
|
||||
auto _save = std::shared_ptr<value_type>{};
|
||||
if constexpr(concepts::is_string_type<Tp>::value)
|
||||
{
|
||||
if(_val.get_name() != "time_format")
|
||||
_val.set(settings::format(_val.get(), settings::instance()->get_tag()));
|
||||
if(_val.get_name() == "config_file")
|
||||
{
|
||||
_save = std::make_shared<value_type>(_val);
|
||||
_val.set(Tp{});
|
||||
}
|
||||
}
|
||||
|
||||
if(all_info)
|
||||
{
|
||||
_ar(cereal::make_nvp(_val.get_env_name().c_str(), _val));
|
||||
}
|
||||
else
|
||||
{
|
||||
Tp _v = _val.get();
|
||||
_ar.setNextName(_val.get_env_name().c_str());
|
||||
_ar.startNode();
|
||||
_ar(cereal::make_nvp("name", _val.get_name()));
|
||||
_ar(cereal::make_nvp("value", _v));
|
||||
if(custom_setting_serializer::options[DESC])
|
||||
_ar(cereal::make_nvp("description", _val.get_description()));
|
||||
if(custom_setting_serializer::options[CATEGORY])
|
||||
_ar(cereal::make_nvp("description", _val.get_categories()));
|
||||
if(custom_setting_serializer::options[VAL])
|
||||
_ar(cereal::make_nvp("choices", _val.get_choices()));
|
||||
_ar.finishNode();
|
||||
}
|
||||
|
||||
if(_save) _val.set(_save->get());
|
||||
}
|
||||
};
|
||||
} // namespace operation
|
||||
} // namespace tim
|
||||
|
||||
template <typename... Tp>
|
||||
void
|
||||
push(type_list<Tp...>)
|
||||
{
|
||||
ROCPROFSYS_FOLD_EXPRESSION(
|
||||
settings::push_serialize_map_callback<Tp, custom_setting_serializer>());
|
||||
ROCPROFSYS_FOLD_EXPRESSION(
|
||||
settings::push_serialize_data_callback<Tp, custom_setting_serializer>(
|
||||
type_list<std::string>{}));
|
||||
}
|
||||
|
||||
template <typename... Tp>
|
||||
void
|
||||
pop(type_list<Tp...>)
|
||||
{
|
||||
ROCPROFSYS_FOLD_EXPRESSION(
|
||||
settings::pop_serialize_map_callback<Tp, custom_setting_serializer>());
|
||||
ROCPROFSYS_FOLD_EXPRESSION(
|
||||
settings::pop_serialize_data_callback<Tp, custom_setting_serializer>(
|
||||
type_list<std::string>{}));
|
||||
}
|
||||
|
||||
void
|
||||
update_choices(const std::shared_ptr<settings>&);
|
||||
|
||||
void
|
||||
generate_config(std::string _config_file, const std::set<std::string>& _config_fmts,
|
||||
const std::array<bool, TOTAL>& _options)
|
||||
{
|
||||
custom_setting_serializer::options = _options;
|
||||
|
||||
auto _settings = tim::settings::shared_instance();
|
||||
tim::settings::push();
|
||||
_settings->find("suppress_config")->second->reset();
|
||||
_settings->find("suppress_parsing")->second->reset();
|
||||
|
||||
_config_file = settings::format(_config_file, _settings->get_tag());
|
||||
bool _absolute = _config_file.at(0) == '/';
|
||||
auto _dirs = tim::delimit(_config_file, "/\\/");
|
||||
_config_file = _dirs.back();
|
||||
_dirs.pop_back();
|
||||
|
||||
std::string _output_dir = ".";
|
||||
if(!_dirs.empty() && !(_dirs.size() == 1 && _dirs.at(0) == "."))
|
||||
{
|
||||
_output_dir = std::string{ (_absolute) ? "/" : "" } + _dirs.front();
|
||||
_dirs.erase(_dirs.begin());
|
||||
for(const auto& itr : _dirs)
|
||||
_output_dir = TIMEMORY_JOIN('/', _output_dir, itr);
|
||||
}
|
||||
_output_dir += "/";
|
||||
|
||||
auto _fmts = std::set<std::string>{};
|
||||
std::string _txt_ext = ".cfg";
|
||||
for(std::string itr : { ".cfg", ".txt", ".json", ".xml" })
|
||||
{
|
||||
if(_config_file.length() <= itr.length()) continue;
|
||||
auto _pos = _config_file.rfind(itr);
|
||||
if(_pos == _config_file.length() - itr.length())
|
||||
{
|
||||
if(itr == ".cfg" || itr == ".txt") _txt_ext = itr;
|
||||
_fmts.emplace(itr.substr(1));
|
||||
_config_file = _config_file.substr(0, _pos);
|
||||
}
|
||||
}
|
||||
|
||||
if(_fmts.empty() && _config_fmts.size() == 1)
|
||||
_fmts = _config_fmts;
|
||||
else if(!_fmts.empty())
|
||||
{
|
||||
for(auto& itr : _config_fmts)
|
||||
_fmts.emplace(itr);
|
||||
}
|
||||
|
||||
update_choices(_settings);
|
||||
|
||||
using json_t = cereal::PrettyJSONOutputArchive;
|
||||
using xml_t = cereal::XMLOutputArchive;
|
||||
|
||||
// stores the original serializer and replaces it with the custom one
|
||||
push(type_list<json_t, xml_t>{});
|
||||
|
||||
static std::time_t _time{ std::time(nullptr) };
|
||||
|
||||
auto _serialize = [_settings](auto&& _ar) {
|
||||
_ar->setNextName(TIMEMORY_PROJECT_NAME);
|
||||
_ar->startNode();
|
||||
(*_ar)(cereal::make_nvp("version", std::string{ ROCPROFSYS_VERSION_STRING }));
|
||||
(*_ar)(cereal::make_nvp("date", tim::get_local_datetime("%F_%H.%M", &_time)));
|
||||
settings::serialize_settings(*_ar, *_settings);
|
||||
_ar->finishNode();
|
||||
};
|
||||
|
||||
auto _nout = 0;
|
||||
auto _open = [&_nout](std::ofstream& _ofs, const std::string& _fname,
|
||||
const std::string& _type) -> std::ofstream& {
|
||||
++_nout;
|
||||
if(file_exists(_fname))
|
||||
{
|
||||
if(force_config)
|
||||
{
|
||||
if(settings::verbose() >= 1)
|
||||
std::cout << "[rocprof-sys-avail] File '" << _fname
|
||||
<< "' exists. Overwrite force...\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "[rocprof-sys-avail] File '" << _fname
|
||||
<< "' exists. Overwrite? " << std::flush;
|
||||
std::string _response = {};
|
||||
std::cin >> _response;
|
||||
if(!tim::get_bool(_response, false)) std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if(filepath::open(_ofs, _fname))
|
||||
{
|
||||
if(settings::verbose() >= 0)
|
||||
printf("[rocprof-sys-avail] Outputting %s configuration file '%s'...\n",
|
||||
_type.c_str(), _fname.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(
|
||||
TIMEMORY_JOIN(" ", "Error opening", _type, "output file:", _fname));
|
||||
}
|
||||
return _ofs;
|
||||
};
|
||||
|
||||
if(_fmts.count("json") > 0)
|
||||
{
|
||||
std::stringstream _ss{};
|
||||
output_archive<cereal::PrettyJSONOutputArchive>::indent_length() = 4;
|
||||
_serialize(output_archive<cereal::PrettyJSONOutputArchive>::get(_ss));
|
||||
auto _fname = settings::compose_output_filename(_config_file, ".json", false, -1,
|
||||
true, _output_dir);
|
||||
std::ofstream ofs{};
|
||||
_open(ofs, _fname, "JSON") << _ss.str() << "\n";
|
||||
}
|
||||
|
||||
if(_fmts.count("xml") > 0)
|
||||
{
|
||||
std::stringstream _ss{};
|
||||
output_archive<cereal::XMLOutputArchive>::indent() = true;
|
||||
_serialize(output_archive<cereal::XMLOutputArchive>::get(_ss));
|
||||
auto _fname = settings::compose_output_filename(_config_file, ".xml", false, -1,
|
||||
true, _output_dir);
|
||||
std::ofstream ofs{};
|
||||
_open(ofs, _fname, "XML") << _ss.str() << "\n";
|
||||
}
|
||||
|
||||
if(_fmts.count("txt") > 0 || _fmts.count("cfg") > 0 || _nout == 0)
|
||||
{
|
||||
std::stringstream _ss{};
|
||||
size_t _w = min_width;
|
||||
|
||||
std::vector<std::shared_ptr<tim::vsettings>> _data{};
|
||||
for(const auto& itr : *_settings)
|
||||
{
|
||||
if(exclude_setting(itr.second->get_env_name())) continue;
|
||||
for(const auto& citr : itr.second->get_categories())
|
||||
if(citr == "deprecated") continue;
|
||||
if(ignore_setting(itr.second)) continue;
|
||||
_data.emplace_back(itr.second);
|
||||
}
|
||||
|
||||
if(alphabetical)
|
||||
std::sort(_data.begin(), _data.end(), [](auto _lhs, auto _rhs) {
|
||||
return _lhs->get_name() < _rhs->get_name();
|
||||
});
|
||||
else
|
||||
{
|
||||
_settings->ordering();
|
||||
std::sort(_data.begin(), _data.end(), [](auto _lhs, auto _rhs) {
|
||||
auto _lomni = _lhs->get_categories().count("rocprofsys") > 0;
|
||||
auto _romni = _rhs->get_categories().count("rocprofsys") > 0;
|
||||
if(_lomni && !_romni) return true;
|
||||
if(_romni && !_lomni) return false;
|
||||
for(const auto* itr :
|
||||
{ "ROCPROFSYS_CONFIG", "ROCPROFSYS_MODE", "ROCPROFSYS_TRACE",
|
||||
"ROCPROFSYS_PROFILE", "ROCPROFSYS_USE_SAMPLING",
|
||||
"ROCPROFSYS_USE_PROCESS_SAMPLING", "ROCPROFSYS_USE_ROCM",
|
||||
"ROCPROFSYS_USE_AMD_SMI", "ROCPROFSYS_USE_KOKKOSP",
|
||||
"ROCPROFSYS_USE_OMPT", "ROCPROFSYS_USE", "ROCPROFSYS_OUTPUT" })
|
||||
{
|
||||
if(_lhs->get_env_name().find(itr) == 0 &&
|
||||
_rhs->get_env_name().find(itr) != 0)
|
||||
return true;
|
||||
if(_rhs->get_env_name().find(itr) == 0 &&
|
||||
_lhs->get_env_name().find(itr) != 0)
|
||||
return false;
|
||||
}
|
||||
for(const auto* itr :
|
||||
{ "ROCPROFSYS_SUPPRESS_PARSING", "ROCPROFSYS_SUPPRESS_CONFIG" })
|
||||
{
|
||||
if(_lhs->get_env_name().find(itr) == 0 &&
|
||||
_rhs->get_env_name().find(itr) != 0)
|
||||
return false;
|
||||
if(_rhs->get_env_name().find(itr) == 0 &&
|
||||
_lhs->get_env_name().find(itr) != 0)
|
||||
return true;
|
||||
}
|
||||
return _lhs->get_name() < _rhs->get_name();
|
||||
});
|
||||
}
|
||||
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
_w = std::max(_w, itr->get_env_name().length());
|
||||
}
|
||||
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
if(exclude_setting(itr->get_env_name())) continue;
|
||||
|
||||
auto _has_info =
|
||||
(all_info || _options[DESC] || _options[CATEGORY] || _options[VAL]);
|
||||
|
||||
if(_has_info) _ss << "\n# name:\n# " << itr->get_name() << "\n#\n";
|
||||
|
||||
if(_options[DESC] || all_info)
|
||||
{
|
||||
_ss << "# description:\n";
|
||||
auto _desc = tim::delimit(itr->get_description(), " \n");
|
||||
std::stringstream _line{};
|
||||
_line << "# ";
|
||||
auto _write = [&_line, &_ss, _w](std::string_view _str) {
|
||||
if(_line.str().length() + _str.length() + 1 >= _w)
|
||||
{
|
||||
_ss << _line.str() << "\n";
|
||||
_line = std::stringstream{};
|
||||
_line << "# ";
|
||||
}
|
||||
_line << " " << _str;
|
||||
};
|
||||
for(auto& iitr : _desc)
|
||||
_write(iitr);
|
||||
_ss << _line.str() << "\n#\n";
|
||||
}
|
||||
if(_options[CATEGORY] || all_info)
|
||||
{
|
||||
_ss << "# categories:\n";
|
||||
for(const auto& iitr : itr->get_categories())
|
||||
_ss << "# " << iitr << "\n";
|
||||
_ss << "#\n";
|
||||
}
|
||||
if((_options[VAL] || all_info) && !itr->get_choices().empty())
|
||||
{
|
||||
_ss << "# choices:\n";
|
||||
for(const auto& iitr : itr->get_choices())
|
||||
_ss << "# " << iitr << "\n";
|
||||
_ss << "#\n";
|
||||
}
|
||||
if(_has_info) _ss << "\n";
|
||||
_ss << std::left << std::setw(_w + 10) << itr->get_env_name() << " = ";
|
||||
auto _v = itr->as_string();
|
||||
if(itr->get_name() == "config_file") _v = {};
|
||||
if(!_v.empty() && expand_keys && itr->get_name() != "time_format")
|
||||
_v = settings::format(_v, _settings->get_tag());
|
||||
_ss << _v << "\n";
|
||||
}
|
||||
auto _fname = settings::compose_output_filename(_config_file, _txt_ext, false, -1,
|
||||
true, _output_dir);
|
||||
std::ofstream ofs{};
|
||||
_open(ofs, _fname, "text")
|
||||
<< "# auto-generated by rocprof-sys-avail (version "
|
||||
<< ROCPROFSYS_VERSION_STRING << ") on "
|
||||
<< tim::get_local_datetime("%F @ %H:%M", &_time) << "\n\n"
|
||||
<< _ss.str();
|
||||
}
|
||||
|
||||
// restores the original serializer
|
||||
pop(type_list<json_t, xml_t>{});
|
||||
|
||||
tim::settings::pop();
|
||||
}
|
||||
|
||||
void
|
||||
update_choices(const std::shared_ptr<settings>& _settings)
|
||||
{
|
||||
std::vector<info_type> _info = get_component_info<TIMEMORY_NATIVE_COMPONENTS_END>();
|
||||
|
||||
if(_settings->get_verbose() >= 2 || _settings->get_debug())
|
||||
printf("[rocprof-sys-avail] # of component found: %zu\n", _info.size());
|
||||
|
||||
_info.erase(std::remove_if(_info.begin(), _info.end(),
|
||||
[](const auto& itr) {
|
||||
if(!itr.is_available()) return true;
|
||||
// NOLINTNEXTLINE
|
||||
for(const auto& nitr :
|
||||
{ "cuda", "cupti", "nvtx", "roofline", "_bundle",
|
||||
"data_integer", "data_unsigned", "data_floating",
|
||||
"printer" })
|
||||
{
|
||||
if(itr.name().find(nitr) != std::string::npos)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
_info.end());
|
||||
|
||||
std::vector<std::string> _component_choices = {};
|
||||
_component_choices.reserve(_info.size());
|
||||
for(const auto& itr : _info)
|
||||
_component_choices.emplace_back(itr.id_type());
|
||||
if(_settings->get_verbose() >= 2 || _settings->get_debug())
|
||||
printf("[rocprof-sys-avail] # of component choices: %zu\n",
|
||||
_component_choices.size());
|
||||
_settings->find("ROCPROFSYS_TIMEMORY_COMPONENTS")
|
||||
->second->set_choices(_component_choices);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
void
|
||||
generate_config(std::string _config_file, const std::set<std::string>& _config_fmts,
|
||||
const std::array<bool, TOTAL>&);
|
||||
@@ -0,0 +1,208 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "get_categories.hpp"
|
||||
#include "info_type.hpp"
|
||||
|
||||
#include <timemory/components/metadata.hpp>
|
||||
#include <timemory/components/properties.hpp>
|
||||
#include <timemory/defines.h>
|
||||
#include <timemory/enum.h>
|
||||
#include <timemory/mpl/type_traits.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/type_list.hpp>
|
||||
#include <timemory/variadic/macros.hpp>
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
struct unknown
|
||||
{};
|
||||
|
||||
template <typename T, typename U = typename T::value_type>
|
||||
constexpr bool
|
||||
available_value_type_alias(int)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U = unknown>
|
||||
constexpr bool
|
||||
available_value_type_alias(long)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Type, bool>
|
||||
struct component_value_type;
|
||||
|
||||
template <typename Type>
|
||||
struct component_value_type<Type, true>
|
||||
{
|
||||
using type = typename Type::value_type;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct component_value_type<Type, false>
|
||||
{
|
||||
using type = unknown;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
using component_value_type_t =
|
||||
typename component_value_type<Type, available_value_type_alias<Type>(0)>::type;
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename Type = void>
|
||||
struct get_availability;
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename Type>
|
||||
struct get_availability
|
||||
{
|
||||
using this_type = get_availability<Type>;
|
||||
using metadata_t = ::tim::component::metadata<Type>;
|
||||
using property_t = ::tim::component::properties<Type>;
|
||||
|
||||
static info_type get_info();
|
||||
auto operator()() const { return get_info(); }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename... Types>
|
||||
struct get_availability<type_list<Types...>>
|
||||
{
|
||||
using data_type = std::vector<info_type>;
|
||||
|
||||
static data_type get_info(data_type& _v)
|
||||
{
|
||||
TIMEMORY_FOLD_EXPRESSION(_v.emplace_back(get_availability<Types>::get_info()));
|
||||
return _v;
|
||||
}
|
||||
|
||||
static data_type get_info()
|
||||
{
|
||||
data_type _v{};
|
||||
return get_info(_v);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) operator()(Args&&... _args)
|
||||
{
|
||||
return get_info(std::forward<Args>(_args)...);
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename Type>
|
||||
info_type
|
||||
get_availability<Type>::get_info()
|
||||
{
|
||||
using namespace tim;
|
||||
using value_type = component_value_type_t<Type>;
|
||||
using category_types = typename trait::component_apis<Type>::type;
|
||||
|
||||
auto _cleanup = [](std::string _type, const std::string& _pattern) {
|
||||
auto _pos = std::string::npos;
|
||||
while((_pos = _type.find(_pattern)) != std::string::npos)
|
||||
_type.erase(_pos, _pattern.length());
|
||||
return _type;
|
||||
};
|
||||
auto _replace = [](std::string _type, const std::string& _pattern,
|
||||
const std::string& _with) {
|
||||
auto _pos = std::string::npos;
|
||||
while((_pos = _type.find(_pattern)) != std::string::npos)
|
||||
_type.replace(_pos, _pattern.length(), _with);
|
||||
return _type;
|
||||
};
|
||||
|
||||
bool has_metadata = metadata_t::specialized();
|
||||
bool has_properties = property_t::specialized();
|
||||
bool is_available = trait::is_available<Type>::value;
|
||||
bool file_output = trait::generates_output<Type>::value;
|
||||
auto name = component::metadata<Type>::name();
|
||||
auto label = (file_output)
|
||||
? ((has_metadata) ? metadata_t::label() : Type::get_label())
|
||||
: std::string("");
|
||||
auto description =
|
||||
(has_metadata) ? metadata_t::description() : Type::get_description();
|
||||
auto data_type = demangle<value_type>();
|
||||
string_t enum_type = property_t::enum_string();
|
||||
string_t id_type = property_t::id();
|
||||
auto ids_set = property_t::ids();
|
||||
|
||||
if(!has_properties)
|
||||
{
|
||||
enum_type = "";
|
||||
id_type = "";
|
||||
ids_set.clear();
|
||||
}
|
||||
string_t ids_str = {};
|
||||
{
|
||||
auto itr = ids_set.begin();
|
||||
string_t db = (markdown) ? "`\"" : (csv) ? "" : "\"";
|
||||
string_t de = (markdown) ? "\"`" : (csv) ? "" : "\"";
|
||||
if(has_metadata) description += ". " + metadata_t::extra_description();
|
||||
description += ".";
|
||||
while(itr->empty())
|
||||
++itr;
|
||||
if(itr != ids_set.end())
|
||||
ids_str = TIMEMORY_JOIN("", TIMEMORY_JOIN("", db, *itr++, de));
|
||||
for(; itr != ids_set.end(); ++itr)
|
||||
{
|
||||
if(!itr->empty())
|
||||
ids_str = TIMEMORY_JOIN(", ", ids_str, TIMEMORY_JOIN("", db, *itr, de));
|
||||
}
|
||||
}
|
||||
|
||||
string_t categories = get_categories(category_types{});
|
||||
description = _replace(_replace(description, ". .", "."), "..", ".");
|
||||
data_type = _replace(_cleanup(data_type, "::__1"), "> >", ">>");
|
||||
return info_type{ name, is_available,
|
||||
str_vec_t{ data_type, enum_type, id_type, ids_str, label,
|
||||
description, categories } };
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <>
|
||||
struct get_availability<void>
|
||||
{
|
||||
template <typename... Tp, typename... Args>
|
||||
decltype(auto) operator()(tim::type_list<Tp...>, Args&&... _args) const
|
||||
{
|
||||
return get_availability<tim::type_list<Tp...>>{}(std::forward<Args>(_args)...);
|
||||
}
|
||||
|
||||
template <typename Tp, typename... Args>
|
||||
decltype(auto) operator()(Args&&... _args) const
|
||||
{
|
||||
return get_availability<tim::type_list<Tp>>{}(std::forward<Args>(_args)...);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/type_list.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
template <typename... Tp>
|
||||
auto
|
||||
get_categories(type_list<Tp...>)
|
||||
{
|
||||
auto _cleanup = [](std::string _type, const std::string& _pattern) {
|
||||
auto _pos = std::string::npos;
|
||||
while((_pos = _type.find(_pattern)) != std::string::npos)
|
||||
_type.erase(_pos, _pattern.length());
|
||||
return _type;
|
||||
};
|
||||
(void) _cleanup; // unused but set if sizeof...(Tp) == 0
|
||||
|
||||
auto _vec = str_vec_t{ _cleanup(demangle<Tp>(), "tim::")... };
|
||||
std::sort(_vec.begin(), _vec.end(), [](const auto& lhs, const auto& rhs) {
|
||||
// prioritize project category
|
||||
auto lpos = lhs.find("project::");
|
||||
auto rpos = rhs.find("project::");
|
||||
return (lpos == rpos) ? (lhs < rhs) : (lpos < rpos);
|
||||
});
|
||||
std::stringstream _ss{};
|
||||
for(auto&& itr : _vec)
|
||||
{
|
||||
_ss << ", " << itr;
|
||||
}
|
||||
std::string _v = _ss.str();
|
||||
if(!_v.empty()) return _v.substr(2);
|
||||
return _v;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "info_type.hpp"
|
||||
#include "enumerated_list.hpp"
|
||||
#include "get_availability.hpp"
|
||||
|
||||
#include "api.hpp"
|
||||
#include "library/components/backtrace.hpp"
|
||||
#include "library/components/fork_gotcha.hpp"
|
||||
#include "library/components/mpi_gotcha.hpp"
|
||||
#include "library/components/pthread_gotcha.hpp"
|
||||
|
||||
#include <timemory/components/definition.hpp>
|
||||
#include <timemory/enum.h>
|
||||
#include <timemory/utility/macros.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
template <size_t EndV>
|
||||
std::vector<info_type>
|
||||
get_component_info()
|
||||
{
|
||||
using index_seq_t = std::make_index_sequence<EndV>;
|
||||
using enum_list_t = typename enumerated_list<tim::type_list<>, index_seq_t>::type;
|
||||
|
||||
auto _info = std::vector<info_type>{};
|
||||
return get_availability<>{}(enum_list_t{}, _info);
|
||||
}
|
||||
|
||||
template std::vector<info_type>
|
||||
get_component_info<TIMEMORY_NATIVE_COMPONENTS_END>();
|
||||
|
||||
template std::vector<info_type>
|
||||
get_component_info<TIMEMORY_COMPONENTS_END>();
|
||||
@@ -0,0 +1,75 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <timemory/enum.h>
|
||||
#include <timemory/utility/macros.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
struct info_type : info_type_base
|
||||
{
|
||||
TIMEMORY_DEFAULT_OBJECT(info_type)
|
||||
|
||||
template <typename... Args>
|
||||
info_type(Args&&... _args)
|
||||
: info_type_base{ std::forward<Args>(_args)... }
|
||||
{}
|
||||
|
||||
const auto& name() const { return std::get<0>(*this); }
|
||||
auto is_available() const { return std::get<1>(*this); }
|
||||
const auto& info() const { return std::get<2>(*this); }
|
||||
const auto& data_type() const { return info().at(0); }
|
||||
const auto& enum_type() const { return info().at(1); }
|
||||
const auto& id_type() const { return info().at(2); }
|
||||
const auto& id_strings() const { return info().at(3); }
|
||||
const auto& label() const { return info().at(4); }
|
||||
const auto& description() const { return info().at(5); }
|
||||
const auto& categories() const { return info().at(6); }
|
||||
|
||||
bool valid() const { return !name().empty() && info().size() >= 6; }
|
||||
|
||||
bool operator<(const info_type& rhs) const { return name() < rhs.name(); }
|
||||
bool operator!=(const info_type& rhs) const { return !(*this == rhs); }
|
||||
bool operator==(const info_type& rhs) const
|
||||
{
|
||||
if(info().size() != rhs.info().size()) return false;
|
||||
for(size_t i = 0; i < info().size(); ++i)
|
||||
{
|
||||
if(info().at(i) != rhs.info().at(i)) return false;
|
||||
}
|
||||
return name() == rhs.name() && is_available() == rhs.is_available();
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t EndV>
|
||||
std::vector<info_type>
|
||||
get_component_info();
|
||||
|
||||
extern template std::vector<info_type>
|
||||
get_component_info<TIMEMORY_NATIVE_COMPONENTS_END>();
|
||||
|
||||
extern template std::vector<info_type>
|
||||
get_component_info<TIMEMORY_COMPONENTS_END>();
|
||||
@@ -0,0 +1,34 @@
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# rocprofiler-systems-causal target
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_executable(
|
||||
rocprofiler-systems-causal
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprof-sys-causal.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprof-sys-causal.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/impl.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(rocprofiler-systems-causal PRIVATE TIMEMORY_CMAKE=1)
|
||||
target_include_directories(rocprofiler-systems-causal PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_link_libraries(
|
||||
rocprofiler-systems-causal
|
||||
PRIVATE
|
||||
rocprofiler-systems::rocprofiler-systems-compile-definitions
|
||||
rocprofiler-systems::rocprofiler-systems-headers
|
||||
rocprofiler-systems::rocprofiler-systems-common-library
|
||||
rocprofiler-systems::rocprofiler-systems-core
|
||||
)
|
||||
set_target_properties(
|
||||
rocprofiler-systems-causal
|
||||
PROPERTIES
|
||||
BUILD_RPATH "\$ORIGIN:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
|
||||
INSTALL_RPATH "${ROCPROFSYS_EXE_INSTALL_RPATH}"
|
||||
OUTPUT_NAME ${BINARY_NAME_PREFIX}-causal
|
||||
)
|
||||
|
||||
rocprofiler_systems_strip_target(rocprofiler-systems-causal)
|
||||
|
||||
install(TARGETS rocprofiler-systems-causal DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
@@ -0,0 +1,949 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "rocprof-sys-causal.hpp"
|
||||
|
||||
#include "common/defines.h"
|
||||
#include "common/delimit.hpp"
|
||||
#include "common/environment.hpp"
|
||||
#include "common/join.hpp"
|
||||
#include "common/setup.hpp"
|
||||
#include "core/mproc.hpp"
|
||||
#include "core/utility.hpp"
|
||||
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/log/color.hpp>
|
||||
#include <timemory/utility/argparse.hpp>
|
||||
#include <timemory/utility/console.hpp>
|
||||
#include <timemory/utility/delimit.hpp>
|
||||
#include <timemory/utility/filepath.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <gnu/lib-names.h>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/wait.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
namespace color = ::tim::log::color;
|
||||
namespace filepath = ::tim::filepath;
|
||||
namespace console = ::tim::utility::console;
|
||||
namespace argparse = ::tim::argparse;
|
||||
using namespace ::timemory::join;
|
||||
using ::rocprofsys::utility::parse_numeric_range;
|
||||
using ::tim::get_env;
|
||||
using ::tim::log::monochrome;
|
||||
using ::tim::log::stream;
|
||||
|
||||
namespace std
|
||||
{
|
||||
std::string
|
||||
to_string(bool _v)
|
||||
{
|
||||
return (_v) ? "true" : "false";
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
namespace
|
||||
{
|
||||
int verbose = 0;
|
||||
auto updated_envs = std::set<std::string_view>{};
|
||||
auto original_envs = std::set<std::string>{};
|
||||
auto child_pids = std::set<pid_t>{};
|
||||
auto launcher = std::string{};
|
||||
|
||||
inline signal_handler&
|
||||
get_signal_handler(int _sig)
|
||||
{
|
||||
static auto _v = std::unordered_map<int, signal_handler>{};
|
||||
auto itr = _v.emplace(_sig, signal_handler{});
|
||||
return itr.first->second;
|
||||
}
|
||||
|
||||
void
|
||||
create_signal_handler(int sig, signal_handler& sh, void (*func)(int))
|
||||
{
|
||||
if(sig < 1) return;
|
||||
sh.m_custom_sigaction.sa_handler = func;
|
||||
sigemptyset(&sh.m_custom_sigaction.sa_mask);
|
||||
sh.m_custom_sigaction.sa_flags = SA_RESTART;
|
||||
if(sigaction(sig, &sh.m_custom_sigaction, &sh.m_original_sigaction) == -1)
|
||||
{
|
||||
std::cerr << "Failed to create signal handler for " << sig << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
forward_signal(int sig)
|
||||
{
|
||||
for(auto itr : child_pids)
|
||||
{
|
||||
TIMEMORY_PRINTF_WARNING(stderr, "Killing pid=%i with signal %i...\n", itr, sig);
|
||||
kill(itr, sig);
|
||||
diagnose_status(itr, wait_pid(itr));
|
||||
}
|
||||
signal(sig, SIG_DFL);
|
||||
kill(getpid(), sig);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int
|
||||
get_verbose()
|
||||
{
|
||||
verbose = get_env("ROCPROFSYS_CAUSAL_VERBOSE",
|
||||
get_env<int>("ROCPROFSYS_VERBOSE", verbose, false));
|
||||
auto _debug = get_env("ROCPROFSYS_CAUSAL_DEBUG",
|
||||
get_env<bool>("ROCPROFSYS_DEBUG", false, false));
|
||||
if(_debug) verbose += 8;
|
||||
return verbose;
|
||||
}
|
||||
|
||||
void
|
||||
forward_signals(const std::set<int>& _signals)
|
||||
{
|
||||
for(auto itr : _signals)
|
||||
create_signal_handler(itr, get_signal_handler(itr), &forward_signal);
|
||||
}
|
||||
|
||||
void
|
||||
add_child_pid(pid_t _v)
|
||||
{
|
||||
child_pids.emplace(_v);
|
||||
}
|
||||
|
||||
void
|
||||
remove_child_pid(pid_t _v)
|
||||
{
|
||||
child_pids.erase(_v);
|
||||
}
|
||||
|
||||
int
|
||||
wait_pid(pid_t _pid, int _opts)
|
||||
{
|
||||
return ::rocprofsys::mproc::wait_pid(_pid, _opts);
|
||||
}
|
||||
|
||||
int
|
||||
diagnose_status(pid_t _pid, int _status)
|
||||
{
|
||||
return ::rocprofsys::mproc::diagnose_status(_pid, _status, get_verbose());
|
||||
}
|
||||
|
||||
std::string
|
||||
get_realpath(const std::string& _v)
|
||||
{
|
||||
auto* _tmp = realpath(_v.c_str(), nullptr);
|
||||
auto _ret = std::string{ _tmp };
|
||||
free(_tmp);
|
||||
return _ret;
|
||||
}
|
||||
|
||||
void
|
||||
print_command(const std::vector<char*>& _argv, std::string_view _prefix)
|
||||
{
|
||||
if(verbose >= 1)
|
||||
stream(std::cout, color::info())
|
||||
<< _prefix << "Executing '" << join(array_config{ " " }, _argv) << "'...\n";
|
||||
|
||||
std::cerr << color::end() << std::flush;
|
||||
}
|
||||
|
||||
std::vector<char*>
|
||||
get_initial_environment()
|
||||
{
|
||||
auto _env = std::vector<char*>{};
|
||||
if(environ != nullptr)
|
||||
{
|
||||
int idx = 0;
|
||||
while(environ[idx] != nullptr)
|
||||
{
|
||||
auto* _v = environ[idx++];
|
||||
original_envs.emplace(_v);
|
||||
_env.emplace_back(strdup(_v));
|
||||
}
|
||||
}
|
||||
|
||||
update_env(_env, "ROCPROFSYS_MODE", "causal");
|
||||
update_env(_env, "ROCPROFSYS_USE_CAUSAL", true);
|
||||
update_env(_env, "ROCPROFSYS_USE_SAMPLING", false);
|
||||
update_env(_env, "ROCPROFSYS_TRACE", false);
|
||||
update_env(_env, "ROCPROFSYS_PROFILE", false);
|
||||
update_env(_env, "ROCPROFSYS_USE_PROCESS_SAMPLING", false);
|
||||
update_env(_env, "ROCPROFSYS_THREAD_POOL_SIZE",
|
||||
get_env<int>("ROCPROFSYS_THREAD_POOL_SIZE", 0));
|
||||
update_env(_env, "ROCPROFSYS_LAUNCHER", "rocprof-sys-causal");
|
||||
|
||||
return _env;
|
||||
}
|
||||
|
||||
void
|
||||
prepare_command_for_run(char* _exe, std::vector<char*>& _argv)
|
||||
{
|
||||
if(!launcher.empty())
|
||||
{
|
||||
bool _injected = false;
|
||||
auto _new_argv = std::vector<char*>{};
|
||||
for(auto* itr : _argv)
|
||||
{
|
||||
if(!_injected && std::regex_search(itr, std::regex{ launcher }))
|
||||
{
|
||||
_new_argv.emplace_back(_exe);
|
||||
_new_argv.emplace_back(strdup("--"));
|
||||
_injected = true;
|
||||
}
|
||||
_new_argv.emplace_back(itr);
|
||||
}
|
||||
|
||||
if(!_injected)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
join("", "rocprof-sys-causal was unable to match \"", launcher,
|
||||
"\" to any arguments on the command line: \"",
|
||||
join(array_config{ " ", "", "" }, _argv), "\""));
|
||||
}
|
||||
|
||||
std::swap(_argv, _new_argv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
prepare_environment_for_run(std::vector<char*>& _env)
|
||||
{
|
||||
if(launcher.empty())
|
||||
{
|
||||
update_env(_env, "LD_PRELOAD",
|
||||
join(":", LIBPTHREAD_SO,
|
||||
get_realpath(get_internal_libpath("librocprof-sys-dl.so"))),
|
||||
true);
|
||||
update_env(_env, "ROCPROFSYS_SCRIPT_DIR", get_internal_script_path());
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
get_internal_libpath(const std::string& _lib)
|
||||
{
|
||||
auto _exe = std::string_view{ realpath("/proc/self/exe", nullptr) };
|
||||
auto _pos = _exe.find_last_of('/');
|
||||
auto _dir = std::string{ "./" };
|
||||
if(_pos != std::string_view::npos) _dir = _exe.substr(0, _pos);
|
||||
return rocprofsys::common::join("/", _dir, "..", "lib", _lib);
|
||||
}
|
||||
|
||||
std::string
|
||||
get_internal_script_path(void)
|
||||
{
|
||||
auto _exe = std::string_view{ realpath("/proc/self/exe", nullptr) };
|
||||
auto _pos = _exe.find_last_of('/');
|
||||
auto _dir = std::string{ "./" };
|
||||
if(_pos != std::string_view::npos) _dir = _exe.substr(0, _pos);
|
||||
|
||||
auto _script_dir = get_realpath(
|
||||
rocprofsys::common::join("/", _dir, "..", "libexec", "rocprofiler-systems"));
|
||||
|
||||
return _script_dir;
|
||||
}
|
||||
|
||||
void
|
||||
print_updated_environment(std::vector<char*> _env, std::string_view _prefix)
|
||||
{
|
||||
if(get_verbose() < 0) return;
|
||||
|
||||
std::sort(_env.begin(), _env.end(), [](auto* _lhs, auto* _rhs) {
|
||||
if(!_lhs) return false;
|
||||
if(!_rhs) return true;
|
||||
return std::string_view{ _lhs } < std::string_view{ _rhs };
|
||||
});
|
||||
|
||||
std::vector<std::string_view> _updates = {};
|
||||
std::vector<std::string_view> _general = {};
|
||||
|
||||
for(auto* itr : _env)
|
||||
{
|
||||
if(itr == nullptr) continue;
|
||||
|
||||
auto _is_omni = (std::string_view{ itr }.find("ROCPROFSYS") == 0);
|
||||
auto _updated = false;
|
||||
for(const auto& vitr : updated_envs)
|
||||
{
|
||||
if(std::string_view{ itr }.find(vitr) == 0)
|
||||
{
|
||||
_updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_updated)
|
||||
_updates.emplace_back(itr);
|
||||
else if(verbose >= 1 && _is_omni)
|
||||
_general.emplace_back(itr);
|
||||
}
|
||||
|
||||
if(_general.size() + _updates.size() == 0 || verbose < 0) return;
|
||||
|
||||
std::cerr << std::endl;
|
||||
|
||||
for(auto& itr : _general)
|
||||
stream(std::cerr, color::source()) << _prefix << itr << "\n";
|
||||
for(auto& itr : _updates)
|
||||
stream(std::cerr, color::source()) << _prefix << itr << "\n";
|
||||
|
||||
std::cerr << color::end() << std::flush;
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
void
|
||||
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
|
||||
bool _append, std::string_view _join_delim)
|
||||
{
|
||||
updated_envs.emplace(_env_var);
|
||||
|
||||
auto _key = join("", _env_var, "=");
|
||||
for(auto& itr : _environ)
|
||||
{
|
||||
if(!itr) continue;
|
||||
if(std::string_view{ itr }.find(_key) == 0)
|
||||
{
|
||||
if(_append)
|
||||
{
|
||||
if(std::string_view{ itr }.find(join("", _env_val)) ==
|
||||
std::string_view::npos)
|
||||
{
|
||||
auto _val = std::string{ itr }.substr(_key.length());
|
||||
free(itr);
|
||||
if(_env_var == "LD_PRELOAD")
|
||||
{
|
||||
itr =
|
||||
strdup(join('=', _env_var, join(_join_delim, _val, _env_val))
|
||||
.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
itr =
|
||||
strdup(join('=', _env_var, join(_join_delim, _env_val, _val))
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free(itr);
|
||||
itr = strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
_environ.emplace_back(
|
||||
strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str()));
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
void
|
||||
add_default_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val)
|
||||
{
|
||||
auto _key = join("", _env_var, "=");
|
||||
for(auto& itr : _environ)
|
||||
{
|
||||
if(!itr) continue;
|
||||
if(std::string_view{ itr }.find(_key) == 0) return;
|
||||
}
|
||||
|
||||
updated_envs.emplace(_env_var);
|
||||
_environ.emplace_back(
|
||||
strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str()));
|
||||
}
|
||||
|
||||
void
|
||||
remove_env(std::vector<char*>& _environ, std::string_view _env_var)
|
||||
{
|
||||
auto _key = join("", _env_var, "=");
|
||||
auto _match = [&_key](auto itr) { return std::string_view{ itr }.find(_key) == 0; };
|
||||
|
||||
_environ.erase(std::remove_if(_environ.begin(), _environ.end(), _match),
|
||||
_environ.end());
|
||||
|
||||
for(const auto& itr : original_envs)
|
||||
{
|
||||
if(std::string_view{ itr }.find(_key) == 0)
|
||||
_environ.emplace_back(strdup(itr.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char*>
|
||||
parse_args(int argc, char** argv, std::vector<char*>& _env,
|
||||
std::vector<std::map<std::string_view, std::string>>& _causal_envs)
|
||||
{
|
||||
using parser_t = argparse::argument_parser;
|
||||
using parser_err_t = typename parser_t::result_type;
|
||||
|
||||
auto help_check = [](parser_t& p, int _argc, char** _argv) {
|
||||
std::set<std::string> help_args = { "-h", "--help", "-?" };
|
||||
return (p.exists("help") || _argc == 1 ||
|
||||
(_argc > 1 && help_args.find(_argv[1]) != help_args.end()));
|
||||
};
|
||||
|
||||
auto _pec = EXIT_SUCCESS;
|
||||
auto help_action = [&_pec, argc, argv](parser_t& p) {
|
||||
if(_pec != EXIT_SUCCESS)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "Error in command:";
|
||||
for(int i = 0; i < argc; ++i)
|
||||
msg << " " << argv[i];
|
||||
msg << "\n\n";
|
||||
stream(std::cerr, color::fatal()) << msg.str();
|
||||
std::cerr << std::flush;
|
||||
}
|
||||
|
||||
p.print_help();
|
||||
exit(_pec);
|
||||
};
|
||||
|
||||
const auto* _desc = R"desc(
|
||||
Causal profiling usually requires multiple runs to reliably resolve the speedup estimates.
|
||||
This executable is designed to streamline that process.
|
||||
For example (assume all commands end with '-- <exe> <args>'):
|
||||
|
||||
rocprof-sys-causal -n 5 -- <exe> # runs <exe> 5x with causal profiling enabled
|
||||
|
||||
rocprof-sys-causal -s 0 5,10,15,20 # runs <exe> 2x with virtual speedups:
|
||||
# - 0
|
||||
# - randomly selected from 5, 10, 15, and 20
|
||||
|
||||
rocprof-sys-causal -F func_A func_B func_(A|B) # runs <exe> 3x with the function scope limited to:
|
||||
# 1. func_A
|
||||
# 2. func_B
|
||||
# 3. func_A or func_B
|
||||
General tips:
|
||||
- Insert progress points at hotspots in your code or use rocprof-sys's runtime instrumentation
|
||||
- Note: binary rewrite will produce a incompatible new binary
|
||||
- Collect a flat profile via sampling
|
||||
- E.g., rocprof-sys-sample -F -- <exe> <args>
|
||||
- Inspect sampling_wall_clock.txt and sampling_cpu_clock.txt for functions to target
|
||||
- Run rocprof-sys-causal in "function" mode first (does not require debug info)
|
||||
- Run rocprof-sys-causal in "line" mode when you are targeting one function (requires debug info)
|
||||
- Preferably, use predictions from the "function" mode to determine which function to target
|
||||
- Limit the virtual speedups to a smaller pool, e.g., 0,5,10,25,50, to get reliable predictions quicker
|
||||
- Make use of the binary, source, and function scope to limit the functions/lines selected for experiments
|
||||
- Note: source scope requires debug info
|
||||
)desc";
|
||||
|
||||
auto parser = parser_t{ basename(argv[0]), _desc };
|
||||
|
||||
parser.on_error([](parser_t&, const parser_err_t& _err) {
|
||||
stream(std::cerr, color::fatal()) << _err << "\n";
|
||||
exit(EXIT_FAILURE);
|
||||
});
|
||||
|
||||
parser.enable_help();
|
||||
parser.enable_version("rocprof-sys-causal", ROCPROFSYS_ARGPARSE_VERSION_INFO);
|
||||
|
||||
auto _cols = std::get<0>(console::get_columns());
|
||||
if(_cols > parser.get_help_width() + 8)
|
||||
parser.set_description_width(
|
||||
std::min<int>(_cols - parser.get_help_width() - 8, 120));
|
||||
|
||||
parser.start_group("DEBUG OPTIONS", "");
|
||||
parser.add_argument({ "--monochrome" }, "Disable colorized output")
|
||||
.max_count(1)
|
||||
.dtype("bool")
|
||||
.action([&](parser_t& p) {
|
||||
auto _monochrome = p.get<bool>("monochrome");
|
||||
monochrome() = _monochrome;
|
||||
p.set_use_color(!_monochrome);
|
||||
update_env(_env, "ROCPROFSYS_MONOCHROME", (_monochrome) ? "1" : "0");
|
||||
update_env(_env, "MONOCHROME", (_monochrome) ? "1" : "0");
|
||||
});
|
||||
parser.add_argument({ "--debug" }, "Debug output")
|
||||
.max_count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_DEBUG", p.get<bool>("debug"));
|
||||
});
|
||||
parser.add_argument({ "-v", "--verbose" }, "Verbose output")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<int>("verbose");
|
||||
verbose = _v;
|
||||
update_env(_env, "ROCPROFSYS_VERBOSE", _v);
|
||||
});
|
||||
|
||||
std::string _config_file = {};
|
||||
std::string _config_folder = "rocprof-sys-causal-config";
|
||||
bool _generate_configs = false;
|
||||
bool _add_defaults = true;
|
||||
|
||||
parser.start_group("GENERAL OPTIONS", "");
|
||||
parser.add_argument({ "-c", "--config" }, "Base configuration file")
|
||||
.min_count(0)
|
||||
.dtype("filepath")
|
||||
.action([&](parser_t& p) {
|
||||
_config_file =
|
||||
join(array_config{ ":" }, p.get<std::vector<std::string>>("config"));
|
||||
});
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-l", "--launcher" },
|
||||
"When running MPI jobs, rocprof-sys-causal needs to be *before* the "
|
||||
"executable "
|
||||
"which launches the MPI processes (i.e. before `mpirun`, `srun`, etc.). Pass "
|
||||
"the name of the target executable (or a regex for matching to the name of "
|
||||
"the target) for causal profiling, e.g., `rocprof-sys-causal -l foo -- "
|
||||
"mpirun "
|
||||
"-n 4 foo`. This ensures that the rocprof-sys library is LD_PRELOADed on the "
|
||||
"proper target")
|
||||
.count(1)
|
||||
.dtype("executable")
|
||||
.action([&](parser_t& p) { launcher = p.get<std::string>("launcher"); });
|
||||
parser
|
||||
.add_argument({ "-g", "--generate-configs" },
|
||||
"Generate config files instead of passing environment variables "
|
||||
"directly. If no arguments are provided, the config files will be "
|
||||
"placed in ${PWD}/rocprof-sys-causal-config folder")
|
||||
.min_count(0)
|
||||
.max_count(1)
|
||||
.dtype("folder")
|
||||
.action([&](parser_t& p) {
|
||||
_generate_configs = true;
|
||||
auto _dir = p.get<std::string>("generate-configs");
|
||||
if(!_dir.empty()) _config_folder = std::move(_dir);
|
||||
if(!filepath::exists(_config_folder)) filepath::makedir(_config_folder);
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--no-defaults" },
|
||||
"Do not activate default features which are recommended for causal "
|
||||
"profiling. For example: PID-tagging of output files and "
|
||||
"timestamped subdirectories are disabled by default. Kokkos tools "
|
||||
"support is added by default (ROCPROFSYS_USE_KOKKOSP=ON) because, "
|
||||
"for Kokkos applications, the Kokkos-Tools callbacks are used for "
|
||||
"progress points. Activation of OpenMP tools support is similar")
|
||||
.min_count(0)
|
||||
.max_count(1)
|
||||
.dtype("bool")
|
||||
.action([&](parser_t& p) { _add_defaults = !p.get<bool>("no-defaults"); });
|
||||
|
||||
parser.start_group("CAUSAL PROFILING OPTIONS (General)",
|
||||
"These settings will be applied to all causal profiling runs");
|
||||
parser
|
||||
.add_argument({ "-m", "--mode" },
|
||||
"Causal profiling mode. Function mode tends to resolve statistics "
|
||||
"faster than line mode (due to smaller sampling space). Ideally, "
|
||||
"use function mode first to identify a function to target and then "
|
||||
"switch to line mode + function scope setting")
|
||||
.count(1)
|
||||
.dtype("string")
|
||||
.choices({ "function", "line" })
|
||||
.choice_alias("function", { "func" })
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_CAUSAL_MODE", p.get<std::string>("mode"));
|
||||
});
|
||||
|
||||
parser.add_argument({ "-b", "--backend" }, "Causal profiling sampling backend.")
|
||||
.count(1)
|
||||
.dtype("string")
|
||||
.choices({ "auto", "perf", "timer" })
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_CAUSAL_BACKEND", p.get<std::string>("backend"));
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument({ "-o", "--output-name" },
|
||||
"Output filename of causal profiling data w/o extension")
|
||||
.min_count(1)
|
||||
.dtype("filename")
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_CAUSAL_FILE", p.get<std::string>("output-name"));
|
||||
});
|
||||
|
||||
bool _reset = false;
|
||||
|
||||
parser
|
||||
.add_argument({ "-r", "--reset" },
|
||||
"Overwrite any existing experiment results during the first run")
|
||||
.max_count(1)
|
||||
.dtype("bool")
|
||||
.action([&](parser_t& p) { _reset = p.get<bool>("reset"); });
|
||||
|
||||
parser
|
||||
.add_argument({ "-e", "--end-to-end" },
|
||||
"Single causal experiment for the entire application runtime")
|
||||
.max_count(1)
|
||||
.dtype("bool")
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_CAUSAL_END_TO_END", p.get<bool>("end-to-end"));
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument({ "-w", "--wait" },
|
||||
"Set the wait time (i.e. delay) before starting the first causal "
|
||||
"experiment (in seconds)")
|
||||
.count(1)
|
||||
.dtype("seconds")
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_CAUSAL_DELAY", p.get<double>("wait"));
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-d", "--duration" },
|
||||
"Set the length of time (in seconds) to perform causal experimentationafter "
|
||||
"the first experiment is started. Once this amount of time has elapsed, no "
|
||||
"more causal experiments will be started but any currently running "
|
||||
"experiment will be allowed to finish.")
|
||||
.count(1)
|
||||
.dtype("seconds")
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_CAUSAL_DURATION", p.get<double>("duration"));
|
||||
});
|
||||
|
||||
int64_t _niterations = 1;
|
||||
auto _virtual_speedups = std::vector<std::string>{};
|
||||
auto _function_scopes = std::vector<std::string>{};
|
||||
auto _binary_scopes = std::vector<std::string>{};
|
||||
auto _source_scopes = std::vector<std::string>{};
|
||||
auto _function_excludes = std::vector<std::string>{};
|
||||
auto _binary_excludes = std::vector<std::string>{};
|
||||
auto _source_excludes = std::vector<std::string>{};
|
||||
|
||||
parser
|
||||
.add_argument({ "-n", "--iterations" },
|
||||
"Number of times to repeat the combination of run configurations")
|
||||
.count(1)
|
||||
.dtype("int")
|
||||
.action([&](parser_t& p) { _niterations = p.get<int64_t>("iterations"); });
|
||||
|
||||
parser.start_group(
|
||||
"CAUSAL PROFILING OPTIONS (Combinatorial)",
|
||||
"Each individual argument to these options will multiply the number runs by the "
|
||||
"number of arguments and the number of iterations. E.g. -n 2 -B \"MAIN\" -F "
|
||||
"\"foo\" \"bar\" will produce 4 runs: 2 iterations x 1 binary scope x 2 function "
|
||||
"scopes (MAIN+foo, MAIN+bar, MAIN+foo, MAIN+bar)");
|
||||
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-s", "--speedups" },
|
||||
"Pool of virtual speedups to sample from during experimentation. "
|
||||
"Each space designates a group and multiple speedups can be "
|
||||
"grouped together by commas, e.g. '-s 0 0,10,20-50' is two groups: "
|
||||
"group #1 is '0' and group #2 is '0 10 20 25 30 35 40 45 50' -- "
|
||||
"unless end-to-end mode is activated: in end-to-end mode, only one "
|
||||
"speedup is selected for the entire run so all groups are "
|
||||
"expanded. If a range is specified, the default increment is 5, "
|
||||
"however, this can be overridden by suffixing the range with a colon and the "
|
||||
"desired increment, e.g., '0-40:10' would expand to '0 10 20 30 40'")
|
||||
.min_count(1)
|
||||
.max_count(-1)
|
||||
.dtype("integer | range | range:increment")
|
||||
.action([&](parser_t& p) {
|
||||
auto _val = p.get<std::vector<std::string>>("speedups");
|
||||
if(p.get<bool>("end-to-end"))
|
||||
{
|
||||
_virtual_speedups.clear();
|
||||
for(const auto& itr : _val)
|
||||
{
|
||||
for(const auto& ditr : tim::delimit(itr, ",; \t\n\r"))
|
||||
{
|
||||
for(auto nitr :
|
||||
parse_numeric_range<int64_t, std::vector<int64_t>>(
|
||||
ditr, "virtual speedup", 5L))
|
||||
{
|
||||
_virtual_speedups.emplace_back(std::to_string(nitr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_virtual_speedups = _val;
|
||||
}
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument({ "-B", "--binary-scope" },
|
||||
"Restricts causal experiments to the binaries matching the list of "
|
||||
"regular expressions. Each space designates a group and multiple "
|
||||
"scopes can be grouped together with a semi-colon")
|
||||
.min_count(0)
|
||||
.max_count(-1)
|
||||
.dtype("integers")
|
||||
.action([&](parser_t& p) {
|
||||
_binary_scopes = p.get<std::vector<std::string>>("binary-scope");
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument({ "-S", "--source-scope" },
|
||||
"Restricts causal experiments to the source files or source file + "
|
||||
"lineno pairs (i.e. <file> or <file>:<line>) matching the list of "
|
||||
"regular expressions. Each space designates a group and multiple "
|
||||
"scopes can be grouped together with a semi-colon")
|
||||
.min_count(0)
|
||||
.max_count(-1)
|
||||
.dtype("integers")
|
||||
.action([&](parser_t& p) {
|
||||
_source_scopes = p.get<std::vector<std::string>>("source-scope");
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-F", "--function-scope" },
|
||||
"Restricts causal experiments to the functions matching the list of "
|
||||
"regular expressions. Each space designates a group and multiple "
|
||||
"scopes can be grouped together with a semi-colon")
|
||||
.min_count(0)
|
||||
.max_count(-1)
|
||||
.dtype("regex-list")
|
||||
.action([&](parser_t& p) {
|
||||
_function_scopes = p.get<std::vector<std::string>>("function-scope");
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-BE", "--binary-exclude" },
|
||||
"Excludes causal experiments from being performed on the binaries matching "
|
||||
"the list of regular expressions. Each space designates a group and multiple "
|
||||
"excludes can be grouped together with a semi-colon")
|
||||
.min_count(0)
|
||||
.max_count(-1)
|
||||
.dtype("integers")
|
||||
.action([&](parser_t& p) {
|
||||
_binary_excludes = p.get<std::vector<std::string>>("binary-exclude");
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-SE", "--source-exclude" },
|
||||
"Excludes causal experiments from being performed on the code from the "
|
||||
"source files or source file + lineno pair (i.e. <file> or <file>:<line>) "
|
||||
"matching the list of regular expressions. Each space designates a group and "
|
||||
"multiple excludes can be grouped together with a semi-colon")
|
||||
.min_count(0)
|
||||
.max_count(-1)
|
||||
.dtype("integers")
|
||||
.action([&](parser_t& p) {
|
||||
_source_excludes = p.get<std::vector<std::string>>("source-exclude");
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-FE", "--function-exclude" },
|
||||
"Excludes causal experiments from being performed on the functions matching "
|
||||
"the list of regular expressions. Each space designates a group and multiple "
|
||||
"excludes can be grouped together with a semi-colon")
|
||||
.min_count(0)
|
||||
.max_count(-1)
|
||||
.dtype("regex-list")
|
||||
.action([&](parser_t& p) {
|
||||
_function_excludes = p.get<std::vector<std::string>>("function-exclude");
|
||||
});
|
||||
|
||||
parser.end_group();
|
||||
|
||||
auto _inpv = std::vector<char*>{};
|
||||
auto _outv = std::vector<char*>{};
|
||||
bool _hash = false;
|
||||
for(int i = 0; i < argc; ++i)
|
||||
{
|
||||
if(_hash)
|
||||
{
|
||||
_outv.emplace_back(argv[i]);
|
||||
}
|
||||
else if(std::string_view{ argv[i] } == "--")
|
||||
{
|
||||
_hash = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inpv.emplace_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
auto _cerr = parser.parse_args(_inpv.size(), _inpv.data());
|
||||
if(help_check(parser, argc, argv))
|
||||
help_action(parser);
|
||||
else if(_cerr)
|
||||
throw std::runtime_error(_cerr.what());
|
||||
|
||||
if(_niterations < 1) _niterations = 1;
|
||||
auto _get_size = [](const auto& _v) { return std::max<size_t>(_v.size(), 1); };
|
||||
|
||||
auto _causal_envs_tmp = std::vector<std::map<std::string_view, std::string>>{};
|
||||
auto _fill = [&_causal_envs_tmp](std::string_view _env_var, const auto& _data,
|
||||
bool _quote) {
|
||||
if(_data.empty()) return;
|
||||
if(_causal_envs_tmp.empty()) _causal_envs_tmp.emplace_back();
|
||||
auto _tmp = _causal_envs_tmp;
|
||||
_causal_envs_tmp.clear();
|
||||
_causal_envs_tmp.reserve(_data.size() * _tmp.size());
|
||||
for(auto ditr : _data)
|
||||
{
|
||||
if(_quote)
|
||||
{
|
||||
ditr.insert(0, "\"");
|
||||
ditr += "\"";
|
||||
}
|
||||
|
||||
// duplicate the env, add the env variable, emplace back
|
||||
for(auto itr : _tmp)
|
||||
{
|
||||
itr[_env_var] = ditr;
|
||||
_causal_envs_tmp.emplace_back(itr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(_add_defaults)
|
||||
{
|
||||
add_default_env(_env, "ROCPROFSYS_TIME_OUTPUT", false);
|
||||
add_default_env(_env, "ROCPROFSYS_USE_PID", false);
|
||||
add_default_env(_env, "ROCPROFSYS_USE_KOKKOSP", true);
|
||||
|
||||
#if defined(ROCPROFSYS_USE_OMPT) && ROCPROFSYS_USE_OMPT > 0
|
||||
add_default_env(_env, "ROCPROFSYS_USE_OMPT", true);
|
||||
#endif
|
||||
|
||||
#if(defined(ROCPROFSYS_USE_MPI) && ROCPROFSYS_USE_MPI > 0) || \
|
||||
(defined(ROCPROFSYS_USE_MPI_HEADERS) && ROCPROFSYS_USE_MPI_HEADERS > 0)
|
||||
add_default_env(_env, "ROCPROFSYS_USE_MPIP", true);
|
||||
#endif
|
||||
}
|
||||
|
||||
_fill("ROCPROFSYS_CAUSAL_BINARY_EXCLUDE", _binary_excludes, _generate_configs);
|
||||
_fill("ROCPROFSYS_CAUSAL_SOURCE_EXCLUDE", _source_excludes, _generate_configs);
|
||||
_fill("ROCPROFSYS_CAUSAL_FUNCTION_EXCLUDE", _function_excludes, _generate_configs);
|
||||
|
||||
_fill("ROCPROFSYS_CAUSAL_BINARY_SCOPE", _binary_scopes, _generate_configs);
|
||||
_fill("ROCPROFSYS_CAUSAL_SOURCE_SCOPE", _source_scopes, _generate_configs);
|
||||
_fill("ROCPROFSYS_CAUSAL_FUNCTION_SCOPE", _function_scopes, _generate_configs);
|
||||
|
||||
_fill("ROCPROFSYS_CAUSAL_FIXED_SPEEDUP", _virtual_speedups, false);
|
||||
|
||||
// make sure at least one env exists
|
||||
if(_causal_envs_tmp.empty()) _causal_envs_tmp.emplace_back();
|
||||
|
||||
// duplicate for the number of iterations
|
||||
_causal_envs.clear();
|
||||
_causal_envs.reserve(_niterations * _causal_envs_tmp.size());
|
||||
for(int64_t i = 0; i < _niterations; ++i)
|
||||
{
|
||||
for(const auto& itr : _causal_envs_tmp)
|
||||
_causal_envs.emplace_back(itr);
|
||||
}
|
||||
|
||||
if(_generate_configs)
|
||||
{
|
||||
auto _is_omni_cfg = [](std::string_view itr) {
|
||||
return (itr.find("ROCPROFSYS") == 0 && itr.find("ROCPROFSYS_MODE") != 0 &&
|
||||
itr.find("ROCPROFSYS_DEBUG_") != 0 && itr.find('=') < itr.length());
|
||||
// rocprof-sys has miscellaneous env options starting with ROCPROFSYS_DEBUG_
|
||||
// that are not official options
|
||||
};
|
||||
|
||||
auto _omni_env_m = std::map<std::string, std::string>{};
|
||||
for(auto* itr : _env)
|
||||
{
|
||||
if(_is_omni_cfg(itr))
|
||||
{
|
||||
auto _env_var = std::string{ itr };
|
||||
auto _pos = _env_var.find('=');
|
||||
auto _env_val = _env_var.substr(_pos + 1);
|
||||
_env_var = _env_var.substr(0, _pos);
|
||||
_omni_env_m.emplace(_env_var, _env_val);
|
||||
}
|
||||
}
|
||||
|
||||
_env.erase(std::remove_if(_env.begin(), _env.end(), _is_omni_cfg), _env.end());
|
||||
|
||||
auto _omni_env = std::vector<std::pair<std::string, std::string>>{};
|
||||
// make sure that ROCPROFSYS_CONFIG_FILE is the first entry
|
||||
{
|
||||
auto citr = _omni_env_m.find("ROCPROFSYS_CONFIG_FILE");
|
||||
if(citr != _omni_env_m.end())
|
||||
{
|
||||
_omni_env.emplace_back(citr->first, citr->second);
|
||||
_omni_env_m.erase(citr);
|
||||
}
|
||||
}
|
||||
for(const auto& itr : _omni_env_m)
|
||||
_omni_env.emplace_back(itr.first, itr.second);
|
||||
|
||||
_causal_envs_tmp = std::move(_causal_envs);
|
||||
_causal_envs.clear();
|
||||
auto _write_config =
|
||||
[_omni_env](std::ostream& _os,
|
||||
const std::map<std::string_view, std::string>& _data) {
|
||||
size_t _width = 0;
|
||||
for(const auto& itr : _omni_env)
|
||||
_width = std::max(_width, itr.first.length());
|
||||
|
||||
for(const auto& itr : _data)
|
||||
_width = std::max(_width, itr.first.length());
|
||||
|
||||
_os << "# rocprofsys common settings\n";
|
||||
for(const auto& itr : _omni_env)
|
||||
_os << std::setw(_width + 1) << std::left << itr.first << " = "
|
||||
<< itr.second << "\n";
|
||||
|
||||
_os << "\n# rocprofsys causal settings\n";
|
||||
for(const auto& itr : _data)
|
||||
_os << std::setw(_width + 1) << std::left << itr.first << " = "
|
||||
<< itr.second << "\n";
|
||||
};
|
||||
|
||||
int nwidth = (std::log10(_causal_envs_tmp.size()) + 1);
|
||||
for(size_t i = 0; i < _causal_envs_tmp.size(); ++i)
|
||||
{
|
||||
std::stringstream fname{};
|
||||
fname.fill('0');
|
||||
fname << _config_folder << "/causal-" << std::setw(nwidth) << i << ".cfg";
|
||||
std::ofstream _ofs{ fname.str() };
|
||||
_write_config(_ofs, _causal_envs_tmp.at(i));
|
||||
auto _cfg_name = (_config_file.empty())
|
||||
? fname.str()
|
||||
: join(array_config{ ":" }, _config_file, fname.str());
|
||||
auto _cfg =
|
||||
std::map<std::string_view, std::string>{ { "ROCPROFSYS_CONFIG_FILE",
|
||||
_cfg_name } };
|
||||
_causal_envs.emplace_back(_cfg);
|
||||
}
|
||||
}
|
||||
|
||||
if(_reset)
|
||||
_causal_envs.front().emplace(std::string_view{ "ROCPROFSYS_CAUSAL_FILE_RESET" },
|
||||
std::string{ "true" });
|
||||
|
||||
return _outv;
|
||||
}
|
||||
|
||||
// explicit instantiation for usage in rocprof-sys-causal.cpp
|
||||
template void
|
||||
update_env(std::vector<char*>&, std::string_view, const std::string& _env_val,
|
||||
bool _append, std::string_view);
|
||||
@@ -0,0 +1,136 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "rocprof-sys-causal.hpp"
|
||||
|
||||
#include <timemory/log/macros.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
auto _base_env = get_initial_environment();
|
||||
auto _causal_env = std::vector<std::map<std::string_view, std::string>>{};
|
||||
|
||||
bool _has_double_hyphen = false;
|
||||
for(int i = 1; i < argc; ++i)
|
||||
{
|
||||
auto _arg = std::string_view{ argv[i] };
|
||||
if(_arg == "--" || _arg == "-?" || _arg == "-h" || _arg == "--help" ||
|
||||
_arg == "--version")
|
||||
_has_double_hyphen = true;
|
||||
}
|
||||
|
||||
std::vector<char*> _argv = {};
|
||||
if(_has_double_hyphen)
|
||||
{
|
||||
_argv = parse_args(argc, argv, _base_env, _causal_env);
|
||||
}
|
||||
else
|
||||
{
|
||||
_argv.reserve(argc);
|
||||
for(int i = 1; i < argc; ++i)
|
||||
_argv.emplace_back(argv[i]);
|
||||
_causal_env.resize(1);
|
||||
}
|
||||
|
||||
prepare_command_for_run(argv[0], _argv);
|
||||
prepare_environment_for_run(_base_env);
|
||||
|
||||
if(get_verbose() >= 3)
|
||||
{
|
||||
TIMEMORY_PRINTF_INFO(stderr, "causal environments to be executed:\n");
|
||||
size_t _n = 0;
|
||||
for(auto& citr : _causal_env)
|
||||
{
|
||||
auto _env = _base_env;
|
||||
for(const auto& eitr : citr)
|
||||
update_env(_env, eitr.first, eitr.second);
|
||||
auto _prefix = std::to_string(_n++) + ": ";
|
||||
print_updated_environment(_env, _prefix);
|
||||
}
|
||||
}
|
||||
|
||||
if(!_argv.empty())
|
||||
{
|
||||
if(_causal_env.size() == 1)
|
||||
{
|
||||
auto _env = _base_env;
|
||||
for(const auto& eitr : _causal_env.front())
|
||||
update_env(_env, eitr.first, eitr.second);
|
||||
print_updated_environment(_env, "0: ");
|
||||
print_command(_argv, "0: ");
|
||||
_argv.emplace_back(nullptr);
|
||||
_env.emplace_back(nullptr);
|
||||
return execvpe(_argv.front(), _argv.data(), _env.data());
|
||||
}
|
||||
|
||||
forward_signals({ SIGINT, SIGTERM, SIGQUIT });
|
||||
size_t _ncount = 0;
|
||||
size_t _width = std::log10(_causal_env.size()) + 1;
|
||||
for(auto& citr : _causal_env)
|
||||
{
|
||||
auto _n = _ncount++;
|
||||
auto _main_pid = getpid();
|
||||
auto _pid = fork();
|
||||
|
||||
if(get_verbose() >= 3)
|
||||
{
|
||||
TIMEMORY_PRINTF_INFO(stderr, "process %i returned %i from fork...\n",
|
||||
getpid(), _pid);
|
||||
}
|
||||
|
||||
if(_pid == 0)
|
||||
{
|
||||
auto _prefix = std::stringstream{};
|
||||
_prefix << std::setw(_width) << std::right << _n << "/"
|
||||
<< std::setw(_width) << std::left << _causal_env.size() << ": ["
|
||||
<< _main_pid << " -> " << getpid() << "] ";
|
||||
|
||||
auto _env = _base_env;
|
||||
for(const auto& eitr : citr)
|
||||
update_env(_env, eitr.first, eitr.second);
|
||||
print_updated_environment(_env, _prefix.str());
|
||||
print_command(_argv, _prefix.str());
|
||||
_argv.emplace_back(nullptr);
|
||||
_env.emplace_back(nullptr);
|
||||
return execvpe(_argv.front(), _argv.data(), _env.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
add_child_pid(_pid);
|
||||
auto _status = wait_pid(_pid);
|
||||
auto _ret = diagnose_status(_pid, _status);
|
||||
remove_child_pid(_pid);
|
||||
if(_ret != 0) return _ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define TIMEMORY_PROJECT_NAME "rocprof-sys-causal"
|
||||
|
||||
#include <csignal>
|
||||
#include <map>
|
||||
#include <sched.h>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
int
|
||||
get_verbose();
|
||||
|
||||
std::string
|
||||
get_realpath(const std::string&);
|
||||
|
||||
void
|
||||
print_command(const std::vector<char*>& _argv, std::string_view);
|
||||
|
||||
void print_updated_environment(std::vector<char*>, std::string_view);
|
||||
|
||||
std::vector<char*>
|
||||
get_initial_environment();
|
||||
|
||||
void
|
||||
prepare_command_for_run(char*, std::vector<char*>&);
|
||||
|
||||
void
|
||||
prepare_environment_for_run(std::vector<char*>&);
|
||||
|
||||
std::string
|
||||
get_internal_libpath(const std::string& _lib);
|
||||
|
||||
std::string
|
||||
get_internal_script_path(void);
|
||||
|
||||
template <typename Tp>
|
||||
void
|
||||
update_env(std::vector<char*>&, std::string_view, Tp&&, bool _append = false,
|
||||
std::string_view _join_delim = ":");
|
||||
|
||||
template <typename Tp>
|
||||
void
|
||||
add_default_env(std::vector<char*>&, std::string_view, Tp&&);
|
||||
|
||||
void
|
||||
remove_env(std::vector<char*>&, std::string_view);
|
||||
|
||||
std::vector<char*>
|
||||
parse_args(int argc, char** argv, std::vector<char*>&,
|
||||
std::vector<std::map<std::string_view, std::string>>&);
|
||||
|
||||
using sigaction_t = struct sigaction;
|
||||
|
||||
struct signal_handler
|
||||
{
|
||||
sigaction_t m_custom_sigaction = {};
|
||||
sigaction_t m_original_sigaction = {};
|
||||
};
|
||||
|
||||
void
|
||||
forward_signals(const std::set<int>&);
|
||||
|
||||
void add_child_pid(pid_t);
|
||||
|
||||
void remove_child_pid(pid_t);
|
||||
|
||||
int
|
||||
wait_pid(pid_t, int = 0);
|
||||
|
||||
int
|
||||
diagnose_status(pid_t, int);
|
||||
@@ -0,0 +1,26 @@
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# TODO: [DFG] Remove this file after 'rocprofsys' rebranding is complete
|
||||
# rocprofiler-systems-exe target (deprecated 'omnitrace' executable, now 'rocprofiler-systems-instrument')
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_executable(rocprofiler-systems-exe ${CMAKE_CURRENT_LIST_DIR}/rocprof-sys.cpp)
|
||||
|
||||
target_link_libraries(
|
||||
rocprofiler-systems-exe
|
||||
PRIVATE rocprofiler-systems::rocprofiler-systems-threading
|
||||
)
|
||||
|
||||
set_target_properties(
|
||||
rocprofiler-systems-exe
|
||||
PROPERTIES
|
||||
OUTPUT_NAME rocprof-sys
|
||||
BUILD_RPATH "\$ORIGIN:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
|
||||
INSTALL_RPATH "${ROCPROFSYS_EXE_INSTALL_RPATH}"
|
||||
INSTALL_RPATH_USE_LINK_PATH ON
|
||||
)
|
||||
|
||||
rocprofiler_systems_strip_target(rocprofiler-systems-exe)
|
||||
|
||||
install(TARGETS rocprofiler-systems-exe DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
@@ -0,0 +1,162 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <future>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <pthread.h>
|
||||
#include <regex>
|
||||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
static const char* _warning = R"warning(
|
||||
|
||||
WWWWWWWW WWWWWWWW iiii !!!
|
||||
W::::::W W::::::W i::::i !!:!!
|
||||
W::::::W W::::::W iiii !:::!
|
||||
W::::::W W::::::W !:::!
|
||||
W:::::W WWWWW W:::::Waaaaaaaaaaaaa rrrrr rrrrrrrrr nnnn nnnnnnnn iiiiiiinnnn nnnnnnnn ggggggggg ggggg!:::!
|
||||
W:::::W W:::::W W:::::W a::::::::::::a r::::rrr:::::::::r n:::nn::::::::nn i:::::in:::nn::::::::nn g:::::::::ggg::::g!:::!
|
||||
W:::::W W:::::::W W:::::W aaaaaaaaa:::::ar:::::::::::::::::r n::::::::::::::nn i::::in::::::::::::::nn g:::::::::::::::::g!:::!
|
||||
W:::::W W:::::::::W W:::::W a::::arr::::::rrrrr::::::rnn:::::::::::::::n i::::inn:::::::::::::::ng::::::ggggg::::::gg!:::!
|
||||
W:::::W W:::::W:::::W W:::::W aaaaaaa:::::a r:::::r r:::::r n:::::nnnn:::::n i::::i n:::::nnnn:::::ng:::::g g:::::g !:::!
|
||||
W:::::W W:::::W W:::::W W:::::W aa::::::::::::a r:::::r rrrrrrr n::::n n::::n i::::i n::::n n::::ng:::::g g:::::g !:::!
|
||||
W:::::W:::::W W:::::W:::::W a::::aaaa::::::a r:::::r n::::n n::::n i::::i n::::n n::::ng:::::g g:::::g !!:!!
|
||||
W:::::::::W W:::::::::W a::::a a:::::a r:::::r n::::n n::::n i::::i n::::n n::::ng::::::g g:::::g !!!
|
||||
W:::::::W W:::::::W a::::a a:::::a r:::::r n::::n n::::ni::::::i n::::n n::::ng:::::::ggggg:::::g
|
||||
W:::::W W:::::W a:::::aaaa::::::a r:::::r n::::n n::::ni::::::i n::::n n::::n g::::::::::::::::g !!!
|
||||
W:::W W:::W a::::::::::aa:::ar:::::r n::::n n::::ni::::::i n::::n n::::n gg::::::::::::::g !!:!!
|
||||
WWW WWW aaaaaaaaaa aaaarrrrrrr nnnnnn nnnnnniiiiiiii nnnnnn nnnnnn gggggggg::::::g !!!
|
||||
g:::::g
|
||||
gggggg g:::::g
|
||||
g:::::gg gg:::::g
|
||||
g::::::ggg:::::::g
|
||||
gg:::::::::::::g
|
||||
ggg::::::ggg
|
||||
gggggg
|
||||
|
||||
|
||||
ROCm Systems Profiler has renamed the "rocprof-sys" executable to "rocprof-sys-instrument".
|
||||
|
||||
This executable only exists to provide this deprecation warning and maintain backwards compatibility for a few releases.
|
||||
This executable will soon invoke "rocprof-sys-instrument" with the arguments you just provided after we've given you
|
||||
a chance to read this message.
|
||||
|
||||
If you are running this job interactively, please acknowledge that you've read this message and whether you want to continue.
|
||||
If you are running this job non-interactively, we will resume executing after ~1 minute unless CI or ROCPROFSYS_CI is defined
|
||||
in the environment, in which case, we will throw an error.
|
||||
|
||||
Thanks for using ROCm Systems Profiler and happy optimizing!
|
||||
)warning";
|
||||
|
||||
auto _completed = std::promise<void>{};
|
||||
bool _acknowledged = false;
|
||||
auto _emit_warning = []() {
|
||||
// emit warning
|
||||
std::cerr << _warning << std::endl;
|
||||
};
|
||||
auto _get_env = [](const char* _var) {
|
||||
auto* _val = getenv(_var);
|
||||
if(_val == nullptr) return false;
|
||||
return !std::regex_match(
|
||||
_val, std::regex{ "0|false|off|no", std::regex_constants::icase });
|
||||
};
|
||||
auto _env_failure = [_emit_warning, argv](std::string_view _env_var) {
|
||||
// emit warning
|
||||
_emit_warning();
|
||||
std::cerr
|
||||
<< "[" << argv[0] << "] Detected " << _env_var
|
||||
<< " environment variable. Exiting to prevent consuming CI resources. "
|
||||
"Use \"rocprof-sys-instrument\" executable instead of \"rocprof-sys\" "
|
||||
"to prevent this error."
|
||||
<< std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
};
|
||||
auto _wait_for_input = [&_completed, &_acknowledged, _emit_warning]() {
|
||||
// emit warning and wait for input
|
||||
_emit_warning();
|
||||
|
||||
std::cerr << "Do you want to continue? [Y/n] " << std::flush;
|
||||
auto _input = char{};
|
||||
std::cin.get(_input);
|
||||
|
||||
_input = tolower(_input);
|
||||
if(_input == 'n') std::exit(EXIT_SUCCESS);
|
||||
|
||||
_acknowledged = true;
|
||||
_completed.set_value();
|
||||
};
|
||||
|
||||
for(const auto* itr : { "CI", "ROCPROFSYS_CI" })
|
||||
{
|
||||
if(_get_env(itr)) _env_failure(itr);
|
||||
}
|
||||
|
||||
{
|
||||
auto _thr = std::thread{ _wait_for_input };
|
||||
|
||||
_completed.get_future().wait_for(std::chrono::seconds{ 60 });
|
||||
|
||||
if(!_acknowledged)
|
||||
{
|
||||
std::cerr << "[" << argv[0]
|
||||
<< "] No acknowledgement after 1 minute. Continuing..."
|
||||
<< std::endl;
|
||||
_thr.detach();
|
||||
}
|
||||
else
|
||||
_thr.join();
|
||||
}
|
||||
|
||||
// generate the new command
|
||||
auto _argv = std::vector<char*>{};
|
||||
_argv.emplace_back(
|
||||
strdup(std::string{ std::string{ argv[0] } + "-instrument" }.c_str()));
|
||||
for(int i = 1; i < argc; ++i)
|
||||
_argv.emplace_back(argv[i]);
|
||||
|
||||
// echo the new command for diagnostic purposes
|
||||
auto _cmdss = std::stringstream{};
|
||||
for(const auto& itr : _argv)
|
||||
if(itr) _cmdss << " " << itr;
|
||||
|
||||
auto _cmd = _cmdss.str();
|
||||
if(!_cmd.empty())
|
||||
std::cerr << "[" << argv[0] << "] Executing: \"" << _cmd.substr(1) << "\"...\n"
|
||||
<< std::endl;
|
||||
|
||||
// make sure command ends with nullptr
|
||||
_argv.emplace_back(nullptr);
|
||||
|
||||
return execvp(_argv.front(), _argv.data());
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# rocprofiler-systems-instrument target (formerly rocprofiler-systems-exe target prior to 1.8.1)
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF)
|
||||
|
||||
add_executable(rocprofiler-systems-instrument)
|
||||
|
||||
target_sources(
|
||||
rocprofiler-systems-instrument
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/details.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/function_signature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/function_signature.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/fwd.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/info.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal_libs.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal_libs.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/log.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/log.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/module_function.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/module_function.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprof-sys-instrument.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprof-sys-instrument.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
rocprofiler-systems-instrument
|
||||
PRIVATE
|
||||
rocprofiler-systems::rocprofiler-systems-headers
|
||||
rocprofiler-systems::rocprofiler-systems-dyninst
|
||||
rocprofiler-systems::rocprofiler-systems-compile-options
|
||||
rocprofiler-systems::rocprofiler-systems-compile-definitions
|
||||
rocprofiler-systems::rocprofiler-systems-sanitizer
|
||||
timemory::timemory-headers
|
||||
timemory::timemory-extensions
|
||||
timemory::timemory-core
|
||||
)
|
||||
|
||||
add_target_flag_if_avail(rocprofiler-systems-instrument "-Wno-deprecated-declarations")
|
||||
|
||||
set_target_properties(
|
||||
rocprofiler-systems-instrument
|
||||
PROPERTIES
|
||||
BUILD_RPATH "\$ORIGIN:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
|
||||
INSTALL_RPATH "${ROCPROFSYS_EXE_INSTALL_RPATH}"
|
||||
INSTALL_RPATH_USE_LINK_PATH ON
|
||||
OUTPUT_NAME ${BINARY_NAME_PREFIX}-instrument
|
||||
)
|
||||
|
||||
if(ROCPROFSYS_BUILD_DYNINST)
|
||||
target_compile_definitions(
|
||||
rocprofiler-systems-instrument
|
||||
PRIVATE ROCPROFSYS_BUILD_DYNINST=1
|
||||
)
|
||||
endif()
|
||||
|
||||
add_target_flag_if_avail(rocprofiler-systems-instrument "-Wno-deprecated-declarations")
|
||||
|
||||
rocprofiler_systems_strip_target(rocprofiler-systems-instrument)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^(DEBUG|Debug)")
|
||||
string(REPLACE " " ";" _FLAGS "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
target_compile_options(rocprofiler-systems-instrument PRIVATE ${_FLAGS})
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS rocprofiler-systems-instrument
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
OPTIONAL
|
||||
)
|
||||
@@ -0,0 +1,984 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "function_signature.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "log.hpp"
|
||||
#include "rocprof-sys-instrument.hpp"
|
||||
|
||||
#include <timemory/components/rusage/components.hpp>
|
||||
#include <timemory/components/timing/wall_clock.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <link.h>
|
||||
#include <linux/limits.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
static int expect_error = NO_ERROR;
|
||||
static int error_print = 0;
|
||||
|
||||
// set of whole function names to exclude
|
||||
strset_t
|
||||
get_whole_function_names()
|
||||
{
|
||||
return strset_t{
|
||||
"sem_init", "sem_destroy", "sem_open", "sem_close", "sem_post", "sem_wait",
|
||||
"sem_getvalue", "sem_clockwait", "sem_timedwait", "sem_trywait", "sem_unlink",
|
||||
"fork", "do_futex_wait", "dl_iterate_phdr", "dlinfo", "dlopen", "dlmopen",
|
||||
"dlvsym", "dlsym", "dlerror", "dladdr", "_dl_sym", "_dl_vsym", "_dl_addr",
|
||||
"_dl_relocate_static_pie", "getenv", "setenv", "unsetenv", "printf", "fprintf",
|
||||
"vprintf", "buffered_vfprintf", "vfprintf", "printf_positional", "puts", "fputs",
|
||||
"vfputs", "fflush", "fwrite", "malloc", "malloc_stats", "malloc_trim", "mallopt",
|
||||
"calloc", "free", "pvalloc", "valloc", "sysmalloc", "posix_memalign", "freehook",
|
||||
"mallochook", "memalignhook", "mprobe", "reallochook", "mmap", "munmap", "fopen",
|
||||
"fclose", "fmemopen", "fmemclose", "backtrace", "backtrace_symbols",
|
||||
"backtrace_symbols_fd", "sigaddset", "sigandset", "sigdelset", "sigemptyset",
|
||||
"sigfillset", "sighold", "sigisemptyset", "sigismember", "sigorset", "sigrelse",
|
||||
"sigvec", "strtok", "strstr", "sbrk", "strxfrm", "atexit", "ompt_start_tool",
|
||||
"nanosleep", "cfree", "tolower", "toupper", "fileno", "fileno_unlocked", "exit",
|
||||
"quick_exit", "abort", "mbind", "migrate_pages", "move_pages",
|
||||
"numa_migrate_pages", "numa_move_pages", "numa_alloc", "numa_alloc_local",
|
||||
"numa_alloc_interleaved", "numa_alloc_onnode", "numa_realloc", "numa_free",
|
||||
"round_and_return", "_init", "_fini", "_start", "__do_global_dtors_aux",
|
||||
"__libc_csu_init", "__libc_csu_fini", "__hip_module_ctor", "__hip_module_dtor",
|
||||
"__hipRegisterManagedVar", "__hipRegisterFunction", "__hipPushCallConfiguration",
|
||||
"__hipPopCallConfiguration", "hipApiName", "enlarge_userbuf",
|
||||
// below are functions which never terminate
|
||||
"rocr::core::Signal::WaitAny", "rocr::core::Runtime::AsyncEventsLoop",
|
||||
"rocr::core::BusyWaitSignal::WaitAcquire",
|
||||
"rocr::core::BusyWaitSignal::WaitRelaxed", "rocr::HSA::hsa_signal_wait_scacquire",
|
||||
"rocr::os::ThreadTrampoline", "rocr::image::ImageRuntime::CreateImageManager",
|
||||
"rocr::AMD::GpuAgent::GetInfo", "rocr::HSA::hsa_agent_get_info",
|
||||
"event_base_loop", "bootstrapRoot", "bootstrapNetAccept", "ncclCommInitRank",
|
||||
"ncclCommInitAll", "ncclCommDestroy", "ncclCommCount", "ncclCommCuDevice",
|
||||
"ncclCommUserRank", "ncclReduce", "ncclBcast", "ncclBroadcast", "ncclAllReduce",
|
||||
"ncclReduceScatter", "ncclAllGather", "ncclGroupStart", "ncclGroupEnd",
|
||||
"ncclSend", "ncclRecv", "ncclGather", "ncclScatter", "ncclAllToAll",
|
||||
"ncclAllToAllv", "ncclSocketAccept", "vaBeginPicture", "vaCreateBuffer",
|
||||
"vaCreateConfig", "vaCreateContext", "vaCreateSurfaces", "vaDestroySurfaces",
|
||||
"vaSyncSurface", "vaDestroyBuffer", "vaDestroyConfig", "vaDestroyContext",
|
||||
"vaEndPicture", "vaExportSurfaceHandle", "vaGetConfigAttributes", "vaInitialize",
|
||||
"vaQueryConfigEntrypoints", "vaQuerySurfaceAttributes", "vaQuerySurfaceStatus",
|
||||
"vaRenderPicture", "vaTerminate", "vaDisplayIsValid"
|
||||
};
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Helper functions because the syntax for getting a function or module name is unwieldy
|
||||
//
|
||||
std::string_view
|
||||
get_name(procedure_t* _func)
|
||||
{
|
||||
static auto _v = std::unordered_map<procedure_t*, std::string>{};
|
||||
|
||||
auto itr = _v.find(_func);
|
||||
if(itr == _v.end())
|
||||
{
|
||||
_v.emplace(_func, (_func) ? _func->getDemangledName() : std::string{});
|
||||
}
|
||||
|
||||
return _v.at(_func);
|
||||
}
|
||||
|
||||
std::string_view
|
||||
get_name(module_t* _module)
|
||||
{
|
||||
static auto _v = std::unordered_map<module_t*, std::string>{};
|
||||
|
||||
auto itr = _v.find(_module);
|
||||
if(itr == _v.end())
|
||||
{
|
||||
char _name[FUNCNAMELEN + 1];
|
||||
memset(_name, '\0', FUNCNAMELEN + 1);
|
||||
|
||||
if(_module)
|
||||
{
|
||||
_module->getFullName(_name, FUNCNAMELEN);
|
||||
_v.emplace(_module, std::string{ _name });
|
||||
}
|
||||
else
|
||||
{
|
||||
_v.emplace(nullptr, std::string{});
|
||||
}
|
||||
}
|
||||
|
||||
return _v.at(_module);
|
||||
}
|
||||
|
||||
symtab_func_t*
|
||||
get_symtab_function(procedure_t* _func)
|
||||
{
|
||||
static auto _v = std::unordered_map<procedure_t*, symtab_func_t*>{};
|
||||
|
||||
auto itr = _v.find(_func);
|
||||
if(itr == _v.end())
|
||||
{
|
||||
auto _name = _func->getName();
|
||||
{
|
||||
auto nitr = symtab_data.mangled_symbol_names.find(_name);
|
||||
if(nitr != symtab_data.mangled_symbol_names.end())
|
||||
{
|
||||
_v.emplace(_func, nitr->second->getFunction());
|
||||
return _v.at(_func);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& fitr : symtab_data.symbols)
|
||||
{
|
||||
if(_name == fitr.first->getName())
|
||||
{
|
||||
_v.emplace(_func, fitr.first);
|
||||
return _v.at(_func);
|
||||
}
|
||||
}
|
||||
|
||||
auto _dname = _func->getDemangledName();
|
||||
|
||||
{
|
||||
auto nitr = symtab_data.typed_func_names.find(_dname);
|
||||
if(nitr != symtab_data.typed_func_names.end())
|
||||
{
|
||||
_v.emplace(_func, nitr->second);
|
||||
return _v.at(_func);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto nitr = symtab_data.typed_symbol_names.find(_dname);
|
||||
if(nitr != symtab_data.typed_symbol_names.end())
|
||||
{
|
||||
_v.emplace(_func, nitr->second->getFunction());
|
||||
return _v.at(_func);
|
||||
}
|
||||
}
|
||||
|
||||
if(_v.find(_func) == _v.end()) _v.emplace(_func, nullptr);
|
||||
}
|
||||
|
||||
return _v.at(_func);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string
|
||||
get_return_type(procedure_t* func)
|
||||
{
|
||||
if(func && func->isInstrumentable() && func->getReturnType())
|
||||
return func->getReturnType()->getName();
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
auto
|
||||
get_parameter_types(procedure_t* func)
|
||||
{
|
||||
auto _param_names = std::vector<std::string>{};
|
||||
if(func && func->isInstrumentable())
|
||||
{
|
||||
auto* _params = func->getParams();
|
||||
if(_params)
|
||||
{
|
||||
_param_names.reserve(_params->size());
|
||||
for(auto* itr : *_params)
|
||||
{
|
||||
std::string _name = itr->getType()->getName();
|
||||
if(_name.empty()) _name = itr->getName();
|
||||
_param_names.emplace_back(_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _param_names;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// We create a new name that embeds the file and line information in the name
|
||||
//
|
||||
function_signature
|
||||
get_func_file_line_info(module_t* module, procedure_t* func)
|
||||
{
|
||||
using address_t = Dyninst::Address;
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Getting function line info for", get_name(func));
|
||||
|
||||
auto _file_name = get_name(module);
|
||||
auto _func_name = get_name(func);
|
||||
auto _return_type = get_return_type(func);
|
||||
auto _param_types = get_parameter_types(func);
|
||||
auto _base_addr = address_t{};
|
||||
auto _last_addr = address_t{};
|
||||
auto _src_lines = std::vector<statement_t>{};
|
||||
|
||||
if(func->getAddressRange(_base_addr, _last_addr) &&
|
||||
module->getSourceLines(_base_addr, _src_lines) && !_src_lines.empty())
|
||||
{
|
||||
auto _row = _src_lines.front().lineNumber();
|
||||
return function_signature(_return_type, _func_name, _file_name, _param_types,
|
||||
{ _row, 0 }, { 0, 0 }, false, true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return function_signature(_return_type, _func_name, _file_name, _param_types,
|
||||
{ 0, 0 }, { 0, 0 }, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Gets information (line number, filename, and column number) about
|
||||
// the instrumented loop and formats it properly.
|
||||
//
|
||||
function_signature
|
||||
get_loop_file_line_info(module_t* module, procedure_t* func, flow_graph_t*,
|
||||
basic_loop_t* loopToInstrument)
|
||||
{
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Getting loop line info for", get_name(func));
|
||||
|
||||
auto basic_blocks = std::vector<BPatch_basicBlock*>{};
|
||||
loopToInstrument->getLoopBasicBlocksExclusive(basic_blocks);
|
||||
|
||||
if(basic_blocks.empty()) return function_signature{ "", "", "" };
|
||||
|
||||
auto* _block = basic_blocks.front();
|
||||
auto _base_addr = _block->getStartAddress();
|
||||
auto _last_addr = _block->getEndAddress();
|
||||
for(const auto& itr : basic_blocks)
|
||||
{
|
||||
if(itr == _block) continue;
|
||||
if(itr->dominates(_block))
|
||||
{
|
||||
_base_addr = itr->getStartAddress();
|
||||
_last_addr = itr->getEndAddress();
|
||||
_block = itr;
|
||||
}
|
||||
}
|
||||
|
||||
auto _file_name = get_name(module);
|
||||
auto _func_name = get_name(func);
|
||||
auto _return_type = get_return_type(func);
|
||||
auto _param_types = get_parameter_types(func);
|
||||
auto _lines_beg = std::vector<statement_t>{};
|
||||
auto _lines_end = std::vector<statement_t>{};
|
||||
|
||||
if(module->getSourceLines(_base_addr, _lines_beg))
|
||||
{
|
||||
// filename = lines[0].fileName();
|
||||
int _row1 = 0;
|
||||
int _col1 = 0;
|
||||
for(auto& itr : _lines_beg)
|
||||
{
|
||||
if(itr.lineNumber() > 0)
|
||||
{
|
||||
_row1 = itr.lineNumber();
|
||||
_col1 = itr.lineOffset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_row1 == 0 && _col1 == 0)
|
||||
return function_signature(_return_type, _func_name, _file_name, _param_types);
|
||||
|
||||
int _row2 = 0;
|
||||
int _col2 = 0;
|
||||
for(auto& itr : _lines_beg)
|
||||
{
|
||||
_row2 = std::max(_row2, itr.lineNumber());
|
||||
_col2 = std::max(_col2, itr.lineOffset());
|
||||
}
|
||||
|
||||
if(_col1 < 0) _col1 = 0;
|
||||
|
||||
if(module->getSourceLines(_last_addr, _lines_end))
|
||||
{
|
||||
for(auto& itr : _lines_end)
|
||||
{
|
||||
_row2 = std::max(_row2, itr.lineNumber());
|
||||
_col2 = std::max(_col2, itr.lineOffset());
|
||||
}
|
||||
if(_col2 < 0) _col2 = 0;
|
||||
if(_row2 < _row1) _row1 = _row2; // Fix for wrong line numbers
|
||||
|
||||
return function_signature(_return_type, _func_name, _file_name, _param_types,
|
||||
{ _row1, _row2 }, { _col1, _col2 }, true, true,
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return function_signature(_return_type, _func_name, _file_name, _param_types,
|
||||
{ _row1, 0 }, { _col1, 0 }, true, true, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return function_signature(_return_type, _func_name, _file_name, _param_types,
|
||||
{ 0, 0 }, { 0, 0 }, true, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Gets information (line number, filename, and column number) about
|
||||
// the instrumented loop and formats it properly.
|
||||
//
|
||||
std::map<basic_block_t*, basic_block_signature>
|
||||
get_basic_block_file_line_info(module_t* module, procedure_t* func)
|
||||
{
|
||||
std::map<basic_block_t*, basic_block_signature> _data{};
|
||||
if(!func) return _data;
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Getting basic block line info for", get_name(func));
|
||||
|
||||
auto* _cfg = func->getCFG();
|
||||
auto _basic_blocks = std::set<BPatch_basicBlock*>{};
|
||||
_cfg->getAllBasicBlocks(_basic_blocks);
|
||||
|
||||
if(_basic_blocks.empty()) return _data;
|
||||
|
||||
auto _file_name = get_name(module);
|
||||
auto _func_name = get_name(func);
|
||||
auto _return_type = get_return_type(func);
|
||||
auto _param_types = get_parameter_types(func);
|
||||
|
||||
for(auto&& itr : _basic_blocks)
|
||||
{
|
||||
auto _base_addr = itr->getStartAddress();
|
||||
auto _last_addr = itr->getEndAddress();
|
||||
|
||||
verbprintf(4,
|
||||
"[%s][%s] basic_block: size = %lu: base_addr = %lu, last_addr = %lu\n",
|
||||
_file_name.data(), _func_name.data(),
|
||||
(unsigned long) (_last_addr - _base_addr), _base_addr, _last_addr);
|
||||
|
||||
auto _lines_beg = std::vector<statement_t>{};
|
||||
auto _lines_end = std::vector<statement_t>{};
|
||||
|
||||
if(module->getSourceLines(_base_addr, _lines_beg) && !_lines_beg.empty())
|
||||
{
|
||||
int _row1 = _lines_beg.front().lineNumber();
|
||||
int _col1 = _lines_beg.front().lineOffset();
|
||||
|
||||
verbprintf(4, "size of _lines_end = %lu\n",
|
||||
(unsigned long) _lines_end.size());
|
||||
|
||||
if(module->getSourceLines(_last_addr, _lines_end) && !_lines_end.empty())
|
||||
{
|
||||
int _row2 = _lines_end.back().lineNumber();
|
||||
int _col2 = _lines_end.back().lineOffset();
|
||||
|
||||
if(_row2 < _row1) std::swap(_row1, _row2);
|
||||
if(_row1 == _row2 && _col2 < _col1) std::swap(_col1, _col2);
|
||||
|
||||
_data.emplace(
|
||||
itr, basic_block_signature{
|
||||
_base_addr, _last_addr,
|
||||
function_signature(_return_type, _func_name, _file_name,
|
||||
_param_types, { _row1, _row2 },
|
||||
{ _col1, _col2 }, true, true, true) });
|
||||
}
|
||||
else
|
||||
{
|
||||
_data.emplace(itr,
|
||||
basic_block_signature{
|
||||
_base_addr, _last_addr,
|
||||
function_signature(_return_type, _func_name, _file_name,
|
||||
_param_types, { _row1, 0 },
|
||||
{ _col1, 0 }, true, true, false) });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_data.emplace(itr, basic_block_signature{
|
||||
_base_addr, _last_addr,
|
||||
function_signature(_return_type, _func_name,
|
||||
_file_name, _param_types) });
|
||||
}
|
||||
}
|
||||
|
||||
return _data;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// We create a new name that embeds the file and line information in the name
|
||||
//
|
||||
std::vector<statement_t>
|
||||
get_source_code(module_t* module, procedure_t* func)
|
||||
{
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Getting source code for", get_name(func));
|
||||
|
||||
std::vector<statement_t> _lines{};
|
||||
if(!module || !func) return _lines;
|
||||
auto* _cfg = func->getCFG();
|
||||
std::set<BPatch_basicBlock*> _basic_blocks{};
|
||||
_cfg->getAllBasicBlocks(_basic_blocks);
|
||||
|
||||
for(auto&& itr : _basic_blocks)
|
||||
{
|
||||
auto _base_addr = itr->getStartAddress();
|
||||
auto _last_addr = itr->getEndAddress();
|
||||
for(decltype(_base_addr) _addr = _base_addr; _addr <= _last_addr; ++_addr)
|
||||
{
|
||||
std::vector<statement_t> _src{};
|
||||
if(module->getSourceLines(_addr, _src))
|
||||
{
|
||||
for(auto&& iitr : _src)
|
||||
_lines.emplace_back(iitr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _lines;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// For compatibility purposes
|
||||
//
|
||||
procedure_t*
|
||||
find_function(image_t* app_image, const std::string& _name, const strset_t& _extra)
|
||||
{
|
||||
if(_name.empty()) return nullptr;
|
||||
|
||||
auto _find = [app_image](const std::string& _f) -> procedure_t* {
|
||||
// Extract the vector of functions
|
||||
std::vector<procedure_t*> _found;
|
||||
auto* ret = app_image->findFunction(_f.c_str(), _found, false, true, true);
|
||||
if(ret == nullptr || _found.empty()) return nullptr;
|
||||
return _found.at(0);
|
||||
};
|
||||
|
||||
procedure_t* _func = _find(_name);
|
||||
auto itr = _extra.begin();
|
||||
while(_func == nullptr && itr != _extra.end())
|
||||
{
|
||||
_func = _find(*itr);
|
||||
++itr;
|
||||
}
|
||||
|
||||
if(!_func)
|
||||
{
|
||||
verbprintf(1, "function: '%s' ... not found\n", _name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
verbprintf(1, "function: '%s' ... found\n", _name.c_str());
|
||||
}
|
||||
|
||||
return _func;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Get the realpath to this exe
|
||||
//
|
||||
bool
|
||||
is_text_file(const std::string& filename)
|
||||
{
|
||||
std::ifstream _file{ filename, std::ios::in | std::ios::binary };
|
||||
if(!_file.is_open())
|
||||
{
|
||||
errprintf(-1, "Error! '%s' could not be opened...\n", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr size_t buffer_size = 1024;
|
||||
char buffer[buffer_size];
|
||||
while(_file.read(buffer, sizeof(buffer)))
|
||||
{
|
||||
for(char itr : buffer)
|
||||
{
|
||||
if(itr == '\0') return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_file.gcount() > 0)
|
||||
{
|
||||
for(std::streamsize i = 0; i < _file.gcount(); ++i)
|
||||
{
|
||||
if(buffer[i] == '\0') return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Get the realpath to this exe
|
||||
//
|
||||
std::string&
|
||||
rocprofsys_get_exe_realpath()
|
||||
{
|
||||
static std::string _v = []() {
|
||||
auto _cmd_line = tim::read_command_line(tim::process::get_id());
|
||||
if(!_cmd_line.empty())
|
||||
{
|
||||
using array_config_t = timemory::join::array_config;
|
||||
ROCPROFSYS_ADD_DETAILED_LOG_ENTRY(array_config_t{ " ", "[ ", " ]" },
|
||||
"cmdline:: ", _cmd_line);
|
||||
return _cmd_line.front();
|
||||
// return tim::filepath::realpath(_cmd_line.front(), nullptr, false);
|
||||
}
|
||||
return std::string{};
|
||||
}();
|
||||
return _v;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Error callback routine.
|
||||
//
|
||||
std::vector<std::string>
|
||||
rocprofsys_get_link_map(const char* _lib, const std::string& _exclude_linked_by,
|
||||
const std::string& _exclude_re, std::vector<int>&& _open_modes)
|
||||
{
|
||||
if(_open_modes.empty()) _open_modes = { (RTLD_LAZY | RTLD_NOLOAD) };
|
||||
|
||||
auto _get_chain = [&_open_modes](const char* _name) {
|
||||
void* _handle = nullptr;
|
||||
bool _noload = false;
|
||||
for(auto _mode : _open_modes)
|
||||
{
|
||||
_handle = dlopen(_name, _mode);
|
||||
_noload = (_mode & RTLD_NOLOAD) == RTLD_NOLOAD;
|
||||
if(_handle) break;
|
||||
}
|
||||
|
||||
auto _chain = std::vector<std::string>{};
|
||||
if(_handle)
|
||||
{
|
||||
struct link_map* _link_map = nullptr;
|
||||
dlinfo(_handle, RTLD_DI_LINKMAP, &_link_map);
|
||||
struct link_map* _next = _link_map;
|
||||
while(_next)
|
||||
{
|
||||
if(_name == nullptr && _next == _link_map &&
|
||||
std::string_view{ _next->l_name }.empty())
|
||||
{
|
||||
// only insert exe name if dlopened the exe and
|
||||
// empty name is first entry
|
||||
_chain.emplace_back(rocprofsys_get_exe_realpath());
|
||||
}
|
||||
else if(!std::string_view{ _next->l_name }.empty())
|
||||
{
|
||||
_chain.emplace_back(_next->l_name);
|
||||
}
|
||||
_next = _next->l_next;
|
||||
}
|
||||
|
||||
if(_noload == false) dlclose(_handle);
|
||||
}
|
||||
return _chain;
|
||||
};
|
||||
|
||||
auto _full_chain = _get_chain(_lib);
|
||||
auto _excl_chain = (_exclude_linked_by.empty())
|
||||
? std::vector<std::string>{}
|
||||
: _get_chain(_exclude_linked_by.c_str());
|
||||
auto _fini_chain = std::vector<std::string>{};
|
||||
_fini_chain.reserve(_full_chain.size());
|
||||
|
||||
for(const auto& itr : _full_chain)
|
||||
{
|
||||
auto _found = std::any_of(_excl_chain.begin(), _excl_chain.end(),
|
||||
[itr](const auto& _v) { return (itr == _v); });
|
||||
if(!_found)
|
||||
{
|
||||
if(_exclude_re.empty() || !std::regex_search(itr, std::regex{ _exclude_re }))
|
||||
_fini_chain.emplace_back(itr);
|
||||
else
|
||||
_excl_chain.emplace_back(itr);
|
||||
}
|
||||
}
|
||||
|
||||
return _fini_chain;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Get the path of a loaded dynamic binary
|
||||
//
|
||||
std::optional<std::string>
|
||||
rocprofsys_get_loaded_path(const char* _name, std::vector<int>&& _open_modes)
|
||||
{
|
||||
if(_open_modes.empty()) _open_modes = { (RTLD_LAZY | RTLD_NOLOAD) };
|
||||
|
||||
void* _handle = nullptr;
|
||||
bool _noload = false;
|
||||
for(auto _mode : _open_modes)
|
||||
{
|
||||
_handle = dlopen(_name, _mode);
|
||||
_noload = (_mode & RTLD_NOLOAD) == RTLD_NOLOAD;
|
||||
if(_handle) break;
|
||||
}
|
||||
|
||||
if(_handle)
|
||||
{
|
||||
struct link_map* _link_map = nullptr;
|
||||
dlinfo(_handle, RTLD_DI_LINKMAP, &_link_map);
|
||||
if(_link_map != nullptr && !std::string_view{ _link_map->l_name }.empty())
|
||||
{
|
||||
return tim::filepath::realpath(_link_map->l_name, nullptr, false);
|
||||
}
|
||||
if(_noload == false) dlclose(_handle);
|
||||
}
|
||||
|
||||
return std::optional<std::string>{};
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Get the path of a loaded dynamic binary
|
||||
//
|
||||
std::optional<std::string>
|
||||
rocprofsys_get_origin(const char* _name, std::vector<int>&& _open_modes)
|
||||
{
|
||||
if(_open_modes.empty()) _open_modes = { (RTLD_LAZY | RTLD_NOLOAD) };
|
||||
|
||||
void* _handle = nullptr;
|
||||
bool _noload = false;
|
||||
for(auto _mode : _open_modes)
|
||||
{
|
||||
_handle = dlopen(_name, _mode);
|
||||
_noload = (_mode & RTLD_NOLOAD) == RTLD_NOLOAD;
|
||||
if(_handle) break;
|
||||
}
|
||||
|
||||
if(_handle)
|
||||
{
|
||||
char _buffer[PATH_MAX + 1];
|
||||
memset(_buffer, '\0', PATH_MAX * sizeof(char));
|
||||
dlinfo(_handle, RTLD_DI_ORIGIN, _buffer);
|
||||
if(strnlen(_buffer, PATH_MAX + 1) <= PATH_MAX)
|
||||
{
|
||||
return tim::filepath::realpath(_buffer, nullptr, false);
|
||||
}
|
||||
if(_noload == false) dlclose(_handle);
|
||||
}
|
||||
|
||||
return std::optional<std::string>{};
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Error callback routine.
|
||||
//
|
||||
void
|
||||
errorFunc(error_level_t level, int num, const char** params)
|
||||
{
|
||||
error_func_real(level, num, params);
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
void
|
||||
error_func_real(error_level_t level, int num, const char* const* params)
|
||||
{
|
||||
char line[4096];
|
||||
|
||||
const char* msg = bpatch->getEnglishErrorString(num);
|
||||
bpatch->formatErrorString(line, sizeof(line), msg, params);
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Dyninst error function called with level", level,
|
||||
":: ID# =", num, "::", line)
|
||||
.force(level < BPatchInfo);
|
||||
|
||||
if(num == 0)
|
||||
{
|
||||
// conditional reporting of warnings and informational messages
|
||||
if(error_print > 0)
|
||||
{
|
||||
if(level == BPatchInfo)
|
||||
{
|
||||
errprintf(2, "%s :: %i :: %s\n%s", std::to_string(level).c_str(), num,
|
||||
line, tim::log::color::end());
|
||||
}
|
||||
else
|
||||
{
|
||||
verbprintf(0, "%s :: %i :: %s\n%s", std::to_string(level).c_str(), num,
|
||||
line, tim::log::color::end());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// reporting of actual errors
|
||||
if(num != expect_error)
|
||||
{
|
||||
verbprintf(-1, "%s :: %i :: %s\n%s", std::to_string(level).c_str(), num, line,
|
||||
tim::log::color::end());
|
||||
// We consider some errors fatal.
|
||||
if(num == 101) throw std::runtime_error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Just log it
|
||||
//
|
||||
void
|
||||
error_func_fake(error_level_t level, int num, const char* const* params)
|
||||
{
|
||||
char line[4096];
|
||||
|
||||
const char* msg = bpatch->getEnglishErrorString(num);
|
||||
bpatch->formatErrorString(line, sizeof(line), msg, params);
|
||||
|
||||
// just log it
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Dyninst error function called with level", level,
|
||||
":: ID# =", num, "::", line)
|
||||
.force(level < BPatchInfo);
|
||||
}
|
||||
|
||||
#include "internal_libs.hpp"
|
||||
|
||||
#include <timemory/components/timing/wall_clock.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
|
||||
using ::timemory::join::join;
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Read the symtab data from Dyninst
|
||||
//
|
||||
void
|
||||
process_modules(const std::vector<module_t*>& _app_modules)
|
||||
{
|
||||
parse_internal_libs_data();
|
||||
|
||||
auto _erase_nullptrs = [](auto& _vec) {
|
||||
_vec.erase(std::remove_if(_vec.begin(), _vec.end(),
|
||||
[](const auto* itr) { return (itr == nullptr); }),
|
||||
_vec.end());
|
||||
};
|
||||
|
||||
auto _wc = tim::component::wall_clock{};
|
||||
auto _pr = tim::component::peak_rss{};
|
||||
_wc.start();
|
||||
_pr.start();
|
||||
|
||||
for(auto* itr : _app_modules)
|
||||
{
|
||||
auto* _module = SymTab::convert(itr);
|
||||
if(_module) symtab_data.modules.emplace_back(_module);
|
||||
}
|
||||
|
||||
_erase_nullptrs(symtab_data.modules);
|
||||
|
||||
verbprintf(0, "Processing %zu modules...\n", symtab_data.modules.size());
|
||||
|
||||
if(symtab_data.modules.empty()) return;
|
||||
|
||||
const auto& _data = get_internal_libs_data();
|
||||
auto _names = std::set<std::string_view>{};
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
if(!itr.first.empty())
|
||||
{
|
||||
_names.emplace(itr.first);
|
||||
for(const auto& ditr : itr.second)
|
||||
_names.emplace(ditr.first);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto* itr : symtab_data.modules)
|
||||
{
|
||||
const auto* _base_name = tim::filepath::basename(itr->fullName());
|
||||
auto _real_name = tim::filepath::realpath(itr->fullName(), nullptr, false);
|
||||
|
||||
if(!_base_name) continue;
|
||||
|
||||
if(_names.count(_base_name) == 0 && _names.count(_real_name) == 0)
|
||||
{
|
||||
verbprintf(2, "Processing symbol table for module '%s'...\n",
|
||||
itr->fullName().c_str());
|
||||
}
|
||||
|
||||
symtab_data.functions.emplace(itr, std::vector<symtab_func_t*>{});
|
||||
if(itr->getAllFunctions().empty()) continue;
|
||||
_erase_nullptrs(symtab_data.functions.at(itr));
|
||||
|
||||
for(auto* fitr : symtab_data.functions.at(itr))
|
||||
{
|
||||
symtab_data.typed_func_names[tim::demangle(fitr->getName())] = fitr;
|
||||
|
||||
symtab_data.symbols.emplace(fitr, std::vector<symtab_symbol_t*>{});
|
||||
if(!fitr->getSymbols(symtab_data.symbols.at(fitr))) continue;
|
||||
_erase_nullptrs(symtab_data.symbols.at(fitr));
|
||||
|
||||
for(auto* sitr : symtab_data.symbols.at(fitr))
|
||||
{
|
||||
symtab_data.mangled_symbol_names[sitr->getMangledName()] = sitr;
|
||||
symtab_data.typed_symbol_names[sitr->getTypedName()] = sitr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_pr.stop();
|
||||
_wc.stop();
|
||||
verbprintf(0, "Processing %zu modules... Done (%.3f %s, %.3f %s)\n",
|
||||
_app_modules.size(), _wc.get(), _wc.display_unit().c_str(), _pr.get(),
|
||||
_pr.display_unit().c_str());
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// I/O assistance
|
||||
//
|
||||
|
||||
namespace std
|
||||
{
|
||||
std::string
|
||||
to_string(instruction_category_t _category)
|
||||
{
|
||||
using namespace Dyninst::InstructionAPI;
|
||||
switch(_category)
|
||||
{
|
||||
case c_CallInsn: return "function_call";
|
||||
case c_ReturnInsn: return "return";
|
||||
case c_BranchInsn: return "branch";
|
||||
case c_CompareInsn: return "compare";
|
||||
case c_PrefetchInsn: return "prefetch";
|
||||
case c_SysEnterInsn: return "sys_enter";
|
||||
case c_SyscallInsn: return "sys_call";
|
||||
case c_VectorInsn: return "vector";
|
||||
case c_GPUKernelExitInsn: return "gpu_kernel_exit";
|
||||
case c_NoCategory: return "no_category";
|
||||
}
|
||||
return std::string{ "unknown_category_id_" } +
|
||||
std::to_string(static_cast<int>(_category));
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(error_level_t _level)
|
||||
{
|
||||
switch(_level)
|
||||
{
|
||||
case BPatchFatal:
|
||||
{
|
||||
return JOIN("", tim::log::color::fatal(), "FatalError");
|
||||
}
|
||||
case BPatchSerious:
|
||||
{
|
||||
return JOIN("", tim::log::color::fatal(), "SeriousError");
|
||||
}
|
||||
case BPatchWarning:
|
||||
{
|
||||
return JOIN("", tim::log::color::warning(), "Warning");
|
||||
}
|
||||
case BPatchInfo:
|
||||
{
|
||||
return JOIN("", tim::log::color::info(), "Info");
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
return JOIN("", tim::log::color::warning(), "UnknownErrorLevel",
|
||||
static_cast<int>(_level));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string&&
|
||||
to_lower(std::string&& _v)
|
||||
{
|
||||
for(auto& itr : std::move(_v))
|
||||
itr = tolower(itr);
|
||||
return std::move(_v);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string
|
||||
to_string(symbol_visibility_t _v)
|
||||
{
|
||||
return to_lower(SymTab::Symbol::symbolVisibility2Str(_v) + 3);
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(symbol_linkage_t _v)
|
||||
{
|
||||
return to_lower(SymTab::Symbol::symbolLinkage2Str(_v) + 3);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
template <typename Tp>
|
||||
Tp
|
||||
from_string(std::string_view _v)
|
||||
{
|
||||
if constexpr(std::is_same<Tp, symbol_visibility_t>::value)
|
||||
{
|
||||
for(const auto& itr :
|
||||
{ SV_UNKNOWN, SV_DEFAULT, SV_INTERNAL, SV_HIDDEN, SV_PROTECTED })
|
||||
if(_v == std::to_string(itr)) return itr;
|
||||
return SV_UNKNOWN;
|
||||
}
|
||||
else if constexpr(std::is_same<Tp, symbol_linkage_t>::value)
|
||||
{
|
||||
for(const auto& itr : { SL_UNKNOWN, SL_GLOBAL, SL_LOCAL, SL_WEAK, SL_UNIQUE })
|
||||
if(_v == std::to_string(itr)) return itr;
|
||||
return SL_UNKNOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(std::is_empty<Tp>::value, "Error! not defined");
|
||||
return Tp{};
|
||||
}
|
||||
}
|
||||
|
||||
template symbol_visibility_t
|
||||
from_string<symbol_visibility_t>(std::string_view _v);
|
||||
|
||||
template symbol_linkage_t
|
||||
from_string<symbol_linkage_t>(std::string_view _v);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& _os, symbol_linkage_t _v)
|
||||
{
|
||||
return (_os << std::to_string(_v));
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& _os, symbol_visibility_t _v)
|
||||
{
|
||||
return (_os << std::to_string(_v));
|
||||
}
|
||||
|
||||
std::istream&
|
||||
operator>>(std::istream& _is, symbol_linkage_t& _v)
|
||||
{
|
||||
auto _v_s = std::string{};
|
||||
_is >> _v_s;
|
||||
_v = from_string<symbol_linkage_t>(_v_s);
|
||||
return _is;
|
||||
}
|
||||
|
||||
std::istream&
|
||||
operator>>(std::istream& _is, symbol_visibility_t& _v)
|
||||
{
|
||||
auto _v_s = std::string{};
|
||||
_is >> _v_s;
|
||||
_v = from_string<symbol_visibility_t>(_v_s);
|
||||
return _is;
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
// 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 "function_signature.hpp"
|
||||
|
||||
function_signature::function_signature(std::string_view _ret, std::string_view _name,
|
||||
std::string_view _file, location_t _row,
|
||||
location_t _col, bool _loop, bool _info_beg,
|
||||
bool _info_end)
|
||||
: m_loop(_loop)
|
||||
, m_info_beg(_info_beg)
|
||||
, m_info_end(_info_end)
|
||||
, m_row(std::move(_row))
|
||||
, m_col(std::move(_col))
|
||||
, m_return(_ret)
|
||||
, m_name(tim::demangle(_name.data()))
|
||||
, m_file(_file)
|
||||
{
|
||||
if(m_file.find('/') != std::string_view::npos)
|
||||
m_file = m_file.substr(m_file.find_last_of('/') + 1);
|
||||
}
|
||||
|
||||
function_signature::function_signature(std::string_view _ret, std::string_view _name,
|
||||
std::string_view _file,
|
||||
const std::vector<std::string>& _params,
|
||||
location_t _row, location_t _col, bool _loop,
|
||||
bool _info_beg, bool _info_end)
|
||||
: function_signature(_ret, _name, _file, _row, _col, _loop, _info_beg, _info_end)
|
||||
{
|
||||
m_params = "(";
|
||||
for(const auto& itr : _params)
|
||||
m_params.append(itr + ", ");
|
||||
if(!_params.empty()) m_params = m_params.substr(0, m_params.length() - 2);
|
||||
m_params += ")";
|
||||
}
|
||||
|
||||
std::string
|
||||
function_signature::get(function_signature& sig)
|
||||
{
|
||||
return sig.get();
|
||||
}
|
||||
|
||||
std::string
|
||||
function_signature::get(bool _all, bool _save) const
|
||||
{
|
||||
if(!_all && _save && !m_signature.empty()) return m_signature;
|
||||
|
||||
std::stringstream ss;
|
||||
if((_all || use_return_info) && !m_return.empty()) ss << m_return << " ";
|
||||
ss << m_name;
|
||||
if(_all || use_args_info) ss << m_params;
|
||||
if(m_loop)
|
||||
{
|
||||
auto _row_col_str = [](unsigned long _row, unsigned long _col) {
|
||||
std::stringstream _ss{};
|
||||
if(_row == 0 && _col == 0) return std::string{};
|
||||
if(_col > 0)
|
||||
_ss << "{" << _row << "," << _col << "}";
|
||||
else
|
||||
_ss << "{" << _row << "}";
|
||||
return _ss.str();
|
||||
};
|
||||
|
||||
auto _rc1 = _row_col_str(m_row.first, m_col.first);
|
||||
auto _rc2 = _row_col_str(m_row.second, m_col.second);
|
||||
if(m_info_end && !_rc1.empty() && !_rc2.empty() && _rc1 != _rc2)
|
||||
ss << " [" << _rc1 << "-" << _rc2 << "]";
|
||||
else if(m_info_end && !_rc1.empty() && !_rc2.empty() && _rc1 == _rc2)
|
||||
ss << " [" << _rc1 << "]";
|
||||
else if(m_info_end && !_rc1.empty() && _rc2.empty())
|
||||
ss << " [" << _rc1 << "]";
|
||||
else if(!m_info_end && !_rc1.empty())
|
||||
ss << " [" << _rc1 << "]";
|
||||
else if(m_loop_num < std::numeric_limits<uint32_t>::max())
|
||||
ss << " [loop#" << m_loop_num << "]";
|
||||
else
|
||||
errprintf(3, "line info for %s is empty! [{%s}] [{%s}]\n", m_name.c_str(),
|
||||
_rc1.c_str(), _rc2.c_str());
|
||||
}
|
||||
if((_all || use_file_info) && m_file.length() > 0) ss << " [" << m_file;
|
||||
if((_all || use_line_info) && m_row.first > 0) ss << ":" << m_row.first;
|
||||
if((_all || use_file_info) && m_file.length() > 0) ss << "]";
|
||||
|
||||
if(_save) m_signature = ss.str();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
function_signature::get_coverage(bool _basic_block) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
if(!m_return.empty()) ss << m_return << " ";
|
||||
ss << m_name << m_params;
|
||||
if(_basic_block && m_loop && m_info_beg)
|
||||
{
|
||||
if(m_file.length() > 0) ss << " [" << m_file << "]";
|
||||
auto _row_col_str = [](unsigned long _row, unsigned long _col) {
|
||||
std::stringstream _ss{};
|
||||
if(_row == 0 && _col == 0) return std::string{};
|
||||
if(_col > 0)
|
||||
_ss << "{" << _row << "," << _col << "}";
|
||||
else
|
||||
_ss << "{" << _row << "}";
|
||||
return _ss.str();
|
||||
};
|
||||
|
||||
auto _rc1 = _row_col_str(m_row.first, m_col.first);
|
||||
auto _rc2 = _row_col_str(m_row.second, m_col.second);
|
||||
if(m_info_end && !_rc1.empty() && !_rc2.empty() && _rc1 != _rc2)
|
||||
ss << " [" << _rc1 << "-" << _rc2 << "]";
|
||||
else if(m_info_end && !_rc1.empty() && !_rc2.empty() && _rc1 == _rc2)
|
||||
ss << " [" << _rc1 << "]";
|
||||
else if(m_info_end && !_rc1.empty() && _rc2.empty())
|
||||
ss << " [" << _rc1 << "]";
|
||||
else if(!m_info_end && !_rc1.empty())
|
||||
ss << " [" << _rc1 << "]";
|
||||
else
|
||||
errprintf(3, "line info for %s is empty!\n", m_name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_file.length() > 0) ss << " [" << m_file;
|
||||
if(m_row.first > 0) ss << ":" << m_row.first;
|
||||
if(m_file.length() > 0) ss << "]";
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// 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 "fwd.hpp"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
struct function_signature
|
||||
{
|
||||
using location_t = std::pair<unsigned long, unsigned long>;
|
||||
|
||||
TIMEMORY_DEFAULT_OBJECT(function_signature)
|
||||
|
||||
function_signature(std::string_view _ret, std::string_view _name,
|
||||
std::string_view _file, location_t _row = { 0, 0 },
|
||||
location_t _col = { 0, 0 }, bool _loop = false,
|
||||
bool _info_beg = false, bool _info_end = false);
|
||||
|
||||
function_signature(std::string_view _ret, std::string_view _name,
|
||||
std::string_view _file, const std::vector<std::string>& _params,
|
||||
location_t _row = { 0, 0 }, location_t _col = { 0, 0 },
|
||||
bool _loop = false, bool _info_beg = false,
|
||||
bool _info_end = false);
|
||||
|
||||
function_signature& set_loop_number(uint32_t _n)
|
||||
{
|
||||
m_loop_num = _n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static std::string get(function_signature& sig);
|
||||
std::string get(bool _all = false, bool _save = true) const;
|
||||
std::string get_coverage(bool _is_basic_block) const;
|
||||
|
||||
bool m_loop = false;
|
||||
bool m_info_beg = false;
|
||||
bool m_info_end = false;
|
||||
uint32_t m_loop_num = std::numeric_limits<uint32_t>::max();
|
||||
location_t m_row = { 0, 0 };
|
||||
location_t m_col = { 0, 0 };
|
||||
std::string m_return = {};
|
||||
std::string m_name = {};
|
||||
std::string m_params = "()";
|
||||
std::string m_file = {};
|
||||
mutable std::string m_signature = {};
|
||||
|
||||
friend bool operator==(const function_signature& lhs, const function_signature& rhs)
|
||||
{
|
||||
return lhs.get() == rhs.get();
|
||||
}
|
||||
|
||||
friend bool operator<(const function_signature& lhs, const function_signature& rhs)
|
||||
{
|
||||
const auto loop_max = std::numeric_limits<uint32_t>::max();
|
||||
if(lhs.m_loop && !rhs.m_loop) return false;
|
||||
if(!lhs.m_loop && rhs.m_loop) return true;
|
||||
if(lhs.m_loop_num < loop_max && rhs.m_loop_num == loop_max) return false;
|
||||
if(lhs.m_loop_num == loop_max && rhs.m_loop_num < loop_max) return true;
|
||||
return std::tie(lhs.m_file, lhs.m_name, lhs.m_return, lhs.m_params,
|
||||
lhs.m_row.first, lhs.m_col.first, lhs.m_loop_num) <
|
||||
std::tie(rhs.m_file, rhs.m_name, rhs.m_return, rhs.m_params,
|
||||
rhs.m_row.first, rhs.m_col.first, rhs.m_loop_num);
|
||||
}
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& _ar, const unsigned)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
(void) get();
|
||||
_ar(cereal::make_nvp("loop", m_loop), cereal::make_nvp("info_beg", m_info_beg),
|
||||
cereal::make_nvp("info_end", m_info_end), cereal::make_nvp("row", m_row),
|
||||
cereal::make_nvp("col", m_col), cereal::make_nvp("return", m_return),
|
||||
cereal::make_nvp("name", m_name), cereal::make_nvp("params", m_params),
|
||||
cereal::make_nvp("file", m_file), cereal::make_nvp("signature", m_signature));
|
||||
(void) get();
|
||||
}
|
||||
};
|
||||
|
||||
struct basic_block_signature
|
||||
{
|
||||
using address_t = Dyninst::Address;
|
||||
|
||||
address_t start_address = {};
|
||||
address_t last_address = {};
|
||||
function_signature signature = {};
|
||||
};
|
||||
@@ -0,0 +1,399 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "log.hpp"
|
||||
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/mpl/apply.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/policy.hpp>
|
||||
#include <timemory/tpls/cereal/archives.hpp>
|
||||
#include <timemory/tpls/cereal/cereal.hpp>
|
||||
#include <timemory/utility/argparse.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/popen.hpp>
|
||||
#include <timemory/variadic/macros.hpp>
|
||||
|
||||
#include <BPatch.h>
|
||||
#include <BPatch_Vector.h>
|
||||
#include <BPatch_addressSpace.h>
|
||||
#include <BPatch_basicBlock.h>
|
||||
#include <BPatch_basicBlockLoop.h>
|
||||
#include <BPatch_callbacks.h>
|
||||
#include <BPatch_function.h>
|
||||
#include <BPatch_instruction.h>
|
||||
#include <BPatch_object.h>
|
||||
#include <BPatch_point.h>
|
||||
#include <BPatch_process.h>
|
||||
#include <BPatch_snippet.h>
|
||||
#include <BPatch_statement.h>
|
||||
#include <Function.h>
|
||||
#include <Instruction.h>
|
||||
#include <InstructionCategories.h>
|
||||
#include <Module.h>
|
||||
#include <Symtab.h>
|
||||
#include <SymtabReader.h>
|
||||
#include <dyntypes.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unistd.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#define MUTNAMELEN 1024
|
||||
#define FUNCNAMELEN 32 * 1024
|
||||
#define NO_ERROR -1
|
||||
#define TIMEMORY_BIN_DIR "bin"
|
||||
|
||||
#if !defined(PATH_MAX)
|
||||
# define PATH_MAX std::numeric_limits<int>::max();
|
||||
#endif
|
||||
|
||||
struct function_signature;
|
||||
struct basic_block_signature;
|
||||
struct module_function;
|
||||
|
||||
using string_t = std::string;
|
||||
using string_view_t = std::string_view;
|
||||
using stringstream_t = std::stringstream;
|
||||
using strvec_t = std::vector<string_t>;
|
||||
using strset_t = std::set<string_t>;
|
||||
using regexvec_t = std::vector<std::regex>;
|
||||
using fmodset_t = std::set<module_function>;
|
||||
using fixed_modset_t = std::map<fmodset_t*, bool>;
|
||||
using exec_callback_t = BPatchExecCallback;
|
||||
using exit_callback_t = BPatchExitCallback;
|
||||
using fork_callback_t = BPatchForkCallback;
|
||||
using patch_t = BPatch;
|
||||
using process_t = BPatch_process;
|
||||
using thread_t = BPatch_thread;
|
||||
using binary_edit_t = BPatch_binaryEdit;
|
||||
using image_t = BPatch_image;
|
||||
using module_t = BPatch_module;
|
||||
using procedure_t = BPatch_function;
|
||||
using snippet_t = BPatch_snippet;
|
||||
using call_expr_t = BPatch_funcCallExpr;
|
||||
using address_space_t = BPatch_addressSpace;
|
||||
using flow_graph_t = BPatch_flowGraph;
|
||||
using statement_t = BPatch_statement;
|
||||
using basic_block_t = BPatch_basicBlock;
|
||||
using basic_loop_t = BPatch_basicBlockLoop;
|
||||
using procedure_loc_t = BPatch_procedureLocation;
|
||||
using point_t = BPatch_point;
|
||||
using object_t = BPatch_object;
|
||||
using local_var_t = BPatch_localVar;
|
||||
using sequence_t = BPatch_sequence;
|
||||
using const_expr_t = BPatch_constExpr;
|
||||
using error_level_t = BPatchErrorLevel;
|
||||
using snippet_handle_t = BPatchSnippetHandle;
|
||||
using patch_pointer_t = std::shared_ptr<patch_t>;
|
||||
using snippet_pointer_t = std::shared_ptr<snippet_t>;
|
||||
using call_expr_pointer_t = std::shared_ptr<call_expr_t>;
|
||||
using snippet_vec_t = std::vector<snippet_t*>;
|
||||
using procedure_vec_t = std::vector<procedure_t*>;
|
||||
using basic_block_set_t = std::set<basic_block_t*>;
|
||||
using basic_loop_vec_t = std::vector<basic_loop_t*>;
|
||||
using snippet_pointer_vec_t = std::vector<snippet_pointer_t>;
|
||||
using instruction_t = Dyninst::InstructionAPI::Instruction;
|
||||
using instruction_category_t = Dyninst::InstructionAPI::InsnCategory;
|
||||
|
||||
namespace SymTab = ::Dyninst::SymtabAPI;
|
||||
using symtab_t = SymTab::Symtab;
|
||||
using symtab_module_t = SymTab::Module;
|
||||
using symtab_symbol_t = SymTab::Symbol;
|
||||
using symtab_func_t = SymTab::Function;
|
||||
using symbol_linkage_t = SymTab::Symbol::SymbolLinkage;
|
||||
using symbol_visibility_t = SymTab::Symbol::SymbolVisibility;
|
||||
|
||||
constexpr auto SL_UNKNOWN = symtab_symbol_t::SL_UNKNOWN;
|
||||
constexpr auto SL_GLOBAL = symtab_symbol_t::SL_GLOBAL;
|
||||
constexpr auto SL_LOCAL = symtab_symbol_t::SL_LOCAL;
|
||||
constexpr auto SL_WEAK = symtab_symbol_t::SL_WEAK;
|
||||
constexpr auto SL_UNIQUE = symtab_symbol_t::SL_UNIQUE;
|
||||
|
||||
constexpr auto SV_UNKNOWN = symtab_symbol_t::SV_UNKNOWN;
|
||||
constexpr auto SV_DEFAULT = symtab_symbol_t::SV_DEFAULT;
|
||||
constexpr auto SV_INTERNAL = symtab_symbol_t::SV_INTERNAL;
|
||||
constexpr auto SV_HIDDEN = symtab_symbol_t::SV_HIDDEN;
|
||||
constexpr auto SV_PROTECTED = symtab_symbol_t::SV_PROTECTED;
|
||||
|
||||
constexpr auto SL_END_V =
|
||||
std::max({ SL_UNKNOWN, SL_GLOBAL, SL_LOCAL, SL_WEAK, SL_UNIQUE }) + 1;
|
||||
|
||||
constexpr auto SV_END_V =
|
||||
std::max({ SV_UNKNOWN, SV_DEFAULT, SV_INTERNAL, SV_HIDDEN, SV_PROTECTED }) + 1;
|
||||
|
||||
void
|
||||
rocprofsys_prefork_callback(thread_t* parent, thread_t* child);
|
||||
|
||||
enum CodeCoverageMode
|
||||
{
|
||||
CODECOV_NONE = 0,
|
||||
CODECOV_FUNCTION,
|
||||
CODECOV_BASIC_BLOCK
|
||||
};
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Global Variables
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
// label settings
|
||||
//
|
||||
extern bool use_return_info;
|
||||
extern bool use_args_info;
|
||||
extern bool use_file_info;
|
||||
extern bool use_line_info;
|
||||
//
|
||||
// heuristic settings
|
||||
//
|
||||
extern bool allow_overlapping;
|
||||
extern bool loop_level_instr;
|
||||
extern bool instr_dynamic_callsites;
|
||||
extern bool instr_traps;
|
||||
extern bool instr_loop_traps;
|
||||
extern bool parse_all_modules;
|
||||
extern size_t min_address_range;
|
||||
extern size_t min_loop_address_range;
|
||||
extern size_t min_instructions;
|
||||
extern size_t min_loop_instructions;
|
||||
//
|
||||
// debug settings
|
||||
//
|
||||
extern bool werror;
|
||||
extern bool debug_print;
|
||||
extern bool instr_print;
|
||||
extern int verbose_level;
|
||||
extern int num_log_entries;
|
||||
//
|
||||
// instrumentation settings
|
||||
//
|
||||
extern bool simulate;
|
||||
extern bool include_uninstr;
|
||||
extern bool include_internal_linked_libs;
|
||||
//
|
||||
// string settings
|
||||
//
|
||||
extern string_t main_fname;
|
||||
extern string_t argv0;
|
||||
extern string_t cmdv0;
|
||||
extern string_t default_components;
|
||||
extern string_t prefer_library;
|
||||
//
|
||||
// global variables
|
||||
//
|
||||
extern patch_pointer_t bpatch;
|
||||
extern call_expr_t* terminate_expr;
|
||||
extern snippet_vec_t init_names;
|
||||
extern snippet_vec_t fini_names;
|
||||
extern fmodset_t available_module_functions;
|
||||
extern fmodset_t instrumented_module_functions;
|
||||
extern fmodset_t overlapping_module_functions;
|
||||
extern fmodset_t excluded_module_functions;
|
||||
extern fixed_modset_t fixed_module_functions;
|
||||
extern regexvec_t func_include;
|
||||
extern regexvec_t func_exclude;
|
||||
extern regexvec_t file_include;
|
||||
extern regexvec_t file_exclude;
|
||||
extern regexvec_t file_restrict;
|
||||
extern regexvec_t func_restrict;
|
||||
extern regexvec_t caller_include;
|
||||
extern regexvec_t func_internal_include;
|
||||
extern regexvec_t file_internal_include;
|
||||
extern regexvec_t instruction_exclude;
|
||||
extern CodeCoverageMode coverage_mode;
|
||||
//
|
||||
// symtab variables
|
||||
//
|
||||
struct symtab_data_s
|
||||
{
|
||||
std::vector<symtab_module_t*> modules = {};
|
||||
std::map<symtab_module_t*, std::vector<symtab_func_t*>> functions = {};
|
||||
std::map<symtab_func_t*, std::vector<symtab_symbol_t*>> symbols = {};
|
||||
std::unordered_map<std::string, symtab_symbol_t*> mangled_symbol_names = {};
|
||||
std::unordered_map<std::string, symtab_func_t*> typed_func_names = {};
|
||||
std::unordered_map<std::string, symtab_symbol_t*> typed_symbol_names = {};
|
||||
};
|
||||
|
||||
extern symtab_data_s symtab_data;
|
||||
extern std::set<symbol_linkage_t> enabled_linkage;
|
||||
extern std::set<symbol_visibility_t> enabled_visibility;
|
||||
|
||||
// logging
|
||||
extern std::unique_ptr<std::ofstream> log_ofs;
|
||||
//
|
||||
//======================================================================================//
|
||||
|
||||
// control debug printf statements
|
||||
#define errprintf(LEVEL, ...) \
|
||||
{ \
|
||||
char _logmsgbuff[FUNCNAMELEN]; \
|
||||
snprintf(_logmsgbuff, FUNCNAMELEN, __VA_ARGS__); \
|
||||
ROCPROFSYS_ADD_LOG_ENTRY(_logmsgbuff); \
|
||||
if(werror || LEVEL < 0) \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stderr, "[rocprof-sys][exe] Error! " __VA_ARGS__); \
|
||||
char _buff[FUNCNAMELEN]; \
|
||||
sprintf(_buff, "[rocprof-sys][exe] Error! " __VA_ARGS__); \
|
||||
throw std::runtime_error(std::string{ _buff }); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stderr, "[rocprof-sys][exe] Warning! " __VA_ARGS__); \
|
||||
} \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
// control verbose printf statements
|
||||
#define verbprintf(LEVEL, ...) \
|
||||
{ \
|
||||
char _logmsgbuff[FUNCNAMELEN]; \
|
||||
snprintf(_logmsgbuff, FUNCNAMELEN, __VA_ARGS__); \
|
||||
ROCPROFSYS_ADD_LOG_ENTRY(_logmsgbuff); \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stdout, "[rocprof-sys][exe] " __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
#define verbprintf_bare(LEVEL, ...) \
|
||||
{ \
|
||||
char _logmsgbuff[FUNCNAMELEN]; \
|
||||
snprintf(_logmsgbuff, FUNCNAMELEN, __VA_ARGS__); \
|
||||
ROCPROFSYS_ADD_LOG_ENTRY(_logmsgbuff); \
|
||||
if(debug_print || verbose_level >= LEVEL) fprintf(stdout, __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
template <typename... T>
|
||||
void
|
||||
consume_parameters(T&&...)
|
||||
{}
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
void
|
||||
process_modules(const std::vector<module_t*>&);
|
||||
|
||||
strset_t
|
||||
get_whole_function_names();
|
||||
|
||||
function_signature
|
||||
get_func_file_line_info(module_t* mutatee_module, procedure_t* f);
|
||||
|
||||
function_signature
|
||||
get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* cfGraph,
|
||||
basic_loop_t* loopToInstrument);
|
||||
|
||||
std::map<basic_block_t*, basic_block_signature>
|
||||
get_basic_block_file_line_info(module_t* module, procedure_t* func);
|
||||
|
||||
std::vector<statement_t>
|
||||
get_source_code(module_t* module, procedure_t* func);
|
||||
|
||||
std::tuple<size_t, size_t>
|
||||
query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc,
|
||||
flow_graph_t* cfGraph = nullptr, basic_loop_t* loopToInstrument = nullptr);
|
||||
|
||||
bool
|
||||
query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc, flow_graph_t* cfGraph,
|
||||
basic_loop_t* loopToInstrument, bool allow_traps);
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
insert_instr(address_space_t* mutatee, const std::vector<point_t*>& _points, Tp traceFunc,
|
||||
procedure_loc_t traceLoc, bool allow_traps = instr_traps);
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
|
||||
procedure_loc_t traceLoc, flow_graph_t* cfGraph = nullptr,
|
||||
basic_loop_t* loopToInstrument = nullptr, bool allow_traps = instr_traps);
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc,
|
||||
basic_block_t* basicBlock, bool allow_traps = instr_traps);
|
||||
|
||||
procedure_t*
|
||||
find_function(image_t* appImage, const string_t& functionName, const strset_t& = {});
|
||||
|
||||
void
|
||||
error_func_real(error_level_t level, int num, const char* const* params);
|
||||
|
||||
void
|
||||
error_func_fake(error_level_t level, int num, const char* const* params);
|
||||
|
||||
std::string_view
|
||||
get_name(procedure_t*);
|
||||
|
||||
std::string_view
|
||||
get_name(module_t*);
|
||||
|
||||
symtab_func_t*
|
||||
get_symtab_function(procedure_t*);
|
||||
|
||||
namespace std
|
||||
{
|
||||
std::string to_string(instruction_category_t);
|
||||
std::string to_string(error_level_t);
|
||||
std::string to_string(symbol_visibility_t);
|
||||
std::string to_string(symbol_linkage_t);
|
||||
} // namespace std
|
||||
|
||||
template <typename Tp>
|
||||
Tp from_string(std::string_view);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream&, symbol_linkage_t);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream&, symbol_visibility_t);
|
||||
|
||||
std::istream&
|
||||
operator>>(std::istream&, symbol_linkage_t&);
|
||||
|
||||
std::istream&
|
||||
operator>>(std::istream&, symbol_visibility_t&);
|
||||
@@ -0,0 +1,264 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "fwd.hpp"
|
||||
#include "module_function.hpp"
|
||||
|
||||
#include <timemory/log/color.hpp>
|
||||
#include <timemory/mpl/policy.hpp>
|
||||
#include <timemory/settings.hpp>
|
||||
#include <timemory/settings/types.hpp>
|
||||
#include <timemory/tpls/cereal/cereal.hpp>
|
||||
#include <timemory/utility/delimit.hpp>
|
||||
#include <timemory/utility/filepath.hpp>
|
||||
|
||||
static inline void
|
||||
dump_info(std::ostream& _os, const fmodset_t& _data)
|
||||
{
|
||||
module_function::reset_width();
|
||||
for(const auto& itr : _data)
|
||||
module_function::update_width(itr);
|
||||
|
||||
module_function::write_header(_os);
|
||||
for(const auto& itr : _data)
|
||||
_os << itr << '\n';
|
||||
|
||||
module_function::reset_width();
|
||||
}
|
||||
//
|
||||
template <typename ArchiveT,
|
||||
std::enable_if_t<tim::concepts::is_archive<ArchiveT>::value, int> = 0>
|
||||
static inline void
|
||||
dump_info(ArchiveT& _ar, const fmodset_t& _data)
|
||||
{
|
||||
_ar(tim::cereal::make_nvp("module_functions", _data));
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
dump_info(const string_t& _label, string_t _oname, const string_t& _ext,
|
||||
const fmodset_t& _data, int _level, bool _fail)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
namespace policy = tim::policy;
|
||||
|
||||
auto _cfg = tim::settings::compose_filename_config{};
|
||||
_cfg.subdirectory = "instrumentation";
|
||||
_oname = tim::settings::compose_output_filename(_oname, _ext, _cfg);
|
||||
auto _handle_error = [&]() {
|
||||
std::stringstream _msg{};
|
||||
_msg << "[dump_info] Error opening '" << _oname << " for output";
|
||||
verbprintf(_level, "%s\n", _msg.str().c_str());
|
||||
if(_fail)
|
||||
throw std::runtime_error(std::string{ "[rocprof-sys][exe]" } + _msg.str());
|
||||
};
|
||||
|
||||
if(!debug_print && verbose_level < _level) return;
|
||||
|
||||
if(_ext == "txt")
|
||||
{
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf_bare(_level, "%s", ::tim::log::color::source());
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
dump_info(ofs, _data);
|
||||
verbprintf_bare(_level, "Done\n%s", ::tim::log::color::end());
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else if(_ext == "xml")
|
||||
{
|
||||
std::stringstream oss{};
|
||||
{
|
||||
using output_policy = policy::output_archive<cereal::XMLOutputArchive>;
|
||||
output_policy::indent() = true;
|
||||
auto ar = output_policy::get(oss);
|
||||
|
||||
ar->setNextName("rocprofsys");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf_bare(_level, "%s", ::tim::log::color::source());
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
ofs << oss.str() << std::endl;
|
||||
verbprintf_bare(_level, "Done\n%s", ::tim::log::color::end());
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else if(_ext == "json")
|
||||
{
|
||||
std::stringstream oss{};
|
||||
{
|
||||
using output_policy = policy::output_archive<cereal::PrettyJSONOutputArchive>;
|
||||
auto ar = output_policy::get(oss);
|
||||
|
||||
ar->setNextName("rocprofsys");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf_bare(_level, "%s", ::tim::log::color::source());
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
ofs << oss.str() << std::endl;
|
||||
verbprintf_bare(_level, "Done\n%s", ::tim::log::color::end());
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(TIMEMORY_JOIN(
|
||||
"", "[rocprof-sys][exe] Error in ", __FUNCTION__, " :: filename '", _oname,
|
||||
"' does not have one of recognized file extensions: txt, json, xml"));
|
||||
}
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
dump_info(const string_t& _oname, const fmodset_t& _data, int _level, bool _fail,
|
||||
const string_t& _type, const strset_t& _ext)
|
||||
{
|
||||
for(const auto& itr : _ext)
|
||||
dump_info(_type, _oname, itr, _data, _level, _fail);
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
load_info(const string_t& _label, const string_t& _iname, fmodset_t& _data, int _level)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
namespace policy = tim::policy;
|
||||
|
||||
auto _pos = _iname.find_last_of('.');
|
||||
std::string _ext = {};
|
||||
if(_pos != std::string::npos) _ext = _iname.substr(_pos + 1);
|
||||
|
||||
auto _handle_error = [&]() {
|
||||
std::stringstream _msg{};
|
||||
_msg << "[load_info] Error opening '" << _iname << " for input";
|
||||
verbprintf(_level, "%s\n", _msg.str().c_str());
|
||||
throw std::runtime_error(std::string{ "[rocprof-sys][exe]" } + _msg.str());
|
||||
};
|
||||
|
||||
if(_ext == "xml")
|
||||
{
|
||||
verbprintf(_level, "Reading '%s'... ", _iname.c_str());
|
||||
std::ifstream ifs{ _iname };
|
||||
if(!ifs)
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
using input_policy = policy::input_archive<cereal::XMLInputArchive>;
|
||||
auto ar = input_policy::get(ifs);
|
||||
|
||||
ar->setNextName("rocprofsys");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
ifs.close();
|
||||
}
|
||||
else if(_ext == "json")
|
||||
{
|
||||
verbprintf(_level, "Reading '%s'... ", _iname.c_str());
|
||||
std::ifstream ifs{ _iname };
|
||||
if(!ifs)
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
using input_policy = policy::input_archive<cereal::JSONInputArchive>;
|
||||
auto ar = input_policy::get(ifs);
|
||||
|
||||
ar->setNextName("rocprofsys");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
ifs.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(TIMEMORY_JOIN(
|
||||
"", "[rocprof-sys][exe] Error in ", __FUNCTION__, " :: filename '", _iname,
|
||||
"' does not have one of recognized extentions: txt, json, xml :: ", _ext));
|
||||
}
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
load_info(const string_t& _inp, std::map<std::string, fmodset_t*>& _data, int _level)
|
||||
{
|
||||
std::vector<std::string> _exceptions{};
|
||||
_exceptions.reserve(_data.size());
|
||||
for(auto& itr : _data)
|
||||
{
|
||||
try
|
||||
{
|
||||
fmodset_t _tmp{};
|
||||
load_info(itr.first, _inp, _tmp, _level);
|
||||
// add to the existing
|
||||
itr.second->insert(_tmp.begin(), _tmp.end());
|
||||
// if it did not throw it was successfully loaded
|
||||
_exceptions.clear();
|
||||
break;
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
_exceptions.emplace_back(_e.what());
|
||||
}
|
||||
}
|
||||
if(!_exceptions.empty())
|
||||
{
|
||||
std::stringstream _msg{};
|
||||
for(auto& itr : _exceptions)
|
||||
{
|
||||
_msg << "[rocprof-sys][exe] " << itr << "\n";
|
||||
}
|
||||
throw std::runtime_error(_msg.str());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "internal_libs.hpp"
|
||||
#include "binary/analysis.hpp"
|
||||
#include "binary/binary_info.hpp"
|
||||
#include "binary/link_map.hpp"
|
||||
#include "binary/scope_filter.hpp"
|
||||
#include "binary/symbol.hpp"
|
||||
#include "common/defines.h"
|
||||
#include "core/utility.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <timemory/components/rusage/components.hpp>
|
||||
#include <timemory/components/timing/wall_clock.hpp>
|
||||
#include <timemory/environment/types.hpp>
|
||||
#include <timemory/log/macros.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/filepath.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <dlfcn.h>
|
||||
#include <initializer_list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace filepath = ::tim::filepath;
|
||||
using ::tim::delimit;
|
||||
using ::tim::get_env;
|
||||
using ::timemory::join::join;
|
||||
using strview_init_t = std::initializer_list<std::string_view>;
|
||||
using strview_set_t = std::set<std::string_view>;
|
||||
using open_modes_vec_t = std::vector<int>;
|
||||
|
||||
auto
|
||||
get_exe_realpath()
|
||||
{
|
||||
return filepath::realpath("/proc/self/exe", nullptr, false);
|
||||
}
|
||||
|
||||
auto&
|
||||
get_symtab_file_cache()
|
||||
{
|
||||
static auto _cache = std::unordered_map<std::string, std::pair<symtab_t*, bool>>{};
|
||||
return _cache;
|
||||
}
|
||||
|
||||
symtab_t*
|
||||
get_symtab_file(const std::string& _name)
|
||||
{
|
||||
auto& _cache = get_symtab_file_cache();
|
||||
auto itr = _cache.find(_name);
|
||||
if(itr == _cache.end())
|
||||
{
|
||||
symtab_t* _v = SymTab::Symtab::findOpenSymtab(_name);
|
||||
bool _closable = (_v == nullptr);
|
||||
if(!_v) SymTab::Symtab::openFile(_v, _name);
|
||||
|
||||
TIMEMORY_PREFER(_v != nullptr)
|
||||
<< "Warning! Dyninst could not open a Symtab instance for file '" << _name
|
||||
<< "'\n";
|
||||
_cache.emplace(_name, std::make_pair(_v, _closable));
|
||||
}
|
||||
|
||||
return _cache.at(_name).first;
|
||||
}
|
||||
|
||||
bool
|
||||
close_symtab_file(const std::string& _name)
|
||||
{
|
||||
auto& _cache = get_symtab_file_cache();
|
||||
auto itr = _cache.find(_name);
|
||||
if(itr != _cache.end())
|
||||
{
|
||||
symtab_t* _symtab = itr->second.first;
|
||||
bool _closable = itr->second.second;
|
||||
if(_symtab && _closable) SymTab::Symtab::closeSymtab(_symtab);
|
||||
_cache.erase(itr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <template <typename, typename...> class ContainerT, typename... TailT>
|
||||
bool
|
||||
check_regex_restrictions(const ContainerT<std::string, TailT...>& _names,
|
||||
const regexvec_t& _regexes)
|
||||
{
|
||||
for(const auto& nitr : _names)
|
||||
for(const auto& ritr : _regexes)
|
||||
if(std::regex_search(nitr, ritr)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
get_linked_path(const char* _name,
|
||||
open_modes_vec_t&& _open_modes = { (RTLD_LAZY | RTLD_NOLOAD) })
|
||||
{
|
||||
void* _handle = nullptr;
|
||||
bool _noload = false;
|
||||
for(auto _mode : _open_modes)
|
||||
{
|
||||
_handle = dlopen(_name, _mode);
|
||||
_noload = (_mode & RTLD_NOLOAD) == RTLD_NOLOAD;
|
||||
if(_handle) break;
|
||||
}
|
||||
|
||||
tim::scope::destructor _dtor{ [&_noload, &_handle]() {
|
||||
if(_noload == false) dlclose(_handle);
|
||||
} };
|
||||
|
||||
if(_handle)
|
||||
{
|
||||
struct link_map* _link_map = nullptr;
|
||||
dlinfo(_handle, RTLD_DI_LINKMAP, &_link_map);
|
||||
if(_link_map != nullptr && !std::string_view{ _link_map->l_name }.empty())
|
||||
{
|
||||
return filepath::realpath(_link_map->l_name, nullptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
return std::optional<std::string>{};
|
||||
}
|
||||
|
||||
std::set<std::string>
|
||||
get_link_map(const std::string& _lib,
|
||||
open_modes_vec_t&& _open_modes = { (RTLD_LAZY | RTLD_NOLOAD) })
|
||||
{
|
||||
void* _handle = nullptr;
|
||||
bool _noload = false;
|
||||
for(auto _mode : _open_modes)
|
||||
{
|
||||
_handle = dlopen(_lib.c_str(), _mode);
|
||||
_noload = (_mode & RTLD_NOLOAD) == RTLD_NOLOAD;
|
||||
if(_handle) break;
|
||||
}
|
||||
|
||||
auto _chain = std::set<std::string>{};
|
||||
if(_handle)
|
||||
{
|
||||
struct link_map* _link_map = nullptr;
|
||||
dlinfo(_handle, RTLD_DI_LINKMAP, &_link_map);
|
||||
struct link_map* _next = _link_map;
|
||||
while(_next)
|
||||
{
|
||||
if(!std::string_view{ _next->l_name }.empty() &&
|
||||
std::string_view{ _next->l_name } != _lib)
|
||||
{
|
||||
_chain.emplace(filepath::realpath(_next->l_name, nullptr, false));
|
||||
}
|
||||
_next = _next->l_next;
|
||||
}
|
||||
|
||||
if(_noload == false) dlclose(_handle);
|
||||
}
|
||||
return _chain;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
get_library_search_paths_impl()
|
||||
{
|
||||
auto _paths = std::vector<std::string>{};
|
||||
|
||||
auto _path_exists = [](const std::string& _filename) {
|
||||
struct stat dummy;
|
||||
return (_filename.empty()) ? false : (stat(_filename.c_str(), &dummy) == 0);
|
||||
};
|
||||
|
||||
auto _emplace_if_exists = [&_paths, _path_exists](const std::string& _directory) {
|
||||
if(_path_exists(_directory)) _paths.emplace_back(_directory);
|
||||
};
|
||||
|
||||
// search paths from environment variables
|
||||
for(const auto& itr : delimit(get_env("LD_LIBRARY_PATH", std::string{}, false), ":"))
|
||||
_emplace_if_exists(itr);
|
||||
|
||||
for(const auto& itr : { get_env<std::string>("ROCPROFSYS_ROCM_PATH", ""),
|
||||
get_env<std::string>("ROCM_PATH", ""),
|
||||
std::string{ ROCPROFSYS_DEFAULT_ROCM_PATH } })
|
||||
{
|
||||
if(!itr.empty())
|
||||
{
|
||||
for(const auto& ditr : delimit(itr, ":"))
|
||||
{
|
||||
_emplace_if_exists(join('/', ditr, "lib"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search ld.so.cache
|
||||
// apparently ubuntu doesn't like pclosing NULL, so a shared pointer custom
|
||||
// destructor is out. Ugh.
|
||||
FILE* ldconfig = popen("/sbin/ldconfig -p", "r");
|
||||
if(ldconfig)
|
||||
{
|
||||
constexpr size_t buffer_size = 512;
|
||||
char buffer[buffer_size];
|
||||
// ignore first line
|
||||
if(fgets(buffer, buffer_size, ldconfig))
|
||||
{
|
||||
// each line constaining relevant info should be in form:
|
||||
// <LIBRARY_BASENAME> (...) => <RESOLVED_ABSOLUTE_PATH>
|
||||
// example:
|
||||
// libz.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libz.so
|
||||
auto _get_entry = [](const std::string& _inp) {
|
||||
auto _paren_pos = _inp.find('(');
|
||||
auto _arrow_pos = _inp.find("=>", _paren_pos);
|
||||
if(_arrow_pos == std::string::npos || _paren_pos == std::string::npos)
|
||||
return std::string{};
|
||||
if(_arrow_pos + 2 < _inp.length())
|
||||
{
|
||||
auto _pos = _inp.find_first_not_of(" \t", _arrow_pos + 2);
|
||||
if(_pos < _inp.length()) return _inp.substr(_pos);
|
||||
}
|
||||
return std::string{};
|
||||
};
|
||||
|
||||
auto _data = std::stringstream{};
|
||||
while(fgets(buffer, buffer_size, ldconfig) != nullptr)
|
||||
{
|
||||
_data << buffer;
|
||||
auto _len = strnlen(buffer, buffer_size);
|
||||
if(_len > 0 && buffer[_len - 1] == '\n')
|
||||
{
|
||||
auto _v = _data.str();
|
||||
if(!_v.empty())
|
||||
{
|
||||
_v = _v.substr(_v.find_first_not_of(" \t"));
|
||||
if(_v.length() > 1)
|
||||
{
|
||||
auto _entry = _get_entry(_v.substr(0, _v.length() - 1));
|
||||
if(!_entry.empty()) _emplace_if_exists(_entry);
|
||||
}
|
||||
}
|
||||
_data = std::stringstream{};
|
||||
}
|
||||
}
|
||||
}
|
||||
pclose(ldconfig);
|
||||
}
|
||||
|
||||
// search hard-coded system paths
|
||||
for(const char* itr :
|
||||
{ "/usr/local/lib", "/usr/share/lib", "/usr/lib", "/usr/lib64",
|
||||
"/usr/lib/x86_64-linux-gnu", "/lib", "/lib64", "/lib/x86_64-linux-gnu" })
|
||||
{
|
||||
_emplace_if_exists(itr);
|
||||
}
|
||||
|
||||
return _paths;
|
||||
}
|
||||
|
||||
std::set<std::string>
|
||||
get_internal_basic_libs_impl()
|
||||
{
|
||||
auto _libs = std::set<std::string>{};
|
||||
const auto _exclude = strview_set_t{ LIBM_SO, LIBMVEC_SO };
|
||||
|
||||
// GNU libraries likely to be used by instrumentation
|
||||
const auto _gnu_libs = strview_init_t{ LD_LINUX_X86_64_SO, LD_SO,
|
||||
LIBANL_SO, LIBBROKENLOCALE_SO,
|
||||
LIBCRYPT_SO, LIBC_SO,
|
||||
LIBDL_SO, LIBGCC_S_SO,
|
||||
LIBMVEC_SO, LIBM_SO,
|
||||
LIBNSL_SO, LIBNSS_COMPAT_SO,
|
||||
LIBNSS_DB_SO, LIBNSS_DNS_SO,
|
||||
LIBNSS_FILES_SO, LIBNSS_HESIOD_SO,
|
||||
LIBNSS_LDAP_SO, LIBNSS_NISPLUS_SO,
|
||||
LIBNSS_NIS_SO, LIBNSS_TEST1_SO,
|
||||
LIBNSS_TEST2_SO, LIBPTHREAD_SO,
|
||||
LIBRESOLV_SO, LIBRT_SO,
|
||||
LIBTHREAD_DB_SO, LIBUTIL_SO };
|
||||
|
||||
// shared libraries used by or provided by dyninst
|
||||
const auto _dyn_libs = strview_init_t{ "libdyninstAPI_RT.so",
|
||||
"libcommon.so",
|
||||
"libbfd.so",
|
||||
"libelf.so",
|
||||
"libdwarf.so",
|
||||
"libdw.so",
|
||||
"libtbb.so",
|
||||
"libtbbmalloc.so",
|
||||
"libtbbmalloc_proxy.so",
|
||||
"libz.so",
|
||||
"libzstd.so",
|
||||
"libbz2.so",
|
||||
"liblzma.so" };
|
||||
|
||||
// shared libraries used by rocprof-sys
|
||||
const auto _omni_libs = strview_init_t{ "libstdc++.so.6",
|
||||
"libgotcha.so",
|
||||
"libunwind-coredump.so",
|
||||
"libunwind-generic.so",
|
||||
"libunwind-ptrace.so",
|
||||
"libunwind-setjmp.so",
|
||||
"libunwind.so",
|
||||
"libunwind-x86_64.so",
|
||||
"libpapi.so",
|
||||
"libpfm.so",
|
||||
"librocm_smi64.so",
|
||||
"libroctx64.so",
|
||||
"librocmtools.so",
|
||||
"libroctracer64.so",
|
||||
"librocprofiler64.so",
|
||||
"librocprofiler-register.so",
|
||||
"librocprofiler-sdk.so",
|
||||
"librocprofiler-sdk-roctx.so",
|
||||
"libamd_smi.so",
|
||||
"libamd_comgr.so" };
|
||||
|
||||
// shared libraries potentially used by timemory
|
||||
const auto _3rdparty_libs = strview_init_t{ "libcaliper.so",
|
||||
"liblikwid.so",
|
||||
"libprofiler.so",
|
||||
"libtcmalloc.so",
|
||||
"libtcmalloc_and_profiler.so",
|
||||
"libtcmalloc_debug.so",
|
||||
"libtcmalloc_minimal.so",
|
||||
"libtcmalloc_minimal_debug.so" };
|
||||
|
||||
for(const auto& gitr : { _gnu_libs, _dyn_libs, _omni_libs, _3rdparty_libs })
|
||||
{
|
||||
for(auto itr : gitr)
|
||||
{
|
||||
if(!itr.empty() && _exclude.count(itr) == 0) _libs.emplace(itr);
|
||||
}
|
||||
}
|
||||
|
||||
// auto _link_map = binary::get_link_map(nullptr, "", "", { (RTLD_LAZY | RTLD_NOLOAD)
|
||||
// }); for(const auto& itr : _link_map)
|
||||
// _libs.emplace(itr.real());
|
||||
|
||||
return _libs;
|
||||
}
|
||||
|
||||
std::set<std::string>
|
||||
get_internal_libs_impl()
|
||||
{
|
||||
auto _libs = get_internal_basic_libs();
|
||||
for(auto itr : get_internal_basic_libs())
|
||||
{
|
||||
if(!itr.empty())
|
||||
{
|
||||
if(parse_all_modules)
|
||||
{
|
||||
auto _lib_v = find_libraries(itr);
|
||||
if(!_lib_v.empty())
|
||||
{
|
||||
for(const auto& litr : _lib_v)
|
||||
{
|
||||
verbprintf(2, "Library '%s' found: %s\n", itr.data(),
|
||||
litr.c_str());
|
||||
_libs.emplace(litr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
verbprintf(2, "Library '%s' not found\n", itr.data());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto _lib_v = find_library(itr);
|
||||
if(_lib_v)
|
||||
{
|
||||
verbprintf(2, "Library '%s' found: '%s'\n", itr.data(),
|
||||
_lib_v->c_str());
|
||||
_libs.emplace(*_lib_v);
|
||||
if(include_internal_linked_libs)
|
||||
{
|
||||
for(auto&& litr : get_link_map(*_lib_v))
|
||||
{
|
||||
verbprintf(2, "Library '%s' found: '%s' (linked by '%s')\n",
|
||||
itr.data(), litr.c_str(), _lib_v->c_str());
|
||||
_libs.emplace(litr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
verbprintf(2, "Library '%s' not found\n", itr.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _libs;
|
||||
}
|
||||
|
||||
library_module_map_t
|
||||
get_internal_libs_data_impl()
|
||||
{
|
||||
auto _wc = tim::component::wall_clock{};
|
||||
auto _pr = tim::component::peak_rss{};
|
||||
_wc.start();
|
||||
_pr.start();
|
||||
|
||||
auto _libs_v = get_internal_libs();
|
||||
auto _libs = std::vector<std::string>{};
|
||||
_libs.assign(_libs_v.begin(), _libs_v.end());
|
||||
|
||||
auto _rocprofsys_base_path = filepath::dirname(
|
||||
filepath::dirname(filepath::realpath("/proc/self/exe", nullptr, false)));
|
||||
auto _rocprofsys_lib_path = std::string{};
|
||||
|
||||
for(const auto* itr : { "lib", "lib64" })
|
||||
{
|
||||
for(const auto* litr :
|
||||
{ "librocprof-sys-dl.so", "librocprof-sys-user.so", "librocprof-sys-rt.so" })
|
||||
{
|
||||
auto _libpath = join('/', _rocprofsys_base_path, itr, litr);
|
||||
if(filepath::exists(_libpath))
|
||||
{
|
||||
_libs.emplace_back(filepath::realpath(_libpath, nullptr, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rocprofsys::utility::filter_sort_unique(
|
||||
_libs, [](const auto& itr) { return itr.empty() || !filepath::exists(itr); });
|
||||
|
||||
auto _data = library_module_map_t{};
|
||||
for(const auto& itr : _libs)
|
||||
{
|
||||
auto _fpath = filepath::realpath(itr, nullptr, false);
|
||||
// allow the user to request this library be considered for instrumentation
|
||||
if(check_regex_restrictions(strvec_t{ itr, _fpath }, file_internal_include))
|
||||
continue;
|
||||
|
||||
_data.emplace(_fpath, module_func_map_t{});
|
||||
}
|
||||
|
||||
auto _odata = ordered(_data);
|
||||
for(const auto& itr : _odata)
|
||||
{
|
||||
symtab_t* _symtab = get_symtab_file(itr.first);
|
||||
if(!_symtab) continue;
|
||||
|
||||
verbprintf(0, "[internal] parsing library: '%s'...\n", itr.first.c_str());
|
||||
|
||||
auto _wc_v = tim::component::wall_clock{};
|
||||
auto _pr_v = tim::component::peak_rss{};
|
||||
_wc_v.start();
|
||||
_pr_v.start();
|
||||
|
||||
auto _modules = std::vector<symtab_module_t*>{};
|
||||
_symtab->getAllModules(_modules);
|
||||
|
||||
for(const auto& mitr : _modules)
|
||||
{
|
||||
const auto& _mname = mitr->fileName();
|
||||
const auto& _mpath = mitr->fullName();
|
||||
// allow the user to request this library be considered for instrumentation
|
||||
if(check_regex_restrictions(strvec_t{ _mname, _mpath },
|
||||
file_internal_include))
|
||||
continue;
|
||||
|
||||
verbprintf(3, "[internal] parsing module: '%s' (via '%s')...\n",
|
||||
_mname.c_str(), filepath::basename(itr.first));
|
||||
|
||||
_data[itr.first].emplace(_mpath, func_set_t{});
|
||||
_data[itr.first].emplace(_mname, func_set_t{});
|
||||
|
||||
auto _funcs = mitr->getAllFunctions();
|
||||
|
||||
for(const auto& fitr : _funcs)
|
||||
{
|
||||
auto _fname = fitr->getName();
|
||||
auto _dname = tim::demangle(_fname);
|
||||
|
||||
_data[itr.first][_mpath].emplace(_fname);
|
||||
_data[itr.first][_mpath].emplace(_dname);
|
||||
}
|
||||
}
|
||||
|
||||
_pr_v.stop();
|
||||
_wc_v.stop();
|
||||
verbprintf(1, "[internal] parsing library: '%s'... Done (%.3f %s, %.3f %s)\n",
|
||||
itr.first.c_str(), _wc_v.get(), _wc_v.display_unit().c_str(),
|
||||
_pr_v.get(), _pr_v.display_unit().c_str());
|
||||
|
||||
// close_symtab_file(itr.first);
|
||||
}
|
||||
|
||||
_pr.stop();
|
||||
_wc.stop();
|
||||
verbprintf(0, "[internal] binary info processing required %.3f %s and %.3f %s\n",
|
||||
_wc.get(), _wc.display_unit().c_str(), _pr.get(),
|
||||
_pr.display_unit().c_str());
|
||||
|
||||
return _data;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename Tp, typename... TailT>
|
||||
std::set<Tp>
|
||||
ordered(const std::unordered_set<Tp, TailT...>& _unordered)
|
||||
{
|
||||
auto _ordered = std::set<Tp>{};
|
||||
for(const auto& itr : _unordered)
|
||||
_ordered.emplace(itr);
|
||||
return _ordered;
|
||||
}
|
||||
|
||||
template <typename KeyT, typename MappedT, typename... TailT>
|
||||
std::map<KeyT, MappedT>
|
||||
ordered(const std::unordered_map<KeyT, MappedT, TailT...>& _unordered)
|
||||
{
|
||||
auto _ordered = std::map<KeyT, MappedT>{};
|
||||
for(const auto& itr : _unordered)
|
||||
_ordered.emplace(itr.first, itr.second);
|
||||
return _ordered;
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
find_library(std::string_view _lib_v)
|
||||
{
|
||||
auto _lib = get_linked_path(_lib_v.data(), { (RTLD_LAZY | RTLD_NOLOAD) });
|
||||
if(_lib) return _lib;
|
||||
|
||||
for(const auto& itr : get_library_search_paths())
|
||||
{
|
||||
auto _path = join('/', itr, _lib_v);
|
||||
if(filepath::exists(_path)) return std::optional<std::string>{ _path };
|
||||
}
|
||||
|
||||
return std::optional<std::string>{};
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
find_libraries(std::string_view _lib_v)
|
||||
{
|
||||
auto _libs = std::vector<std::string>{};
|
||||
|
||||
auto _lib = get_linked_path(_lib_v.data(), { (RTLD_LAZY | RTLD_NOLOAD) });
|
||||
if(_lib) _libs.emplace_back(*_lib);
|
||||
|
||||
for(const auto& itr : get_library_search_paths())
|
||||
{
|
||||
auto _path = join('/', itr, _lib_v);
|
||||
if(filepath::exists(_path)) _libs.emplace_back(_path);
|
||||
}
|
||||
|
||||
return _libs;
|
||||
}
|
||||
|
||||
const std::vector<std::string>&
|
||||
get_library_search_paths()
|
||||
{
|
||||
static auto _v = get_library_search_paths_impl();
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::set<std::string>&
|
||||
get_internal_basic_libs()
|
||||
{
|
||||
static auto _v = get_internal_basic_libs_impl();
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::set<std::string>&
|
||||
get_internal_libs()
|
||||
{
|
||||
static auto _v = get_internal_libs_impl();
|
||||
return _v;
|
||||
}
|
||||
|
||||
const library_module_map_t&
|
||||
get_internal_libs_data()
|
||||
{
|
||||
static auto _v = get_internal_libs_data_impl();
|
||||
return _v;
|
||||
}
|
||||
|
||||
void
|
||||
parse_internal_libs_data()
|
||||
{
|
||||
(void) get_internal_libs_data();
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <gnu/lib-names.h>
|
||||
|
||||
#if !defined(LD_LINUX_X86_64_SO)
|
||||
# define LD_LINUX_X86_64_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LD_SO)
|
||||
# define LD_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBANL_SO)
|
||||
# define LIBANL_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBBROKENLOCALE_SO)
|
||||
# define LIBBROKENLOCALE_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBCRYPT_SO)
|
||||
# define LIBCRYPT_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBC_SO)
|
||||
# define LIBC_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBDL_SO)
|
||||
# define LIBDL_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBGCC_S_SO)
|
||||
# define LIBGCC_S_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBMVEC_SO)
|
||||
# define LIBMVEC_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBM_SO)
|
||||
# define LIBM_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSL_SO)
|
||||
# define LIBNSL_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_COMPAT_SO)
|
||||
# define LIBNSS_COMPAT_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_DB_SO)
|
||||
# define LIBNSS_DB_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_DNS_SO)
|
||||
# define LIBNSS_DNS_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_FILES_SO)
|
||||
# define LIBNSS_FILES_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_HESIOD_SO)
|
||||
# define LIBNSS_HESIOD_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_LDAP_SO)
|
||||
# define LIBNSS_LDAP_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_NISPLUS_SO)
|
||||
# define LIBNSS_NISPLUS_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_NIS_SO)
|
||||
# define LIBNSS_NIS_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_TEST1_SO)
|
||||
# define LIBNSS_TEST1_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBNSS_TEST2_SO)
|
||||
# define LIBNSS_TEST2_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBPTHREAD_SO)
|
||||
# define LIBPTHREAD_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBRESOLV_SO)
|
||||
# define LIBRESOLV_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBRT_SO)
|
||||
# define LIBRT_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBTHREAD_DB_SO)
|
||||
# define LIBTHREAD_DB_SO ""
|
||||
#endif
|
||||
|
||||
#if !defined(LIBUTIL_SO)
|
||||
# define LIBUTIL_SO ""
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
using func_set_t = std::unordered_set<std::string>;
|
||||
using module_func_map_t = std::unordered_map<std::string, func_set_t>;
|
||||
using library_module_map_t = std::unordered_map<std::string, module_func_map_t>;
|
||||
|
||||
template <typename Tp, typename... TailT>
|
||||
std::set<Tp>
|
||||
ordered(const std::unordered_set<Tp, TailT...>&);
|
||||
|
||||
template <typename KeyT, typename MappedT, typename... TailT>
|
||||
std::map<KeyT, MappedT>
|
||||
ordered(const std::unordered_map<KeyT, MappedT, TailT...>&);
|
||||
|
||||
std::optional<std::string> find_library(std::string_view);
|
||||
|
||||
std::vector<std::string> find_libraries(std::string_view);
|
||||
|
||||
const std::vector<std::string>&
|
||||
get_library_search_paths();
|
||||
|
||||
std::set<std::string>&
|
||||
get_internal_basic_libs();
|
||||
|
||||
std::set<std::string>&
|
||||
get_internal_libs();
|
||||
|
||||
const library_module_map_t&
|
||||
get_internal_libs_data();
|
||||
|
||||
void
|
||||
parse_internal_libs_data();
|
||||
@@ -0,0 +1,160 @@
|
||||
// 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 "log.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
namespace color = tim::log::color;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<log_entry> log_entries = {};
|
||||
|
||||
auto
|
||||
get_color_regex(std::string _v)
|
||||
{
|
||||
auto _p = _v.find("[");
|
||||
if(_p != std::string::npos) _v.insert(_p, "\\");
|
||||
return JOIN("", "\\", _v);
|
||||
}
|
||||
|
||||
auto _color_regex = std::regex{ JOIN("", "(", get_color_regex(tim::log::color::info()),
|
||||
"|", get_color_regex(tim::log::color::source()), "|",
|
||||
get_color_regex(tim::log::color::warning()), "|",
|
||||
get_color_regex(tim::log::color::fatal()), "|",
|
||||
get_color_regex(tim::log::color::end()), ")"),
|
||||
std::regex_constants::optimize };
|
||||
} // namespace
|
||||
|
||||
log_entry::log_entry(std::string _msg)
|
||||
: m_message{ std::move(_msg) }
|
||||
, m_backtrace{ tim::get_unw_stack<4, 1>() }
|
||||
{
|
||||
if(log_ofs) *log_ofs << as_string("", "", "") << "\n";
|
||||
}
|
||||
|
||||
log_entry::log_entry(source_location _loc, std::string _msg)
|
||||
: m_location{ _loc }
|
||||
, m_message{ std::move(_msg) }
|
||||
, m_backtrace{ tim::get_unw_stack<4, 1>() }
|
||||
{
|
||||
if(log_ofs) *log_ofs << as_string("", "", "") << "\n";
|
||||
}
|
||||
|
||||
std::string
|
||||
log_entry::as_string(const char* _color, const char* _src, const char* _end) const
|
||||
{
|
||||
std::stringstream _ss;
|
||||
if(m_location.function && m_location.file)
|
||||
{
|
||||
_ss << "[" << _src << m_location.file << ":" << m_location.line << _end << "]["
|
||||
<< _src << m_location.function << _end << "]";
|
||||
}
|
||||
|
||||
bool _remove_color = (strlen(_color) + strlen(_src) + strlen(_end) == 0);
|
||||
|
||||
_ss << " " << _color << std::regex_replace(m_message, std::regex{ "\n" }, " ... ")
|
||||
<< _end;
|
||||
|
||||
return (_remove_color) ? std::regex_replace(_ss.str(), _color_regex, "") : _ss.str();
|
||||
}
|
||||
|
||||
log_entry&
|
||||
log_entry::add_log_entry(log_entry&& _v)
|
||||
{
|
||||
return log_entries.emplace_back(std::move(_v));
|
||||
}
|
||||
|
||||
void
|
||||
print_log_entries(std::ostream& _os, int64_t _count,
|
||||
const std::function<bool(const log_entry&)>& _condition,
|
||||
const std::function<void()>& _prelude, const char* _color,
|
||||
bool _color_entries)
|
||||
{
|
||||
size_t i0 = (_count < 0) ? 0 : std::max<int64_t>(log_entries.size() - _count, 0);
|
||||
size_t _w = std::log10(log_entries.size()) + 1;
|
||||
|
||||
if(dynamic_cast<std::ofstream*>(&_os) ||
|
||||
(&_os != &std::cout && &_os != &std::cerr && &_os != &std::clog))
|
||||
{
|
||||
_color = "";
|
||||
_color_entries = false;
|
||||
}
|
||||
|
||||
const char* _end =
|
||||
(strlen(_color) > 0 || _color_entries) ? tim::log::color::end() : "";
|
||||
|
||||
if(_prelude)
|
||||
{
|
||||
for(size_t i = i0; i < log_entries.size(); ++i)
|
||||
{
|
||||
if(!_condition || _condition(log_entries.at(i)))
|
||||
{
|
||||
_prelude();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the requested number of log entries
|
||||
auto _last = std::string{};
|
||||
size_t _last_i = 0;
|
||||
size_t _last_n = 0;
|
||||
for(size_t i = i0; i < log_entries.size(); ++i)
|
||||
{
|
||||
auto& itr = log_entries.at(i);
|
||||
|
||||
if(!_condition || _condition(itr))
|
||||
{
|
||||
auto _msg = ((_color_entries) ? itr.as_string() : itr.as_string("", "", ""));
|
||||
if(_msg != _last)
|
||||
{
|
||||
if(_last_n > 0 && !_last.empty())
|
||||
{
|
||||
_os << "[" << _color << std::setw(_w) << _last_i << "/"
|
||||
<< log_entries.size() << _end << "] ... repeated " << _last_n
|
||||
<< " times ...\n";
|
||||
}
|
||||
_last_n = 0;
|
||||
_last_i = i;
|
||||
_last = _msg;
|
||||
_os << "[" << _color << std::setw(_w) << i << "/" << log_entries.size()
|
||||
<< _end << "]" << _msg << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
++_last_n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_last_n > 0)
|
||||
{
|
||||
_os << "[" << _color << std::setw(_w) << _last_i << "/" << log_entries.size()
|
||||
<< _end << "] ... repeated " << _last_n << " times ...\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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/log/color.hpp>
|
||||
#include <timemory/utility/backtrace.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#if !defined(JOIN)
|
||||
# define JOIN(...) ::timemory::join::join(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
struct log_entry;
|
||||
|
||||
void
|
||||
print_log_entries(std::ostream& = std::cerr, int64_t _count = 10,
|
||||
const std::function<bool(const log_entry&)>& _cond = {},
|
||||
const std::function<void()>& _prelude = {},
|
||||
const char* _color = tim::log::color::warning(),
|
||||
bool _color_entries = true);
|
||||
|
||||
struct log_entry
|
||||
{
|
||||
struct source_location
|
||||
{
|
||||
const char* function = nullptr;
|
||||
const char* file = nullptr;
|
||||
int line = 0;
|
||||
};
|
||||
|
||||
log_entry(std::string _msg);
|
||||
log_entry(source_location _loc, std::string _msg);
|
||||
|
||||
std::string as_string(const char* _color = tim::log::color::info(),
|
||||
const char* _src = tim::log::color::source(),
|
||||
const char* _end = tim::log::color::end()) const;
|
||||
|
||||
static log_entry& add_log_entry(log_entry&&);
|
||||
|
||||
log_entry& force(bool _v = true)
|
||||
{
|
||||
m_forced = _v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool forced() const { return m_forced; }
|
||||
|
||||
private:
|
||||
bool m_forced = false; // if should always be displayed
|
||||
source_location m_location = {};
|
||||
std::string m_message = {};
|
||||
tim::unwind::stack<4> m_backtrace = {};
|
||||
|
||||
friend void print_log_entries(std::ostream&, int64_t,
|
||||
std::function<bool(const log_entry&)>, const char*,
|
||||
bool);
|
||||
};
|
||||
|
||||
#define ROCPROFSYS_ADD_LOG_ENTRY(...) \
|
||||
log_entry::add_log_entry( \
|
||||
{ log_entry::source_location{ __FUNCTION__, __FILE__, __LINE__ }, \
|
||||
timemory::join::join(' ', __VA_ARGS__) })
|
||||
|
||||
#define ROCPROFSYS_ADD_DETAILED_LOG_ENTRY(DELIM, ...) \
|
||||
log_entry::add_log_entry( \
|
||||
{ log_entry::source_location{ __FUNCTION__, __FILE__, __LINE__ }, \
|
||||
timemory::join::join(DELIM, __VA_ARGS__) })
|
||||
+1029
File diff ditekan karena terlalu besar
Load Diff
+261
@@ -0,0 +1,261 @@
|
||||
// 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 "function_signature.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/tpls/cereal/cereal/cereal.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
struct module_function
|
||||
{
|
||||
using width_t = std::array<size_t, 4>;
|
||||
using address_t = Dyninst::Address;
|
||||
using instr_addr_pair_t = std::pair<instruction_t, address_t>;
|
||||
using str_msg_t = std::tuple<int, string_t, string_t, string_t, string_t>;
|
||||
using str_msg_vec_t = std::vector<str_msg_t>;
|
||||
|
||||
static constexpr size_t absolute_max_width = 80;
|
||||
static width_t& get_width();
|
||||
static void reset_width();
|
||||
static void update_width(const module_function& rhs);
|
||||
static void write_header(std::ostream& os);
|
||||
|
||||
TIMEMORY_DEFAULT_OBJECT(module_function)
|
||||
|
||||
module_function(module_t* mod, procedure_t* proc);
|
||||
|
||||
// code coverage
|
||||
void register_source(address_space_t* _addr_space, procedure_t* _entr_trace,
|
||||
const std::vector<point_t*>&) const;
|
||||
std::pair<size_t, size_t> register_coverage(address_space_t* _addr_space,
|
||||
procedure_t* _entr_trace) const;
|
||||
|
||||
// instrumentation
|
||||
std::pair<size_t, size_t> operator()(address_space_t* _addr_space,
|
||||
procedure_t* _entr_trace,
|
||||
procedure_t* _exit_trace) const;
|
||||
|
||||
// applies logic for all "is_*" and "can_*" checks below
|
||||
bool should_instrument() const;
|
||||
bool should_coverage_instrument() const;
|
||||
|
||||
// hard constraints
|
||||
bool is_instrumentable() const; // checks whether can instrument
|
||||
bool can_instrument_entry() const; // checks for entry points
|
||||
bool can_instrument_exit() const; // checks for exit points
|
||||
bool is_internal_constrained() const; // checks internal usage constraint
|
||||
bool is_module_constrained() const; // checks module constraints
|
||||
bool is_routine_constrained() const; // checks function constraints
|
||||
|
||||
// user bypass of heuristics
|
||||
bool is_user_restricted() const; // checks user restrict regexes
|
||||
bool is_user_included() const; // checks user include regexes
|
||||
bool is_user_excluded() const; // checks user exclude regexes
|
||||
|
||||
// applied before dynamic-callsite constraint
|
||||
bool is_overlapping_constrained() const; // checks overlapping constrains
|
||||
bool is_entry_trap_constrained() const; // checks entry trap constraint
|
||||
bool is_exit_trap_constrained() const; // checks exit trap constraint
|
||||
|
||||
// applied before address range and # instruction constraints
|
||||
bool is_dynamic_callsite_forced() const; // checks dynamic callsites
|
||||
|
||||
// user exclusion based on instructions
|
||||
bool is_instruction_constrained() const;
|
||||
|
||||
// estimate the size/work of the function
|
||||
bool is_address_range_constrained() const; // checks address range constraint
|
||||
bool is_num_instructions_constrained() const; // check # instructions constraint
|
||||
|
||||
bool is_visibility_constrained() const;
|
||||
bool is_linkage_constrained() const;
|
||||
|
||||
size_t start_address = 0;
|
||||
uint64_t address_range = 0;
|
||||
uint64_t num_instructions = 0;
|
||||
module_t* module = nullptr;
|
||||
procedure_t* function = nullptr;
|
||||
symtab_func_t* symtab_function = nullptr;
|
||||
flow_graph_t* flow_graph = nullptr;
|
||||
string_t module_name = {};
|
||||
string_t function_name = {};
|
||||
function_signature signature = {};
|
||||
basic_block_set_t basic_blocks = {};
|
||||
basic_loop_vec_t loop_blocks = {};
|
||||
std::map<instruction_category_t, int64_t> instruction_types = {};
|
||||
std::vector<std::vector<instr_addr_pair_t>> instructions = {};
|
||||
|
||||
mutable str_msg_vec_t messages = {};
|
||||
|
||||
bool is_overlapping() const; // checks if func overlaps
|
||||
|
||||
private:
|
||||
symbol_linkage_t get_linkage() const;
|
||||
symbol_visibility_t get_visibility() const;
|
||||
bool is_loop_num_instructions_constrained() const; // checks loop instr constraint
|
||||
bool is_loop_address_range_constrained() const; // checks loop addr range constraint
|
||||
bool contains_dynamic_callsites() const;
|
||||
bool should_instrument(bool _coverage) const;
|
||||
bool contains_user_callsite() const; // checks user caller regexes
|
||||
|
||||
public:
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& ar, const unsigned);
|
||||
|
||||
friend bool operator<(const module_function& lhs, const module_function& rhs)
|
||||
{
|
||||
return std::tie(lhs.module_name, lhs.function_name, lhs.start_address,
|
||||
lhs.address_range, lhs.num_instructions) <
|
||||
std::tie(rhs.module_name, rhs.function_name, rhs.start_address,
|
||||
rhs.address_range, rhs.num_instructions);
|
||||
}
|
||||
|
||||
friend bool operator==(const module_function& lhs, const module_function& rhs)
|
||||
{
|
||||
return std::tie(lhs.start_address, lhs.address_range, lhs.num_instructions,
|
||||
lhs.module_name, lhs.function_name) ==
|
||||
std::tie(rhs.start_address, rhs.address_range, rhs.num_instructions,
|
||||
rhs.module_name, rhs.function_name);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const module_function& rhs)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
auto w0 = std::min<size_t>(get_width()[0], absolute_max_width);
|
||||
auto w1 = std::min<size_t>(get_width()[1], absolute_max_width);
|
||||
auto w2 = std::min<size_t>(get_width()[2], absolute_max_width);
|
||||
|
||||
auto _get_str = [](const std::string& _inc) {
|
||||
if(_inc.length() > absolute_max_width)
|
||||
return _inc.substr(0, absolute_max_width - 3) + "...";
|
||||
return _inc;
|
||||
};
|
||||
|
||||
std::stringstream _addr{};
|
||||
_addr << "0x" << std::hex << rhs.start_address;
|
||||
// clang-format off
|
||||
ss << std::setw(14) << _addr.str() << " "
|
||||
<< std::setw(14) << rhs.address_range << " "
|
||||
<< std::setw(14) << rhs.num_instructions << " "
|
||||
<< std::setw(6) << std::setprecision(2) << std::fixed << (rhs.address_range / static_cast<double>(rhs.num_instructions)) << " "
|
||||
<< std::setw(7) << std::to_string(rhs.get_linkage()) << " "
|
||||
<< std::setw(10) << std::to_string(rhs.get_visibility()) << " "
|
||||
<< std::setw(w0 + 8) << std::left << _get_str(rhs.module_name) << " "
|
||||
<< std::setw(w1 + 8) << std::left << _get_str(rhs.function_name) << " "
|
||||
<< std::setw(w2 + 8) << std::left << _get_str(rhs.signature.get());
|
||||
// clang-format on
|
||||
|
||||
os << ss.str();
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ArchiveT>
|
||||
void
|
||||
module_function::serialize(ArchiveT& ar, const unsigned)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
if constexpr(tim::concepts::is_output_archive<ArchiveT>::value)
|
||||
{
|
||||
std::stringstream _addr{};
|
||||
_addr << "0x" << std::hex << start_address;
|
||||
ar(cereal::make_nvp("start_address", _addr.str()));
|
||||
}
|
||||
|
||||
ar(cereal::make_nvp("address_range", address_range),
|
||||
cereal::make_nvp("num_instructions", num_instructions),
|
||||
cereal::make_nvp("module", module_name),
|
||||
cereal::make_nvp("function", function_name),
|
||||
cereal::make_nvp("signature", signature));
|
||||
|
||||
if constexpr(tim::concepts::is_output_archive<ArchiveT>::value)
|
||||
{
|
||||
ar(cereal::make_nvp("linkage", std::to_string(get_linkage())),
|
||||
cereal::make_nvp("visibility", std::to_string(get_visibility())),
|
||||
cereal::make_nvp("num_basic_blocks", basic_blocks.size()),
|
||||
cereal::make_nvp("num_outer_loops", loop_blocks.size()));
|
||||
ar.setNextName("heuristics");
|
||||
ar.startNode();
|
||||
ar(cereal::make_nvp("should_instrument", should_instrument()),
|
||||
cereal::make_nvp("should_coverage_instrument", should_coverage_instrument()),
|
||||
cereal::make_nvp("is_instrumentable", is_instrumentable()),
|
||||
cereal::make_nvp("can_instrument_entry", can_instrument_entry()),
|
||||
cereal::make_nvp("can_instrument_exit", can_instrument_exit()),
|
||||
cereal::make_nvp("contains_dynamic_callsites", contains_dynamic_callsites()),
|
||||
cereal::make_nvp("is_internal_constrained", is_internal_constrained()),
|
||||
cereal::make_nvp("is_module_constrained", is_module_constrained()),
|
||||
cereal::make_nvp("is_routine_constrained", is_routine_constrained()),
|
||||
cereal::make_nvp("is_user_restricted", is_user_restricted()),
|
||||
cereal::make_nvp("is_user_included", is_user_included()),
|
||||
cereal::make_nvp("contains_user_callsite", contains_user_callsite()),
|
||||
cereal::make_nvp("is_user_excluded", is_user_excluded()),
|
||||
cereal::make_nvp("is_overlapping_constrained", is_overlapping_constrained()),
|
||||
cereal::make_nvp("is_entry_trap_constrained", is_entry_trap_constrained()),
|
||||
cereal::make_nvp("is_exit_trap_constrained", is_exit_trap_constrained()),
|
||||
cereal::make_nvp("is_dynamic_callsite_forced", is_dynamic_callsite_forced()),
|
||||
cereal::make_nvp("is_linkage_constrained", is_linkage_constrained()),
|
||||
cereal::make_nvp("is_visibility_constrained", is_visibility_constrained()),
|
||||
cereal::make_nvp("is_address_range_constrained",
|
||||
is_address_range_constrained()),
|
||||
cereal::make_nvp("is_num_instructions_constrained",
|
||||
is_num_instructions_constrained()),
|
||||
cereal::make_nvp("is_instruction_constrained", is_instruction_constrained()),
|
||||
cereal::make_nvp("is_loop_address_range_constrained",
|
||||
is_loop_address_range_constrained()),
|
||||
cereal::make_nvp("is_loop_num_instructions_constrained",
|
||||
is_loop_num_instructions_constrained()));
|
||||
ar.finishNode();
|
||||
ar.setNextName("instruction_breakdown");
|
||||
ar.startNode();
|
||||
for(auto itr : instruction_types)
|
||||
ar(cereal::make_nvp(std::to_string(itr.first).c_str(), itr.second));
|
||||
ar.finishNode();
|
||||
// instructions can inflate JSON size so only output when verbosity is increased
|
||||
// above default
|
||||
if(debug_print || verbose_level > 3 || instr_print)
|
||||
{
|
||||
ar.setNextName("instructions");
|
||||
ar.startNode();
|
||||
ar.makeArray();
|
||||
for(auto&& itr : instructions)
|
||||
{
|
||||
ar.startNode();
|
||||
for(auto&& iitr : itr)
|
||||
{
|
||||
std::stringstream _addr{};
|
||||
_addr << "0x" << std::hex << iitr.second;
|
||||
ar(cereal::make_nvp(_addr.str().c_str(), iitr.first.format()));
|
||||
}
|
||||
ar.finishNode();
|
||||
}
|
||||
ar.finishNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
+2884
File diff ditekan karena terlalu besar
Load Diff
+542
@@ -0,0 +1,542 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "function_signature.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "info.hpp"
|
||||
#include "log.hpp"
|
||||
#include "module_function.hpp"
|
||||
|
||||
#include <timemory/utility/filepath.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <ios>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
bool
|
||||
is_text_file(const std::string& filename);
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
inline string_t
|
||||
to_lower(string_t s)
|
||||
{
|
||||
for(auto& itr : s)
|
||||
itr = tolower(itr);
|
||||
return s;
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
template <typename Tp, std::enable_if_t<!std::is_same<Tp, std::string>::value, int> = 0>
|
||||
snippet_pointer_t
|
||||
get_snippet(Tp arg)
|
||||
{
|
||||
return std::make_shared<snippet_t>(const_expr_t{ arg });
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
template <typename Tp, std::enable_if_t<std::is_same<Tp, std::string>::value, int> = 0>
|
||||
snippet_pointer_t
|
||||
get_snippet(const Tp& arg)
|
||||
{
|
||||
return std::make_shared<snippet_t>(const_expr_t{ arg.c_str() });
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
template <typename... Args>
|
||||
snippet_pointer_vec_t
|
||||
get_snippets(Args&&... args)
|
||||
{
|
||||
snippet_pointer_vec_t _tmp{};
|
||||
TIMEMORY_FOLD_EXPRESSION(_tmp.push_back(get_snippet(std::forward<Args>(args))));
|
||||
return _tmp;
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
struct rocprofsys_call_expr
|
||||
{
|
||||
using snippet_pointer_t = std::shared_ptr<snippet_t>;
|
||||
|
||||
template <typename... Args>
|
||||
rocprofsys_call_expr(Args&&... args)
|
||||
: m_params(get_snippets(std::forward<Args>(args)...))
|
||||
{}
|
||||
|
||||
snippet_vec_t get_params()
|
||||
{
|
||||
snippet_vec_t _ret;
|
||||
for(auto& itr : m_params)
|
||||
_ret.push_back(itr.get());
|
||||
return _ret;
|
||||
}
|
||||
|
||||
inline call_expr_pointer_t get(procedure_t* func)
|
||||
{
|
||||
return call_expr_pointer_t((func) ? new call_expr_t(*func, get_params())
|
||||
: nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
snippet_pointer_vec_t m_params;
|
||||
};
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
struct rocprofsys_snippet_vec
|
||||
{
|
||||
using entry_type = std::vector<rocprofsys_call_expr>;
|
||||
using value_type = std::vector<call_expr_pointer_t>;
|
||||
|
||||
template <typename... Args>
|
||||
void generate(procedure_t* func, Args&&... args)
|
||||
{
|
||||
auto _expr = rocprofsys_call_expr(std::forward<Args>(args)...);
|
||||
auto _call = _expr.get(func);
|
||||
if(_call)
|
||||
{
|
||||
m_entries.push_back(_expr);
|
||||
m_data.push_back(_call);
|
||||
// m_data.push_back(entry_type{ _call, _expr });
|
||||
}
|
||||
}
|
||||
|
||||
void append(snippet_vec_t& _obj)
|
||||
{
|
||||
for(auto& itr : m_data)
|
||||
_obj.push_back(itr.get());
|
||||
}
|
||||
|
||||
private:
|
||||
entry_type m_entries;
|
||||
value_type m_data;
|
||||
};
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
static inline bool
|
||||
rocprofsys_get_is_executable(std::string_view _cmd, bool _default_v)
|
||||
{
|
||||
bool _is_executable = _default_v;
|
||||
|
||||
if(_cmd.empty())
|
||||
{
|
||||
if(!tim::filepath::exists(std::string{ _cmd }))
|
||||
{
|
||||
verbprintf(
|
||||
0,
|
||||
"Warning! '%s' was not found. Dyninst may fail to open the binary for "
|
||||
"instrumentation...\n",
|
||||
_cmd.data());
|
||||
}
|
||||
|
||||
Dyninst::SymtabAPI::Symtab* _symtab = nullptr;
|
||||
if(Dyninst::SymtabAPI::Symtab::openFile(_symtab, _cmd.data()))
|
||||
{
|
||||
_is_executable = _symtab->isExecutable() && _symtab->isExec();
|
||||
Dyninst::SymtabAPI::Symtab::closeSymtab(_symtab);
|
||||
}
|
||||
}
|
||||
return _is_executable;
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
static inline address_space_t*
|
||||
rocprofsys_get_address_space(patch_pointer_t& _bpatch, int _cmdc, char** _cmdv,
|
||||
const std::vector<std::string>& _cmdenv, bool _rewrite,
|
||||
int _pid = -1, const std::string& _name = {})
|
||||
{
|
||||
address_space_t* mutatee = nullptr;
|
||||
|
||||
if(_rewrite)
|
||||
{
|
||||
if(is_text_file(_name))
|
||||
{
|
||||
errprintf(-127,
|
||||
"'%s' is a text file. rocprof-sys only supports instrumenting "
|
||||
"binary files",
|
||||
_name.c_str());
|
||||
}
|
||||
|
||||
verbprintf(1, "Opening '%s' for binary rewrite... ", _name.c_str());
|
||||
fflush(stderr);
|
||||
if(!_name.empty()) mutatee = _bpatch->openBinary(_name.c_str(), false);
|
||||
if(!mutatee)
|
||||
{
|
||||
verbprintf(-1, "Failed to open binary '%s'\n", _name.c_str());
|
||||
throw std::runtime_error("Failed to open binary");
|
||||
}
|
||||
verbprintf_bare(1, "Done\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
bool _attach = (_pid >= 0);
|
||||
|
||||
// override the current environment create/attach to process, revert environment
|
||||
using strpair_t = std::pair<std::string, std::string>;
|
||||
auto _imported = std::vector<strpair_t>{};
|
||||
auto _exported = std::vector<strpair_t>{};
|
||||
auto _get_env_pair = [](const std::string& _full) {
|
||||
auto _pos = _full.find('=');
|
||||
if(_pos < _full.length())
|
||||
return std::make_pair(_full.substr(0, _pos), _full.substr(_pos + 1));
|
||||
return strpair_t{};
|
||||
};
|
||||
|
||||
if(environ)
|
||||
{
|
||||
size_t _idx = 0;
|
||||
while(environ[_idx] != nullptr)
|
||||
_imported.emplace_back(_get_env_pair(environ[_idx++]));
|
||||
}
|
||||
|
||||
for(const auto& itr : _cmdenv)
|
||||
{
|
||||
_exported.emplace_back(_get_env_pair(itr));
|
||||
}
|
||||
|
||||
for(const auto& itr : _exported)
|
||||
{
|
||||
setenv(itr.first.c_str(), itr.second.c_str(), 1);
|
||||
verbprintf(4, "[env] %s=%s\n", itr.first.c_str(), itr.second.c_str());
|
||||
}
|
||||
|
||||
if(_attach)
|
||||
{
|
||||
verbprintf(1, "Attaching to process %i... ", _pid);
|
||||
fflush(stderr);
|
||||
char* _cmdv0 = (_cmdc > 0) ? _cmdv[0] : nullptr;
|
||||
mutatee = _bpatch->processAttach(_cmdv0, _pid);
|
||||
if(!mutatee)
|
||||
{
|
||||
verbprintf(-1, "Failed to connect to process %i\n", (int) _pid);
|
||||
throw std::runtime_error("Failed to attach to process");
|
||||
}
|
||||
verbprintf_bare(1, "Done\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_cmdc < 1) errprintf(-127, "No command provided");
|
||||
|
||||
if(is_text_file(_cmdv[0]))
|
||||
{
|
||||
errprintf(-1,
|
||||
"'%s' is a text file. rocprof-sys only supports instrumenting "
|
||||
"binary files",
|
||||
_cmdv[0]);
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
for(int i = 0; i < _cmdc; ++i)
|
||||
{
|
||||
if(!_cmdv || !_cmdv[i]) continue;
|
||||
ss << " " << _cmdv[i];
|
||||
}
|
||||
auto _cmd_msg = ss.str();
|
||||
if(_cmd_msg.length() > 1) _cmd_msg = _cmd_msg.substr(1);
|
||||
|
||||
verbprintf(1, "Creating process '%s'... ", _cmd_msg.c_str());
|
||||
fflush(stderr);
|
||||
mutatee = _bpatch->processCreate(_cmdv[0], (const char**) _cmdv, nullptr);
|
||||
if(!mutatee)
|
||||
{
|
||||
verbprintf(-1, "Failed to create process: '%s'\n", _cmd_msg.c_str());
|
||||
throw std::runtime_error("Failed to create process");
|
||||
}
|
||||
verbprintf_bare(1, "Done\n");
|
||||
}
|
||||
}
|
||||
|
||||
return mutatee;
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
TIMEMORY_NOINLINE inline void
|
||||
rocprofsys_thread_exit(thread_t* thread, BPatch_exitType exit_type)
|
||||
{
|
||||
if(!thread) return;
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Executing the thread callback");
|
||||
|
||||
BPatch_process* app = thread->getProcess();
|
||||
|
||||
if(!terminate_expr)
|
||||
{
|
||||
fprintf(stderr, "[rocprof-sys][exe] continuing execution\n");
|
||||
app->continueExecution();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(exit_type)
|
||||
{
|
||||
case ExitedNormally:
|
||||
{
|
||||
fprintf(stderr, "[rocprof-sys][exe] Thread exited normally\n");
|
||||
break;
|
||||
}
|
||||
case ExitedViaSignal:
|
||||
{
|
||||
fprintf(stderr, "[rocprof-sys][exe] Thread terminated unexpectedly\n");
|
||||
break;
|
||||
}
|
||||
case NoExit:
|
||||
default:
|
||||
{
|
||||
fprintf(stderr, "[rocprof-sys][exe] %s invoked with NoExit\n", __FUNCTION__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// terminate_expr = nullptr;
|
||||
thread->oneTimeCode(*terminate_expr);
|
||||
|
||||
fprintf(stderr, "[rocprof-sys][exe] continuing execution\n");
|
||||
app->continueExecution();
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
TIMEMORY_NOINLINE inline void
|
||||
rocprofsys_fork_callback(thread_t* parent, thread_t* child)
|
||||
{
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Executing the fork callback");
|
||||
|
||||
if(child)
|
||||
{
|
||||
auto* app = child->getProcess();
|
||||
if(app)
|
||||
{
|
||||
verbprintf(4, "Stopping execution and detaching child fork...\n");
|
||||
app->stopExecution();
|
||||
app->detach(true);
|
||||
// app->terminateExecution();
|
||||
// app->continueExecution();
|
||||
}
|
||||
}
|
||||
|
||||
if(parent)
|
||||
{
|
||||
auto* app = parent->getProcess();
|
||||
if(app)
|
||||
{
|
||||
verbprintf(4, "Continuing execution on parent after fork callback...\n");
|
||||
app->continueExecution();
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
// path resolution helpers
|
||||
//
|
||||
std::string&
|
||||
rocprofsys_get_exe_realpath();
|
||||
//
|
||||
std::optional<std::string>
|
||||
rocprofsys_get_origin(const char* _name,
|
||||
std::vector<int>&& _open_modes = { (RTLD_LAZY | RTLD_NOLOAD) });
|
||||
//
|
||||
std::vector<std::string>
|
||||
rocprofsys_get_link_map(const char* _lib, const std::string& _exclude_linked_by = {},
|
||||
const std::string& _exclude_re = {},
|
||||
std::vector<int>&& _open_modes = { (RTLD_LAZY | RTLD_NOLOAD) });
|
||||
//
|
||||
//======================================================================================//
|
||||
// insert_instr -- insert instrumentation into a function
|
||||
//
|
||||
template <typename Tp>
|
||||
bool
|
||||
insert_instr(address_space_t* mutatee, const std::vector<point_t*>& _points, Tp traceFunc,
|
||||
procedure_loc_t, bool allow_traps)
|
||||
{
|
||||
if(!traceFunc || _points.empty()) return false;
|
||||
|
||||
auto _names = [&_points]() {
|
||||
std::set<std::string> _v{};
|
||||
for(const auto& itr : _points)
|
||||
if(itr && itr->getFunction()) _v.emplace(get_name(itr->getFunction()));
|
||||
return _v;
|
||||
}();
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Inserting", _points.size(),
|
||||
"instrumentation points into function(s)", _names);
|
||||
|
||||
auto _trace = traceFunc.get();
|
||||
auto _traps = std::set<point_t*>{};
|
||||
if(!allow_traps)
|
||||
{
|
||||
for(const auto& itr : _points)
|
||||
{
|
||||
if(itr && itr->usesTrap_NP()) _traps.insert(itr);
|
||||
}
|
||||
}
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Found", _traps.size(),
|
||||
"instrumentation points using traps in function(s)", _names);
|
||||
|
||||
size_t _n = 0;
|
||||
for(const auto& itr : _points)
|
||||
{
|
||||
if(!itr || _traps.count(itr) > 0) continue;
|
||||
mutatee->insertSnippet(*_trace, *itr);
|
||||
++_n;
|
||||
}
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Inserted", _n, "instrumentation points in function(s)",
|
||||
_names);
|
||||
|
||||
return (_n > 0);
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
// insert_instr -- insert instrumentation into loops
|
||||
//
|
||||
template <typename Tp>
|
||||
bool
|
||||
insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
|
||||
procedure_loc_t traceLoc, flow_graph_t* cfGraph,
|
||||
basic_loop_t* loopToInstrument, bool allow_traps)
|
||||
{
|
||||
if(!funcToInstr) return false;
|
||||
module_t* module = funcToInstr->getModule();
|
||||
if(!module || !traceFunc) return false;
|
||||
|
||||
std::vector<point_t*>* _points = nullptr;
|
||||
auto _trace = traceFunc.get();
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Searching for loop instrumentation points in function",
|
||||
get_name(funcToInstr));
|
||||
|
||||
if(!cfGraph) funcToInstr->getCFG();
|
||||
if(cfGraph && loopToInstrument)
|
||||
{
|
||||
if(traceLoc == BPatch_entry)
|
||||
_points = cfGraph->findLoopInstPoints(BPatch_locLoopEntry, loopToInstrument);
|
||||
else if(traceLoc == BPatch_exit)
|
||||
_points = cfGraph->findLoopInstPoints(BPatch_locLoopExit, loopToInstrument);
|
||||
}
|
||||
else
|
||||
{
|
||||
_points = funcToInstr->findPoint(traceLoc);
|
||||
}
|
||||
|
||||
if(_points == nullptr) return false;
|
||||
if(_points->empty()) return false;
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Inserting max of", _points->size(),
|
||||
"loop instrumentation points in function",
|
||||
get_name(funcToInstr));
|
||||
|
||||
std::set<point_t*> _traps{};
|
||||
if(!allow_traps)
|
||||
{
|
||||
for(auto& itr : *_points)
|
||||
{
|
||||
if(itr && itr->usesTrap_NP()) _traps.insert(itr);
|
||||
}
|
||||
}
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Found", _traps.size(),
|
||||
"loop instrumentation points using traps in function",
|
||||
get_name(funcToInstr));
|
||||
|
||||
size_t _n = 0;
|
||||
for(auto& itr : *_points)
|
||||
{
|
||||
if(!itr || _traps.count(itr) > 0) continue;
|
||||
mutatee->insertSnippet(*_trace, *itr);
|
||||
++_n;
|
||||
}
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Inserted", _n, "loop instrumentation points in function",
|
||||
get_name(funcToInstr));
|
||||
|
||||
return (_n > 0);
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
// insert_instr -- insert instrumentation into basic blocks
|
||||
//
|
||||
template <typename Tp>
|
||||
bool
|
||||
insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc,
|
||||
basic_block_t* basicBlock, bool allow_traps)
|
||||
{
|
||||
if(!basicBlock) return false;
|
||||
|
||||
point_t* _point = nullptr;
|
||||
auto _trace = traceFunc.get();
|
||||
|
||||
ROCPROFSYS_ADD_LOG_ENTRY(
|
||||
"Searching for basic-block entry and exit instrumentation points ::",
|
||||
*basicBlock);
|
||||
|
||||
basic_block_t* _bb = basicBlock;
|
||||
switch(traceLoc)
|
||||
{
|
||||
case BPatch_entry: _point = _bb->findEntryPoint(); break;
|
||||
case BPatch_exit: _point = _bb->findExitPoint(); break;
|
||||
default:
|
||||
verbprintf(0, "Warning! trace location type %i not supported\n",
|
||||
(int) traceLoc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_point == nullptr)
|
||||
{
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("No instrumentation points were found in basic-block ",
|
||||
*basicBlock);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!allow_traps && _point->usesTrap_NP())
|
||||
{
|
||||
ROCPROFSYS_ADD_LOG_ENTRY("Basic-block", *basicBlock,
|
||||
"uses traps and traps are disallowed");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(traceLoc)
|
||||
{
|
||||
case BPatch_entry:
|
||||
case BPatch_exit: return (mutatee->insertSnippet(*_trace, *_point) != nullptr);
|
||||
default:
|
||||
{
|
||||
verbprintf(0, "Warning! trace location type %i not supported\n",
|
||||
(int) traceLoc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# rocprofiler-systems-run target
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_executable(
|
||||
rocprofiler-systems-run
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprof-sys-run.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprof-sys-run.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/impl.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(rocprofiler-systems-run PRIVATE TIMEMORY_CMAKE=1)
|
||||
target_include_directories(rocprofiler-systems-run PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_link_libraries(
|
||||
rocprofiler-systems-run
|
||||
PRIVATE
|
||||
rocprofiler-systems::rocprofiler-systems-compile-definitions
|
||||
rocprofiler-systems::rocprofiler-systems-headers
|
||||
rocprofiler-systems::rocprofiler-systems-common-library
|
||||
rocprofiler-systems::rocprofiler-systems-core
|
||||
rocprofiler-systems::rocprofiler-systems-sanitizer
|
||||
)
|
||||
set_target_properties(
|
||||
rocprofiler-systems-run
|
||||
PROPERTIES
|
||||
BUILD_RPATH "\$ORIGIN:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
|
||||
INSTALL_RPATH "${ROCPROFSYS_EXE_INSTALL_RPATH}"
|
||||
OUTPUT_NAME ${BINARY_NAME_PREFIX}-run
|
||||
)
|
||||
|
||||
rocprofiler_systems_strip_target(rocprofiler-systems-run)
|
||||
|
||||
install(TARGETS rocprofiler-systems-run DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
@@ -0,0 +1,368 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "rocprof-sys-run.hpp"
|
||||
|
||||
#include "common/defines.h"
|
||||
#include "common/delimit.hpp"
|
||||
#include "common/environment.hpp"
|
||||
#include "common/join.hpp"
|
||||
#include "common/setup.hpp"
|
||||
#include "core/argparse.hpp"
|
||||
#include "core/config.hpp"
|
||||
#include "core/state.hpp"
|
||||
#include "core/timemory.hpp"
|
||||
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/environment/types.hpp>
|
||||
#include <timemory/log/color.hpp>
|
||||
#include <timemory/settings/types.hpp>
|
||||
#include <timemory/settings/vsettings.hpp>
|
||||
#include <timemory/signals/signal_handlers.hpp>
|
||||
#include <timemory/utility/argparse.hpp>
|
||||
#include <timemory/utility/console.hpp>
|
||||
#include <timemory/utility/filepath.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/wait.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
namespace color = ::tim::log::color;
|
||||
namespace filepath = ::tim::filepath; // NOLINT
|
||||
namespace console = ::tim::utility::console;
|
||||
namespace argparse = ::tim::argparse;
|
||||
namespace signals = ::tim::signals;
|
||||
using settings = ::rocprofsys::settings;
|
||||
using namespace ::timemory::join;
|
||||
using ::tim::get_env;
|
||||
using ::tim::log::stream;
|
||||
|
||||
namespace std
|
||||
{
|
||||
std::string
|
||||
to_string(bool _v)
|
||||
{
|
||||
return (_v) ? "true" : "false";
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string
|
||||
get_internal_libpath(const std::string& _lib)
|
||||
{
|
||||
auto _exe = std::string_view{ realpath("/proc/self/exe", nullptr) };
|
||||
auto _pos = _exe.find_last_of('/');
|
||||
auto _dir = std::string{ "./" };
|
||||
if(_pos != std::string_view::npos) _dir = _exe.substr(0, _pos);
|
||||
return rocprofsys::common::join("/", _dir, "..", "lib", _lib);
|
||||
}
|
||||
|
||||
parser_data_t&
|
||||
get_initial_environment(parser_data_t& _data)
|
||||
{
|
||||
if(environ != nullptr)
|
||||
{
|
||||
int idx = 0;
|
||||
while(environ[idx] != nullptr)
|
||||
{
|
||||
auto* _v = environ[idx++];
|
||||
_data.initial.emplace(_v);
|
||||
_data.current.emplace_back(strdup(_v));
|
||||
}
|
||||
}
|
||||
|
||||
return _data;
|
||||
}
|
||||
|
||||
int
|
||||
get_verbose(parser_data_t& _data)
|
||||
{
|
||||
auto& verbose = _data.verbose;
|
||||
verbose = get_env("ROCPROFSYS_CAUSAL_VERBOSE",
|
||||
get_env<int>("ROCPROFSYS_VERBOSE", verbose, false));
|
||||
auto _debug = get_env("ROCPROFSYS_CAUSAL_DEBUG",
|
||||
get_env<bool>("ROCPROFSYS_DEBUG", false, false));
|
||||
if(_debug) verbose += 8;
|
||||
return verbose;
|
||||
}
|
||||
|
||||
std::string
|
||||
get_realpath(const std::string& _v)
|
||||
{
|
||||
auto* _tmp = realpath(_v.c_str(), nullptr);
|
||||
auto _ret = std::string{ _tmp };
|
||||
free(_tmp);
|
||||
return _ret;
|
||||
}
|
||||
|
||||
auto
|
||||
toggle_suppression(std::tuple<bool, bool> _inp)
|
||||
{
|
||||
auto _out =
|
||||
std::make_tuple(settings::suppress_config(), settings::suppress_parsing());
|
||||
std::tie(settings::suppress_config(), settings::suppress_parsing()) = _inp;
|
||||
return _out;
|
||||
}
|
||||
|
||||
// disable suppression when exe loads but store original values for restoration later
|
||||
auto initial_suppression = toggle_suppression({ true, true });
|
||||
} // namespace
|
||||
|
||||
void
|
||||
print_command(const parser_data_t& _data, std::string_view _prefix)
|
||||
{
|
||||
auto verbose = _data.verbose;
|
||||
const auto& _argv = _data.command;
|
||||
if(verbose >= 1)
|
||||
stream(std::cout, color::info())
|
||||
<< _prefix << "Executing '" << join(array_config{ " " }, _argv) << "'...\n";
|
||||
|
||||
std::cerr << color::end() << std::flush;
|
||||
}
|
||||
|
||||
void
|
||||
prepare_command_for_run(char* _exe, parser_data_t& _data)
|
||||
{
|
||||
if(!_data.launcher.empty())
|
||||
{
|
||||
bool _injected = false;
|
||||
auto _new_argv = std::vector<char*>{};
|
||||
for(auto* itr : _data.command)
|
||||
{
|
||||
if(!_injected && std::regex_search(itr, std::regex{ _data.launcher }))
|
||||
{
|
||||
_new_argv.emplace_back(_exe);
|
||||
_new_argv.emplace_back(strdup("--"));
|
||||
_injected = true;
|
||||
}
|
||||
_new_argv.emplace_back(itr);
|
||||
}
|
||||
|
||||
if(!_injected)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
join("", "rocprof-sys-run was unable to match \"", _data.launcher,
|
||||
"\" to any arguments on the command line: \"",
|
||||
join(array_config{ " ", "", "" }, _data.command), "\""));
|
||||
}
|
||||
|
||||
std::swap(_data.command, _new_argv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
prepare_environment_for_run(parser_data_t& _data)
|
||||
{
|
||||
if(_data.launcher.empty())
|
||||
{
|
||||
rocprofsys::argparse::add_ld_preload(_data);
|
||||
rocprofsys::argparse::add_ld_library_path(_data);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_updated_environment(parser_data_t& _data, std::string_view _prefix)
|
||||
{
|
||||
auto _verbose = get_verbose(_data);
|
||||
|
||||
if(_verbose < 0) return;
|
||||
|
||||
auto _env = _data.current;
|
||||
const auto& _updated_envs = _data.updated;
|
||||
|
||||
std::sort(_env.begin(), _env.end(), [](auto* _lhs, auto* _rhs) {
|
||||
if(!_lhs) return false;
|
||||
if(!_rhs) return true;
|
||||
return std::string_view{ _lhs } < std::string_view{ _rhs };
|
||||
});
|
||||
|
||||
std::vector<std::string_view> _updates = {};
|
||||
std::vector<std::string_view> _general = {};
|
||||
|
||||
for(auto* itr : _env)
|
||||
{
|
||||
if(itr == nullptr) continue;
|
||||
|
||||
auto _is_omni = (std::string_view{ itr }.find("ROCPROFSYS") == 0);
|
||||
auto _updated = false;
|
||||
for(const auto& vitr : _updated_envs)
|
||||
{
|
||||
if(std::string_view{ itr }.find(vitr) == 0)
|
||||
{
|
||||
_updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_updated)
|
||||
_updates.emplace_back(itr);
|
||||
else if(_verbose >= 1 && _is_omni)
|
||||
_general.emplace_back(itr);
|
||||
}
|
||||
|
||||
if(_general.size() + _updates.size() == 0 || _verbose < 0) return;
|
||||
|
||||
std::cerr << std::endl;
|
||||
|
||||
for(auto& itr : _general)
|
||||
stream(std::cerr, color::source()) << _prefix << itr << "\n";
|
||||
for(auto& itr : _updates)
|
||||
stream(std::cerr, color::source()) << _prefix << itr << "\n";
|
||||
|
||||
std::cerr << color::end() << std::flush;
|
||||
}
|
||||
|
||||
parser_data_t&
|
||||
parse_args(int argc, char** argv, parser_data_t& _parser_data, bool& _fork_exec)
|
||||
{
|
||||
get_initial_environment(_parser_data);
|
||||
|
||||
bool _do_parse_args = false;
|
||||
for(int i = 1; i < argc; ++i)
|
||||
{
|
||||
auto _arg = std::string_view{ argv[i] };
|
||||
if(_arg == "--" || _arg == "-?" || _arg == "-h" || _arg == "--help" ||
|
||||
_arg == "--version")
|
||||
_do_parse_args = true;
|
||||
}
|
||||
|
||||
if(!_do_parse_args && argc > 1 && std::string_view{ argv[1] }.find('-') == 0)
|
||||
_do_parse_args = true;
|
||||
|
||||
if(!_do_parse_args) return parse_command(argc, argv, _parser_data);
|
||||
|
||||
using parser_t = argparse::argument_parser;
|
||||
using parser_err_t = typename parser_t::result_type;
|
||||
|
||||
toggle_suppression(initial_suppression);
|
||||
rocprofsys::argparse::init_parser(_parser_data);
|
||||
|
||||
// no need for backtraces
|
||||
signals::disable_signal_detection(signals::signal_settings::get_enabled());
|
||||
|
||||
auto help_check = [](parser_t& p, int _argc, char** _argv) {
|
||||
std::set<std::string> help_args = { "-h", "--help", "-?" };
|
||||
return (p.exists("help") || _argc == 1 ||
|
||||
(_argc > 1 && help_args.find(_argv[1]) != help_args.end()));
|
||||
};
|
||||
|
||||
const auto* _desc = R"desc(
|
||||
Command line interface to rocprof-sys configuration.
|
||||
)desc";
|
||||
|
||||
auto parser = parser_t{ basename(argv[0]), _desc };
|
||||
|
||||
parser.on_error([](parser_t&, const parser_err_t& _err) {
|
||||
stream(std::cerr, color::fatal()) << _err << "\n";
|
||||
exit(EXIT_FAILURE);
|
||||
});
|
||||
|
||||
parser.enable_help("", "Usage: rocprof-sys-run <OPTIONS> -- <COMMAND> <ARGS>");
|
||||
parser.enable_version("rocprof-sys-run", ROCPROFSYS_ARGPARSE_VERSION_INFO);
|
||||
|
||||
auto _cols = std::get<0>(console::get_columns());
|
||||
if(_cols > parser.get_help_width() + 8)
|
||||
parser.set_description_width(
|
||||
std::min<int>(_cols - parser.get_help_width() - 8, 120));
|
||||
|
||||
// disable options related to causal profiling
|
||||
_parser_data.processed_groups.emplace("causal");
|
||||
|
||||
rocprofsys::argparse::add_core_arguments(parser, _parser_data);
|
||||
rocprofsys::argparse::add_extended_arguments(parser, _parser_data);
|
||||
|
||||
parser.start_group("EXECUTION OPTIONS", "");
|
||||
parser.add_argument({ "--fork" }, "Execute via fork + execvpe instead of execvpe")
|
||||
.min_count(0)
|
||||
.max_count(1)
|
||||
.dtype("boolean")
|
||||
.action([&](parser_t& p) { _fork_exec = p.get<bool>("fork"); });
|
||||
|
||||
auto _inpv = std::vector<char*>{};
|
||||
auto& _outv = _parser_data.command;
|
||||
bool _hash = false;
|
||||
for(int i = 0; i < argc; ++i)
|
||||
{
|
||||
if(argv[i] == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(_hash)
|
||||
{
|
||||
_outv.emplace_back(strdup(argv[i]));
|
||||
}
|
||||
else if(std::string_view{ argv[i] } == "--")
|
||||
{
|
||||
_hash = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inpv.emplace_back(strdup(argv[i]));
|
||||
}
|
||||
}
|
||||
|
||||
auto _cerr = parser.parse_args(_inpv.size(), _inpv.data());
|
||||
if(_cerr)
|
||||
{
|
||||
std::cerr << _cerr.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
tim::log::monochrome() = _parser_data.monochrome;
|
||||
|
||||
return _parser_data;
|
||||
}
|
||||
|
||||
parser_data_t&
|
||||
parse_command(int argc, char** argv, parser_data_t& _parser_data)
|
||||
{
|
||||
toggle_suppression(initial_suppression);
|
||||
rocprofsys::argparse::init_parser(_parser_data);
|
||||
|
||||
// no need for backtraces
|
||||
signals::disable_signal_detection(signals::signal_settings::get_enabled());
|
||||
|
||||
auto& _outv = _parser_data.command;
|
||||
bool _hash = false;
|
||||
for(int i = 1; i < argc; ++i)
|
||||
{
|
||||
_outv.emplace_back(strdup(argv[i]));
|
||||
}
|
||||
|
||||
return _parser_data;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "rocprof-sys-run.hpp"
|
||||
#include "core/mproc.hpp"
|
||||
|
||||
#include <timemory/log/color.hpp>
|
||||
#include <timemory/log/macros.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
auto* _getenv_at_load = getenv("TIMEMORY_LIBRARY_CTOR");
|
||||
auto _setenv_at_load = setenv("TIMEMORY_LIBRARY_CTOR", "0", 0);
|
||||
} // namespace
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
if(!_getenv_at_load)
|
||||
unsetenv("TIMEMORY_LIBRARY_CTOR");
|
||||
else
|
||||
setenv("TIMEMORY_LIBRARY_CTOR", _getenv_at_load, 1);
|
||||
|
||||
auto _print_usage = [argv]() {
|
||||
std::cerr << tim::log::color::fatal() << "Usage: " << argv[0]
|
||||
<< " <OPTIONS> -- <COMMAND> <ARGS>" << tim::log::color::end()
|
||||
<< std::endl;
|
||||
};
|
||||
|
||||
if(argc == 1)
|
||||
{
|
||||
_print_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
auto _parse_data = parser_data_t{};
|
||||
auto _fork_exec = false;
|
||||
parse_args(argc, argv, _parse_data, _fork_exec);
|
||||
prepare_command_for_run(argv[0], _parse_data);
|
||||
prepare_environment_for_run(_parse_data);
|
||||
|
||||
auto& _argv = _parse_data.command;
|
||||
auto& _envp = _parse_data.current;
|
||||
if(!_argv.empty())
|
||||
{
|
||||
print_updated_environment(_parse_data, "ROCPROFSYS: ");
|
||||
print_command(_parse_data, "ROCPROFSYS: ");
|
||||
_argv.emplace_back(nullptr);
|
||||
_envp.emplace_back(nullptr);
|
||||
|
||||
if(_fork_exec)
|
||||
{
|
||||
auto _main_pid = getpid();
|
||||
auto _pid = fork();
|
||||
|
||||
if(_pid == 0)
|
||||
{
|
||||
return execvpe(_argv.front(), _argv.data(), _envp.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto _status = rocprofsys::mproc::wait_pid(_pid);
|
||||
auto _ec = rocprofsys::mproc::diagnose_status(_pid, _status);
|
||||
if(_ec != 0 && _parse_data.verbose >= 0)
|
||||
{
|
||||
TIMEMORY_PRINTF_FATAL(
|
||||
stderr, "process %i exiting with non-zero exit code: %i\n", _pid,
|
||||
_ec);
|
||||
}
|
||||
else if(_parse_data.verbose >= 2)
|
||||
{
|
||||
TIMEMORY_PRINTF_FATAL(
|
||||
stderr,
|
||||
"rocprof-sys run in process %i completed. exit code: %i\n", _pid,
|
||||
_ec);
|
||||
}
|
||||
return _ec;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return execvpe(_argv.front(), _argv.data(), _envp.data());
|
||||
}
|
||||
}
|
||||
|
||||
_print_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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/argparse.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <map>
|
||||
#include <sched.h>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
using parser_data_t = rocprofsys::argparse::parser_data;
|
||||
|
||||
void
|
||||
print_command(const parser_data_t&, std::string_view);
|
||||
|
||||
void
|
||||
print_updated_environment(parser_data_t&, std::string_view);
|
||||
|
||||
void
|
||||
prepare_command_for_run(char*, parser_data_t&);
|
||||
|
||||
void
|
||||
prepare_environment_for_run(parser_data_t&);
|
||||
|
||||
parser_data_t&
|
||||
parse_args(int argc, char** argv, parser_data_t&, bool&);
|
||||
|
||||
parser_data_t&
|
||||
parse_command(int argc, char** argv, parser_data_t&);
|
||||
@@ -0,0 +1,32 @@
|
||||
# ------------------------------------------------------------------------------#
|
||||
#
|
||||
# rocprofiler-systems-sample target
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_executable(
|
||||
rocprofiler-systems-sample
|
||||
${CMAKE_CURRENT_LIST_DIR}/rocprof-sys-sample.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/impl.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(rocprofiler-systems-sample PRIVATE TIMEMORY_CMAKE=1)
|
||||
target_include_directories(rocprofiler-systems-sample PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_link_libraries(
|
||||
rocprofiler-systems-sample
|
||||
PRIVATE
|
||||
rocprofiler-systems::rocprofiler-systems-compile-definitions
|
||||
rocprofiler-systems::rocprofiler-systems-headers
|
||||
rocprofiler-systems::rocprofiler-systems-common-library
|
||||
)
|
||||
set_target_properties(
|
||||
rocprofiler-systems-sample
|
||||
PROPERTIES
|
||||
BUILD_RPATH "\$ORIGIN:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
|
||||
INSTALL_RPATH "${ROCPROFSYS_EXE_INSTALL_RPATH}"
|
||||
OUTPUT_NAME ${BINARY_NAME_PREFIX}-sample
|
||||
)
|
||||
|
||||
rocprofiler_systems_strip_target(rocprofiler-systems-sample)
|
||||
|
||||
install(TARGETS rocprofiler-systems-sample DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
@@ -0,0 +1,875 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "rocprof-sys-sample.hpp"
|
||||
|
||||
#include "common/delimit.hpp"
|
||||
#include "common/environment.hpp"
|
||||
#include "common/join.hpp"
|
||||
#include "common/setup.hpp"
|
||||
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/log/color.hpp>
|
||||
#include <timemory/utility/argparse.hpp>
|
||||
#include <timemory/utility/console.hpp>
|
||||
#include <timemory/utility/filepath.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
namespace color = tim::log::color;
|
||||
using namespace timemory::join;
|
||||
using tim::get_env;
|
||||
using tim::log::monochrome;
|
||||
using tim::log::stream;
|
||||
|
||||
namespace
|
||||
{
|
||||
int verbose = 0;
|
||||
auto updated_envs = std::set<std::string_view>{};
|
||||
auto original_envs = std::set<std::string>{};
|
||||
auto clock_id_choices = []() {
|
||||
auto clock_name = [](std::string _v) {
|
||||
constexpr auto _clock_prefix = std::string_view{ "clock_" };
|
||||
for(auto& itr : _v)
|
||||
itr = tolower(itr);
|
||||
auto _pos = _v.find(_clock_prefix);
|
||||
if(_pos == 0) _v = _v.substr(_pos + _clock_prefix.length());
|
||||
if(_v == "process_cputime_id") _v = "cputime";
|
||||
return _v;
|
||||
};
|
||||
|
||||
#define ROCPROFSYS_CLOCK_IDENTIFIER(VAL) \
|
||||
std::make_tuple(clock_name(#VAL), VAL, std::string_view{ #VAL })
|
||||
|
||||
auto _choices = std::vector<std::string>{};
|
||||
auto _aliases = std::map<std::string, std::vector<std::string>>{};
|
||||
for(auto itr : { ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_REALTIME),
|
||||
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_MONOTONIC),
|
||||
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_PROCESS_CPUTIME_ID),
|
||||
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_MONOTONIC_RAW),
|
||||
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_REALTIME_COARSE),
|
||||
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_MONOTONIC_COARSE),
|
||||
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_BOOTTIME) })
|
||||
{
|
||||
auto _choice = std::to_string(std::get<1>(itr));
|
||||
_choices.emplace_back(_choice);
|
||||
_aliases[_choice] = { std::get<0>(itr), std::string{ std::get<2>(itr) } };
|
||||
}
|
||||
|
||||
#undef ROCPROFSYS_CLOCK_IDENTIFIER
|
||||
|
||||
return std::make_pair(_choices, _aliases);
|
||||
}();
|
||||
} // namespace
|
||||
|
||||
std::string
|
||||
get_realpath(const std::string& _v)
|
||||
{
|
||||
auto* _tmp = realpath(_v.c_str(), nullptr);
|
||||
auto _ret = std::string{ _tmp };
|
||||
free(_tmp);
|
||||
return _ret;
|
||||
}
|
||||
|
||||
void
|
||||
print_command(const std::vector<char*>& _argv)
|
||||
{
|
||||
if(verbose >= 1)
|
||||
stream(std::cout, color::info())
|
||||
<< "Executing '" << join(array_config{ " " }, _argv) << "'...\n";
|
||||
}
|
||||
|
||||
std::vector<char*>
|
||||
get_initial_environment()
|
||||
{
|
||||
auto _env = std::vector<char*>{};
|
||||
if(environ != nullptr)
|
||||
{
|
||||
int idx = 0;
|
||||
while(environ[idx] != nullptr)
|
||||
{
|
||||
auto* _v = environ[idx++];
|
||||
original_envs.emplace(_v);
|
||||
_env.emplace_back(strdup(_v));
|
||||
}
|
||||
}
|
||||
|
||||
auto _dl_libpath = get_realpath(get_internal_libpath("librocprof-sys-dl.so"));
|
||||
auto _omni_libpath = get_realpath(get_internal_libpath("librocprof-sys.so"));
|
||||
auto _libexecpath = get_realpath(get_internal_script_path());
|
||||
|
||||
update_env(_env, "LD_PRELOAD", _dl_libpath, UPD_APPEND);
|
||||
update_env(_env, "LD_LIBRARY_PATH", tim::filepath::dirname(_dl_libpath), UPD_APPEND);
|
||||
update_env(_env, "ROCPROFSYS_SCRIPT_PATH", _libexecpath, UPD_REPLACE);
|
||||
|
||||
auto _mode = get_env<std::string>("ROCPROFSYS_MODE", "sampling", false);
|
||||
|
||||
update_env(_env, "ROCPROFSYS_USE_SAMPLING", (_mode != "causal"));
|
||||
|
||||
#if defined(ROCPROFSYS_USE_OMPT)
|
||||
if(!getenv("OMP_TOOL_LIBRARIES"))
|
||||
update_env(_env, "OMP_TOOL_LIBRARIES", _dl_libpath, UPD_APPEND);
|
||||
#endif
|
||||
return _env;
|
||||
}
|
||||
|
||||
std::string
|
||||
get_internal_libpath(const std::string& _lib)
|
||||
{
|
||||
auto _exe = std::string_view{ realpath("/proc/self/exe", nullptr) };
|
||||
auto _pos = _exe.find_last_of('/');
|
||||
auto _dir = std::string{ "./" };
|
||||
if(_pos != std::string_view::npos) _dir = _exe.substr(0, _pos);
|
||||
return rocprofsys::common::join("/", _dir, "..", "lib", _lib);
|
||||
}
|
||||
|
||||
std::string
|
||||
get_internal_script_path(void)
|
||||
{
|
||||
auto _exe = std::string_view{ realpath("/proc/self/exe", nullptr) };
|
||||
auto _pos = _exe.find_last_of('/');
|
||||
auto _dir = std::string{ "./" };
|
||||
if(_pos != std::string_view::npos) _dir = _exe.substr(0, _pos);
|
||||
|
||||
auto _script_dir =
|
||||
rocprofsys::common::join("/", _dir, "..", "libexec", "rocprofiler-systems");
|
||||
|
||||
return _script_dir;
|
||||
}
|
||||
|
||||
void
|
||||
print_updated_environment(std::vector<char*> _env)
|
||||
{
|
||||
if(get_env<int>("ROCPROFSYS_VERBOSE", 0) < 0) return;
|
||||
|
||||
std::sort(_env.begin(), _env.end(), [](auto* _lhs, auto* _rhs) {
|
||||
if(!_lhs) return false;
|
||||
if(!_rhs) return true;
|
||||
return std::string_view{ _lhs } < std::string_view{ _rhs };
|
||||
});
|
||||
|
||||
std::vector<char*> _updates = {};
|
||||
std::vector<char*> _general = {};
|
||||
|
||||
for(auto* itr : _env)
|
||||
{
|
||||
if(itr == nullptr) continue;
|
||||
|
||||
auto _is_omni = (std::string_view{ itr }.find("ROCPROFSYS") == 0);
|
||||
auto _updated = false;
|
||||
for(const auto& vitr : updated_envs)
|
||||
{
|
||||
if(std::string_view{ itr }.find(vitr) == 0)
|
||||
{
|
||||
_updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_updated)
|
||||
_updates.emplace_back(itr);
|
||||
else if(verbose >= 1 && _is_omni)
|
||||
_general.emplace_back(itr);
|
||||
}
|
||||
|
||||
if(_general.size() + _updates.size() == 0 || verbose < 0) return;
|
||||
|
||||
std::cerr << std::endl;
|
||||
|
||||
for(auto& itr : _general)
|
||||
stream(std::cerr, color::source()) << itr << "\n";
|
||||
for(auto& itr : _updates)
|
||||
stream(std::cerr, color::source()) << itr << "\n";
|
||||
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
void
|
||||
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
|
||||
update_mode&& _mode, std::string_view _join_delim)
|
||||
{
|
||||
updated_envs.emplace(_env_var);
|
||||
|
||||
auto _prepend = (_mode & UPD_PREPEND) == UPD_PREPEND;
|
||||
auto _append = (_mode & UPD_APPEND) == UPD_APPEND;
|
||||
auto _weak_upd = (_mode & UPD_WEAK) == UPD_WEAK;
|
||||
|
||||
auto _key = join("", _env_var, "=");
|
||||
for(auto& itr : _environ)
|
||||
{
|
||||
if(!itr) continue;
|
||||
if(std::string_view{ itr }.find(_key) == 0)
|
||||
{
|
||||
if(_weak_upd)
|
||||
{
|
||||
// if the value has changed, do not update but allow overridding the value
|
||||
// inherited from the initial env
|
||||
if(original_envs.find(std::string{ itr }) == original_envs.end()) return;
|
||||
}
|
||||
|
||||
if(_prepend || _append)
|
||||
{
|
||||
if(std::string_view{ itr }.find(join("", _env_val)) ==
|
||||
std::string_view::npos)
|
||||
{
|
||||
auto _val = std::string{ itr }.substr(_key.length());
|
||||
free(itr);
|
||||
if(_prepend)
|
||||
itr =
|
||||
strdup(join('=', _env_var, join(_join_delim, _val, _env_val))
|
||||
.c_str());
|
||||
else
|
||||
itr =
|
||||
strdup(join('=', _env_var, join(_join_delim, _env_val, _val))
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free(itr);
|
||||
itr = strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
_environ.emplace_back(
|
||||
strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str()));
|
||||
}
|
||||
|
||||
void
|
||||
remove_env(std::vector<char*>& _environ, std::string_view _env_var)
|
||||
{
|
||||
auto _key = join("", _env_var, "=");
|
||||
auto _match = [&_key](auto itr) { return std::string_view{ itr }.find(_key) == 0; };
|
||||
|
||||
_environ.erase(std::remove_if(_environ.begin(), _environ.end(), _match),
|
||||
_environ.end());
|
||||
|
||||
for(const auto& itr : original_envs)
|
||||
{
|
||||
if(std::string_view{ itr }.find(_key) == 0)
|
||||
_environ.emplace_back(strdup(itr.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char*>
|
||||
parse_args(int argc, char** argv, std::vector<char*>& _env)
|
||||
{
|
||||
using parser_t = tim::argparse::argument_parser;
|
||||
using parser_err_t = typename parser_t::result_type;
|
||||
|
||||
auto help_check = [](parser_t& p, int _argc, char** _argv) {
|
||||
std::set<std::string> help_args = { "-h", "--help", "-?" };
|
||||
return (p.exists("help") || _argc == 1 ||
|
||||
(_argc > 1 && help_args.find(_argv[1]) != help_args.end()));
|
||||
};
|
||||
|
||||
auto _pec = EXIT_SUCCESS;
|
||||
auto help_action = [&_pec, argc, argv](parser_t& p) {
|
||||
if(_pec != EXIT_SUCCESS)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "Error in command:";
|
||||
for(int i = 0; i < argc; ++i)
|
||||
msg << " " << argv[i];
|
||||
msg << "\n\n";
|
||||
stream(std::cerr, color::fatal()) << msg.str();
|
||||
std::cerr << std::flush;
|
||||
}
|
||||
|
||||
p.print_help();
|
||||
exit(_pec);
|
||||
};
|
||||
|
||||
auto* _dl_libpath =
|
||||
realpath(get_internal_libpath("librocprof-sys-dl.so").c_str(), nullptr);
|
||||
auto* _omni_libpath =
|
||||
realpath(get_internal_libpath("librocprof-sys.so").c_str(), nullptr);
|
||||
|
||||
auto parser = parser_t(argv[0]);
|
||||
|
||||
parser.on_error([](parser_t&, const parser_err_t& _err) {
|
||||
stream(std::cerr, color::fatal()) << _err << "\n";
|
||||
exit(EXIT_FAILURE);
|
||||
});
|
||||
|
||||
const auto* _cputime_desc =
|
||||
R"(Sample based on a CPU-clock timer (default). Accepts zero or more arguments:
|
||||
%{INDENT}%0. Enables sampling based on CPU-clock timer.
|
||||
%{INDENT}%1. Interrupts per second. E.g., 100 == sample every 10 milliseconds of CPU-time.
|
||||
%{INDENT}%2. Delay (in seconds of CPU-clock time). I.e., how long each thread should wait before taking first sample.
|
||||
%{INDENT}%3+ Thread IDs to target for sampling, starting at 0 (the main thread).
|
||||
%{INDENT}% May be specified as index or range, e.g., '0 2-4' will be interpreted as:
|
||||
%{INDENT}% sample the main thread (0), do not sample the first child thread but sample the 2nd, 3rd, and 4th child threads)";
|
||||
|
||||
const auto* _realtime_desc =
|
||||
R"(Sample based on a real-clock timer. Accepts zero or more arguments:
|
||||
%{INDENT}%0. Enables sampling based on real-clock timer.
|
||||
%{INDENT}%1. Interrupts per second. E.g., 100 == sample every 10 milliseconds of realtime.
|
||||
%{INDENT}%2. Delay (in seconds of real-clock time). I.e., how long each thread should wait before taking first sample.
|
||||
%{INDENT}%3+ Thread IDs to target for sampling, starting at 0 (the main thread).
|
||||
%{INDENT}% May be specified as index or range, e.g., '0 2-4' will be interpreted as:
|
||||
%{INDENT}% sample the main thread (0), do not sample the first child thread but sample the 2nd, 3rd, and 4th child threads
|
||||
%{INDENT}% When sampling with a real-clock timer, please note that enabling this will cause threads which are typically "idle"
|
||||
%{INDENT}% to consume more resources since, while idle, the real-clock time increases (and therefore triggers taking samples)
|
||||
%{INDENT}% whereas the CPU-clock time does not.)";
|
||||
|
||||
const auto* _hsa_interrupt_desc =
|
||||
R"(Set the value of the HSA_ENABLE_INTERRUPT environment variable.
|
||||
%{INDENT}% ROCm version 5.2 and older have a bug which will cause a deadlock if a sample is taken while waiting for the signal
|
||||
%{INDENT}% that a kernel completed -- which happens when sampling with a real-clock timer. We require this option to be set to
|
||||
%{INDENT}% when --realtime is specified to make users aware that, while this may fix the bug, it can have a negative impact on
|
||||
%{INDENT}% performance.
|
||||
%{INDENT}% Values:
|
||||
%{INDENT}% 0 avoid triggering the bug, potentially at the cost of reduced performance
|
||||
%{INDENT}% 1 do not modify how ROCm is notified about kernel completion)";
|
||||
|
||||
const auto* _trace_policy_desc =
|
||||
R"(Policy for new data when the buffer size limit is reached:
|
||||
%{INDENT}%- discard : new data is ignored
|
||||
%{INDENT}%- ring_buffer : new data overwrites oldest data)";
|
||||
|
||||
parser.set_use_color(true);
|
||||
parser.enable_help();
|
||||
parser.enable_version("rocprof-sys-sample", ROCPROFSYS_ARGPARSE_VERSION_INFO);
|
||||
|
||||
auto _cols = std::get<0>(tim::utility::console::get_columns());
|
||||
if(_cols > parser.get_help_width() + 8)
|
||||
parser.set_description_width(
|
||||
std::min<int>(_cols - parser.get_help_width() - 8, 120));
|
||||
|
||||
parser.start_group("DEBUG OPTIONS", "");
|
||||
parser.add_argument({ "--monochrome" }, "Disable colorized output")
|
||||
.max_count(1)
|
||||
.dtype("bool")
|
||||
.action([&](parser_t& p) {
|
||||
auto _monochrome = p.get<bool>("monochrome");
|
||||
monochrome() = _monochrome;
|
||||
p.set_use_color(!_monochrome);
|
||||
update_env(_env, "ROCPROFSYS_MONOCHROME", (_monochrome) ? "1" : "0");
|
||||
update_env(_env, "MONOCHROME", (_monochrome) ? "1" : "0");
|
||||
});
|
||||
parser.add_argument({ "--debug" }, "Debug output")
|
||||
.max_count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_DEBUG", p.get<bool>("debug"));
|
||||
});
|
||||
parser.add_argument({ "-v", "--verbose" }, "Verbose output")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<int>("verbose");
|
||||
verbose = _v;
|
||||
update_env(_env, "ROCPROFSYS_VERBOSE", _v);
|
||||
});
|
||||
|
||||
parser.start_group("GENERAL OPTIONS",
|
||||
"These are options which are ubiquitously applied");
|
||||
parser.add_argument({ "-c", "--config" }, "Configuration file")
|
||||
.min_count(0)
|
||||
.dtype("filepath")
|
||||
.action([&](parser_t& p) {
|
||||
update_env(
|
||||
_env, "ROCPROFSYS_CONFIG_FILE",
|
||||
join(array_config{ ":" }, p.get<std::vector<std::string>>("config")));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "-o", "--output" },
|
||||
"Output path. Accepts 1-2 parameters corresponding to the output "
|
||||
"path and the output prefix")
|
||||
.min_count(1)
|
||||
.max_count(2)
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<std::vector<std::string>>("output");
|
||||
update_env(_env, "ROCPROFSYS_OUTPUT_PATH", _v.at(0));
|
||||
if(_v.size() > 1) update_env(_env, "ROCPROFSYS_OUTPUT_PREFIX", _v.at(1));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "-T", "--trace" }, "Generate a detailed trace (perfetto output)")
|
||||
.max_count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_TRACE", p.get<bool>("trace"));
|
||||
});
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-P", "--profile" },
|
||||
"Generate a call-stack-based profile (conflicts with --flat-profile)")
|
||||
.max_count(1)
|
||||
.conflicts({ "flat-profile" })
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_PROFILE", p.get<bool>("profile"));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "-F", "--flat-profile" },
|
||||
"Generate a flat profile (conflicts with --profile)")
|
||||
.max_count(1)
|
||||
.conflicts({ "profile" })
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_PROFILE", p.get<bool>("flat-profile"));
|
||||
update_env(_env, "ROCPROFSYS_FLAT_PROFILE", p.get<bool>("flat-profile"));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "-H", "--host" },
|
||||
"Enable sampling host-based metrics for the process. E.g. CPU "
|
||||
"frequency, memory usage, etc.")
|
||||
.max_count(1)
|
||||
.action([&](parser_t& p) {
|
||||
auto _h = p.get<bool>("host");
|
||||
auto _d = p.get<bool>("device");
|
||||
update_env(_env, "ROCPROFSYS_USE_PROCESS_SAMPLING", _h || _d);
|
||||
update_env(_env, "ROCPROFSYS_CPU_FREQ_ENABLED", _h);
|
||||
if(_h) update_env(_env, "ROCPROFSYS_USE_AMD_SMI", _d);
|
||||
});
|
||||
parser
|
||||
.add_argument({ "-D", "--device" },
|
||||
"Enable sampling device-based metrics for the process. E.g. GPU "
|
||||
"temperature, memory usage, etc.")
|
||||
.max_count(1)
|
||||
.action([&](parser_t& p) {
|
||||
auto _h = p.get<bool>("host");
|
||||
auto _d = p.get<bool>("device");
|
||||
update_env(_env, "ROCPROFSYS_USE_PROCESS_SAMPLING", _h || _d);
|
||||
update_env(_env, "ROCPROFSYS_USE_AMD_SMI", _d);
|
||||
if(_d) update_env(_env, "ROCPROFSYS_CPU_FREQ_ENABLED", _h);
|
||||
});
|
||||
parser
|
||||
.add_argument({ "-w", "--wait" },
|
||||
"This option is a combination of '--trace-wait' and "
|
||||
"'--sampling-wait'. See the descriptions for those two options.")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_TRACE_DELAY", p.get<double>("wait"));
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_DELAY", p.get<double>("wait"));
|
||||
});
|
||||
parser
|
||||
.add_argument(
|
||||
{ "-d", "--duration" },
|
||||
"This option is a combination of '--trace-duration' and "
|
||||
"'--sampling-duration'. See the descriptions for those two options.")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_TRACE_DURATION", p.get<double>("duration"));
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_DURATION", p.get<double>("duration"));
|
||||
});
|
||||
|
||||
parser.start_group("TRACING OPTIONS", "Specific options controlling tracing (i.e. "
|
||||
"deterministic measurements of every event)");
|
||||
parser
|
||||
.add_argument({ "--trace-file" },
|
||||
"Specify the trace output filename. Relative filepath will be with "
|
||||
"respect to output path and output prefix.")
|
||||
.count(1)
|
||||
.dtype("filepath")
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_PERFETTO_FILE",
|
||||
p.get<std::string>("trace-file"));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--trace-buffer-size" },
|
||||
"Size limit for the trace output (in KB)")
|
||||
.count(1)
|
||||
.dtype("KB")
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_PERFETTO_BUFFER_SIZE_KB",
|
||||
p.get<int64_t>("trace-buffer-size"));
|
||||
});
|
||||
parser.add_argument({ "--trace-fill-policy" }, _trace_policy_desc)
|
||||
.count(1)
|
||||
.choices({ "discard", "ring_buffer" })
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_PERFETTO_FILL_POLICY",
|
||||
p.get<std::string>("trace-fill-policy"));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--trace-wait" },
|
||||
"Set the wait time (in seconds) "
|
||||
"before collecting trace and/or profiling data"
|
||||
"(in seconds). By default, the duration is in seconds of realtime "
|
||||
"but that can changed via --trace-clock-id.")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_TRACE_DELAY", p.get<double>("trace-wait"));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--trace-duration" },
|
||||
"Set the duration of the trace and/or profile data collection (in "
|
||||
"seconds). By default, the duration is in seconds of realtime but "
|
||||
"that can changed via --trace-clock-id.")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_TRACE_DURATION",
|
||||
p.get<double>("trace-duration"));
|
||||
});
|
||||
parser
|
||||
.add_argument(
|
||||
{ "--trace-periods" },
|
||||
"More powerful version of specifying trace delay and/or duration. Format is "
|
||||
"one or more groups of: <DELAY>:<DURATION>, <DELAY>:<DURATION>:<REPEAT>, "
|
||||
"and/or <DELAY>:<DURATION>:<REPEAT>:<CLOCK_ID>.")
|
||||
.min_count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_TRACE_PERIODS",
|
||||
join(array_config{ ",", "", "" },
|
||||
p.get<std::vector<std::string>>("trace-periods")));
|
||||
});
|
||||
parser
|
||||
.add_argument(
|
||||
{ "--trace-clock-id" },
|
||||
"Set the default clock ID for for trace delay/duration. Note: \"cputime\" is "
|
||||
"the *process* CPU time and might need to be scaled based on the number of "
|
||||
"threads, i.e. 4 seconds of CPU-time for an application with 4 fully active "
|
||||
"threads would equate to ~1 second of realtime. If this proves to be "
|
||||
"difficult to handle in practice, please file a feature request for "
|
||||
"rocprof-sys to auto-scale based on the number of threads.")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_TRACE_PERIOD_CLOCK_ID",
|
||||
p.get<double>("trace-clock-id"));
|
||||
})
|
||||
.choices(clock_id_choices.first)
|
||||
.choice_aliases(clock_id_choices.second);
|
||||
|
||||
parser.start_group("PROFILE OPTIONS",
|
||||
"Specific options controlling profiling (i.e. deterministic "
|
||||
"measurements which are aggregated into a summary)");
|
||||
parser.add_argument({ "--profile-format" }, "Data formats for profiling results")
|
||||
.min_count(1)
|
||||
.max_count(3)
|
||||
.required({ "profile|flat-profile" })
|
||||
.choices({ "text", "json", "console" })
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<std::set<std::string>>("profile");
|
||||
update_env(_env, "ROCPROFSYS_PROFILE", true);
|
||||
if(!_v.empty())
|
||||
{
|
||||
update_env(_env, "ROCPROFSYS_TEXT_OUTPUT", _v.count("text") != 0);
|
||||
update_env(_env, "ROCPROFSYS_JSON_OUTPUT", _v.count("json") != 0);
|
||||
update_env(_env, "ROCPROFSYS_COUT_OUTPUT", _v.count("console") != 0);
|
||||
}
|
||||
});
|
||||
|
||||
parser
|
||||
.add_argument({ "--profile-diff" },
|
||||
"Generate a diff output b/t the profile collected and an existing "
|
||||
"profile from another run Accepts 1-2 parameters corresponding to "
|
||||
"the input path and the input prefix")
|
||||
.min_count(1)
|
||||
.max_count(2)
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<std::vector<std::string>>("profile-diff");
|
||||
update_env(_env, "ROCPROFSYS_DIFF_OUTPUT", true);
|
||||
update_env(_env, "ROCPROFSYS_INPUT_PATH", _v.at(0));
|
||||
if(_v.size() > 1) update_env(_env, "ROCPROFSYS_INPUT_PREFIX", _v.at(1));
|
||||
});
|
||||
|
||||
parser.start_group(
|
||||
"HOST/DEVICE (PROCESS SAMPLING) OPTIONS",
|
||||
"Process sampling is background measurements for resources available to the "
|
||||
"entire process. These samples are not tied to specific lines/regions of code");
|
||||
parser
|
||||
.add_argument({ "--process-freq" },
|
||||
"Set the default host/device sampling frequency "
|
||||
"(number of interrupts per second)")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_PROCESS_SAMPLING_FREQ",
|
||||
p.get<double>("process-freq"));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--process-wait" }, "Set the default wait time (i.e. delay) "
|
||||
"before taking first host/device sample "
|
||||
"(in seconds of realtime)")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_PROCESS_SAMPLING_DELAY",
|
||||
p.get<double>("process-wait"));
|
||||
});
|
||||
parser
|
||||
.add_argument(
|
||||
{ "--process-duration" },
|
||||
"Set the duration of the host/device sampling (in seconds of realtime)")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_PROCESS_DURATION",
|
||||
p.get<double>("process-duration"));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--cpus" },
|
||||
"CPU IDs for frequency sampling. Supports integers and/or ranges")
|
||||
.dtype("int or range")
|
||||
.required({ "host" })
|
||||
.action([&](parser_t& p) {
|
||||
update_env(
|
||||
_env, "ROCPROFSYS_SAMPLING_CPUS",
|
||||
join(array_config{ "," }, p.get<std::vector<std::string>>("cpus")));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--gpus" },
|
||||
"GPU IDs for SMI queries. Supports integers and/or ranges")
|
||||
.dtype("int or range")
|
||||
.required({ "device" })
|
||||
.action([&](parser_t& p) {
|
||||
update_env(
|
||||
_env, "ROCPROFSYS_SAMPLING_GPUS",
|
||||
join(array_config{ "," }, p.get<std::vector<std::string>>("gpus")));
|
||||
});
|
||||
|
||||
parser.start_group("GENERAL SAMPLING OPTIONS",
|
||||
"General options for timer-based sampling per-thread");
|
||||
parser
|
||||
.add_argument({ "-f", "--freq" }, "Set the default sampling frequency "
|
||||
"(number of interrupts per second)")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_FREQ", p.get<double>("freq"));
|
||||
});
|
||||
parser
|
||||
.add_argument(
|
||||
{ "--sampling-wait" },
|
||||
"Set the default wait time (i.e. delay) before taking first sample "
|
||||
"(in seconds). This delay time is based on the clock of the sampler, i.e., a "
|
||||
"delay of 1 second for CPU-clock sampler may not equal 1 second of realtime")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_DELAY", p.get<double>("sampling-wait"));
|
||||
});
|
||||
parser
|
||||
.add_argument(
|
||||
{ "--sampling-duration" },
|
||||
"Set the duration of the sampling (in seconds of realtime). I.e., it is "
|
||||
"possible (currently) to set a CPU-clock time delay that exceeds the "
|
||||
"real-time duration... resulting in zero samples being taken")
|
||||
.count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_DURATION",
|
||||
p.get<double>("sampling-duration"));
|
||||
});
|
||||
parser
|
||||
.add_argument({ "-t", "--tids" },
|
||||
"Specify the default thread IDs for sampling, where 0 (zero) is "
|
||||
"the main thread and each thread created by the target application "
|
||||
"is assigned an atomically incrementing value.")
|
||||
.min_count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_TIDS",
|
||||
join(array_config{ ", " }, p.get<std::vector<int64_t>>("tids")));
|
||||
});
|
||||
|
||||
parser.start_group(
|
||||
"SAMPLING TIMER OPTIONS",
|
||||
"These options determine the heuristic for deciding when to take a sample");
|
||||
parser.add_argument({ "--cputime" }, _cputime_desc)
|
||||
.min_count(0)
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<std::deque<std::string>>("cputime");
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME", true);
|
||||
if(!_v.empty())
|
||||
{
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_FREQ", _v.front());
|
||||
_v.pop_front();
|
||||
}
|
||||
if(!_v.empty())
|
||||
{
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_DELAY", _v.front());
|
||||
_v.pop_front();
|
||||
}
|
||||
if(!_v.empty())
|
||||
{
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_TIDS",
|
||||
join(array_config{ "," }, _v));
|
||||
}
|
||||
});
|
||||
|
||||
parser.add_argument({ "--realtime" }, _realtime_desc)
|
||||
.min_count(0)
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<std::deque<std::string>>("realtime");
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME", true);
|
||||
if(!_v.empty())
|
||||
{
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_FREQ", _v.front());
|
||||
_v.pop_front();
|
||||
}
|
||||
if(!_v.empty())
|
||||
{
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_DELAY", _v.front());
|
||||
_v.pop_front();
|
||||
}
|
||||
if(!_v.empty())
|
||||
{
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_TIDS",
|
||||
join(array_config{ "," }, _v));
|
||||
}
|
||||
});
|
||||
|
||||
std::set<std::string> _backend_choices = { "all", "kokkosp", "mpip",
|
||||
"ompt", "rcclp", "amd-smi",
|
||||
"mutex-locks", "spin-locks", "rw-locks",
|
||||
"rocm" };
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_MPI) && !defined(ROCPROFSYS_USE_MPI_HEADERS)
|
||||
_backend_choices.erase("mpip");
|
||||
#endif
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_OMPT)
|
||||
_backend_choices.erase("ompt");
|
||||
#endif
|
||||
|
||||
#if !defined(ROCPROFSYS_USE_ROCM)
|
||||
_backend_choices.erase("rocm");
|
||||
_backend_choices.erase("amd-smi");
|
||||
_backend_choices.erase("rcclp");
|
||||
#endif
|
||||
|
||||
parser.start_group("BACKEND OPTIONS",
|
||||
"These options control region information captured "
|
||||
"w/o sampling or instrumentation");
|
||||
parser.add_argument({ "-I", "--include" }, "Include data from these backends")
|
||||
.choices(_backend_choices)
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<std::set<std::string>>("include");
|
||||
auto _update = [&](const auto& _opt, bool _cond) {
|
||||
if(_cond || _v.count("all") > 0) update_env(_env, _opt, true);
|
||||
};
|
||||
_update("ROCPROFSYS_USE_KOKKOSP", _v.count("kokkosp") > 0);
|
||||
_update("ROCPROFSYS_USE_MPIP", _v.count("mpip") > 0);
|
||||
_update("ROCPROFSYS_USE_OMPT", _v.count("ompt") > 0);
|
||||
_update("ROCPROFSYS_USE_ROCM", _v.count("rocm") > 0);
|
||||
_update("ROCPROFSYS_USE_RCCLP", _v.count("rcclp") > 0);
|
||||
_update("ROCPROFSYS_USE_AMD_SMI", _v.count("amd-smi") > 0);
|
||||
_update("ROCPROFSYS_TRACE_THREAD_LOCKS", _v.count("mutex-locks") > 0);
|
||||
_update("ROCPROFSYS_TRACE_THREAD_RW_LOCKS", _v.count("rw-locks") > 0);
|
||||
_update("ROCPROFSYS_TRACE_THREAD_SPIN_LOCKS", _v.count("spin-locks") > 0);
|
||||
|
||||
if(_v.count("all") > 0 || _v.count("ompt") > 0)
|
||||
update_env(_env, "OMP_TOOL_LIBRARIES", _dl_libpath, UPD_APPEND);
|
||||
|
||||
if(_v.count("all") > 0 || _v.count("kokkosp") > 0)
|
||||
update_env(_env, "KOKKOS_TOOLS_LIBS", _omni_libpath, UPD_APPEND);
|
||||
});
|
||||
|
||||
parser.add_argument({ "-E", "--exclude" }, "Exclude data from these backends")
|
||||
.choices(_backend_choices)
|
||||
.action([&](parser_t& p) {
|
||||
auto _v = p.get<std::set<std::string>>("exclude");
|
||||
auto _update = [&](const auto& _opt, bool _cond) {
|
||||
if(_cond || _v.count("all") > 0) update_env(_env, _opt, false);
|
||||
};
|
||||
_update("ROCPROFSYS_USE_KOKKOSP", _v.count("kokkosp") > 0);
|
||||
_update("ROCPROFSYS_USE_MPIP", _v.count("mpip") > 0);
|
||||
_update("ROCPROFSYS_USE_OMPT", _v.count("ompt") > 0);
|
||||
_update("ROCPROFSYS_USE_ROCM", _v.count("rocm") > 0);
|
||||
_update("ROCPROFSYS_USE_RCCLP", _v.count("rcclp") > 0);
|
||||
_update("ROCPROFSYS_USE_AMD_SMI", _v.count("amd-smi") > 0);
|
||||
_update("ROCPROFSYS_TRACE_THREAD_LOCKS", _v.count("mutex-locks") > 0);
|
||||
_update("ROCPROFSYS_TRACE_THREAD_RW_LOCKS", _v.count("rw-locks") > 0);
|
||||
_update("ROCPROFSYS_TRACE_THREAD_SPIN_LOCKS", _v.count("spin-locks") > 0);
|
||||
|
||||
// if(_v.count("all") > 0 || _v.count("rocprofiler") > 0)
|
||||
// {
|
||||
// remove_env(_env, "ROCP_TOOL_LIB");
|
||||
// remove_env(_env, "ROCP_HSA_INTERCEPT");
|
||||
// }
|
||||
|
||||
if(_v.count("all") > 0 || _v.count("ompt") > 0)
|
||||
remove_env(_env, "OMP_TOOL_LIBRARIES");
|
||||
|
||||
if(_v.count("all") > 0 || _v.count("kokkosp") > 0)
|
||||
remove_env(_env, "KOKKOS_TOOLS_LIBS");
|
||||
});
|
||||
|
||||
parser.start_group("HARDWARE COUNTER OPTIONS", "See also: rocprof-sys-avail -H");
|
||||
parser
|
||||
.add_argument({ "-C", "--cpu-events" },
|
||||
"Set the CPU hardware counter events to record (ref: "
|
||||
"`rocprof-sys-avail -H -c CPU`)")
|
||||
.action([&](parser_t& p) {
|
||||
auto _events =
|
||||
join(array_config{ "," }, p.get<std::vector<std::string>>("cpu-events"));
|
||||
update_env(_env, "ROCPROFSYS_PAPI_EVENTS", _events);
|
||||
});
|
||||
|
||||
parser.start_group("MISCELLANEOUS OPTIONS", "");
|
||||
parser
|
||||
.add_argument({ "-i", "--inlines" },
|
||||
"Include inline info in output when available")
|
||||
.max_count(1)
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_INCLUDE_INLINES",
|
||||
p.get<bool>("inlines"));
|
||||
});
|
||||
|
||||
parser.add_argument({ "--hsa-interrupt" }, _hsa_interrupt_desc)
|
||||
.count(1)
|
||||
.dtype("int")
|
||||
.choices({ 0, 1 })
|
||||
.action([&](parser_t& p) {
|
||||
update_env(_env, "HSA_ENABLE_INTERRUPT", p.get<int>("hsa-interrupt"));
|
||||
});
|
||||
|
||||
parser.end_group();
|
||||
|
||||
auto _inpv = std::vector<char*>{};
|
||||
auto _outv = std::vector<char*>{};
|
||||
bool _hash = false;
|
||||
for(int i = 0; i < argc; ++i)
|
||||
{
|
||||
if(_hash)
|
||||
{
|
||||
_outv.emplace_back(argv[i]);
|
||||
}
|
||||
else if(std::string_view{ argv[i] } == "--")
|
||||
{
|
||||
_hash = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inpv.emplace_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
auto _cerr = parser.parse_args(_inpv.size(), _inpv.data());
|
||||
if(help_check(parser, argc, argv))
|
||||
help_action(parser);
|
||||
else if(_cerr)
|
||||
throw std::runtime_error(_cerr.what());
|
||||
|
||||
if(parser.exists("realtime") && !parser.exists("cputime"))
|
||||
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME", false);
|
||||
if(parser.exists("profile") && parser.exists("flat-profile"))
|
||||
throw std::runtime_error(
|
||||
"Error! '--profile' argument conflicts with '--flat-profile' argument");
|
||||
|
||||
free(_dl_libpath);
|
||||
free(_omni_libpath);
|
||||
|
||||
return _outv;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022-2025 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 "rocprof-sys-sample.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
auto _env = get_initial_environment();
|
||||
|
||||
bool _has_double_hyphen = false;
|
||||
for(int i = 1; i < argc; ++i)
|
||||
{
|
||||
auto _arg = std::string_view{ argv[i] };
|
||||
if(_arg == "--" || _arg == "-?" || _arg == "-h" || _arg == "--help" ||
|
||||
_arg == "--version")
|
||||
_has_double_hyphen = true;
|
||||
}
|
||||
|
||||
std::vector<char*> _argv = {};
|
||||
if(_has_double_hyphen)
|
||||
{
|
||||
_argv = parse_args(argc, argv, _env);
|
||||
}
|
||||
else
|
||||
{
|
||||
_argv.reserve(argc);
|
||||
for(int i = 1; i < argc; ++i)
|
||||
_argv.emplace_back(argv[i]);
|
||||
}
|
||||
|
||||
print_updated_environment(_env);
|
||||
|
||||
if(!_argv.empty())
|
||||
{
|
||||
print_command(_argv);
|
||||
_argv.emplace_back(nullptr);
|
||||
_env.emplace_back(nullptr);
|
||||
|
||||
return execvpe(_argv.front(), _argv.data(), _env.data());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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 <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
enum update_mode : int
|
||||
{
|
||||
UPD_REPLACE = 0x1,
|
||||
UPD_PREPEND = 0x2,
|
||||
UPD_APPEND = 0x3,
|
||||
UPD_WEAK = 0x4,
|
||||
};
|
||||
|
||||
std::string
|
||||
get_realpath(const std::string& _fpath);
|
||||
|
||||
void
|
||||
print_command(const std::vector<char*>& _argv);
|
||||
|
||||
void
|
||||
print_updated_environment(std::vector<char*> _env);
|
||||
|
||||
std::vector<char*>
|
||||
get_initial_environment();
|
||||
|
||||
std::string
|
||||
get_internal_libpath(const std::string& _lib);
|
||||
|
||||
std::string
|
||||
get_internal_script_path(void);
|
||||
|
||||
template <typename Tp>
|
||||
void
|
||||
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
|
||||
update_mode&& _mode = UPD_REPLACE, std::string_view _join_delim = ":");
|
||||
|
||||
void
|
||||
remove_env(std::vector<char*>& _environ, std::string_view _env_var);
|
||||
|
||||
std::vector<char*>
|
||||
parse_args(int argc, char** argv, std::vector<char*>& envp);
|
||||
@@ -0,0 +1,523 @@
|
||||
set(ROCPROFSYS_ABORT_FAIL_REGEX
|
||||
"### ERROR ###|unknown-hash=|address of faulting memory reference|exiting with non-zero exit code|terminate called after throwing an instance|calling abort.. in |Exit code: [1-9]"
|
||||
CACHE INTERNAL
|
||||
"Regex to catch abnormal exits when a PASS_REGULAR_EXPRESSION is set"
|
||||
FORCE
|
||||
)
|
||||
|
||||
# adds a ctest for executable
|
||||
function(ROCPROFILER_SYSTEMS_ADD_BIN_TEST)
|
||||
cmake_parse_arguments(
|
||||
TEST
|
||||
"" # options
|
||||
"NAME;TARGET;TIMEOUT;WORKING_DIRECTORY" # single value args
|
||||
"ARGS;ENVIRONMENT;LABELS;PROPERTIES;PASS_REGEX;FAIL_REGEX;SKIP_REGEX;DEPENDS;COMMAND" # multiple
|
||||
# value args
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
if(NOT TEST_WORKING_DIRECTORY)
|
||||
set(TEST_WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
if(NOT TEST_ENVIRONMENT)
|
||||
set(TEST_ENVIRONMENT
|
||||
"ROCPROFSYS_TRACE=ON"
|
||||
"ROCPROFSYS_PROFILE=ON"
|
||||
"ROCPROFSYS_USE_SAMPLING=ON"
|
||||
"ROCPROFSYS_TIME_OUTPUT=OFF"
|
||||
"LD_LIBRARY_PATH=${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}:$ENV{LD_LIBRARY_PATH}"
|
||||
)
|
||||
endif()
|
||||
|
||||
# common
|
||||
list(
|
||||
APPEND
|
||||
TEST_ENVIRONMENT
|
||||
"ROCPROFSYS_CI=ON"
|
||||
"ROCPROFSYS_CI_TIMEOUT=${TEST_TIMEOUT}"
|
||||
"ROCPROFSYS_CONFIG_FILE="
|
||||
"ROCPROFSYS_OUTPUT_PATH=${PROJECT_BINARY_DIR}/rocprof-sys-tests-output"
|
||||
"TWD=${TEST_WORKING_DIRECTORY}"
|
||||
)
|
||||
# copy for inverse
|
||||
set(TEST_ENVIRONMENT_INV "${TEST_ENVIRONMENT}")
|
||||
|
||||
# different for regular test and inverse test
|
||||
list(APPEND TEST_ENVIRONMENT "ROCPROFSYS_OUTPUT_PREFIX=${TEST_NAME}/")
|
||||
list(APPEND TEST_ENVIRONMENT_INV "ROCPROFSYS_OUTPUT_PREFIX=${TEST_NAME}-inverse/")
|
||||
|
||||
if(
|
||||
NOT "${TEST_PASS_REGEX}" STREQUAL ""
|
||||
AND NOT "${TEST_FAIL_REGEX}" STREQUAL ""
|
||||
AND NOT "${TEST_FAIL_REGEX}" MATCHES "\\|ROCPROFSYS_ABORT_FAIL_REGEX"
|
||||
)
|
||||
rocprofiler_systems_message(
|
||||
FATAL_ERROR
|
||||
"${TEST_NAME} has set pass and fail regexes but fail regex does not include '|ROCPROFSYS_ABORT_FAIL_REGEX'"
|
||||
)
|
||||
endif()
|
||||
|
||||
if("${TEST_FAIL_REGEX}" STREQUAL "")
|
||||
set(TEST_FAIL_REGEX "(${ROCPROFSYS_ABORT_FAIL_REGEX})")
|
||||
else()
|
||||
string(
|
||||
REPLACE
|
||||
"|ROCPROFSYS_ABORT_FAIL_REGEX"
|
||||
"|${ROCPROFSYS_ABORT_FAIL_REGEX}"
|
||||
TEST_FAIL_REGEX
|
||||
"${TEST_FAIL_REGEX}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(TEST_COMMAND)
|
||||
add_test(
|
||||
NAME ${TEST_NAME}
|
||||
COMMAND ${TEST_COMMAND} ${TEST_ARGS}
|
||||
WORKING_DIRECTORY ${TEST_WORKING_DIRECTORY}
|
||||
)
|
||||
|
||||
set_tests_properties(
|
||||
${TEST_NAME}
|
||||
PROPERTIES
|
||||
ENVIRONMENT "${TEST_ENVIRONMENT}"
|
||||
TIMEOUT ${TEST_TIMEOUT}
|
||||
DEPENDS "${TEST_DEPENDS}"
|
||||
LABELS "rocprofiler-systems-bin;${TEST_LABELS}"
|
||||
PASS_REGULAR_EXPRESSION "${TEST_PASS_REGEX}"
|
||||
FAIL_REGULAR_EXPRESSION "${TEST_FAIL_REGEX}"
|
||||
SKIP_REGULAR_EXPRESSION "${TEST_SKIP_REGEX}"
|
||||
${TEST_PROPERTIES}
|
||||
)
|
||||
elseif(TARGET ${TEST_TARGET})
|
||||
add_test(
|
||||
NAME ${TEST_NAME}
|
||||
COMMAND $<TARGET_FILE:${TEST_TARGET}> ${TEST_ARGS}
|
||||
WORKING_DIRECTORY ${TEST_WORKING_DIRECTORY}
|
||||
)
|
||||
|
||||
set_tests_properties(
|
||||
${TEST_NAME}
|
||||
PROPERTIES
|
||||
ENVIRONMENT "${TEST_ENVIRONMENT}"
|
||||
TIMEOUT ${TEST_TIMEOUT}
|
||||
DEPENDS "${TEST_DEPENDS}"
|
||||
LABELS "rocprofiler-systems-bin;${TEST_LABELS}"
|
||||
PASS_REGULAR_EXPRESSION "${TEST_PASS_REGEX}"
|
||||
FAIL_REGULAR_EXPRESSION "${TEST_FAIL_REGEX}"
|
||||
SKIP_REGULAR_EXPRESSION "${TEST_SKIP_REGEX}"
|
||||
${TEST_PROPERTIES}
|
||||
)
|
||||
elseif(ROCPROFSYS_BUILD_TESTING)
|
||||
message(FATAL_ERROR "Error! ${TEST_TARGET} does not exist")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-instrument-help
|
||||
TARGET rocprofiler-systems-instrument
|
||||
ARGS --help
|
||||
LABELS rocprofiler-systems-instrument
|
||||
TIMEOUT 45
|
||||
PASS_REGEX
|
||||
".*\\\[rocprof-sys-instrument\\\] Usage:.*\\\[DEBUG OPTIONS\\\].*\\\[MODE OPTIONS\\\].*\\\[LIBRARY OPTIONS\\\].*\\\[SYMBOL SELECTION OPTIONS\\\].*\\\[RUNTIME OPTIONS\\\].*\\\[GRANULARITY OPTIONS\\\].*\\\[DYNINST OPTIONS\\\].*"
|
||||
)
|
||||
|
||||
# on RedHat, /usr/bin/ls is a script for `coreutils --coreutils-prog=ls`
|
||||
if(EXISTS /usr/bin/coreutils)
|
||||
set(LS_NAME "coreutils")
|
||||
set(LS_ARGS "--coreutils-prog=ls")
|
||||
else()
|
||||
set(LS_NAME ls)
|
||||
set(LS_ARGS)
|
||||
endif()
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-instrument-simulate-ls
|
||||
TARGET rocprofiler-systems-instrument
|
||||
ARGS --simulate
|
||||
--print-format
|
||||
json
|
||||
txt
|
||||
xml
|
||||
-v
|
||||
2
|
||||
--all-functions
|
||||
--
|
||||
${LS_NAME}
|
||||
${LS_ARGS}
|
||||
LABELS "simulate"
|
||||
TIMEOUT 240
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-instrument-simulate-ls-check
|
||||
DEPENDS rocprofiler-systems-instrument-simulate-ls
|
||||
COMMAND
|
||||
ls
|
||||
rocprof-sys-tests-output/rocprofiler-systems-instrument-simulate-ls/instrumentation
|
||||
TIMEOUT 60
|
||||
PASS_REGEX
|
||||
".*available.json.*available.txt.*available.xml.*excluded.json.*excluded.txt.*excluded.xml.*instrumented.json.*instrumented.txt.*instrumented.xml.*overlapping.json.*overlapping.txt.*overlapping.xml.*"
|
||||
FAIL_REGEX "No such file or directory|not found|ROCPROFSYS_ABORT_FAIL_REGEX"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-instrument-simulate-lib
|
||||
TARGET rocprofiler-systems-instrument
|
||||
ARGS --print-available functions -v 2 --
|
||||
$<TARGET_FILE:rocprofiler-systems-user-library>
|
||||
LABELS "simulate"
|
||||
TIMEOUT 120
|
||||
PASS_REGEX
|
||||
"\\\[rocprof-sys\\\]\\\[exe\\\] Runtime instrumentation is not possible!(.*)\n(.*)\\\[rocprof-sys\\\]\\\[exe\\\] Switching to binary rewrite mode and assuming '--simulate --all-functions'"
|
||||
)
|
||||
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/rocprof-sys-tests-output/tmp)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-instrument-simulate-lib-basename
|
||||
TARGET rocprofiler-systems-instrument
|
||||
ARGS --print-available
|
||||
functions
|
||||
-v
|
||||
2
|
||||
-o
|
||||
${PROJECT_BINARY_DIR}/rocprof-sys-tests-output/rocprof-sys-instrument-simulate-lib-basename/${CMAKE_SHARED_LIBRARY_PREFIX}$<TARGET_FILE_BASE_NAME:rocprofiler-systems-user-library>${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
--
|
||||
${CMAKE_SHARED_LIBRARY_PREFIX}$<TARGET_FILE_BASE_NAME:rocprofiler-systems-user-library>${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
LABELS "simulate"
|
||||
TIMEOUT 120
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/rocprof-sys-tests-output/tmp
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-instrument-write-log
|
||||
TARGET rocprofiler-systems-instrument
|
||||
ARGS --print-instrumented
|
||||
functions
|
||||
-v
|
||||
1
|
||||
--log-file
|
||||
user.log
|
||||
--
|
||||
${LS_NAME}
|
||||
${LS_ARGS}
|
||||
LABELS "log"
|
||||
TIMEOUT 120
|
||||
PASS_REGEX "Opening .*/instrumentation/user.log"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-instrument-write-log-check
|
||||
DEPENDS rocprofiler-systems-instrument-write-log
|
||||
COMMAND
|
||||
ls
|
||||
rocprof-sys-tests-output/rocprofiler-systems-instrument-write-log/instrumentation/user.log
|
||||
LABELS "log"
|
||||
TIMEOUT 60
|
||||
PASS_REGEX "user.log"
|
||||
FAIL_REGEX "No such file or directory|not found|ROCPROFSYS_ABORT_FAIL_REGEX"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-help
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --help
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
PASS_REGEX
|
||||
".*\\\[rocprof-sys-avail\\\] Usage:.*\\\[DEBUG OPTIONS\\\].*\\\[INFO OPTIONS\\\].*\\\[FILTER OPTIONS\\\].*\\\[COLUMN OPTIONS\\\].*\\\[DISPLAY OPTIONS\\\].*\\\[OUTPUT OPTIONS\\\].*"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-all
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --all
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-all-expand-keys
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --all --expand-keys
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
FAIL_REGEX "%[a-zA-Z_]%|ROCPROFSYS_ABORT_FAIL_REGEX"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-all-only-available-alphabetical
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --all --available --alphabetical --debug --output
|
||||
${CMAKE_CURRENT_BINARY_DIR}/rocprof-sys-avail-all-only-available-alphabetical.log
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
PROPERTIES
|
||||
ATTACHED_FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/rocprof-sys-avail-all-only-available-alphabetical.log
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-all-csv
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --all --csv --csv-separator "#"
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
PASS_REGEX
|
||||
"COMPONENT#AVAILABLE#VALUE_TYPE#STRING_IDS#FILENAME#DESCRIPTION#CATEGORY#.*ENVIRONMENT VARIABLE#VALUE#DATA TYPE#DESCRIPTION#CATEGORIES#.*HARDWARE COUNTER#DEVICE#AVAILABLE#DESCRIPTION#"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-filter-wall-clock-available
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS -r wall_clock -C --available
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
PASS_REGEX
|
||||
"\\\|[-]+\\\|\n\\\|[ ]+COMPONENT[ ]+\\\|\n\\\|[-]+\\\|\n\\\| (wall_clock)[ ]+\\\|\n\\\|[-]+\\\|"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-category-filter-rocprofiler-systems
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --categories settings::rocprofsys --brief
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
PASS_REGEX "ROCPROFSYS_(SETTINGS_DESC|OUTPUT_FILE|OUTPUT_PREFIX)"
|
||||
FAIL_REGEX
|
||||
"ROCPROFSYS_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)|ROCPROFSYS_ABORT_FAIL_REGEX"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-category-filter-timemory
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --categories settings::timemory --brief --advanced
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
PASS_REGEX
|
||||
"ROCPROFSYS_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)"
|
||||
FAIL_REGEX "ROCPROFSYS_(SETTINGS_DESC|OUTPUT_FILE)|ROCPROFSYS_ABORT_FAIL_REGEX"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-regex-negation
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS -R
|
||||
rocprofsys
|
||||
~timemory
|
||||
-r
|
||||
_P
|
||||
~PERFETTO
|
||||
~PROCESS_SAMPLING
|
||||
~KOKKOSP
|
||||
--csv
|
||||
--brief
|
||||
--advanced
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
TIMEOUT 45
|
||||
PASS_REGEX
|
||||
"ENVIRONMENT VARIABLE,[ \n]+ROCPROFSYS_THREAD_POOL_SIZE,[ \n]+ROCPROFSYS_USE_PID,[ \n]+"
|
||||
FAIL_REGEX "ROCPROFSYS_TRACE|ROCPROFSYS_ABORT_FAIL_REGEX"
|
||||
)
|
||||
|
||||
string(
|
||||
REPLACE
|
||||
"+"
|
||||
"\\\+"
|
||||
_AVAIL_CFG_PATH
|
||||
"${PROJECT_BINARY_DIR}/rocprof-sys-tests-output/rocprof-sys-avail/rocprof-sys-"
|
||||
)
|
||||
# use of TWD == Test Working Directory (added by function)
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-write-config
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS -G
|
||||
%env{TWD}%/rocprof-sys-tests-output/rocprof-sys-avail/rocprof-sys-test.cfg
|
||||
-F
|
||||
txt
|
||||
json
|
||||
xml
|
||||
--force
|
||||
--all
|
||||
-c
|
||||
rocprofsys
|
||||
TIMEOUT 45
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
PASS_REGEX
|
||||
"Outputting JSON configuration file '${_AVAIL_CFG_PATH}test\\\.json'(.*)Outputting XML configuration file '${_AVAIL_CFG_PATH}test\\\.xml'(.*)Outputting text configuration file '${_AVAIL_CFG_PATH}test\\\.cfg'(.*)"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-write-config-tweak
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS -G %env{TWD}%/rocprof-sys-tests-output/rocprof-sys-avail/rocprof-sys-tweak.cfg -F
|
||||
txt json xml --force
|
||||
TIMEOUT 45
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
ENVIRONMENT "ROCPROFSYS_TRACE=OFF;ROCPROFSYS_PROFILE=ON"
|
||||
PASS_REGEX
|
||||
"Outputting JSON configuration file '${_AVAIL_CFG_PATH}tweak\\\.json'(.*)Outputting XML configuration file '${_AVAIL_CFG_PATH}tweak\\\.xml'(.*)Outputting text configuration file '${_AVAIL_CFG_PATH}tweak\\\.cfg'(.*)"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-list-keys
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --list-keys --expand-keys
|
||||
TIMEOUT 45
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
PASS_REGEX "Output Keys:\n(.*)%argv%(.*)%argv_hash%"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-list-keys-markdown
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --list-keys --expand-keys --markdown
|
||||
TIMEOUT 45
|
||||
LABELS "rocprofiler-systems-avail;markdown"
|
||||
PASS_REGEX "(.*)`%argv%`(.*)`%argv_hash%`"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-list-categories
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS --list-categories
|
||||
TIMEOUT 45
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
PASS_REGEX " component::(.*) hw_counters::(.*) settings::"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-avail-core-categories
|
||||
TARGET rocprofiler-systems-avail
|
||||
ARGS -c core
|
||||
TIMEOUT 45
|
||||
LABELS "rocprofiler-systems-avail"
|
||||
PASS_REGEX
|
||||
"ROCPROFSYS_CONFIG_FILE(.*)ROCPROFSYS_ENABLED(.*)ROCPROFSYS_SUPPRESS_CONFIG(.*)ROCPROFSYS_SUPPRESS_PARSING(.*)ROCPROFSYS_VERBOSE"
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-run-help
|
||||
TARGET rocprofiler-systems-run
|
||||
ARGS --help
|
||||
TIMEOUT 45
|
||||
LABELS "rocprofiler-systems-run"
|
||||
)
|
||||
|
||||
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/rocprof-sys-tests-config")
|
||||
file(
|
||||
WRITE
|
||||
"${PROJECT_BINARY_DIR}/rocprof-sys-tests-config/empty.cfg"
|
||||
"
|
||||
#
|
||||
# empty config file
|
||||
#
|
||||
"
|
||||
)
|
||||
|
||||
add_executable(sleeper ${CMAKE_CURRENT_SOURCE_DIR}/sleeper.cpp)
|
||||
set_target_properties(
|
||||
sleeper
|
||||
PROPERTIES
|
||||
BUILD_TYPE RelWithDebInfo
|
||||
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin/testing
|
||||
)
|
||||
|
||||
rocprofiler_systems_add_bin_test(
|
||||
NAME rocprofiler-systems-run-args
|
||||
TARGET rocprofiler-systems-run
|
||||
ARGS --monochrome
|
||||
--debug=false
|
||||
-v
|
||||
1
|
||||
-c
|
||||
%env{TWD}%/rocprof-sys-tests-config/empty.cfg
|
||||
-o
|
||||
rocprof-sys-tests-output
|
||||
rocprofiler-systems-run-args-output/
|
||||
-TPHD
|
||||
-S
|
||||
cputime
|
||||
realtime
|
||||
--trace-wait=1.0e-12
|
||||
--trace-duration=5.0
|
||||
--wait=1.0
|
||||
--duration=3.0
|
||||
--trace-file=perfetto-run-args-trace.proto
|
||||
--trace-buffer-size=100
|
||||
--trace-fill-policy=ring_buffer
|
||||
--profile-format
|
||||
console
|
||||
json
|
||||
text
|
||||
--process-freq
|
||||
1000
|
||||
--process-wait
|
||||
0.0
|
||||
--process-duration
|
||||
10
|
||||
--cpus
|
||||
0-4
|
||||
--gpus
|
||||
0
|
||||
-f
|
||||
1000
|
||||
--sampling-wait
|
||||
1.0
|
||||
--sampling-duration
|
||||
10
|
||||
-t
|
||||
0-3
|
||||
--sample-cputime
|
||||
1000
|
||||
1.0
|
||||
0-3
|
||||
--sample-realtime
|
||||
10
|
||||
0.5
|
||||
0-3
|
||||
-I
|
||||
all
|
||||
-E
|
||||
mutex-locks
|
||||
rw-locks
|
||||
spin-locks
|
||||
-C
|
||||
perf::INSTRUCTIONS
|
||||
--inlines
|
||||
--hsa-interrupt
|
||||
0
|
||||
--use-causal=false
|
||||
--use-kokkosp
|
||||
--num-threads-hint=4
|
||||
--sampling-allocator-size=32
|
||||
--ci
|
||||
--dl-verbose=3
|
||||
--perfetto-annotations=off
|
||||
--kokkosp-kernel-logger
|
||||
--kokkosp-name-length-max=1024
|
||||
--kokkosp-prefix="[kokkos]"
|
||||
--tmpdir
|
||||
${CMAKE_BINARY_DIR}/rocprof-sys-tests-config/tmpdir
|
||||
--perfetto-backend
|
||||
inprocess
|
||||
--use-pid
|
||||
false
|
||||
--time-output
|
||||
off
|
||||
--thread-pool-size
|
||||
0
|
||||
--timemory-components
|
||||
wall_clock
|
||||
cpu_clock
|
||||
peak_rss
|
||||
page_rss
|
||||
--fork
|
||||
--
|
||||
$<TARGET_FILE:sleeper>
|
||||
5
|
||||
TIMEOUT 45
|
||||
LABELS "rocprofiler-systems-run"
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <ratio>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
double _val = 0.0;
|
||||
if(argc > 0)
|
||||
{
|
||||
auto _ss = std::stringstream{};
|
||||
_ss << argv[1];
|
||||
_ss >> _val;
|
||||
}
|
||||
|
||||
intmax_t _nsec = _val * std::nano::den;
|
||||
auto _end = clock_type::now() + std::chrono::nanoseconds{ _nsec };
|
||||
while(clock_type::now() < _end)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::nanoseconds{ _nsec / 10 });
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user