Add 'projects/rocprofiler-sdk/' from commit 'bf0fad1d5406fbc51403ba1aa9621a9d4a9bce2b'
git-subtree-dir: projects/rocprofiler-sdk git-subtree-mainline:50a90550e9git-subtree-split:bf0fad1d54
Αυτή η υποβολή περιλαμβάνεται σε:
@@ -0,0 +1,72 @@
|
||||
#
|
||||
# Builds common utilities into a static library
|
||||
#
|
||||
rocprofiler_activate_clang_tidy()
|
||||
|
||||
set(common_sources
|
||||
demangle.cpp
|
||||
elf_utils.cpp
|
||||
environment.cpp
|
||||
logging.cpp
|
||||
md5sum.cpp
|
||||
sha256.cpp
|
||||
simple_timer.cpp
|
||||
static_object.cpp
|
||||
static_tl_object.cpp
|
||||
string_entry.cpp
|
||||
utility.cpp
|
||||
uuid_v7.cpp)
|
||||
set(common_headers
|
||||
abi.hpp
|
||||
defines.hpp
|
||||
demangle.hpp
|
||||
elf_utils.hpp
|
||||
environment.hpp
|
||||
filesystem.hpp
|
||||
hasher.hpp
|
||||
logging.hpp
|
||||
md5sum.hpp
|
||||
mpl.hpp
|
||||
scope_destructor.hpp
|
||||
sha256.hpp
|
||||
simple_timer.hpp
|
||||
static_object.hpp
|
||||
static_tl_object.hpp
|
||||
string_entry.hpp
|
||||
stringize_arg.hpp
|
||||
synchronized.hpp
|
||||
units.hpp
|
||||
utility.hpp
|
||||
uuid_v7.hpp)
|
||||
|
||||
add_library(rocprofiler-sdk-common-library STATIC)
|
||||
add_library(rocprofiler-sdk::rocprofiler-sdk-common-library ALIAS
|
||||
rocprofiler-sdk-common-library)
|
||||
|
||||
add_subdirectory(container)
|
||||
add_subdirectory(memory)
|
||||
|
||||
target_sources(rocprofiler-sdk-common-library PRIVATE ${common_sources} ${common_headers})
|
||||
target_include_directories(rocprofiler-sdk-common-library
|
||||
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/source>)
|
||||
|
||||
target_link_libraries(
|
||||
rocprofiler-sdk-common-library
|
||||
PUBLIC $<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-headers>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-build-flags>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-threading>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-memcheck>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-cxx-filesystem>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-glog>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-fmt>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-yaml-cpp>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-dl>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-ptl>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-atomic>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-hsakmt-nolink>
|
||||
$<BUILD_INTERFACE:rocprofiler-sdk::rocprofiler-sdk-elfio>)
|
||||
|
||||
set_target_properties(rocprofiler-sdk-common-library PROPERTIES OUTPUT_NAME
|
||||
rocprofiler-sdk-common)
|
||||
|
||||
add_subdirectory(details)
|
||||
@@ -0,0 +1,92 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 <rocprofiler-sdk/ext_version.h>
|
||||
|
||||
#include "lib/common/defines.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace abi
|
||||
{
|
||||
constexpr auto
|
||||
compute_table_offset(size_t num_funcs)
|
||||
{
|
||||
return (num_funcs * sizeof(void*)) + sizeof(size_t);
|
||||
}
|
||||
} // namespace abi
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
|
||||
// ROCP_SDK_ENFORCE_ABI_VERSIONING will cause a compiler error if the size of the API table
|
||||
// changed (most likely due to addition of new dispatch table entry) to make sure the developer is
|
||||
// reminded to update the table versioning value before changing the value in
|
||||
// ROCP_SDK_ENFORCE_ABI_VERSIONING to make this static assert pass.
|
||||
//
|
||||
// ROCP_SDK_ENFORCE_ABI will cause a compiler error if the order of the members in the API table
|
||||
// change. Do not reorder member variables and change existing ROCP_SDK_ENFORCE_ABI values --
|
||||
// always
|
||||
//
|
||||
// Please note: rocprofiler will do very strict compile time checks to make
|
||||
// sure these versioning values are appropriately updated -- so commenting out this check, only
|
||||
// updating the size field in ROCP_SDK_ENFORCE_ABI_VERSIONING, etc. will result in the
|
||||
// rocprofiler-sdk failing to build and you will be forced to do the work anyway.
|
||||
#if !defined(ROCPROFILER_UNSAFE_NO_VERSION_CHECK)
|
||||
# define ROCP_SDK_ENFORCE_ABI_VERSIONING(TABLE, NUM) \
|
||||
static_assert( \
|
||||
sizeof(TABLE) == ::rocprofiler::common::abi::compute_table_offset(NUM), \
|
||||
"size of the API table struct has changed. Update the STEP_VERSION number (or " \
|
||||
"in rare cases, the MAJOR_VERSION number)");
|
||||
|
||||
# define ROCP_SDK_ENFORCE_ABI(TABLE, ENTRY, NUM) \
|
||||
static_assert( \
|
||||
offsetof(TABLE, ENTRY) == ::rocprofiler::common::abi::compute_table_offset(NUM), \
|
||||
"ABI break for " #TABLE "." #ENTRY \
|
||||
". Only add new function pointers to end of struct and do not rearrange them");
|
||||
#else
|
||||
# define ROCP_SDK_ENFORCE_ABI_VERSIONING(TABLE, NUM)
|
||||
# define ROCP_SDK_ENFORCE_ABI(TABLE, ENTRY, NUM)
|
||||
#endif
|
||||
|
||||
// These are guarded by ROCPROFILER_CI=1
|
||||
#if !defined(ROCPROFILER_UNSAFE_NO_VERSION_CHECK) && (defined(ROCPROFILER_CI) && ROCPROFILER_CI > 0)
|
||||
# define INTERNAL_CI_ROCP_SDK_ENFORCE_ABI_VERSIONING(TABLE, NUM) \
|
||||
static_assert( \
|
||||
sizeof(TABLE) == ::rocprofiler::common::abi::compute_table_offset(NUM), \
|
||||
"size of the API table struct has changed. Update the STEP_VERSION number (or " \
|
||||
"in rare cases, the MAJOR_VERSION number)");
|
||||
|
||||
# define INTERNAL_CI_ROCP_SDK_ENFORCE_ABI(TABLE, ENTRY, NUM) \
|
||||
static_assert( \
|
||||
offsetof(TABLE, ENTRY) == ::rocprofiler::common::abi::compute_table_offset(NUM), \
|
||||
"ABI break for " #TABLE "." #ENTRY \
|
||||
". Only add new function pointers to end of struct and do not rearrange them");
|
||||
#else
|
||||
# define INTERNAL_CI_ROCP_SDK_ENFORCE_ABI_VERSIONING(TABLE, NUM)
|
||||
# define INTERNAL_CI_ROCP_SDK_ENFORCE_ABI(TABLE, ENTRY, NUM)
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# add container sources and headers to common library target
|
||||
#
|
||||
set(containers_headers
|
||||
ring_buffer.hpp c_array.hpp operators.hpp record_header_buffer.hpp ring_buffer.hpp
|
||||
small_vector.hpp stable_vector.hpp static_vector.hpp)
|
||||
set(containers_sources ring_buffer.cpp record_header_buffer.cpp ring_buffer.cpp
|
||||
small_vector.cpp)
|
||||
|
||||
target_sources(rocprofiler-sdk-common-library PRIVATE ${containers_sources}
|
||||
${containers_headers})
|
||||
@@ -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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
template <typename Tp>
|
||||
struct c_array
|
||||
{
|
||||
// Construct an array wrapper from a base pointer and array size
|
||||
c_array(Tp* _base, size_t _size)
|
||||
: m_base{_base}
|
||||
, m_size{_size}
|
||||
{}
|
||||
|
||||
~c_array() = default;
|
||||
c_array(const c_array&) = default;
|
||||
c_array& operator=(const c_array&) = default;
|
||||
c_array& operator=(c_array&&) noexcept = default;
|
||||
|
||||
// Get the size of the wrapped array
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
// Access an element by index
|
||||
Tp& operator[](size_t i) { return m_base[i]; }
|
||||
|
||||
// Access an element by index
|
||||
const Tp& operator[](size_t i) const { return m_base[i]; }
|
||||
|
||||
// Access an element by index with bounds check
|
||||
Tp& at(size_t i)
|
||||
{
|
||||
if(i < m_size) return m_base[i];
|
||||
throw std::out_of_range(std::string{typeid(*this).name()} + std::to_string(i) +
|
||||
" exceeds size " + std::to_string(m_size));
|
||||
}
|
||||
|
||||
// Access an element by index with bounds check
|
||||
const Tp& at(size_t i) const
|
||||
{
|
||||
if(i < m_size) return m_base[i];
|
||||
throw std::out_of_range(std::string{typeid(*this).name()} + std::to_string(i) +
|
||||
" exceeds size " + std::to_string(m_size));
|
||||
}
|
||||
|
||||
// Get a slice of this array, from a start index (inclusive) to end index (exclusive)
|
||||
c_array<Tp> slice(size_t start, size_t end) { return c_array<Tp>(&m_base[start], end - start); }
|
||||
|
||||
void pop_front()
|
||||
{
|
||||
++m_base;
|
||||
--m_size;
|
||||
}
|
||||
|
||||
void pop_back() { --m_size; }
|
||||
|
||||
operator Tp*() const { return m_base; }
|
||||
|
||||
// Iterator class for convenient range-based for loop support
|
||||
template <typename Up>
|
||||
struct iterator
|
||||
{
|
||||
// Start the iterator at a given pointer
|
||||
explicit iterator(Tp* p)
|
||||
: m_ptr{p}
|
||||
{}
|
||||
|
||||
// Advance to the next element
|
||||
void operator++() { ++m_ptr; }
|
||||
void operator++(int) { m_ptr++; }
|
||||
|
||||
// Get the current element
|
||||
Up& operator*() const { return *m_ptr; }
|
||||
|
||||
// Compare iterators
|
||||
bool operator==(const iterator& rhs) const { return m_ptr == rhs.m_ptr; }
|
||||
bool operator!=(const iterator& rhs) const { return m_ptr != rhs.m_ptr; }
|
||||
|
||||
private:
|
||||
Tp* m_ptr = nullptr;
|
||||
};
|
||||
|
||||
// Get an iterator positioned at the beginning of the wrapped array
|
||||
iterator<Tp> begin() { return iterator<Tp>{m_base}; }
|
||||
iterator<const Tp> begin() const { return iterator<const Tp>{m_base}; }
|
||||
|
||||
// Get an iterator positioned at the end of the wrapped array
|
||||
iterator<Tp> end() { return iterator<Tp>{&m_base[m_size]}; }
|
||||
iterator<const Tp> end() const { return iterator<const Tp>{&m_base[m_size]}; }
|
||||
|
||||
private:
|
||||
Tp* m_base = nullptr;
|
||||
size_t m_size = 0;
|
||||
};
|
||||
|
||||
// Function for automatic template argument deduction
|
||||
template <typename Tp>
|
||||
c_array<Tp>
|
||||
wrap_c_array(Tp* base, size_t size)
|
||||
{
|
||||
return c_array<Tp>(base, size);
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,239 @@
|
||||
// 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 <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#define ROCPROFILER_IMPORT_TEMPLATE2(template_name)
|
||||
#define ROCPROFILER_IMPORT_TEMPLATE1(template_name)
|
||||
|
||||
// Import a 2-type-argument operator template into boost (if necessary) and
|
||||
// provide a specialization of 'is_chained_base<>' for it.
|
||||
#define ROCPROFILER_OPERATOR_TEMPLATE2(template_name2) \
|
||||
ROCPROFILER_IMPORT_TEMPLATE2(template_name2) \
|
||||
template <typename T, typename U, typename B> \
|
||||
struct is_chained_base<::rocprofiler::common::container::template_name2<T, U, B>> \
|
||||
{ \
|
||||
using value = ::rocprofiler::common::container::true_t; \
|
||||
};
|
||||
|
||||
// Import a 1-type-argument operator template into boost (if necessary) and
|
||||
// provide a specialization of 'is_chained_base<>' for it.
|
||||
#define ROCPROFILER_OPERATOR_TEMPLATE1(template_name1) \
|
||||
ROCPROFILER_IMPORT_TEMPLATE1(template_name1) \
|
||||
template <typename T, typename B> \
|
||||
struct is_chained_base<::rocprofiler::common::container::template_name1<T, B>> \
|
||||
{ \
|
||||
using value = ::rocprofiler::common::container::true_t; \
|
||||
};
|
||||
|
||||
#define ROCPROFILER_OPERATOR_TEMPLATE(template_name) \
|
||||
template <typename T, \
|
||||
typename U = T, \
|
||||
typename B = empty_base<T>, \
|
||||
typename O = typename is_chained_base<U>::value> \
|
||||
struct template_name; \
|
||||
\
|
||||
template <typename T, typename U, typename B> \
|
||||
struct template_name<T, U, B, false_t> : template_name##2 < T \
|
||||
, U \
|
||||
, B > \
|
||||
{}; \
|
||||
\
|
||||
template <typename T, typename U> \
|
||||
struct template_name<T, U, empty_base<T>, true_t> : template_name##1 < T \
|
||||
, U > \
|
||||
{}; \
|
||||
\
|
||||
template <typename T, typename B> \
|
||||
struct template_name<T, T, B, false_t> : template_name##1 < T \
|
||||
, B > \
|
||||
{}; \
|
||||
\
|
||||
template <typename T, typename U, typename B, typename O> \
|
||||
struct is_chained_base<template_name<T, U, B, O>> \
|
||||
{ \
|
||||
using value = ::rocprofiler::common::container::true_t; \
|
||||
}; \
|
||||
\
|
||||
ROCPROFILER_OPERATOR_TEMPLATE2(template_name##2) \
|
||||
ROCPROFILER_OPERATOR_TEMPLATE1(template_name##1)
|
||||
|
||||
#define ROCPROFILER_BINARY_OPERATOR_COMMUTATIVE(NAME, OP) \
|
||||
template <typename T, typename U, typename B = empty_base<T>> \
|
||||
struct NAME##2 : B{friend T operator OP(T lhs, const U& rhs){return lhs OP## = rhs; \
|
||||
} \
|
||||
friend T operator OP(const U& lhs, T rhs) { return rhs OP## = lhs; } \
|
||||
} \
|
||||
; \
|
||||
\
|
||||
template <typename T, typename B = empty_base<T>> \
|
||||
struct NAME##1 : B{friend T operator OP(T lhs, const T& rhs){return lhs OP## = rhs; \
|
||||
} \
|
||||
} \
|
||||
;
|
||||
|
||||
#define ROCPROFILER_BINARY_OPERATOR_NON_COMMUTATIVE(NAME, OP) \
|
||||
template <typename T, typename U, typename B = empty_base<T>> \
|
||||
struct NAME##2 : B{friend T operator OP(T lhs, const U& rhs){return lhs OP## = rhs; \
|
||||
} \
|
||||
} \
|
||||
;
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
struct true_t
|
||||
{};
|
||||
|
||||
struct false_t
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
class empty_base
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
struct is_chained_base
|
||||
{
|
||||
using value = true_t;
|
||||
};
|
||||
|
||||
ROCPROFILER_BINARY_OPERATOR_COMMUTATIVE(addable, +)
|
||||
ROCPROFILER_BINARY_OPERATOR_NON_COMMUTATIVE(subtractable, -)
|
||||
|
||||
ROCPROFILER_OPERATOR_TEMPLATE(addable)
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct incrementable : B
|
||||
{
|
||||
friend T operator++(T& x, int)
|
||||
{
|
||||
incrementable_type nrv(x);
|
||||
++x;
|
||||
return nrv;
|
||||
}
|
||||
|
||||
private: // The use of this typedef works around a Borland bug
|
||||
typedef T incrementable_type;
|
||||
};
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct decrementable : B
|
||||
{
|
||||
friend T operator--(T& x, int)
|
||||
{
|
||||
decrementable_type nrv(x);
|
||||
--x;
|
||||
return nrv;
|
||||
}
|
||||
|
||||
private: // The use of this typedef works around a Borland bug
|
||||
typedef T decrementable_type;
|
||||
};
|
||||
|
||||
template <typename T, typename P, typename B = empty_base<T>>
|
||||
struct dereferenceable : B
|
||||
{
|
||||
P operator->() const { return ::std::addressof(*static_cast<const T&>(*this)); }
|
||||
};
|
||||
|
||||
template <typename T, typename N, typename R, typename B = empty_base<T>>
|
||||
struct indexable : B
|
||||
{
|
||||
R operator[](N n) const { return *(static_cast<const T&>(*this) + n); }
|
||||
};
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct equality_comparable1 : B
|
||||
{
|
||||
friend bool operator!=(const T& x, const T& y) { return !static_cast<bool>(x == y); }
|
||||
};
|
||||
|
||||
template <typename T, typename P, typename B = empty_base<T>>
|
||||
struct input_iteratable : equality_comparable1<T, incrementable<T, dereferenceable<T, P, B>>>
|
||||
{};
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct output_iteratable : incrementable<T, B>
|
||||
{};
|
||||
|
||||
template <typename T, typename P, typename B = empty_base<T>>
|
||||
struct forward_iteratable : input_iteratable<T, P, B>
|
||||
{};
|
||||
|
||||
template <typename T, typename P, typename B = empty_base<T>>
|
||||
struct bidirectional_iteratable : forward_iteratable<T, P, decrementable<T, B>>
|
||||
{};
|
||||
|
||||
// template <typename T, typename U, typename B = empty_base<T>>
|
||||
// struct subtractable2;
|
||||
|
||||
template <typename T, typename U, typename B = empty_base<T>>
|
||||
struct additive2 : addable2<T, U, subtractable2<T, U, B>>
|
||||
{};
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct less_than_comparable1 : B
|
||||
{
|
||||
friend bool operator>(const T& x, const T& y) { return y < x; }
|
||||
friend bool operator<=(const T& x, const T& y) { return !static_cast<bool>(y < x); }
|
||||
friend bool operator>=(const T& x, const T& y) { return !static_cast<bool>(x < y); }
|
||||
};
|
||||
|
||||
// To avoid repeated derivation from equality_comparable,
|
||||
// which is an indirect base typename of bidirectional_iterable,
|
||||
// random_access_iteratable must not be derived from totally_ordered1
|
||||
// but from less_than_comparable1 only. (Helmut Zeisel, 02-Dec-2001)
|
||||
template <typename T, typename P, typename D, typename R, typename B = empty_base<T>>
|
||||
struct random_access_iteratable
|
||||
: bidirectional_iteratable<T, P, less_than_comparable1<T, additive2<T, D, indexable<T, D, R, B>>>>
|
||||
{};
|
||||
|
||||
template <typename CategoryT,
|
||||
typename Tp,
|
||||
typename DistanceT = std::ptrdiff_t,
|
||||
typename PointerT = Tp*,
|
||||
typename ReferenceT = Tp&>
|
||||
struct iterator_helper
|
||||
{
|
||||
using iterator_category = CategoryT;
|
||||
using value_type = Tp;
|
||||
using difference_type = DistanceT;
|
||||
using pointer = PointerT;
|
||||
using reference = ReferenceT;
|
||||
};
|
||||
|
||||
template <typename T, typename V, typename D = std::ptrdiff_t, typename P = V*, typename R = V&>
|
||||
struct random_access_iterator_helper
|
||||
: random_access_iteratable<T, P, D, R, iterator_helper<std::random_access_iterator_tag, V, D, P, R>>
|
||||
{
|
||||
friend D requires_difference_operator(const T& x, const T& y) { return x - y; }
|
||||
}; // random_access_iterator_helper
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,173 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/container/record_header_buffer.hpp"
|
||||
|
||||
#include <rocprofiler-sdk/fwd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <new>
|
||||
|
||||
namespace rocprofiler::common::container
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// record_header_buffer RAII locker
|
||||
struct rhb_raii_lock
|
||||
{
|
||||
explicit rhb_raii_lock(record_header_buffer& _rhb)
|
||||
: m_rhb{_rhb}
|
||||
{
|
||||
m_rhb.lock();
|
||||
}
|
||||
|
||||
~rhb_raii_lock() { m_rhb.unlock(); }
|
||||
|
||||
record_header_buffer& m_rhb;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
record_header_buffer::record_header_buffer(size_t num_bytes) { allocate(num_bytes); }
|
||||
|
||||
record_header_buffer::record_header_buffer(record_header_buffer&& _rhs) noexcept
|
||||
{
|
||||
this->operator=(std::move(_rhs));
|
||||
}
|
||||
|
||||
record_header_buffer&
|
||||
record_header_buffer::operator=(record_header_buffer&& _rhs) noexcept
|
||||
{
|
||||
if(this != &_rhs)
|
||||
{
|
||||
auto _lk = rhb_raii_lock{_rhs};
|
||||
m_index = _rhs.m_index.load(std::memory_order_acquire);
|
||||
m_buffer = std::move(_rhs.m_buffer);
|
||||
m_headers = std::move(_rhs.m_headers);
|
||||
_rhs.reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
record_header_buffer::allocate(size_t num_bytes)
|
||||
{
|
||||
if(m_buffer.is_initialized()) return false;
|
||||
|
||||
auto _lk = rhb_raii_lock{*this};
|
||||
m_buffer.init(num_bytes);
|
||||
rocprofiler_record_header_t record = {};
|
||||
record.hash = 0;
|
||||
record.payload = nullptr;
|
||||
m_headers.resize(m_buffer.capacity(), record);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
record_header_buffer::get_num_record_headers()
|
||||
{
|
||||
auto _lk = rhb_raii_lock{*this};
|
||||
|
||||
auto _size = m_index.load(std::memory_order_acquire);
|
||||
size_t _ret = 0;
|
||||
for(size_t i = 0; i < _size; ++i)
|
||||
{
|
||||
if(auto& itr = m_headers.at(i); itr.hash > 0 && itr.payload != nullptr) ++_ret;
|
||||
}
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
record_header_buffer::clear()
|
||||
{
|
||||
auto _lk = rhb_raii_lock{*this};
|
||||
|
||||
auto _n = m_index.load(std::memory_order_acquire);
|
||||
{
|
||||
auto _sz = m_buffer.capacity();
|
||||
if(!m_buffer.clear(std::nothrow_t{})) return 0;
|
||||
std::for_each(m_headers.begin(), m_headers.end(), [](auto& itr) {
|
||||
rocprofiler_record_header_t record = {};
|
||||
record.hash = 0;
|
||||
record.payload = nullptr;
|
||||
itr = record;
|
||||
});
|
||||
rocprofiler_record_header_t record = {};
|
||||
record.hash = 0;
|
||||
record.payload = nullptr;
|
||||
m_headers.resize(_sz, record);
|
||||
m_index.store(0, std::memory_order_release);
|
||||
}
|
||||
|
||||
return _n;
|
||||
}
|
||||
|
||||
size_t
|
||||
record_header_buffer::reset()
|
||||
{
|
||||
auto _lk = rhb_raii_lock{*this};
|
||||
|
||||
auto _n = m_index.load(std::memory_order_acquire);
|
||||
m_buffer.destroy();
|
||||
m_buffer.clear();
|
||||
m_headers.clear();
|
||||
m_index.store(0, std::memory_order_release);
|
||||
|
||||
return _n;
|
||||
}
|
||||
|
||||
void
|
||||
record_header_buffer::save(std::fstream& _fs)
|
||||
{
|
||||
auto _lk = rhb_raii_lock{*this};
|
||||
|
||||
auto _idx = m_index.load(std::memory_order_acquire);
|
||||
auto _sz = m_headers.size();
|
||||
_fs.write(reinterpret_cast<char*>(&_idx), sizeof(_idx));
|
||||
_fs.write(reinterpret_cast<char*>(&_sz), sizeof(_sz));
|
||||
_fs.write(reinterpret_cast<char*>(m_headers.data()), sizeof(rocprofiler_record_header_t) * _sz);
|
||||
m_buffer.save(_fs);
|
||||
}
|
||||
|
||||
void
|
||||
record_header_buffer::load(std::fstream& _fs)
|
||||
{
|
||||
auto _lk = rhb_raii_lock{*this};
|
||||
|
||||
{
|
||||
auto _idx = size_t{0};
|
||||
_fs.read(reinterpret_cast<char*>(&_idx), sizeof(_idx));
|
||||
m_index.store(_idx, std::memory_order_release);
|
||||
}
|
||||
|
||||
{
|
||||
auto _sz = size_t{0};
|
||||
_fs.read(reinterpret_cast<char*>(&_sz), sizeof(_sz));
|
||||
m_headers.resize(_sz);
|
||||
_fs.read(reinterpret_cast<char*>(m_headers.data()),
|
||||
sizeof(rocprofiler_record_header_t) * _sz);
|
||||
}
|
||||
|
||||
m_buffer.load(_fs);
|
||||
}
|
||||
} // namespace rocprofiler::common::container
|
||||
@@ -0,0 +1,362 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/container/ring_buffer.hpp"
|
||||
#include "lib/common/scope_destructor.hpp"
|
||||
|
||||
#include <rocprofiler-sdk/fwd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
/// @brief this struct stores all the record information in an ring_buffer.
|
||||
/// It is thread-safe to have multiple threads emplace records into the buffer.
|
||||
struct record_header_buffer
|
||||
{
|
||||
using base_buffer_t = base::ring_buffer;
|
||||
using record_vec_t = std::vector<rocprofiler_record_header_t>;
|
||||
using record_ptr_vec_t = std::vector<rocprofiler_record_header_t*>;
|
||||
|
||||
record_header_buffer() = default;
|
||||
explicit record_header_buffer(size_t nbytes);
|
||||
~record_header_buffer() = default;
|
||||
|
||||
record_header_buffer(const record_header_buffer&) = delete;
|
||||
record_header_buffer(record_header_buffer&&) noexcept;
|
||||
|
||||
record_header_buffer& operator=(const record_header_buffer&) = delete;
|
||||
record_header_buffer& operator =(record_header_buffer&&) noexcept;
|
||||
|
||||
// allocate the buffer if it is not already allocated. Will return false if buffer is already
|
||||
// allocated
|
||||
bool allocate(size_t nbytes);
|
||||
|
||||
// return whether the buffer has been allocated
|
||||
bool is_allocated() const;
|
||||
|
||||
/// place an object in the buffer using its typeid hash code
|
||||
template <typename Tp>
|
||||
bool emplace(Tp&);
|
||||
|
||||
/// place an object in the buffer using the specified numerical identifier
|
||||
template <typename Tp>
|
||||
bool emplace(uint64_t, Tp&);
|
||||
|
||||
/// place an object in the buffer using the specified numerical identifier
|
||||
template <typename Tp>
|
||||
bool emplace(uint32_t, uint32_t, Tp&);
|
||||
|
||||
/// this function will return the number of record headers
|
||||
size_t get_num_record_headers();
|
||||
|
||||
/// this function will invoke functor with a vector of pointers to the record headers.
|
||||
/// if ClearRecordsV is true, the container will be cleared after invoking the functor
|
||||
template <typename ClearRecordsT, typename FuncT, typename... Args>
|
||||
size_t process_record_headers(ClearRecordsT, FuncT&& functor, Args&&... args);
|
||||
|
||||
/// record_header_buffer is a multiple writer, single reader data structure so
|
||||
/// this function prevents writing via emplace
|
||||
void lock();
|
||||
|
||||
/// potentially re-enable emplace if no other readers have locked
|
||||
void unlock();
|
||||
|
||||
/// record_header_buffer is a multiple writer, single reader data structure so
|
||||
/// this function prevents reading while emplacing
|
||||
void read_lock();
|
||||
|
||||
/// potentially allow reading after writing via emplace
|
||||
void read_unlock();
|
||||
|
||||
/// check if writing is available
|
||||
bool is_locked() const;
|
||||
|
||||
/// restores to original empty state
|
||||
size_t clear();
|
||||
|
||||
/// binary save to file
|
||||
void save(std::fstream& _fs);
|
||||
|
||||
/// binary load from file
|
||||
void load(std::fstream& _fs);
|
||||
|
||||
/// full deallocation
|
||||
size_t reset();
|
||||
|
||||
/// the number of header entries
|
||||
auto size() const;
|
||||
|
||||
/// the number of bytes in the buffer
|
||||
auto capacity() const;
|
||||
|
||||
/// the number of used bytes in the buffer
|
||||
auto count() const;
|
||||
|
||||
/// the number of free bytes in the buffer
|
||||
auto free() const;
|
||||
|
||||
/// true if no bytes are used in the buffer
|
||||
auto is_empty() const;
|
||||
|
||||
/// true if all the bytes are used in the buffer or there is no buffer allocation
|
||||
auto is_full() const;
|
||||
|
||||
private:
|
||||
/// this is an explicit write lock that does not guard against deadlocking like lock()
|
||||
void write_lock();
|
||||
|
||||
/// this is an explicit write unlock that does not guard against deadlocking like unlock()
|
||||
void write_unlock();
|
||||
|
||||
private:
|
||||
std::atomic<int64_t> m_requested = {0};
|
||||
std::atomic<int64_t> m_locked = {0};
|
||||
std::atomic<size_t> m_index = {};
|
||||
std::shared_mutex m_shared = {};
|
||||
base_buffer_t m_buffer = {};
|
||||
record_vec_t m_headers = {};
|
||||
};
|
||||
|
||||
inline bool
|
||||
record_header_buffer::is_locked() const
|
||||
{
|
||||
return m_locked.load(std::memory_order_acquire) > 0;
|
||||
}
|
||||
|
||||
inline void
|
||||
record_header_buffer::lock()
|
||||
{
|
||||
auto n = m_locked.fetch_add(1, std::memory_order_release);
|
||||
if(n == 0) write_lock();
|
||||
}
|
||||
|
||||
inline void
|
||||
record_header_buffer::unlock()
|
||||
{
|
||||
auto n = m_locked.fetch_sub(1, std::memory_order_release);
|
||||
if(n <= 1) write_unlock();
|
||||
}
|
||||
|
||||
inline void
|
||||
record_header_buffer::read_lock()
|
||||
{
|
||||
m_shared.lock_shared();
|
||||
}
|
||||
|
||||
inline void
|
||||
record_header_buffer::read_unlock()
|
||||
{
|
||||
m_shared.unlock_shared();
|
||||
}
|
||||
|
||||
inline void
|
||||
record_header_buffer::write_lock()
|
||||
{
|
||||
m_shared.lock();
|
||||
}
|
||||
|
||||
inline void
|
||||
record_header_buffer::write_unlock()
|
||||
{
|
||||
m_shared.unlock();
|
||||
}
|
||||
|
||||
inline bool
|
||||
record_header_buffer::is_allocated() const
|
||||
{
|
||||
return m_buffer.is_initialized();
|
||||
}
|
||||
|
||||
inline auto
|
||||
record_header_buffer::size() const
|
||||
{
|
||||
return m_index.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
inline auto
|
||||
record_header_buffer::capacity() const
|
||||
{
|
||||
return std::min<size_t>(m_headers.size(), m_buffer.capacity());
|
||||
}
|
||||
|
||||
inline auto
|
||||
record_header_buffer::count() const
|
||||
{
|
||||
return m_buffer.count();
|
||||
}
|
||||
|
||||
inline auto
|
||||
record_header_buffer::free() const
|
||||
{
|
||||
return m_buffer.free();
|
||||
}
|
||||
|
||||
inline auto
|
||||
record_header_buffer::is_empty() const
|
||||
{
|
||||
return (m_buffer.is_empty() && m_requested.load() == 0) || m_headers.empty();
|
||||
}
|
||||
|
||||
inline auto
|
||||
record_header_buffer::is_full() const
|
||||
{
|
||||
return m_buffer.is_full() || size() == m_headers.size();
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
record_header_buffer::emplace(uint64_t _hash, Tp& _v)
|
||||
{
|
||||
if(m_headers.empty()) return false;
|
||||
|
||||
constexpr auto request_size = sizeof(Tp);
|
||||
constexpr auto align_size = alignof(Tp);
|
||||
|
||||
// notify there was a request
|
||||
m_requested.fetch_add(1);
|
||||
|
||||
// in theory, we shouldn't need to lock here but the thread sanitizer says there is a race.
|
||||
// the lock will be short-lived so hopefully, it will scale fine
|
||||
write_lock();
|
||||
auto* _addr = m_buffer.request(request_size, align_size, false);
|
||||
write_unlock();
|
||||
|
||||
read_lock();
|
||||
if(_addr)
|
||||
{
|
||||
// if there is space in the buffer, atomically get an index
|
||||
// for where the header record should be placed.
|
||||
// NOTE: m_headers was resized to be large enough to accomodate
|
||||
// sizeof(Tp) == 1 for every entry in buffer
|
||||
auto idx = m_index.fetch_add(1, std::memory_order_release);
|
||||
|
||||
// placement new
|
||||
new(_addr) Tp{_v};
|
||||
|
||||
auto record = rocprofiler_record_header_t{};
|
||||
record.hash = _hash;
|
||||
record.payload = _addr;
|
||||
m_headers.at(idx) = record;
|
||||
}
|
||||
read_unlock();
|
||||
|
||||
// remove notification of request
|
||||
m_requested.fetch_sub(1);
|
||||
|
||||
return (_addr != nullptr);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
record_header_buffer::emplace(uint32_t _category, uint32_t _kind, Tp& _v)
|
||||
{
|
||||
if(m_headers.empty()) return false;
|
||||
|
||||
constexpr auto request_size = sizeof(Tp);
|
||||
constexpr auto align_size = alignof(Tp);
|
||||
|
||||
// notify there was a request
|
||||
m_requested.fetch_add(1);
|
||||
|
||||
// in theory, we shouldn't need to lock here but the thread sanitizer says there is a race.
|
||||
// the lock will be short-lived so hopefully, it will scale fine
|
||||
write_lock();
|
||||
auto* _addr = m_buffer.request(request_size, align_size, false);
|
||||
write_unlock();
|
||||
|
||||
read_lock();
|
||||
if(_addr)
|
||||
{
|
||||
// if there is space in the buffer, atomically get an index
|
||||
// for where the header record should be placed.
|
||||
// NOTE: m_headers was resized to be large enough to accomodate
|
||||
// sizeof(Tp) == 1 for every entry in buffer
|
||||
auto idx = m_index.fetch_add(1, std::memory_order_release);
|
||||
|
||||
// placement new
|
||||
new(_addr) Tp{_v};
|
||||
|
||||
auto record = rocprofiler_record_header_t{};
|
||||
record.category = _category;
|
||||
record.kind = _kind;
|
||||
record.payload = _addr;
|
||||
m_headers.at(idx) = record;
|
||||
}
|
||||
read_unlock();
|
||||
|
||||
// remove notification of request
|
||||
m_requested.fetch_sub(1);
|
||||
|
||||
return (_addr != nullptr);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
bool
|
||||
record_header_buffer::emplace(Tp& _v)
|
||||
{
|
||||
// if enumerations are not used, use the typeid hash code
|
||||
return emplace(typeid(Tp).hash_code(), _v);
|
||||
}
|
||||
|
||||
template <typename ClearRecordsT, typename FuncT, typename... Args>
|
||||
size_t
|
||||
record_header_buffer::process_record_headers(ClearRecordsT, FuncT&& _functor, Args&&... _args)
|
||||
{
|
||||
// RAII for lock/unlock
|
||||
auto _lk = scope_destructor{[&]() { unlock(); }, [&]() { lock(); }};
|
||||
|
||||
auto _n = m_index.load(std::memory_order_acquire);
|
||||
auto _records = record_ptr_vec_t{};
|
||||
_records.reserve(_n);
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
if(auto& itr = m_headers.at(i); itr.hash > 0 && itr.payload != nullptr)
|
||||
_records.emplace_back(&itr);
|
||||
}
|
||||
|
||||
// get number of records before vector is moved
|
||||
auto _num_records = _records.size();
|
||||
|
||||
// invoke the callback
|
||||
std::forward<FuncT>(_functor)(std::move(_records), std::forward<Args>(_args)...);
|
||||
|
||||
// clear the container
|
||||
if constexpr(ClearRecordsT::value) clear();
|
||||
|
||||
return _num_records;
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,279 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "ring_buffer.hpp"
|
||||
#include "lib/common/environment.hpp"
|
||||
#include "lib/common/units.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
namespace base
|
||||
{
|
||||
ring_buffer::~ring_buffer() { destroy(); }
|
||||
|
||||
ring_buffer::ring_buffer(ring_buffer&& rhs) noexcept
|
||||
: m_init{rhs.m_init}
|
||||
, m_ptr{rhs.m_ptr}
|
||||
, m_size{rhs.m_size}
|
||||
, m_read_count{rhs.m_read_count.load()}
|
||||
, m_write_count{rhs.m_write_count.load()}
|
||||
{
|
||||
rhs.reset();
|
||||
}
|
||||
|
||||
ring_buffer&
|
||||
ring_buffer::operator=(ring_buffer&& rhs) noexcept
|
||||
{
|
||||
if(this == &rhs) return *this;
|
||||
destroy();
|
||||
m_init = rhs.m_init;
|
||||
m_ptr = rhs.m_ptr;
|
||||
m_size = rhs.m_size;
|
||||
m_read_count = rhs.m_read_count.load();
|
||||
m_write_count = rhs.m_write_count.load();
|
||||
rhs.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
ring_buffer::init(size_t _size)
|
||||
{
|
||||
ROCP_FATAL_IF(m_init)
|
||||
<< "rocprofiler::common::container::base::ring_buffer::init(size_t) :: already initialized";
|
||||
|
||||
m_init = true;
|
||||
|
||||
// Round up to multiple of page size.
|
||||
_size += units::get_page_size() - ((_size % units::get_page_size() > 0)
|
||||
? (_size % units::get_page_size())
|
||||
: units::get_page_size());
|
||||
|
||||
if((_size % units::get_page_size()) > 0)
|
||||
{
|
||||
std::ostringstream _oss{};
|
||||
ROCP_FATAL << fmt::format("Error! size is not a multiple of page size: {} % {} = {}",
|
||||
_size,
|
||||
units::get_page_size(),
|
||||
(_size % units::get_page_size()));
|
||||
}
|
||||
|
||||
m_size = _size;
|
||||
m_read_count = 0;
|
||||
m_write_count = 0;
|
||||
|
||||
// Map twice the buffer size.
|
||||
if((m_ptr =
|
||||
mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) ==
|
||||
MAP_FAILED)
|
||||
{
|
||||
destroy();
|
||||
auto _err = errno;
|
||||
ROCP_FATAL << fmt::format("mmap failed with errno {} :: {}", _err, strerror(_err));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ring_buffer::destroy()
|
||||
{
|
||||
if(m_ptr && m_init)
|
||||
{
|
||||
// Unmap the mapped virtual memmory.
|
||||
auto ret = munmap(m_ptr, m_size);
|
||||
if(ret != 0) perror("ring_buffer: munmap failed");
|
||||
}
|
||||
m_init = false;
|
||||
m_size = 0;
|
||||
m_read_count = 0;
|
||||
m_write_count = 0;
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
std::string
|
||||
ring_buffer::as_string() const
|
||||
{
|
||||
std::ostringstream ss{};
|
||||
ss << std::boolalpha << "is_initialized: " << is_initialized() << ", capacity: " << capacity()
|
||||
<< ", count: " << count() << ", free: " << free() << ", is_empty: " << is_empty()
|
||||
<< ", is_full: " << is_full() << ", pointer: " << m_ptr << ", read count: " << m_read_count
|
||||
<< ", write count: " << m_write_count;
|
||||
return ss.str();
|
||||
}
|
||||
//
|
||||
|
||||
void*
|
||||
ring_buffer::request(size_t _length, size_t _align, bool _wrap)
|
||||
{
|
||||
if(m_ptr == nullptr || m_size == 0) return nullptr;
|
||||
|
||||
if(is_full()) return (_wrap) ? retrieve(_length, _align) : nullptr;
|
||||
|
||||
LOG_IF(FATAL, _align == 0) << "alignment must be non-zero";
|
||||
|
||||
// if write count is at the tail of buffer, bump to the end of buffer
|
||||
size_t _write_count = 0;
|
||||
size_t _offset = 0;
|
||||
size_t _write_pos = 0;
|
||||
do
|
||||
{
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
if(_length > free()) return nullptr;
|
||||
_offset = 0;
|
||||
_write_count = m_write_count.load(std::memory_order_acquire);
|
||||
auto _modulo = m_size - (_write_count % m_size);
|
||||
if(_modulo < _length) _offset = _modulo;
|
||||
auto _align_modulo = (_write_count % _align);
|
||||
auto _align_offset = (_align_modulo > 0) ? (_align - _align_modulo) : 0;
|
||||
_write_pos = _write_count + _align_offset;
|
||||
} while(!m_write_count.compare_exchange_strong(
|
||||
_write_count, _write_pos + _length + _offset, std::memory_order_seq_cst));
|
||||
|
||||
// pointer in buffer
|
||||
void* _out = write_ptr(_write_pos);
|
||||
|
||||
return _out;
|
||||
}
|
||||
//
|
||||
|
||||
void*
|
||||
ring_buffer::retrieve(size_t _length, size_t _align) const
|
||||
{
|
||||
if(m_ptr == nullptr || m_size == 0) return nullptr;
|
||||
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
|
||||
// if read count is at the tail of buffer, bump to the end of buffer
|
||||
size_t _read_count = 0;
|
||||
size_t _offset = 0;
|
||||
size_t _read_pos = 0;
|
||||
do
|
||||
{
|
||||
if(_length > count()) return nullptr;
|
||||
_offset = 0;
|
||||
_read_count = m_read_count.load(std::memory_order_acquire);
|
||||
auto _modulo = m_size - (_read_count % m_size);
|
||||
if(_modulo < _length) _offset = _modulo;
|
||||
auto _align_modulo = (_read_count % _align);
|
||||
auto _align_offset = (_align_modulo > 0) ? (_align - _align_modulo) : 0;
|
||||
_read_pos = _read_count + _align_offset;
|
||||
} while(!m_read_count.compare_exchange_strong(
|
||||
_read_count, _read_pos + _length + _offset, std::memory_order_seq_cst));
|
||||
|
||||
// pointer in buffer
|
||||
void* _out = read_ptr(_read_pos);
|
||||
|
||||
return _out;
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
ring_buffer::reset()
|
||||
{
|
||||
m_init = false;
|
||||
m_size = 0;
|
||||
m_ptr = nullptr;
|
||||
m_read_count.store(0);
|
||||
m_write_count.store(0);
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
ring_buffer::save(std::fstream& _fs)
|
||||
{
|
||||
auto _read_count = m_read_count.load();
|
||||
auto _write_count = m_write_count.load();
|
||||
_fs.write(reinterpret_cast<char*>(&m_size), sizeof(m_size));
|
||||
_fs.write(reinterpret_cast<char*>(&_read_count), sizeof(_read_count));
|
||||
_fs.write(reinterpret_cast<char*>(&_write_count), sizeof(_write_count));
|
||||
_fs.write(reinterpret_cast<char*>(m_ptr), m_size * sizeof(char));
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
ring_buffer::load(std::fstream& _fs)
|
||||
{
|
||||
destroy();
|
||||
|
||||
size_t _read_count = 0;
|
||||
size_t _write_count = 0;
|
||||
size_t _size = 0;
|
||||
|
||||
_fs.read(reinterpret_cast<char*>(&_size), sizeof(_size));
|
||||
|
||||
init(_size);
|
||||
|
||||
if(!m_ptr) throw std::bad_alloc{};
|
||||
|
||||
_fs.read(reinterpret_cast<char*>(&_read_count), sizeof(_read_count));
|
||||
_fs.read(reinterpret_cast<char*>(&_write_count), sizeof(_write_count));
|
||||
_fs.read(reinterpret_cast<char*>(m_ptr), m_size * sizeof(char));
|
||||
|
||||
m_read_count.store(_read_count, std::memory_order_release);
|
||||
m_write_count.store(_write_count, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool
|
||||
ring_buffer::can_clear() const
|
||||
{
|
||||
auto _read_count = m_read_count.load(std::memory_order_acquire);
|
||||
return (_read_count == 0);
|
||||
}
|
||||
|
||||
bool
|
||||
ring_buffer::clear()
|
||||
{
|
||||
ROCP_CI_LOG_IF(WARNING, !can_clear())
|
||||
<< "ring_buffer does not permit invoking clear() member function when the read pointer is "
|
||||
"non-zero because this introduces thread-safety issues";
|
||||
|
||||
m_write_count.store(0, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ring_buffer::clear(std::nothrow_t)
|
||||
{
|
||||
if(!can_clear()) return false;
|
||||
|
||||
m_write_count.store(0, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
} // namespace base
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,451 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/units.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <new>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
template <typename Tp>
|
||||
struct ring_buffer;
|
||||
//
|
||||
namespace base
|
||||
{
|
||||
/// \struct rocprofiler::common::container::base::ring_buffer
|
||||
/// \brief Ring buffer implementation, with support for mmap as backend (Linux only).
|
||||
struct ring_buffer
|
||||
{
|
||||
template <typename Tp>
|
||||
friend struct container::ring_buffer;
|
||||
|
||||
ring_buffer() = default;
|
||||
explicit ring_buffer(size_t _size) { init(_size); }
|
||||
|
||||
~ring_buffer();
|
||||
|
||||
ring_buffer(ring_buffer&&) noexcept;
|
||||
ring_buffer& operator=(ring_buffer&&) noexcept;
|
||||
|
||||
/// Returns whether the buffer has been allocated
|
||||
bool is_initialized() const { return m_init; }
|
||||
|
||||
/// Get the total number of bytes supported
|
||||
size_t capacity() const { return m_size; }
|
||||
|
||||
/// Creates new ring buffer.
|
||||
void init(size_t size);
|
||||
|
||||
/// Destroy ring buffer.
|
||||
void destroy();
|
||||
|
||||
/// Request a pointer for writing at least \param n bytes. If the current write pointer is not
|
||||
/// perfectly divisible by \param align (i.e. if write_addr % align != 0), the returned address
|
||||
/// will be shifted to an address that is a multiple of that alignment to prevent undefined
|
||||
/// behavior.
|
||||
void* request(size_t n, size_t align, bool wrap = true);
|
||||
|
||||
/// Retrieve a pointer for reading at least \param n bytes. If the current read pointer is not
|
||||
/// perfectly divisible by \param align (i.e. if read_addr % align != 0), the returned address
|
||||
/// will be shifted to an address that is a multiple of that alignment to prevent undefined
|
||||
/// behavior.
|
||||
void* retrieve(size_t n, size_t align) const;
|
||||
|
||||
/// Write class-type data to buffer (uses placement new).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> write(Tp* in, std::enable_if_t<std::is_class<Tp>::value, int> = 0);
|
||||
|
||||
/// Write non-class-type data to buffer (uses memcpy).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> write(Tp* in, std::enable_if_t<!std::is_class<Tp>::value, int> = 0);
|
||||
|
||||
/// Request a pointer to an allocation. This is similar to a "write" except the
|
||||
/// memory is uninitialized. Typically used by allocators. If Tp is a class type,
|
||||
/// be sure to use a placement new instead of a memcpy.
|
||||
template <typename Tp>
|
||||
Tp* request(bool wrap = true);
|
||||
|
||||
/// Read class-type data from buffer (uses placement new).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> read(Tp* _dest,
|
||||
std::enable_if_t<std::is_class<Tp>::value, int> = 0) const;
|
||||
|
||||
/// Read non-class-type data from buffer (uses memcpy).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> read(Tp* _dest,
|
||||
std::enable_if_t<!std::is_class<Tp>::value, int> = 0) const;
|
||||
|
||||
/// Retrieve a pointer to the head allocation (read).
|
||||
template <typename Tp>
|
||||
Tp* retrieve() const;
|
||||
|
||||
/// Returns number of bytes currently held by the buffer.
|
||||
size_t count() const { return (m_write_count - m_read_count); }
|
||||
|
||||
/// Returns how many bytes are availiable in the buffer.
|
||||
size_t free() const { return (m_size - count()); }
|
||||
|
||||
/// Returns if the buffer is empty.
|
||||
bool is_empty() const { return (count() == 0); }
|
||||
|
||||
/// Returns if the buffer is full.
|
||||
bool is_full() const { return (count() == m_size); }
|
||||
|
||||
/// Display info about buffer
|
||||
std::string as_string() const;
|
||||
|
||||
/// save the entire buffer to a filestream
|
||||
void save(std::fstream& _fs);
|
||||
|
||||
/// load the entire buffer from a filestream
|
||||
void load(std::fstream& _fs);
|
||||
|
||||
/// query whether the read pointer is zero and thus clearing is supported
|
||||
bool can_clear() const;
|
||||
|
||||
/// reset the read and write pointer to their initial values.
|
||||
/// effectively, wiping and existing memory. Please note,
|
||||
/// this should be used with care in a double buffer system
|
||||
/// where you are not actually using the read pointer.
|
||||
/// If the read pointer is non-zero, this will throw an exception
|
||||
bool clear();
|
||||
|
||||
/// reset the read and write pointer to their initial values.
|
||||
/// effectively, wiping and existing memory. Please note,
|
||||
/// this should be used with care in a double buffer system
|
||||
/// where you are not actually using the read pointer.
|
||||
bool clear(std::nothrow_t);
|
||||
|
||||
private:
|
||||
/// Returns the current write pointer.
|
||||
void* write_ptr(size_t _write_count) const
|
||||
{
|
||||
return static_cast<char*>(m_ptr) + (_write_count % m_size);
|
||||
}
|
||||
|
||||
/// Returns the current read pointer.
|
||||
void* read_ptr(size_t _read_count) const
|
||||
{
|
||||
return static_cast<char*>(m_ptr) + (_read_count % m_size);
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
bool m_init = false;
|
||||
void* m_ptr = nullptr;
|
||||
size_t m_size = 0;
|
||||
mutable std::atomic<size_t> m_read_count = 0;
|
||||
std::atomic<size_t> m_write_count = 0;
|
||||
};
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
ring_buffer::write(Tp* in, std::enable_if_t<std::is_class<Tp>::value, int>)
|
||||
{
|
||||
if(in == nullptr || m_ptr == nullptr) return {0, nullptr};
|
||||
|
||||
constexpr auto _length = sizeof(Tp);
|
||||
constexpr auto _align = alignof(Tp);
|
||||
void* _out_p = request(_length, _align, true);
|
||||
|
||||
if(_out_p == nullptr) return {0, nullptr};
|
||||
|
||||
// Copy in.
|
||||
new(_out_p) Tp{std::move(*in)};
|
||||
|
||||
// pointer in buffer
|
||||
Tp* _out = reinterpret_cast<Tp*>(_out_p);
|
||||
|
||||
return {_length, _out};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
ring_buffer::write(Tp* in, std::enable_if_t<!std::is_class<Tp>::value, int>)
|
||||
{
|
||||
if(in == nullptr || m_ptr == nullptr) return {0, nullptr};
|
||||
|
||||
constexpr auto _length = sizeof(Tp);
|
||||
constexpr auto _align = alignof(Tp);
|
||||
void* _out_p = request(_length, _align, true);
|
||||
|
||||
if(_out_p == nullptr) return {0, nullptr};
|
||||
|
||||
// Copy in.
|
||||
memcpy(_out_p, in, _length);
|
||||
|
||||
// pointer in buffer
|
||||
Tp* _out = reinterpret_cast<Tp*>(_out_p);
|
||||
|
||||
return {_length, _out};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
Tp*
|
||||
ring_buffer::request(bool wrap)
|
||||
{
|
||||
if(m_ptr == nullptr) return nullptr;
|
||||
|
||||
return reinterpret_cast<Tp*>(request(sizeof(Tp), alignof(Tp), wrap));
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
ring_buffer::read(Tp* _dest, std::enable_if_t<std::is_class<Tp>::value, int>) const
|
||||
{
|
||||
if(is_empty() || _dest == nullptr) return {0, nullptr};
|
||||
|
||||
constexpr auto _length = sizeof(Tp);
|
||||
constexpr auto _align = alignof(Tp);
|
||||
void* _out_p = retrieve(_length, _align);
|
||||
|
||||
if(_out_p == nullptr) return {0, nullptr};
|
||||
|
||||
// pointer in buffer
|
||||
Tp* in = reinterpret_cast<Tp*>(_out_p);
|
||||
|
||||
// Copy out for BYTE, nothing magic here.
|
||||
*_dest = *in;
|
||||
|
||||
return {_length, in};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
ring_buffer::read(Tp* _dest, std::enable_if_t<!std::is_class<Tp>::value, int>) const
|
||||
{
|
||||
if(is_empty() || _dest == nullptr) return {0, nullptr};
|
||||
|
||||
constexpr auto _length = sizeof(Tp);
|
||||
constexpr auto _align = alignof(Tp);
|
||||
void* _out_p = retrieve(_length, _align);
|
||||
|
||||
if(_out_p == nullptr) return {0, nullptr};
|
||||
|
||||
// pointer in buffer
|
||||
Tp* in = reinterpret_cast<Tp*>(_out_p);
|
||||
|
||||
using Up = typename std::remove_const<Tp>::type;
|
||||
|
||||
// Copy out for BYTE, nothing magic here.
|
||||
Up* _out = const_cast<Up*>(_dest);
|
||||
memcpy(_out, in, _length);
|
||||
|
||||
return {_length, in};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
Tp*
|
||||
ring_buffer::retrieve() const
|
||||
{
|
||||
if(m_ptr == nullptr) return nullptr;
|
||||
|
||||
return reinterpret_cast<Tp*>(retrieve(sizeof(Tp), alignof(Tp)));
|
||||
}
|
||||
//
|
||||
} // namespace base
|
||||
//
|
||||
/// \struct rocprofiler::common::container::ring_buffer
|
||||
/// \brief Ring buffer wrapper around \ref rocprofiler::common::container::base::ring_buffer for
|
||||
/// data of type Tp. If the data object size is larger than the page size (typically 4KB), behavior
|
||||
/// is undefined. During initialization, one requests a minimum number of objects and the buffer
|
||||
/// will support that number of object + the remainder of the page, e.g. if a page is 1000 bytes,
|
||||
/// the object is 1 byte, and the buffer is requested to support 1500 objects, then an allocation
|
||||
/// supporting 2000 objects (i.e. 2 pages) will be created.
|
||||
template <typename Tp>
|
||||
struct ring_buffer : private base::ring_buffer
|
||||
{
|
||||
using base_type = base::ring_buffer;
|
||||
using value_type = Tp;
|
||||
|
||||
static size_t get_items_per_page();
|
||||
|
||||
ring_buffer() = default;
|
||||
~ring_buffer() = default;
|
||||
|
||||
explicit ring_buffer(size_t _size)
|
||||
: base_type{_size * aligned_data_size()}
|
||||
{}
|
||||
|
||||
ring_buffer(const ring_buffer&);
|
||||
ring_buffer(ring_buffer&&) noexcept = default;
|
||||
|
||||
ring_buffer& operator=(const ring_buffer&);
|
||||
ring_buffer& operator=(ring_buffer&&) noexcept = default;
|
||||
|
||||
/// Returns whether the buffer has been allocated
|
||||
bool is_initialized() const { return base_type::is_initialized(); }
|
||||
|
||||
/// Get the total number of Tp instances supported
|
||||
size_t capacity() const { return (base_type::capacity()) / aligned_data_size(); }
|
||||
|
||||
/// Creates new ring buffer.
|
||||
void init(size_t _size) { base_type::init(_size * aligned_data_size()); }
|
||||
|
||||
/// Destroy ring buffer.
|
||||
void destroy() { base_type::destroy(); }
|
||||
|
||||
/// Size of the data type
|
||||
static constexpr size_t data_size() { return sizeof(Tp); }
|
||||
|
||||
/// Size of the data type + padding
|
||||
static constexpr size_t aligned_data_size();
|
||||
|
||||
/// Write data to buffer. Return pointer to location of write
|
||||
Tp* write(Tp* in) { return base_type::write<Tp>(in).second; }
|
||||
|
||||
/// Read data from buffer. Return pointer to location of read
|
||||
Tp* read(Tp* _dest) const { return base_type::read<Tp>(_dest).second; }
|
||||
|
||||
/// Get an uninitialized address at tail of buffer.
|
||||
Tp* request(bool wrap = true) { return base_type::request<Tp>(wrap); }
|
||||
|
||||
/// Read data from head of buffer.
|
||||
Tp* retrieve() { return base_type::retrieve<Tp>(); }
|
||||
|
||||
/// Returns number of Tp instances currently held by the buffer.
|
||||
size_t count() const { return (base_type::count()) / aligned_data_size(); }
|
||||
|
||||
/// Returns how many Tp instances are availiable in the buffer.
|
||||
size_t free() const { return (base_type::free()) / aligned_data_size(); }
|
||||
|
||||
/// Returns if the buffer is empty.
|
||||
bool is_empty() const { return base_type::is_empty(); }
|
||||
|
||||
/// Returns if the buffer is full.
|
||||
bool is_full() const { return (base_type::free() < aligned_data_size()); }
|
||||
|
||||
bool clear() { return base_type::clear(); }
|
||||
|
||||
template <typename... Args>
|
||||
auto emplace(Args&&... args)
|
||||
{
|
||||
Tp _obj{std::forward<Args>(args)...};
|
||||
return write(&_obj);
|
||||
}
|
||||
|
||||
using base_type::load;
|
||||
using base_type::save;
|
||||
|
||||
std::string as_string() const
|
||||
{
|
||||
std::ostringstream ss{};
|
||||
size_t _w = std::log10(base_type::capacity()) + 1;
|
||||
ss << std::boolalpha << std::right << "data size: " << std::setw(_w) << data_size()
|
||||
<< "B, aligned data size: " << std::setw(_w) << aligned_data_size()
|
||||
<< " B, is_initialized: " << std::setw(5) << is_initialized()
|
||||
<< ", is_empty: " << std::setw(5) << is_empty() << ", is_full: " << std::setw(5)
|
||||
<< is_full() << ", capacity: " << std::setw(_w) << capacity()
|
||||
<< ", count: " << std::setw(_w) << count() << ", free: " << std::setw(_w) << free()
|
||||
<< ", raw capacity: " << std::setw(_w) << base_type::capacity()
|
||||
<< " B, raw count: " << std::setw(_w) << base_type::count()
|
||||
<< " B, raw free: " << std::setw(_w) << base_type::free()
|
||||
<< " B, pointer: " << std::setw(15) << base_type::m_ptr
|
||||
<< ", raw read count: " << std::setw(_w) << base_type::m_read_count
|
||||
<< ", raw write count: " << std::setw(_w) << base_type::m_write_count;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const ring_buffer& obj)
|
||||
{
|
||||
return os << obj.as_string();
|
||||
}
|
||||
};
|
||||
//
|
||||
template <typename Tp>
|
||||
size_t
|
||||
ring_buffer<Tp>::get_items_per_page()
|
||||
{
|
||||
return std::max<size_t>(units::get_page_size() / sizeof(Tp), 1);
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
constexpr size_t
|
||||
ring_buffer<Tp>::aligned_data_size()
|
||||
{
|
||||
constexpr auto _data_size = sizeof(Tp);
|
||||
constexpr auto _data_align = alignof(Tp);
|
||||
constexpr auto _align_modulo = _data_size % _data_align;
|
||||
constexpr auto _result =
|
||||
(_align_modulo == 0) ? _data_size : (_data_size + (_data_align - _align_modulo));
|
||||
|
||||
static_assert(_result >= _data_size && _result < (_data_size + _data_align),
|
||||
"should neither be < sizeof(Tp) nor > sizeof(Tp) + alignof(Tp)");
|
||||
|
||||
return _result;
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
ring_buffer<Tp>::ring_buffer(const ring_buffer<Tp>& rhs)
|
||||
: base_type{rhs}
|
||||
{
|
||||
size_t _n = rhs.count();
|
||||
char* _end = static_cast<char*>(rhs.m_ptr) + rhs.m_size;
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
char* _addr = static_cast<char*>(rhs.read_ptr(m_read_count)) + (i * sizeof(Tp));
|
||||
if((_addr + sizeof(Tp)) > _end) _addr = static_cast<char*>(rhs.m_ptr);
|
||||
Tp* _in = static_cast<Tp*>(static_cast<void*>(_addr));
|
||||
write(_in);
|
||||
}
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
ring_buffer<Tp>&
|
||||
ring_buffer<Tp>::operator=(const ring_buffer<Tp>& rhs)
|
||||
{
|
||||
if(this == &rhs) return *this;
|
||||
|
||||
base_type::operator=(rhs);
|
||||
size_t _n = rhs.count();
|
||||
char* _end = static_cast<char*>(rhs.m_ptr) + rhs.m_size;
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
char* _addr = static_cast<char*>(rhs.read_ptr(m_read_count)) + (i * sizeof(Tp));
|
||||
if((_addr + sizeof(Tp)) > _end) _addr = static_cast<char*>(rhs.m_ptr);
|
||||
Tp* _in = static_cast<Tp*>(static_cast<void*>(_addr));
|
||||
write(_in);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
//
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,146 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/container/small_vector.hpp"
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
namespace
|
||||
{
|
||||
[[noreturn]] void
|
||||
report_size_overflow(size_t min_size, size_t max_size);
|
||||
|
||||
[[noreturn]] void
|
||||
report_at_maximum_capacity(size_t max_size);
|
||||
|
||||
/// Report that min_size doesn't fit into this vector's size type. Throws
|
||||
/// std::length_error
|
||||
void
|
||||
report_size_overflow(size_t min_size, size_t max_size)
|
||||
{
|
||||
std::string Reason =
|
||||
"small_vector unable to grow. Requested capacity (" + std::to_string(min_size) +
|
||||
") is larger than maximum value for size type (" + std::to_string(max_size) + ")";
|
||||
throw std::length_error(Reason);
|
||||
}
|
||||
|
||||
/// Report that this vector is already at maximum capacity. Throws
|
||||
/// std::length_error
|
||||
void
|
||||
report_at_maximum_capacity(size_t max_size)
|
||||
{
|
||||
std::string Reason =
|
||||
"small_vector capacity unable to grow. Already at maximum size " + std::to_string(max_size);
|
||||
throw std::length_error(Reason);
|
||||
}
|
||||
|
||||
template <typename SizeT>
|
||||
size_t
|
||||
get_new_capacity(size_t min_size, size_t /*t_size*/, size_t old_capacity)
|
||||
{
|
||||
constexpr size_t max_size = std::numeric_limits<SizeT>::max();
|
||||
|
||||
// Ensure we can fit the new capacity.
|
||||
// This is only going to be applicable when the capacity is 32 bit.
|
||||
if(min_size > max_size) report_size_overflow(min_size, max_size);
|
||||
|
||||
// Ensure we can meet the guarantee of space for at least one more element.
|
||||
// The above check alone will not catch the case where grow is called with a
|
||||
// default min_size of 0, but the current capacity cannot be increased.
|
||||
// This is only going to be applicable when the capacity is 32 bit.
|
||||
if(old_capacity == max_size) report_at_maximum_capacity(max_size);
|
||||
|
||||
// In theory 2*capacity can overflow if the capacity is 64 bit, but the
|
||||
// original capacity would never be large enough for this to be a problem.
|
||||
size_t new_capacity = (2 * old_capacity) + 1; // Always grow.
|
||||
return std::clamp(new_capacity, min_size, max_size);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename SizeT>
|
||||
void*
|
||||
small_vector_base<SizeT>::replace_allocation(void* new_elts,
|
||||
size_t t_size,
|
||||
size_t new_capacity,
|
||||
size_t v_size)
|
||||
{
|
||||
void* new_eltsReplace = ::malloc(new_capacity * t_size);
|
||||
if(v_size != 0u) memcpy(new_eltsReplace, new_elts, v_size * t_size);
|
||||
free(new_elts);
|
||||
return new_eltsReplace;
|
||||
}
|
||||
|
||||
// Note: Moving this function into the header may cause performance regression.
|
||||
template <typename SizeT>
|
||||
void*
|
||||
small_vector_base<SizeT>::malloc_for_grow(void* first_el,
|
||||
size_t min_size,
|
||||
size_t t_size,
|
||||
size_t& new_capacity)
|
||||
{
|
||||
new_capacity = get_new_capacity<SizeT>(min_size, t_size, this->capacity());
|
||||
// Even if capacity is not 0 now, if the vector was originally created with
|
||||
// capacity 0, it's possible for the malloc to return first_el.
|
||||
void* new_elts = ::malloc(new_capacity * t_size);
|
||||
if(new_elts == first_el) new_elts = replace_allocation(new_elts, t_size, new_capacity);
|
||||
return new_elts;
|
||||
}
|
||||
|
||||
// Note: Moving this function into the header may cause performance regression.
|
||||
template <typename SizeT>
|
||||
void
|
||||
small_vector_base<SizeT>::grow_pod(void* first_el, size_t min_size, size_t t_size)
|
||||
{
|
||||
size_t new_capacity = get_new_capacity<SizeT>(min_size, t_size, this->capacity());
|
||||
void* new_elts;
|
||||
if(m_begin_x == first_el)
|
||||
{
|
||||
new_elts = ::malloc(new_capacity * t_size);
|
||||
if(new_elts == first_el) new_elts = replace_allocation(new_elts, t_size, new_capacity);
|
||||
|
||||
// Copy the elements over. No need to run dtors on PODs.
|
||||
memcpy(new_elts, this->m_begin_x, size() * t_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this wasn't grown from the inline copy, grow the allocated space.
|
||||
new_elts = ::realloc(this->m_begin_x, new_capacity * t_size);
|
||||
if(new_elts == first_el)
|
||||
new_elts = replace_allocation(new_elts, t_size, new_capacity, size());
|
||||
}
|
||||
|
||||
this->m_begin_x = new_elts;
|
||||
this->m_capacity = new_capacity;
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
|
||||
// explicit instantiations
|
||||
template class rocprofiler::common::container::small_vector_base<uint32_t>;
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
template class rocprofiler::common::container::small_vector_base<uint64_t>;
|
||||
#endif
|
||||
Το diff αρχείου καταστέλλεται επειδή είναι πολύ μεγάλο
Φόρτωση Διαφορών
@@ -0,0 +1,407 @@
|
||||
// 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 "lib/common/container/operators.hpp"
|
||||
#include "lib/common/container/static_vector.hpp"
|
||||
#include "lib/common/defines.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
struct reserve_size
|
||||
{
|
||||
explicit reserve_size(size_t _v)
|
||||
: value{_v}
|
||||
{}
|
||||
|
||||
size_t value;
|
||||
};
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV = 64>
|
||||
class stable_vector
|
||||
{
|
||||
public:
|
||||
using value_type = Tp;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using size_type = size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
static constexpr const size_t chunk_size = ChunkSizeV;
|
||||
|
||||
private:
|
||||
template <size_t N>
|
||||
struct is_pow2
|
||||
{
|
||||
static constexpr bool value = (N & (N - 1)) == 0;
|
||||
};
|
||||
|
||||
static_assert(ChunkSizeV > 0, "ChunkSize needs to be greater than zero");
|
||||
static_assert(is_pow2<ChunkSizeV>::value, "ChunkSize needs to be a power of 2");
|
||||
|
||||
using this_type = stable_vector<Tp, ChunkSizeV>;
|
||||
using const_this_type = const stable_vector<Tp, ChunkSizeV>;
|
||||
|
||||
template <typename ContainerT>
|
||||
struct iterator_base
|
||||
{
|
||||
iterator_base(ContainerT* c = nullptr, size_type i = 0)
|
||||
: m_container(c)
|
||||
, m_index(i)
|
||||
{}
|
||||
|
||||
iterator_base& operator+=(size_type i)
|
||||
{
|
||||
m_index += i;
|
||||
return *this;
|
||||
}
|
||||
iterator_base& operator-=(size_type i)
|
||||
{
|
||||
m_index -= i;
|
||||
return *this;
|
||||
}
|
||||
iterator_base& operator++()
|
||||
{
|
||||
++m_index;
|
||||
return *this;
|
||||
}
|
||||
iterator_base& operator--()
|
||||
{
|
||||
--m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
difference_type operator-(const iterator_base& it)
|
||||
{
|
||||
assert(m_container == it.m_container);
|
||||
return m_index - it.m_index;
|
||||
}
|
||||
|
||||
bool operator<(const iterator_base& it) const
|
||||
{
|
||||
assert(m_container == it.m_container);
|
||||
return m_index < it.m_index;
|
||||
}
|
||||
bool operator==(const iterator_base& it) const
|
||||
{
|
||||
return m_container == it.m_container && m_index == it.m_index;
|
||||
}
|
||||
|
||||
protected:
|
||||
ContainerT* m_container;
|
||||
size_type m_index;
|
||||
};
|
||||
|
||||
public:
|
||||
struct const_iterator;
|
||||
|
||||
struct iterator
|
||||
: public iterator_base<this_type>
|
||||
//, std::iterator<std::random_access_iterator_tag, value_type>
|
||||
, public random_access_iterator_helper<iterator, value_type>
|
||||
{
|
||||
using iterator_base<this_type>::iterator_base;
|
||||
friend struct const_iterator;
|
||||
|
||||
reference operator*() { return (*this->m_container)[this->m_index]; }
|
||||
};
|
||||
|
||||
struct const_iterator
|
||||
: public iterator_base<const_this_type>
|
||||
//, std::iterator<std::random_access_iterator_tag, const value_type>
|
||||
, public random_access_iterator_helper<const_iterator, const value_type>
|
||||
{
|
||||
using iterator_base<const_this_type>::iterator_base;
|
||||
|
||||
explicit const_iterator(const iterator& it)
|
||||
: iterator_base<const_this_type>(it.m_container, it.m_index)
|
||||
{}
|
||||
|
||||
const_reference operator*() const { return (*this->m_container)[this->m_index]; }
|
||||
|
||||
bool operator==(const const_iterator& it) const
|
||||
{
|
||||
return iterator_base<const_this_type>::operator==(it);
|
||||
}
|
||||
|
||||
friend bool operator==(const iterator& l, const const_iterator& r) { return r == l; }
|
||||
};
|
||||
|
||||
stable_vector() = default;
|
||||
explicit stable_vector(size_type count, const Tp& value);
|
||||
explicit stable_vector(size_type count);
|
||||
explicit stable_vector(reserve_size&& reserve_count);
|
||||
|
||||
template <typename InputItrT,
|
||||
typename = std::enable_if_t<
|
||||
std::is_convertible<typename std::iterator_traits<InputItrT>::iterator_category,
|
||||
std::input_iterator_tag>::value>>
|
||||
stable_vector(InputItrT first, InputItrT last);
|
||||
|
||||
explicit stable_vector(std::initializer_list<Tp>);
|
||||
|
||||
stable_vector(const stable_vector& other);
|
||||
stable_vector(stable_vector&& other) noexcept;
|
||||
|
||||
stable_vector& operator=(stable_vector v);
|
||||
|
||||
iterator begin() noexcept { return {this, 0}; }
|
||||
const_iterator begin() const noexcept { return {this, 0}; }
|
||||
const_iterator cbegin() const noexcept { return begin(); }
|
||||
|
||||
iterator end() noexcept { return {this, size()}; }
|
||||
const_iterator end() const noexcept { return {this, size()}; }
|
||||
const_iterator cend() const noexcept { return end(); }
|
||||
|
||||
size_type size() const noexcept
|
||||
{
|
||||
return empty() ? 0 : (m_chunks.size() - 1) * ChunkSizeV + m_chunks.back()->size();
|
||||
}
|
||||
size_type max_size() const noexcept { return std::numeric_limits<size_type>::max(); }
|
||||
size_type capacity() const noexcept { return m_chunks.size() * ChunkSizeV; }
|
||||
|
||||
bool empty() const noexcept { return m_chunks.empty(); }
|
||||
|
||||
void reserve(size_type new_capacity);
|
||||
void shrink_to_fit() noexcept {}
|
||||
|
||||
bool operator==(const this_type& c) const
|
||||
{
|
||||
return size() == c.size() && std::equal(cbegin(), cend(), c.cbegin());
|
||||
}
|
||||
bool operator!=(const this_type& c) const { return !operator==(c); }
|
||||
|
||||
void swap(this_type& v) noexcept { std::swap(m_chunks, v.m_chunks); }
|
||||
|
||||
friend void swap(this_type& l, this_type& r) noexcept { l.swap(r); }
|
||||
|
||||
reference front() { return m_chunks.front()->front(); }
|
||||
const_reference front() const { return front(); }
|
||||
|
||||
reference back() { return m_chunks.back()->back(); }
|
||||
const_reference back() const { return back(); }
|
||||
|
||||
void push_back(const Tp& t);
|
||||
void push_back(Tp&& t);
|
||||
|
||||
template <typename... Args>
|
||||
reference emplace_back(Args&&... args);
|
||||
|
||||
reference operator[](size_type i);
|
||||
|
||||
const_reference operator[](size_type i) const;
|
||||
|
||||
reference at(size_type i);
|
||||
|
||||
const_reference at(size_type i) const;
|
||||
|
||||
private:
|
||||
using chunk_type = container::static_vector<Tp, ChunkSizeV, true>;
|
||||
using storage_type = std::vector<std::unique_ptr<chunk_type>>;
|
||||
|
||||
void add_chunk();
|
||||
chunk_type& last_chunk();
|
||||
|
||||
storage_type m_chunks;
|
||||
};
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(size_type count, const Tp& value)
|
||||
{
|
||||
for(size_type i = 0; i < count; ++i)
|
||||
{
|
||||
push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(size_type count)
|
||||
{
|
||||
for(size_type i = 0; i < count; ++i)
|
||||
{
|
||||
emplace_back();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(reserve_size&& reserve_count)
|
||||
{
|
||||
reserve(reserve_count.value);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
template <typename InputItrT, typename>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(InputItrT first, InputItrT last)
|
||||
{
|
||||
for(; first != last; ++first)
|
||||
{
|
||||
push_back(*first);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(const stable_vector& other)
|
||||
{
|
||||
for(const auto& chunk : other.m_chunks)
|
||||
{
|
||||
m_chunks.emplace_back(std::make_unique<chunk_type>(*chunk));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(stable_vector&& other) noexcept
|
||||
: m_chunks(std::move(other.m_chunks))
|
||||
{}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(std::initializer_list<Tp> ilist)
|
||||
{
|
||||
for(const auto& t : ilist)
|
||||
{
|
||||
push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>&
|
||||
stable_vector<Tp, ChunkSizeV>::operator=(stable_vector v)
|
||||
{
|
||||
swap(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::add_chunk()
|
||||
{
|
||||
m_chunks.emplace_back(std::make_unique<chunk_type>());
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::chunk_type&
|
||||
stable_vector<Tp, ChunkSizeV>::last_chunk()
|
||||
{
|
||||
if(ROCPROFILER_UNLIKELY(m_chunks.empty() || m_chunks.back()->size() == ChunkSizeV))
|
||||
{
|
||||
add_chunk();
|
||||
}
|
||||
|
||||
return *m_chunks.back();
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::reserve(size_type new_capacity)
|
||||
{
|
||||
const size_t initial_capacity = capacity();
|
||||
for(difference_type i = new_capacity - initial_capacity; i > 0; i -= ChunkSizeV)
|
||||
{
|
||||
add_chunk();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::push_back(const Tp& t)
|
||||
{
|
||||
last_chunk().push_back(t);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::push_back(Tp&& t)
|
||||
{
|
||||
last_chunk().push_back(std::move(t));
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
template <typename... Args>
|
||||
typename stable_vector<Tp, ChunkSizeV>::reference
|
||||
stable_vector<Tp, ChunkSizeV>::emplace_back(Args&&... args)
|
||||
{
|
||||
return last_chunk().emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::reference
|
||||
stable_vector<Tp, ChunkSizeV>::operator[](size_type i)
|
||||
{
|
||||
return (*m_chunks[i / ChunkSizeV])[i % ChunkSizeV];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::const_reference
|
||||
stable_vector<Tp, ChunkSizeV>::operator[](size_type i) const
|
||||
{
|
||||
return const_cast<this_type&>(*this)[i];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::reference
|
||||
stable_vector<Tp, ChunkSizeV>::at(size_type i)
|
||||
{
|
||||
if(ROCPROFILER_UNLIKELY(i >= size()))
|
||||
{
|
||||
throw std::out_of_range("stable_vector::at(" + std::to_string(i) + "). size is " +
|
||||
std::to_string(size()));
|
||||
}
|
||||
|
||||
return operator[](i);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::const_reference
|
||||
stable_vector<Tp, ChunkSizeV>::at(size_type i) const
|
||||
{
|
||||
return const_cast<this_type&>(*this).at(i);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV, typename... Args>
|
||||
auto
|
||||
resize(stable_vector<Tp, ChunkSizeV>& _v, size_t _n, Args&&... args)
|
||||
{
|
||||
if(_n > _v.capacity()) _v.reserve(_n);
|
||||
|
||||
while(_v.size() < _n)
|
||||
_v.emplace_back(std::forward<Args>(args)...);
|
||||
|
||||
return _v.size();
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,230 @@
|
||||
// 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 "lib/common/container/c_array.hpp"
|
||||
#include "lib/common/defines.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
template <typename Tp, size_t N, bool AtomicSizeV = false>
|
||||
struct static_vector
|
||||
{
|
||||
using count_type = std::conditional_t<AtomicSizeV, std::atomic<size_t>, size_t>;
|
||||
using this_type = static_vector<Tp, N>;
|
||||
using value_type = Tp;
|
||||
|
||||
static_vector() = default;
|
||||
static_vector(const static_vector&) = default;
|
||||
static_vector(static_vector&&) noexcept = default;
|
||||
static_vector& operator=(const static_vector&) = default;
|
||||
static_vector& operator=(static_vector&&) noexcept = default;
|
||||
|
||||
explicit static_vector(size_t _n, Tp _v = {});
|
||||
explicit static_vector(c_array<Tp>&&);
|
||||
|
||||
template <size_t M>
|
||||
explicit static_vector(std::array<Tp, M>&&);
|
||||
|
||||
static_vector& operator=(std::initializer_list<Tp>&& _v);
|
||||
static_vector& operator=(std::pair<std::array<Tp, N>, size_t>&&);
|
||||
|
||||
template <typename... Args>
|
||||
value_type& emplace_back(Args&&... _v);
|
||||
|
||||
template <typename Up>
|
||||
decltype(auto) push_back(Up&& _v)
|
||||
{
|
||||
return emplace_back(Tp{std::forward<Up>(_v)});
|
||||
}
|
||||
|
||||
void pop_back() { --m_size; }
|
||||
|
||||
void clear();
|
||||
void reserve(size_t) noexcept {}
|
||||
void shrink_to_fit() noexcept {}
|
||||
auto capacity() noexcept { return N; }
|
||||
|
||||
size_t size() const { return m_size; }
|
||||
bool empty() const { return (size() == 0); }
|
||||
|
||||
auto begin() { return m_data.begin(); }
|
||||
auto begin() const { return m_data.begin(); }
|
||||
auto cbegin() const { return m_data.cbegin(); }
|
||||
|
||||
auto end() { return m_data.begin() + size(); }
|
||||
auto end() const { return m_data.begin() + size(); }
|
||||
auto cend() const { return m_data.cbegin() + size(); }
|
||||
|
||||
decltype(auto) operator[](size_t _idx) { return m_data[_idx]; }
|
||||
decltype(auto) operator[](size_t _idx) const { return m_data[_idx]; }
|
||||
|
||||
decltype(auto) at(size_t _idx) { return m_data.at(_idx); }
|
||||
decltype(auto) at(size_t _idx) const { return m_data.at(_idx); }
|
||||
|
||||
decltype(auto) front() { return m_data.front(); }
|
||||
decltype(auto) front() const { return m_data.front(); }
|
||||
decltype(auto) back() { return *(m_data.begin() + size() - 1); }
|
||||
decltype(auto) back() const { return *(m_data.begin() + size() - 1); }
|
||||
|
||||
auto* data() { return m_data.data(); }
|
||||
const auto* data() const { return m_data.data(); }
|
||||
|
||||
void swap(this_type& _v) noexcept;
|
||||
|
||||
friend void swap(this_type& _lhs, this_type& _rhs) noexcept { _lhs.swap(_rhs); }
|
||||
|
||||
private:
|
||||
void update_size(size_t);
|
||||
|
||||
private:
|
||||
count_type m_size = count_type{0};
|
||||
std::array<Tp, N> m_data = {};
|
||||
};
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
static_vector<Tp, N, AtomicSizeV>::static_vector(size_t _n, Tp _v)
|
||||
{
|
||||
m_data.fill(_v);
|
||||
update_size(_n);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
static_vector<Tp, N, AtomicSizeV>::static_vector(c_array<Tp>&& _v)
|
||||
{
|
||||
auto _n = std::min<size_t>(N, _v.size());
|
||||
for(size_t i = 0; i < _n; ++i, ++m_size)
|
||||
m_data[i] = _v[i];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
template <size_t M>
|
||||
static_vector<Tp, N, AtomicSizeV>::static_vector(std::array<Tp, M>&& _v)
|
||||
{
|
||||
auto _n = std::min<size_t>(N, M);
|
||||
for(size_t i = 0; i < _n; ++i, ++m_size)
|
||||
m_data[i] = _v[i];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
static_vector<Tp, N, AtomicSizeV>&
|
||||
static_vector<Tp, N, AtomicSizeV>::operator=(std::initializer_list<Tp>&& _v)
|
||||
{
|
||||
if(ROCPROFILER_UNLIKELY(_v.size() > N))
|
||||
{
|
||||
throw std::out_of_range(std::string{"static_vector::operator=(initializer_list) size > "} +
|
||||
std::to_string(N));
|
||||
}
|
||||
|
||||
clear();
|
||||
for(auto&& itr : _v)
|
||||
m_data[m_size++] = itr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
static_vector<Tp, N, AtomicSizeV>&
|
||||
static_vector<Tp, N, AtomicSizeV>::operator=(std::pair<std::array<Tp, N>, size_t>&& _v)
|
||||
{
|
||||
update_size(0);
|
||||
m_data = std::move(_v.first);
|
||||
update_size(_v.second);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
void
|
||||
static_vector<Tp, N, AtomicSizeV>::clear()
|
||||
{
|
||||
update_size(0);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
void
|
||||
static_vector<Tp, N, AtomicSizeV>::swap(this_type& _v) noexcept
|
||||
{
|
||||
if constexpr(AtomicSizeV)
|
||||
{
|
||||
auto _t_size = m_size;
|
||||
auto _v_size = _v.m_size;
|
||||
std::swap(m_data, _v.m_data);
|
||||
update_size(_v_size);
|
||||
_v.update_size(_t_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::swap(m_size, _v.m_size);
|
||||
std::swap(m_data, _v.m_data);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
template <typename... Args>
|
||||
Tp&
|
||||
static_vector<Tp, N, AtomicSizeV>::emplace_back(Args&&... _v)
|
||||
{
|
||||
auto _idx = m_size++;
|
||||
if(_idx >= N)
|
||||
{
|
||||
throw std::out_of_range(std::string{"static_vector::emplace_back - reached capacity "} +
|
||||
std::to_string(N));
|
||||
}
|
||||
|
||||
if constexpr(sizeof...(Args) > 0)
|
||||
{
|
||||
if constexpr(std::is_assignable<Tp, decltype(std::forward<Args>(_v))...>::value)
|
||||
m_data[_idx] = {std::forward<Args>(_v)...};
|
||||
else
|
||||
m_data[_idx] = Tp{std::forward<Args>(_v)...};
|
||||
}
|
||||
else if constexpr(std::is_move_assignable<Tp>::value || std::is_copy_assignable<Tp>::value)
|
||||
{
|
||||
m_data[_idx] = {};
|
||||
}
|
||||
return m_data[_idx];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
void
|
||||
static_vector<Tp, N, AtomicSizeV>::update_size(size_t _n)
|
||||
{
|
||||
if constexpr(AtomicSizeV)
|
||||
m_size.store(_n);
|
||||
else
|
||||
m_size = _n;
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,144 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 <rocprofiler-sdk/defines.h>
|
||||
|
||||
#define ROCPROFILER_VISIBILITY(MODE) ROCPROFILER_ATTRIBUTE(visibility(MODE))
|
||||
#define ROCPROFILER_INTERNAL_API ROCPROFILER_VISIBILITY("internal")
|
||||
#define ROCPROFILER_INLINE ROCPROFILER_ATTRIBUTE(always_inline) inline
|
||||
#define ROCPROFILER_NOINLINE ROCPROFILER_ATTRIBUTE(noinline)
|
||||
#define ROCPROFILER_HOT ROCPROFILER_ATTRIBUTE(hot)
|
||||
#define ROCPROFILER_COLD ROCPROFILER_ATTRIBUTE(cold)
|
||||
#define ROCPROFILER_CONST ROCPROFILER_ATTRIBUTE(const)
|
||||
#define ROCPROFILER_PURE ROCPROFILER_ATTRIBUTE(pure)
|
||||
#define ROCPROFILER_WEAK ROCPROFILER_ATTRIBUTE(weak)
|
||||
#define ROCPROFILER_PACKED ROCPROFILER_ATTRIBUTE(__packed__)
|
||||
#define ROCPROFILER_PACKED_ALIGN(VAL) ROCPROFILER_PACKED ROCPROFILER_ATTRIBUTE(__aligned__(VAL))
|
||||
#define ROCPROFILER_LIKELY(...) __builtin_expect((__VA_ARGS__), 1)
|
||||
#define ROCPROFILER_UNLIKELY(...) __builtin_expect((__VA_ARGS__), 0)
|
||||
|
||||
#if defined(ROCPROFILER_CI) && ROCPROFILER_CI > 0
|
||||
# if defined(NDEBUG)
|
||||
# undef NDEBUG
|
||||
# endif
|
||||
# if !defined(DEBUG)
|
||||
# define DEBUG 1
|
||||
# endif
|
||||
# if defined(__cplusplus)
|
||||
# include <cassert>
|
||||
# else
|
||||
# include <assert.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define ROCPROFILER_STRINGIZE(X) ROCPROFILER_STRINGIZE2(X)
|
||||
#define ROCPROFILER_STRINGIZE2(X) #X
|
||||
#define ROCPROFILER_VAR_NAME_COMBINE(X, Y) X##Y
|
||||
#define ROCPROFILER_VARIABLE(X, Y) ROCPROFILER_VAR_NAME_COMBINE(X, Y)
|
||||
#define ROCPROFILER_LINESTR ROCPROFILER_STRINGIZE(__LINE__)
|
||||
#define ROCPROFILER_ESC(...) __VA_ARGS__
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# if !defined(ROCPROFILER_FOLD_EXPRESSION)
|
||||
# define ROCPROFILER_FOLD_EXPRESSION(...) ((__VA_ARGS__), ...)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define ROCPROFILER_COMPUTE_VERSION(MAJOR, MINOR, PATCH) \
|
||||
ROCPROFILER_SDK_COMPUTE_VERSION(MAJOR, MINOR, PATCH)
|
||||
|
||||
// Below are used in HSA, HIP, and Marker API tracing
|
||||
#define IMPL_DETAIL_EXPAND(X) X
|
||||
#define IMPL_DETAIL_FOR_EACH_NARG(...) \
|
||||
IMPL_DETAIL_FOR_EACH_NARG_(__VA_ARGS__, IMPL_DETAIL_FOR_EACH_RSEQ_N())
|
||||
#define IMPL_DETAIL_FOR_EACH_NARG_(...) IMPL_DETAIL_EXPAND(IMPL_DETAIL_FOR_EACH_ARG_N(__VA_ARGS__))
|
||||
#define IMPL_DETAIL_FOR_EACH_ARG_N( \
|
||||
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, N, ...) \
|
||||
N
|
||||
#define IMPL_DETAIL_FOR_EACH_RSEQ_N() 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||
#define IMPL_DETAIL_CONCATENATE(X, Y) X##Y
|
||||
#define IMPL_DETAIL_FOR_EACH_(N, MACRO, PREFIX, ...) \
|
||||
IMPL_DETAIL_EXPAND(IMPL_DETAIL_CONCATENATE(MACRO, N)(PREFIX, __VA_ARGS__))
|
||||
#define IMPL_DETAIL_FOR_EACH(MACRO, PREFIX, ...) \
|
||||
IMPL_DETAIL_FOR_EACH_(IMPL_DETAIL_FOR_EACH_NARG(__VA_ARGS__), MACRO, PREFIX, __VA_ARGS__)
|
||||
|
||||
#define ADDR_MEMBER_0(...)
|
||||
#define ADDR_MEMBER_1(PREFIX, FIELD) static_cast<void*>(&PREFIX.FIELD)
|
||||
#define ADDR_MEMBER_2(PREFIX, A, B) ADDR_MEMBER_1(PREFIX, A), ADDR_MEMBER_1(PREFIX, B)
|
||||
#define ADDR_MEMBER_3(PREFIX, A, B, C) ADDR_MEMBER_2(PREFIX, A, B), ADDR_MEMBER_1(PREFIX, C)
|
||||
#define ADDR_MEMBER_4(PREFIX, A, B, C, D) ADDR_MEMBER_3(PREFIX, A, B, C), ADDR_MEMBER_1(PREFIX, D)
|
||||
#define ADDR_MEMBER_5(PREFIX, A, B, C, D, E) \
|
||||
ADDR_MEMBER_4(PREFIX, A, B, C, D), ADDR_MEMBER_1(PREFIX, E)
|
||||
#define ADDR_MEMBER_6(PREFIX, A, B, C, D, E, F) \
|
||||
ADDR_MEMBER_5(PREFIX, A, B, C, D, E), ADDR_MEMBER_1(PREFIX, F)
|
||||
#define ADDR_MEMBER_7(PREFIX, A, B, C, D, E, F, G) \
|
||||
ADDR_MEMBER_6(PREFIX, A, B, C, D, E, F), ADDR_MEMBER_1(PREFIX, G)
|
||||
#define ADDR_MEMBER_8(PREFIX, A, B, C, D, E, F, G, H) \
|
||||
ADDR_MEMBER_7(PREFIX, A, B, C, D, E, F, G), ADDR_MEMBER_1(PREFIX, H)
|
||||
#define ADDR_MEMBER_9(PREFIX, A, B, C, D, E, F, G, H, I) \
|
||||
ADDR_MEMBER_8(PREFIX, A, B, C, D, E, F, G, H), ADDR_MEMBER_1(PREFIX, I)
|
||||
#define ADDR_MEMBER_10(PREFIX, A, B, C, D, E, F, G, H, I, J) \
|
||||
ADDR_MEMBER_9(PREFIX, A, B, C, D, E, F, G, H, I), ADDR_MEMBER_1(PREFIX, J)
|
||||
#define ADDR_MEMBER_11(PREFIX, A, B, C, D, E, F, G, H, I, J, K) \
|
||||
ADDR_MEMBER_10(PREFIX, A, B, C, D, E, F, G, H, I, J), ADDR_MEMBER_1(PREFIX, K)
|
||||
#define ADDR_MEMBER_12(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L) \
|
||||
ADDR_MEMBER_11(PREFIX, A, B, C, D, E, F, G, H, I, J, K), ADDR_MEMBER_1(PREFIX, L)
|
||||
#define ADDR_MEMBER_13(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M) \
|
||||
ADDR_MEMBER_12(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L), ADDR_MEMBER_1(PREFIX, M)
|
||||
#define ADDR_MEMBER_14(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M, N) \
|
||||
ADDR_MEMBER_13(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M), ADDR_MEMBER_1(PREFIX, N)
|
||||
#define ADDR_MEMBER_15(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) \
|
||||
ADDR_MEMBER_14(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M, N), ADDR_MEMBER_1(PREFIX, O)
|
||||
|
||||
#define NAMED_MEMBER_0(...)
|
||||
#define NAMED_MEMBER_1(PREFIX, FIELD) std::make_pair(#FIELD, PREFIX.FIELD)
|
||||
#define NAMED_MEMBER_2(PREFIX, A, B) NAMED_MEMBER_1(PREFIX, A), NAMED_MEMBER_1(PREFIX, B)
|
||||
#define NAMED_MEMBER_3(PREFIX, A, B, C) NAMED_MEMBER_2(PREFIX, A, B), NAMED_MEMBER_1(PREFIX, C)
|
||||
#define NAMED_MEMBER_4(PREFIX, A, B, C, D) \
|
||||
NAMED_MEMBER_3(PREFIX, A, B, C), NAMED_MEMBER_1(PREFIX, D)
|
||||
#define NAMED_MEMBER_5(PREFIX, A, B, C, D, E) \
|
||||
NAMED_MEMBER_4(PREFIX, A, B, C, D), NAMED_MEMBER_1(PREFIX, E)
|
||||
#define NAMED_MEMBER_6(PREFIX, A, B, C, D, E, F) \
|
||||
NAMED_MEMBER_5(PREFIX, A, B, C, D, E), NAMED_MEMBER_1(PREFIX, F)
|
||||
#define NAMED_MEMBER_7(PREFIX, A, B, C, D, E, F, G) \
|
||||
NAMED_MEMBER_6(PREFIX, A, B, C, D, E, F), NAMED_MEMBER_1(PREFIX, G)
|
||||
#define NAMED_MEMBER_8(PREFIX, A, B, C, D, E, F, G, H) \
|
||||
NAMED_MEMBER_7(PREFIX, A, B, C, D, E, F, G), NAMED_MEMBER_1(PREFIX, H)
|
||||
#define NAMED_MEMBER_9(PREFIX, A, B, C, D, E, F, G, H, I) \
|
||||
NAMED_MEMBER_8(PREFIX, A, B, C, D, E, F, G, H), NAMED_MEMBER_1(PREFIX, I)
|
||||
#define NAMED_MEMBER_10(PREFIX, A, B, C, D, E, F, G, H, I, J) \
|
||||
NAMED_MEMBER_9(PREFIX, A, B, C, D, E, F, G, H, I), NAMED_MEMBER_1(PREFIX, J)
|
||||
#define NAMED_MEMBER_11(PREFIX, A, B, C, D, E, F, G, H, I, J, K) \
|
||||
NAMED_MEMBER_10(PREFIX, A, B, C, D, E, F, G, H, I, J), NAMED_MEMBER_1(PREFIX, K)
|
||||
#define NAMED_MEMBER_12(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L) \
|
||||
NAMED_MEMBER_11(PREFIX, A, B, C, D, E, F, G, H, I, J, K), NAMED_MEMBER_1(PREFIX, L)
|
||||
#define NAMED_MEMBER_13(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M) \
|
||||
NAMED_MEMBER_12(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L), NAMED_MEMBER_1(PREFIX, M)
|
||||
#define NAMED_MEMBER_14(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M, N) \
|
||||
NAMED_MEMBER_13(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M), NAMED_MEMBER_1(PREFIX, N)
|
||||
#define NAMED_MEMBER_15(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) \
|
||||
NAMED_MEMBER_14(PREFIX, A, B, C, D, E, F, G, H, I, J, K, L, M, N), NAMED_MEMBER_1(PREFIX, O)
|
||||
|
||||
#define GET_ADDR_MEMBER_FIELDS(VAR, ...) IMPL_DETAIL_FOR_EACH(ADDR_MEMBER_, VAR, __VA_ARGS__)
|
||||
#define GET_NAMED_MEMBER_FIELDS(VAR, ...) IMPL_DETAIL_FOR_EACH(NAMED_MEMBER_, VAR, __VA_ARGS__)
|
||||
@@ -0,0 +1,164 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/demangle.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
std::string
|
||||
cxa_demangle(std::string_view _mangled_name, int* _status)
|
||||
{
|
||||
// return the mangled since there is no buffer
|
||||
if(_mangled_name.empty())
|
||||
{
|
||||
*_status = -2;
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
auto _demangled_name = std::string{_mangled_name};
|
||||
|
||||
// PARAMETERS to __cxa_demangle
|
||||
// mangled_name:
|
||||
// A NULL-terminated character string containing the name to be demangled.
|
||||
// buffer:
|
||||
// A region of memory, allocated with malloc, of *length bytes, into which the
|
||||
// demangled name is stored. If output_buffer is not long enough, it is expanded
|
||||
// using realloc. output_buffer may instead be NULL; in that case, the demangled
|
||||
// name is placed in a region of memory allocated with malloc.
|
||||
// _buflen:
|
||||
// If length is non-NULL, the length of the buffer containing the demangled name
|
||||
// is placed in *length.
|
||||
// status:
|
||||
// *status is set to one of the following values
|
||||
size_t _demang_len = 0;
|
||||
char* _demang = abi::__cxa_demangle(_demangled_name.c_str(), nullptr, &_demang_len, _status);
|
||||
switch(*_status)
|
||||
{
|
||||
// 0 : The demangling operation succeeded.
|
||||
// -1 : A memory allocation failure occurred.
|
||||
// -2 : mangled_name is not a valid name under the C++ ABI mangling rules.
|
||||
// -3 : One of the arguments is invalid.
|
||||
case 0:
|
||||
{
|
||||
if(_demang) _demangled_name = std::string{_demang};
|
||||
break;
|
||||
}
|
||||
case -1:
|
||||
{
|
||||
ROCP_ERROR << "memory allocation failure occurred demangling " << _demangled_name;
|
||||
break;
|
||||
}
|
||||
case -2: break;
|
||||
case -3:
|
||||
{
|
||||
ROCP_ERROR << "Invalid argument in: (\"" << _demangled_name << "\", nullptr, nullptr, "
|
||||
<< _status << ")";
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
};
|
||||
|
||||
// if it "demangled" but the length is zero, set the status to -2
|
||||
if(_demang_len == 0 && *_status == 0) *_status = -2;
|
||||
|
||||
// free allocated buffer
|
||||
::free(_demang);
|
||||
return _demangled_name;
|
||||
}
|
||||
|
||||
// C++ symbol demangle
|
||||
std::string
|
||||
cxx_demangle(std::string_view symbol)
|
||||
{
|
||||
int _status = 0;
|
||||
auto demangled_str = cxa_demangle(symbol, &_status);
|
||||
|
||||
if(_status == 0) return demangled_str;
|
||||
|
||||
return std::string{symbol};
|
||||
}
|
||||
|
||||
// The function extracts the kernel name from
|
||||
// input string. By using the iterators it finds the
|
||||
// window in the string which contains only the kernel name.
|
||||
// For example 'Foo<int, float>::foo(a[], int (int))' -> 'foo'
|
||||
std::string
|
||||
truncate_name(std::string_view name)
|
||||
{
|
||||
auto rit = name.rbegin();
|
||||
auto rend = name.rend();
|
||||
uint32_t counter = 0;
|
||||
char open_token = 0;
|
||||
char close_token = 0;
|
||||
while(rit != rend)
|
||||
{
|
||||
if(counter == 0)
|
||||
{
|
||||
switch(*rit)
|
||||
{
|
||||
case ')':
|
||||
counter = 1;
|
||||
open_token = ')';
|
||||
close_token = '(';
|
||||
break;
|
||||
case '>':
|
||||
counter = 1;
|
||||
open_token = '>';
|
||||
close_token = '<';
|
||||
break;
|
||||
case ']':
|
||||
counter = 1;
|
||||
open_token = ']';
|
||||
close_token = '[';
|
||||
break;
|
||||
case ' ': ++rit; continue;
|
||||
}
|
||||
if(counter == 0) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(*rit == open_token) counter++;
|
||||
if(*rit == close_token) counter--;
|
||||
}
|
||||
++rit;
|
||||
}
|
||||
auto rbeg = rit;
|
||||
while((rit != rend) && (*rit != ' ') && (*rit != ':'))
|
||||
rit++;
|
||||
return std::string{name.substr(rend - rit, rit - rbeg)};
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,53 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 <cxxabi.h>
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
[[nodiscard]] std::string
|
||||
cxa_demangle(std::string_view _mangled_name, int* _status) __attribute__((nonnull(2)));
|
||||
|
||||
// C++ symbol demangle
|
||||
std::string
|
||||
cxx_demangle(std::string_view symbol);
|
||||
|
||||
// The function extracts the kernel name from
|
||||
// input string. By using the iterators it finds the
|
||||
// window in the string which contains only the kernel name.
|
||||
// For example 'Foo<int, float>::foo(a[], int (int))' -> 'foo'
|
||||
std::string
|
||||
truncate_name(std::string_view name);
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,30 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2023-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.
|
||||
|
||||
#
|
||||
# add details sources and headers to common library target
|
||||
#
|
||||
set(details_headers mpl.hpp)
|
||||
set(details_sources)
|
||||
|
||||
target_sources(rocprofiler-sdk-common-library PRIVATE ${details_sources}
|
||||
${details_headers})
|
||||
@@ -0,0 +1,211 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023 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 <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace mpl
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template <typename... Tp>
|
||||
struct type_list
|
||||
{
|
||||
static constexpr auto size() { return sizeof...(Tp); }
|
||||
};
|
||||
|
||||
template <typename InTuple, typename OutTuple>
|
||||
struct reverse;
|
||||
|
||||
template <template <typename...> class InTuple,
|
||||
typename InT,
|
||||
typename... InTail,
|
||||
template <typename...>
|
||||
class OutTuple,
|
||||
typename... OutTail>
|
||||
struct reverse<InTuple<InT, InTail...>, OutTuple<OutTail...>>
|
||||
: reverse<InTuple<InTail...>, OutTuple<InT, OutTail...>>
|
||||
{};
|
||||
|
||||
template <template <typename...> class InTuple,
|
||||
template <typename...>
|
||||
class OutTuple,
|
||||
typename... OutTail>
|
||||
struct reverse<InTuple<>, OutTuple<OutTail...>>
|
||||
{
|
||||
using type = OutTuple<OutTail...>;
|
||||
};
|
||||
|
||||
template <template <typename...> class InTuple, typename... InTail>
|
||||
struct reverse<InTuple<InTail...>, void> : reverse<InTuple<InTail...>, InTuple<>>
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
struct function_traits;
|
||||
|
||||
template <typename T>
|
||||
struct function_traits<T&> : function_traits<T>
|
||||
{};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct function_traits<std::function<R(Args...)>>
|
||||
{
|
||||
static constexpr bool is_memfun = false;
|
||||
static constexpr bool is_const = false;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = args_type;
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct function_traits<R (*)(Args...)>
|
||||
{
|
||||
static constexpr bool is_memfun = false;
|
||||
static constexpr bool is_const = false;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = args_type;
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct function_traits<R(Args...)>
|
||||
{
|
||||
static constexpr bool is_memfun = false;
|
||||
static constexpr bool is_const = false;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = args_type;
|
||||
};
|
||||
|
||||
// member function pointer
|
||||
template <typename C, typename R, typename... Args>
|
||||
struct function_traits<R (C::*)(Args...)>
|
||||
{
|
||||
static constexpr bool is_memfun = true;
|
||||
static constexpr bool is_const = false;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = type_list<C&, Args...>;
|
||||
};
|
||||
|
||||
// const member function pointer
|
||||
template <typename C, typename R, typename... Args>
|
||||
struct function_traits<R (C::*)(Args...) const>
|
||||
{
|
||||
static constexpr bool is_memfun = true;
|
||||
static constexpr bool is_const = true;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = type_list<C&, Args...>;
|
||||
};
|
||||
|
||||
// member object pointer
|
||||
template <typename C, typename R>
|
||||
struct function_traits<R(C::*)>
|
||||
{
|
||||
static constexpr bool is_memfun = true;
|
||||
static constexpr bool is_const = false;
|
||||
static const size_t nargs = 0;
|
||||
using result_type = R;
|
||||
using args_type = type_list<>;
|
||||
using call_type = type_list<C&>;
|
||||
};
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct function_traits<std::function<R(Args...) noexcept>>
|
||||
{
|
||||
static constexpr bool is_memfun = false;
|
||||
static constexpr bool is_const = false;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = args_type;
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct function_traits<R (*)(Args...) noexcept>
|
||||
{
|
||||
static constexpr bool is_memfun = false;
|
||||
static constexpr bool is_const = false;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = args_type;
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct function_traits<R(Args...) noexcept>
|
||||
{
|
||||
static constexpr bool is_memfun = false;
|
||||
static constexpr bool is_const = false;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = args_type;
|
||||
};
|
||||
|
||||
// member function pointer
|
||||
template <typename C, typename R, typename... Args>
|
||||
struct function_traits<R (C::*)(Args...) noexcept>
|
||||
{
|
||||
static constexpr bool is_memfun = true;
|
||||
static constexpr bool is_const = false;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = type_list<C&, Args...>;
|
||||
};
|
||||
|
||||
// const member function pointer
|
||||
template <typename C, typename R, typename... Args>
|
||||
struct function_traits<R (C::*)(Args...) const noexcept>
|
||||
{
|
||||
static constexpr bool is_memfun = true;
|
||||
static constexpr bool is_const = true;
|
||||
static constexpr size_t nargs = sizeof...(Args);
|
||||
using result_type = R;
|
||||
using args_type = type_list<Args...>;
|
||||
using call_type = type_list<C&, Args...>;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace impl
|
||||
} // namespace mpl
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,228 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/elf_utils.hpp"
|
||||
|
||||
#include <rocprofiler-sdk/cxx/utility.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <elfio/elfio.hpp>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace elf_utils
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const ELFIO::Elf_Xword PAGE_SIZE = sysconf(_SC_PAGESIZE);
|
||||
|
||||
using ::rocprofiler::sdk::utility::as_hex;
|
||||
} // namespace
|
||||
|
||||
SymbolEntry::SymbolEntry(unsigned int _idx, const accessor_type& _accessor)
|
||||
: index{_idx}
|
||||
{
|
||||
if(!_accessor.get_symbol(index, name, value, size, bind, type, section_index, other))
|
||||
ROCP_WARNING << "ELFIO::symbol_section_accessor::get_symbol failed of symbol " << _idx;
|
||||
}
|
||||
|
||||
DynamicEntry::DynamicEntry(unsigned int _idx, const accessor_type& _accessor)
|
||||
: index{_idx}
|
||||
{
|
||||
if(!_accessor.get_entry(_idx, tag, value, name)) return;
|
||||
}
|
||||
|
||||
RelocationEntry::RelocationEntry(unsigned int _idx, const accessor_type& _accessor)
|
||||
: index{_idx}
|
||||
{
|
||||
if(!_accessor.get_entry(_idx, offset, symbol, type, addend))
|
||||
ROCP_WARNING << "ELFIO::relocation_section_accessor::get_entry failed for symbol " << _idx;
|
||||
}
|
||||
|
||||
ElfInfo::ElfInfo(std::string _fname)
|
||||
: filename{std::move(_fname)}
|
||||
{}
|
||||
|
||||
bool
|
||||
ElfInfo::has_symbol(const std::function<bool(std::string_view)>& _checker) const
|
||||
{
|
||||
for(const auto& itr : symbol_entries)
|
||||
{
|
||||
if(!itr.name.empty() && _checker(itr.name)) return true;
|
||||
}
|
||||
// For stripped binaries
|
||||
for(const auto& itr : dynamic_symbol_entries)
|
||||
{
|
||||
if(!itr.name.empty() && _checker(itr.name)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfInfo
|
||||
read(const std::string& _inp)
|
||||
{
|
||||
auto _info = ElfInfo{_inp};
|
||||
auto& reader = _info.reader;
|
||||
auto& sections = _info.sections;
|
||||
auto& symbol_entries = _info.symbol_entries;
|
||||
auto& dynamic_symbol_entries = _info.dynamic_symbol_entries;
|
||||
auto& dynamic_entries = _info.dynamic_entries;
|
||||
auto& reloc_entries = _info.reloc_entries;
|
||||
|
||||
ROCP_TRACE << "\nReading " << _inp;
|
||||
|
||||
if(!reader.load(_inp))
|
||||
ROCP_WARNING << fmt::format("ELF parsing for '{}' did not succeed", _inp);
|
||||
|
||||
if(reader.get_class() == ELFIO::ELFCLASS32)
|
||||
ROCP_TRACE << " - ELF 32-bit";
|
||||
else
|
||||
ROCP_TRACE << " - ELF 64-bit";
|
||||
|
||||
ROCP_TRACE << " - ELF file encoding: "
|
||||
<< ((reader.get_encoding() == ELFIO::ELFDATA2LSB) ? std::string_view{"Little endian"}
|
||||
: std::string_view{"Big endian"});
|
||||
|
||||
ROCP_TRACE << " - ELF version: " << reader.get_elf_version();
|
||||
ROCP_TRACE << " - ELF header size: " << reader.get_header_size();
|
||||
ROCP_TRACE << " - ELF OS ABI: " << reader.get_os_abi();
|
||||
|
||||
// Print ELF file sections info
|
||||
ELFIO::Elf_Half sec_num = reader.sections.size();
|
||||
ROCP_TRACE << " - Number of sections: " << sec_num;
|
||||
|
||||
for(ELFIO::Elf_Half j = 0; j < sec_num; ++j)
|
||||
{
|
||||
ELFIO::section* psec = reader.sections[j];
|
||||
sections.emplace_back(psec);
|
||||
}
|
||||
|
||||
std::sort(sections.begin(), sections.end(), [](const Section* lhs, const Section* rhs) {
|
||||
return std::string_view{lhs->get_name()} < std::string_view{rhs->get_name()};
|
||||
});
|
||||
|
||||
for(ELFIO::Elf_Half j = 0; j < sec_num; ++j)
|
||||
{
|
||||
Section* psec = sections.at(j);
|
||||
ROCP_TRACE << " [" << j << "] \t" << std::setw(20) << psec->get_name() << "\t : \t"
|
||||
<< "size / entry-size = " << std::setw(6) << psec->get_size() << " / "
|
||||
<< std::setw(3) << psec->get_entry_size()
|
||||
<< " | addr: " << as_hex(psec->get_address(), 16)
|
||||
<< " | offset: " << as_hex(psec->get_offset(), 16);
|
||||
|
||||
if(psec->get_size() == 0) continue;
|
||||
|
||||
if(psec->get_type() == ELFIO::SHT_SYMTAB)
|
||||
{
|
||||
const ELFIO::symbol_section_accessor _symbols(reader, psec);
|
||||
ROCP_TRACE << " - Number of symbol entries: " << _symbols.get_symbols_num();
|
||||
for(ELFIO::Elf_Xword k = 0; k < _symbols.get_symbols_num(); ++k)
|
||||
symbol_entries.emplace_back(k, _symbols);
|
||||
}
|
||||
else if(psec->get_type() == ELFIO::SHT_DYNSYM)
|
||||
{
|
||||
const ELFIO::symbol_section_accessor _symbols(reader, psec);
|
||||
ROCP_TRACE << " - Number of dynamic symbol entries: " << _symbols.get_symbols_num();
|
||||
for(ELFIO::Elf_Xword k = 0; k < _symbols.get_symbols_num(); ++k)
|
||||
dynamic_symbol_entries.emplace_back(k, _symbols);
|
||||
}
|
||||
else if(psec->get_type() == ELFIO::SHT_DYNAMIC)
|
||||
{
|
||||
const ELFIO::dynamic_section_accessor dynamic{reader, psec};
|
||||
ROCP_TRACE << " - Number of dynamic entries: " << dynamic.get_entries_num();
|
||||
for(ELFIO::Elf_Xword k = 0; k < dynamic.get_entries_num(); ++k)
|
||||
dynamic_entries.emplace_back(k, dynamic);
|
||||
}
|
||||
else if(psec->get_type() == ELFIO::SHT_REL || psec->get_type() == ELFIO::SHT_RELA)
|
||||
{
|
||||
const ELFIO::relocation_section_accessor reloc{reader, psec};
|
||||
ROCP_TRACE << " - Number of relocation entries: " << reloc.get_entries_num();
|
||||
for(ELFIO::Elf_Xword k = 0; k < reloc.get_entries_num(); ++k)
|
||||
reloc_entries.emplace_back(k, reloc);
|
||||
}
|
||||
}
|
||||
|
||||
ROCP_TRACE << " - Symbols:";
|
||||
for(size_t k = 0; k < symbol_entries.size(); ++k)
|
||||
{
|
||||
if(!symbol_entries.at(k).name.empty())
|
||||
ROCP_TRACE << " [" << k << "] " << symbol_entries.at(k).name;
|
||||
}
|
||||
|
||||
ROCP_TRACE << " - Dynamic Symbols:";
|
||||
for(size_t k = 0; k < dynamic_symbol_entries.size(); ++k)
|
||||
{
|
||||
if(!dynamic_symbol_entries.at(k).name.empty())
|
||||
ROCP_TRACE << " [" << k << "] " << dynamic_symbol_entries.at(k).name;
|
||||
}
|
||||
|
||||
ROCP_TRACE << " - Dynamic entries:";
|
||||
for(size_t k = 0; k < dynamic_entries.size(); ++k)
|
||||
{
|
||||
if(!dynamic_entries.at(k).name.empty())
|
||||
ROCP_TRACE << " [" << k << "] " << dynamic_entries.at(k).name;
|
||||
}
|
||||
|
||||
ROCP_TRACE << " - Relocation entries:";
|
||||
for(size_t k = 0; k < reloc_entries.size(); ++k)
|
||||
{
|
||||
auto _sym_idx = reloc_entries.at(k).symbol;
|
||||
auto _name = std::string{};
|
||||
if(_sym_idx < symbol_entries.size()) _name = symbol_entries.at(_sym_idx).name;
|
||||
if(!_name.empty()) ROCP_TRACE << " [" << k << "] " << _name;
|
||||
}
|
||||
|
||||
// Print ELF file segments info
|
||||
ELFIO::Elf_Half seg_num = reader.segments.size();
|
||||
ROCP_TRACE << " - Number of segments: " << seg_num;
|
||||
for(ELFIO::Elf_Half j = 0; j < seg_num; ++j)
|
||||
{
|
||||
const ELFIO::segment* pseg = reader.segments[j];
|
||||
ROCP_TRACE << " [" << std::setw(2) << j << "] flags: " << as_hex(pseg->get_flags(), 16)
|
||||
<< " offset: " << as_hex(pseg->get_offset(), 16)
|
||||
<< " align: " << as_hex(pseg->get_align(), 16)
|
||||
<< " virt: " << as_hex(pseg->get_virtual_address(), 16)
|
||||
<< " phys: " << as_hex(pseg->get_physical_address(), 16)
|
||||
<< " fsize: " << std::setw(8) << pseg->get_file_size()
|
||||
<< " msize: " << std::setw(8) << pseg->get_memory_size();
|
||||
}
|
||||
|
||||
return _info;
|
||||
}
|
||||
} // namespace elf_utils
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,122 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 <elfio/elfio.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace elf_utils
|
||||
{
|
||||
using Section = ELFIO::section;
|
||||
using Segment = ELFIO::segment;
|
||||
|
||||
struct SymbolEntry
|
||||
{
|
||||
using accessor_type = ELFIO::symbol_section_accessor;
|
||||
|
||||
SymbolEntry(unsigned int _idx, const accessor_type& _accessor);
|
||||
|
||||
unsigned int index = 0;
|
||||
std::string name = {};
|
||||
ELFIO::Elf64_Addr value = {};
|
||||
ELFIO::Elf_Xword size = {};
|
||||
unsigned char bind = {};
|
||||
unsigned char type = {};
|
||||
ELFIO::Elf_Half section_index = {};
|
||||
unsigned char other = {};
|
||||
};
|
||||
|
||||
struct DynamicEntry
|
||||
{
|
||||
using accessor_type = ELFIO::dynamic_section_accessor;
|
||||
|
||||
DynamicEntry(unsigned int _idx, const accessor_type& _accessor);
|
||||
|
||||
unsigned int index = 0;
|
||||
std::string name = {};
|
||||
ELFIO::Elf_Xword tag = {};
|
||||
ELFIO::Elf_Xword value = {};
|
||||
};
|
||||
|
||||
struct RelocationEntry
|
||||
{
|
||||
using accessor_type = ELFIO::relocation_section_accessor;
|
||||
|
||||
RelocationEntry(unsigned int _idx, const accessor_type& _accessor);
|
||||
|
||||
unsigned int index = 0;
|
||||
ELFIO::Elf64_Addr offset = {};
|
||||
ELFIO::Elf_Word symbol = {};
|
||||
ELFIO::Elf_Word type = {};
|
||||
ELFIO::Elf_Sxword addend = {};
|
||||
};
|
||||
|
||||
struct ElfInfo
|
||||
{
|
||||
explicit ElfInfo(std::string);
|
||||
|
||||
std::string filename = {};
|
||||
ELFIO::elfio reader = {};
|
||||
std::vector<Section*> sections = {};
|
||||
std::vector<SymbolEntry> symbol_entries = {};
|
||||
std::vector<SymbolEntry> dynamic_symbol_entries = {};
|
||||
std::vector<DynamicEntry> dynamic_entries = {};
|
||||
std::vector<RelocationEntry> reloc_entries = {};
|
||||
|
||||
bool has_symbol(const std::function<bool(std::string_view)>&) const;
|
||||
|
||||
friend bool operator==(const ElfInfo& lhs, const ElfInfo& rhs)
|
||||
{
|
||||
return (lhs.filename == rhs.filename);
|
||||
}
|
||||
|
||||
friend bool operator<(const ElfInfo& lhs, const ElfInfo& rhs)
|
||||
{
|
||||
return (lhs.filename < rhs.filename);
|
||||
}
|
||||
|
||||
friend bool operator>(const ElfInfo& lhs, const ElfInfo& rhs)
|
||||
{
|
||||
return !(lhs == rhs || lhs < rhs);
|
||||
}
|
||||
|
||||
friend bool operator<=(const ElfInfo& lhs, const ElfInfo& rhs) { return !(lhs > rhs); }
|
||||
friend bool operator>=(const ElfInfo& lhs, const ElfInfo& rhs) { return !(lhs < rhs); }
|
||||
};
|
||||
|
||||
ElfInfo
|
||||
read(const std::string& _inp);
|
||||
} // namespace elf_utils
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,206 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/environment.hpp"
|
||||
#include "lib/common/demangle.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
std::string
|
||||
get_env(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};
|
||||
}
|
||||
|
||||
std::string
|
||||
get_env(std::string_view env_id, const char* _default)
|
||||
{
|
||||
return get_env(env_id, std::string_view{_default});
|
||||
}
|
||||
|
||||
bool
|
||||
get_env(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())
|
||||
{
|
||||
ROCP_FATAL << fmt::format("No boolean value provided for {}", 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 < std::string_view{env_var}.length(); ++i)
|
||||
env_var[i] = tolower(env_var[i]);
|
||||
|
||||
for(const auto& itr : {"off", "false", "no", "n", "f", "0"})
|
||||
if(std::string_view{env_var} == itr) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
return _default;
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
Tp
|
||||
get_env(std::string_view env_id, Tp _default, std::enable_if_t<std::is_integral<Tp>::value, sfinae>)
|
||||
{
|
||||
static_assert(!std::is_same<Tp, bool>::value, "unexpected! should be using bool overload");
|
||||
static_assert(
|
||||
sizeof(Tp) <= sizeof(uint64_t),
|
||||
"change use of stol/stoul if instantiating for type larger than a 64-bit integer");
|
||||
|
||||
if(env_id.empty()) return _default;
|
||||
char* env_var = ::std::getenv(env_id.data());
|
||||
if(env_var)
|
||||
{
|
||||
try
|
||||
{
|
||||
// use stol/stoul
|
||||
if constexpr(std::is_signed<Tp>::value)
|
||||
return static_cast<Tp>(std::stol(env_var));
|
||||
else
|
||||
return static_cast<Tp>(std::stoul(env_var));
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler][get_env] Exception thrown converting getenv(\"" << env_id
|
||||
<< "\") = " << env_var << " to " << cxx_demangle(typeid(Tp).name())
|
||||
<< " :: " << _e.what() << ". Using default value of " << _default << "\n";
|
||||
}
|
||||
return _default;
|
||||
}
|
||||
return _default;
|
||||
}
|
||||
|
||||
int
|
||||
set_env(std::string_view env_id, bool value, int override)
|
||||
{
|
||||
return ::setenv(env_id.data(), (value) ? "1" : "0", override);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
int
|
||||
set_env(std::string_view env_id, Tp value, int override)
|
||||
{
|
||||
auto str_value = std::stringstream{};
|
||||
str_value << value;
|
||||
return ::setenv(env_id.data(), str_value.str().c_str(), override);
|
||||
}
|
||||
|
||||
#define SPECIALIZE_GET_ENV(TYPE) \
|
||||
template TYPE get_env<TYPE>( \
|
||||
std::string_view, TYPE, std::enable_if_t<std::is_integral<TYPE>::value, sfinae>); \
|
||||
template int set_env<TYPE>(std::string_view, TYPE, int);
|
||||
|
||||
#define SPECIALIZE_SET_ENV(TYPE) template int set_env<TYPE>(std::string_view, TYPE, int);
|
||||
|
||||
SPECIALIZE_GET_ENV(int8_t)
|
||||
SPECIALIZE_GET_ENV(int16_t)
|
||||
SPECIALIZE_GET_ENV(int32_t)
|
||||
SPECIALIZE_GET_ENV(int64_t)
|
||||
SPECIALIZE_GET_ENV(uint8_t)
|
||||
SPECIALIZE_GET_ENV(uint16_t)
|
||||
SPECIALIZE_GET_ENV(uint32_t)
|
||||
SPECIALIZE_GET_ENV(uint64_t)
|
||||
|
||||
SPECIALIZE_SET_ENV(const char*)
|
||||
SPECIALIZE_SET_ENV(std::string)
|
||||
SPECIALIZE_SET_ENV(std::string_view)
|
||||
SPECIALIZE_SET_ENV(float)
|
||||
SPECIALIZE_SET_ENV(double)
|
||||
} // namespace impl
|
||||
|
||||
env_store::env_store(std::initializer_list<env_config>&& _container)
|
||||
{
|
||||
for(const auto& itr : _container)
|
||||
{
|
||||
m_original.emplace_back(env_config{itr.env_name, get_env(itr.env_name, ""), 1});
|
||||
m_modified.emplace_back(env_config{itr.env_name, itr.env_value, 1});
|
||||
}
|
||||
}
|
||||
|
||||
env_store::~env_store() { pop(); }
|
||||
|
||||
bool
|
||||
env_store::push()
|
||||
{
|
||||
// not that push ignored bc already pushed
|
||||
if(m_pushed) return false;
|
||||
|
||||
for(const auto& itr : m_modified)
|
||||
itr();
|
||||
|
||||
m_pushed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
env_store::pop(bool unset_if_empty)
|
||||
{
|
||||
if(!m_pushed) return false;
|
||||
|
||||
for(const auto& itr : m_original)
|
||||
{
|
||||
auto _current = get_env(itr.env_name, "");
|
||||
if(!unset_if_empty && itr.env_value.empty())
|
||||
continue;
|
||||
else if(_current == itr.env_value)
|
||||
continue;
|
||||
else if(_current != itr.env_value)
|
||||
{
|
||||
ROCP_INFO << fmt::format("[rocprofiler][env][pop] {}=\"{}\" => {}=\"{}\"",
|
||||
itr.env_name,
|
||||
_current,
|
||||
itr.env_name,
|
||||
itr.env_value);
|
||||
}
|
||||
itr();
|
||||
}
|
||||
|
||||
m_pushed = false;
|
||||
return true;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,136 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/logging.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
struct sfinae
|
||||
{};
|
||||
|
||||
std::string get_env(std::string_view, std::string_view);
|
||||
|
||||
std::string
|
||||
get_env(std::string_view, const char*);
|
||||
|
||||
bool
|
||||
get_env(std::string_view, bool);
|
||||
|
||||
template <typename Tp>
|
||||
Tp get_env(std::string_view, Tp, std::enable_if_t<std::is_integral<Tp>::value, sfinae> = {});
|
||||
|
||||
int
|
||||
set_env(std::string_view, bool, int override = 0);
|
||||
|
||||
template <typename Tp>
|
||||
int
|
||||
set_env(std::string_view, Tp, int override = 0);
|
||||
} // namespace impl
|
||||
|
||||
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>(impl::get_env(env_id, static_cast<Up>(_default)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return impl::get_env(env_id, std::forward<Tp>(_default));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
inline auto
|
||||
set_env(std::string_view env_id, Tp&& value, int override = 0)
|
||||
{
|
||||
return impl::set_env(env_id, std::forward<Tp>(value), override);
|
||||
}
|
||||
|
||||
struct env_config
|
||||
{
|
||||
std::string env_name = {};
|
||||
std::string env_value = {};
|
||||
int overwrite = 0;
|
||||
|
||||
auto operator()(bool _verbose = false) const
|
||||
{
|
||||
if(env_name.empty())
|
||||
return -1;
|
||||
else if(_verbose)
|
||||
{
|
||||
ROCP_INFO << "[rocprofiler][set_env] setenv(\"" << env_name << "\", \"" << env_value
|
||||
<< "\", " << overwrite << ")\n";
|
||||
}
|
||||
return (env_value.empty() && overwrite > 0)
|
||||
? unsetenv(env_name.c_str())
|
||||
: setenv(env_name.c_str(), env_value.c_str(), overwrite);
|
||||
}
|
||||
};
|
||||
|
||||
struct env_store
|
||||
{
|
||||
template <template <typename, typename...> class ContainerT, typename... TailT>
|
||||
explicit env_store(ContainerT<env_config, TailT...>&& _container);
|
||||
explicit env_store(std::initializer_list<env_config>&& _container);
|
||||
|
||||
~env_store();
|
||||
env_store(const env_store&) = default;
|
||||
env_store(env_store&&) noexcept = default;
|
||||
env_store& operator=(const env_store&) = default;
|
||||
env_store& operator=(env_store&&) noexcept = default;
|
||||
|
||||
bool push();
|
||||
bool pop(bool unset_if_empty = true);
|
||||
bool is_pushed() const { return m_pushed; }
|
||||
|
||||
private:
|
||||
bool m_pushed = false;
|
||||
std::vector<env_config> m_original = {};
|
||||
std::vector<env_config> m_modified = {};
|
||||
};
|
||||
|
||||
template <template <typename, typename...> class ContainerT, typename... TailT>
|
||||
env_store::env_store(ContainerT<env_config, TailT...>&& _container)
|
||||
{
|
||||
for(const auto& itr : _container)
|
||||
{
|
||||
m_original.emplace_back(env_config{itr.env_name, get_env(itr.env_name, ""), 1});
|
||||
m_modified.emplace_back(env_config{itr.env_name, itr.env_value, 1});
|
||||
}
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,77 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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
|
||||
|
||||
#if !defined(ROCPROFILER_HAS_GHC_LIB_FILESYSTEM)
|
||||
# if defined __has_include
|
||||
# if __has_include(<ghc/filesystem.hpp>)
|
||||
# define ROCPROFILER_HAS_GHC_LIB_FILESYSTEM 1
|
||||
# else
|
||||
# define ROCPROFILER_HAS_GHC_LIB_FILESYSTEM 0
|
||||
# endif
|
||||
# else
|
||||
# define ROCPROFILER_HAS_GHC_LIB_FILESYSTEM 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ROCPROFILER_HAS_GHC_LIB_FILESYSTEM == 0
|
||||
# if defined __has_include
|
||||
# if __has_include(<version>)
|
||||
# include <version>
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(__cpp_lib_filesystem)
|
||||
# define ROCPROFILER_HAS_CPP_LIB_FILESYSTEM 1
|
||||
# else
|
||||
# if defined __has_include
|
||||
# if __has_include(<filesystem>)
|
||||
# define ROCPROFILER_HAS_CPP_LIB_FILESYSTEM 1
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// include the correct filesystem header
|
||||
#if defined(ROCPROFILER_HAS_GHC_LIB_FILESYSTEM) && ROCPROFILER_HAS_GHC_LIB_FILESYSTEM > 0
|
||||
# include <ghc/filesystem.hpp>
|
||||
#elif defined(ROCPROFILER_HAS_CPP_LIB_FILESYSTEM) && ROCPROFILER_HAS_CPP_LIB_FILESYSTEM > 0
|
||||
# include <filesystem>
|
||||
#else
|
||||
# include <experimental/filesystem>
|
||||
#endif
|
||||
|
||||
// create a namespace alias
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
#if defined(ROCPROFILER_HAS_GHC_LIB_FILESYSTEM) && ROCPROFILER_HAS_GHC_LIB_FILESYSTEM > 0
|
||||
namespace filesystem = ::ghc::filesystem; // NOLINT(misc-unused-alias-decls)
|
||||
#elif defined(ROCPROFILER_HAS_CPP_LIB_FILESYSTEM) && ROCPROFILER_HAS_CPP_LIB_FILESYSTEM > 0
|
||||
namespace filesystem = ::std::filesystem; // NOLINT(misc-unused-alias-decls)
|
||||
#else
|
||||
namespace filesystem = ::std::experimental::filesystem; // NOLINT(misc-unused-alias-decls)
|
||||
#endif
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,148 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/defines.hpp"
|
||||
|
||||
#include <rocprofiler-sdk/cxx/details/mpl.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
// A helper class which computes a 64-bit hash of the input data.
|
||||
// The algorithm used is FNV-1a as it is fast and easy to implement and has
|
||||
// relatively few collisions.
|
||||
// WARNING: This hash function should not be used for any cryptographic purpose.
|
||||
struct fnv1a_hasher
|
||||
{
|
||||
// Creates an empty hash object
|
||||
fnv1a_hasher() = default;
|
||||
~fnv1a_hasher() = default;
|
||||
|
||||
fnv1a_hasher(const fnv1a_hasher&) = default;
|
||||
fnv1a_hasher(fnv1a_hasher&&) noexcept = default;
|
||||
|
||||
fnv1a_hasher& operator=(const fnv1a_hasher&) = default;
|
||||
fnv1a_hasher& operator=(fnv1a_hasher&&) noexcept = default;
|
||||
|
||||
// Hashes a numeric value.
|
||||
template <typename Tp, typename std::enable_if_t<std::is_arithmetic<Tp>::value, bool> = true>
|
||||
fnv1a_hasher& update(Tp data);
|
||||
|
||||
template <typename Tp>
|
||||
fnv1a_hasher& update(const std::optional<Tp>& data);
|
||||
|
||||
// Using the loop instead of "update(str, strlen(str))" to avoid looping twice
|
||||
fnv1a_hasher& update(const char* str);
|
||||
|
||||
// Hashes a byte array.
|
||||
fnv1a_hasher& update(const char* data, size_t size);
|
||||
|
||||
fnv1a_hasher& update(std::string_view s) { return update(s.data(), s.size()); }
|
||||
|
||||
fnv1a_hasher& update(const std::string& s) { return update(s.data(), s.size()); }
|
||||
|
||||
// Usage: uint64_t hashed_value = Hash::combine(33, false, "ABC", 458L, 3u, 'x');
|
||||
template <typename Tp, typename... TailT>
|
||||
static uint64_t combine(Tp&& arg, TailT&&... args);
|
||||
|
||||
// fnv1a_hasher.update_all(33, false, "ABC")` is shorthand for calling fnv1a_hasher.update(...)
|
||||
// for each value in the same order
|
||||
template <typename Tp, typename... TailT>
|
||||
fnv1a_hasher& update_all(Tp&& arg, TailT&&... args);
|
||||
|
||||
uint64_t digest() const { return m_result; }
|
||||
|
||||
private:
|
||||
static constexpr uint64_t kFnv1a64OffsetBasis = 0xcbf29ce484222325;
|
||||
static constexpr uint64_t kFnv1a64Prime = 0x100000001b3;
|
||||
|
||||
uint64_t m_result = kFnv1a64OffsetBasis;
|
||||
};
|
||||
|
||||
template <typename Tp, typename std::enable_if_t<std::is_arithmetic<Tp>::value, bool>>
|
||||
fnv1a_hasher&
|
||||
fnv1a_hasher::update(Tp data)
|
||||
{
|
||||
return update(reinterpret_cast<const char*>(&data), sizeof(data));
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
fnv1a_hasher&
|
||||
fnv1a_hasher::update(const std::optional<Tp>& data)
|
||||
{
|
||||
return (data) ? update(*data) : *this;
|
||||
}
|
||||
|
||||
inline fnv1a_hasher&
|
||||
fnv1a_hasher::update(const char* str)
|
||||
{
|
||||
constexpr auto max_n = 4096;
|
||||
|
||||
size_t n = 0;
|
||||
for(const auto* p = str; *p != 0; ++p)
|
||||
{
|
||||
update(*p);
|
||||
if(++n >= max_n) break; // prevent infinite loop
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline fnv1a_hasher&
|
||||
fnv1a_hasher::update(const char* data, size_t size)
|
||||
{
|
||||
for(size_t i = 0; i < size; ++i)
|
||||
{
|
||||
m_result ^= static_cast<uint8_t>(data[i]);
|
||||
// Note: Arithmetic overflow of unsigned integers is well defined in C++ standard unlike
|
||||
// signed integers.
|
||||
m_result *= kFnv1a64Prime;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Tp, typename... TailT>
|
||||
uint64_t
|
||||
fnv1a_hasher::combine(Tp&& arg, TailT&&... args)
|
||||
{
|
||||
return fnv1a_hasher{}.update_all(std::forward<Tp>(arg), std::forward<TailT>(args)...).digest();
|
||||
}
|
||||
|
||||
template <typename Tp, typename... TailT>
|
||||
fnv1a_hasher&
|
||||
fnv1a_hasher::update_all(Tp&& arg, TailT&&... args)
|
||||
{
|
||||
update(arg);
|
||||
if constexpr(sizeof...(TailT) > 0) update_all(std::forward<TailT>(args)...);
|
||||
return *this;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,230 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/logging.hpp"
|
||||
#include "lib/common/environment.hpp"
|
||||
#include "lib/common/filesystem.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <glog/logging.h>
|
||||
#include <glog/vlog_is_on.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
namespace fs = ::rocprofiler::common::filesystem;
|
||||
|
||||
void
|
||||
install_failure_signal_handler()
|
||||
{
|
||||
static auto _once = std::once_flag{};
|
||||
std::call_once(_once, []() { google::InstallFailureSignalHandler(); });
|
||||
}
|
||||
|
||||
struct log_level_info
|
||||
{
|
||||
int32_t google_level = 0;
|
||||
int32_t verbose_level = 0;
|
||||
};
|
||||
|
||||
env_store
|
||||
get_glog_env_config(const logging_config& cfg)
|
||||
{
|
||||
auto as_env_config = [](std::string_view _var, auto _val) {
|
||||
return env_config{std::string{_var}, fmt::format("{}", _val), 1};
|
||||
};
|
||||
|
||||
auto _data = std::vector<env_config>{
|
||||
as_env_config("GLOG_minloglevel", cfg.loglevel),
|
||||
as_env_config("GLOG_logtostderr", cfg.logtostderr ? 1 : 0),
|
||||
as_env_config("GLOG_alsologtostderr", cfg.alsologtostderr ? 1 : 0),
|
||||
as_env_config("GLOG_stderrthreshold", cfg.loglevel),
|
||||
as_env_config("GLOG_v", cfg.vlog_level),
|
||||
};
|
||||
|
||||
if(!cfg.logdir.empty())
|
||||
{
|
||||
_data.emplace_back(as_env_config("GOOGLE_LOG_DIR", cfg.logdir));
|
||||
_data.emplace_back(as_env_config("GLOG_log_dir", cfg.logdir));
|
||||
}
|
||||
if(!cfg.vlog_modules.empty())
|
||||
{
|
||||
_data.emplace_back(as_env_config("GLOG_vmodule", cfg.vlog_modules));
|
||||
}
|
||||
|
||||
return env_store{std::move(_data)};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
init_logging(std::string_view env_prefix, logging_config cfg)
|
||||
{
|
||||
static auto _once = std::once_flag{};
|
||||
std::call_once(_once, [env_prefix, &cfg]() {
|
||||
auto get_argv0 = []() {
|
||||
auto ifs = std::ifstream{"/proc/self/cmdline"};
|
||||
auto sarg = std::string{};
|
||||
while(ifs && !ifs.eof())
|
||||
{
|
||||
ifs >> sarg;
|
||||
if(!sarg.empty()) break;
|
||||
}
|
||||
return sarg;
|
||||
};
|
||||
|
||||
auto to_lower = [](std::string val) {
|
||||
for(auto& itr : val)
|
||||
itr = tolower(itr);
|
||||
return val;
|
||||
};
|
||||
|
||||
const auto env_opts = std::unordered_map<std::string_view, log_level_info>{
|
||||
{"trace", {google::INFO, ROCP_LOG_LEVEL_TRACE}},
|
||||
{"info", {google::INFO, ROCP_LOG_LEVEL_INFO}},
|
||||
{"warning", {google::WARNING, ROCP_LOG_LEVEL_WARNING}},
|
||||
{"error", {google::ERROR, ROCP_LOG_LEVEL_ERROR}},
|
||||
{"fatal", {google::FATAL, ROCP_LOG_LEVEL_NONE}}};
|
||||
|
||||
auto supported = std::vector<std::string>{};
|
||||
supported.reserve(env_opts.size());
|
||||
for(auto itr : env_opts)
|
||||
supported.emplace_back(itr.first);
|
||||
|
||||
if(cfg.name.empty()) cfg.name = to_lower(std::string{env_prefix});
|
||||
|
||||
cfg.logdir = get_env(fmt::format("{}_LOG_DIR", env_prefix), cfg.logdir);
|
||||
cfg.vlog_modules = get_env(fmt::format("{}_vmodule", env_prefix), cfg.vlog_modules);
|
||||
cfg.logtostderr = cfg.logdir.empty(); // log to stderr if no log dir set
|
||||
// cfg.alsologtostderr = !cfg.logdir.empty(); // log to file if log dir set
|
||||
|
||||
auto loglvl = to_lower(common::get_env(fmt::format("{}_LOG_LEVEL", env_prefix), ""));
|
||||
// default to warning
|
||||
auto& loglvl_v = cfg.loglevel;
|
||||
auto& vlog_level = cfg.vlog_level;
|
||||
if(!loglvl.empty() && loglvl.find_first_not_of("-0123456789") == std::string::npos)
|
||||
{
|
||||
auto val = std::stol(loglvl);
|
||||
if(val < 0)
|
||||
{
|
||||
loglvl_v = google::FATAL;
|
||||
vlog_level = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
// default to trace in case val > ROCP_LOG_LEVEL_TRACE
|
||||
auto itr = env_opts.at("trace");
|
||||
for(auto oitr : env_opts)
|
||||
{
|
||||
if(oitr.second.verbose_level == val)
|
||||
{
|
||||
itr = oitr.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
loglvl_v = itr.google_level;
|
||||
vlog_level = itr.verbose_level;
|
||||
}
|
||||
}
|
||||
else if(!loglvl.empty())
|
||||
{
|
||||
if(env_opts.find(loglvl) == env_opts.end())
|
||||
throw std::runtime_error{fmt::format(
|
||||
"invalid specifier for {}_LOG_LEVEL: {}. Supported: {}",
|
||||
env_prefix,
|
||||
loglvl,
|
||||
fmt::format("{}", fmt::join(supported.begin(), supported.end(), ", ")))};
|
||||
else
|
||||
{
|
||||
loglvl_v = env_opts.at(loglvl).google_level;
|
||||
vlog_level = env_opts.at(loglvl).verbose_level;
|
||||
}
|
||||
}
|
||||
|
||||
auto _env_store = get_glog_env_config(cfg);
|
||||
|
||||
update_logging(cfg);
|
||||
_env_store.push();
|
||||
|
||||
if(!google::IsGoogleLoggingInitialized())
|
||||
{
|
||||
static auto argv0 = get_argv0();
|
||||
// Prevent glog from crashing if vmodule is empty
|
||||
if(FLAGS_vmodule.empty()) FLAGS_vmodule = " ";
|
||||
|
||||
google::InitGoogleLogging(argv0.c_str());
|
||||
|
||||
// Swap out memory to avoid leaking the string
|
||||
if(!FLAGS_vmodule.empty()) std::string{}.swap(FLAGS_vmodule);
|
||||
if(!FLAGS_log_dir.empty()) std::string{}.swap(FLAGS_log_dir);
|
||||
}
|
||||
|
||||
update_logging(cfg);
|
||||
|
||||
ROCP_INFO << "logging initialized via " << fmt::format("{}_LOG_LEVEL", env_prefix)
|
||||
<< ". Log Level: " << loglvl << ". Verbose Log Level: " << vlog_level;
|
||||
|
||||
_env_store.pop(false);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
update_logging(const logging_config& cfg)
|
||||
{
|
||||
static auto _mtx = std::mutex{};
|
||||
auto _lk = std::unique_lock<std::mutex>{_mtx};
|
||||
|
||||
FLAGS_timestamp_in_logfile_name = false;
|
||||
FLAGS_logtostderr = cfg.logtostderr;
|
||||
FLAGS_minloglevel = cfg.loglevel;
|
||||
FLAGS_stderrthreshold = cfg.loglevel;
|
||||
FLAGS_alsologtostderr = cfg.alsologtostderr;
|
||||
FLAGS_v = cfg.vlog_level;
|
||||
|
||||
// if(!cfg.logdir.empty()) FLAGS_log_dir = cfg.logdir.c_str();
|
||||
|
||||
if(cfg.install_failure_handler) install_failure_signal_handler();
|
||||
|
||||
if(!cfg.logdir.empty() && !fs::exists(cfg.logdir))
|
||||
{
|
||||
fs::create_directories(cfg.logdir);
|
||||
|
||||
if(cfg.logdir_gitignore)
|
||||
{
|
||||
auto ignore = fs::path{cfg.logdir} / ".gitignore";
|
||||
if(!fs::exists(ignore))
|
||||
{
|
||||
std::ofstream ofs{ignore.string()};
|
||||
ofs << "/**" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,84 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/defines.hpp"
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#define ROCP_LOG_LEVEL_TRACE 4
|
||||
#define ROCP_LOG_LEVEL_INFO 3
|
||||
#define ROCP_LOG_LEVEL_WARNING 2
|
||||
#define ROCP_LOG_LEVEL_ERROR 1
|
||||
#define ROCP_LOG_LEVEL_NONE 0
|
||||
|
||||
#define ROCP_TRACE VLOG(ROCP_LOG_LEVEL_TRACE)
|
||||
#define ROCP_INFO LOG(INFO)
|
||||
#define ROCP_WARNING LOG(WARNING)
|
||||
#define ROCP_ERROR LOG(ERROR)
|
||||
#define ROCP_FATAL LOG(FATAL)
|
||||
#define ROCP_DFATAL DLOG(FATAL)
|
||||
|
||||
#define ROCP_TRACE_IF(CONDITION) VLOG_IF(ROCP_LOG_LEVEL_TRACE, (CONDITION))
|
||||
#define ROCP_INFO_IF(CONDITION) LOG_IF(INFO, (CONDITION))
|
||||
#define ROCP_WARNING_IF(CONDITION) LOG_IF(WARNING, (CONDITION))
|
||||
#define ROCP_ERROR_IF(CONDITION) LOG_IF(ERROR, (CONDITION))
|
||||
#define ROCP_FATAL_IF(CONDITION) LOG_IF(FATAL, (CONDITION))
|
||||
#define ROCP_DFATAL_IF(CONDITION) DLOG_IF(FATAL, (CONDITION))
|
||||
|
||||
#if defined(ROCPROFILER_CI)
|
||||
# define ROCP_CI_LOG_IF(NON_CI_LEVEL, ...) ROCP_FATAL_IF(__VA_ARGS__)
|
||||
# define ROCP_CI_LOG(NON_CI_LEVEL, ...) ROCP_FATAL
|
||||
#else
|
||||
# define ROCP_CI_LOG_IF(NON_CI_LEVEL, ...) ROCP_##NON_CI_LEVEL##_IF(__VA_ARGS__)
|
||||
# define ROCP_CI_LOG(NON_CI_LEVEL, ...) ROCP_##NON_CI_LEVEL
|
||||
#endif
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
struct logging_config
|
||||
{
|
||||
bool install_failure_handler = false;
|
||||
bool logtostderr = true;
|
||||
bool alsologtostderr = false;
|
||||
bool logdir_gitignore = false; // add .gitignore to logdir
|
||||
int32_t loglevel = google::WARNING;
|
||||
int32_t vlog_level = ROCP_LOG_LEVEL_WARNING;
|
||||
std::string vlog_modules = {};
|
||||
std::string name = {};
|
||||
std::string logdir = {};
|
||||
};
|
||||
|
||||
void
|
||||
init_logging(std::string_view env_prefix, logging_config cfg = logging_config{});
|
||||
|
||||
void
|
||||
update_logging(const logging_config& cfg);
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,378 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2025 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 "lib/common/md5sum.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <iomanip>
|
||||
#include <string_view>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using size_type = typename md5sum::size_type;
|
||||
|
||||
// Constants for md5sumTransform routine.
|
||||
constexpr uint32_t S11 = 7;
|
||||
constexpr uint32_t S12 = 12;
|
||||
constexpr uint32_t S13 = 17;
|
||||
constexpr uint32_t S14 = 22;
|
||||
constexpr uint32_t S21 = 5;
|
||||
constexpr uint32_t S22 = 9;
|
||||
constexpr uint32_t S23 = 14;
|
||||
constexpr uint32_t S24 = 20;
|
||||
constexpr uint32_t S31 = 4;
|
||||
constexpr uint32_t S32 = 11;
|
||||
constexpr uint32_t S33 = 16;
|
||||
constexpr uint32_t S34 = 23;
|
||||
constexpr uint32_t S41 = 6;
|
||||
constexpr uint32_t S42 = 10;
|
||||
constexpr uint32_t S43 = 15;
|
||||
constexpr uint32_t S44 = 21;
|
||||
|
||||
// low level logic operations
|
||||
static inline uint32_t
|
||||
F(uint32_t x, uint32_t y, uint32_t z);
|
||||
|
||||
static inline uint32_t
|
||||
G(uint32_t x, uint32_t y, uint32_t z);
|
||||
|
||||
static inline uint32_t
|
||||
H(uint32_t x, uint32_t y, uint32_t z);
|
||||
|
||||
static inline uint32_t
|
||||
I(uint32_t x, uint32_t y, uint32_t z);
|
||||
|
||||
static inline uint32_t
|
||||
rotate_left(uint32_t x, int n);
|
||||
|
||||
static inline void
|
||||
FF(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
|
||||
|
||||
static inline void
|
||||
GG(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
|
||||
|
||||
static inline void
|
||||
HH(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
|
||||
|
||||
static inline void
|
||||
II(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
|
||||
|
||||
// F, G, H and I are basic md5sum functions.
|
||||
inline uint32_t
|
||||
F(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return (x & y) | (~x & z);
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
G(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return (x & z) | (y & ~z);
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
H(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
I(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return y ^ (x | ~z);
|
||||
}
|
||||
|
||||
// rotate_left rotates x left n bits.
|
||||
inline uint32_t
|
||||
rotate_left(uint32_t x, int n)
|
||||
{
|
||||
return (x << n) | (x >> (32 - n));
|
||||
}
|
||||
|
||||
// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||
// Rotation is separate from addition to prevent recomputation.
|
||||
inline void
|
||||
FF(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
|
||||
{
|
||||
a = rotate_left(a + F(b, c, d) + x + ac, s) + b;
|
||||
}
|
||||
|
||||
inline void
|
||||
GG(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
|
||||
{
|
||||
a = rotate_left(a + G(b, c, d) + x + ac, s) + b;
|
||||
}
|
||||
|
||||
inline void
|
||||
HH(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
|
||||
{
|
||||
a = rotate_left(a + H(b, c, d) + x + ac, s) + b;
|
||||
}
|
||||
|
||||
inline void
|
||||
II(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
|
||||
{
|
||||
a = rotate_left(a + I(b, c, d) + x + ac, s) + b;
|
||||
}
|
||||
|
||||
// decodes input (unsigned char) into output (uint32_t). Assumes len is a multiple of 4.
|
||||
void
|
||||
decode(uint32_t output[], const uint8_t input[], size_type len)
|
||||
{
|
||||
for(unsigned int i = 0, j = 0; j < len; i++, j += 4)
|
||||
output[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
|
||||
(((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
|
||||
}
|
||||
|
||||
// encodes input (uint32_t) into output (unsigned char). Assumes len is
|
||||
// a multiple of 4.
|
||||
void
|
||||
encode(uint8_t output[], const uint32_t input[], size_type len)
|
||||
{
|
||||
for(size_type i = 0, j = 0; j < len; i++, j += 4)
|
||||
{
|
||||
output[j] = input[i] & 0xff;
|
||||
output[j + 1] = (input[i] >> 8) & 0xff;
|
||||
output[j + 2] = (input[i] >> 16) & 0xff;
|
||||
output[j + 3] = (input[i] >> 24) & 0xff;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// apply md5sum algo on a block
|
||||
void
|
||||
md5sum::transform(const uint8_t block[blocksize])
|
||||
{
|
||||
uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||
decode(x, block, blocksize);
|
||||
|
||||
/* Round 1 */
|
||||
FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
|
||||
FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
|
||||
FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
|
||||
FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
|
||||
FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
|
||||
FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
|
||||
FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
|
||||
FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
|
||||
FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
|
||||
FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
|
||||
FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||
FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||
FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||
FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||
FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||
FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
|
||||
GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
|
||||
GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||
GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
|
||||
GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
|
||||
GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||
GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||
GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
|
||||
GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
|
||||
GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||
GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
|
||||
GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
|
||||
GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||
GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
|
||||
GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
|
||||
GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
|
||||
HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
|
||||
HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||
HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||
HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
|
||||
HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
|
||||
HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
|
||||
HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||
HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||
HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
|
||||
HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
|
||||
HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
|
||||
HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
|
||||
HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||
HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||
HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
|
||||
II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
|
||||
II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||
II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
|
||||
II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||
II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
|
||||
II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||
II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
|
||||
II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
|
||||
II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||
II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
|
||||
II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||
II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
|
||||
II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||
II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
|
||||
II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
|
||||
// Zeroize sensitive information.
|
||||
memset(x, 0, sizeof x);
|
||||
}
|
||||
|
||||
md5sum&
|
||||
md5sum::update(std::string_view inp)
|
||||
{
|
||||
return update(inp.data(), inp.length());
|
||||
}
|
||||
|
||||
// md5sum block update operation. Continues an md5sum message-digest
|
||||
// operation, processing another message block
|
||||
md5sum&
|
||||
md5sum::update(const unsigned char input[], size_type length)
|
||||
{
|
||||
// compute number of bytes mod 64
|
||||
size_type index = count[0] / 8 % blocksize;
|
||||
|
||||
// Update number of bits
|
||||
if((count[0] += (length << 3)) < (length << 3)) count[1]++;
|
||||
count[1] += (length >> 29);
|
||||
|
||||
// number of bytes we need to fill in buffer
|
||||
size_type firstpart = 64 - index;
|
||||
size_type i = 0;
|
||||
|
||||
// transform as many times as possible.
|
||||
if(length >= firstpart)
|
||||
{
|
||||
// fill buffer first, transform
|
||||
memcpy(&buffer[index], input, firstpart);
|
||||
transform(buffer.data());
|
||||
|
||||
// transform chunks of blocksize (64 bytes)
|
||||
for(i = firstpart; i + blocksize <= length; i += blocksize)
|
||||
transform(&input[i]);
|
||||
|
||||
index = 0;
|
||||
}
|
||||
|
||||
// buffer remaining input
|
||||
memcpy(&buffer[index], &input[i], length - i);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// for convenience provide a verson with signed char
|
||||
md5sum&
|
||||
md5sum::update(const char input[], size_type length)
|
||||
{
|
||||
return update((const unsigned char*) input, length);
|
||||
}
|
||||
|
||||
// md5sum finalization. Ends an md5sum message-digest operation, writing the
|
||||
// the message digest and zeroizing the context.
|
||||
md5sum&
|
||||
md5sum::finalize()
|
||||
{
|
||||
static unsigned char padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
if(!finalized)
|
||||
{
|
||||
// Save number of bits
|
||||
unsigned char bits[8];
|
||||
encode(bits, count.data(), 8);
|
||||
|
||||
// pad out to 56 mod 64.
|
||||
size_type index = count[0] / 8 % 64;
|
||||
size_type padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||
update(padding, padLen);
|
||||
|
||||
// Append length (before padding)
|
||||
update(bits, 8);
|
||||
|
||||
// Store state in digest
|
||||
encode(digest.data(), state.data(), 16);
|
||||
|
||||
// Zeroize sensitive information.
|
||||
memset(buffer.data(), 0, sizeof buffer);
|
||||
memset(count.data(), 0, sizeof count);
|
||||
|
||||
finalized = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// return hex representation of digest as string
|
||||
std::string
|
||||
md5sum::hexdigest() const
|
||||
{
|
||||
if(!finalized) return std::string{};
|
||||
|
||||
char buf[33];
|
||||
for(int i = 0; i < 16; i++)
|
||||
snprintf(buf + i * 2, 3, "%02x", digest[i]);
|
||||
buf[32] = '\0';
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
std::string
|
||||
md5sum::hexliteral() const
|
||||
{
|
||||
if(!finalized) return std::string{};
|
||||
|
||||
auto _oss = std::ostringstream{};
|
||||
_oss << "X'";
|
||||
for(auto itr : rawdigest())
|
||||
_oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(itr);
|
||||
_oss << "'";
|
||||
return _oss.str();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& out, md5sum md5)
|
||||
{
|
||||
return out << md5.hexdigest();
|
||||
}
|
||||
|
||||
std::string
|
||||
compute_md5sum(std::string_view inp)
|
||||
{
|
||||
return md5sum{inp}.finalize().hexdigest();
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,138 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2025 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 "lib/common/defines.hpp"
|
||||
#include "lib/common/mpl.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
// helper function
|
||||
std::string
|
||||
compute_md5sum(std::string_view inp);
|
||||
|
||||
// helper function for array of string, string_view, or const char*
|
||||
template <template <typename, typename...> class ContainerT, typename Tp, typename... TailT>
|
||||
std::string
|
||||
compute_md5sum(const ContainerT<Tp, TailT...>& inp,
|
||||
std::enable_if_t<mpl::is_string_type<Tp>::value, int> = 0);
|
||||
|
||||
// a small class for calculating MD5 hashes of strings or byte arrays
|
||||
//
|
||||
// usage:
|
||||
// 1) feed it blocks of uchars with update(...)
|
||||
// 2) finalize()
|
||||
// 3) get hexdigest() string
|
||||
// or
|
||||
// md5sum{...}.hexdigest()
|
||||
//
|
||||
// assumes that char is 8 bit and int is 32 bit
|
||||
class md5sum
|
||||
{
|
||||
public:
|
||||
using size_type = uint32_t; // must be 32bit
|
||||
using raw_digest_t = std::array<uint8_t, 16>;
|
||||
static constexpr int blocksize = 64;
|
||||
|
||||
template <typename Tp, typename... Args>
|
||||
explicit md5sum(Tp&& arg, Args&&... args);
|
||||
|
||||
md5sum() = default;
|
||||
~md5sum() = default;
|
||||
md5sum(const md5sum&) = default;
|
||||
md5sum(md5sum&&) = default;
|
||||
|
||||
md5sum& operator=(const md5sum&) = default;
|
||||
md5sum& operator=(md5sum&&) = default;
|
||||
|
||||
md5sum& update(std::string_view inp);
|
||||
md5sum& update(const unsigned char* buf, size_type length);
|
||||
md5sum& update(const char* buf, size_type length);
|
||||
md5sum& finalize();
|
||||
std::string hexdigest() const;
|
||||
std::string hexliteral() const;
|
||||
raw_digest_t rawdigest() const { return digest; }
|
||||
|
||||
template <typename Tp, typename Up = std::enable_if_t<std::is_arithmetic<Tp>::value, int>>
|
||||
md5sum& update(Tp inp);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream&, md5sum md5);
|
||||
|
||||
private:
|
||||
void transform(const uint8_t block[blocksize]);
|
||||
|
||||
bool finalized = false;
|
||||
// 64bit counter for number of bits (lo, hi)
|
||||
std::array<uint32_t, 2> count = {0, 0};
|
||||
std::array<uint8_t, blocksize> buffer{}; // overflow bytes from last 64 byte chunk
|
||||
// digest so far, initialized to magic initialization constants.
|
||||
std::array<uint32_t, 4> state = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
|
||||
std::array<uint8_t, 16> digest{}; // result
|
||||
};
|
||||
|
||||
template <typename Tp, typename... Args>
|
||||
md5sum::md5sum(Tp&& arg, Args&&... args)
|
||||
{
|
||||
auto _update = [&](auto&& _val) {
|
||||
using value_type = common::mpl::unqualified_type_t<decltype(_val)>;
|
||||
static_assert(!std::is_pointer<value_type>::value,
|
||||
"constructor cannot be called with pointer argument");
|
||||
update(std::forward<decltype(_val)>(_val));
|
||||
};
|
||||
|
||||
_update(std::forward<Tp>(arg));
|
||||
(_update(std::forward<Args>(args)), ...);
|
||||
finalize();
|
||||
}
|
||||
|
||||
template <typename Tp, typename Up>
|
||||
md5sum&
|
||||
md5sum::update(Tp inp)
|
||||
{
|
||||
static_assert(std::is_arithmetic<Tp>::value, "expected arithmetic type");
|
||||
return update(reinterpret_cast<const char*>(&inp), sizeof(Tp));
|
||||
}
|
||||
|
||||
template <template <typename, typename...> class ContainerT, typename Tp, typename... TailT>
|
||||
std::string
|
||||
compute_md5sum(const ContainerT<Tp, TailT...>& inp,
|
||||
std::enable_if_t<mpl::is_string_type<Tp>::value, int>)
|
||||
{
|
||||
auto _val = md5sum{};
|
||||
for(const auto& itr : inp)
|
||||
_val.update(std::string_view{inp});
|
||||
_val.finalize();
|
||||
return _val.hexdigest();
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# add container sources and headers to common library target
|
||||
#
|
||||
set(memory_headers deleter.hpp pool.hpp pool_allocator.hpp stateless_allocator.hpp)
|
||||
set(memory_sources)
|
||||
|
||||
target_sources(rocprofiler-sdk-common-library PRIVATE ${memory_sources} ${memory_headers})
|
||||
@@ -0,0 +1,48 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/defines.hpp"
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace memory
|
||||
{
|
||||
// this type is a template parameter for allocators to execute a function when the allocator
|
||||
// destroys an object. In the rocprofiler library, this is used to ensure
|
||||
// rocprofiler::registration::finalize is called on the first instance of a data structure being
|
||||
// destroyed after main exits
|
||||
template <typename Tp>
|
||||
struct deleter;
|
||||
|
||||
// default deleter type
|
||||
template <>
|
||||
struct deleter<void>
|
||||
{
|
||||
constexpr void operator()() const {}
|
||||
};
|
||||
} // namespace memory
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,103 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/defines.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace memory
|
||||
{
|
||||
template <size_t BlockSize, size_t ReservedBlocks = 0>
|
||||
class pool
|
||||
{
|
||||
public:
|
||||
explicit pool(size_t size)
|
||||
: m_size(size)
|
||||
{
|
||||
for(size_t i = 0; i < ReservedBlocks; i++)
|
||||
{
|
||||
append();
|
||||
}
|
||||
}
|
||||
|
||||
void* allocate()
|
||||
{
|
||||
if(m_addrs.empty())
|
||||
{
|
||||
append();
|
||||
}
|
||||
|
||||
auto* ptr = m_addrs.top();
|
||||
m_addrs.pop();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(void* ptr) { m_addrs.push(ptr); }
|
||||
|
||||
void rebind(size_t size)
|
||||
{
|
||||
if(!(m_addrs.empty() && m_blocks.empty()))
|
||||
{
|
||||
ROCP_FATAL << "cannot call pool::rebind() after alloc";
|
||||
}
|
||||
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
private:
|
||||
// Refill the address stack by allocating another block of memory
|
||||
void append()
|
||||
{
|
||||
auto block = std::make_unique<uint8_t[]>(BlockSize);
|
||||
auto total_size = BlockSize % m_size == 0 ? BlockSize : BlockSize - m_size;
|
||||
|
||||
// Divide the block into chunks of m_size bytes, and add their addrs
|
||||
for(size_t i = 0; i < total_size; i += m_size)
|
||||
{
|
||||
m_addrs.push(&block.get()[i]);
|
||||
}
|
||||
|
||||
// Keep the memory of the block alive by adding it to our stack
|
||||
m_blocks.push(std::move(block));
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_size = {};
|
||||
std::stack<void*> m_addrs = {};
|
||||
std::stack<std::unique_ptr<uint8_t[]>> m_blocks = {};
|
||||
};
|
||||
|
||||
} // namespace memory
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,176 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/defines.hpp"
|
||||
#include "lib/common/memory/deleter.hpp"
|
||||
#include "lib/common/memory/pool.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace memory
|
||||
{
|
||||
// template <typename Tp, size_t Alignment, size_t BlockSize, size_t ReservedBlocks, typename
|
||||
// DeleterT> pool_allocator<Tp, Alignment, BlockSize, ReservedBlocks, DeleterT>::
|
||||
template <typename Tp,
|
||||
size_t Alignment = 64,
|
||||
size_t BlockSize = 4096,
|
||||
size_t ReservedBlocks = 0,
|
||||
typename DeleterT = deleter<void>>
|
||||
class pool_allocator
|
||||
{
|
||||
public:
|
||||
using value_type = Tp;
|
||||
using pointer = Tp*;
|
||||
using const_pointer = const Tp*;
|
||||
using reference = Tp&;
|
||||
using const_reference = const Tp&;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using propagate_on_container_move_assignment = std::false_type;
|
||||
using is_always_equal = value_type;
|
||||
|
||||
pool_allocator() = default;
|
||||
|
||||
// Rebind copy constructor
|
||||
template <typename Up>
|
||||
pool_allocator(const pool_allocator<Up>& rhs);
|
||||
|
||||
pool_allocator(const pool_allocator& rhs) = default;
|
||||
pool_allocator(pool_allocator&& rhs) noexcept = default;
|
||||
pool_allocator& operator=(const pool_allocator& rhs) = default;
|
||||
pool_allocator& operator=(pool_allocator&& rhs) noexcept = default;
|
||||
|
||||
value_type* allocate(size_t n);
|
||||
void deallocate(value_type* ptr, size_t n);
|
||||
void construct(value_type* const _p, const value_type& _v) const;
|
||||
void construct(value_type* const _p, value_type&& _v) const;
|
||||
void construct_at(value_type* const _p, const value_type& _v) const;
|
||||
void construct_at(value_type* const _p, value_type&& _v) const;
|
||||
void destroy(value_type* const _p) const;
|
||||
void destroy_at(value_type* const _p) const;
|
||||
|
||||
template <typename Up>
|
||||
struct rebind
|
||||
{
|
||||
using other = pool_allocator<Up, Alignment, BlockSize, ReservedBlocks>;
|
||||
};
|
||||
|
||||
private:
|
||||
using pool_type = pool<BlockSize, ReservedBlocks>;
|
||||
|
||||
std::shared_ptr<pool_type> m_pool = std::make_shared<pool_type>(sizeof(value_type));
|
||||
};
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedBlocks, typename DeleterT>
|
||||
template <typename Up>
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedBlocks, DeleterT>::pool_allocator(
|
||||
const pool_allocator<Up>& rhs)
|
||||
: m_pool{rhs.m_pool}
|
||||
{
|
||||
m_pool->rebind(sizeof(value_type));
|
||||
}
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedBlocks, typename DeleterT>
|
||||
typename pool_allocator<Tp, AlignV, BlockSz, ReservedBlocks, DeleterT>::value_type*
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedBlocks, DeleterT>::allocate(size_t n)
|
||||
{
|
||||
if(n > 1)
|
||||
{
|
||||
return static_cast<value_type*>(::aligned_alloc(AlignV, sizeof(value_type) * n));
|
||||
}
|
||||
|
||||
return static_cast<value_type*>(m_pool->allocate());
|
||||
}
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedBlocks, typename DeleterT>
|
||||
void
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedBlocks, DeleterT>::deallocate(value_type* ptr, size_t n)
|
||||
{
|
||||
DeleterT{}();
|
||||
if(n > 1)
|
||||
{
|
||||
::free(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
m_pool->deallocate(ptr);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedV, typename DeleterT>
|
||||
void
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedV, DeleterT>::construct(value_type* const _p,
|
||||
const value_type& _v) const
|
||||
{
|
||||
::new((void*) _p) value_type{_v};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedV, typename DeleterT>
|
||||
void
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedV, DeleterT>::construct(value_type* const _p,
|
||||
value_type&& _v) const
|
||||
{
|
||||
::new((void*) _p) value_type{std::move(_v)};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedV, typename DeleterT>
|
||||
void
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedV, DeleterT>::construct_at(value_type* const _p,
|
||||
const value_type& _v) const
|
||||
{
|
||||
::new((void*) _p) value_type{_v};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedV, typename DeleterT>
|
||||
void
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedV, DeleterT>::construct_at(value_type* const _p,
|
||||
value_type&& _v) const
|
||||
{
|
||||
::new((void*) _p) value_type{std::move(_v)};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedV, typename DeleterT>
|
||||
void
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedV, DeleterT>::destroy(value_type* const _p) const
|
||||
{
|
||||
DeleterT{}();
|
||||
_p->~value_type();
|
||||
}
|
||||
|
||||
template <typename Tp, size_t AlignV, size_t BlockSz, size_t ReservedV, typename DeleterT>
|
||||
void
|
||||
pool_allocator<Tp, AlignV, BlockSz, ReservedV, DeleterT>::destroy_at(value_type* const _p) const
|
||||
{
|
||||
DeleterT{}();
|
||||
_p->~value_type();
|
||||
}
|
||||
} // namespace memory
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,183 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/defines.hpp"
|
||||
#include "lib/common/memory/deleter.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace memory
|
||||
{
|
||||
template <typename Tp, size_t Alignment = 64, typename DeleterT = deleter<void>>
|
||||
class stateless_allocator
|
||||
{
|
||||
public:
|
||||
using value_type = Tp;
|
||||
using pointer = Tp*;
|
||||
using const_pointer = const Tp*;
|
||||
using reference = Tp&;
|
||||
using const_reference = const Tp&;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
|
||||
template <typename Up>
|
||||
struct rebind
|
||||
{
|
||||
using other = stateless_allocator<Up, Alignment, DeleterT>;
|
||||
};
|
||||
|
||||
stateless_allocator() = default;
|
||||
stateless_allocator(const stateless_allocator& rhs) = default;
|
||||
stateless_allocator(stateless_allocator&& rhs) noexcept = default;
|
||||
stateless_allocator& operator=(const stateless_allocator& rhs) = default;
|
||||
stateless_allocator& operator=(stateless_allocator&& rhs) noexcept = default;
|
||||
|
||||
template <typename Up>
|
||||
stateless_allocator(const stateless_allocator<Up, Alignment, DeleterT>& rhs);
|
||||
|
||||
static Tp* allocate(size_t n);
|
||||
static void deallocate(Tp* ptr, size_t n);
|
||||
static void construct(value_type* const _p, const value_type& _v);
|
||||
static void construct(value_type* const _p, value_type&& _v);
|
||||
static void construct_at(value_type* const _p, const value_type& _v);
|
||||
static void construct_at(value_type* const _p, value_type&& _v);
|
||||
static void destroy(value_type* const _p);
|
||||
static void destroy_at(value_type* const _p);
|
||||
};
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
template <typename Up>
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::stateless_allocator(
|
||||
const stateless_allocator<Up, Alignment, DeleterT>& rhs)
|
||||
{
|
||||
(void) rhs;
|
||||
}
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
Tp*
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::allocate(size_t n)
|
||||
{
|
||||
constexpr auto alignment_v = Alignment / sizeof(void*);
|
||||
Tp* ptr = nullptr;
|
||||
|
||||
if constexpr(sizeof(Tp) >= alignment_v && sizeof(Tp) % alignment_v == 0)
|
||||
ptr = static_cast<Tp*>(::aligned_alloc(Alignment / sizeof(void*), sizeof(Tp) * n));
|
||||
else
|
||||
ptr = static_cast<Tp*>(::malloc(sizeof(Tp) * n));
|
||||
|
||||
if(ptr) return ptr;
|
||||
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
void
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::deallocate(Tp* ptr, size_t n)
|
||||
{
|
||||
(void) n;
|
||||
::free(ptr);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
void
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::construct(value_type* const _p, const value_type& _v)
|
||||
{
|
||||
::new((void*) _p) value_type{_v};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
void
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::construct(value_type* const _p, value_type&& _v)
|
||||
{
|
||||
::new((void*) _p) value_type{std::move(_v)};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
void
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::construct_at(value_type* const _p,
|
||||
const value_type& _v)
|
||||
{
|
||||
::new((void*) _p) value_type{_v};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
void
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::construct_at(value_type* const _p, value_type&& _v)
|
||||
{
|
||||
::new((void*) _p) value_type{std::move(_v)};
|
||||
}
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
void
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::destroy(value_type* const _p)
|
||||
{
|
||||
DeleterT{}();
|
||||
_p->~value_type();
|
||||
}
|
||||
|
||||
template <typename Tp, size_t Alignment, typename DeleterT>
|
||||
void
|
||||
stateless_allocator<Tp, Alignment, DeleterT>::destroy_at(value_type* const _p)
|
||||
{
|
||||
DeleterT{}();
|
||||
_p->~value_type();
|
||||
}
|
||||
|
||||
template <typename LhsTp,
|
||||
size_t LhsAlignment,
|
||||
typename LhsDeleterT,
|
||||
typename RhsTp,
|
||||
size_t RhsAlignment,
|
||||
typename RhsDeleterT>
|
||||
constexpr bool
|
||||
operator==(const stateless_allocator<LhsTp, LhsAlignment, LhsDeleterT>&,
|
||||
const stateless_allocator<RhsTp, RhsAlignment, RhsDeleterT>&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename LhsTp,
|
||||
size_t LhsAlignment,
|
||||
typename LhsDeleterT,
|
||||
typename RhsTp,
|
||||
size_t RhsAlignment,
|
||||
typename RhsDeleterT>
|
||||
constexpr bool
|
||||
operator!=(const stateless_allocator<LhsTp, LhsAlignment, LhsDeleterT>&,
|
||||
const stateless_allocator<RhsTp, RhsAlignment, RhsDeleterT>&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} // namespace memory
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,175 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/details/mpl.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace mpl
|
||||
{
|
||||
// dummy tuple with low instantiation cost
|
||||
template <typename... Tp>
|
||||
using type_list = impl::type_list<Tp...>;
|
||||
|
||||
/// get the index of a type in expansion
|
||||
template <typename Tp, typename Type>
|
||||
struct index_of;
|
||||
|
||||
template <typename Tp, template <typename...> class Tuple, typename... Types>
|
||||
struct index_of<Tp, Tuple<Tp, Types...>>
|
||||
{
|
||||
static constexpr size_t value = 0;
|
||||
};
|
||||
|
||||
template <typename Tp, typename Head, template <typename...> class Tuple, typename... Tail>
|
||||
struct index_of<Tp, Tuple<Head, Tail...>>
|
||||
{
|
||||
static constexpr size_t value = 1 + index_of<Tp, Tuple<Tail...>>::value;
|
||||
};
|
||||
|
||||
/// get the index of a type in expansion
|
||||
template <typename Tp>
|
||||
struct size_of;
|
||||
|
||||
template <typename... Tp>
|
||||
struct size_of<type_list<Tp...>>
|
||||
{
|
||||
static constexpr size_t value = sizeof...(Tp);
|
||||
};
|
||||
|
||||
template <typename... Tp>
|
||||
struct size_of<std::tuple<Tp...>>
|
||||
{
|
||||
static constexpr size_t value = sizeof...(Tp);
|
||||
};
|
||||
|
||||
// check if type is in expansion
|
||||
//
|
||||
template <typename... Tp>
|
||||
struct is_one_of
|
||||
{
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template <typename F, typename S, template <typename...> class Tuple, typename... T>
|
||||
struct is_one_of<F, S, Tuple<T...>>
|
||||
{
|
||||
static constexpr bool value = std::is_same<F, S>::value || is_one_of<F, Tuple<T...>>::value;
|
||||
};
|
||||
|
||||
template <typename F, typename S, template <typename...> class Tuple, typename... T>
|
||||
struct is_one_of<F, Tuple<S, T...>>
|
||||
{
|
||||
static constexpr bool value = is_one_of<F, S, Tuple<T...>>::value;
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
struct is_pair_impl
|
||||
{
|
||||
static constexpr auto value = false;
|
||||
};
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
struct is_pair_impl<std::pair<LhsT, RhsT>>
|
||||
{
|
||||
static constexpr auto value = true;
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
struct is_pair : is_pair_impl<std::remove_cv_t<std::remove_reference_t<std::decay_t<Tp>>>>
|
||||
{};
|
||||
|
||||
template <typename Tp>
|
||||
struct is_string_type_impl
|
||||
{
|
||||
static constexpr auto value =
|
||||
is_one_of<Tp, type_list<const char*, char*, std::string, std::string_view>>::value;
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
struct is_string_type
|
||||
: is_string_type_impl<std::remove_cv_t<std::remove_reference_t<std::decay_t<Tp>>>>
|
||||
{};
|
||||
|
||||
template <typename, typename = void>
|
||||
constexpr bool is_type_complete_v = false; // NOLINT(misc-definitions-in-headers)
|
||||
|
||||
template <typename T> // NOLINTNEXTLINE(misc-definitions-in-headers)
|
||||
constexpr bool is_type_complete_v<T, std::void_t<decltype(sizeof(T))>> = true;
|
||||
|
||||
template <typename Tp, size_t N>
|
||||
struct indirection_level_impl_n
|
||||
{
|
||||
using value_type = std::conditional_t<std::is_function<Tp>::value, Tp, std::decay_t<Tp>>;
|
||||
static_assert(!std::is_pointer<value_type>::value, "missing overload");
|
||||
static constexpr size_t value = N;
|
||||
};
|
||||
|
||||
template <typename Tp, size_t N>
|
||||
struct indirection_level_impl_n<Tp*, N> : indirection_level_impl_n<Tp, N + 1>
|
||||
{};
|
||||
|
||||
template <typename Tp, size_t N>
|
||||
struct indirection_level_impl_n<Tp* const, N> : indirection_level_impl_n<Tp, N + 1>
|
||||
{};
|
||||
|
||||
template <typename Tp>
|
||||
struct indirection_level
|
||||
: indirection_level_impl_n<std::remove_cv_t<std::remove_reference_t<std::decay_t<Tp>>>, 0>
|
||||
{};
|
||||
|
||||
template <typename Tp>
|
||||
struct unqualified_type
|
||||
{
|
||||
using type = std::remove_reference_t<std::remove_cv_t<std::decay_t<Tp>>>;
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
using unqualified_type_t = typename unqualified_type<Tp>::type;
|
||||
|
||||
template <typename Tp>
|
||||
struct assert_false
|
||||
{
|
||||
static constexpr auto value = false;
|
||||
};
|
||||
|
||||
template <typename InTuple>
|
||||
using reverse = typename impl::reverse<InTuple, void>::type;
|
||||
|
||||
template <typename Tp>
|
||||
using function_traits = impl::function_traits<Tp>;
|
||||
|
||||
template <typename Tp>
|
||||
using function_args_t = typename impl::function_traits<Tp>::args_type;
|
||||
} // namespace mpl
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,86 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/defines.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
struct scope_destructor
|
||||
{
|
||||
/// \fn scope_destructor(FuncT&& _fini, InitT&& _init)
|
||||
/// \tparam FuncT "std::function<void()> or void (*)()"
|
||||
/// \tparam InitT "std::function<void()> or void (*)()"
|
||||
/// \param _fini Function to execute when object is destroyed
|
||||
/// \param _init Function to execute when object is created (optional)
|
||||
///
|
||||
/// \brief Provides a utility to perform an operation when exiting a scope.
|
||||
template <typename FuncT, typename InitT = void (*)()>
|
||||
scope_destructor(
|
||||
FuncT&& _fini,
|
||||
InitT&& _init = []() {});
|
||||
|
||||
~scope_destructor() { m_functor(); }
|
||||
|
||||
// delete copy operations
|
||||
scope_destructor(const scope_destructor&) = delete;
|
||||
scope_destructor& operator=(const scope_destructor&) = delete;
|
||||
|
||||
// allow move operations
|
||||
scope_destructor(scope_destructor&& rhs) noexcept;
|
||||
scope_destructor& operator=(scope_destructor&& rhs) noexcept;
|
||||
|
||||
private:
|
||||
std::function<void()> m_functor = []() {};
|
||||
};
|
||||
|
||||
template <typename FuncT, typename InitT>
|
||||
scope_destructor::scope_destructor(FuncT&& _fini, InitT&& _init)
|
||||
: m_functor{std::forward<FuncT>(_fini)}
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
inline scope_destructor::scope_destructor(scope_destructor&& rhs) noexcept
|
||||
: m_functor{std::move(rhs.m_functor)}
|
||||
{
|
||||
rhs.m_functor = []() {};
|
||||
}
|
||||
|
||||
inline scope_destructor&
|
||||
scope_destructor::operator=(scope_destructor&& rhs) noexcept
|
||||
{
|
||||
if(this != &rhs)
|
||||
{
|
||||
m_functor = std::move(rhs.m_functor);
|
||||
rhs.m_functor = []() {};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,229 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2025 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 "lib/common/sha256.hpp"
|
||||
#include "lib/common/defines.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
#include "lib/common/mpl.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
sha256::sha256() { reset(); }
|
||||
|
||||
sha256::sha256(const std::string& data)
|
||||
{
|
||||
reset();
|
||||
update(data);
|
||||
finalize();
|
||||
}
|
||||
|
||||
void
|
||||
sha256::update(const uint8_t* data, size_t len)
|
||||
{
|
||||
ROCP_CI_LOG_IF(INFO, m_finalized) << "attempt to update sha256 after finalized";
|
||||
if(m_finalized) return;
|
||||
for(size_t i = 0; i < len; ++i)
|
||||
{
|
||||
m_data[m_datalen++] = data[i];
|
||||
if(m_datalen == 64)
|
||||
{
|
||||
transform();
|
||||
m_bitlen += 512;
|
||||
m_datalen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sha256::update(const std::string& data)
|
||||
{
|
||||
ROCP_CI_LOG_IF(INFO, m_finalized) << "attempt to update sha256 after finalized";
|
||||
if(m_finalized) return;
|
||||
update(reinterpret_cast<const uint8_t*>(data.data()), data.size());
|
||||
}
|
||||
|
||||
void
|
||||
sha256::finalize()
|
||||
{
|
||||
if(m_finalized) return;
|
||||
|
||||
uint32_t idx = m_datalen;
|
||||
if(m_datalen < 56)
|
||||
{
|
||||
m_data[idx++] = 0x80;
|
||||
while(idx < 56)
|
||||
m_data[idx++] = 0x00;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data[idx++] = 0x80;
|
||||
while(idx < 64)
|
||||
m_data[idx++] = 0x00;
|
||||
transform();
|
||||
std::memset(m_data.data(), 0, 56);
|
||||
}
|
||||
|
||||
m_bitlen += m_datalen * 8;
|
||||
for(int j = 0; j < 8; ++j)
|
||||
m_data[63 - j] = static_cast<uint8_t>((m_bitlen >> (8 * j)) & 0xFF);
|
||||
|
||||
transform();
|
||||
|
||||
m_finalized = true;
|
||||
}
|
||||
|
||||
std::string
|
||||
sha256::hexdigest()
|
||||
{
|
||||
finalize();
|
||||
|
||||
auto oss = std::ostringstream{};
|
||||
for(int j = 0; j < 8; ++j)
|
||||
oss << std::hex << std::setfill('0') << std::setw(8) << m_state[j];
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::array<uint32_t, 8>
|
||||
sha256::rawdigest()
|
||||
{
|
||||
finalize();
|
||||
|
||||
return m_state;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sha256::rotr(uint32_t x, uint32_t n)
|
||||
{
|
||||
return (x >> n) | (x << (32 - n));
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sha256::ch(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return (x & y) ^ (~x & z);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sha256::maj(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return (x & y) ^ (x & z) ^ (y & z);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sha256::sig0(uint32_t x)
|
||||
{
|
||||
return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sha256::sig1(uint32_t x)
|
||||
{
|
||||
return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sha256::theta0(uint32_t x)
|
||||
{
|
||||
return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sha256::theta1(uint32_t x)
|
||||
{
|
||||
return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
|
||||
}
|
||||
|
||||
void
|
||||
sha256::transform()
|
||||
{
|
||||
uint32_t m[64];
|
||||
for(int i = 0; i < 16; ++i)
|
||||
{
|
||||
m[i] = (m_data[i * 4] << 24) | (m_data[i * 4 + 1] << 16) | (m_data[i * 4 + 2] << 8) |
|
||||
(m_data[i * 4 + 3]);
|
||||
}
|
||||
for(int i = 16; i < 64; ++i)
|
||||
{
|
||||
m[i] = theta1(m[i - 2]) + m[i - 7] + theta0(m[i - 15]) + m[i - 16];
|
||||
}
|
||||
|
||||
uint32_t a = m_state[0];
|
||||
uint32_t b = m_state[1];
|
||||
uint32_t c = m_state[2];
|
||||
uint32_t d = m_state[3];
|
||||
uint32_t e = m_state[4];
|
||||
uint32_t f = m_state[5];
|
||||
uint32_t g = m_state[6];
|
||||
uint32_t h = m_state[7];
|
||||
|
||||
for(int i = 0; i < 64; ++i)
|
||||
{
|
||||
uint32_t t1 = h + sig1(e) + ch(e, f, g) + m_k[i] + m[i];
|
||||
uint32_t t2 = sig0(a) + maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
m_state[0] += a;
|
||||
m_state[1] += b;
|
||||
m_state[2] += c;
|
||||
m_state[3] += d;
|
||||
m_state[4] += e;
|
||||
m_state[5] += f;
|
||||
m_state[6] += g;
|
||||
m_state[7] += h;
|
||||
}
|
||||
|
||||
void
|
||||
sha256::reset()
|
||||
{
|
||||
m_state = {0x6a09e667,
|
||||
0xbb67ae85,
|
||||
0x3c6ef372,
|
||||
0xa54ff53a,
|
||||
0x510e527f,
|
||||
0x9b05688c,
|
||||
0x1f83d9ab,
|
||||
0x5be0cd19};
|
||||
m_datalen = 0;
|
||||
m_bitlen = 0;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,81 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2025 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 "lib/common/defines.hpp"
|
||||
#include "lib/common/mpl.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
// --- SHA-256 Implementation ---
|
||||
class sha256
|
||||
{
|
||||
public:
|
||||
sha256();
|
||||
explicit sha256(const std::string& data);
|
||||
|
||||
void update(const uint8_t* data, size_t len);
|
||||
void update(const std::string& data);
|
||||
|
||||
void finalize();
|
||||
std::string hexdigest();
|
||||
std::array<uint32_t, 8> rawdigest();
|
||||
|
||||
private:
|
||||
bool m_finalized = false;
|
||||
std::array<uint8_t, 64> m_data = {};
|
||||
std::array<uint32_t, 8> m_state = {};
|
||||
uint32_t m_datalen = 0;
|
||||
uint64_t m_bitlen = 0;
|
||||
|
||||
static constexpr std::array<uint32_t, 64> m_k = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
|
||||
0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
|
||||
0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
|
||||
0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
|
||||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
||||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
|
||||
0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
|
||||
0xc67178f2};
|
||||
|
||||
static uint32_t rotr(uint32_t x, uint32_t n);
|
||||
static uint32_t ch(uint32_t x, uint32_t y, uint32_t z);
|
||||
static uint32_t maj(uint32_t x, uint32_t y, uint32_t z);
|
||||
static uint32_t sig0(uint32_t x);
|
||||
static uint32_t sig1(uint32_t x);
|
||||
static uint32_t theta0(uint32_t x);
|
||||
static uint32_t theta1(uint32_t x);
|
||||
|
||||
void transform();
|
||||
void reset();
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,93 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 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 "lib/common/simple_timer.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
simple_timer::simple_timer(std::string&& label)
|
||||
: m_label{std::move(label)}
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
||||
simple_timer::simple_timer(std::string&& label, defer_start)
|
||||
: m_label{std::move(label)}
|
||||
{}
|
||||
|
||||
simple_timer::~simple_timer()
|
||||
{
|
||||
if(m_quiet)
|
||||
return;
|
||||
else if(m_end <= m_beg)
|
||||
stop();
|
||||
|
||||
ROCP_WARNING << fmt::format("{} :: {:12.6f} sec", m_label, get());
|
||||
}
|
||||
|
||||
void
|
||||
simple_timer::start()
|
||||
{
|
||||
m_beg = clock_type::now();
|
||||
}
|
||||
|
||||
void
|
||||
simple_timer::stop()
|
||||
{
|
||||
m_end = clock_type::now();
|
||||
}
|
||||
|
||||
double
|
||||
simple_timer::get() const
|
||||
{
|
||||
if(m_end <= m_beg) return {};
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(m_end - m_beg).count();
|
||||
}
|
||||
|
||||
size_t
|
||||
simple_timer::get_nsec() const
|
||||
{
|
||||
if(m_end <= m_beg) return {};
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(m_end - m_beg).count();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& _os, const simple_timer& _val)
|
||||
{
|
||||
_val.set_quiet(true);
|
||||
_os << fmt::format("{} :: {:12.6f} sec", _val.label(), _val.get());
|
||||
|
||||
return _os;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,67 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 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 "lib/common/defines.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
struct defer_start
|
||||
{};
|
||||
|
||||
struct simple_timer
|
||||
{
|
||||
using duration_t = std::chrono::duration<double>;
|
||||
|
||||
explicit simple_timer(std::string&& label);
|
||||
explicit simple_timer(std::string&& label, defer_start);
|
||||
~simple_timer();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
double get() const;
|
||||
size_t get_nsec() const;
|
||||
std::string_view label() const { return std::string_view{m_label}; }
|
||||
void set_quiet(bool v) const { m_quiet = v; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& _os, const simple_timer& _val);
|
||||
|
||||
private:
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
using time_point_t = std::chrono::time_point<clock_type, std::chrono::nanoseconds>;
|
||||
|
||||
std::string m_label = {};
|
||||
time_point_t m_beg = {};
|
||||
time_point_t m_end = {};
|
||||
mutable bool m_quiet = false;
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,79 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/static_object.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <stack>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto*&
|
||||
get_static_object_stack()
|
||||
{
|
||||
static auto* _v = new std::stack<static_dtor_func_t>{};
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
destroy_static_objects()
|
||||
{
|
||||
static auto _sync = std::mutex{};
|
||||
auto _lk = std::unique_lock<std::mutex>{_sync};
|
||||
|
||||
auto*& _stack = get_static_object_stack();
|
||||
if(_stack)
|
||||
{
|
||||
while(!_stack->empty())
|
||||
{
|
||||
auto& itr = _stack->top();
|
||||
if(itr) itr();
|
||||
_stack->pop();
|
||||
}
|
||||
|
||||
delete _stack;
|
||||
_stack = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
register_static_dtor(static_dtor_func_t&& _func)
|
||||
{
|
||||
static auto _sync = std::mutex{};
|
||||
auto _lk = std::unique_lock<std::mutex>{_sync};
|
||||
|
||||
auto*& _stack = get_static_object_stack();
|
||||
if(_stack)
|
||||
{
|
||||
_stack->push(_func);
|
||||
}
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,136 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/defines.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
using static_dtor_func_t = void (*)();
|
||||
|
||||
void
|
||||
destroy_static_objects();
|
||||
|
||||
void
|
||||
register_static_dtor(static_dtor_func_t&&);
|
||||
|
||||
namespace
|
||||
{
|
||||
struct anonymous
|
||||
{};
|
||||
} // namespace
|
||||
|
||||
template <typename Tp>
|
||||
constexpr size_t
|
||||
static_buffer_size()
|
||||
{
|
||||
constexpr auto type_sz = sizeof(Tp);
|
||||
constexpr auto void_sz = sizeof(void*);
|
||||
if constexpr(void_sz > type_sz) return void_sz;
|
||||
return type_sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This struct is used to create static singleton objects which have the properties of a
|
||||
* heap-allocated static object without a memory leak.
|
||||
*
|
||||
* @tparam Tp Data type of singleton
|
||||
* @tparam ContextT Use to differentiate singletons in different translation units (if using default
|
||||
* parameter) or ensure the singleton can be accessed in different translation units (not
|
||||
* recommended) as long as this type is not in an anonymous namespace
|
||||
*
|
||||
* This template works by creating a buffer of at least `sizeof(Tp)` bytes in the binary and does a
|
||||
* placement new into that buffer. The object created is NOT heap allocated, the address of the
|
||||
* object is an address in between the library load address and the load address + size of library.
|
||||
*/
|
||||
template <typename Tp, typename ContextT = anonymous>
|
||||
struct static_object
|
||||
{
|
||||
static_object() = delete;
|
||||
~static_object() = delete;
|
||||
static_object(const static_object&) = delete;
|
||||
static_object(static_object&&) noexcept = delete;
|
||||
static_object& operator=(const static_object&) = delete;
|
||||
static_object& operator=(static_object&&) noexcept = delete;
|
||||
|
||||
template <typename... Args>
|
||||
static Tp*& construct(Args&&... args);
|
||||
|
||||
static Tp* get() { return m_object; }
|
||||
|
||||
static constexpr bool is_trivial_standard_layout();
|
||||
|
||||
private:
|
||||
static Tp* m_object;
|
||||
static std::array<std::byte, static_buffer_size<Tp>()> m_buffer;
|
||||
};
|
||||
|
||||
template <typename Tp, typename ContextT>
|
||||
Tp* static_object<Tp, ContextT>::m_object = nullptr;
|
||||
|
||||
template <typename Tp, typename ContextT>
|
||||
std::array<std::byte, static_buffer_size<Tp>()> static_object<Tp, ContextT>::m_buffer = {};
|
||||
|
||||
template <typename Tp, typename ContextT>
|
||||
constexpr bool
|
||||
static_object<Tp, ContextT>::is_trivial_standard_layout()
|
||||
{
|
||||
return (std::is_standard_layout<Tp>::value && std::is_trivially_destructible<Tp>::value);
|
||||
}
|
||||
|
||||
template <typename Tp, typename ContextT>
|
||||
template <typename... Args>
|
||||
Tp*&
|
||||
static_object<Tp, ContextT>::construct(Args&&... args)
|
||||
{
|
||||
if constexpr(!is_trivial_standard_layout())
|
||||
{
|
||||
static auto _once = std::once_flag{};
|
||||
std::call_once(_once, []() {
|
||||
register_static_dtor([]() {
|
||||
if(static_object<Tp, ContextT>::m_object)
|
||||
{
|
||||
static_object<Tp, ContextT>::m_object->~Tp();
|
||||
static_object<Tp, ContextT>::m_object = nullptr;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ROCP_FATAL_IF(m_object)
|
||||
<< "reconstructing static object. Use get() function to retrieve pointer";
|
||||
|
||||
m_object = new(m_buffer.data()) Tp{std::forward<Args>(args)...};
|
||||
return m_object;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,78 @@
|
||||
// MIT License
|
||||
//
|
||||
// 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 "lib/common/static_tl_object.hpp"
|
||||
#include "lib/common/scope_destructor.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <stack>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto*&
|
||||
get_static_tl_object_stack()
|
||||
{
|
||||
static thread_local auto* _v = new std::stack<static_dtor_func_t>{};
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
thread_local auto _static_tl_object_dtor = std::optional<scope_destructor>{};
|
||||
|
||||
void
|
||||
destroy_static_tl_objects()
|
||||
{
|
||||
if(auto*& _stack = get_static_tl_object_stack(); _stack)
|
||||
{
|
||||
while(!_stack->empty())
|
||||
{
|
||||
auto& itr = _stack->top();
|
||||
if(itr) itr();
|
||||
_stack->pop();
|
||||
}
|
||||
|
||||
delete _stack;
|
||||
_stack = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
register_static_tl_dtor(static_dtor_func_t&& _func)
|
||||
{
|
||||
// make sure the thread-local scope destructor exists
|
||||
if(!_static_tl_object_dtor)
|
||||
_static_tl_object_dtor = scope_destructor{[]() { destroy_static_tl_objects(); }};
|
||||
|
||||
if(auto*& _stack = get_static_tl_object_stack(); _stack)
|
||||
{
|
||||
_stack->push(_func);
|
||||
}
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,120 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/defines.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
#include "lib/common/static_object.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
void
|
||||
destroy_static_tl_objects();
|
||||
|
||||
void
|
||||
register_static_tl_dtor(static_dtor_func_t&&);
|
||||
|
||||
/**
|
||||
* @brief This struct is used to create static singleton objects which have the properties of a
|
||||
* heap-allocated static object without a memory leak.
|
||||
*
|
||||
* @tparam Tp Data type of singleton
|
||||
* @tparam ContextT Use to differentiate singletons in different translation units (if using default
|
||||
* parameter) or ensure the singleton can be accessed in different translation units (not
|
||||
* recommended) as long as this type is not in an anonymous namespace
|
||||
*
|
||||
* This template works by creating a buffer of at least `sizeof(Tp)` bytes in the binary and does a
|
||||
* placement new into that buffer. The object created is NOT heap allocated, the address of the
|
||||
* object is an address in between the library load address and the load address + size of library.
|
||||
*/
|
||||
template <typename Tp, typename ContextT = anonymous>
|
||||
struct static_tl_object
|
||||
{
|
||||
static_tl_object() = delete;
|
||||
~static_tl_object() = delete;
|
||||
static_tl_object(const static_tl_object&) = delete;
|
||||
static_tl_object(static_tl_object&&) noexcept = delete;
|
||||
static_tl_object& operator=(const static_tl_object&) = delete;
|
||||
static_tl_object& operator=(static_tl_object&&) noexcept = delete;
|
||||
|
||||
template <typename... Args>
|
||||
static Tp*& construct(Args&&... args);
|
||||
|
||||
static Tp* get() { return m_object; }
|
||||
|
||||
static constexpr bool is_trivial_standard_layout();
|
||||
|
||||
private:
|
||||
static thread_local Tp* m_object;
|
||||
static thread_local std::array<std::byte, static_buffer_size<Tp>()> m_buffer;
|
||||
};
|
||||
|
||||
template <typename Tp, typename ContextT>
|
||||
thread_local Tp* static_tl_object<Tp, ContextT>::m_object = nullptr;
|
||||
|
||||
template <typename Tp, typename ContextT>
|
||||
thread_local std::array<std::byte, static_buffer_size<Tp>()>
|
||||
static_tl_object<Tp, ContextT>::m_buffer = {};
|
||||
|
||||
template <typename Tp, typename ContextT>
|
||||
constexpr bool
|
||||
static_tl_object<Tp, ContextT>::is_trivial_standard_layout()
|
||||
{
|
||||
return (std::is_standard_layout<Tp>::value && std::is_trivially_destructible<Tp>::value);
|
||||
}
|
||||
|
||||
template <typename Tp, typename ContextT>
|
||||
template <typename... Args>
|
||||
Tp*&
|
||||
static_tl_object<Tp, ContextT>::construct(Args&&... args)
|
||||
{
|
||||
if constexpr(!is_trivial_standard_layout())
|
||||
{
|
||||
static thread_local auto _once = std::once_flag{};
|
||||
std::call_once(_once, []() {
|
||||
register_static_tl_dtor([]() {
|
||||
if(static_tl_object<Tp, ContextT>::m_object)
|
||||
{
|
||||
static_tl_object<Tp, ContextT>::m_object->~Tp();
|
||||
static_tl_object<Tp, ContextT>::m_object = nullptr;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ROCP_FATAL_IF(m_object)
|
||||
<< "reconstructing static object. Use get() function to retrieve pointer";
|
||||
|
||||
m_object = new(m_buffer.data()) Tp{std::forward<Args>(args)...};
|
||||
return m_object;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,102 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/string_entry.hpp"
|
||||
#include "lib/common/scope_destructor.hpp"
|
||||
#include "lib/common/static_object.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using name_array_t = std::unordered_map<size_t, std::unique_ptr<std::string>>;
|
||||
|
||||
auto&
|
||||
get_sync()
|
||||
{
|
||||
static auto*& _v = static_object<std::shared_mutex>::construct();
|
||||
return *CHECK_NOTNULL(_v);
|
||||
}
|
||||
|
||||
name_array_t*
|
||||
get_string_array()
|
||||
{
|
||||
static auto*& _v = static_object<name_array_t>::construct();
|
||||
return _v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const std::string*
|
||||
get_string_entry(std::string_view name)
|
||||
{
|
||||
if(!get_string_array()) return nullptr;
|
||||
|
||||
auto _hash_v = std::hash<std::string_view>{}(name);
|
||||
{
|
||||
auto _lk = std::shared_lock<std::shared_mutex>{get_sync()};
|
||||
if(get_string_array()->count(_hash_v) > 0) return get_string_array()->at(_hash_v).get();
|
||||
}
|
||||
|
||||
auto _lk = std::unique_lock<std::shared_mutex>{get_sync()};
|
||||
return get_string_array()
|
||||
->emplace(_hash_v, std::make_unique<std::string>(name))
|
||||
.first->second.get();
|
||||
}
|
||||
|
||||
const std::string*
|
||||
get_string_entry(size_t _hash_v)
|
||||
{
|
||||
if(!get_string_array()) return nullptr;
|
||||
|
||||
auto _lk = std::shared_lock<std::shared_mutex>{get_sync()};
|
||||
if(get_string_array()->count(_hash_v) > 0) return get_string_array()->at(_hash_v).get();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t
|
||||
add_string_entry(std::string_view name)
|
||||
{
|
||||
if(!get_string_array()) return 0;
|
||||
|
||||
auto _hash_v = std::hash<std::string_view>{}(name);
|
||||
{
|
||||
auto _lk = std::shared_lock<std::shared_mutex>{get_sync()};
|
||||
if(get_string_array()->count(_hash_v) > 0) return _hash_v;
|
||||
}
|
||||
|
||||
auto _lk = std::unique_lock<std::shared_mutex>{get_sync()};
|
||||
get_string_array()->emplace(_hash_v, std::make_unique<std::string>(name));
|
||||
|
||||
return _hash_v;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,42 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
const std::string*
|
||||
get_string_entry(std::string_view name);
|
||||
|
||||
const std::string*
|
||||
get_string_entry(size_t hash);
|
||||
|
||||
size_t
|
||||
add_string_entry(std::string_view name);
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,128 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/container/small_vector.hpp"
|
||||
#include "lib/common/mpl.hpp"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
struct stringified_argument
|
||||
{
|
||||
int32_t indirection_level = 0;
|
||||
int32_t dereference_count = 0;
|
||||
const char* type = nullptr;
|
||||
const char* name = nullptr;
|
||||
std::string value = {};
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
using stringified_argument_array_t =
|
||||
container::small_vector<stringified_argument, std::min<size_t>(N, 6)>;
|
||||
|
||||
template <typename Tp, typename FuncT>
|
||||
auto
|
||||
stringize_arg_impl(const Tp& _v, const int32_t max_deref, int32_t& deref_cnt, FuncT&& impl)
|
||||
{
|
||||
using value_type = std::decay_t<Tp>;
|
||||
using nonpointer_type = std::remove_pointer_t<Tp>;
|
||||
|
||||
if constexpr(common::mpl::is_string_type<value_type>::value &&
|
||||
!std::is_pointer<nonpointer_type>::value)
|
||||
{
|
||||
if constexpr(std::is_pointer<value_type>::value)
|
||||
{
|
||||
if(!_v) return std::string{"(null)"};
|
||||
}
|
||||
|
||||
return std::string{_v};
|
||||
}
|
||||
else if constexpr(fmt::is_formattable<value_type>::value && !std::is_pointer<value_type>::value)
|
||||
{
|
||||
return fmt::format("{}", _v);
|
||||
}
|
||||
else if constexpr(std::is_pointer<value_type>::value &&
|
||||
!std::is_pointer<nonpointer_type>::value &&
|
||||
common::mpl::is_type_complete_v<nonpointer_type> &&
|
||||
!std::is_void<nonpointer_type>::value)
|
||||
{
|
||||
if(_v && deref_cnt < max_deref)
|
||||
return stringize_arg_impl(*_v, max_deref, ++deref_cnt, std::forward<FuncT>(impl));
|
||||
else if(_v)
|
||||
return std::forward<FuncT>(impl)(_v);
|
||||
else
|
||||
return std::string{"(null)"};
|
||||
}
|
||||
else if constexpr(std::is_pointer<value_type>::value && std::is_pointer<nonpointer_type>::value)
|
||||
{
|
||||
using next_nonpointer_type = std::remove_pointer_t<nonpointer_type>;
|
||||
|
||||
if(_v)
|
||||
{
|
||||
if constexpr(!std::is_void<next_nonpointer_type>::value)
|
||||
{
|
||||
if(deref_cnt < max_deref)
|
||||
return stringize_arg_impl(
|
||||
*_v, max_deref, ++deref_cnt, std::forward<FuncT>(impl));
|
||||
else
|
||||
return std::forward<FuncT>(impl)(_v);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::forward<FuncT>(impl)(_v);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::string{"(null)"};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::forward<FuncT>(impl)(_v);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, typename FuncT>
|
||||
common::stringified_argument
|
||||
stringize_arg(int32_t max_deref, const std::pair<const char*, Tp>& arg, FuncT&& impl)
|
||||
{
|
||||
auto _arg = common::stringified_argument{};
|
||||
_arg.indirection_level = mpl::indirection_level<Tp>::value;
|
||||
_arg.type = typeid(Tp).name();
|
||||
_arg.name = arg.first;
|
||||
_arg.value = stringize_arg_impl(
|
||||
arg.second, max_deref, _arg.dereference_count, std::forward<FuncT>(impl));
|
||||
return _arg;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,181 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 <cstddef>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
/**
|
||||
* Sychronized is a wrapper that adds lock based write/read
|
||||
* protection around a datatype. The protected data is accessed
|
||||
* only by rlock/wlock. rlock(lambda) gets a reader lock of the
|
||||
* protected value, passing the protected value to the lambda as a
|
||||
* const. wlock(lambda) gets a writer lock on the protective value
|
||||
* and does the same. The reason for this class is to make it less
|
||||
* error prone to access shared data and more obvious when a lock
|
||||
* is being held.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* Synchronized<int> x(9);
|
||||
* x.rlock([](const auto& data){
|
||||
* // data = 9
|
||||
* });
|
||||
*
|
||||
* x.wlock([](auto& data){
|
||||
* // set data to new value
|
||||
* });
|
||||
*/
|
||||
template <typename LockedType, bool IsMappedTypeV = false>
|
||||
class Synchronized
|
||||
{
|
||||
public:
|
||||
using value_type = LockedType;
|
||||
using this_type = Synchronized<value_type, IsMappedTypeV>;
|
||||
|
||||
Synchronized() = default;
|
||||
~Synchronized() = default;
|
||||
|
||||
explicit Synchronized(value_type&& data)
|
||||
: m_data{std::move(data)}
|
||||
{}
|
||||
|
||||
Synchronized(Synchronized&& data) noexcept = default;
|
||||
Synchronized& operator=(Synchronized&& data) noexcept = default;
|
||||
|
||||
// Do not allow this data structure to be copied, std::move only.
|
||||
Synchronized(const Synchronized&) = delete;
|
||||
|
||||
// return a copy of the data
|
||||
value_type get() const;
|
||||
|
||||
template <typename FuncT, typename... Args>
|
||||
decltype(auto) rlock(FuncT&& lambda, Args&&... args) const;
|
||||
|
||||
template <typename FuncT, typename... Args>
|
||||
decltype(auto) wlock(FuncT&& lambda, Args&&... args);
|
||||
|
||||
// This overload to wlock allows a synchronized map whose keys map to synchronized data to
|
||||
// use a read lock on the key data and then a write lock on the mapped data.
|
||||
template <typename FuncT,
|
||||
typename... Args,
|
||||
bool EnableForMappedType = IsMappedTypeV,
|
||||
std::enable_if_t<EnableForMappedType, int> = 0>
|
||||
decltype(auto) wlock(FuncT&& lambda, Args&&... args) const;
|
||||
|
||||
// Upgradable lock. If read returns false, write will be called with a unique_lock.
|
||||
// Essentially a helper function that does .rlock() followed by .wlock().
|
||||
template <typename ReadFuncT, typename WriteFuncT, typename... Args>
|
||||
bool ulock(ReadFuncT&& read, WriteFuncT&& write, Args&&... args);
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex m_mutex = {};
|
||||
value_type m_data = {};
|
||||
};
|
||||
|
||||
//
|
||||
// member definitions
|
||||
//
|
||||
template <typename LockedType, bool IsMappedTypeV>
|
||||
typename Synchronized<LockedType, IsMappedTypeV>::value_type
|
||||
Synchronized<LockedType, IsMappedTypeV>::get() const
|
||||
{
|
||||
auto lock = std::shared_lock{m_mutex};
|
||||
return m_data;
|
||||
}
|
||||
|
||||
template <typename LockedType, bool IsMappedTypeV>
|
||||
template <typename FuncT, typename... Args>
|
||||
decltype(auto)
|
||||
Synchronized<LockedType, IsMappedTypeV>::rlock(FuncT&& lambda, Args&&... args) const
|
||||
{
|
||||
static_assert(std::is_invocable<FuncT, const value_type&, Args...>::value,
|
||||
"function must accept const reference to locked type");
|
||||
|
||||
auto lock = std::shared_lock{m_mutex};
|
||||
return std::forward<FuncT>(lambda)(m_data, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename LockedType, bool IsMappedTypeV>
|
||||
template <typename FuncT, typename... Args>
|
||||
decltype(auto)
|
||||
Synchronized<LockedType, IsMappedTypeV>::wlock(FuncT&& lambda, Args&&... args)
|
||||
{
|
||||
static_assert(std::is_invocable<FuncT, value_type&, Args...>::value,
|
||||
"function must accept reference to locked type");
|
||||
|
||||
auto lock = std::unique_lock{m_mutex};
|
||||
return std::forward<FuncT>(lambda)(m_data, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// This overload to wlock allows a synchronized map whose keys map to synchronized data to
|
||||
// use a read lock on the key data and then a write lock on the mapped data.
|
||||
template <typename LockedType, bool IsMappedTypeV>
|
||||
template <typename FuncT,
|
||||
typename... Args,
|
||||
bool EnableForMappedType,
|
||||
std::enable_if_t<EnableForMappedType, int>>
|
||||
decltype(auto)
|
||||
Synchronized<LockedType, IsMappedTypeV>::wlock(FuncT&& lambda, Args&&... args) const
|
||||
{
|
||||
return const_cast<this_type*>(this)->wlock(std::forward<FuncT>(lambda),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Upgradable lock. If read returns false, write will be called with a unique_lock.
|
||||
// Essentially a helper function that does .rlock() followed by .wlock().
|
||||
template <typename LockedType, bool IsMappedTypeV>
|
||||
template <typename ReadFuncT, typename WriteFuncT, typename... Args>
|
||||
bool
|
||||
Synchronized<LockedType, IsMappedTypeV>::ulock(ReadFuncT&& read, WriteFuncT&& write, Args&&... args)
|
||||
{
|
||||
static_assert(std::is_invocable<ReadFuncT, const value_type&, Args...>::value,
|
||||
"read function must accept const reference to locked type");
|
||||
static_assert(std::is_invocable<WriteFuncT, value_type&, Args...>::value,
|
||||
"write function must accept reference to locked type");
|
||||
|
||||
using read_return_type = std::invoke_result_t<ReadFuncT, const value_type&, Args...>;
|
||||
using write_return_type = std::invoke_result_t<WriteFuncT, value_type&, Args...>;
|
||||
|
||||
static_assert(std::is_same<read_return_type, write_return_type>::value,
|
||||
"read and write functions must return same type");
|
||||
static_assert(std::is_same<read_return_type, bool>::value,
|
||||
"read/write functions must return bool");
|
||||
|
||||
{
|
||||
auto lock = std::shared_lock{m_mutex};
|
||||
if(read(m_data, std::forward<Args>(args)...)) return true;
|
||||
}
|
||||
|
||||
auto lock = std::unique_lock{m_mutex};
|
||||
return write(m_data, std::forward<Args>(args)...);
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,371 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/environment.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace units
|
||||
{
|
||||
static constexpr int64_t nsec = 1;
|
||||
static constexpr int64_t usec = 1000 * nsec;
|
||||
static constexpr int64_t msec = 1000 * usec;
|
||||
static constexpr int64_t csec = 10 * msec;
|
||||
static constexpr int64_t dsec = 10 * csec;
|
||||
static constexpr int64_t sec = 10 * dsec;
|
||||
static constexpr int64_t minute = 60 * sec;
|
||||
static constexpr int64_t hour = 60 * minute;
|
||||
|
||||
static constexpr int64_t byte = 1;
|
||||
static constexpr int64_t kilobyte = 1000 * byte;
|
||||
static constexpr int64_t megabyte = 1000 * kilobyte;
|
||||
static constexpr int64_t gigabyte = 1000 * megabyte;
|
||||
static constexpr int64_t terabyte = 1000 * gigabyte;
|
||||
static constexpr int64_t petabyte = 1000 * terabyte;
|
||||
|
||||
static constexpr int64_t kibibyte = 1024 * byte;
|
||||
static constexpr int64_t mebibyte = 1024 * kibibyte;
|
||||
static constexpr int64_t gibibyte = 1024 * mebibyte;
|
||||
static constexpr int64_t tebibyte = 1024 * gibibyte;
|
||||
static constexpr int64_t pebibyte = 1024 * tebibyte;
|
||||
|
||||
static constexpr int64_t B = 1;
|
||||
static constexpr int64_t KB = 1000 * B;
|
||||
static constexpr int64_t MB = 1000 * KB;
|
||||
static constexpr int64_t GB = 1000 * MB;
|
||||
static constexpr int64_t TB = 1000 * GB;
|
||||
static constexpr int64_t PB = 1000 * TB;
|
||||
|
||||
static constexpr int64_t Bi = 1;
|
||||
static constexpr int64_t KiB = 1024 * Bi;
|
||||
static constexpr int64_t MiB = 1024 * KiB;
|
||||
static constexpr int64_t GiB = 1024 * MiB;
|
||||
static constexpr int64_t TiB = 1024 * GiB;
|
||||
static constexpr int64_t PiB = 1024 * TiB;
|
||||
|
||||
static constexpr int64_t nanowatt = 1;
|
||||
static constexpr int64_t microwatt = 1000 * nanowatt;
|
||||
static constexpr int64_t milliwatt = 1000 * microwatt;
|
||||
static constexpr int64_t watt = 1000 * milliwatt;
|
||||
static constexpr int64_t kilowatt = 1000 * watt;
|
||||
static constexpr int64_t megawatt = 1000 * kilowatt;
|
||||
static constexpr int64_t gigawatt = 1000 * megawatt;
|
||||
|
||||
static constexpr int64_t hertz = 1;
|
||||
static constexpr int64_t kilohertz = 1000 * hertz;
|
||||
static constexpr int64_t megahertz = 1000 * kilohertz;
|
||||
static constexpr int64_t gigahertz = 1000 * megahertz;
|
||||
|
||||
static constexpr int64_t Hz = 1;
|
||||
static constexpr int64_t KHz = 1000 * Hz;
|
||||
static constexpr int64_t MHz = 1000 * KHz;
|
||||
static constexpr int64_t GHz = 1000 * MHz;
|
||||
|
||||
inline int64_t
|
||||
get_page_size()
|
||||
{
|
||||
static auto _pagesz = sysconf(_SC_PAGESIZE);
|
||||
return _pagesz;
|
||||
}
|
||||
|
||||
const int64_t clocks_per_sec = sysconf(_SC_CLK_TCK);
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::string
|
||||
time_repr(int64_t _unit)
|
||||
{
|
||||
switch(_unit)
|
||||
{
|
||||
case nsec: return "nsec"; break;
|
||||
case usec: return "usec"; break;
|
||||
case msec: return "msec"; break;
|
||||
case csec: return "csec"; break;
|
||||
case dsec: return "dsec"; break;
|
||||
case sec: return "sec"; break;
|
||||
default: return "UNK"; break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::string
|
||||
mem_repr(int64_t _unit)
|
||||
{
|
||||
switch(_unit)
|
||||
{
|
||||
case byte: return "B"; break;
|
||||
case kilobyte: return "KB"; break;
|
||||
case megabyte: return "MB"; break;
|
||||
case gigabyte: return "GB"; break;
|
||||
case terabyte: return "TB"; break;
|
||||
case petabyte: return "PB"; break;
|
||||
case kibibyte: return "KiB"; break;
|
||||
case mebibyte: return "MiB"; break;
|
||||
case gibibyte: return "GiB"; break;
|
||||
case tebibyte: return "TiB"; break;
|
||||
case pebibyte: return "PiB"; break;
|
||||
default: return "UNK"; break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::string
|
||||
freq_repr(int64_t _unit)
|
||||
{
|
||||
switch(_unit)
|
||||
{
|
||||
case hertz: return "Hz"; break;
|
||||
case kilohertz: return "KHz"; break;
|
||||
case megahertz: return "MHz"; break;
|
||||
case gigahertz: return "GHz"; break;
|
||||
default: return "UNK"; break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::string
|
||||
power_repr(int64_t _unit)
|
||||
{
|
||||
switch(_unit)
|
||||
{
|
||||
case nanowatt: return "nanowatts"; break;
|
||||
case microwatt: return "microwatts"; break;
|
||||
case milliwatt: return "milliwatts"; break;
|
||||
case watt: return "watts"; break;
|
||||
case kilowatt: return "kilowatts"; break;
|
||||
case megawatt: return "megawatts"; break;
|
||||
case gigawatt: return "gigawatts"; break;
|
||||
default: return "UNK"; break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::tuple<std::string, int64_t>
|
||||
get_memory_unit(std::string _unit)
|
||||
{
|
||||
using string_t = std::string;
|
||||
using return_type = std::tuple<string_t, int64_t>;
|
||||
using inner_t = std::tuple<string_t, string_t, int64_t>;
|
||||
|
||||
if(_unit.empty()) return return_type{"MB", units::megabyte};
|
||||
|
||||
for(auto& itr : _unit)
|
||||
itr = tolower(itr);
|
||||
|
||||
for(const auto& itr : {inner_t{"byte", "b", units::byte},
|
||||
inner_t{"kilobyte", "kb", units::kilobyte},
|
||||
inner_t{"megabyte", "mb", units::megabyte},
|
||||
inner_t{"gigabyte", "gb", units::gigabyte},
|
||||
inner_t{"terabyte", "tb", units::terabyte},
|
||||
inner_t{"petabyte", "pb", units::petabyte},
|
||||
inner_t{"kibibyte", "kib", units::KiB},
|
||||
inner_t{"mebibyte", "mib", units::MiB},
|
||||
inner_t{"gibibyte", "gib", units::GiB},
|
||||
inner_t{"tebibyte", "tib", units::TiB},
|
||||
inner_t{"pebibyte", "pib", units::PiB}})
|
||||
{
|
||||
if(_unit == std::get<0>(itr) || _unit == std::get<1>(itr))
|
||||
{
|
||||
if(std::get<2>(itr) == units::byte)
|
||||
return return_type{std::get<0>(itr), std::get<2>(itr)};
|
||||
return return_type{mem_repr(std::get<2>(itr)), std::get<2>(itr)};
|
||||
}
|
||||
}
|
||||
|
||||
ROCP_WARNING << "Warning!! No memory unit matching \"" << _unit << "\". Using default...\n";
|
||||
|
||||
return return_type{"MB", units::megabyte};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::tuple<std::string, int64_t>
|
||||
get_timing_unit(std::string _unit)
|
||||
{
|
||||
using string_t = std::string;
|
||||
using strset_t = std::unordered_set<string_t>;
|
||||
using return_type = std::tuple<string_t, int64_t>;
|
||||
using inner_t = std::tuple<string_t, strset_t, int64_t>;
|
||||
|
||||
if(_unit.empty()) return return_type{"sec", units::sec};
|
||||
|
||||
for(auto& itr : _unit)
|
||||
itr = tolower(itr);
|
||||
|
||||
for(const auto& itr :
|
||||
{inner_t{"nsec", strset_t{"ns", "nanosecond", "nanoseconds"}, units::nsec},
|
||||
inner_t{"usec", strset_t{"us", "microsecond", "microseconds"}, units::usec},
|
||||
inner_t{"msec", strset_t{"ms", "millisecond", "milliseconds"}, units::msec},
|
||||
inner_t{"csec", strset_t{"cs", "centisecond", "centiseconds"}, units::csec},
|
||||
inner_t{"dsec", strset_t{"ds", "decisecond", "deciseconds"}, units::dsec},
|
||||
inner_t{"sec", strset_t{"s", "second", "seconds"}, units::sec},
|
||||
inner_t{"min", strset_t{"minute", "minutes"}, units::minute},
|
||||
inner_t{"hr", strset_t{"hr", "hour", "hours"}, units::hour}})
|
||||
{
|
||||
if(_unit == std::get<0>(itr) || std::get<1>(itr).find(_unit) != std::get<1>(itr).end())
|
||||
{
|
||||
return return_type{time_repr(std::get<2>(itr)), std::get<2>(itr)};
|
||||
}
|
||||
}
|
||||
|
||||
ROCP_WARNING << "Warning!! No timing unit matching \"" << _unit << "\". Using default...\n";
|
||||
|
||||
return return_type{"sec", units::sec};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::tuple<std::string, int64_t>
|
||||
get_frequncy_unit(std::string _unit)
|
||||
{
|
||||
using string_t = std::string;
|
||||
using return_type = std::tuple<string_t, int64_t>;
|
||||
using inner_t = std::tuple<string_t, string_t, int64_t>;
|
||||
|
||||
if(_unit.empty()) return return_type{"MHz", units::megahertz};
|
||||
|
||||
for(auto& itr : _unit)
|
||||
itr = tolower(itr);
|
||||
|
||||
for(const auto& itr : {inner_t{"hertz", "hz", units::hertz},
|
||||
inner_t{"kilohertz", "khz", units::kilohertz},
|
||||
inner_t{"megahertz", "mhz", units::megahertz},
|
||||
inner_t{"gigahertz", "ghz", units::gigahertz}})
|
||||
{
|
||||
if(_unit == std::get<0>(itr) || _unit == std::get<1>(itr))
|
||||
{
|
||||
return return_type{freq_repr(std::get<2>(itr)), std::get<2>(itr)};
|
||||
}
|
||||
}
|
||||
|
||||
ROCP_WARNING << "Warning!! No frequency unit matching \"" << _unit << "\". Using default...\n";
|
||||
|
||||
return return_type{"MHz", units::megahertz};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::tuple<std::string, int64_t>
|
||||
get_power_unit(const std::string& _unit)
|
||||
{
|
||||
using string_t = std::string;
|
||||
using return_type = std::tuple<string_t, int64_t>;
|
||||
using inner_t = std::tuple<string_t, string_t, int64_t>;
|
||||
|
||||
if(_unit.empty()) return return_type{"watts", units::watt};
|
||||
|
||||
auto _lunit = _unit;
|
||||
for(auto& itr : _lunit)
|
||||
itr = tolower(itr);
|
||||
|
||||
for(const auto& itr : {inner_t{"nanowatt", "nW", units::nanowatt},
|
||||
inner_t{"microwatt", "uW", units::microwatt},
|
||||
inner_t{"milliwatt", "mW", units::milliwatt},
|
||||
inner_t{"watt", "W", units::watt},
|
||||
inner_t{"kilowatt", "KW", units::kilowatt},
|
||||
inner_t{"megawatt", "MW", units::megawatt},
|
||||
inner_t{"gigawatt", "GW", units::gigawatt}})
|
||||
{
|
||||
if(_lunit == std::get<0>(itr) || _lunit + "s" == std::get<0>(itr) ||
|
||||
_unit == std::get<1>(itr))
|
||||
{
|
||||
return return_type{power_repr(std::get<2>(itr)), std::get<2>(itr)};
|
||||
}
|
||||
}
|
||||
|
||||
ROCP_WARNING << "Warning!! No power unit matching \"" << _unit << "\". Using default...\n";
|
||||
|
||||
return return_type{"watts", units::watt};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
namespace temperature
|
||||
{
|
||||
enum unit_system : int8_t
|
||||
{
|
||||
Celsius = 0,
|
||||
Fahrenheit,
|
||||
Kelvin
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
Tp
|
||||
convert(Tp _v, unit_system _from, unit_system _to)
|
||||
{
|
||||
switch(_from)
|
||||
{
|
||||
case Celsius:
|
||||
{
|
||||
switch(_to)
|
||||
{
|
||||
case Celsius: return _v;
|
||||
case Fahrenheit: return static_cast<Tp>((_v * 1.8) + 32);
|
||||
case Kelvin: return (_v - 273);
|
||||
}
|
||||
}
|
||||
case Fahrenheit:
|
||||
{
|
||||
switch(_to)
|
||||
{
|
||||
case Celsius: return static_cast<Tp>((_v - 32) / 1.8);
|
||||
case Fahrenheit: return _v;
|
||||
case Kelvin: return (_v - 273);
|
||||
}
|
||||
}
|
||||
case Kelvin:
|
||||
{
|
||||
switch(_to)
|
||||
{
|
||||
case Celsius: return (_v + 273);
|
||||
case Fahrenheit: return static_cast<Tp>(((_v + 273) * 1.8) + 32);
|
||||
case Kelvin: return _v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace temperature
|
||||
} // namespace units
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,160 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-2025 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 "lib/common/utility.hpp"
|
||||
#include "lib/common/defines.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::string_view
|
||||
get_clock_name(clockid_t _id)
|
||||
{
|
||||
#define CLOCK_NAME_CASE_STATEMENT(NAME) \
|
||||
case NAME: return #NAME;
|
||||
switch(_id)
|
||||
{
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_REALTIME)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_MONOTONIC)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_PROCESS_CPUTIME_ID)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_THREAD_CPUTIME_ID)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_MONOTONIC_RAW)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_REALTIME_COARSE)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_MONOTONIC_COARSE)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_BOOTTIME)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_REALTIME_ALARM)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_BOOTTIME_ALARM)
|
||||
CLOCK_NAME_CASE_STATEMENT(CLOCK_TAI)
|
||||
default: break;
|
||||
}
|
||||
return "CLOCK_UNKNOWN";
|
||||
#undef CLOCK_NAME_CASE_STATEMENT
|
||||
}
|
||||
|
||||
auto _process_init_ns = timestamp_ns();
|
||||
} // namespace
|
||||
|
||||
uint64_t
|
||||
get_clock_period_ns_impl(clockid_t _clk_id)
|
||||
{
|
||||
constexpr auto nanosec = std::nano::den;
|
||||
|
||||
struct timespec ts;
|
||||
auto ret = clock_getres(_clk_id, &ts);
|
||||
|
||||
if(ROCPROFILER_UNLIKELY(ret != 0))
|
||||
{
|
||||
auto _err = errno;
|
||||
ROCP_FATAL << "error getting clock resolution for " << get_clock_name(_clk_id) << ": "
|
||||
<< strerror(_err);
|
||||
}
|
||||
else if(ROCPROFILER_UNLIKELY(ts.tv_sec != 0 ||
|
||||
ts.tv_nsec >= std::numeric_limits<uint32_t>::max()))
|
||||
{
|
||||
ROCP_FATAL << "clock_getres(" << get_clock_name(_clk_id)
|
||||
<< ") returned very low frequency (<1Hz)";
|
||||
}
|
||||
|
||||
return (static_cast<uint64_t>(ts.tv_sec) * nanosec) + static_cast<uint64_t>(ts.tv_nsec);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
get_process_start_time_ns(pid_t _pid)
|
||||
{
|
||||
if(_pid == getpid()) return _process_init_ns;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
read_command_line(pid_t _pid)
|
||||
{
|
||||
auto _cmdline = std::vector<std::string>{};
|
||||
auto fcmdline = std::stringstream{};
|
||||
fcmdline << "/proc/" << _pid << "/cmdline";
|
||||
auto ifs = std::ifstream{fcmdline.str().c_str()};
|
||||
if(ifs)
|
||||
{
|
||||
char cstr;
|
||||
std::string sarg;
|
||||
while(!ifs.eof())
|
||||
{
|
||||
ifs >> cstr;
|
||||
if(!ifs.eof())
|
||||
{
|
||||
if(cstr != '\0')
|
||||
{
|
||||
sarg += cstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cmdline.push_back(sarg);
|
||||
sarg = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
return _cmdline;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
|
||||
namespace
|
||||
{
|
||||
std::atomic<bool>&
|
||||
debugger_block()
|
||||
{
|
||||
static std::atomic<bool> block = {true};
|
||||
return block;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
void
|
||||
rocprofiler_debugger_block()
|
||||
{
|
||||
while(debugger_block().load() == true)
|
||||
{};
|
||||
// debugger_block().exchange(true);
|
||||
}
|
||||
|
||||
void
|
||||
rocprofiler_debugger_continue()
|
||||
{
|
||||
debugger_block().exchange(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023-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 "lib/common/defines.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
template <typename... Tp>
|
||||
void
|
||||
consume_args(Tp&&...)
|
||||
{}
|
||||
|
||||
uint64_t
|
||||
get_clock_period_ns_impl(clockid_t _clk_id);
|
||||
|
||||
inline uint64_t
|
||||
get_tid()
|
||||
{
|
||||
// system calls are expensive so store this in a thread-local
|
||||
static thread_local uint64_t _v = ::syscall(__NR_gettid);
|
||||
return _v;
|
||||
}
|
||||
|
||||
inline uint64_t
|
||||
get_ticks(clockid_t clk_id_v) noexcept
|
||||
{
|
||||
constexpr auto nanosec = std::nano::den;
|
||||
auto&& ts = timespec{};
|
||||
auto ret = clock_gettime(clk_id_v, &ts);
|
||||
|
||||
if(ROCPROFILER_UNLIKELY(ret != 0))
|
||||
{
|
||||
auto _err = errno;
|
||||
ROCP_FATAL << "clock_gettime failed: " << strerror(_err);
|
||||
}
|
||||
|
||||
return (static_cast<uint64_t>(ts.tv_sec) * nanosec) + static_cast<uint64_t>(ts.tv_nsec);
|
||||
}
|
||||
|
||||
static constexpr int default_clock_id = CLOCK_BOOTTIME;
|
||||
|
||||
// CLOCK_MONOTONIC_RAW equates to HSA-runtime library implementation of os::ReadAccurateClock()
|
||||
// CLOCK_BOOTTIME equates to HSA-runtime library implementation of os::ReadSystemClock()
|
||||
template <int ClockT = default_clock_id>
|
||||
inline uint64_t
|
||||
timestamp_ns()
|
||||
{
|
||||
constexpr auto _clk = ClockT;
|
||||
static auto _clk_period = get_clock_period_ns_impl(_clk);
|
||||
|
||||
if(ROCPROFILER_LIKELY(_clk_period == 1)) return get_ticks(_clk);
|
||||
return get_ticks(_clk) / _clk_period;
|
||||
}
|
||||
|
||||
// returns the process start time (in CLOCK_BOOTTIME nanoseconds) via /proc/<pid>/stat
|
||||
uint64_t
|
||||
get_process_start_time_ns(pid_t _pid);
|
||||
|
||||
std::vector<std::string>
|
||||
read_command_line(pid_t _pid);
|
||||
|
||||
template <class Container, typename Key = typename Container::key_type>
|
||||
const auto*
|
||||
get_val(const Container& map, const Key& key)
|
||||
{
|
||||
auto pos = map.find(key);
|
||||
return (pos != map.end() ? &pos->second : nullptr);
|
||||
}
|
||||
|
||||
template <class Container, typename Key = typename Container::key_type>
|
||||
auto*
|
||||
get_val(Container& map, const Key& key)
|
||||
{
|
||||
auto pos = map.find(key);
|
||||
return (pos != map.end() ? &pos->second : nullptr);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
constexpr void
|
||||
assert_public_data_type_properties()
|
||||
{
|
||||
static_assert(std::is_standard_layout<Tp>::value,
|
||||
"public data type struct should have a standard layout");
|
||||
static_assert(std::is_trivial<Tp>::value, "public data type should be trivial");
|
||||
static_assert(std::is_default_constructible<Tp>::value,
|
||||
"public data type struct should be default constructible");
|
||||
static_assert(std::is_trivially_copy_constructible<Tp>::value,
|
||||
"public data type struct should be trivially copy constructible");
|
||||
static_assert(std::is_trivially_move_constructible<Tp>::value,
|
||||
"public data type struct should be trivially move constructible");
|
||||
static_assert(std::is_trivially_copy_assignable<Tp>::value,
|
||||
"public data type struct should be trivially move assignable");
|
||||
static_assert(std::is_trivially_move_assignable<Tp>::value,
|
||||
"public data type struct should be trivially move assignable");
|
||||
static_assert(std::is_trivially_copyable<Tp>::value,
|
||||
"public data type struct should be trivially move assignable");
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
constexpr void
|
||||
assert_public_api_struct_properties()
|
||||
{
|
||||
assert_public_data_type_properties<Tp>();
|
||||
static_assert(std::is_class<Tp>::value, "this is not a public API struct");
|
||||
static_assert(offsetof(Tp, size) == 0, "public API struct should have a size field first");
|
||||
static_assert(sizeof(std::declval<Tp>().size) == sizeof(uint64_t),
|
||||
"public API struct size field should be 64 bits");
|
||||
}
|
||||
|
||||
// used to set the "size" field to the offset of the "reserved_padding" field.
|
||||
// The reserved_padding field is extra unused bytes added to the a struct to
|
||||
// avoid an ABI break if/when new fields are added. This is only done
|
||||
// for fields which are regularly passed by value
|
||||
template <typename Tp, typename Up = Tp>
|
||||
constexpr auto
|
||||
compute_runtime_sizeof(int) -> decltype(std::declval<Up>().reserved_padding, size_t{})
|
||||
{
|
||||
return offsetof(Tp, reserved_padding);
|
||||
}
|
||||
|
||||
template <typename Tp, typename Up = Tp>
|
||||
constexpr auto
|
||||
compute_runtime_sizeof(long)
|
||||
{
|
||||
return sizeof(Tp);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
constexpr auto
|
||||
compute_runtime_sizeof()
|
||||
{
|
||||
return compute_runtime_sizeof<Tp>(0);
|
||||
}
|
||||
|
||||
template <typename Tp, typename... Args>
|
||||
decltype(auto)
|
||||
init_public_api_struct(Tp&& val, Args&&... args)
|
||||
{
|
||||
assert_public_api_struct_properties<Tp>();
|
||||
|
||||
::memset(&val, 0, sizeof(Tp));
|
||||
|
||||
if constexpr(sizeof...(Args) == 0)
|
||||
val.size = compute_runtime_sizeof<Tp>();
|
||||
else
|
||||
val = {compute_runtime_sizeof<Tp>(), std::forward<Args>(args)...};
|
||||
|
||||
return std::forward<Tp>(val);
|
||||
}
|
||||
|
||||
template <typename Tp, typename... Args>
|
||||
Tp&
|
||||
init_public_api_struct(Tp& val, Args&&... args)
|
||||
{
|
||||
assert_public_api_struct_properties<Tp>();
|
||||
|
||||
::memset(&val, 0, sizeof(Tp));
|
||||
|
||||
if constexpr(sizeof...(Args) == 0)
|
||||
val.size = compute_runtime_sizeof<Tp>();
|
||||
else
|
||||
val = {compute_runtime_sizeof<Tp>(), std::forward<Args>(args)...};
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapper that will call a function when the
|
||||
* wrapper is being destroyed. This is primarily useful
|
||||
* for static variables where we want to run some destruction
|
||||
* operations when the program exits.
|
||||
*/
|
||||
template <typename Tp>
|
||||
class static_cleanup_wrapper
|
||||
{
|
||||
public:
|
||||
using data_type = Tp;
|
||||
using functor_type = std::function<void(Tp&)>;
|
||||
|
||||
static_cleanup_wrapper(data_type&& data, functor_type&& destroy_func)
|
||||
: m_data(std::move(data))
|
||||
, m_destroy_func(std::move(destroy_func))
|
||||
{}
|
||||
|
||||
static_cleanup_wrapper(functor_type&& destroy_func)
|
||||
: m_destroy_func(std::move(destroy_func))
|
||||
{}
|
||||
|
||||
~static_cleanup_wrapper() { m_destroy_func(m_data); }
|
||||
|
||||
void destroy() { m_destroy_func(m_data); }
|
||||
|
||||
data_type& get() { return m_data; }
|
||||
const data_type& get() const { return m_data; }
|
||||
|
||||
private:
|
||||
data_type m_data = {};
|
||||
functor_type m_destroy_func = {};
|
||||
};
|
||||
|
||||
template <typename Tp = long, typename RatioT = std::ratio<1, 1000>>
|
||||
void
|
||||
yield(std::chrono::duration<Tp, RatioT> duration = std::chrono::milliseconds{10})
|
||||
{
|
||||
std::this_thread::yield();
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
template <typename PredicateT, typename Tp = long, typename RatioT = std::ratio<1, 1000>>
|
||||
bool
|
||||
yield(PredicateT&& predicate,
|
||||
std::chrono::duration<Tp, RatioT> max_yield_time,
|
||||
std::chrono::duration<Tp, RatioT> query_interval = std::chrono::milliseconds{10})
|
||||
{
|
||||
auto now = []() { return std::chrono::steady_clock::now(); };
|
||||
auto start = now();
|
||||
auto result = false;
|
||||
while(!(result = predicate()))
|
||||
{
|
||||
yield(query_interval);
|
||||
if((now() - start) > max_yield_time)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return the result of the last predicate query
|
||||
return result;
|
||||
}
|
||||
|
||||
class assert_single_threaded
|
||||
{
|
||||
public:
|
||||
assert_single_threaded(std::atomic<bool>& lock)
|
||||
: m_is_initialized(lock)
|
||||
{
|
||||
bool expected = false;
|
||||
if(!m_is_initialized.compare_exchange_strong(expected, true))
|
||||
{
|
||||
ROCP_FATAL << "This code must be run in a single thread!!!";
|
||||
}
|
||||
}
|
||||
|
||||
~assert_single_threaded() { m_is_initialized.store(false, std::memory_order_release); }
|
||||
|
||||
private:
|
||||
std::atomic<bool>& m_is_initialized;
|
||||
};
|
||||
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
|
||||
extern "C" {
|
||||
void
|
||||
rocprofiler_debugger_block();
|
||||
void
|
||||
rocprofiler_debugger_continue();
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2025 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 "lib/common/uuid_v7.hpp"
|
||||
#include "lib/common/defines.hpp"
|
||||
#include "lib/common/logging.hpp"
|
||||
#include "lib/common/mpl.hpp"
|
||||
#include "lib/common/sha256.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
uint64_t
|
||||
get_process_start_ticks_since_boot(pid_t pid)
|
||||
{
|
||||
auto line = std::string{};
|
||||
|
||||
// Read the stat file
|
||||
if(auto stat_file = std::ifstream{fmt::format("/proc/{}/stat", pid)}; !stat_file.is_open())
|
||||
{
|
||||
ROCP_CI_LOG(WARNING) << fmt::format("failed to open /proc/{}/stat for process start time",
|
||||
pid);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read entire line
|
||||
std::getline(stat_file, line);
|
||||
}
|
||||
|
||||
// Locate the end of the comm field (")")
|
||||
size_t rparen = line.rfind(')');
|
||||
if(rparen == std::string::npos)
|
||||
{
|
||||
ROCP_CI_LOG(WARNING) << fmt::format("Malformed stat file for pid {}", pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Tokenize fields after ") "
|
||||
auto iss = std::istringstream{line.substr(rparen + 2)};
|
||||
auto token = std::string{};
|
||||
// Skip fields 3 through 21
|
||||
for(int i = 0; i < 20; ++i)
|
||||
{
|
||||
if(!(iss >> token))
|
||||
{
|
||||
ROCP_CI_LOG(WARNING) << fmt::format("Unexpected end of /proc/{}/stat", pid);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Field 22: starttime in clock ticks since boot
|
||||
uint64_t start_ticks = 0;
|
||||
if(!(iss >> start_ticks))
|
||||
{
|
||||
ROCP_CI_LOG(WARNING) << fmt::format(
|
||||
"Unexpected end of /proc/{}/stat. Failed to read start ticks", pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return start_ticks;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
compute_system_seed(std::string_view machine_id, pid_t pid, pid_t ppid, uint64_t pstart_ticks)
|
||||
{
|
||||
// If no machine_id provided, read from /etc/machine-id
|
||||
ROCP_CI_LOG_IF(WARNING, machine_id.empty())
|
||||
<< fmt::format("compute_system_seed provided empty machine id");
|
||||
|
||||
// Hash for seed value
|
||||
return std::hash<std::string>{}(
|
||||
sha256{fmt::format("{}|{}|{}|{}", machine_id, pid, ppid, pstart_ticks)}.hexdigest());
|
||||
}
|
||||
|
||||
std::string
|
||||
generate_uuid_v7(uint64_t timestamp_ns, uint64_t seed, std::string_view delim)
|
||||
{
|
||||
constexpr auto nanosec_per_millisec = std::nano::den / std::milli::den;
|
||||
auto timestamp_ms = timestamp_ns / nanosec_per_millisec;
|
||||
|
||||
auto uuid = std::array<uint8_t, 16>{};
|
||||
|
||||
// First 6 bytes = timestamp
|
||||
for(int i = 0; i < 6; ++i)
|
||||
{
|
||||
uuid[i] = static_cast<uint8_t>((timestamp_ms >> (40 - 8 * i)) & 0xFF);
|
||||
}
|
||||
|
||||
// Set version to 7 with ordering based on timestamp.
|
||||
uuid[6] = static_cast<uint8_t>((timestamp_ms >> 8) & 0x0F);
|
||||
uuid[6] |= 0x70;
|
||||
|
||||
uuid[7] = static_cast<uint8_t>(timestamp_ms & 0xFF);
|
||||
|
||||
// Seeded RNG
|
||||
auto rand64 = std::mt19937_64{seed}();
|
||||
|
||||
for(int i = 0; i < 8; ++i)
|
||||
{
|
||||
uuid[8 + i] = static_cast<uint8_t>((rand64 >> (56 - 8 * i)) & 0xFF);
|
||||
}
|
||||
|
||||
// Set variant to RFC 4122
|
||||
uuid[8] = (uuid[8] & 0x3F) | 0x80;
|
||||
|
||||
// Format as UUID string
|
||||
auto oss = std::ostringstream{};
|
||||
oss << std::hex << std::setfill('0');
|
||||
for(int i = 0; i < 16; ++i)
|
||||
{
|
||||
oss << std::setw(2) << static_cast<int>(uuid[i]);
|
||||
if(i == 3 || i == 5 || i == 7 || i == 9) oss << delim;
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,49 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2025 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 "lib/common/defines.hpp"
|
||||
#include "lib/common/mpl.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
uint64_t
|
||||
get_process_start_ticks_since_boot(pid_t pid);
|
||||
|
||||
// use this function to create a deterministic random number seed for the system and process
|
||||
uint64_t
|
||||
compute_system_seed(std::string_view machine_id, pid_t pid, pid_t ppid, uint64_t pstart_ticks);
|
||||
|
||||
std::string
|
||||
generate_uuid_v7(uint64_t timestamp_ns,
|
||||
uint64_t seed = std::random_device{}(),
|
||||
std::string_view delim = "-");
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
Αναφορά σε νέο ζήτημα
Block a user