Initial Implementation: include (#10)

* Initial Implementation: include

* Initial Implementation: lib details (#11)

* Initial Implementation: lib details

* Initial Implementation: lib (#12)

* Initial Implementation: lib

* Initial Implementation: source (#13)

* Initial Implementation: source

* Initial Implementation: samples (#14)

* Initial Implementation: samples

* Initial Implementation: tests (#15)

* Initial Implementation: tests

* Initial Implementation: scripts (#16)

* Initial Implementation: scripts

* Initial Implementation: cmake (#17)

* Initial Implementation: cmake

* Initial Implementation: top-level files (#18)

* Initial Implementation: top-level files

- clang-format
- clang-tidy
- cmake-format
- ignore build and cache directories
- main CMakeLists.txt
- pyproject.toml (python formatting)
- VERSION file

* Initial Implementation: workflow (#19)

* Fix unused variable

- rocprofiler_register_warn_level

[ROCm/rocprofiler-register commit: fa4295db6b]
This commit is contained in:
Jonathan R. Madsen
2023-08-17 14:59:24 -05:00
committed by GitHub
parent 8ef9cf2642
commit 7e6e33cfce
68 changed files with 7367 additions and 0 deletions
@@ -0,0 +1,4 @@
#
#
#
add_subdirectory(rocprofiler-register)
@@ -0,0 +1,36 @@
#
# builds the rocprofiler-register library
#
add_library(rocprofiler-register SHARED)
add_library(rocprofiler-register::rocprofiler-register ALIAS rocprofiler-register)
add_subdirectory(details)
target_sources(rocprofiler-register PRIVATE rocprofiler_register.cpp)
if(ROCPROFILER_REGISTER_BUILD_TESTS)
# make sure header is C-compatible
target_sources(rocprofiler-register PRIVATE rocprofiler_register.c)
endif()
target_include_directories(
rocprofiler-register PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/source
${PROJECT_BINARY_DIR}/source)
target_link_libraries(
rocprofiler-register
PUBLIC rocprofiler-register::headers
PRIVATE rocprofiler-register::build-flags rocprofiler-register::memcheck
rocprofiler-register::stdcxxfs rocprofiler-register::dl)
set_target_properties(
rocprofiler-register
PROPERTIES OUTPUT_NAME rocprofiler-register
SOVERSION ${PROJECT_VERSION_MAJOR}
VERSION ${PROJECT_VERSION})
install(
TARGETS rocprofiler-register
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT core
EXPORT ${PROJECT_NAME}-library-targets)
@@ -0,0 +1,10 @@
#
# builds the rocprofiler-register library
#
set(rocprofiler_register_details_sources dl.cpp utility.cpp)
set(rocprofiler_register_details_headers environment.hpp join.hpp dl.hpp log.hpp
utility.hpp)
target_sources(rocprofiler-register PRIVATE ${rocprofiler_register_details_sources}
${rocprofiler_register_details_headers})
@@ -0,0 +1,125 @@
// 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.
#define GNU_SOURCE 1
#include "dl.hpp"
#include "join.hpp"
#include "utility.hpp"
#include <filesystem>
#include <fstream>
#include <optional>
#include <string>
#include <string_view>
#include <dlfcn.h>
#include <elf.h>
#include <link.h>
#include <sys/types.h>
#include <unistd.h>
namespace fs = ::std::filesystem;
namespace rocprofiler_register
{
namespace binary
{
namespace
{
const open_modes_vec_t default_link_open_modes = { (RTLD_LAZY | RTLD_NOLOAD),
(RTLD_LAZY | RTLD_LOCAL) };
} // namespace
std::vector<segment_address_ranges>
get_segment_addresses(pid_t _pid)
{
auto _data = std::vector<segment_address_ranges>{};
auto _fname = common::join('/', "/proc", _pid, "maps");
auto ifs = std::ifstream{ _fname };
if(!ifs)
{
fprintf(stderr, "Failure opening %s\n", _fname.c_str());
}
else
{
auto _get_entry = [&_data](std::string_view _name) -> segment_address_ranges& {
for(auto& itr : _data)
{
if(itr.filepath == _name) return itr;
}
return _data.emplace_back(
segment_address_ranges{ .filepath = std::string{ _name } });
};
while(ifs)
{
std::string _line = {};
if(std::getline(ifs, _line) && !_line.empty())
{
auto _delim = utility::delimit(_line, " \t\n\r");
if(_delim.size() > 5 && fs::exists(fs::path{ _delim.back() }))
{
auto& _entry = _get_entry(_delim.back());
auto _addr = utility::delimit(_delim.front(), "-");
auto load_address = std::stoull(_addr.front(), nullptr, 16);
auto last_address = std::stoull(_addr.back(), nullptr, 16);
_entry.ranges.emplace_back(
address_range{ load_address, last_address });
}
}
}
}
return _data;
}
std::optional<std::string>
get_linked_path(std::string_view _name, open_modes_vec_t&& _open_modes)
{
if(_name.empty()) return fs::current_path().string();
if(_open_modes.empty()) _open_modes = default_link_open_modes;
void* _handle = nullptr;
bool _noload = false;
for(auto _mode : _open_modes)
{
_handle = dlopen(_name.data(), _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 fs::absolute(fs::path{ _link_map->l_name }).string();
}
if(_noload == false) dlclose(_handle);
}
return std::nullopt;
}
} // namespace binary
} // namespace rocprofiler_register
@@ -0,0 +1,60 @@
// MIT License
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include <dlfcn.h>
#include <sys/types.h>
#include <unistd.h>
namespace rocprofiler_register
{
namespace binary
{
using open_modes_vec_t = std::vector<int>;
struct address_range
{
uintptr_t start = 0;
uintptr_t last = 0;
};
struct segment_address_ranges
{
std::string filepath = {};
std::vector<address_range> ranges = {};
};
std::vector<segment_address_ranges>
get_segment_addresses(pid_t _pid = getpid());
// helper function for translating generic lib name to resolved path
std::optional<std::string>
get_linked_path(std::string_view, open_modes_vec_t&& = {});
} // namespace binary
} // namespace rocprofiler_register
@@ -0,0 +1,191 @@
// Copyright (c) 2023 Advanced Micro Devices, Inc.
//
// 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 <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#if !defined(ROCPROFILER_REGISTER_ENVIRON_LOG_NAME)
# if defined(ROCPROFILER_REGISTER_COMMON_LIBRARY_NAME)
# define ROCPROFILER_REGISTER_ENVIRON_LOG_NAME \
"[" ROCPROFILER_REGISTER_COMMON_LIBRARY_NAME "]"
# else
# define ROCPROFILER_REGISTER_ENVIRON_LOG_NAME "[environ]"
# endif
#endif
#if !defined(ROCPROFILER_REGISTER_ENVIRON_LOG_START)
# if defined(ROCPROFILER_REGISTER_COMMON_LIBRARY_LOG_START)
# define ROCPROFILER_REGISTER_ENVIRON_LOG_START \
ROCPROFILER_REGISTER_COMMON_LIBRARY_LOG_START
# elif defined(ROCPROFILER_REGISTER_LOG_COLORS_AVAILABLE)
# define ROCPROFILER_REGISTER_ENVIRON_LOG_START \
fprintf(stderr, "%s", ::rocprofiler_register::log::color::dmesg());
# else
# define ROCPROFILER_REGISTER_ENVIRON_LOG_START
# endif
#endif
#if !defined(ROCPROFILER_REGISTER_ENVIRON_LOG_END)
# if defined(ROCPROFILER_REGISTER_COMMON_LIBRARY_LOG_END)
# define ROCPROFILER_REGISTER_ENVIRON_LOG_END \
ROCPROFILER_REGISTER_COMMON_LIBRARY_LOG_END
# elif defined(ROCPROFILER_REGISTER_LOG_COLORS_AVAILABLE)
# define ROCPROFILER_REGISTER_ENVIRON_LOG_END \
fprintf(stderr, "%s", ::rocprofiler_register::log::color::end());
# else
# define ROCPROFILER_REGISTER_ENVIRON_LOG_END
# endif
#endif
#define ROCPROFILER_REGISTER_ENVIRON_LOG(CONDITION, ...) \
if(CONDITION) \
{ \
fflush(stderr); \
ROCPROFILER_REGISTER_ENVIRON_LOG_START \
fprintf(stderr, \
"[rocprofiler-register]" ROCPROFILER_REGISTER_ENVIRON_LOG_NAME "[%i] ", \
getpid()); \
fprintf(stderr, __VA_ARGS__); \
ROCPROFILER_REGISTER_ENVIRON_LOG_END \
fflush(stderr); \
}
namespace rocprofiler_register
{
namespace common
{
namespace
{
inline std::string
get_env_impl(std::string_view env_id, std::string_view _default)
{
if(env_id.empty()) return std::string{ _default };
char* env_var = ::std::getenv(env_id.data());
if(env_var) return std::string{ env_var };
return std::string{ _default };
}
inline std::string
get_env_impl(std::string_view env_id, const char* _default)
{
return get_env_impl(env_id, std::string_view{ _default });
}
inline int
get_env_impl(std::string_view env_id, int _default)
{
if(env_id.empty()) return _default;
char* env_var = ::std::getenv(env_id.data());
if(env_var)
{
try
{
return std::stoi(env_var);
} catch(std::exception& _e)
{
fprintf(stderr,
"[rocprofiler_register][get_env] Exception thrown converting "
"getenv(\"%s\") = "
"%s to integer :: %s. Using default value of %i\n",
env_id.data(),
env_var,
_e.what(),
_default);
}
return _default;
}
return _default;
}
inline bool
get_env_impl(std::string_view env_id, bool _default)
{
if(env_id.empty()) return _default;
char* env_var = ::std::getenv(env_id.data());
if(env_var)
{
if(std::string_view{ env_var }.empty())
{
throw std::runtime_error(std::string{ "No boolean value provided for " } +
std::string{ env_id });
}
if(std::string_view{ env_var }.find_first_not_of("0123456789") ==
std::string_view::npos)
{
return static_cast<bool>(std::stoi(env_var));
}
for(size_t i = 0; i < strlen(env_var); ++i)
env_var[i] = tolower(env_var[i]);
for(const auto& itr : { "off", "false", "no", "n", "f", "0" })
if(strcmp(env_var, itr) == 0) return false;
return true;
}
return _default;
}
} // namespace
template <typename Tp>
inline auto
get_env(std::string_view env_id, Tp&& _default)
{
if constexpr(std::is_enum<Tp>::value)
{
using Up = std::underlying_type_t<Tp>;
// cast to underlying type -> get_env -> cast to enum type
return static_cast<Tp>(get_env_impl(env_id, static_cast<Up>(_default)));
}
else
{
return get_env_impl(env_id, std::forward<Tp>(_default));
}
}
struct env_config
{
std::string env_name = {};
std::string env_value = {};
int override = 0;
auto operator()(bool _verbose = false) const
{
if(env_name.empty()) return -1;
ROCPROFILER_REGISTER_ENVIRON_LOG(_verbose,
"setenv(\"%s\", \"%s\", %i)\n",
env_name.c_str(),
env_value.c_str(),
override);
return setenv(env_name.c_str(), env_value.c_str(), override);
}
};
} // namespace common
} // namespace rocprofiler_register
@@ -0,0 +1,184 @@
// Copyright (c) 2023 Advanced Micro Devices, Inc.
//
// 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 <array>
#include <initializer_list>
#include <ios>
#include <sstream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#if !defined(ROCPROFILER_REGISTER_FOLD_EXPRESSION)
# define ROCPROFILER_REGISTER_FOLD_EXPRESSION(...) ((__VA_ARGS__), ...)
#endif
namespace rocprofiler_register
{
namespace common
{
namespace
{
template <typename Tp>
struct is_string_impl : std::false_type
{ };
template <>
struct is_string_impl<std::string> : std::true_type
{ };
template <>
struct is_string_impl<std::string_view> : std::true_type
{ };
template <>
struct is_string_impl<const char*> : std::true_type
{ };
template <>
struct is_string_impl<char*> : std::true_type
{ };
template <typename Tp>
struct is_string : is_string_impl<std::remove_cv_t<std::decay_t<Tp>>>
{ };
template <typename ArgT>
auto
as_string(ArgT&& _v, std::enable_if_t<is_string<ArgT>::value, int> = 0)
{
if constexpr(std::is_pointer<std::decay_t<ArgT>>::value)
{
return (_v == nullptr) ? std::string{ "\"\"" }
: (std::string{ "\"" } + _v + std::string{ "\"" });
}
else
{
return std::string{ "\"" } + _v + std::string{ "\"" };
}
}
template <typename ArgT>
auto
as_string(ArgT&& _v, std::enable_if_t<!is_string<ArgT>::value, long> = 0)
{
return _v;
}
template <typename DelimT, typename... Args>
auto
join(DelimT&& _delim, Args&&... _args)
{
using delim_type = std::remove_cv_t<std::remove_reference_t<DelimT>>;
std::stringstream _ss{};
_ss << std::boolalpha;
if constexpr(std::is_same<delim_type, char>::value)
{
const char _delim_c[2] = { _delim, '\0' };
ROCPROFILER_REGISTER_FOLD_EXPRESSION(_ss << _delim_c << _args);
auto _ret = _ss.str();
return (_ret.length() > 1) ? _ret.substr(1) : std::string{};
}
else
{
ROCPROFILER_REGISTER_FOLD_EXPRESSION(_ss << _delim << _args);
auto _ret = _ss.str();
auto&& _len = std::string{ _delim }.length();
return (_ret.length() > _len) ? _ret.substr(_len) : std::string{};
}
}
struct QuoteStrings
{ };
template <typename DelimT, typename... Args>
auto
join(QuoteStrings&&, DelimT&& _delim, Args&&... _args)
{
using delim_type = std::remove_cv_t<std::remove_reference_t<DelimT>>;
std::stringstream _ss{};
_ss << std::boolalpha;
if constexpr(std::is_same<delim_type, char>::value)
{
const char _delim_c[2] = { _delim, '\0' };
ROCPROFILER_REGISTER_FOLD_EXPRESSION(_ss << _delim_c << as_string(_args));
auto _ret = _ss.str();
return (_ret.length() > 1) ? _ret.substr(1) : std::string{};
}
else
{
ROCPROFILER_REGISTER_FOLD_EXPRESSION(_ss << _delim << as_string(_args));
auto _ret = _ss.str();
auto&& _len = std::string{ _delim }.length();
return (_ret.length() > _len) ? _ret.substr(_len) : std::string{};
}
}
template <typename... Args>
auto
join(std::array<std::string_view, 3>&& _delim, Args&&... _args)
{
return join("",
std::get<0>(_delim),
join(std::get<1>(_delim), std::forward<Args>(_args)...),
std::get<2>(_delim));
}
template <typename... Args>
auto
join(QuoteStrings&&, std::array<std::string_view, 3>&& _delim, Args&&... _args)
{
return join(QuoteStrings{},
"",
std::get<0>(_delim),
join(std::get<1>(_delim), std::forward<Args>(_args)...),
std::get<2>(_delim));
}
template <typename DelimB, typename DelimT, typename DelimE, typename... Args>
auto
join(std::tuple<DelimB, DelimT, DelimE>&& _delim, Args&&... _args)
{
return join("",
std::get<0>(_delim),
join(std::get<1>(_delim), std::forward<Args>(_args)...),
std::get<2>(_delim));
}
template <typename DelimB, typename DelimT, typename DelimE, typename... Args>
auto
join(QuoteStrings&&, std::tuple<DelimB, DelimT, DelimE>&& _delim, Args&&... _args)
{
return join(QuoteStrings{},
"",
std::get<0>(_delim),
join(std::get<1>(_delim), std::forward<Args>(_args)...),
std::get<2>(_delim));
}
} // namespace
} // namespace common
} // namespace rocprofiler_register
@@ -0,0 +1,518 @@
// MIT License
//
// Copyright (c) 2020, The Regents of the University of California,
// through Lawrence Berkeley National Laboratory (subject to receipt of any
// required approvals from the U.S. Dept. of Energy). All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR rhs
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR rhsWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR rhs DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <rocprofiler-register/version.h>
#ifndef ROCPROFILER_REGISTER_LOG_COLORS_AVAILABLE
# define ROCPROFILER_REGISTER_LOG_COLORS_AVAILABLE 1
#endif
#ifndef ROCPROFILER_REGISTER_PROJECT_NAME
# define ROCPROFILER_REGISTER_PROJECT_NAME "rocprofiler-register"
#endif
#ifndef ROCP_REG_FILE_NAME
# define ROCP_REG_FILE_NAME \
::std::string{ __FILE__ } \
.substr(::std::string_view{ __FILE__ }.find_last_of('/') + 1) \
.c_str()
#endif
#include <cstdlib>
#include <iostream>
#include <ostream>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace rocprofiler_register
{
namespace log
{
bool&
monochrome();
inline bool&
monochrome()
{
static bool _v = []() {
auto _val = false;
const char* _env_cstr = nullptr;
_env_cstr = std::getenv("ROCPROFILER_REGISTER_MONOCHROME");
if(!_env_cstr) _env_cstr = std::getenv("MONOCHROME");
if(_env_cstr)
{
auto _env = std::string{ _env_cstr };
// check if numeric
if(_env.find_first_not_of("0123456789") == std::string::npos)
{
return _env.length() > 1 || _env[0] != '0';
}
for(auto& itr : _env)
itr = tolower(itr);
// check for matches to acceptable forms of false
for(const auto& itr : { "off", "false", "no", "n", "f" })
{
if(_env == itr) return false;
}
// check for matches to acceptable forms of true
for(const auto& itr : { "on", "true", "yes", "y", "t" })
{
if(_env == itr) return true;
}
}
return _val;
}();
return _v;
}
namespace color
{
static constexpr auto info_value = "\033[01;34m";
static constexpr auto warning_value = "\033[01;33m";
static constexpr auto fatal_value = "\033[01;31m";
static constexpr auto source_value = "\033[01;32m";
static constexpr auto dmesg_value = "\033[01;37m";
static constexpr auto end_value = "\033[0m";
inline const char*
info()
{
return (log::monochrome()) ? "" : info_value;
}
inline const char*
warning()
{
return (log::monochrome()) ? "" : warning_value;
}
inline const char*
fatal()
{
return (log::monochrome()) ? "" : fatal_value;
}
inline const char*
source()
{
return (log::monochrome()) ? "" : source_value;
}
inline const char*
dmesg()
{
return (log::monochrome()) ? "" : dmesg_value;
}
inline const char*
end()
{
return (log::monochrome()) ? "" : end_value;
}
} // namespace color
} // namespace log
} // namespace rocprofiler_register
namespace rocprofiler_register
{
namespace log
{
struct base
{
public:
static base indent(size_t, size_t = 2) { return base{}; }
template <typename Tp>
auto operator<<(Tp&&)
{
return base{};
}
};
struct logger : public base
{
logger() = default;
explicit logger(bool _exit)
: m_exit{ _exit }
{ }
~logger()
{
if(m_done)
{
std::cerr << color::end() << "\n";
if(m_exit) abort();
}
}
logger(logger&& rhs) noexcept
{
m_exit = rhs.m_exit;
m_done = rhs.m_done;
rhs.m_done = false;
}
logger& operator=(logger&& rhs) noexcept
{
m_exit = rhs.m_exit;
m_done = rhs.m_done;
rhs.m_done = false;
return *this;
}
logger&& indent(size_t _n, size_t _tab_size = 4)
{
for(size_t i = 0; i < _n; i++)
for(size_t j = 0; j < _tab_size; j++)
std::cerr << " ";
return std::move(*this);
}
template <typename Tp>
logger&& operator<<(Tp&& _v)
{
std::cerr << std::forward<Tp>(_v);
return std::move(*this);
}
private:
bool m_exit = false;
bool m_done = true;
};
template <typename Tp>
inline auto&
get_color_hist()
{
static thread_local std::vector<std::pair<Tp*, const char*>> _v{};
return _v;
}
template <typename Tp>
inline auto
push_color_hist(Tp* _v, const char* _c)
{
if(!monochrome()) get_color_hist<Tp>().emplace_back(_v, _c);
return _c;
}
template <typename Tp>
inline std::string
pop_color_hist(Tp* _v)
{
if(monochrome()) return std::string{};
auto& _hist = get_color_hist<Tp>();
for(auto itr = _hist.rbegin(); itr != _hist.rend(); ++itr)
{
Tp* _addr = itr->first;
if(_addr == _v)
{
auto fitr = _hist.begin();
std::advance(fitr, std::distance(_hist.rbegin(), itr));
_hist.erase(fitr);
}
}
for(auto itr = _hist.rbegin(); itr != _hist.rend(); ++itr)
{
if(itr->first == _v) return itr->second;
}
return color::end();
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
info(std::basic_ostream<CharT, Traits>& os)
{
return (os << push_color_hist(&os, color::info()));
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
warning(std::basic_ostream<CharT, Traits>& os)
{
return (os << push_color_hist(&os, color::warning()));
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
fatal(std::basic_ostream<CharT, Traits>& os)
{
return (os << push_color_hist(&os, color::fatal()));
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
source(std::basic_ostream<CharT, Traits>& os)
{
return (os << push_color_hist(&os, color::source()));
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
end(std::basic_ostream<CharT, Traits>& os)
{
return (os << push_color_hist(&os, color::end()));
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
pop(std::basic_ostream<CharT, Traits>& os)
{
return (os << pop_color_hist(&os));
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
reset(std::basic_ostream<CharT, Traits>& os)
{
pop_color_hist(&os);
return (os << color::end());
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
flush(std::basic_ostream<CharT, Traits>& os)
{
return (os << pop << std::flush);
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
endl(std::basic_ostream<CharT, Traits>& os)
{
return (os << pop << "\n" << std::flush);
}
template <typename StreamT>
struct stream_base
{
stream_base(StreamT& _os, const char* _color)
: m_os{ _os }
{
m_os << push_color_hist(&_os, _color);
}
~stream_base() { m_os << pop_color_hist(&m_os); }
stream_base(const stream_base&) = delete;
stream_base& operator=(const stream_base&) = delete;
stream_base(stream_base&& rhs) noexcept = default;
stream_base& operator=(stream_base&& rhs) noexcept = default;
template <typename Tp>
stream_base& operator<<(Tp&& _v)
{
m_os << std::forward<Tp>(_v);
return *this;
}
template <typename Tp>
stream_base& operator<<(Tp& _v)
{
m_os << _v;
return *this;
}
stream_base& put(char _c)
{
m_os.put(_c);
return *this;
}
stream_base& endl()
{
m_os << std::endl;
return *this;
}
template <typename... Args>
stream_base& write(Args&&... _args)
{
m_os.write(std::forward<Args>(_args)...);
return *this;
}
stream_base& flush()
{
m_os << std::flush;
return *this;
}
auto tellp() { return m_os.tellp(); }
template <typename... Args>
stream_base& seekp(Args&&... _args)
{
m_os.seekp(std::forward<Args>(_args)...);
return *this;
}
private:
StreamT& m_os;
};
template <typename StreamT>
stream_base<StreamT>
stream(StreamT& _os, const char* _color)
{
return stream_base<StreamT>{ _os, _color };
}
template <typename StreamT>
stream_base<StreamT>
info_stream(StreamT& _os)
{
return stream_base<StreamT>{ _os, color::info() };
}
template <typename StreamT>
stream_base<StreamT>
source_stream(StreamT& _os)
{
return stream_base<StreamT>{ _os, color::source() };
}
template <typename StreamT>
stream_base<StreamT>
warning_stream(StreamT& _os)
{
return stream_base<StreamT>{ _os, color::warning() };
}
template <typename StreamT>
stream_base<StreamT>
fatal_stream(StreamT& _os)
{
return stream_base<StreamT>{ _os, color::fatal() };
}
inline std::string
string(const char* _color, std::string_view _v)
{
return std::string{ _color } + std::string{ _v } + std::string{ color::end() };
}
inline std::string
string(const char* _color, std::stringstream& _v)
{
return std::string{ _color } + _v.str() + std::string{ color::end() };
}
inline std::string
string(const char* _color, std::stringstream&& _v)
{
return std::string{ _color } + _v.str() + std::string{ color::end() };
}
template <typename Tp>
inline auto
info_string(Tp&& _v)
{
return string(color::info(), std::forward<Tp>(_v));
}
template <typename Tp>
inline auto
source_string(Tp&& _v)
{
return string(color::source(), std::forward<Tp>(_v));
}
template <typename Tp>
inline auto
warning_string(Tp&& _v)
{
return string(color::warning(), std::forward<Tp>(_v));
}
template <typename Tp>
inline auto
fatal_string(Tp&& _v)
{
return string(color::fatal(), std::forward<Tp>(_v));
}
} // namespace log
} // namespace rocprofiler_register
#if !defined(ROCPROFILER_REGISTER_LOG)
# define ROCPROFILER_REGISTER_LOG(COLOR, EXIT_CODE) \
(::rocprofiler_register::log::logger(EXIT_CODE) \
<< ::rocprofiler_register::log::color::end() \
<< ::rocprofiler_register::log::color::source() << "[" \
<< ROCPROFILER_REGISTER_PROJECT_NAME << "][" << ROCP_REG_FILE_NAME << ":" \
<< __LINE__ << "][" << getpid() << "] " \
<< ::rocprofiler_register::log::color::end() << COLOR)
#endif
#if defined(NDEBUG)
# if !defined(ROCPROFILER_REGISTER_INFO)
# define ROCPROFILER_REGISTER_INFO (::rocprofiler_register::log::base())
# endif
# if !defined(ROCPROFILER_REGISTER_ASSERT)
# define ROCPROFILER_REGISTER_ASSERT(COND) (::rocprofiler_register::log::base())
# endif
#else
# if !defined(ROCPROFILER_REGISTER_INFO)
# define ROCPROFILER_REGISTER_INFO \
ROCPROFILER_REGISTER_LOG(::rocprofiler_register::log::color::info(), false)
# endif
# if !defined(ROCPROFILER_REGISTER_ASSERT)
# define ROCPROFILER_REGISTER_ASSERT(COND) \
(COND) ? ::rocprofiler_register::log::base() : ROCPROFILER_REGISTER_FATAL
# endif
#endif
#if !defined(ROCPROFILER_REGISTER_WARNING)
# define ROCPROFILER_REGISTER_WARNING \
ROCPROFILER_REGISTER_LOG(::rocprofiler_register::log::color::warning(), false)
#endif
#if !defined(ROCPROFILER_REGISTER_FATAL)
# define ROCPROFILER_REGISTER_FATAL \
ROCPROFILER_REGISTER_LOG(::rocprofiler_register::log::color::fatal(), true)
#endif
#if !defined(ROCPROFILER_REGISTER_PREFER)
# define ROCPROFILER_REGISTER_PREFER(COND) \
(COND) ? ::rocprofiler_register::log::base() : ROCPROFILER_REGISTER_WARNING
#endif
#if !defined(ROCPROFILER_REGISTER_REQUIRE)
# define ROCPROFILER_REGISTER_REQUIRE(COND) \
(COND) ? ::rocprofiler_register::log::base() : ROCPROFILER_REGISTER_FATAL
#endif
@@ -0,0 +1,127 @@
// 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 "utility.hpp"
#include <string>
#include <string_view>
#include <sys/types.h>
#include <unistd.h>
namespace rocprofiler_register
{
namespace utility
{
namespace
{
template <typename ContainerT, typename... Args>
inline auto
emplace_impl(ContainerT& _c, int, Args&&... _args)
-> decltype(_c.emplace_back(std::forward<Args>(_args)...))
{
return _c.emplace_back(std::forward<Args>(_args)...);
}
template <typename ContainerT, typename... Args>
inline auto
emplace_impl(ContainerT& _c, long, Args&&... _args)
-> decltype(_c.emplace(std::forward<Args>(_args)...))
{
return _c.emplace(std::forward<Args>(_args)...);
}
template <typename ContainerT, typename... Args>
inline auto
emplace(ContainerT& _c, Args&&... _args)
{
return emplace_impl(_c, 0, std::forward<Args>(_args)...);
}
template <typename ContainerT, typename ArgT>
inline auto
reserve_impl(ContainerT& _c, int, ArgT _arg) -> decltype(_c.reserve(_arg), bool())
{
_c.reserve(_arg);
return true;
}
template <typename ContainerT, typename ArgT>
inline auto
reserve_impl(ContainerT&, long, ArgT)
{
return false;
}
template <typename ContainerT, typename ArgT>
inline auto
reserve(ContainerT& _c, ArgT _arg)
{
return reserve_impl(_c, 0, _arg);
}
} // namespace
template <typename ContainerT>
ContainerT
delimit(const std::string& line, std::string_view delimiters)
{
ContainerT _result{};
size_t _beginp = 0; // position that is the beginning of the new string
size_t _delimp = 0; // position of the delimiter in the string
if(reserve(_result, 0))
{
size_t _nmax = 0;
for(char itr : line)
{
for(size_t j = 0; j < delimiters.length(); ++j)
{
if(itr == delimiters.at(j)) ++_nmax;
}
}
reserve(_result, _nmax);
}
while(_beginp < line.length() && _delimp < line.length())
{
// find the first character (starting at _delimp) that is not a delimiter
_beginp = line.find_first_not_of(delimiters, _delimp);
// if no a character after or at _end that is not a delimiter is not found
// then we are done
if(_beginp == std::string::npos) break;
// starting at the position of the new string, find the next delimiter
_delimp = line.find_first_of(delimiters, _beginp);
std::string _tmp{};
// starting at the position of the new string, get the characters
// between this position and the next delimiter
_tmp = line.substr(_beginp, _delimp - _beginp);
// don't add empty strings
if(!_tmp.empty()) emplace(_result, _tmp);
}
return _result;
}
template std::vector<std::string>
delimit(const std::string&, std::string_view);
template std::set<std::string>
delimit(const std::string&, std::string_view);
} // namespace utility
} // namespace rocprofiler_register
@@ -0,0 +1,44 @@
// 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 <set>
#include <string>
#include <string_view>
#include <vector>
namespace rocprofiler_register
{
namespace utility
{
template <typename ContainerT = std::vector<std::string>>
ContainerT
delimit(const std::string& line, std::string_view delimiters = "\"',;: ");
extern template std::vector<std::string>
delimit(const std::string&, std::string_view);
extern template std::set<std::string>
delimit(const std::string&, std::string_view);
} // namespace utility
} // namespace rocprofiler_register
@@ -0,0 +1,24 @@
// Copyright (c) 2023 Advanced Micro Devices, Inc.
//
// 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 <rocprofiler-register/rocprofiler-register.h>
#include <rocprofiler-register/version.h>
int ROCPROFILER_REGISTER_INTERNAL_API rocp_register_sym = 0;
@@ -0,0 +1,374 @@
// Copyright (c) 2023 Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#define GNU_SOURCE 1
#include <rocprofiler-register/rocprofiler-register.h>
#include "details/dl.hpp"
#include "details/environment.hpp"
#include "details/log.hpp"
#include <array>
#include <atomic>
#include <bitset>
#include <filesystem>
#include <mutex>
#include <regex>
#include <stdexcept>
#include <string_view>
#include <utility>
#include <dlfcn.h>
extern "C" {
#pragma weak rocprofiler_configure
#pragma weak rocprofiler_set_api_table
#pragma weak rocprofiler_register_import_hip
#pragma weak rocprofiler_register_import_hip_static
#pragma weak rocprofiler_register_import_hsa
#pragma weak rocprofiler_register_import_hsa_static
#pragma weak rocprofiler_register_import_roctx
#pragma weak rocprofiler_register_import_roctx_static
extern rocprofiler_configure_result_t*
rocprofiler_configure(uint32_t, const char*, uint32_t, uint32_t);
extern int
rocprofiler_set_api_table(const char*, uint64_t, uint64_t, void**, uint64_t);
extern uint32_t
rocprofiler_register_import_hip(void);
extern uint32_t
rocprofiler_register_import_hsa(void);
extern uint32_t
rocprofiler_register_import_roctx(void);
extern uint32_t
rocprofiler_register_import_hip_static(void);
extern uint32_t
rocprofiler_register_import_hsa_static(void);
extern uint32_t
rocprofiler_register_import_roctx_static(void);
}
namespace
{
namespace fs = ::std::filesystem;
using namespace rocprofiler_register;
using rocprofiler_set_api_table_t = decltype(::rocprofiler_set_api_table)*;
using bitset_t = std::bitset<sizeof(rocprofiler_register_library_indentifier_t::handle)>;
static_assert(sizeof(bitset_t) ==
sizeof(rocprofiler_register_library_indentifier_t::handle),
"bitset should be same at uint64_t");
int rocprofiler_register_verbose = common::get_env("ROCPROFILER_REGISTER_VERBOSE", 0);
constexpr int rocprofiler_register_info_level = 2;
constexpr auto rocprofiler_lib_name = "librocprofiler64.so";
constexpr auto rocprofiler_lib_register_entrypoint = "rocprofiler_set_api_table";
constexpr auto rocprofiler_register_lib_name =
"librocprofiler-register.so." ROCPROFILER_REGISTER_SOVERSION;
enum rocp_reg_supported_library // NOLINT(performance-enum-size)
{
ROCP_REG_HSA = 0,
ROCP_REG_HIP,
ROCP_REG_ROCTX,
ROCP_REG_LAST,
};
template <size_t>
struct supported_library_trait
{
static constexpr bool specialized = false;
static constexpr auto value = ROCP_REG_LAST;
static constexpr const char* const common_name = nullptr;
static constexpr const char* const symbol_name = nullptr;
static constexpr const char* const library_name = nullptr;
};
#define ROCP_REG_DEFINE_LIBRARY_TRAITS(ENUM, NAME, SYM_NAME, LIB_NAME) \
template <> \
struct supported_library_trait<ENUM> \
{ \
static constexpr bool specialized = true; \
static constexpr auto value = ENUM; \
static constexpr auto common_name = NAME; \
static constexpr auto symbol_name = SYM_NAME; \
static constexpr auto library_name = LIB_NAME; \
};
ROCP_REG_DEFINE_LIBRARY_TRAITS(ROCP_REG_HSA,
"hsa",
"rocprofiler_register_import_hsa",
"libhsa-runtime64.so.[2-9]($|\\.[0-9\\.]+)")
ROCP_REG_DEFINE_LIBRARY_TRAITS(ROCP_REG_HIP,
"hip",
"rocprofiler_register_import_hip",
"libamdhip64.so.[6-9]($|\\.[0-9\\.]+)")
ROCP_REG_DEFINE_LIBRARY_TRAITS(ROCP_REG_ROCTX,
"roctx",
"rocprofiler_register_import_roctx",
"libroctx64.so.[4-9]($|\\.[0-9\\.]+)")
auto
get_this_library_path()
{
auto _this_lib_path = binary::get_linked_path(rocprofiler_register_lib_name,
{ RTLD_NOLOAD | RTLD_LAZY });
ROCPROFILER_REGISTER_REQUIRE(_this_lib_path)
<< rocprofiler_register_lib_name
<< " could not locate itself in the list of loaded libraries";
return fs::path{ *_this_lib_path }.parent_path().string();
}
struct rocp_import
{
rocp_reg_supported_library library_idx = ROCP_REG_LAST;
std::string_view common_name = {};
std::string_view symbol_name = {};
std::string_view library_name = {};
};
template <size_t... Idx>
auto rocp_reg_get_imports(std::index_sequence<Idx...>)
{
auto _data = std::vector<rocp_import>{};
auto _import_scan = [&_data](auto _info) {
if(_info.specialized)
{
_data.emplace_back(rocp_import{
_info.value, _info.common_name, _info.symbol_name, _info.library_name });
}
};
(_import_scan(supported_library_trait<Idx>{}), ...);
return _data;
}
auto
rocp_reg_scan_for_tools()
{
auto _rocp_reg_lib = common::get_env("ROCPROFILER_REGISTER_LIBRARY", std::string{});
bool _force_tool =
common::get_env("ROCPROFILER_REGISTER_FORCE_LOAD", !_rocp_reg_lib.empty());
bool _found_tool = (rocprofiler_configure != nullptr || _force_tool);
static void* rocprofiler_lib_handle = nullptr;
static rocprofiler_set_api_table_t rocprofiler_lib_config_fn = nullptr;
if(_force_tool)
{
if(rocprofiler_lib_handle && rocprofiler_lib_config_fn)
return std::make_pair(rocprofiler_lib_handle, rocprofiler_lib_config_fn);
if(_rocp_reg_lib.empty()) _rocp_reg_lib = rocprofiler_lib_name;
auto _rocp_reg_lib_path = fs::path{ _rocp_reg_lib };
auto _rocp_reg_lib_path_fname = _rocp_reg_lib_path.filename();
auto _rocp_reg_lib_path_abs =
(_rocp_reg_lib_path.is_absolute())
? _rocp_reg_lib_path
: (fs::path{ get_this_library_path() } / _rocp_reg_lib_path_fname);
// check to see if the rocprofiler library is already loaded
rocprofiler_lib_handle =
dlopen(_rocp_reg_lib_path.c_str(), RTLD_NOLOAD | RTLD_LAZY);
// try to load with the given path
if(!rocprofiler_lib_handle)
{
rocprofiler_lib_handle =
dlopen(_rocp_reg_lib_path.c_str(), RTLD_GLOBAL | RTLD_LAZY);
}
// try to load with the absoulte path
if(!rocprofiler_lib_handle)
{
_rocp_reg_lib_path = _rocp_reg_lib_path_abs;
rocprofiler_lib_handle =
dlopen(_rocp_reg_lib_path.c_str(), RTLD_GLOBAL | RTLD_LAZY);
}
// try to load with the basename path
if(!rocprofiler_lib_handle)
{
_rocp_reg_lib_path = _rocp_reg_lib_path_fname;
rocprofiler_lib_handle =
dlopen(_rocp_reg_lib_path.c_str(), RTLD_GLOBAL | RTLD_LAZY);
}
if(rocprofiler_register_verbose >= rocprofiler_register_info_level)
ROCPROFILER_REGISTER_INFO << "loaded " << _rocp_reg_lib_path_fname.string()
<< " library at " << _rocp_reg_lib_path.string();
ROCPROFILER_REGISTER_REQUIRE(rocprofiler_lib_handle)
<< _rocp_reg_lib << " failed to load\n";
*(void**) (&rocprofiler_lib_config_fn) =
dlsym(rocprofiler_lib_handle, rocprofiler_lib_register_entrypoint);
ROCPROFILER_REGISTER_REQUIRE(rocprofiler_lib_config_fn)
<< _rocp_reg_lib << " did not contain '"
<< rocprofiler_lib_register_entrypoint << "' symbol\n";
}
else if(_found_tool && rocprofiler_set_api_table)
{
rocprofiler_lib_config_fn = &rocprofiler_set_api_table;
}
return std::make_pair(rocprofiler_lib_handle, rocprofiler_lib_config_fn);
}
constexpr auto library_seq = std::make_index_sequence<ROCP_REG_LAST>{};
auto global_mutex = std::recursive_mutex{};
auto import_info = rocp_reg_get_imports(library_seq);
auto instance_counters = std::array<std::atomic_uint64_t, ROCP_REG_LAST>{};
} // namespace
extern "C" {
rocprofiler_register_error_code_t
rocprofiler_register_library_api_table(
const char* common_name,
rocprofiler_register_import_func_t import_func,
uint32_t lib_version,
void** api_tables,
uint64_t api_table_length,
rocprofiler_register_library_indentifier_t* register_id)
{
if(api_table_length < 1) return ROCP_REG_BAD_API_TABLE_LENGTH;
(void) lib_version;
(void) api_tables;
auto _lk = std::unique_lock<std::recursive_mutex>{ global_mutex, std::defer_lock };
if(_lk.owns_lock()) return ROCP_REG_DEADLOCK;
auto _scan_result = rocp_reg_scan_for_tools();
rocp_import* _import_match = nullptr;
for(auto& itr : import_info)
{
if(itr.common_name == common_name)
{
_import_match = &itr;
break;
}
}
// not a supported library name
if(!_import_match || _import_match->library_idx == ROCP_REG_LAST)
return ROCP_REG_UNSUPPORTED_API;
if(import_func != nullptr &&
common::get_env<bool>("ROCPROFILER_REGISTER_SECURE", false))
{
auto _import_func_addr = reinterpret_cast<uintptr_t>(import_func);
auto _segment_addresses = binary::get_segment_addresses();
auto _in_address_range = [](uintptr_t _addr,
const std::vector<binary::address_range>& _range) {
for(auto ritr : _range)
{
if(_addr >= ritr.start && _addr < ritr.last) return true;
}
return false;
};
// check that the address of the import function is within the expected library
// name
bool _valid_addr = false;
for(const auto& itr : _segment_addresses)
{
if(_in_address_range(_import_func_addr, itr.ranges))
{
if(std::regex_search(fs::path{ itr.filepath }.filename().string(),
std::regex{ _import_match->library_name.data() }))
{
_valid_addr = true;
}
}
}
// the library provided
if(!_valid_addr) return ROCP_REG_INVALID_API_ADDRESS;
}
constexpr auto offset_factor = 64 / std::max<size_t>(ROCP_REG_LAST, 8);
// if ROCP_REG_LAST > 8, then we can no longer encode 8 instances per lib
// because we ran out of bits (i.e. max of 8 * 8 = 64)
static_assert((offset_factor * ROCP_REG_LAST) <= sizeof(uint64_t) * 8,
"ROCP_REG_LAST has exceeded the max allowable size");
// too many instances of the same library
if(instance_counters.at(_import_match->library_idx) >= offset_factor)
return ROCP_REG_EXCESS_API_INSTANCES;
auto _instance_val = instance_counters.at(_import_match->library_idx)++;
auto& _bits = *reinterpret_cast<bitset_t*>(&register_id->handle);
_bits = bitset_t{ (offset_factor * _import_match->library_idx) + _instance_val };
if(_bits.to_ulong() != register_id->handle)
throw std::runtime_error("error encoding register_id");
// rocprofiler library is dlopened and we have the functor to pass the API data
auto _activate_rocprofiler = (_scan_result.second != nullptr);
if(_activate_rocprofiler)
{
auto _ret = _scan_result.second(
common_name, lib_version, _instance_val, api_tables, api_table_length);
if(_ret != 0) return ROCP_REG_ROCPROFILER_ERROR;
}
else
{
return ROCP_REG_NO_TOOLS;
}
return ROCP_REG_SUCCESS;
}
const char*
rocprofiler_register_error_string(rocprofiler_register_error_code_t _ec)
{
switch(_ec)
{
case ROCP_REG_SUCCESS: return "rocprofiler_register_success";
case ROCP_REG_NO_TOOLS: return "rocprofiler_register_no_tools";
case ROCP_REG_DEADLOCK: return "rocprofiler_register_deadlock";
case ROCP_REG_BAD_API_TABLE_LENGTH:
return "rocprofiler_register_bad_api_table_length";
case ROCP_REG_UNSUPPORTED_API: return "rocprofiler_register_unsupported_api";
case ROCP_REG_INVALID_API_ADDRESS:
return "rocprofiler_register_invalid_api_address";
case ROCP_REG_ROCPROFILER_ERROR: return "rocprofiler_register_rocprofiler_error";
case ROCP_REG_EXCESS_API_INSTANCES:
return "rocprofiler_register_excess_api_instances";
case ROCP_REG_ERROR_CODE_END: return "rocprofiler_register_unknown_error";
}
return "rocprofiler_register_unknown_error";
}
}